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 +620 -311
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +236 -172
- package/dist/daemon.js.map +1 -1
- package/dist/tui.js +235 -102
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
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
|
|
252
|
+
import { execSync as execSync9 } from "child_process";
|
|
253
253
|
function exec2(cmd, cwd, timeoutMs = 3e4) {
|
|
254
|
-
return
|
|
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
|
|
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
|
|
293
|
+
import { execSync as execSync18 } from "child_process";
|
|
294
294
|
import { join as join27 } from "path";
|
|
295
|
-
import { readFileSync as
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 (!
|
|
424
|
-
const result =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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 (!
|
|
576
|
-
return parseEnvFile(
|
|
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
|
|
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
|
-
#
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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" |
|
|
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" |
|
|
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" |
|
|
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" |
|
|
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" |
|
|
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
|
-
#
|
|
1963
|
+
# Requires \`sis session context\`.
|
|
1878
1964
|
${SESSION_RESOLVE}
|
|
1879
1965
|
|
|
1880
|
-
sis session context "$session_id" --cwd "$cwd" |
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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=$(
|
|
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
|
-
'
|
|
3805
|
-
'
|
|
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
|
-
"#
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
'
|
|
3811
|
-
|
|
3812
|
-
"
|
|
3813
|
-
"
|
|
3814
|
-
"
|
|
3815
|
-
"
|
|
3816
|
-
"
|
|
3817
|
-
"
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
"
|
|
3821
|
-
"
|
|
3822
|
-
"
|
|
3823
|
-
"
|
|
3824
|
-
"
|
|
3825
|
-
"
|
|
3826
|
-
"
|
|
3827
|
-
"
|
|
3828
|
-
"
|
|
3829
|
-
|
|
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 (!
|
|
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
|
|
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
|
|
4031
|
+
lvl = level === void 0 ? "urgent" : level;
|
|
3883
4032
|
}
|
|
3884
4033
|
if (tmuxSess) ensureSwitchScript();
|
|
3885
|
-
const
|
|
3886
|
-
if (
|
|
3887
|
-
const
|
|
3888
|
-
if (
|
|
3889
|
-
|
|
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(
|
|
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 (!
|
|
4135
|
+
if (!existsSync11(p)) {
|
|
3979
4136
|
return null;
|
|
3980
4137
|
}
|
|
3981
|
-
return JSON.parse(
|
|
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 (
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
4149
|
-
return Promise.resolve(JSON.parse(
|
|
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 (!
|
|
4316
|
+
if (!existsSync12(outputPath)) return;
|
|
4160
4317
|
try {
|
|
4161
|
-
const out = JSON.parse(
|
|
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 (!
|
|
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 (
|
|
4260
|
-
result.output = JSON.parse(
|
|
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
|
|
4724
|
+
import { readFileSync as readFileSync16, readdirSync as readdirSync4 } from "fs";
|
|
4568
4725
|
|
|
4569
4726
|
// src/tui/lib/reports.ts
|
|
4570
|
-
import { readFileSync as
|
|
4727
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
4571
4728
|
function loadReportContent(report) {
|
|
4572
4729
|
try {
|
|
4573
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
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 (!
|
|
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
|
|
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 &&
|
|
5190
|
+
if (reportPath && existsSync15(reportPath)) {
|
|
5034
5191
|
try {
|
|
5035
|
-
const body =
|
|
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
|
|
5316
|
+
import { execSync as execSync11 } from "child_process";
|
|
5160
5317
|
|
|
5161
5318
|
// src/cli/onboard.ts
|
|
5162
|
-
import { execSync as
|
|
5163
|
-
import { existsSync as
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
5417
|
+
if (!existsSync16(plistPath2)) {
|
|
5204
5418
|
return { checked: false, allCorrect: false, incorrectProfiles: [] };
|
|
5205
5419
|
}
|
|
5206
5420
|
try {
|
|
5207
|
-
const json =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (!
|
|
5580
|
+
if (!existsSync16(pluginsDir)) return false;
|
|
5367
5581
|
const dest = join16(pluginsDir, "sisyphus-baleia.lua");
|
|
5368
|
-
if (
|
|
5582
|
+
if (existsSync16(dest)) return true;
|
|
5369
5583
|
const src = bundledBaleiaPluginPath();
|
|
5370
|
-
if (!
|
|
5584
|
+
if (!existsSync16(src)) return false;
|
|
5371
5585
|
try {
|
|
5372
|
-
writeFileSync8(dest,
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (
|
|
5415
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5660
|
+
execSync10("which pip3", { stdio: "pipe" });
|
|
5447
5661
|
return true;
|
|
5448
5662
|
} catch {
|
|
5449
5663
|
try {
|
|
5450
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5688
|
+
execSync10("which pip3", { stdio: "pipe" });
|
|
5475
5689
|
return "pip3";
|
|
5476
5690
|
} catch {
|
|
5477
5691
|
return "pip";
|
|
5478
5692
|
}
|
|
5479
5693
|
})();
|
|
5480
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
5660
|
-
import { readFileSync as
|
|
5918
|
+
import { execSync as execSync12 } from "child_process";
|
|
5919
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
5661
5920
|
function isTmuxInstalled2() {
|
|
5662
5921
|
try {
|
|
5663
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
5872
|
-
import { existsSync as
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
6156
|
+
if (!existsSync17(pidFile)) return false;
|
|
5898
6157
|
try {
|
|
5899
|
-
const pid = parseInt(
|
|
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 =
|
|
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 (
|
|
5945
|
-
if (
|
|
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 =
|
|
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 (!
|
|
6226
|
+
if (!existsSync17(path)) return null;
|
|
5968
6227
|
try {
|
|
5969
|
-
const parsed = JSON.parse(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
6282
|
-
import { existsSync as
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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 =
|
|
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
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
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: "
|
|
6529
|
-
fix: "
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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 (!
|
|
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
|
|
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 (
|
|
6995
|
+
if (existsSync20(configPath)) {
|
|
6700
6996
|
try {
|
|
6701
|
-
existing = JSON.parse(
|
|
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
|
|
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 =
|
|
7504
|
+
recentCommits = execSync16("git log --oneline -15 2>/dev/null", { encoding: "utf-8" }).trim();
|
|
7209
7505
|
} catch {
|
|
7210
7506
|
}
|
|
7211
7507
|
try {
|
|
7212
|
-
topLevelFiles =
|
|
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
|
|
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 (!
|
|
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 (
|
|
8030
|
+
if (existsSync21(summaryPath)) {
|
|
7735
8031
|
try {
|
|
7736
|
-
const raw =
|
|
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 (!
|
|
8046
|
+
if (!existsSync21(eventsPath)) return null;
|
|
7751
8047
|
let cwd = null;
|
|
7752
8048
|
try {
|
|
7753
|
-
const lines =
|
|
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 (!
|
|
8067
|
+
if (!existsSync21(sPath)) return null;
|
|
7772
8068
|
let session2;
|
|
7773
8069
|
try {
|
|
7774
|
-
session2 = JSON.parse(
|
|
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 (!
|
|
7836
|
-
const lines =
|
|
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 (
|
|
8145
|
+
if (existsSync21(summaryPath)) {
|
|
7850
8146
|
try {
|
|
7851
|
-
return { id: idOrName, summary: JSON.parse(
|
|
8147
|
+
return { id: idOrName, summary: JSON.parse(readFileSync23(summaryPath, "utf-8")) };
|
|
7852
8148
|
} catch {
|
|
7853
8149
|
}
|
|
7854
8150
|
}
|
|
7855
|
-
if (
|
|
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
|
|
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 (
|
|
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 =
|
|
8321
|
-
const histExists =
|
|
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 (
|
|
8623
|
+
if (existsSync22(stPath)) {
|
|
8328
8624
|
try {
|
|
8329
|
-
const state = JSON.parse(
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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 (!
|
|
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(
|
|
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 (
|
|
8701
|
-
const existing =
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
10628
|
+
if (!existsSync24(path)) {
|
|
10333
10629
|
const state2 = createDefaultCompanion();
|
|
10334
10630
|
saveCompanion(state2);
|
|
10335
10631
|
return state2;
|
|
10336
10632
|
}
|
|
10337
|
-
const raw =
|
|
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 (!
|
|
10714
|
+
if (!existsSync25(path)) return defaultMemoryState();
|
|
10419
10715
|
let raw;
|
|
10420
10716
|
try {
|
|
10421
|
-
raw =
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
10813
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
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
|
|
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 (!
|
|
11147
|
+
if (!existsSync29(path)) return null;
|
|
10852
11148
|
try {
|
|
10853
|
-
return JSON.parse(
|
|
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 (
|
|
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
|
|
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 (
|
|
11215
|
+
if (existsSync30(bundled)) return bundled;
|
|
10920
11216
|
const sourceRoot = resolve10(here, "..", "..", "..", "deploy");
|
|
10921
|
-
if (
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
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 (
|
|
11405
|
+
if (existsSync31(src)) copyFileSync2(src, deployStateBackupPath(provider));
|
|
11110
11406
|
}
|
|
11111
11407
|
function readSshPubkey(path) {
|
|
11112
|
-
if (!
|
|
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
|
|
11416
|
+
return readFileSync34(path, "utf-8").trim();
|
|
11121
11417
|
}
|
|
11122
11418
|
function readOutputs(provider) {
|
|
11123
|
-
const result =
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
11482
|
-
import { existsSync as
|
|
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 =
|
|
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 (
|
|
11536
|
-
if (
|
|
11537
|
-
if (
|
|
11538
|
-
if (
|
|
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
|
|
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 (!
|
|
12118
|
+
if (!existsSync33(path)) continue;
|
|
11823
12119
|
let session2;
|
|
11824
12120
|
try {
|
|
11825
|
-
session2 = JSON.parse(
|
|
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(
|
|
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 (!
|
|
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(
|
|
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
|
|
12034
|
-
import { readFileSync as
|
|
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 (!
|
|
12341
|
+
if (!existsSync34(p)) return null;
|
|
12046
12342
|
try {
|
|
12047
|
-
return JSON.parse(
|
|
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
|
|
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
|
-
|
|
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 (!
|
|
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.");
|