sisyphi 1.1.38 → 1.1.39

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
@@ -249,13 +249,13 @@ var init_env = __esm({
249
249
  });
250
250
 
251
251
  // src/shared/exec.ts
252
- import { execSync as execSync8 } from "child_process";
252
+ import { execSync as execSync9 } from "child_process";
253
253
  function exec2(cmd, cwd, timeoutMs = 3e4) {
254
- return execSync8(cmd, { encoding: "utf-8", env: EXEC_ENV, cwd, timeout: timeoutMs }).trim();
254
+ return execSync9(cmd, { encoding: "utf-8", env: EXEC_ENV, cwd, timeout: timeoutMs }).trim();
255
255
  }
256
256
  function execSafe(cmd, cwd, timeoutMs) {
257
257
  try {
258
- return execSync8(cmd, { encoding: "utf-8", env: EXEC_ENV, cwd, stdio: ["pipe", "pipe", "pipe"], timeout: timeoutMs }).trim();
258
+ return execSync9(cmd, { encoding: "utf-8", env: EXEC_ENV, cwd, stdio: ["pipe", "pipe", "pipe"], timeout: timeoutMs }).trim();
259
259
  } catch {
260
260
  return null;
261
261
  }
@@ -290,9 +290,9 @@ __export(tmux_exports, {
290
290
  switchToSession: () => switchToSession,
291
291
  windowExists: () => windowExists
292
292
  });
293
- import { execSync as execSync17 } from "child_process";
293
+ import { execSync as execSync18 } from "child_process";
294
294
  import { join as join27 } from "path";
295
- import { readFileSync as readFileSync29, writeFileSync as writeFileSync16, mkdtempSync, rmSync as rmSync6, cpSync as cpSync3, existsSync as existsSync26, mkdirSync as mkdirSync13 } from "fs";
295
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync16, mkdtempSync, rmSync as rmSync6, cpSync as cpSync3, existsSync as existsSync27, mkdirSync as mkdirSync13 } from "fs";
296
296
  import { tmpdir as tmpdir3 } from "os";
