@tarcisiopgs/lisa 0.7.0 → 0.8.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 +38 -13
- package/dist/index.js +84 -49
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,21 +89,21 @@ Worktree mode is ideal when you want to keep working in the repo while lisa reso
|
|
|
89
89
|
|
|
90
90
|
Config lives in `.lisa/config.yaml`:
|
|
91
91
|
|
|
92
|
+
**Linear:**
|
|
92
93
|
```yaml
|
|
93
94
|
provider: claude
|
|
94
95
|
source: linear
|
|
95
96
|
workflow: branch
|
|
96
97
|
|
|
97
98
|
source_config:
|
|
98
|
-
team:
|
|
99
|
-
project:
|
|
99
|
+
team: Engineering
|
|
100
|
+
project: Web App
|
|
100
101
|
label: ready
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
pick_from: Todo
|
|
103
|
+
in_progress: In Progress
|
|
104
|
+
done: In Review
|
|
104
105
|
|
|
105
106
|
github: cli
|
|
106
|
-
|
|
107
107
|
workspace: .
|
|
108
108
|
repos:
|
|
109
109
|
- name: app
|
|
@@ -119,16 +119,41 @@ logs:
|
|
|
119
119
|
format: text
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
**Trello:**
|
|
123
|
+
```yaml
|
|
124
|
+
provider: claude
|
|
125
|
+
source: trello
|
|
126
|
+
workflow: branch
|
|
127
|
+
|
|
128
|
+
source_config:
|
|
129
|
+
board: Product
|
|
130
|
+
pick_from: Backlog
|
|
131
|
+
label: ready
|
|
132
|
+
in_progress: In Progress
|
|
133
|
+
done: Code Review
|
|
134
|
+
|
|
135
|
+
github: cli
|
|
136
|
+
workspace: .
|
|
137
|
+
|
|
138
|
+
loop:
|
|
139
|
+
cooldown: 10
|
|
140
|
+
max_sessions: 0
|
|
141
|
+
|
|
142
|
+
logs:
|
|
143
|
+
dir: .lisa/logs
|
|
144
|
+
format: text
|
|
145
|
+
```
|
|
146
|
+
|
|
122
147
|
### Source-specific fields
|
|
123
148
|
|
|
124
149
|
| Field | Linear | Trello |
|
|
125
150
|
|-------|--------|--------|
|
|
126
|
-
| `team` | Team name | Board name |
|
|
127
|
-
| `project` | Project name |
|
|
151
|
+
| `team` / `board` | Team name | Board name |
|
|
152
|
+
| `project` | Project name | — |
|
|
153
|
+
| `pick_from` | Status to pick issues from (e.g. Todo) | List to pick cards from (e.g. Backlog) |
|
|
128
154
|
| `label` | Label to filter issues | Label to filter cards |
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `done_status` | Destination status (e.g. In Review) | Destination column (e.g. Code Review) |
|
|
155
|
+
| `in_progress` | In-progress status (e.g. In Progress) | In-progress column |
|
|
156
|
+
| `done` | Destination status (e.g. In Review) | Destination column (e.g. Code Review) |
|
|
132
157
|
|
|
133
158
|
CLI flags override config values:
|
|
134
159
|
|
|
@@ -139,8 +164,8 @@ lisa run --provider gemini --label "urgent"
|
|
|
139
164
|
## How It Works
|
|
140
165
|
|
|
141
166
|
1. **Fetch** — Calls the Linear GraphQL API or Trello REST API to get the next issue matching the configured label, team, and project. Issues are sorted by priority.
|
|
142
|
-
2. **Activate** — Moves the issue to the configured `
|
|
167
|
+
2. **Activate** — Moves the issue to the configured `in_progress` status (e.g. "In Progress") so your team can see it's being worked on.
|
|
143
168
|
3. **Implement** — Builds a prompt with the issue context and sends it to the AI coding agent. In branch mode, the agent creates a branch and works in the current checkout. In worktree mode, lisa creates an isolated worktree first.
|
|
144
169
|
4. **PR** — Creates a pull request via the GitHub API (CLI or token) referencing the original issue. In multi-repo workspaces, the correct repo is detected automatically.
|
|
145
|
-
5. **Update** — Moves the issue to the configured `
|
|
170
|
+
5. **Update** — Moves the issue to the configured `done` status and removes the pickup label via the source API.
|
|
146
171
|
6. **Loop** — Waits `cooldown` seconds, then picks the next issue. Repeats until no issues remain or the limit is reached.
|
package/dist/index.js
CHANGED
|
@@ -21,9 +21,9 @@ var DEFAULT_CONFIG = {
|
|
|
21
21
|
team: "",
|
|
22
22
|
project: "",
|
|
23
23
|
label: "",
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
pick_from: "",
|
|
25
|
+
in_progress: "",
|
|
26
|
+
done: ""
|
|
27
27
|
},
|
|
28
28
|
github: "cli",
|
|
29
29
|
workflow: "branch",
|
|
@@ -52,12 +52,24 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
52
52
|
}
|
|
53
53
|
const raw = readFileSync(configPath, "utf-8");
|
|
54
54
|
const parsed = parse(raw);
|
|
55
|
+
const rawSource = parsed.source_config ?? {};
|
|
56
|
+
const sourceConfig = {
|
|
57
|
+
team: rawSource.team ?? rawSource.board ?? "",
|
|
58
|
+
project: rawSource.project ?? rawSource.list ?? rawSource.pick_from ?? "",
|
|
59
|
+
label: rawSource.label ?? "",
|
|
60
|
+
pick_from: rawSource.pick_from ?? rawSource.initial_status ?? "",
|
|
61
|
+
in_progress: rawSource.in_progress ?? rawSource.active_status ?? "",
|
|
62
|
+
done: rawSource.done ?? rawSource.done_status ?? ""
|
|
63
|
+
};
|
|
64
|
+
if (parsed.source === "trello" && !sourceConfig.pick_from) {
|
|
65
|
+
sourceConfig.pick_from = sourceConfig.project;
|
|
66
|
+
}
|
|
55
67
|
const config2 = {
|
|
56
68
|
...DEFAULT_CONFIG,
|
|
57
69
|
...parsed,
|
|
58
|
-
source_config:
|
|
59
|
-
loop: { ...DEFAULT_CONFIG.loop, ...parsed.loop },
|
|
60
|
-
logs: { ...DEFAULT_CONFIG.logs, ...parsed.logs }
|
|
70
|
+
source_config: sourceConfig,
|
|
71
|
+
loop: { ...DEFAULT_CONFIG.loop, ...parsed.loop ?? {} },
|
|
72
|
+
logs: { ...DEFAULT_CONFIG.logs, ...parsed.logs ?? {} }
|
|
61
73
|
};
|
|
62
74
|
if (!config2.base_branch) config2.base_branch = "main";
|
|
63
75
|
for (const repo of config2.repos) {
|
|
@@ -71,7 +83,10 @@ function saveConfig(config2, cwd = process.cwd()) {
|
|
|
71
83
|
if (!existsSync(dir)) {
|
|
72
84
|
mkdirSync(dir, { recursive: true });
|
|
73
85
|
}
|
|
74
|
-
|
|
86
|
+
const sc = config2.source_config;
|
|
87
|
+
const sourceYaml = config2.source === "trello" ? { board: sc.team, pick_from: sc.pick_from || sc.project, label: sc.label, in_progress: sc.in_progress, done: sc.done } : { team: sc.team, project: sc.project, label: sc.label, pick_from: sc.pick_from, in_progress: sc.in_progress, done: sc.done };
|
|
88
|
+
const output = { ...config2, source_config: sourceYaml };
|
|
89
|
+
writeFileSync(configPath, stringify(output), "utf-8");
|
|
75
90
|
}
|
|
76
91
|
function mergeWithFlags(config2, flags) {
|
|
77
92
|
const merged = { ...config2 };
|
|
@@ -585,7 +600,7 @@ var LinearSource = class {
|
|
|
585
600
|
teamName: config2.team,
|
|
586
601
|
projectName: config2.project,
|
|
587
602
|
labelName: config2.label,
|
|
588
|
-
statusName: config2.
|
|
603
|
+
statusName: config2.pick_from
|
|
589
604
|
}
|
|
590
605
|
);
|
|
591
606
|
const issues = data.issues.nodes;
|
|
@@ -931,6 +946,13 @@ async function findBranchByIssueId(repoRoot, issueId) {
|
|
|
931
946
|
], { cwd: repoRoot });
|
|
932
947
|
const remoteMatch = remote.split("\n").map((b) => b.trim()).filter(Boolean).find((b) => b.toLowerCase().includes(needle));
|
|
933
948
|
if (remoteMatch) return remoteMatch.replace("origin/", "");
|
|
949
|
+
const { stdout: lsRemote } = await execa2("git", [
|
|
950
|
+
"ls-remote",
|
|
951
|
+
"--heads",
|
|
952
|
+
"origin"
|
|
953
|
+
], { cwd: repoRoot });
|
|
954
|
+
const lsMatch = lsRemote.split("\n").map((l) => l.trim()).filter(Boolean).map((l) => l.split(" ")[1]?.replace("refs/heads/", "") ?? "").find((b) => b.toLowerCase().includes(needle));
|
|
955
|
+
if (lsMatch) return lsMatch;
|
|
934
956
|
return void 0;
|
|
935
957
|
}
|
|
936
958
|
function determineRepoPath(repos, issue, workspace) {
|
|
@@ -995,11 +1017,11 @@ async function runLoop(config2, opts) {
|
|
|
995
1017
|
}
|
|
996
1018
|
ok(`Picked up: ${issue.id} \u2014 ${issue.title}`);
|
|
997
1019
|
try {
|
|
998
|
-
const
|
|
999
|
-
await source.updateStatus(issue.id,
|
|
1000
|
-
ok(`Moved ${issue.id} to "${
|
|
1020
|
+
const inProgress = config2.source_config.in_progress;
|
|
1021
|
+
await source.updateStatus(issue.id, inProgress);
|
|
1022
|
+
ok(`Moved ${issue.id} to "${inProgress}"`);
|
|
1001
1023
|
} catch (err) {
|
|
1002
|
-
warn(`Failed to update
|
|
1024
|
+
warn(`Failed to update status: ${err instanceof Error ? err.message : String(err)}`);
|
|
1003
1025
|
}
|
|
1004
1026
|
const prUrl = config2.workflow === "worktree" ? await runWorktreeSession(config2, issue, logFile, session) : await runBranchSession(config2, issue, logFile, session);
|
|
1005
1027
|
if (prUrl) {
|
|
@@ -1011,7 +1033,7 @@ async function runLoop(config2, opts) {
|
|
|
1011
1033
|
}
|
|
1012
1034
|
}
|
|
1013
1035
|
try {
|
|
1014
|
-
const doneStatus = config2.source_config.
|
|
1036
|
+
const doneStatus = config2.source_config.done;
|
|
1015
1037
|
await source.updateStatus(issue.id, doneStatus);
|
|
1016
1038
|
ok(`Updated ${issue.id} status to "${doneStatus}"`);
|
|
1017
1039
|
} catch (err) {
|
|
@@ -1249,14 +1271,20 @@ var status = defineCommand({
|
|
|
1249
1271
|
async run() {
|
|
1250
1272
|
banner();
|
|
1251
1273
|
const config2 = loadConfig();
|
|
1274
|
+
const isLinear = config2.source === "linear";
|
|
1252
1275
|
console.log(pc2.cyan("Configuration:"));
|
|
1253
|
-
console.log(` Provider:
|
|
1254
|
-
console.log(` Source:
|
|
1255
|
-
console.log(` Workflow:
|
|
1256
|
-
console.log(` Label:
|
|
1257
|
-
console.log(` Team:
|
|
1258
|
-
|
|
1259
|
-
|
|
1276
|
+
console.log(` Provider: ${pc2.bold(config2.provider)}`);
|
|
1277
|
+
console.log(` Source: ${pc2.bold(config2.source)}`);
|
|
1278
|
+
console.log(` Workflow: ${pc2.bold(config2.workflow)}`);
|
|
1279
|
+
console.log(` Label: ${pc2.bold(config2.source_config.label)}`);
|
|
1280
|
+
console.log(` ${isLinear ? "Team" : "Board"}: ${pc2.bold(config2.source_config.team)}`);
|
|
1281
|
+
if (isLinear) {
|
|
1282
|
+
console.log(` Project: ${pc2.bold(config2.source_config.project)}`);
|
|
1283
|
+
}
|
|
1284
|
+
console.log(` Pick from: ${pc2.bold(config2.source_config.pick_from)}`);
|
|
1285
|
+
console.log(` In progress: ${pc2.bold(config2.source_config.in_progress)}`);
|
|
1286
|
+
console.log(` Done: ${pc2.bold(config2.source_config.done)}`);
|
|
1287
|
+
console.log(` Logs: ${pc2.dim(config2.logs.dir)}`);
|
|
1260
1288
|
const { readdirSync: readdirSync2, existsSync: existsSync5 } = await import("fs");
|
|
1261
1289
|
if (existsSync5(config2.logs.dir)) {
|
|
1262
1290
|
const logs = readdirSync2(config2.logs.dir).filter((f) => f.endsWith(".log"));
|
|
@@ -1344,57 +1372,64 @@ Then run: ${pc2.cyan(`source ${shell}`)}`
|
|
|
1344
1372
|
}
|
|
1345
1373
|
const githubMethod = await detectGitHubMethod();
|
|
1346
1374
|
const teamAnswer = await clack.text({
|
|
1347
|
-
message: source === "linear" ? "
|
|
1375
|
+
message: source === "linear" ? "Team?" : "Board?"
|
|
1348
1376
|
});
|
|
1349
1377
|
if (clack.isCancel(teamAnswer)) return process.exit(0);
|
|
1350
1378
|
const team = teamAnswer;
|
|
1351
|
-
const projectAnswer = await clack.text({
|
|
1352
|
-
message: source === "linear" ? "Project name?" : "Trello list name?"
|
|
1353
|
-
});
|
|
1354
|
-
if (clack.isCancel(projectAnswer)) return process.exit(0);
|
|
1355
|
-
const project = projectAnswer;
|
|
1356
1379
|
const labelAnswer = await clack.text({
|
|
1357
1380
|
message: "Label to pick up?",
|
|
1358
1381
|
initialValue: "ready"
|
|
1359
1382
|
});
|
|
1360
1383
|
if (clack.isCancel(labelAnswer)) return process.exit(0);
|
|
1361
1384
|
const label = labelAnswer;
|
|
1362
|
-
let
|
|
1363
|
-
let
|
|
1364
|
-
let
|
|
1385
|
+
let project;
|
|
1386
|
+
let pickFrom;
|
|
1387
|
+
let inProgress;
|
|
1388
|
+
let done;
|
|
1365
1389
|
if (source === "trello") {
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1390
|
+
const pickFromAnswer = await clack.text({
|
|
1391
|
+
message: "Pick up cards from which list?",
|
|
1392
|
+
initialValue: "Backlog"
|
|
1393
|
+
});
|
|
1394
|
+
if (clack.isCancel(pickFromAnswer)) return process.exit(0);
|
|
1395
|
+
pickFrom = pickFromAnswer;
|
|
1396
|
+
project = pickFrom;
|
|
1397
|
+
const inProgressAnswer = await clack.text({
|
|
1398
|
+
message: "Move to which column while working?",
|
|
1369
1399
|
initialValue: "In Progress"
|
|
1370
1400
|
});
|
|
1371
|
-
if (clack.isCancel(
|
|
1372
|
-
|
|
1401
|
+
if (clack.isCancel(inProgressAnswer)) return process.exit(0);
|
|
1402
|
+
inProgress = inProgressAnswer;
|
|
1373
1403
|
const doneAnswer = await clack.text({
|
|
1374
|
-
message: "
|
|
1404
|
+
message: "Move to which column after PR?",
|
|
1375
1405
|
initialValue: "Code Review"
|
|
1376
1406
|
});
|
|
1377
1407
|
if (clack.isCancel(doneAnswer)) return process.exit(0);
|
|
1378
|
-
|
|
1408
|
+
done = doneAnswer;
|
|
1379
1409
|
} else {
|
|
1380
|
-
const
|
|
1381
|
-
message: "
|
|
1410
|
+
const projectAnswer = await clack.text({
|
|
1411
|
+
message: "Project?"
|
|
1412
|
+
});
|
|
1413
|
+
if (clack.isCancel(projectAnswer)) return process.exit(0);
|
|
1414
|
+
project = projectAnswer;
|
|
1415
|
+
const pickFromAnswer = await clack.text({
|
|
1416
|
+
message: "Pick up issues from which status?",
|
|
1382
1417
|
initialValue: "Backlog"
|
|
1383
1418
|
});
|
|
1384
|
-
if (clack.isCancel(
|
|
1385
|
-
|
|
1386
|
-
const
|
|
1387
|
-
message: "
|
|
1419
|
+
if (clack.isCancel(pickFromAnswer)) return process.exit(0);
|
|
1420
|
+
pickFrom = pickFromAnswer;
|
|
1421
|
+
const inProgressAnswer = await clack.text({
|
|
1422
|
+
message: "Move to which status while working?",
|
|
1388
1423
|
initialValue: "In Progress"
|
|
1389
1424
|
});
|
|
1390
|
-
if (clack.isCancel(
|
|
1391
|
-
|
|
1425
|
+
if (clack.isCancel(inProgressAnswer)) return process.exit(0);
|
|
1426
|
+
inProgress = inProgressAnswer;
|
|
1392
1427
|
const doneAnswer = await clack.text({
|
|
1393
|
-
message: "
|
|
1428
|
+
message: "Move to which status after PR?",
|
|
1394
1429
|
initialValue: "In Review"
|
|
1395
1430
|
});
|
|
1396
1431
|
if (clack.isCancel(doneAnswer)) return process.exit(0);
|
|
1397
|
-
|
|
1432
|
+
done = doneAnswer;
|
|
1398
1433
|
}
|
|
1399
1434
|
const workflowAnswer = await clack.select({
|
|
1400
1435
|
message: "How should Lisa work on issues?",
|
|
@@ -1445,9 +1480,9 @@ Then run: ${pc2.cyan(`source ${shell}`)}`
|
|
|
1445
1480
|
team,
|
|
1446
1481
|
project,
|
|
1447
1482
|
label,
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1483
|
+
pick_from: pickFrom,
|
|
1484
|
+
in_progress: inProgress,
|
|
1485
|
+
done
|
|
1451
1486
|
},
|
|
1452
1487
|
github: githubMethod,
|
|
1453
1488
|
workflow,
|