sisyphi 1.1.38 → 1.1.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/cli.js +847 -468
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +251 -173
- package/dist/daemon.js.map +1 -1
- package/dist/tui.js +307 -160
- 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 existsSync28, 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 (!existsSync28(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 (!existsSync28(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 existsSync29, 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 (!existsSync29(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 (!existsSync29(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 existsSync36, 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);
|
|
@@ -2346,7 +2420,7 @@ var DEFAULT_CONFIG = {
|
|
|
2346
2420
|
},
|
|
2347
2421
|
companionPopup: true,
|
|
2348
2422
|
requiredPlugins: [
|
|
2349
|
-
{ name: "devcore", marketplace: "crouton-kit" }
|
|
2423
|
+
{ name: "devcore", marketplace: "crouton-kit", owner: "crouton-labs" }
|
|
2350
2424
|
]
|
|
2351
2425
|
};
|
|
2352
2426
|
function readJsonFile(filePath) {
|
|
@@ -2420,7 +2494,7 @@ function isMarketplaceInstalled(marketplace) {
|
|
|
2420
2494
|
const output = exec("claude plugins marketplace list");
|
|
2421
2495
|
return output.includes(marketplace);
|
|
2422
2496
|
}
|
|
2423
|
-
function installMarketplace(marketplace, owner
|
|
2497
|
+
function installMarketplace(marketplace, owner) {
|
|
2424
2498
|
console.log(`Adding marketplace: ${owner}/${marketplace}`);
|
|
2425
2499
|
execSync2(`claude plugins marketplace add ${owner}/${marketplace}`, { stdio: "inherit" });
|
|
2426
2500
|
}
|
|
@@ -2458,7 +2532,7 @@ async function ensureRequiredPlugins(cwd) {
|
|
|
2458
2532
|
if (existing) continue;
|
|
2459
2533
|
console.log(`Required plugin ${key} not found \u2014 installing...`);
|
|
2460
2534
|
if (!isMarketplaceInstalled(plugin.marketplace)) {
|
|
2461
|
-
installMarketplace(plugin.marketplace);
|
|
2535
|
+
installMarketplace(plugin.marketplace, plugin.owner);
|
|
2462
2536
|
}
|
|
2463
2537
|
installPlugin(key);
|
|
2464
2538
|
const verified = resolveInstalledPlugin(key);
|
|
@@ -2513,6 +2587,7 @@ function isInstalled() {
|
|
|
2513
2587
|
async function ensureDaemonInstalled() {
|
|
2514
2588
|
if (process.platform !== "darwin") return;
|
|
2515
2589
|
const sisyphusPlugin = ensureSisyphusPluginInstalled();
|
|
2590
|
+
await ensureRequiredPlugins(process.cwd());
|
|
2516
2591
|
if (!isInstalled()) {
|
|
2517
2592
|
const nodePath = process.execPath;
|
|
2518
2593
|
const daemonPath = daemonBinPath();
|
|
@@ -2523,7 +2598,6 @@ async function ensureDaemonInstalled() {
|
|
|
2523
2598
|
writeFileSync2(plistPath(), plist, "utf8");
|
|
2524
2599
|
execSync3(`launchctl load -w ${plistPath()}`);
|
|
2525
2600
|
const keybindResult = await setupTmuxKeybind();
|
|
2526
|
-
await ensureRequiredPlugins(process.cwd());
|
|
2527
2601
|
printGettingStarted(keybindResult, sisyphusPlugin);
|
|
2528
2602
|
}
|
|
2529
2603
|
await waitForDaemon();
|
|
@@ -2861,14 +2935,14 @@ function registerStart(program2) {
|
|
|
2861
2935
|
process.exit(1);
|
|
2862
2936
|
}
|
|
2863
2937
|
const sessionId = response.data?.sessionId;
|
|
2864
|
-
console.
|
|
2938
|
+
console.error(`Task handed off to sisyphus orchestrator (session ${sessionId})`);
|
|
2865
2939
|
if (opts.tmuxCheck === false) {
|
|
2866
2940
|
const tmuxSessionName2 = response.data?.tmuxSessionName;
|
|
2867
2941
|
if (tmuxSessionName2) {
|
|
2868
|
-
console.
|
|
2869
|
-
console.
|
|
2942
|
+
console.error(`Tmux session: ${tmuxSessionName2}`);
|
|
2943
|
+
console.error(` tmux attach -t ${tmuxSessionName2}`);
|
|
2870
2944
|
}
|
|
2871
|
-
console.
|
|
2945
|
+
console.error(`Monitor: sis status ${sessionId}`);
|
|
2872
2946
|
return;
|
|
2873
2947
|
}
|
|
2874
2948
|
let tmuxSession;
|
|
@@ -2912,7 +2986,7 @@ function registerStart(program2) {
|
|
|
2912
2986
|
if (!process.env["TMUX"]) {
|
|
2913
2987
|
attachToTmuxSession(tmuxSession);
|
|
2914
2988
|
}
|
|
2915
|
-
console.
|
|
2989
|
+
console.error(`Monitor: sis status ${sessionId}`);
|
|
2916
2990
|
});
|
|
2917
2991
|
}
|
|
2918
2992
|
|
|
@@ -2965,24 +3039,38 @@ function statusColor(status) {
|
|
|
2965
3039
|
return "white";
|
|
2966
3040
|
}
|
|
2967
3041
|
}
|
|
3042
|
+
var COLOR_ENABLED = process.env["FORCE_COLOR"] === "1" || process.stdout.isTTY === true && process.env["NO_COLOR"] === void 0 && process.env["TERM"] !== "dumb";
|
|
3043
|
+
function wrap(open, close = "\x1B[0m") {
|
|
3044
|
+
return (s) => COLOR_ENABLED ? `${open}${s}${close}` : s;
|
|
3045
|
+
}
|
|
3046
|
+
var bold = wrap("\x1B[1m");
|
|
3047
|
+
var dim = wrap("\x1B[2m");
|
|
3048
|
+
var red = wrap("\x1B[31m");
|
|
3049
|
+
var green = wrap("\x1B[32m");
|
|
3050
|
+
var yellow = wrap("\x1B[33m");
|
|
3051
|
+
var cyan = wrap("\x1B[36m");
|
|
3052
|
+
var gray = wrap("\x1B[90m");
|
|
3053
|
+
var magenta = wrap("\x1B[35m");
|
|
3054
|
+
var white = wrap("\x1B[37m");
|
|
3055
|
+
var COLOR_FNS = {
|
|
3056
|
+
red,
|
|
3057
|
+
green,
|
|
3058
|
+
yellow,
|
|
3059
|
+
cyan,
|
|
3060
|
+
gray,
|
|
3061
|
+
magenta,
|
|
3062
|
+
white,
|
|
3063
|
+
bold,
|
|
3064
|
+
dim
|
|
3065
|
+
};
|
|
3066
|
+
function colorize(text, colorName) {
|
|
3067
|
+
const fn = COLOR_FNS[colorName];
|
|
3068
|
+
return fn ? fn(text) : text;
|
|
3069
|
+
}
|
|
2968
3070
|
|
|
2969
3071
|
// src/cli/commands/status.ts
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
yellow: "\x1B[33m",
|
|
2973
|
-
cyan: "\x1B[36m",
|
|
2974
|
-
red: "\x1B[31m",
|
|
2975
|
-
gray: "\x1B[90m",
|
|
2976
|
-
white: "\x1B[37m"
|
|
2977
|
-
};
|
|
2978
|
-
var RESET = "\x1B[0m";
|
|
2979
|
-
var BOLD = "\x1B[1m";
|
|
2980
|
-
var DIM = "\x1B[2m";
|
|
2981
|
-
function colorize(text, status) {
|
|
2982
|
-
const colorName = statusColor(status);
|
|
2983
|
-
const code = COLOR_CODES[colorName];
|
|
2984
|
-
if (!code) return `${text}\x1B[0m`;
|
|
2985
|
-
return `${code}${text}\x1B[0m`;
|
|
3072
|
+
function colorize2(text, status) {
|
|
3073
|
+
return colorize(text, statusColor(status));
|
|
2986
3074
|
}
|
|
2987
3075
|
function inferOrchestratorPhase(session2) {
|
|
2988
3076
|
const cycles = session2.orchestratorCycles;
|
|
@@ -3003,15 +3091,15 @@ function inferOrchestratorPhase(session2) {
|
|
|
3003
3091
|
}
|
|
3004
3092
|
}
|
|
3005
3093
|
function formatAgent(agent2, verbose) {
|
|
3006
|
-
const status =
|
|
3007
|
-
const name =
|
|
3008
|
-
const type =
|
|
3094
|
+
const status = colorize2(agent2.status, agent2.status);
|
|
3095
|
+
const name = bold(agent2.name);
|
|
3096
|
+
const type = dim(`(${agent2.agentType})`);
|
|
3009
3097
|
const duration = formatDuration(agent2.activeMs);
|
|
3010
|
-
let line = ` ${agent2.id} ${name} ${type} \u2014 ${status} ${
|
|
3098
|
+
let line = ` ${agent2.id} ${name} ${type} \u2014 ${status} ${dim(`(${duration})`)}`;
|
|
3011
3099
|
if (verbose && agent2.instruction) {
|
|
3012
3100
|
const truncated = agent2.instruction.length > 200 ? agent2.instruction.slice(0, 200) + "..." : agent2.instruction;
|
|
3013
3101
|
line += `
|
|
3014
|
-
${
|
|
3102
|
+
${dim(`Instruction: ${truncated}`)}`;
|
|
3015
3103
|
}
|
|
3016
3104
|
if (agent2.reports.length > 0) {
|
|
3017
3105
|
for (const r of agent2.reports) {
|
|
@@ -3029,10 +3117,10 @@ function formatAgent(agent2, verbose) {
|
|
|
3029
3117
|
function formatCycle(cycle, phase) {
|
|
3030
3118
|
let duration;
|
|
3031
3119
|
if (cycle.completedAt) {
|
|
3032
|
-
duration = ` ${
|
|
3120
|
+
duration = ` ${dim(`(${formatDuration(cycle.activeMs)})`)}`;
|
|
3033
3121
|
} else {
|
|
3034
3122
|
const elapsed = formatDuration(cycle.activeMs);
|
|
3035
|
-
duration = ` ${
|
|
3123
|
+
duration = ` ${dim(`(running, ${elapsed})`)}`;
|
|
3036
3124
|
}
|
|
3037
3125
|
const agents = cycle.agentsSpawned.length > 0 ? ` \u2014 agents: ${cycle.agentsSpawned.join(", ")}` : "";
|
|
3038
3126
|
const phaseStr = phase ? ` \u2014 orchestrator: ${phase}` : "";
|
|
@@ -3081,10 +3169,10 @@ function capturePaneOutput(paneId, lines = 50) {
|
|
|
3081
3169
|
}
|
|
3082
3170
|
}
|
|
3083
3171
|
function printSession(session2, verbose) {
|
|
3084
|
-
const status =
|
|
3172
|
+
const status = colorize2(session2.status, session2.status);
|
|
3085
3173
|
const sessionDuration = formatDuration(session2.createdAt, session2.completedAt);
|
|
3086
3174
|
console.log(`
|
|
3087
|
-
${
|
|
3175
|
+
${bold(`Session: ${session2.id}`)}`);
|
|
3088
3176
|
console.log(` Status: ${status}`);
|
|
3089
3177
|
const effortLabel = session2.effort != null ? session2.effort : "high (default)";
|
|
3090
3178
|
console.log(` Effort: ${effortLabel}`);
|
|
@@ -3105,7 +3193,7 @@ ${BOLD}Session: ${session2.id}${RESET}`);
|
|
|
3105
3193
|
if (session2.handoff) {
|
|
3106
3194
|
const h = session2.handoff;
|
|
3107
3195
|
if (h.lastError) {
|
|
3108
|
-
console.log(` Handoff: ${
|
|
3196
|
+
console.log(` Handoff: ${red("error")} \u2014 ${h.lastError}`);
|
|
3109
3197
|
} else if (h.reclaimedAt) {
|
|
3110
3198
|
const where = h.target ? `${h.target.provider}:${h.target.repo}` : "cloud";
|
|
3111
3199
|
console.log(` Handoff: reclaimed from ${where} at ${h.reclaimedAt}`);
|
|
@@ -3120,27 +3208,27 @@ ${BOLD}Session: ${session2.id}${RESET}`);
|
|
|
3120
3208
|
const runningAgents = session2.agents.filter((a) => a.status === "running");
|
|
3121
3209
|
if (runningAgents.length > 0) {
|
|
3122
3210
|
console.log(`
|
|
3123
|
-
${
|
|
3211
|
+
${bold(`Active agents (${runningAgents.length}):`)}`);
|
|
3124
3212
|
for (const agent2 of runningAgents) {
|
|
3125
|
-
const name =
|
|
3126
|
-
const type =
|
|
3213
|
+
const name = bold(agent2.name);
|
|
3214
|
+
const type = dim(`(${agent2.agentType})`);
|
|
3127
3215
|
const duration = formatDuration(agent2.activeMs);
|
|
3128
3216
|
console.log(` ${agent2.id} ${name} ${type} running ${duration}`);
|
|
3129
3217
|
if (verbose && agent2.instruction) {
|
|
3130
3218
|
const truncated = agent2.instruction.length > 200 ? agent2.instruction.slice(0, 200) + "..." : agent2.instruction;
|
|
3131
|
-
console.log(` ${
|
|
3219
|
+
console.log(` ${dim(`Instruction: ${truncated}`)}`);
|
|
3132
3220
|
}
|
|
3133
3221
|
}
|
|
3134
3222
|
}
|
|
3135
3223
|
const roadmap = readRoadmap(session2.cwd, session2.id);
|
|
3136
3224
|
if (roadmap) {
|
|
3137
3225
|
console.log(`
|
|
3138
|
-
${
|
|
3226
|
+
${bold("Roadmap:")}`);
|
|
3139
3227
|
console.log(roadmap);
|
|
3140
3228
|
}
|
|
3141
3229
|
if (session2.orchestratorCycles.length > 0) {
|
|
3142
3230
|
console.log(`
|
|
3143
|
-
${
|
|
3231
|
+
${bold("Cycles:")}`);
|
|
3144
3232
|
const cycles = session2.orchestratorCycles;
|
|
3145
3233
|
for (let i = 0; i < cycles.length; i++) {
|
|
3146
3234
|
const isLast = i === cycles.length - 1;
|
|
@@ -3151,12 +3239,12 @@ ${BOLD}Roadmap:${RESET}`);
|
|
|
3151
3239
|
if (log) {
|
|
3152
3240
|
const lines = log.split("\n");
|
|
3153
3241
|
const preview = lines.slice(0, 20).join("\n");
|
|
3154
|
-
console.log(` ${
|
|
3242
|
+
console.log(` ${dim("--- cycle log ---")}`);
|
|
3155
3243
|
for (const line of preview.split("\n")) {
|
|
3156
|
-
console.log(` ${
|
|
3244
|
+
console.log(` ${dim(line)}`);
|
|
3157
3245
|
}
|
|
3158
3246
|
if (lines.length > 20) {
|
|
3159
|
-
console.log(` ${
|
|
3247
|
+
console.log(` ${dim(`... (${lines.length - 20} more lines)`)}`);
|
|
3160
3248
|
}
|
|
3161
3249
|
}
|
|
3162
3250
|
}
|
|
@@ -3164,7 +3252,7 @@ ${BOLD}Roadmap:${RESET}`);
|
|
|
3164
3252
|
}
|
|
3165
3253
|
if (session2.agents.length > 0) {
|
|
3166
3254
|
console.log(`
|
|
3167
|
-
${
|
|
3255
|
+
${bold("Agents:")}`);
|
|
3168
3256
|
for (const agent2 of session2.agents) {
|
|
3169
3257
|
console.log(formatAgent(agent2, verbose));
|
|
3170
3258
|
}
|
|
@@ -3194,7 +3282,7 @@ ${BOLD}Roadmap:${RESET}`);
|
|
|
3194
3282
|
}
|
|
3195
3283
|
if (verbose && session2.completionReport) {
|
|
3196
3284
|
console.log(`
|
|
3197
|
-
${
|
|
3285
|
+
${bold("Completion Report:")}`);
|
|
3198
3286
|
console.log(session2.completionReport);
|
|
3199
3287
|
}
|
|
3200
3288
|
}
|
|
@@ -3226,38 +3314,45 @@ function registerStatus(program2) {
|
|
|
3226
3314
|
|
|
3227
3315
|
// src/cli/commands/list.ts
|
|
3228
3316
|
import { basename as basename3 } from "path";
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3317
|
+
function statusColor2(s) {
|
|
3318
|
+
switch (s) {
|
|
3319
|
+
case "active":
|
|
3320
|
+
return "green";
|
|
3321
|
+
case "paused":
|
|
3322
|
+
return "yellow";
|
|
3323
|
+
case "completed":
|
|
3324
|
+
return "cyan";
|
|
3325
|
+
default:
|
|
3326
|
+
return "";
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
function colorStatus(s) {
|
|
3330
|
+
const name = statusColor2(s);
|
|
3331
|
+
if (!name) return s;
|
|
3332
|
+
return colorize(s, name);
|
|
3333
|
+
}
|
|
3239
3334
|
function handoffAnnotation(h) {
|
|
3240
3335
|
if (!h) return "";
|
|
3241
3336
|
if (h.lastError) {
|
|
3242
|
-
return ` ${
|
|
3337
|
+
return ` ${red(`handoff error: ${h.lastError}`)}`;
|
|
3243
3338
|
}
|
|
3244
3339
|
if (h.reclaimedAt) {
|
|
3245
|
-
return ` ${
|
|
3340
|
+
return ` ${dim("(reclaimed)")}`;
|
|
3246
3341
|
}
|
|
3247
3342
|
if (h.sentAt && h.target) {
|
|
3248
|
-
return ` ${
|
|
3343
|
+
return ` ${magenta(`\u2192 ${h.target.provider}:${h.target.repo}`)}`;
|
|
3249
3344
|
}
|
|
3250
3345
|
if (h.target) {
|
|
3251
|
-
return ` ${
|
|
3346
|
+
return ` ${magenta(`handoff queued \u2192 ${h.target.provider}:${h.target.repo}`)}`;
|
|
3252
3347
|
}
|
|
3253
|
-
return ` ${
|
|
3348
|
+
return ` ${magenta("quiesce queued")}`;
|
|
3254
3349
|
}
|
|
3255
3350
|
function truncateTask(task, max) {
|
|
3256
3351
|
if (task.length <= max) return task;
|
|
3257
3352
|
return task.slice(0, max - 1) + "\u2026";
|
|
3258
3353
|
}
|
|
3259
3354
|
function registerList(program2) {
|
|
3260
|
-
program2.command("list").description("List sessions (defaults to current project)").option("-a, --all", "Show sessions from all projects").option("--cwd <path>", "Project directory to list sessions for (overrides SISYPHUS_CWD)").action(async (opts) => {
|
|
3355
|
+
program2.command("list").description("List sessions (defaults to current project)").option("-a, --all", "Show sessions from all projects").option("--cwd <path>", "Project directory to list sessions for (overrides SISYPHUS_CWD)").option("-j, --json", "Output raw JSON").action(async (opts) => {
|
|
3261
3356
|
const cwd = opts.cwd ?? process.env["SISYPHUS_CWD"] ?? process.cwd();
|
|
3262
3357
|
const request = { type: "list", cwd, all: opts.all };
|
|
3263
3358
|
const response = await sendRequest(request);
|
|
@@ -3265,29 +3360,32 @@ function registerList(program2) {
|
|
|
3265
3360
|
const sessions = response.data?.sessions ?? [];
|
|
3266
3361
|
const totalCount = response.data?.totalCount;
|
|
3267
3362
|
const filtered = response.data?.filtered;
|
|
3363
|
+
if (opts.json) {
|
|
3364
|
+
console.log(JSON.stringify(sessions));
|
|
3365
|
+
return;
|
|
3366
|
+
}
|
|
3268
3367
|
if (sessions.length === 0) {
|
|
3269
3368
|
if (filtered && totalCount && totalCount > 0) {
|
|
3270
3369
|
console.log(`No sessions in this project. ${totalCount} session(s) in other projects.`);
|
|
3271
|
-
console.log(`${
|
|
3370
|
+
console.log(`${dim("Run ")}sis list --all${dim(" to show all.")}`);
|
|
3272
3371
|
} else {
|
|
3273
3372
|
console.log("No sessions");
|
|
3274
3373
|
}
|
|
3275
3374
|
return;
|
|
3276
3375
|
}
|
|
3277
3376
|
for (const s of sessions) {
|
|
3278
|
-
const
|
|
3279
|
-
const
|
|
3280
|
-
const agents = `${DIM2}${s.agentCount} agent(s)${RESET2}`;
|
|
3377
|
+
const status = colorStatus(s.status);
|
|
3378
|
+
const agents = dim(`${s.agentCount} agent(s)`);
|
|
3281
3379
|
const task = truncateTask(s.task, 60);
|
|
3282
|
-
const label = s.name ? `${s.name} ${
|
|
3283
|
-
const cwdLabel = opts.all && s.cwd ? ` ${
|
|
3380
|
+
const label = s.name ? `${s.name} ${dim(`(${s.id.slice(0, 8)})`)}` : s.id;
|
|
3381
|
+
const cwdLabel = opts.all && s.cwd ? ` ${dim(basename3(s.cwd))}` : "";
|
|
3284
3382
|
const handoffLabel = handoffAnnotation(s.handoff);
|
|
3285
|
-
console.log(` ${
|
|
3383
|
+
console.log(` ${bold(label)} ${status} ${agents} ${task}${cwdLabel}${handoffLabel}`);
|
|
3286
3384
|
}
|
|
3287
3385
|
if (filtered && totalCount && totalCount > sessions.length) {
|
|
3288
3386
|
const otherCount = totalCount - sessions.length;
|
|
3289
3387
|
console.log(`
|
|
3290
|
-
${
|
|
3388
|
+
${dim(`${otherCount} more session(s) in other projects. Run `)}sis list --all${dim(" to show all.")}`);
|
|
3291
3389
|
}
|
|
3292
3390
|
} else {
|
|
3293
3391
|
console.error(`Error: ${response.error}`);
|
|
@@ -3349,7 +3447,7 @@ function registerTell(program2) {
|
|
|
3349
3447
|
};
|
|
3350
3448
|
const response = await sendRequest(request);
|
|
3351
3449
|
if (response.ok) {
|
|
3352
|
-
console.
|
|
3450
|
+
console.error(`Sent to ${targetRaw}${submit2 ? "" : " (not submitted)"}`);
|
|
3353
3451
|
} else {
|
|
3354
3452
|
console.error(`Error: ${response.error}`);
|
|
3355
3453
|
process.exit(1);
|
|
@@ -3426,7 +3524,7 @@ function summaryLine(entry) {
|
|
|
3426
3524
|
return `${role} ${ts} ${preview}`;
|
|
3427
3525
|
}
|
|
3428
3526
|
function registerRead(program2) {
|
|
3429
|
-
program2.command("read <target>").description("Print the Claude conversation transcript for a target ('orchestrator' or 'agent-NNN').").option("--session <sessionId>", "Session ID (defaults to SISYPHUS_SESSION_ID)").option("--cycle <n>", "Orchestrator cycle number (default: most recent live, else last completed)").option("--tail <n>", "Show last N turns", void 0).option("--head <n>", "Show first N turns", void 0).option("--raw", "Print raw JSONL (no formatting, no filtering)").option("--summary", "One-line-per-turn summary instead of full content").option("--tool-detail", "Include full tool inputs/outputs (default: truncated to 400/600 chars)").action(async (targetRaw, opts) => {
|
|
3527
|
+
program2.command("read <target>").description("Print the Claude conversation transcript for a target ('orchestrator' or 'agent-NNN').").option("--session <sessionId>", "Session ID (defaults to SISYPHUS_SESSION_ID)").option("--cycle <n>", "Orchestrator cycle number (default: most recent live, else last completed)").option("--tail <n>", "Show last N turns", void 0).option("--head <n>", "Show first N turns", void 0).option("--raw", "Print raw JSONL (no formatting, no filtering)").option("--summary", "One-line-per-turn summary instead of full content").option("--tool-detail", "Include full tool inputs/outputs (default: truncated to 400/600 chars)").option("-j, --json", "Output JSONL \u2014 one JSON object per turn (mutually exclusive with --raw and --summary)").action(async (targetRaw, opts) => {
|
|
3430
3528
|
const sessionId = opts.session ?? process.env.SISYPHUS_SESSION_ID;
|
|
3431
3529
|
if (!sessionId) {
|
|
3432
3530
|
console.error("Error: provide --session or set SISYPHUS_SESSION_ID");
|
|
@@ -3488,6 +3586,14 @@ function registerRead(program2) {
|
|
|
3488
3586
|
process.exit(1);
|
|
3489
3587
|
}
|
|
3490
3588
|
const raw = readFileSync7(path, "utf-8");
|
|
3589
|
+
if (opts.json && opts.raw) {
|
|
3590
|
+
console.error("Error: --json and --raw are mutually exclusive");
|
|
3591
|
+
process.exit(1);
|
|
3592
|
+
}
|
|
3593
|
+
if (opts.json && opts.summary) {
|
|
3594
|
+
console.error("Error: --json and --summary are mutually exclusive");
|
|
3595
|
+
process.exit(1);
|
|
3596
|
+
}
|
|
3491
3597
|
if (opts.raw) {
|
|
3492
3598
|
process.stdout.write(raw);
|
|
3493
3599
|
return;
|
|
@@ -3512,6 +3618,16 @@ function registerRead(program2) {
|
|
|
3512
3618
|
entries = entries.slice(-tail);
|
|
3513
3619
|
sliceNote = sliceNote ? `${sliceNote}, then last ${tail}` : `last ${tail} of ${totalTurns}`;
|
|
3514
3620
|
}
|
|
3621
|
+
if (opts.json) {
|
|
3622
|
+
for (const e of entries) {
|
|
3623
|
+
console.log(JSON.stringify({
|
|
3624
|
+
role: e.type,
|
|
3625
|
+
timestamp: e.timestamp,
|
|
3626
|
+
content: e.message?.content
|
|
3627
|
+
}));
|
|
3628
|
+
}
|
|
3629
|
+
return;
|
|
3630
|
+
}
|
|
3515
3631
|
console.log(`=== ${label} \u2014 ${entries.length} turn(s)${sliceNote ? ` (${sliceNote})` : ""} ===`);
|
|
3516
3632
|
console.log(`transcript: ${path}
|
|
3517
3633
|
`);
|
|
@@ -3562,7 +3678,7 @@ function registerMessage(program2) {
|
|
|
3562
3678
|
const request = { type: "message", sessionId, content, source, ...opts.agent ? { agentId: opts.agent } : {} };
|
|
3563
3679
|
const response = await sendRequest(request);
|
|
3564
3680
|
if (response.ok) {
|
|
3565
|
-
console.
|
|
3681
|
+
console.error("Message queued");
|
|
3566
3682
|
} else {
|
|
3567
3683
|
console.error(`Error: ${response.error}`);
|
|
3568
3684
|
process.exit(1);
|
|
@@ -3571,7 +3687,7 @@ function registerMessage(program2) {
|
|
|
3571
3687
|
}
|
|
3572
3688
|
|
|
3573
3689
|
// src/cli/commands/ask.ts
|
|
3574
|
-
import { existsSync as
|
|
3690
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14, watchFile, unwatchFile } from "fs";
|
|
3575
3691
|
import { join as join13, resolve as resolve4 } from "path";
|
|
3576
3692
|
import { ulid } from "ulid";
|
|
3577
3693
|
|
|
@@ -3692,7 +3808,7 @@ function parseDeck(deckPath) {
|
|
|
3692
3808
|
|
|
3693
3809
|
// src/daemon/ask-store.ts
|
|
3694
3810
|
init_paths();
|
|
3695
|
-
import { existsSync as
|
|
3811
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync13, readdirSync as readdirSync3 } from "fs";
|
|
3696
3812
|
|
|
3697
3813
|
// src/daemon/history.ts
|
|
3698
3814
|
init_paths();
|
|
@@ -3786,47 +3902,115 @@ init_atomic();
|
|
|
3786
3902
|
// src/daemon/notify.ts
|
|
3787
3903
|
init_shell();
|
|
3788
3904
|
import { spawn, execFile } from "child_process";
|
|
3789
|
-
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as
|
|
3905
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync10 } from "fs";
|
|
3790
3906
|
import { join as join12 } from "path";
|
|
3791
3907
|
import { homedir as homedir7 } from "os";
|
|
3908
|
+
|
|
3909
|
+
// src/shared/platform.ts
|
|
3910
|
+
import { execSync as execSync8 } from "child_process";
|
|
3911
|
+
import { existsSync as existsSync9, readFileSync as readFileSync12 } from "fs";
|
|
3912
|
+
var cachedPlatform;
|
|
3913
|
+
function detectPlatform() {
|
|
3914
|
+
if (cachedPlatform) return cachedPlatform;
|
|
3915
|
+
if (process.platform === "darwin") {
|
|
3916
|
+
cachedPlatform = "darwin";
|
|
3917
|
+
} else if (process.platform === "win32") {
|
|
3918
|
+
cachedPlatform = "win32";
|
|
3919
|
+
} else if (process.platform === "linux") {
|
|
3920
|
+
cachedPlatform = isWsl() ? "wsl" : "linux";
|
|
3921
|
+
} else {
|
|
3922
|
+
cachedPlatform = "unknown";
|
|
3923
|
+
}
|
|
3924
|
+
return cachedPlatform;
|
|
3925
|
+
}
|
|
3926
|
+
function isWsl() {
|
|
3927
|
+
if (process.env["WSL_DISTRO_NAME"] || process.env["WSL_INTEROP"]) return true;
|
|
3928
|
+
try {
|
|
3929
|
+
if (existsSync9("/proc/version")) {
|
|
3930
|
+
const v = readFileSync12("/proc/version", "utf-8").toLowerCase();
|
|
3931
|
+
if (v.includes("microsoft") || v.includes("wsl")) return true;
|
|
3932
|
+
}
|
|
3933
|
+
} catch {
|
|
3934
|
+
}
|
|
3935
|
+
return false;
|
|
3936
|
+
}
|
|
3937
|
+
function isWslHost() {
|
|
3938
|
+
return detectPlatform() === "wsl";
|
|
3939
|
+
}
|
|
3940
|
+
function isLinuxLike() {
|
|
3941
|
+
const p = detectPlatform();
|
|
3942
|
+
return p === "linux" || p === "wsl";
|
|
3943
|
+
}
|
|
3944
|
+
var cmdCache = /* @__PURE__ */ new Map();
|
|
3945
|
+
function hasCommand(cmd) {
|
|
3946
|
+
const cached = cmdCache.get(cmd);
|
|
3947
|
+
if (cached !== void 0) return cached;
|
|
3948
|
+
try {
|
|
3949
|
+
execSync8(`command -v ${cmd}`, { stdio: "pipe", shell: "/bin/sh" });
|
|
3950
|
+
cmdCache.set(cmd, true);
|
|
3951
|
+
return true;
|
|
3952
|
+
} catch {
|
|
3953
|
+
cmdCache.set(cmd, false);
|
|
3954
|
+
return false;
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
function platformLabel() {
|
|
3958
|
+
switch (detectPlatform()) {
|
|
3959
|
+
case "darwin":
|
|
3960
|
+
return "macOS";
|
|
3961
|
+
case "wsl": {
|
|
3962
|
+
const distro = process.env["WSL_DISTRO_NAME"];
|
|
3963
|
+
return distro ? `WSL (${distro})` : "WSL";
|
|
3964
|
+
}
|
|
3965
|
+
case "linux":
|
|
3966
|
+
return "Linux";
|
|
3967
|
+
case "win32":
|
|
3968
|
+
return "Windows (native)";
|
|
3969
|
+
default:
|
|
3970
|
+
return "unknown";
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
|
|
3974
|
+
// src/daemon/notify.ts
|
|
3792
3975
|
var TMUX_SOCKET = `/tmp/tmux-${process.getuid?.() ?? 0}/default`;
|
|
3793
3976
|
var SWITCH_SCRIPT = [
|
|
3794
3977
|
"#!/bin/bash",
|
|
3795
3978
|
'SESSION="$1"',
|
|
3796
3979
|
`TMUX_SOCKET="${TMUX_SOCKET}"`,
|
|
3797
|
-
"TMUX=/opt/homebrew/bin/tmux",
|
|
3798
3980
|
"",
|
|
3799
3981
|
"# Find any attached client (user is likely on a different session)",
|
|
3800
|
-
`CLIENT_TTY=$(
|
|
3982
|
+
`CLIENT_TTY=$(tmux -S "$TMUX_SOCKET" list-clients -F '#{client_tty}' 2>/dev/null | head -1)`,
|
|
3801
3983
|
'[ -z "$CLIENT_TTY" ] && exit 0',
|
|
3802
3984
|
"",
|
|
3803
3985
|
"# Switch that client to the target session",
|
|
3804
|
-
'
|
|
3805
|
-
'
|
|
3986
|
+
'tmux -S "$TMUX_SOCKET" switch-client -c "$CLIENT_TTY" -t "$SESSION" 2>/dev/null',
|
|
3987
|
+
'tmux -S "$TMUX_SOCKET" select-window -t "$SESSION" 2>/dev/null',
|
|
3806
3988
|
"",
|
|
3807
|
-
"#
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
'
|
|
3811
|
-
|
|
3812
|
-
"
|
|
3813
|
-
"
|
|
3814
|
-
"
|
|
3815
|
-
"
|
|
3816
|
-
"
|
|
3817
|
-
"
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
"
|
|
3821
|
-
"
|
|
3822
|
-
"
|
|
3823
|
-
"
|
|
3824
|
-
"
|
|
3825
|
-
"
|
|
3826
|
-
"
|
|
3827
|
-
"
|
|
3828
|
-
"
|
|
3829
|
-
|
|
3989
|
+
"# macOS-only: bring iTerm2 to front and select the tab with this client",
|
|
3990
|
+
'if [ "$(uname -s)" = "Darwin" ] && command -v osascript >/dev/null 2>&1; then',
|
|
3991
|
+
` TTY_SHORT=$(echo "$CLIENT_TTY" | sed 's|/dev/||')`,
|
|
3992
|
+
' osascript -e "',
|
|
3993
|
+
' tell application \\"iTerm2\\"',
|
|
3994
|
+
" activate",
|
|
3995
|
+
" repeat with w in windows",
|
|
3996
|
+
" tell w",
|
|
3997
|
+
" repeat with t in tabs",
|
|
3998
|
+
" tell t",
|
|
3999
|
+
" repeat with s in sessions",
|
|
4000
|
+
" tell s",
|
|
4001
|
+
' if tty contains \\"$TTY_SHORT\\" then',
|
|
4002
|
+
" select t",
|
|
4003
|
+
" return",
|
|
4004
|
+
" end if",
|
|
4005
|
+
" end tell",
|
|
4006
|
+
" end repeat",
|
|
4007
|
+
" end tell",
|
|
4008
|
+
" end repeat",
|
|
4009
|
+
" end tell",
|
|
4010
|
+
" end repeat",
|
|
4011
|
+
" end tell",
|
|
4012
|
+
` " 2>/dev/null || osascript -e 'tell application "iTerm2" to activate' 2>/dev/null`,
|
|
4013
|
+
"fi",
|
|
3830
4014
|
""
|
|
3831
4015
|
].join("\n");
|
|
3832
4016
|
function ensureSwitchScript() {
|
|
@@ -3847,7 +4031,7 @@ function ensureNotifyProcess() {
|
|
|
3847
4031
|
return notifyProcess;
|
|
3848
4032
|
}
|
|
3849
4033
|
const binary = getNotifyBinary();
|
|
3850
|
-
if (!
|
|
4034
|
+
if (!existsSync10(binary)) {
|
|
3851
4035
|
return null;
|
|
3852
4036
|
}
|
|
3853
4037
|
notifyProcess = spawn(binary, [], {
|
|
@@ -3865,6 +4049,13 @@ function ensureNotifyProcess() {
|
|
|
3865
4049
|
notifyProcess.stderr?.unref();
|
|
3866
4050
|
return notifyProcess;
|
|
3867
4051
|
}
|
|
4052
|
+
function sendLinuxNotification(title, msg, level) {
|
|
4053
|
+
if (!hasCommand("notify-send")) return false;
|
|
4054
|
+
const urgency = level === "urgent" ? "critical" : "normal";
|
|
4055
|
+
execFile("notify-send", ["--app-name=Sisyphus", `--urgency=${urgency}`, title, msg], () => {
|
|
4056
|
+
});
|
|
4057
|
+
return true;
|
|
4058
|
+
}
|
|
3868
4059
|
function sendTerminalNotification(titleOrOpts, message, tmuxSession, level) {
|
|
3869
4060
|
let title;
|
|
3870
4061
|
let msg;
|
|
@@ -3874,33 +4065,41 @@ function sendTerminalNotification(titleOrOpts, message, tmuxSession, level) {
|
|
|
3874
4065
|
title = titleOrOpts.title;
|
|
3875
4066
|
msg = titleOrOpts.message;
|
|
3876
4067
|
tmuxSess = titleOrOpts.tmuxSession;
|
|
3877
|
-
lvl = titleOrOpts.level
|
|
4068
|
+
lvl = titleOrOpts.level === void 0 ? "urgent" : titleOrOpts.level;
|
|
3878
4069
|
} else {
|
|
3879
4070
|
title = titleOrOpts;
|
|
3880
4071
|
msg = message;
|
|
3881
4072
|
tmuxSess = tmuxSession;
|
|
3882
|
-
lvl = level
|
|
4073
|
+
lvl = level === void 0 ? "urgent" : level;
|
|
3883
4074
|
}
|
|
3884
4075
|
if (tmuxSess) ensureSwitchScript();
|
|
3885
|
-
const
|
|
3886
|
-
if (
|
|
3887
|
-
const
|
|
3888
|
-
if (
|
|
3889
|
-
|
|
4076
|
+
const platform = detectPlatform();
|
|
4077
|
+
if (platform === "darwin") {
|
|
4078
|
+
const proc = ensureNotifyProcess();
|
|
4079
|
+
if (proc?.stdin?.writable) {
|
|
4080
|
+
const payload = { title, message: msg, level: lvl };
|
|
4081
|
+
if (tmuxSess) payload.tmuxSession = tmuxSess;
|
|
4082
|
+
proc.stdin.write(JSON.stringify(payload) + "\n");
|
|
4083
|
+
return;
|
|
4084
|
+
}
|
|
4085
|
+
const tnArgs = ["-title", title, "-message", msg];
|
|
4086
|
+
if (lvl === "urgent") tnArgs.push("-sound", "default");
|
|
4087
|
+
execFile("terminal-notifier", tnArgs, (err) => {
|
|
4088
|
+
if (err) {
|
|
4089
|
+
const soundClause = lvl === "urgent" ? ' sound name "default"' : "";
|
|
4090
|
+
execFile("osascript", [
|
|
4091
|
+
"-e",
|
|
4092
|
+
`display notification "${escapeAppleScript(msg)}" with title "${escapeAppleScript(title)}"${soundClause}`
|
|
4093
|
+
], () => {
|
|
4094
|
+
});
|
|
4095
|
+
}
|
|
4096
|
+
});
|
|
4097
|
+
return;
|
|
4098
|
+
}
|
|
4099
|
+
if (platform === "linux" || platform === "wsl") {
|
|
4100
|
+
sendLinuxNotification(title, msg, lvl);
|
|
3890
4101
|
return;
|
|
3891
4102
|
}
|
|
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
4103
|
}
|
|
3905
4104
|
|
|
3906
4105
|
// src/daemon/ask-store.ts
|
|
@@ -3962,7 +4161,7 @@ function writeDecisions(cwd, sessionId, askId, deck) {
|
|
|
3962
4161
|
function readDecisions(cwd, sessionId, askId) {
|
|
3963
4162
|
const p = askDecisionsPath(cwd, sessionId, askId);
|
|
3964
4163
|
try {
|
|
3965
|
-
return JSON.parse(
|
|
4164
|
+
return JSON.parse(readFileSync13(p, { encoding: "utf-8" }));
|
|
3966
4165
|
} catch (_e) {
|
|
3967
4166
|
return null;
|
|
3968
4167
|
}
|
|
@@ -3975,10 +4174,10 @@ function writeOutput(cwd, sessionId, askId, responses, completedAt) {
|
|
|
3975
4174
|
}
|
|
3976
4175
|
function readMeta(cwd, sessionId, askId) {
|
|
3977
4176
|
const p = askMetaPath(cwd, sessionId, askId);
|
|
3978
|
-
if (!
|
|
4177
|
+
if (!existsSync11(p)) {
|
|
3979
4178
|
return null;
|
|
3980
4179
|
}
|
|
3981
|
-
return JSON.parse(
|
|
4180
|
+
return JSON.parse(readFileSync13(p, "utf-8"));
|
|
3982
4181
|
}
|
|
3983
4182
|
async function updateMeta(cwd, sessionId, askId, patch) {
|
|
3984
4183
|
return withLock(askId, () => {
|
|
@@ -4002,7 +4201,7 @@ function buildAutoResponses(deck) {
|
|
|
4002
4201
|
}
|
|
4003
4202
|
async function autoResolveAsk(cwd, sessionId, askId, deck) {
|
|
4004
4203
|
try {
|
|
4005
|
-
if (
|
|
4204
|
+
if (existsSync11(askOutputPath(cwd, sessionId, askId))) return false;
|
|
4006
4205
|
const d = deck ?? readDecisions(cwd, sessionId, askId);
|
|
4007
4206
|
if (!d) return false;
|
|
4008
4207
|
const responses = buildAutoResponses(d);
|
|
@@ -4099,7 +4298,7 @@ function mintAskId() {
|
|
|
4099
4298
|
return ulid();
|
|
4100
4299
|
}
|
|
4101
4300
|
function resolveClaudeSessionId(cwd, sessionId, askedBy) {
|
|
4102
|
-
if (!
|
|
4301
|
+
if (!existsSync12(statePath(cwd, sessionId))) return void 0;
|
|
4103
4302
|
const session2 = getSession(cwd, sessionId);
|
|
4104
4303
|
if (askedBy === ORCHESTRATOR_ASKED_BY) {
|
|
4105
4304
|
const last = session2.orchestratorCycles[session2.orchestratorCycles.length - 1];
|
|
@@ -4136,7 +4335,7 @@ async function markAnswered(cwd, sessionId, askId) {
|
|
|
4136
4335
|
});
|
|
4137
4336
|
if (meta.blocking && durationMs > 0) {
|
|
4138
4337
|
try {
|
|
4139
|
-
if (
|
|
4338
|
+
if (existsSync12(statePath(cwd, sessionId))) {
|
|
4140
4339
|
await incrementUserBlockedMs(cwd, sessionId, durationMs, meta.askedAt, meta.askedBy);
|
|
4141
4340
|
}
|
|
4142
4341
|
} catch {
|
|
@@ -4145,8 +4344,8 @@ async function markAnswered(cwd, sessionId, askId) {
|
|
|
4145
4344
|
}
|
|
4146
4345
|
function waitForOutput(cwd, sessionId, askId, initialPpid) {
|
|
4147
4346
|
const outputPath = askOutputPath(cwd, sessionId, askId);
|
|
4148
|
-
if (
|
|
4149
|
-
return Promise.resolve(JSON.parse(
|
|
4347
|
+
if (existsSync12(outputPath)) {
|
|
4348
|
+
return Promise.resolve(JSON.parse(readFileSync14(outputPath, "utf-8")));
|
|
4150
4349
|
}
|
|
4151
4350
|
return new Promise((res, _rej) => {
|
|
4152
4351
|
let ppidWatcher;
|
|
@@ -4156,9 +4355,9 @@ function waitForOutput(cwd, sessionId, askId, initialPpid) {
|
|
|
4156
4355
|
process.removeListener("SIGINT", onSigint);
|
|
4157
4356
|
};
|
|
4158
4357
|
const onChange = () => {
|
|
4159
|
-
if (!
|
|
4358
|
+
if (!existsSync12(outputPath)) return;
|
|
4160
4359
|
try {
|
|
4161
|
-
const out = JSON.parse(
|
|
4360
|
+
const out = JSON.parse(readFileSync14(outputPath, "utf-8"));
|
|
4162
4361
|
cleanup();
|
|
4163
4362
|
res(out);
|
|
4164
4363
|
} catch (err) {
|
|
@@ -4197,7 +4396,7 @@ async function submit(file, opts) {
|
|
|
4197
4396
|
const { cwd, sessionId } = resolveSessionEnv(opts);
|
|
4198
4397
|
const askedBy = process.env.SISYPHUS_AGENT_ID ?? ORCHESTRATOR_ASKED_BY;
|
|
4199
4398
|
const deckPath = resolve4(file);
|
|
4200
|
-
if (!
|
|
4399
|
+
if (!existsSync12(deckPath)) {
|
|
4201
4400
|
console.error(`Error: deck file not found: ${deckPath}`);
|
|
4202
4401
|
process.exit(1);
|
|
4203
4402
|
}
|
|
@@ -4256,8 +4455,8 @@ async function peek(askId, opts) {
|
|
|
4256
4455
|
};
|
|
4257
4456
|
if (meta.completedAt) result.completedAt = meta.completedAt;
|
|
4258
4457
|
try {
|
|
4259
|
-
if (
|
|
4260
|
-
result.output = JSON.parse(
|
|
4458
|
+
if (existsSync12(outputPath)) {
|
|
4459
|
+
result.output = JSON.parse(readFileSync14(outputPath, "utf-8"));
|
|
4261
4460
|
}
|
|
4262
4461
|
} catch (err) {
|
|
4263
4462
|
if (!(err instanceof SyntaxError)) throw err;
|
|
@@ -4564,13 +4763,13 @@ function registerSessionDangerous(program2) {
|
|
|
4564
4763
|
|
|
4565
4764
|
// src/tui/lib/context.ts
|
|
4566
4765
|
init_paths();
|
|
4567
|
-
import { readFileSync as
|
|
4766
|
+
import { readFileSync as readFileSync16, readdirSync as readdirSync4 } from "fs";
|
|
4568
4767
|
|
|
4569
4768
|
// src/tui/lib/reports.ts
|
|
4570
|
-
import { readFileSync as
|
|
4769
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
4571
4770
|
function loadReportContent(report) {
|
|
4572
4771
|
try {
|
|
4573
|
-
return
|
|
4772
|
+
return readFileSync15(report.filePath, "utf-8");
|
|
4574
4773
|
} catch {
|
|
4575
4774
|
return report.summary;
|
|
4576
4775
|
}
|
|
@@ -4587,7 +4786,7 @@ function resolveReports(reports) {
|
|
|
4587
4786
|
// src/tui/lib/context.ts
|
|
4588
4787
|
function readFileSafe(filePath) {
|
|
4589
4788
|
try {
|
|
4590
|
-
return
|
|
4789
|
+
return readFileSync16(filePath, "utf-8");
|
|
4591
4790
|
} catch {
|
|
4592
4791
|
return null;
|
|
4593
4792
|
}
|
|
@@ -4747,12 +4946,12 @@ function registerSessionContext(program2) {
|
|
|
4747
4946
|
}
|
|
4748
4947
|
|
|
4749
4948
|
// src/cli/commands/spawn.ts
|
|
4750
|
-
import { existsSync as
|
|
4949
|
+
import { existsSync as existsSync14 } from "fs";
|
|
4751
4950
|
import { join as join15, resolve as resolve5 } from "path";
|
|
4752
4951
|
|
|
4753
4952
|
// src/daemon/frontmatter.ts
|
|
4754
4953
|
init_paths();
|
|
4755
|
-
import { readFileSync as
|
|
4954
|
+
import { readFileSync as readFileSync17, existsSync as existsSync13, readdirSync as readdirSync5 } from "fs";
|
|
4756
4955
|
import { homedir as homedir8 } from "os";
|
|
4757
4956
|
import { join as join14, basename as basename4 } from "path";
|
|
4758
4957
|
function parseAgentFrontmatter(content) {
|
|
@@ -4804,7 +5003,7 @@ function discoverAgentTypes(pluginDir, cwd) {
|
|
|
4804
5003
|
if (seen.has(qualifiedName)) continue;
|
|
4805
5004
|
seen.add(qualifiedName);
|
|
4806
5005
|
try {
|
|
4807
|
-
const content =
|
|
5006
|
+
const content = readFileSync17(join14(dir, file), "utf-8");
|
|
4808
5007
|
const fm = parseAgentFrontmatter(content);
|
|
4809
5008
|
results.push({ qualifiedName, source, description: fm.description, model: fm.model });
|
|
4810
5009
|
} catch {
|
|
@@ -4819,7 +5018,7 @@ function discoverAgentTypes(pluginDir, cwd) {
|
|
|
4819
5018
|
scanDir(join14(pluginDir, "agents"), "sisyphus", "bundled");
|
|
4820
5019
|
try {
|
|
4821
5020
|
const registryPath = join14(homedir8(), ".claude", "plugins", "installed_plugins.json");
|
|
4822
|
-
const registry = JSON.parse(
|
|
5021
|
+
const registry = JSON.parse(readFileSync17(registryPath, "utf-8"));
|
|
4823
5022
|
const pluginEntries = registry.plugins ?? registry;
|
|
4824
5023
|
for (const key of Object.keys(pluginEntries)) {
|
|
4825
5024
|
const atIdx = key.indexOf("@");
|
|
@@ -4899,7 +5098,7 @@ function registerSpawn(program2) {
|
|
|
4899
5098
|
}
|
|
4900
5099
|
if (opts.repo && opts.repo !== ".") {
|
|
4901
5100
|
const repoPath = join15(sisyphusCwd, opts.repo);
|
|
4902
|
-
if (!
|
|
5101
|
+
if (!existsSync14(repoPath)) {
|
|
4903
5102
|
console.error(`Error: repo directory does not exist: ${repoPath}`);
|
|
4904
5103
|
process.exit(1);
|
|
4905
5104
|
}
|
|
@@ -5006,10 +5205,10 @@ function registerReport(program2) {
|
|
|
5006
5205
|
}
|
|
5007
5206
|
|
|
5008
5207
|
// src/cli/commands/await.ts
|
|
5009
|
-
import { existsSync as
|
|
5208
|
+
import { existsSync as existsSync15, readFileSync as readFileSync18 } from "fs";
|
|
5010
5209
|
var AWAIT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
|
|
5011
5210
|
function registerAwait(program2) {
|
|
5012
|
-
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) => {
|
|
5211
|
+
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)").option("-j, --json", "Output result as a single JSON object (report as string field)").action(async (agentId, opts) => {
|
|
5013
5212
|
assertTmux();
|
|
5014
5213
|
const sessionId = opts.session ?? process.env.SISYPHUS_SESSION_ID;
|
|
5015
5214
|
if (!sessionId) {
|
|
@@ -5027,19 +5226,24 @@ function registerAwait(program2) {
|
|
|
5027
5226
|
const reportPath = data.reportPath;
|
|
5028
5227
|
const agentName = data.agentName;
|
|
5029
5228
|
const agentType = data.agentType;
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
console.log(`[${status}] ${agentId} (${label})`);
|
|
5033
|
-
if (reportPath && existsSync14(reportPath)) {
|
|
5229
|
+
let report = "";
|
|
5230
|
+
if (reportPath && existsSync15(reportPath)) {
|
|
5034
5231
|
try {
|
|
5035
|
-
|
|
5036
|
-
if (body.length > 0) {
|
|
5037
|
-
process.stdout.write(body.endsWith("\n") ? body : body + "\n");
|
|
5038
|
-
}
|
|
5232
|
+
report = readFileSync18(reportPath, "utf-8");
|
|
5039
5233
|
} catch (err) {
|
|
5040
5234
|
console.error(`Warning: could not read report at ${reportPath}: ${err instanceof Error ? err.message : err}`);
|
|
5041
5235
|
}
|
|
5042
5236
|
}
|
|
5237
|
+
if (opts.json) {
|
|
5238
|
+
console.log(JSON.stringify({ agentId, status, agentName, agentType, reportPath, report }));
|
|
5239
|
+
return;
|
|
5240
|
+
}
|
|
5241
|
+
const shortType = agentType && agentType !== "worker" ? agentType.replace(/^sisyphus:/, "") : "";
|
|
5242
|
+
const label = shortType ? `${shortType}-${agentName}` : agentName;
|
|
5243
|
+
console.log(`[${status}] ${agentId} (${label})`);
|
|
5244
|
+
if (report.length > 0) {
|
|
5245
|
+
process.stdout.write(report.endsWith("\n") ? report : report + "\n");
|
|
5246
|
+
}
|
|
5043
5247
|
});
|
|
5044
5248
|
}
|
|
5045
5249
|
|
|
@@ -5156,14 +5360,71 @@ function registerSegmentUnregister(program2) {
|
|
|
5156
5360
|
}
|
|
5157
5361
|
|
|
5158
5362
|
// src/cli/commands/setup.ts
|
|
5159
|
-
import { execSync as
|
|
5363
|
+
import { execSync as execSync11 } from "child_process";
|
|
5160
5364
|
|
|
5161
5365
|
// src/cli/onboard.ts
|
|
5162
|
-
import { execSync as
|
|
5163
|
-
import { existsSync as
|
|
5366
|
+
import { execSync as execSync10 } from "child_process";
|
|
5367
|
+
import { existsSync as existsSync16, readFileSync as readFileSync19, writeFileSync as writeFileSync8 } from "fs";
|
|
5164
5368
|
import { homedir as homedir9 } from "os";
|
|
5165
5369
|
import { dirname as dirname5, join as join16 } from "path";
|
|
5166
5370
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5371
|
+
|
|
5372
|
+
// src/shared/clipboard.ts
|
|
5373
|
+
import { execFileSync as execFileSync2, spawnSync as spawnSync3 } from "child_process";
|
|
5374
|
+
function detectClipboard() {
|
|
5375
|
+
const platform = detectPlatform();
|
|
5376
|
+
if (platform === "darwin") {
|
|
5377
|
+
return {
|
|
5378
|
+
copy: { cmd: "pbcopy", args: [] },
|
|
5379
|
+
paste: { cmd: "pbpaste", args: [] },
|
|
5380
|
+
hint: null
|
|
5381
|
+
};
|
|
5382
|
+
}
|
|
5383
|
+
if (platform === "wsl") {
|
|
5384
|
+
const copy = hasCommand("clip.exe") ? { cmd: "clip.exe", args: [] } : null;
|
|
5385
|
+
const paste = hasCommand("powershell.exe") ? { cmd: "powershell.exe", args: ["-NoProfile", "-Command", "Get-Clipboard"] } : null;
|
|
5386
|
+
return {
|
|
5387
|
+
copy,
|
|
5388
|
+
paste,
|
|
5389
|
+
hint: copy && paste ? null : "WSL clipboard needs Windows interop. Ensure /mnt/c/Windows/System32 is on PATH (it is by default)."
|
|
5390
|
+
};
|
|
5391
|
+
}
|
|
5392
|
+
if (platform === "linux") {
|
|
5393
|
+
if (process.env["WAYLAND_DISPLAY"] && hasCommand("wl-copy") && hasCommand("wl-paste")) {
|
|
5394
|
+
return {
|
|
5395
|
+
copy: { cmd: "wl-copy", args: [] },
|
|
5396
|
+
paste: { cmd: "wl-paste", args: ["--no-newline"] },
|
|
5397
|
+
hint: null
|
|
5398
|
+
};
|
|
5399
|
+
}
|
|
5400
|
+
if (hasCommand("xclip")) {
|
|
5401
|
+
return {
|
|
5402
|
+
copy: { cmd: "xclip", args: ["-selection", "clipboard"] },
|
|
5403
|
+
paste: { cmd: "xclip", args: ["-selection", "clipboard", "-o"] },
|
|
5404
|
+
hint: null
|
|
5405
|
+
};
|
|
5406
|
+
}
|
|
5407
|
+
if (hasCommand("xsel")) {
|
|
5408
|
+
return {
|
|
5409
|
+
copy: { cmd: "xsel", args: ["--clipboard", "--input"] },
|
|
5410
|
+
paste: { cmd: "xsel", args: ["--clipboard", "--output"] },
|
|
5411
|
+
hint: null
|
|
5412
|
+
};
|
|
5413
|
+
}
|
|
5414
|
+
return {
|
|
5415
|
+
copy: null,
|
|
5416
|
+
paste: null,
|
|
5417
|
+
hint: "Install a clipboard tool: `sudo apt install xclip` (or wl-clipboard for Wayland)."
|
|
5418
|
+
};
|
|
5419
|
+
}
|
|
5420
|
+
return {
|
|
5421
|
+
copy: null,
|
|
5422
|
+
paste: null,
|
|
5423
|
+
hint: "Clipboard not supported on this platform."
|
|
5424
|
+
};
|
|
5425
|
+
}
|
|
5426
|
+
|
|
5427
|
+
// src/cli/onboard.ts
|
|
5167
5428
|
function detectTerminal() {
|
|
5168
5429
|
const termProgram = process.env["TERM_PROGRAM"] || "";
|
|
5169
5430
|
const isIterm = termProgram === "iTerm.app" || !!process.env["ITERM_SESSION_ID"];
|
|
@@ -5171,7 +5432,7 @@ function detectTerminal() {
|
|
|
5171
5432
|
}
|
|
5172
5433
|
function isTmuxAvailable() {
|
|
5173
5434
|
try {
|
|
5174
|
-
|
|
5435
|
+
execSync10("which tmux", { stdio: "pipe" });
|
|
5175
5436
|
return true;
|
|
5176
5437
|
} catch {
|
|
5177
5438
|
return false;
|
|
@@ -5179,7 +5440,7 @@ function isTmuxAvailable() {
|
|
|
5179
5440
|
}
|
|
5180
5441
|
function isBrewAvailable() {
|
|
5181
5442
|
try {
|
|
5182
|
-
|
|
5443
|
+
execSync10("which brew", { stdio: "pipe" });
|
|
5183
5444
|
return true;
|
|
5184
5445
|
} catch {
|
|
5185
5446
|
return false;
|
|
@@ -5189,7 +5450,7 @@ function tryAutoInstallTmux() {
|
|
|
5189
5450
|
if (!isBrewAvailable()) return false;
|
|
5190
5451
|
try {
|
|
5191
5452
|
console.log(" Installing tmux via Homebrew...");
|
|
5192
|
-
|
|
5453
|
+
execSync10("brew install tmux", { stdio: "inherit" });
|
|
5193
5454
|
return isTmuxAvailable();
|
|
5194
5455
|
} catch {
|
|
5195
5456
|
return false;
|
|
@@ -5200,11 +5461,11 @@ function checkItermOptionKey() {
|
|
|
5200
5461
|
return { checked: false, allCorrect: true, incorrectProfiles: [] };
|
|
5201
5462
|
}
|
|
5202
5463
|
const plistPath2 = join16(homedir9(), "Library", "Preferences", "com.googlecode.iterm2.plist");
|
|
5203
|
-
if (!
|
|
5464
|
+
if (!existsSync16(plistPath2)) {
|
|
5204
5465
|
return { checked: false, allCorrect: false, incorrectProfiles: [] };
|
|
5205
5466
|
}
|
|
5206
5467
|
try {
|
|
5207
|
-
const json =
|
|
5468
|
+
const json = execSync10(
|
|
5208
5469
|
`plutil -extract "New Bookmarks" json -o - "${plistPath2}"`,
|
|
5209
5470
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
5210
5471
|
);
|
|
@@ -5224,7 +5485,7 @@ function checkItermOptionKey() {
|
|
|
5224
5485
|
}
|
|
5225
5486
|
}
|
|
5226
5487
|
function hasExistingTmuxConf() {
|
|
5227
|
-
return
|
|
5488
|
+
return existsSync16(join16(homedir9(), ".tmux.conf")) || existsSync16(join16(homedir9(), ".config", "tmux", "tmux.conf"));
|
|
5228
5489
|
}
|
|
5229
5490
|
var SISYPHUS_DEFAULTS_MARKER = "# sisyphus-managed \u2014 do not edit";
|
|
5230
5491
|
function buildTmuxDefaults() {
|
|
@@ -5341,7 +5602,7 @@ function writeTmuxDefaults() {
|
|
|
5341
5602
|
}
|
|
5342
5603
|
function isNvimAvailable() {
|
|
5343
5604
|
try {
|
|
5344
|
-
|
|
5605
|
+
execSync10("which nvim", { stdio: "pipe" });
|
|
5345
5606
|
return true;
|
|
5346
5607
|
} catch {
|
|
5347
5608
|
return false;
|
|
@@ -5349,13 +5610,13 @@ function isNvimAvailable() {
|
|
|
5349
5610
|
}
|
|
5350
5611
|
function getNvimVersion() {
|
|
5351
5612
|
try {
|
|
5352
|
-
return
|
|
5613
|
+
return execSync10("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "") || "unknown";
|
|
5353
5614
|
} catch {
|
|
5354
5615
|
return "unknown";
|
|
5355
5616
|
}
|
|
5356
5617
|
}
|
|
5357
5618
|
function hasLazyVimConfig() {
|
|
5358
|
-
return
|
|
5619
|
+
return existsSync16(join16(homedir9(), ".config", "nvim", "lazy-lock.json"));
|
|
5359
5620
|
}
|
|
5360
5621
|
function bundledBaleiaPluginPath() {
|
|
5361
5622
|
const distDir = dirname5(fileURLToPath2(import.meta.url));
|
|
@@ -5363,13 +5624,13 @@ function bundledBaleiaPluginPath() {
|
|
|
5363
5624
|
}
|
|
5364
5625
|
function installBaleiaPlugin() {
|
|
5365
5626
|
const pluginsDir = join16(homedir9(), ".config", "nvim", "lua", "plugins");
|
|
5366
|
-
if (!
|
|
5627
|
+
if (!existsSync16(pluginsDir)) return false;
|
|
5367
5628
|
const dest = join16(pluginsDir, "sisyphus-baleia.lua");
|
|
5368
|
-
if (
|
|
5629
|
+
if (existsSync16(dest)) return true;
|
|
5369
5630
|
const src = bundledBaleiaPluginPath();
|
|
5370
|
-
if (!
|
|
5631
|
+
if (!existsSync16(src)) return false;
|
|
5371
5632
|
try {
|
|
5372
|
-
writeFileSync8(dest,
|
|
5633
|
+
writeFileSync8(dest, readFileSync19(src, "utf-8"), "utf8");
|
|
5373
5634
|
return true;
|
|
5374
5635
|
} catch {
|
|
5375
5636
|
return false;
|
|
@@ -5385,7 +5646,7 @@ function tryAutoInstallNvim() {
|
|
|
5385
5646
|
}
|
|
5386
5647
|
try {
|
|
5387
5648
|
console.log(" Installing neovim via Homebrew...");
|
|
5388
|
-
|
|
5649
|
+
execSync10("brew install neovim", { stdio: "inherit" });
|
|
5389
5650
|
} catch {
|
|
5390
5651
|
return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false, baleiaInstalled: false };
|
|
5391
5652
|
}
|
|
@@ -5394,7 +5655,7 @@ function tryAutoInstallNvim() {
|
|
|
5394
5655
|
}
|
|
5395
5656
|
const nvimConfigDir = join16(homedir9(), ".config", "nvim");
|
|
5396
5657
|
let lazyVimInstalled = false;
|
|
5397
|
-
if (!
|
|
5658
|
+
if (!existsSync16(nvimConfigDir)) {
|
|
5398
5659
|
const cloneCmd = [
|
|
5399
5660
|
"git",
|
|
5400
5661
|
"-c core.hooksPath=/dev/null",
|
|
@@ -5406,13 +5667,13 @@ function tryAutoInstallNvim() {
|
|
|
5406
5667
|
].join(" ");
|
|
5407
5668
|
try {
|
|
5408
5669
|
console.log(" Cloning LazyVim starter config...");
|
|
5409
|
-
|
|
5670
|
+
execSync10(cloneCmd, {
|
|
5410
5671
|
stdio: "inherit",
|
|
5411
5672
|
env: { ...process.env, GIT_LFS_SKIP_SMUDGE: "1" }
|
|
5412
5673
|
});
|
|
5413
5674
|
const gitDir = join16(nvimConfigDir, ".git");
|
|
5414
|
-
if (
|
|
5415
|
-
|
|
5675
|
+
if (existsSync16(gitDir)) {
|
|
5676
|
+
execSync10(`rm -rf "${gitDir}"`, { stdio: "pipe" });
|
|
5416
5677
|
}
|
|
5417
5678
|
lazyVimInstalled = true;
|
|
5418
5679
|
} catch (err) {
|
|
@@ -5427,7 +5688,7 @@ function tryAutoInstallNvim() {
|
|
|
5427
5688
|
}
|
|
5428
5689
|
function isTermrenderAvailable() {
|
|
5429
5690
|
try {
|
|
5430
|
-
|
|
5691
|
+
execSync10("which termrender", { stdio: "pipe" });
|
|
5431
5692
|
return true;
|
|
5432
5693
|
} catch {
|
|
5433
5694
|
return false;
|
|
@@ -5435,7 +5696,7 @@ function isTermrenderAvailable() {
|
|
|
5435
5696
|
}
|
|
5436
5697
|
function isPipxAvailable() {
|
|
5437
5698
|
try {
|
|
5438
|
-
|
|
5699
|
+
execSync10("which pipx", { stdio: "pipe" });
|
|
5439
5700
|
return true;
|
|
5440
5701
|
} catch {
|
|
5441
5702
|
return false;
|
|
@@ -5443,11 +5704,11 @@ function isPipxAvailable() {
|
|
|
5443
5704
|
}
|
|
5444
5705
|
function isPipAvailable() {
|
|
5445
5706
|
try {
|
|
5446
|
-
|
|
5707
|
+
execSync10("which pip3", { stdio: "pipe" });
|
|
5447
5708
|
return true;
|
|
5448
5709
|
} catch {
|
|
5449
5710
|
try {
|
|
5450
|
-
|
|
5711
|
+
execSync10("which pip", { stdio: "pipe" });
|
|
5451
5712
|
return true;
|
|
5452
5713
|
} catch {
|
|
5453
5714
|
return false;
|
|
@@ -5461,7 +5722,7 @@ function tryAutoInstallTermrender() {
|
|
|
5461
5722
|
if (isPipxAvailable()) {
|
|
5462
5723
|
try {
|
|
5463
5724
|
console.log(" Installing termrender via pipx...");
|
|
5464
|
-
|
|
5725
|
+
execSync10("pipx install termrender", { stdio: "inherit" });
|
|
5465
5726
|
if (isTermrenderAvailable()) return { installed: true, autoInstalled: true };
|
|
5466
5727
|
} catch {
|
|
5467
5728
|
}
|
|
@@ -5471,19 +5732,45 @@ function tryAutoInstallTermrender() {
|
|
|
5471
5732
|
console.log(" Installing termrender via pip...");
|
|
5472
5733
|
const pip = (() => {
|
|
5473
5734
|
try {
|
|
5474
|
-
|
|
5735
|
+
execSync10("which pip3", { stdio: "pipe" });
|
|
5475
5736
|
return "pip3";
|
|
5476
5737
|
} catch {
|
|
5477
5738
|
return "pip";
|
|
5478
5739
|
}
|
|
5479
5740
|
})();
|
|
5480
|
-
|
|
5741
|
+
execSync10(`${pip} install termrender`, { stdio: "inherit" });
|
|
5481
5742
|
if (isTermrenderAvailable()) return { installed: true, autoInstalled: true };
|
|
5482
5743
|
} catch {
|
|
5483
5744
|
}
|
|
5484
5745
|
}
|
|
5485
5746
|
return { installed: false, autoInstalled: false };
|
|
5486
5747
|
}
|
|
5748
|
+
function detectPlatformReadiness() {
|
|
5749
|
+
const clip = detectClipboard();
|
|
5750
|
+
const linuxLike = isLinuxLike();
|
|
5751
|
+
const wsl = isWslHost();
|
|
5752
|
+
let wslSystemdEnabled = null;
|
|
5753
|
+
if (wsl) {
|
|
5754
|
+
try {
|
|
5755
|
+
execSync10("systemctl is-system-running --quiet 2>/dev/null", { stdio: "pipe" });
|
|
5756
|
+
wslSystemdEnabled = true;
|
|
5757
|
+
} catch {
|
|
5758
|
+
try {
|
|
5759
|
+
execSync10("systemctl --user --version", { stdio: "pipe" });
|
|
5760
|
+
wslSystemdEnabled = true;
|
|
5761
|
+
} catch {
|
|
5762
|
+
wslSystemdEnabled = false;
|
|
5763
|
+
}
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5766
|
+
return {
|
|
5767
|
+
label: platformLabel(),
|
|
5768
|
+
clipboardTool: clip.copy === null ? null : clip.copy.cmd,
|
|
5769
|
+
clipboardHint: clip.hint,
|
|
5770
|
+
notifySendAvailable: linuxLike ? hasCommand("notify-send") : true,
|
|
5771
|
+
wslSystemdEnabled
|
|
5772
|
+
};
|
|
5773
|
+
}
|
|
5487
5774
|
function runOnboarding() {
|
|
5488
5775
|
const terminal = detectTerminal();
|
|
5489
5776
|
const tmuxAlreadyInstalled = isTmuxAvailable();
|
|
@@ -5493,10 +5780,10 @@ function runOnboarding() {
|
|
|
5493
5780
|
if (!tmuxAlreadyInstalled && process.platform === "darwin") {
|
|
5494
5781
|
tmuxAutoInstalled = tryAutoInstallTmux();
|
|
5495
5782
|
tmuxInstalled = tmuxAutoInstalled;
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5783
|
+
}
|
|
5784
|
+
if (tmuxInstalled && !hasExistingTmuxConf()) {
|
|
5785
|
+
writeTmuxDefaults();
|
|
5786
|
+
tmuxDefaultsWritten = true;
|
|
5500
5787
|
}
|
|
5501
5788
|
let itermOptionKey = { checked: false, allCorrect: true, incorrectProfiles: [] };
|
|
5502
5789
|
if (terminal.isIterm) {
|
|
@@ -5505,13 +5792,14 @@ function runOnboarding() {
|
|
|
5505
5792
|
const nvim = tryAutoInstallNvim();
|
|
5506
5793
|
const sisyphusPlugin = ensureSisyphusPluginInstalled();
|
|
5507
5794
|
const termrender = tryAutoInstallTermrender();
|
|
5508
|
-
|
|
5795
|
+
const platform = detectPlatformReadiness();
|
|
5796
|
+
return { tmuxInstalled, tmuxAutoInstalled, terminal, itermOptionKey, tmuxDefaultsWritten, nvim, sisyphusPlugin, termrender, platform };
|
|
5509
5797
|
}
|
|
5510
5798
|
|
|
5511
5799
|
// src/cli/commands/setup.ts
|
|
5512
5800
|
function getTmuxVersion() {
|
|
5513
5801
|
try {
|
|
5514
|
-
return
|
|
5802
|
+
return execSync11("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
5515
5803
|
} catch {
|
|
5516
5804
|
return "installed";
|
|
5517
5805
|
}
|
|
@@ -5520,6 +5808,7 @@ function printResults(result, daemonOk, keybindMsg) {
|
|
|
5520
5808
|
console.log("");
|
|
5521
5809
|
console.log("Setting up Sisyphus...");
|
|
5522
5810
|
console.log("");
|
|
5811
|
+
console.log(` \u2713 Platform: ${result.platform.label}`);
|
|
5523
5812
|
if (result.tmuxInstalled) {
|
|
5524
5813
|
const detail = getTmuxVersion();
|
|
5525
5814
|
console.log(` \u2713 tmux: ${detail}${result.tmuxAutoInstalled ? " (just installed)" : ""}`);
|
|
@@ -5527,6 +5816,23 @@ function printResults(result, daemonOk, keybindMsg) {
|
|
|
5527
5816
|
const hint = process.platform === "darwin" ? "Install Homebrew (https://brew.sh) then: brew install tmux" : "apt install tmux (Debian/Ubuntu) or your package manager";
|
|
5528
5817
|
console.log(` \u2717 tmux: Not installed \u2014 ${hint}`);
|
|
5529
5818
|
}
|
|
5819
|
+
if (process.platform !== "darwin") {
|
|
5820
|
+
if (result.platform.clipboardTool !== null) {
|
|
5821
|
+
console.log(` \u2713 Clipboard: ${result.platform.clipboardTool}`);
|
|
5822
|
+
} else {
|
|
5823
|
+
const hint = result.platform.clipboardHint === null ? "Install xclip / wl-clipboard" : result.platform.clipboardHint;
|
|
5824
|
+
console.log(` \u2717 Clipboard: no backend \u2014 ${hint}`);
|
|
5825
|
+
}
|
|
5826
|
+
if (result.platform.notifySendAvailable) {
|
|
5827
|
+
console.log(" \u2713 Notifications: notify-send");
|
|
5828
|
+
} else {
|
|
5829
|
+
console.log(" \u26A0 Notifications: notify-send missing \u2014 sudo apt install libnotify-bin");
|
|
5830
|
+
}
|
|
5831
|
+
}
|
|
5832
|
+
if (result.platform.wslSystemdEnabled === false) {
|
|
5833
|
+
console.log(" \u26A0 WSL systemd: disabled \u2014 add `[boot]\\nsystemd=true` to /etc/wsl.conf");
|
|
5834
|
+
console.log(" then run `wsl --shutdown` in PowerShell. The daemon can still be started manually with `sisyphusd &`.");
|
|
5835
|
+
}
|
|
5530
5836
|
if (result.tmuxDefaultsWritten) {
|
|
5531
5837
|
console.log(" \u2713 tmux config: Sensible defaults written to ~/.tmux.conf");
|
|
5532
5838
|
}
|
|
@@ -5656,11 +5962,11 @@ function registerSetupKeybind(program2) {
|
|
|
5656
5962
|
}
|
|
5657
5963
|
|
|
5658
5964
|
// src/cli/commands/check-keybinds.ts
|
|
5659
|
-
import { execSync as
|
|
5660
|
-
import { readFileSync as
|
|
5965
|
+
import { execSync as execSync12 } from "child_process";
|
|
5966
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
5661
5967
|
function isTmuxInstalled2() {
|
|
5662
5968
|
try {
|
|
5663
|
-
|
|
5969
|
+
execSync12("which tmux", { stdio: "pipe" });
|
|
5664
5970
|
return true;
|
|
5665
5971
|
} catch {
|
|
5666
5972
|
return false;
|
|
@@ -5668,14 +5974,14 @@ function isTmuxInstalled2() {
|
|
|
5668
5974
|
}
|
|
5669
5975
|
function getTmuxVersion2() {
|
|
5670
5976
|
try {
|
|
5671
|
-
return
|
|
5977
|
+
return execSync12("tmux -V", { stdio: ["pipe", "pipe", "pipe"] }).toString().trim();
|
|
5672
5978
|
} catch {
|
|
5673
5979
|
return null;
|
|
5674
5980
|
}
|
|
5675
5981
|
}
|
|
5676
5982
|
function isTmuxServerRunning() {
|
|
5677
5983
|
try {
|
|
5678
|
-
|
|
5984
|
+
execSync12("tmux list-sessions", { stdio: ["pipe", "pipe", "pipe"] });
|
|
5679
5985
|
return true;
|
|
5680
5986
|
} catch {
|
|
5681
5987
|
return false;
|
|
@@ -5683,7 +5989,7 @@ function isTmuxServerRunning() {
|
|
|
5683
5989
|
}
|
|
5684
5990
|
function getTmuxPrefix() {
|
|
5685
5991
|
try {
|
|
5686
|
-
return
|
|
5992
|
+
return execSync12("tmux show-options -gv prefix", { stdio: ["pipe", "pipe", "pipe"] }).toString().trim() || null;
|
|
5687
5993
|
} catch {
|
|
5688
5994
|
return null;
|
|
5689
5995
|
}
|
|
@@ -5710,7 +6016,7 @@ function runCheck() {
|
|
|
5710
6016
|
let userConfAlreadySources = false;
|
|
5711
6017
|
if (userConfPath !== null) {
|
|
5712
6018
|
try {
|
|
5713
|
-
userConfAlreadySources =
|
|
6019
|
+
userConfAlreadySources = readFileSync20(userConfPath, "utf-8").includes(sisyphusConfPath);
|
|
5714
6020
|
} catch {
|
|
5715
6021
|
}
|
|
5716
6022
|
}
|
|
@@ -5868,8 +6174,8 @@ function registerCheckKeybinds(program2) {
|
|
|
5868
6174
|
|
|
5869
6175
|
// src/cli/commands/check-statusbar.ts
|
|
5870
6176
|
init_paths();
|
|
5871
|
-
import { execSync as
|
|
5872
|
-
import { existsSync as
|
|
6177
|
+
import { execSync as execSync13 } from "child_process";
|
|
6178
|
+
import { existsSync as existsSync17, readFileSync as readFileSync21 } from "fs";
|
|
5873
6179
|
import { homedir as homedir10 } from "os";
|
|
5874
6180
|
import { join as join17 } from "path";
|
|
5875
6181
|
var SISYPHUS_LEFT_TOKEN = "@sisyphus_left";
|
|
@@ -5878,7 +6184,7 @@ var TMUX_DEFAULT_STATUS_LEFT = "[#S] ";
|
|
|
5878
6184
|
var TMUX_DEFAULT_STATUS_RIGHT_PREFIX = '"#{=21:pane_title}"';
|
|
5879
6185
|
function isTmuxInstalled3() {
|
|
5880
6186
|
try {
|
|
5881
|
-
|
|
6187
|
+
execSync13("which tmux", { stdio: "pipe" });
|
|
5882
6188
|
return true;
|
|
5883
6189
|
} catch {
|
|
5884
6190
|
return false;
|
|
@@ -5886,7 +6192,7 @@ function isTmuxInstalled3() {
|
|
|
5886
6192
|
}
|
|
5887
6193
|
function isTmuxServerRunning2() {
|
|
5888
6194
|
try {
|
|
5889
|
-
|
|
6195
|
+
execSync13("tmux list-sessions", { stdio: ["pipe", "pipe", "pipe"] });
|
|
5890
6196
|
return true;
|
|
5891
6197
|
} catch {
|
|
5892
6198
|
return false;
|
|
@@ -5894,9 +6200,9 @@ function isTmuxServerRunning2() {
|
|
|
5894
6200
|
}
|
|
5895
6201
|
function isDaemonRunning() {
|
|
5896
6202
|
const pidFile = daemonPidPath();
|
|
5897
|
-
if (!
|
|
6203
|
+
if (!existsSync17(pidFile)) return false;
|
|
5898
6204
|
try {
|
|
5899
|
-
const pid = parseInt(
|
|
6205
|
+
const pid = parseInt(readFileSync21(pidFile, "utf-8").trim(), 10);
|
|
5900
6206
|
if (Number.isNaN(pid) || pid <= 0) return false;
|
|
5901
6207
|
process.kill(pid, 0);
|
|
5902
6208
|
return true;
|
|
@@ -5906,7 +6212,7 @@ function isDaemonRunning() {
|
|
|
5906
6212
|
}
|
|
5907
6213
|
function showOption(name) {
|
|
5908
6214
|
try {
|
|
5909
|
-
const out =
|
|
6215
|
+
const out = execSync13(`tmux show-options -g ${name}`, { stdio: ["pipe", "pipe", "pipe"] }).toString().trim();
|
|
5910
6216
|
if (out.length === 0) return null;
|
|
5911
6217
|
const prefix = `${name} `;
|
|
5912
6218
|
const stripped = out.startsWith(prefix) ? out.slice(prefix.length) : out;
|
|
@@ -5941,8 +6247,8 @@ function probeTmuxOptions(serverRunning) {
|
|
|
5941
6247
|
function findUserTmuxConf() {
|
|
5942
6248
|
const xdg = join17(homedir10(), ".config", "tmux", "tmux.conf");
|
|
5943
6249
|
const dotfile = join17(homedir10(), ".tmux.conf");
|
|
5944
|
-
if (
|
|
5945
|
-
if (
|
|
6250
|
+
if (existsSync17(xdg)) return xdg;
|
|
6251
|
+
if (existsSync17(dotfile)) return dotfile;
|
|
5946
6252
|
return null;
|
|
5947
6253
|
}
|
|
5948
6254
|
function probeUserConf() {
|
|
@@ -5952,7 +6258,7 @@ function probeUserConf() {
|
|
|
5952
6258
|
}
|
|
5953
6259
|
let contents = "";
|
|
5954
6260
|
try {
|
|
5955
|
-
contents =
|
|
6261
|
+
contents = readFileSync21(path, "utf-8");
|
|
5956
6262
|
} catch {
|
|
5957
6263
|
return { path, setsStatusLeft: false, setsStatusRight: false, sourcesSisyphusManaged: false };
|
|
5958
6264
|
}
|
|
@@ -5964,9 +6270,9 @@ function probeUserConf() {
|
|
|
5964
6270
|
}
|
|
5965
6271
|
function loadGlobalSisyphusConfig() {
|
|
5966
6272
|
const path = globalConfigPath();
|
|
5967
|
-
if (!
|
|
6273
|
+
if (!existsSync17(path)) return null;
|
|
5968
6274
|
try {
|
|
5969
|
-
const parsed = JSON.parse(
|
|
6275
|
+
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
5970
6276
|
return parsed.statusBar === void 0 ? null : parsed.statusBar;
|
|
5971
6277
|
} catch {
|
|
5972
6278
|
return null;
|
|
@@ -6222,7 +6528,7 @@ function registerCheckStatusbar(program2) {
|
|
|
6222
6528
|
|
|
6223
6529
|
// src/cli/commands/home-init.ts
|
|
6224
6530
|
init_shell();
|
|
6225
|
-
import { execSync as
|
|
6531
|
+
import { execSync as execSync14 } from "child_process";
|
|
6226
6532
|
function registerHomeInit(parent) {
|
|
6227
6533
|
parent.command("home-init <name> <cwd>").description("Bootstrap a tmux home session with the sisyphus dashboard.").action((name, cwd) => {
|
|
6228
6534
|
ensureSession(name, cwd);
|
|
@@ -6232,7 +6538,7 @@ function registerHomeInit(parent) {
|
|
|
6232
6538
|
}
|
|
6233
6539
|
function sessionExists(name) {
|
|
6234
6540
|
try {
|
|
6235
|
-
|
|
6541
|
+
execSync14(`tmux has-session -t ${shellQuote(name)}`, { stdio: "pipe" });
|
|
6236
6542
|
return true;
|
|
6237
6543
|
} catch {
|
|
6238
6544
|
return false;
|
|
@@ -6240,13 +6546,13 @@ function sessionExists(name) {
|
|
|
6240
6546
|
}
|
|
6241
6547
|
function ensureSession(name, cwd) {
|
|
6242
6548
|
if (sessionExists(name)) return;
|
|
6243
|
-
|
|
6549
|
+
execSync14(
|
|
6244
6550
|
`tmux new-session -d -s ${shellQuote(name)} -c ${shellQuote(cwd)}`,
|
|
6245
6551
|
{ stdio: "pipe" }
|
|
6246
6552
|
);
|
|
6247
6553
|
}
|
|
6248
6554
|
function setSessionCwd(name, cwd) {
|
|
6249
|
-
|
|
6555
|
+
execSync14(
|
|
6250
6556
|
`tmux set-option -t ${shellQuote(name)} @sisyphus_cwd ${shellQuote(cwd.replace(/\/+$/, ""))}`,
|
|
6251
6557
|
{ stdio: "pipe" }
|
|
6252
6558
|
);
|
|
@@ -6278,8 +6584,8 @@ function registerQuiesce(parent) {
|
|
|
6278
6584
|
|
|
6279
6585
|
// src/cli/commands/doctor.ts
|
|
6280
6586
|
init_paths();
|
|
6281
|
-
import { execSync as
|
|
6282
|
-
import { existsSync as
|
|
6587
|
+
import { execSync as execSync15 } from "child_process";
|
|
6588
|
+
import { existsSync as existsSync18, statSync as statSync3 } from "fs";
|
|
6283
6589
|
import { homedir as homedir11 } from "os";
|
|
6284
6590
|
import { join as join18 } from "path";
|
|
6285
6591
|
function checkNodeVersion() {
|
|
@@ -6291,7 +6597,7 @@ function checkNodeVersion() {
|
|
|
6291
6597
|
}
|
|
6292
6598
|
function checkClaudeCli() {
|
|
6293
6599
|
try {
|
|
6294
|
-
|
|
6600
|
+
execSync15("which claude", { stdio: "pipe" });
|
|
6295
6601
|
return { name: "Claude CLI", status: "ok", detail: "Found on PATH" };
|
|
6296
6602
|
} catch {
|
|
6297
6603
|
return {
|
|
@@ -6304,7 +6610,7 @@ function checkClaudeCli() {
|
|
|
6304
6610
|
}
|
|
6305
6611
|
function checkGit() {
|
|
6306
6612
|
try {
|
|
6307
|
-
const version =
|
|
6613
|
+
const version = execSync15("git --version", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
6308
6614
|
return { name: "git", status: "ok", detail: version };
|
|
6309
6615
|
} catch {
|
|
6310
6616
|
return { name: "git", status: "fail", detail: "Not found on PATH", fix: "Install git: https://git-scm.com/downloads" };
|
|
@@ -6312,7 +6618,7 @@ function checkGit() {
|
|
|
6312
6618
|
}
|
|
6313
6619
|
function checkTmuxVersion() {
|
|
6314
6620
|
try {
|
|
6315
|
-
const version =
|
|
6621
|
+
const version = execSync15("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
6316
6622
|
const match = version.match(/(\d+\.\d+)/);
|
|
6317
6623
|
if (!match) return { name: "tmux version", status: "warn", detail: `Could not parse version: ${version}` };
|
|
6318
6624
|
const ver = parseFloat(match[1]);
|
|
@@ -6338,7 +6644,7 @@ function checkDaemonInstalled() {
|
|
|
6338
6644
|
};
|
|
6339
6645
|
}
|
|
6340
6646
|
const pid = daemonPidPath();
|
|
6341
|
-
if (
|
|
6647
|
+
if (existsSync18(pid)) {
|
|
6342
6648
|
return { name: "Daemon setup", status: "ok", detail: `PID file found at ${pid}` };
|
|
6343
6649
|
}
|
|
6344
6650
|
return {
|
|
@@ -6350,7 +6656,7 @@ function checkDaemonInstalled() {
|
|
|
6350
6656
|
}
|
|
6351
6657
|
function checkDaemonRunning() {
|
|
6352
6658
|
const pid = daemonPidPath();
|
|
6353
|
-
if (!
|
|
6659
|
+
if (!existsSync18(pid)) {
|
|
6354
6660
|
const fix = process.platform === "darwin" ? "launchctl load -w ~/Library/LaunchAgents/com.sisyphus.daemon.plist" : "sisyphusd & \u2014 or check if the process is running";
|
|
6355
6661
|
return {
|
|
6356
6662
|
name: "Daemon process",
|
|
@@ -6361,7 +6667,7 @@ function checkDaemonRunning() {
|
|
|
6361
6667
|
}
|
|
6362
6668
|
try {
|
|
6363
6669
|
const sock = socketPath();
|
|
6364
|
-
|
|
6670
|
+
execSync15(`test -S "${sock}"`, { stdio: "pipe" });
|
|
6365
6671
|
return { name: "Daemon process", status: "ok", detail: `Socket at ${sock}` };
|
|
6366
6672
|
} catch {
|
|
6367
6673
|
return {
|
|
@@ -6374,13 +6680,13 @@ function checkDaemonRunning() {
|
|
|
6374
6680
|
}
|
|
6375
6681
|
function checkTmux() {
|
|
6376
6682
|
try {
|
|
6377
|
-
|
|
6683
|
+
execSync15("which tmux", { stdio: "pipe" });
|
|
6378
6684
|
} catch {
|
|
6379
6685
|
const installHint = process.platform === "darwin" ? "brew install tmux" : "apt install tmux (Debian/Ubuntu) or your package manager";
|
|
6380
6686
|
return { name: "tmux", status: "fail", detail: "Not found on PATH", fix: installHint };
|
|
6381
6687
|
}
|
|
6382
6688
|
try {
|
|
6383
|
-
|
|
6689
|
+
execSync15("tmux list-sessions", { stdio: "pipe" });
|
|
6384
6690
|
return { name: "tmux", status: "ok", detail: "Running" };
|
|
6385
6691
|
} catch {
|
|
6386
6692
|
return { name: "tmux", status: "warn", detail: "Installed but no server running" };
|
|
@@ -6388,7 +6694,7 @@ function checkTmux() {
|
|
|
6388
6694
|
}
|
|
6389
6695
|
function checkCycleScript() {
|
|
6390
6696
|
const path = cycleScriptPath();
|
|
6391
|
-
if (!
|
|
6697
|
+
if (!existsSync18(path)) {
|
|
6392
6698
|
return {
|
|
6393
6699
|
name: "Cycle script",
|
|
6394
6700
|
status: "fail",
|
|
@@ -6413,7 +6719,7 @@ function checkCycleScript() {
|
|
|
6413
6719
|
function checkTmuxKeybind() {
|
|
6414
6720
|
const existing = getExistingBinding(DEFAULT_CYCLE_KEY);
|
|
6415
6721
|
if (existing === null) {
|
|
6416
|
-
if (
|
|
6722
|
+
if (existsSync18(sisyphusTmuxConfPath())) {
|
|
6417
6723
|
return {
|
|
6418
6724
|
name: `Tmux keybind (${DEFAULT_CYCLE_KEY})`,
|
|
6419
6725
|
status: "warn",
|
|
@@ -6439,7 +6745,7 @@ function checkTmuxKeybind() {
|
|
|
6439
6745
|
}
|
|
6440
6746
|
function checkGlobalDir() {
|
|
6441
6747
|
const dir = globalDir();
|
|
6442
|
-
if (
|
|
6748
|
+
if (existsSync18(dir)) {
|
|
6443
6749
|
return { name: "Data directory", status: "ok", detail: dir };
|
|
6444
6750
|
}
|
|
6445
6751
|
return { name: "Data directory", status: "warn", detail: `${dir} does not exist (created on first use)` };
|
|
@@ -6491,7 +6797,7 @@ function checkSisyphusPlugin() {
|
|
|
6491
6797
|
function checkTermrender() {
|
|
6492
6798
|
if (isTermrenderAvailable()) {
|
|
6493
6799
|
try {
|
|
6494
|
-
const version =
|
|
6800
|
+
const version = execSync15("termrender --version", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
6495
6801
|
return { name: "termrender", status: "ok", detail: version };
|
|
6496
6802
|
} catch {
|
|
6497
6803
|
return { name: "termrender", status: "ok", detail: "installed" };
|
|
@@ -6510,31 +6816,67 @@ function checkNvim() {
|
|
|
6510
6816
|
return { name: "nvim", status: "warn", detail: "Not installed", fix };
|
|
6511
6817
|
}
|
|
6512
6818
|
try {
|
|
6513
|
-
const version =
|
|
6819
|
+
const version = execSync15("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "");
|
|
6514
6820
|
return { name: "nvim", status: "ok", detail: version ?? "installed" };
|
|
6515
6821
|
} catch {
|
|
6516
6822
|
return { name: "nvim", status: "ok", detail: "installed" };
|
|
6517
6823
|
}
|
|
6518
6824
|
}
|
|
6519
6825
|
function checkNotifyBinary() {
|
|
6520
|
-
if (process.platform
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6826
|
+
if (process.platform === "darwin") {
|
|
6827
|
+
const binary = join18(homedir11(), ".sisyphus", "SisyphusNotify.app", "Contents", "MacOS", "sisyphus-notify");
|
|
6828
|
+
if (existsSync18(binary)) {
|
|
6829
|
+
return { name: "Notifications", status: "ok", detail: "SisyphusNotify.app built" };
|
|
6830
|
+
}
|
|
6831
|
+
return {
|
|
6832
|
+
name: "Notifications",
|
|
6833
|
+
status: "warn",
|
|
6834
|
+
detail: "SisyphusNotify.app not built (click-to-switch unavailable)",
|
|
6835
|
+
fix: "Requires Xcode CLI tools: xcode-select --install, then reinstall sisyphus"
|
|
6836
|
+
};
|
|
6837
|
+
}
|
|
6838
|
+
const ready = detectPlatformReadiness();
|
|
6839
|
+
if (ready.notifySendAvailable) {
|
|
6840
|
+
return { name: "Notifications", status: "ok", detail: "notify-send (libnotify)" };
|
|
6524
6841
|
}
|
|
6525
6842
|
return {
|
|
6526
6843
|
name: "Notifications",
|
|
6527
6844
|
status: "warn",
|
|
6528
|
-
detail: "
|
|
6529
|
-
fix: "
|
|
6845
|
+
detail: "notify-send not found \u2014 OS banners disabled",
|
|
6846
|
+
fix: "sudo apt install libnotify-bin (or your distro's equivalent)"
|
|
6530
6847
|
};
|
|
6531
6848
|
}
|
|
6849
|
+
function checkClipboard() {
|
|
6850
|
+
const ready = detectPlatformReadiness();
|
|
6851
|
+
if (ready.clipboardTool !== null) {
|
|
6852
|
+
return { name: "Clipboard", status: "ok", detail: ready.clipboardTool };
|
|
6853
|
+
}
|
|
6854
|
+
const fix = ready.clipboardHint === null ? "Install xclip or wl-clipboard" : ready.clipboardHint;
|
|
6855
|
+
return {
|
|
6856
|
+
name: "Clipboard",
|
|
6857
|
+
status: "fail",
|
|
6858
|
+
detail: "No clipboard backend detected",
|
|
6859
|
+
fix
|
|
6860
|
+
};
|
|
6861
|
+
}
|
|
6862
|
+
function checkPlatform() {
|
|
6863
|
+
const ready = detectPlatformReadiness();
|
|
6864
|
+
if (ready.wslSystemdEnabled === false) {
|
|
6865
|
+
return {
|
|
6866
|
+
name: "Platform",
|
|
6867
|
+
status: "warn",
|
|
6868
|
+
detail: `${platformLabel()} \u2014 systemd disabled (daemon needs manual start)`,
|
|
6869
|
+
fix: "Add `[boot]\\nsystemd=true` to /etc/wsl.conf, then `wsl --shutdown` from PowerShell"
|
|
6870
|
+
};
|
|
6871
|
+
}
|
|
6872
|
+
return { name: "Platform", status: "ok", detail: platformLabel() };
|
|
6873
|
+
}
|
|
6532
6874
|
var SYMBOLS = { ok: "\u2713", warn: "!", fail: "\u2717" };
|
|
6533
6875
|
function registerDoctor(program2) {
|
|
6534
6876
|
program2.command("doctor").description("Check sisyphus installation health").action(() => {
|
|
6535
6877
|
const itermCheck = checkItermRightOptionKey();
|
|
6536
|
-
const notifyCheck = checkNotifyBinary();
|
|
6537
6878
|
const checks = [
|
|
6879
|
+
checkPlatform(),
|
|
6538
6880
|
checkNodeVersion(),
|
|
6539
6881
|
checkClaudeCli(),
|
|
6540
6882
|
checkGit(),
|
|
@@ -6549,7 +6891,8 @@ function registerDoctor(program2) {
|
|
|
6549
6891
|
checkTmuxKeybind(),
|
|
6550
6892
|
checkSisyphusPlugin(),
|
|
6551
6893
|
checkNvim(),
|
|
6552
|
-
|
|
6894
|
+
checkNotifyBinary(),
|
|
6895
|
+
checkClipboard(),
|
|
6553
6896
|
checkTermrender()
|
|
6554
6897
|
];
|
|
6555
6898
|
let hasIssues = false;
|
|
@@ -6572,7 +6915,7 @@ function registerDoctor(program2) {
|
|
|
6572
6915
|
}
|
|
6573
6916
|
|
|
6574
6917
|
// src/cli/commands/init.ts
|
|
6575
|
-
import { existsSync as
|
|
6918
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
6576
6919
|
import { join as join19 } from "path";
|
|
6577
6920
|
var DEFAULT_CONFIG2 = {};
|
|
6578
6921
|
var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
|
|
@@ -6586,7 +6929,7 @@ function registerInit(program2) {
|
|
|
6586
6929
|
const cwd = process.cwd();
|
|
6587
6930
|
const sisDir = join19(cwd, ".sisyphus");
|
|
6588
6931
|
const configPath = join19(sisDir, "config.json");
|
|
6589
|
-
if (
|
|
6932
|
+
if (existsSync19(configPath)) {
|
|
6590
6933
|
console.log(`Already initialized: ${configPath}`);
|
|
6591
6934
|
return;
|
|
6592
6935
|
}
|
|
@@ -6595,7 +6938,7 @@ function registerInit(program2) {
|
|
|
6595
6938
|
console.log(`Created ${configPath}`);
|
|
6596
6939
|
if (opts.orchestrator) {
|
|
6597
6940
|
const orchPath = join19(sisDir, "orchestrator.md");
|
|
6598
|
-
if (!
|
|
6941
|
+
if (!existsSync19(orchPath)) {
|
|
6599
6942
|
writeFileSync9(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
|
|
6600
6943
|
console.log(`Created ${orchPath}`);
|
|
6601
6944
|
}
|
|
@@ -6638,7 +6981,7 @@ function registerUninstall(program2) {
|
|
|
6638
6981
|
|
|
6639
6982
|
// src/cli/commands/configure-upload.ts
|
|
6640
6983
|
init_paths();
|
|
6641
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
6984
|
+
import { chmodSync as chmodSync2, existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync22, writeFileSync as writeFileSync10 } from "fs";
|
|
6642
6985
|
import { createInterface as createInterface3 } from "readline";
|
|
6643
6986
|
import { dirname as dirname6 } from "path";
|
|
6644
6987
|
async function readUrlFromInput(interactive) {
|
|
@@ -6696,9 +7039,9 @@ function registerConfigureUpload(program2) {
|
|
|
6696
7039
|
const url = parsed.origin + (strippedPath.length > 0 ? strippedPath : "");
|
|
6697
7040
|
const configPath = globalConfigPath();
|
|
6698
7041
|
let existing = {};
|
|
6699
|
-
if (
|
|
7042
|
+
if (existsSync20(configPath)) {
|
|
6700
7043
|
try {
|
|
6701
|
-
existing = JSON.parse(
|
|
7044
|
+
existing = JSON.parse(readFileSync22(configPath, "utf-8"));
|
|
6702
7045
|
} catch {
|
|
6703
7046
|
console.error(`Error: ${configPath} could not be parsed \u2014 fix or delete it first`);
|
|
6704
7047
|
process.exit(1);
|
|
@@ -6713,7 +7056,8 @@ function registerConfigureUpload(program2) {
|
|
|
6713
7056
|
}
|
|
6714
7057
|
|
|
6715
7058
|
// src/cli/commands/getting-started.ts
|
|
6716
|
-
import { execSync as
|
|
7059
|
+
import { execSync as execSync16 } from "child_process";
|
|
7060
|
+
import { existsSync as existsSync21 } from "fs";
|
|
6717
7061
|
import { dirname as dirname7, join as join20 } from "path";
|
|
6718
7062
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6719
7063
|
function templatePath(name) {
|
|
@@ -7084,6 +7428,7 @@ sis admin getting-started --tutorial 4
|
|
|
7084
7428
|
`);
|
|
7085
7429
|
}
|
|
7086
7430
|
function printStep4() {
|
|
7431
|
+
const demoPath = join20(process.cwd(), "tmp", "sisyphus-tutorial-demo");
|
|
7087
7432
|
console.log(`
|
|
7088
7433
|
<claude-instructions>
|
|
7089
7434
|
# Sisyphus Getting Started \u2014 Step 4: Demo Session
|
|
@@ -7125,26 +7470,29 @@ Have the user confirm they understand these keybinds before proceeding.
|
|
|
7125
7470
|
|
|
7126
7471
|
### 3. Set expectations, copy demo app, and launch
|
|
7127
7472
|
|
|
7128
|
-
First, copy the demo todo app
|
|
7473
|
+
First, copy the demo todo app into ./tmp/ under the user's current directory and init a git repo (sisyphus needs git). Run from the user's current shell \u2014 don't \`cd\` away first:
|
|
7129
7474
|
\`\`\`
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
git -C
|
|
7134
|
-
git -C
|
|
7475
|
+
mkdir -p ${join20(process.cwd(), "tmp")}
|
|
7476
|
+
rm -rf ${demoPath}
|
|
7477
|
+
cp -r ${templatePath("tutorial-demo")} ${demoPath}
|
|
7478
|
+
git -C ${demoPath} init
|
|
7479
|
+
git -C ${demoPath} add -A
|
|
7480
|
+
git -C ${demoPath} commit -m "Initial todo app"
|
|
7135
7481
|
\`\`\`
|
|
7136
7482
|
|
|
7137
7483
|
Tell the user:
|
|
7138
7484
|
|
|
7139
|
-
> I've set up a small todo app in
|
|
7140
|
-
> with a few files.
|
|
7485
|
+
> I've set up a small todo app in ${demoPath} \u2014 a Node.js API
|
|
7486
|
+
> with a few files. It lives under \`tmp/\` in your current directory so sisyphus
|
|
7487
|
+
> runs somewhere you can see, not in system /tmp. I'm going to launch sisyphus
|
|
7488
|
+
> on it. Here's what will happen:
|
|
7141
7489
|
> 1. The dashboard opens automatically (you'll be switched to it)
|
|
7142
7490
|
> 2. Press **Ctrl+p** to come back here to Claude \u2014 I'll guide you through what to watch
|
|
7143
7491
|
> 3. The session takes a few minutes. You can watch agents work live!
|
|
7144
7492
|
|
|
7145
|
-
Then launch from the demo directory:
|
|
7493
|
+
Then launch from the demo directory (sisyphus operates in whichever directory you launch it from, so we \`cd\` in so the \`.sisyphus/\` state lands inside the demo):
|
|
7146
7494
|
\`\`\`
|
|
7147
|
-
cd
|
|
7495
|
+
cd ${demoPath} && sis start "Add three improvements to this todo app: (1) add a priority field (high/medium/low) to todos, (2) add a GET /todos/stats endpoint that returns counts of total/done/pending todos, (3) add tests for the new features. Explain your thinking at each step." -c "TUTORIAL DEMO: A user is watching this session to learn how sisyphus works. Be EXTRA VERBOSE \u2014 explain your reasoning, narrate what you're doing, and make your planning visible. When spawning agents, give each agent context that this is a tutorial demo and they should explain their work clearly. Keep scope small: 2-3 agents, 1-2 cycles."
|
|
7148
7496
|
\`\`\`
|
|
7149
7497
|
|
|
7150
7498
|
After launching, tell them:
|
|
@@ -7187,8 +7535,8 @@ Between polls, encourage the user to explore:
|
|
|
7187
7535
|
|
|
7188
7536
|
Once the session shows "completed":
|
|
7189
7537
|
|
|
7190
|
-
- Show them what the agents built: \`cd
|
|
7191
|
-
- Run the tests to prove the work: \`cd
|
|
7538
|
+
- Show them what the agents built: \`cd ${demoPath} && git log --oneline\`
|
|
7539
|
+
- Run the tests to prove the work: \`cd ${demoPath} && node --test test.js\`
|
|
7192
7540
|
- Show the session artifacts: find the session dir in \`.sisyphus/sessions/\` and show \`roadmap.md\`
|
|
7193
7541
|
- Explain: "Every session creates a roadmap, agent reports, and logs \u2014 all stored in .sisyphus/sessions/"
|
|
7194
7542
|
|
|
@@ -7202,20 +7550,26 @@ sis admin getting-started --tutorial 5
|
|
|
7202
7550
|
`);
|
|
7203
7551
|
}
|
|
7204
7552
|
function printStep5() {
|
|
7553
|
+
const demoPath = join20(process.cwd(), "tmp", "sisyphus-tutorial-demo");
|
|
7554
|
+
const demoExists = existsSync21(demoPath);
|
|
7205
7555
|
let recentCommits = "";
|
|
7206
7556
|
let topLevelFiles = "";
|
|
7207
7557
|
try {
|
|
7208
|
-
recentCommits =
|
|
7558
|
+
recentCommits = execSync16("git log --oneline -15 2>/dev/null", { encoding: "utf-8" }).trim();
|
|
7209
7559
|
} catch {
|
|
7210
7560
|
}
|
|
7211
7561
|
try {
|
|
7212
|
-
topLevelFiles =
|
|
7562
|
+
topLevelFiles = execSync16("ls -1 2>/dev/null", { encoding: "utf-8" }).trim();
|
|
7213
7563
|
} catch {
|
|
7214
7564
|
}
|
|
7215
7565
|
console.log(`
|
|
7216
7566
|
<claude-instructions>
|
|
7217
7567
|
# Sisyphus Getting Started \u2014 Step 5: What's Next
|
|
7218
7568
|
|
|
7569
|
+
## Environment Data
|
|
7570
|
+
- demoPath: ${demoPath}
|
|
7571
|
+
- demoExists: ${demoExists}
|
|
7572
|
+
|
|
7219
7573
|
## Codebase Context
|
|
7220
7574
|
<recent-commits>
|
|
7221
7575
|
${recentCommits || "(no git repo detected)"}
|
|
@@ -7278,14 +7632,38 @@ Or directly: \`sis start "your task" -c "any background context"\`
|
|
|
7278
7632
|
|
|
7279
7633
|
Look at the recent commits and top-level files above. Based on what you can see of their project, suggest 2-3 concrete sisyphus-scale tasks they could try. Be specific to their codebase \u2014 reference actual directories, patterns, or areas you can see.
|
|
7280
7634
|
|
|
7281
|
-
If there are no commits or files (e.g., they ran this from /
|
|
7635
|
+
If there are no commits or files (e.g., they ran this from an empty scratch dir, or from the tutorial demo dir itself at tmp/sisyphus-tutorial-demo), skip this section.
|
|
7282
7636
|
|
|
7283
7637
|
Format as:
|
|
7284
7638
|
> Based on your codebase, here are some tasks sisyphus would be great for:
|
|
7285
7639
|
> - "..."
|
|
7286
7640
|
> - "..."
|
|
7287
7641
|
|
|
7288
|
-
### 5.
|
|
7642
|
+
### 5. Clean up the demo dir
|
|
7643
|
+
|
|
7644
|
+
**Only if demoExists is true** (see Environment Data above). The tutorial dropped a
|
|
7645
|
+
demo todo app at \`${demoPath}\` with its own \`.sisyphus/\` session state. Ask the user
|
|
7646
|
+
before deleting \u2014 they may want to poke around the session files first.
|
|
7647
|
+
|
|
7648
|
+
Ask exactly:
|
|
7649
|
+
|
|
7650
|
+
> Want me to remove the tutorial demo at \`${demoPath}\`? It's safe to delete \u2014 it
|
|
7651
|
+
> only contains the demo todo app and the session sisyphus just ran on it.
|
|
7652
|
+
|
|
7653
|
+
If they say yes, run:
|
|
7654
|
+
\`\`\`
|
|
7655
|
+
rm -rf ${demoPath}
|
|
7656
|
+
\`\`\`
|
|
7657
|
+
|
|
7658
|
+
Then check whether \`${join20(process.cwd(), "tmp")}\` is now empty, and if so remove it too:
|
|
7659
|
+
\`\`\`
|
|
7660
|
+
rmdir ${join20(process.cwd(), "tmp")} 2>/dev/null || true
|
|
7661
|
+
\`\`\`
|
|
7662
|
+
|
|
7663
|
+
If they say no or want to explore first, leave it. They can clean up later with the same
|
|
7664
|
+
\`rm -rf\` command.
|
|
7665
|
+
|
|
7666
|
+
### 6. There's more to learn
|
|
7289
7667
|
|
|
7290
7668
|
Tell them:
|
|
7291
7669
|
|
|
@@ -7687,22 +8065,10 @@ function registerGettingStarted(program2) {
|
|
|
7687
8065
|
|
|
7688
8066
|
// src/cli/commands/history.ts
|
|
7689
8067
|
init_paths();
|
|
7690
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
8068
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync23, existsSync as existsSync22 } from "fs";
|
|
7691
8069
|
import { resolve as resolve6 } from "path";
|
|
7692
|
-
var RESET3 = "\x1B[0m";
|
|
7693
|
-
var BOLD3 = "\x1B[1m";
|
|
7694
|
-
var DIM3 = "\x1B[2m";
|
|
7695
|
-
var COLOR = {
|
|
7696
|
-
green: "\x1B[32m",
|
|
7697
|
-
yellow: "\x1B[33m",
|
|
7698
|
-
cyan: "\x1B[36m",
|
|
7699
|
-
red: "\x1B[31m",
|
|
7700
|
-
gray: "\x1B[90m",
|
|
7701
|
-
white: "\x1B[37m",
|
|
7702
|
-
magenta: "\x1B[35m"
|
|
7703
|
-
};
|
|
7704
8070
|
function c(color, text) {
|
|
7705
|
-
return
|
|
8071
|
+
return colorize(text, color);
|
|
7706
8072
|
}
|
|
7707
8073
|
var INTERACTIVE_AGENT_TYPES = /* @__PURE__ */ new Set([
|
|
7708
8074
|
"sisyphus:requirements",
|
|
@@ -7727,13 +8093,13 @@ function splitAgentTime(agents) {
|
|
|
7727
8093
|
}
|
|
7728
8094
|
function loadAllSummaries() {
|
|
7729
8095
|
const base = historyBaseDir();
|
|
7730
|
-
if (!
|
|
8096
|
+
if (!existsSync22(base)) return [];
|
|
7731
8097
|
const results = [];
|
|
7732
8098
|
for (const name of readdirSync6(base)) {
|
|
7733
8099
|
const summaryPath = historySessionSummaryPath(name);
|
|
7734
|
-
if (
|
|
8100
|
+
if (existsSync22(summaryPath)) {
|
|
7735
8101
|
try {
|
|
7736
|
-
const raw =
|
|
8102
|
+
const raw = readFileSync23(summaryPath, "utf-8");
|
|
7737
8103
|
results.push({ id: name, summary: JSON.parse(raw) });
|
|
7738
8104
|
continue;
|
|
7739
8105
|
} catch {
|
|
@@ -7747,10 +8113,10 @@ function loadAllSummaries() {
|
|
|
7747
8113
|
}
|
|
7748
8114
|
function buildLiveSummary(sessionId) {
|
|
7749
8115
|
const eventsPath = historyEventsPath(sessionId);
|
|
7750
|
-
if (!
|
|
8116
|
+
if (!existsSync22(eventsPath)) return null;
|
|
7751
8117
|
let cwd = null;
|
|
7752
8118
|
try {
|
|
7753
|
-
const lines =
|
|
8119
|
+
const lines = readFileSync23(eventsPath, "utf-8").split("\n");
|
|
7754
8120
|
for (const line of lines) {
|
|
7755
8121
|
if (!line.trim()) continue;
|
|
7756
8122
|
try {
|
|
@@ -7768,10 +8134,10 @@ function buildLiveSummary(sessionId) {
|
|
|
7768
8134
|
}
|
|
7769
8135
|
if (!cwd) return null;
|
|
7770
8136
|
const sPath = statePath(cwd, sessionId);
|
|
7771
|
-
if (!
|
|
8137
|
+
if (!existsSync22(sPath)) return null;
|
|
7772
8138
|
let session2;
|
|
7773
8139
|
try {
|
|
7774
|
-
session2 = JSON.parse(
|
|
8140
|
+
session2 = JSON.parse(readFileSync23(sPath, "utf-8"));
|
|
7775
8141
|
} catch {
|
|
7776
8142
|
return null;
|
|
7777
8143
|
}
|
|
@@ -7832,8 +8198,8 @@ function buildLiveSummary(sessionId) {
|
|
|
7832
8198
|
}
|
|
7833
8199
|
function loadEvents(sessionId) {
|
|
7834
8200
|
const eventsPath = historyEventsPath(sessionId);
|
|
7835
|
-
if (!
|
|
7836
|
-
const lines =
|
|
8201
|
+
if (!existsSync22(eventsPath)) return [];
|
|
8202
|
+
const lines = readFileSync23(eventsPath, "utf-8").split("\n").filter((l) => l.trim());
|
|
7837
8203
|
const events = [];
|
|
7838
8204
|
for (const line of lines) {
|
|
7839
8205
|
try {
|
|
@@ -7846,13 +8212,13 @@ function loadEvents(sessionId) {
|
|
|
7846
8212
|
}
|
|
7847
8213
|
function findSession(idOrName) {
|
|
7848
8214
|
const summaryPath = historySessionSummaryPath(idOrName);
|
|
7849
|
-
if (
|
|
8215
|
+
if (existsSync22(summaryPath)) {
|
|
7850
8216
|
try {
|
|
7851
|
-
return { id: idOrName, summary: JSON.parse(
|
|
8217
|
+
return { id: idOrName, summary: JSON.parse(readFileSync23(summaryPath, "utf-8")) };
|
|
7852
8218
|
} catch {
|
|
7853
8219
|
}
|
|
7854
8220
|
}
|
|
7855
|
-
if (
|
|
8221
|
+
if (existsSync22(historySessionDir(idOrName))) {
|
|
7856
8222
|
const live = buildLiveSummary(idOrName);
|
|
7857
8223
|
if (live) return { id: idOrName, summary: live };
|
|
7858
8224
|
}
|
|
@@ -7920,9 +8286,9 @@ function listSessions(opts) {
|
|
|
7920
8286
|
const agents = s.agentCount > 0 ? `${s.agentCount} agents` : "";
|
|
7921
8287
|
const cycles = s.cycleCount > 0 ? `${s.cycleCount} cycles` : "";
|
|
7922
8288
|
const meta = [agents, cycles, dur].filter(Boolean).join(", ");
|
|
7923
|
-
console.log(`${date} ${status} ${name} ${
|
|
8289
|
+
console.log(`${date} ${status} ${name} ${dim(meta)} ${proj}`);
|
|
7924
8290
|
const taskPreview = s.task.length > 100 ? s.task.slice(0, 100) + "..." : s.task;
|
|
7925
|
-
console.log(` ${
|
|
8291
|
+
console.log(` ${dim(taskPreview)}`);
|
|
7926
8292
|
console.log("");
|
|
7927
8293
|
}
|
|
7928
8294
|
const total = loadAllSummaries().length;
|
|
@@ -7961,54 +8327,54 @@ function showSession(idOrName, opts) {
|
|
|
7961
8327
|
}
|
|
7962
8328
|
const inProgress = s.completedAt == null;
|
|
7963
8329
|
const inProgressTag = inProgress ? ` ${c("yellow", "(in progress)")}` : "";
|
|
7964
|
-
console.log(`${
|
|
7965
|
-
console.log(`${
|
|
7966
|
-
console.log(`${
|
|
7967
|
-
console.log(`${
|
|
7968
|
-
console.log(`${
|
|
7969
|
-
console.log(`${
|
|
8330
|
+
console.log(`${bold(s.name ?? s.sessionId.slice(0, 8))} ${fmtStatus(s.status)}${inProgressTag}`);
|
|
8331
|
+
console.log(`${dim("ID:")} ${s.sessionId}`);
|
|
8332
|
+
console.log(`${dim("Project:")} ${s.cwd}`);
|
|
8333
|
+
console.log(`${dim("Model:")} ${s.model ?? "default"}`);
|
|
8334
|
+
console.log(`${dim("Started:")} ${fmtDate(s.startedAt)}`);
|
|
8335
|
+
console.log(`${dim("Ended:")} ${s.completedAt ? fmtDate(s.completedAt) : c("gray", "\u2014 still running")}`);
|
|
7970
8336
|
const { computeMs, interactiveMs } = splitAgentTime(s.agents);
|
|
7971
8337
|
const wallStr = s.wallClockMs ? formatDuration(s.wallClockMs) : "\u2014";
|
|
7972
|
-
console.log(`${
|
|
8338
|
+
console.log(`${dim("Active:")} ${formatDuration(s.activeMs)} ${dim("Wall:")} ${wallStr}`);
|
|
7973
8339
|
if (interactiveMs > 0) {
|
|
7974
|
-
console.log(`${
|
|
8340
|
+
console.log(`${dim("Compute:")} ${formatDuration(computeMs)} ${dim("Interactive:")} ${formatDuration(interactiveMs)} ${dim("(TUI wait time, not compute)")}`);
|
|
7975
8341
|
}
|
|
7976
8342
|
if (s.userBlockedMs > 0) {
|
|
7977
|
-
console.log(`${
|
|
8343
|
+
console.log(`${dim("Waiting on user:")} ${formatDuration(s.userBlockedMs)} ${dim("(blocked on sis ask, not compute)")}`);
|
|
7978
8344
|
}
|
|
7979
8345
|
console.log("");
|
|
7980
|
-
console.log(
|
|
8346
|
+
console.log(bold("Task"));
|
|
7981
8347
|
console.log(s.task);
|
|
7982
8348
|
console.log("");
|
|
7983
8349
|
if (s.context) {
|
|
7984
|
-
console.log(
|
|
8350
|
+
console.log(bold("Context"));
|
|
7985
8351
|
console.log(s.context.length > 500 ? s.context.slice(0, 500) + "..." : s.context);
|
|
7986
8352
|
console.log("");
|
|
7987
8353
|
}
|
|
7988
8354
|
if (s.agents.length > 0) {
|
|
7989
|
-
console.log(`${
|
|
8355
|
+
console.log(`${bold("Agents")} (${s.agents.length})`);
|
|
7990
8356
|
for (const a of s.agents) {
|
|
7991
8357
|
const name = a.nickname ? `${a.name} "${a.nickname}"` : a.name;
|
|
7992
8358
|
const type = a.agentType ? c("gray", ` [${a.agentType}]`) : "";
|
|
7993
8359
|
const interactive = isInteractiveAgent(a.agentType) ? c("yellow", " (interactive)") : "";
|
|
7994
8360
|
const blocked = a.userBlockedMs ?? 0;
|
|
7995
|
-
const waiting = blocked > 0 ? ` ${
|
|
7996
|
-
console.log(` ${fmtStatus(a.status)} ${name}${type}${interactive} ${
|
|
8361
|
+
const waiting = blocked > 0 ? ` ${dim(`\xB7 ${formatDuration(blocked)} waiting`)}` : "";
|
|
8362
|
+
console.log(` ${fmtStatus(a.status)} ${name}${type}${interactive} ${dim(formatDuration(a.activeMs))}${waiting}`);
|
|
7997
8363
|
}
|
|
7998
8364
|
console.log("");
|
|
7999
8365
|
}
|
|
8000
8366
|
if (s.cycles.length > 0) {
|
|
8001
|
-
console.log(`${
|
|
8367
|
+
console.log(`${bold("Cycles")} (${s.cycles.length})`);
|
|
8002
8368
|
for (const cy of s.cycles) {
|
|
8003
8369
|
const mode = cy.mode ? c("magenta", cy.mode) : "";
|
|
8004
8370
|
const blocked = cy.userBlockedMs ?? 0;
|
|
8005
|
-
const waiting = blocked > 0 ? ` ${
|
|
8006
|
-
console.log(` ${
|
|
8371
|
+
const waiting = blocked > 0 ? ` ${dim(`\xB7 ${formatDuration(blocked)} waiting`)}` : "";
|
|
8372
|
+
console.log(` ${dim(`#${cy.cycle}`)} ${mode} ${cy.agentsSpawned} agents ${dim(formatDuration(cy.activeMs))}${waiting}`);
|
|
8007
8373
|
}
|
|
8008
8374
|
console.log("");
|
|
8009
8375
|
}
|
|
8010
8376
|
if (s.messages.length > 0) {
|
|
8011
|
-
console.log(`${
|
|
8377
|
+
console.log(`${bold("Messages")} (${s.messages.length})`);
|
|
8012
8378
|
for (const m of s.messages) {
|
|
8013
8379
|
const src = c("gray", m.source);
|
|
8014
8380
|
const preview = m.content.length > 120 ? m.content.slice(0, 120) + "..." : m.content;
|
|
@@ -8017,12 +8383,12 @@ function showSession(idOrName, opts) {
|
|
|
8017
8383
|
console.log("");
|
|
8018
8384
|
}
|
|
8019
8385
|
if (s.completionReport) {
|
|
8020
|
-
console.log(
|
|
8386
|
+
console.log(bold("Completion Report"));
|
|
8021
8387
|
console.log(s.completionReport);
|
|
8022
8388
|
console.log("");
|
|
8023
8389
|
}
|
|
8024
8390
|
if (s.achievements.length > 0) {
|
|
8025
|
-
console.log(
|
|
8391
|
+
console.log(bold("Achievements Unlocked"));
|
|
8026
8392
|
console.log(` ${s.achievements.join(", ")}`);
|
|
8027
8393
|
console.log("");
|
|
8028
8394
|
}
|
|
@@ -8035,7 +8401,7 @@ function showSession(idOrName, opts) {
|
|
|
8035
8401
|
`hour ${sig.hourOfDay}`,
|
|
8036
8402
|
sig.idleDurationMs > 6e4 ? `idle ${formatDuration(sig.idleDurationMs)}` : null
|
|
8037
8403
|
].filter(Boolean);
|
|
8038
|
-
console.log(
|
|
8404
|
+
console.log(dim(`Final signals: ${parts.join(" \xB7 ")}`));
|
|
8039
8405
|
}
|
|
8040
8406
|
}
|
|
8041
8407
|
function formatEventData(e) {
|
|
@@ -8046,11 +8412,11 @@ function formatEventData(e) {
|
|
|
8046
8412
|
case "session-named":
|
|
8047
8413
|
return c("white", d.name);
|
|
8048
8414
|
case "agent-spawned":
|
|
8049
|
-
return `${c("white", d.agentId)} ${d.agentType ?? ""} ${
|
|
8415
|
+
return `${c("white", d.agentId)} ${d.agentType ?? ""} ${dim(`${d.instruction?.slice(0, 60) ?? ""}...`)}`;
|
|
8050
8416
|
case "agent-nicknamed":
|
|
8051
8417
|
return `${d.agentId} "${c("white", d.nickname)}"`;
|
|
8052
8418
|
case "agent-completed":
|
|
8053
|
-
return `${d.agentId} ${
|
|
8419
|
+
return `${d.agentId} ${dim(formatDuration(d.activeMs))} ${dim(d.reportSummary?.slice(0, 60) ?? "")}`;
|
|
8054
8420
|
case "agent-exited":
|
|
8055
8421
|
return `${d.agentId} ${fmtStatus(d.status)} ${d.reason ?? ""}`;
|
|
8056
8422
|
case "cycle-boundary":
|
|
@@ -8066,15 +8432,15 @@ function formatEventData(e) {
|
|
|
8066
8432
|
return `${c("cyan", d.type)} ${c("gray", fileParts)}`;
|
|
8067
8433
|
}
|
|
8068
8434
|
case "agent-killed":
|
|
8069
|
-
return `${d.agentId} ${fmtStatus(d.status)} ${
|
|
8435
|
+
return `${d.agentId} ${fmtStatus(d.status)} ${dim(formatDuration(d.activeMs))} ${d.reason ?? ""}`;
|
|
8070
8436
|
case "agent-restarted":
|
|
8071
|
-
return `${d.agentId} restart #${d.restartCount} ${
|
|
8437
|
+
return `${d.agentId} restart #${d.restartCount} ${dim(`was ${d.previousStatus}`)}`;
|
|
8072
8438
|
case "rollback":
|
|
8073
8439
|
return `cycle ${d.fromCycle} \u2192 ${d.toCycle} ${d.killedAgentCount} agents killed`;
|
|
8074
8440
|
case "session-resumed":
|
|
8075
8441
|
return `was ${fmtStatus(d.previousStatus)} ${d.lostAgentCount} agents lost`;
|
|
8076
8442
|
case "session-continued":
|
|
8077
|
-
return `${d.cycleCount} cycles ${
|
|
8443
|
+
return `${d.cycleCount} cycles ${dim(formatDuration(d.activeMs))}`;
|
|
8078
8444
|
case "session-end":
|
|
8079
8445
|
return `${fmtStatus(d.status)} ${formatDuration(d.activeMs)} ${d.agentCount} agents ${d.cycleCount} cycles`;
|
|
8080
8446
|
default:
|
|
@@ -8172,29 +8538,29 @@ function showStats(opts) {
|
|
|
8172
8538
|
}, null, 2));
|
|
8173
8539
|
return;
|
|
8174
8540
|
}
|
|
8175
|
-
console.log(
|
|
8541
|
+
console.log(bold("Session History Stats"));
|
|
8176
8542
|
console.log("");
|
|
8177
|
-
console.log(` ${
|
|
8178
|
-
const timeLine = ` ${
|
|
8543
|
+
console.log(` ${bold("Sessions:")} ${sessions.length} total ${c("cyan", `${completed.length} completed`)} ${c("red", `${killed.length} killed`)}`);
|
|
8544
|
+
const timeLine = ` ${bold("Time:")} ${formatDuration(totalActiveMs)} total ${formatDuration(avgMs)} avg` + (p50Ms != null && p90Ms != null ? ` ${dim(`p50=${formatDuration(p50Ms)} p90=${formatDuration(p90Ms)}`)}` : "");
|
|
8179
8545
|
console.log(timeLine);
|
|
8180
8546
|
if (avgEfficiency != null) {
|
|
8181
8547
|
const effColor = avgEfficiency >= 0.7 ? "green" : avgEfficiency >= 0.4 ? "yellow" : "red";
|
|
8182
|
-
console.log(` ${
|
|
8548
|
+
console.log(` ${bold("Efficiency:")} ${c(effColor, (avgEfficiency * 100).toFixed(1) + "%")} ${dim(`(${efficiencyValues.length} sessions with data)`)}`);
|
|
8183
8549
|
}
|
|
8184
|
-
console.log(` ${
|
|
8185
|
-
console.log(` ${
|
|
8186
|
-
console.log(` ${
|
|
8550
|
+
console.log(` ${bold("Agents:")} ${totalAgents} spawned (${(totalAgents / sessions.length).toFixed(1)} avg/session)`);
|
|
8551
|
+
console.log(` ${bold("Cycles:")} ${totalCycles} total (${(totalCycles / sessions.length).toFixed(1)} avg/session)`);
|
|
8552
|
+
console.log(` ${bold("Messages:")} ${totalMessages} total`);
|
|
8187
8553
|
console.log("");
|
|
8188
|
-
console.log(
|
|
8554
|
+
console.log(bold("By Project"));
|
|
8189
8555
|
const sorted = [...byProject.entries()].sort((a, b) => b[1].count - a[1].count);
|
|
8190
8556
|
for (const [proj, data] of sorted) {
|
|
8191
8557
|
console.log(` ${c("gray", fmtProject(proj))} ${data.count} sessions ${formatDuration(data.activeMs)} ${data.agents} agents`);
|
|
8192
8558
|
}
|
|
8193
8559
|
if (agentTypeMap.size > 0) {
|
|
8194
8560
|
console.log("");
|
|
8195
|
-
console.log(
|
|
8561
|
+
console.log(bold("By Agent Type"));
|
|
8196
8562
|
const typeHeader = ` ${"Type".padEnd(20)} ${"Count".padStart(6)} ${"Avg Time".padStart(10)} ${"Crash %".padStart(8)} ${"Done %".padStart(8)}`;
|
|
8197
|
-
console.log(
|
|
8563
|
+
console.log(dim(typeHeader));
|
|
8198
8564
|
const sortedTypes = [...agentTypeMap.entries()].sort((a, b) => b[1].count - a[1].count);
|
|
8199
8565
|
for (const [type, data] of sortedTypes) {
|
|
8200
8566
|
const avgTime = formatDuration(data.totalMs / data.count);
|
|
@@ -8206,12 +8572,12 @@ function showStats(opts) {
|
|
|
8206
8572
|
}
|
|
8207
8573
|
if (sessions.length >= 5) {
|
|
8208
8574
|
console.log("");
|
|
8209
|
-
console.log(
|
|
8575
|
+
console.log(bold("Temporal Patterns"));
|
|
8210
8576
|
const topBlocks = [...hourBlocks.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
8211
|
-
console.log(` ${
|
|
8577
|
+
console.log(` ${dim("Busiest times:")} ${topBlocks.map(([label, count]) => `${label} (${count})`).join(" ")}`);
|
|
8212
8578
|
const dayOrder = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
8213
8579
|
const dayParts = dayOrder.map((d) => `${d} ${dayCounts.get(d) ?? 0}`);
|
|
8214
|
-
console.log(` ${
|
|
8580
|
+
console.log(` ${dim("By day:")} ${dayParts.join(" ")}`);
|
|
8215
8581
|
}
|
|
8216
8582
|
}
|
|
8217
8583
|
function registerHistory(program2) {
|
|
@@ -8241,7 +8607,7 @@ function registerHistory(program2) {
|
|
|
8241
8607
|
init_paths();
|
|
8242
8608
|
import { execFile as execFile2 } from "child_process";
|
|
8243
8609
|
import { promisify } from "util";
|
|
8244
|
-
import { existsSync as
|
|
8610
|
+
import { existsSync as existsSync23, readFileSync as readFileSync24, mkdirSync as mkdirSync10, symlinkSync, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "fs";
|
|
8245
8611
|
import { homedir as homedir12 } from "os";
|
|
8246
8612
|
import { join as join22 } from "path";
|
|
8247
8613
|
function sanitizeName(name) {
|
|
@@ -8253,7 +8619,7 @@ function buildOutputPath(label, dir) {
|
|
|
8253
8619
|
const base = `sisyphus-${label}-${date}`;
|
|
8254
8620
|
let candidate = join22(dir, `${base}.zip`);
|
|
8255
8621
|
let counter = 1;
|
|
8256
|
-
while (
|
|
8622
|
+
while (existsSync23(candidate)) {
|
|
8257
8623
|
counter++;
|
|
8258
8624
|
candidate = join22(dir, `${base}-${counter}.zip`);
|
|
8259
8625
|
}
|
|
@@ -8317,16 +8683,16 @@ async function exportSessionToZip(sessionId, cwd, options) {
|
|
|
8317
8683
|
const reveal = options?.reveal ?? true;
|
|
8318
8684
|
const sessDir = sessionDir(cwd, sessionId);
|
|
8319
8685
|
const histDir = historySessionDir(sessionId);
|
|
8320
|
-
const sessExists =
|
|
8321
|
-
const histExists =
|
|
8686
|
+
const sessExists = existsSync23(sessDir);
|
|
8687
|
+
const histExists = existsSync23(histDir);
|
|
8322
8688
|
if (!sessExists && !histExists) {
|
|
8323
8689
|
throw new Error(`No data found for session ${sessionId}`);
|
|
8324
8690
|
}
|
|
8325
8691
|
let label = sessionId.slice(0, 8);
|
|
8326
8692
|
const stPath = statePath(cwd, sessionId);
|
|
8327
|
-
if (
|
|
8693
|
+
if (existsSync23(stPath)) {
|
|
8328
8694
|
try {
|
|
8329
|
-
const state = JSON.parse(
|
|
8695
|
+
const state = JSON.parse(readFileSync24(stPath, "utf-8"));
|
|
8330
8696
|
if (state.name) {
|
|
8331
8697
|
label = sanitizeName(state.name);
|
|
8332
8698
|
}
|
|
@@ -8465,12 +8831,12 @@ function buildManifest(args2) {
|
|
|
8465
8831
|
}
|
|
8466
8832
|
|
|
8467
8833
|
// src/shared/version.ts
|
|
8468
|
-
import { readFileSync as
|
|
8834
|
+
import { readFileSync as readFileSync25 } from "fs";
|
|
8469
8835
|
import { resolve as resolve7 } from "path";
|
|
8470
8836
|
function readSisyphusVersion() {
|
|
8471
8837
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
8472
8838
|
try {
|
|
8473
|
-
const raw =
|
|
8839
|
+
const raw = readFileSync25(resolve7(import.meta.dirname, rel), "utf-8");
|
|
8474
8840
|
const pkg = JSON.parse(raw);
|
|
8475
8841
|
if (pkg.name === "sisyphi" && pkg.version) return pkg.version;
|
|
8476
8842
|
} catch {
|
|
@@ -8565,13 +8931,13 @@ function registerUpload(program2) {
|
|
|
8565
8931
|
}
|
|
8566
8932
|
|
|
8567
8933
|
// src/cli/commands/scratch.ts
|
|
8568
|
-
import { execSync as
|
|
8934
|
+
import { execSync as execSync17 } from "child_process";
|
|
8569
8935
|
init_shell();
|
|
8570
8936
|
function findHomeSession(cwd) {
|
|
8571
8937
|
const normalizedCwd = cwd.replace(/\/+$/, "");
|
|
8572
8938
|
let output;
|
|
8573
8939
|
try {
|
|
8574
|
-
output =
|
|
8940
|
+
output = execSync17('tmux list-sessions -F "#{session_id}|#{session_name}"', {
|
|
8575
8941
|
encoding: "utf-8",
|
|
8576
8942
|
stdio: ["pipe", "pipe", "pipe"]
|
|
8577
8943
|
}).trim();
|
|
@@ -8585,7 +8951,7 @@ function findHomeSession(cwd) {
|
|
|
8585
8951
|
const name = line.slice(pipeIdx + 1);
|
|
8586
8952
|
if (name.startsWith("ssyph_")) continue;
|
|
8587
8953
|
try {
|
|
8588
|
-
const val =
|
|
8954
|
+
const val = execSync17(
|
|
8589
8955
|
`tmux show-options -t ${shellQuote(sessId)} -v @sisyphus_cwd`,
|
|
8590
8956
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
8591
8957
|
).trim();
|
|
@@ -8601,7 +8967,7 @@ function registerScratch(program2) {
|
|
|
8601
8967
|
const cwd = opts.cwd ?? process.env["SISYPHUS_CWD"] ?? process.cwd();
|
|
8602
8968
|
const homeSession = findHomeSession(cwd);
|
|
8603
8969
|
if (!homeSession) {
|
|
8604
|
-
const current =
|
|
8970
|
+
const current = execSync17('tmux display-message -p "#{session_name}"', {
|
|
8605
8971
|
encoding: "utf-8"
|
|
8606
8972
|
}).trim();
|
|
8607
8973
|
openScratchWindow(current, cwd, promptParts.join(" "));
|
|
@@ -8611,7 +8977,7 @@ function registerScratch(program2) {
|
|
|
8611
8977
|
});
|
|
8612
8978
|
}
|
|
8613
8979
|
function openScratchWindow(tmuxSession, cwd, prompt) {
|
|
8614
|
-
const windowId =
|
|
8980
|
+
const windowId = execSync17(
|
|
8615
8981
|
`tmux new-window -t ${shellQuote(tmuxSession + ":")} -n "scratch" -c ${shellQuote(cwd)} -P -F "#{window_id}"`,
|
|
8616
8982
|
{ encoding: "utf-8" }
|
|
8617
8983
|
).trim();
|
|
@@ -8619,7 +8985,7 @@ function openScratchWindow(tmuxSession, cwd, prompt) {
|
|
|
8619
8985
|
if (prompt) {
|
|
8620
8986
|
cmd += ` -p ${shellQuote(prompt)}`;
|
|
8621
8987
|
}
|
|
8622
|
-
|
|
8988
|
+
execSync17(
|
|
8623
8989
|
`tmux send-keys -t ${shellQuote(windowId)} ${shellQuote(cmd)} Enter`
|
|
8624
8990
|
);
|
|
8625
8991
|
console.log(`Scratch session opened in ${tmuxSession}`);
|
|
@@ -8628,7 +8994,7 @@ function openScratchWindow(tmuxSession, cwd, prompt) {
|
|
|
8628
8994
|
// src/cli/commands/review.ts
|
|
8629
8995
|
init_paths();
|
|
8630
8996
|
import { join as join23, resolve as resolve8, dirname as dirname8 } from "path";
|
|
8631
|
-
import { existsSync as
|
|
8997
|
+
import { existsSync as existsSync24, readFileSync as readFileSync26, writeFileSync as writeFileSync12, renameSync as renameSync3, readdirSync as readdirSync7 } from "fs";
|
|
8632
8998
|
var _statusCheck = ["draft", "question", "approved", "rejected", "deferred"];
|
|
8633
8999
|
function resolveContextArtifact(file, opts, filename, notFoundMessage) {
|
|
8634
9000
|
const cwd = opts.cwd || process.env.SISYPHUS_CWD || process.cwd();
|
|
@@ -8636,18 +9002,18 @@ function resolveContextArtifact(file, opts, filename, notFoundMessage) {
|
|
|
8636
9002
|
const sessionId = opts.sessionId || process.env.SISYPHUS_SESSION_ID;
|
|
8637
9003
|
if (sessionId) {
|
|
8638
9004
|
const target = join23(contextDir(cwd, sessionId), filename);
|
|
8639
|
-
if (!
|
|
9005
|
+
if (!existsSync24(target)) {
|
|
8640
9006
|
console.error(`Error: File not found: ${target}`);
|
|
8641
9007
|
process.exit(1);
|
|
8642
9008
|
}
|
|
8643
9009
|
return target;
|
|
8644
9010
|
}
|
|
8645
9011
|
const dir = sessionsDir(cwd);
|
|
8646
|
-
if (
|
|
9012
|
+
if (existsSync24(dir)) {
|
|
8647
9013
|
const sessions = readdirSync7(dir);
|
|
8648
9014
|
for (const session2 of sessions.reverse()) {
|
|
8649
9015
|
const candidate = join23(dir, session2, "context", filename);
|
|
8650
|
-
if (
|
|
9016
|
+
if (existsSync24(candidate)) return candidate;
|
|
8651
9017
|
}
|
|
8652
9018
|
}
|
|
8653
9019
|
console.error(`Error: ${notFoundMessage}`);
|
|
@@ -8689,16 +9055,16 @@ Examples:
|
|
|
8689
9055
|
"requirements.json",
|
|
8690
9056
|
"No requirements.json found. Provide a path or use --session-id."
|
|
8691
9057
|
);
|
|
8692
|
-
if (!
|
|
9058
|
+
if (!existsSync24(targetPath)) {
|
|
8693
9059
|
console.error(`Error: File not found: ${targetPath}`);
|
|
8694
9060
|
process.exit(1);
|
|
8695
9061
|
}
|
|
8696
|
-
const parsed = JSON.parse(
|
|
9062
|
+
const parsed = JSON.parse(readFileSync26(targetPath, "utf-8"));
|
|
8697
9063
|
const rendered = renderRequirementsMarkdown(parsed);
|
|
8698
9064
|
const outPath = join23(dirname8(targetPath), "requirements.md");
|
|
8699
9065
|
const tmpPath = outPath + ".tmp";
|
|
8700
|
-
if (
|
|
8701
|
-
const existing =
|
|
9066
|
+
if (existsSync24(outPath)) {
|
|
9067
|
+
const existing = readFileSync26(outPath, "utf-8");
|
|
8702
9068
|
if (existing !== rendered) {
|
|
8703
9069
|
if (!opts.force) {
|
|
8704
9070
|
process.stderr.write(
|
|
@@ -9079,7 +9445,7 @@ function renderRequirementsMarkdown(json) {
|
|
|
9079
9445
|
|
|
9080
9446
|
// src/cli/commands/companion.ts
|
|
9081
9447
|
import { basename as basename5, dirname as dirname11, join as join28 } from "path";
|
|
9082
|
-
import { mkdirSync as mkdirSync14, readFileSync as
|
|
9448
|
+
import { mkdirSync as mkdirSync14, readFileSync as readFileSync31, writeFileSync as writeFileSync17 } from "fs";
|
|
9083
9449
|
init_paths();
|
|
9084
9450
|
|
|
9085
9451
|
// src/shared/companion-render.ts
|
|
@@ -9270,7 +9636,7 @@ var MOOD_COLORS = {
|
|
|
9270
9636
|
function getMoodTmuxColor(mood) {
|
|
9271
9637
|
return MOOD_COLORS[mood].tmux;
|
|
9272
9638
|
}
|
|
9273
|
-
function
|
|
9639
|
+
function colorize3(text, mood, tmux) {
|
|
9274
9640
|
const { ansi, tmux: tmuxColor } = MOOD_COLORS[mood];
|
|
9275
9641
|
if (tmux) {
|
|
9276
9642
|
return `#[fg=${tmuxColor}]${text}#[fg=default]`;
|
|
@@ -9368,7 +9734,7 @@ function applyColor(result, fields, facePart, mood, opts) {
|
|
|
9368
9734
|
const useColor = opts?.color === true || opts?.tmuxFormat === true;
|
|
9369
9735
|
if (!useColor || facePart === null || !fields.includes("face")) return result;
|
|
9370
9736
|
const tmux = opts?.tmuxFormat === true;
|
|
9371
|
-
const coloredFace =
|
|
9737
|
+
const coloredFace = colorize3(facePart, mood, tmux);
|
|
9372
9738
|
return result.replace(facePart, coloredFace);
|
|
9373
9739
|
}
|
|
9374
9740
|
|
|
@@ -10181,7 +10547,7 @@ function stripAnsiForWidth(s) {
|
|
|
10181
10547
|
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
10182
10548
|
}
|
|
10183
10549
|
function renderBadgeCard(def, unlock, opts) {
|
|
10184
|
-
const
|
|
10550
|
+
const dim2 = opts?.dim === true || unlock === null;
|
|
10185
10551
|
const art = BADGE_ART[def.id] ?? [];
|
|
10186
10552
|
const lines = [];
|
|
10187
10553
|
const category = def.category.toUpperCase();
|
|
@@ -10195,7 +10561,7 @@ function renderBadgeCard(def, unlock, opts) {
|
|
|
10195
10561
|
const artSlice = art.slice(0, artMaxLines);
|
|
10196
10562
|
for (const artLine of artSlice) {
|
|
10197
10563
|
const centered = centerLine(artLine, CARD_INNER);
|
|
10198
|
-
lines.push(`\u2502${
|
|
10564
|
+
lines.push(`\u2502${dim2 ? dimText(centered) : centered}\u2502`);
|
|
10199
10565
|
}
|
|
10200
10566
|
for (let i = artSlice.length; i < artMaxLines; i++) {
|
|
10201
10567
|
lines.push(`\u2502${" ".repeat(CARD_INNER)}\u2502`);
|
|
@@ -10206,7 +10572,7 @@ function renderBadgeCard(def, unlock, opts) {
|
|
|
10206
10572
|
const descLines = wrapText(def.description, CARD_INNER - 4);
|
|
10207
10573
|
for (const dl of descLines.slice(0, 2)) {
|
|
10208
10574
|
const centered = centerLine(dl, CARD_INNER);
|
|
10209
|
-
lines.push(`\u2502${
|
|
10575
|
+
lines.push(`\u2502${dim2 ? dimText(centered) : centered}\u2502`);
|
|
10210
10576
|
}
|
|
10211
10577
|
const usedContent = 1 + 1 + artMaxLines + 1 + 1 + Math.min(descLines.length, 2);
|
|
10212
10578
|
const remaining = CARD_HEIGHT - 2 - usedContent;
|
|
@@ -10266,7 +10632,7 @@ function createBadgeGallery(unlockedAchievements, startIndex) {
|
|
|
10266
10632
|
|
|
10267
10633
|
// src/daemon/companion-memory.ts
|
|
10268
10634
|
init_paths();
|
|
10269
|
-
import { existsSync as
|
|
10635
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync12, readFileSync as readFileSync28, renameSync as renameSync5, writeFileSync as writeFileSync14 } from "fs";
|
|
10270
10636
|
import { dirname as dirname10, join as join25 } from "path";
|
|
10271
10637
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
10272
10638
|
import { z as z2 } from "zod";
|
|
@@ -10278,7 +10644,7 @@ var COOLDOWN_MS = 5 * 60 * 1e3;
|
|
|
10278
10644
|
|
|
10279
10645
|
// src/daemon/companion.ts
|
|
10280
10646
|
init_paths();
|
|
10281
|
-
import { existsSync as
|
|
10647
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync11, readFileSync as readFileSync27, renameSync as renameSync4, writeFileSync as writeFileSync13 } from "fs";
|
|
10282
10648
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
10283
10649
|
import { dirname as dirname9, join as join24 } from "path";
|
|
10284
10650
|
|
|
@@ -10329,12 +10695,12 @@ function normalizeCompanion(state) {
|
|
|
10329
10695
|
// src/daemon/companion.ts
|
|
10330
10696
|
function loadCompanion() {
|
|
10331
10697
|
const path = companionPath();
|
|
10332
|
-
if (!
|
|
10698
|
+
if (!existsSync25(path)) {
|
|
10333
10699
|
const state2 = createDefaultCompanion();
|
|
10334
10700
|
saveCompanion(state2);
|
|
10335
10701
|
return state2;
|
|
10336
10702
|
}
|
|
10337
|
-
const raw =
|
|
10703
|
+
const raw = readFileSync27(path, "utf-8");
|
|
10338
10704
|
const state = JSON.parse(raw);
|
|
10339
10705
|
return normalizeCompanion(state);
|
|
10340
10706
|
}
|
|
@@ -10415,10 +10781,10 @@ function fillDefaults(state) {
|
|
|
10415
10781
|
}
|
|
10416
10782
|
function loadMemoryStrict() {
|
|
10417
10783
|
const path = resolvedMemoryPath();
|
|
10418
|
-
if (!
|
|
10784
|
+
if (!existsSync26(path)) return defaultMemoryState();
|
|
10419
10785
|
let raw;
|
|
10420
10786
|
try {
|
|
10421
|
-
raw =
|
|
10787
|
+
raw = readFileSync28(path, "utf-8");
|
|
10422
10788
|
} catch (err) {
|
|
10423
10789
|
throw new MemoryStoreParseError(err);
|
|
10424
10790
|
}
|
|
@@ -10461,7 +10827,7 @@ var ObservationZodSchema = z2.object({
|
|
|
10461
10827
|
});
|
|
10462
10828
|
|
|
10463
10829
|
// src/daemon/companion-popup.ts
|
|
10464
|
-
import { writeFileSync as writeFileSync15, readFileSync as
|
|
10830
|
+
import { writeFileSync as writeFileSync15, readFileSync as readFileSync29, unlinkSync as unlinkSync3, existsSync as existsSync27 } from "fs";
|
|
10465
10831
|
import { tmpdir as tmpdir2 } from "os";
|
|
10466
10832
|
import { join as join26, resolve as resolve9 } from "path";
|
|
10467
10833
|
init_exec();
|
|
@@ -10515,7 +10881,7 @@ function showCommentaryPopupQueue(pages) {
|
|
|
10515
10881
|
if (contentHeight > maxContentHeight) maxContentHeight = contentHeight;
|
|
10516
10882
|
writeFileSync15(`${POPUP_TMP_PREFIX}-${i}.txt`, content);
|
|
10517
10883
|
}
|
|
10518
|
-
const whipAvailable =
|
|
10884
|
+
const whipAvailable = existsSync27(WHIP_ANIMATION_PATH);
|
|
10519
10885
|
if (whipAvailable && maxContentHeight < WHIP_ANIMATION_ROWS + 2) {
|
|
10520
10886
|
maxContentHeight = WHIP_ANIMATION_ROWS + 2;
|
|
10521
10887
|
}
|
|
@@ -10589,7 +10955,7 @@ fi
|
|
|
10589
10955
|
}
|
|
10590
10956
|
let raw;
|
|
10591
10957
|
try {
|
|
10592
|
-
raw =
|
|
10958
|
+
raw = readFileSync29(POPUP_RESULT_PREFIX, "utf8").trim();
|
|
10593
10959
|
} catch {
|
|
10594
10960
|
return null;
|
|
10595
10961
|
} finally {
|
|
@@ -10766,7 +11132,7 @@ function registerCompanion(program2) {
|
|
|
10766
11132
|
const cachePath = join28(globalDir(), "companion-context-cache", `${opts.sessionId}.json`);
|
|
10767
11133
|
let prev = {};
|
|
10768
11134
|
try {
|
|
10769
|
-
prev = JSON.parse(
|
|
11135
|
+
prev = JSON.parse(readFileSync31(cachePath, "utf-8"));
|
|
10770
11136
|
} catch {
|
|
10771
11137
|
prev = {};
|
|
10772
11138
|
}
|
|
@@ -10809,8 +11175,8 @@ import { join as join29 } from "path";
|
|
|
10809
11175
|
init_paths();
|
|
10810
11176
|
init_exec();
|
|
10811
11177
|
init_creds();
|
|
10812
|
-
import { spawn as spawn2, spawnSync as
|
|
10813
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
11178
|
+
import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
|
|
11179
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync32, mkdirSync as mkdirSync16, readFileSync as readFileSync34 } from "fs";
|
|
10814
11180
|
|
|
10815
11181
|
// src/cli/deploy/pricing.ts
|
|
10816
11182
|
var LAST_VERIFIED = "2026-05-06";
|
|
@@ -10845,12 +11211,12 @@ function formatCostLine(provider, instanceType) {
|
|
|
10845
11211
|
// src/cli/deploy/runtime.ts
|
|
10846
11212
|
init_atomic();
|
|
10847
11213
|
init_paths();
|
|
10848
|
-
import { existsSync as
|
|
11214
|
+
import { existsSync as existsSync30, readFileSync as readFileSync33, unlinkSync as unlinkSync4 } from "fs";
|
|
10849
11215
|
function readRuntimeState(provider) {
|
|
10850
11216
|
const path = deployRuntimePath(provider);
|
|
10851
|
-
if (!
|
|
11217
|
+
if (!existsSync30(path)) return null;
|
|
10852
11218
|
try {
|
|
10853
|
-
return JSON.parse(
|
|
11219
|
+
return JSON.parse(readFileSync33(path, "utf-8"));
|
|
10854
11220
|
} catch {
|
|
10855
11221
|
return null;
|
|
10856
11222
|
}
|
|
@@ -10860,7 +11226,7 @@ function writeRuntimeState(provider, state) {
|
|
|
10860
11226
|
}
|
|
10861
11227
|
function clearRuntimeState(provider) {
|
|
10862
11228
|
const path = deployRuntimePath(provider);
|
|
10863
|
-
if (
|
|
11229
|
+
if (existsSync30(path)) unlinkSync4(path);
|
|
10864
11230
|
}
|
|
10865
11231
|
|
|
10866
11232
|
// src/cli/deploy/tailnet.ts
|
|
@@ -10910,15 +11276,15 @@ function isTailscaleAvailable() {
|
|
|
10910
11276
|
}
|
|
10911
11277
|
|
|
10912
11278
|
// src/cli/deploy/templates.ts
|
|
10913
|
-
import { existsSync as
|
|
11279
|
+
import { existsSync as existsSync31 } from "fs";
|
|
10914
11280
|
import { dirname as dirname12, resolve as resolve10 } from "path";
|
|
10915
11281
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
10916
11282
|
function deployRoot() {
|
|
10917
11283
|
const here = dirname12(fileURLToPath4(import.meta.url));
|
|
10918
11284
|
const bundled = resolve10(here, "..", "deploy");
|
|
10919
|
-
if (
|
|
11285
|
+
if (existsSync31(bundled)) return bundled;
|
|
10920
11286
|
const sourceRoot = resolve10(here, "..", "..", "..", "deploy");
|
|
10921
|
-
if (
|
|
11287
|
+
if (existsSync31(sourceRoot)) return sourceRoot;
|
|
10922
11288
|
throw new Error(
|
|
10923
11289
|
`Could not locate deploy/ templates. Looked at:
|
|
10924
11290
|
${bundled}
|
|
@@ -11083,7 +11449,7 @@ async function authTailscale() {
|
|
|
11083
11449
|
function runTerraform(provider, args2, extraEnv) {
|
|
11084
11450
|
ensureProviderStateDir(provider);
|
|
11085
11451
|
ensureTerraformInstalled();
|
|
11086
|
-
const result =
|
|
11452
|
+
const result = spawnSync4("terraform", args2, {
|
|
11087
11453
|
cwd: providerModuleDir(provider),
|
|
11088
11454
|
stdio: "inherit",
|
|
11089
11455
|
env: { ...EXEC_ENV, ...extraEnv }
|
|
@@ -11092,7 +11458,7 @@ function runTerraform(provider, args2, extraEnv) {
|
|
|
11092
11458
|
return result.status === null ? 1 : result.status;
|
|
11093
11459
|
}
|
|
11094
11460
|
function ensureTerraformInstalled() {
|
|
11095
|
-
const result =
|
|
11461
|
+
const result = spawnSync4("terraform", ["version"], { stdio: "pipe", env: EXEC_ENV });
|
|
11096
11462
|
if (result.error || result.status !== 0) {
|
|
11097
11463
|
const platform = process.platform;
|
|
11098
11464
|
const hint = platform === "darwin" ? "brew install terraform" : "See https://developer.hashicorp.com/terraform/install";
|
|
@@ -11102,14 +11468,14 @@ function ensureTerraformInstalled() {
|
|
|
11102
11468
|
function ensureProviderStateDir(provider) {
|
|
11103
11469
|
ensureDeployDir();
|
|
11104
11470
|
const dir = deployProviderDir(provider);
|
|
11105
|
-
if (!
|
|
11471
|
+
if (!existsSync32(dir)) mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
11106
11472
|
}
|
|
11107
11473
|
function backupState(provider) {
|
|
11108
11474
|
const src = deployStatePath(provider);
|
|
11109
|
-
if (
|
|
11475
|
+
if (existsSync32(src)) copyFileSync2(src, deployStateBackupPath(provider));
|
|
11110
11476
|
}
|
|
11111
11477
|
function readSshPubkey(path) {
|
|
11112
|
-
if (!
|
|
11478
|
+
if (!existsSync32(path)) {
|
|
11113
11479
|
const privateKeyPath = path.replace(/\.pub$/, "");
|
|
11114
11480
|
throw new Error(
|
|
11115
11481
|
`SSH pubkey not found at ${path}. Generate one with:
|
|
@@ -11117,10 +11483,10 @@ function readSshPubkey(path) {
|
|
|
11117
11483
|
or pass --ssh-key <path>.`
|
|
11118
11484
|
);
|
|
11119
11485
|
}
|
|
11120
|
-
return
|
|
11486
|
+
return readFileSync34(path, "utf-8").trim();
|
|
11121
11487
|
}
|
|
11122
11488
|
function readOutputs(provider) {
|
|
11123
|
-
const result =
|
|
11489
|
+
const result = spawnSync4("terraform", ["output", "-json", `-state=${deployStatePath(provider)}`], {
|
|
11124
11490
|
cwd: providerModuleDir(provider),
|
|
11125
11491
|
encoding: "utf-8",
|
|
11126
11492
|
env: EXEC_ENV
|
|
@@ -11144,7 +11510,7 @@ function readOutputs(provider) {
|
|
|
11144
11510
|
}
|
|
11145
11511
|
}
|
|
11146
11512
|
function isProvisioned(provider) {
|
|
11147
|
-
if (!
|
|
11513
|
+
if (!existsSync32(deployStatePath(provider))) return false;
|
|
11148
11514
|
return readOutputs(provider) !== null;
|
|
11149
11515
|
}
|
|
11150
11516
|
async function deployUp(provider, opts) {
|
|
@@ -11230,7 +11596,7 @@ Applied \u2014 but could not parse outputs. Run \`sis deploy ${provider} status\
|
|
|
11230
11596
|
console.log("");
|
|
11231
11597
|
}
|
|
11232
11598
|
async function deployDown(provider, opts) {
|
|
11233
|
-
if (!
|
|
11599
|
+
if (!existsSync32(deployStatePath(provider))) {
|
|
11234
11600
|
console.log(`No ${provider} state found at ${deployStatePath(provider)}. Nothing to destroy.`);
|
|
11235
11601
|
return;
|
|
11236
11602
|
}
|
|
@@ -11311,7 +11677,7 @@ function effectiveSshTarget(provider) {
|
|
|
11311
11677
|
}
|
|
11312
11678
|
function deploySsh(provider, remoteCmd) {
|
|
11313
11679
|
const target = effectiveSshTarget(provider);
|
|
11314
|
-
const moshAvailable =
|
|
11680
|
+
const moshAvailable = spawnSync4("mosh", ["--version"], { stdio: "pipe", env: EXEC_ENV }).status === 0;
|
|
11315
11681
|
const bin = moshAvailable && remoteCmd.length === 0 ? "mosh" : "ssh";
|
|
11316
11682
|
const args2 = remoteCmd.length > 0 ? [target, ...remoteCmd] : [target];
|
|
11317
11683
|
const child = spawn2(bin, args2, { stdio: "inherit", env: EXEC_ENV });
|
|
@@ -11427,10 +11793,10 @@ import { hostname } from "os";
|
|
|
11427
11793
|
|
|
11428
11794
|
// src/cli/deploy/ssh-exec.ts
|
|
11429
11795
|
init_exec();
|
|
11430
|
-
import { spawn as spawn3, spawnSync as
|
|
11796
|
+
import { spawn as spawn3, spawnSync as spawnSync5 } from "child_process";
|
|
11431
11797
|
function runOnBox(provider, cmd) {
|
|
11432
11798
|
const target = effectiveSshTarget(provider);
|
|
11433
|
-
const result =
|
|
11799
|
+
const result = spawnSync5("ssh", [target, cmd], {
|
|
11434
11800
|
encoding: "utf-8",
|
|
11435
11801
|
env: EXEC_ENV
|
|
11436
11802
|
});
|
|
@@ -11478,11 +11844,11 @@ function ensureGroveRegistered(provider, repo, instancePath) {
|
|
|
11478
11844
|
|
|
11479
11845
|
// src/cli/cloud/repo.ts
|
|
11480
11846
|
init_exec();
|
|
11481
|
-
import { spawnSync as
|
|
11482
|
-
import { existsSync as
|
|
11847
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
11848
|
+
import { existsSync as existsSync33 } from "fs";
|
|
11483
11849
|
import { basename as basename6, join as join30 } from "path";
|
|
11484
11850
|
function captureGit(args2, cwd) {
|
|
11485
|
-
const result =
|
|
11851
|
+
const result = spawnSync6("git", args2, {
|
|
11486
11852
|
encoding: "utf-8",
|
|
11487
11853
|
env: EXEC_ENV,
|
|
11488
11854
|
cwd: cwd ?? process.cwd()
|
|
@@ -11532,10 +11898,10 @@ function buildRsyncArgs(localDir, remoteTarget) {
|
|
|
11532
11898
|
];
|
|
11533
11899
|
}
|
|
11534
11900
|
function detectPackageManager(toplevel) {
|
|
11535
|
-
if (
|
|
11536
|
-
if (
|
|
11537
|
-
if (
|
|
11538
|
-
if (
|
|
11901
|
+
if (existsSync33(join30(toplevel, "pnpm-lock.yaml"))) return "pnpm";
|
|
11902
|
+
if (existsSync33(join30(toplevel, "bun.lockb"))) return "bun";
|
|
11903
|
+
if (existsSync33(join30(toplevel, "yarn.lock"))) return "yarn";
|
|
11904
|
+
if (existsSync33(join30(toplevel, "package-lock.json"))) return "npm";
|
|
11539
11905
|
return null;
|
|
11540
11906
|
}
|
|
11541
11907
|
function packageManagerInstallCmd(pm) {
|
|
@@ -11744,7 +12110,7 @@ function cloudStatus(provider, repo) {
|
|
|
11744
12110
|
|
|
11745
12111
|
// src/cli/cloud/handoff.ts
|
|
11746
12112
|
import { spawn as spawn5 } from "child_process";
|
|
11747
|
-
import { existsSync as
|
|
12113
|
+
import { existsSync as existsSync34, readFileSync as readFileSync35, writeFileSync as writeFileSync18 } from "fs";
|
|
11748
12114
|
init_exec();
|
|
11749
12115
|
init_paths();
|
|
11750
12116
|
init_shell();
|
|
@@ -11819,10 +12185,10 @@ async function waitForSentOrError(cwd, sessionId) {
|
|
|
11819
12185
|
const path = statePath(cwd, sessionId);
|
|
11820
12186
|
while (Date.now() - start < MAX_WAIT_MS) {
|
|
11821
12187
|
await sleep2(POLL_INTERVAL_MS);
|
|
11822
|
-
if (!
|
|
12188
|
+
if (!existsSync34(path)) continue;
|
|
11823
12189
|
let session2;
|
|
11824
12190
|
try {
|
|
11825
|
-
session2 = JSON.parse(
|
|
12191
|
+
session2 = JSON.parse(readFileSync35(path, "utf-8"));
|
|
11826
12192
|
} catch (err) {
|
|
11827
12193
|
void err;
|
|
11828
12194
|
continue;
|
|
@@ -11886,7 +12252,7 @@ async function cloudReclaim(sessionId, opts) {
|
|
|
11886
12252
|
await rsyncDown(target, remotePath, localPath, { withDelete: false });
|
|
11887
12253
|
}
|
|
11888
12254
|
const localStatePath = statePath(cwd, sessionId);
|
|
11889
|
-
const merged = JSON.parse(
|
|
12255
|
+
const merged = JSON.parse(readFileSync35(localStatePath, "utf-8"));
|
|
11890
12256
|
merged.cwd = cwd;
|
|
11891
12257
|
merged.handoff = local.handoff;
|
|
11892
12258
|
writeFileSync18(localStatePath, JSON.stringify(merged, null, 2));
|
|
@@ -11916,11 +12282,11 @@ async function cloudReclaim(sessionId, opts) {
|
|
|
11916
12282
|
}
|
|
11917
12283
|
function readLocalSession(cwd, sessionId) {
|
|
11918
12284
|
const path = statePath(cwd, sessionId);
|
|
11919
|
-
if (!
|
|
12285
|
+
if (!existsSync34(path)) {
|
|
11920
12286
|
console.error(`No local state.json for ${sessionId} at ${path}.`);
|
|
11921
12287
|
process.exit(1);
|
|
11922
12288
|
}
|
|
11923
|
-
return JSON.parse(
|
|
12289
|
+
return JSON.parse(readFileSync35(path, "utf-8"));
|
|
11924
12290
|
}
|
|
11925
12291
|
async function waitForBoxPaused(provider, remoteSessionDir) {
|
|
11926
12292
|
const POLL_INTERVAL_MS = 2e3;
|
|
@@ -12030,8 +12396,8 @@ function attachNotify(diagnostic2) {
|
|
|
12030
12396
|
|
|
12031
12397
|
// src/cli/commands/tmux-sessions.ts
|
|
12032
12398
|
init_paths();
|
|
12033
|
-
import { execSync as
|
|
12034
|
-
import { readFileSync as
|
|
12399
|
+
import { execSync as execSync19 } from "child_process";
|
|
12400
|
+
import { readFileSync as readFileSync36, existsSync as existsSync35 } from "fs";
|
|
12035
12401
|
var DOT_MAP = {
|
|
12036
12402
|
"orchestrator:processing": { icon: "\u25CF", color: "#d4ad6a" },
|
|
12037
12403
|
"orchestrator:idle": { icon: "\u25CF", color: "#d47766" },
|
|
@@ -12042,16 +12408,16 @@ var DOT_MAP = {
|
|
|
12042
12408
|
};
|
|
12043
12409
|
function readManifest() {
|
|
12044
12410
|
const p = sessionsManifestPath();
|
|
12045
|
-
if (!
|
|
12411
|
+
if (!existsSync35(p)) return null;
|
|
12046
12412
|
try {
|
|
12047
|
-
return JSON.parse(
|
|
12413
|
+
return JSON.parse(readFileSync36(p, "utf-8"));
|
|
12048
12414
|
} catch {
|
|
12049
12415
|
return null;
|
|
12050
12416
|
}
|
|
12051
12417
|
}
|
|
12052
12418
|
function tmuxExec(cmd) {
|
|
12053
12419
|
try {
|
|
12054
|
-
return
|
|
12420
|
+
return execSync19(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
12055
12421
|
} catch {
|
|
12056
12422
|
return null;
|
|
12057
12423
|
}
|
|
@@ -12087,10 +12453,23 @@ if (nodeVersion < 22) {
|
|
|
12087
12453
|
console.error(`Sisyphus requires Node.js v22+ (current: v${process.versions.node})`);
|
|
12088
12454
|
process.exit(1);
|
|
12089
12455
|
}
|
|
12456
|
+
if (process.platform === "win32") {
|
|
12457
|
+
console.error(`Sisyphus does not run on native Windows (PowerShell / cmd.exe).
|
|
12458
|
+
|
|
12459
|
+
It depends on tmux, bash, and POSIX sockets \u2014 please run it inside WSL2:
|
|
12460
|
+
|
|
12461
|
+
1. Install WSL2: https://learn.microsoft.com/windows/wsl/install
|
|
12462
|
+
2. Open your WSL distro (Ubuntu is a safe default).
|
|
12463
|
+
3. Install Node.js v22+ and Claude Code inside WSL.
|
|
12464
|
+
4. Re-run \`sis\` from the WSL shell, not PowerShell.
|
|
12465
|
+
|
|
12466
|
+
Tip: enable systemd in /etc/wsl.conf for the recommended daemon setup.`);
|
|
12467
|
+
process.exit(1);
|
|
12468
|
+
}
|
|
12090
12469
|
var program = new Command();
|
|
12091
12470
|
program.name("sis").description("tmux-integrated orchestration daemon for Claude Code").version(
|
|
12092
12471
|
JSON.parse(
|
|
12093
|
-
|
|
12472
|
+
readFileSync37(join32(dirname13(fileURLToPath5(import.meta.url)), "..", "package.json"), "utf-8")
|
|
12094
12473
|
).version
|
|
12095
12474
|
);
|
|
12096
12475
|
program.configureHelp({
|
|
@@ -12165,7 +12544,7 @@ Run 'sis admin getting-started' for a complete usage guide.
|
|
|
12165
12544
|
var args = process.argv.slice(2);
|
|
12166
12545
|
var firstArg = args[0];
|
|
12167
12546
|
var skipWelcome = ["admin", "help", "--help", "-h", "--version", "-V"];
|
|
12168
|
-
if (!
|
|
12547
|
+
if (!existsSync36(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
|
|
12169
12548
|
mkdirSync17(globalDir(), { recursive: true });
|
|
12170
12549
|
console.log("");
|
|
12171
12550
|
console.log(" Welcome to Sisyphus. Run 'sis admin setup' to get started.");
|