297
297
  function getWindowId() {
298
298
  const pane = process.env["TMUX_PANE"];
@@ -312,7 +312,7 @@ function windowExists(windowId) {
312
312
  }
313
313
  function listAllWindowIds() {
314
314
  try {
315
- const output = execSync17('tmux list-windows -a -F "#{window_id}"', { encoding: "utf-8", env: EXEC_ENV });
315
+ const output = execSync18('tmux list-windows -a -F "#{window_id}"', { encoding: "utf-8", env: EXEC_ENV });
316
316
  return new Set(output.trim().split("\n").filter(Boolean));
317
317
  } catch {
318
318
  return /* @__PURE__ */ new Set();
@@ -346,7 +346,7 @@ function registerDashboardWindow(cwd) {
346
346
  function setupCompanionPlugin() {
347
347
  const srcDir = join27(import.meta.dirname, "templates", "companion-plugin");
348
348
  const destDir = join27(globalDir(), "companion-plugin");
349
- if (!existsSync26(destDir)) mkdirSync13(destDir, { recursive: true });
349
+ if (!existsSync27(destDir)) mkdirSync13(destDir, { recursive: true });
350
350
  cpSync3(srcDir, destDir, { recursive: true });
351
351
  return destDir;
352
352
  }
@@ -376,7 +376,7 @@ function openCompanionPane(cwd) {
376
376
  const templatePath2 = join27(import.meta.dirname, "templates", "dashboard-claude.md");
377
377
  let template;
378
378
  try {
379
- template = readFileSync29(templatePath2, "utf-8");
379
+ template = readFileSync30(templatePath2, "utf-8");
380
380
  } catch {
381
381
  template = `You are a Sisyphus dashboard companion. Help the user manage multi-agent sessions.
382
382
  Project: ${cwd}
@@ -404,7 +404,7 @@ function editInPopup(cwd, editor, opts) {
404
404
  try {
405
405
  writeFileSync16(filePath, opts?.content ? opts.content : "", "utf-8");
406
406
  openEditorPopup(cwd, editor, filePath, opts?.size);
407
- const result = readFileSync29(filePath, "utf-8").trim();
407
+ const result = readFileSync30(filePath, "utf-8").trim();
408
408
  return result || null;
409
409
  } finally {
410
410
  rmSync6(tmpDir, { recursive: true, force: true });
@@ -416,38 +416,38 @@ function promptInPopup(prompt, opts) {
416
416
  const outFile = join27(tmpDir, "result");
417
417
  try {
418
418
  const script = `printf ${shellQuote(prompt + " ")} && read -r line && printf '%s' "$line" > ${shellQuote(outFile)}`;
419
- execSync17(
419
+ execSync18(
420
420
  `tmux display-popup -E -w ${w} -h ${h} ${shellQuote(`bash -c ${shellQuote(script)}`)}`,
421
421
  { stdio: "inherit", env: EXEC_ENV }
422
422
  );
423
- if (!existsSync26(outFile)) return null;
424
- const result = readFileSync29(outFile, "utf-8").trim();
423
+ if (!existsSync27(outFile)) return null;
424
+ const result = readFileSync30(outFile, "utf-8").trim();
425
425
  return result || null;
426
426
  } finally {
427
427
  rmSync6(tmpDir, { recursive: true, force: true });
428
428
  }
429
429
  }
430
430
  function openLogPopup() {
431
- execSync17(
431
+ execSync18(
432
432
  `tmux display-popup -E -w 90% -h 80% ${shellQuote("tail -f ~/.sisyphus/daemon.log")}`,
433
433
  { stdio: "inherit", env: EXEC_ENV }
434
434
  );
435
435
  }
436
436
  function openShellPopup(cwd, command) {
437
- execSync17(
437
+ execSync18(
438
438
  `tmux display-popup -E -w 90% -h 80% -d ${shellQuote(cwd)} ${shellQuote(`sh -c '${command.replace(/'/g, "'\\''")}; echo; echo "Press enter to close"; read'`)}`,
439
439
  { stdio: "inherit", env: EXEC_ENV }
440
440
  );
441
441
  }
442
442
  function openInFileManager(path) {
443
- execSync17(`open ${shellQuote(path)}`, { stdio: "inherit", env: EXEC_ENV });
443
+ execSync18(`open ${shellQuote(path)}`, { stdio: "inherit", env: EXEC_ENV });
444
444
  }
445
445
  function openClaudeResumePopup(cwd, claudeSessionId, resumeEnv, resumeArgs) {
446
446
  const pathEnv = augmentedPath();
447
447
  const envPrefix = resumeEnv ? `${resumeEnv} && ` : "";
448
448
  const args2 = resumeArgs ? `${resumeArgs} --resume ${shellQuote(claudeSessionId)}` : `--resume ${shellQuote(claudeSessionId)}`;
449
449
  const cmd = `${envPrefix}PATH=${shellQuote(pathEnv)} claude ${args2}`;
450
- execSync17(
450
+ execSync18(
451
451
  `tmux display-popup -E -w 90% -h 80% -d ${shellQuote(cwd)} ${shellQuote(cmd)}`,
452
452
  { stdio: "inherit", env: EXEC_ENV }
453
453
  );
@@ -507,12 +507,12 @@ function openEditorPopup(cwd, editor, filePath, size) {
507
507
  const { w = "90%", h = "90%" } = size ?? {};
508
508
  const editorBin = editor.split(/\s+/)[0].split("/").pop();
509
509
  if (TERMINAL_EDITORS.has(editorBin)) {
510
- execSync17(
510
+ execSync18(
511
511
  `tmux display-popup -E -w ${w} -h ${h} -d ${shellQuote(cwd)} ${shellQuote(`${editor} ${shellQuote(filePath)}`)}`,
512
512
  { stdio: "inherit", env: EXEC_ENV }
513
513
  );
514
514
  } else {
515
- execSync17(`${editor} ${shellQuote(filePath)}`, { stdio: "inherit", cwd, env: EXEC_ENV });
515
+ execSync18(`${editor} ${shellQuote(filePath)}`, { stdio: "inherit", cwd, env: EXEC_ENV });
516
516
  }
517
517
  }
518
518
  var TERMINAL_EDITORS;
@@ -539,14 +539,14 @@ __export(creds_exports, {
539
539
  readTailscaleEnv: () => readTailscaleEnv,
540
540
  writeTailscaleEnv: () => writeTailscaleEnv
541
541
  });
542
- import { chmodSync as chmodSync3, existsSync as existsSync27, mkdirSync as mkdirSync15, readFileSync as readFileSync31 } from "fs";
542
+ import { chmodSync as chmodSync3, existsSync as existsSync28, mkdirSync as mkdirSync15, readFileSync as readFileSync32 } from "fs";
543
543
  import { createInterface as createInterface4 } from "readline";
544
544
  function isValidProvider(value) {
545
545
  return PROVIDERS.includes(value);
546
546
  }
547
547
  function ensureDeployDir() {
548
548
  const dir = deployDir();
549
- if (!existsSync27(dir)) mkdirSync15(dir, { recursive: true, mode: 448 });
549
+ if (!existsSync28(dir)) mkdirSync15(dir, { recursive: true, mode: 448 });
550
550
  }
551
551
  function parseEnvFile(text) {
552
552
  const out = {};
@@ -572,8 +572,8 @@ function serializeEnvFile(values) {
572
572
  return lines.join("\n") + "\n";
573
573
  }
574
574
  function readEnvFile(path) {
575
- if (!existsSync27(path)) return null;
576
- return parseEnvFile(readFileSync31(path, "utf-8"));
575
+ if (!existsSync28(path)) return null;
576
+ return parseEnvFile(readFileSync32(path, "utf-8"));
577
577
  }
578
578
  function writeEnvFile(path, values) {
579
579
  ensureDeployDir();
@@ -668,7 +668,7 @@ var init_creds = __esm({
668
668
 
669
669
  // src/cli/index.ts
670
670
  import { Command } from "commander";
671
- import { existsSync as existsSync34, mkdirSync as mkdirSync17, readFileSync as readFileSync36 } from "fs";
671
+ import { existsSync as existsSync35, mkdirSync as mkdirSync17, readFileSync as readFileSync37 } from "fs";
672
672
  import { dirname as dirname13, join as join32 } from "path";
673
673
  import { fileURLToPath as fileURLToPath5 } from "url";
674
674
 
@@ -970,6 +970,97 @@ function userTmuxConfPath() {
970
970
  if (existsSync2(dotfile)) return dotfile;
971
971
  return null;
972
972
  }
973
+ var SISYPHUS_CLIP_SCRIPT = `#!/bin/bash
974
+ # Cross-platform clipboard wrapper.
975
+ # sisyphus-clip # read stdin, write to clipboard
976
+ # sisyphus-clip copy # same as default
977
+ # sisyphus-clip paste # write clipboard contents to stdout
978
+
979
+ mode="\${1:-copy}"
980
+ uname_s="$(uname -s 2>/dev/null || echo unknown)"
981
+
982
+ is_wsl=0
983
+ if [ "$uname_s" = "Linux" ]; then
984
+ if [ -n "\${WSL_DISTRO_NAME:-}\${WSL_INTEROP:-}" ]; then is_wsl=1; fi
985
+ if [ -r /proc/version ] && grep -qiE 'microsoft|wsl' /proc/version 2>/dev/null; then is_wsl=1; fi
986
+ fi
987
+
988
+ case "$mode" in
989
+ copy)
990
+ if [ "$uname_s" = "Darwin" ]; then
991
+ exec pbcopy
992
+ elif [ "$is_wsl" = "1" ] && command -v clip.exe >/dev/null 2>&1; then
993
+ exec clip.exe
994
+ elif [ -n "\${WAYLAND_DISPLAY:-}" ] && command -v wl-copy >/dev/null 2>&1; then
995
+ exec wl-copy
996
+ elif command -v xclip >/dev/null 2>&1; then
997
+ exec xclip -selection clipboard
998
+ elif command -v xsel >/dev/null 2>&1; then
999
+ exec xsel --clipboard --input
1000
+ else
1001
+ echo "sisyphus-clip: no clipboard backend (install xclip / wl-clipboard / clip.exe)" >&2
1002
+ exit 1
1003
+ fi
1004
+ ;;
1005
+ paste)
1006
+ if [ "$uname_s" = "Darwin" ]; then
1007
+ exec pbpaste
1008
+ elif [ "$is_wsl" = "1" ] && command -v powershell.exe >/dev/null 2>&1; then
1009
+ # Get-Clipboard appends \\r\\n; strip the trailing CR.
1010
+ powershell.exe -NoProfile -Command Get-Clipboard | sed 's/\\r$//'
1011
+ exit "\${PIPESTATUS[0]:-0}"
1012
+ elif [ -n "\${WAYLAND_DISPLAY:-}" ] && command -v wl-paste >/dev/null 2>&1; then
1013
+ exec wl-paste --no-newline
1014
+ elif command -v xclip >/dev/null 2>&1; then
1015
+ exec xclip -selection clipboard -o
1016
+ elif command -v xsel >/dev/null 2>&1; then
1017
+ exec xsel --clipboard --output
1018
+ else
1019
+ echo "sisyphus-clip: no clipboard backend (install xclip / wl-clipboard / powershell.exe)" >&2
1020
+ exit 1
1021
+ fi
1022
+ ;;
1023
+ *)
1024
+ echo "Usage: sisyphus-clip [copy|paste]" >&2
1025
+ exit 2
1026
+ ;;
1027
+ esac
1028
+ `;
1029
+ var SISYPHUS_OPEN_SCRIPT = `#!/bin/bash
1030
+ # Cross-platform "open path in default file manager / handler".
1031
+ # sisyphus-open <path>
1032
+
1033
+ if [ $# -lt 1 ]; then
1034
+ echo "Usage: sisyphus-open <path>" >&2
1035
+ exit 2
1036
+ fi
1037
+ target="$1"
1038
+
1039
+ uname_s="$(uname -s 2>/dev/null || echo unknown)"
1040
+ is_wsl=0
1041
+ if [ "$uname_s" = "Linux" ]; then
1042
+ if [ -n "\${WSL_DISTRO_NAME:-}\${WSL_INTEROP:-}" ]; then is_wsl=1; fi
1043
+ if [ -r /proc/version ] && grep -qiE 'microsoft|wsl' /proc/version 2>/dev/null; then is_wsl=1; fi
1044
+ fi
1045
+
1046
+ if [ "$uname_s" = "Darwin" ]; then
1047
+ exec open "$target"
1048
+ elif [ "$is_wsl" = "1" ] && command -v explorer.exe >/dev/null 2>&1; then
1049
+ win="$target"
1050
+ if command -v wslpath >/dev/null 2>&1; then
1051
+ win=$(wslpath -w "$target" 2>/dev/null || echo "$target")
1052
+ fi
1053
+ # explorer.exe returns 1 even on success when launching a new window \u2014 ignore.
1054
+ explorer.exe "$win" >/dev/null 2>&1 || true
1055
+ elif command -v xdg-open >/dev/null 2>&1; then
1056
+ exec xdg-open "$target"
1057
+ else
1058
+ echo "sisyphus-open: no opener available (install xdg-utils on Linux)" >&2
1059
+ exit 1
1060
+ fi
1061
+ `;
1062
+ var CLIP_BIN = "$HOME/.sisyphus/bin/sisyphus-clip";
1063
+ var OPEN_BIN = "$HOME/.sisyphus/bin/sisyphus-open";
973
1064
  var CYCLE_SCRIPT = `#!/bin/bash
974
1065
  # Target by $N session ID (column 5 in TSV) \u2014 tmux -t <name> can substring-match
975
1066
  # the wrong session under sparse env.
@@ -1298,7 +1389,7 @@ read -n 1 -s -r -p "Press a key to close."
1298
1389
  `;
1299
1390
  var RESTART_AGENT_SCRIPT = `#!/bin/bash
1300
1391
  # Pick a sisyphus agent and restart it (fzf picker with confirm for running agents).
1301
- # Assumes macOS (fzf optional). Requires \`sis status --json\`.
1392
+ # fzf optional. Requires \`sis status --json\`.
1302
1393
  ${SESSION_RESOLVE}
1303
1394
 
1304
1395
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
@@ -1354,14 +1445,13 @@ file="$cwd/.sisyphus/sessions/$session_id/goal.md"
1354
1445
  exec nvim "$file"
1355
1446
  `;
1356
1447
  var OPEN_DIR_SCRIPT = `#!/bin/bash
1357
- # Open session dir in Finder (macOS).
1358
- # macOS-only \u2014 Linux/Windows port deferred.
1448
+ # Open session dir in the platform file manager (Finder/Explorer/xdg-open).
1359
1449
  # Run from a sisyphus session pane, not the home dashboard.
1360
1450
  ${SESSION_RESOLVE}
1361
1451
 
1362
1452
  dir="$cwd/.sisyphus/sessions/$session_id"
1363
1453
  [ ! -d "$dir" ] && { tmux display-message "Session dir not found: $dir"; exit 0; }
1364
- exec open "$dir"
1454
+ exec ${OPEN_BIN} "$dir"
1365
1455
  `;
1366
1456
  var OPEN_LOGS_SCRIPT = `#!/bin/bash
1367
1457
  # Tail the newest cycle log for this session in a popup.
@@ -1522,7 +1612,7 @@ fi
1522
1612
  `;
1523
1613
  var JUMP_TO_PANE_SCRIPT = `#!/bin/bash
1524
1614
  # Pick a sisyphus agent and jump to its tmux pane.
1525
- # Assumes macOS (pbcopy, fzf optional). Requires \`sis status --json\`.
1615
+ # fzf optional. Requires \`sis status --json\`.
1526
1616
  ${SESSION_RESOLVE}
1527
1617
 
1528
1618
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
@@ -1568,7 +1658,7 @@ tmux select-pane -t "$target_pane"
1568
1658
  `;
1569
1659
  var MSG_AGENT_SCRIPT = `#!/bin/bash
1570
1660
  # Pick a sisyphus agent and send it a message via nvim.
1571
- # Assumes macOS (fzf optional). Requires \`sis status --json\` and \`--agent\` on message.
1661
+ # fzf optional. Requires \`sis status --json\` and \`--agent\` on message.
1572
1662
  ${SESSION_RESOLVE}
1573
1663
 
1574
1664
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
@@ -1610,7 +1700,7 @@ exec sis message --session "$session_id" --agent "\${ids[$idx]}" "$(cat "$tmpfil
1610
1700
  `;
1611
1701
  var RERUN_AGENT_SCRIPT = `#!/bin/bash
1612
1702
  # Pick a sisyphus agent and spawn a retry with its original instruction.
1613
- # Assumes macOS (fzf optional). Requires \`sis status --json\`.
1703
+ # fzf optional. Requires \`sis status --json\`.
1614
1704
  ${SESSION_RESOLVE}
1615
1705
 
1616
1706
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
@@ -1661,7 +1751,7 @@ exec sis agent spawn --session "$session_id" --agent-type "\${atypes[$idx]}" --n
1661
1751
  `;
1662
1752
  var OPEN_CLAUDE_AGENT_SCRIPT = `#!/bin/bash
1663
1753
  # Pick a sisyphus agent or orchestrator cycle and resume its Claude session.
1664
- # Assumes macOS (fzf optional). Requires \`sis status --json\`.
1754
+ # fzf optional. Requires \`sis status --json\`.
1665
1755
  ${SESSION_RESOLVE}
1666
1756
 
1667
1757
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
@@ -1709,7 +1799,7 @@ cd "$cwd" && exec claude --resume "$cid"
1709
1799
  var TAIL_AGENT_LOGS_SCRIPT = `#!/bin/bash
1710
1800
  # Pick a sisyphus agent and view its tmux pane scrollback (last 2000 lines) in less.
1711
1801
  # Uses tmux capture-pane \u2014 no tail -f, no pipe-pane side effects.
1712
- # Assumes macOS (fzf optional). Requires \`sis status --json\`.
1802
+ # fzf optional. Requires \`sis status --json\`.
1713
1803
  ${SESSION_RESOLVE}
1714
1804
 
1715
1805
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
@@ -1751,7 +1841,7 @@ tmux capture-pane -t "$target_pane" -p -S -2000 | less +G
1751
1841
  `;
1752
1842
  var KILL_AGENT_SCRIPT = `#!/bin/bash
1753
1843
  # Pick a sisyphus agent and kill it (with red confirmation prompt).
1754
- # Assumes macOS (fzf optional). Requires \`sis status --json\` and \`sis agent kill\`.
1844
+ # fzf optional. Requires \`sis status --json\` and \`sis agent kill\`.
1755
1845
  ${SESSION_RESOLVE}
1756
1846
 
1757
1847
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
@@ -1794,7 +1884,7 @@ read -n 1 -s -r -p "Press a key to close."
1794
1884
  `;
1795
1885
  var COPY_AGENT_ID_SCRIPT = `#!/bin/bash
1796
1886
  # Pick a sisyphus agent and copy its ID to clipboard.
1797
- # Assumes macOS (pbcopy, fzf optional). Requires \`sis status --json\`.
1887
+ # Requires \`sis status --json\`. fzf optional.
1798
1888
  ${SESSION_RESOLVE}
1799
1889
 
1800
1890
  command -v jq &>/dev/null || { echo "jq required"; sleep 1; exit 1; }
@@ -1829,60 +1919,56 @@ else
1829
1919
  fi
1830
1920
 
1831
1921
  aid="\${ids[$idx]}"
1832
- printf '%s' "$aid" | pbcopy
1922
+ printf '%s' "$aid" | ${CLIP_BIN}
1833
1923
  tmux display-message "Copied $aid"
1834
1924
  `;
1835
1925
  var COPY_LOGS_SCRIPT = `#!/bin/bash
1836
1926
  # Copy last 200 lines of the newest cycle log to clipboard.
1837
- # Assumes macOS (pbcopy).
1838
1927
  ${SESSION_RESOLVE}
1839
1928
 
1840
1929
  dir="$cwd/.sisyphus/sessions/$session_id/logs"
1841
1930
  [ -d "$dir" ] || { tmux display-message "No logs dir"; exit 0; }
1842
1931
  latest=$(ls -t "$dir"/cycle-*.md 2>/dev/null | head -1)
1843
1932
  [ -z "$latest" ] && { tmux display-message "No cycle logs yet"; exit 0; }
1844
- tail -n 200 "$latest" | pbcopy
1933
+ tail -n 200 "$latest" | ${CLIP_BIN}
1845
1934
  tmux display-message "Copied last 200 lines of $(basename "$latest")"
1846
1935
  `;
1847
1936
  var COPY_LATEST_REPORT_SCRIPT = `#!/bin/bash
1848
1937
  # Copy the newest report file to clipboard.
1849
- # Assumes macOS (pbcopy).
1850
1938
  ${SESSION_RESOLVE}
1851
1939
 
1852
1940
  dir="$cwd/.sisyphus/sessions/$session_id/reports"
1853
1941
  [ -d "$dir" ] || { tmux display-message "No reports dir"; exit 0; }
1854
1942
  latest=$(ls -t "$dir" 2>/dev/null | head -1)
1855
1943
  [ -z "$latest" ] && { tmux display-message "No reports yet"; exit 0; }
1856
- cat "$dir/$latest" | pbcopy
1944
+ cat "$dir/$latest" | ${CLIP_BIN}
1857
1945
  tmux display-message "Copied $latest"
1858
1946
  `;
1859
1947
  var COPY_PATH_SCRIPT = `#!/bin/bash
1860
1948
  # Copy the session directory path to clipboard.
1861
- # Assumes macOS (pbcopy).
1862
1949
  ${SESSION_RESOLVE}
1863
1950
 
1864
- printf '%s' "$cwd/.sisyphus/sessions/$session_id" | pbcopy
1951
+ printf '%s' "$cwd/.sisyphus/sessions/$session_id" | ${CLIP_BIN}
1865
1952
  tmux display-message "Copied session path"
1866
1953
  `;
1867
1954
  var COPY_ID_SCRIPT = `#!/bin/bash
1868
1955
  # Copy the session ID to clipboard.
1869
- # Assumes macOS (pbcopy).
1870
1956
  ${SESSION_RESOLVE}
1871
1957
 
1872
- printf '%s' "$session_id" | pbcopy
1958
+ printf '%s' "$session_id" | ${CLIP_BIN}
1873
1959
  tmux display-message "Copied session ID"
1874
1960
  `;
1875
1961
  var COPY_CONTEXT_SCRIPT = `#!/bin/bash
1876
1962
  # Copy the session context XML to clipboard.
1877
- # Assumes macOS (pbcopy). Requires \`sis session context\`.
1963
+ # Requires \`sis session context\`.
1878
1964
  ${SESSION_RESOLVE}
1879
1965
 
1880
- sis session context "$session_id" --cwd "$cwd" | pbcopy
1966
+ sis session context "$session_id" --cwd "$cwd" | ${CLIP_BIN}
1881
1967
  tmux display-message "Copied session context (XML)"
1882
1968
  `;
1883
1969
  var EDIT_CONTEXT_FILE_SCRIPT = `#!/bin/bash
1884
1970
  # Pick a context file for the current session and open it in nvim.
1885
- # Excludes archive/ subdirectory. Assumes macOS (fzf optional).
1971
+ # Excludes archive/ subdirectory. fzf optional.
1886
1972
  ${SESSION_RESOLVE}
1887
1973
 
1888
1974
  ctx_dir="$cwd/.sisyphus/sessions/$session_id/context"
@@ -1914,17 +2000,10 @@ file="$ctx_dir/$picked"
1914
2000
  exec nvim "$file"
1915
2001
  `;
1916
2002
  var QUICK_SPAWN_EXPLORE_SCRIPT = `#!/bin/bash
1917
- # Spawn an Explore agent with the macOS clipboard contents as the instruction.
1918
- # macOS only \u2014 pbpaste hard dependency.
2003
+ # Spawn an Explore agent with the clipboard contents as the instruction.
1919
2004
  ${SESSION_RESOLVE}
1920
2005
 
1921
- if ! command -v pbpaste >/dev/null 2>&1; then
1922
- echo "pbpaste not found \u2014 macOS only for now"
1923
- read -n 1 -s -r -p "Press a key to close."
1924
- exit 1
1925
- fi
1926
-
1927
- instruction=$(pbpaste)
2006
+ instruction=$(${CLIP_BIN} paste 2>/dev/null)
1928
2007
  if [ -z "\${instruction// }" ]; then
1929
2008
  echo "Clipboard is empty \u2014 copy a task description first"
1930
2009
  read -n 1 -s -r -p "Press a key to close."
@@ -1948,17 +2027,10 @@ exit_code=$?
1948
2027
  exit $exit_code
1949
2028
  `;
1950
2029
  var QUICK_SPAWN_DEBUG_SCRIPT = `#!/bin/bash
1951
- # Spawn a Debug agent with the macOS clipboard contents as the instruction.
1952
- # macOS only \u2014 pbpaste hard dependency.
2030
+ # Spawn a Debug agent with the clipboard contents as the instruction.
1953
2031
  ${SESSION_RESOLVE}
1954
2032
 
1955
- if ! command -v pbpaste >/dev/null 2>&1; then
1956
- echo "pbpaste not found \u2014 macOS only for now"
1957
- read -n 1 -s -r -p "Press a key to close."
1958
- exit 1
1959
- fi
1960
-
1961
- instruction=$(pbpaste)
2033
+ instruction=$(${CLIP_BIN} paste 2>/dev/null)
1962
2034
  if [ -z "\${instruction// }" ]; then
1963
2035
  echo "Clipboard is empty \u2014 copy a task description first"
1964
2036
  read -n 1 -s -r -p "Press a key to close."
@@ -2056,6 +2128,8 @@ function installAllScripts() {
2056
2128
  installScript("sisyphus-kill-pane", KILL_PANE_SCRIPT);
2057
2129
  installScript("sisyphus-new", NEW_PROMPT_SCRIPT);
2058
2130
  installScript("sisyphus-msg", MESSAGE_SCRIPT);
2131
+ installScript("sisyphus-clip", SISYPHUS_CLIP_SCRIPT);
2132
+ installScript("sisyphus-open", SISYPHUS_OPEN_SCRIPT);
2059
2133
  installScript("sisyphus-kill-session", KILL_SESSION_SCRIPT);
2060
2134
  installScript("sisyphus-delete-session", DELETE_SESSION_SCRIPT);
2061
2135
  installScript("sisyphus-status-popup", STATUS_POPUP_SCRIPT);
@@ -3571,7 +3645,7 @@ function registerMessage(program2) {
3571
3645
  }
3572
3646
 
3573
3647
  // src/cli/commands/ask.ts
3574
- import { existsSync as existsSync11, readFileSync as readFileSync13, watchFile, unwatchFile } from "fs";
3648
+ import { existsSync as existsSync12, readFileSync as readFileSync14, watchFile, unwatchFile } from "fs";
3575
3649
  import { join as join13, resolve as resolve4 } from "path";
3576
3650
  import { ulid } from "ulid";
3577
3651
 
@@ -3692,7 +3766,7 @@ function parseDeck(deckPath) {
3692
3766
 
3693
3767
  // src/daemon/ask-store.ts
3694
3768
  init_paths();
3695
- import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync12, readdirSync as readdirSync3 } from "fs";
3769
+ import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync13, readdirSync as readdirSync3 } from "fs";
3696
3770
 
3697
3771
  // src/daemon/history.ts
3698
3772
  init_paths();
@@ -3786,47 +3860,115 @@ init_atomic();
3786
3860
  // src/daemon/notify.ts
3787
3861
  init_shell();
3788
3862
  import { spawn, execFile } from "child_process";
3789
- import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync9 } from "fs";
3863
+ import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync10 } from "fs";
3790
3864
  import { join as join12 } from "path";
3791
3865
  import { homedir as homedir7 } from "os";
3866
+
3867
+ // src/shared/platform.ts
3868
+ import { execSync as execSync8 } from "child_process";
3869
+ import { existsSync as existsSync9, readFileSync as readFileSync12 } from "fs";
3870
+ var cachedPlatform;
3871
+ function detectPlatform() {
3872
+ if (cachedPlatform) return cachedPlatform;
3873
+ if (process.platform === "darwin") {
3874
+ cachedPlatform = "darwin";
3875
+ } else if (process.platform === "win32") {
3876
+ cachedPlatform = "win32";
3877
+ } else if (process.platform === "linux") {
3878
+ cachedPlatform = isWsl() ? "wsl" : "linux";
3879
+ } else {
3880
+ cachedPlatform = "unknown";
3881
+ }
3882
+ return cachedPlatform;
3883
+ }
3884
+ function isWsl() {
3885
+ if (process.env["WSL_DISTRO_NAME"] || process.env["WSL_INTEROP"]) return true;
3886
+ try {
3887
+ if (existsSync9("/proc/version")) {
3888
+ const v = readFileSync12("/proc/version", "utf-8").toLowerCase();
3889
+ if (v.includes("microsoft") || v.includes("wsl")) return true;
3890
+ }
3891
+ } catch {
3892
+ }
3893
+ return false;
3894
+ }
3895
+ function isWslHost() {
3896
+ return detectPlatform() === "wsl";
3897
+ }
3898
+ function isLinuxLike() {
3899
+ const p = detectPlatform();
3900
+ return p === "linux" || p === "wsl";
3901
+ }
3902
+ var cmdCache = /* @__PURE__ */ new Map();
3903
+ function hasCommand(cmd) {
3904
+ const cached = cmdCache.get(cmd);
3905
+ if (cached !== void 0) return cached;
3906
+ try {
3907
+ execSync8(`command -v ${cmd}`, { stdio: "pipe", shell: "/bin/sh" });
3908
+ cmdCache.set(cmd, true);
3909
+ return true;
3910
+ } catch {
3911
+ cmdCache.set(cmd, false);
3912
+ return false;
3913
+ }
3914
+ }
3915
+ function platformLabel() {
3916
+ switch (detectPlatform()) {
3917
+ case "darwin":
3918
+ return "macOS";
3919
+ case "wsl": {
3920
+ const distro = process.env["WSL_DISTRO_NAME"];
3921
+ return distro ? `WSL (${distro})` : "WSL";
3922
+ }
3923
+ case "linux":
3924
+ return "Linux";
3925
+ case "win32":
3926
+ return "Windows (native)";
3927
+ default:
3928
+ return "unknown";
3929
+ }
3930
+ }
3931
+
3932
+ // src/daemon/notify.ts
3792
3933
  var TMUX_SOCKET = `/tmp/tmux-${process.getuid?.() ?? 0}/default`;
3793
3934
  var SWITCH_SCRIPT = [
3794
3935
  "#!/bin/bash",
3795
3936
  'SESSION="$1"',
3796
3937
  `TMUX_SOCKET="${TMUX_SOCKET}"`,
3797
- "TMUX=/opt/homebrew/bin/tmux",
3798
3938
  "",
3799
3939
  "# Find any attached client (user is likely on a different session)",
3800
- `CLIENT_TTY=$("$TMUX" -S "$TMUX_SOCKET" list-clients -F '#{client_tty}' 2>/dev/null | head -1)`,
3940
+ `CLIENT_TTY=$(tmux -S "$TMUX_SOCKET" list-clients -F '#{client_tty}' 2>/dev/null | head -1)`,
3801
3941
  '[ -z "$CLIENT_TTY" ] && exit 0',
3802
3942
  "",
3803
3943
  "# Switch that client to the target session",
3804
- '"$TMUX" -S "$TMUX_SOCKET" switch-client -c "$CLIENT_TTY" -t "$SESSION" 2>/dev/null',
3805
- '"$TMUX" -S "$TMUX_SOCKET" select-window -t "$SESSION" 2>/dev/null',
3944
+ 'tmux -S "$TMUX_SOCKET" switch-client -c "$CLIENT_TTY" -t "$SESSION" 2>/dev/null',
3945
+ 'tmux -S "$TMUX_SOCKET" select-window -t "$SESSION" 2>/dev/null',
3806
3946
  "",
3807
- "# Bring iTerm2 to front and select the tab with this client",
3808
- `TTY_SHORT=$(echo "$CLIENT_TTY" | sed 's|/dev/||')`,
3809
- 'osascript -e "',
3810
- ' tell application \\"iTerm2\\"',
3811
- " activate",
3812
- " repeat with w in windows",
3813
- " tell w",
3814
- " repeat with t in tabs",
3815
- " tell t",
3816
- " repeat with s in sessions",
3817
- " tell s",
3818
- ' if tty contains \\"$TTY_SHORT\\" then',
3819
- " select t",
3820
- " return",
3821
- " end if",
3822
- " end tell",
3823
- " end repeat",
3824
- " end tell",
3825
- " end repeat",
3826
- " end tell",
3827
- " end repeat",
3828
- " end tell",
3829
- `" 2>/dev/null || osascript -e 'tell application "iTerm2" to activate' 2>/dev/null`,
3947
+ "# macOS-only: bring iTerm2 to front and select the tab with this client",
3948
+ 'if [ "$(uname -s)" = "Darwin" ] && command -v osascript >/dev/null 2>&1; then',
3949
+ ` TTY_SHORT=$(echo "$CLIENT_TTY" | sed 's|/dev/||')`,
3950
+ ' osascript -e "',
3951
+ ' tell application \\"iTerm2\\"',
3952
+ " activate",
3953
+ " repeat with w in windows",
3954
+ " tell w",
3955
+ " repeat with t in tabs",
3956
+ " tell t",
3957
+ " repeat with s in sessions",
3958
+ " tell s",
3959
+ ' if tty contains \\"$TTY_SHORT\\" then',
3960
+ " select t",
3961
+ " return",
3962
+ " end if",
3963
+ " end tell",
3964
+ " end repeat",
3965
+ " end tell",
3966
+ " end repeat",
3967
+ " end tell",
3968
+ " end repeat",
3969
+ " end tell",
3970
+ ` " 2>/dev/null || osascript -e 'tell application "iTerm2" to activate' 2>/dev/null`,
3971
+ "fi",
3830
3972
  ""
3831
3973
  ].join("\n");
3832
3974
  function ensureSwitchScript() {
@@ -3847,7 +3989,7 @@ function ensureNotifyProcess() {
3847
3989
  return notifyProcess;
3848
3990
  }
3849
3991
  const binary = getNotifyBinary();
3850
- if (!existsSync9(binary)) {
3992
+ if (!existsSync10(binary)) {
3851
3993
  return null;
3852
3994
  }
3853
3995
  notifyProcess = spawn(binary, [], {
@@ -3865,6 +4007,13 @@ function ensureNotifyProcess() {
3865
4007
  notifyProcess.stderr?.unref();
3866
4008
  return notifyProcess;
3867
4009
  }
4010
+ function sendLinuxNotification(title, msg, level) {
4011
+ if (!hasCommand("notify-send")) return false;
4012
+ const urgency = level === "urgent" ? "critical" : "normal";
4013
+ execFile("notify-send", ["--app-name=Sisyphus", `--urgency=${urgency}`, title, msg], () => {
4014
+ });
4015
+ return true;
4016
+ }
3868
4017
  function sendTerminalNotification(titleOrOpts, message, tmuxSession, level) {
3869
4018
  let title;
3870
4019
  let msg;
@@ -3874,33 +4023,41 @@ function sendTerminalNotification(titleOrOpts, message, tmuxSession, level) {
3874
4023
  title = titleOrOpts.title;
3875
4024
  msg = titleOrOpts.message;
3876
4025
  tmuxSess = titleOrOpts.tmuxSession;
3877
- lvl = titleOrOpts.level ?? "urgent";
4026
+ lvl = titleOrOpts.level === void 0 ? "urgent" : titleOrOpts.level;
3878
4027
  } else {
3879
4028
  title = titleOrOpts;
3880
4029
  msg = message;
3881
4030
  tmuxSess = tmuxSession;
3882
- lvl = level ?? "urgent";
4031
+ lvl = level === void 0 ? "urgent" : level;
3883
4032
  }
3884
4033
  if (tmuxSess) ensureSwitchScript();
3885
- const proc = ensureNotifyProcess();
3886
- if (proc?.stdin?.writable) {
3887
- const payload = { title, message: msg, level: lvl };
3888
- if (tmuxSess) payload.tmuxSession = tmuxSess;
3889
- proc.stdin.write(JSON.stringify(payload) + "\n");
4034
+ const platform = detectPlatform();
4035
+ if (platform === "darwin") {
4036
+ const proc = ensureNotifyProcess();
4037
+ if (proc?.stdin?.writable) {
4038
+ const payload = { title, message: msg, level: lvl };
4039
+ if (tmuxSess) payload.tmuxSession = tmuxSess;
4040
+ proc.stdin.write(JSON.stringify(payload) + "\n");
4041
+ return;
4042
+ }
4043
+ const tnArgs = ["-title", title, "-message", msg];
4044
+ if (lvl === "urgent") tnArgs.push("-sound", "default");
4045
+ execFile("terminal-notifier", tnArgs, (err) => {
4046
+ if (err) {
4047
+ const soundClause = lvl === "urgent" ? ' sound name "default"' : "";
4048
+ execFile("osascript", [
4049
+ "-e",
4050
+ `display notification "${escapeAppleScript(msg)}" with title "${escapeAppleScript(title)}"${soundClause}`
4051
+ ], () => {
4052
+ });
4053
+ }
4054
+ });
4055
+ return;
4056
+ }
4057
+ if (platform === "linux" || platform === "wsl") {
4058
+ sendLinuxNotification(title, msg, lvl);
3890
4059
  return;
3891
4060
  }
3892
- const tnArgs = ["-title", title, "-message", msg];
3893
- if (lvl === "urgent") tnArgs.push("-sound", "default");
3894
- execFile("terminal-notifier", tnArgs, (err) => {
3895
- if (err) {
3896
- const soundClause = lvl === "urgent" ? ' sound name "default"' : "";
3897
- execFile("osascript", [
3898
- "-e",
3899
- `display notification "${escapeAppleScript(msg)}" with title "${escapeAppleScript(title)}"${soundClause}`
3900
- ], () => {
3901
- });
3902
- }
3903
- });
3904
4061
  }
3905
4062
 
3906
4063
  // src/daemon/ask-store.ts
@@ -3962,7 +4119,7 @@ function writeDecisions(cwd, sessionId, askId, deck) {
3962
4119
  function readDecisions(cwd, sessionId, askId) {
3963
4120
  const p = askDecisionsPath(cwd, sessionId, askId);
3964
4121
  try {
3965
- return JSON.parse(readFileSync12(p, { encoding: "utf-8" }));
4122
+ return JSON.parse(readFileSync13(p, { encoding: "utf-8" }));
3966
4123
  } catch (_e) {
3967
4124
  return null;
3968
4125
  }
@@ -3975,10 +4132,10 @@ function writeOutput(cwd, sessionId, askId, responses, completedAt) {
3975
4132
  }
3976
4133
  function readMeta(cwd, sessionId, askId) {
3977
4134
  const p = askMetaPath(cwd, sessionId, askId);
3978
- if (!existsSync10(p)) {
4135
+ if (!existsSync11(p)) {
3979
4136
  return null;
3980
4137
  }
3981
- return JSON.parse(readFileSync12(p, "utf-8"));
4138
+ return JSON.parse(readFileSync13(p, "utf-8"));
3982
4139
  }
3983
4140
  async function updateMeta(cwd, sessionId, askId, patch) {
3984
4141
  return withLock(askId, () => {
@@ -4002,7 +4159,7 @@ function buildAutoResponses(deck) {
4002
4159
  }
4003
4160
  async function autoResolveAsk(cwd, sessionId, askId, deck) {
4004
4161
  try {
4005
- if (existsSync10(askOutputPath(cwd, sessionId, askId))) return false;
4162
+ if (existsSync11(askOutputPath(cwd, sessionId, askId))) return false;
4006
4163
  const d = deck ?? readDecisions(cwd, sessionId, askId);
4007
4164
  if (!d) return false;
4008
4165
  const responses = buildAutoResponses(d);
@@ -4099,7 +4256,7 @@ function mintAskId() {
4099
4256
  return ulid();
4100
4257
  }
4101
4258
  function resolveClaudeSessionId(cwd, sessionId, askedBy) {
4102
- if (!existsSync11(statePath(cwd, sessionId))) return void 0;
4259
+ if (!existsSync12(statePath(cwd, sessionId))) return void 0;
4103
4260
  const session2 = getSession(cwd, sessionId);
4104
4261
  if (askedBy === ORCHESTRATOR_ASKED_BY) {
4105
4262
  const last = session2.orchestratorCycles[session2.orchestratorCycles.length - 1];
@@ -4136,7 +4293,7 @@ async function markAnswered(cwd, sessionId, askId) {
4136
4293
  });
4137
4294
  if (meta.blocking && durationMs > 0) {
4138
4295
  try {
4139
- if (existsSync11(statePath(cwd, sessionId))) {
4296
+ if (existsSync12(statePath(cwd, sessionId))) {
4140
4297
  await incrementUserBlockedMs(cwd, sessionId, durationMs, meta.askedAt, meta.askedBy);
4141
4298
  }
4142
4299
  } catch {
@@ -4145,8 +4302,8 @@ async function markAnswered(cwd, sessionId, askId) {
4145
4302
  }
4146
4303
  function waitForOutput(cwd, sessionId, askId, initialPpid) {
4147
4304
  const outputPath = askOutputPath(cwd, sessionId, askId);
4148
- if (existsSync11(outputPath)) {
4149
- return Promise.resolve(JSON.parse(readFileSync13(outputPath, "utf-8")));
4305
+ if (existsSync12(outputPath)) {
4306
+ return Promise.resolve(JSON.parse(readFileSync14(outputPath, "utf-8")));
4150
4307
  }
4151
4308
  return new Promise((res, _rej) => {
4152
4309
  let ppidWatcher;
@@ -4156,9 +4313,9 @@ function waitForOutput(cwd, sessionId, askId, initialPpid) {
4156
4313
  process.removeListener("SIGINT", onSigint);
4157
4314
  };
4158
4315
  const onChange = () => {
4159
- if (!existsSync11(outputPath)) return;
4316
+ if (!existsSync12(outputPath)) return;
4160
4317
  try {
4161
- const out = JSON.parse(readFileSync13(outputPath, "utf-8"));
4318
+ const out = JSON.parse(readFileSync14(outputPath, "utf-8"));
4162
4319
  cleanup();
4163
4320
  res(out);
4164
4321
  } catch (err) {
@@ -4197,7 +4354,7 @@ async function submit(file, opts) {
4197
4354
  const { cwd, sessionId } = resolveSessionEnv(opts);
4198
4355
  const askedBy = process.env.SISYPHUS_AGENT_ID ?? ORCHESTRATOR_ASKED_BY;
4199
4356
  const deckPath = resolve4(file);
4200
- if (!existsSync11(deckPath)) {
4357
+ if (!existsSync12(deckPath)) {
4201
4358
  console.error(`Error: deck file not found: ${deckPath}`);
4202
4359
  process.exit(1);
4203
4360
  }
@@ -4256,8 +4413,8 @@ async function peek(askId, opts) {
4256
4413
  };
4257
4414
  if (meta.completedAt) result.completedAt = meta.completedAt;
4258
4415
  try {
4259
- if (existsSync11(outputPath)) {
4260
- result.output = JSON.parse(readFileSync13(outputPath, "utf-8"));
4416
+ if (existsSync12(outputPath)) {
4417
+ result.output = JSON.parse(readFileSync14(outputPath, "utf-8"));
4261
4418
  }
4262
4419
  } catch (err) {
4263
4420
  if (!(err instanceof SyntaxError)) throw err;
@@ -4564,13 +4721,13 @@ function registerSessionDangerous(program2) {
4564
4721
 
4565
4722
  // src/tui/lib/context.ts
4566
4723
  init_paths();
4567
- import { readFileSync as readFileSync15, readdirSync as readdirSync4 } from "fs";
4724
+ import { readFileSync as readFileSync16, readdirSync as readdirSync4 } from "fs";
4568
4725
 
4569
4726
  // src/tui/lib/reports.ts
4570
- import { readFileSync as readFileSync14 } from "fs";
4727
+ import { readFileSync as readFileSync15 } from "fs";
4571
4728
  function loadReportContent(report) {
4572
4729
  try {
4573
- return readFileSync14(report.filePath, "utf-8");
4730
+ return readFileSync15(report.filePath, "utf-8");
4574
4731
  } catch {
4575
4732
  return report.summary;
4576
4733
  }
@@ -4587,7 +4744,7 @@ function resolveReports(reports) {
4587
4744
  // src/tui/lib/context.ts
4588
4745
  function readFileSafe(filePath) {
4589
4746
  try {
4590
- return readFileSync15(filePath, "utf-8");
4747
+ return readFileSync16(filePath, "utf-8");
4591
4748
  } catch {
4592
4749
  return null;
4593
4750
  }
@@ -4747,12 +4904,12 @@ function registerSessionContext(program2) {
4747
4904
  }
4748
4905
 
4749
4906
  // src/cli/commands/spawn.ts
4750
- import { existsSync as existsSync13 } from "fs";
4907
+ import { existsSync as existsSync14 } from "fs";
4751
4908
  import { join as join15, resolve as resolve5 } from "path";
4752
4909
 
4753
4910
  // src/daemon/frontmatter.ts
4754
4911
  init_paths();
4755
- import { readFileSync as readFileSync16, existsSync as existsSync12, readdirSync as readdirSync5 } from "fs";
4912
+ import { readFileSync as readFileSync17, existsSync as existsSync13, readdirSync as readdirSync5 } from "fs";
4756
4913
  import { homedir as homedir8 } from "os";
4757
4914
  import { join as join14, basename as basename4 } from "path";
4758
4915
  function parseAgentFrontmatter(content) {
@@ -4804,7 +4961,7 @@ function discoverAgentTypes(pluginDir, cwd) {
4804
4961
  if (seen.has(qualifiedName)) continue;
4805
4962
  seen.add(qualifiedName);
4806
4963
  try {
4807
- const content = readFileSync16(join14(dir, file), "utf-8");
4964
+ const content = readFileSync17(join14(dir, file), "utf-8");
4808
4965
  const fm = parseAgentFrontmatter(content);
4809
4966
  results.push({ qualifiedName, source, description: fm.description, model: fm.model });
4810
4967
  } catch {
@@ -4819,7 +4976,7 @@ function discoverAgentTypes(pluginDir, cwd) {
4819
4976
  scanDir(join14(pluginDir, "agents"), "sisyphus", "bundled");
4820
4977
  try {
4821
4978
  const registryPath = join14(homedir8(), ".claude", "plugins", "installed_plugins.json");
4822
- const registry = JSON.parse(readFileSync16(registryPath, "utf-8"));
4979
+ const registry = JSON.parse(readFileSync17(registryPath, "utf-8"));
4823
4980
  const pluginEntries = registry.plugins ?? registry;
4824
4981
  for (const key of Object.keys(pluginEntries)) {
4825
4982
  const atIdx = key.indexOf("@");
@@ -4899,7 +5056,7 @@ function registerSpawn(program2) {
4899
5056
  }
4900
5057
  if (opts.repo && opts.repo !== ".") {
4901
5058
  const repoPath = join15(sisyphusCwd, opts.repo);
4902
- if (!existsSync13(repoPath)) {
5059
+ if (!existsSync14(repoPath)) {
4903
5060
  console.error(`Error: repo directory does not exist: ${repoPath}`);
4904
5061
  process.exit(1);
4905
5062
  }
@@ -5006,7 +5163,7 @@ function registerReport(program2) {
5006
5163
  }
5007
5164
 
5008
5165
  // src/cli/commands/await.ts
5009
- import { existsSync as existsSync14, readFileSync as readFileSync17 } from "fs";
5166
+ import { existsSync as existsSync15, readFileSync as readFileSync18 } from "fs";
5010
5167
  var AWAIT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
5011
5168
  function registerAwait(program2) {
5012
5169
  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)").action(async (agentId, opts) => {
@@ -5030,9 +5187,9 @@ function registerAwait(program2) {
5030
5187
  const shortType = agentType && agentType !== "worker" ? agentType.replace(/^sisyphus:/, "") : "";
5031
5188
  const label = shortType ? `${shortType}-${agentName}` : agentName;
5032
5189
  console.log(`[${status}] ${agentId} (${label})`);
5033
- if (reportPath && existsSync14(reportPath)) {
5190
+ if (reportPath && existsSync15(reportPath)) {
5034
5191
  try {
5035
- const body = readFileSync17(reportPath, "utf-8");
5192
+ const body = readFileSync18(reportPath, "utf-8");
5036
5193
  if (body.length > 0) {
5037
5194
  process.stdout.write(body.endsWith("\n") ? body : body + "\n");
5038
5195
  }
@@ -5156,14 +5313,71 @@ function registerSegmentUnregister(program2) {
5156
5313
  }
5157
5314
 
5158
5315
  // src/cli/commands/setup.ts
5159
- import { execSync as execSync10 } from "child_process";
5316
+ import { execSync as execSync11 } from "child_process";
5160
5317
 
5161
5318
  // src/cli/onboard.ts
5162
- import { execSync as execSync9 } from "child_process";
5163
- import { existsSync as existsSync15, readFileSync as readFileSync18, writeFileSync as writeFileSync8 } from "fs";
5319
+ import { execSync as execSync10 } from "child_process";
5320
+ import { existsSync as existsSync16, readFileSync as readFileSync19, writeFileSync as writeFileSync8 } from "fs";
5164
5321
  import { homedir as homedir9 } from "os";
5165
5322
  import { dirname as dirname5, join as join16 } from "path";
5166
5323
  import { fileURLToPath as fileURLToPath2 } from "url";
5324
+
5325
+ // src/shared/clipboard.ts
5326
+ import { execFileSync as execFileSync2, spawnSync as spawnSync3 } from "child_process";
5327
+ function detectClipboard() {
5328
+ const platform = detectPlatform();
5329
+ if (platform === "darwin") {
5330
+ return {
5331
+ copy: { cmd: "pbcopy", args: [] },
5332
+ paste: { cmd: "pbpaste", args: [] },
5333
+ hint: null
5334
+ };
5335
+ }
5336
+ if (platform === "wsl") {
5337
+ const copy = hasCommand("clip.exe") ? { cmd: "clip.exe", args: [] } : null;
5338
+ const paste = hasCommand("powershell.exe") ? { cmd: "powershell.exe", args: ["-NoProfile", "-Command", "Get-Clipboard"] } : null;
5339
+ return {
5340
+ copy,
5341
+ paste,
5342
+ hint: copy && paste ? null : "WSL clipboard needs Windows interop. Ensure /mnt/c/Windows/System32 is on PATH (it is by default)."
5343
+ };
5344
+ }
5345
+ if (platform === "linux") {
5346
+ if (process.env["WAYLAND_DISPLAY"] && hasCommand("wl-copy") && hasCommand("wl-paste")) {
5347
+ return {
5348
+ copy: { cmd: "wl-copy", args: [] },
5349
+ paste: { cmd: "wl-paste", args: ["--no-newline"] },
5350
+ hint: null
5351
+ };
5352
+ }
5353
+ if (hasCommand("xclip")) {
5354
+ return {
5355
+ copy: { cmd: "xclip", args: ["-selection", "clipboard"] },
5356
+ paste: { cmd: "xclip", args: ["-selection", "clipboard", "-o"] },
5357
+ hint: null
5358
+ };
5359
+ }
5360
+ if (hasCommand("xsel")) {
5361
+ return {
5362
+ copy: { cmd: "xsel", args: ["--clipboard", "--input"] },
5363
+ paste: { cmd: "xsel", args: ["--clipboard", "--output"] },
5364
+ hint: null
5365
+ };
5366
+ }
5367
+ return {
5368
+ copy: null,
5369
+ paste: null,
5370
+ hint: "Install a clipboard tool: `sudo apt install xclip` (or wl-clipboard for Wayland)."
5371
+ };
5372
+ }
5373
+ return {
5374
+ copy: null,
5375
+ paste: null,
5376
+ hint: "Clipboard not supported on this platform."
5377
+ };
5378
+ }
5379
+
5380
+ // src/cli/onboard.ts
5167
5381
  function detectTerminal() {
5168
5382
  const termProgram = process.env["TERM_PROGRAM"] || "";
5169
5383
  const isIterm = termProgram === "iTerm.app" || !!process.env["ITERM_SESSION_ID"];
@@ -5171,7 +5385,7 @@ function detectTerminal() {
5171
5385
  }
5172
5386
  function isTmuxAvailable() {
5173
5387
  try {
5174
- execSync9("which tmux", { stdio: "pipe" });
5388
+ execSync10("which tmux", { stdio: "pipe" });
5175
5389
  return true;
5176
5390
  } catch {
5177
5391
  return false;
@@ -5179,7 +5393,7 @@ function isTmuxAvailable() {
5179
5393
  }
5180
5394
  function isBrewAvailable() {
5181
5395
  try {
5182
- execSync9("which brew", { stdio: "pipe" });
5396
+ execSync10("which brew", { stdio: "pipe" });
5183
5397
  return true;
5184
5398
  } catch {
5185
5399
  return false;
@@ -5189,7 +5403,7 @@ function tryAutoInstallTmux() {
5189
5403
  if (!isBrewAvailable()) return false;
5190
5404
  try {
5191
5405
  console.log(" Installing tmux via Homebrew...");
5192
- execSync9("brew install tmux", { stdio: "inherit" });
5406
+ execSync10("brew install tmux", { stdio: "inherit" });
5193
5407
  return isTmuxAvailable();
5194
5408
  } catch {
5195
5409
  return false;
@@ -5200,11 +5414,11 @@ function checkItermOptionKey() {
5200
5414
  return { checked: false, allCorrect: true, incorrectProfiles: [] };
5201
5415
  }
5202
5416
  const plistPath2 = join16(homedir9(), "Library", "Preferences", "com.googlecode.iterm2.plist");
5203
- if (!existsSync15(plistPath2)) {
5417
+ if (!existsSync16(plistPath2)) {
5204
5418
  return { checked: false, allCorrect: false, incorrectProfiles: [] };
5205
5419
  }
5206
5420
  try {
5207
- const json = execSync9(
5421
+ const json = execSync10(
5208
5422
  `plutil -extract "New Bookmarks" json -o - "${plistPath2}"`,
5209
5423
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5210
5424
  );
@@ -5224,7 +5438,7 @@ function checkItermOptionKey() {
5224
5438
  }
5225
5439
  }
5226
5440
  function hasExistingTmuxConf() {
5227
- return existsSync15(join16(homedir9(), ".tmux.conf")) || existsSync15(join16(homedir9(), ".config", "tmux", "tmux.conf"));
5441
+ return existsSync16(join16(homedir9(), ".tmux.conf")) || existsSync16(join16(homedir9(), ".config", "tmux", "tmux.conf"));
5228
5442
  }
5229
5443
  var SISYPHUS_DEFAULTS_MARKER = "# sisyphus-managed \u2014 do not edit";
5230
5444
  function buildTmuxDefaults() {
@@ -5341,7 +5555,7 @@ function writeTmuxDefaults() {
5341
5555
  }
5342
5556
  function isNvimAvailable() {
5343
5557
  try {
5344
- execSync9("which nvim", { stdio: "pipe" });
5558
+ execSync10("which nvim", { stdio: "pipe" });
5345
5559
  return true;
5346
5560
  } catch {
5347
5561
  return false;
@@ -5349,13 +5563,13 @@ function isNvimAvailable() {
5349
5563
  }
5350
5564
  function getNvimVersion() {
5351
5565
  try {
5352
- return execSync9("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "") || "unknown";
5566
+ return execSync10("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "") || "unknown";
5353
5567
  } catch {
5354
5568
  return "unknown";
5355
5569
  }
5356
5570
  }
5357
5571
  function hasLazyVimConfig() {
5358
- return existsSync15(join16(homedir9(), ".config", "nvim", "lazy-lock.json"));
5572
+ return existsSync16(join16(homedir9(), ".config", "nvim", "lazy-lock.json"));
5359
5573
  }
5360
5574
  function bundledBaleiaPluginPath() {
5361
5575
  const distDir = dirname5(fileURLToPath2(import.meta.url));
@@ -5363,13 +5577,13 @@ function bundledBaleiaPluginPath() {
5363
5577
  }
5364
5578
  function installBaleiaPlugin() {
5365
5579
  const pluginsDir = join16(homedir9(), ".config", "nvim", "lua", "plugins");
5366
- if (!existsSync15(pluginsDir)) return false;
5580
+ if (!existsSync16(pluginsDir)) return false;
5367
5581
  const dest = join16(pluginsDir, "sisyphus-baleia.lua");
5368
- if (existsSync15(dest)) return true;
5582
+ if (existsSync16(dest)) return true;
5369
5583
  const src = bundledBaleiaPluginPath();
5370
- if (!existsSync15(src)) return false;
5584
+ if (!existsSync16(src)) return false;
5371
5585
  try {
5372
- writeFileSync8(dest, readFileSync18(src, "utf-8"), "utf8");
5586
+ writeFileSync8(dest, readFileSync19(src, "utf-8"), "utf8");
5373
5587
  return true;
5374
5588
  } catch {
5375
5589
  return false;
@@ -5385,7 +5599,7 @@ function tryAutoInstallNvim() {
5385
5599
  }
5386
5600
  try {
5387
5601
  console.log(" Installing neovim via Homebrew...");
5388
- execSync9("brew install neovim", { stdio: "inherit" });
5602
+ execSync10("brew install neovim", { stdio: "inherit" });
5389
5603
  } catch {
5390
5604
  return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false, baleiaInstalled: false };
5391
5605
  }
@@ -5394,7 +5608,7 @@ function tryAutoInstallNvim() {
5394
5608
  }
5395
5609
  const nvimConfigDir = join16(homedir9(), ".config", "nvim");
5396
5610
  let lazyVimInstalled = false;
5397
- if (!existsSync15(nvimConfigDir)) {
5611
+ if (!existsSync16(nvimConfigDir)) {
5398
5612
  const cloneCmd = [
5399
5613
  "git",
5400
5614
  "-c core.hooksPath=/dev/null",
@@ -5406,13 +5620,13 @@ function tryAutoInstallNvim() {
5406
5620
  ].join(" ");
5407
5621
  try {
5408
5622
  console.log(" Cloning LazyVim starter config...");
5409
- execSync9(cloneCmd, {
5623
+ execSync10(cloneCmd, {
5410
5624
  stdio: "inherit",
5411
5625
  env: { ...process.env, GIT_LFS_SKIP_SMUDGE: "1" }
5412
5626
  });
5413
5627
  const gitDir = join16(nvimConfigDir, ".git");
5414
- if (existsSync15(gitDir)) {
5415
- execSync9(`rm -rf "${gitDir}"`, { stdio: "pipe" });
5628
+ if (existsSync16(gitDir)) {
5629
+ execSync10(`rm -rf "${gitDir}"`, { stdio: "pipe" });
5416
5630
  }
5417
5631
  lazyVimInstalled = true;
5418
5632
  } catch (err) {
@@ -5427,7 +5641,7 @@ function tryAutoInstallNvim() {
5427
5641
  }
5428
5642
  function isTermrenderAvailable() {
5429
5643
  try {
5430
- execSync9("which termrender", { stdio: "pipe" });
5644
+ execSync10("which termrender", { stdio: "pipe" });
5431
5645
  return true;
5432
5646
  } catch {
5433
5647
  return false;
@@ -5435,7 +5649,7 @@ function isTermrenderAvailable() {
5435
5649
  }
5436
5650
  function isPipxAvailable() {
5437
5651
  try {
5438
- execSync9("which pipx", { stdio: "pipe" });
5652
+ execSync10("which pipx", { stdio: "pipe" });
5439
5653
  return true;
5440
5654
  } catch {
5441
5655
  return false;
@@ -5443,11 +5657,11 @@ function isPipxAvailable() {
5443
5657
  }
5444
5658
  function isPipAvailable() {
5445
5659
  try {
5446
- execSync9("which pip3", { stdio: "pipe" });
5660
+ execSync10("which pip3", { stdio: "pipe" });
5447
5661
  return true;
5448
5662
  } catch {
5449
5663
  try {
5450
- execSync9("which pip", { stdio: "pipe" });
5664
+ execSync10("which pip", { stdio: "pipe" });
5451
5665
  return true;
5452
5666
  } catch {
5453
5667
  return false;
@@ -5461,7 +5675,7 @@ function tryAutoInstallTermrender() {
5461
5675
  if (isPipxAvailable()) {
5462
5676
  try {
5463
5677
  console.log(" Installing termrender via pipx...");
5464
- execSync9("pipx install termrender", { stdio: "inherit" });
5678
+ execSync10("pipx install termrender", { stdio: "inherit" });
5465
5679
  if (isTermrenderAvailable()) return { installed: true, autoInstalled: true };
5466
5680
  } catch {
5467
5681
  }
@@ -5471,19 +5685,45 @@ function tryAutoInstallTermrender() {
5471
5685
  console.log(" Installing termrender via pip...");
5472
5686
  const pip = (() => {
5473
5687
  try {
5474
- execSync9("which pip3", { stdio: "pipe" });
5688
+ execSync10("which pip3", { stdio: "pipe" });
5475
5689
  return "pip3";
5476
5690
  } catch {
5477
5691
  return "pip";
5478
5692
  }
5479
5693
  })();
5480
- execSync9(`${pip} install termrender`, { stdio: "inherit" });
5694
+ execSync10(`${pip} install termrender`, { stdio: "inherit" });
5481
5695
  if (isTermrenderAvailable()) return { installed: true, autoInstalled: true };
5482
5696
  } catch {
5483
5697
  }
5484
5698
  }
5485
5699
  return { installed: false, autoInstalled: false };
5486
5700
  }
5701
+ function detectPlatformReadiness() {
5702
+ const clip = detectClipboard();
5703
+ const linuxLike = isLinuxLike();
5704
+ const wsl = isWslHost();
5705
+ let wslSystemdEnabled = null;
5706
+ if (wsl) {
5707
+ try {
5708
+ execSync10("systemctl is-system-running --quiet 2>/dev/null", { stdio: "pipe" });
5709
+ wslSystemdEnabled = true;
5710
+ } catch {
5711
+ try {
5712
+ execSync10("systemctl --user --version", { stdio: "pipe" });
5713
+ wslSystemdEnabled = true;
5714
+ } catch {
5715
+ wslSystemdEnabled = false;
5716
+ }
5717
+ }
5718
+ }
5719
+ return {
5720
+ label: platformLabel(),
5721
+ clipboardTool: clip.copy === null ? null : clip.copy.cmd,
5722
+ clipboardHint: clip.hint,
5723
+ notifySendAvailable: linuxLike ? hasCommand("notify-send") : true,
5724
+ wslSystemdEnabled
5725
+ };
5726
+ }
5487
5727
  function runOnboarding() {
5488
5728
  const terminal = detectTerminal();
5489
5729
  const tmuxAlreadyInstalled = isTmuxAvailable();
@@ -5505,13 +5745,14 @@ function runOnboarding() {
5505
5745
  const nvim = tryAutoInstallNvim();
5506
5746
  const sisyphusPlugin = ensureSisyphusPluginInstalled();
5507
5747
  const termrender = tryAutoInstallTermrender();
5508
- return { tmuxInstalled, tmuxAutoInstalled, terminal, itermOptionKey, tmuxDefaultsWritten, nvim, sisyphusPlugin, termrender };
5748
+ const platform = detectPlatformReadiness();
5749
+ return { tmuxInstalled, tmuxAutoInstalled, terminal, itermOptionKey, tmuxDefaultsWritten, nvim, sisyphusPlugin, termrender, platform };
5509
5750
  }
5510
5751
 
5511
5752
  // src/cli/commands/setup.ts
5512
5753
  function getTmuxVersion() {
5513
5754
  try {
5514
- return execSync10("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
5755
+ return execSync11("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
5515
5756
  } catch {
5516
5757
  return "installed";
5517
5758
  }
@@ -5520,6 +5761,7 @@ function printResults(result, daemonOk, keybindMsg) {
5520
5761
  console.log("");
5521
5762
  console.log("Setting up Sisyphus...");
5522
5763
  console.log("");
5764
+ console.log(` \u2713 Platform: ${result.platform.label}`);
5523
5765
  if (result.tmuxInstalled) {
5524
5766
  const detail = getTmuxVersion();
5525
5767
  console.log(` \u2713 tmux: ${detail}${result.tmuxAutoInstalled ? " (just installed)" : ""}`);
@@ -5527,6 +5769,23 @@ function printResults(result, daemonOk, keybindMsg) {
5527
5769
  const hint = process.platform === "darwin" ? "Install Homebrew (https://brew.sh) then: brew install tmux" : "apt install tmux (Debian/Ubuntu) or your package manager";
5528
5770
  console.log(` \u2717 tmux: Not installed \u2014 ${hint}`);
5529
5771
  }
5772
+ if (process.platform !== "darwin") {
5773
+ if (result.platform.clipboardTool !== null) {
5774
+ console.log(` \u2713 Clipboard: ${result.platform.clipboardTool}`);
5775
+ } else {
5776
+ const hint = result.platform.clipboardHint === null ? "Install xclip / wl-clipboard" : result.platform.clipboardHint;
5777
+ console.log(` \u2717 Clipboard: no backend \u2014 ${hint}`);
5778
+ }
5779
+ if (result.platform.notifySendAvailable) {
5780
+ console.log(" \u2713 Notifications: notify-send");
5781
+ } else {
5782
+ console.log(" \u26A0 Notifications: notify-send missing \u2014 sudo apt install libnotify-bin");
5783
+ }
5784
+ }
5785
+ if (result.platform.wslSystemdEnabled === false) {
5786
+ console.log(" \u26A0 WSL systemd: disabled \u2014 add `[boot]\\nsystemd=true` to /etc/wsl.conf");
5787
+ console.log(" then run `wsl --shutdown` in PowerShell. The daemon can still be started manually with `sisyphusd &`.");
5788
+ }
5530
5789
  if (result.tmuxDefaultsWritten) {
5531
5790
  console.log(" \u2713 tmux config: Sensible defaults written to ~/.tmux.conf");
5532
5791
  }
@@ -5656,11 +5915,11 @@ function registerSetupKeybind(program2) {
5656
5915
  }
5657
5916
 
5658
5917
  // src/cli/commands/check-keybinds.ts
5659
- import { execSync as execSync11 } from "child_process";
5660
- import { readFileSync as readFileSync19 } from "fs";
5918
+ import { execSync as execSync12 } from "child_process";
5919
+ import { readFileSync as readFileSync20 } from "fs";
5661
5920
  function isTmuxInstalled2() {
5662
5921
  try {
5663
- execSync11("which tmux", { stdio: "pipe" });
5922
+ execSync12("which tmux", { stdio: "pipe" });
5664
5923
  return true;
5665
5924
  } catch {
5666
5925
  return false;
@@ -5668,14 +5927,14 @@ function isTmuxInstalled2() {
5668
5927
  }
5669
5928
  function getTmuxVersion2() {
5670
5929
  try {
5671
- return execSync11("tmux -V", { stdio: ["pipe", "pipe", "pipe"] }).toString().trim();
5930
+ return execSync12("tmux -V", { stdio: ["pipe", "pipe", "pipe"] }).toString().trim();
5672
5931
  } catch {
5673
5932
  return null;
5674
5933
  }
5675
5934
  }
5676
5935
  function isTmuxServerRunning() {
5677
5936
  try {
5678
- execSync11("tmux list-sessions", { stdio: ["pipe", "pipe", "pipe"] });
5937
+ execSync12("tmux list-sessions", { stdio: ["pipe", "pipe", "pipe"] });
5679
5938
  return true;
5680
5939
  } catch {
5681
5940
  return false;
@@ -5683,7 +5942,7 @@ function isTmuxServerRunning() {
5683
5942
  }
5684
5943
  function getTmuxPrefix() {
5685
5944
  try {
5686
- return execSync11("tmux show-options -gv prefix", { stdio: ["pipe", "pipe", "pipe"] }).toString().trim() || null;
5945
+ return execSync12("tmux show-options -gv prefix", { stdio: ["pipe", "pipe", "pipe"] }).toString().trim() || null;
5687
5946
  } catch {
5688
5947
  return null;
5689
5948
  }
@@ -5710,7 +5969,7 @@ function runCheck() {
5710
5969
  let userConfAlreadySources = false;
5711
5970
  if (userConfPath !== null) {
5712
5971
  try {
5713
- userConfAlreadySources = readFileSync19(userConfPath, "utf-8").includes(sisyphusConfPath);
5972
+ userConfAlreadySources = readFileSync20(userConfPath, "utf-8").includes(sisyphusConfPath);
5714
5973
  } catch {
5715
5974
  }
5716
5975
  }
@@ -5868,8 +6127,8 @@ function registerCheckKeybinds(program2) {
5868
6127
 
5869
6128
  // src/cli/commands/check-statusbar.ts
5870
6129
  init_paths();
5871
- import { execSync as execSync12 } from "child_process";
5872
- import { existsSync as existsSync16, readFileSync as readFileSync20 } from "fs";
6130
+ import { execSync as execSync13 } from "child_process";
6131
+ import { existsSync as existsSync17, readFileSync as readFileSync21 } from "fs";
5873
6132
  import { homedir as homedir10 } from "os";
5874
6133
  import { join as join17 } from "path";
5875
6134
  var SISYPHUS_LEFT_TOKEN = "@sisyphus_left";
@@ -5878,7 +6137,7 @@ var TMUX_DEFAULT_STATUS_LEFT = "[#S] ";
5878
6137
  var TMUX_DEFAULT_STATUS_RIGHT_PREFIX = '"#{=21:pane_title}"';
5879
6138
  function isTmuxInstalled3() {
5880
6139
  try {
5881
- execSync12("which tmux", { stdio: "pipe" });
6140
+ execSync13("which tmux", { stdio: "pipe" });
5882
6141
  return true;
5883
6142
  } catch {
5884
6143
  return false;
@@ -5886,7 +6145,7 @@ function isTmuxInstalled3() {
5886
6145
  }
5887
6146
  function isTmuxServerRunning2() {
5888
6147
  try {
5889
- execSync12("tmux list-sessions", { stdio: ["pipe", "pipe", "pipe"] });
6148
+ execSync13("tmux list-sessions", { stdio: ["pipe", "pipe", "pipe"] });
5890
6149
  return true;
5891
6150
  } catch {
5892
6151
  return false;
@@ -5894,9 +6153,9 @@ function isTmuxServerRunning2() {
5894
6153
  }
5895
6154
  function isDaemonRunning() {
5896
6155
  const pidFile = daemonPidPath();
5897
- if (!existsSync16(pidFile)) return false;
6156
+ if (!existsSync17(pidFile)) return false;
5898
6157
  try {
5899
- const pid = parseInt(readFileSync20(pidFile, "utf-8").trim(), 10);
6158
+ const pid = parseInt(readFileSync21(pidFile, "utf-8").trim(), 10);
5900
6159
  if (Number.isNaN(pid) || pid <= 0) return false;
5901
6160
  process.kill(pid, 0);
5902
6161
  return true;
@@ -5906,7 +6165,7 @@ function isDaemonRunning() {
5906
6165
  }
5907
6166
  function showOption(name) {
5908
6167
  try {
5909
- const out = execSync12(`tmux show-options -g ${name}`, { stdio: ["pipe", "pipe", "pipe"] }).toString().trim();
6168
+ const out = execSync13(`tmux show-options -g ${name}`, { stdio: ["pipe", "pipe", "pipe"] }).toString().trim();
5910
6169
  if (out.length === 0) return null;
5911
6170
  const prefix = `${name} `;
5912
6171
  const stripped = out.startsWith(prefix) ? out.slice(prefix.length) : out;
@@ -5941,8 +6200,8 @@ function probeTmuxOptions(serverRunning) {
5941
6200
  function findUserTmuxConf() {
5942
6201
  const xdg = join17(homedir10(), ".config", "tmux", "tmux.conf");
5943
6202
  const dotfile = join17(homedir10(), ".tmux.conf");
5944
- if (existsSync16(xdg)) return xdg;
5945
- if (existsSync16(dotfile)) return dotfile;
6203
+ if (existsSync17(xdg)) return xdg;
6204
+ if (existsSync17(dotfile)) return dotfile;
5946
6205
  return null;
5947
6206
  }
5948
6207
  function probeUserConf() {
@@ -5952,7 +6211,7 @@ function probeUserConf() {
5952
6211
  }
5953
6212
  let contents = "";
5954
6213
  try {
5955
- contents = readFileSync20(path, "utf-8");
6214
+ contents = readFileSync21(path, "utf-8");
5956
6215
  } catch {
5957
6216
  return { path, setsStatusLeft: false, setsStatusRight: false, sourcesSisyphusManaged: false };
5958
6217
  }
@@ -5964,9 +6223,9 @@ function probeUserConf() {
5964
6223
  }
5965
6224
  function loadGlobalSisyphusConfig() {
5966
6225
  const path = globalConfigPath();
5967
- if (!existsSync16(path)) return null;
6226
+ if (!existsSync17(path)) return null;
5968
6227
  try {
5969
- const parsed = JSON.parse(readFileSync20(path, "utf-8"));
6228
+ const parsed = JSON.parse(readFileSync21(path, "utf-8"));
5970
6229
  return parsed.statusBar === void 0 ? null : parsed.statusBar;
5971
6230
  } catch {
5972
6231
  return null;
@@ -6222,7 +6481,7 @@ function registerCheckStatusbar(program2) {
6222
6481
 
6223
6482
  // src/cli/commands/home-init.ts
6224
6483
  init_shell();
6225
- import { execSync as execSync13 } from "child_process";
6484
+ import { execSync as execSync14 } from "child_process";
6226
6485
  function registerHomeInit(parent) {
6227
6486
  parent.command("home-init <name> <cwd>").description("Bootstrap a tmux home session with the sisyphus dashboard.").action((name, cwd) => {
6228
6487
  ensureSession(name, cwd);
@@ -6232,7 +6491,7 @@ function registerHomeInit(parent) {
6232
6491
  }
6233
6492
  function sessionExists(name) {
6234
6493
  try {
6235
- execSync13(`tmux has-session -t ${shellQuote(name)}`, { stdio: "pipe" });
6494
+ execSync14(`tmux has-session -t ${shellQuote(name)}`, { stdio: "pipe" });
6236
6495
  return true;
6237
6496
  } catch {
6238
6497
  return false;
@@ -6240,13 +6499,13 @@ function sessionExists(name) {
6240
6499
  }
6241
6500
  function ensureSession(name, cwd) {
6242
6501
  if (sessionExists(name)) return;
6243
- execSync13(
6502
+ execSync14(
6244
6503
  `tmux new-session -d -s ${shellQuote(name)} -c ${shellQuote(cwd)}`,
6245
6504
  { stdio: "pipe" }
6246
6505
  );
6247
6506
  }
6248
6507
  function setSessionCwd(name, cwd) {
6249
- execSync13(
6508
+ execSync14(
6250
6509
  `tmux set-option -t ${shellQuote(name)} @sisyphus_cwd ${shellQuote(cwd.replace(/\/+$/, ""))}`,
6251
6510
  { stdio: "pipe" }
6252
6511
  );
@@ -6278,8 +6537,8 @@ function registerQuiesce(parent) {
6278
6537
 
6279
6538
  // src/cli/commands/doctor.ts
6280
6539
  init_paths();
6281
- import { execSync as execSync14 } from "child_process";
6282
- import { existsSync as existsSync17, statSync as statSync3 } from "fs";
6540
+ import { execSync as execSync15 } from "child_process";
6541
+ import { existsSync as existsSync18, statSync as statSync3 } from "fs";
6283
6542
  import { homedir as homedir11 } from "os";
6284
6543
  import { join as join18 } from "path";
6285
6544
  function checkNodeVersion() {
@@ -6291,7 +6550,7 @@ function checkNodeVersion() {
6291
6550
  }
6292
6551
  function checkClaudeCli() {
6293
6552
  try {
6294
- execSync14("which claude", { stdio: "pipe" });
6553
+ execSync15("which claude", { stdio: "pipe" });
6295
6554
  return { name: "Claude CLI", status: "ok", detail: "Found on PATH" };
6296
6555
  } catch {
6297
6556
  return {
@@ -6304,7 +6563,7 @@ function checkClaudeCli() {
6304
6563
  }
6305
6564
  function checkGit() {
6306
6565
  try {
6307
- const version = execSync14("git --version", { encoding: "utf-8", stdio: "pipe" }).trim();
6566
+ const version = execSync15("git --version", { encoding: "utf-8", stdio: "pipe" }).trim();
6308
6567
  return { name: "git", status: "ok", detail: version };
6309
6568
  } catch {
6310
6569
  return { name: "git", status: "fail", detail: "Not found on PATH", fix: "Install git: https://git-scm.com/downloads" };
@@ -6312,7 +6571,7 @@ function checkGit() {
6312
6571
  }
6313
6572
  function checkTmuxVersion() {
6314
6573
  try {
6315
- const version = execSync14("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
6574
+ const version = execSync15("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
6316
6575
  const match = version.match(/(\d+\.\d+)/);
6317
6576
  if (!match) return { name: "tmux version", status: "warn", detail: `Could not parse version: ${version}` };
6318
6577
  const ver = parseFloat(match[1]);
@@ -6338,7 +6597,7 @@ function checkDaemonInstalled() {
6338
6597
  };
6339
6598
  }
6340
6599
  const pid = daemonPidPath();
6341
- if (existsSync17(pid)) {
6600
+ if (existsSync18(pid)) {
6342
6601
  return { name: "Daemon setup", status: "ok", detail: `PID file found at ${pid}` };
6343
6602
  }
6344
6603
  return {
@@ -6350,7 +6609,7 @@ function checkDaemonInstalled() {
6350
6609
  }
6351
6610
  function checkDaemonRunning() {
6352
6611
  const pid = daemonPidPath();
6353
- if (!existsSync17(pid)) {
6612
+ if (!existsSync18(pid)) {
6354
6613
  const fix = process.platform === "darwin" ? "launchctl load -w ~/Library/LaunchAgents/com.sisyphus.daemon.plist" : "sisyphusd & \u2014 or check if the process is running";
6355
6614
  return {
6356
6615
  name: "Daemon process",
@@ -6361,7 +6620,7 @@ function checkDaemonRunning() {
6361
6620
  }
6362
6621
  try {
6363
6622
  const sock = socketPath();
6364
- execSync14(`test -S "${sock}"`, { stdio: "pipe" });
6623
+ execSync15(`test -S "${sock}"`, { stdio: "pipe" });
6365
6624
  return { name: "Daemon process", status: "ok", detail: `Socket at ${sock}` };
6366
6625
  } catch {
6367
6626
  return {
@@ -6374,13 +6633,13 @@ function checkDaemonRunning() {
6374
6633
  }
6375
6634
  function checkTmux() {
6376
6635
  try {
6377
- execSync14("which tmux", { stdio: "pipe" });
6636
+ execSync15("which tmux", { stdio: "pipe" });
6378
6637
  } catch {
6379
6638
  const installHint = process.platform === "darwin" ? "brew install tmux" : "apt install tmux (Debian/Ubuntu) or your package manager";
6380
6639
  return { name: "tmux", status: "fail", detail: "Not found on PATH", fix: installHint };
6381
6640
  }
6382
6641
  try {
6383
- execSync14("tmux list-sessions", { stdio: "pipe" });
6642
+ execSync15("tmux list-sessions", { stdio: "pipe" });
6384
6643
  return { name: "tmux", status: "ok", detail: "Running" };
6385
6644
  } catch {
6386
6645
  return { name: "tmux", status: "warn", detail: "Installed but no server running" };
@@ -6388,7 +6647,7 @@ function checkTmux() {
6388
6647
  }
6389
6648
  function checkCycleScript() {
6390
6649
  const path = cycleScriptPath();
6391
- if (!existsSync17(path)) {
6650
+ if (!existsSync18(path)) {
6392
6651
  return {
6393
6652
  name: "Cycle script",
6394
6653
  status: "fail",
@@ -6413,7 +6672,7 @@ function checkCycleScript() {
6413
6672
  function checkTmuxKeybind() {
6414
6673
  const existing = getExistingBinding(DEFAULT_CYCLE_KEY);
6415
6674
  if (existing === null) {
6416
- if (existsSync17(sisyphusTmuxConfPath())) {
6675
+ if (existsSync18(sisyphusTmuxConfPath())) {
6417
6676
  return {
6418
6677
  name: `Tmux keybind (${DEFAULT_CYCLE_KEY})`,
6419
6678
  status: "warn",
@@ -6439,7 +6698,7 @@ function checkTmuxKeybind() {
6439
6698
  }
6440
6699
  function checkGlobalDir() {
6441
6700
  const dir = globalDir();
6442
- if (existsSync17(dir)) {
6701
+ if (existsSync18(dir)) {
6443
6702
  return { name: "Data directory", status: "ok", detail: dir };
6444
6703
  }
6445
6704
  return { name: "Data directory", status: "warn", detail: `${dir} does not exist (created on first use)` };
@@ -6491,7 +6750,7 @@ function checkSisyphusPlugin() {
6491
6750
  function checkTermrender() {
6492
6751
  if (isTermrenderAvailable()) {
6493
6752
  try {
6494
- const version = execSync14("termrender --version", { encoding: "utf-8", stdio: "pipe" }).trim();
6753
+ const version = execSync15("termrender --version", { encoding: "utf-8", stdio: "pipe" }).trim();
6495
6754
  return { name: "termrender", status: "ok", detail: version };
6496
6755
  } catch {
6497
6756
  return { name: "termrender", status: "ok", detail: "installed" };
@@ -6510,31 +6769,67 @@ function checkNvim() {
6510
6769
  return { name: "nvim", status: "warn", detail: "Not installed", fix };
6511
6770
  }
6512
6771
  try {
6513
- const version = execSync14("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "");
6772
+ const version = execSync15("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "");
6514
6773
  return { name: "nvim", status: "ok", detail: version ?? "installed" };
6515
6774
  } catch {
6516
6775
  return { name: "nvim", status: "ok", detail: "installed" };
6517
6776
  }
6518
6777
  }
6519
6778
  function checkNotifyBinary() {
6520
- if (process.platform !== "darwin") return null;
6521
- const binary = join18(homedir11(), ".sisyphus", "SisyphusNotify.app", "Contents", "MacOS", "sisyphus-notify");
6522
- if (existsSync17(binary)) {
6523
- return { name: "Notifications", status: "ok", detail: "SisyphusNotify.app built" };
6779
+ if (process.platform === "darwin") {
6780
+ const binary = join18(homedir11(), ".sisyphus", "SisyphusNotify.app", "Contents", "MacOS", "sisyphus-notify");
6781
+ if (existsSync18(binary)) {
6782
+ return { name: "Notifications", status: "ok", detail: "SisyphusNotify.app built" };
6783
+ }
6784
+ return {
6785
+ name: "Notifications",
6786
+ status: "warn",
6787
+ detail: "SisyphusNotify.app not built (click-to-switch unavailable)",
6788
+ fix: "Requires Xcode CLI tools: xcode-select --install, then reinstall sisyphus"
6789
+ };
6790
+ }
6791
+ const ready = detectPlatformReadiness();
6792
+ if (ready.notifySendAvailable) {
6793
+ return { name: "Notifications", status: "ok", detail: "notify-send (libnotify)" };
6524
6794
  }
6525
6795
  return {
6526
6796
  name: "Notifications",
6527
6797
  status: "warn",
6528
- detail: "SisyphusNotify.app not built (click-to-switch unavailable)",
6529
- fix: "Requires Xcode CLI tools: xcode-select --install, then reinstall sisyphus"
6798
+ detail: "notify-send not found \u2014 OS banners disabled",
6799
+ fix: "sudo apt install libnotify-bin (or your distro's equivalent)"
6800
+ };
6801
+ }
6802
+ function checkClipboard() {
6803
+ const ready = detectPlatformReadiness();
6804
+ if (ready.clipboardTool !== null) {
6805
+ return { name: "Clipboard", status: "ok", detail: ready.clipboardTool };
6806
+ }
6807
+ const fix = ready.clipboardHint === null ? "Install xclip or wl-clipboard" : ready.clipboardHint;
6808
+ return {
6809
+ name: "Clipboard",
6810
+ status: "fail",
6811
+ detail: "No clipboard backend detected",
6812
+ fix
6530
6813
  };
6531
6814
  }
6815
+ function checkPlatform() {
6816
+ const ready = detectPlatformReadiness();
6817
+ if (ready.wslSystemdEnabled === false) {
6818
+ return {
6819
+ name: "Platform",
6820
+ status: "warn",
6821
+ detail: `${platformLabel()} \u2014 systemd disabled (daemon needs manual start)`,
6822
+ fix: "Add `[boot]\\nsystemd=true` to /etc/wsl.conf, then `wsl --shutdown` from PowerShell"
6823
+ };
6824
+ }
6825
+ return { name: "Platform", status: "ok", detail: platformLabel() };
6826
+ }
6532
6827
  var SYMBOLS = { ok: "\u2713", warn: "!", fail: "\u2717" };
6533
6828
  function registerDoctor(program2) {
6534
6829
  program2.command("doctor").description("Check sisyphus installation health").action(() => {
6535
6830
  const itermCheck = checkItermRightOptionKey();
6536
- const notifyCheck = checkNotifyBinary();
6537
6831
  const checks = [
6832
+ checkPlatform(),
6538
6833
  checkNodeVersion(),
6539
6834
  checkClaudeCli(),
6540
6835
  checkGit(),
@@ -6549,7 +6844,8 @@ function registerDoctor(program2) {
6549
6844
  checkTmuxKeybind(),
6550
6845
  checkSisyphusPlugin(),
6551
6846
  checkNvim(),
6552
- ...notifyCheck ? [notifyCheck] : [],
6847
+ checkNotifyBinary(),
6848
+ checkClipboard(),
6553
6849
  checkTermrender()
6554
6850
  ];
6555
6851
  let hasIssues = false;
@@ -6572,7 +6868,7 @@ function registerDoctor(program2) {
6572
6868
  }
6573
6869
 
6574
6870
  // src/cli/commands/init.ts
6575
- import { existsSync as existsSync18, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
6871
+ import { existsSync as existsSync19, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
6576
6872
  import { join as join19 } from "path";
6577
6873
  var DEFAULT_CONFIG2 = {};
6578
6874
  var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
@@ -6586,7 +6882,7 @@ function registerInit(program2) {
6586
6882
  const cwd = process.cwd();
6587
6883
  const sisDir = join19(cwd, ".sisyphus");
6588
6884
  const configPath = join19(sisDir, "config.json");
6589
- if (existsSync18(configPath)) {
6885
+ if (existsSync19(configPath)) {
6590
6886
  console.log(`Already initialized: ${configPath}`);
6591
6887
  return;
6592
6888
  }
@@ -6595,7 +6891,7 @@ function registerInit(program2) {
6595
6891
  console.log(`Created ${configPath}`);
6596
6892
  if (opts.orchestrator) {
6597
6893
  const orchPath = join19(sisDir, "orchestrator.md");
6598
- if (!existsSync18(orchPath)) {
6894
+ if (!existsSync19(orchPath)) {
6599
6895
  writeFileSync9(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
6600
6896
  console.log(`Created ${orchPath}`);
6601
6897
  }
@@ -6638,7 +6934,7 @@ function registerUninstall(program2) {
6638
6934
 
6639
6935
  // src/cli/commands/configure-upload.ts
6640
6936
  init_paths();
6641
- import { chmodSync as chmodSync2, existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync21, writeFileSync as writeFileSync10 } from "fs";
6937
+ import { chmodSync as chmodSync2, existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync22, writeFileSync as writeFileSync10 } from "fs";
6642
6938
  import { createInterface as createInterface3 } from "readline";
6643
6939
  import { dirname as dirname6 } from "path";
6644
6940
  async function readUrlFromInput(interactive) {
@@ -6696,9 +6992,9 @@ function registerConfigureUpload(program2) {
6696
6992
  const url = parsed.origin + (strippedPath.length > 0 ? strippedPath : "");
6697
6993
  const configPath = globalConfigPath();
6698
6994
  let existing = {};
6699
- if (existsSync19(configPath)) {
6995
+ if (existsSync20(configPath)) {
6700
6996
  try {
6701
- existing = JSON.parse(readFileSync21(configPath, "utf-8"));
6997
+ existing = JSON.parse(readFileSync22(configPath, "utf-8"));
6702
6998
  } catch {
6703
6999
  console.error(`Error: ${configPath} could not be parsed \u2014 fix or delete it first`);
6704
7000
  process.exit(1);
@@ -6713,7 +7009,7 @@ function registerConfigureUpload(program2) {
6713
7009
  }
6714
7010
 
6715
7011
  // src/cli/commands/getting-started.ts
6716
- import { execSync as execSync15 } from "child_process";
7012
+ import { execSync as execSync16 } from "child_process";
6717
7013
  import { dirname as dirname7, join as join20 } from "path";
6718
7014
  import { fileURLToPath as fileURLToPath3 } from "url";
6719
7015
  function templatePath(name) {
@@ -7205,11 +7501,11 @@ function printStep5() {
7205
7501
  let recentCommits = "";
7206
7502
  let topLevelFiles = "";
7207
7503
  try {
7208
- recentCommits = execSync15("git log --oneline -15 2>/dev/null", { encoding: "utf-8" }).trim();
7504
+ recentCommits = execSync16("git log --oneline -15 2>/dev/null", { encoding: "utf-8" }).trim();
7209
7505
  } catch {
7210
7506
  }
7211
7507
  try {
7212
- topLevelFiles = execSync15("ls -1 2>/dev/null", { encoding: "utf-8" }).trim();
7508
+ topLevelFiles = execSync16("ls -1 2>/dev/null", { encoding: "utf-8" }).trim();
7213
7509
  } catch {
7214
7510
  }
7215
7511
  console.log(`
@@ -7687,7 +7983,7 @@ function registerGettingStarted(program2) {
7687
7983
 
7688
7984
  // src/cli/commands/history.ts
7689
7985
  init_paths();
7690
- import { readdirSync as readdirSync6, readFileSync as readFileSync22, existsSync as existsSync20 } from "fs";
7986
+ import { readdirSync as readdirSync6, readFileSync as readFileSync23, existsSync as existsSync21 } from "fs";
7691
7987
  import { resolve as resolve6 } from "path";
7692
7988
  var RESET3 = "\x1B[0m";
7693
7989
  var BOLD3 = "\x1B[1m";
@@ -7727,13 +8023,13 @@ function splitAgentTime(agents) {
7727
8023
  }
7728
8024
  function loadAllSummaries() {
7729
8025
  const base = historyBaseDir();
7730
- if (!existsSync20(base)) return [];
8026
+ if (!existsSync21(base)) return [];
7731
8027
  const results = [];
7732
8028
  for (const name of readdirSync6(base)) {
7733
8029
  const summaryPath = historySessionSummaryPath(name);
7734
- if (existsSync20(summaryPath)) {
8030
+ if (existsSync21(summaryPath)) {
7735
8031
  try {
7736
- const raw = readFileSync22(summaryPath, "utf-8");
8032
+ const raw = readFileSync23(summaryPath, "utf-8");
7737
8033
  results.push({ id: name, summary: JSON.parse(raw) });
7738
8034
  continue;
7739
8035
  } catch {
@@ -7747,10 +8043,10 @@ function loadAllSummaries() {
7747
8043
  }
7748
8044
  function buildLiveSummary(sessionId) {
7749
8045
  const eventsPath = historyEventsPath(sessionId);
7750
- if (!existsSync20(eventsPath)) return null;
8046
+ if (!existsSync21(eventsPath)) return null;
7751
8047
  let cwd = null;
7752
8048
  try {
7753
- const lines = readFileSync22(eventsPath, "utf-8").split("\n");
8049
+ const lines = readFileSync23(eventsPath, "utf-8").split("\n");
7754
8050
  for (const line of lines) {
7755
8051
  if (!line.trim()) continue;
7756
8052
  try {
@@ -7768,10 +8064,10 @@ function buildLiveSummary(sessionId) {
7768
8064
  }
7769
8065
  if (!cwd) return null;
7770
8066
  const sPath = statePath(cwd, sessionId);
7771
- if (!existsSync20(sPath)) return null;
8067
+ if (!existsSync21(sPath)) return null;
7772
8068
  let session2;
7773
8069
  try {
7774
- session2 = JSON.parse(readFileSync22(sPath, "utf-8"));
8070
+ session2 = JSON.parse(readFileSync23(sPath, "utf-8"));
7775
8071
  } catch {
7776
8072
  return null;
7777
8073
  }
@@ -7832,8 +8128,8 @@ function buildLiveSummary(sessionId) {
7832
8128
  }
7833
8129
  function loadEvents(sessionId) {
7834
8130
  const eventsPath = historyEventsPath(sessionId);
7835
- if (!existsSync20(eventsPath)) return [];
7836
- const lines = readFileSync22(eventsPath, "utf-8").split("\n").filter((l) => l.trim());
8131
+ if (!existsSync21(eventsPath)) return [];
8132
+ const lines = readFileSync23(eventsPath, "utf-8").split("\n").filter((l) => l.trim());
7837
8133
  const events = [];
7838
8134
  for (const line of lines) {
7839
8135
  try {
@@ -7846,13 +8142,13 @@ function loadEvents(sessionId) {
7846
8142
  }
7847
8143
  function findSession(idOrName) {
7848
8144
  const summaryPath = historySessionSummaryPath(idOrName);
7849
- if (existsSync20(summaryPath)) {
8145
+ if (existsSync21(summaryPath)) {
7850
8146
  try {
7851
- return { id: idOrName, summary: JSON.parse(readFileSync22(summaryPath, "utf-8")) };
8147
+ return { id: idOrName, summary: JSON.parse(readFileSync23(summaryPath, "utf-8")) };
7852
8148
  } catch {
7853
8149
  }
7854
8150
  }
7855
- if (existsSync20(historySessionDir(idOrName))) {
8151
+ if (existsSync21(historySessionDir(idOrName))) {
7856
8152
  const live = buildLiveSummary(idOrName);
7857
8153
  if (live) return { id: idOrName, summary: live };
7858
8154
  }
@@ -8241,7 +8537,7 @@ function registerHistory(program2) {
8241
8537
  init_paths();
8242
8538
  import { execFile as execFile2 } from "child_process";
8243
8539
  import { promisify } from "util";
8244
- import { existsSync as existsSync21, readFileSync as readFileSync23, mkdirSync as mkdirSync10, symlinkSync, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "fs";
8540
+ import { existsSync as existsSync22, readFileSync as readFileSync24, mkdirSync as mkdirSync10, symlinkSync, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "fs";
8245
8541
  import { homedir as homedir12 } from "os";
8246
8542
  import { join as join22 } from "path";
8247
8543
  function sanitizeName(name) {
@@ -8253,7 +8549,7 @@ function buildOutputPath(label, dir) {
8253
8549
  const base = `sisyphus-${label}-${date}`;
8254
8550
  let candidate = join22(dir, `${base}.zip`);
8255
8551
  let counter = 1;
8256
- while (existsSync21(candidate)) {
8552
+ while (existsSync22(candidate)) {
8257
8553
  counter++;
8258
8554
  candidate = join22(dir, `${base}-${counter}.zip`);
8259
8555
  }
@@ -8317,16 +8613,16 @@ async function exportSessionToZip(sessionId, cwd, options) {
8317
8613
  const reveal = options?.reveal ?? true;
8318
8614
  const sessDir = sessionDir(cwd, sessionId);
8319
8615
  const histDir = historySessionDir(sessionId);
8320
- const sessExists = existsSync21(sessDir);
8321
- const histExists = existsSync21(histDir);
8616
+ const sessExists = existsSync22(sessDir);
8617
+ const histExists = existsSync22(histDir);
8322
8618
  if (!sessExists && !histExists) {
8323
8619
  throw new Error(`No data found for session ${sessionId}`);
8324
8620
  }
8325
8621
  let label = sessionId.slice(0, 8);
8326
8622
  const stPath = statePath(cwd, sessionId);
8327
- if (existsSync21(stPath)) {
8623
+ if (existsSync22(stPath)) {
8328
8624
  try {
8329
- const state = JSON.parse(readFileSync23(stPath, "utf-8"));
8625
+ const state = JSON.parse(readFileSync24(stPath, "utf-8"));
8330
8626
  if (state.name) {
8331
8627
  label = sanitizeName(state.name);
8332
8628
  }
@@ -8465,12 +8761,12 @@ function buildManifest(args2) {
8465
8761
  }
8466
8762
 
8467
8763
  // src/shared/version.ts
8468
- import { readFileSync as readFileSync24 } from "fs";
8764
+ import { readFileSync as readFileSync25 } from "fs";
8469
8765
  import { resolve as resolve7 } from "path";
8470
8766
  function readSisyphusVersion() {
8471
8767
  for (const rel of ["../package.json", "../../package.json"]) {
8472
8768
  try {
8473
- const raw = readFileSync24(resolve7(import.meta.dirname, rel), "utf-8");
8769
+ const raw = readFileSync25(resolve7(import.meta.dirname, rel), "utf-8");
8474
8770
  const pkg = JSON.parse(raw);
8475
8771
  if (pkg.name === "sisyphi" && pkg.version) return pkg.version;
8476
8772
  } catch {
@@ -8565,13 +8861,13 @@ function registerUpload(program2) {
8565
8861
  }
8566
8862
 
8567
8863
  // src/cli/commands/scratch.ts
8568
- import { execSync as execSync16 } from "child_process";
8864
+ import { execSync as execSync17 } from "child_process";
8569
8865
  init_shell();
8570
8866
  function findHomeSession(cwd) {
8571
8867
  const normalizedCwd = cwd.replace(/\/+$/, "");
8572
8868
  let output;
8573
8869
  try {
8574
- output = execSync16('tmux list-sessions -F "#{session_id}|#{session_name}"', {
8870
+ output = execSync17('tmux list-sessions -F "#{session_id}|#{session_name}"', {
8575
8871
  encoding: "utf-8",
8576
8872
  stdio: ["pipe", "pipe", "pipe"]
8577
8873
  }).trim();
@@ -8585,7 +8881,7 @@ function findHomeSession(cwd) {
8585
8881
  const name = line.slice(pipeIdx + 1);
8586
8882
  if (name.startsWith("ssyph_")) continue;
8587
8883
  try {
8588
- const val = execSync16(
8884
+ const val = execSync17(
8589
8885
  `tmux show-options -t ${shellQuote(sessId)} -v @sisyphus_cwd`,
8590
8886
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
8591
8887
  ).trim();
@@ -8601,7 +8897,7 @@ function registerScratch(program2) {
8601
8897
  const cwd = opts.cwd ?? process.env["SISYPHUS_CWD"] ?? process.cwd();
8602
8898
  const homeSession = findHomeSession(cwd);
8603
8899
  if (!homeSession) {
8604
- const current = execSync16('tmux display-message -p "#{session_name}"', {
8900
+ const current = execSync17('tmux display-message -p "#{session_name}"', {
8605
8901
  encoding: "utf-8"
8606
8902
  }).trim();
8607
8903
  openScratchWindow(current, cwd, promptParts.join(" "));
@@ -8611,7 +8907,7 @@ function registerScratch(program2) {
8611
8907
  });
8612
8908
  }
8613
8909
  function openScratchWindow(tmuxSession, cwd, prompt) {
8614
- const windowId = execSync16(
8910
+ const windowId = execSync17(
8615
8911
  `tmux new-window -t ${shellQuote(tmuxSession + ":")} -n "scratch" -c ${shellQuote(cwd)} -P -F "#{window_id}"`,
8616
8912
  { encoding: "utf-8" }
8617
8913
  ).trim();
@@ -8619,7 +8915,7 @@ function openScratchWindow(tmuxSession, cwd, prompt) {
8619
8915
  if (prompt) {
8620
8916
  cmd += ` -p ${shellQuote(prompt)}`;
8621
8917
  }
8622
- execSync16(
8918
+ execSync17(
8623
8919
  `tmux send-keys -t ${shellQuote(windowId)} ${shellQuote(cmd)} Enter`
8624
8920
  );
8625
8921
  console.log(`Scratch session opened in ${tmuxSession}`);
@@ -8628,7 +8924,7 @@ function openScratchWindow(tmuxSession, cwd, prompt) {
8628
8924
  // src/cli/commands/review.ts
8629
8925
  init_paths();
8630
8926
  import { join as join23, resolve as resolve8, dirname as dirname8 } from "path";
8631
- import { existsSync as existsSync22, readFileSync as readFileSync25, writeFileSync as writeFileSync12, renameSync as renameSync3, readdirSync as readdirSync7 } from "fs";
8927
+ import { existsSync as existsSync23, readFileSync as readFileSync26, writeFileSync as writeFileSync12, renameSync as renameSync3, readdirSync as readdirSync7 } from "fs";
8632
8928
  var _statusCheck = ["draft", "question", "approved", "rejected", "deferred"];
8633
8929
  function resolveContextArtifact(file, opts, filename, notFoundMessage) {
8634
8930
  const cwd = opts.cwd || process.env.SISYPHUS_CWD || process.cwd();
@@ -8636,18 +8932,18 @@ function resolveContextArtifact(file, opts, filename, notFoundMessage) {
8636
8932
  const sessionId = opts.sessionId || process.env.SISYPHUS_SESSION_ID;
8637
8933
  if (sessionId) {
8638
8934
  const target = join23(contextDir(cwd, sessionId), filename);
8639
- if (!existsSync22(target)) {
8935
+ if (!existsSync23(target)) {
8640
8936
  console.error(`Error: File not found: ${target}`);
8641
8937
  process.exit(1);
8642
8938
  }
8643
8939
  return target;
8644
8940
  }
8645
8941
  const dir = sessionsDir(cwd);
8646
- if (existsSync22(dir)) {
8942
+ if (existsSync23(dir)) {
8647
8943
  const sessions = readdirSync7(dir);
8648
8944
  for (const session2 of sessions.reverse()) {
8649
8945
  const candidate = join23(dir, session2, "context", filename);
8650
- if (existsSync22(candidate)) return candidate;
8946
+ if (existsSync23(candidate)) return candidate;
8651
8947
  }
8652
8948
  }
8653
8949
  console.error(`Error: ${notFoundMessage}`);
@@ -8689,16 +8985,16 @@ Examples:
8689
8985
  "requirements.json",
8690
8986
  "No requirements.json found. Provide a path or use --session-id."
8691
8987
  );
8692
- if (!existsSync22(targetPath)) {
8988
+ if (!existsSync23(targetPath)) {
8693
8989
  console.error(`Error: File not found: ${targetPath}`);
8694
8990
  process.exit(1);
8695
8991
  }
8696
- const parsed = JSON.parse(readFileSync25(targetPath, "utf-8"));
8992
+ const parsed = JSON.parse(readFileSync26(targetPath, "utf-8"));
8697
8993
  const rendered = renderRequirementsMarkdown(parsed);
8698
8994
  const outPath = join23(dirname8(targetPath), "requirements.md");
8699
8995
  const tmpPath = outPath + ".tmp";
8700
- if (existsSync22(outPath)) {
8701
- const existing = readFileSync25(outPath, "utf-8");
8996
+ if (existsSync23(outPath)) {
8997
+ const existing = readFileSync26(outPath, "utf-8");
8702
8998
  if (existing !== rendered) {
8703
8999
  if (!opts.force) {
8704
9000
  process.stderr.write(
@@ -9079,7 +9375,7 @@ function renderRequirementsMarkdown(json) {
9079
9375
 
9080
9376
  // src/cli/commands/companion.ts
9081
9377
  import { basename as basename5, dirname as dirname11, join as join28 } from "path";
9082
- import { mkdirSync as mkdirSync14, readFileSync as readFileSync30, writeFileSync as writeFileSync17 } from "fs";
9378
+ import { mkdirSync as mkdirSync14, readFileSync as readFileSync31, writeFileSync as writeFileSync17 } from "fs";
9083
9379
  init_paths();
9084
9380
 
9085
9381
  // src/shared/companion-render.ts
@@ -10266,7 +10562,7 @@ function createBadgeGallery(unlockedAchievements, startIndex) {
10266
10562
 
10267
10563
  // src/daemon/companion-memory.ts
10268
10564
  init_paths();
10269
- import { existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as readFileSync27, renameSync as renameSync5, writeFileSync as writeFileSync14 } from "fs";
10565
+ import { existsSync as existsSync25, mkdirSync as mkdirSync12, readFileSync as readFileSync28, renameSync as renameSync5, writeFileSync as writeFileSync14 } from "fs";
10270
10566
  import { dirname as dirname10, join as join25 } from "path";
10271
10567
  import { randomUUID as randomUUID4 } from "crypto";
10272
10568
  import { z as z2 } from "zod";
@@ -10278,7 +10574,7 @@ var COOLDOWN_MS = 5 * 60 * 1e3;
10278
10574
 
10279
10575
  // src/daemon/companion.ts
10280
10576
  init_paths();
10281
- import { existsSync as existsSync23, mkdirSync as mkdirSync11, readFileSync as readFileSync26, renameSync as renameSync4, writeFileSync as writeFileSync13 } from "fs";
10577
+ import { existsSync as existsSync24, mkdirSync as mkdirSync11, readFileSync as readFileSync27, renameSync as renameSync4, writeFileSync as writeFileSync13 } from "fs";
10282
10578
  import { randomUUID as randomUUID3 } from "crypto";
10283
10579
  import { dirname as dirname9, join as join24 } from "path";
10284
10580
 
@@ -10329,12 +10625,12 @@ function normalizeCompanion(state) {
10329
10625
  // src/daemon/companion.ts
10330
10626
  function loadCompanion() {
10331
10627
  const path = companionPath();
10332
- if (!existsSync23(path)) {
10628
+ if (!existsSync24(path)) {
10333
10629
  const state2 = createDefaultCompanion();
10334
10630
  saveCompanion(state2);
10335
10631
  return state2;
10336
10632
  }
10337
- const raw = readFileSync26(path, "utf-8");
10633
+ const raw = readFileSync27(path, "utf-8");
10338
10634
  const state = JSON.parse(raw);
10339
10635
  return normalizeCompanion(state);
10340
10636
  }
@@ -10415,10 +10711,10 @@ function fillDefaults(state) {
10415
10711
  }
10416
10712
  function loadMemoryStrict() {
10417
10713
  const path = resolvedMemoryPath();
10418
- if (!existsSync24(path)) return defaultMemoryState();
10714
+ if (!existsSync25(path)) return defaultMemoryState();
10419
10715
  let raw;
10420
10716
  try {
10421
- raw = readFileSync27(path, "utf-8");
10717
+ raw = readFileSync28(path, "utf-8");
10422
10718
  } catch (err) {
10423
10719
  throw new MemoryStoreParseError(err);
10424
10720
  }
@@ -10461,7 +10757,7 @@ var ObservationZodSchema = z2.object({
10461
10757
  });
10462
10758
 
10463
10759
  // src/daemon/companion-popup.ts
10464
- import { writeFileSync as writeFileSync15, readFileSync as readFileSync28, unlinkSync as unlinkSync3, existsSync as existsSync25 } from "fs";
10760
+ import { writeFileSync as writeFileSync15, readFileSync as readFileSync29, unlinkSync as unlinkSync3, existsSync as existsSync26 } from "fs";
10465
10761
  import { tmpdir as tmpdir2 } from "os";
10466
10762
  import { join as join26, resolve as resolve9 } from "path";
10467
10763
  init_exec();
@@ -10515,7 +10811,7 @@ function showCommentaryPopupQueue(pages) {
10515
10811
  if (contentHeight > maxContentHeight) maxContentHeight = contentHeight;
10516
10812
  writeFileSync15(`${POPUP_TMP_PREFIX}-${i}.txt`, content);
10517
10813
  }
10518
- const whipAvailable = existsSync25(WHIP_ANIMATION_PATH);
10814
+ const whipAvailable = existsSync26(WHIP_ANIMATION_PATH);
10519
10815
  if (whipAvailable && maxContentHeight < WHIP_ANIMATION_ROWS + 2) {
10520
10816
  maxContentHeight = WHIP_ANIMATION_ROWS + 2;
10521
10817
  }
@@ -10589,7 +10885,7 @@ fi
10589
10885
  }
10590
10886
  let raw;
10591
10887
  try {
10592
- raw = readFileSync28(POPUP_RESULT_PREFIX, "utf8").trim();
10888
+ raw = readFileSync29(POPUP_RESULT_PREFIX, "utf8").trim();
10593
10889
  } catch {
10594
10890
  return null;
10595
10891
  } finally {
@@ -10766,7 +11062,7 @@ function registerCompanion(program2) {
10766
11062
  const cachePath = join28(globalDir(), "companion-context-cache", `${opts.sessionId}.json`);
10767
11063
  let prev = {};
10768
11064
  try {
10769
- prev = JSON.parse(readFileSync30(cachePath, "utf-8"));
11065
+ prev = JSON.parse(readFileSync31(cachePath, "utf-8"));
10770
11066
  } catch {
10771
11067
  prev = {};
10772
11068
  }
@@ -10809,8 +11105,8 @@ import { join as join29 } from "path";
10809
11105
  init_paths();
10810
11106
  init_exec();
10811
11107
  init_creds();
10812
- import { spawn as spawn2, spawnSync as spawnSync3 } from "child_process";
10813
- import { copyFileSync as copyFileSync2, existsSync as existsSync30, mkdirSync as mkdirSync16, readFileSync as readFileSync33 } from "fs";
11108
+ import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
11109
+ import { copyFileSync as copyFileSync2, existsSync as existsSync31, mkdirSync as mkdirSync16, readFileSync as readFileSync34 } from "fs";
10814
11110
 
10815
11111
  // src/cli/deploy/pricing.ts
10816
11112
  var LAST_VERIFIED = "2026-05-06";
@@ -10845,12 +11141,12 @@ function formatCostLine(provider, instanceType) {
10845
11141
  // src/cli/deploy/runtime.ts
10846
11142
  init_atomic();
10847
11143
  init_paths();
10848
- import { existsSync as existsSync28, readFileSync as readFileSync32, unlinkSync as unlinkSync4 } from "fs";
11144
+ import { existsSync as existsSync29, readFileSync as readFileSync33, unlinkSync as unlinkSync4 } from "fs";
10849
11145
  function readRuntimeState(provider) {
10850
11146
  const path = deployRuntimePath(provider);
10851
- if (!existsSync28(path)) return null;
11147
+ if (!existsSync29(path)) return null;
10852
11148
  try {
10853
- return JSON.parse(readFileSync32(path, "utf-8"));
11149
+ return JSON.parse(readFileSync33(path, "utf-8"));
10854
11150
  } catch {
10855
11151
  return null;
10856
11152
  }
@@ -10860,7 +11156,7 @@ function writeRuntimeState(provider, state) {
10860
11156
  }
10861
11157
  function clearRuntimeState(provider) {
10862
11158
  const path = deployRuntimePath(provider);
10863
- if (existsSync28(path)) unlinkSync4(path);
11159
+ if (existsSync29(path)) unlinkSync4(path);
10864
11160
  }
10865
11161
 
10866
11162
  // src/cli/deploy/tailnet.ts
@@ -10910,15 +11206,15 @@ function isTailscaleAvailable() {
10910
11206
  }
10911
11207
 
10912
11208
  // src/cli/deploy/templates.ts
10913
- import { existsSync as existsSync29 } from "fs";
11209
+ import { existsSync as existsSync30 } from "fs";
10914
11210
  import { dirname as dirname12, resolve as resolve10 } from "path";
10915
11211
  import { fileURLToPath as fileURLToPath4 } from "url";
10916
11212
  function deployRoot() {
10917
11213
  const here = dirname12(fileURLToPath4(import.meta.url));
10918
11214
  const bundled = resolve10(here, "..", "deploy");
10919
- if (existsSync29(bundled)) return bundled;
11215
+ if (existsSync30(bundled)) return bundled;
10920
11216
  const sourceRoot = resolve10(here, "..", "..", "..", "deploy");
10921
- if (existsSync29(sourceRoot)) return sourceRoot;
11217
+ if (existsSync30(sourceRoot)) return sourceRoot;
10922
11218
  throw new Error(
10923
11219
  `Could not locate deploy/ templates. Looked at:
10924
11220
  ${bundled}
@@ -11083,7 +11379,7 @@ async function authTailscale() {
11083
11379
  function runTerraform(provider, args2, extraEnv) {
11084
11380
  ensureProviderStateDir(provider);
11085
11381
  ensureTerraformInstalled();
11086
- const result = spawnSync3("terraform", args2, {
11382
+ const result = spawnSync4("terraform", args2, {
11087
11383
  cwd: providerModuleDir(provider),
11088
11384
  stdio: "inherit",
11089
11385
  env: { ...EXEC_ENV, ...extraEnv }
@@ -11092,7 +11388,7 @@ function runTerraform(provider, args2, extraEnv) {
11092
11388
  return result.status === null ? 1 : result.status;
11093
11389
  }
11094
11390
  function ensureTerraformInstalled() {
11095
- const result = spawnSync3("terraform", ["version"], { stdio: "pipe", env: EXEC_ENV });
11391
+ const result = spawnSync4("terraform", ["version"], { stdio: "pipe", env: EXEC_ENV });
11096
11392
  if (result.error || result.status !== 0) {
11097
11393
  const platform = process.platform;
11098
11394
  const hint = platform === "darwin" ? "brew install terraform" : "See https://developer.hashicorp.com/terraform/install";
@@ -11102,14 +11398,14 @@ function ensureTerraformInstalled() {
11102
11398
  function ensureProviderStateDir(provider) {
11103
11399
  ensureDeployDir();
11104
11400
  const dir = deployProviderDir(provider);
11105
- if (!existsSync30(dir)) mkdirSync16(dir, { recursive: true, mode: 448 });
11401
+ if (!existsSync31(dir)) mkdirSync16(dir, { recursive: true, mode: 448 });
11106
11402
  }
11107
11403
  function backupState(provider) {
11108
11404
  const src = deployStatePath(provider);
11109
- if (existsSync30(src)) copyFileSync2(src, deployStateBackupPath(provider));
11405
+ if (existsSync31(src)) copyFileSync2(src, deployStateBackupPath(provider));
11110
11406
  }
11111
11407
  function readSshPubkey(path) {
11112
- if (!existsSync30(path)) {
11408
+ if (!existsSync31(path)) {
11113
11409
  const privateKeyPath = path.replace(/\.pub$/, "");
11114
11410
  throw new Error(
11115
11411
  `SSH pubkey not found at ${path}. Generate one with:
@@ -11117,10 +11413,10 @@ function readSshPubkey(path) {
11117
11413
  or pass --ssh-key <path>.`
11118
11414
  );
11119
11415
  }
11120
- return readFileSync33(path, "utf-8").trim();
11416
+ return readFileSync34(path, "utf-8").trim();
11121
11417
  }
11122
11418
  function readOutputs(provider) {
11123
- const result = spawnSync3("terraform", ["output", "-json", `-state=${deployStatePath(provider)}`], {
11419
+ const result = spawnSync4("terraform", ["output", "-json", `-state=${deployStatePath(provider)}`], {
11124
11420
  cwd: providerModuleDir(provider),
11125
11421
  encoding: "utf-8",
11126
11422
  env: EXEC_ENV
@@ -11144,7 +11440,7 @@ function readOutputs(provider) {
11144
11440
  }
11145
11441
  }
11146
11442
  function isProvisioned(provider) {
11147
- if (!existsSync30(deployStatePath(provider))) return false;
11443
+ if (!existsSync31(deployStatePath(provider))) return false;
11148
11444
  return readOutputs(provider) !== null;
11149
11445
  }
11150
11446
  async function deployUp(provider, opts) {
@@ -11230,7 +11526,7 @@ Applied \u2014 but could not parse outputs. Run \`sis deploy ${provider} status\
11230
11526
  console.log("");
11231
11527
  }
11232
11528
  async function deployDown(provider, opts) {
11233
- if (!existsSync30(deployStatePath(provider))) {
11529
+ if (!existsSync31(deployStatePath(provider))) {
11234
11530
  console.log(`No ${provider} state found at ${deployStatePath(provider)}. Nothing to destroy.`);
11235
11531
  return;
11236
11532
  }
@@ -11311,7 +11607,7 @@ function effectiveSshTarget(provider) {
11311
11607
  }
11312
11608
  function deploySsh(provider, remoteCmd) {
11313
11609
  const target = effectiveSshTarget(provider);
11314
- const moshAvailable = spawnSync3("mosh", ["--version"], { stdio: "pipe", env: EXEC_ENV }).status === 0;
11610
+ const moshAvailable = spawnSync4("mosh", ["--version"], { stdio: "pipe", env: EXEC_ENV }).status === 0;
11315
11611
  const bin = moshAvailable && remoteCmd.length === 0 ? "mosh" : "ssh";
11316
11612
  const args2 = remoteCmd.length > 0 ? [target, ...remoteCmd] : [target];
11317
11613
  const child = spawn2(bin, args2, { stdio: "inherit", env: EXEC_ENV });
@@ -11427,10 +11723,10 @@ import { hostname } from "os";
11427
11723
 
11428
11724
  // src/cli/deploy/ssh-exec.ts
11429
11725
  init_exec();
11430
- import { spawn as spawn3, spawnSync as spawnSync4 } from "child_process";
11726
+ import { spawn as spawn3, spawnSync as spawnSync5 } from "child_process";
11431
11727
  function runOnBox(provider, cmd) {
11432
11728
  const target = effectiveSshTarget(provider);
11433
- const result = spawnSync4("ssh", [target, cmd], {
11729
+ const result = spawnSync5("ssh", [target, cmd], {
11434
11730
  encoding: "utf-8",
11435
11731
  env: EXEC_ENV
11436
11732
  });
@@ -11478,11 +11774,11 @@ function ensureGroveRegistered(provider, repo, instancePath) {
11478
11774
 
11479
11775
  // src/cli/cloud/repo.ts
11480
11776
  init_exec();
11481
- import { spawnSync as spawnSync5 } from "child_process";
11482
- import { existsSync as existsSync31 } from "fs";
11777
+ import { spawnSync as spawnSync6 } from "child_process";
11778
+ import { existsSync as existsSync32 } from "fs";
11483
11779
  import { basename as basename6, join as join30 } from "path";
11484
11780
  function captureGit(args2, cwd) {
11485
- const result = spawnSync5("git", args2, {
11781
+ const result = spawnSync6("git", args2, {
11486
11782
  encoding: "utf-8",
11487
11783
  env: EXEC_ENV,
11488
11784
  cwd: cwd ?? process.cwd()
@@ -11532,10 +11828,10 @@ function buildRsyncArgs(localDir, remoteTarget) {
11532
11828
  ];
11533
11829
  }
11534
11830
  function detectPackageManager(toplevel) {
11535
- if (existsSync31(join30(toplevel, "pnpm-lock.yaml"))) return "pnpm";
11536
- if (existsSync31(join30(toplevel, "bun.lockb"))) return "bun";
11537
- if (existsSync31(join30(toplevel, "yarn.lock"))) return "yarn";
11538
- if (existsSync31(join30(toplevel, "package-lock.json"))) return "npm";
11831
+ if (existsSync32(join30(toplevel, "pnpm-lock.yaml"))) return "pnpm";
11832
+ if (existsSync32(join30(toplevel, "bun.lockb"))) return "bun";
11833
+ if (existsSync32(join30(toplevel, "yarn.lock"))) return "yarn";
11834
+ if (existsSync32(join30(toplevel, "package-lock.json"))) return "npm";
11539
11835
  return null;
11540
11836
  }
11541
11837
  function packageManagerInstallCmd(pm) {
@@ -11744,7 +12040,7 @@ function cloudStatus(provider, repo) {
11744
12040
 
11745
12041
  // src/cli/cloud/handoff.ts
11746
12042
  import { spawn as spawn5 } from "child_process";
11747
- import { existsSync as existsSync32, readFileSync as readFileSync34, writeFileSync as writeFileSync18 } from "fs";
12043
+ import { existsSync as existsSync33, readFileSync as readFileSync35, writeFileSync as writeFileSync18 } from "fs";
11748
12044
  init_exec();
11749
12045
  init_paths();
11750
12046
  init_shell();
@@ -11819,10 +12115,10 @@ async function waitForSentOrError(cwd, sessionId) {
11819
12115
  const path = statePath(cwd, sessionId);
11820
12116
  while (Date.now() - start < MAX_WAIT_MS) {
11821
12117
  await sleep2(POLL_INTERVAL_MS);
11822
- if (!existsSync32(path)) continue;
12118
+ if (!existsSync33(path)) continue;
11823
12119
  let session2;
11824
12120
  try {
11825
- session2 = JSON.parse(readFileSync34(path, "utf-8"));
12121
+ session2 = JSON.parse(readFileSync35(path, "utf-8"));
11826
12122
  } catch (err) {
11827
12123
  void err;
11828
12124
  continue;
@@ -11886,7 +12182,7 @@ async function cloudReclaim(sessionId, opts) {
11886
12182
  await rsyncDown(target, remotePath, localPath, { withDelete: false });
11887
12183
  }
11888
12184
  const localStatePath = statePath(cwd, sessionId);
11889
- const merged = JSON.parse(readFileSync34(localStatePath, "utf-8"));
12185
+ const merged = JSON.parse(readFileSync35(localStatePath, "utf-8"));
11890
12186
  merged.cwd = cwd;
11891
12187
  merged.handoff = local.handoff;
11892
12188
  writeFileSync18(localStatePath, JSON.stringify(merged, null, 2));
@@ -11916,11 +12212,11 @@ async function cloudReclaim(sessionId, opts) {
11916
12212
  }
11917
12213
  function readLocalSession(cwd, sessionId) {
11918
12214
  const path = statePath(cwd, sessionId);
11919
- if (!existsSync32(path)) {
12215
+ if (!existsSync33(path)) {
11920
12216
  console.error(`No local state.json for ${sessionId} at ${path}.`);
11921
12217
  process.exit(1);
11922
12218
  }
11923
- return JSON.parse(readFileSync34(path, "utf-8"));
12219
+ return JSON.parse(readFileSync35(path, "utf-8"));
11924
12220
  }
11925
12221
  async function waitForBoxPaused(provider, remoteSessionDir) {
11926
12222
  const POLL_INTERVAL_MS = 2e3;
@@ -12030,8 +12326,8 @@ function attachNotify(diagnostic2) {
12030
12326
 
12031
12327
  // src/cli/commands/tmux-sessions.ts
12032
12328
  init_paths();
12033
- import { execSync as execSync18 } from "child_process";
12034
- import { readFileSync as readFileSync35, existsSync as existsSync33 } from "fs";
12329
+ import { execSync as execSync19 } from "child_process";
12330
+ import { readFileSync as readFileSync36, existsSync as existsSync34 } from "fs";
12035
12331
  var DOT_MAP = {
12036
12332
  "orchestrator:processing": { icon: "\u25CF", color: "#d4ad6a" },
12037
12333
  "orchestrator:idle": { icon: "\u25CF", color: "#d47766" },
@@ -12042,16 +12338,16 @@ var DOT_MAP = {
12042
12338
  };
12043
12339
  function readManifest() {
12044
12340
  const p = sessionsManifestPath();
12045
- if (!existsSync33(p)) return null;
12341
+ if (!existsSync34(p)) return null;
12046
12342
  try {
12047
- return JSON.parse(readFileSync35(p, "utf-8"));
12343
+ return JSON.parse(readFileSync36(p, "utf-8"));
12048
12344
  } catch {
12049
12345
  return null;
12050
12346
  }
12051
12347
  }
12052
12348
  function tmuxExec(cmd) {
12053
12349
  try {
12054
- return execSync18(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
12350
+ return execSync19(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
12055
12351
  } catch {
12056
12352
  return null;
12057
12353
  }
@@ -12087,10 +12383,23 @@ if (nodeVersion < 22) {
12087
12383
  console.error(`Sisyphus requires Node.js v22+ (current: v${process.versions.node})`);
12088
12384
  process.exit(1);
12089
12385
  }
12386
+ if (process.platform === "win32") {
12387
+ console.error(`Sisyphus does not run on native Windows (PowerShell / cmd.exe).
12388
+
12389
+ It depends on tmux, bash, and POSIX sockets \u2014 please run it inside WSL2:
12390
+
12391
+ 1. Install WSL2: https://learn.microsoft.com/windows/wsl/install
12392
+ 2. Open your WSL distro (Ubuntu is a safe default).
12393
+ 3. Install Node.js v22+ and Claude Code inside WSL.
12394
+ 4. Re-run \`sis\` from the WSL shell, not PowerShell.
12395
+
12396
+ Tip: enable systemd in /etc/wsl.conf for the recommended daemon setup.`);
12397
+ process.exit(1);
12398
+ }
12090
12399
  var program = new Command();
12091
12400
  program.name("sis").description("tmux-integrated orchestration daemon for Claude Code").version(
12092
12401
  JSON.parse(
12093
- readFileSync36(join32(dirname13(fileURLToPath5(import.meta.url)), "..", "package.json"), "utf-8")
12402
+ readFileSync37(join32(dirname13(fileURLToPath5(import.meta.url)), "..", "package.json"), "utf-8")
12094
12403
  ).version
12095
12404
  );
12096
12405
  program.configureHelp({
@@ -12165,7 +12474,7 @@ Run 'sis admin getting-started' for a complete usage guide.
12165
12474
  var args = process.argv.slice(2);
12166
12475
  var firstArg = args[0];
12167
12476
  var skipWelcome = ["admin", "help", "--help", "-h", "--version", "-V"];
12168
- if (!existsSync34(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
12477
+ if (!existsSync35(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
12169
12478
  mkdirSync17(globalDir(), { recursive: true });
12170
12479
  console.log("");
12171
12480
  console.log(" Welcome to Sisyphus. Run 'sis admin setup' to get started.");