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.
Files changed (59) hide show
  1. package/README.md +6 -4
  2. package/dist/chunk-DBR33QHM.js +185 -0
  3. package/dist/chunk-DBR33QHM.js.map +1 -0
  4. package/dist/cli.js +159 -22
  5. package/dist/cli.js.map +1 -1
  6. package/dist/daemon.js +30 -2
  7. package/dist/daemon.js.map +1 -1
  8. package/dist/templates/CLAUDE.md +1 -0
  9. package/dist/templates/agent-plugin/agents/operator.md +1 -0
  10. package/dist/templates/agent-plugin/agents/plan.md +68 -4
  11. package/dist/templates/agent-plugin/agents/review-plan.md +1 -1
  12. package/dist/templates/agent-plugin/agents/review.md +1 -0
  13. package/dist/templates/agent-plugin/agents/spec-draft.md +32 -4
  14. package/dist/templates/agent-plugin/agents/test-spec.md +1 -0
  15. package/dist/templates/companion-plugin/.claude-plugin/plugin.json +1 -0
  16. package/dist/templates/companion-plugin/hooks/hooks.json +12 -0
  17. package/dist/templates/companion-plugin/hooks/user-prompt-context.sh +3 -0
  18. package/dist/templates/dashboard-claude.md +1 -1
  19. package/dist/templates/orchestrator-base.md +5 -9
  20. package/dist/templates/orchestrator-planning.md +5 -49
  21. package/dist/tui.js +341 -184
  22. package/dist/tui.js.map +1 -1
  23. package/package.json +1 -1
  24. package/templates/CLAUDE.md +1 -0
  25. package/templates/agent-plugin/agents/operator.md +1 -0
  26. package/templates/agent-plugin/agents/plan.md +68 -4
  27. package/templates/agent-plugin/agents/review-plan.md +1 -1
  28. package/templates/agent-plugin/agents/review.md +1 -0
  29. package/templates/agent-plugin/agents/spec-draft.md +32 -4
  30. package/templates/agent-plugin/agents/test-spec.md +1 -0
  31. package/templates/companion-plugin/.claude-plugin/plugin.json +1 -0
  32. package/templates/companion-plugin/hooks/hooks.json +12 -0
  33. package/templates/companion-plugin/hooks/user-prompt-context.sh +3 -0
  34. package/templates/dashboard-claude.md +1 -1
  35. package/templates/orchestrator-base.md +5 -9
  36. package/templates/orchestrator-planning.md +5 -49
  37. package/dist/chunk-ZE2SKB4B.js +0 -35
  38. package/dist/chunk-ZE2SKB4B.js.map +0 -1
  39. package/dist/templates/agent-plugin/.claude/agents/debug.md +0 -39
  40. package/dist/templates/agent-plugin/.claude/agents/plan.md +0 -101
  41. package/dist/templates/agent-plugin/.claude/agents/review-plan.md +0 -81
  42. package/dist/templates/agent-plugin/.claude/agents/review.md +0 -56
  43. package/dist/templates/agent-plugin/.claude/agents/spec-draft.md +0 -73
  44. package/dist/templates/agent-plugin/.claude/agents/test-spec.md +0 -56
  45. package/dist/templates/orchestrator-plugin/.claude/commands/begin.md +0 -62
  46. package/dist/templates/orchestrator-plugin/.claude/skills/orchestration/SKILL.md +0 -40
  47. package/dist/templates/orchestrator-plugin/.claude/skills/orchestration/task-patterns.md +0 -222
  48. package/dist/templates/orchestrator-plugin/.claude/skills/orchestration/workflow-examples.md +0 -208
  49. package/dist/templates/resources/.claude/agents/debug.md +0 -39
  50. package/dist/templates/resources/.claude/agents/plan.md +0 -101
  51. package/dist/templates/resources/.claude/agents/review-plan.md +0 -81
  52. package/dist/templates/resources/.claude/agents/review.md +0 -56
  53. package/dist/templates/resources/.claude/agents/spec-draft.md +0 -73
  54. package/dist/templates/resources/.claude/agents/test-spec.md +0 -56
  55. package/dist/templates/resources/.claude/commands/begin.md +0 -62
  56. package/dist/templates/resources/.claude/skills/orchestration/SKILL.md +0 -40
  57. package/dist/templates/resources/.claude/skills/orchestration/task-patterns.md +0 -222
  58. package/dist/templates/resources/.claude/skills/orchestration/workflow-examples.md +0 -208
  59. 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
- computeActiveTimeMs
7
- } from "./chunk-ZE2SKB4B.js";
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 mode = node.mode ? ` \xB7 ${node.mode}` : "";
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 agentDetail = cycleAgents.length === 1 ? truncate(cycleAgents[0].agentType || cycleAgents[0].name, 14) : cycleAgents.length === 2 ? cycleAgents.map((a) => truncate(a.agentType || a.name, 10)).join(" + ") : `${n} agent${n !== 1 ? "s" : ""}`;
567
- const row2 = [
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(`C${cycle.cycle}`, { bold: isRunning || isNewest, dim: rowDim }),
570
- seg(" ", {}),
571
- ...isRunning ? [seg("running", { color: "green", bold: true })] : [seg(duration, { dim: rowDim })],
572
- seg(" \xB7 ", { dim: true }),
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(row2);
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: "[\u2191\u2193]" }),
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: "[\u2190/tab]" }),
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: "[l]" }),
1386
- /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "ogs hide " }),
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: "[\u2191\u2193]" }),
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: "[\u2190/tab]" }),
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: "[l]" }),
1413
- /* @__PURE__ */ jsxs9(Text10, { dimColor: true, children: [
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: "[\u2191\u2193]" }),
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: "[l]" }),
1447
- /* @__PURE__ */ jsxs9(Text10, { dimColor: true, children: [
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 = 16;
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(" \u2191\u2193 move cursor", " \u2190\u2192 collapse/expand"),
1549
- row(" tab switch pane", " enter expand/open report"),
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", " k kill session/agent"),
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", " j jump to pane"),
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(" l toggle logs", " q quit"),
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 l tail logs", " space \u2192 o open dir"),
1561
- row(" space \u2192 / search", " space \u2192 a spawn agent"),
1562
- row(" space \u2192 m msg agent", " space \u2192 ! shell"),
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
- function openCompanionPopup(cwd2) {
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
- execSync(
1676
- `tmux display-popup -E -w 80% -h 80% -d ${shellQuote(cwd2)} ${shellQuote(`claude --dangerously-skip-permissions --system-prompt "$(cat ${shellQuote(promptPath)})"`)}`,
1677
- { stdio: "inherit", env: EXEC_ENV }
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 (existsSync(pp)) {
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 (existsSync(gp)) {
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 (existsSync(ld)) {
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 === "m") {
1851
- handlers.onMessage();
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.onKill();
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 === "l") {
1907
- handlers.onToggleLogs();
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
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 { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
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(true);
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
- onKill: () => {
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
- openCompanionPopup(cwd2);
2612
+ openCompanionPane(cwd2);
2571
2613
  } catch {
2572
- notify("Failed to open companion popup");
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
- onJumpToPane: () => {
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(Fragment3, { children: [
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 }),