@tarcisiopgs/lisa 1.15.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -3
- package/dist/{chunk-OYQ6TOAG.js → chunk-GZ2ZAQO4.js} +9 -2
- package/dist/{chunk-WZIPTRJL.js → chunk-ITQEGO5A.js} +4 -4
- package/dist/{chunk-NXGXGHS3.js → chunk-UQPR5OXK.js} +1 -1
- package/dist/{guardrails-KI5NEJVE.js → guardrails-I5ACG5LQ.js} +2 -2
- package/dist/index.js +183 -98
- package/dist/{kanban-ECJSRP4C.js → kanban-QZ5NRPJ5.js} +1 -1
- package/dist/{paths-HQQDKACV.js → paths-WQN4NBC6.js} +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -112,7 +112,7 @@ Set the tokens for your chosen source and PR platform:
|
|
|
112
112
|
# PR platform
|
|
113
113
|
GITHUB_TOKEN # GitHub (platform: cli or token)
|
|
114
114
|
GITLAB_TOKEN # GitLab (platform: gitlab)
|
|
115
|
-
BITBUCKET_TOKEN # Bitbucket (platform: bitbucket)
|
|
115
|
+
BITBUCKET_TOKEN # Bitbucket app password (platform: bitbucket)
|
|
116
116
|
BITBUCKET_USERNAME # Bitbucket username
|
|
117
117
|
|
|
118
118
|
# Issue sources
|
|
@@ -125,9 +125,14 @@ PLANE_BASE_URL # optional, defaults to https://api.plane.so
|
|
|
125
125
|
GITLAB_TOKEN # source: gitlab-issues
|
|
126
126
|
GITLAB_BASE_URL # optional, defaults to https://gitlab.com
|
|
127
127
|
GITHUB_TOKEN # source: github-issues
|
|
128
|
-
JIRA_BASE_URL # source: jira
|
|
128
|
+
JIRA_BASE_URL # source: jira (e.g. https://yourorg.atlassian.net)
|
|
129
129
|
JIRA_EMAIL
|
|
130
|
-
JIRA_API_TOKEN
|
|
130
|
+
JIRA_API_TOKEN # generate at id.atlassian.com — expires, regenerate if 401
|
|
131
|
+
|
|
132
|
+
# Aider provider (one of)
|
|
133
|
+
GEMINI_API_KEY
|
|
134
|
+
OPENAI_API_KEY
|
|
135
|
+
ANTHROPIC_API_KEY
|
|
131
136
|
```
|
|
132
137
|
|
|
133
138
|
---
|
|
@@ -196,6 +201,22 @@ validation:
|
|
|
196
201
|
require_acceptance_criteria: true
|
|
197
202
|
```
|
|
198
203
|
|
|
204
|
+
### Source-specific notes
|
|
205
|
+
|
|
206
|
+
**GitHub Issues / GitLab Issues** — `pick_from`, `in_progress`, and `done` are **labels**, not statuses. Make sure `in_progress` differs from `pick_from`; using the same value causes Lisa to re-pick issues that are already being worked on.
|
|
207
|
+
|
|
208
|
+
**Trello** — `team`, `pick_from`, `in_progress`, and `done` are list **names** (not IDs).
|
|
209
|
+
|
|
210
|
+
**Jira** — `team` is your project **key** (e.g. `ENG`). `JIRA_API_TOKEN` is generated at [id.atlassian.com](https://id.atlassian.com) and expires — regenerate if you get 401 errors.
|
|
211
|
+
|
|
212
|
+
**Goose** — `lisa init` asks which backend to use (gemini-cli, anthropic, openai, etc.) and saves it to config. No env vars needed. You can also set `GOOSE_PROVIDER` manually — it takes precedence over the config value.
|
|
213
|
+
|
|
214
|
+
**Aider** — requires a direct LLM API key (`GEMINI_API_KEY`, `OPENAI_API_KEY`, or `ANTHROPIC_API_KEY`). Does not support OAuth or cached credentials.
|
|
215
|
+
|
|
216
|
+
**OpenCode** — if `~/.config/opencode/config.json` contains MCP entries, remove them or set the file to `{}` to prevent OpenCode from hanging on startup.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
199
220
|
### Workflow Modes
|
|
200
221
|
|
|
201
222
|
**Branch** — The agent creates a branch in your current checkout. Simple setup, works everywhere.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/paths.ts
|
|
4
4
|
import { createHash } from "crypto";
|
|
5
5
|
import { existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from "fs";
|
|
6
|
-
import { homedir } from "os";
|
|
6
|
+
import { homedir, platform } from "os";
|
|
7
7
|
import { join, resolve } from "path";
|
|
8
8
|
var MAX_LOG_FILES = 20;
|
|
9
9
|
function projectHash(cwd) {
|
|
@@ -11,7 +11,14 @@ function projectHash(cwd) {
|
|
|
11
11
|
return createHash("sha256").update(absolute).digest("hex").slice(0, 12);
|
|
12
12
|
}
|
|
13
13
|
function getCacheDir(cwd) {
|
|
14
|
-
|
|
14
|
+
let base;
|
|
15
|
+
if (process.env.XDG_CACHE_HOME) {
|
|
16
|
+
base = process.env.XDG_CACHE_HOME;
|
|
17
|
+
} else if (platform() === "darwin") {
|
|
18
|
+
base = join(homedir(), "Library", "Caches");
|
|
19
|
+
} else {
|
|
20
|
+
base = join(homedir(), ".cache");
|
|
21
|
+
}
|
|
15
22
|
return join(base, "lisa", projectHash(cwd));
|
|
16
23
|
}
|
|
17
24
|
function getLogsDir(cwd) {
|
|
@@ -193,7 +193,7 @@ var GitHubIssuesSource = class {
|
|
|
193
193
|
const filterLabels = isOrphanDetection ? [config.pick_from] : Array.isArray(config.label) ? config.label : [config.label];
|
|
194
194
|
const label = filterLabels.map((l) => encodeURIComponent(l)).join(",");
|
|
195
195
|
const path = `/repos/${owner}/${repo}/issues?labels=${label}&state=open&sort=created&direction=asc&per_page=100`;
|
|
196
|
-
const issues = await githubGet(path);
|
|
196
|
+
const issues = (await githubGet(path)).filter((i) => !i.pull_request);
|
|
197
197
|
if (issues.length === 0) return null;
|
|
198
198
|
const unblocked = [];
|
|
199
199
|
const blocked = [];
|
|
@@ -333,7 +333,7 @@ var GitHubIssuesSource = class {
|
|
|
333
333
|
const labels = Array.isArray(config.label) ? config.label : [config.label];
|
|
334
334
|
const label = labels.map((l) => encodeURIComponent(l)).join(",");
|
|
335
335
|
const path = `/repos/${owner}/${repo}/issues?labels=${label}&state=open&sort=created&direction=asc&per_page=100`;
|
|
336
|
-
const issues = await githubGet(path);
|
|
336
|
+
const issues = (await githubGet(path)).filter((i) => !i.pull_request);
|
|
337
337
|
return issues.map((issue) => ({
|
|
338
338
|
id: makeIssueId(owner, repo, issue.number),
|
|
339
339
|
title: issue.title,
|
|
@@ -448,10 +448,10 @@ var GitLabIssuesSource = class {
|
|
|
448
448
|
);
|
|
449
449
|
const activeBlockers = links.filter((link) => {
|
|
450
450
|
if (link.link_type === "is_blocked_by") {
|
|
451
|
-
return link.
|
|
451
|
+
return link.state !== "closed";
|
|
452
452
|
}
|
|
453
453
|
return false;
|
|
454
|
-
}).map((link) => link.
|
|
454
|
+
}).map((link) => link.iid);
|
|
455
455
|
if (activeBlockers.length === 0) {
|
|
456
456
|
unblocked.push(issue2);
|
|
457
457
|
} else {
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
extractContext,
|
|
7
7
|
extractErrorType,
|
|
8
8
|
migrateGuardrails
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UQPR5OXK.js";
|
|
10
10
|
import {
|
|
11
11
|
ensureCacheDir,
|
|
12
12
|
getLogsDir,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
getPlanPath,
|
|
15
15
|
getPrCachePath,
|
|
16
16
|
rotateLogFiles
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-GZ2ZAQO4.js";
|
|
18
18
|
import {
|
|
19
19
|
fetchPrFeedback,
|
|
20
20
|
formatPrFeedbackEntry
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
ok,
|
|
33
33
|
setOutputMode,
|
|
34
34
|
warn
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-ITQEGO5A.js";
|
|
36
36
|
import {
|
|
37
37
|
notify,
|
|
38
38
|
resetTitle,
|
|
@@ -53,7 +53,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
|
53
53
|
import { resolve } from "path";
|
|
54
54
|
import { parse, stringify } from "yaml";
|
|
55
55
|
var DEFAULT_OVERSEER_CONFIG = {
|
|
56
|
-
enabled:
|
|
56
|
+
enabled: true,
|
|
57
57
|
check_interval: 30,
|
|
58
58
|
stuck_threshold: 300
|
|
59
59
|
};
|
|
@@ -378,7 +378,7 @@ async function hasCodeChanges(repoPath, baseBranch) {
|
|
|
378
378
|
|
|
379
379
|
// src/providers/aider.ts
|
|
380
380
|
import { execSync } from "child_process";
|
|
381
|
-
import { appendFileSync as appendFileSync2, mkdtempSync,
|
|
381
|
+
import { appendFileSync as appendFileSync2, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
382
382
|
import { tmpdir } from "os";
|
|
383
383
|
import { join as join2 } from "path";
|
|
384
384
|
|
|
@@ -532,7 +532,8 @@ var AIDER_API_KEY_ENV_VARS = [
|
|
|
532
532
|
"COHERE_API_KEY",
|
|
533
533
|
"MISTRAL_API_KEY",
|
|
534
534
|
"DEEPSEEK_API_KEY",
|
|
535
|
-
"AZURE_API_KEY"
|
|
535
|
+
"AZURE_API_KEY",
|
|
536
|
+
"XAI_API_KEY"
|
|
536
537
|
];
|
|
537
538
|
var AiderProvider = class {
|
|
538
539
|
name = "aider";
|
|
@@ -559,7 +560,7 @@ var AiderProvider = class {
|
|
|
559
560
|
writeFileSync2(promptFile, prompt, "utf-8");
|
|
560
561
|
try {
|
|
561
562
|
const modelFlag = opts.model ? `--model ${opts.model}` : "";
|
|
562
|
-
const command = `aider --message
|
|
563
|
+
const command = `aider --message-file '${promptFile}' --yes-always ${modelFlag}`;
|
|
563
564
|
const { proc, isPty } = spawnWithPty(command, {
|
|
564
565
|
cwd: opts.cwd,
|
|
565
566
|
env: { ...process.env, ...opts.env }
|
|
@@ -613,7 +614,7 @@ var AiderProvider = class {
|
|
|
613
614
|
};
|
|
614
615
|
} finally {
|
|
615
616
|
try {
|
|
616
|
-
|
|
617
|
+
rmSync2(tmpDir, { recursive: true, force: true });
|
|
617
618
|
} catch {
|
|
618
619
|
}
|
|
619
620
|
}
|
|
@@ -622,7 +623,7 @@ var AiderProvider = class {
|
|
|
622
623
|
|
|
623
624
|
// src/providers/claude.ts
|
|
624
625
|
import { execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
625
|
-
import { appendFileSync as appendFileSync3, mkdtempSync as mkdtempSync2,
|
|
626
|
+
import { appendFileSync as appendFileSync3, mkdtempSync as mkdtempSync2, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
626
627
|
import { tmpdir as tmpdir2 } from "os";
|
|
627
628
|
import { join as join3 } from "path";
|
|
628
629
|
var ClaudeProvider = class {
|
|
@@ -711,7 +712,7 @@ var ClaudeProvider = class {
|
|
|
711
712
|
};
|
|
712
713
|
} finally {
|
|
713
714
|
try {
|
|
714
|
-
|
|
715
|
+
rmSync3(tmpDir, { recursive: true, force: true });
|
|
715
716
|
} catch {
|
|
716
717
|
}
|
|
717
718
|
}
|
|
@@ -720,14 +721,14 @@ var ClaudeProvider = class {
|
|
|
720
721
|
|
|
721
722
|
// src/providers/codex.ts
|
|
722
723
|
import { execSync as execSync3 } from "child_process";
|
|
723
|
-
import { appendFileSync as appendFileSync4, mkdtempSync as mkdtempSync3,
|
|
724
|
+
import { appendFileSync as appendFileSync4, mkdtempSync as mkdtempSync3, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
724
725
|
import { tmpdir as tmpdir3 } from "os";
|
|
725
726
|
import { join as join4 } from "path";
|
|
726
727
|
var CodexProvider = class {
|
|
727
728
|
name = "codex";
|
|
728
729
|
async isAvailable() {
|
|
729
730
|
try {
|
|
730
|
-
execSync3("codex
|
|
731
|
+
execSync3("which codex", { stdio: "ignore" });
|
|
731
732
|
return true;
|
|
732
733
|
} catch {
|
|
733
734
|
return false;
|
|
@@ -794,7 +795,7 @@ var CodexProvider = class {
|
|
|
794
795
|
};
|
|
795
796
|
} finally {
|
|
796
797
|
try {
|
|
797
|
-
|
|
798
|
+
rmSync4(tmpDir, { recursive: true, force: true });
|
|
798
799
|
} catch {
|
|
799
800
|
}
|
|
800
801
|
}
|
|
@@ -803,7 +804,7 @@ var CodexProvider = class {
|
|
|
803
804
|
|
|
804
805
|
// src/providers/copilot.ts
|
|
805
806
|
import { execSync as execSync4 } from "child_process";
|
|
806
|
-
import { appendFileSync as appendFileSync5, mkdtempSync as mkdtempSync4,
|
|
807
|
+
import { appendFileSync as appendFileSync5, mkdtempSync as mkdtempSync4, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
807
808
|
import { tmpdir as tmpdir4 } from "os";
|
|
808
809
|
import { join as join5 } from "path";
|
|
809
810
|
var CopilotProvider = class {
|
|
@@ -822,7 +823,8 @@ var CopilotProvider = class {
|
|
|
822
823
|
const promptFile = join5(tmpDir, "prompt.md");
|
|
823
824
|
writeFileSync5(promptFile, prompt, "utf-8");
|
|
824
825
|
try {
|
|
825
|
-
const
|
|
826
|
+
const modelFlag = opts.model ? `--model ${opts.model}` : "";
|
|
827
|
+
const command = `copilot --allow-all ${modelFlag} -p "$(cat '${promptFile}')"`;
|
|
826
828
|
const { proc, isPty } = spawnWithPty(command, {
|
|
827
829
|
cwd: opts.cwd,
|
|
828
830
|
env: { ...process.env, ...opts.env }
|
|
@@ -876,7 +878,7 @@ var CopilotProvider = class {
|
|
|
876
878
|
};
|
|
877
879
|
} finally {
|
|
878
880
|
try {
|
|
879
|
-
|
|
881
|
+
rmSync5(tmpDir, { recursive: true, force: true });
|
|
880
882
|
} catch {
|
|
881
883
|
}
|
|
882
884
|
}
|
|
@@ -885,13 +887,13 @@ var CopilotProvider = class {
|
|
|
885
887
|
|
|
886
888
|
// src/providers/cursor.ts
|
|
887
889
|
import { execSync as execSync5 } from "child_process";
|
|
888
|
-
import { appendFileSync as appendFileSync6, mkdtempSync as mkdtempSync5,
|
|
890
|
+
import { appendFileSync as appendFileSync6, mkdtempSync as mkdtempSync5, rmSync as rmSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
889
891
|
import { tmpdir as tmpdir5 } from "os";
|
|
890
892
|
import { join as join6 } from "path";
|
|
891
893
|
function findCursorBinary() {
|
|
892
894
|
for (const bin of ["agent", "cursor-agent"]) {
|
|
893
895
|
try {
|
|
894
|
-
execSync5(
|
|
896
|
+
execSync5(`which ${bin}`, { stdio: "ignore" });
|
|
895
897
|
return bin;
|
|
896
898
|
} catch {
|
|
897
899
|
}
|
|
@@ -900,12 +902,17 @@ function findCursorBinary() {
|
|
|
900
902
|
}
|
|
901
903
|
var CursorProvider = class {
|
|
902
904
|
name = "cursor";
|
|
905
|
+
_bin = void 0;
|
|
906
|
+
resolveBin() {
|
|
907
|
+
if (this._bin === void 0) this._bin = findCursorBinary();
|
|
908
|
+
return this._bin;
|
|
909
|
+
}
|
|
903
910
|
async isAvailable() {
|
|
904
|
-
return
|
|
911
|
+
return this.resolveBin() !== null;
|
|
905
912
|
}
|
|
906
913
|
async run(prompt, opts) {
|
|
907
914
|
const start = Date.now();
|
|
908
|
-
const bin =
|
|
915
|
+
const bin = this.resolveBin();
|
|
909
916
|
if (!bin) {
|
|
910
917
|
return {
|
|
911
918
|
success: false,
|
|
@@ -972,7 +979,7 @@ var CursorProvider = class {
|
|
|
972
979
|
};
|
|
973
980
|
} finally {
|
|
974
981
|
try {
|
|
975
|
-
|
|
982
|
+
rmSync6(tmpDir, { recursive: true, force: true });
|
|
976
983
|
} catch {
|
|
977
984
|
}
|
|
978
985
|
}
|
|
@@ -981,7 +988,7 @@ var CursorProvider = class {
|
|
|
981
988
|
|
|
982
989
|
// src/providers/gemini.ts
|
|
983
990
|
import { execSync as execSync6 } from "child_process";
|
|
984
|
-
import { appendFileSync as appendFileSync7, mkdtempSync as mkdtempSync6,
|
|
991
|
+
import { appendFileSync as appendFileSync7, mkdtempSync as mkdtempSync6, rmSync as rmSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
985
992
|
import { tmpdir as tmpdir6 } from "os";
|
|
986
993
|
import { join as join7 } from "path";
|
|
987
994
|
var GEMINI_ERROR_PATTERN = /^Error (executing tool|generating content)/;
|
|
@@ -1056,7 +1063,7 @@ var GeminiProvider = class {
|
|
|
1056
1063
|
};
|
|
1057
1064
|
} finally {
|
|
1058
1065
|
try {
|
|
1059
|
-
|
|
1066
|
+
rmSync7(tmpDir, { recursive: true, force: true });
|
|
1060
1067
|
} catch {
|
|
1061
1068
|
}
|
|
1062
1069
|
}
|
|
@@ -1065,7 +1072,7 @@ var GeminiProvider = class {
|
|
|
1065
1072
|
|
|
1066
1073
|
// src/providers/goose.ts
|
|
1067
1074
|
import { execSync as execSync7 } from "child_process";
|
|
1068
|
-
import { appendFileSync as appendFileSync8, mkdtempSync as mkdtempSync7,
|
|
1075
|
+
import { appendFileSync as appendFileSync8, mkdtempSync as mkdtempSync7, rmSync as rmSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
1069
1076
|
import { tmpdir as tmpdir7 } from "os";
|
|
1070
1077
|
import { join as join8 } from "path";
|
|
1071
1078
|
var GooseProvider = class {
|
|
@@ -1140,7 +1147,7 @@ var GooseProvider = class {
|
|
|
1140
1147
|
};
|
|
1141
1148
|
} finally {
|
|
1142
1149
|
try {
|
|
1143
|
-
|
|
1150
|
+
rmSync8(tmpDir, { recursive: true, force: true });
|
|
1144
1151
|
} catch {
|
|
1145
1152
|
}
|
|
1146
1153
|
}
|
|
@@ -1149,7 +1156,7 @@ var GooseProvider = class {
|
|
|
1149
1156
|
|
|
1150
1157
|
// src/providers/opencode.ts
|
|
1151
1158
|
import { execSync as execSync8 } from "child_process";
|
|
1152
|
-
import { appendFileSync as appendFileSync9, mkdtempSync as mkdtempSync8,
|
|
1159
|
+
import { appendFileSync as appendFileSync9, mkdtempSync as mkdtempSync8, rmSync as rmSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
1153
1160
|
import { tmpdir as tmpdir8 } from "os";
|
|
1154
1161
|
import { join as join9 } from "path";
|
|
1155
1162
|
var OpenCodeProvider = class {
|
|
@@ -1168,7 +1175,8 @@ var OpenCodeProvider = class {
|
|
|
1168
1175
|
const promptFile = join9(tmpDir, "prompt.md");
|
|
1169
1176
|
writeFileSync9(promptFile, prompt, "utf-8");
|
|
1170
1177
|
try {
|
|
1171
|
-
const
|
|
1178
|
+
const modelFlag = opts.model ? `--model ${opts.model}` : "";
|
|
1179
|
+
const command = `opencode run ${modelFlag} "$(cat '${promptFile}')"`;
|
|
1172
1180
|
const { proc, isPty } = spawnWithPty(command, {
|
|
1173
1181
|
cwd: opts.cwd,
|
|
1174
1182
|
env: { ...process.env, ...opts.env }
|
|
@@ -1222,7 +1230,7 @@ var OpenCodeProvider = class {
|
|
|
1222
1230
|
};
|
|
1223
1231
|
} finally {
|
|
1224
1232
|
try {
|
|
1225
|
-
|
|
1233
|
+
rmSync9(tmpDir, { recursive: true, force: true });
|
|
1226
1234
|
} catch {
|
|
1227
1235
|
}
|
|
1228
1236
|
}
|
|
@@ -1267,8 +1275,8 @@ var ELIGIBLE_ERROR_PATTERNS = [
|
|
|
1267
1275
|
/ECONNRESET/,
|
|
1268
1276
|
/ENOTFOUND/,
|
|
1269
1277
|
/fetch failed/i,
|
|
1270
|
-
/
|
|
1271
|
-
|
|
1278
|
+
/\btimeout\b/i,
|
|
1279
|
+
/\btimed?\s*out\b/i,
|
|
1272
1280
|
/network.?error/i,
|
|
1273
1281
|
/not installed/i,
|
|
1274
1282
|
/not in PATH/i,
|
|
@@ -1534,9 +1542,12 @@ async function deleteProviderComments(prUrl) {
|
|
|
1534
1542
|
const [, owner, repo, prNumber] = match;
|
|
1535
1543
|
const { stdout } = await execa2("gh", [
|
|
1536
1544
|
"api",
|
|
1545
|
+
"--paginate",
|
|
1546
|
+
"--jq",
|
|
1547
|
+
".[]",
|
|
1537
1548
|
`/repos/${owner}/${repo}/issues/${prNumber}/comments`
|
|
1538
1549
|
]);
|
|
1539
|
-
const comments = JSON.parse(
|
|
1550
|
+
const comments = stdout.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
1540
1551
|
for (const comment of comments) {
|
|
1541
1552
|
if (PROVIDER_ATTRIBUTION_RE.test(comment.body)) {
|
|
1542
1553
|
try {
|
|
@@ -1572,7 +1583,7 @@ async function appendPrAttribution(prUrl, providerUsed) {
|
|
|
1572
1583
|
// src/cli/detection.ts
|
|
1573
1584
|
function getVersion() {
|
|
1574
1585
|
try {
|
|
1575
|
-
const pkgPath = resolvePath(new URL(".", import.meta.url).pathname, "
|
|
1586
|
+
const pkgPath = resolvePath(new URL(".", import.meta.url).pathname, "../package.json");
|
|
1576
1587
|
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
1577
1588
|
return pkg.version;
|
|
1578
1589
|
} catch {
|
|
@@ -1581,7 +1592,7 @@ function getVersion() {
|
|
|
1581
1592
|
}
|
|
1582
1593
|
var CURSOR_FREE_PLAN_ERROR = "Free plans can only use Auto";
|
|
1583
1594
|
async function isCursorFreePlan() {
|
|
1584
|
-
const { mkdtempSync: mkdtempSync9, unlinkSync:
|
|
1595
|
+
const { mkdtempSync: mkdtempSync9, unlinkSync: unlinkSync4, writeFileSync: writeFileSync11 } = await import("fs");
|
|
1585
1596
|
const tmpDir = mkdtempSync9(join10(tmpdir9(), "lisa-cursor-check-"));
|
|
1586
1597
|
const promptFile = join10(tmpDir, "prompt.txt");
|
|
1587
1598
|
writeFileSync11(promptFile, "test", "utf-8");
|
|
@@ -1606,7 +1617,7 @@ async function isCursorFreePlan() {
|
|
|
1606
1617
|
return errorOutput.includes(CURSOR_FREE_PLAN_ERROR);
|
|
1607
1618
|
} finally {
|
|
1608
1619
|
try {
|
|
1609
|
-
|
|
1620
|
+
unlinkSync4(promptFile);
|
|
1610
1621
|
} catch {
|
|
1611
1622
|
}
|
|
1612
1623
|
try {
|
|
@@ -1912,9 +1923,50 @@ async function runConfigWizard(existing) {
|
|
|
1912
1923
|
if (clack2.isCancel(selected)) return process.exit(0);
|
|
1913
1924
|
providerName = selected;
|
|
1914
1925
|
}
|
|
1926
|
+
let gooseProvider;
|
|
1927
|
+
if (providerName === "goose") {
|
|
1928
|
+
const gooseProviderAnswer = await clack2.select({
|
|
1929
|
+
message: "Which backend should Goose use?",
|
|
1930
|
+
initialValue: initial?.provider_options?.goose?.goose_provider ?? process.env.GOOSE_PROVIDER ?? "gemini-cli",
|
|
1931
|
+
options: [
|
|
1932
|
+
{ value: "gemini-cli", label: "Gemini CLI", hint: "requires Gemini CLI installed" },
|
|
1933
|
+
{ value: "anthropic", label: "Anthropic", hint: "requires ANTHROPIC_API_KEY" },
|
|
1934
|
+
{ value: "openai", label: "OpenAI", hint: "requires OPENAI_API_KEY" },
|
|
1935
|
+
{ value: "google", label: "Google (direct)", hint: "requires GOOGLE_API_KEY" },
|
|
1936
|
+
{ value: "ollama", label: "Ollama", hint: "local models" }
|
|
1937
|
+
]
|
|
1938
|
+
});
|
|
1939
|
+
if (clack2.isCancel(gooseProviderAnswer)) return process.exit(0);
|
|
1940
|
+
gooseProvider = gooseProviderAnswer;
|
|
1941
|
+
} else if (providerName === "aider") {
|
|
1942
|
+
clack2.log.info(
|
|
1943
|
+
`Aider requires a direct LLM API key in your environment.
|
|
1944
|
+
Set one of: ${pc.bold("GEMINI_API_KEY")}, ${pc.bold("OPENAI_API_KEY")}, ${pc.bold("ANTHROPIC_API_KEY")}, etc.
|
|
1945
|
+
Aider does not use OAuth or cached credentials.`
|
|
1946
|
+
);
|
|
1947
|
+
} else if (providerName === "opencode") {
|
|
1948
|
+
clack2.log.info(
|
|
1949
|
+
`OpenCode tip: if you have MCP entries in ${pc.cyan("~/.config/opencode/config.json")},
|
|
1950
|
+
remove them or set the file to ${pc.cyan("{}")} \u2014 MCP tools can cause OpenCode to hang.`
|
|
1951
|
+
);
|
|
1952
|
+
}
|
|
1915
1953
|
let selectedModels = [];
|
|
1916
1954
|
let availableModels = providerModels[providerName];
|
|
1917
|
-
if (providerName === "
|
|
1955
|
+
if (providerName === "goose" && gooseProvider) {
|
|
1956
|
+
const gooseModelsByBackend = {
|
|
1957
|
+
"gemini-cli": [
|
|
1958
|
+
"gemini-2.5-pro",
|
|
1959
|
+
"gemini-2.5-flash",
|
|
1960
|
+
"gemini-2.0-flash",
|
|
1961
|
+
"gemini-2.5-flash-lite"
|
|
1962
|
+
],
|
|
1963
|
+
anthropic: ["claude-sonnet-4-6", "claude-opus-4-6", "claude-haiku-4-5", "claude-sonnet-4-5"],
|
|
1964
|
+
openai: ["gpt-4o", "gpt-4o-mini", "o3", "o4-mini"],
|
|
1965
|
+
google: ["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash"],
|
|
1966
|
+
ollama: ["llama3.3", "qwen2.5-coder", "mistral"]
|
|
1967
|
+
};
|
|
1968
|
+
availableModels = gooseModelsByBackend[gooseProvider] ?? [];
|
|
1969
|
+
} else if (providerName === "cursor") {
|
|
1918
1970
|
const isFree = await isCursorFreePlan();
|
|
1919
1971
|
if (isFree) {
|
|
1920
1972
|
availableModels = ["auto"];
|
|
@@ -2069,21 +2121,29 @@ Then reload: ${pc.cyan(`source ${shell}`)}`
|
|
|
2069
2121
|
});
|
|
2070
2122
|
if (clack2.isCancel(projectAnswer)) return process.exit(0);
|
|
2071
2123
|
project = projectAnswer;
|
|
2124
|
+
const isLabelBasedSource = source === "github-issues" || source === "gitlab-issues";
|
|
2072
2125
|
const pickFromAnswer = await clack2.text({
|
|
2073
|
-
message: "Pick up issues in which status?",
|
|
2074
|
-
initialValue: initial?.source_config.pick_from ?? "Backlog",
|
|
2075
|
-
placeholder: "e.g. Backlog, Todo"
|
|
2126
|
+
message: isLabelBasedSource ? "Pick up issues in which state? (open, closed, or a label name)" : "Pick up issues in which status?",
|
|
2127
|
+
initialValue: initial?.source_config.pick_from ?? (isLabelBasedSource ? "open" : "Backlog"),
|
|
2128
|
+
placeholder: isLabelBasedSource ? "e.g. open" : "e.g. Backlog, Todo"
|
|
2076
2129
|
});
|
|
2077
2130
|
if (clack2.isCancel(pickFromAnswer)) return process.exit(0);
|
|
2078
2131
|
pickFrom = pickFromAnswer;
|
|
2079
2132
|
const inProgressAnswer = await clack2.text({
|
|
2080
|
-
message: "Move to which status while the agent is working?",
|
|
2081
|
-
initialValue: initial?.source_config.in_progress ?? "In Progress"
|
|
2133
|
+
message: isLabelBasedSource ? "Which label to apply while the agent is working? (must differ from pick_from label)" : "Move to which status while the agent is working?",
|
|
2134
|
+
initialValue: initial?.source_config.in_progress ?? "In Progress",
|
|
2135
|
+
placeholder: isLabelBasedSource ? "e.g. in-progress" : void 0
|
|
2082
2136
|
});
|
|
2083
2137
|
if (clack2.isCancel(inProgressAnswer)) return process.exit(0);
|
|
2084
2138
|
inProgress = inProgressAnswer;
|
|
2139
|
+
if (isLabelBasedSource && inProgress === pickFrom) {
|
|
2140
|
+
clack2.log.warning(
|
|
2141
|
+
`"in_progress" label is the same as "pick_from" label ("${pickFrom}").
|
|
2142
|
+
This will cause Lisa to re-pick the issue on recovery. Consider using a different label.`
|
|
2143
|
+
);
|
|
2144
|
+
}
|
|
2085
2145
|
const doneAnswer = await clack2.text({
|
|
2086
|
-
message: "Move to which status after the PR is created?",
|
|
2146
|
+
message: isLabelBasedSource ? "Which label to apply after the PR is created?" : "Move to which status after the PR is created?",
|
|
2087
2147
|
initialValue: initial?.source_config.done ?? "In Review"
|
|
2088
2148
|
});
|
|
2089
2149
|
if (clack2.isCancel(doneAnswer)) return process.exit(0);
|
|
@@ -2144,7 +2204,10 @@ Then reload: ${pc.cyan(`source ${shell}`)}`
|
|
|
2144
2204
|
provider: providerName,
|
|
2145
2205
|
provider_options: {
|
|
2146
2206
|
...initial?.provider_options || {},
|
|
2147
|
-
[providerName]: {
|
|
2207
|
+
[providerName]: {
|
|
2208
|
+
models: selectedModels,
|
|
2209
|
+
...gooseProvider ? { goose_provider: gooseProvider } : {}
|
|
2210
|
+
}
|
|
2148
2211
|
},
|
|
2149
2212
|
source,
|
|
2150
2213
|
source_config: {
|
|
@@ -2217,8 +2280,8 @@ var feedback = defineCommand2({
|
|
|
2217
2280
|
},
|
|
2218
2281
|
async run({ args }) {
|
|
2219
2282
|
const { fetchPrFeedback: fetchPrFeedback2, formatPrFeedbackEntry: formatPrFeedbackEntry2 } = await import("./pr-feedback-DGHNP3E7.js");
|
|
2220
|
-
const { appendRawEntrySync } = await import("./guardrails-
|
|
2221
|
-
const { ensureCacheDir: ensureCacheDir2 } = await import("./paths-
|
|
2283
|
+
const { appendRawEntrySync } = await import("./guardrails-I5ACG5LQ.js");
|
|
2284
|
+
const { ensureCacheDir: ensureCacheDir2 } = await import("./paths-WQN4NBC6.js");
|
|
2222
2285
|
const prUrl = args.pr;
|
|
2223
2286
|
const issueId = args.issue ?? "unknown";
|
|
2224
2287
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -2781,14 +2844,14 @@ var LinearSource = class {
|
|
|
2781
2844
|
`mutation($teamId: String!, $name: String!) {
|
|
2782
2845
|
issueLabelCreate(input: { teamId: $teamId, name: $name }) {
|
|
2783
2846
|
success
|
|
2784
|
-
|
|
2847
|
+
issueLabel { id name }
|
|
2785
2848
|
}
|
|
2786
2849
|
}`,
|
|
2787
2850
|
{ teamId: issueData.issue.team.id, name: labelName }
|
|
2788
2851
|
);
|
|
2789
|
-
if (created.issueLabelCreate.success && created.issueLabelCreate.
|
|
2852
|
+
if (created.issueLabelCreate.success && created.issueLabelCreate.issueLabel) {
|
|
2790
2853
|
log(`Label "${labelName}" created automatically in team ${issueData.issue.team.id}`);
|
|
2791
|
-
teamLabel = created.issueLabelCreate.
|
|
2854
|
+
teamLabel = created.issueLabelCreate.issueLabel;
|
|
2792
2855
|
} else {
|
|
2793
2856
|
const refetch = await gql(
|
|
2794
2857
|
`query($identifier: String!) {
|
|
@@ -2989,7 +3052,7 @@ var PlaneSource = class {
|
|
|
2989
3052
|
labelNames.map((name) => resolveLabelId(workspaceSlug, projectId, name))
|
|
2990
3053
|
);
|
|
2991
3054
|
const data = await planeGet(
|
|
2992
|
-
`/workspaces/${workspaceSlug}/projects/${projectId}/
|
|
3055
|
+
`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/?state=${stateId}&per_page=100`
|
|
2993
3056
|
);
|
|
2994
3057
|
const matching = data.results.filter((i) => i.state === stateId).filter((i) => labelIds.every((lid) => i.labels.includes(lid)));
|
|
2995
3058
|
if (matching.length === 0) return null;
|
|
@@ -3002,14 +3065,14 @@ var PlaneSource = class {
|
|
|
3002
3065
|
const blocked = [];
|
|
3003
3066
|
for (const issue3 of matching) {
|
|
3004
3067
|
const relations = await fetchAll(
|
|
3005
|
-
`/workspaces/${workspaceSlug}/projects/${projectId}/
|
|
3068
|
+
`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/${issue3.id}/relations/`
|
|
3006
3069
|
);
|
|
3007
3070
|
const blockerIds = relations.filter((r) => r.relation_type === "blocked_by").map((r) => r.related_issue);
|
|
3008
3071
|
const activeBlockers = [];
|
|
3009
3072
|
for (const blockerId of blockerIds) {
|
|
3010
3073
|
try {
|
|
3011
3074
|
const blocker = await planeGet(
|
|
3012
|
-
`/workspaces/${workspaceSlug}/projects/${projectId}/
|
|
3075
|
+
`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/${blockerId}/`
|
|
3013
3076
|
);
|
|
3014
3077
|
if (!doneStateIds.has(blocker.state)) {
|
|
3015
3078
|
activeBlockers.push(blockerId);
|
|
@@ -3050,7 +3113,7 @@ var PlaneSource = class {
|
|
|
3050
3113
|
try {
|
|
3051
3114
|
const { workspaceSlug, projectId, issueId } = parseIssueId(id);
|
|
3052
3115
|
const issue2 = await planeGet(
|
|
3053
|
-
`/workspaces/${workspaceSlug}/projects/${projectId}/
|
|
3116
|
+
`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/${issueId}/`
|
|
3054
3117
|
);
|
|
3055
3118
|
const webUrl = `${getAppUrl()}/${workspaceSlug}/projects/${projectId}/issues/${issue2.id}`;
|
|
3056
3119
|
return {
|
|
@@ -3067,14 +3130,14 @@ var PlaneSource = class {
|
|
|
3067
3130
|
const { workspaceSlug, projectId, issueId: planeIssueId } = parseIssueId(issueId);
|
|
3068
3131
|
const stateId = await resolveStateId(workspaceSlug, projectId, stateName);
|
|
3069
3132
|
await planePatch(
|
|
3070
|
-
`/workspaces/${workspaceSlug}/projects/${projectId}/
|
|
3133
|
+
`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/${planeIssueId}/`,
|
|
3071
3134
|
{ state: stateId }
|
|
3072
3135
|
);
|
|
3073
3136
|
}
|
|
3074
3137
|
async attachPullRequest(issueId, prUrl) {
|
|
3075
3138
|
const { workspaceSlug, projectId, issueId: planeIssueId } = parseIssueId(issueId);
|
|
3076
3139
|
await planePost(
|
|
3077
|
-
`/workspaces/${workspaceSlug}/projects/${projectId}/
|
|
3140
|
+
`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/${planeIssueId}/comments/`,
|
|
3078
3141
|
{ comment_html: `<p>Pull request: <a href="${prUrl}">${prUrl}</a></p>` }
|
|
3079
3142
|
);
|
|
3080
3143
|
}
|
|
@@ -3093,7 +3156,7 @@ var PlaneSource = class {
|
|
|
3093
3156
|
labelNames.map((name) => resolveLabelId(workspaceSlug, projectId, name))
|
|
3094
3157
|
);
|
|
3095
3158
|
const data = await planeGet(
|
|
3096
|
-
`/workspaces/${workspaceSlug}/projects/${projectId}/
|
|
3159
|
+
`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/?state=${stateId}&per_page=100`
|
|
3097
3160
|
);
|
|
3098
3161
|
return data.results.filter((i) => i.state === stateId).filter((i) => labelIds.every((lid) => i.labels.includes(lid))).map((i) => {
|
|
3099
3162
|
const webUrl = `${getAppUrl()}/${workspaceSlug}/projects/${projectId}/issues/${i.id}`;
|
|
@@ -3108,14 +3171,14 @@ var PlaneSource = class {
|
|
|
3108
3171
|
async removeLabel(issueId, labelName) {
|
|
3109
3172
|
const { workspaceSlug, projectId, issueId: planeIssueId } = parseIssueId(issueId);
|
|
3110
3173
|
const issue2 = await planeGet(
|
|
3111
|
-
`/workspaces/${workspaceSlug}/projects/${projectId}/
|
|
3174
|
+
`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/${planeIssueId}/`
|
|
3112
3175
|
);
|
|
3113
3176
|
const labels = await fetchLabels(workspaceSlug, projectId);
|
|
3114
3177
|
const labelObj = labels.find((l) => l.name.toLowerCase() === labelName.toLowerCase());
|
|
3115
3178
|
if (!labelObj || !issue2.labels.includes(labelObj.id)) return;
|
|
3116
3179
|
const updatedLabels = issue2.labels.filter((lid) => lid !== labelObj.id);
|
|
3117
3180
|
await planePatch(
|
|
3118
|
-
`/workspaces/${workspaceSlug}/projects/${projectId}/
|
|
3181
|
+
`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/${planeIssueId}/`,
|
|
3119
3182
|
{ labels: updatedLabels }
|
|
3120
3183
|
);
|
|
3121
3184
|
}
|
|
@@ -3157,6 +3220,24 @@ function extractStories(result) {
|
|
|
3157
3220
|
if (Array.isArray(result)) return result;
|
|
3158
3221
|
return result.data ?? [];
|
|
3159
3222
|
}
|
|
3223
|
+
function extractNext(result) {
|
|
3224
|
+
if (Array.isArray(result)) return null;
|
|
3225
|
+
return result.next ?? null;
|
|
3226
|
+
}
|
|
3227
|
+
async function searchStoriesAll(body) {
|
|
3228
|
+
const all = [];
|
|
3229
|
+
let next = null;
|
|
3230
|
+
do {
|
|
3231
|
+
const req = next ? { ...body, next } : body;
|
|
3232
|
+
const result = await shortcutPost(
|
|
3233
|
+
"/api/v3/stories/search",
|
|
3234
|
+
req
|
|
3235
|
+
);
|
|
3236
|
+
all.push(...extractStories(result));
|
|
3237
|
+
next = extractNext(result);
|
|
3238
|
+
} while (next);
|
|
3239
|
+
return all;
|
|
3240
|
+
}
|
|
3160
3241
|
async function resolveWorkflowStateId(stateName) {
|
|
3161
3242
|
const workflows = await shortcutGet("/api/v3/workflows");
|
|
3162
3243
|
for (const workflow of workflows) {
|
|
@@ -3218,15 +3299,11 @@ var ShortcutSource = class {
|
|
|
3218
3299
|
const seen = /* @__PURE__ */ new Set();
|
|
3219
3300
|
const allStories = [];
|
|
3220
3301
|
for (const stateId of stateIds) {
|
|
3221
|
-
const
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
archived: false
|
|
3227
|
-
}
|
|
3228
|
-
);
|
|
3229
|
-
for (const story2 of extractStories(searchResult)) {
|
|
3302
|
+
for (const story2 of await searchStoriesAll({
|
|
3303
|
+
workflow_state_id: stateId,
|
|
3304
|
+
label_name: primaryLabel,
|
|
3305
|
+
archived: false
|
|
3306
|
+
})) {
|
|
3230
3307
|
if (!seen.has(story2.id)) {
|
|
3231
3308
|
seen.add(story2.id);
|
|
3232
3309
|
allStories.push(story2);
|
|
@@ -3322,15 +3399,11 @@ var ShortcutSource = class {
|
|
|
3322
3399
|
const seen = /* @__PURE__ */ new Set();
|
|
3323
3400
|
const allStories = [];
|
|
3324
3401
|
for (const stateId of stateIds) {
|
|
3325
|
-
const
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
archived: false
|
|
3331
|
-
}
|
|
3332
|
-
);
|
|
3333
|
-
for (const story of extractStories(searchResult)) {
|
|
3402
|
+
for (const story of await searchStoriesAll({
|
|
3403
|
+
workflow_state_id: stateId,
|
|
3404
|
+
label_name: primaryLabel,
|
|
3405
|
+
archived: false
|
|
3406
|
+
})) {
|
|
3334
3407
|
if (!seen.has(story.id)) {
|
|
3335
3408
|
seen.add(story.id);
|
|
3336
3409
|
allStories.push(story);
|
|
@@ -3909,6 +3982,7 @@ async function injectRejectedPrFeedback(workspace, issueId, prUrls) {
|
|
|
3909
3982
|
clearPrUrl(workspace, issueId);
|
|
3910
3983
|
}
|
|
3911
3984
|
async function recoverOrphanIssues(source, config2) {
|
|
3985
|
+
if (!config2.source_config.in_progress) return;
|
|
3912
3986
|
if (config2.source_config.in_progress === config2.source_config.pick_from) return;
|
|
3913
3987
|
const orphanConfig = {
|
|
3914
3988
|
...config2.source_config,
|
|
@@ -4609,6 +4683,7 @@ async function appendPrAttribution2(prUrl, providerUsed) {
|
|
|
4609
4683
|
if (!getRes.ok) return;
|
|
4610
4684
|
const prData = await getRes.json();
|
|
4611
4685
|
const currentDescription = prData.description ?? "";
|
|
4686
|
+
const currentTitle = prData.title ?? "";
|
|
4612
4687
|
const providerName = formatProviderName2(providerUsed);
|
|
4613
4688
|
const attribution = `
|
|
4614
4689
|
|
|
@@ -4621,7 +4696,7 @@ async function appendPrAttribution2(prUrl, providerUsed) {
|
|
|
4621
4696
|
Authorization: authHeader,
|
|
4622
4697
|
"Content-Type": "application/json"
|
|
4623
4698
|
},
|
|
4624
|
-
body: JSON.stringify({ description: newDescription }),
|
|
4699
|
+
body: JSON.stringify({ description: newDescription, title: currentTitle }),
|
|
4625
4700
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS6)
|
|
4626
4701
|
});
|
|
4627
4702
|
} catch {
|
|
@@ -4722,7 +4797,7 @@ function buildPrCreateInstruction(platform2, targetBranch) {
|
|
|
4722
4797
|
REPO=$(git remote get-url origin | sed 's/.*bitbucket\\.org[:/][^/]*\\///;s/\\.git$//')
|
|
4723
4798
|
BRANCH=$(git branch --show-current)
|
|
4724
4799
|
curl -X POST \\
|
|
4725
|
-
-H "Authorization:
|
|
4800
|
+
-H "Authorization: Basic $(printf '%s:%s' "$BITBUCKET_USERNAME" "$BITBUCKET_TOKEN" | base64)" \\
|
|
4726
4801
|
-H "Content-Type: application/json" \\
|
|
4727
4802
|
"https://api.bitbucket.org/2.0/repositories/$WORKSPACE/$REPO/pullrequests" \\
|
|
4728
4803
|
--data "{\\"title\\":\\"<conventional-commit-title>\\",\\"description\\":\\"<markdown-summary>\\",\\"source\\":{\\"branch\\":{\\"name\\":\\"$BRANCH\\"}},\\"destination\\":{\\"branch\\":{\\"name\\":\\"${dest}\\"}}}"
|
|
@@ -4841,6 +4916,7 @@ function buildRulesSection(env, variant = "issue", extraRules = "") {
|
|
|
4841
4916
|
- **ALL git commits, branch names, PR titles, and PR descriptions MUST be in English.**
|
|
4842
4917
|
- The issue description may be in any language \u2014 read it for context but write all code artifacts in English.
|
|
4843
4918
|
- Do NOT install new dependencies unless the issue explicitly requires it.
|
|
4919
|
+
- Do NOT use documentation lookup MCP tools (e.g., Context7, codesearch, Exa) \u2014 they have free-tier rate limits that will block your execution. Read files directly from the repository. Web search is allowed only when strictly necessary (e.g., looking up an external API format not available in the codebase).
|
|
4844
4920
|
${envRule}${extraRules}- If you get stuck or the issue is unclear, STOP and explain why.
|
|
4845
4921
|
${scopeRule}
|
|
4846
4922
|
- If the repo has a CLAUDE.md, read it first and follow its conventions.`;
|
|
@@ -5172,14 +5248,17 @@ ${generatorBlock}
|
|
|
5172
5248
|
|
|
5173
5249
|
2. **Determine execution order**: If multiple repos are affected, decide the order. Repos that produce APIs, schemas, or shared libraries should come first. Repos that consume them should come later.
|
|
5174
5250
|
|
|
5175
|
-
3. **Write the plan**:
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5251
|
+
3. **Write the plan file to disk**: Use a bash command or file-write tool to write the plan to \`${resolvedPlanPath}\`.
|
|
5252
|
+
**You MUST write the file to disk. Do NOT print the JSON to stdout or in a code block.**
|
|
5253
|
+
|
|
5254
|
+
The file must be valid JSON with this structure (replace angle-bracket placeholders with real values):
|
|
5255
|
+
- \`repoPath\`: absolute path to the affected repository
|
|
5256
|
+
- \`scope\`: concise English description of what to implement in that repo
|
|
5257
|
+
- \`order\`: integer starting at 1 (lower = executes first)
|
|
5258
|
+
|
|
5259
|
+
Use your write_file tool, or a bash command such as:
|
|
5260
|
+
\`\`\`bash
|
|
5261
|
+
printf '%s' '{"steps":[{"repoPath":"/absolute/path","scope":"description of work","order":1}]}' > '${resolvedPlanPath}'
|
|
5183
5262
|
\`\`\`
|
|
5184
5263
|
|
|
5185
5264
|
## Rules
|
|
@@ -5618,19 +5697,20 @@ function registerCleanup() {
|
|
|
5618
5697
|
}
|
|
5619
5698
|
|
|
5620
5699
|
// src/loop/manifest.ts
|
|
5621
|
-
import { existsSync as existsSync8, readFileSync as readFileSync8, unlinkSync
|
|
5700
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, unlinkSync } from "fs";
|
|
5622
5701
|
function readLisaManifest(cwd, issueId) {
|
|
5623
5702
|
const manifestPath = getManifestPath(cwd, issueId);
|
|
5624
5703
|
if (!existsSync8(manifestPath)) return null;
|
|
5625
5704
|
try {
|
|
5626
5705
|
return JSON.parse(readFileSync8(manifestPath, "utf-8").trim());
|
|
5627
5706
|
} catch {
|
|
5707
|
+
warn(`Failed to parse manifest at ${manifestPath} \u2014 agent may not have written it correctly`);
|
|
5628
5708
|
return null;
|
|
5629
5709
|
}
|
|
5630
5710
|
}
|
|
5631
5711
|
function cleanupManifest(cwd, issueId) {
|
|
5632
5712
|
try {
|
|
5633
|
-
|
|
5713
|
+
unlinkSync(getManifestPath(cwd, issueId));
|
|
5634
5714
|
} catch {
|
|
5635
5715
|
}
|
|
5636
5716
|
}
|
|
@@ -5664,14 +5744,13 @@ function readPlanFile(filePath) {
|
|
|
5664
5744
|
}
|
|
5665
5745
|
|
|
5666
5746
|
// src/loop/multi-repo-session.ts
|
|
5667
|
-
import { appendFileSync as appendFileSync10, unlinkSync as
|
|
5747
|
+
import { appendFileSync as appendFileSync10, unlinkSync as unlinkSync2 } from "fs";
|
|
5668
5748
|
import { join as join14, resolve as resolve7 } from "path";
|
|
5669
5749
|
async function runWorktreeMultiRepoSession(config2, issue2, logFile, session, models) {
|
|
5670
5750
|
const workspace = resolve7(config2.workspace);
|
|
5671
|
-
const
|
|
5672
|
-
const planPath = join14(workspace, `.lisa-plan-${safeId}.json`);
|
|
5751
|
+
const planPath = getPlanPath(workspace, issue2.id);
|
|
5673
5752
|
try {
|
|
5674
|
-
|
|
5753
|
+
unlinkSync2(planPath);
|
|
5675
5754
|
} catch {
|
|
5676
5755
|
}
|
|
5677
5756
|
initLogFile(logFile);
|
|
@@ -5710,7 +5789,7 @@ ${planResult.output}
|
|
|
5710
5789
|
if (!planResult.success) {
|
|
5711
5790
|
error(`Planning phase failed for ${issue2.id}. Check ${logFile}`);
|
|
5712
5791
|
try {
|
|
5713
|
-
|
|
5792
|
+
unlinkSync2(planPath);
|
|
5714
5793
|
} catch {
|
|
5715
5794
|
}
|
|
5716
5795
|
activeProviderPids.delete(issue2.id);
|
|
@@ -5725,7 +5804,7 @@ ${planResult.output}
|
|
|
5725
5804
|
if (!plan?.steps || plan.steps.length === 0) {
|
|
5726
5805
|
error(`Agent did not produce a valid execution plan for ${issue2.id}. Aborting.`);
|
|
5727
5806
|
try {
|
|
5728
|
-
|
|
5807
|
+
unlinkSync2(planPath);
|
|
5729
5808
|
} catch {
|
|
5730
5809
|
}
|
|
5731
5810
|
activeProviderPids.delete(issue2.id);
|
|
@@ -5741,7 +5820,7 @@ ${planResult.output}
|
|
|
5741
5820
|
`Plan produced ${sortedSteps.length} step(s): ${sortedSteps.map((s) => s.repoPath).join(" \u2192 ")}`
|
|
5742
5821
|
);
|
|
5743
5822
|
try {
|
|
5744
|
-
|
|
5823
|
+
unlinkSync2(planPath);
|
|
5745
5824
|
} catch {
|
|
5746
5825
|
}
|
|
5747
5826
|
const prUrls = [];
|
|
@@ -6470,13 +6549,13 @@ async function getChangedFiles(repoPath, baseBranch, dependencyBranch) {
|
|
|
6470
6549
|
}
|
|
6471
6550
|
|
|
6472
6551
|
// src/loop/branch-session.ts
|
|
6473
|
-
import { appendFileSync as appendFileSync12, unlinkSync as
|
|
6552
|
+
import { appendFileSync as appendFileSync12, unlinkSync as unlinkSync3 } from "fs";
|
|
6474
6553
|
import { join as join16, resolve as resolve10 } from "path";
|
|
6475
6554
|
async function runBranchSession(config2, issue2, logFile, session, models) {
|
|
6476
6555
|
const workspace = resolve10(config2.workspace);
|
|
6477
6556
|
const manifestPath = join16(workspace, ".lisa-manifest.json");
|
|
6478
6557
|
try {
|
|
6479
|
-
|
|
6558
|
+
unlinkSync3(manifestPath);
|
|
6480
6559
|
} catch {
|
|
6481
6560
|
}
|
|
6482
6561
|
const testRunner = detectTestRunner(workspace);
|
|
@@ -6579,7 +6658,7 @@ ${result.output}
|
|
|
6579
6658
|
}
|
|
6580
6659
|
const manifest = readManifestFile(manifestPath);
|
|
6581
6660
|
try {
|
|
6582
|
-
|
|
6661
|
+
unlinkSync3(manifestPath);
|
|
6583
6662
|
} catch {
|
|
6584
6663
|
}
|
|
6585
6664
|
let prUrl = manifest?.prUrl;
|
|
@@ -7036,7 +7115,7 @@ var run = defineCommand5({
|
|
|
7036
7115
|
if (isTTY) {
|
|
7037
7116
|
const { render } = await import("ink");
|
|
7038
7117
|
const { createElement } = await import("react");
|
|
7039
|
-
const { KanbanApp } = await import("./kanban-
|
|
7118
|
+
const { KanbanApp } = await import("./kanban-QZ5NRPJ5.js");
|
|
7040
7119
|
const demoConfig = {
|
|
7041
7120
|
provider: "claude",
|
|
7042
7121
|
source: "linear",
|
|
@@ -7073,6 +7152,12 @@ var run = defineCommand5({
|
|
|
7073
7152
|
label: args.label,
|
|
7074
7153
|
bell: args.bell
|
|
7075
7154
|
});
|
|
7155
|
+
if (merged.provider === "goose") {
|
|
7156
|
+
const gooseProvider = merged.provider_options?.goose?.goose_provider;
|
|
7157
|
+
if (gooseProvider && !process.env.GOOSE_PROVIDER) {
|
|
7158
|
+
process.env.GOOSE_PROVIDER = gooseProvider;
|
|
7159
|
+
}
|
|
7160
|
+
}
|
|
7076
7161
|
if (args.lifecycle || args["lifecycle-timeout"]) {
|
|
7077
7162
|
const lifecycleTimeout = args["lifecycle-timeout"] ? Number.parseInt(args["lifecycle-timeout"], 10) : void 0;
|
|
7078
7163
|
merged.lifecycle = {
|
|
@@ -7105,7 +7190,7 @@ Add them to your ${shell} and run: source ${shell}`));
|
|
|
7105
7190
|
if (isTTY) {
|
|
7106
7191
|
const { render } = await import("ink");
|
|
7107
7192
|
const { createElement } = await import("react");
|
|
7108
|
-
const { KanbanApp } = await import("./kanban-
|
|
7193
|
+
const { KanbanApp } = await import("./kanban-QZ5NRPJ5.js");
|
|
7109
7194
|
render(createElement(KanbanApp, { config: merged }), { exitOnCtrlC: false });
|
|
7110
7195
|
}
|
|
7111
7196
|
await runLoop(merged, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tarcisiopgs/lisa",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "Autonomous issue resolver",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"loop",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"lisa": "dist/index.js"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@clack/prompts": "^1.0
|
|
30
|
+
"@clack/prompts": "^1.1.0",
|
|
31
31
|
"citty": "^0.2.1",
|
|
32
32
|
"execa": "^9.6.1",
|
|
33
33
|
"ink": "^6.8.0",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@vitest/coverage-v8": "^4.0.18",
|
|
44
44
|
"concurrently": "^9.2.1",
|
|
45
45
|
"husky": "^9.1.7",
|
|
46
|
-
"lint-staged": "^16.3.
|
|
46
|
+
"lint-staged": "^16.3.2",
|
|
47
47
|
"tsup": "^8.5.1",
|
|
48
48
|
"tsx": "^4.21.0",
|
|
49
49
|
"typescript": "^5.9.3",
|