@tarcisiopgs/lisa 1.7.6 → 1.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 +6 -2
- package/dist/index.js +213 -99
- package/dist/{kanban-YP3TJJUT.js → kanban-5C3WZIKC.js} +28 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,9 +82,12 @@ Lisa follows a deterministic pipeline:
|
|
|
82
82
|
| Cursor Agent | `cursor` | `agent` / `cursor-agent` |
|
|
83
83
|
| Goose | `goose` | `goose` |
|
|
84
84
|
| Aider | `aider` | `aider` |
|
|
85
|
+
| OpenAI Codex | `codex` | `codex` |
|
|
85
86
|
|
|
86
87
|
At least one provider must be installed and available in your PATH.
|
|
87
88
|
|
|
89
|
+
> **Cursor Free plan** — `lisa init` automatically detects Free accounts and restricts model selection to `auto` only. On paid plans, a curated list of top-tier models is shown (`composer-1.5`, `opus-4.6`, `sonnet-4.6`, `gpt-5.3-codex`, etc.).
|
|
90
|
+
|
|
88
91
|
### Fallback Chain
|
|
89
92
|
|
|
90
93
|
Configure multiple models — Lisa tries each in order. Transient errors (429, quota, timeout, network) trigger the next model; non-transient errors stop the chain.
|
|
@@ -183,12 +186,13 @@ When running in an interactive terminal, `lisa run` renders a real-time Kanban b
|
|
|
183
186
|
|-----|--------|
|
|
184
187
|
| `Tab` | Move to next column |
|
|
185
188
|
| `Shift+Tab` | Move to previous column |
|
|
186
|
-
| `↑` / `↓` | Navigate cards
|
|
189
|
+
| `↑` / `↓` | Navigate cards / scroll output |
|
|
187
190
|
| `Enter` | Open issue detail view (streams provider output) |
|
|
188
191
|
| `Esc` | Close detail view, return to board |
|
|
192
|
+
| `p` | Pause / resume — loop finishes the current issue then waits |
|
|
189
193
|
| `q` | Quit |
|
|
190
194
|
|
|
191
|
-
The terminal tab title also updates in real time: it shows a spinner with the active issue ID while work is in progress, and a checkmark when done.
|
|
195
|
+
The sidebar legend updates contextually: board shortcuts when browsing the Kanban, scroll and back hints when viewing issue detail. The terminal tab title also updates in real time: it shows a spinner with the active issue ID while work is in progress, and a checkmark when done.
|
|
192
196
|
|
|
193
197
|
## Configuration
|
|
194
198
|
|
package/dist/index.js
CHANGED
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
} from "./chunk-YZKNBQN6.js";
|
|
10
10
|
|
|
11
11
|
// src/cli.ts
|
|
12
|
-
import { execSync as
|
|
12
|
+
import { execSync as execSync9 } from "child_process";
|
|
13
13
|
import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync6 } from "fs";
|
|
14
|
-
import { tmpdir as
|
|
15
|
-
import { join as
|
|
14
|
+
import { tmpdir as tmpdir9 } from "os";
|
|
15
|
+
import { join as join13, resolve as resolvePath } from "path";
|
|
16
16
|
import * as clack from "@clack/prompts";
|
|
17
17
|
import { defineCommand, runMain } from "citty";
|
|
18
18
|
import pc2 from "picocolors";
|
|
@@ -186,7 +186,8 @@ var PROVIDER_DISPLAY_NAMES = {
|
|
|
186
186
|
copilot: "GitHub Copilot CLI",
|
|
187
187
|
cursor: "Cursor Agent",
|
|
188
188
|
goose: "Goose",
|
|
189
|
-
aider: "Aider"
|
|
189
|
+
aider: "Aider",
|
|
190
|
+
codex: "OpenAI Codex"
|
|
190
191
|
};
|
|
191
192
|
function formatProviderName(providerUsed) {
|
|
192
193
|
const providerKey = providerUsed.split("/")[0] ?? providerUsed;
|
|
@@ -281,8 +282,8 @@ function determineRepoPath(repos, issue2, workspace) {
|
|
|
281
282
|
}
|
|
282
283
|
|
|
283
284
|
// src/loop.ts
|
|
284
|
-
import { appendFileSync as
|
|
285
|
-
import { join as
|
|
285
|
+
import { appendFileSync as appendFileSync11, existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync as unlinkSync9 } from "fs";
|
|
286
|
+
import { join as join12, resolve as resolve5 } from "path";
|
|
286
287
|
import { execa as execa3 } from "execa";
|
|
287
288
|
|
|
288
289
|
// src/output/logger.ts
|
|
@@ -1129,16 +1130,16 @@ var ClaudeProvider = class {
|
|
|
1129
1130
|
}
|
|
1130
1131
|
};
|
|
1131
1132
|
|
|
1132
|
-
// src/providers/
|
|
1133
|
+
// src/providers/codex.ts
|
|
1133
1134
|
import { execSync as execSync3, spawn as spawn3 } from "child_process";
|
|
1134
1135
|
import { appendFileSync as appendFileSync5, mkdtempSync as mkdtempSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
1135
1136
|
import { tmpdir as tmpdir3 } from "os";
|
|
1136
1137
|
import { join as join6 } from "path";
|
|
1137
|
-
var
|
|
1138
|
-
name = "
|
|
1138
|
+
var CodexProvider = class {
|
|
1139
|
+
name = "codex";
|
|
1139
1140
|
async isAvailable() {
|
|
1140
1141
|
try {
|
|
1141
|
-
execSync3("
|
|
1142
|
+
execSync3("codex --version", { stdio: "ignore" });
|
|
1142
1143
|
return true;
|
|
1143
1144
|
} catch {
|
|
1144
1145
|
return false;
|
|
@@ -1150,9 +1151,12 @@ var CopilotProvider = class {
|
|
|
1150
1151
|
const promptFile = join6(tmpDir, "prompt.md");
|
|
1151
1152
|
writeFileSync6(promptFile, prompt, "utf-8");
|
|
1152
1153
|
try {
|
|
1153
|
-
const
|
|
1154
|
+
const modelFlag = opts.model ? `--model ${opts.model}` : "";
|
|
1155
|
+
const cmd = `codex exec --dangerously-bypass-approvals-and-sandbox --ephemeral ${modelFlag} "$(cat '${promptFile}')"`;
|
|
1156
|
+
const proc = spawn3("sh", ["-c", cmd], {
|
|
1154
1157
|
cwd: opts.cwd,
|
|
1155
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
1158
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1159
|
+
env: { ...process.env, CODEX_QUIET_MODE: "1" }
|
|
1156
1160
|
});
|
|
1157
1161
|
if (proc.pid) opts.onProcess?.(proc.pid);
|
|
1158
1162
|
const overseer = opts.overseer?.enabled ? startOverseer(proc, opts.cwd, opts.overseer) : null;
|
|
@@ -1206,15 +1210,92 @@ var CopilotProvider = class {
|
|
|
1206
1210
|
}
|
|
1207
1211
|
};
|
|
1208
1212
|
|
|
1209
|
-
// src/providers/
|
|
1213
|
+
// src/providers/copilot.ts
|
|
1210
1214
|
import { execSync as execSync4, spawn as spawn4 } from "child_process";
|
|
1211
1215
|
import { appendFileSync as appendFileSync6, mkdtempSync as mkdtempSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
1212
1216
|
import { tmpdir as tmpdir4 } from "os";
|
|
1213
1217
|
import { join as join7 } from "path";
|
|
1218
|
+
var CopilotProvider = class {
|
|
1219
|
+
name = "copilot";
|
|
1220
|
+
async isAvailable() {
|
|
1221
|
+
try {
|
|
1222
|
+
execSync4("copilot version", { stdio: "ignore" });
|
|
1223
|
+
return true;
|
|
1224
|
+
} catch {
|
|
1225
|
+
return false;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
async run(prompt, opts) {
|
|
1229
|
+
const start = Date.now();
|
|
1230
|
+
const tmpDir = mkdtempSync4(join7(tmpdir4(), "lisa-"));
|
|
1231
|
+
const promptFile = join7(tmpDir, "prompt.md");
|
|
1232
|
+
writeFileSync7(promptFile, prompt, "utf-8");
|
|
1233
|
+
try {
|
|
1234
|
+
const proc = spawn4("sh", ["-c", `copilot --allow-all -p "$(cat '${promptFile}')"`], {
|
|
1235
|
+
cwd: opts.cwd,
|
|
1236
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1237
|
+
});
|
|
1238
|
+
if (proc.pid) opts.onProcess?.(proc.pid);
|
|
1239
|
+
const overseer = opts.overseer?.enabled ? startOverseer(proc, opts.cwd, opts.overseer) : null;
|
|
1240
|
+
const chunks = [];
|
|
1241
|
+
proc.stdout.on("data", (chunk) => {
|
|
1242
|
+
const text2 = chunk.toString();
|
|
1243
|
+
if (getOutputMode() !== "tui") process.stdout.write(text2);
|
|
1244
|
+
if (opts.issueId) {
|
|
1245
|
+
kanbanEmitter.emit("issue:output", opts.issueId, text2);
|
|
1246
|
+
}
|
|
1247
|
+
chunks.push(text2);
|
|
1248
|
+
try {
|
|
1249
|
+
appendFileSync6(opts.logFile, text2);
|
|
1250
|
+
} catch {
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1253
|
+
proc.stderr.on("data", (chunk) => {
|
|
1254
|
+
const text2 = chunk.toString();
|
|
1255
|
+
if (getOutputMode() !== "tui") process.stderr.write(text2);
|
|
1256
|
+
try {
|
|
1257
|
+
appendFileSync6(opts.logFile, text2);
|
|
1258
|
+
} catch {
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
const exitCode = await new Promise((resolve6) => {
|
|
1262
|
+
proc.on("close", (code) => {
|
|
1263
|
+
overseer?.stop();
|
|
1264
|
+
resolve6(code ?? 1);
|
|
1265
|
+
});
|
|
1266
|
+
});
|
|
1267
|
+
if (overseer?.wasKilled()) {
|
|
1268
|
+
chunks.push(STUCK_MESSAGE);
|
|
1269
|
+
}
|
|
1270
|
+
return {
|
|
1271
|
+
success: exitCode === 0 && !overseer?.wasKilled(),
|
|
1272
|
+
output: chunks.join(""),
|
|
1273
|
+
duration: Date.now() - start
|
|
1274
|
+
};
|
|
1275
|
+
} catch (err) {
|
|
1276
|
+
return {
|
|
1277
|
+
success: false,
|
|
1278
|
+
output: err instanceof Error ? err.message : String(err),
|
|
1279
|
+
duration: Date.now() - start
|
|
1280
|
+
};
|
|
1281
|
+
} finally {
|
|
1282
|
+
try {
|
|
1283
|
+
unlinkSync4(promptFile);
|
|
1284
|
+
} catch {
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1290
|
+
// src/providers/cursor.ts
|
|
1291
|
+
import { execSync as execSync5, spawn as spawn5 } from "child_process";
|
|
1292
|
+
import { appendFileSync as appendFileSync7, mkdtempSync as mkdtempSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync8 } from "fs";
|
|
1293
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
1294
|
+
import { join as join8 } from "path";
|
|
1214
1295
|
function findCursorBinary() {
|
|
1215
1296
|
for (const bin of ["agent", "cursor-agent"]) {
|
|
1216
1297
|
try {
|
|
1217
|
-
|
|
1298
|
+
execSync5(`${bin} --version`, { stdio: "ignore" });
|
|
1218
1299
|
return bin;
|
|
1219
1300
|
} catch {
|
|
1220
1301
|
}
|
|
@@ -1236,12 +1317,12 @@ var CursorProvider = class {
|
|
|
1236
1317
|
duration: Date.now() - start
|
|
1237
1318
|
};
|
|
1238
1319
|
}
|
|
1239
|
-
const tmpDir =
|
|
1240
|
-
const promptFile =
|
|
1241
|
-
|
|
1320
|
+
const tmpDir = mkdtempSync5(join8(tmpdir5(), "lisa-"));
|
|
1321
|
+
const promptFile = join8(tmpDir, "prompt.md");
|
|
1322
|
+
writeFileSync8(promptFile, prompt, "utf-8");
|
|
1242
1323
|
try {
|
|
1243
1324
|
const modelFlag = opts.model ? `--model ${opts.model}` : "";
|
|
1244
|
-
const proc =
|
|
1325
|
+
const proc = spawn5(
|
|
1245
1326
|
"sh",
|
|
1246
1327
|
["-c", `${bin} -p "$(cat '${promptFile}')" --output-format text --force ${modelFlag}`],
|
|
1247
1328
|
{
|
|
@@ -1260,7 +1341,7 @@ var CursorProvider = class {
|
|
|
1260
1341
|
}
|
|
1261
1342
|
chunks.push(text2);
|
|
1262
1343
|
try {
|
|
1263
|
-
|
|
1344
|
+
appendFileSync7(opts.logFile, text2);
|
|
1264
1345
|
} catch {
|
|
1265
1346
|
}
|
|
1266
1347
|
});
|
|
@@ -1268,7 +1349,7 @@ var CursorProvider = class {
|
|
|
1268
1349
|
const text2 = chunk.toString();
|
|
1269
1350
|
if (getOutputMode() !== "tui") process.stderr.write(text2);
|
|
1270
1351
|
try {
|
|
1271
|
-
|
|
1352
|
+
appendFileSync7(opts.logFile, text2);
|
|
1272
1353
|
} catch {
|
|
1273
1354
|
}
|
|
1274
1355
|
});
|
|
@@ -1294,7 +1375,7 @@ var CursorProvider = class {
|
|
|
1294
1375
|
};
|
|
1295
1376
|
} finally {
|
|
1296
1377
|
try {
|
|
1297
|
-
|
|
1378
|
+
unlinkSync5(promptFile);
|
|
1298
1379
|
} catch {
|
|
1299
1380
|
}
|
|
1300
1381
|
}
|
|
@@ -1302,15 +1383,15 @@ var CursorProvider = class {
|
|
|
1302
1383
|
};
|
|
1303
1384
|
|
|
1304
1385
|
// src/providers/gemini.ts
|
|
1305
|
-
import { execSync as
|
|
1306
|
-
import { appendFileSync as
|
|
1307
|
-
import { tmpdir as
|
|
1308
|
-
import { join as
|
|
1386
|
+
import { execSync as execSync6, spawn as spawn6 } from "child_process";
|
|
1387
|
+
import { appendFileSync as appendFileSync8, mkdtempSync as mkdtempSync6, unlinkSync as unlinkSync6, writeFileSync as writeFileSync9 } from "fs";
|
|
1388
|
+
import { tmpdir as tmpdir6 } from "os";
|
|
1389
|
+
import { join as join9 } from "path";
|
|
1309
1390
|
var GeminiProvider = class {
|
|
1310
1391
|
name = "gemini";
|
|
1311
1392
|
async isAvailable() {
|
|
1312
1393
|
try {
|
|
1313
|
-
|
|
1394
|
+
execSync6("gemini --version", { stdio: "ignore" });
|
|
1314
1395
|
return true;
|
|
1315
1396
|
} catch {
|
|
1316
1397
|
return false;
|
|
@@ -1318,12 +1399,12 @@ var GeminiProvider = class {
|
|
|
1318
1399
|
}
|
|
1319
1400
|
async run(prompt, opts) {
|
|
1320
1401
|
const start = Date.now();
|
|
1321
|
-
const tmpDir =
|
|
1322
|
-
const promptFile =
|
|
1323
|
-
|
|
1402
|
+
const tmpDir = mkdtempSync6(join9(tmpdir6(), "lisa-"));
|
|
1403
|
+
const promptFile = join9(tmpDir, "prompt.md");
|
|
1404
|
+
writeFileSync9(promptFile, prompt, "utf-8");
|
|
1324
1405
|
try {
|
|
1325
1406
|
const modelFlag = opts.model ? `--model ${opts.model}` : "";
|
|
1326
|
-
const proc =
|
|
1407
|
+
const proc = spawn6("sh", ["-c", `gemini --yolo ${modelFlag} -p "$(cat '${promptFile}')"`], {
|
|
1327
1408
|
cwd: opts.cwd,
|
|
1328
1409
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1329
1410
|
});
|
|
@@ -1338,7 +1419,7 @@ var GeminiProvider = class {
|
|
|
1338
1419
|
}
|
|
1339
1420
|
chunks.push(text2);
|
|
1340
1421
|
try {
|
|
1341
|
-
|
|
1422
|
+
appendFileSync8(opts.logFile, text2);
|
|
1342
1423
|
} catch {
|
|
1343
1424
|
}
|
|
1344
1425
|
});
|
|
@@ -1346,7 +1427,7 @@ var GeminiProvider = class {
|
|
|
1346
1427
|
const text2 = chunk.toString();
|
|
1347
1428
|
if (getOutputMode() !== "tui") process.stderr.write(text2);
|
|
1348
1429
|
try {
|
|
1349
|
-
|
|
1430
|
+
appendFileSync8(opts.logFile, text2);
|
|
1350
1431
|
} catch {
|
|
1351
1432
|
}
|
|
1352
1433
|
});
|
|
@@ -1372,7 +1453,7 @@ var GeminiProvider = class {
|
|
|
1372
1453
|
};
|
|
1373
1454
|
} finally {
|
|
1374
1455
|
try {
|
|
1375
|
-
|
|
1456
|
+
unlinkSync6(promptFile);
|
|
1376
1457
|
} catch {
|
|
1377
1458
|
}
|
|
1378
1459
|
}
|
|
@@ -1380,15 +1461,15 @@ var GeminiProvider = class {
|
|
|
1380
1461
|
};
|
|
1381
1462
|
|
|
1382
1463
|
// src/providers/goose.ts
|
|
1383
|
-
import { execSync as
|
|
1384
|
-
import { appendFileSync as
|
|
1385
|
-
import { tmpdir as
|
|
1386
|
-
import { join as
|
|
1464
|
+
import { execSync as execSync7, spawn as spawn7 } from "child_process";
|
|
1465
|
+
import { appendFileSync as appendFileSync9, mkdtempSync as mkdtempSync7, unlinkSync as unlinkSync7, writeFileSync as writeFileSync10 } from "fs";
|
|
1466
|
+
import { tmpdir as tmpdir7 } from "os";
|
|
1467
|
+
import { join as join10 } from "path";
|
|
1387
1468
|
var GooseProvider = class {
|
|
1388
1469
|
name = "goose";
|
|
1389
1470
|
async isAvailable() {
|
|
1390
1471
|
try {
|
|
1391
|
-
|
|
1472
|
+
execSync7("goose --version", { stdio: "ignore" });
|
|
1392
1473
|
return true;
|
|
1393
1474
|
} catch {
|
|
1394
1475
|
return false;
|
|
@@ -1396,12 +1477,12 @@ var GooseProvider = class {
|
|
|
1396
1477
|
}
|
|
1397
1478
|
async run(prompt, opts) {
|
|
1398
1479
|
const start = Date.now();
|
|
1399
|
-
const tmpDir =
|
|
1400
|
-
const promptFile =
|
|
1401
|
-
|
|
1480
|
+
const tmpDir = mkdtempSync7(join10(tmpdir7(), "lisa-"));
|
|
1481
|
+
const promptFile = join10(tmpDir, "prompt.md");
|
|
1482
|
+
writeFileSync10(promptFile, prompt, "utf-8");
|
|
1402
1483
|
try {
|
|
1403
1484
|
const modelFlag = opts.model ? `--model ${opts.model}` : "";
|
|
1404
|
-
const proc =
|
|
1485
|
+
const proc = spawn7("sh", ["-c", `goose run ${modelFlag} --text "$(cat '${promptFile}')"`], {
|
|
1405
1486
|
cwd: opts.cwd,
|
|
1406
1487
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1407
1488
|
});
|
|
@@ -1416,7 +1497,7 @@ var GooseProvider = class {
|
|
|
1416
1497
|
}
|
|
1417
1498
|
chunks.push(text2);
|
|
1418
1499
|
try {
|
|
1419
|
-
|
|
1500
|
+
appendFileSync9(opts.logFile, text2);
|
|
1420
1501
|
} catch {
|
|
1421
1502
|
}
|
|
1422
1503
|
});
|
|
@@ -1424,7 +1505,7 @@ var GooseProvider = class {
|
|
|
1424
1505
|
const text2 = chunk.toString();
|
|
1425
1506
|
if (getOutputMode() !== "tui") process.stderr.write(text2);
|
|
1426
1507
|
try {
|
|
1427
|
-
|
|
1508
|
+
appendFileSync9(opts.logFile, text2);
|
|
1428
1509
|
} catch {
|
|
1429
1510
|
}
|
|
1430
1511
|
});
|
|
@@ -1450,7 +1531,7 @@ var GooseProvider = class {
|
|
|
1450
1531
|
};
|
|
1451
1532
|
} finally {
|
|
1452
1533
|
try {
|
|
1453
|
-
|
|
1534
|
+
unlinkSync7(promptFile);
|
|
1454
1535
|
} catch {
|
|
1455
1536
|
}
|
|
1456
1537
|
}
|
|
@@ -1458,15 +1539,15 @@ var GooseProvider = class {
|
|
|
1458
1539
|
};
|
|
1459
1540
|
|
|
1460
1541
|
// src/providers/opencode.ts
|
|
1461
|
-
import { execSync as
|
|
1462
|
-
import { appendFileSync as
|
|
1463
|
-
import { tmpdir as
|
|
1464
|
-
import { join as
|
|
1542
|
+
import { execSync as execSync8, spawn as spawn8 } from "child_process";
|
|
1543
|
+
import { appendFileSync as appendFileSync10, mkdtempSync as mkdtempSync8, unlinkSync as unlinkSync8, writeFileSync as writeFileSync11 } from "fs";
|
|
1544
|
+
import { tmpdir as tmpdir8 } from "os";
|
|
1545
|
+
import { join as join11 } from "path";
|
|
1465
1546
|
var OpenCodeProvider = class {
|
|
1466
1547
|
name = "opencode";
|
|
1467
1548
|
async isAvailable() {
|
|
1468
1549
|
try {
|
|
1469
|
-
|
|
1550
|
+
execSync8("opencode --version", { stdio: "ignore" });
|
|
1470
1551
|
return true;
|
|
1471
1552
|
} catch {
|
|
1472
1553
|
return false;
|
|
@@ -1474,11 +1555,11 @@ var OpenCodeProvider = class {
|
|
|
1474
1555
|
}
|
|
1475
1556
|
async run(prompt, opts) {
|
|
1476
1557
|
const start = Date.now();
|
|
1477
|
-
const tmpDir =
|
|
1478
|
-
const promptFile =
|
|
1479
|
-
|
|
1558
|
+
const tmpDir = mkdtempSync8(join11(tmpdir8(), "lisa-"));
|
|
1559
|
+
const promptFile = join11(tmpDir, "prompt.md");
|
|
1560
|
+
writeFileSync11(promptFile, prompt, "utf-8");
|
|
1480
1561
|
try {
|
|
1481
|
-
const proc =
|
|
1562
|
+
const proc = spawn8("sh", ["-c", `opencode run "$(cat '${promptFile}')"`], {
|
|
1482
1563
|
cwd: opts.cwd,
|
|
1483
1564
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1484
1565
|
});
|
|
@@ -1493,7 +1574,7 @@ var OpenCodeProvider = class {
|
|
|
1493
1574
|
}
|
|
1494
1575
|
chunks.push(text2);
|
|
1495
1576
|
try {
|
|
1496
|
-
|
|
1577
|
+
appendFileSync10(opts.logFile, text2);
|
|
1497
1578
|
} catch {
|
|
1498
1579
|
}
|
|
1499
1580
|
});
|
|
@@ -1501,7 +1582,7 @@ var OpenCodeProvider = class {
|
|
|
1501
1582
|
const text2 = chunk.toString();
|
|
1502
1583
|
if (getOutputMode() !== "tui") process.stderr.write(text2);
|
|
1503
1584
|
try {
|
|
1504
|
-
|
|
1585
|
+
appendFileSync10(opts.logFile, text2);
|
|
1505
1586
|
} catch {
|
|
1506
1587
|
}
|
|
1507
1588
|
});
|
|
@@ -1527,7 +1608,7 @@ var OpenCodeProvider = class {
|
|
|
1527
1608
|
};
|
|
1528
1609
|
} finally {
|
|
1529
1610
|
try {
|
|
1530
|
-
|
|
1611
|
+
unlinkSync8(promptFile);
|
|
1531
1612
|
} catch {
|
|
1532
1613
|
}
|
|
1533
1614
|
}
|
|
@@ -1542,7 +1623,8 @@ var providers = {
|
|
|
1542
1623
|
copilot: () => new CopilotProvider(),
|
|
1543
1624
|
cursor: () => new CursorProvider(),
|
|
1544
1625
|
goose: () => new GooseProvider(),
|
|
1545
|
-
aider: () => new AiderProvider()
|
|
1626
|
+
aider: () => new AiderProvider(),
|
|
1627
|
+
codex: () => new CodexProvider()
|
|
1546
1628
|
};
|
|
1547
1629
|
async function getAllProvidersWithAvailability() {
|
|
1548
1630
|
const all = Object.values(providers).map((f) => f());
|
|
@@ -1671,7 +1753,7 @@ function formatAttemptsReport(attempts) {
|
|
|
1671
1753
|
}
|
|
1672
1754
|
|
|
1673
1755
|
// src/session/lifecycle.ts
|
|
1674
|
-
import { spawn as
|
|
1756
|
+
import { spawn as spawn9 } from "child_process";
|
|
1675
1757
|
import { createConnection } from "net";
|
|
1676
1758
|
import { resolve as resolve4 } from "path";
|
|
1677
1759
|
var managedResources = [];
|
|
@@ -1709,7 +1791,7 @@ function waitForPort(port, timeoutMs) {
|
|
|
1709
1791
|
}
|
|
1710
1792
|
function spawnResource(config2, baseCwd) {
|
|
1711
1793
|
const cwd = config2.cwd ? resolve4(baseCwd, config2.cwd) : baseCwd;
|
|
1712
|
-
const child =
|
|
1794
|
+
const child = spawn9("sh", ["-c", config2.up], {
|
|
1713
1795
|
cwd,
|
|
1714
1796
|
stdio: "ignore",
|
|
1715
1797
|
detached: true
|
|
@@ -1719,7 +1801,7 @@ function spawnResource(config2, baseCwd) {
|
|
|
1719
1801
|
}
|
|
1720
1802
|
function runSetupCommand(command, cwd) {
|
|
1721
1803
|
return new Promise((resolve6, reject) => {
|
|
1722
|
-
const child =
|
|
1804
|
+
const child = spawn9("sh", ["-c", command], {
|
|
1723
1805
|
cwd,
|
|
1724
1806
|
stdio: "inherit"
|
|
1725
1807
|
});
|
|
@@ -1790,7 +1872,7 @@ async function stopResources() {
|
|
|
1790
1872
|
}
|
|
1791
1873
|
} else {
|
|
1792
1874
|
await new Promise((resolve6) => {
|
|
1793
|
-
const down =
|
|
1875
|
+
const down = spawn9("sh", ["-c", config2.down], {
|
|
1794
1876
|
stdio: "ignore"
|
|
1795
1877
|
});
|
|
1796
1878
|
down.on("close", () => resolve6());
|
|
@@ -3154,11 +3236,27 @@ function createSource(name) {
|
|
|
3154
3236
|
var activeCleanup = null;
|
|
3155
3237
|
var activeProviderPid = null;
|
|
3156
3238
|
var shuttingDown = false;
|
|
3239
|
+
var loopPaused = false;
|
|
3240
|
+
kanbanEmitter.on("loop:pause", () => {
|
|
3241
|
+
loopPaused = true;
|
|
3242
|
+
});
|
|
3243
|
+
kanbanEmitter.on("loop:resume", () => {
|
|
3244
|
+
loopPaused = false;
|
|
3245
|
+
});
|
|
3157
3246
|
function resolveModels(config2) {
|
|
3158
3247
|
if (!config2.models || config2.models.length === 0) {
|
|
3159
3248
|
return [{ provider: config2.provider }];
|
|
3160
3249
|
}
|
|
3161
|
-
const knownProviders = /* @__PURE__ */ new Set([
|
|
3250
|
+
const knownProviders = /* @__PURE__ */ new Set([
|
|
3251
|
+
"claude",
|
|
3252
|
+
"gemini",
|
|
3253
|
+
"opencode",
|
|
3254
|
+
"copilot",
|
|
3255
|
+
"cursor",
|
|
3256
|
+
"goose",
|
|
3257
|
+
"aider",
|
|
3258
|
+
"codex"
|
|
3259
|
+
]);
|
|
3162
3260
|
for (const m of config2.models) {
|
|
3163
3261
|
if (knownProviders.has(m) && m !== config2.provider) {
|
|
3164
3262
|
warn(
|
|
@@ -3182,7 +3280,7 @@ function resolveModels(config2) {
|
|
|
3182
3280
|
}
|
|
3183
3281
|
var PLAN_FILE = ".lisa-plan.json";
|
|
3184
3282
|
function readLisaPlan(dir) {
|
|
3185
|
-
const planPath =
|
|
3283
|
+
const planPath = join12(dir, PLAN_FILE);
|
|
3186
3284
|
if (!existsSync6(planPath)) return null;
|
|
3187
3285
|
try {
|
|
3188
3286
|
return JSON.parse(readFileSync5(planPath, "utf-8").trim());
|
|
@@ -3192,13 +3290,13 @@ function readLisaPlan(dir) {
|
|
|
3192
3290
|
}
|
|
3193
3291
|
function cleanupPlan(dir) {
|
|
3194
3292
|
try {
|
|
3195
|
-
|
|
3293
|
+
unlinkSync9(join12(dir, PLAN_FILE));
|
|
3196
3294
|
} catch {
|
|
3197
3295
|
}
|
|
3198
3296
|
}
|
|
3199
3297
|
var MANIFEST_FILE = ".lisa-manifest.json";
|
|
3200
3298
|
function readLisaManifest(dir) {
|
|
3201
|
-
const manifestPath =
|
|
3299
|
+
const manifestPath = join12(dir, MANIFEST_FILE);
|
|
3202
3300
|
if (!existsSync6(manifestPath)) return null;
|
|
3203
3301
|
try {
|
|
3204
3302
|
return JSON.parse(readFileSync5(manifestPath, "utf-8").trim());
|
|
@@ -3208,7 +3306,7 @@ function readLisaManifest(dir) {
|
|
|
3208
3306
|
}
|
|
3209
3307
|
function cleanupManifest(dir) {
|
|
3210
3308
|
try {
|
|
3211
|
-
|
|
3309
|
+
unlinkSync9(join12(dir, MANIFEST_FILE));
|
|
3212
3310
|
} catch {
|
|
3213
3311
|
}
|
|
3214
3312
|
}
|
|
@@ -3320,6 +3418,7 @@ async function runLoop(config2, opts) {
|
|
|
3320
3418
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
|
|
3321
3419
|
const logFile = resolve5(config2.logs.dir, `session_${session}_${timestamp2}.log`);
|
|
3322
3420
|
divider(session);
|
|
3421
|
+
await waitIfPaused();
|
|
3323
3422
|
startSpinner("fetching issue...");
|
|
3324
3423
|
if (opts.issueId) {
|
|
3325
3424
|
log(`Fetching issue '${opts.issueId}' from ${config2.source}...`);
|
|
@@ -3600,7 +3699,7 @@ async function runNativeWorktreeSession(config2, issue2, logFile, session, model
|
|
|
3600
3699
|
});
|
|
3601
3700
|
stopSpinner();
|
|
3602
3701
|
try {
|
|
3603
|
-
|
|
3702
|
+
appendFileSync11(
|
|
3604
3703
|
logFile,
|
|
3605
3704
|
`
|
|
3606
3705
|
${"=".repeat(80)}
|
|
@@ -3707,7 +3806,7 @@ async function runManualWorktreeSession(config2, issue2, logFile, session, model
|
|
|
3707
3806
|
});
|
|
3708
3807
|
stopSpinner();
|
|
3709
3808
|
try {
|
|
3710
|
-
|
|
3809
|
+
appendFileSync11(
|
|
3711
3810
|
logFile,
|
|
3712
3811
|
`
|
|
3713
3812
|
${"=".repeat(80)}
|
|
@@ -3766,7 +3865,7 @@ async function runWorktreeMultiRepoSession(config2, issue2, logFile, session, mo
|
|
|
3766
3865
|
});
|
|
3767
3866
|
stopSpinner();
|
|
3768
3867
|
try {
|
|
3769
|
-
|
|
3868
|
+
appendFileSync11(
|
|
3770
3869
|
logFile,
|
|
3771
3870
|
`
|
|
3772
3871
|
${"=".repeat(80)}
|
|
@@ -3902,7 +4001,7 @@ async function runMultiRepoStep(config2, issue2, step, previousResults, logFile,
|
|
|
3902
4001
|
stopSpinner();
|
|
3903
4002
|
if (repoConfig?.lifecycle) await stopResources();
|
|
3904
4003
|
try {
|
|
3905
|
-
|
|
4004
|
+
appendFileSync11(
|
|
3906
4005
|
logFile,
|
|
3907
4006
|
`
|
|
3908
4007
|
${"=".repeat(80)}
|
|
@@ -3981,7 +4080,7 @@ async function runBranchSession(config2, issue2, logFile, session, models) {
|
|
|
3981
4080
|
});
|
|
3982
4081
|
stopSpinner();
|
|
3983
4082
|
try {
|
|
3984
|
-
|
|
4083
|
+
appendFileSync11(
|
|
3985
4084
|
logFile,
|
|
3986
4085
|
`
|
|
3987
4086
|
${"=".repeat(80)}
|
|
@@ -4026,6 +4125,11 @@ async function cleanupWorktree(repoRoot, worktreePath) {
|
|
|
4026
4125
|
function sleep(ms) {
|
|
4027
4126
|
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
4028
4127
|
}
|
|
4128
|
+
async function waitIfPaused() {
|
|
4129
|
+
while (loopPaused) {
|
|
4130
|
+
await sleep(500);
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4029
4133
|
|
|
4030
4134
|
// src/cli.ts
|
|
4031
4135
|
function sleep2(ms) {
|
|
@@ -4082,7 +4186,7 @@ Add them to your ${shell} and run: source ${shell}`));
|
|
|
4082
4186
|
if (isTUI) {
|
|
4083
4187
|
const { render } = await import("ink");
|
|
4084
4188
|
const { createElement } = await import("react");
|
|
4085
|
-
const { KanbanApp } = await import("./kanban-
|
|
4189
|
+
const { KanbanApp } = await import("./kanban-5C3WZIKC.js");
|
|
4086
4190
|
render(createElement(KanbanApp, { config: merged }), { exitOnCtrlC: false });
|
|
4087
4191
|
}
|
|
4088
4192
|
await runLoop(merged, {
|
|
@@ -4182,21 +4286,21 @@ function getVersion() {
|
|
|
4182
4286
|
}
|
|
4183
4287
|
var CURSOR_FREE_PLAN_ERROR = "Free plans can only use Auto";
|
|
4184
4288
|
async function isCursorFreePlan() {
|
|
4185
|
-
const { mkdtempSync:
|
|
4186
|
-
const tmpDir =
|
|
4187
|
-
const promptFile =
|
|
4188
|
-
|
|
4289
|
+
const { mkdtempSync: mkdtempSync9, unlinkSync: unlinkSync10, writeFileSync: writeFileSync12 } = await import("fs");
|
|
4290
|
+
const tmpDir = mkdtempSync9(join13(tmpdir9(), "lisa-cursor-check-"));
|
|
4291
|
+
const promptFile = join13(tmpDir, "prompt.txt");
|
|
4292
|
+
writeFileSync12(promptFile, "test", "utf-8");
|
|
4189
4293
|
try {
|
|
4190
4294
|
const bin = ["agent", "cursor-agent"].find((b) => {
|
|
4191
4295
|
try {
|
|
4192
|
-
|
|
4296
|
+
execSync9(`${b} --version`, { stdio: "ignore" });
|
|
4193
4297
|
return true;
|
|
4194
4298
|
} catch {
|
|
4195
4299
|
return false;
|
|
4196
4300
|
}
|
|
4197
4301
|
});
|
|
4198
4302
|
if (!bin) return false;
|
|
4199
|
-
const output =
|
|
4303
|
+
const output = execSync9(`${bin} -p "$(cat '${promptFile}')" --output-format text`, {
|
|
4200
4304
|
cwd: process.cwd(),
|
|
4201
4305
|
encoding: "utf-8",
|
|
4202
4306
|
timeout: 3e4
|
|
@@ -4207,11 +4311,11 @@ async function isCursorFreePlan() {
|
|
|
4207
4311
|
return errorOutput.includes(CURSOR_FREE_PLAN_ERROR);
|
|
4208
4312
|
} finally {
|
|
4209
4313
|
try {
|
|
4210
|
-
|
|
4314
|
+
unlinkSync10(promptFile);
|
|
4211
4315
|
} catch {
|
|
4212
4316
|
}
|
|
4213
4317
|
try {
|
|
4214
|
-
|
|
4318
|
+
execSync9(`rm -rf ${tmpDir}`, { stdio: "ignore" });
|
|
4215
4319
|
} catch {
|
|
4216
4320
|
}
|
|
4217
4321
|
}
|
|
@@ -4278,38 +4382,45 @@ var issue = defineCommand({
|
|
|
4278
4382
|
meta: { name: "issue", description: "Issue tracker operations for use inside worktrees" },
|
|
4279
4383
|
subCommands: { get: issueGet, done: issueDone }
|
|
4280
4384
|
});
|
|
4281
|
-
var
|
|
4385
|
+
var CURSOR_PREFERRED_MODELS = [
|
|
4282
4386
|
"auto",
|
|
4283
4387
|
"composer-1.5",
|
|
4388
|
+
"composer-1",
|
|
4389
|
+
"gpt-5.3-codex",
|
|
4390
|
+
"gpt-5.2",
|
|
4391
|
+
"gpt-5.1-codex-max",
|
|
4284
4392
|
"opus-4.6-thinking",
|
|
4285
4393
|
"opus-4.6",
|
|
4394
|
+
"sonnet-4.6-thinking",
|
|
4286
4395
|
"sonnet-4.6",
|
|
4287
|
-
"
|
|
4288
|
-
"
|
|
4289
|
-
"
|
|
4396
|
+
"gemini-3.1-pro",
|
|
4397
|
+
"gemini-3-pro",
|
|
4398
|
+
"grok",
|
|
4399
|
+
"kimi-k2.5"
|
|
4290
4400
|
];
|
|
4291
4401
|
function fetchCursorModels() {
|
|
4292
4402
|
try {
|
|
4293
4403
|
const bin = ["agent", "cursor-agent"].find((b) => {
|
|
4294
4404
|
try {
|
|
4295
|
-
|
|
4405
|
+
execSync9(`${b} --version`, { stdio: "ignore" });
|
|
4296
4406
|
return true;
|
|
4297
4407
|
} catch {
|
|
4298
4408
|
return false;
|
|
4299
4409
|
}
|
|
4300
4410
|
});
|
|
4301
|
-
if (!bin) return
|
|
4302
|
-
const raw =
|
|
4411
|
+
if (!bin) return CURSOR_PREFERRED_MODELS;
|
|
4412
|
+
const raw = execSync9(`${bin} --list-models`, { encoding: "utf-8", timeout: 1e4 });
|
|
4303
4413
|
const clean = raw.replace(/\x1b\[[0-9;]*[mGKHFA-Z]/g, "");
|
|
4304
|
-
const
|
|
4305
|
-
|
|
4414
|
+
const all = clean.split("\n").map((l) => l.trim()).filter((l) => l.includes(" - ")).map((l) => (l.split(" - ")[0] ?? "").trim()).filter(Boolean);
|
|
4415
|
+
const filtered = CURSOR_PREFERRED_MODELS.filter((m) => all.includes(m));
|
|
4416
|
+
return filtered.length > 0 ? filtered : CURSOR_PREFERRED_MODELS;
|
|
4306
4417
|
} catch {
|
|
4307
|
-
return
|
|
4418
|
+
return CURSOR_PREFERRED_MODELS;
|
|
4308
4419
|
}
|
|
4309
4420
|
}
|
|
4310
4421
|
function fetchOpenCodeModels() {
|
|
4311
4422
|
try {
|
|
4312
|
-
const raw =
|
|
4423
|
+
const raw = execSync9("opencode models", { encoding: "utf-8", timeout: 1e4 });
|
|
4313
4424
|
const hasAnthropic = Boolean(process.env.ANTHROPIC_API_KEY);
|
|
4314
4425
|
const hasGoogle = Boolean(
|
|
4315
4426
|
process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || process.env.GOOGLE_GENERATIVE_AI_API_KEY
|
|
@@ -4353,7 +4464,8 @@ async function runConfigWizard(existing) {
|
|
|
4353
4464
|
copilot: "GitHub Copilot CLI",
|
|
4354
4465
|
cursor: "Cursor Agent",
|
|
4355
4466
|
goose: "Goose",
|
|
4356
|
-
aider: "Aider"
|
|
4467
|
+
aider: "Aider",
|
|
4468
|
+
codex: "OpenAI Codex"
|
|
4357
4469
|
};
|
|
4358
4470
|
const providerModels = {
|
|
4359
4471
|
claude: ["claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5", "claude-sonnet-4-5"],
|
|
@@ -4361,7 +4473,8 @@ async function runConfigWizard(existing) {
|
|
|
4361
4473
|
// opencode: populated dynamically below (fetchOpenCodeModels)
|
|
4362
4474
|
copilot: ["claude-opus-4.6", "claude-sonnet-4.6", "claude-haiku-4.5", "gpt-5.2"],
|
|
4363
4475
|
goose: ["claude-sonnet-4-5", "claude-opus-4-5", "claude-haiku-4-5"],
|
|
4364
|
-
aider: ["claude-opus-4-6", "claude-sonnet-4-5", "claude-haiku-4-5"]
|
|
4476
|
+
aider: ["claude-opus-4-6", "claude-sonnet-4-5", "claude-haiku-4-5"],
|
|
4477
|
+
codex: ["gpt-5.1-codex-mini", "gpt-5.1-codex-max", "gpt-5.2-codex", "gpt-5.2", "gpt-5.3-codex"]
|
|
4365
4478
|
// cursor: populated dynamically below (fetchCursorModels)
|
|
4366
4479
|
};
|
|
4367
4480
|
const allProviders = await getAllProvidersWithAvailability();
|
|
@@ -4375,6 +4488,7 @@ async function runConfigWizard(existing) {
|
|
|
4375
4488
|
${pc2.bold("Gemini CLI")} ${pc2.dim("npm i -g @google/gemini-cli")}
|
|
4376
4489
|
${pc2.bold("OpenCode")} ${pc2.dim("npm i -g opencode")}
|
|
4377
4490
|
${pc2.bold("GitHub Copilot CLI")} ${pc2.dim("npm i -g @github/copilot-cli")}
|
|
4491
|
+
${pc2.bold("OpenAI Codex")} ${pc2.dim("npm i -g @openai/codex")}
|
|
4378
4492
|
${pc2.bold("Goose")} ${pc2.dim("https://block.github.io/goose")}
|
|
4379
4493
|
${pc2.bold("Aider")} ${pc2.dim("pip install aider-chat")}`
|
|
4380
4494
|
);
|
|
@@ -4662,12 +4776,12 @@ async function detectGitHubMethod() {
|
|
|
4662
4776
|
}
|
|
4663
4777
|
async function detectGitRepos() {
|
|
4664
4778
|
const cwd = process.cwd();
|
|
4665
|
-
if (existsSync7(
|
|
4779
|
+
if (existsSync7(join13(cwd, ".git"))) {
|
|
4666
4780
|
clack.log.info("Found a git repository in the current directory.");
|
|
4667
4781
|
return [];
|
|
4668
4782
|
}
|
|
4669
4783
|
const entries = readdirSync(cwd, { withFileTypes: true });
|
|
4670
|
-
const gitDirs = entries.filter((e) => e.isDirectory() && existsSync7(
|
|
4784
|
+
const gitDirs = entries.filter((e) => e.isDirectory() && existsSync7(join13(cwd, e.name, ".git"))).map((e) => e.name);
|
|
4671
4785
|
if (gitDirs.length === 0) {
|
|
4672
4786
|
return [];
|
|
4673
4787
|
}
|
|
@@ -4677,7 +4791,7 @@ async function detectGitRepos() {
|
|
|
4677
4791
|
});
|
|
4678
4792
|
if (clack.isCancel(selected)) return process.exit(0);
|
|
4679
4793
|
return selected.map((dir) => ({
|
|
4680
|
-
name: getGitRepoName(
|
|
4794
|
+
name: getGitRepoName(join13(cwd, dir)) ?? dir,
|
|
4681
4795
|
path: `./${dir}`,
|
|
4682
4796
|
match: "",
|
|
4683
4797
|
base_branch: ""
|
|
@@ -4685,7 +4799,7 @@ async function detectGitRepos() {
|
|
|
4685
4799
|
}
|
|
4686
4800
|
function detectDefaultBranch(repoPath) {
|
|
4687
4801
|
try {
|
|
4688
|
-
const ref =
|
|
4802
|
+
const ref = execSync9("git symbolic-ref refs/remotes/origin/HEAD --short", {
|
|
4689
4803
|
cwd: repoPath,
|
|
4690
4804
|
encoding: "utf-8"
|
|
4691
4805
|
}).trim();
|
|
@@ -4696,7 +4810,7 @@ function detectDefaultBranch(repoPath) {
|
|
|
4696
4810
|
}
|
|
4697
4811
|
function getGitRepoName(repoPath) {
|
|
4698
4812
|
try {
|
|
4699
|
-
const url =
|
|
4813
|
+
const url = execSync9("git remote get-url origin", { cwd: repoPath, encoding: "utf-8" }).trim();
|
|
4700
4814
|
const match = url.match(/\/([^/]+?)(?:\.git)?$/) ?? url.match(/:([^/]+?)(?:\.git)?$/);
|
|
4701
4815
|
return match?.[1] ?? null;
|
|
4702
4816
|
} catch {
|
|
@@ -337,11 +337,7 @@ function IssueDetail({ card, onBack }) {
|
|
|
337
337
|
] }) : visibleLines.map((line, i) => (
|
|
338
338
|
// biome-ignore lint/suspicious/noArrayIndexKey: log lines have no stable key
|
|
339
339
|
/* @__PURE__ */ jsx4(Text4, { color: "white", dimColor: true, children: line }, i)
|
|
340
|
-
)) })
|
|
341
|
-
/* @__PURE__ */ jsxs4(Box4, { justifyContent: "space-between", borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
|
|
342
|
-
/* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: "[\u2191\u2193] scroll" }),
|
|
343
|
-
/* @__PURE__ */ jsx4(Text4, { color: "yellow", dimColor: true, children: "[Esc] back to board" })
|
|
344
|
-
] })
|
|
340
|
+
)) })
|
|
345
341
|
]
|
|
346
342
|
}
|
|
347
343
|
);
|
|
@@ -352,14 +348,15 @@ import { existsSync } from "fs";
|
|
|
352
348
|
import { basename, join } from "path";
|
|
353
349
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
354
350
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
355
|
-
function Sidebar({ provider, source, cwd }) {
|
|
356
|
-
const dir = basename(cwd);
|
|
351
|
+
function Sidebar({ provider, source, cwd, activeView, paused = false }) {
|
|
352
|
+
const dir = basename(cwd).toUpperCase();
|
|
357
353
|
const cwdLabel = existsSync(join(cwd, ".git")) ? "REPOSITORY" : "WORKSPACE";
|
|
358
354
|
return /* @__PURE__ */ jsxs5(
|
|
359
355
|
Box5,
|
|
360
356
|
{
|
|
361
357
|
flexDirection: "column",
|
|
362
358
|
width: 28,
|
|
359
|
+
flexShrink: 0,
|
|
363
360
|
borderStyle: "single",
|
|
364
361
|
borderColor: "yellow",
|
|
365
362
|
paddingX: 1,
|
|
@@ -377,8 +374,8 @@ function Sidebar({ provider, source, cwd }) {
|
|
|
377
374
|
/* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D" })
|
|
378
375
|
] }),
|
|
379
376
|
/* @__PURE__ */ jsxs5(Box5, { marginBottom: 1, children: [
|
|
380
|
-
/* @__PURE__ */ jsx5(Text5, { color: "green", children: "\u25B6 " }),
|
|
381
|
-
/* @__PURE__ */ jsx5(Text5, { color: "green", bold: true, children: "RUNNING" })
|
|
377
|
+
/* @__PURE__ */ jsx5(Text5, { color: paused ? "yellow" : "green", children: paused ? "\u23F8 " : "\u25B6 " }),
|
|
378
|
+
/* @__PURE__ */ jsx5(Text5, { color: paused ? "yellow" : "green", bold: true, children: paused ? "PAUSED" : "RUNNING" })
|
|
382
379
|
] }),
|
|
383
380
|
/* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
|
|
384
381
|
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
@@ -404,11 +401,15 @@ function Sidebar({ provider, source, cwd }) {
|
|
|
404
401
|
] }),
|
|
405
402
|
/* @__PURE__ */ jsx5(Box5, { flexGrow: 1 }),
|
|
406
403
|
/* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
|
|
407
|
-
/* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
|
|
404
|
+
activeView === "board" ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
|
|
408
405
|
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[Tab] next column" }),
|
|
409
406
|
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[\u2191\u2193] navigate " }),
|
|
410
407
|
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[\u21B5] view detail " }),
|
|
408
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: paused ? "[p] resume " : "[p] pause " }),
|
|
411
409
|
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[q] quit " })
|
|
410
|
+
] }) : /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
|
|
411
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[\u2191\u2193] scroll " }),
|
|
412
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[Esc] back to board" })
|
|
412
413
|
] })
|
|
413
414
|
]
|
|
414
415
|
}
|
|
@@ -423,6 +424,7 @@ function KanbanApp({ config }) {
|
|
|
423
424
|
const [activeView, setActiveView] = useState3("board");
|
|
424
425
|
const [activeColIndex, setActiveColIndex] = useState3(0);
|
|
425
426
|
const [activeCardIndex, setActiveCardIndex] = useState3(0);
|
|
427
|
+
const [paused, setPaused] = useState3(false);
|
|
426
428
|
useEffect3(() => {
|
|
427
429
|
const onExit = () => exit();
|
|
428
430
|
kanbanEmitter.on("tui:exit", onExit);
|
|
@@ -454,6 +456,12 @@ function KanbanApp({ config }) {
|
|
|
454
456
|
if (key.escape) setActiveView("board");
|
|
455
457
|
return;
|
|
456
458
|
}
|
|
459
|
+
if (input === "p") {
|
|
460
|
+
const next = !paused;
|
|
461
|
+
setPaused(next);
|
|
462
|
+
kanbanEmitter.emit(next ? "loop:pause" : "loop:resume");
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
457
465
|
if (key.tab && !key.shift) {
|
|
458
466
|
const nextCol = (activeColIndex + 1) % 3;
|
|
459
467
|
setActiveColIndex(nextCol);
|
|
@@ -489,7 +497,16 @@ function KanbanApp({ config }) {
|
|
|
489
497
|
};
|
|
490
498
|
const selectedCard = activeView === "detail" ? columnCards[activeColIndex]?.[activeCardIndex] ?? null : null;
|
|
491
499
|
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "row", height: process.stdout.rows, children: [
|
|
492
|
-
/* @__PURE__ */ jsx6(
|
|
500
|
+
/* @__PURE__ */ jsx6(
|
|
501
|
+
Sidebar,
|
|
502
|
+
{
|
|
503
|
+
provider: config.provider,
|
|
504
|
+
source: config.source,
|
|
505
|
+
cwd: process.cwd(),
|
|
506
|
+
activeView,
|
|
507
|
+
paused
|
|
508
|
+
}
|
|
509
|
+
),
|
|
493
510
|
activeView === "board" || !selectedCard ? /* @__PURE__ */ jsx6(
|
|
494
511
|
Board,
|
|
495
512
|
{
|