sisyphi 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-6G226ZK7.js +11 -0
- package/dist/chunk-6G226ZK7.js.map +1 -0
- package/dist/{chunk-YGBGKMTF.js → chunk-JXKUI4P6.js} +28 -1
- package/dist/{chunk-YGBGKMTF.js.map → chunk-JXKUI4P6.js.map} +1 -1
- package/dist/chunk-LWWRGQWM.js +87 -0
- package/dist/chunk-LWWRGQWM.js.map +1 -0
- package/dist/{chunk-DBR33QHM.js → chunk-T7ETTIQK.js} +81 -2
- package/dist/chunk-T7ETTIQK.js.map +1 -0
- package/dist/cli.js +241 -133
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +397 -349
- package/dist/daemon.js.map +1 -1
- package/dist/{paths-FYYSBD27.js → paths-NUUALUVP.js} +2 -2
- package/dist/templates/agent-plugin/hooks/plan-user-prompt.sh +19 -0
- package/dist/templates/agent-plugin/hooks/spec-user-prompt.sh +22 -0
- package/dist/templates/orchestrator-planning.md +8 -0
- package/dist/tui.js +850 -841
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/templates/agent-plugin/hooks/plan-user-prompt.sh +19 -0
- package/templates/agent-plugin/hooks/spec-user-prompt.sh +22 -0
- package/templates/orchestrator-planning.md +8 -0
- package/dist/chunk-DBR33QHM.js.map +0 -1
- package/dist/chunk-KQBSC5KY.js +0 -31
- package/dist/chunk-KQBSC5KY.js.map +0 -1
- /package/dist/{paths-FYYSBD27.js.map → paths-NUUALUVP.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildCompanionContext,
|
|
4
|
-
computeActiveTimeMs
|
|
5
|
-
|
|
4
|
+
computeActiveTimeMs,
|
|
5
|
+
formatDuration,
|
|
6
|
+
rawSend,
|
|
7
|
+
statusColor
|
|
8
|
+
} from "./chunk-T7ETTIQK.js";
|
|
9
|
+
import {
|
|
10
|
+
shellQuote
|
|
11
|
+
} from "./chunk-6G226ZK7.js";
|
|
6
12
|
import {
|
|
7
13
|
daemonLogPath,
|
|
8
14
|
daemonPidPath,
|
|
@@ -10,17 +16,15 @@ import {
|
|
|
10
16
|
globalDir,
|
|
11
17
|
roadmapPath,
|
|
12
18
|
socketPath
|
|
13
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-JXKUI4P6.js";
|
|
14
20
|
|
|
15
21
|
// src/cli/index.ts
|
|
16
22
|
import { Command } from "commander";
|
|
23
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4 } from "fs";
|
|
17
24
|
|
|
18
25
|
// src/cli/commands/start.ts
|
|
19
26
|
import { execSync as execSync5 } from "child_process";
|
|
20
27
|
|
|
21
|
-
// src/cli/client.ts
|
|
22
|
-
import { connect as connect2 } from "net";
|
|
23
|
-
|
|
24
28
|
// src/cli/install.ts
|
|
25
29
|
import { execSync as execSync2 } from "child_process";
|
|
26
30
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -304,37 +308,8 @@ async function waitForDaemon(maxWaitMs = 6e3) {
|
|
|
304
308
|
}
|
|
305
309
|
|
|
306
310
|
// src/cli/client.ts
|
|
307
|
-
function
|
|
308
|
-
|
|
309
|
-
return new Promise((resolve2, reject) => {
|
|
310
|
-
const socket = connect2(sock);
|
|
311
|
-
let data = "";
|
|
312
|
-
const timeout = setTimeout(() => {
|
|
313
|
-
socket.destroy();
|
|
314
|
-
reject(new Error("Request timed out after 10s"));
|
|
315
|
-
}, 1e4);
|
|
316
|
-
socket.on("connect", () => {
|
|
317
|
-
socket.write(JSON.stringify(request) + "\n");
|
|
318
|
-
});
|
|
319
|
-
socket.on("data", (chunk) => {
|
|
320
|
-
data += chunk.toString();
|
|
321
|
-
const newlineIdx = data.indexOf("\n");
|
|
322
|
-
if (newlineIdx !== -1) {
|
|
323
|
-
clearTimeout(timeout);
|
|
324
|
-
const line = data.slice(0, newlineIdx);
|
|
325
|
-
socket.destroy();
|
|
326
|
-
try {
|
|
327
|
-
resolve2(JSON.parse(line));
|
|
328
|
-
} catch {
|
|
329
|
-
reject(new Error(`Invalid JSON response from daemon: ${line}`));
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
socket.on("error", (err) => {
|
|
334
|
-
clearTimeout(timeout);
|
|
335
|
-
reject(err);
|
|
336
|
-
});
|
|
337
|
-
});
|
|
311
|
+
function rawSend2(request) {
|
|
312
|
+
return rawSend(request, 1e4);
|
|
338
313
|
}
|
|
339
314
|
async function sendRequest(request) {
|
|
340
315
|
const sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
@@ -344,7 +319,7 @@ async function sendRequest(request) {
|
|
|
344
319
|
let lastErr;
|
|
345
320
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
346
321
|
try {
|
|
347
|
-
return await
|
|
322
|
+
return await rawSend2(request);
|
|
348
323
|
} catch (err) {
|
|
349
324
|
lastErr = err;
|
|
350
325
|
const code = err.code;
|
|
@@ -364,17 +339,51 @@ async function sendRequest(request) {
|
|
|
364
339
|
}
|
|
365
340
|
}
|
|
366
341
|
if (process.platform !== "darwin") {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
342
|
+
const lines = [`Sisyphus daemon is not running.`];
|
|
343
|
+
if (process.platform === "linux") {
|
|
344
|
+
lines.push(
|
|
345
|
+
"",
|
|
346
|
+
" Start options:",
|
|
347
|
+
" sisyphusd & # Run in background",
|
|
348
|
+
" nohup sisyphusd > ~/.sisyphus/daemon.log 2>&1 & # Persist after logout",
|
|
349
|
+
"",
|
|
350
|
+
" For systemd (recommended):",
|
|
351
|
+
" # Create ~/.config/systemd/user/sisyphus.service with:",
|
|
352
|
+
" # [Unit]",
|
|
353
|
+
" # Description=Sisyphus Daemon",
|
|
354
|
+
" # [Service]",
|
|
355
|
+
" # ExecStart=/usr/bin/env node <path-to-sisyphusd>",
|
|
356
|
+
" # Restart=always",
|
|
357
|
+
" # [Install]",
|
|
358
|
+
" # WantedBy=default.target",
|
|
359
|
+
" systemctl --user enable --now sisyphus"
|
|
360
|
+
);
|
|
361
|
+
} else {
|
|
362
|
+
lines.push(
|
|
363
|
+
"",
|
|
364
|
+
" Start it manually: sisyphusd &"
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
lines.push(
|
|
368
|
+
"",
|
|
369
|
+
" Diagnose: sisyphus doctor",
|
|
370
|
+
" Logs: tail -f ~/.sisyphus/daemon.log"
|
|
371
371
|
);
|
|
372
|
+
throw new Error(lines.join("\n"));
|
|
372
373
|
}
|
|
373
374
|
throw lastErr;
|
|
374
375
|
}
|
|
375
376
|
|
|
376
377
|
// src/cli/tmux.ts
|
|
377
378
|
import { execSync as execSync3 } from "child_process";
|
|
379
|
+
function isTmuxInstalled() {
|
|
380
|
+
try {
|
|
381
|
+
execSync3("which tmux", { stdio: "pipe" });
|
|
382
|
+
return true;
|
|
383
|
+
} catch {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
378
387
|
function assertTmux() {
|
|
379
388
|
if (!process.env.TMUX) {
|
|
380
389
|
throw new Error("Not running inside a tmux pane. Sisyphus requires tmux.");
|
|
@@ -388,21 +397,21 @@ function getTmuxSession() {
|
|
|
388
397
|
// src/cli/commands/dashboard.ts
|
|
389
398
|
import { join as join3 } from "path";
|
|
390
399
|
import { execSync as execSync4 } from "child_process";
|
|
391
|
-
function
|
|
392
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
393
|
-
}
|
|
394
|
-
function isDashboardOpen(tmuxSession) {
|
|
400
|
+
function ensureDashboard(tmuxSession, cwd) {
|
|
395
401
|
try {
|
|
396
402
|
const windows = execSync4(
|
|
397
403
|
`tmux list-windows -t ${shellQuote(tmuxSession)} -F "#{window_name}"`,
|
|
398
404
|
{ encoding: "utf-8" }
|
|
399
405
|
);
|
|
400
|
-
|
|
406
|
+
const isOpen = windows.split("\n").some((name) => name.trim() === "sisyphus-dashboard");
|
|
407
|
+
if (isOpen) {
|
|
408
|
+
execSync4(
|
|
409
|
+
`tmux select-window -t ${shellQuote(tmuxSession)}:sisyphus-dashboard`
|
|
410
|
+
);
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
401
413
|
} catch {
|
|
402
|
-
return false;
|
|
403
414
|
}
|
|
404
|
-
}
|
|
405
|
-
function launchDashboard(tmuxSession, cwd) {
|
|
406
415
|
const tuiPath = join3(import.meta.dirname, "tui.js");
|
|
407
416
|
const windowId = execSync4(
|
|
408
417
|
`tmux new-window -n "sisyphus-dashboard" -c ${shellQuote(cwd)} -P -F "#{window_id}"`,
|
|
@@ -412,36 +421,33 @@ function launchDashboard(tmuxSession, cwd) {
|
|
|
412
421
|
execSync4(
|
|
413
422
|
`tmux send-keys -t ${shellQuote(windowId)} ${shellQuote(cmd)} Enter`
|
|
414
423
|
);
|
|
424
|
+
return true;
|
|
415
425
|
}
|
|
416
426
|
function registerDashboard(program2) {
|
|
417
427
|
program2.command("dashboard").description("Launch the TUI dashboard for monitoring and managing sessions").action(async () => {
|
|
418
428
|
assertTmux();
|
|
419
429
|
const tmuxSession = getTmuxSession();
|
|
420
|
-
|
|
421
|
-
launchDashboard(tmuxSession, cwd);
|
|
430
|
+
ensureDashboard(tmuxSession, process.cwd());
|
|
422
431
|
});
|
|
423
432
|
}
|
|
424
433
|
|
|
425
434
|
// src/cli/commands/start.ts
|
|
426
|
-
function shellQuote2(s) {
|
|
427
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
428
|
-
}
|
|
429
|
-
function isTmuxInstalled() {
|
|
430
|
-
try {
|
|
431
|
-
execSync5("which tmux", { stdio: "pipe" });
|
|
432
|
-
return true;
|
|
433
|
-
} catch {
|
|
434
|
-
return false;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
435
|
function registerStart(program2) {
|
|
438
|
-
program2.command("start").description("Start a new sisyphus session").argument("<task>", "Task description for the orchestrator").option("-c, --context <context>", "Background context for the orchestrator").option("-n, --name <name>", "Human-readable name for the session").action(async (task, opts) => {
|
|
436
|
+
program2.command("start").description("Start a new sisyphus session").argument("<task>", "Task description for the orchestrator").option("-c, --context <context>", "Background context for the orchestrator").option("-n, --name <name>", "Human-readable name for the session").option("--no-tmux-check", "Skip the tmux session check").action(async (task, opts) => {
|
|
439
437
|
const cwd = process.env["SISYPHUS_CWD"] ?? process.cwd();
|
|
440
|
-
if (!process.env["TMUX"] &&
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
438
|
+
if (!process.env["TMUX"] && opts.tmuxCheck !== false) {
|
|
439
|
+
if (!isTmuxInstalled()) {
|
|
440
|
+
console.error("Error: tmux is not installed. Sisyphus requires tmux for agent panes.");
|
|
441
|
+
console.error(" Install: brew install tmux (macOS) or apt install tmux (Linux)");
|
|
442
|
+
console.error(" Then: tmux new-session");
|
|
443
|
+
process.exit(1);
|
|
444
|
+
}
|
|
445
|
+
console.error("Error: Not running inside a tmux session.");
|
|
446
|
+
console.error(" Sisyphus uses tmux to manage agent panes.");
|
|
447
|
+
console.error(" Start a tmux session first: tmux new-session");
|
|
448
|
+
console.error("");
|
|
449
|
+
console.error(' To skip this check: sisyphus start --no-tmux-check "task"');
|
|
450
|
+
process.exit(1);
|
|
445
451
|
}
|
|
446
452
|
const request = { type: "start", task, context: opts.context, cwd, name: opts.name };
|
|
447
453
|
const response = await sendRequest(request);
|
|
@@ -450,13 +456,12 @@ function registerStart(program2) {
|
|
|
450
456
|
const tmuxSessionName = response.data?.tmuxSessionName;
|
|
451
457
|
if (process.env["TMUX"]) {
|
|
452
458
|
try {
|
|
453
|
-
execSync5(`tmux set-option @sisyphus_cwd ${
|
|
459
|
+
execSync5(`tmux set-option @sisyphus_cwd ${shellQuote(cwd)}`, { stdio: "ignore" });
|
|
454
460
|
} catch {
|
|
455
461
|
}
|
|
456
462
|
try {
|
|
457
463
|
const tmuxSession = getTmuxSession();
|
|
458
|
-
if (
|
|
459
|
-
launchDashboard(tmuxSession, cwd);
|
|
464
|
+
if (ensureDashboard(tmuxSession, cwd)) {
|
|
460
465
|
console.log(`Dashboard opened in tmux window "sisyphus-dashboard"`);
|
|
461
466
|
}
|
|
462
467
|
} catch {
|
|
@@ -636,7 +641,7 @@ Follow up:`);
|
|
|
636
641
|
|
|
637
642
|
// src/cli/commands/continue.ts
|
|
638
643
|
function registerContinue(program2) {
|
|
639
|
-
program2.command("continue").description("
|
|
644
|
+
program2.command("continue").description("Clear roadmap and continue working on a completed session (stays in current cycle)").addHelpText("after", "\n Use `continue` when a session completed but you want to add more work.\n Use `resume` when you want to restart with specific new instructions.\n").action(async () => {
|
|
640
645
|
assertTmux();
|
|
641
646
|
const sessionId = process.env.SISYPHUS_SESSION_ID;
|
|
642
647
|
if (!sessionId) {
|
|
@@ -656,47 +661,22 @@ function registerContinue(program2) {
|
|
|
656
661
|
|
|
657
662
|
// src/cli/commands/status.ts
|
|
658
663
|
import { readFileSync as readFileSync3 } from "fs";
|
|
659
|
-
var
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
running: "\x1B[32m",
|
|
667
|
-
// green
|
|
668
|
-
killed: "\x1B[31m",
|
|
669
|
-
// red
|
|
670
|
-
crashed: "\x1B[31m",
|
|
671
|
-
// red
|
|
672
|
-
lost: "\x1B[90m"
|
|
673
|
-
// gray
|
|
664
|
+
var COLOR_CODES = {
|
|
665
|
+
green: "\x1B[32m",
|
|
666
|
+
yellow: "\x1B[33m",
|
|
667
|
+
cyan: "\x1B[36m",
|
|
668
|
+
red: "\x1B[31m",
|
|
669
|
+
gray: "\x1B[90m",
|
|
670
|
+
white: "\x1B[37m"
|
|
674
671
|
};
|
|
675
672
|
var RESET = "\x1B[0m";
|
|
676
673
|
var BOLD = "\x1B[1m";
|
|
677
674
|
var DIM = "\x1B[2m";
|
|
678
675
|
function colorize(text, status) {
|
|
679
|
-
const
|
|
680
|
-
|
|
681
|
-
return `${
|
|
682
|
-
}
|
|
683
|
-
function formatMs(ms) {
|
|
684
|
-
const totalSeconds = Math.floor(ms / 1e3);
|
|
685
|
-
if (totalSeconds < 0) return "0s";
|
|
686
|
-
const hours = Math.floor(totalSeconds / 3600);
|
|
687
|
-
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
688
|
-
const seconds = totalSeconds % 60;
|
|
689
|
-
const parts = [];
|
|
690
|
-
if (hours > 0) parts.push(`${hours}h`);
|
|
691
|
-
if (minutes > 0) parts.push(`${minutes}m`);
|
|
692
|
-
parts.push(`${seconds}s`);
|
|
693
|
-
return parts.join(" ");
|
|
694
|
-
}
|
|
695
|
-
function formatDuration(startOrMs, endIso) {
|
|
696
|
-
if (typeof startOrMs === "number") return formatMs(startOrMs);
|
|
697
|
-
const start = new Date(startOrMs).getTime();
|
|
698
|
-
const end = endIso ? new Date(endIso).getTime() : Date.now();
|
|
699
|
-
return formatMs(end - start);
|
|
676
|
+
const colorName = statusColor(status);
|
|
677
|
+
const code = COLOR_CODES[colorName];
|
|
678
|
+
if (!code) return `${text}\x1B[0m`;
|
|
679
|
+
return `${code}${text}\x1B[0m`;
|
|
700
680
|
}
|
|
701
681
|
function inferOrchestratorPhase(session) {
|
|
702
682
|
const cycles = session.orchestratorCycles;
|
|
@@ -788,7 +768,7 @@ ${BOLD}Session: ${session.id}${RESET}`);
|
|
|
788
768
|
console.log(` Duration: ${sessionDuration}${session.completedAt ? "" : " (ongoing)"} (${activeTime} active)`);
|
|
789
769
|
const lastActivity = computeLastActivity(session);
|
|
790
770
|
if (lastActivity) {
|
|
791
|
-
console.log(` Last activity: ${
|
|
771
|
+
console.log(` Last activity: ${formatDuration(Date.now() - lastActivity.getTime())} ago`);
|
|
792
772
|
}
|
|
793
773
|
console.log(` Orchestrator cycles: ${session.orchestratorCycles.length}`);
|
|
794
774
|
const runningAgents = session.agents.filter((a) => a.status === "running");
|
|
@@ -849,7 +829,7 @@ function registerStatus(program2) {
|
|
|
849
829
|
|
|
850
830
|
// src/cli/commands/list.ts
|
|
851
831
|
import { basename } from "path";
|
|
852
|
-
var
|
|
832
|
+
var STATUS_COLORS = {
|
|
853
833
|
active: "\x1B[32m",
|
|
854
834
|
paused: "\x1B[33m",
|
|
855
835
|
completed: "\x1B[36m"
|
|
@@ -880,7 +860,7 @@ function registerList(program2) {
|
|
|
880
860
|
return;
|
|
881
861
|
}
|
|
882
862
|
for (const s of sessions) {
|
|
883
|
-
const color =
|
|
863
|
+
const color = STATUS_COLORS[s.status] ?? "";
|
|
884
864
|
const status = `${color}${s.status}${RESET2}`;
|
|
885
865
|
const agents = `${DIM2}${s.agentCount} agent(s)${RESET2}`;
|
|
886
866
|
const task = truncateTask(s.task, 60);
|
|
@@ -927,7 +907,7 @@ function registerReport(program2) {
|
|
|
927
907
|
|
|
928
908
|
// src/cli/commands/resume.ts
|
|
929
909
|
function registerResume(program2) {
|
|
930
|
-
program2.command("resume").description("
|
|
910
|
+
program2.command("resume").description("Respawn orchestrator with new instructions (for paused/completed sessions)").addHelpText("after", "\n Use `resume` to restart a paused or completed session with new instructions.\n Use `continue` to keep working on a completed session without new instructions.\n").argument("<session-id>", "Session ID to resume").argument("[message]", "Additional instructions for the orchestrator").action(async (sessionId, message) => {
|
|
931
911
|
const cwd = process.cwd();
|
|
932
912
|
const request = { type: "resume", sessionId, cwd, message };
|
|
933
913
|
const response = await sendRequest(request);
|
|
@@ -994,7 +974,7 @@ function registerNotify(program2) {
|
|
|
994
974
|
notify.command("pane-exited").description("Notify daemon that a tmux pane exited").requiredOption("--pane-id <paneId>", "Pane ID that exited").action(async (opts) => {
|
|
995
975
|
try {
|
|
996
976
|
const request = { type: "pane-exited", paneId: opts.paneId };
|
|
997
|
-
await
|
|
977
|
+
await rawSend2(request);
|
|
998
978
|
} catch {
|
|
999
979
|
}
|
|
1000
980
|
});
|
|
@@ -1107,25 +1087,81 @@ function registerSetupKeybind(program2) {
|
|
|
1107
1087
|
// src/cli/commands/doctor.ts
|
|
1108
1088
|
import { execSync as execSync7 } from "child_process";
|
|
1109
1089
|
import { existsSync as existsSync3, statSync } from "fs";
|
|
1090
|
+
function checkNodeVersion() {
|
|
1091
|
+
const major = parseInt(process.versions.node.split(".")[0], 10);
|
|
1092
|
+
if (major < 22) {
|
|
1093
|
+
return { name: "Node.js", status: "fail", detail: `v${process.versions.node} (v22+ required)`, fix: "Install Node.js 22+: https://nodejs.org" };
|
|
1094
|
+
}
|
|
1095
|
+
return { name: "Node.js", status: "ok", detail: `v${process.versions.node}` };
|
|
1096
|
+
}
|
|
1097
|
+
function checkClaudeCli() {
|
|
1098
|
+
try {
|
|
1099
|
+
execSync7("which claude", { stdio: "pipe" });
|
|
1100
|
+
return { name: "Claude CLI", status: "ok", detail: "Found on PATH" };
|
|
1101
|
+
} catch {
|
|
1102
|
+
return {
|
|
1103
|
+
name: "Claude CLI",
|
|
1104
|
+
status: "fail",
|
|
1105
|
+
detail: "Not found on PATH",
|
|
1106
|
+
fix: "Install Claude Code: https://docs.anthropic.com/en/docs/claude-code/overview"
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
function checkGit() {
|
|
1111
|
+
try {
|
|
1112
|
+
const version = execSync7("git --version", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
1113
|
+
return { name: "git", status: "ok", detail: version };
|
|
1114
|
+
} catch {
|
|
1115
|
+
return { name: "git", status: "fail", detail: "Not found on PATH", fix: "Install git: https://git-scm.com/downloads" };
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
function checkTmuxVersion() {
|
|
1119
|
+
try {
|
|
1120
|
+
const version = execSync7("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
1121
|
+
const match = version.match(/(\d+\.\d+)/);
|
|
1122
|
+
if (!match) return { name: "tmux version", status: "warn", detail: `Could not parse version: ${version}` };
|
|
1123
|
+
const ver = parseFloat(match[1]);
|
|
1124
|
+
if (ver < 3.2) {
|
|
1125
|
+
const upgradeHint = process.platform === "darwin" ? "brew install tmux (or upgrade)" : "apt install tmux (Debian/Ubuntu) or your package manager";
|
|
1126
|
+
return { name: "tmux version", status: "warn", detail: `${version} (3.2+ recommended for popup support)`, fix: upgradeHint };
|
|
1127
|
+
}
|
|
1128
|
+
return { name: "tmux version", status: "ok", detail: version };
|
|
1129
|
+
} catch {
|
|
1130
|
+
return { name: "tmux version", status: "warn", detail: "Could not determine version" };
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1110
1133
|
function checkDaemonInstalled() {
|
|
1111
|
-
if (
|
|
1112
|
-
|
|
1134
|
+
if (process.platform === "darwin") {
|
|
1135
|
+
if (isInstalled()) {
|
|
1136
|
+
return { name: "Daemon plist", status: "ok", detail: "Installed in LaunchAgents" };
|
|
1137
|
+
}
|
|
1138
|
+
return {
|
|
1139
|
+
name: "Daemon plist",
|
|
1140
|
+
status: "fail",
|
|
1141
|
+
detail: "Not installed",
|
|
1142
|
+
fix: 'Run any sisyphus command to auto-install, or: sisyphus start "test"'
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
const pid = daemonPidPath();
|
|
1146
|
+
if (existsSync3(pid)) {
|
|
1147
|
+
return { name: "Daemon setup", status: "ok", detail: `PID file found at ${pid}` };
|
|
1113
1148
|
}
|
|
1114
1149
|
return {
|
|
1115
|
-
name: "Daemon
|
|
1150
|
+
name: "Daemon setup",
|
|
1116
1151
|
status: "fail",
|
|
1117
|
-
detail: "
|
|
1118
|
-
fix:
|
|
1152
|
+
detail: "Daemon not running (no PID file)",
|
|
1153
|
+
fix: "Start manually: sisyphusd & \u2014 or configure via systemd"
|
|
1119
1154
|
};
|
|
1120
1155
|
}
|
|
1121
1156
|
function checkDaemonRunning() {
|
|
1122
1157
|
const pid = daemonPidPath();
|
|
1123
1158
|
if (!existsSync3(pid)) {
|
|
1159
|
+
const fix = process.platform === "darwin" ? "launchctl load -w ~/Library/LaunchAgents/com.sisyphus.daemon.plist" : "sisyphusd & \u2014 or check if the process is running";
|
|
1124
1160
|
return {
|
|
1125
1161
|
name: "Daemon process",
|
|
1126
1162
|
status: "fail",
|
|
1127
1163
|
detail: "No PID file found",
|
|
1128
|
-
fix
|
|
1164
|
+
fix
|
|
1129
1165
|
};
|
|
1130
1166
|
}
|
|
1131
1167
|
try {
|
|
@@ -1145,7 +1181,8 @@ function checkTmux() {
|
|
|
1145
1181
|
try {
|
|
1146
1182
|
execSync7("which tmux", { stdio: "pipe" });
|
|
1147
1183
|
} catch {
|
|
1148
|
-
|
|
1184
|
+
const installHint = process.platform === "darwin" ? "brew install tmux" : "apt install tmux (Debian/Ubuntu) or your package manager";
|
|
1185
|
+
return { name: "tmux", status: "fail", detail: "Not found on PATH", fix: installHint };
|
|
1149
1186
|
}
|
|
1150
1187
|
try {
|
|
1151
1188
|
execSync7("tmux list-sessions", { stdio: "pipe" });
|
|
@@ -1216,10 +1253,14 @@ var SYMBOLS = { ok: "\u2713", warn: "!", fail: "\u2717" };
|
|
|
1216
1253
|
function registerDoctor(program2) {
|
|
1217
1254
|
program2.command("doctor").description("Check sisyphus installation health").action(async () => {
|
|
1218
1255
|
const checks = [
|
|
1256
|
+
checkNodeVersion(),
|
|
1257
|
+
checkClaudeCli(),
|
|
1258
|
+
checkGit(),
|
|
1259
|
+
checkTmux(),
|
|
1260
|
+
checkTmuxVersion(),
|
|
1219
1261
|
checkGlobalDir(),
|
|
1220
1262
|
checkDaemonInstalled(),
|
|
1221
1263
|
checkDaemonRunning(),
|
|
1222
|
-
checkTmux(),
|
|
1223
1264
|
checkCycleScript(),
|
|
1224
1265
|
checkTmuxKeybind()
|
|
1225
1266
|
];
|
|
@@ -1229,7 +1270,7 @@ function registerDoctor(program2) {
|
|
|
1229
1270
|
console.log(` ${sym} ${c.name}: ${c.detail}`);
|
|
1230
1271
|
if (c.status !== "ok") hasIssues = true;
|
|
1231
1272
|
}
|
|
1232
|
-
const fixable = checks.filter((c) => c.fix);
|
|
1273
|
+
const fixable = checks.filter((c) => c.fix && c.status !== "ok");
|
|
1233
1274
|
if (fixable.length > 0) {
|
|
1234
1275
|
console.log("\nFixes:");
|
|
1235
1276
|
for (const c of fixable) {
|
|
@@ -1251,18 +1292,9 @@ function registerCompanionContext(program2) {
|
|
|
1251
1292
|
}
|
|
1252
1293
|
|
|
1253
1294
|
// src/cli/commands/getting-started.ts
|
|
1254
|
-
import { execSync as execSync8 } from "child_process";
|
|
1255
|
-
function isTmuxInstalled2() {
|
|
1256
|
-
try {
|
|
1257
|
-
execSync8("which tmux", { stdio: "pipe" });
|
|
1258
|
-
return true;
|
|
1259
|
-
} catch {
|
|
1260
|
-
return false;
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
1295
|
function registerGettingStarted(program2) {
|
|
1264
1296
|
program2.command("getting-started").description("Show a complete guide to using sisyphus effectively").action(() => {
|
|
1265
|
-
const hasTmux =
|
|
1297
|
+
const hasTmux = isTmuxInstalled();
|
|
1266
1298
|
const inTmux = !!process.env["TMUX"];
|
|
1267
1299
|
const lines = [
|
|
1268
1300
|
"",
|
|
@@ -1374,15 +1406,67 @@ function registerGettingStarted(program2) {
|
|
|
1374
1406
|
" Health:",
|
|
1375
1407
|
" sisyphus doctor Check installation health",
|
|
1376
1408
|
" tail -f ~/.sisyphus/daemon.log Watch daemon logs",
|
|
1409
|
+
"",
|
|
1410
|
+
" \u2500\u2500\u2500 Next Steps \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1411
|
+
"",
|
|
1412
|
+
" 1. Run `sisyphus doctor` to check your setup",
|
|
1413
|
+
" 2. Start a tmux session: `tmux new-session`",
|
|
1414
|
+
' 3. Try it: `sisyphus start "your task description"`',
|
|
1377
1415
|
""
|
|
1378
1416
|
);
|
|
1379
1417
|
console.log(lines.join("\n"));
|
|
1380
1418
|
});
|
|
1381
1419
|
}
|
|
1382
1420
|
|
|
1421
|
+
// src/cli/commands/init.ts
|
|
1422
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
1423
|
+
import { join as join4 } from "path";
|
|
1424
|
+
var DEFAULT_CONFIG = {};
|
|
1425
|
+
var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
|
|
1426
|
+
|
|
1427
|
+
<!-- This file overrides the default orchestrator system prompt. -->
|
|
1428
|
+
<!-- Delete this file to use the built-in prompt. -->
|
|
1429
|
+
<!-- See: https://github.com/silasrhyneer/sisyphi for details. -->
|
|
1430
|
+
`;
|
|
1431
|
+
function registerInit(program2) {
|
|
1432
|
+
program2.command("init").description("Initialize sisyphus configuration for this project").option("--orchestrator", "Also create a custom orchestrator prompt template").action((opts) => {
|
|
1433
|
+
const cwd = process.cwd();
|
|
1434
|
+
const sisDir = join4(cwd, ".sisyphus");
|
|
1435
|
+
const configPath = join4(sisDir, "config.json");
|
|
1436
|
+
if (existsSync4(configPath)) {
|
|
1437
|
+
console.log(`Already initialized: ${configPath}`);
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
mkdirSync3(sisDir, { recursive: true });
|
|
1441
|
+
writeFileSync3(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n", "utf-8");
|
|
1442
|
+
console.log(`Created ${configPath}`);
|
|
1443
|
+
if (opts.orchestrator) {
|
|
1444
|
+
const orchPath = join4(sisDir, "orchestrator.md");
|
|
1445
|
+
if (!existsSync4(orchPath)) {
|
|
1446
|
+
writeFileSync3(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
|
|
1447
|
+
console.log(`Created ${orchPath}`);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
console.log("");
|
|
1451
|
+
console.log("Configuration options (add to .sisyphus/config.json):");
|
|
1452
|
+
console.log(' orchestratorEffort \u2014 "low" | "medium" | "high" | "max" (default: "high")');
|
|
1453
|
+
console.log(' agentEffort \u2014 "low" | "medium" | "high" | "max" (default: "medium")');
|
|
1454
|
+
console.log(" pollIntervalMs \u2014 Daemon poll interval in ms (default: 5000)");
|
|
1455
|
+
console.log(" autoUpdate \u2014 Auto-update daemon on restart (default: true)");
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1383
1459
|
// src/cli/index.ts
|
|
1460
|
+
var nodeVersion = parseInt(process.versions.node.split(".")[0], 10);
|
|
1461
|
+
if (nodeVersion < 22) {
|
|
1462
|
+
console.error(`Sisyphus requires Node.js v22+ (current: v${process.versions.node})`);
|
|
1463
|
+
process.exit(1);
|
|
1464
|
+
}
|
|
1384
1465
|
var program = new Command();
|
|
1385
1466
|
program.name("sisyphus").description("tmux-integrated orchestration daemon for Claude Code").version("0.1.0");
|
|
1467
|
+
program.configureHelp({
|
|
1468
|
+
sortSubcommands: false
|
|
1469
|
+
});
|
|
1386
1470
|
registerStart(program);
|
|
1387
1471
|
registerSpawn(program);
|
|
1388
1472
|
registerSubmit(program);
|
|
@@ -1405,6 +1489,30 @@ registerSetupKeybind(program);
|
|
|
1405
1489
|
registerDoctor(program);
|
|
1406
1490
|
registerCompanionContext(program);
|
|
1407
1491
|
registerGettingStarted(program);
|
|
1492
|
+
registerInit(program);
|
|
1493
|
+
program.addHelpText("after", `
|
|
1494
|
+
Examples:
|
|
1495
|
+
$ sisyphus start "Implement auth system" Start a new session
|
|
1496
|
+
$ sisyphus start "Build @spec.md" -n auth Start with a name and spec reference
|
|
1497
|
+
$ sisyphus status Check current sessions
|
|
1498
|
+
$ sisyphus dashboard Open the TUI
|
|
1499
|
+
$ sisyphus doctor Verify installation
|
|
1500
|
+
|
|
1501
|
+
Run 'sisyphus getting-started' for a complete usage guide.
|
|
1502
|
+
`);
|
|
1503
|
+
var args = process.argv.slice(2);
|
|
1504
|
+
var firstArg = args[0];
|
|
1505
|
+
var skipWelcome = ["doctor", "getting-started", "help", "--help", "-h", "init", "uninstall", "--version", "-V"];
|
|
1506
|
+
if (!existsSync5(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
|
|
1507
|
+
mkdirSync4(globalDir(), { recursive: true });
|
|
1508
|
+
console.log("");
|
|
1509
|
+
console.log(" Welcome to Sisyphus \u2014 multi-agent orchestration for Claude Code.");
|
|
1510
|
+
console.log("");
|
|
1511
|
+
console.log(" First time? Run these commands:");
|
|
1512
|
+
console.log(" sisyphus doctor Check your setup");
|
|
1513
|
+
console.log(" sisyphus getting-started Learn the basics");
|
|
1514
|
+
console.log("");
|
|
1515
|
+
}
|
|
1408
1516
|
program.parseAsync(process.argv).catch((err) => {
|
|
1409
1517
|
console.error(err.message);
|
|
1410
1518
|
process.exit(1);
|