sisyphi 1.2.19 → 1.2.21

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/dist/cli.js CHANGED
@@ -87,8 +87,8 @@ function askReviewSubmitFlagPath(cwd, sessionId, askId) {
87
87
  function askVisualsDir(cwd, sessionId, askId) {
88
88
  return join(askEntryDir(cwd, sessionId, askId), "visuals");
89
89
  }
90
- function tmuxSessionName(cwd, sessionLabel) {
91
- return `ssyph_${basename(cwd)}_${sessionLabel}`;
90
+ function tmuxSessionName(cwd, sessionLabel2) {
91
+ return `ssyph_${basename(cwd)}_${sessionLabel2}`;
92
92
  }
93
93
  function sessionsManifestPath() {
94
94
  return join(globalDir(), "sessions-manifest.json");
@@ -422,7 +422,7 @@ var init_state = __esm({
422
422
  });
423
423
 
424
424
  // src/shared/platform.ts
425
- import { execSync as execSync7 } from "child_process";
425
+ import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
426
426
  import { existsSync as existsSync9, readFileSync as readFileSync10 } from "fs";
427
427
  function detectPlatform() {
428
428
  if (cachedPlatform) return cachedPlatform;
@@ -467,6 +467,20 @@ function hasCommand(cmd) {
467
467
  return false;
468
468
  }
469
469
  }
470
+ function isTerminalFrontmost() {
471
+ if (detectPlatform() !== "darwin") return false;
472
+ try {
473
+ const raw = execFileSync2(
474
+ "osascript",
475
+ ["-e", 'tell application "System Events" to get name of first application process whose frontmost is true'],
476
+ { timeout: 1e3, stdio: ["ignore", "pipe", "ignore"] }
477
+ );
478
+ const name = raw.toString().trim().toLowerCase();
479
+ return TERMINAL_APP_NAMES.has(name);
480
+ } catch {
481
+ return false;
482
+ }
483
+ }
470
484
  function platformLabel() {
471
485
  switch (detectPlatform()) {
472
486
  case "darwin":
@@ -483,11 +497,24 @@ function platformLabel() {
483
497
  return "unknown";
484
498
  }
485
499
  }
486
- var cachedPlatform, cmdCache;
500
+ var cachedPlatform, cmdCache, TERMINAL_APP_NAMES;
487
501
  var init_platform = __esm({
488
502
  "src/shared/platform.ts"() {
489
503
  "use strict";
490
504
  cmdCache = /* @__PURE__ */ new Map();
505
+ TERMINAL_APP_NAMES = /* @__PURE__ */ new Set([
506
+ "iterm2",
507
+ "iterm",
508
+ "terminal",
509
+ "apple_terminal",
510
+ "alacritty",
511
+ "kitty",
512
+ "wezterm",
513
+ "ghostty",
514
+ "hyper",
515
+ "warp",
516
+ "tmux"
517
+ ]);
491
518
  }
492
519
  });
493
520
 
@@ -635,13 +662,14 @@ var init_notify = __esm({
635
662
  });
636
663
 
637
664
  // src/daemon/ask-store.ts
638
- import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync11, readdirSync as readdirSync3 } from "fs";
665
+ import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
639
666
  import { basename as basename3 } from "path";
640
- function maybeNotifyOnAskCreated(cwd, sessionId, meta) {
667
+ function maybeNotifyOnAskCreated(cwd, sessionId, meta, suppress = false) {
641
668
  if (process.env.NODE_ENV === "test" || process.env.SISYPHUS_DISABLE_NOTIFY === "1") return;
642
669
  const isActionable = meta.kind !== void 0 && ACTIONABLE_KINDS.has(meta.kind);
643
670
  const isHeartbeat = meta.askedBy === HEARTBEAT_ASKED_BY;
644
671
  if (!isActionable && !isHeartbeat) return;
672
+ if (suppress) return;
645
673
  try {
646
674
  const config = loadConfig(cwd);
647
675
  if (config.notifications?.enabled === false) return;
@@ -667,8 +695,7 @@ function createAsk(cwd, sessionId, params) {
667
695
  ...params.title !== void 0 ? { title: params.title } : {},
668
696
  ...params.subtitle !== void 0 ? { subtitle: params.subtitle } : {},
669
697
  ...params.kind !== void 0 ? { kind: params.kind } : {},
670
- ...params.orphanTarget !== void 0 ? { orphanTarget: params.orphanTarget } : {},
671
- ...params.modeTransition !== void 0 ? { modeTransition: params.modeTransition } : {}
698
+ ...params.orphanTarget !== void 0 ? { orphanTarget: params.orphanTarget } : {}
672
699
  };
673
700
  atomicWrite(askMetaPath(cwd, sessionId, params.askId), JSON.stringify(meta, null, 2));
674
701
  emitHistoryEvent(sessionId, "ask-issued", {
@@ -677,7 +704,7 @@ function createAsk(cwd, sessionId, params) {
677
704
  blocking: params.blocking,
678
705
  askedAt
679
706
  });
680
- maybeNotifyOnAskCreated(cwd, sessionId, meta);
707
+ maybeNotifyOnAskCreated(cwd, sessionId, meta, params.suppressTerminalNotification === true);
681
708
  return meta;
682
709
  }
683
710
  function writeDecisions(cwd, sessionId, askId, deck) {
@@ -898,9 +925,9 @@ var init_exec = __esm({
898
925
  });
899
926
 
900
927
  // src/daemon/frontmatter.ts
901
- import { readFileSync as readFileSync15, existsSync as existsSync14, readdirSync as readdirSync5 } from "fs";
928
+ import { readFileSync as readFileSync16, existsSync as existsSync14, readdirSync as readdirSync5 } from "fs";
902
929
  import { homedir as homedir8 } from "os";
903
- import { join as join15, basename as basename5 } from "path";
930
+ import { join as join16, basename as basename5 } from "path";
904
931
  function parseAgentFrontmatter(content) {
905
932
  const match = content.match(/^---\n([\s\S]*?)\n---/);
906
933
  if (!match) return {};
@@ -950,7 +977,7 @@ function discoverAgentTypes(pluginDir, cwd) {
950
977
  if (seen.has(qualifiedName)) continue;
951
978
  seen.add(qualifiedName);
952
979
  try {
953
- const content = readFileSync15(join15(dir, file), "utf-8");
980
+ const content = readFileSync16(join16(dir, file), "utf-8");
954
981
  const fm = parseAgentFrontmatter(content);
955
982
  results.push({ qualifiedName, source, description: fm.description, model: fm.model });
956
983
  } catch {
@@ -958,14 +985,14 @@ function discoverAgentTypes(pluginDir, cwd) {
958
985
  }
959
986
  }
960
987
  }
961
- scanDir(join15(projectAgentPluginDir(cwd), "agents"), null, "project-sis");
962
- scanDir(join15(userAgentPluginDir(), "agents"), null, "user-sis");
963
- scanDir(join15(cwd, ".claude", "agents"), null, "project");
964
- scanDir(join15(homedir8(), ".claude", "agents"), null, "user");
965
- scanDir(join15(pluginDir, "agents"), "sisyphus", "bundled");
988
+ scanDir(join16(projectAgentPluginDir(cwd), "agents"), null, "project-sis");
989
+ scanDir(join16(userAgentPluginDir(), "agents"), null, "user-sis");
990
+ scanDir(join16(cwd, ".claude", "agents"), null, "project");
991
+ scanDir(join16(homedir8(), ".claude", "agents"), null, "user");
992
+ scanDir(join16(pluginDir, "agents"), "sisyphus", "bundled");
966
993
  try {
967
- const registryPath2 = join15(homedir8(), ".claude", "plugins", "installed_plugins.json");
968
- const registry = JSON.parse(readFileSync15(registryPath2, "utf-8"));
994
+ const registryPath2 = join16(homedir8(), ".claude", "plugins", "installed_plugins.json");
995
+ const registry = JSON.parse(readFileSync16(registryPath2, "utf-8"));
969
996
  const pluginEntries = registry.plugins ?? registry;
970
997
  for (const key of Object.keys(pluginEntries)) {
971
998
  const atIdx = key.indexOf("@");
@@ -974,7 +1001,7 @@ function discoverAgentTypes(pluginDir, cwd) {
974
1001
  const entry = pluginEntries[key];
975
1002
  const installPath = Array.isArray(entry) ? entry[0]?.installPath : entry?.installPath;
976
1003
  if (installPath) {
977
- scanDir(join15(installPath, "agents"), namespace, "plugin");
1004
+ scanDir(join16(installPath, "agents"), namespace, "plugin");
978
1005
  }
979
1006
  }
980
1007
  } catch {
@@ -996,8 +1023,8 @@ var init_effort_render = __esm({
996
1023
  });
997
1024
 
998
1025
  // src/daemon/extensions.ts
999
- import { existsSync as existsSync15, readFileSync as readFileSync16, readdirSync as readdirSync6, copyFileSync as copyFileSync2, mkdirSync as mkdirSync8, statSync as statSync3, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
1000
- import { resolve as resolve6, join as join16, basename as basename6, relative } from "path";
1026
+ import { existsSync as existsSync15, readFileSync as readFileSync17, readdirSync as readdirSync6, copyFileSync as copyFileSync2, mkdirSync as mkdirSync8, statSync as statSync3, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
1027
+ import { resolve as resolve6, join as join17, basename as basename6, relative } from "path";
1001
1028
  function resolveBundledTemplateDir(kind) {
1002
1029
  const built = resolve6(import.meta.dirname, "../templates", kind);
1003
1030
  if (existsSync15(built)) return built;
@@ -1014,12 +1041,12 @@ var init_extensions = __esm({
1014
1041
  });
1015
1042
 
1016
1043
  // src/shared/version.ts
1017
- import { readFileSync as readFileSync21 } from "fs";
1044
+ import { readFileSync as readFileSync22 } from "fs";
1018
1045
  import { resolve as resolve7 } from "path";
1019
1046
  function readSisyphusVersion() {
1020
1047
  for (const rel of ["../package.json", "../../package.json"]) {
1021
1048
  try {
1022
- const raw = readFileSync21(resolve7(import.meta.dirname, rel), "utf-8");
1049
+ const raw = readFileSync22(resolve7(import.meta.dirname, rel), "utf-8");
1023
1050
  const pkg = JSON.parse(raw);
1024
1051
  if (pkg.name === "sisyphi" && pkg.version) return pkg.version;
1025
1052
  } catch {
@@ -1086,9 +1113,9 @@ var init_upload = __esm({
1086
1113
  // src/shared/session-export.ts
1087
1114
  import { execFile as execFile2 } from "child_process";
1088
1115
  import { promisify } from "util";
1089
- import { existsSync as existsSync25, readFileSync as readFileSync25, mkdirSync as mkdirSync11, symlinkSync, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "fs";
1116
+ import { existsSync as existsSync25, readFileSync as readFileSync26, mkdirSync as mkdirSync11, symlinkSync, rmSync as rmSync5, writeFileSync as writeFileSync13 } from "fs";
1090
1117
  import { homedir as homedir12 } from "os";
1091
- import { join as join22 } from "path";
1118
+ import { join as join23 } from "path";
1092
1119
  function sanitizeName(name) {
1093
1120
  return name.replace(/[^a-zA-Z0-9-_]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
1094
1121
  }
@@ -1096,11 +1123,11 @@ function buildOutputPath(label, dir) {
1096
1123
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1097
1124
  mkdirSync11(dir, { recursive: true });
1098
1125
  const base = `sisyphus-${label}-${date}`;
1099
- let candidate = join22(dir, `${base}.zip`);
1126
+ let candidate = join23(dir, `${base}.zip`);
1100
1127
  let counter = 1;
1101
1128
  while (existsSync25(candidate)) {
1102
1129
  counter++;
1103
- candidate = join22(dir, `${base}-${counter}.zip`);
1130
+ candidate = join23(dir, `${base}-${counter}.zip`);
1104
1131
  }
1105
1132
  return candidate;
1106
1133
  }
@@ -1170,24 +1197,24 @@ async function exportSessionToZip(sessionId, cwd, options) {
1170
1197
  const stPath = statePath(cwd, sessionId);
1171
1198
  if (existsSync25(stPath)) {
1172
1199
  try {
1173
- const state = JSON.parse(readFileSync25(stPath, "utf-8"));
1200
+ const state = JSON.parse(readFileSync26(stPath, "utf-8"));
1174
1201
  if (state.name) {
1175
1202
  label = sanitizeName(state.name);
1176
1203
  }
1177
1204
  } catch {
1178
1205
  }
1179
1206
  }
1180
- const dir = options?.outputDir ?? join22(homedir12(), "Downloads");
1207
+ const dir = options?.outputDir ?? join23(homedir12(), "Downloads");
1181
1208
  const outputPath = buildOutputPath(label, dir);
1182
1209
  const tmpDir = `/tmp/sisyphus-export-${sessionId.slice(0, 8)}-${Date.now()}`;
1183
1210
  try {
1184
1211
  mkdirSync11(tmpDir, { recursive: true });
1185
- writeFileSync12(join22(tmpDir, "CLAUDE.md"), generateGuide(), "utf-8");
1212
+ writeFileSync13(join23(tmpDir, "CLAUDE.md"), generateGuide(), "utf-8");
1186
1213
  if (sessExists) {
1187
- symlinkSync(sessDir, join22(tmpDir, "session"));
1214
+ symlinkSync(sessDir, join23(tmpDir, "session"));
1188
1215
  }
1189
1216
  if (histExists) {
1190
- symlinkSync(histDir, join22(tmpDir, "history"));
1217
+ symlinkSync(histDir, join23(tmpDir, "history"));
1191
1218
  }
1192
1219
  const parts = ["CLAUDE.md", sessExists ? "session/" : "", histExists ? "history/" : ""].filter(Boolean);
1193
1220
  await execFileAsync("zip", ["-rq", outputPath, ...parts], { cwd: tmpDir });
@@ -1398,9 +1425,9 @@ var init_companion_normalize = __esm({
1398
1425
  });
1399
1426
 
1400
1427
  // src/daemon/companion.ts
1401
- import { existsSync as existsSync27, mkdirSync as mkdirSync12, readFileSync as readFileSync27, renameSync as renameSync4, writeFileSync as writeFileSync14 } from "fs";
1428
+ import { existsSync as existsSync27, mkdirSync as mkdirSync12, readFileSync as readFileSync28, renameSync as renameSync4, writeFileSync as writeFileSync15 } from "fs";
1402
1429
  import { randomUUID as randomUUID3 } from "crypto";
1403
- import { dirname as dirname9, join as join24 } from "path";
1430
+ import { dirname as dirname9, join as join25 } from "path";
1404
1431
  function loadCompanion() {
1405
1432
  const path = companionPath();
1406
1433
  if (!existsSync27(path)) {
@@ -1408,7 +1435,7 @@ function loadCompanion() {
1408
1435
  saveCompanion(state2);
1409
1436
  return state2;
1410
1437
  }
1411
- const raw = readFileSync27(path, "utf-8");
1438
+ const raw = readFileSync28(path, "utf-8");
1412
1439
  const state = JSON.parse(raw);
1413
1440
  return normalizeCompanion(state);
1414
1441
  }
@@ -1416,8 +1443,8 @@ function saveCompanion(state) {
1416
1443
  const path = companionPath();
1417
1444
  const dir = dirname9(path);
1418
1445
  mkdirSync12(dir, { recursive: true });
1419
- const tmp = join24(dir, `.companion.${randomUUID3()}.tmp`);
1420
- writeFileSync14(tmp, JSON.stringify(state, null, 2), "utf-8");
1446
+ const tmp = join25(dir, `.companion.${randomUUID3()}.tmp`);
1447
+ writeFileSync15(tmp, JSON.stringify(state, null, 2), "utf-8");
1421
1448
  renameSync4(tmp, path);
1422
1449
  }
1423
1450
  function createDefaultCompanion() {
@@ -1470,8 +1497,8 @@ var init_companion = __esm({
1470
1497
  });
1471
1498
 
1472
1499
  // src/daemon/companion-memory.ts
1473
- import { existsSync as existsSync28, mkdirSync as mkdirSync13, readFileSync as readFileSync28, renameSync as renameSync5, writeFileSync as writeFileSync15 } from "fs";
1474
- import { dirname as dirname10, join as join25 } from "path";
1500
+ import { existsSync as existsSync28, mkdirSync as mkdirSync13, readFileSync as readFileSync29, renameSync as renameSync5, writeFileSync as writeFileSync16 } from "fs";
1501
+ import { dirname as dirname10, join as join26 } from "path";
1475
1502
  import { randomUUID as randomUUID4 } from "crypto";
1476
1503
  import { z } from "zod";
1477
1504
  function isSafeObservationText(text) {
@@ -1501,7 +1528,7 @@ function loadMemoryStrict() {
1501
1528
  if (!existsSync28(path)) return defaultMemoryState();
1502
1529
  let raw;
1503
1530
  try {
1504
- raw = readFileSync28(path, "utf-8");
1531
+ raw = readFileSync29(path, "utf-8");
1505
1532
  } catch (err) {
1506
1533
  throw new MemoryStoreParseError(err);
1507
1534
  }
@@ -1597,10 +1624,10 @@ var init_companion_render = __esm({
1597
1624
  });
1598
1625
 
1599
1626
  // src/daemon/companion-popup.ts
1600
- import { writeFileSync as writeFileSync16, readFileSync as readFileSync29, unlinkSync as unlinkSync4, existsSync as existsSync29 } from "fs";
1601
- import { tmpdir as tmpdir2 } from "os";
1602
- import { join as join26, resolve as resolve10 } from "path";
1603
- function wrapText(text, width) {
1627
+ import { writeFileSync as writeFileSync17, readFileSync as readFileSync30, unlinkSync as unlinkSync6, existsSync as existsSync29 } from "fs";
1628
+ import { tmpdir as tmpdir3 } from "os";
1629
+ import { join as join27, resolve as resolve10 } from "path";
1630
+ function wrapText2(text, width) {
1604
1631
  const words = text.split(" ");
1605
1632
  const lines = [];
1606
1633
  let current = "";
@@ -1630,16 +1657,16 @@ function showCommentaryPopupQueue(pages) {
1630
1657
  const defaultTitle = ` (${face}) `;
1631
1658
  let maxContentHeight = 0;
1632
1659
  for (let i = 0; i < pages.length; i++) {
1633
- const lines = wrapText(pages[i].text, INNER_WIDTH);
1660
+ const lines = wrapText2(pages[i].text, INNER_WIDTH2);
1634
1661
  const isLast = i === pages.length - 1;
1635
1662
  const hint = isLast ? "[0:ok 1:good 2:bad 3:whip]" : "[enter:next 0-3:rate]";
1636
- const hintPad = Math.max(0, Math.floor((INNER_WIDTH - hint.length) / 2));
1663
+ const hintPad = Math.max(0, Math.floor((INNER_WIDTH2 - hint.length) / 2));
1637
1664
  const hintLine = " ".repeat(hintPad + 2) + hint;
1638
1665
  const content = "\n\n" + lines.map((l) => ` ${l}`).join("\n") + "\n\n" + hintLine + "\n";
1639
1666
  const contentLineCount = content.split("\n").length - 1;
1640
1667
  const contentHeight = Math.max(contentLineCount + 2, 5);
1641
1668
  if (contentHeight > maxContentHeight) maxContentHeight = contentHeight;
1642
- writeFileSync16(`${POPUP_TMP_PREFIX}-${i}.txt`, content);
1669
+ writeFileSync17(`${POPUP_TMP_PREFIX}-${i}.txt`, content);
1643
1670
  }
1644
1671
  const whipAvailable = existsSync29(WHIP_ANIMATION_PATH);
1645
1672
  if (whipAvailable && maxContentHeight < WHIP_ANIMATION_ROWS + 2) {
@@ -1688,9 +1715,9 @@ if [ ! -f "$RESULT_FILE" ]; then
1688
1715
  printf 'neutral' > "$RESULT_FILE"
1689
1716
  fi
1690
1717
  `;
1691
- writeFileSync16(POPUP_SCRIPT, script, { mode: 493 });
1718
+ writeFileSync17(POPUP_SCRIPT, script, { mode: 493 });
1692
1719
  try {
1693
- unlinkSync4(POPUP_RESULT_PREFIX);
1720
+ unlinkSync6(POPUP_RESULT_PREFIX);
1694
1721
  } catch {
1695
1722
  }
1696
1723
  const clientsRaw = execSafe('tmux list-clients -F "#{client_name} #{client_width}"');
@@ -1700,27 +1727,27 @@ fi
1700
1727
  const client = line.slice(0, lastSpace);
1701
1728
  const clientWidth = parseInt(line.slice(lastSpace + 1), 10);
1702
1729
  if (!clientWidth) continue;
1703
- const x = Math.max(0, clientWidth - POPUP_WIDTH);
1730
+ const x = Math.max(0, clientWidth - POPUP_WIDTH2 - 1);
1704
1731
  const args2 = [
1705
1732
  `-c ${shellQuote(client)}`,
1706
1733
  "-E -b rounded",
1707
1734
  `-T ${shellQuote(initialTitle)}`,
1708
1735
  `-S "fg=${moodColor}"`,
1709
1736
  `-s "fg=${moodColor}"`,
1710
- `-x ${x} -y 0`,
1711
- `-w ${POPUP_WIDTH} -h ${maxContentHeight}`,
1737
+ `-x ${x} -y 2`,
1738
+ `-w ${POPUP_WIDTH2} -h ${maxContentHeight}`,
1712
1739
  shellQuote(POPUP_SCRIPT)
1713
1740
  ].join(" ");
1714
1741
  execSafe(`tmux display-popup ${args2}`);
1715
1742
  }
1716
1743
  let raw;
1717
1744
  try {
1718
- raw = readFileSync29(POPUP_RESULT_PREFIX, "utf8").trim();
1745
+ raw = readFileSync30(POPUP_RESULT_PREFIX, "utf8").trim();
1719
1746
  } catch {
1720
1747
  return null;
1721
1748
  } finally {
1722
1749
  try {
1723
- unlinkSync4(POPUP_RESULT_PREFIX);
1750
+ unlinkSync6(POPUP_RESULT_PREFIX);
1724
1751
  } catch {
1725
1752
  }
1726
1753
  }
@@ -1734,7 +1761,7 @@ fi
1734
1761
  }
1735
1762
  return null;
1736
1763
  }
1737
- var POPUP_WIDTH, INNER_WIDTH, POPUP_DURATION, POPUP_TMP_PREFIX, POPUP_SCRIPT, POPUP_RESULT_PREFIX, WHIP_ANIMATION_PATH, WHIP_ANIMATION_ROWS;
1764
+ var POPUP_WIDTH2, INNER_WIDTH2, POPUP_DURATION, POPUP_TMP_PREFIX, POPUP_SCRIPT, POPUP_RESULT_PREFIX, WHIP_ANIMATION_PATH, WHIP_ANIMATION_ROWS;
1738
1765
  var init_companion_popup = __esm({
1739
1766
  "src/daemon/companion-popup.ts"() {
1740
1767
  "use strict";
@@ -1743,12 +1770,12 @@ var init_companion_popup = __esm({
1743
1770
  init_config();
1744
1771
  init_exec();
1745
1772
  init_shell();
1746
- POPUP_WIDTH = 38;
1747
- INNER_WIDTH = POPUP_WIDTH - 6;
1773
+ POPUP_WIDTH2 = 38;
1774
+ INNER_WIDTH2 = POPUP_WIDTH2 - 6;
1748
1775
  POPUP_DURATION = 15;
1749
- POPUP_TMP_PREFIX = join26(tmpdir2(), "sisyphus-popup");
1750
- POPUP_SCRIPT = join26(tmpdir2(), "sisyphus-popup.sh");
1751
- POPUP_RESULT_PREFIX = join26(tmpdir2(), "sisyphus-popup-result");
1776
+ POPUP_TMP_PREFIX = join27(tmpdir3(), "sisyphus-popup");
1777
+ POPUP_SCRIPT = join27(tmpdir3(), "sisyphus-popup.sh");
1778
+ POPUP_RESULT_PREFIX = join27(tmpdir3(), "sisyphus-popup-result");
1752
1779
  WHIP_ANIMATION_PATH = resolve10(import.meta.dirname, "../templates/whip-animation.sh");
1753
1780
  WHIP_ANIMATION_ROWS = 12;
1754
1781
  }
@@ -1776,9 +1803,9 @@ __export(tmux_exports, {
1776
1803
  windowExists: () => windowExists
1777
1804
  });
1778
1805
  import { execSync as execSync17 } from "child_process";
1779
- import { join as join27 } from "path";
1780
- import { readFileSync as readFileSync30, writeFileSync as writeFileSync17, mkdtempSync, rmSync as rmSync7, cpSync as cpSync3, existsSync as existsSync30, mkdirSync as mkdirSync14 } from "fs";
1781
- import { tmpdir as tmpdir3 } from "os";
1806
+ import { join as join28 } from "path";
1807
+ import { readFileSync as readFileSync31, writeFileSync as writeFileSync18, mkdtempSync, rmSync as rmSync7, cpSync as cpSync3, existsSync as existsSync30, mkdirSync as mkdirSync14 } from "fs";
1808
+ import { tmpdir as tmpdir4 } from "os";
1782
1809
  function getWindowId() {
1783
1810
  const pane = process.env["TMUX_PANE"];
1784
1811
  if (pane) {
@@ -1829,8 +1856,8 @@ function registerDashboardWindow(cwd) {
1829
1856
  }
1830
1857
  }
1831
1858
  function setupCompanionPlugin() {
1832
- const srcDir = join27(import.meta.dirname, "templates", "companion-plugin");
1833
- const destDir = join27(globalDir(), "companion-plugin");
1859
+ const srcDir = join28(import.meta.dirname, "templates", "companion-plugin");
1860
+ const destDir = join28(globalDir(), "companion-plugin");
1834
1861
  if (!existsSync30(destDir)) mkdirSync14(destDir, { recursive: true });
1835
1862
  cpSync3(srcDir, destDir, { recursive: true });
1836
1863
  return destDir;
@@ -1858,18 +1885,18 @@ function openCompanionPane(cwd) {
1858
1885
  return;
1859
1886
  }
1860
1887
  const pluginDir = setupCompanionPlugin();
1861
- const templatePath2 = join27(import.meta.dirname, "templates", "dashboard-claude.md");
1888
+ const templatePath2 = join28(import.meta.dirname, "templates", "dashboard-claude.md");
1862
1889
  let template;
1863
1890
  try {
1864
- template = readFileSync30(templatePath2, "utf-8");
1891
+ template = readFileSync31(templatePath2, "utf-8");
1865
1892
  } catch {
1866
1893
  template = `You are a Sisyphus dashboard companion. Help the user manage multi-agent sessions.
1867
1894
  Project: ${cwd}
1868
1895
  Run \`sis session inspect list\` and \`sis session inspect status\` to see current state.`;
1869
1896
  }
1870
1897
  const rendered = template.replace(/\{\{CWD\}\}/g, cwd);
1871
- const promptPath = join27(globalDir(), "dashboard-companion-prompt.md");
1872
- writeFileSync17(promptPath, rendered, "utf-8");
1898
+ const promptPath = join28(globalDir(), "dashboard-companion-prompt.md");
1899
+ writeFileSync18(promptPath, rendered, "utf-8");
1873
1900
  const pathEnv = augmentedPath();
1874
1901
  const claudeCmd = `SISYPHUS_COMPANION_CWD=${shellQuote(cwd)} PATH=${shellQuote(pathEnv)} claude --dangerously-skip-permissions --plugin-dir ${shellQuote(pluginDir)} --append-system-prompt "$(cat ${shellQuote(promptPath)})"`;
1875
1902
  const result = exec2(
@@ -1884,12 +1911,12 @@ function switchToSession(sessionName) {
1884
1911
  execSafe(`tmux switch-client -t ${shellQuote(sessionName)}`);
1885
1912
  }
1886
1913
  function editInPopup(cwd, editor, opts) {
1887
- const tmpDir = mkdtempSync(join27(tmpdir3(), "sisyphus-"));
1888
- const filePath = join27(tmpDir, "input.md");
1914
+ const tmpDir = mkdtempSync(join28(tmpdir4(), "sisyphus-"));
1915
+ const filePath = join28(tmpDir, "input.md");
1889
1916
  try {
1890
- writeFileSync17(filePath, opts?.content ? opts.content : "", "utf-8");
1917
+ writeFileSync18(filePath, opts?.content ? opts.content : "", "utf-8");
1891
1918
  openEditorPopup(cwd, editor, filePath, opts?.size);
1892
- const result = readFileSync30(filePath, "utf-8").trim();
1919
+ const result = readFileSync31(filePath, "utf-8").trim();
1893
1920
  return result || null;
1894
1921
  } finally {
1895
1922
  rmSync7(tmpDir, { recursive: true, force: true });
@@ -1897,8 +1924,8 @@ function editInPopup(cwd, editor, opts) {
1897
1924
  }
1898
1925
  function promptInPopup(prompt, opts) {
1899
1926
  const { w = "50%", h = "3" } = opts ?? {};
1900
- const tmpDir = mkdtempSync(join27(tmpdir3(), "sisyphus-"));
1901
- const outFile = join27(tmpDir, "result");
1927
+ const tmpDir = mkdtempSync(join28(tmpdir4(), "sisyphus-"));
1928
+ const outFile = join28(tmpDir, "result");
1902
1929
  try {
1903
1930
  const script = `printf ${shellQuote(prompt + " ")} && read -r line && printf '%s' "$line" > ${shellQuote(outFile)}`;
1904
1931
  execSync17(
@@ -1906,7 +1933,7 @@ function promptInPopup(prompt, opts) {
1906
1933
  { stdio: "inherit", env: EXEC_ENV }
1907
1934
  );
1908
1935
  if (!existsSync30(outFile)) return null;
1909
- const result = readFileSync30(outFile, "utf-8").trim();
1936
+ const result = readFileSync31(outFile, "utf-8").trim();
1910
1937
  return result || null;
1911
1938
  } finally {
1912
1939
  rmSync7(tmpDir, { recursive: true, force: true });
@@ -1937,10 +1964,10 @@ function openClaudeResumePopup(cwd, claudeSessionId, resumeEnv, resumeArgs) {
1937
1964
  { stdio: "inherit", env: EXEC_ENV }
1938
1965
  );
1939
1966
  }
1940
- function openClaudeResumeSession(cwd, sessionId, claudeSessionId, sessionLabel, resumeEnv, resumeArgs, cycleNum, mode) {
1941
- const sessionName = tmuxSessionName(cwd, sessionLabel);
1967
+ function openClaudeResumeSession(cwd, sessionId, claudeSessionId, sessionLabel2, resumeEnv, resumeArgs, cycleNum, mode) {
1968
+ const sessionName = tmuxSessionName(cwd, sessionLabel2);
1942
1969
  const cycleLabel = cycleNum != null ? `c${cycleNum}` : "";
1943
- const paneTitle = cycleLabel ? `ssph:orch ${sessionLabel} ${cycleLabel}` : `ssph:orch ${sessionLabel}`;
1970
+ const paneTitle = cycleLabel ? `ssph:orch ${sessionLabel2} ${cycleLabel}` : `ssph:orch ${sessionLabel2}`;
1944
1971
  const existing = execSafe('tmux list-sessions -F "#{session_id}|#{session_name}"');
1945
1972
  const existingLine = existing?.split("\n").find((line) => line.slice(line.indexOf("|") + 1) === sessionName);
1946
1973
  if (existingLine) {
@@ -1948,7 +1975,7 @@ function openClaudeResumeSession(cwd, sessionId, claudeSessionId, sessionLabel,
1948
1975
  execSafe(`tmux set-option -t ${shellQuote(existingSessId)} @sisyphus_cwd ${shellQuote(cwd.replace(/\/+$/, ""))}`);
1949
1976
  execSafe(`tmux set-option -t ${shellQuote(existingSessId)} @sisyphus_session_id ${shellQuote(sessionId)}`);
1950
1977
  const firstPaneId2 = execSafe(`tmux list-panes -t ${shellQuote(existingSessId)} -F '#{pane_id}'`)?.split("\n")[0];
1951
- if (firstPaneId2) applyOrchestratorPaneStyle(firstPaneId2, paneTitle, sessionLabel, cycleLabel, mode);
1978
+ if (firstPaneId2) applyOrchestratorPaneStyle(firstPaneId2, paneTitle, sessionLabel2, cycleLabel, mode);
1952
1979
  return sessionName;
1953
1980
  }
1954
1981
  const pathEnv = augmentedPath();
@@ -1964,14 +1991,14 @@ function openClaudeResumeSession(cwd, sessionId, claudeSessionId, sessionLabel,
1964
1991
  execSafe(`tmux set -w -t ${shellQuote(newSessId + ":")} pane-border-status top`);
1965
1992
  execSafe(`tmux set -w -t ${shellQuote(newSessId + ":")} allow-rename off`);
1966
1993
  execSafe(`tmux set -w -t ${shellQuote(newSessId + ":")} automatic-rename off`);
1967
- if (firstPaneId) applyOrchestratorPaneStyle(firstPaneId, paneTitle, sessionLabel, cycleLabel, mode);
1994
+ if (firstPaneId) applyOrchestratorPaneStyle(firstPaneId, paneTitle, sessionLabel2, cycleLabel, mode);
1968
1995
  return sessionName;
1969
1996
  }
1970
- function applyOrchestratorPaneStyle(paneId, title, sessionLabel, cycleLabel, mode) {
1997
+ function applyOrchestratorPaneStyle(paneId, title, sessionLabel2, cycleLabel, mode) {
1971
1998
  const color = "yellow";
1972
1999
  execSafe(`tmux select-pane -t ${shellQuote(paneId)} -T ${shellQuote(title)}`);
1973
2000
  execSafe(`tmux set -p -t ${shellQuote(paneId)} @pane_role ${shellQuote("orch")}`);
1974
- execSafe(`tmux set -p -t ${shellQuote(paneId)} @pane_session ${shellQuote(sessionLabel)}`);
2001
+ execSafe(`tmux set -p -t ${shellQuote(paneId)} @pane_session ${shellQuote(sessionLabel2)}`);
1975
2002
  if (cycleLabel) execSafe(`tmux set -p -t ${shellQuote(paneId)} @pane_cycle ${shellQuote(cycleLabel)}`);
1976
2003
  if (mode) execSafe(`tmux set -p -t ${shellQuote(paneId)} @pane_mode ${shellQuote(mode)}`);
1977
2004
  const gitBranch = `#(cd #{pane_current_path} && git branch --show-current 2>/dev/null)`;
@@ -2024,7 +2051,7 @@ __export(creds_exports, {
2024
2051
  readTailscaleEnv: () => readTailscaleEnv,
2025
2052
  writeTailscaleEnv: () => writeTailscaleEnv
2026
2053
  });
2027
- import { chmodSync as chmodSync3, existsSync as existsSync31, mkdirSync as mkdirSync16, readFileSync as readFileSync32 } from "fs";
2054
+ import { chmodSync as chmodSync3, existsSync as existsSync31, mkdirSync as mkdirSync16, readFileSync as readFileSync33 } from "fs";
2028
2055
  import { createInterface as createInterface4 } from "readline";
2029
2056
  function isValidProvider(value) {
2030
2057
  return PROVIDERS.includes(value);
@@ -2058,7 +2085,7 @@ function serializeEnvFile(values) {
2058
2085
  }
2059
2086
  function readEnvFile(path) {
2060
2087
  if (!existsSync31(path)) return null;
2061
- return parseEnvFile(readFileSync32(path, "utf-8"));
2088
+ return parseEnvFile(readFileSync33(path, "utf-8"));
2062
2089
  }
2063
2090
  function writeEnvFile(path, values) {
2064
2091
  ensureDeployDir();
@@ -2188,12 +2215,12 @@ var init_pricing = __esm({
2188
2215
  });
2189
2216
 
2190
2217
  // src/cli/deploy/runtime.ts
2191
- import { existsSync as existsSync32, readFileSync as readFileSync33, unlinkSync as unlinkSync5 } from "fs";
2218
+ import { existsSync as existsSync32, readFileSync as readFileSync34, unlinkSync as unlinkSync7 } from "fs";
2192
2219
  function readRuntimeState(provider) {
2193
2220
  const path = deployRuntimePath(provider);
2194
2221
  if (!existsSync32(path)) return null;
2195
2222
  try {
2196
- return JSON.parse(readFileSync33(path, "utf-8"));
2223
+ return JSON.parse(readFileSync34(path, "utf-8"));
2197
2224
  } catch {
2198
2225
  return null;
2199
2226
  }
@@ -2203,7 +2230,7 @@ function writeRuntimeState(provider, state) {
2203
2230
  }
2204
2231
  function clearRuntimeState(provider) {
2205
2232
  const path = deployRuntimePath(provider);
2206
- if (existsSync32(path)) unlinkSync5(path);
2233
+ if (existsSync32(path)) unlinkSync7(path);
2207
2234
  }
2208
2235
  var init_runtime = __esm({
2209
2236
  "src/cli/deploy/runtime.ts"() {
@@ -2447,7 +2474,7 @@ var init_tailscale = __esm({
2447
2474
 
2448
2475
  // src/cli/deploy/runner.ts
2449
2476
  import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
2450
- import { copyFileSync as copyFileSync3, existsSync as existsSync34, mkdirSync as mkdirSync17, readFileSync as readFileSync34 } from "fs";
2477
+ import { copyFileSync as copyFileSync3, existsSync as existsSync34, mkdirSync as mkdirSync17, readFileSync as readFileSync35 } from "fs";
2451
2478
  function runTerraform(provider, args2, extraEnv) {
2452
2479
  ensureProviderStateDir(provider);
2453
2480
  ensureTerraformInstalled();
@@ -2485,7 +2512,7 @@ function readSshPubkey(path) {
2485
2512
  or pass --ssh-key <path>.`
2486
2513
  );
2487
2514
  }
2488
- return readFileSync34(path, "utf-8").trim();
2515
+ return readFileSync35(path, "utf-8").trim();
2489
2516
  }
2490
2517
  function readOutputs(provider) {
2491
2518
  const result = spawnSync5("terraform", ["output", "-json", `-state=${deployStatePath(provider)}`], {
@@ -2800,7 +2827,7 @@ var init_grove = __esm({
2800
2827
  // src/cli/cloud/repo.ts
2801
2828
  import { spawnSync as spawnSync7 } from "child_process";
2802
2829
  import { existsSync as existsSync35 } from "fs";
2803
- import { basename as basename8, join as join30 } from "path";
2830
+ import { basename as basename8, join as join31 } from "path";
2804
2831
  function captureGit(args2, cwd) {
2805
2832
  const result = spawnSync7("git", args2, {
2806
2833
  encoding: "utf-8",
@@ -2840,10 +2867,10 @@ function buildRsyncArgs(localDir, remoteTarget) {
2840
2867
  ];
2841
2868
  }
2842
2869
  function detectPackageManager(toplevel) {
2843
- if (existsSync35(join30(toplevel, "pnpm-lock.yaml"))) return "pnpm";
2844
- if (existsSync35(join30(toplevel, "bun.lockb"))) return "bun";
2845
- if (existsSync35(join30(toplevel, "yarn.lock"))) return "yarn";
2846
- if (existsSync35(join30(toplevel, "package-lock.json"))) return "npm";
2870
+ if (existsSync35(join31(toplevel, "pnpm-lock.yaml"))) return "pnpm";
2871
+ if (existsSync35(join31(toplevel, "bun.lockb"))) return "bun";
2872
+ if (existsSync35(join31(toplevel, "yarn.lock"))) return "yarn";
2873
+ if (existsSync35(join31(toplevel, "package-lock.json"))) return "npm";
2847
2874
  return null;
2848
2875
  }
2849
2876
  function packageManagerInstallCmd(pm) {
@@ -3126,7 +3153,7 @@ var init_protocol = __esm({
3126
3153
  });
3127
3154
 
3128
3155
  // src/daemon/spawn-helpers.ts
3129
- import { writeFileSync as writeFileSync20, existsSync as existsSync38 } from "fs";
3156
+ import { writeFileSync as writeFileSync21, existsSync as existsSync38 } from "fs";
3130
3157
  import { resolve as resolve13 } from "path";
3131
3158
  var init_spawn_helpers = __esm({
3132
3159
  "src/daemon/spawn-helpers.ts"() {
@@ -3308,7 +3335,7 @@ var init_help_inject = __esm({
3308
3335
  });
3309
3336
 
3310
3337
  // src/shared/digest-verbs.ts
3311
- import { existsSync as existsSync39, readFileSync as readFileSync37 } from "fs";
3338
+ import { existsSync as existsSync39, readFileSync as readFileSync38 } from "fs";
3312
3339
  var init_digest_verbs = __esm({
3313
3340
  "src/shared/digest-verbs.ts"() {
3314
3341
  "use strict";
@@ -3324,8 +3351,8 @@ var init_colors = __esm({
3324
3351
  });
3325
3352
 
3326
3353
  // src/daemon/orchestrator-modes.ts
3327
- import { existsSync as existsSync40, readdirSync as readdirSync10, readFileSync as readFileSync38 } from "fs";
3328
- import { resolve as resolve14, join as join32 } from "path";
3354
+ import { existsSync as existsSync40, readdirSync as readdirSync10, readFileSync as readFileSync39 } from "fs";
3355
+ import { resolve as resolve14, join as join33 } from "path";
3329
3356
  import { homedir as homedir14 } from "os";
3330
3357
  var init_orchestrator_modes = __esm({
3331
3358
  "src/daemon/orchestrator-modes.ts"() {
@@ -3336,8 +3363,8 @@ var init_orchestrator_modes = __esm({
3336
3363
  });
3337
3364
 
3338
3365
  // src/daemon/lib/render-plugin.ts
3339
- import { readdirSync as readdirSync11, readFileSync as readFileSync39, writeFileSync as writeFileSync21, mkdirSync as mkdirSync18, copyFileSync as copyFileSync4, rmSync as rmSync8, existsSync as existsSync41 } from "fs";
3340
- import { join as join33 } from "path";
3366
+ import { readdirSync as readdirSync11, readFileSync as readFileSync40, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18, copyFileSync as copyFileSync4, rmSync as rmSync8, existsSync as existsSync41 } from "fs";
3367
+ import { join as join34 } from "path";
3341
3368
  var init_render_plugin = __esm({
3342
3369
  "src/daemon/lib/render-plugin.ts"() {
3343
3370
  "use strict";
@@ -3418,10 +3445,10 @@ var init_orphan_sweep = __esm({
3418
3445
  });
3419
3446
 
3420
3447
  // src/daemon/agent.ts
3421
- import { readFileSync as readFileSync40, writeFileSync as writeFileSync22, mkdirSync as mkdirSync19, readdirSync as readdirSync12, existsSync as existsSync43, unlinkSync as unlinkSync6 } from "fs";
3448
+ import { readFileSync as readFileSync41, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, readdirSync as readdirSync12, existsSync as existsSync43, unlinkSync as unlinkSync8 } from "fs";
3422
3449
  import { execSync as execSync21 } from "child_process";
3423
3450
  import { randomUUID as randomUUID5 } from "crypto";
3424
- import { resolve as resolve15, relative as relative2, dirname as dirname13, join as join34 } from "path";
3451
+ import { resolve as resolve15, relative as relative2, dirname as dirname13, join as join35 } from "path";
3425
3452
  var init_agent = __esm({
3426
3453
  "src/daemon/agent.ts"() {
3427
3454
  "use strict";
@@ -3499,25 +3526,11 @@ var init_pane_monitor = __esm({
3499
3526
  }
3500
3527
  });
3501
3528
 
3502
- // src/daemon/mode-notify.ts
3503
- import { existsSync as existsSync44 } from "fs";
3504
- import { ulid as ulid3 } from "ulid";
3505
- var init_mode_notify = __esm({
3506
- "src/daemon/mode-notify.ts"() {
3507
- "use strict";
3508
- init_ask_store();
3509
- init_state();
3510
- init_orchestrator_modes();
3511
- init_paths();
3512
- init_types();
3513
- }
3514
- });
3515
-
3516
3529
  // src/daemon/orchestrator.ts
3517
- import { existsSync as existsSync45, readdirSync as readdirSync13, readFileSync as readFileSync41, writeFileSync as writeFileSync23 } from "fs";
3530
+ import { existsSync as existsSync44, readdirSync as readdirSync13, readFileSync as readFileSync42, writeFileSync as writeFileSync24 } from "fs";
3518
3531
  import { execSync as execSync22 } from "child_process";
3519
3532
  import { randomUUID as randomUUID6 } from "crypto";
3520
- import { resolve as resolve16, join as join35, relative as relative3 } from "path";
3533
+ import { resolve as resolve16, join as join36, relative as relative3 } from "path";
3521
3534
  var init_orchestrator = __esm({
3522
3535
  "src/daemon/orchestrator.ts"() {
3523
3536
  "use strict";
@@ -3539,13 +3552,12 @@ var init_orchestrator = __esm({
3539
3552
  init_tmux2();
3540
3553
  init_pane_registry();
3541
3554
  init_pane_monitor();
3542
- init_mode_notify();
3543
3555
  init_plugins();
3544
3556
  }
3545
3557
  });
3546
3558
 
3547
3559
  // src/daemon/status-dots.ts
3548
- import { readFileSync as readFileSync42 } from "fs";
3560
+ import { readFileSync as readFileSync43 } from "fs";
3549
3561
  var COMPLETED_TTL_MS;
3550
3562
  var init_status_dots = __esm({
3551
3563
  "src/daemon/status-dots.ts"() {
@@ -3557,9 +3569,17 @@ var init_status_dots = __esm({
3557
3569
  }
3558
3570
  });
3559
3571
 
3572
+ // src/daemon/mode-transition.ts
3573
+ var init_mode_transition = __esm({
3574
+ "src/daemon/mode-transition.ts"() {
3575
+ "use strict";
3576
+ init_orchestrator_modes();
3577
+ }
3578
+ });
3579
+
3560
3580
  // src/daemon/uploader.ts
3561
3581
  import { rm } from "fs/promises";
3562
- import { tmpdir as tmpdir4 } from "os";
3582
+ import { tmpdir as tmpdir5 } from "os";
3563
3583
  var init_uploader = __esm({
3564
3584
  "src/daemon/uploader.ts"() {
3565
3585
  "use strict";
@@ -3598,7 +3618,7 @@ var init_format = __esm({
3598
3618
 
3599
3619
  // src/daemon/session-manager.ts
3600
3620
  import { v4 as uuidv4 } from "uuid";
3601
- import { existsSync as existsSync46, readFileSync as readFileSync43, readdirSync as readdirSync14, rmSync as rmSync9 } from "fs";
3621
+ import { existsSync as existsSync45, readFileSync as readFileSync44, readdirSync as readdirSync14, rmSync as rmSync9 } from "fs";
3602
3622
  var init_session_manager = __esm({
3603
3623
  "src/daemon/session-manager.ts"() {
3604
3624
  "use strict";
@@ -3621,6 +3641,7 @@ var init_session_manager = __esm({
3621
3641
  init_companion_render();
3622
3642
  init_companion_commentary();
3623
3643
  init_companion_popup();
3644
+ init_mode_transition();
3624
3645
  init_history();
3625
3646
  init_uploader();
3626
3647
  init_upload();
@@ -3635,7 +3656,7 @@ var init_session_manager = __esm({
3635
3656
  // src/daemon/transcript-digest.ts
3636
3657
  import { openSync, fstatSync, readSync, closeSync } from "fs";
3637
3658
  import { homedir as homedir15 } from "os";
3638
- import { join as join36 } from "path";
3659
+ import { join as join37 } from "path";
3639
3660
  var BYTE_CAP, TAIL_BYTES;
3640
3661
  var init_transcript_digest = __esm({
3641
3662
  "src/daemon/transcript-digest.ts"() {
@@ -3649,7 +3670,7 @@ var init_transcript_digest = __esm({
3649
3670
  import {
3650
3671
  closeSync as closeSync2,
3651
3672
  constants,
3652
- existsSync as existsSync47,
3673
+ existsSync as existsSync46,
3653
3674
  fstatSync as fstatSync2,
3654
3675
  lstatSync as lstatSync2,
3655
3676
  openSync as openSync2,
@@ -3685,17 +3706,17 @@ var init_ask_visual = __esm({
3685
3706
 
3686
3707
  // src/daemon/server.ts
3687
3708
  import { createServer } from "net";
3688
- import { unlinkSync as unlinkSync7, existsSync as existsSync48, writeFileSync as writeFileSync24, readFileSync as readFileSync45, mkdirSync as mkdirSync20, readdirSync as readdirSync15, rmSync as rmSync11, chmodSync as chmodSync4 } from "fs";
3689
- import { join as join37, basename as basename10, dirname as dirname15 } from "path";
3709
+ import { unlinkSync as unlinkSync9, existsSync as existsSync47, writeFileSync as writeFileSync25, readFileSync as readFileSync46, mkdirSync as mkdirSync20, readdirSync as readdirSync15, rmSync as rmSync11, chmodSync as chmodSync4 } from "fs";
3710
+ import { join as join38, basename as basename10, dirname as dirname15 } from "path";
3690
3711
  import { scanInbox } from "@crouton-kit/humanloop";
3691
3712
  function registryPath() {
3692
- return join37(globalDir(), "session-registry.json");
3713
+ return join38(globalDir(), "session-registry.json");
3693
3714
  }
3694
3715
  function loadSessionRegistry() {
3695
3716
  const p = registryPath();
3696
- if (!existsSync48(p)) return {};
3717
+ if (!existsSync47(p)) return {};
3697
3718
  try {
3698
- return JSON.parse(readFileSync45(p, "utf-8"));
3719
+ return JSON.parse(readFileSync46(p, "utf-8"));
3699
3720
  } catch (err) {
3700
3721
  console.warn("[sisyphus] Failed to parse session registry:", err instanceof Error ? err.message : err);
3701
3722
  return {};
@@ -3726,8 +3747,8 @@ var init_server = __esm({
3726
3747
 
3727
3748
  // src/cli/index.ts
3728
3749
  import { Command } from "commander";
3729
- import { existsSync as existsSync50, mkdirSync as mkdirSync21, readFileSync as readFileSync46 } from "fs";
3730
- import { dirname as dirname16, join as join38 } from "path";
3750
+ import { existsSync as existsSync49, mkdirSync as mkdirSync21, readFileSync as readFileSync47 } from "fs";
3751
+ import { dirname as dirname16, join as join39 } from "path";
3731
3752
  import { fileURLToPath as fileURLToPath6 } from "url";
3732
3753
 
3733
3754
  // src/cli/commands/start.ts
@@ -5833,6 +5854,10 @@ function assertTmux() {
5833
5854
  throw new Error("Not running inside a tmux pane. Sisyphus requires tmux.");
5834
5855
  }
5835
5856
  }
5857
+ function getTmuxSession() {
5858
+ assertTmux();
5859
+ return execSync4('tmux display-message -p "#{session_name}"', { encoding: "utf8" }).trim();
5860
+ }
5836
5861
  function getTmuxSessionInfo() {
5837
5862
  assertTmux();
5838
5863
  const out = execSync4('tmux display-message -p "#{session_id}|#{session_name}"', { encoding: "utf8" }).trim();
@@ -5849,6 +5874,27 @@ function getCurrentTmuxSessionHome(sessionId) {
5849
5874
  return "";
5850
5875
  }
5851
5876
  }
5877
+ function findHomeSession(cwd) {
5878
+ const normalizedCwd = cwd.replace(/\/+$/, "");
5879
+ let output;
5880
+ try {
5881
+ output = execSync4('tmux list-sessions -F "#{session_id}|#{session_name}"', {
5882
+ encoding: "utf-8",
5883
+ stdio: ["pipe", "pipe", "pipe"]
5884
+ }).trim();
5885
+ } catch {
5886
+ return null;
5887
+ }
5888
+ for (const line of output.split("\n").filter(Boolean)) {
5889
+ const pipeIdx = line.indexOf("|");
5890
+ if (pipeIdx < 0) continue;
5891
+ const sessId = line.slice(0, pipeIdx);
5892
+ const name = line.slice(pipeIdx + 1);
5893
+ if (name.startsWith("ssyph_")) continue;
5894
+ if (getCurrentTmuxSessionHome(sessId) === normalizedCwd) return sessId;
5895
+ }
5896
+ return null;
5897
+ }
5852
5898
 
5853
5899
  // src/cli/commands/start.ts
5854
5900
  init_shell();
@@ -5910,12 +5956,30 @@ function registerStart(parent, root) {
5910
5956
  if (root) buildStartCommand(root, true);
5911
5957
  }
5912
5958
  function buildStartCommand(target, hidden) {
5913
- target.command("start", { hidden }).description("Start a new sisyphus session").argument("[task]", "Task description for the orchestrator (omit when using --stdin)").option("--context <context>", "Background context for the orchestrator").option("--name <name>", "Human-readable name for the session").option("--effort <tier>", "Pipeline effort tier (low|medium|high|xhigh)").option("--stdin", "Read the task description from stdin (avoids shell escaping for long prompts)").option("--context-stdin", "Read the context from stdin (mutually exclusive with --stdin)").option("--accept-cwd-mismatch", "Proceed even when invocation cwd differs from the current tmux session's home (linking will be inconsistent)").addHelpText("after", `
5959
+ target.command("start", { hidden }).description("Start a new sisyphus session").argument("[task]", "Task description for the orchestrator (omit when using --stdin)").option("-c, --context <context>", "Background context for the orchestrator").option("--name <name>", "Human-readable name for the session").option("--effort <tier>", "Pipeline effort tier (low|medium|high|xhigh)").option("--stdin", "Read the task description from stdin (avoids shell escaping for long prompts)").option("--context-stdin", "Read the context from stdin (mutually exclusive with --stdin)").option("--accept-cwd-mismatch", "Proceed even when invocation cwd differs from the current tmux session's home (linking will be inconsistent)").addHelpText("after", `
5914
5960
  session lifecycle start: launch a new orchestrated sisyphus session.
5915
5961
 
5962
+ Writing the handoff
5963
+ task the goal \u2014 what to build or fix and what "done" looks like. This is the
5964
+ persistent objective the orchestrator re-reads every cycle; keep it focused.
5965
+ -c/--context background that informs the work but isn't the goal itself: relevant file
5966
+ paths, constraints, specs, adjacent concerns, prior findings. Rendered
5967
+ apart from the task so the orchestrator references it without conflating it.
5968
+ Keep context factual, not diagnostic \u2014 point at files, areas, and constraints; don't
5969
+ speculate on root causes or fixes, which biases the orchestrator down the wrong path.
5970
+
5971
+ Example
5972
+ sis start "Fix the JWT refresh bug \u2014 app shows a blank screen on token expiry instead of redirecting to login" -c "Auth system lives in src/auth/. Key files: interceptor.ts (HTTP interceptor), token-store.ts (token persistence), refresh.ts (refresh flow). Tests in src/auth/__tests__/. Don't break the logout flow."
5973
+
5974
+ Long task or context? Pipe via stdin to avoid shell escaping:
5975
+ cat task.md | sis start --stdin -c "short context here"
5976
+ cat ctx.md | sis start "short task" --context-stdin
5977
+ The same --stdin / --context-stdin pattern exists on \`agent spawn\`, \`orch message\`,
5978
+ \`orch tell\`, \`session resume\`, and agent-side \`agent submit\` / \`agent report\` / \`orch yield\`.
5979
+
5916
5980
  Input
5917
5981
  [task] optional positional \u2014 task description string; omit when using --stdin or pass \`-\` to read stdin
5918
- --context <context> optional \u2014 background context injected alongside the task; also readable via --context-stdin
5982
+ -c, --context <context> optional \u2014 background context injected alongside the task; also readable via --context-stdin
5919
5983
  --name <name> optional \u2014 human-readable label for the session
5920
5984
  --effort <tier> optional \u2014 pipeline effort tier: low | medium | high | xhigh
5921
5985
  --stdin optional \u2014 read task from stdin instead of positional; mutually exclusive with --context-stdin
@@ -6102,6 +6166,27 @@ function openDashboardWindow(tmuxSession, cwd) {
6102
6166
  }
6103
6167
  } catch {
6104
6168
  }
6169
+ try {
6170
+ const windows = execSync6(
6171
+ `tmux list-windows -t ${shellQuote(tmuxSession)} -F "#{window_id} #{window_name}"`,
6172
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
6173
+ ).trim();
6174
+ for (const line of windows.split("\n").filter(Boolean)) {
6175
+ const spaceIdx = line.indexOf(" ");
6176
+ if (spaceIdx < 0) continue;
6177
+ const wid = line.slice(0, spaceIdx);
6178
+ const wname = line.slice(spaceIdx + 1);
6179
+ if (wname === "sisyphus-dashboard") {
6180
+ execSync6(
6181
+ `tmux set-option -t ${shellQuote(tmuxSession)} @sisyphus_dashboard ${shellQuote(wid)}`,
6182
+ { stdio: "pipe" }
6183
+ );
6184
+ execSync6(`tmux select-window -t ${shellQuote(wid)}`, { stdio: "pipe" });
6185
+ return false;
6186
+ }
6187
+ }
6188
+ } catch {
6189
+ }
6105
6190
  const tuiPath = join6(import.meta.dirname, "tui.js");
6106
6191
  const windowId = execSync6(
6107
6192
  `tmux new-window -t ${shellQuote(tmuxSession + ":")} -n "sisyphus-dashboard" -c ${shellQuote(cwd)} -P -F "#{window_id}"`,
@@ -6570,8 +6655,8 @@ Exit codes: 0 ok | 2 usage (missing content or --session) | 3 not_found.`
6570
6655
  }
6571
6656
 
6572
6657
  // src/cli/commands/ask.ts
6573
- import { existsSync as existsSync12, readFileSync as readFileSync12, unlinkSync as unlinkSync3, watchFile, unwatchFile } from "fs";
6574
- import { basename as basename4, join as join13, resolve as resolve5 } from "path";
6658
+ import { existsSync as existsSync12, readFileSync as readFileSync13, unlinkSync as unlinkSync5, watchFile, unwatchFile } from "fs";
6659
+ import { basename as basename4, join as join14, resolve as resolve5 } from "path";
6575
6660
  import { ulid } from "ulid";
6576
6661
 
6577
6662
  // src/shared/ask-schema.ts
@@ -6634,6 +6719,125 @@ init_state();
6634
6719
  init_types();
6635
6720
  init_exec();
6636
6721
  init_shell();
6722
+ init_platform();
6723
+
6724
+ // src/cli/commands/ask-popup.ts
6725
+ init_exec();
6726
+ init_shell();
6727
+ import { writeFileSync as writeFileSync8, readFileSync as readFileSync12, unlinkSync as unlinkSync4 } from "fs";
6728
+ import { tmpdir } from "os";
6729
+ import { join as join13 } from "path";
6730
+ var POPUP_WIDTH = 46;
6731
+ var INNER_WIDTH = POPUP_WIDTH - 6;
6732
+ var POPUP_TIMEOUT = 10;
6733
+ var ACCENT_COLOR = "colour214";
6734
+ function wrapText(text, width) {
6735
+ const words = text.split(/\s+/).filter(Boolean);
6736
+ const lines = [];
6737
+ let current = "";
6738
+ for (const word of words) {
6739
+ if (current && current.length + 1 + word.length > width) {
6740
+ lines.push(current);
6741
+ current = word;
6742
+ } else {
6743
+ current = current ? `${current} ${word}` : word;
6744
+ }
6745
+ }
6746
+ if (current) lines.push(current);
6747
+ return lines.length > 0 ? lines : [""];
6748
+ }
6749
+ function hasAttachedClient() {
6750
+ const out = execSafe('tmux list-clients -F "#{client_name}"');
6751
+ return !!out && out.split("\n").filter(Boolean).length > 0;
6752
+ }
6753
+ function showAskTriagePopup(opts) {
6754
+ const scriptPath2 = join13(tmpdir(), `sisyphus-ask-${opts.askId}.sh`);
6755
+ const resultPath = join13(tmpdir(), `sisyphus-ask-${opts.askId}.result`);
6756
+ try {
6757
+ const safeTitle = opts.title.trim().length > 0 ? opts.title : "Question pending";
6758
+ const titleLines = wrapText(safeTitle, INNER_WIDTH);
6759
+ const bodyLines = [
6760
+ "",
6761
+ ...titleLines.map((l) => ` ${l}`),
6762
+ "",
6763
+ " [1] Dismiss",
6764
+ " [2] Open in dashboard",
6765
+ " [3] Open in agent",
6766
+ "",
6767
+ ` (auto-dismiss in ${POPUP_TIMEOUT}s)`,
6768
+ ""
6769
+ ];
6770
+ const content = bodyLines.join("\n") + "\n";
6771
+ const height = bodyLines.length + 2;
6772
+ const script = `#!/bin/sh
6773
+ printf '\\033[?25l'
6774
+ stty -echo 2>/dev/null
6775
+ RESULT_FILE=${shellQuote(resultPath)}
6776
+ printf '\\033[2J\\033[H'
6777
+ cat <<'__SISYPHUS_ASK_EOF__'
6778
+ ${content}__SISYPHUS_ASK_EOF__
6779
+ while IFS= read -r -n1 -t ${POPUP_TIMEOUT} k; do
6780
+ case "$k" in
6781
+ 1|d|D|q|Q|'') printf 'dismiss' > "$RESULT_FILE"; break ;;
6782
+ 2|b|B) printf 'dashboard' > "$RESULT_FILE"; break ;;
6783
+ 3|a|A) printf 'agent' > "$RESULT_FILE"; break ;;
6784
+ esac
6785
+ done
6786
+ if [ ! -f "$RESULT_FILE" ]; then printf 'dismiss' > "$RESULT_FILE"; fi
6787
+ `;
6788
+ writeFileSync8(scriptPath2, script, { mode: 493 });
6789
+ try {
6790
+ unlinkSync4(resultPath);
6791
+ } catch {
6792
+ }
6793
+ const popupTitle = ` \u2691 ${opts.sessionLabel} `;
6794
+ const clientsRaw = execSafe('tmux list-clients -F "#{client_name} #{client_width}"');
6795
+ if (!clientsRaw) return "dismiss";
6796
+ const lines = clientsRaw.split("\n").filter(Boolean);
6797
+ if (lines.length === 0) return "dismiss";
6798
+ let raw = null;
6799
+ for (const line of lines) {
6800
+ const lastSpace = line.lastIndexOf(" ");
6801
+ const client = line.slice(0, lastSpace);
6802
+ const clientWidth = parseInt(line.slice(lastSpace + 1), 10);
6803
+ if (!clientWidth) continue;
6804
+ const x = Math.max(0, clientWidth - POPUP_WIDTH - 1);
6805
+ const args2 = [
6806
+ `-c ${shellQuote(client)}`,
6807
+ "-E -b double",
6808
+ `-T ${shellQuote(popupTitle)}`,
6809
+ `-S "fg=${ACCENT_COLOR}"`,
6810
+ `-s "fg=${ACCENT_COLOR}"`,
6811
+ `-x ${x} -y 2`,
6812
+ `-w ${POPUP_WIDTH} -h ${height}`,
6813
+ shellQuote(scriptPath2)
6814
+ ].join(" ");
6815
+ execSafe(`tmux display-popup ${args2}`);
6816
+ try {
6817
+ raw = readFileSync12(resultPath, "utf8").trim();
6818
+ } catch {
6819
+ raw = null;
6820
+ }
6821
+ if (raw) break;
6822
+ }
6823
+ if (raw === "dashboard") return "dashboard";
6824
+ if (raw === "agent") return "agent";
6825
+ return "dismiss";
6826
+ } catch {
6827
+ return "dismiss";
6828
+ } finally {
6829
+ try {
6830
+ unlinkSync4(resultPath);
6831
+ } catch {
6832
+ }
6833
+ try {
6834
+ unlinkSync4(scriptPath2);
6835
+ } catch {
6836
+ }
6837
+ }
6838
+ }
6839
+
6840
+ // src/cli/commands/ask.ts
6637
6841
  import { approveDeck, notifyDeck, launchReview, display } from "@crouton-kit/humanloop";
6638
6842
  var ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
6639
6843
  function validateAskId(askId) {
@@ -7025,7 +7229,7 @@ async function markAnswered(cwd, sessionId, askId) {
7025
7229
  function waitForOutput(cwd, sessionId, askId, initialPpid) {
7026
7230
  const outputPath = askOutputPath(cwd, sessionId, askId);
7027
7231
  if (existsSync12(outputPath)) {
7028
- return Promise.resolve(JSON.parse(readFileSync12(outputPath, "utf-8")));
7232
+ return Promise.resolve(JSON.parse(readFileSync13(outputPath, "utf-8")));
7029
7233
  }
7030
7234
  return new Promise((res, _rej) => {
7031
7235
  let ppidWatcher;
@@ -7037,7 +7241,7 @@ function waitForOutput(cwd, sessionId, askId, initialPpid) {
7037
7241
  const onChange = () => {
7038
7242
  if (!existsSync12(outputPath)) return;
7039
7243
  try {
7040
- const out = JSON.parse(readFileSync12(outputPath, "utf-8"));
7244
+ const out = JSON.parse(readFileSync13(outputPath, "utf-8"));
7041
7245
  cleanup();
7042
7246
  res(out);
7043
7247
  } catch (err) {
@@ -7064,22 +7268,86 @@ function waitForOutput(cwd, sessionId, askId, initialPpid) {
7064
7268
  process.once("SIGINT", onSigint);
7065
7269
  });
7066
7270
  }
7271
+ function focusAgentPane(callerPane) {
7272
+ const sess = execSafe(`tmux display-message -p -t ${shellQuote(callerPane)} "#{session_name}"`)?.trim();
7273
+ const win = execSafe(`tmux display-message -p -t ${shellQuote(callerPane)} "#{window_id}"`)?.trim();
7274
+ if (sess) switchAllClientsToSession(sess);
7275
+ if (win) execSafe(`tmux select-window -t ${shellQuote(win)}`);
7276
+ }
7277
+ function switchAllClientsToSession(session2) {
7278
+ const clients = execSafe('tmux list-clients -F "#{client_name}"');
7279
+ if (!clients) return;
7280
+ for (const c of clients.split("\n").filter(Boolean)) {
7281
+ execSafe(`tmux switch-client -c ${shellQuote(c)} -t ${shellQuote(session2)}`);
7282
+ }
7283
+ }
7067
7284
  function maybeSpawnAskPane(cwd, sessionId, askId, kind) {
7068
7285
  if (kind === "review") return;
7069
7286
  const callerPane = process.env.TMUX_PANE;
7070
7287
  if (!callerPane) return;
7071
7288
  if (process.env.SISYPHUS_DISABLE_ASK_PANE === "1") return;
7072
- const tuiPath = join13(import.meta.dirname, "tui.js");
7289
+ const tuiPath = join14(import.meta.dirname, "tui.js");
7073
7290
  const cmd = `node ${shellQuote(tuiPath)} --cwd ${shellQuote(cwd)} --session-id ${shellQuote(sessionId)} --ask ${shellQuote(askId)}`;
7074
- execSafe(`tmux split-window -d -h -t ${shellQuote(callerPane)} -c ${shellQuote(cwd)} ${shellQuote(cmd)}`);
7291
+ execSafe(`tmux split-window -h -t ${shellQuote(callerPane)} -c ${shellQuote(cwd)} ${shellQuote(cmd)}`);
7292
+ focusAgentPane(callerPane);
7075
7293
  }
7076
7294
  function maybeSpawnReviewPane(cwd, sessionId, askId) {
7077
7295
  const callerPane = process.env.TMUX_PANE;
7078
7296
  if (!callerPane) return;
7079
7297
  if (process.env.SISYPHUS_DISABLE_ASK_PANE === "1") return;
7080
- const cliPath = join13(import.meta.dirname, "cli.js");
7298
+ const cliPath = join14(import.meta.dirname, "cli.js");
7081
7299
  const cmd = `node ${shellQuote(cliPath)} ask review open ${shellQuote(askId)} --session ${shellQuote(sessionId)}`;
7082
7300
  execSafe(`tmux split-window -h -t ${shellQuote(callerPane)} -c ${shellQuote(cwd)} ${shellQuote(cmd)}`);
7301
+ focusAgentPane(callerPane);
7302
+ }
7303
+ function sessionLabel(cwd, sessionId) {
7304
+ try {
7305
+ if (existsSync12(statePath(cwd, sessionId))) {
7306
+ const s = getSession(cwd, sessionId);
7307
+ if (s.name) return s.name;
7308
+ }
7309
+ } catch {
7310
+ }
7311
+ return sessionId.slice(0, 8);
7312
+ }
7313
+ function shouldSuppressBanner() {
7314
+ const popupWillShow = !!process.env.TMUX_PANE && process.env.SISYPHUS_DISABLE_ASK_PANE !== "1" && hasAttachedClient();
7315
+ return popupWillShow && isTerminalFrontmost();
7316
+ }
7317
+ async function triageAsk(cwd, sessionId, askId, kind, title) {
7318
+ const callerPane = process.env.TMUX_PANE;
7319
+ if (!callerPane) return;
7320
+ if (process.env.SISYPHUS_DISABLE_ASK_PANE === "1") return;
7321
+ const choice = showAskTriagePopup({
7322
+ askId,
7323
+ sessionLabel: sessionLabel(cwd, sessionId),
7324
+ title: title !== void 0 ? title : "Question pending"
7325
+ });
7326
+ if (choice === "dismiss") return;
7327
+ if (choice === "agent") {
7328
+ if (kind === "review") maybeSpawnReviewPane(cwd, sessionId, askId);
7329
+ else maybeSpawnAskPane(cwd, sessionId, askId, kind);
7330
+ return;
7331
+ }
7332
+ await openInDashboard(cwd, sessionId, askId, kind);
7333
+ }
7334
+ async function openInDashboard(cwd, sessionId, askId, kind) {
7335
+ try {
7336
+ assertTmux();
7337
+ const tmuxSession = findHomeSession(cwd) ?? getTmuxSession();
7338
+ if (kind === "review") {
7339
+ openDashboardWindow(tmuxSession, cwd);
7340
+ switchAllClientsToSession(tmuxSession);
7341
+ const cliPath = join14(import.meta.dirname, "cli.js");
7342
+ const cmd = `node ${shellQuote(cliPath)} ask review open ${shellQuote(askId)} --session ${shellQuote(sessionId)}`;
7343
+ execSafe(`tmux display-popup -E -w 90% -h 90% -d ${shellQuote(cwd)} ${shellQuote(cmd)}`);
7344
+ return;
7345
+ }
7346
+ await rawSend({ type: "focus-set", cwd, sessionId, askId });
7347
+ openDashboardWindow(tmuxSession, cwd);
7348
+ switchAllClientsToSession(tmuxSession);
7349
+ } catch {
7350
+ }
7083
7351
  }
7084
7352
  async function submitDeck(deck, opts, options) {
7085
7353
  const blocking = options?.blocking !== false;
@@ -7089,6 +7357,8 @@ async function submitDeck(deck, opts, options) {
7089
7357
  const claudeSessionId = resolveClaudeSessionId(cwd, sessionId, askedBy);
7090
7358
  const askId = mintAskId();
7091
7359
  const q0 = deck.interactions[0];
7360
+ const askTitle = deck.title !== void 0 ? deck.title : q0?.title;
7361
+ const suppressTerminalNotification = blocking ? shouldSuppressBanner() : false;
7092
7362
  createAsk(cwd, sessionId, {
7093
7363
  askId,
7094
7364
  askedBy,
@@ -7096,15 +7366,16 @@ async function submitDeck(deck, opts, options) {
7096
7366
  pid: process.pid,
7097
7367
  claudeSessionId,
7098
7368
  cwd,
7099
- title: deck.title !== void 0 ? deck.title : q0?.title,
7369
+ title: askTitle,
7100
7370
  subtitle: q0?.subtitle,
7101
- kind: options?.kindOverride !== void 0 ? options.kindOverride : q0?.kind
7371
+ kind: options?.kindOverride !== void 0 ? options.kindOverride : q0?.kind,
7372
+ suppressTerminalNotification
7102
7373
  });
7103
7374
  writeDecisions(cwd, sessionId, askId, deck);
7104
7375
  if (!blocking) {
7105
7376
  return { askId };
7106
7377
  }
7107
- maybeSpawnAskPane(cwd, sessionId, askId, q0?.kind);
7378
+ await triageAsk(cwd, sessionId, askId, q0?.kind, askTitle);
7108
7379
  const output = await waitForOutput(cwd, sessionId, askId, initialPpid);
7109
7380
  await markAnswered(cwd, sessionId, askId);
7110
7381
  return { askId, output };
@@ -7157,6 +7428,8 @@ async function submitReview(absFile, opts, options) {
7157
7428
  const initialPpid = process.ppid;
7158
7429
  const claudeSessionId = resolveClaudeSessionId(cwd, sessionId, askedBy);
7159
7430
  const askId = mintAskId();
7431
+ const reviewTitle = `Review ${basename4(absFile)}`;
7432
+ const suppressTerminalNotification = blocking ? shouldSuppressBanner() : false;
7160
7433
  createAsk(cwd, sessionId, {
7161
7434
  askId,
7162
7435
  askedBy,
@@ -7164,12 +7437,13 @@ async function submitReview(absFile, opts, options) {
7164
7437
  pid: process.pid,
7165
7438
  claudeSessionId,
7166
7439
  cwd,
7167
- title: `Review ${basename4(absFile)}`,
7168
- kind: "review"
7440
+ title: reviewTitle,
7441
+ kind: "review",
7442
+ suppressTerminalNotification
7169
7443
  });
7170
7444
  writeReview(cwd, sessionId, askId, { file: absFile });
7171
7445
  if (!blocking) return { askId };
7172
- maybeSpawnReviewPane(cwd, sessionId, askId);
7446
+ await triageAsk(cwd, sessionId, askId, "review", reviewTitle);
7173
7447
  const output = await waitForOutput(cwd, sessionId, askId, initialPpid);
7174
7448
  await markAnswered(cwd, sessionId, askId);
7175
7449
  return { askId, output };
@@ -7201,7 +7475,7 @@ async function reviewOpen(askId, opts) {
7201
7475
  if (!reviewData) exitUsage("missing-review-pointer", `review.json missing for ${askId}`, { received: askId });
7202
7476
  const draftPath = askReviewDraftPath(cwd, sessionId, askId);
7203
7477
  const flagPath = askReviewSubmitFlagPath(cwd, sessionId, askId);
7204
- if (existsSync12(flagPath)) unlinkSync3(flagPath);
7478
+ if (existsSync12(flagPath)) unlinkSync5(flagPath);
7205
7479
  const result = await launchReview(reviewData.file, {
7206
7480
  output: draftPath,
7207
7481
  submitFlagPath: flagPath,
@@ -7227,7 +7501,7 @@ async function reviewSubmit(askId, opts) {
7227
7501
  let comments = [];
7228
7502
  if (existsSync12(draftPath)) {
7229
7503
  try {
7230
- const draft = JSON.parse(readFileSync12(draftPath, "utf-8"));
7504
+ const draft = JSON.parse(readFileSync13(draftPath, "utf-8"));
7231
7505
  if (Array.isArray(draft.comments)) comments = draft.comments;
7232
7506
  } catch {
7233
7507
  }
@@ -7344,7 +7618,7 @@ async function peek(askId, opts) {
7344
7618
  if (meta.completedAt) result.completedAt = meta.completedAt;
7345
7619
  try {
7346
7620
  if (existsSync12(outputPath)) {
7347
- result.output = JSON.parse(readFileSync12(outputPath, "utf-8"));
7621
+ result.output = JSON.parse(readFileSync13(outputPath, "utf-8"));
7348
7622
  }
7349
7623
  } catch (err) {
7350
7624
  if (!(err instanceof SyntaxError)) throw err;
@@ -7756,13 +8030,13 @@ Exit codes: 0 ok | 2 usage (bad state) | 3 not_found.`
7756
8030
 
7757
8031
  // src/tui/lib/context.ts
7758
8032
  init_paths();
7759
- import { readFileSync as readFileSync14, readdirSync as readdirSync4 } from "fs";
8033
+ import { readFileSync as readFileSync15, readdirSync as readdirSync4 } from "fs";
7760
8034
 
7761
8035
  // src/tui/lib/reports.ts
7762
- import { readFileSync as readFileSync13 } from "fs";
8036
+ import { readFileSync as readFileSync14 } from "fs";
7763
8037
  function loadReportContent(report) {
7764
8038
  try {
7765
- return readFileSync13(report.filePath, "utf-8");
8039
+ return readFileSync14(report.filePath, "utf-8");
7766
8040
  } catch {
7767
8041
  return report.summary;
7768
8042
  }
@@ -7779,7 +8053,7 @@ function resolveReports(reports) {
7779
8053
  // src/tui/lib/context.ts
7780
8054
  function readFileSafe(filePath) {
7781
8055
  try {
7782
- return readFileSync14(filePath, "utf-8");
8056
+ return readFileSync15(filePath, "utf-8");
7783
8057
  } catch {
7784
8058
  return null;
7785
8059
  }
@@ -7958,7 +8232,7 @@ Exit codes: 0 ok | 3 not_found (unknown session).`
7958
8232
 
7959
8233
  // src/cli/commands/spawn.ts
7960
8234
  import { existsSync as existsSync13 } from "fs";
7961
- import { join as join14 } from "path";
8235
+ import { join as join15 } from "path";
7962
8236
  function registerSpawn(program2) {
7963
8237
  program2.command("spawn").description("Spawn a new agent (orchestrator only)").option("--agent-type <type>", "Agent type (e.g. sisyphus:debug, devcore:programmer)").option("--name <name>", "Agent name").option("--stdin", "Force-read instruction from stdin (avoids shell escaping for long prompts)").option("--repo <name>", "Repo subdirectory to use for this agent").option("--session <sessionId>", "Session ID (defaults to SISYPHUS_SESSION_ID env var)").addHelpText(
7964
8238
  "after",
@@ -8035,7 +8309,7 @@ Exit codes: 0 ok | 2 usage | 3 not_found (unknown session) | 5 conflict.`
8035
8309
  });
8036
8310
  }
8037
8311
  if (opts.repo && opts.repo !== ".") {
8038
- const repoPath = join14(sisyphusCwd, opts.repo);
8312
+ const repoPath = join15(sisyphusCwd, opts.repo);
8039
8313
  if (!existsSync13(repoPath)) {
8040
8314
  exitError({
8041
8315
  code: "repo_not_found",
@@ -8221,7 +8495,7 @@ Exit codes: 0 ok | 2 usage | 3 not_found | 60 transient.`
8221
8495
  }
8222
8496
 
8223
8497
  // src/cli/commands/await.ts
8224
- import { existsSync as existsSync16, readFileSync as readFileSync17 } from "fs";
8498
+ import { existsSync as existsSync16, readFileSync as readFileSync18 } from "fs";
8225
8499
  var AWAIT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
8226
8500
  function registerAwait(program2) {
8227
8501
  program2.command("await").description("Block until an agent reaches a terminal status, then print its final report inline. Marks the agent as consumed-inline so its report is suppressed from the next cycle.").argument("<agentId>", "Agent ID to await").option("--session <sessionId>", "Session ID (defaults to SISYPHUS_SESSION_ID env var)").addHelpText(
@@ -8261,7 +8535,7 @@ Exit codes: 0 ok | 2 usage (missing --session) | 3 not_found (unknown agent) | 6
8261
8535
  let report = "";
8262
8536
  if (reportPath && existsSync16(reportPath)) {
8263
8537
  try {
8264
- report = readFileSync17(reportPath, "utf-8");
8538
+ report = readFileSync18(reportPath, "utf-8");
8265
8539
  } catch (err) {
8266
8540
  process.stderr.write(`Warning: could not read report at ${reportPath}: ${err instanceof Error ? err.message : err}
8267
8541
  `);
@@ -8466,15 +8740,15 @@ import { execSync as execSync10 } from "child_process";
8466
8740
 
8467
8741
  // src/cli/onboard.ts
8468
8742
  import { execSync as execSync9 } from "child_process";
8469
- import { existsSync as existsSync17, readFileSync as readFileSync18, writeFileSync as writeFileSync9 } from "fs";
8743
+ import { existsSync as existsSync17, readFileSync as readFileSync19, writeFileSync as writeFileSync10 } from "fs";
8470
8744
  import { homedir as homedir9 } from "os";
8471
- import { dirname as dirname5, join as join17 } from "path";
8745
+ import { dirname as dirname5, join as join18 } from "path";
8472
8746
  import { fileURLToPath as fileURLToPath2 } from "url";
8473
8747
  init_platform();
8474
8748
 
8475
8749
  // src/shared/clipboard.ts
8476
8750
  init_platform();
8477
- import { execFileSync as execFileSync2, spawnSync } from "child_process";
8751
+ import { execFileSync as execFileSync3, spawnSync } from "child_process";
8478
8752
  function detectClipboard() {
8479
8753
  const platform = detectPlatform();
8480
8754
  if (platform === "darwin") {
@@ -8565,7 +8839,7 @@ function checkItermOptionKey() {
8565
8839
  if (process.platform !== "darwin") {
8566
8840
  return { checked: false, allCorrect: true, incorrectProfiles: [] };
8567
8841
  }
8568
- const plistPath2 = join17(homedir9(), "Library", "Preferences", "com.googlecode.iterm2.plist");
8842
+ const plistPath2 = join18(homedir9(), "Library", "Preferences", "com.googlecode.iterm2.plist");
8569
8843
  if (!existsSync17(plistPath2)) {
8570
8844
  return { checked: false, allCorrect: false, incorrectProfiles: [] };
8571
8845
  }
@@ -8590,7 +8864,7 @@ function checkItermOptionKey() {
8590
8864
  }
8591
8865
  }
8592
8866
  function hasExistingTmuxConf() {
8593
- return existsSync17(join17(homedir9(), ".tmux.conf")) || existsSync17(join17(homedir9(), ".config", "tmux", "tmux.conf"));
8867
+ return existsSync17(join18(homedir9(), ".tmux.conf")) || existsSync17(join18(homedir9(), ".config", "tmux", "tmux.conf"));
8594
8868
  }
8595
8869
  var SISYPHUS_DEFAULTS_MARKER = "# sisyphus-managed \u2014 do not edit";
8596
8870
  function buildTmuxDefaults() {
@@ -8702,8 +8976,8 @@ source-file -q ${sisyphusConf} ${SISYPHUS_DEFAULTS_MARKER}
8702
8976
  `;
8703
8977
  }
8704
8978
  function writeTmuxDefaults() {
8705
- const confPath = join17(homedir9(), ".tmux.conf");
8706
- writeFileSync9(confPath, buildTmuxDefaults(), "utf8");
8979
+ const confPath = join18(homedir9(), ".tmux.conf");
8980
+ writeFileSync10(confPath, buildTmuxDefaults(), "utf8");
8707
8981
  }
8708
8982
  function isNvimAvailable() {
8709
8983
  try {
@@ -8721,21 +8995,21 @@ function getNvimVersion() {
8721
8995
  }
8722
8996
  }
8723
8997
  function hasLazyVimConfig() {
8724
- return existsSync17(join17(homedir9(), ".config", "nvim", "lazy-lock.json"));
8998
+ return existsSync17(join18(homedir9(), ".config", "nvim", "lazy-lock.json"));
8725
8999
  }
8726
9000
  function bundledBaleiaPluginPath() {
8727
9001
  const distDir = dirname5(fileURLToPath2(import.meta.url));
8728
- return join17(distDir, "templates", "baleia.lua");
9002
+ return join18(distDir, "templates", "baleia.lua");
8729
9003
  }
8730
9004
  function installBaleiaPlugin() {
8731
- const pluginsDir = join17(homedir9(), ".config", "nvim", "lua", "plugins");
9005
+ const pluginsDir = join18(homedir9(), ".config", "nvim", "lua", "plugins");
8732
9006
  if (!existsSync17(pluginsDir)) return false;
8733
- const dest = join17(pluginsDir, "sisyphus-baleia.lua");
9007
+ const dest = join18(pluginsDir, "sisyphus-baleia.lua");
8734
9008
  if (existsSync17(dest)) return true;
8735
9009
  const src = bundledBaleiaPluginPath();
8736
9010
  if (!existsSync17(src)) return false;
8737
9011
  try {
8738
- writeFileSync9(dest, readFileSync18(src, "utf-8"), "utf8");
9012
+ writeFileSync10(dest, readFileSync19(src, "utf-8"), "utf8");
8739
9013
  return true;
8740
9014
  } catch {
8741
9015
  return false;
@@ -8758,7 +9032,7 @@ function tryAutoInstallNvim() {
8758
9032
  if (!isNvimAvailable()) {
8759
9033
  return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false, baleiaInstalled: false };
8760
9034
  }
8761
- const nvimConfigDir = join17(homedir9(), ".config", "nvim");
9035
+ const nvimConfigDir = join18(homedir9(), ".config", "nvim");
8762
9036
  let lazyVimInstalled = false;
8763
9037
  if (!existsSync17(nvimConfigDir)) {
8764
9038
  const cloneCmd = [
@@ -8776,7 +9050,7 @@ function tryAutoInstallNvim() {
8776
9050
  stdio: "inherit",
8777
9051
  env: { ...process.env, GIT_LFS_SKIP_SMUDGE: "1" }
8778
9052
  });
8779
- const gitDir = join17(nvimConfigDir, ".git");
9053
+ const gitDir = join18(nvimConfigDir, ".git");
8780
9054
  if (existsSync17(gitDir)) {
8781
9055
  execSync9(`rm -rf "${gitDir}"`, { stdio: "pipe" });
8782
9056
  }
@@ -9063,7 +9337,7 @@ Exit codes: 0 ok | 1 conflict or requires-force`
9063
9337
 
9064
9338
  // src/cli/commands/check-keybinds.ts
9065
9339
  import { execSync as execSync11 } from "child_process";
9066
- import { readFileSync as readFileSync19 } from "fs";
9340
+ import { readFileSync as readFileSync20 } from "fs";
9067
9341
  function isTmuxInstalled2() {
9068
9342
  try {
9069
9343
  execSync11("which tmux", { stdio: "pipe" });
@@ -9116,7 +9390,7 @@ function runCheck() {
9116
9390
  let userConfAlreadySources = false;
9117
9391
  if (userConfPath !== null) {
9118
9392
  try {
9119
- userConfAlreadySources = readFileSync19(userConfPath, "utf-8").includes(sisyphusConfPath);
9393
+ userConfAlreadySources = readFileSync20(userConfPath, "utf-8").includes(sisyphusConfPath);
9120
9394
  } catch {
9121
9395
  }
9122
9396
  }
@@ -9287,9 +9561,9 @@ Exit codes: 0 ok`
9287
9561
  // src/cli/commands/check-statusbar.ts
9288
9562
  init_paths();
9289
9563
  import { execSync as execSync12 } from "child_process";
9290
- import { existsSync as existsSync18, readFileSync as readFileSync20 } from "fs";
9564
+ import { existsSync as existsSync18, readFileSync as readFileSync21 } from "fs";
9291
9565
  import { homedir as homedir10 } from "os";
9292
- import { join as join18 } from "path";
9566
+ import { join as join19 } from "path";
9293
9567
  var SISYPHUS_LEFT_TOKEN = "@sisyphus_left";
9294
9568
  var SISYPHUS_RIGHT_TOKEN = "@sisyphus_right";
9295
9569
  var TMUX_DEFAULT_STATUS_LEFT = "[#S] ";
@@ -9314,7 +9588,7 @@ function isDaemonRunning() {
9314
9588
  const pidFile = daemonPidPath();
9315
9589
  if (!existsSync18(pidFile)) return false;
9316
9590
  try {
9317
- const pid = parseInt(readFileSync20(pidFile, "utf-8").trim(), 10);
9591
+ const pid = parseInt(readFileSync21(pidFile, "utf-8").trim(), 10);
9318
9592
  if (Number.isNaN(pid) || pid <= 0) return false;
9319
9593
  process.kill(pid, 0);
9320
9594
  return true;
@@ -9357,8 +9631,8 @@ function probeTmuxOptions(serverRunning) {
9357
9631
  };
9358
9632
  }
9359
9633
  function findUserTmuxConf() {
9360
- const xdg = join18(homedir10(), ".config", "tmux", "tmux.conf");
9361
- const dotfile = join18(homedir10(), ".tmux.conf");
9634
+ const xdg = join19(homedir10(), ".config", "tmux", "tmux.conf");
9635
+ const dotfile = join19(homedir10(), ".tmux.conf");
9362
9636
  if (existsSync18(xdg)) return xdg;
9363
9637
  if (existsSync18(dotfile)) return dotfile;
9364
9638
  return null;
@@ -9370,21 +9644,21 @@ function probeUserConf() {
9370
9644
  }
9371
9645
  let contents = "";
9372
9646
  try {
9373
- contents = readFileSync20(path, "utf-8");
9647
+ contents = readFileSync21(path, "utf-8");
9374
9648
  } catch {
9375
9649
  return { path, setsStatusLeft: false, setsStatusRight: false, sourcesSisyphusManaged: false };
9376
9650
  }
9377
9651
  const lines = contents.split("\n").filter((line) => !line.trim().startsWith("#"));
9378
9652
  const setsStatusLeft = lines.some((line) => /^\s*(set|set-option)\s+-g(?:\s+-\w+)*\s+status-left\b/.test(line));
9379
9653
  const setsStatusRight = lines.some((line) => /^\s*(set|set-option)\s+-g(?:\s+-\w+)*\s+status-right\b/.test(line));
9380
- const sourcesSisyphusManaged = contents.includes(join18(homedir10(), ".sisyphus", "tmux.conf"));
9654
+ const sourcesSisyphusManaged = contents.includes(join19(homedir10(), ".sisyphus", "tmux.conf"));
9381
9655
  return { path, setsStatusLeft, setsStatusRight, sourcesSisyphusManaged };
9382
9656
  }
9383
9657
  function loadGlobalSisyphusConfig() {
9384
9658
  const path = globalConfigPath();
9385
9659
  if (!existsSync18(path)) return null;
9386
9660
  try {
9387
- const parsed = JSON.parse(readFileSync20(path, "utf-8"));
9661
+ const parsed = JSON.parse(readFileSync21(path, "utf-8"));
9388
9662
  return parsed.statusBar === void 0 ? null : parsed.statusBar;
9389
9663
  } catch {
9390
9664
  return null;
@@ -9724,7 +9998,7 @@ init_paths();
9724
9998
  import { execSync as execSync14 } from "child_process";
9725
9999
  import { existsSync as existsSync19, statSync as statSync4 } from "fs";
9726
10000
  import { homedir as homedir11 } from "os";
9727
- import { join as join19 } from "path";
10001
+ import { join as join20 } from "path";
9728
10002
  init_platform();
9729
10003
  init_plugins();
9730
10004
  function checkNodeVersion() {
@@ -9958,7 +10232,7 @@ function checkNvim() {
9958
10232
  }
9959
10233
  function checkNotifyBinary() {
9960
10234
  if (process.platform === "darwin") {
9961
- const binary = join19(homedir11(), ".sisyphus", "SisyphusNotify.app", "Contents", "MacOS", "sisyphus-notify");
10235
+ const binary = join20(homedir11(), ".sisyphus", "SisyphusNotify.app", "Contents", "MacOS", "sisyphus-notify");
9962
10236
  if (existsSync19(binary)) {
9963
10237
  return { name: "Notifications", status: "ok", detail: "SisyphusNotify.app built" };
9964
10238
  }
@@ -10072,13 +10346,13 @@ init_version();
10072
10346
  init_platform();
10073
10347
  init_paths();
10074
10348
  init_state();
10075
- import { execFileSync as execFileSync3, spawnSync as spawnSync2 } from "child_process";
10076
- import { existsSync as existsSync20, readFileSync as readFileSync22 } from "fs";
10349
+ import { execFileSync as execFileSync4, spawnSync as spawnSync2 } from "child_process";
10350
+ import { existsSync as existsSync20, readFileSync as readFileSync23 } from "fs";
10077
10351
  import os from "os";
10078
10352
  var REPO = "crouton-labs/sisyphus";
10079
10353
  function tryCmd(bin, args2) {
10080
10354
  try {
10081
- const out = execFileSync3(bin, args2, {
10355
+ const out = execFileSync4(bin, args2, {
10082
10356
  encoding: "utf-8",
10083
10357
  stdio: ["ignore", "pipe", "ignore"],
10084
10358
  timeout: 5e3
@@ -10134,7 +10408,7 @@ function tailLog(lines) {
10134
10408
  const path = daemonLogPath();
10135
10409
  if (!existsSync20(path)) return null;
10136
10410
  try {
10137
- const all = readFileSync22(path, "utf-8").split("\n");
10411
+ const all = readFileSync23(path, "utf-8").split("\n");
10138
10412
  return all.slice(-lines).join("\n").trim() || null;
10139
10413
  } catch {
10140
10414
  return null;
@@ -10423,8 +10697,8 @@ Exit codes: 0 ok | 1 filing error | 2 usage | 60 cloud unreachable (retry-safe)`
10423
10697
  }
10424
10698
 
10425
10699
  // src/cli/commands/init.ts
10426
- import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10 } from "fs";
10427
- import { join as join20 } from "path";
10700
+ import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync11 } from "fs";
10701
+ import { join as join21 } from "path";
10428
10702
  var DEFAULT_CONFIG2 = {};
10429
10703
  var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
10430
10704
 
@@ -10453,19 +10727,19 @@ Effects
10453
10727
  Exit codes: 0 ok`
10454
10728
  ).action((opts) => {
10455
10729
  const cwd = process.cwd();
10456
- const sisDir = join20(cwd, ".sisyphus");
10457
- const configPath = join20(sisDir, "config.json");
10730
+ const sisDir = join21(cwd, ".sisyphus");
10731
+ const configPath = join21(sisDir, "config.json");
10458
10732
  if (existsSync21(configPath)) {
10459
10733
  console.log(`Already initialized: ${configPath}`);
10460
10734
  return;
10461
10735
  }
10462
10736
  mkdirSync9(sisDir, { recursive: true });
10463
- writeFileSync10(configPath, JSON.stringify(DEFAULT_CONFIG2, null, 2) + "\n", "utf-8");
10737
+ writeFileSync11(configPath, JSON.stringify(DEFAULT_CONFIG2, null, 2) + "\n", "utf-8");
10464
10738
  console.log(`Created ${configPath}`);
10465
10739
  if (opts.orchestrator) {
10466
- const orchPath = join20(sisDir, "orchestrator.md");
10740
+ const orchPath = join21(sisDir, "orchestrator.md");
10467
10741
  if (!existsSync21(orchPath)) {
10468
- writeFileSync10(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
10742
+ writeFileSync11(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
10469
10743
  console.log(`Created ${orchPath}`);
10470
10744
  }
10471
10745
  }
@@ -10525,7 +10799,7 @@ Exit codes: 0 ok`
10525
10799
 
10526
10800
  // src/cli/commands/configure-upload.ts
10527
10801
  init_paths();
10528
- import { chmodSync as chmodSync2, existsSync as existsSync22, mkdirSync as mkdirSync10, readFileSync as readFileSync23, writeFileSync as writeFileSync11 } from "fs";
10802
+ import { chmodSync as chmodSync2, existsSync as existsSync22, mkdirSync as mkdirSync10, readFileSync as readFileSync24, writeFileSync as writeFileSync12 } from "fs";
10529
10803
  import { createInterface as createInterface3 } from "readline";
10530
10804
  import { dirname as dirname6 } from "path";
10531
10805
  async function readUrlFromInput(interactive) {
@@ -10606,7 +10880,7 @@ Exit codes: 0 ok | 1 validation or write error`
10606
10880
  let existing = {};
10607
10881
  if (existsSync22(configPath)) {
10608
10882
  try {
10609
- existing = JSON.parse(readFileSync23(configPath, "utf-8"));
10883
+ existing = JSON.parse(readFileSync24(configPath, "utf-8"));
10610
10884
  } catch {
10611
10885
  console.error(`Error: ${configPath} could not be parsed \u2014 fix or delete it first`);
10612
10886
  process.exit(1);
@@ -10614,7 +10888,7 @@ Exit codes: 0 ok | 1 validation or write error`
10614
10888
  }
10615
10889
  const merged = { ...existing, upload: { url, token } };
10616
10890
  mkdirSync10(dirname6(configPath), { recursive: true });
10617
- writeFileSync11(configPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
10891
+ writeFileSync12(configPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
10618
10892
  chmodSync2(configPath, 384);
10619
10893
  console.log(`\u2713 upload configured (${configPath})`);
10620
10894
  });
@@ -10623,10 +10897,10 @@ Exit codes: 0 ok | 1 validation or write error`
10623
10897
  // src/cli/commands/getting-started.ts
10624
10898
  import { execSync as execSync15 } from "child_process";
10625
10899
  import { existsSync as existsSync23 } from "fs";
10626
- import { dirname as dirname7, join as join21 } from "path";
10900
+ import { dirname as dirname7, join as join22 } from "path";
10627
10901
  import { fileURLToPath as fileURLToPath3 } from "url";
10628
10902
  function templatePath(name) {
10629
- return join21(dirname7(fileURLToPath3(import.meta.url)), "templates", name);
10903
+ return join22(dirname7(fileURLToPath3(import.meta.url)), "templates", name);
10630
10904
  }
10631
10905
  function isClaudeCode() {
10632
10906
  return !!process.env["CLAUDECODE"];
@@ -10993,7 +11267,7 @@ sis ui guide --tutorial 4
10993
11267
  `);
10994
11268
  }
10995
11269
  function printStep4() {
10996
- const demoPath = join21(process.cwd(), "tmp", "sisyphus-tutorial-demo");
11270
+ const demoPath = join22(process.cwd(), "tmp", "sisyphus-tutorial-demo");
10997
11271
  console.log(`
10998
11272
  <claude-instructions>
10999
11273
  # Sisyphus Getting Started \u2014 Step 4: Demo Session
@@ -11037,7 +11311,7 @@ Have the user confirm they understand these keybinds before proceeding.
11037
11311
 
11038
11312
  First, copy the demo todo app into ./tmp/ under the user's current directory and init a git repo (sisyphus needs git). Run from the user's current shell \u2014 don't \`cd\` away first:
11039
11313
  \`\`\`
11040
- mkdir -p ${join21(process.cwd(), "tmp")}
11314
+ mkdir -p ${join22(process.cwd(), "tmp")}
11041
11315
  rm -rf ${demoPath}
11042
11316
  cp -r ${templatePath("tutorial-demo")} ${demoPath}
11043
11317
  git -C ${demoPath} init
@@ -11115,7 +11389,7 @@ sis ui guide --tutorial 5
11115
11389
  `);
11116
11390
  }
11117
11391
  function printStep5() {
11118
- const demoPath = join21(process.cwd(), "tmp", "sisyphus-tutorial-demo");
11392
+ const demoPath = join22(process.cwd(), "tmp", "sisyphus-tutorial-demo");
11119
11393
  const demoExists = existsSync23(demoPath);
11120
11394
  let recentCommits = "";
11121
11395
  let topLevelFiles = "";
@@ -11220,9 +11494,9 @@ If they say yes, run:
11220
11494
  rm -rf ${demoPath}
11221
11495
  \`\`\`
11222
11496
 
11223
- Then check whether \`${join21(process.cwd(), "tmp")}\` is now empty, and if so remove it too:
11497
+ Then check whether \`${join22(process.cwd(), "tmp")}\` is now empty, and if so remove it too:
11224
11498
  \`\`\`
11225
- rmdir ${join21(process.cwd(), "tmp")} 2>/dev/null || true
11499
+ rmdir ${join22(process.cwd(), "tmp")} 2>/dev/null || true
11226
11500
  \`\`\`
11227
11501
 
11228
11502
  If they say no or want to explore first, leave it. They can clean up later with the same
@@ -11655,7 +11929,7 @@ Exit codes: 0 ok | 2 usage (invalid --tutorial value)`
11655
11929
 
11656
11930
  // src/cli/commands/history.ts
11657
11931
  init_paths();
11658
- import { readdirSync as readdirSync7, readFileSync as readFileSync24, existsSync as existsSync24 } from "fs";
11932
+ import { readdirSync as readdirSync7, readFileSync as readFileSync25, existsSync as existsSync24 } from "fs";
11659
11933
  import { resolve as resolve8 } from "path";
11660
11934
  function loadAllSummaries() {
11661
11935
  const base = historyBaseDir();
@@ -11665,7 +11939,7 @@ function loadAllSummaries() {
11665
11939
  const summaryPath = historySessionSummaryPath(name);
11666
11940
  if (existsSync24(summaryPath)) {
11667
11941
  try {
11668
- const raw = readFileSync24(summaryPath, "utf-8");
11942
+ const raw = readFileSync25(summaryPath, "utf-8");
11669
11943
  results.push({ id: name, summary: JSON.parse(raw) });
11670
11944
  continue;
11671
11945
  } catch {
@@ -11682,7 +11956,7 @@ function buildLiveSummary(sessionId) {
11682
11956
  if (!existsSync24(eventsPath)) return null;
11683
11957
  let cwd = null;
11684
11958
  try {
11685
- const lines = readFileSync24(eventsPath, "utf-8").split("\n");
11959
+ const lines = readFileSync25(eventsPath, "utf-8").split("\n");
11686
11960
  for (const line of lines) {
11687
11961
  if (!line.trim()) continue;
11688
11962
  try {
@@ -11703,7 +11977,7 @@ function buildLiveSummary(sessionId) {
11703
11977
  if (!existsSync24(sPath)) return null;
11704
11978
  let session2;
11705
11979
  try {
11706
- session2 = JSON.parse(readFileSync24(sPath, "utf-8"));
11980
+ session2 = JSON.parse(readFileSync25(sPath, "utf-8"));
11707
11981
  } catch {
11708
11982
  return null;
11709
11983
  }
@@ -11765,7 +12039,7 @@ function buildLiveSummary(sessionId) {
11765
12039
  function loadEvents(sessionId) {
11766
12040
  const eventsPath = historyEventsPath(sessionId);
11767
12041
  if (!existsSync24(eventsPath)) return [];
11768
- const lines = readFileSync24(eventsPath, "utf-8").split("\n").filter((l) => l.trim());
12042
+ const lines = readFileSync25(eventsPath, "utf-8").split("\n").filter((l) => l.trim());
11769
12043
  const events = [];
11770
12044
  for (const line of lines) {
11771
12045
  try {
@@ -11780,7 +12054,7 @@ function findSession(idOrName) {
11780
12054
  const summaryPath = historySessionSummaryPath(idOrName);
11781
12055
  if (existsSync24(summaryPath)) {
11782
12056
  try {
11783
- return { id: idOrName, summary: JSON.parse(readFileSync24(summaryPath, "utf-8")) };
12057
+ return { id: idOrName, summary: JSON.parse(readFileSync25(summaryPath, "utf-8")) };
11784
12058
  } catch {
11785
12059
  }
11786
12060
  }
@@ -12028,7 +12302,7 @@ Exit codes: 0 ok | 2 usage | 1 export_failed.`).action(async (sessionIdArg, opts
12028
12302
 
12029
12303
  // src/cli/commands/upload.ts
12030
12304
  import { rmSync as rmSync6 } from "fs";
12031
- import { tmpdir } from "os";
12305
+ import { tmpdir as tmpdir2 } from "os";
12032
12306
  init_config();
12033
12307
  init_session_export();
12034
12308
  init_upload();
@@ -12084,7 +12358,7 @@ Exit codes: 0 ok | 1 upload or export error | 2 usage`
12084
12358
  }
12085
12359
  let zipPath;
12086
12360
  try {
12087
- zipPath = await exportSessionToZip(sessionId, cwd, { reveal: false, outputDir: tmpdir() });
12361
+ zipPath = await exportSessionToZip(sessionId, cwd, { reveal: false, outputDir: tmpdir2() });
12088
12362
  } catch (err) {
12089
12363
  exitError({
12090
12364
  code: "export_failed",
@@ -12149,7 +12423,7 @@ Exit codes: 0 ok | 1 upload or export error | 2 usage`
12149
12423
  // src/cli/commands/scratch.ts
12150
12424
  import { execSync as execSync16 } from "child_process";
12151
12425
  init_shell();
12152
- function findHomeSession(cwd) {
12426
+ function findHomeSession2(cwd) {
12153
12427
  const normalizedCwd = cwd.replace(/\/+$/, "");
12154
12428
  let output;
12155
12429
  try {
@@ -12197,7 +12471,7 @@ Effects
12197
12471
  Exit codes: 0 ok | 2 usage.`).action((promptParts, opts) => {
12198
12472
  assertTmux();
12199
12473
  const cwd = opts.cwd ?? process.env["SISYPHUS_CWD"] ?? process.cwd();
12200
- const homeSession = findHomeSession(cwd);
12474
+ const homeSession = findHomeSession2(cwd);
12201
12475
  if (!homeSession) {
12202
12476
  const current = execSync16('tmux display-message -p "#{session_name}"', {
12203
12477
  encoding: "utf-8"
@@ -12225,15 +12499,15 @@ function openScratchWindow(tmuxSession, cwd, prompt) {
12225
12499
 
12226
12500
  // src/cli/commands/review.ts
12227
12501
  init_paths();
12228
- import { join as join23, resolve as resolve9, dirname as dirname8 } from "path";
12229
- import { existsSync as existsSync26, readFileSync as readFileSync26, writeFileSync as writeFileSync13, renameSync as renameSync3, readdirSync as readdirSync8 } from "fs";
12502
+ import { join as join24, resolve as resolve9, dirname as dirname8 } from "path";
12503
+ import { existsSync as existsSync26, readFileSync as readFileSync27, writeFileSync as writeFileSync14, renameSync as renameSync3, readdirSync as readdirSync8 } from "fs";
12230
12504
  var _statusCheck = ["draft", "question", "approved", "rejected", "deferred"];
12231
12505
  function resolveContextArtifact(file, opts, filename, notFoundMessage) {
12232
12506
  const cwd = opts.cwd || process.env.SISYPHUS_CWD || process.cwd();
12233
12507
  if (file) return resolve9(file);
12234
12508
  const sessionId = opts.sessionId || process.env.SISYPHUS_SESSION_ID;
12235
12509
  if (sessionId) {
12236
- const target = join23(contextDir(cwd, sessionId), filename);
12510
+ const target = join24(contextDir(cwd, sessionId), filename);
12237
12511
  if (!existsSync26(target)) {
12238
12512
  exitUsage("file-not-found", `File not found: ${target}`, { received: target });
12239
12513
  }
@@ -12243,7 +12517,7 @@ function resolveContextArtifact(file, opts, filename, notFoundMessage) {
12243
12517
  if (existsSync26(dir)) {
12244
12518
  const sessions = readdirSync8(dir);
12245
12519
  for (const session2 of sessions.reverse()) {
12246
- const candidate = join23(dir, session2, "context", filename);
12520
+ const candidate = join24(dir, session2, "context", filename);
12247
12521
  if (existsSync26(candidate)) return candidate;
12248
12522
  }
12249
12523
  }
@@ -12302,12 +12576,12 @@ Exit codes: 0 ok | 2 usage.`).action(async (file, opts) => {
12302
12576
  if (!existsSync26(targetPath)) {
12303
12577
  exitUsage("file-not-found", `File not found: ${targetPath}`, { received: targetPath });
12304
12578
  }
12305
- const parsed = JSON.parse(readFileSync26(targetPath, "utf-8"));
12579
+ const parsed = JSON.parse(readFileSync27(targetPath, "utf-8"));
12306
12580
  const rendered = renderRequirementsMarkdown(parsed);
12307
- const outPath = join23(dirname8(targetPath), "requirements.md");
12581
+ const outPath = join24(dirname8(targetPath), "requirements.md");
12308
12582
  const tmpPath = outPath + ".tmp";
12309
12583
  if (existsSync26(outPath)) {
12310
- const existing = readFileSync26(outPath, "utf-8");
12584
+ const existing = readFileSync27(outPath, "utf-8");
12311
12585
  if (existing !== rendered) {
12312
12586
  if (!opts.force) {
12313
12587
  exitUsage("conflict", `${outPath} has been hand-edited (differs from rendered output)`, {
@@ -12320,7 +12594,7 @@ Exit codes: 0 ok | 2 usage.`).action(async (file, opts) => {
12320
12594
  `);
12321
12595
  }
12322
12596
  }
12323
- writeFileSync13(tmpPath, rendered, "utf-8");
12597
+ writeFileSync14(tmpPath, rendered, "utf-8");
12324
12598
  renameSync3(tmpPath, outPath);
12325
12599
  emitJsonOk({ path: resolve9(outPath) });
12326
12600
  return;
@@ -12684,8 +12958,8 @@ function renderRequirementsMarkdown(json) {
12684
12958
  }
12685
12959
 
12686
12960
  // src/cli/commands/companion.ts
12687
- import { basename as basename7, dirname as dirname11, join as join28 } from "path";
12688
- import { mkdirSync as mkdirSync15, readFileSync as readFileSync31, writeFileSync as writeFileSync18 } from "fs";
12961
+ import { basename as basename7, dirname as dirname11, join as join29 } from "path";
12962
+ import { mkdirSync as mkdirSync15, readFileSync as readFileSync32, writeFileSync as writeFileSync19 } from "fs";
12689
12963
  init_paths();
12690
12964
  init_companion_types();
12691
12965
  init_companion_memory();
@@ -12788,10 +13062,10 @@ Effects
12788
13062
  Writes/updates ~/.sisyphus/companion-context-cache/<session-id>.json on each call that produces output.
12789
13063
 
12790
13064
  Exit codes: 0 ok.`).action((opts) => {
12791
- const cachePath = join28(globalDir(), "companion-context-cache", `${opts.sessionId}.json`);
13065
+ const cachePath = join29(globalDir(), "companion-context-cache", `${opts.sessionId}.json`);
12792
13066
  let prev = {};
12793
13067
  try {
12794
- prev = JSON.parse(readFileSync31(cachePath, "utf-8"));
13068
+ prev = JSON.parse(readFileSync32(cachePath, "utf-8"));
12795
13069
  } catch {
12796
13070
  prev = {};
12797
13071
  }
@@ -12805,7 +13079,7 @@ Exit codes: 0 ok.`).action((opts) => {
12805
13079
  process.stdout.write(renderFullContext(next));
12806
13080
  }
12807
13081
  mkdirSync15(dirname11(cachePath), { recursive: true });
12808
- writeFileSync18(cachePath, JSON.stringify(next), "utf-8");
13082
+ writeFileSync19(cachePath, JSON.stringify(next), "utf-8");
12809
13083
  });
12810
13084
  companion.command("pane").description("Open (or focus) a side claude pane next to the dashboard").option("--cwd <path>", "Project directory (default: current directory)").addHelpText("after", `
12811
13085
  companion pane: open (or focus) the companion side pane next to the dashboard.
@@ -12909,7 +13183,7 @@ Exit codes: 0 ok | 3 not_found.`
12909
13183
  }));
12910
13184
  emitJsonOk({ ...companionData, badges });
12911
13185
  } else {
12912
- emitJsonOk(companionData);
13186
+ emitJsonOk({ ...companionData });
12913
13187
  }
12914
13188
  });
12915
13189
  }
@@ -12919,7 +13193,7 @@ init_runner();
12919
13193
  init_creds();
12920
13194
  init_tailscale();
12921
13195
  import { homedir as homedir13 } from "os";
12922
- import { join as join29 } from "path";
13196
+ import { join as join30 } from "path";
12923
13197
  function assertArch(raw) {
12924
13198
  if (raw === "arm" || raw === "x86") return raw;
12925
13199
  throw new Error(`Invalid --arch: ${raw}. Must be 'arm' or 'x86'.`);
@@ -12965,7 +13239,7 @@ Exit codes: 0 ok | 1 error (bad credentials or API failure).`).action(async () =
12965
13239
  const sub = deploy.command(provider).description(`${provider} commands.`).addHelpText("before", `
12966
13240
  deploy ${provider}: per-provider box lifecycle. up | down | status | ssh | logs | update.
12967
13241
  `);
12968
- sub.command("up").description(`Provision the ${provider} box (terraform init \u2192 plan \u2192 apply).`).option("--region <region>", `Provider region (defaults: hetzner=nbg1, aws=us-east-1).`).option("--arch <arch>", "'arm' (default) or 'x86'. Picks the default --size and image.", "arm").option("--size <size>", "Instance type override (defaults follow --arch).").option("--ssh-key <path>", "Path to SSH public key.", join29(homedir13(), ".ssh", "id_ed25519.pub")).option("--no-chromium", "Skip headless Chromium install.").option("--no-auto-update", "Skip the daily auto-update systemd timer.").option("--name <name>", "Box hostname / Tailscale node name.", "sisyphus").option("--yes", "Skip the re-provision confirmation prompt when state already exists.").addHelpText("after", `
13242
+ sub.command("up").description(`Provision the ${provider} box (terraform init \u2192 plan \u2192 apply).`).option("--region <region>", `Provider region (defaults: hetzner=nbg1, aws=us-east-1).`).option("--arch <arch>", "'arm' (default) or 'x86'. Picks the default --size and image.", "arm").option("--size <size>", "Instance type override (defaults follow --arch).").option("--ssh-key <path>", "Path to SSH public key.", join30(homedir13(), ".ssh", "id_ed25519.pub")).option("--no-chromium", "Skip headless Chromium install.").option("--no-auto-update", "Skip the daily auto-update systemd timer.").option("--name <name>", "Box hostname / Tailscale node name.", "sisyphus").option("--yes", "Skip the re-provision confirmation prompt when state already exists.").addHelpText("after", `
12969
13243
  deploy ${provider} up: provision the ${provider} box via Terraform (init \u2192 plan \u2192 apply).
12970
13244
 
12971
13245
  Input
@@ -13112,14 +13386,14 @@ init_runner2();
13112
13386
 
13113
13387
  // src/cli/cloud/handoff.ts
13114
13388
  import { spawn as spawn5 } from "child_process";
13115
- import { existsSync as existsSync36, readFileSync as readFileSync35, writeFileSync as writeFileSync19 } from "fs";
13389
+ import { existsSync as existsSync36, readFileSync as readFileSync36, writeFileSync as writeFileSync20 } from "fs";
13116
13390
  init_exec();
13117
13391
  init_paths();
13118
13392
  init_shell();
13119
13393
  init_runner();
13120
13394
  init_ssh_exec();
13121
13395
  init_provider_pick();
13122
- import { join as join31 } from "path";
13396
+ import { join as join32 } from "path";
13123
13397
  async function cloudHandoff(sessionId, opts) {
13124
13398
  const cwd = process.env["SISYPHUS_CWD"] ?? process.cwd();
13125
13399
  const request = {
@@ -13164,7 +13438,7 @@ async function waitForSentOrError(cwd, sessionId) {
13164
13438
  if (!existsSync36(path)) continue;
13165
13439
  let session2;
13166
13440
  try {
13167
- session2 = JSON.parse(readFileSync35(path, "utf-8"));
13441
+ session2 = JSON.parse(readFileSync36(path, "utf-8"));
13168
13442
  } catch (err) {
13169
13443
  void err;
13170
13444
  continue;
@@ -13245,16 +13519,16 @@ async function cloudReclaim(sessionId, opts) {
13245
13519
  await rsyncDown(target, `${remoteRepoDir}/`, `${cwd}/`, { withDelete: false, excludeSisyphus: true, update: true });
13246
13520
  for (const name of ["config.json", "orchestrator.md", "orchestrator-settings.json"]) {
13247
13521
  const remotePath = `${remoteRepoDir}/.sisyphus/${name}`;
13248
- const localPath = join31(projectDir(cwd), name);
13522
+ const localPath = join32(projectDir(cwd), name);
13249
13523
  const probe = runOnBox(provider, `test -f ${shellQuote(remotePath.replace(/^~\//, ""))} && echo y || echo n`);
13250
13524
  if (probe.stdout.trim() !== "y") continue;
13251
13525
  await rsyncDown(target, remotePath, localPath, { withDelete: false });
13252
13526
  }
13253
13527
  const localStatePath = statePath(cwd, sessionId);
13254
- const merged = JSON.parse(readFileSync35(localStatePath, "utf-8"));
13528
+ const merged = JSON.parse(readFileSync36(localStatePath, "utf-8"));
13255
13529
  merged.cwd = cwd;
13256
13530
  merged.handoff = local.handoff;
13257
- writeFileSync19(localStatePath, JSON.stringify(merged, null, 2));
13531
+ writeFileSync20(localStatePath, JSON.stringify(merged, null, 2));
13258
13532
  const reclaimMessage = `Session reclaimed from cloud (${provider}:${repo}). Resuming locally.`;
13259
13533
  console.log(`\u2192 local sis resume`);
13260
13534
  const resumeResp = await sendRequest({
@@ -13287,7 +13561,7 @@ function readLocalSession(cwd, sessionId) {
13287
13561
  received: sessionId
13288
13562
  });
13289
13563
  }
13290
- return JSON.parse(readFileSync35(path, "utf-8"));
13564
+ return JSON.parse(readFileSync36(path, "utf-8"));
13291
13565
  }
13292
13566
  async function waitForBoxPaused(provider, remoteSessionDir) {
13293
13567
  const POLL_INTERVAL_MS = 2e3;
@@ -13571,7 +13845,7 @@ function attachNotify(diagnostic2) {
13571
13845
  // src/cli/commands/tmux-sessions.ts
13572
13846
  init_paths();
13573
13847
  import { execSync as execSync18 } from "child_process";
13574
- import { readFileSync as readFileSync36, existsSync as existsSync37 } from "fs";
13848
+ import { readFileSync as readFileSync37, existsSync as existsSync37 } from "fs";
13575
13849
  var DOT_MAP = {
13576
13850
  "orchestrator:processing": { icon: "\u25CF", color: "#d4ad6a" },
13577
13851
  "orchestrator:idle": { icon: "\u25CF", color: "#d47766" },
@@ -13584,7 +13858,7 @@ function readManifest() {
13584
13858
  const p = sessionsManifestPath();
13585
13859
  if (!existsSync37(p)) return null;
13586
13860
  try {
13587
- return JSON.parse(readFileSync36(p, "utf-8"));
13861
+ return JSON.parse(readFileSync37(p, "utf-8"));
13588
13862
  } catch {
13589
13863
  return null;
13590
13864
  }
@@ -13625,7 +13899,7 @@ init_server();
13625
13899
  init_ask_store();
13626
13900
  init_state();
13627
13901
  init_paths();
13628
- import { existsSync as existsSync49 } from "fs";
13902
+ import { existsSync as existsSync48 } from "fs";
13629
13903
  var HEARTBEAT_ASKED_BY2 = "system:heartbeat";
13630
13904
  var ORPHAN_ASKED_BY2 = "system:orphan-handler";
13631
13905
  function isHeartbeatZombie(cwd, sessionId, askId) {
@@ -13633,14 +13907,14 @@ function isHeartbeatZombie(cwd, sessionId, askId) {
13633
13907
  if (!meta) return false;
13634
13908
  if (meta.askedBy !== HEARTBEAT_ASKED_BY2) return false;
13635
13909
  if (meta.status === "answered") return false;
13636
- if (existsSync49(askOutputPath(cwd, sessionId, askId))) return false;
13910
+ if (existsSync48(askOutputPath(cwd, sessionId, askId))) return false;
13637
13911
  for (const candidateId of listAsks(cwd, sessionId)) {
13638
13912
  if (candidateId === askId) continue;
13639
13913
  const candidateMeta = readMeta(cwd, sessionId, candidateId);
13640
13914
  if (!candidateMeta) continue;
13641
13915
  if (candidateMeta.heartbeatAskId !== askId) continue;
13642
13916
  if (candidateMeta.status === "answered") return true;
13643
- if (existsSync49(askOutputPath(cwd, sessionId, candidateId))) return true;
13917
+ if (existsSync48(askOutputPath(cwd, sessionId, candidateId))) return true;
13644
13918
  }
13645
13919
  return false;
13646
13920
  }
@@ -13649,7 +13923,7 @@ function isOrphanZombie(cwd, sessionId, askId) {
13649
13923
  if (!meta) return false;
13650
13924
  if (meta.askedBy !== ORPHAN_ASKED_BY2) return false;
13651
13925
  if (meta.status === "answered") return false;
13652
- if (existsSync49(askOutputPath(cwd, sessionId, askId))) return false;
13926
+ if (existsSync48(askOutputPath(cwd, sessionId, askId))) return false;
13653
13927
  if (meta.orphanTarget?.kind !== "agent") return false;
13654
13928
  let session2;
13655
13929
  try {
@@ -13662,27 +13936,6 @@ function isOrphanZombie(cwd, sessionId, askId) {
13662
13936
  if (!agent2) return false;
13663
13937
  return agent2.status !== "running";
13664
13938
  }
13665
- function isModeGateZombie(cwd, sessionId, askId) {
13666
- const meta = readMeta(cwd, sessionId, askId);
13667
- if (!meta) return false;
13668
- if (meta.modeTransition !== true) return false;
13669
- if (meta.status === "answered") return false;
13670
- if (existsSync49(askOutputPath(cwd, sessionId, askId))) return false;
13671
- const deck = readDecisions(cwd, sessionId, askId);
13672
- if (!deck) return false;
13673
- const source = deck.source;
13674
- const chain = source?.modeChain;
13675
- if (!chain || chain.length === 0) return false;
13676
- let currentMode;
13677
- try {
13678
- const session2 = getSession(cwd, sessionId);
13679
- currentMode = session2.orchestratorCycles[session2.orchestratorCycles.length - 1]?.mode;
13680
- } catch {
13681
- return false;
13682
- }
13683
- if (!currentMode) return false;
13684
- return !chain.some((e) => e.mode === currentMode);
13685
- }
13686
13939
  async function resolveZombie(cwd, sessionId, entry) {
13687
13940
  const { askId, kind } = entry;
13688
13941
  const completedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -13700,11 +13953,6 @@ async function resolveZombie(cwd, sessionId, entry) {
13700
13953
  selectedOptionId = "dismiss";
13701
13954
  freetext = "auto-resolved: agent superseded by replacement (clean-zombies sweep)";
13702
13955
  break;
13703
- case "mode-gate":
13704
- interactionId = "mode-transition";
13705
- selectedOptionId = "ack";
13706
- freetext = "auto-resolved: session advanced past mode-transition (clean-zombies sweep)";
13707
- break;
13708
13956
  }
13709
13957
  writeOutput(cwd, sessionId, askId, [{
13710
13958
  id: interactionId,
@@ -13723,14 +13971,12 @@ async function sweepSession(cwd, sessionId) {
13723
13971
  const meta = readMeta(cwd, sessionId, askId);
13724
13972
  const agentId = meta?.orphanTarget?.kind === "agent" ? meta.orphanTarget.agentId : "unknown";
13725
13973
  zombies.push({ sessionId, askId, kind: "orphan", reason: `agent ${agentId} is no longer running` });
13726
- } else if (isModeGateZombie(cwd, sessionId, askId)) {
13727
- zombies.push({ sessionId, askId, kind: "mode-gate", reason: "session advanced past mode-transition" });
13728
13974
  }
13729
13975
  }
13730
13976
  return zombies;
13731
13977
  }
13732
13978
  function registerCleanZombies(program2) {
13733
- program2.command("clean-zombies").description("Sweep all sessions for zombie asks (heartbeats whose original is answered, orphans whose agent is superseded, stale mode-gate notifications) and dismiss them").addHelpText(
13979
+ program2.command("clean-zombies").description("Sweep all sessions for zombie asks (heartbeats whose original is answered, orphans whose agent is superseded) and dismiss them").addHelpText(
13734
13980
  "after",
13735
13981
  `
13736
13982
  clean-zombies: sweep all sessions and dismiss stale ask records.
@@ -13743,8 +13989,8 @@ Output (stdout, plain text \u2014 human-readable maintenance summary; not for ag
13743
13989
 
13744
13990
  Effects
13745
13991
  Mutates ask records: writes response.json and updates status to "answered" for each
13746
- zombie found. Dismisses heartbeat asks whose original was answered, orphan asks whose
13747
- target agent is no longer running, and mode-gate asks the session has advanced past.
13992
+ zombie found. Dismisses heartbeat asks whose original was answered and orphan asks whose
13993
+ target agent is no longer running.
13748
13994
 
13749
13995
  Exit codes: 0 ok`
13750
13996
  ).action(async () => {
@@ -13757,7 +14003,7 @@ Exit codes: 0 ok`
13757
14003
  const allZombies = [];
13758
14004
  const sessionsSweept = /* @__PURE__ */ new Set();
13759
14005
  for (const [sessionId, cwd] of entries) {
13760
- if (!existsSync49(statePath(cwd, sessionId))) continue;
14006
+ if (!existsSync48(statePath(cwd, sessionId))) continue;
13761
14007
  try {
13762
14008
  const zombies = await sweepSession(cwd, sessionId);
13763
14009
  if (zombies.length > 0) {
@@ -13773,8 +14019,7 @@ Exit codes: 0 ok`
13773
14019
  }
13774
14020
  const summary = {
13775
14021
  heartbeats: allZombies.filter((z4) => z4.kind === "heartbeat").length,
13776
- orphans: allZombies.filter((z4) => z4.kind === "orphan").length,
13777
- modeGates: allZombies.filter((z4) => z4.kind === "mode-gate").length
14022
+ orphans: allZombies.filter((z4) => z4.kind === "orphan").length
13778
14023
  };
13779
14024
  const total = allZombies.length;
13780
14025
  if (total === 0) {
@@ -13782,7 +14027,7 @@ Exit codes: 0 ok`
13782
14027
  return;
13783
14028
  }
13784
14029
  for (const [sessionId, cwd] of entries) {
13785
- if (!existsSync49(statePath(cwd, sessionId))) continue;
14030
+ if (!existsSync48(statePath(cwd, sessionId))) continue;
13786
14031
  const sessionZombies = allZombies.filter((z4) => z4.sessionId === sessionId);
13787
14032
  for (const zombie of sessionZombies) {
13788
14033
  try {
@@ -13795,7 +14040,7 @@ Exit codes: 0 ok`
13795
14040
  }
13796
14041
  }
13797
14042
  }
13798
- console.log(`Dismissed ${total} zombie${total === 1 ? "" : "s"} across ${sessionsSweept.size} session${sessionsSweept.size === 1 ? "" : "s"}: ${summary.heartbeats} heartbeat${summary.heartbeats === 1 ? "" : "s"}, ${summary.orphans} orphan${summary.orphans === 1 ? "" : "s"}, ${summary.modeGates} mode-gate${summary.modeGates === 1 ? "" : "s"}.`);
14043
+ console.log(`Dismissed ${total} zombie${total === 1 ? "" : "s"} across ${sessionsSweept.size} session${sessionsSweept.size === 1 ? "" : "s"}: ${summary.heartbeats} heartbeat${summary.heartbeats === 1 ? "" : "s"}, ${summary.orphans} orphan${summary.orphans === 1 ? "" : "s"}.`);
13799
14044
  });
13800
14045
  }
13801
14046
 
@@ -13824,7 +14069,7 @@ var sessionCounts = null;
13824
14069
  var program = new Command();
13825
14070
  program.name("sis").description("tmux-integrated orchestration daemon for Claude Code").helpOption("-h, --help", "print -h for any node or leaf").version(
13826
14071
  JSON.parse(
13827
- readFileSync46(join38(dirname16(fileURLToPath6(import.meta.url)), "..", "package.json"), "utf-8")
14072
+ readFileSync47(join39(dirname16(fileURLToPath6(import.meta.url)), "..", "package.json"), "utf-8")
13828
14073
  ).version,
13829
14074
  "--version",
13830
14075
  "output the version number"
@@ -13936,7 +14181,7 @@ registerHomeInit(diagnostic);
13936
14181
  var args = process.argv.slice(2);
13937
14182
  var firstArg = args[0];
13938
14183
  var skipWelcome = ["session", "start", "dashboard", "agent", "orch", "ask", "ui", "segment", "admin", "feedback", "help", "--help", "--version"];
13939
- if (!existsSync50(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
14184
+ if (!existsSync49(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
13940
14185
  mkdirSync21(globalDir(), { recursive: true });
13941
14186
  console.log("");
13942
14187
  console.log(" Welcome to Sisyphus. Run 'sis admin install setup' to get started.");