sisyphi 1.0.2 → 1.0.4
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 -4
- package/dist/chunk-DBR33QHM.js +185 -0
- package/dist/chunk-DBR33QHM.js.map +1 -0
- package/dist/cli.js +159 -22
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +30 -2
- package/dist/daemon.js.map +1 -1
- package/dist/templates/CLAUDE.md +1 -0
- package/dist/templates/agent-plugin/agents/operator.md +1 -0
- package/dist/templates/agent-plugin/agents/plan.md +68 -4
- package/dist/templates/agent-plugin/agents/review-plan.md +1 -1
- package/dist/templates/agent-plugin/agents/review.md +1 -0
- package/dist/templates/agent-plugin/agents/spec-draft.md +32 -4
- package/dist/templates/agent-plugin/agents/test-spec.md +1 -0
- package/dist/templates/companion-plugin/.claude-plugin/plugin.json +1 -0
- package/dist/templates/companion-plugin/hooks/hooks.json +12 -0
- package/dist/templates/companion-plugin/hooks/user-prompt-context.sh +3 -0
- package/dist/templates/dashboard-claude.md +1 -1
- package/dist/templates/orchestrator-base.md +5 -9
- package/dist/templates/orchestrator-planning.md +5 -49
- package/dist/tui.js +341 -184
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/templates/CLAUDE.md +1 -0
- package/templates/agent-plugin/agents/operator.md +1 -0
- package/templates/agent-plugin/agents/plan.md +68 -4
- package/templates/agent-plugin/agents/review-plan.md +1 -1
- package/templates/agent-plugin/agents/review.md +1 -0
- package/templates/agent-plugin/agents/spec-draft.md +32 -4
- package/templates/agent-plugin/agents/test-spec.md +1 -0
- package/templates/companion-plugin/.claude-plugin/plugin.json +1 -0
- package/templates/companion-plugin/hooks/hooks.json +12 -0
- package/templates/companion-plugin/hooks/user-prompt-context.sh +3 -0
- package/templates/dashboard-claude.md +1 -1
- package/templates/orchestrator-base.md +5 -9
- package/templates/orchestrator-planning.md +5 -49
- package/dist/chunk-ZE2SKB4B.js +0 -35
- package/dist/chunk-ZE2SKB4B.js.map +0 -1
- package/dist/templates/agent-plugin/.claude/agents/debug.md +0 -39
- package/dist/templates/agent-plugin/.claude/agents/plan.md +0 -101
- package/dist/templates/agent-plugin/.claude/agents/review-plan.md +0 -81
- package/dist/templates/agent-plugin/.claude/agents/review.md +0 -56
- package/dist/templates/agent-plugin/.claude/agents/spec-draft.md +0 -73
- package/dist/templates/agent-plugin/.claude/agents/test-spec.md +0 -56
- package/dist/templates/orchestrator-plugin/.claude/commands/begin.md +0 -62
- package/dist/templates/orchestrator-plugin/.claude/skills/orchestration/SKILL.md +0 -40
- package/dist/templates/orchestrator-plugin/.claude/skills/orchestration/task-patterns.md +0 -222
- package/dist/templates/orchestrator-plugin/.claude/skills/orchestration/workflow-examples.md +0 -208
- package/dist/templates/resources/.claude/agents/debug.md +0 -39
- package/dist/templates/resources/.claude/agents/plan.md +0 -101
- package/dist/templates/resources/.claude/agents/review-plan.md +0 -81
- package/dist/templates/resources/.claude/agents/review.md +0 -56
- package/dist/templates/resources/.claude/agents/spec-draft.md +0 -73
- package/dist/templates/resources/.claude/agents/test-spec.md +0 -56
- package/dist/templates/resources/.claude/commands/begin.md +0 -62
- package/dist/templates/resources/.claude/skills/orchestration/SKILL.md +0 -40
- package/dist/templates/resources/.claude/skills/orchestration/task-patterns.md +0 -222
- package/dist/templates/resources/.claude/skills/orchestration/workflow-examples.md +0 -208
- package/dist/templates/resources/.claude-plugin/plugin.json +0 -8
package/dist/tui.js
CHANGED
|
@@ -3,9 +3,12 @@ import {
|
|
|
3
3
|
loadConfig
|
|
4
4
|
} from "./chunk-KQBSC5KY.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
buildSessionContext,
|
|
7
|
+
computeActiveTimeMs,
|
|
8
|
+
resolveReports
|
|
9
|
+
} from "./chunk-DBR33QHM.js";
|
|
8
10
|
import {
|
|
11
|
+
contextDir,
|
|
9
12
|
globalDir,
|
|
10
13
|
goalPath,
|
|
11
14
|
logsDir,
|
|
@@ -308,7 +311,8 @@ function renderNodeContent(node, maxWidth) {
|
|
|
308
311
|
const isRunning = !node.completedAt;
|
|
309
312
|
const dur = node.completedAt ? formatDuration(node.timestamp, node.completedAt) : "running";
|
|
310
313
|
const agents = `${node.agentCount} agent${node.agentCount !== 1 ? "s" : ""}`;
|
|
311
|
-
const
|
|
314
|
+
const modeShort = node.mode === "implementation" ? "impl" : node.mode === "planning" ? "plan" : node.mode;
|
|
315
|
+
const mode = modeShort ? ` \xB7 ${modeShort}` : "";
|
|
312
316
|
return {
|
|
313
317
|
icon: isRunning ? "\u25CF" : "\u25CB",
|
|
314
318
|
label: `C${node.cycleNumber}`,
|
|
@@ -363,6 +367,22 @@ function renderNodeContent(node, maxWidth) {
|
|
|
363
367
|
dim: true
|
|
364
368
|
};
|
|
365
369
|
}
|
|
370
|
+
case "context":
|
|
371
|
+
return {
|
|
372
|
+
icon: "\u229E",
|
|
373
|
+
label: `Context (${node.fileCount})`,
|
|
374
|
+
meta: "",
|
|
375
|
+
color: "white",
|
|
376
|
+
dim: node.fileCount === 0
|
|
377
|
+
};
|
|
378
|
+
case "context-file":
|
|
379
|
+
return {
|
|
380
|
+
icon: "\xB7",
|
|
381
|
+
label: node.label,
|
|
382
|
+
meta: "",
|
|
383
|
+
color: "gray",
|
|
384
|
+
dim: false
|
|
385
|
+
};
|
|
366
386
|
}
|
|
367
387
|
}
|
|
368
388
|
|
|
@@ -550,6 +570,10 @@ function buildLines(session, planContent, goalContent, logsContent, width, paneA
|
|
|
550
570
|
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
551
571
|
);
|
|
552
572
|
const reversedCycles = [...cycles].reverse();
|
|
573
|
+
const shortType = (t) => {
|
|
574
|
+
const colonIdx = t.indexOf(":");
|
|
575
|
+
return colonIdx >= 0 ? t.slice(colonIdx + 1) : t;
|
|
576
|
+
};
|
|
553
577
|
for (let i = 0; i < reversedCycles.length; i++) {
|
|
554
578
|
const cycle = reversedCycles[i];
|
|
555
579
|
const olderCycle = reversedCycles[i + 1];
|
|
@@ -562,19 +586,36 @@ function buildLines(session, planContent, goalContent, logsContent, width, paneA
|
|
|
562
586
|
const duration = isRunning ? "running" : formatDuration(cycle.timestamp, cycle.completedAt);
|
|
563
587
|
const n = cycle.agentsSpawned.length;
|
|
564
588
|
const startTime = formatTime(cycle.timestamp);
|
|
589
|
+
const modeLabel = cycle.mode ? cycle.mode === "implementation" ? "impl" : cycle.mode === "planning" ? "plan" : cycle.mode : "";
|
|
590
|
+
const cycModeColor = cycle.mode === "planning" ? "blue" : cycle.mode === "implementation" ? "green" : "cyan";
|
|
565
591
|
const cycleAgents = agents.filter((a) => cycle.agentsSpawned.includes(a.id));
|
|
566
|
-
const
|
|
567
|
-
const
|
|
592
|
+
const cyclePad = `C${cycle.cycle}`.padEnd(4);
|
|
593
|
+
const durPad = (isRunning ? "running" : duration).padEnd(9);
|
|
594
|
+
const headerRow = [
|
|
568
595
|
seg(` ${dot} `, { color: dotColor }),
|
|
569
|
-
seg(
|
|
570
|
-
seg("
|
|
571
|
-
|
|
572
|
-
seg("
|
|
573
|
-
seg(agentDetail, { dim: rowDim }),
|
|
574
|
-
...cycle.mode ? [seg(" \xB7 ", { dim: true }), seg(cycle.mode, { color: cycle.mode === "planning" ? "blue" : cycle.mode === "implementation" ? "green" : "cyan" })] : [],
|
|
575
|
-
seg(` ${startTime}`, { dim: true })
|
|
596
|
+
seg(cyclePad, { bold: isRunning || isNewest, dim: rowDim }),
|
|
597
|
+
...isRunning ? [seg(durPad, { color: "green", bold: true })] : [seg(durPad, { dim: rowDim })],
|
|
598
|
+
seg(startTime, { dim: true }),
|
|
599
|
+
...modeLabel ? [seg(" ", {}), seg(modeLabel, { color: cycModeColor })] : []
|
|
576
600
|
];
|
|
577
|
-
lines.push(
|
|
601
|
+
lines.push(headerRow);
|
|
602
|
+
if (cycleAgents.length > 0) {
|
|
603
|
+
const typeGroups = /* @__PURE__ */ new Map();
|
|
604
|
+
for (const a of cycleAgents) {
|
|
605
|
+
const t = shortType(a.agentType || a.name || a.id);
|
|
606
|
+
typeGroups.set(t, (typeGroups.get(t) ?? 0) + 1);
|
|
607
|
+
}
|
|
608
|
+
const agentNames = [...typeGroups.entries()].map(([t, count]) => count > 1 ? `${count}\xD7 ${t}` : t).join(", ");
|
|
609
|
+
lines.push([
|
|
610
|
+
seg(" ", {}),
|
|
611
|
+
seg(truncate(agentNames, contentWidth - 6), { dim: rowDim })
|
|
612
|
+
]);
|
|
613
|
+
} else if (n > 0) {
|
|
614
|
+
lines.push([
|
|
615
|
+
seg(" ", {}),
|
|
616
|
+
seg(`${n} agent${n !== 1 ? "s" : ""}`, { dim: rowDim })
|
|
617
|
+
]);
|
|
618
|
+
}
|
|
578
619
|
const cycleTime = new Date(cycle.timestamp).getTime();
|
|
579
620
|
const olderCycleTime = olderCycle ? new Date(olderCycle.timestamp).getTime() : 0;
|
|
580
621
|
const cycleMsgs = sortedMsgs.filter((m) => {
|
|
@@ -1320,8 +1361,8 @@ function InputBar({ mode, defaultText, onSubmit, onCancel }) {
|
|
|
1320
1361
|
|
|
1321
1362
|
// src/tui/components/StatusLine.tsx
|
|
1322
1363
|
import { Box as Box10, Text as Text10 } from "ink";
|
|
1323
|
-
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1324
|
-
function StatusLine({ mode, detailFocused = false, logsFocused = false, showLogs = false }) {
|
|
1364
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1365
|
+
function StatusLine({ mode, detailFocused = false, logsFocused = false, showLogs = false, cursorNodeType }) {
|
|
1325
1366
|
if (mode === "report-detail") {
|
|
1326
1367
|
return null;
|
|
1327
1368
|
}
|
|
@@ -1378,17 +1419,15 @@ function StatusLine({ mode, detailFocused = false, logsFocused = false, showLogs
|
|
|
1378
1419
|
}
|
|
1379
1420
|
if (logsFocused) {
|
|
1380
1421
|
return /* @__PURE__ */ jsxs9(Box10, { paddingX: 1, children: [
|
|
1381
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[
|
|
1422
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[jk/\u2191\u2193]" }),
|
|
1382
1423
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " scroll " }),
|
|
1383
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[
|
|
1424
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[h/\u2190/tab]" }),
|
|
1384
1425
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " back " }),
|
|
1385
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[
|
|
1386
|
-
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "
|
|
1426
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[t]" }),
|
|
1427
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "oggle logs " }),
|
|
1387
1428
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2502 " }),
|
|
1388
1429
|
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[m]" }),
|
|
1389
1430
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "sg " }),
|
|
1390
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[k]" }),
|
|
1391
|
-
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "ill " }),
|
|
1392
1431
|
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[g]" }),
|
|
1393
1432
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "oal " }),
|
|
1394
1433
|
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[n]" }),
|
|
@@ -1405,20 +1444,15 @@ function StatusLine({ mode, detailFocused = false, logsFocused = false, showLogs
|
|
|
1405
1444
|
}
|
|
1406
1445
|
if (detailFocused) {
|
|
1407
1446
|
return /* @__PURE__ */ jsxs9(Box10, { paddingX: 1, children: [
|
|
1408
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[
|
|
1447
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[jk/\u2191\u2193]" }),
|
|
1409
1448
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " scroll " }),
|
|
1410
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[
|
|
1449
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[h/\u2190/tab]" }),
|
|
1411
1450
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " back " }),
|
|
1412
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[
|
|
1413
|
-
/* @__PURE__ */
|
|
1414
|
-
showLogs ? "ogs hide" : "ogs show",
|
|
1415
|
-
" "
|
|
1416
|
-
] }),
|
|
1451
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[t]" }),
|
|
1452
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "oggle logs " }),
|
|
1417
1453
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2502 " }),
|
|
1418
1454
|
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[m]" }),
|
|
1419
1455
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "sg " }),
|
|
1420
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[k]" }),
|
|
1421
|
-
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "ill " }),
|
|
1422
1456
|
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[g]" }),
|
|
1423
1457
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "oal " }),
|
|
1424
1458
|
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[n]" }),
|
|
@@ -1434,25 +1468,24 @@ function StatusLine({ mode, detailFocused = false, logsFocused = false, showLogs
|
|
|
1434
1468
|
] });
|
|
1435
1469
|
}
|
|
1436
1470
|
return /* @__PURE__ */ jsxs9(Box10, { paddingX: 1, children: [
|
|
1437
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[
|
|
1471
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[hjkl]" }),
|
|
1438
1472
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " navigate " }),
|
|
1439
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[\u2190\u2192]" }),
|
|
1440
|
-
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " collapse/expand " }),
|
|
1441
1473
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2502 " }),
|
|
1474
|
+
cursorNodeType === "context-file" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
1475
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[e]" }),
|
|
1476
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "dit " }),
|
|
1477
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[\u23CE]" }),
|
|
1478
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " open " })
|
|
1479
|
+
] }),
|
|
1442
1480
|
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[space]" }),
|
|
1443
1481
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " leader " }),
|
|
1444
1482
|
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[tab]" }),
|
|
1445
1483
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " detail " }),
|
|
1446
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[
|
|
1447
|
-
/* @__PURE__ */
|
|
1448
|
-
showLogs ? "ogs hide" : "ogs show",
|
|
1449
|
-
" "
|
|
1450
|
-
] }),
|
|
1484
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[t]" }),
|
|
1485
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "oggle logs " }),
|
|
1451
1486
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2502 " }),
|
|
1452
1487
|
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[m]" }),
|
|
1453
1488
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "sg " }),
|
|
1454
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[k]" }),
|
|
1455
|
-
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "ill " }),
|
|
1456
1489
|
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[n]" }),
|
|
1457
1490
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "ew " }),
|
|
1458
1491
|
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "[R]" }),
|
|
@@ -1466,7 +1499,7 @@ function StatusLine({ mode, detailFocused = false, logsFocused = false, showLogs
|
|
|
1466
1499
|
import { Box as Box11, Text as Text11 } from "ink";
|
|
1467
1500
|
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1468
1501
|
var LEADER_WIDTH = 26;
|
|
1469
|
-
var LEADER_HEIGHT =
|
|
1502
|
+
var LEADER_HEIGHT = 19;
|
|
1470
1503
|
var COPY_HEIGHT = 9;
|
|
1471
1504
|
var INNER = LEADER_WIDTH - 2;
|
|
1472
1505
|
function pad(s) {
|
|
@@ -1495,6 +1528,9 @@ function LeaderOverlay({ mode, rows, cols }) {
|
|
|
1495
1528
|
/* @__PURE__ */ jsx11(Text11, { children: pad(" m message agent") }),
|
|
1496
1529
|
/* @__PURE__ */ jsx11(Text11, { children: pad(" / search") }),
|
|
1497
1530
|
/* @__PURE__ */ jsx11(Text11, { children: pad(" ! shell command") }),
|
|
1531
|
+
/* @__PURE__ */ jsx11(Text11, { children: pad(" j jump to pane") }),
|
|
1532
|
+
/* @__PURE__ */ jsx11(Text11, { children: pad(" k kill session/agent") }),
|
|
1533
|
+
/* @__PURE__ */ jsx11(Text11, { children: pad(" q quit") }),
|
|
1498
1534
|
/* @__PURE__ */ jsx11(Text11, { children: pad(" ? help") }),
|
|
1499
1535
|
/* @__PURE__ */ jsx11(Text11, { children: pad(" 1-9 jump to session") }),
|
|
1500
1536
|
/* @__PURE__ */ jsx11(Text11, { children: pad(" ") }),
|
|
@@ -1545,21 +1581,21 @@ function HelpOverlay({ mode, rows, cols }) {
|
|
|
1545
1581
|
if (mode !== "help") return null;
|
|
1546
1582
|
const marginLeft = Math.max(0, Math.floor((cols - HELP_WIDTH) / 2));
|
|
1547
1583
|
const lines = [
|
|
1548
|
-
row("
|
|
1549
|
-
row("
|
|
1584
|
+
row(" hjkl/\u2191\u2193\u2190\u2192 navigate", " tab switch pane"),
|
|
1585
|
+
row(" enter expand/open", " t toggle logs"),
|
|
1550
1586
|
"",
|
|
1551
|
-
row(" n new session", "
|
|
1587
|
+
row(" n new session", " m message orch."),
|
|
1552
1588
|
row(" R resume session", " C continue session"),
|
|
1553
1589
|
row(" b rollback cycle", " x restart agent"),
|
|
1554
|
-
row(" r re-run agent", "
|
|
1555
|
-
row(" m message orch.", " g edit goal"),
|
|
1590
|
+
row(" r re-run agent", " g edit goal"),
|
|
1556
1591
|
row(" p open roadmap", " w go to window"),
|
|
1557
|
-
row("
|
|
1592
|
+
row(" c claude companion", " q quit"),
|
|
1558
1593
|
"",
|
|
1559
1594
|
row(" space \u2192 y copy submenu", " space \u2192 d delete session"),
|
|
1560
|
-
row(" space \u2192
|
|
1561
|
-
row(" space \u2192
|
|
1562
|
-
row(" space \u2192
|
|
1595
|
+
row(" space \u2192 j jump to pane", " space \u2192 k kill"),
|
|
1596
|
+
row(" space \u2192 q quit", " space \u2192 o open dir"),
|
|
1597
|
+
row(" space \u2192 l tail logs", " space \u2192 / search"),
|
|
1598
|
+
row(" space \u2192 a spawn agent", " space \u2192 m msg agent"),
|
|
1563
1599
|
row(" space \u2192 ? help", " space \u2192 1-9 jump"),
|
|
1564
1600
|
"",
|
|
1565
1601
|
row(" y \u2192 p session path", " y \u2192 C LLM context"),
|
|
@@ -1628,18 +1664,21 @@ function send(request) {
|
|
|
1628
1664
|
}
|
|
1629
1665
|
|
|
1630
1666
|
// src/tui/hooks/usePolling.ts
|
|
1631
|
-
import { readFileSync as readFileSync2, existsSync, readdirSync } from "fs";
|
|
1667
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, readdirSync } from "fs";
|
|
1632
1668
|
import { join as join2 } from "path";
|
|
1633
1669
|
|
|
1634
1670
|
// src/tui/lib/tmux.ts
|
|
1635
1671
|
import { execSync } from "child_process";
|
|
1636
1672
|
import { join } from "path";
|
|
1637
|
-
import { readFileSync, writeFileSync, mkdtempSync, rmSync } from "fs";
|
|
1673
|
+
import { readFileSync, writeFileSync, mkdtempSync, rmSync, cpSync, existsSync, mkdirSync } from "fs";
|
|
1638
1674
|
import { tmpdir } from "os";
|
|
1639
1675
|
var EXEC_ENV = {
|
|
1640
1676
|
...process.env,
|
|
1641
1677
|
PATH: `/opt/homebrew/bin:/usr/local/bin:${process.env["PATH"] ?? "/usr/bin:/bin"}`
|
|
1642
1678
|
};
|
|
1679
|
+
function exec(cmd) {
|
|
1680
|
+
return execSync(cmd, { encoding: "utf-8", env: EXEC_ENV }).trim();
|
|
1681
|
+
}
|
|
1643
1682
|
function execSafe(cmd) {
|
|
1644
1683
|
try {
|
|
1645
1684
|
return execSync(cmd, { encoding: "utf-8", env: EXEC_ENV, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
@@ -1659,7 +1698,23 @@ function selectPane(paneId) {
|
|
|
1659
1698
|
function windowExists(windowId) {
|
|
1660
1699
|
return execSafe(`tmux display-message -t "${windowId}" -p "#{window_id}"`) !== null;
|
|
1661
1700
|
}
|
|
1662
|
-
|
|
1701
|
+
var companionPaneId = null;
|
|
1702
|
+
function setupCompanionPlugin() {
|
|
1703
|
+
const srcDir = join(import.meta.dirname, "templates", "companion-plugin");
|
|
1704
|
+
const destDir = join(globalDir(), "companion-plugin");
|
|
1705
|
+
if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
|
|
1706
|
+
cpSync(srcDir, destDir, { recursive: true });
|
|
1707
|
+
return destDir;
|
|
1708
|
+
}
|
|
1709
|
+
function isPaneAlive(paneId) {
|
|
1710
|
+
return execSafe(`tmux display-message -t ${shellQuote(paneId)} -p "#{pane_id}"`) !== null;
|
|
1711
|
+
}
|
|
1712
|
+
function openCompanionPane(cwd2) {
|
|
1713
|
+
if (companionPaneId && isPaneAlive(companionPaneId)) {
|
|
1714
|
+
execSafe(`tmux select-pane -t ${shellQuote(companionPaneId)}`);
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1717
|
+
const pluginDir = setupCompanionPlugin();
|
|
1663
1718
|
const templatePath = join(import.meta.dirname, "templates", "dashboard-claude.md");
|
|
1664
1719
|
let template;
|
|
1665
1720
|
try {
|
|
@@ -1672,10 +1727,12 @@ Run \`sisyphus list\` and \`sisyphus status\` to see current state.`;
|
|
|
1672
1727
|
const rendered = template.replace(/\{\{CWD\}\}/g, cwd2);
|
|
1673
1728
|
const promptPath = join(globalDir(), "dashboard-companion-prompt.md");
|
|
1674
1729
|
writeFileSync(promptPath, rendered, "utf-8");
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1730
|
+
const pathEnv = `/opt/homebrew/bin:/usr/local/bin:${process.env["PATH"] ?? "/usr/bin:/bin"}`;
|
|
1731
|
+
const claudeCmd = `SISYPHUS_COMPANION_CWD=${shellQuote(cwd2)} PATH=${shellQuote(pathEnv)} claude --dangerously-skip-permissions --plugin-dir ${shellQuote(pluginDir)} --append-system-prompt "$(cat ${shellQuote(promptPath)})"`;
|
|
1732
|
+
const result = exec(
|
|
1733
|
+
`tmux split-window -h -d -l 33% -P -F "#{pane_id}" -c ${shellQuote(cwd2)} ${shellQuote(claudeCmd)}`
|
|
1678
1734
|
);
|
|
1735
|
+
companionPaneId = result.trim() || null;
|
|
1679
1736
|
}
|
|
1680
1737
|
var TERMINAL_EDITORS = /* @__PURE__ */ new Set(["nvim", "vim", "vi", "nano", "emacs", "micro", "helix", "hx", "joe", "ne", "kak"]);
|
|
1681
1738
|
function switchToSession(sessionName) {
|
|
@@ -1731,6 +1788,7 @@ function usePolling(cwd2, selectedSessionId, intervalMs = 2500) {
|
|
|
1731
1788
|
logsContent: "",
|
|
1732
1789
|
logsCycles: [],
|
|
1733
1790
|
paneAlive: true,
|
|
1791
|
+
contextFiles: [],
|
|
1734
1792
|
error: null
|
|
1735
1793
|
});
|
|
1736
1794
|
const selectedIdRef = useRef2(selectedSessionId);
|
|
@@ -1747,6 +1805,7 @@ function usePolling(cwd2, selectedSessionId, intervalMs = 2500) {
|
|
|
1747
1805
|
let logsContent = "";
|
|
1748
1806
|
let logsCycles = [];
|
|
1749
1807
|
let paneAlive = true;
|
|
1808
|
+
let contextFiles = [];
|
|
1750
1809
|
if (selectedIdRef.current) {
|
|
1751
1810
|
const statusRes = await send({ type: "status", sessionId: selectedIdRef.current, cwd: cwd2 });
|
|
1752
1811
|
if (mountedRef.current && statusRes.ok) {
|
|
@@ -1761,21 +1820,21 @@ function usePolling(cwd2, selectedSessionId, intervalMs = 2500) {
|
|
|
1761
1820
|
}
|
|
1762
1821
|
try {
|
|
1763
1822
|
const pp = roadmapPath(cwd2, selectedIdRef.current);
|
|
1764
|
-
if (
|
|
1823
|
+
if (existsSync2(pp)) {
|
|
1765
1824
|
planContent = readFileSync2(pp, "utf-8");
|
|
1766
1825
|
}
|
|
1767
1826
|
} catch {
|
|
1768
1827
|
}
|
|
1769
1828
|
try {
|
|
1770
1829
|
const gp = goalPath(cwd2, selectedIdRef.current);
|
|
1771
|
-
if (
|
|
1830
|
+
if (existsSync2(gp)) {
|
|
1772
1831
|
goalContent = readFileSync2(gp, "utf-8");
|
|
1773
1832
|
}
|
|
1774
1833
|
} catch {
|
|
1775
1834
|
}
|
|
1776
1835
|
try {
|
|
1777
1836
|
const ld = logsDir(cwd2, selectedIdRef.current);
|
|
1778
|
-
if (
|
|
1837
|
+
if (existsSync2(ld)) {
|
|
1779
1838
|
const files = readdirSync(ld).filter((f) => f.startsWith("cycle-")).sort();
|
|
1780
1839
|
logsCycles = files.map((f) => {
|
|
1781
1840
|
const match = f.match(/cycle-(\d+)\.md$/);
|
|
@@ -1787,9 +1846,16 @@ function usePolling(cwd2, selectedSessionId, intervalMs = 2500) {
|
|
|
1787
1846
|
}
|
|
1788
1847
|
} catch {
|
|
1789
1848
|
}
|
|
1849
|
+
try {
|
|
1850
|
+
const cd = contextDir(cwd2, selectedIdRef.current);
|
|
1851
|
+
if (existsSync2(cd)) {
|
|
1852
|
+
contextFiles = readdirSync(cd).filter((f) => !f.startsWith(".")).sort();
|
|
1853
|
+
}
|
|
1854
|
+
} catch {
|
|
1855
|
+
}
|
|
1790
1856
|
}
|
|
1791
1857
|
if (mountedRef.current) {
|
|
1792
|
-
setState({ sessions, selectedSession, planContent, goalContent, logsContent, logsCycles, paneAlive, error: null });
|
|
1858
|
+
setState({ sessions, selectedSession, planContent, goalContent, logsContent, logsCycles, paneAlive, contextFiles, error: null });
|
|
1793
1859
|
}
|
|
1794
1860
|
} catch (err) {
|
|
1795
1861
|
if (mountedRef.current) {
|
|
@@ -1847,12 +1913,28 @@ function useKeybindings(handlers, isActive) {
|
|
|
1847
1913
|
handlers.onSpace();
|
|
1848
1914
|
return;
|
|
1849
1915
|
}
|
|
1850
|
-
if (input === "
|
|
1851
|
-
handlers.
|
|
1916
|
+
if (input === "h") {
|
|
1917
|
+
handlers.onLeft();
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
1920
|
+
if (input === "j") {
|
|
1921
|
+
handlers.onMoveDown();
|
|
1852
1922
|
return;
|
|
1853
1923
|
}
|
|
1854
1924
|
if (input === "k") {
|
|
1855
|
-
handlers.
|
|
1925
|
+
handlers.onMoveUp();
|
|
1926
|
+
return;
|
|
1927
|
+
}
|
|
1928
|
+
if (input === "l") {
|
|
1929
|
+
handlers.onRight();
|
|
1930
|
+
return;
|
|
1931
|
+
}
|
|
1932
|
+
if (input === "t") {
|
|
1933
|
+
handlers.onToggleLogs();
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
if (input === "m") {
|
|
1937
|
+
handlers.onMessage();
|
|
1856
1938
|
return;
|
|
1857
1939
|
}
|
|
1858
1940
|
if (input === "w") {
|
|
@@ -1883,10 +1965,6 @@ function useKeybindings(handlers, isActive) {
|
|
|
1883
1965
|
handlers.onReRun();
|
|
1884
1966
|
return;
|
|
1885
1967
|
}
|
|
1886
|
-
if (input === "j") {
|
|
1887
|
-
handlers.onJumpToPane();
|
|
1888
|
-
return;
|
|
1889
|
-
}
|
|
1890
1968
|
if (input === "R") {
|
|
1891
1969
|
handlers.onResume();
|
|
1892
1970
|
return;
|
|
@@ -1903,8 +1981,8 @@ function useKeybindings(handlers, isActive) {
|
|
|
1903
1981
|
handlers.onRollback();
|
|
1904
1982
|
return;
|
|
1905
1983
|
}
|
|
1906
|
-
if (input === "
|
|
1907
|
-
handlers.
|
|
1984
|
+
if (input === "e") {
|
|
1985
|
+
handlers.onEdit();
|
|
1908
1986
|
return;
|
|
1909
1987
|
}
|
|
1910
1988
|
},
|
|
@@ -1957,6 +2035,18 @@ function useLeaderKey(mode, onAction) {
|
|
|
1957
2035
|
onAction({ type: "shell-command" });
|
|
1958
2036
|
return;
|
|
1959
2037
|
}
|
|
2038
|
+
if (input === "j") {
|
|
2039
|
+
onAction({ type: "jump-to-pane" });
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
if (input === "k") {
|
|
2043
|
+
onAction({ type: "kill" });
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
if (input === "q") {
|
|
2047
|
+
onAction({ type: "quit" });
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
1960
2050
|
const digit = parseInt(input, 10);
|
|
1961
2051
|
if (!isNaN(digit) && digit >= 1 && digit <= 9) {
|
|
1962
2052
|
onAction({ type: "jump-to-session", index: digit });
|
|
@@ -2003,96 +2093,22 @@ function useLeaderKey(mode, onAction) {
|
|
|
2003
2093
|
);
|
|
2004
2094
|
}
|
|
2005
2095
|
|
|
2006
|
-
// src/tui/lib/reports.ts
|
|
2007
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
2008
|
-
function loadReportContent(report) {
|
|
2009
|
-
try {
|
|
2010
|
-
return readFileSync3(report.filePath, "utf-8");
|
|
2011
|
-
} catch {
|
|
2012
|
-
return report.summary;
|
|
2013
|
-
}
|
|
2014
|
-
}
|
|
2015
|
-
function resolveReports(reports) {
|
|
2016
|
-
return [...reports].reverse().map((r) => ({
|
|
2017
|
-
type: r.type,
|
|
2018
|
-
timestamp: r.timestamp,
|
|
2019
|
-
content: loadReportContent(r),
|
|
2020
|
-
summary: r.summary
|
|
2021
|
-
}));
|
|
2022
|
-
}
|
|
2023
|
-
|
|
2024
2096
|
// src/tui/lib/clipboard.ts
|
|
2025
2097
|
import { execSync as execSync2 } from "child_process";
|
|
2026
2098
|
function copyToClipboard(text) {
|
|
2027
2099
|
execSync2("pbcopy", { input: text });
|
|
2028
2100
|
}
|
|
2029
2101
|
|
|
2030
|
-
// src/tui/lib/context.ts
|
|
2031
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
2032
|
-
function readFileSafe(filePath) {
|
|
2033
|
-
try {
|
|
2034
|
-
return readFileSync4(filePath, "utf-8");
|
|
2035
|
-
} catch {
|
|
2036
|
-
return null;
|
|
2037
|
-
}
|
|
2038
|
-
}
|
|
2039
|
-
function escapeXml(s) {
|
|
2040
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2041
|
-
}
|
|
2042
|
-
function buildSessionContext(session, cwd2) {
|
|
2043
|
-
const goal = readFileSafe(goalPath(cwd2, session.id));
|
|
2044
|
-
const roadmap = readFileSafe(roadmapPath(cwd2, session.id));
|
|
2045
|
-
const agentsXml = session.agents.map((agent) => {
|
|
2046
|
-
const reportBlocks = resolveReports(agent.reports);
|
|
2047
|
-
const reportsXml = [...reportBlocks].reverse().map((block) => {
|
|
2048
|
-
return ` <report type="${block.type}" time="${escapeXml(block.timestamp)}">${escapeXml(block.content)}</report>`;
|
|
2049
|
-
}).join("\n");
|
|
2050
|
-
return [
|
|
2051
|
-
` <agent id="${escapeXml(agent.id)}" name="${escapeXml(agent.name)}" type="${escapeXml(agent.agentType)}" status="${escapeXml(agent.status)}">`,
|
|
2052
|
-
` <instruction>${escapeXml(agent.instruction)}</instruction>`,
|
|
2053
|
-
...reportsXml ? [reportsXml] : [],
|
|
2054
|
-
` </agent>`
|
|
2055
|
-
].join("\n");
|
|
2056
|
-
}).join("\n");
|
|
2057
|
-
const cyclesXml = session.orchestratorCycles.map((cycle) => {
|
|
2058
|
-
const agents = cycle.agentsSpawned.join(", ");
|
|
2059
|
-
const mode = cycle.mode ? ` mode="${escapeXml(cycle.mode)}"` : "";
|
|
2060
|
-
return ` <cycle number="${cycle.cycle}"${mode} agents="${escapeXml(agents)}" />`;
|
|
2061
|
-
}).join("\n");
|
|
2062
|
-
const lines = [
|
|
2063
|
-
"<context>",
|
|
2064
|
-
`<session id="${escapeXml(session.id)}" status="${escapeXml(session.status)}">`,
|
|
2065
|
-
` <task>${escapeXml(session.task)}</task>`,
|
|
2066
|
-
` <cwd>${escapeXml(session.cwd)}</cwd>`
|
|
2067
|
-
];
|
|
2068
|
-
if (goal) lines.push(` <goal>${escapeXml(goal)}</goal>`);
|
|
2069
|
-
if (roadmap) lines.push(` <roadmap>${escapeXml(roadmap)}</roadmap>`);
|
|
2070
|
-
if (session.agents.length > 0) {
|
|
2071
|
-
lines.push(" <agents>");
|
|
2072
|
-
lines.push(agentsXml);
|
|
2073
|
-
lines.push(" </agents>");
|
|
2074
|
-
}
|
|
2075
|
-
if (session.orchestratorCycles.length > 0) {
|
|
2076
|
-
lines.push(" <cycles>");
|
|
2077
|
-
lines.push(cyclesXml);
|
|
2078
|
-
lines.push(" </cycles>");
|
|
2079
|
-
}
|
|
2080
|
-
if (session.completionReport) {
|
|
2081
|
-
lines.push(` <completion-report>${escapeXml(session.completionReport)}</completion-report>`);
|
|
2082
|
-
}
|
|
2083
|
-
lines.push("</session>");
|
|
2084
|
-
lines.push("</context>");
|
|
2085
|
-
return lines.join("\n");
|
|
2086
|
-
}
|
|
2087
|
-
|
|
2088
2102
|
// src/tui/lib/tree.ts
|
|
2103
|
+
import { readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
|
|
2104
|
+
import { join as join3 } from "path";
|
|
2089
2105
|
function sessionSortKey(s) {
|
|
2090
2106
|
if (s.status === "completed") return 4;
|
|
2091
2107
|
const open = s.tmuxWindowId ? windowExists(s.tmuxWindowId) : false;
|
|
2092
2108
|
if (s.status === "active") return open ? 0 : 1;
|
|
2093
2109
|
return open ? 2 : 3;
|
|
2094
2110
|
}
|
|
2095
|
-
function buildTree(sessions, selectedSession, expanded) {
|
|
2111
|
+
function buildTree(sessions, selectedSession, expanded, cwd2) {
|
|
2096
2112
|
const nodes = [];
|
|
2097
2113
|
const sorted = [...sessions].sort((a, b) => {
|
|
2098
2114
|
const keyDiff = sessionSortKey(a) - sessionSortKey(b);
|
|
@@ -2213,6 +2229,41 @@ function buildTree(sessions, selectedSession, expanded) {
|
|
|
2213
2229
|
}
|
|
2214
2230
|
}
|
|
2215
2231
|
}
|
|
2232
|
+
const ctxDir = contextDir(cwd2, s.id);
|
|
2233
|
+
let contextFiles = [];
|
|
2234
|
+
try {
|
|
2235
|
+
if (existsSync3(ctxDir)) {
|
|
2236
|
+
contextFiles = readdirSync2(ctxDir).filter(
|
|
2237
|
+
(f) => !f.startsWith(".")
|
|
2238
|
+
).sort();
|
|
2239
|
+
}
|
|
2240
|
+
} catch {
|
|
2241
|
+
}
|
|
2242
|
+
const ctxNodeId = `context:${s.id}`;
|
|
2243
|
+
const ctxExpanded = expanded.has(ctxNodeId);
|
|
2244
|
+
nodes.push({
|
|
2245
|
+
id: ctxNodeId,
|
|
2246
|
+
type: "context",
|
|
2247
|
+
depth: 1,
|
|
2248
|
+
expandable: contextFiles.length > 0,
|
|
2249
|
+
expanded: ctxExpanded && contextFiles.length > 0,
|
|
2250
|
+
sessionId: s.id,
|
|
2251
|
+
fileCount: contextFiles.length
|
|
2252
|
+
});
|
|
2253
|
+
if (ctxExpanded && contextFiles.length > 0) {
|
|
2254
|
+
for (const filename of contextFiles) {
|
|
2255
|
+
nodes.push({
|
|
2256
|
+
id: `context-file:${s.id}:${filename}`,
|
|
2257
|
+
type: "context-file",
|
|
2258
|
+
depth: 2,
|
|
2259
|
+
expandable: false,
|
|
2260
|
+
expanded: false,
|
|
2261
|
+
sessionId: s.id,
|
|
2262
|
+
label: filename,
|
|
2263
|
+
filePath: join3(ctxDir, filename)
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2216
2267
|
}
|
|
2217
2268
|
return nodes;
|
|
2218
2269
|
}
|
|
@@ -2228,7 +2279,8 @@ function findParentIndex(nodes, index) {
|
|
|
2228
2279
|
}
|
|
2229
2280
|
|
|
2230
2281
|
// src/tui/App.tsx
|
|
2231
|
-
import {
|
|
2282
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
2283
|
+
import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2232
2284
|
function resolveEditor(configEditor) {
|
|
2233
2285
|
if (configEditor) return configEditor;
|
|
2234
2286
|
if (process.env.EDITOR) return process.env.EDITOR;
|
|
@@ -2247,13 +2299,13 @@ function App({ cwd: cwd2 }) {
|
|
|
2247
2299
|
const [selectedSessionId, setSelectedSessionId] = useState4(null);
|
|
2248
2300
|
const [detailScrollOffset, setDetailScrollOffset] = useState4(0);
|
|
2249
2301
|
const [logsScrollOffset, setLogsScrollOffset] = useState4(0);
|
|
2250
|
-
const [showLogs, setShowLogs] = useState4(
|
|
2302
|
+
const [showLogs, setShowLogs] = useState4(false);
|
|
2251
2303
|
const [focusPane, setFocusPane] = useState4("tree");
|
|
2252
2304
|
const [searchFilter, setSearchFilter] = useState4(null);
|
|
2253
2305
|
const [targetAgentId, setTargetAgentId] = useState4(null);
|
|
2254
2306
|
const cursorNodeIdRef = useRef3(null);
|
|
2255
2307
|
const prevNodesRef = useRef3([]);
|
|
2256
|
-
const { sessions, selectedSession, planContent, goalContent, logsContent, logsCycles, paneAlive, error } = usePolling(cwd2, selectedSessionId);
|
|
2308
|
+
const { sessions, selectedSession, planContent, goalContent, logsContent, logsCycles, paneAlive, contextFiles, error } = usePolling(cwd2, selectedSessionId);
|
|
2257
2309
|
const filteredSessions = useMemo4(() => {
|
|
2258
2310
|
if (!searchFilter) return sessions;
|
|
2259
2311
|
const q = searchFilter.toLowerCase();
|
|
@@ -2262,8 +2314,8 @@ function App({ cwd: cwd2 }) {
|
|
|
2262
2314
|
);
|
|
2263
2315
|
}, [sessions, searchFilter]);
|
|
2264
2316
|
const nodes = useMemo4(
|
|
2265
|
-
() => buildTree(filteredSessions, selectedSession, expanded),
|
|
2266
|
-
[filteredSessions, selectedSession, expanded]
|
|
2317
|
+
() => buildTree(filteredSessions, selectedSession, expanded, cwd2),
|
|
2318
|
+
[filteredSessions, selectedSession, expanded, cwd2]
|
|
2267
2319
|
);
|
|
2268
2320
|
if (nodes === prevNodesRef.current) {
|
|
2269
2321
|
const node = nodes[cursorIndex];
|
|
@@ -2492,6 +2544,13 @@ function App({ cwd: cwd2 }) {
|
|
|
2492
2544
|
}
|
|
2493
2545
|
} else if (node.type === "report") {
|
|
2494
2546
|
setMode("report-detail");
|
|
2547
|
+
} else if (node.type === "context-file") {
|
|
2548
|
+
const editor = resolveEditor(config.editor);
|
|
2549
|
+
try {
|
|
2550
|
+
openEditorPopup(cwd2, editor, node.filePath);
|
|
2551
|
+
} catch {
|
|
2552
|
+
notify("Failed to open file in editor");
|
|
2553
|
+
}
|
|
2495
2554
|
}
|
|
2496
2555
|
},
|
|
2497
2556
|
onMessage: () => {
|
|
@@ -2512,24 +2571,7 @@ function App({ cwd: cwd2 }) {
|
|
|
2512
2571
|
notify("Failed to open editor");
|
|
2513
2572
|
}
|
|
2514
2573
|
},
|
|
2515
|
-
|
|
2516
|
-
if (!selectedSessionId) {
|
|
2517
|
-
notify("No session selected");
|
|
2518
|
-
return;
|
|
2519
|
-
}
|
|
2520
|
-
const node = nodes[cursorIndex];
|
|
2521
|
-
if (node && (node.type === "agent" || node.type === "report")) {
|
|
2522
|
-
const agentId = node.agentId;
|
|
2523
|
-
const agent = agents.find((a) => a.id === agentId);
|
|
2524
|
-
if (agent?.status !== "running") {
|
|
2525
|
-
notify(`Agent ${agentId} is not running`);
|
|
2526
|
-
return;
|
|
2527
|
-
}
|
|
2528
|
-
sendAndNotify({ type: "kill-agent", sessionId: selectedSessionId, agentId }, `Killed ${agentId}`);
|
|
2529
|
-
} else {
|
|
2530
|
-
sendAndNotify({ type: "kill", sessionId: selectedSessionId }, "Session killed");
|
|
2531
|
-
}
|
|
2532
|
-
},
|
|
2574
|
+
// kill moved to leader menu (space k)
|
|
2533
2575
|
onGoToWindow: () => {
|
|
2534
2576
|
if (!session?.tmuxWindowId) {
|
|
2535
2577
|
notify("No tmux window");
|
|
@@ -2567,9 +2609,9 @@ function App({ cwd: cwd2 }) {
|
|
|
2567
2609
|
},
|
|
2568
2610
|
onClaude: () => {
|
|
2569
2611
|
try {
|
|
2570
|
-
|
|
2612
|
+
openCompanionPane(cwd2);
|
|
2571
2613
|
} catch {
|
|
2572
|
-
notify("Failed to open companion
|
|
2614
|
+
notify("Failed to open companion pane");
|
|
2573
2615
|
}
|
|
2574
2616
|
},
|
|
2575
2617
|
onOpenPlan: () => {
|
|
@@ -2600,16 +2642,7 @@ function App({ cwd: cwd2 }) {
|
|
|
2600
2642
|
instruction: agent.instruction
|
|
2601
2643
|
}, `Re-spawned ${agent.name}`);
|
|
2602
2644
|
},
|
|
2603
|
-
|
|
2604
|
-
const agent = getAgentForNode(cursorNode);
|
|
2605
|
-
if (!agent?.paneId) {
|
|
2606
|
-
notify("Select an agent with an active pane");
|
|
2607
|
-
return;
|
|
2608
|
-
}
|
|
2609
|
-
if (session?.tmuxSessionName) switchToSession(session.tmuxSessionName);
|
|
2610
|
-
if (session?.tmuxWindowId) selectWindow(session.tmuxWindowId);
|
|
2611
|
-
selectPane(agent.paneId);
|
|
2612
|
-
},
|
|
2645
|
+
// jump-to-pane moved to leader menu (space j)
|
|
2613
2646
|
onResume: () => {
|
|
2614
2647
|
if (!selectedSessionId) {
|
|
2615
2648
|
notify("No session selected");
|
|
@@ -2649,6 +2682,15 @@ function App({ cwd: cwd2 }) {
|
|
|
2649
2682
|
return;
|
|
2650
2683
|
}
|
|
2651
2684
|
setMode("rollback");
|
|
2685
|
+
},
|
|
2686
|
+
onEdit: () => {
|
|
2687
|
+
if (!cursorNode || cursorNode.type !== "context-file") return;
|
|
2688
|
+
const editor = resolveEditor(config.editor);
|
|
2689
|
+
try {
|
|
2690
|
+
openEditorPopup(cwd2, editor, cursorNode.filePath);
|
|
2691
|
+
} catch {
|
|
2692
|
+
notify("Failed to open file in editor");
|
|
2693
|
+
}
|
|
2652
2694
|
}
|
|
2653
2695
|
},
|
|
2654
2696
|
mode === "navigate"
|
|
@@ -2801,6 +2843,44 @@ function App({ cwd: cwd2 }) {
|
|
|
2801
2843
|
setMode("shell-command");
|
|
2802
2844
|
break;
|
|
2803
2845
|
}
|
|
2846
|
+
case "jump-to-pane": {
|
|
2847
|
+
const agent = getAgentForNode(cursorNode);
|
|
2848
|
+
if (!agent?.paneId) {
|
|
2849
|
+
notify("Select an agent with an active pane");
|
|
2850
|
+
setMode("navigate");
|
|
2851
|
+
break;
|
|
2852
|
+
}
|
|
2853
|
+
if (session?.tmuxSessionName) switchToSession(session.tmuxSessionName);
|
|
2854
|
+
if (session?.tmuxWindowId) selectWindow(session.tmuxWindowId);
|
|
2855
|
+
selectPane(agent.paneId);
|
|
2856
|
+
setMode("navigate");
|
|
2857
|
+
break;
|
|
2858
|
+
}
|
|
2859
|
+
case "kill": {
|
|
2860
|
+
if (!selectedSessionId) {
|
|
2861
|
+
notify("No session selected");
|
|
2862
|
+
setMode("navigate");
|
|
2863
|
+
break;
|
|
2864
|
+
}
|
|
2865
|
+
const node = nodes[cursorIndex];
|
|
2866
|
+
if (node && (node.type === "agent" || node.type === "report")) {
|
|
2867
|
+
const agentId = node.agentId;
|
|
2868
|
+
const agent = agents.find((a) => a.id === agentId);
|
|
2869
|
+
if (agent?.status !== "running") {
|
|
2870
|
+
notify(`Agent ${agentId} is not running`);
|
|
2871
|
+
setMode("navigate");
|
|
2872
|
+
break;
|
|
2873
|
+
}
|
|
2874
|
+
sendAndNotify({ type: "kill-agent", sessionId: selectedSessionId, agentId }, `Killed ${agentId}`);
|
|
2875
|
+
} else {
|
|
2876
|
+
sendAndNotify({ type: "kill", sessionId: selectedSessionId }, "Session killed");
|
|
2877
|
+
}
|
|
2878
|
+
setMode("navigate");
|
|
2879
|
+
break;
|
|
2880
|
+
}
|
|
2881
|
+
case "quit":
|
|
2882
|
+
exit();
|
|
2883
|
+
break;
|
|
2804
2884
|
case "dismiss":
|
|
2805
2885
|
setMode("navigate");
|
|
2806
2886
|
break;
|
|
@@ -2928,6 +3008,16 @@ function App({ cwd: cwd2 }) {
|
|
|
2928
3008
|
() => detailAgent ? resolveReports(detailAgent.reports) : [],
|
|
2929
3009
|
[detailAgent]
|
|
2930
3010
|
);
|
|
3011
|
+
const contextFileContent = useMemo4(() => {
|
|
3012
|
+
if (!cursorNode || cursorNode.type !== "context-file") return null;
|
|
3013
|
+
try {
|
|
3014
|
+
if (existsSync4(cursorNode.filePath)) {
|
|
3015
|
+
return readFileSync3(cursorNode.filePath, "utf-8");
|
|
3016
|
+
}
|
|
3017
|
+
} catch {
|
|
3018
|
+
}
|
|
3019
|
+
return null;
|
|
3020
|
+
}, [cursorNode]);
|
|
2931
3021
|
const renderDetailPanel = useCallback2(
|
|
2932
3022
|
(detailWidth2, contentHeight2) => {
|
|
2933
3023
|
if (mode === "report-detail" && reportAgent) {
|
|
@@ -3137,7 +3227,7 @@ function App({ cwd: cwd2 }) {
|
|
|
3137
3227
|
paddingX: 1,
|
|
3138
3228
|
children: [
|
|
3139
3229
|
/* @__PURE__ */ jsx13(Text13, { bold: true, children: " Message" }),
|
|
3140
|
-
msg ? /* @__PURE__ */ jsxs12(
|
|
3230
|
+
msg ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
|
|
3141
3231
|
/* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
3142
3232
|
" ",
|
|
3143
3233
|
cursorNode.source,
|
|
@@ -3153,6 +3243,72 @@ function App({ cwd: cwd2 }) {
|
|
|
3153
3243
|
}
|
|
3154
3244
|
);
|
|
3155
3245
|
}
|
|
3246
|
+
case "context":
|
|
3247
|
+
return /* @__PURE__ */ jsxs12(
|
|
3248
|
+
Box13,
|
|
3249
|
+
{
|
|
3250
|
+
flexDirection: "column",
|
|
3251
|
+
width: detailWidth2,
|
|
3252
|
+
borderStyle: "round",
|
|
3253
|
+
borderColor: "gray",
|
|
3254
|
+
paddingX: 1,
|
|
3255
|
+
children: [
|
|
3256
|
+
/* @__PURE__ */ jsxs12(Text13, { bold: true, children: [
|
|
3257
|
+
" ",
|
|
3258
|
+
/* @__PURE__ */ jsx13(Text13, { color: "white", children: "\u229E" }),
|
|
3259
|
+
" Context (",
|
|
3260
|
+
contextFiles.length,
|
|
3261
|
+
")"
|
|
3262
|
+
] }),
|
|
3263
|
+
contextFiles.length === 0 ? /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
3264
|
+
" ",
|
|
3265
|
+
"No context files found."
|
|
3266
|
+
] }) : contextFiles.map((f) => /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
3267
|
+
" \xB7 ",
|
|
3268
|
+
f
|
|
3269
|
+
] }, f))
|
|
3270
|
+
]
|
|
3271
|
+
}
|
|
3272
|
+
);
|
|
3273
|
+
case "context-file": {
|
|
3274
|
+
const fileLines = contextFileContent != null ? wrapText(cleanMarkdown(stripFrontmatter(contextFileContent)), detailWidth2 - 8) : [];
|
|
3275
|
+
const viewableLines = contentHeight2 - 6;
|
|
3276
|
+
return /* @__PURE__ */ jsxs12(
|
|
3277
|
+
Box13,
|
|
3278
|
+
{
|
|
3279
|
+
flexDirection: "column",
|
|
3280
|
+
width: detailWidth2,
|
|
3281
|
+
borderStyle: "round",
|
|
3282
|
+
borderColor: "white",
|
|
3283
|
+
paddingX: 1,
|
|
3284
|
+
children: [
|
|
3285
|
+
/* @__PURE__ */ jsxs12(Text13, { bold: true, children: [
|
|
3286
|
+
" ",
|
|
3287
|
+
/* @__PURE__ */ jsx13(Text13, { color: "white", children: "\u229E" }),
|
|
3288
|
+
" ",
|
|
3289
|
+
cursorNode.label
|
|
3290
|
+
] }),
|
|
3291
|
+
/* @__PURE__ */ jsx13(Text13, { children: " " }),
|
|
3292
|
+
contextFileContent == null ? /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
3293
|
+
" ",
|
|
3294
|
+
"File not found or unreadable."
|
|
3295
|
+
] }) : fileLines.length === 0 ? /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
3296
|
+
" ",
|
|
3297
|
+
"(empty)"
|
|
3298
|
+
] }) : fileLines.slice(0, viewableLines).map((line, i) => /* @__PURE__ */ jsxs12(Text13, { children: [
|
|
3299
|
+
" ",
|
|
3300
|
+
line
|
|
3301
|
+
] }, i)),
|
|
3302
|
+
fileLines.length > viewableLines && /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
3303
|
+
" ",
|
|
3304
|
+
"\u2026 ",
|
|
3305
|
+
fileLines.length - viewableLines,
|
|
3306
|
+
" more lines"
|
|
3307
|
+
] })
|
|
3308
|
+
]
|
|
3309
|
+
}
|
|
3310
|
+
);
|
|
3311
|
+
}
|
|
3156
3312
|
default:
|
|
3157
3313
|
return /* @__PURE__ */ jsx13(
|
|
3158
3314
|
SessionDetail,
|
|
@@ -3167,7 +3323,7 @@ function App({ cwd: cwd2 }) {
|
|
|
3167
3323
|
);
|
|
3168
3324
|
}
|
|
3169
3325
|
},
|
|
3170
|
-
[cursorNode, session, planContent, goalContent, logsContent, paneAlive, agents, mode, reportAgent, reportBlocks, detailReportBlocks, handleCancel, detailScrollOffset, focusPane]
|
|
3326
|
+
[cursorNode, session, planContent, goalContent, logsContent, paneAlive, agents, mode, reportAgent, reportBlocks, detailReportBlocks, handleCancel, detailScrollOffset, focusPane, contextFiles, contextFileContent]
|
|
3171
3327
|
);
|
|
3172
3328
|
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", width: cols, height: rows, children: [
|
|
3173
3329
|
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", height: contentHeight, children: [
|
|
@@ -3213,7 +3369,8 @@ function App({ cwd: cwd2 }) {
|
|
|
3213
3369
|
mode,
|
|
3214
3370
|
detailFocused: focusPane === "detail",
|
|
3215
3371
|
logsFocused: focusPane === "logs",
|
|
3216
|
-
showLogs
|
|
3372
|
+
showLogs,
|
|
3373
|
+
cursorNodeType: cursorNode?.type
|
|
3217
3374
|
}
|
|
3218
3375
|
),
|
|
3219
3376
|
/* @__PURE__ */ jsx13(LeaderOverlay, { mode, rows, cols }),
|