sisyphi 1.1.7 → 1.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1493 -215
- package/dist/cli.js.map +1 -1
- package/dist/templates/begin.md +22 -0
- package/dist/templates/nvim-tutorial.txt +68 -0
- package/dist/templates/tutorial-demo/CLAUDE.md +14 -0
- package/dist/templates/tutorial-demo/package.json +10 -0
- package/dist/templates/tutorial-demo/server.js +76 -0
- package/dist/templates/tutorial-demo/test.js +48 -0
- package/dist/templates/tutorial-demo/todo.js +35 -0
- package/package.json +1 -1
- package/templates/begin.md +22 -0
- package/templates/nvim-tutorial.txt +68 -0
- package/templates/tutorial-demo/CLAUDE.md +14 -0
- package/templates/tutorial-demo/package.json +10 -0
- package/templates/tutorial-demo/server.js +76 -0
- package/templates/tutorial-demo/test.js +48 -0
- package/templates/tutorial-demo/todo.js +35 -0
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
shellQuote
|
|
11
11
|
} from "./chunk-6G226ZK7.js";
|
|
12
12
|
import {
|
|
13
|
+
cycleLogPath,
|
|
13
14
|
daemonLogPath,
|
|
14
15
|
daemonPidPath,
|
|
15
16
|
daemonUpdatingPath,
|
|
@@ -20,9 +21,9 @@ import {
|
|
|
20
21
|
|
|
21
22
|
// src/cli/index.ts
|
|
22
23
|
import { Command } from "commander";
|
|
23
|
-
import { existsSync as
|
|
24
|
-
import { dirname as
|
|
25
|
-
import { fileURLToPath as
|
|
24
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync5 } from "fs";
|
|
25
|
+
import { dirname as dirname4, join as join8 } from "path";
|
|
26
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
26
27
|
|
|
27
28
|
// src/cli/commands/start.ts
|
|
28
29
|
import { execSync as execSync5 } from "child_process";
|
|
@@ -91,8 +92,9 @@ while IFS= read -r name; do
|
|
|
91
92
|
scwd=$(tmux show-option -t "$name" -v @sisyphus_cwd 2>/dev/null)
|
|
92
93
|
if [ "$scwd" = "$cwd" ]; then
|
|
93
94
|
tmux switch-client -t "$name"
|
|
94
|
-
# Focus the dashboard window
|
|
95
|
-
tmux
|
|
95
|
+
# Focus the dashboard window by stored ID (not name)
|
|
96
|
+
dwid=$(tmux show-option -t "$name" -v @sisyphus_dashboard 2>/dev/null)
|
|
97
|
+
[ -n "$dwid" ] && tmux select-window -t "$dwid" 2>/dev/null
|
|
96
98
|
exit 0
|
|
97
99
|
fi
|
|
98
100
|
done < <(tmux list-sessions -F '#{session_name}')
|
|
@@ -112,7 +114,8 @@ if [ "$pane_count" -le 1 ]; then
|
|
|
112
114
|
scwd=$(tmux show-option -t "$name" -v @sisyphus_cwd 2>/dev/null)
|
|
113
115
|
if [ "$scwd" = "$cwd" ]; then
|
|
114
116
|
tmux switch-client -t "$name"
|
|
115
|
-
tmux
|
|
117
|
+
dwid=$(tmux show-option -t "$name" -v @sisyphus_dashboard 2>/dev/null)
|
|
118
|
+
[ -n "$dwid" ] && tmux select-window -t "$dwid" 2>/dev/null
|
|
116
119
|
tmux kill-session -t "$session"
|
|
117
120
|
exit 0
|
|
118
121
|
fi
|
|
@@ -486,18 +489,22 @@ function getTmuxSession() {
|
|
|
486
489
|
// src/cli/commands/dashboard.ts
|
|
487
490
|
import { join as join3 } from "path";
|
|
488
491
|
import { execSync as execSync4 } from "child_process";
|
|
489
|
-
function
|
|
492
|
+
function openDashboardWindow(tmuxSession, cwd) {
|
|
490
493
|
try {
|
|
491
|
-
const
|
|
492
|
-
`tmux
|
|
493
|
-
{ encoding: "utf-8" }
|
|
494
|
-
);
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
494
|
+
const storedId = execSync4(
|
|
495
|
+
`tmux show-option -t ${shellQuote(tmuxSession)} -v @sisyphus_dashboard`,
|
|
496
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
497
|
+
).trim();
|
|
498
|
+
if (storedId) {
|
|
499
|
+
try {
|
|
500
|
+
execSync4(
|
|
501
|
+
`tmux display-message -t ${shellQuote(storedId)} -p "#{window_id}"`,
|
|
502
|
+
{ stdio: "pipe" }
|
|
503
|
+
);
|
|
504
|
+
execSync4(`tmux select-window -t ${shellQuote(storedId)}`, { stdio: "pipe" });
|
|
505
|
+
return false;
|
|
506
|
+
} catch {
|
|
507
|
+
}
|
|
501
508
|
}
|
|
502
509
|
} catch {
|
|
503
510
|
}
|
|
@@ -506,17 +513,23 @@ function ensureDashboard(tmuxSession, cwd) {
|
|
|
506
513
|
`tmux new-window -n "sisyphus-dashboard" -c ${shellQuote(cwd)} -P -F "#{window_id}"`,
|
|
507
514
|
{ encoding: "utf-8" }
|
|
508
515
|
).trim();
|
|
509
|
-
const cmd = `node ${shellQuote(tuiPath)} --cwd ${shellQuote(cwd)}`;
|
|
516
|
+
const cmd = `node ${shellQuote(tuiPath)} --cwd ${shellQuote(cwd)}; exit`;
|
|
510
517
|
execSync4(
|
|
511
518
|
`tmux send-keys -t ${shellQuote(windowId)} ${shellQuote(cmd)} Enter`
|
|
512
519
|
);
|
|
520
|
+
execSync4(
|
|
521
|
+
`tmux set-option -t ${shellQuote(tmuxSession)} @sisyphus_dashboard ${shellQuote(windowId)}`,
|
|
522
|
+
{ stdio: "pipe" }
|
|
523
|
+
);
|
|
513
524
|
return true;
|
|
514
525
|
}
|
|
515
526
|
function registerDashboard(program2) {
|
|
516
527
|
program2.command("dashboard").description("Launch the TUI dashboard for monitoring and managing sessions").action(async () => {
|
|
517
528
|
assertTmux();
|
|
518
|
-
const
|
|
519
|
-
|
|
529
|
+
const tuiPath = join3(import.meta.dirname, "tui.js");
|
|
530
|
+
execSync4(`node ${shellQuote(tuiPath)} --cwd ${shellQuote(process.cwd())}`, {
|
|
531
|
+
stdio: "inherit"
|
|
532
|
+
});
|
|
520
533
|
});
|
|
521
534
|
}
|
|
522
535
|
|
|
@@ -550,7 +563,7 @@ function registerStart(program2) {
|
|
|
550
563
|
}
|
|
551
564
|
try {
|
|
552
565
|
const tmuxSession = getTmuxSession();
|
|
553
|
-
if (
|
|
566
|
+
if (openDashboardWindow(tmuxSession, cwd)) {
|
|
554
567
|
console.log(`Dashboard opened in tmux window "sisyphus-dashboard"`);
|
|
555
568
|
}
|
|
556
569
|
} catch {
|
|
@@ -644,24 +657,6 @@ function registerSpawn(program2) {
|
|
|
644
657
|
}
|
|
645
658
|
|
|
646
659
|
// src/cli/commands/submit.ts
|
|
647
|
-
import { execSync as execSync6 } from "child_process";
|
|
648
|
-
function isInWorktree() {
|
|
649
|
-
try {
|
|
650
|
-
const gitDir = execSync6("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
|
|
651
|
-
const commonDir = execSync6("git rev-parse --git-common-dir", { encoding: "utf-8" }).trim();
|
|
652
|
-
return gitDir !== commonDir;
|
|
653
|
-
} catch {
|
|
654
|
-
return false;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
function getUncommittedChanges() {
|
|
658
|
-
try {
|
|
659
|
-
const status = execSync6("git status --porcelain", { encoding: "utf-8" }).trim();
|
|
660
|
-
return status || null;
|
|
661
|
-
} catch {
|
|
662
|
-
return null;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
660
|
function registerSubmit(program2) {
|
|
666
661
|
program2.command("submit").description("Submit work report and exit (agent only)").option("--report <report>", "Work report (or pipe via stdin)").option("--session <sessionId>", "Session ID (defaults to SISYPHUS_SESSION_ID env var)").action(async (opts) => {
|
|
667
662
|
assertTmux();
|
|
@@ -671,17 +666,6 @@ function registerSubmit(program2) {
|
|
|
671
666
|
console.error("Error: provide --session or set SISYPHUS_SESSION_ID (and SISYPHUS_AGENT_ID) environment variables");
|
|
672
667
|
process.exit(1);
|
|
673
668
|
}
|
|
674
|
-
if (isInWorktree()) {
|
|
675
|
-
const changes = getUncommittedChanges();
|
|
676
|
-
if (changes) {
|
|
677
|
-
console.error("Error: uncommitted changes in worktree. Commit your changes before submitting.");
|
|
678
|
-
console.error('\nCommit first:\n git add -A && git commit -m "description of changes"\n');
|
|
679
|
-
console.error("Or discard:\n git checkout -- .\n");
|
|
680
|
-
console.error("Uncommitted changes:");
|
|
681
|
-
console.error(changes);
|
|
682
|
-
process.exit(1);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
669
|
const report = opts.report ?? await readStdin();
|
|
686
670
|
if (!report) {
|
|
687
671
|
console.error("Error: provide --report or pipe content via stdin");
|
|
@@ -766,7 +750,8 @@ function registerContinue(program2) {
|
|
|
766
750
|
}
|
|
767
751
|
|
|
768
752
|
// src/cli/commands/status.ts
|
|
769
|
-
import {
|
|
753
|
+
import { execSync as execSync6 } from "child_process";
|
|
754
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
770
755
|
var COLOR_CODES = {
|
|
771
756
|
green: "\x1B[32m",
|
|
772
757
|
yellow: "\x1B[33m",
|
|
@@ -802,12 +787,17 @@ function inferOrchestratorPhase(session) {
|
|
|
802
787
|
return "starting";
|
|
803
788
|
}
|
|
804
789
|
}
|
|
805
|
-
function formatAgent(agent) {
|
|
790
|
+
function formatAgent(agent, verbose) {
|
|
806
791
|
const status = colorize(agent.status, agent.status);
|
|
807
792
|
const name = `${BOLD}${agent.name}${RESET}`;
|
|
808
793
|
const type = `${DIM}(${agent.agentType})${RESET}`;
|
|
809
794
|
const duration = formatDuration(agent.activeMs);
|
|
810
795
|
let line = ` ${agent.id} ${name} ${type} \u2014 ${status} ${DIM}(${duration})${RESET}`;
|
|
796
|
+
if (verbose && agent.instruction) {
|
|
797
|
+
const truncated = agent.instruction.length > 200 ? agent.instruction.slice(0, 200) + "..." : agent.instruction;
|
|
798
|
+
line += `
|
|
799
|
+
${DIM}Instruction: ${truncated}${RESET}`;
|
|
800
|
+
}
|
|
811
801
|
if (agent.reports.length > 0) {
|
|
812
802
|
for (const r of agent.reports) {
|
|
813
803
|
const label = r.type === "final" ? "Final" : "Update";
|
|
@@ -857,7 +847,33 @@ function readRoadmapTodos(cwd, sessionId) {
|
|
|
857
847
|
return [];
|
|
858
848
|
}
|
|
859
849
|
}
|
|
860
|
-
function
|
|
850
|
+
function readFullRoadmap(cwd, sessionId) {
|
|
851
|
+
try {
|
|
852
|
+
return readFileSync3(roadmapPath(cwd, sessionId), "utf8");
|
|
853
|
+
} catch {
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
function readCycleLog(cwd, sessionId, cycle) {
|
|
858
|
+
try {
|
|
859
|
+
const path = cycleLogPath(cwd, sessionId, cycle);
|
|
860
|
+
if (!existsSync4(path)) return null;
|
|
861
|
+
return readFileSync3(path, "utf8");
|
|
862
|
+
} catch {
|
|
863
|
+
return null;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
function capturePaneOutput(paneId, lines = 50) {
|
|
867
|
+
try {
|
|
868
|
+
return execSync6(
|
|
869
|
+
`tmux capture-pane -t "${paneId}" -p -S -${lines}`,
|
|
870
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
871
|
+
).trimEnd();
|
|
872
|
+
} catch {
|
|
873
|
+
return null;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
function printSession(session, verbose) {
|
|
861
877
|
const status = colorize(session.status, session.status);
|
|
862
878
|
const sessionDuration = formatDuration(session.createdAt, session.completedAt);
|
|
863
879
|
console.log(`
|
|
@@ -865,7 +881,7 @@ ${BOLD}Session: ${session.id}${RESET}`);
|
|
|
865
881
|
console.log(` Status: ${status}`);
|
|
866
882
|
console.log(` Task: ${session.task}`);
|
|
867
883
|
if (session.context) {
|
|
868
|
-
const truncated = session.context.length > 120 ? session.context.slice(0, 120) + "..." : session.context;
|
|
884
|
+
const truncated = !verbose && session.context.length > 120 ? session.context.slice(0, 120) + "..." : session.context;
|
|
869
885
|
console.log(` Context: ${truncated}`);
|
|
870
886
|
}
|
|
871
887
|
console.log(` CWD: ${session.cwd}`);
|
|
@@ -886,14 +902,27 @@ ${BOLD}Active agents (${runningAgents.length}):${RESET}`);
|
|
|
886
902
|
const type = `${DIM}(${agent.agentType})${RESET}`;
|
|
887
903
|
const duration = formatDuration(agent.activeMs);
|
|
888
904
|
console.log(` ${agent.id} ${name} ${type} running ${duration}`);
|
|
905
|
+
if (verbose && agent.instruction) {
|
|
906
|
+
const truncated = agent.instruction.length > 200 ? agent.instruction.slice(0, 200) + "..." : agent.instruction;
|
|
907
|
+
console.log(` ${DIM}Instruction: ${truncated}${RESET}`);
|
|
908
|
+
}
|
|
889
909
|
}
|
|
890
910
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
911
|
+
if (verbose) {
|
|
912
|
+
const roadmap = readFullRoadmap(session.cwd, session.id);
|
|
913
|
+
if (roadmap) {
|
|
914
|
+
console.log(`
|
|
915
|
+
${BOLD}Roadmap:${RESET}`);
|
|
916
|
+
console.log(roadmap);
|
|
917
|
+
}
|
|
918
|
+
} else {
|
|
919
|
+
const todos = readRoadmapTodos(session.cwd, session.id);
|
|
920
|
+
if (todos.length > 0) {
|
|
921
|
+
console.log(`
|
|
894
922
|
${BOLD}Remaining (${todos.length} unchecked):${RESET}`);
|
|
895
|
-
|
|
896
|
-
|
|
923
|
+
for (const todo of todos) {
|
|
924
|
+
console.log(` - ${todo}`);
|
|
925
|
+
}
|
|
897
926
|
}
|
|
898
927
|
}
|
|
899
928
|
if (session.orchestratorCycles.length > 0) {
|
|
@@ -904,25 +933,68 @@ ${BOLD}Remaining (${todos.length} unchecked):${RESET}`);
|
|
|
904
933
|
const isLast = i === cycles.length - 1;
|
|
905
934
|
const phase = isLast && session.status === "active" ? inferOrchestratorPhase(session) : void 0;
|
|
906
935
|
console.log(formatCycle(cycles[i], phase));
|
|
936
|
+
if (verbose) {
|
|
937
|
+
const log = readCycleLog(session.cwd, session.id, cycles[i].cycle);
|
|
938
|
+
if (log) {
|
|
939
|
+
const lines = log.split("\n");
|
|
940
|
+
const preview = lines.slice(0, 20).join("\n");
|
|
941
|
+
console.log(` ${DIM}--- cycle log ---${RESET}`);
|
|
942
|
+
for (const line of preview.split("\n")) {
|
|
943
|
+
console.log(` ${DIM}${line}${RESET}`);
|
|
944
|
+
}
|
|
945
|
+
if (lines.length > 20) {
|
|
946
|
+
console.log(` ${DIM}... (${lines.length - 20} more lines)${RESET}`);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
907
950
|
}
|
|
908
951
|
}
|
|
909
952
|
if (session.agents.length > 0) {
|
|
910
953
|
console.log(`
|
|
911
954
|
${BOLD}Agents:${RESET}`);
|
|
912
955
|
for (const agent of session.agents) {
|
|
913
|
-
console.log(formatAgent(agent));
|
|
956
|
+
console.log(formatAgent(agent, verbose));
|
|
914
957
|
}
|
|
915
958
|
}
|
|
959
|
+
if (verbose) {
|
|
960
|
+
const lastCycle = session.orchestratorCycles[session.orchestratorCycles.length - 1];
|
|
961
|
+
if (lastCycle && !lastCycle.completedAt && lastCycle.paneId) {
|
|
962
|
+
const output = capturePaneOutput(lastCycle.paneId);
|
|
963
|
+
if (output) {
|
|
964
|
+
console.log(`
|
|
965
|
+
<orchestrator-pane-output lines="50">`);
|
|
966
|
+
console.log(output);
|
|
967
|
+
console.log(`</orchestrator-pane-output>`);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
for (const agent of runningAgents) {
|
|
971
|
+
if (agent.paneId) {
|
|
972
|
+
const output = capturePaneOutput(agent.paneId, 30);
|
|
973
|
+
if (output) {
|
|
974
|
+
console.log(`
|
|
975
|
+
<agent-pane-output agent="${agent.id}" name="${agent.name}" lines="30">`);
|
|
976
|
+
console.log(output);
|
|
977
|
+
console.log(`</agent-pane-output>`);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
if (verbose && session.completionReport) {
|
|
983
|
+
console.log(`
|
|
984
|
+
${BOLD}Completion Report:${RESET}`);
|
|
985
|
+
console.log(session.completionReport);
|
|
986
|
+
}
|
|
916
987
|
}
|
|
917
988
|
function registerStatus(program2) {
|
|
918
|
-
program2.command("status").description("Show session status").argument("[session-id]", "Session ID (defaults to SISYPHUS_SESSION_ID env)").action(async (sessionIdArg) => {
|
|
989
|
+
program2.command("status").description("Show session status").argument("[session-id]", "Session ID (defaults to SISYPHUS_SESSION_ID env)").option("-v, --verbose", "Show detailed output (roadmap, pane output, agent instructions)").action(async (sessionIdArg, opts) => {
|
|
919
990
|
const sessionId = sessionIdArg ?? process.env.SISYPHUS_SESSION_ID;
|
|
991
|
+
const verbose = opts?.verbose ?? false;
|
|
920
992
|
const request = { type: "status", sessionId };
|
|
921
993
|
const response = await sendRequest(request);
|
|
922
994
|
if (response.ok) {
|
|
923
995
|
const session = response.data?.session;
|
|
924
996
|
if (session) {
|
|
925
|
-
printSession(session);
|
|
997
|
+
printSession(session, verbose);
|
|
926
998
|
} else {
|
|
927
999
|
console.log("No session found");
|
|
928
1000
|
}
|
|
@@ -1193,8 +1265,245 @@ function registerSetupKeybind(program2) {
|
|
|
1193
1265
|
}
|
|
1194
1266
|
|
|
1195
1267
|
// src/cli/commands/doctor.ts
|
|
1268
|
+
import { execSync as execSync8 } from "child_process";
|
|
1269
|
+
import { existsSync as existsSync6, statSync } from "fs";
|
|
1270
|
+
|
|
1271
|
+
// src/cli/onboard.ts
|
|
1196
1272
|
import { execSync as execSync7 } from "child_process";
|
|
1197
|
-
import { existsSync as
|
|
1273
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
1274
|
+
import { homedir as homedir3 } from "os";
|
|
1275
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
1276
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1277
|
+
function detectTerminal() {
|
|
1278
|
+
const termProgram = process.env["TERM_PROGRAM"] || "";
|
|
1279
|
+
const isIterm = termProgram === "iTerm.app" || !!process.env["ITERM_SESSION_ID"];
|
|
1280
|
+
return { name: termProgram || "unknown", isIterm };
|
|
1281
|
+
}
|
|
1282
|
+
function isTmuxAvailable() {
|
|
1283
|
+
try {
|
|
1284
|
+
execSync7("which tmux", { stdio: "pipe" });
|
|
1285
|
+
return true;
|
|
1286
|
+
} catch {
|
|
1287
|
+
return false;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
function isBrewAvailable() {
|
|
1291
|
+
try {
|
|
1292
|
+
execSync7("which brew", { stdio: "pipe" });
|
|
1293
|
+
return true;
|
|
1294
|
+
} catch {
|
|
1295
|
+
return false;
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
function tryAutoInstallTmux() {
|
|
1299
|
+
if (!isBrewAvailable()) return false;
|
|
1300
|
+
try {
|
|
1301
|
+
console.log(" Installing tmux via Homebrew...");
|
|
1302
|
+
execSync7("brew install tmux", { stdio: "inherit" });
|
|
1303
|
+
return isTmuxAvailable();
|
|
1304
|
+
} catch {
|
|
1305
|
+
return false;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
function checkItermOptionKey() {
|
|
1309
|
+
if (process.platform !== "darwin") {
|
|
1310
|
+
return { checked: false, allCorrect: true, incorrectProfiles: [] };
|
|
1311
|
+
}
|
|
1312
|
+
const plistPath2 = join5(homedir3(), "Library", "Preferences", "com.googlecode.iterm2.plist");
|
|
1313
|
+
if (!existsSync5(plistPath2)) {
|
|
1314
|
+
return { checked: false, allCorrect: false, incorrectProfiles: [] };
|
|
1315
|
+
}
|
|
1316
|
+
try {
|
|
1317
|
+
const json = execSync7(
|
|
1318
|
+
`plutil -extract "New Bookmarks" json -o - "${plistPath2}"`,
|
|
1319
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
1320
|
+
);
|
|
1321
|
+
const profiles = JSON.parse(json);
|
|
1322
|
+
const currentProfile = process.env["ITERM_PROFILE"];
|
|
1323
|
+
const incorrect = [];
|
|
1324
|
+
for (const profile of profiles) {
|
|
1325
|
+
const name = profile["Name"] || "Unnamed";
|
|
1326
|
+
if (currentProfile && name !== currentProfile) continue;
|
|
1327
|
+
if (profile["Right Option Key Sends"] !== 2) {
|
|
1328
|
+
incorrect.push(name);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
return { checked: true, allCorrect: incorrect.length === 0, incorrectProfiles: incorrect };
|
|
1332
|
+
} catch {
|
|
1333
|
+
return { checked: false, allCorrect: false, incorrectProfiles: [] };
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
function hasExistingTmuxConf() {
|
|
1337
|
+
return existsSync5(join5(homedir3(), ".tmux.conf")) || existsSync5(join5(homedir3(), ".config", "tmux", "tmux.conf"));
|
|
1338
|
+
}
|
|
1339
|
+
var TMUX_DEFAULTS = `# Sensible tmux defaults (installed by sisyphus)
|
|
1340
|
+
# Customize freely \u2014 sisyphus won't overwrite this file.
|
|
1341
|
+
|
|
1342
|
+
# Enable mouse (click panes, scroll, resize)
|
|
1343
|
+
set -g mouse on
|
|
1344
|
+
|
|
1345
|
+
# Scrollback history
|
|
1346
|
+
set -g history-limit 50000
|
|
1347
|
+
|
|
1348
|
+
# 256 color + true color support
|
|
1349
|
+
set -g default-terminal "tmux-256color"
|
|
1350
|
+
set -as terminal-overrides ",*:Tc"
|
|
1351
|
+
|
|
1352
|
+
# Low escape delay (keeps Option/Meta keybindings responsive)
|
|
1353
|
+
set -sg escape-time 10
|
|
1354
|
+
|
|
1355
|
+
# Window numbering from 1
|
|
1356
|
+
set -g base-index 1
|
|
1357
|
+
setw -g pane-base-index 1
|
|
1358
|
+
|
|
1359
|
+
# Renumber windows when one closes
|
|
1360
|
+
set -g renumber-windows on
|
|
1361
|
+
|
|
1362
|
+
# Clipboard integration
|
|
1363
|
+
set -g set-clipboard on
|
|
1364
|
+
|
|
1365
|
+
# Focus events (for editors)
|
|
1366
|
+
set -g focus-events on
|
|
1367
|
+
|
|
1368
|
+
# --- Pane navigation (no prefix needed) ---
|
|
1369
|
+
bind -n C-h select-pane -L
|
|
1370
|
+
bind -n C-j select-pane -D
|
|
1371
|
+
bind -n C-k select-pane -U
|
|
1372
|
+
bind -n C-l select-pane -R
|
|
1373
|
+
|
|
1374
|
+
# --- Window navigation (no prefix needed) ---
|
|
1375
|
+
bind -n C-n next-window
|
|
1376
|
+
bind -n C-p previous-window
|
|
1377
|
+
|
|
1378
|
+
# --- New window / splits preserve cwd ---
|
|
1379
|
+
bind c new-window -c "#{pane_current_path}"
|
|
1380
|
+
bind '"' split-window -v -c "#{pane_current_path}"
|
|
1381
|
+
bind % split-window -h -c "#{pane_current_path}"
|
|
1382
|
+
|
|
1383
|
+
# --- Kill pane + rebalance ---
|
|
1384
|
+
bind x kill-pane \\; select-layout even-horizontal
|
|
1385
|
+
|
|
1386
|
+
# --- Auto-rebalance on pane close ---
|
|
1387
|
+
set-hook -g after-kill-pane "select-layout even-horizontal"
|
|
1388
|
+
set-hook -g pane-exited "select-layout even-horizontal"
|
|
1389
|
+
|
|
1390
|
+
# --- Manual re-tile ---
|
|
1391
|
+
bind = select-layout even-horizontal
|
|
1392
|
+
|
|
1393
|
+
# --- Scroll (no prefix needed) ---
|
|
1394
|
+
bind -n C-u copy-mode \\; send-keys -X halfpage-up
|
|
1395
|
+
bind -n C-d copy-mode \\; send-keys -X halfpage-down
|
|
1396
|
+
|
|
1397
|
+
# --- Vi copy mode ---
|
|
1398
|
+
setw -g mode-keys vi
|
|
1399
|
+
bind -T copy-mode-vi v send-keys -X begin-selection
|
|
1400
|
+
bind -T copy-mode-vi y send-keys -X copy-selection-and-cancel
|
|
1401
|
+
`;
|
|
1402
|
+
function writeTmuxDefaults() {
|
|
1403
|
+
const confPath = join5(homedir3(), ".tmux.conf");
|
|
1404
|
+
writeFileSync3(confPath, TMUX_DEFAULTS, "utf8");
|
|
1405
|
+
}
|
|
1406
|
+
function isNvimAvailable() {
|
|
1407
|
+
try {
|
|
1408
|
+
execSync7("which nvim", { stdio: "pipe" });
|
|
1409
|
+
return true;
|
|
1410
|
+
} catch {
|
|
1411
|
+
return false;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
function getNvimVersion() {
|
|
1415
|
+
try {
|
|
1416
|
+
return execSync7("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "") || "unknown";
|
|
1417
|
+
} catch {
|
|
1418
|
+
return "unknown";
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
function hasLazyVimConfig() {
|
|
1422
|
+
return existsSync5(join5(homedir3(), ".config", "nvim", "lazy-lock.json"));
|
|
1423
|
+
}
|
|
1424
|
+
function tryAutoInstallNvim() {
|
|
1425
|
+
if (isNvimAvailable()) {
|
|
1426
|
+
return { installed: true, autoInstalled: false, version: getNvimVersion(), lazyVimInstalled: hasLazyVimConfig() };
|
|
1427
|
+
}
|
|
1428
|
+
if (!isBrewAvailable()) {
|
|
1429
|
+
return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false };
|
|
1430
|
+
}
|
|
1431
|
+
try {
|
|
1432
|
+
console.log(" Installing neovim via Homebrew...");
|
|
1433
|
+
execSync7("brew install neovim", { stdio: "inherit" });
|
|
1434
|
+
} catch {
|
|
1435
|
+
return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false };
|
|
1436
|
+
}
|
|
1437
|
+
if (!isNvimAvailable()) {
|
|
1438
|
+
return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false };
|
|
1439
|
+
}
|
|
1440
|
+
const nvimConfigDir = join5(homedir3(), ".config", "nvim");
|
|
1441
|
+
let lazyVimInstalled = false;
|
|
1442
|
+
if (!existsSync5(nvimConfigDir)) {
|
|
1443
|
+
try {
|
|
1444
|
+
console.log(" Cloning LazyVim starter config...");
|
|
1445
|
+
execSync7(`git clone https://github.com/LazyVim/starter ${nvimConfigDir}`, { stdio: "inherit" });
|
|
1446
|
+
const gitDir = join5(nvimConfigDir, ".git");
|
|
1447
|
+
if (existsSync5(gitDir)) {
|
|
1448
|
+
execSync7(`rm -rf "${gitDir}"`, { stdio: "pipe" });
|
|
1449
|
+
}
|
|
1450
|
+
lazyVimInstalled = true;
|
|
1451
|
+
} catch {
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
return { installed: true, autoInstalled: true, version: getNvimVersion(), lazyVimInstalled };
|
|
1455
|
+
}
|
|
1456
|
+
function beginCommandPath() {
|
|
1457
|
+
return join5(homedir3(), ".claude", "commands", "sisyphus", "begin.md");
|
|
1458
|
+
}
|
|
1459
|
+
function bundledBeginCommandPath() {
|
|
1460
|
+
const distDir = dirname2(fileURLToPath2(import.meta.url));
|
|
1461
|
+
return join5(distDir, "templates", "begin.md");
|
|
1462
|
+
}
|
|
1463
|
+
function isBeginCommandInstalled() {
|
|
1464
|
+
return existsSync5(beginCommandPath());
|
|
1465
|
+
}
|
|
1466
|
+
function installBeginCommand() {
|
|
1467
|
+
const dest = beginCommandPath();
|
|
1468
|
+
if (existsSync5(dest)) {
|
|
1469
|
+
return { installed: true, autoInstalled: false, path: dest };
|
|
1470
|
+
}
|
|
1471
|
+
const src = bundledBeginCommandPath();
|
|
1472
|
+
if (!existsSync5(src)) {
|
|
1473
|
+
return { installed: false, autoInstalled: false, path: dest };
|
|
1474
|
+
}
|
|
1475
|
+
try {
|
|
1476
|
+
mkdirSync3(dirname2(dest), { recursive: true });
|
|
1477
|
+
writeFileSync3(dest, readFileSync4(src, "utf-8"), "utf8");
|
|
1478
|
+
return { installed: true, autoInstalled: true, path: dest };
|
|
1479
|
+
} catch {
|
|
1480
|
+
return { installed: false, autoInstalled: false, path: dest };
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
function runOnboarding() {
|
|
1484
|
+
const terminal = detectTerminal();
|
|
1485
|
+
const tmuxAlreadyInstalled = isTmuxAvailable();
|
|
1486
|
+
let tmuxInstalled = tmuxAlreadyInstalled;
|
|
1487
|
+
let tmuxAutoInstalled = false;
|
|
1488
|
+
let tmuxDefaultsWritten = false;
|
|
1489
|
+
if (!tmuxAlreadyInstalled && process.platform === "darwin") {
|
|
1490
|
+
tmuxAutoInstalled = tryAutoInstallTmux();
|
|
1491
|
+
tmuxInstalled = tmuxAutoInstalled;
|
|
1492
|
+
if (tmuxAutoInstalled && !hasExistingTmuxConf()) {
|
|
1493
|
+
writeTmuxDefaults();
|
|
1494
|
+
tmuxDefaultsWritten = true;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
let itermOptionKey = { checked: false, allCorrect: true, incorrectProfiles: [] };
|
|
1498
|
+
if (terminal.isIterm) {
|
|
1499
|
+
itermOptionKey = checkItermOptionKey();
|
|
1500
|
+
}
|
|
1501
|
+
const nvim = tryAutoInstallNvim();
|
|
1502
|
+
const command = installBeginCommand();
|
|
1503
|
+
return { tmuxInstalled, tmuxAutoInstalled, terminal, itermOptionKey, tmuxDefaultsWritten, nvim, command };
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// src/cli/commands/doctor.ts
|
|
1198
1507
|
function checkNodeVersion() {
|
|
1199
1508
|
const major = parseInt(process.versions.node.split(".")[0], 10);
|
|
1200
1509
|
if (major < 22) {
|
|
@@ -1204,7 +1513,7 @@ function checkNodeVersion() {
|
|
|
1204
1513
|
}
|
|
1205
1514
|
function checkClaudeCli() {
|
|
1206
1515
|
try {
|
|
1207
|
-
|
|
1516
|
+
execSync8("which claude", { stdio: "pipe" });
|
|
1208
1517
|
return { name: "Claude CLI", status: "ok", detail: "Found on PATH" };
|
|
1209
1518
|
} catch {
|
|
1210
1519
|
return {
|
|
@@ -1217,7 +1526,7 @@ function checkClaudeCli() {
|
|
|
1217
1526
|
}
|
|
1218
1527
|
function checkGit() {
|
|
1219
1528
|
try {
|
|
1220
|
-
const version =
|
|
1529
|
+
const version = execSync8("git --version", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
1221
1530
|
return { name: "git", status: "ok", detail: version };
|
|
1222
1531
|
} catch {
|
|
1223
1532
|
return { name: "git", status: "fail", detail: "Not found on PATH", fix: "Install git: https://git-scm.com/downloads" };
|
|
@@ -1225,7 +1534,7 @@ function checkGit() {
|
|
|
1225
1534
|
}
|
|
1226
1535
|
function checkTmuxVersion() {
|
|
1227
1536
|
try {
|
|
1228
|
-
const version =
|
|
1537
|
+
const version = execSync8("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
1229
1538
|
const match = version.match(/(\d+\.\d+)/);
|
|
1230
1539
|
if (!match) return { name: "tmux version", status: "warn", detail: `Could not parse version: ${version}` };
|
|
1231
1540
|
const ver = parseFloat(match[1]);
|
|
@@ -1251,7 +1560,7 @@ function checkDaemonInstalled() {
|
|
|
1251
1560
|
};
|
|
1252
1561
|
}
|
|
1253
1562
|
const pid = daemonPidPath();
|
|
1254
|
-
if (
|
|
1563
|
+
if (existsSync6(pid)) {
|
|
1255
1564
|
return { name: "Daemon setup", status: "ok", detail: `PID file found at ${pid}` };
|
|
1256
1565
|
}
|
|
1257
1566
|
return {
|
|
@@ -1263,7 +1572,7 @@ function checkDaemonInstalled() {
|
|
|
1263
1572
|
}
|
|
1264
1573
|
function checkDaemonRunning() {
|
|
1265
1574
|
const pid = daemonPidPath();
|
|
1266
|
-
if (!
|
|
1575
|
+
if (!existsSync6(pid)) {
|
|
1267
1576
|
const fix = process.platform === "darwin" ? "launchctl load -w ~/Library/LaunchAgents/com.sisyphus.daemon.plist" : "sisyphusd & \u2014 or check if the process is running";
|
|
1268
1577
|
return {
|
|
1269
1578
|
name: "Daemon process",
|
|
@@ -1274,7 +1583,7 @@ function checkDaemonRunning() {
|
|
|
1274
1583
|
}
|
|
1275
1584
|
try {
|
|
1276
1585
|
const sock = socketPath();
|
|
1277
|
-
|
|
1586
|
+
execSync8(`test -S "${sock}"`, { stdio: "pipe" });
|
|
1278
1587
|
return { name: "Daemon process", status: "ok", detail: `Socket at ${sock}` };
|
|
1279
1588
|
} catch {
|
|
1280
1589
|
return {
|
|
@@ -1287,13 +1596,13 @@ function checkDaemonRunning() {
|
|
|
1287
1596
|
}
|
|
1288
1597
|
function checkTmux() {
|
|
1289
1598
|
try {
|
|
1290
|
-
|
|
1599
|
+
execSync8("which tmux", { stdio: "pipe" });
|
|
1291
1600
|
} catch {
|
|
1292
1601
|
const installHint = process.platform === "darwin" ? "brew install tmux" : "apt install tmux (Debian/Ubuntu) or your package manager";
|
|
1293
1602
|
return { name: "tmux", status: "fail", detail: "Not found on PATH", fix: installHint };
|
|
1294
1603
|
}
|
|
1295
1604
|
try {
|
|
1296
|
-
|
|
1605
|
+
execSync8("tmux list-sessions", { stdio: "pipe" });
|
|
1297
1606
|
return { name: "tmux", status: "ok", detail: "Running" };
|
|
1298
1607
|
} catch {
|
|
1299
1608
|
return { name: "tmux", status: "warn", detail: "Installed but no server running" };
|
|
@@ -1301,7 +1610,7 @@ function checkTmux() {
|
|
|
1301
1610
|
}
|
|
1302
1611
|
function checkCycleScript() {
|
|
1303
1612
|
const path = cycleScriptPath();
|
|
1304
|
-
if (!
|
|
1613
|
+
if (!existsSync6(path)) {
|
|
1305
1614
|
return {
|
|
1306
1615
|
name: "Cycle script",
|
|
1307
1616
|
status: "fail",
|
|
@@ -1326,7 +1635,7 @@ function checkCycleScript() {
|
|
|
1326
1635
|
function checkTmuxKeybind() {
|
|
1327
1636
|
const existing = getExistingBinding(DEFAULT_KEY);
|
|
1328
1637
|
if (existing === null) {
|
|
1329
|
-
if (
|
|
1638
|
+
if (existsSync6(sisyphusTmuxConfPath())) {
|
|
1330
1639
|
return {
|
|
1331
1640
|
name: `Tmux keybind (${DEFAULT_KEY})`,
|
|
1332
1641
|
status: "warn",
|
|
@@ -1352,25 +1661,85 @@ function checkTmuxKeybind() {
|
|
|
1352
1661
|
}
|
|
1353
1662
|
function checkGlobalDir() {
|
|
1354
1663
|
const dir = globalDir();
|
|
1355
|
-
if (
|
|
1664
|
+
if (existsSync6(dir)) {
|
|
1356
1665
|
return { name: "Data directory", status: "ok", detail: dir };
|
|
1357
1666
|
}
|
|
1358
1667
|
return { name: "Data directory", status: "warn", detail: `${dir} does not exist (created on first use)` };
|
|
1359
1668
|
}
|
|
1669
|
+
function checkTerminal() {
|
|
1670
|
+
if (process.platform !== "darwin") {
|
|
1671
|
+
return { name: "Terminal", status: "ok", detail: "Non-macOS (skipped)" };
|
|
1672
|
+
}
|
|
1673
|
+
const terminal = detectTerminal();
|
|
1674
|
+
if (terminal.isIterm) {
|
|
1675
|
+
return { name: "Terminal", status: "ok", detail: terminal.name };
|
|
1676
|
+
}
|
|
1677
|
+
return {
|
|
1678
|
+
name: "Terminal",
|
|
1679
|
+
status: "warn",
|
|
1680
|
+
detail: terminal.name ? terminal.name : "unknown",
|
|
1681
|
+
fix: "iTerm2 recommended for best experience: https://iterm2.com"
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
function checkItermRightOptionKey() {
|
|
1685
|
+
if (process.platform !== "darwin") return null;
|
|
1686
|
+
const terminal = detectTerminal();
|
|
1687
|
+
if (!terminal.isIterm) return null;
|
|
1688
|
+
const result = checkItermOptionKey();
|
|
1689
|
+
if (!result.checked) return null;
|
|
1690
|
+
if (result.allCorrect) {
|
|
1691
|
+
return { name: "Right Option Key", status: "ok", detail: "Esc+" };
|
|
1692
|
+
}
|
|
1693
|
+
const profiles = result.incorrectProfiles.map((p) => `"${p}"`).join(", ");
|
|
1694
|
+
return {
|
|
1695
|
+
name: "Right Option Key",
|
|
1696
|
+
status: "warn",
|
|
1697
|
+
detail: `Not Esc+ for ${profiles}`,
|
|
1698
|
+
fix: "iTerm2 \u2192 Settings \u2192 Profiles \u2192 Keys \u2192 Right Option Key \u2192 Esc+"
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
function checkBeginCommand() {
|
|
1702
|
+
if (isBeginCommandInstalled()) {
|
|
1703
|
+
return { name: "/begin command", status: "ok", detail: "Installed" };
|
|
1704
|
+
}
|
|
1705
|
+
return {
|
|
1706
|
+
name: "/begin command",
|
|
1707
|
+
status: "warn",
|
|
1708
|
+
detail: "Not installed",
|
|
1709
|
+
fix: "sisyphus setup"
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
function checkNvim() {
|
|
1713
|
+
if (!isNvimAvailable()) {
|
|
1714
|
+
const fix = process.platform === "darwin" ? "brew install neovim" : "Install neovim from https://neovim.io";
|
|
1715
|
+
return { name: "nvim", status: "warn", detail: "Not installed", fix };
|
|
1716
|
+
}
|
|
1717
|
+
try {
|
|
1718
|
+
const version = execSync8("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "");
|
|
1719
|
+
return { name: "nvim", status: "ok", detail: version ?? "installed" };
|
|
1720
|
+
} catch {
|
|
1721
|
+
return { name: "nvim", status: "ok", detail: "installed" };
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1360
1724
|
var SYMBOLS = { ok: "\u2713", warn: "!", fail: "\u2717" };
|
|
1361
1725
|
function registerDoctor(program2) {
|
|
1362
1726
|
program2.command("doctor").description("Check sisyphus installation health").action(async () => {
|
|
1727
|
+
const itermCheck = checkItermRightOptionKey();
|
|
1363
1728
|
const checks = [
|
|
1364
1729
|
checkNodeVersion(),
|
|
1365
1730
|
checkClaudeCli(),
|
|
1366
1731
|
checkGit(),
|
|
1367
1732
|
checkTmux(),
|
|
1368
1733
|
checkTmuxVersion(),
|
|
1734
|
+
checkTerminal(),
|
|
1735
|
+
...itermCheck ? [itermCheck] : [],
|
|
1369
1736
|
checkGlobalDir(),
|
|
1370
1737
|
checkDaemonInstalled(),
|
|
1371
1738
|
checkDaemonRunning(),
|
|
1372
1739
|
checkCycleScript(),
|
|
1373
|
-
checkTmuxKeybind()
|
|
1740
|
+
checkTmuxKeybind(),
|
|
1741
|
+
checkBeginCommand(),
|
|
1742
|
+
checkNvim()
|
|
1374
1743
|
];
|
|
1375
1744
|
let hasIssues = false;
|
|
1376
1745
|
for (const c of checks) {
|
|
@@ -1400,135 +1769,959 @@ function registerCompanionContext(program2) {
|
|
|
1400
1769
|
}
|
|
1401
1770
|
|
|
1402
1771
|
// src/cli/commands/getting-started.ts
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1772
|
+
import { execSync as execSync9 } from "child_process";
|
|
1773
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
1774
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1775
|
+
function templatePath(name) {
|
|
1776
|
+
return join6(dirname3(fileURLToPath3(import.meta.url)), "templates", name);
|
|
1777
|
+
}
|
|
1778
|
+
function isClaudeCode() {
|
|
1779
|
+
return !!process.env["CLAUDECODE"];
|
|
1780
|
+
}
|
|
1781
|
+
function printNonClaudeMessage() {
|
|
1782
|
+
console.log(`
|
|
1783
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
1784
|
+
\u2551 sisyphus getting-started \u2014 Interactive Tutorial \u2551
|
|
1785
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
1786
|
+
|
|
1787
|
+
This command provides an interactive tutorial best experienced
|
|
1788
|
+
inside Claude Code. Claude will walk you through setup step by step.
|
|
1789
|
+
|
|
1790
|
+
To start:
|
|
1791
|
+
1. Open Claude Code: claude
|
|
1792
|
+
2. Run: sisyphus getting-started
|
|
1793
|
+
|
|
1794
|
+
If you just want the quick reference, run:
|
|
1795
|
+
sisyphus --help
|
|
1796
|
+
sisyphus doctor
|
|
1797
|
+
`);
|
|
1798
|
+
}
|
|
1799
|
+
function printStep0() {
|
|
1800
|
+
const tmuxInstalled = isTmuxInstalled();
|
|
1801
|
+
const inTmux = !!process.env["TMUX"];
|
|
1802
|
+
let tmuxSession = null;
|
|
1803
|
+
if (process.env["TMUX"]) {
|
|
1804
|
+
const segments = process.env["TMUX"].split(",");
|
|
1805
|
+
const pathPart = segments[0];
|
|
1806
|
+
if (pathPart) {
|
|
1807
|
+
const parts = pathPart.split("/");
|
|
1808
|
+
tmuxSession = parts[parts.length - 1] || null;
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
console.log(`
|
|
1812
|
+
<claude-instructions>
|
|
1813
|
+
# Sisyphus Getting Started \u2014 Step 0: Entry & Tmux Gate
|
|
1814
|
+
|
|
1815
|
+
## Environment Data
|
|
1816
|
+
- tmuxInstalled: ${tmuxInstalled}
|
|
1817
|
+
- inTmux: ${inTmux}
|
|
1818
|
+
- tmuxSession: ${tmuxSession ?? "none"}
|
|
1819
|
+
|
|
1820
|
+
## Tutorial Overview
|
|
1821
|
+
|
|
1822
|
+
This tutorial has 6 steps. Share this overview so the user knows what's coming and can skip ahead:
|
|
1823
|
+
|
|
1824
|
+
| Step | Topic | Command |
|
|
1825
|
+
|------|-------|---------|
|
|
1826
|
+
| 0 | Entry & tmux gate (you are here) | \`sisyphus getting-started\` |
|
|
1827
|
+
| 1 | Tmux basics \u2014 sessions, panes, navigation | \`--tutorial 1\` |
|
|
1828
|
+
| 2 | Nvim basics \u2014 open, save, quit (optional) | \`--tutorial 2\` |
|
|
1829
|
+
| 3 | Sisyphus concepts \u2014 session model & keybinds | \`--tutorial 3\` |
|
|
1830
|
+
| 4 | Live demo \u2014 launch and observe a real session | \`--tutorial 4\` |
|
|
1831
|
+
| 5 | What's next \u2014 real usage guidance & suggestions | \`--tutorial 5\` |
|
|
1832
|
+
|
|
1833
|
+
Tell the user they can skip to any step with \`sisyphus getting-started --tutorial <N>\`.
|
|
1834
|
+
|
|
1835
|
+
## Instructions for Claude
|
|
1836
|
+
|
|
1837
|
+
You are guiding a user through the Sisyphus interactive tutorial.
|
|
1838
|
+
|
|
1839
|
+
### First: Ask if they want the tutorial
|
|
1840
|
+
|
|
1841
|
+
Ask the user if they'd like the interactive walkthrough. If they decline, give this quick summary and stop:
|
|
1842
|
+
|
|
1843
|
+
> Sisyphus is a multi-agent orchestrator for Claude Code. Start a session with \`sisyphus start "task"\`,
|
|
1844
|
+
> monitor with \`sisyphus dashboard\`, and check health with \`sisyphus doctor\`.
|
|
1845
|
+
|
|
1846
|
+
### If they want the tutorial:
|
|
1847
|
+
|
|
1848
|
+
**Case 1: tmux is NOT installed (tmuxInstalled: false)**
|
|
1849
|
+
- Explain what tmux is in 2-3 sentences: "tmux is a terminal multiplexer \u2014 it lets you split your terminal into multiple panes and keep sessions running in the background. Sisyphus uses it to give each agent its own workspace."
|
|
1850
|
+
- Show this ASCII diagram:
|
|
1851
|
+
|
|
1852
|
+
\`\`\`
|
|
1853
|
+
Regular terminal: With tmux:
|
|
1854
|
+
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
1855
|
+
\u2502 \u2502 \u2502 Agent\u2502 Agent\u2502
|
|
1856
|
+
\u2502 one view \u2502 \u2502 001 \u2502 002 \u2502
|
|
1857
|
+
\u2502 \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2524
|
|
1858
|
+
\u2502 \u2502 \u2502 Agent\u2502 Orch \u2502
|
|
1859
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 003 \u2502 \u2502
|
|
1860
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
1861
|
+
\`\`\`
|
|
1862
|
+
|
|
1863
|
+
- Tell them to install tmux:
|
|
1864
|
+
- macOS: \`brew install tmux\`
|
|
1865
|
+
- Linux: \`apt install tmux\` or their package manager
|
|
1866
|
+
- After install, re-run: \`sisyphus getting-started --tutorial 0\` to verify
|
|
1867
|
+
|
|
1868
|
+
**Case 2: tmux installed but NOT in a tmux session (inTmux: false)**
|
|
1869
|
+
- Tell the user they need to be inside a tmux session for the tutorial
|
|
1870
|
+
- Have them run: \`tmux new-session\`
|
|
1871
|
+
- Then resume the conversation with Claude in the new tmux session: \`claude\`
|
|
1872
|
+
- Then re-run: \`sisyphus getting-started --tutorial 0\` to verify
|
|
1873
|
+
|
|
1874
|
+
**Case 3: In tmux (inTmux: true)**
|
|
1875
|
+
- Tell the user they're all set \u2014 tmux is running
|
|
1876
|
+
- Proceed by running: \`sisyphus getting-started --tutorial 1\`
|
|
1877
|
+
</claude-instructions>
|
|
1878
|
+
`);
|
|
1879
|
+
}
|
|
1880
|
+
function printStep1() {
|
|
1881
|
+
console.log(`
|
|
1882
|
+
<claude-instructions>
|
|
1883
|
+
# Sisyphus Getting Started \u2014 Step 1: Tmux Basics
|
|
1884
|
+
|
|
1885
|
+
## Instructions for Claude
|
|
1886
|
+
|
|
1887
|
+
Teach the user tmux fundamentals. Be conversational and encouraging.
|
|
1888
|
+
|
|
1889
|
+
### 1. Explain the concepts with diagrams
|
|
1890
|
+
|
|
1891
|
+
**Sessions, Windows, and Panes:**
|
|
1892
|
+
|
|
1893
|
+
\`\`\`
|
|
1894
|
+
tmux session "work"
|
|
1895
|
+
\u251C\u2500\u2500 window 1: "code"
|
|
1896
|
+
\u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
1897
|
+
\u2502 \u2502 pane 1 \u2502 pane 2 \u2502
|
|
1898
|
+
\u2502 \u2502 (editor)\u2502 (tests) \u2502
|
|
1899
|
+
\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
1900
|
+
\u2514\u2500\u2500 window 2: "servers"
|
|
1901
|
+
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
1902
|
+
\u2502 pane 1 \u2502
|
|
1903
|
+
\u2502 (dev server) \u2502
|
|
1904
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
1905
|
+
\`\`\`
|
|
1906
|
+
|
|
1907
|
+
- **Session**: A collection of windows. Persists even if you close the terminal.
|
|
1908
|
+
- **Window**: Like a tab. Each window fills the screen.
|
|
1909
|
+
- **Pane**: A split within a window. Sisyphus puts each agent in its own pane.
|
|
1910
|
+
|
|
1911
|
+
### 2. Hands-on: Create a test split
|
|
1912
|
+
|
|
1913
|
+
Run this command for the user:
|
|
1914
|
+
\`\`\`
|
|
1915
|
+
tmux split-window -h
|
|
1916
|
+
\`\`\`
|
|
1917
|
+
|
|
1918
|
+
Tell them: "I just split your terminal. You should see two panes side by side."
|
|
1919
|
+
|
|
1920
|
+
Explain navigation:
|
|
1921
|
+
- \`Ctrl+l\`: move to the right pane
|
|
1922
|
+
- \`Ctrl+h\`: move to the left pane
|
|
1923
|
+
- \`Ctrl+j\`: move to the pane below
|
|
1924
|
+
- \`Ctrl+k\`: move to the pane above
|
|
1925
|
+
- No prefix key needed \u2014 just hold Ctrl and press the direction letter
|
|
1926
|
+
- For windows: \`Ctrl+n\` next window, \`Ctrl+p\` previous window
|
|
1927
|
+
|
|
1928
|
+
Ask them to try navigating between panes.
|
|
1929
|
+
|
|
1930
|
+
### 3. Clean up the test pane
|
|
1931
|
+
|
|
1932
|
+
Once they confirm they can navigate, close the extra pane:
|
|
1933
|
+
\`\`\`
|
|
1934
|
+
tmux kill-pane -t {the other pane}
|
|
1935
|
+
\`\`\`
|
|
1936
|
+
|
|
1937
|
+
Or tell them they can type \`exit\` in the extra pane to close it.
|
|
1938
|
+
|
|
1939
|
+
### 4. Teach essential commands
|
|
1940
|
+
|
|
1941
|
+
- **Detach**: \`Ctrl-b d\` \u2014 leaves tmux running in background, returns to normal terminal
|
|
1942
|
+
- **Reattach**: \`tmux attach\` (or \`tmux a\`) \u2014 reconnects to the running session
|
|
1943
|
+
- **Scroll up/down**: \`Ctrl+u\` / \`Ctrl+d\` \u2014 scroll half-page up/down (no prefix needed). Press \`q\` to exit scroll mode.
|
|
1944
|
+
- **New window**: \`Ctrl-b n\` \u2014 opens a new window in the current directory
|
|
1945
|
+
- **Kill pane**: \`Ctrl-b x\` \u2014 closes the current pane and rebalances layout
|
|
1946
|
+
- **Re-tile**: \`Ctrl-b =\` \u2014 rebalance all panes to equal widths
|
|
1947
|
+
|
|
1948
|
+
### 5. Verification
|
|
1949
|
+
|
|
1950
|
+
Ask the user to confirm: "Can you navigate between panes with Ctrl+h and Ctrl+l?"
|
|
1951
|
+
|
|
1952
|
+
Once confirmed, proceed:
|
|
1953
|
+
\`\`\`
|
|
1954
|
+
sisyphus getting-started --tutorial 2
|
|
1955
|
+
\`\`\`
|
|
1956
|
+
</claude-instructions>
|
|
1957
|
+
`);
|
|
1958
|
+
}
|
|
1959
|
+
function printStep2() {
|
|
1960
|
+
const nvimInstalled = isNvimAvailable();
|
|
1961
|
+
console.log(`
|
|
1962
|
+
<claude-instructions>
|
|
1963
|
+
# Sisyphus Getting Started \u2014 Step 2: Nvim Basics
|
|
1964
|
+
|
|
1965
|
+
## Environment Data
|
|
1966
|
+
- nvimInstalled: ${nvimInstalled}
|
|
1967
|
+
|
|
1968
|
+
## Instructions for Claude
|
|
1969
|
+
|
|
1970
|
+
This step is OPTIONAL. Nvim is useful for reviewing and editing files when you jump into agent panes, but not required.
|
|
1971
|
+
|
|
1972
|
+
Note: The sisyphus dashboard has keys that auto-open files in nvim \u2014 users don't need to know how to open files from the command line. Focus on what they'll need once they're INSIDE nvim.
|
|
1973
|
+
|
|
1974
|
+
### If nvim is NOT installed (nvimInstalled: false)
|
|
1975
|
+
|
|
1976
|
+
Ask the user: "Neovim is handy for reviewing and editing files in tmux panes. Want me to install it, or skip this step?"
|
|
1977
|
+
|
|
1978
|
+
- **Install**: Run \`brew install neovim\` (macOS) or suggest their package manager
|
|
1979
|
+
- **Skip**: That's fine \u2014 they can use \`cat\`, \`less\`, or any editor they prefer. Proceed to step 3.
|
|
1980
|
+
|
|
1981
|
+
### If nvim IS installed (nvimInstalled: true)
|
|
1982
|
+
|
|
1983
|
+
Briefly explain the key concept \u2014 nvim has two modes:
|
|
1984
|
+
|
|
1985
|
+
- **Normal mode** (default): Keys are commands, not text. This is where you navigate.
|
|
1986
|
+
- **Insert mode**: Press \`i\` to enter. Now you type normally. \`Esc\` goes back to normal.
|
|
1987
|
+
|
|
1988
|
+
Then tell the user: "I'm going to open an interactive tutorial file in a pane to your right. It walks you through everything \u2014 navigation, editing, saving. Follow the instructions inside the file."
|
|
1989
|
+
|
|
1990
|
+
Open the bundled tutorial file in a split pane:
|
|
1991
|
+
\`\`\`
|
|
1992
|
+
cp ${templatePath("nvim-tutorial.txt")} /tmp/sisyphus-nvim-tutorial.txt
|
|
1993
|
+
tmux split-window -h "nvim /tmp/sisyphus-nvim-tutorial.txt"
|
|
1994
|
+
\`\`\`
|
|
1995
|
+
|
|
1996
|
+
Tell them to click on the right pane (or \`Ctrl+l\`) and follow the instructions in the file. When they \`:wq\` or \`ZZ\`, the pane closes and they're back in Claude.
|
|
1997
|
+
|
|
1998
|
+
Tell them: "When you jump into an agent's pane and the dashboard opens a file, you'll land in normal mode. Now you know how to look around, make edits, and get out."
|
|
1999
|
+
|
|
2000
|
+
### Verification
|
|
2001
|
+
|
|
2002
|
+
Ask if they were able to edit and save the file (or if they skipped).
|
|
2003
|
+
|
|
2004
|
+
Proceed:
|
|
2005
|
+
\`\`\`
|
|
2006
|
+
sisyphus getting-started --tutorial 3
|
|
2007
|
+
\`\`\`
|
|
2008
|
+
</claude-instructions>
|
|
2009
|
+
`);
|
|
2010
|
+
}
|
|
2011
|
+
function printStep3() {
|
|
2012
|
+
let rightOptionKeyStatus = "unknown";
|
|
2013
|
+
const terminal = detectTerminal();
|
|
2014
|
+
if (!terminal.isIterm) {
|
|
2015
|
+
rightOptionKeyStatus = "not-iterm";
|
|
2016
|
+
} else {
|
|
2017
|
+
const result = checkItermOptionKey();
|
|
2018
|
+
if (!result.checked) {
|
|
2019
|
+
rightOptionKeyStatus = "could-not-check";
|
|
2020
|
+
} else if (result.allCorrect) {
|
|
2021
|
+
rightOptionKeyStatus = "ok";
|
|
1436
2022
|
} else {
|
|
1437
|
-
|
|
1438
|
-
" \u2713 You are inside tmux. Good to go.",
|
|
1439
|
-
""
|
|
1440
|
-
);
|
|
2023
|
+
rightOptionKeyStatus = `incorrect:${result.incorrectProfiles.join(",")}`;
|
|
1441
2024
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
2025
|
+
}
|
|
2026
|
+
console.log(`
|
|
2027
|
+
<claude-instructions>
|
|
2028
|
+
# Sisyphus Getting Started \u2014 Step 3: Sisyphus Concepts & Keybinds
|
|
2029
|
+
|
|
2030
|
+
## Environment Data
|
|
2031
|
+
- rightOptionKeyStatus: ${rightOptionKeyStatus}
|
|
2032
|
+
|
|
2033
|
+
## Instructions for Claude
|
|
2034
|
+
|
|
2035
|
+
### 1. CRITICAL FIRST: Right Option Key Setup
|
|
2036
|
+
|
|
2037
|
+
**This must be done before anything else.** Sisyphus keybinds use the Option key as "Meta". By default, macOS terminals send special characters when you press Option (e.g., Option+s types \`\xDF\`). We need the RIGHT Option key to send escape sequences instead.
|
|
2038
|
+
|
|
2039
|
+
**Check the environment data above:**
|
|
2040
|
+
|
|
2041
|
+
- **rightOptionKeyStatus: ok** \u2014 They're all set, briefly confirm and move on.
|
|
2042
|
+
|
|
2043
|
+
- **rightOptionKeyStatus: incorrect:ProfileName** \u2014 Walk them through the fix:
|
|
2044
|
+
|
|
2045
|
+
> Your Right Option key isn't configured correctly yet. Here's how to fix it:
|
|
2046
|
+
>
|
|
2047
|
+
> 1. Open **iTerm2 Settings** (Cmd+,)
|
|
2048
|
+
> 2. Go to **Profiles** \u2192 select your profile (shown above)
|
|
2049
|
+
> 3. Click the **Keys** tab
|
|
2050
|
+
> 4. At the bottom, find **Right Option Key**
|
|
2051
|
+
> 5. Change it from **Normal** to **Esc+**
|
|
2052
|
+
>
|
|
2053
|
+
> \`\`\`
|
|
2054
|
+
> \u250C\u2500 iTerm2 Settings \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\u2510
|
|
2055
|
+
> \u2502 Profiles > Keys \u2502
|
|
2056
|
+
> \u2502 \u2502
|
|
2057
|
+
> \u2502 Right Option Key: \u2502
|
|
2058
|
+
> \u2502 \u25CB Normal (sends special chars like \xDF) \u2502
|
|
2059
|
+
> \u2502 \u25CF Esc+ (sends escape sequences) \u2190 \u2713 \u2502
|
|
2060
|
+
> \u2514\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
2061
|
+
> \`\`\`
|
|
2062
|
+
>
|
|
2063
|
+
> **Why right and not left?** You'll still want the left Option key for
|
|
2064
|
+
> typing special characters (accents, symbols). The right Option key
|
|
2065
|
+
> becomes your "Meta" key for tmux/sisyphus keybinds.
|
|
2066
|
+
|
|
2067
|
+
After they change it, have them verify by re-running \`sisyphus doctor\` \u2014 look for "Right Option Key: Esc+".
|
|
2068
|
+
|
|
2069
|
+
- **rightOptionKeyStatus: not-iterm** \u2014 They're not using iTerm2. Explain:
|
|
2070
|
+
> Sisyphus keybinds use Option as Meta. In iTerm2 this is configured via
|
|
2071
|
+
> "Right Option Key \u2192 Esc+". For your terminal, look for a similar setting
|
|
2072
|
+
> like "Option sends Meta" or "Option sends Esc+". Without this, pressing
|
|
2073
|
+
> Option+s will type a special character instead of triggering the keybind.
|
|
2074
|
+
|
|
2075
|
+
- **rightOptionKeyStatus: could-not-check** or **unknown** \u2014 Ask them to manually check:
|
|
2076
|
+
> Press Option+s in your terminal. If you see \`\xDF\` (or another special character),
|
|
2077
|
+
> your Option key needs to be reconfigured. In iTerm2: Settings \u2192 Profiles \u2192 Keys \u2192
|
|
2078
|
+
> Right Option Key \u2192 Esc+.
|
|
2079
|
+
|
|
2080
|
+
### 2. Explain the session model
|
|
2081
|
+
|
|
2082
|
+
This is the KEY concept. Use the diagram and be clear:
|
|
2083
|
+
|
|
2084
|
+
\`\`\`
|
|
2085
|
+
YOUR tmux session ("work") Sisyphus tmux session ("sisyphus-abc123")
|
|
2086
|
+
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
2087
|
+
\u2502 \u2502 \u2502 Orch \u2502 Agent \u2502 Agent \u2502
|
|
2088
|
+
\u2502 Your normal work \u2502 \u2190\u2500\u2500\u2192 \u2502 (yellow)\u2502 (blue) \u2502 (green) \u2502
|
|
2089
|
+
\u2502 + dashboard \u2502 \u2502 \u2502 \u2502 \u2502
|
|
2090
|
+
\u2502 \u2502 \u2502 Plans & \u2502 Writes \u2502 Writes \u2502
|
|
2091
|
+
\u2502 \u2502 \u2502 assigns \u2502 code \u2502 tests \u2502
|
|
2092
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
2093
|
+
\`\`\`
|
|
2094
|
+
|
|
2095
|
+
Key points:
|
|
2096
|
+
- Sisyphus creates its OWN tmux session \u2014 it doesn't clutter yours
|
|
2097
|
+
- The **orchestrator** (yellow pane) plans work and spawns agents
|
|
2098
|
+
- **Agents** (colored panes) work in parallel on subtasks
|
|
2099
|
+
- Your session stays clean \u2014 you get a **dashboard** for monitoring
|
|
2100
|
+
- You can jump between your session and the sisyphus session to observe
|
|
2101
|
+
|
|
2102
|
+
### 3. Teach keybinds
|
|
2103
|
+
|
|
2104
|
+
Two keybinds to remember (both use the RIGHT Option key):
|
|
2105
|
+
|
|
2106
|
+
| Keybind | Action |
|
|
2107
|
+
|---------|--------|
|
|
2108
|
+
| Right Option + s | Cycle through sisyphus sessions |
|
|
2109
|
+
| Right Option + Shift + s | Jump back to dashboard |
|
|
2110
|
+
|
|
2111
|
+
### 4. Verify keybinds are installed
|
|
2112
|
+
|
|
2113
|
+
Run \`sisyphus doctor\` and check the output. Look for:
|
|
2114
|
+
- "Cycle script" \u2014 should be \u2713
|
|
2115
|
+
- "Tmux keybind" \u2014 should be \u2713
|
|
2116
|
+
- "Right Option Key" \u2014 should be "Esc+"
|
|
2117
|
+
|
|
2118
|
+
If cycle script or keybind is missing, run: \`sisyphus setup-keybind\`
|
|
2119
|
+
|
|
2120
|
+
### 5. Test the keybind
|
|
2121
|
+
|
|
2122
|
+
Have the user try pressing Right Option + s. Nothing should happen yet (no sisyphus session running) \u2014 and that's fine. The important thing is no special character appears.
|
|
2123
|
+
|
|
2124
|
+
If they see \`\xDF\` or similar, circle back to the Right Option Key setup above.
|
|
2125
|
+
|
|
2126
|
+
### 6. Verification
|
|
2127
|
+
|
|
2128
|
+
Confirm:
|
|
2129
|
+
- They understand the two-session model (their session vs sisyphus session)
|
|
2130
|
+
- \`sisyphus doctor\` shows keybinds installed AND Right Option Key: Esc+
|
|
2131
|
+
- Right Option + s doesn't produce a special character
|
|
2132
|
+
|
|
2133
|
+
Proceed:
|
|
2134
|
+
\`\`\`
|
|
2135
|
+
sisyphus getting-started --tutorial 4
|
|
2136
|
+
\`\`\`
|
|
2137
|
+
</claude-instructions>
|
|
2138
|
+
`);
|
|
2139
|
+
}
|
|
2140
|
+
function printStep4() {
|
|
2141
|
+
console.log(`
|
|
2142
|
+
<claude-instructions>
|
|
2143
|
+
# Sisyphus Getting Started \u2014 Step 4: Demo Session
|
|
2144
|
+
|
|
2145
|
+
## Instructions for Claude
|
|
2146
|
+
|
|
2147
|
+
This is the grand finale \u2014 a live demo session.
|
|
2148
|
+
|
|
2149
|
+
### 1. Health check
|
|
2150
|
+
|
|
2151
|
+
Run \`sisyphus doctor\` first. If any checks are failing, help the user fix them before proceeding.
|
|
2152
|
+
All core checks (tmux, daemon, keybinds) should be \u2713.
|
|
2153
|
+
|
|
2154
|
+
### 2. BEFORE launching: Teach navigation
|
|
2155
|
+
|
|
2156
|
+
**This is critical.** When \`sisyphus start\` runs, it auto-opens the dashboard in a new tmux window. The user will suddenly be looking at the dashboard and may feel "stuck". Teach them how to navigate BEFORE launching:
|
|
2157
|
+
|
|
2158
|
+
Explain clearly:
|
|
2159
|
+
|
|
2160
|
+
> Before we launch, you need to know how to move between tmux windows. Right now you're in a window with Claude. When sisyphus starts, it'll open a dashboard in a new window. Think of windows like tabs:
|
|
2161
|
+
>
|
|
2162
|
+
> \`\`\`
|
|
2163
|
+
> Window 1 (you are here) Window 2 (dashboard)
|
|
2164
|
+
> \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
2165
|
+
> \u2502 Claude Code \u2502 \u2502 Sisyphus \u2502
|
|
2166
|
+
> \u2502 (this session) \u2502 \u2192 \u2502 Dashboard \u2502
|
|
2167
|
+
> \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
2168
|
+
> Ctrl+n \u2192 \u2190 Ctrl+p
|
|
2169
|
+
> \`\`\`
|
|
2170
|
+
>
|
|
2171
|
+
> - **\`Ctrl+n\`** \u2014 next window (go to dashboard)
|
|
2172
|
+
> - **\`Ctrl+p\`** \u2014 previous window (come back here)
|
|
2173
|
+
>
|
|
2174
|
+
> And remember from step 3:
|
|
2175
|
+
> - **Right Option + s** \u2014 jump to the sisyphus agent session (where you can watch agents work live)
|
|
2176
|
+
> - **Right Option + Shift + s** \u2014 jump back to dashboard
|
|
2177
|
+
|
|
2178
|
+
Have the user confirm they understand these keybinds before proceeding.
|
|
2179
|
+
|
|
2180
|
+
### 3. Set expectations, copy demo app, and launch
|
|
2181
|
+
|
|
2182
|
+
First, copy the demo todo app to a temp directory and init a git repo (sisyphus needs git):
|
|
2183
|
+
\`\`\`
|
|
2184
|
+
rm -rf /tmp/sisyphus-tutorial-demo
|
|
2185
|
+
cp -r ${templatePath("tutorial-demo")} /tmp/sisyphus-tutorial-demo
|
|
2186
|
+
git -C /tmp/sisyphus-tutorial-demo init
|
|
2187
|
+
git -C /tmp/sisyphus-tutorial-demo add -A
|
|
2188
|
+
git -C /tmp/sisyphus-tutorial-demo commit -m "Initial todo app"
|
|
2189
|
+
\`\`\`
|
|
2190
|
+
|
|
2191
|
+
Tell the user:
|
|
2192
|
+
|
|
2193
|
+
> I've set up a small todo app in /tmp/sisyphus-tutorial-demo \u2014 a Node.js API
|
|
2194
|
+
> with a few files. I'm going to launch sisyphus on it. Here's what will happen:
|
|
2195
|
+
> 1. The dashboard opens automatically (you'll be switched to it)
|
|
2196
|
+
> 2. Press **Ctrl+p** to come back here to Claude \u2014 I'll guide you through what to watch
|
|
2197
|
+
> 3. The session takes a few minutes. You can watch agents work live!
|
|
2198
|
+
|
|
2199
|
+
Then launch from the demo directory:
|
|
2200
|
+
\`\`\`
|
|
2201
|
+
cd /tmp/sisyphus-tutorial-demo && sisyphus 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."
|
|
2202
|
+
\`\`\`
|
|
2203
|
+
|
|
2204
|
+
After launching, tell them:
|
|
2205
|
+
|
|
2206
|
+
> The dashboard just opened. Press **Ctrl+p** to come back here \u2014 I'll provide live commentary as the session runs so you know what's happening.
|
|
2207
|
+
|
|
2208
|
+
Wait for them to confirm they're back, then start live commentary.
|
|
2209
|
+
|
|
2210
|
+
### 4. Live commentary loop
|
|
2211
|
+
|
|
2212
|
+
**This is the most important part of the demo.** Don't just launch and wait \u2014 actively narrate.
|
|
2213
|
+
|
|
2214
|
+
Once the user is back, start a polling loop. Every ~45 seconds, run \`sisyphus status --verbose <session-id>\` and provide SHORT, contextual commentary about what's happening. The \`--verbose\` flag shows agent instructions, full roadmap, cycle logs, and live pane output from the orchestrator and running agents \u2014 use this rich data to narrate what's actually happening, not just phase names.
|
|
2215
|
+
|
|
2216
|
+
**How to narrate each phase:**
|
|
2217
|
+
|
|
2218
|
+
- **Cycle 1, no agents yet**: "The orchestrator is reading the codebase and planning. It's figuring out how to split the work. Check the dashboard (\`Ctrl+n\`) \u2014 you'll see the roadmap updating."
|
|
2219
|
+
|
|
2220
|
+
- **Agents spawning**: "Agents just spawned! You should see new panes appearing. Try \`Right Option + s\` to jump to the sisyphus session and watch them work. Each colored pane is an independent Claude instance."
|
|
2221
|
+
|
|
2222
|
+
- **Agents working**: "Agent-001 is working on [X], Agent-002 is on [Y]. They're working in parallel \u2014 this is the key advantage of sisyphus. Jump over and watch if you like (\`Right Option + s\`)."
|
|
2223
|
+
|
|
2224
|
+
- **Agents submitting**: "Agent-001 just submitted its report! [N] more to go. When all agents finish, the orchestrator will respawn to review."
|
|
2225
|
+
|
|
2226
|
+
- **Between cycles**: "All agents done. The orchestrator is respawning with fresh context to review the reports and decide what's next. This is the cycle boundary \u2014 the orchestrator never runs out of context because it starts fresh each time."
|
|
2227
|
+
|
|
2228
|
+
- **Completion**: "The session is complete! Let me show you the results."
|
|
2229
|
+
|
|
2230
|
+
**Important:**
|
|
2231
|
+
- Keep commentary to 1-3 sentences per check \u2014 don't wall-of-text
|
|
2232
|
+
- Remind them of navigation keys when relevant ("jump over with Right Option + s to see this live")
|
|
2233
|
+
- If agents are still working with no change, say so briefly ("Still working... Agent-001 is the furthest along")
|
|
2234
|
+
- Reference specific agent names and tasks from the status output
|
|
2235
|
+
- Stop polling when status shows "completed"
|
|
2236
|
+
|
|
2237
|
+
Between polls, encourage the user to explore:
|
|
2238
|
+
> "While we wait, try jumping around: \`Ctrl+n\` for dashboard, \`Right Option + s\` for the agent session, \`Right Option + Shift + s\` to jump back. I'll keep narrating here."
|
|
2239
|
+
|
|
2240
|
+
### 5. After completion
|
|
2241
|
+
|
|
2242
|
+
Once the session shows "completed":
|
|
2243
|
+
|
|
2244
|
+
- Show them what the agents built: \`cd /tmp/sisyphus-tutorial-demo && git log --oneline\`
|
|
2245
|
+
- Run the tests to prove the work: \`cd /tmp/sisyphus-tutorial-demo && node --test test.js\`
|
|
2246
|
+
- Show the session artifacts: find the session dir in \`.sisyphus/sessions/\` and show \`roadmap.md\`
|
|
2247
|
+
- Explain: "Every session creates a roadmap, agent reports, and logs \u2014 all stored in .sisyphus/sessions/"
|
|
2248
|
+
|
|
2249
|
+
### 6. Proceed to wrap-up
|
|
2250
|
+
|
|
2251
|
+
Tell the user the demo is done. Then run:
|
|
2252
|
+
\`\`\`
|
|
2253
|
+
sisyphus getting-started --tutorial 5
|
|
2254
|
+
\`\`\`
|
|
2255
|
+
</claude-instructions>
|
|
2256
|
+
`);
|
|
2257
|
+
}
|
|
2258
|
+
function printStep5() {
|
|
2259
|
+
let recentCommits = "";
|
|
2260
|
+
let topLevelFiles = "";
|
|
2261
|
+
try {
|
|
2262
|
+
recentCommits = execSync9("git log --oneline -15 2>/dev/null", { encoding: "utf-8" }).trim();
|
|
2263
|
+
} catch {
|
|
2264
|
+
}
|
|
2265
|
+
try {
|
|
2266
|
+
topLevelFiles = execSync9("ls -1 2>/dev/null", { encoding: "utf-8" }).trim();
|
|
2267
|
+
} catch {
|
|
2268
|
+
}
|
|
2269
|
+
console.log(`
|
|
2270
|
+
<claude-instructions>
|
|
2271
|
+
# Sisyphus Getting Started \u2014 Step 5: What's Next
|
|
2272
|
+
|
|
2273
|
+
## Codebase Context
|
|
2274
|
+
<recent-commits>
|
|
2275
|
+
${recentCommits || "(no git repo detected)"}
|
|
2276
|
+
</recent-commits>
|
|
2277
|
+
|
|
2278
|
+
<top-level-files>
|
|
2279
|
+
${topLevelFiles || "(could not list)"}
|
|
2280
|
+
</top-level-files>
|
|
2281
|
+
|
|
2282
|
+
## Instructions for Claude
|
|
2283
|
+
|
|
2284
|
+
### 1. Congratulate them
|
|
2285
|
+
|
|
2286
|
+
Tell them they've completed the tutorial and recap what they learned:
|
|
2287
|
+
- tmux basics (sessions, panes, navigation)
|
|
2288
|
+
- nvim basics for reviewing files
|
|
2289
|
+
- The sisyphus session model (separate tmux session for orchestrator + agents)
|
|
2290
|
+
- Monitoring with dashboard and keybinds
|
|
2291
|
+
- A live session lifecycle
|
|
2292
|
+
|
|
2293
|
+
### 2. Navigation cheat sheet
|
|
2294
|
+
|
|
2295
|
+
| Key | Action |
|
|
2296
|
+
|-----|--------|
|
|
2297
|
+
| \`Ctrl+n\` / \`Ctrl+p\` | Next/previous tmux window |
|
|
2298
|
+
| \`Ctrl+h/j/k/l\` | Navigate between panes |
|
|
2299
|
+
| \`Right Option + s\` | Jump to sisyphus agent session |
|
|
2300
|
+
| \`Right Option + Shift + s\` | Jump to dashboard |
|
|
2301
|
+
|
|
2302
|
+
### 3. How to use sisyphus for REAL work
|
|
2303
|
+
|
|
2304
|
+
This is the most important part. Explain clearly:
|
|
2305
|
+
|
|
2306
|
+
> **Sisyphus is for big, end-to-end features \u2014 the kind that need exploration,
|
|
2307
|
+
> planning, and parallel implementation across multiple systems.**
|
|
2308
|
+
>
|
|
2309
|
+
> You don't need to define the task precisely. Broad is fine \u2014 the orchestrator
|
|
2310
|
+
> will explore the codebase, write specs, plan phases, and break it down itself.
|
|
2311
|
+
|
|
2312
|
+
**Real sisyphus sessions (from production use):**
|
|
2313
|
+
- "Design and implement a human-in-the-loop agent inbox system" \u2014 exploration, spec writing, DB schema, API endpoints, UI components, webhook integration, e2e validation
|
|
2314
|
+
- "Build multi-user organization features \u2014 invites, privilege gating, org switcher, workspace sharing, credit tracking" \u2014 touched auth, DB, API, UI, billing, permissions
|
|
2315
|
+
- "Rework all 5 worker onboarding templates to match production pipeline patterns" \u2014 mapped existing patterns, designed new architecture, implemented across templates, validated with e2e tests
|
|
2316
|
+
- "Autonomous failure detection system across 8 sequential phases" \u2014 monitoring, alerting, recovery, dashboard, with each phase building on the last
|
|
2317
|
+
- "Comprehensive code quality audit \u2014 find and fix dead code, null handling, useless fallbacks" \u2014 systematic codebase-wide analysis and cleanup
|
|
2318
|
+
- "Implement @requirements.md" \u2014 point it at a spec and let it go
|
|
2319
|
+
|
|
2320
|
+
**NOT good for sisyphus:**
|
|
2321
|
+
- Five unrelated small tasks bundled together ("fix the login bug, update the README, add a loading spinner") \u2014 these aren't one feature, they're a todo list
|
|
2322
|
+
- Something Claude Code in plan mode would handle \u2014 plan mode already handles substantial single-engineer work. If it fits in one Claude session, just do it directly.
|
|
2323
|
+
- Quick fixes, bug fixes, small refactors \u2014 use regular Claude Code
|
|
2324
|
+
|
|
2325
|
+
**How to start:**
|
|
2326
|
+
The easiest way is the \`/sisyphus:begin\` slash command inside Claude Code. Just tell Claude
|
|
2327
|
+
what you want to build and it'll hand it off to sisyphus with the right context.
|
|
2328
|
+
|
|
2329
|
+
Or directly: \`sisyphus start "your task" -c "any background context"\`
|
|
2330
|
+
|
|
2331
|
+
### 4. Suggest real tasks for THEIR codebase
|
|
2332
|
+
|
|
2333
|
+
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.
|
|
2334
|
+
|
|
2335
|
+
If there are no commits or files (e.g., they ran this from /tmp), skip this section.
|
|
2336
|
+
|
|
2337
|
+
Format as:
|
|
2338
|
+
> Based on your codebase, here are some tasks sisyphus would be great for:
|
|
2339
|
+
> - "..."
|
|
2340
|
+
> - "..."
|
|
2341
|
+
|
|
2342
|
+
### 5. There's more to learn
|
|
2343
|
+
|
|
2344
|
+
Tell them:
|
|
2345
|
+
|
|
2346
|
+
> There's actually a lot of depth to how sisyphus works \u2014 the design is intentional
|
|
2347
|
+
> and there's real reasoning behind why it does things the way it does. If you want
|
|
2348
|
+
> to understand the philosophy, or you want a deeper rundown on the dashboard,
|
|
2349
|
+
> monitoring, configuration, or how to steer sessions \u2014 just ask and I'll explain.
|
|
2350
|
+
|
|
2351
|
+
If the user says yes or asks to learn more, run \`sisyphus getting-started --explain\`
|
|
2352
|
+
and use its output to explain the system to them conversationally. Don't dump the whole
|
|
2353
|
+
thing \u2014 answer what they're curious about, using the reference as your source material.
|
|
2354
|
+
</claude-instructions>
|
|
2355
|
+
`);
|
|
2356
|
+
}
|
|
2357
|
+
function printExplain() {
|
|
2358
|
+
console.log(`
|
|
2359
|
+
<claude-instructions>
|
|
2360
|
+
# Sisyphus \u2014 Comprehensive Reference
|
|
2361
|
+
|
|
2362
|
+
This is a detailed reference for how sisyphus works. The user asked to understand
|
|
2363
|
+
sisyphus more deeply. Use this to answer their questions conversationally \u2014 don't dump
|
|
2364
|
+
the whole thing. Read through it, then respond to what they're curious about.
|
|
2365
|
+
|
|
2366
|
+
## Design Philosophy
|
|
2367
|
+
|
|
2368
|
+
Sisyphus is built on specific insights about how to get the best work out of LLM agents.
|
|
2369
|
+
These aren't arbitrary \u2014 each design decision solves a real failure mode.
|
|
2370
|
+
|
|
2371
|
+
### 1. The Orchestrator as "Human-in-the-Loop"
|
|
2372
|
+
|
|
2373
|
+
When you use Claude Code effectively, YOU are the orchestrator \u2014 you review work,
|
|
2374
|
+
steer direction, break problems down, and assign the next piece. Sisyphus automates
|
|
2375
|
+
that human role. The orchestrator does what a skilled developer does when prompting
|
|
2376
|
+
Claude: explore the codebase, understand the problem, write specs, plan phases,
|
|
2377
|
+
assign focused work, review results, and iterate.
|
|
2378
|
+
|
|
2379
|
+
The strategy layer mirrors how developers actually work on end-to-end features:
|
|
2380
|
+
explore, understand, spec, plan, implement, review, validate. The orchestrator
|
|
2381
|
+
follows this same workflow, but runs it with parallel agents.
|
|
2382
|
+
|
|
2383
|
+
### 2. Fresh Context Kills Shortcuts
|
|
2384
|
+
|
|
2385
|
+
The orchestrator is KILLED after every cycle and respawned fresh. This is the most
|
|
2386
|
+
important design decision.
|
|
2387
|
+
|
|
2388
|
+
When an LLM accumulates context over a long session, it starts taking shortcuts.
|
|
2389
|
+
It "knows" what it did earlier, so it skips re-reading, assumes things still hold,
|
|
2390
|
+
and builds on stale understanding. A fresh start forces honest reassessment every
|
|
2391
|
+
cycle \u2014 the orchestrator reads the actual state, not its memory of it.
|
|
2392
|
+
|
|
2393
|
+
This is inspired by adversarial training (think GANs) \u2014 better results come from
|
|
2394
|
+
adversarial pressure. Each fresh orchestrator effectively audits the previous cycle's
|
|
2395
|
+
work because it has no stake in defending prior decisions. It sees the roadmap, the
|
|
2396
|
+
reports, the code \u2014 and judges them with fresh eyes.
|
|
2397
|
+
|
|
2398
|
+
### 3. Single-Focus Agents
|
|
2399
|
+
|
|
2400
|
+
Each agent gets ONE task with a fully self-contained instruction. No context switching,
|
|
2401
|
+
no juggling multiple concerns, no "also while you're there could you..."
|
|
2402
|
+
|
|
2403
|
+
LLMs perform dramatically better when focused. An agent implementing a priority field
|
|
2404
|
+
doesn't think about the stats endpoint. It reads the relevant context, does its one
|
|
2405
|
+
thing well, and reports back. The orchestrator handles decomposition \u2014 agents handle
|
|
2406
|
+
execution.
|
|
2407
|
+
|
|
2408
|
+
### 4. Shared Context Directory (Saved Research)
|
|
2409
|
+
|
|
2410
|
+
Every session has a context/ directory where agents save research, specs, plans, and
|
|
2411
|
+
design docs. These files persist across ALL cycles and are visible to the orchestrator
|
|
2412
|
+
and subsequent agents.
|
|
2413
|
+
|
|
2414
|
+
This means research is never repeated. Cycle 1 agents explore and write findings to
|
|
2415
|
+
context/explore-auth-system.md. Cycle 3 agents read those findings and build on them.
|
|
2416
|
+
Knowledge accumulates even though the orchestrator itself is stateless.
|
|
2417
|
+
|
|
2418
|
+
### 5. Two-Layer Planning (Strategy + Roadmap)
|
|
2419
|
+
|
|
2420
|
+
The system maintains two documents at different abstraction levels:
|
|
2421
|
+
|
|
2422
|
+
**strategy.md** \u2014 The high-level problem-solving map. What phases exist, what gates
|
|
2423
|
+
between them, what backtrack paths exist. Updated every few cycles when the shape of
|
|
2424
|
+
work changes. Helps the orchestrator see the forest.
|
|
2425
|
+
|
|
2426
|
+
**roadmap.md** \u2014 Working memory. Updated every cycle. Current Stage, Exit Criteria,
|
|
2427
|
+
Active Context, Next Steps. The orchestrator reads this first each cycle to understand
|
|
2428
|
+
where things stand. Helps the orchestrator see the trees.
|
|
2429
|
+
|
|
2430
|
+
This prevents the failure mode where a single document becomes either too abstract
|
|
2431
|
+
to act on or too detailed to show the big picture.
|
|
2432
|
+
|
|
2433
|
+
### 6. Adversarial Review Is Built In
|
|
2434
|
+
|
|
2435
|
+
The orchestrator doesn't just implement \u2014 it runs mandatory critique cycles. After
|
|
2436
|
+
implementation, review agents attack different dimensions: code reuse, quality,
|
|
2437
|
+
efficiency, correctness. Fix agents address the findings. Re-review until only nits
|
|
2438
|
+
remain. Multiple agents auditing each other produces better results than any single
|
|
2439
|
+
agent reviewing its own work.
|
|
2440
|
+
|
|
2441
|
+
The rule: never let 2+ stages complete without critique. Small issues compound into
|
|
2442
|
+
architectural problems if unchecked.
|
|
2443
|
+
|
|
2444
|
+
### 7. Evidence Over Assumptions
|
|
2445
|
+
|
|
2446
|
+
Validation requires PROOF \u2014 command output, test results, HTTP responses. "The code
|
|
2447
|
+
looks correct" is not evidence. "All 14 tests pass" is. This catches the gap between
|
|
2448
|
+
code that looks right and code that works.
|
|
2449
|
+
|
|
2450
|
+
## Architecture Overview
|
|
2451
|
+
|
|
2452
|
+
\`\`\`
|
|
2453
|
+
\u250C\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
2454
|
+
\u2502 USER'S TMUX SESSION \u2502
|
|
2455
|
+
\u2502 \u2502
|
|
2456
|
+
\u2502 \u250C\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\u2510 \u250C\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\u2500\u2500\u2500\u2500\u2500\u2510 \u2502
|
|
2457
|
+
\u2502 \u2502 Window 1: Claude Code \u2502 \u2502 Window 2: Dashboard (TUI) \u2502 \u2502
|
|
2458
|
+
\u2502 \u2502 \u2502 \u2502 \u2502 \u2502
|
|
2459
|
+
\u2502 \u2502 User's normal work \u2502 \u2502 Real-time session monitor \u2502 \u2502
|
|
2460
|
+
\u2502 \u2502 + this conversation \u2502 \u2502 Roadmap, agents, reports \u2502 \u2502
|
|
2461
|
+
\u2502 \u2502 \u2502 \u2502 Interactive controls \u2502 \u2502
|
|
2462
|
+
\u2502 \u2514\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\u2518 \u2514\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\u2500\u2500\u2500\u2500\u2500\u2518 \u2502
|
|
2463
|
+
\u2514\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\u2500\u2500\u2500\u2500\u2500\u252C\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\u2500\u2500\u2500\u2500\u2500\u2518
|
|
2464
|
+
\u2502 Right Option+s / Right Option+Shift+s
|
|
2465
|
+
\u25BC
|
|
2466
|
+
\u250C\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
2467
|
+
\u2502 SISYPHUS TMUX SESSION \u2502
|
|
2468
|
+
\u2502 (created per sisyphus session) \u2502
|
|
2469
|
+
\u2502 \u2502
|
|
2470
|
+
\u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502
|
|
2471
|
+
\u2502 \u2502 Orch \u2502 Agent \u2502 Agent \u2502 Agent \u2502 \u2190 panes \u2502
|
|
2472
|
+
\u2502 \u2502 (yellow) \u2502 (blue) \u2502 (green) \u2502 (magenta)\u2502 \u2502
|
|
2473
|
+
\u2502 \u2502 \u2502 \u2502 \u2502 \u2502 \u2502
|
|
2474
|
+
\u2502 \u2502 Plans, \u2502 Impl \u2502 Tests \u2502 Docs \u2502 \u2190 each is a \u2502
|
|
2475
|
+
\u2502 \u2502 assigns, \u2502 feature \u2502 \u2502 \u2502 Claude Code \u2502
|
|
2476
|
+
\u2502 \u2502 reviews \u2502 \u2502 \u2502 \u2502 instance \u2502
|
|
2477
|
+
\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502
|
|
2478
|
+
\u2514\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
2479
|
+
\u2502
|
|
2480
|
+
\u25BC
|
|
2481
|
+
\u250C\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
2482
|
+
\u2502 DAEMON (sisyphusd) \u2502
|
|
2483
|
+
\u2502 Background process via launchd \u2502
|
|
2484
|
+
\u2502 \u2502
|
|
2485
|
+
\u2502 Listens on ~/.sisyphus/daemon.sock \u2502
|
|
2486
|
+
\u2502 Manages session lifecycle, pane monitoring, state persistence \u2502
|
|
2487
|
+
\u2502 Polls panes to detect when agents/orchestrator finish \u2502
|
|
2488
|
+
\u2514\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
2489
|
+
\`\`\`
|
|
2490
|
+
|
|
2491
|
+
## The Session Lifecycle (in detail)
|
|
2492
|
+
|
|
2493
|
+
\`\`\`
|
|
2494
|
+
\u250C\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
2495
|
+
\u2502 SESSION LIFECYCLE \u2502
|
|
2496
|
+
\u2502 \u2502
|
|
2497
|
+
\u2502 sisyphus start "task" \u2502
|
|
2498
|
+
\u2502 \u2502 \u2502
|
|
2499
|
+
\u2502 \u25BC \u2502
|
|
2500
|
+
\u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 spawn agents \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502
|
|
2501
|
+
\u2502 \u2502 Orch \u2502 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 \u2502 Agents work \u2502 \u2502
|
|
2502
|
+
\u2502 \u2502 plans \u2502 then yields \u2502 in parallel \u2502 \u2502
|
|
2503
|
+
\u2502 \u2514\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502
|
|
2504
|
+
\u2502 \u2502 \u2502 each calls \u2502
|
|
2505
|
+
\u2502 \u2502 orchestrator \u2502 sisyphus submit \u2502
|
|
2506
|
+
\u2502 \u2502 is KILLED \u2502 when done \u2502
|
|
2507
|
+
\u2502 \u2502 \u25BC \u2502
|
|
2508
|
+
\u2502 \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502
|
|
2509
|
+
\u2502 \u2502 \u2502 All agents \u2502 \u2502
|
|
2510
|
+
\u2502 \u2502 \u2502 finished? \u2502 \u2502
|
|
2511
|
+
\u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502
|
|
2512
|
+
\u2502 \u2502 \u2502 yes \u2502
|
|
2513
|
+
\u2502 \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502
|
|
2514
|
+
\u2502 \u2502 \u25BC \u2502
|
|
2515
|
+
\u2502 \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502
|
|
2516
|
+
\u2502 \u2514\u2500\u2500\u2500\u2500 \u2502 Respawn \u2502 Fresh orchestrator with full state \u2502
|
|
2517
|
+
\u2502 next cycle \u2502 Orch \u2502 Reviews reports, plans next cycle \u2502
|
|
2518
|
+
\u2502 \u2514\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2518 \u2502
|
|
2519
|
+
\u2502 \u2502 \u2502
|
|
2520
|
+
\u2502 \u25BC \u2502
|
|
2521
|
+
\u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502
|
|
2522
|
+
\u2502 \u2502 More work \u2502\u2500\u2500yes\u2500\u2500\u2192 \u2502 Spawn \u2502 \u2192 (loop) \u2502
|
|
2523
|
+
\u2502 \u2502 needed? \u2502 \u2502 agents \u2502 \u2502
|
|
2524
|
+
\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502
|
|
2525
|
+
\u2502 \u2502 no \u2502
|
|
2526
|
+
\u2502 \u25BC \u2502
|
|
2527
|
+
\u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502
|
|
2528
|
+
\u2502 \u2502 sisyphus \u2502 \u2502
|
|
2529
|
+
\u2502 \u2502 complete \u2502 \u2502
|
|
2530
|
+
\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502
|
|
2531
|
+
\u2514\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
2532
|
+
\`\`\`
|
|
2533
|
+
|
|
2534
|
+
**Key insight**: The orchestrator is STATELESS. It gets killed after each yield and
|
|
2535
|
+
respawned fresh with the complete session state (roadmap, agent reports, cycle history).
|
|
2536
|
+
This means it never runs out of context, no matter how many cycles a session takes.
|
|
2537
|
+
|
|
2538
|
+
## The Dashboard
|
|
2539
|
+
|
|
2540
|
+
The dashboard is a real-time TUI that shows session state. Launch with \`sisyphus dashboard\`
|
|
2541
|
+
or it auto-opens when a session starts.
|
|
2542
|
+
|
|
2543
|
+
**Dashboard sections:**
|
|
2544
|
+
- **Header**: Session ID, status, task description
|
|
2545
|
+
- **Roadmap**: Current strategic plan with checked/unchecked items
|
|
2546
|
+
- **Agents**: List of all agents with status, duration, and report summaries
|
|
2547
|
+
- **Cycles**: Orchestrator cycle history
|
|
2548
|
+
- **Messages**: Recent session messages
|
|
2549
|
+
|
|
2550
|
+
**Dashboard keys:**
|
|
2551
|
+
| Key | Action |
|
|
2552
|
+
|-----|--------|
|
|
2553
|
+
| \`m\` | Message the orchestrator (steer direction mid-session) |
|
|
2554
|
+
| \`w\` | Jump to the sisyphus tmux session (watch agents work) |
|
|
2555
|
+
| \`k\` | Kill the session |
|
|
2556
|
+
| \`r\` | Resume a paused/completed session |
|
|
2557
|
+
| \`q\` | Quit the dashboard |
|
|
2558
|
+
| \`\u2191/\u2193\` | Scroll through content |
|
|
2559
|
+
| \`Tab\` | Cycle through sections |
|
|
2560
|
+
|
|
2561
|
+
**The \`m\` key is the most powerful feature.** You can message the orchestrator at any time
|
|
2562
|
+
to course-correct: "Focus on the API layer first", "Skip the tests for now",
|
|
2563
|
+
"The approach for auth is wrong, use JWT instead". The orchestrator reads these
|
|
2564
|
+
messages when it respawns each cycle.
|
|
2565
|
+
|
|
2566
|
+
## Monitoring Strategy
|
|
2567
|
+
|
|
2568
|
+
Sisyphus sessions should be actively monitored. Here's what to watch for:
|
|
2569
|
+
|
|
2570
|
+
**Things that go wrong:**
|
|
2571
|
+
- Agents stuck waiting for user input (they're autonomous \u2014 they shouldn't need input)
|
|
2572
|
+
- Agents going down rabbit holes or working on the wrong thing
|
|
2573
|
+
- Merge conflicts between agents touching the same files
|
|
2574
|
+
- Orchestrator spawning too many agents or too few
|
|
2575
|
+
- Agents crashing or getting killed unexpectedly
|
|
2576
|
+
|
|
2577
|
+
**When to intervene:**
|
|
2578
|
+
- Use \`m\` in the dashboard to message the orchestrator with corrections
|
|
2579
|
+
- Use \`sisyphus kill <id>\` to stop a runaway session
|
|
2580
|
+
- Use \`sisyphus resume <id> "new instructions"\` to restart with different direction
|
|
2581
|
+
|
|
2582
|
+
**Useful monitoring commands:**
|
|
2583
|
+
\`\`\`
|
|
2584
|
+
sisyphus status <id> # Quick status check
|
|
2585
|
+
sisyphus status --verbose <id> # Full detail: roadmap, pane output, agent instructions
|
|
2586
|
+
sisyphus dashboard # Interactive TUI
|
|
2587
|
+
tail -f ~/.sisyphus/daemon.log # Daemon activity log
|
|
2588
|
+
\`\`\`
|
|
2589
|
+
|
|
2590
|
+
## The .sisyphus/ Directory
|
|
2591
|
+
|
|
2592
|
+
Everything sisyphus does lives in a \`.sisyphus/\` directory at the root of your project.
|
|
2593
|
+
This is project-local \u2014 each project gets its own. It contains:
|
|
2594
|
+
|
|
2595
|
+
\`\`\`
|
|
2596
|
+
.sisyphus/
|
|
2597
|
+
\u251C\u2500\u2500 config.json # Project-specific config (model, poll interval, etc.)
|
|
2598
|
+
\u251C\u2500\u2500 orchestrator.md # Optional custom orchestrator prompt override
|
|
2599
|
+
\u2514\u2500\u2500 sessions/
|
|
2600
|
+
\u251C\u2500\u2500 <session-id-1>/ # Each session gets its own directory
|
|
2601
|
+
\u251C\u2500\u2500 <session-id-2>/
|
|
2602
|
+
\u2514\u2500\u2500 ...
|
|
2603
|
+
\`\`\`
|
|
2604
|
+
|
|
2605
|
+
There's also a global directory at \`~/.sisyphus/\` for the daemon socket, PID file,
|
|
2606
|
+
logs, keybind scripts, and global config. But the session state \u2014 the roadmaps,
|
|
2607
|
+
reports, context files, cycle logs \u2014 all lives in your project's \`.sisyphus/sessions/\`.
|
|
2608
|
+
|
|
2609
|
+
## Session Files
|
|
2610
|
+
|
|
2611
|
+
Every session creates a directory at \`.sisyphus/sessions/<id>/\` with:
|
|
2612
|
+
|
|
2613
|
+
\`\`\`
|
|
2614
|
+
.sisyphus/sessions/<id>/
|
|
2615
|
+
\u251C\u2500\u2500 state.json # Session state (agents, cycles, status)
|
|
2616
|
+
\u251C\u2500\u2500 roadmap.md # Strategic plan (updated by orchestrator each cycle)
|
|
2617
|
+
\u251C\u2500\u2500 goal.md # Original task description
|
|
2618
|
+
\u251C\u2500\u2500 strategy.md # High-level strategy notes
|
|
2619
|
+
\u251C\u2500\u2500 logs/
|
|
2620
|
+
\u2502 \u251C\u2500\u2500 cycle-000.md # What the orchestrator did in cycle 0
|
|
2621
|
+
\u2502 \u251C\u2500\u2500 cycle-001.md # What it did in cycle 1, etc.
|
|
2622
|
+
\u2502 \u2514\u2500\u2500 ...
|
|
2623
|
+
\u251C\u2500\u2500 reports/
|
|
2624
|
+
\u2502 \u251C\u2500\u2500 agent-001-final.md # Agent's final report
|
|
2625
|
+
\u2502 \u251C\u2500\u2500 agent-002-update.md # Agent's progress update
|
|
2626
|
+
\u2502 \u2514\u2500\u2500 ...
|
|
2627
|
+
\u251C\u2500\u2500 prompts/ # System/user prompts sent to orchestrator and agents
|
|
2628
|
+
\u2514\u2500\u2500 context/ # Shared context files for agents
|
|
2629
|
+
\`\`\`
|
|
2630
|
+
|
|
2631
|
+
## Configuration
|
|
2632
|
+
|
|
2633
|
+
**Global config**: \`~/.sisyphus/config.json\`
|
|
2634
|
+
**Project config**: \`.sisyphus/config.json\` (overrides global)
|
|
2635
|
+
|
|
2636
|
+
Options:
|
|
2637
|
+
- \`model\` \u2014 Claude model for orchestrator and agents
|
|
2638
|
+
- \`orchestratorPrompt\` \u2014 Path to custom orchestrator prompt
|
|
2639
|
+
- \`pollIntervalMs\` \u2014 How often daemon checks pane status (default: 2000)
|
|
2640
|
+
|
|
2641
|
+
## Starting Sessions \u2014 Best Practices
|
|
2642
|
+
|
|
2643
|
+
**The /sisyphus:begin slash command** is the recommended way to start. Inside Claude Code:
|
|
2644
|
+
\`\`\`
|
|
2645
|
+
/sisyphus:begin
|
|
2646
|
+
\`\`\`
|
|
2647
|
+
Then describe your task. Claude will hand it off with the right context.
|
|
2648
|
+
|
|
2649
|
+
**Direct CLI:**
|
|
2650
|
+
\`\`\`
|
|
2651
|
+
sisyphus start "task description" -c "background context"
|
|
2652
|
+
sisyphus start "Implement @requirements.md" -n my-feature
|
|
2653
|
+
\`\`\`
|
|
2654
|
+
|
|
2655
|
+
**Reference files with @**: \`sisyphus start "Build @docs/spec.md"\` \u2014 the orchestrator
|
|
2656
|
+
will read the referenced file as part of its planning.
|
|
2657
|
+
|
|
2658
|
+
**The -c flag** adds background context the orchestrator sees but doesn't act on directly.
|
|
2659
|
+
Use it for constraints: \`-c "Don't modify the auth module, use the existing API"\`
|
|
2660
|
+
|
|
2661
|
+
**The -n flag** gives the session a human-readable name for easier tracking.
|
|
2662
|
+
|
|
2663
|
+
## CLI Command Reference
|
|
2664
|
+
|
|
2665
|
+
| Command | Purpose |
|
|
2666
|
+
|---------|---------|
|
|
2667
|
+
| \`sisyphus start "task"\` | Start a new session |
|
|
2668
|
+
| \`sisyphus status [id]\` | Check session status |
|
|
2669
|
+
| \`sisyphus status -v [id]\` | Detailed status with pane output |
|
|
2670
|
+
| \`sisyphus list\` | List all sessions |
|
|
2671
|
+
| \`sisyphus dashboard\` | Open the TUI dashboard |
|
|
2672
|
+
| \`sisyphus resume <id> "msg"\` | Resume with new instructions |
|
|
2673
|
+
| \`sisyphus kill <id>\` | Stop a session |
|
|
2674
|
+
| \`sisyphus doctor\` | Check installation health |
|
|
2675
|
+
| \`sisyphus setup\` | Run setup/onboarding |
|
|
2676
|
+
| \`sisyphus setup-keybind\` | Install tmux keybinds |
|
|
2677
|
+
|
|
2678
|
+
## Troubleshooting
|
|
2679
|
+
|
|
2680
|
+
**Daemon not running:**
|
|
2681
|
+
\`\`\`
|
|
2682
|
+
sisyphusd restart
|
|
2683
|
+
\`\`\`
|
|
2684
|
+
|
|
2685
|
+
**Keybinds not working (special characters appear):**
|
|
2686
|
+
iTerm2 \u2192 Settings \u2192 Profiles \u2192 Keys \u2192 Right Option Key \u2192 Esc+
|
|
2687
|
+
|
|
2688
|
+
**Agents stuck:** Check \`sisyphus status --verbose <id>\` to see pane output. If an
|
|
2689
|
+
agent is waiting for input, kill the session and restart with clearer instructions.
|
|
2690
|
+
|
|
2691
|
+
**Dashboard not opening:** Run \`sisyphus dashboard\` manually. Must be inside tmux.
|
|
2692
|
+
|
|
2693
|
+
**Session seems hung:** Check \`tail -20 ~/.sisyphus/daemon.log\` for errors.
|
|
2694
|
+
The daemon polls panes every 2s \u2014 if a pane dies unexpectedly, it'll be detected.
|
|
2695
|
+
</claude-instructions>
|
|
2696
|
+
`);
|
|
2697
|
+
}
|
|
2698
|
+
var STEPS = [printStep0, printStep1, printStep2, printStep3, printStep4, printStep5];
|
|
2699
|
+
function registerGettingStarted(program2) {
|
|
2700
|
+
program2.command("getting-started").description("Interactive tutorial (best with Claude Code)").option("--tutorial <step>", "Tutorial step (0-5)", parseInt).option("--explain", "Comprehensive reference for how sisyphus works").action((opts) => {
|
|
2701
|
+
if (opts.explain) {
|
|
2702
|
+
printExplain();
|
|
2703
|
+
return;
|
|
2704
|
+
}
|
|
2705
|
+
if (opts.tutorial !== void 0) {
|
|
2706
|
+
const step = opts.tutorial;
|
|
2707
|
+
if (step < 0 || step > 5 || Number.isNaN(step)) {
|
|
2708
|
+
console.error(`Invalid tutorial step: ${opts.tutorial}. Must be 0-5.`);
|
|
2709
|
+
process.exit(1);
|
|
2710
|
+
}
|
|
2711
|
+
STEPS[step]();
|
|
2712
|
+
return;
|
|
2713
|
+
}
|
|
2714
|
+
if (!isClaudeCode()) {
|
|
2715
|
+
printNonClaudeMessage();
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2718
|
+
printStep0();
|
|
1526
2719
|
});
|
|
1527
2720
|
}
|
|
1528
2721
|
|
|
1529
2722
|
// src/cli/commands/init.ts
|
|
1530
|
-
import { existsSync as
|
|
1531
|
-
import { join as
|
|
2723
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2724
|
+
import { join as join7 } from "path";
|
|
1532
2725
|
var DEFAULT_CONFIG = {};
|
|
1533
2726
|
var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
|
|
1534
2727
|
|
|
@@ -1539,19 +2732,19 @@ var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
|
|
|
1539
2732
|
function registerInit(program2) {
|
|
1540
2733
|
program2.command("init").description("Initialize sisyphus configuration for this project").option("--orchestrator", "Also create a custom orchestrator prompt template").action((opts) => {
|
|
1541
2734
|
const cwd = process.cwd();
|
|
1542
|
-
const sisDir =
|
|
1543
|
-
const configPath =
|
|
1544
|
-
if (
|
|
2735
|
+
const sisDir = join7(cwd, ".sisyphus");
|
|
2736
|
+
const configPath = join7(sisDir, "config.json");
|
|
2737
|
+
if (existsSync7(configPath)) {
|
|
1545
2738
|
console.log(`Already initialized: ${configPath}`);
|
|
1546
2739
|
return;
|
|
1547
2740
|
}
|
|
1548
|
-
|
|
1549
|
-
|
|
2741
|
+
mkdirSync4(sisDir, { recursive: true });
|
|
2742
|
+
writeFileSync4(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n", "utf-8");
|
|
1550
2743
|
console.log(`Created ${configPath}`);
|
|
1551
2744
|
if (opts.orchestrator) {
|
|
1552
|
-
const orchPath =
|
|
1553
|
-
if (!
|
|
1554
|
-
|
|
2745
|
+
const orchPath = join7(sisDir, "orchestrator.md");
|
|
2746
|
+
if (!existsSync7(orchPath)) {
|
|
2747
|
+
writeFileSync4(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
|
|
1555
2748
|
console.log(`Created ${orchPath}`);
|
|
1556
2749
|
}
|
|
1557
2750
|
}
|
|
@@ -1564,6 +2757,94 @@ function registerInit(program2) {
|
|
|
1564
2757
|
});
|
|
1565
2758
|
}
|
|
1566
2759
|
|
|
2760
|
+
// src/cli/commands/setup.ts
|
|
2761
|
+
import { execSync as execSync10 } from "child_process";
|
|
2762
|
+
function getTmuxVersion() {
|
|
2763
|
+
try {
|
|
2764
|
+
return execSync10("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
2765
|
+
} catch {
|
|
2766
|
+
return "installed";
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
function printResults(result, daemonOk, keybindMsg) {
|
|
2770
|
+
console.log("");
|
|
2771
|
+
console.log("Setting up Sisyphus...");
|
|
2772
|
+
console.log("");
|
|
2773
|
+
if (result.tmuxInstalled) {
|
|
2774
|
+
const detail = getTmuxVersion();
|
|
2775
|
+
console.log(` \u2713 tmux: ${detail}${result.tmuxAutoInstalled ? " (just installed)" : ""}`);
|
|
2776
|
+
} else {
|
|
2777
|
+
const hint = process.platform === "darwin" ? "Install Homebrew (https://brew.sh) then: brew install tmux" : "apt install tmux (Debian/Ubuntu) or your package manager";
|
|
2778
|
+
console.log(` \u2717 tmux: Not installed \u2014 ${hint}`);
|
|
2779
|
+
}
|
|
2780
|
+
if (result.tmuxDefaultsWritten) {
|
|
2781
|
+
console.log(" \u2713 tmux config: Sensible defaults written to ~/.tmux.conf");
|
|
2782
|
+
}
|
|
2783
|
+
if (process.platform === "darwin") {
|
|
2784
|
+
if (result.terminal.isIterm) {
|
|
2785
|
+
console.log(` \u2713 Terminal: ${result.terminal.name}`);
|
|
2786
|
+
} else {
|
|
2787
|
+
const name = result.terminal.name ? result.terminal.name : "unknown";
|
|
2788
|
+
console.log(` \u26A0 Terminal: ${name} \u2014 iTerm2 recommended (https://iterm2.com)`);
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
if (result.itermOptionKey.checked) {
|
|
2792
|
+
if (result.itermOptionKey.allCorrect) {
|
|
2793
|
+
console.log(" \u2713 Right Option Key: Esc+");
|
|
2794
|
+
} else {
|
|
2795
|
+
const profiles = result.itermOptionKey.incorrectProfiles.map((p) => `"${p}"`).join(", ");
|
|
2796
|
+
console.log(` \u26A0 Right Option Key: Not set to Esc+ for ${profiles}`);
|
|
2797
|
+
console.log(" Fix: iTerm2 \u2192 Settings \u2192 Profiles \u2192 Keys \u2192 Right Option Key \u2192 Esc+");
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
if (daemonOk) {
|
|
2801
|
+
console.log(" \u2713 Daemon: Running");
|
|
2802
|
+
} else {
|
|
2803
|
+
console.log(" \u2717 Daemon: Failed to start");
|
|
2804
|
+
}
|
|
2805
|
+
console.log(` \u2713 Keybindings: ${keybindMsg}`);
|
|
2806
|
+
if (result.command.installed) {
|
|
2807
|
+
console.log(` \u2713 /begin command: ${result.command.path}${result.command.autoInstalled ? " (just installed)" : ""}`);
|
|
2808
|
+
} else {
|
|
2809
|
+
console.log(" \u2717 /begin command: Failed to install");
|
|
2810
|
+
}
|
|
2811
|
+
if (result.nvim.installed) {
|
|
2812
|
+
const extra = result.nvim.autoInstalled ? " (just installed)" : "";
|
|
2813
|
+
console.log(` \u2713 Editor: nvim ${result.nvim.version}${extra}`);
|
|
2814
|
+
if (result.nvim.lazyVimInstalled) {
|
|
2815
|
+
console.log(" \u2713 LazyVim: Starter config installed to ~/.config/nvim/");
|
|
2816
|
+
}
|
|
2817
|
+
} else {
|
|
2818
|
+
console.log(" \u26A0 Editor: nvim not installed");
|
|
2819
|
+
if (process.platform === "darwin") {
|
|
2820
|
+
console.log(" Install: brew install neovim");
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
console.log("");
|
|
2824
|
+
console.log("Run 'sisyphus getting-started' for a usage guide.");
|
|
2825
|
+
console.log("");
|
|
2826
|
+
}
|
|
2827
|
+
function registerSetup(program2) {
|
|
2828
|
+
program2.command("setup").description("One-time setup: install dependencies, daemon, keybindings, and commands").action(async () => {
|
|
2829
|
+
const result = runOnboarding();
|
|
2830
|
+
let daemonOk = false;
|
|
2831
|
+
try {
|
|
2832
|
+
await ensureDaemonInstalled();
|
|
2833
|
+
daemonOk = true;
|
|
2834
|
+
} catch {
|
|
2835
|
+
daemonOk = isInstalled();
|
|
2836
|
+
}
|
|
2837
|
+
const keybindResult = setupTmuxKeybind();
|
|
2838
|
+
let keybindMsg;
|
|
2839
|
+
if (keybindResult.status === "installed" || keybindResult.status === "already-installed") {
|
|
2840
|
+
keybindMsg = `${DEFAULT_KEY} (cycle), ${DEFAULT_HOME_KEY} (dashboard)`;
|
|
2841
|
+
} else {
|
|
2842
|
+
keybindMsg = keybindResult.message;
|
|
2843
|
+
}
|
|
2844
|
+
printResults(result, daemonOk, keybindMsg);
|
|
2845
|
+
});
|
|
2846
|
+
}
|
|
2847
|
+
|
|
1567
2848
|
// src/cli/index.ts
|
|
1568
2849
|
var nodeVersion = parseInt(process.versions.node.split(".")[0], 10);
|
|
1569
2850
|
if (nodeVersion < 22) {
|
|
@@ -1573,7 +2854,7 @@ if (nodeVersion < 22) {
|
|
|
1573
2854
|
var program = new Command();
|
|
1574
2855
|
program.name("sisyphus").description("tmux-integrated orchestration daemon for Claude Code").version(
|
|
1575
2856
|
JSON.parse(
|
|
1576
|
-
|
|
2857
|
+
readFileSync5(join8(dirname4(fileURLToPath4(import.meta.url)), "..", "package.json"), "utf-8")
|
|
1577
2858
|
).version
|
|
1578
2859
|
);
|
|
1579
2860
|
program.configureHelp({
|
|
@@ -1602,6 +2883,7 @@ registerDoctor(program);
|
|
|
1602
2883
|
registerCompanionContext(program);
|
|
1603
2884
|
registerGettingStarted(program);
|
|
1604
2885
|
registerInit(program);
|
|
2886
|
+
registerSetup(program);
|
|
1605
2887
|
program.addHelpText("after", `
|
|
1606
2888
|
Examples:
|
|
1607
2889
|
$ sisyphus start "Implement auth system" Start a new session
|
|
@@ -1614,15 +2896,11 @@ Run 'sisyphus getting-started' for a complete usage guide.
|
|
|
1614
2896
|
`);
|
|
1615
2897
|
var args = process.argv.slice(2);
|
|
1616
2898
|
var firstArg = args[0];
|
|
1617
|
-
var skipWelcome = ["doctor", "getting-started", "help", "--help", "-h", "init", "uninstall", "--version", "-V"];
|
|
1618
|
-
if (!
|
|
1619
|
-
|
|
1620
|
-
console.log("");
|
|
1621
|
-
console.log(" Welcome to Sisyphus \u2014 multi-agent orchestration for Claude Code.");
|
|
2899
|
+
var skipWelcome = ["doctor", "getting-started", "help", "--help", "-h", "init", "setup", "uninstall", "--version", "-V"];
|
|
2900
|
+
if (!existsSync8(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
|
|
2901
|
+
mkdirSync5(globalDir(), { recursive: true });
|
|
1622
2902
|
console.log("");
|
|
1623
|
-
console.log("
|
|
1624
|
-
console.log(" sisyphus doctor Check your setup");
|
|
1625
|
-
console.log(" sisyphus getting-started Learn the basics");
|
|
2903
|
+
console.log(" Welcome to Sisyphus. Run 'sisyphus setup' to get started.");
|
|
1626
2904
|
console.log("");
|
|
1627
2905
|
}
|
|
1628
2906
|
program.parseAsync(process.argv).catch((err) => {
|