sisyphi 1.1.8 → 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 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 existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync5 } from "fs";
24
- import { dirname as dirname3, join as join7 } from "path";
25
- import { fileURLToPath as fileURLToPath3 } from "url";
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 if it exists
95
- tmux select-window -t "$name:sisyphus-dashboard" 2>/dev/null
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 select-window -t "$name:sisyphus-dashboard" 2>/dev/null
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 ensureDashboard(tmuxSession, cwd) {
492
+ function openDashboardWindow(tmuxSession, cwd) {
490
493
  try {
491
- const windows = execSync4(
492
- `tmux list-windows -t ${shellQuote(tmuxSession)} -F "#{window_name}"`,
493
- { encoding: "utf-8" }
494
- );
495
- const isOpen = windows.split("\n").some((name) => name.trim() === "sisyphus-dashboard");
496
- if (isOpen) {
497
- execSync4(
498
- `tmux select-window -t ${shellQuote(tmuxSession)}:sisyphus-dashboard`
499
- );
500
- return false;
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 tmuxSession = getTmuxSession();
519
- ensureDashboard(tmuxSession, process.cwd());
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 (ensureDashboard(tmuxSession, cwd)) {
566
+ if (openDashboardWindow(tmuxSession, cwd)) {
554
567
  console.log(`Dashboard opened in tmux window "sisyphus-dashboard"`);
555
568
  }
556
569
  } catch {
@@ -737,7 +750,8 @@ function registerContinue(program2) {
737
750
  }
738
751
 
739
752
  // src/cli/commands/status.ts
740
- import { readFileSync as readFileSync3 } from "fs";
753
+ import { execSync as execSync6 } from "child_process";
754
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
741
755
  var COLOR_CODES = {
742
756
  green: "\x1B[32m",
743
757
  yellow: "\x1B[33m",
@@ -773,12 +787,17 @@ function inferOrchestratorPhase(session) {
773
787
  return "starting";
774
788
  }
775
789
  }
776
- function formatAgent(agent) {
790
+ function formatAgent(agent, verbose) {
777
791
  const status = colorize(agent.status, agent.status);
778
792
  const name = `${BOLD}${agent.name}${RESET}`;
779
793
  const type = `${DIM}(${agent.agentType})${RESET}`;
780
794
  const duration = formatDuration(agent.activeMs);
781
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
+ }
782
801
  if (agent.reports.length > 0) {
783
802
  for (const r of agent.reports) {
784
803
  const label = r.type === "final" ? "Final" : "Update";
@@ -828,7 +847,33 @@ function readRoadmapTodos(cwd, sessionId) {
828
847
  return [];
829
848
  }
830
849
  }
831
- function printSession(session) {
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) {
832
877
  const status = colorize(session.status, session.status);
833
878
  const sessionDuration = formatDuration(session.createdAt, session.completedAt);
834
879
  console.log(`
@@ -836,7 +881,7 @@ ${BOLD}Session: ${session.id}${RESET}`);
836
881
  console.log(` Status: ${status}`);
837
882
  console.log(` Task: ${session.task}`);
838
883
  if (session.context) {
839
- 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;
840
885
  console.log(` Context: ${truncated}`);
841
886
  }
842
887
  console.log(` CWD: ${session.cwd}`);
@@ -857,14 +902,27 @@ ${BOLD}Active agents (${runningAgents.length}):${RESET}`);
857
902
  const type = `${DIM}(${agent.agentType})${RESET}`;
858
903
  const duration = formatDuration(agent.activeMs);
859
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
+ }
860
909
  }
861
910
  }
862
- const todos = readRoadmapTodos(session.cwd, session.id);
863
- if (todos.length > 0) {
864
- console.log(`
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(`
865
922
  ${BOLD}Remaining (${todos.length} unchecked):${RESET}`);
866
- for (const todo of todos) {
867
- console.log(` - ${todo}`);
923
+ for (const todo of todos) {
924
+ console.log(` - ${todo}`);
925
+ }
868
926
  }
869
927
  }
870
928
  if (session.orchestratorCycles.length > 0) {
@@ -875,25 +933,68 @@ ${BOLD}Remaining (${todos.length} unchecked):${RESET}`);
875
933
  const isLast = i === cycles.length - 1;
876
934
  const phase = isLast && session.status === "active" ? inferOrchestratorPhase(session) : void 0;
877
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
+ }
878
950
  }
879
951
  }
880
952
  if (session.agents.length > 0) {
881
953
  console.log(`
882
954
  ${BOLD}Agents:${RESET}`);
883
955
  for (const agent of session.agents) {
884
- console.log(formatAgent(agent));
956
+ console.log(formatAgent(agent, verbose));
885
957
  }
886
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
+ }
887
987
  }
888
988
  function registerStatus(program2) {
889
- 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) => {
890
990
  const sessionId = sessionIdArg ?? process.env.SISYPHUS_SESSION_ID;
991
+ const verbose = opts?.verbose ?? false;
891
992
  const request = { type: "status", sessionId };
892
993
  const response = await sendRequest(request);
893
994
  if (response.ok) {
894
995
  const session = response.data?.session;
895
996
  if (session) {
896
- printSession(session);
997
+ printSession(session, verbose);
897
998
  } else {
898
999
  console.log("No session found");
899
1000
  }
@@ -1164,12 +1265,12 @@ function registerSetupKeybind(program2) {
1164
1265
  }
1165
1266
 
1166
1267
  // src/cli/commands/doctor.ts
1167
- import { execSync as execSync7 } from "child_process";
1168
- import { existsSync as existsSync5, statSync } from "fs";
1268
+ import { execSync as execSync8 } from "child_process";
1269
+ import { existsSync as existsSync6, statSync } from "fs";
1169
1270
 
1170
1271
  // src/cli/onboard.ts
1171
- import { execSync as execSync6 } from "child_process";
1172
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1272
+ import { execSync as execSync7 } from "child_process";
1273
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1173
1274
  import { homedir as homedir3 } from "os";
1174
1275
  import { dirname as dirname2, join as join5 } from "path";
1175
1276
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -1180,7 +1281,7 @@ function detectTerminal() {
1180
1281
  }
1181
1282
  function isTmuxAvailable() {
1182
1283
  try {
1183
- execSync6("which tmux", { stdio: "pipe" });
1284
+ execSync7("which tmux", { stdio: "pipe" });
1184
1285
  return true;
1185
1286
  } catch {
1186
1287
  return false;
@@ -1188,7 +1289,7 @@ function isTmuxAvailable() {
1188
1289
  }
1189
1290
  function isBrewAvailable() {
1190
1291
  try {
1191
- execSync6("which brew", { stdio: "pipe" });
1292
+ execSync7("which brew", { stdio: "pipe" });
1192
1293
  return true;
1193
1294
  } catch {
1194
1295
  return false;
@@ -1198,7 +1299,7 @@ function tryAutoInstallTmux() {
1198
1299
  if (!isBrewAvailable()) return false;
1199
1300
  try {
1200
1301
  console.log(" Installing tmux via Homebrew...");
1201
- execSync6("brew install tmux", { stdio: "inherit" });
1302
+ execSync7("brew install tmux", { stdio: "inherit" });
1202
1303
  return isTmuxAvailable();
1203
1304
  } catch {
1204
1305
  return false;
@@ -1209,11 +1310,11 @@ function checkItermOptionKey() {
1209
1310
  return { checked: false, allCorrect: true, incorrectProfiles: [] };
1210
1311
  }
1211
1312
  const plistPath2 = join5(homedir3(), "Library", "Preferences", "com.googlecode.iterm2.plist");
1212
- if (!existsSync4(plistPath2)) {
1313
+ if (!existsSync5(plistPath2)) {
1213
1314
  return { checked: false, allCorrect: false, incorrectProfiles: [] };
1214
1315
  }
1215
1316
  try {
1216
- const json = execSync6(
1317
+ const json = execSync7(
1217
1318
  `plutil -extract "New Bookmarks" json -o - "${plistPath2}"`,
1218
1319
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
1219
1320
  );
@@ -1233,7 +1334,7 @@ function checkItermOptionKey() {
1233
1334
  }
1234
1335
  }
1235
1336
  function hasExistingTmuxConf() {
1236
- return existsSync4(join5(homedir3(), ".tmux.conf")) || existsSync4(join5(homedir3(), ".config", "tmux", "tmux.conf"));
1337
+ return existsSync5(join5(homedir3(), ".tmux.conf")) || existsSync5(join5(homedir3(), ".config", "tmux", "tmux.conf"));
1237
1338
  }
1238
1339
  var TMUX_DEFAULTS = `# Sensible tmux defaults (installed by sisyphus)
1239
1340
  # Customize freely \u2014 sisyphus won't overwrite this file.
@@ -1263,6 +1364,40 @@ set -g set-clipboard on
1263
1364
 
1264
1365
  # Focus events (for editors)
1265
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
1266
1401
  `;
1267
1402
  function writeTmuxDefaults() {
1268
1403
  const confPath = join5(homedir3(), ".tmux.conf");
@@ -1270,7 +1405,7 @@ function writeTmuxDefaults() {
1270
1405
  }
1271
1406
  function isNvimAvailable() {
1272
1407
  try {
1273
- execSync6("which nvim", { stdio: "pipe" });
1408
+ execSync7("which nvim", { stdio: "pipe" });
1274
1409
  return true;
1275
1410
  } catch {
1276
1411
  return false;
@@ -1278,13 +1413,13 @@ function isNvimAvailable() {
1278
1413
  }
1279
1414
  function getNvimVersion() {
1280
1415
  try {
1281
- return execSync6("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "") || "unknown";
1416
+ return execSync7("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "") || "unknown";
1282
1417
  } catch {
1283
1418
  return "unknown";
1284
1419
  }
1285
1420
  }
1286
1421
  function hasLazyVimConfig() {
1287
- return existsSync4(join5(homedir3(), ".config", "nvim", "lazy-lock.json"));
1422
+ return existsSync5(join5(homedir3(), ".config", "nvim", "lazy-lock.json"));
1288
1423
  }
1289
1424
  function tryAutoInstallNvim() {
1290
1425
  if (isNvimAvailable()) {
@@ -1295,7 +1430,7 @@ function tryAutoInstallNvim() {
1295
1430
  }
1296
1431
  try {
1297
1432
  console.log(" Installing neovim via Homebrew...");
1298
- execSync6("brew install neovim", { stdio: "inherit" });
1433
+ execSync7("brew install neovim", { stdio: "inherit" });
1299
1434
  } catch {
1300
1435
  return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false };
1301
1436
  }
@@ -1304,13 +1439,13 @@ function tryAutoInstallNvim() {
1304
1439
  }
1305
1440
  const nvimConfigDir = join5(homedir3(), ".config", "nvim");
1306
1441
  let lazyVimInstalled = false;
1307
- if (!existsSync4(nvimConfigDir)) {
1442
+ if (!existsSync5(nvimConfigDir)) {
1308
1443
  try {
1309
1444
  console.log(" Cloning LazyVim starter config...");
1310
- execSync6(`git clone https://github.com/LazyVim/starter ${nvimConfigDir}`, { stdio: "inherit" });
1445
+ execSync7(`git clone https://github.com/LazyVim/starter ${nvimConfigDir}`, { stdio: "inherit" });
1311
1446
  const gitDir = join5(nvimConfigDir, ".git");
1312
- if (existsSync4(gitDir)) {
1313
- execSync6(`rm -rf "${gitDir}"`, { stdio: "pipe" });
1447
+ if (existsSync5(gitDir)) {
1448
+ execSync7(`rm -rf "${gitDir}"`, { stdio: "pipe" });
1314
1449
  }
1315
1450
  lazyVimInstalled = true;
1316
1451
  } catch {
@@ -1326,15 +1461,15 @@ function bundledBeginCommandPath() {
1326
1461
  return join5(distDir, "templates", "begin.md");
1327
1462
  }
1328
1463
  function isBeginCommandInstalled() {
1329
- return existsSync4(beginCommandPath());
1464
+ return existsSync5(beginCommandPath());
1330
1465
  }
1331
1466
  function installBeginCommand() {
1332
1467
  const dest = beginCommandPath();
1333
- if (existsSync4(dest)) {
1468
+ if (existsSync5(dest)) {
1334
1469
  return { installed: true, autoInstalled: false, path: dest };
1335
1470
  }
1336
1471
  const src = bundledBeginCommandPath();
1337
- if (!existsSync4(src)) {
1472
+ if (!existsSync5(src)) {
1338
1473
  return { installed: false, autoInstalled: false, path: dest };
1339
1474
  }
1340
1475
  try {
@@ -1378,7 +1513,7 @@ function checkNodeVersion() {
1378
1513
  }
1379
1514
  function checkClaudeCli() {
1380
1515
  try {
1381
- execSync7("which claude", { stdio: "pipe" });
1516
+ execSync8("which claude", { stdio: "pipe" });
1382
1517
  return { name: "Claude CLI", status: "ok", detail: "Found on PATH" };
1383
1518
  } catch {
1384
1519
  return {
@@ -1391,7 +1526,7 @@ function checkClaudeCli() {
1391
1526
  }
1392
1527
  function checkGit() {
1393
1528
  try {
1394
- const version = execSync7("git --version", { encoding: "utf-8", stdio: "pipe" }).trim();
1529
+ const version = execSync8("git --version", { encoding: "utf-8", stdio: "pipe" }).trim();
1395
1530
  return { name: "git", status: "ok", detail: version };
1396
1531
  } catch {
1397
1532
  return { name: "git", status: "fail", detail: "Not found on PATH", fix: "Install git: https://git-scm.com/downloads" };
@@ -1399,7 +1534,7 @@ function checkGit() {
1399
1534
  }
1400
1535
  function checkTmuxVersion() {
1401
1536
  try {
1402
- const version = execSync7("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
1537
+ const version = execSync8("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
1403
1538
  const match = version.match(/(\d+\.\d+)/);
1404
1539
  if (!match) return { name: "tmux version", status: "warn", detail: `Could not parse version: ${version}` };
1405
1540
  const ver = parseFloat(match[1]);
@@ -1425,7 +1560,7 @@ function checkDaemonInstalled() {
1425
1560
  };
1426
1561
  }
1427
1562
  const pid = daemonPidPath();
1428
- if (existsSync5(pid)) {
1563
+ if (existsSync6(pid)) {
1429
1564
  return { name: "Daemon setup", status: "ok", detail: `PID file found at ${pid}` };
1430
1565
  }
1431
1566
  return {
@@ -1437,7 +1572,7 @@ function checkDaemonInstalled() {
1437
1572
  }
1438
1573
  function checkDaemonRunning() {
1439
1574
  const pid = daemonPidPath();
1440
- if (!existsSync5(pid)) {
1575
+ if (!existsSync6(pid)) {
1441
1576
  const fix = process.platform === "darwin" ? "launchctl load -w ~/Library/LaunchAgents/com.sisyphus.daemon.plist" : "sisyphusd & \u2014 or check if the process is running";
1442
1577
  return {
1443
1578
  name: "Daemon process",
@@ -1448,7 +1583,7 @@ function checkDaemonRunning() {
1448
1583
  }
1449
1584
  try {
1450
1585
  const sock = socketPath();
1451
- execSync7(`test -S "${sock}"`, { stdio: "pipe" });
1586
+ execSync8(`test -S "${sock}"`, { stdio: "pipe" });
1452
1587
  return { name: "Daemon process", status: "ok", detail: `Socket at ${sock}` };
1453
1588
  } catch {
1454
1589
  return {
@@ -1461,13 +1596,13 @@ function checkDaemonRunning() {
1461
1596
  }
1462
1597
  function checkTmux() {
1463
1598
  try {
1464
- execSync7("which tmux", { stdio: "pipe" });
1599
+ execSync8("which tmux", { stdio: "pipe" });
1465
1600
  } catch {
1466
1601
  const installHint = process.platform === "darwin" ? "brew install tmux" : "apt install tmux (Debian/Ubuntu) or your package manager";
1467
1602
  return { name: "tmux", status: "fail", detail: "Not found on PATH", fix: installHint };
1468
1603
  }
1469
1604
  try {
1470
- execSync7("tmux list-sessions", { stdio: "pipe" });
1605
+ execSync8("tmux list-sessions", { stdio: "pipe" });
1471
1606
  return { name: "tmux", status: "ok", detail: "Running" };
1472
1607
  } catch {
1473
1608
  return { name: "tmux", status: "warn", detail: "Installed but no server running" };
@@ -1475,7 +1610,7 @@ function checkTmux() {
1475
1610
  }
1476
1611
  function checkCycleScript() {
1477
1612
  const path = cycleScriptPath();
1478
- if (!existsSync5(path)) {
1613
+ if (!existsSync6(path)) {
1479
1614
  return {
1480
1615
  name: "Cycle script",
1481
1616
  status: "fail",
@@ -1500,7 +1635,7 @@ function checkCycleScript() {
1500
1635
  function checkTmuxKeybind() {
1501
1636
  const existing = getExistingBinding(DEFAULT_KEY);
1502
1637
  if (existing === null) {
1503
- if (existsSync5(sisyphusTmuxConfPath())) {
1638
+ if (existsSync6(sisyphusTmuxConfPath())) {
1504
1639
  return {
1505
1640
  name: `Tmux keybind (${DEFAULT_KEY})`,
1506
1641
  status: "warn",
@@ -1526,7 +1661,7 @@ function checkTmuxKeybind() {
1526
1661
  }
1527
1662
  function checkGlobalDir() {
1528
1663
  const dir = globalDir();
1529
- if (existsSync5(dir)) {
1664
+ if (existsSync6(dir)) {
1530
1665
  return { name: "Data directory", status: "ok", detail: dir };
1531
1666
  }
1532
1667
  return { name: "Data directory", status: "warn", detail: `${dir} does not exist (created on first use)` };
@@ -1580,7 +1715,7 @@ function checkNvim() {
1580
1715
  return { name: "nvim", status: "warn", detail: "Not installed", fix };
1581
1716
  }
1582
1717
  try {
1583
- const version = execSync7("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "");
1718
+ const version = execSync8("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "");
1584
1719
  return { name: "nvim", status: "ok", detail: version ?? "installed" };
1585
1720
  } catch {
1586
1721
  return { name: "nvim", status: "ok", detail: "installed" };
@@ -1634,6 +1769,12 @@ function registerCompanionContext(program2) {
1634
1769
  }
1635
1770
 
1636
1771
  // src/cli/commands/getting-started.ts
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
+ }
1637
1778
  function isClaudeCode() {
1638
1779
  return !!process.env["CLAUDECODE"];
1639
1780
  }
@@ -1678,7 +1819,7 @@ function printStep0() {
1678
1819
 
1679
1820
  ## Tutorial Overview
1680
1821
 
1681
- This tutorial has 5 steps. Share this overview so the user knows what's coming and can skip ahead:
1822
+ This tutorial has 6 steps. Share this overview so the user knows what's coming and can skip ahead:
1682
1823
 
1683
1824
  | Step | Topic | Command |
1684
1825
  |------|-------|---------|
@@ -1687,6 +1828,7 @@ This tutorial has 5 steps. Share this overview so the user knows what's coming a
1687
1828
  | 2 | Nvim basics \u2014 open, save, quit (optional) | \`--tutorial 2\` |
1688
1829
  | 3 | Sisyphus concepts \u2014 session model & keybinds | \`--tutorial 3\` |
1689
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\` |
1690
1832
 
1691
1833
  Tell the user they can skip to any step with \`sisyphus getting-started --tutorial <N>\`.
1692
1834
 
@@ -1776,9 +1918,12 @@ tmux split-window -h
1776
1918
  Tell them: "I just split your terminal. You should see two panes side by side."
1777
1919
 
1778
1920
  Explain navigation:
1779
- - \`Ctrl-b\` then \`\u2192\` (right arrow): move to the right pane
1780
- - \`Ctrl-b\` then \`\u2190\` (left arrow): move to the left pane
1781
- - The prefix \`Ctrl-b\` is always pressed first, then released, then the arrow key
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
1782
1927
 
1783
1928
  Ask them to try navigating between panes.
1784
1929
 
@@ -1795,11 +1940,14 @@ Or tell them they can type \`exit\` in the extra pane to close it.
1795
1940
 
1796
1941
  - **Detach**: \`Ctrl-b d\` \u2014 leaves tmux running in background, returns to normal terminal
1797
1942
  - **Reattach**: \`tmux attach\` (or \`tmux a\`) \u2014 reconnects to the running session
1798
- - **Scroll mode**: \`Ctrl-b [\` \u2014 lets you scroll up through output. Press \`q\` to exit scroll mode.
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
1799
1947
 
1800
1948
  ### 5. Verification
1801
1949
 
1802
- Ask the user to confirm: "Can you navigate between panes with Ctrl-b + arrow keys?"
1950
+ Ask the user to confirm: "Can you navigate between panes with Ctrl+h and Ctrl+l?"
1803
1951
 
1804
1952
  Once confirmed, proceed:
1805
1953
  \`\`\`
@@ -1819,40 +1967,39 @@ function printStep2() {
1819
1967
 
1820
1968
  ## Instructions for Claude
1821
1969
 
1822
- This step is OPTIONAL. Nvim is useful for reviewing agent work in tmux panes but not required.
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.
1823
1973
 
1824
1974
  ### If nvim is NOT installed (nvimInstalled: false)
1825
1975
 
1826
- Ask the user: "Neovim is handy for reviewing files in tmux panes. Want me to install it, or skip this step?"
1976
+ Ask the user: "Neovim is handy for reviewing and editing files in tmux panes. Want me to install it, or skip this step?"
1827
1977
 
1828
1978
  - **Install**: Run \`brew install neovim\` (macOS) or suggest their package manager
1829
1979
  - **Skip**: That's fine \u2014 they can use \`cat\`, \`less\`, or any editor they prefer. Proceed to step 3.
1830
1980
 
1831
1981
  ### If nvim IS installed (nvimInstalled: true)
1832
1982
 
1833
- Teach exactly 3 things \u2014 no more:
1983
+ Briefly explain the key concept \u2014 nvim has two modes:
1834
1984
 
1835
- 1. **Open a file**: \`nvim filename.txt\`
1836
- 2. **Save and close**: \`ZZ\` (capital Z twice \u2014 hold Shift, press Z twice)
1837
- 3. **Quit without saving**: \`:q!\` (colon, q, exclamation mark, Enter)
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.
1838
1987
 
1839
- ### Hands-on exercise
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."
1840
1989
 
1841
- Create a temporary file for practice:
1990
+ Open the bundled tutorial file in a split pane:
1842
1991
  \`\`\`
1843
- echo "Hello from the sisyphus tutorial!" > /tmp/sisyphus-tutorial-test.txt
1992
+ cp ${templatePath("nvim-tutorial.txt")} /tmp/sisyphus-nvim-tutorial.txt
1993
+ tmux split-window -h "nvim /tmp/sisyphus-nvim-tutorial.txt"
1844
1994
  \`\`\`
1845
1995
 
1846
- Walk them through:
1847
- 1. Open it: \`nvim /tmp/sisyphus-tutorial-test.txt\`
1848
- 2. Look around (they're in normal mode \u2014 arrow keys work for navigation)
1849
- 3. Close it: type \`ZZ\`
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.
1850
1997
 
1851
- Tell them: "That's all you need. When you jump into a sisyphus agent's pane, you can use \`nvim\` to inspect files the agent is working on."
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."
1852
1999
 
1853
2000
  ### Verification
1854
2001
 
1855
- Ask if they were able to open and close the file (or if they skipped).
2002
+ Ask if they were able to edit and save the file (or if they skipped).
1856
2003
 
1857
2004
  Proceed:
1858
2005
  \`\`\`
@@ -1862,13 +2009,75 @@ sisyphus getting-started --tutorial 3
1862
2009
  `);
1863
2010
  }
1864
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";
2022
+ } else {
2023
+ rightOptionKeyStatus = `incorrect:${result.incorrectProfiles.join(",")}`;
2024
+ }
2025
+ }
1865
2026
  console.log(`
1866
2027
  <claude-instructions>
1867
2028
  # Sisyphus Getting Started \u2014 Step 3: Sisyphus Concepts & Keybinds
1868
2029
 
2030
+ ## Environment Data
2031
+ - rightOptionKeyStatus: ${rightOptionKeyStatus}
2032
+
1869
2033
  ## Instructions for Claude
1870
2034
 
1871
- ### 1. Explain the session model
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
1872
2081
 
1873
2082
  This is the KEY concept. Use the diagram and be clear:
1874
2083
 
@@ -1876,7 +2085,7 @@ This is the KEY concept. Use the diagram and be clear:
1876
2085
  YOUR tmux session ("work") Sisyphus tmux session ("sisyphus-abc123")
1877
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
1878
2087
  \u2502 \u2502 \u2502 Orch \u2502 Agent \u2502 Agent \u2502
1879
- \u2502 Your normal work \u2502 \u2190\u2500\u2500\u2192 \u2502 (yellow)\u2502 (blue) \u2502 (green) \u2502
2088
+ \u2502 Your normal work \u2502 \u2190\u2500\u2500\u2192 \u2502 (yellow)\u2502 (blue) \u2502 (green) \u2502
1880
2089
  \u2502 + dashboard \u2502 \u2502 \u2502 \u2502 \u2502
1881
2090
  \u2502 \u2502 \u2502 Plans & \u2502 Writes \u2502 Writes \u2502
1882
2091
  \u2502 \u2502 \u2502 assigns \u2502 code \u2502 tests \u2502
@@ -1890,40 +2099,36 @@ Key points:
1890
2099
  - Your session stays clean \u2014 you get a **dashboard** for monitoring
1891
2100
  - You can jump between your session and the sisyphus session to observe
1892
2101
 
1893
- ### 2. Teach keybinds
2102
+ ### 3. Teach keybinds
1894
2103
 
1895
- Two keybinds to remember:
2104
+ Two keybinds to remember (both use the RIGHT Option key):
1896
2105
 
1897
2106
  | Keybind | Action |
1898
2107
  |---------|--------|
1899
- | \`M-s\` (Option+s) | Cycle through sisyphus sessions |
1900
- | \`M-S\` (Option+Shift+s) | Jump back to dashboard |
1901
-
1902
- "M" means "Meta" which is the Option key on macOS. So \`M-s\` = hold Option, press s.
2108
+ | Right Option + s | Cycle through sisyphus sessions |
2109
+ | Right Option + Shift + s | Jump back to dashboard |
1903
2110
 
1904
- ### 3. Verify keybinds are installed
2111
+ ### 4. Verify keybinds are installed
1905
2112
 
1906
2113
  Run \`sisyphus doctor\` and check the output. Look for:
1907
2114
  - "Cycle script" \u2014 should be \u2713
1908
2115
  - "Tmux keybind" \u2014 should be \u2713
2116
+ - "Right Option Key" \u2014 should be "Esc+"
1909
2117
 
1910
- If either is missing, run: \`sisyphus setup-keybind\`
2118
+ If cycle script or keybind is missing, run: \`sisyphus setup-keybind\`
1911
2119
 
1912
- ### 4. Test the keybind
2120
+ ### 5. Test the keybind
1913
2121
 
1914
- Have the user try pressing \`Option+s\`. Nothing should happen yet (no sisyphus session running) \u2014 and that's fine. The important thing is it doesn't error.
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.
1915
2123
 
1916
- If they get a special character (like \xDF) instead of the keybind firing, explain:
1917
- - Their terminal needs to send Option as Esc+ (Meta)
1918
- - iTerm2: Settings \u2192 Profiles \u2192 Keys \u2192 Right Option Key \u2192 Esc+
1919
- - Other terminals: look for "Meta key" or "Option sends" in preferences
2124
+ If they see \`\xDF\` or similar, circle back to the Right Option Key setup above.
1920
2125
 
1921
- ### 5. Verification
2126
+ ### 6. Verification
1922
2127
 
1923
2128
  Confirm:
1924
2129
  - They understand the two-session model (their session vs sisyphus session)
1925
- - \`sisyphus doctor\` shows keybinds installed
1926
- - Option+s doesn't produce a special character
2130
+ - \`sisyphus doctor\` shows keybinds installed AND Right Option Key: Esc+
2131
+ - Right Option + s doesn't produce a special character
1927
2132
 
1928
2133
  Proceed:
1929
2134
  \`\`\`
@@ -1946,72 +2151,561 @@ This is the grand finale \u2014 a live demo session.
1946
2151
  Run \`sisyphus doctor\` first. If any checks are failing, help the user fix them before proceeding.
1947
2152
  All core checks (tmux, daemon, keybinds) should be \u2713.
1948
2153
 
1949
- ### 2. Launch the demo
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:
1950
2159
 
1951
- Run this command:
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"
1952
2189
  \`\`\`
1953
- sisyphus start "Tutorial demo: explore this repository's structure, identify the main entry points, and write a brief summary of what each top-level directory contains" -c "This is a tutorial demo session. Be extra verbose in your planning and reports so the user watching can understand what's happening. Keep the scope small \u2014 2-3 agents max, 1-2 cycles."
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:
1954
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.
1955
2213
 
1956
- ### 3. Walk through what's happening
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.
1957
2215
 
1958
- Guide the user through observing the session in real-time:
2216
+ **How to narrate each phase:**
1959
2217
 
1960
- **Step A: Dashboard**
1961
- - The dashboard should auto-open. If not, run \`sisyphus dashboard\`
1962
- - Point out: session status, cycle number, agent list
1963
- - Tell them: "Watch the roadmap section \u2014 it updates as the orchestrator plans"
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."
1964
2219
 
1965
- **Step B: Jump to sisyphus session**
1966
- - Have them press \`M-s\` (Option+s) to cycle to the sisyphus tmux session
1967
- - They should see the orchestrator (yellow) working \u2014 reading files, planning
1968
- - Point out: "Each pane is a separate Claude instance working independently"
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."
1969
2221
 
1970
- **Step C: Jump back**
1971
- - Press \`M-S\` (Option+Shift+s) to jump back to the dashboard
1972
- - Or \`M-s\` to cycle through
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\`)."
1973
2223
 
1974
- **Step D: Watch the lifecycle**
1975
- - As agents spawn, they'll appear in both the dashboard and the sisyphus session
1976
- - Agents submit reports when done
1977
- - The orchestrator respawns each cycle with fresh context
1978
- - Eventually the session completes
2224
+ - **Agents submitting**: "Agent-001 just submitted its report! [N] more to go. When all agents finish, the orchestrator will respawn to review."
1979
2225
 
1980
- ### 4. After completion
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
1981
2241
 
1982
2242
  Once the session shows "completed":
1983
2243
 
1984
- - Run \`sisyphus status\` to see the final state
1985
- - Show them the session directory: \`ls .sisyphus/sessions/\` \u2192 find the session \u2192 show \`roadmap.md\`
1986
- - Explain: "Every session creates a roadmap, agent reports, and logs \u2014 all in .sisyphus/sessions/"
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/"
1987
2248
 
1988
- ### 5. Congratulations!
2249
+ ### 6. Proceed to wrap-up
1989
2250
 
1990
- Wrap up with:
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
1991
2272
 
1992
- > You've completed the Sisyphus tutorial! Here's what you learned:
1993
- > - tmux basics (sessions, panes, navigation)
1994
- > - How sisyphus creates separate sessions for orchestrator + agents
1995
- > - How to monitor with the dashboard and keybinds
1996
- > - What a real session lifecycle looks like
1997
- >
1998
- > **Ready for real work?**
1999
- > \`sisyphus start "your actual task here"\`
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.**
2000
2308
  >
2001
- > **Tips:**
2002
- > - Write requirements in a file and reference them: \`sisyphus start "Implement @requirements.md"\`
2003
- > - Monitor actively \u2014 agents can get stuck. Use the dashboard's \`m\` key to message the orchestrator.
2004
- > - Check \`sisyphus --help\` for all commands.
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.
2005
2695
  </claude-instructions>
2006
2696
  `);
2007
2697
  }
2008
- var STEPS = [printStep0, printStep1, printStep2, printStep3, printStep4];
2698
+ var STEPS = [printStep0, printStep1, printStep2, printStep3, printStep4, printStep5];
2009
2699
  function registerGettingStarted(program2) {
2010
- program2.command("getting-started").description("Interactive tutorial (best with Claude Code)").option("--tutorial <step>", "Tutorial step (0-4)", parseInt).action((opts) => {
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
+ }
2011
2705
  if (opts.tutorial !== void 0) {
2012
2706
  const step = opts.tutorial;
2013
- if (step < 0 || step > 4 || Number.isNaN(step)) {
2014
- console.error(`Invalid tutorial step: ${opts.tutorial}. Must be 0-4.`);
2707
+ if (step < 0 || step > 5 || Number.isNaN(step)) {
2708
+ console.error(`Invalid tutorial step: ${opts.tutorial}. Must be 0-5.`);
2015
2709
  process.exit(1);
2016
2710
  }
2017
2711
  STEPS[step]();
@@ -2026,8 +2720,8 @@ function registerGettingStarted(program2) {
2026
2720
  }
2027
2721
 
2028
2722
  // src/cli/commands/init.ts
2029
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
2030
- import { join as join6 } from "path";
2723
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
2724
+ import { join as join7 } from "path";
2031
2725
  var DEFAULT_CONFIG = {};
2032
2726
  var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
2033
2727
 
@@ -2038,9 +2732,9 @@ var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
2038
2732
  function registerInit(program2) {
2039
2733
  program2.command("init").description("Initialize sisyphus configuration for this project").option("--orchestrator", "Also create a custom orchestrator prompt template").action((opts) => {
2040
2734
  const cwd = process.cwd();
2041
- const sisDir = join6(cwd, ".sisyphus");
2042
- const configPath = join6(sisDir, "config.json");
2043
- if (existsSync6(configPath)) {
2735
+ const sisDir = join7(cwd, ".sisyphus");
2736
+ const configPath = join7(sisDir, "config.json");
2737
+ if (existsSync7(configPath)) {
2044
2738
  console.log(`Already initialized: ${configPath}`);
2045
2739
  return;
2046
2740
  }
@@ -2048,8 +2742,8 @@ function registerInit(program2) {
2048
2742
  writeFileSync4(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n", "utf-8");
2049
2743
  console.log(`Created ${configPath}`);
2050
2744
  if (opts.orchestrator) {
2051
- const orchPath = join6(sisDir, "orchestrator.md");
2052
- if (!existsSync6(orchPath)) {
2745
+ const orchPath = join7(sisDir, "orchestrator.md");
2746
+ if (!existsSync7(orchPath)) {
2053
2747
  writeFileSync4(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
2054
2748
  console.log(`Created ${orchPath}`);
2055
2749
  }
@@ -2064,10 +2758,10 @@ function registerInit(program2) {
2064
2758
  }
2065
2759
 
2066
2760
  // src/cli/commands/setup.ts
2067
- import { execSync as execSync8 } from "child_process";
2761
+ import { execSync as execSync10 } from "child_process";
2068
2762
  function getTmuxVersion() {
2069
2763
  try {
2070
- return execSync8("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
2764
+ return execSync10("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
2071
2765
  } catch {
2072
2766
  return "installed";
2073
2767
  }
@@ -2160,7 +2854,7 @@ if (nodeVersion < 22) {
2160
2854
  var program = new Command();
2161
2855
  program.name("sisyphus").description("tmux-integrated orchestration daemon for Claude Code").version(
2162
2856
  JSON.parse(
2163
- readFileSync5(join7(dirname3(fileURLToPath3(import.meta.url)), "..", "package.json"), "utf-8")
2857
+ readFileSync5(join8(dirname4(fileURLToPath4(import.meta.url)), "..", "package.json"), "utf-8")
2164
2858
  ).version
2165
2859
  );
2166
2860
  program.configureHelp({
@@ -2203,7 +2897,7 @@ Run 'sisyphus getting-started' for a complete usage guide.
2203
2897
  var args = process.argv.slice(2);
2204
2898
  var firstArg = args[0];
2205
2899
  var skipWelcome = ["doctor", "getting-started", "help", "--help", "-h", "init", "setup", "uninstall", "--version", "-V"];
2206
- if (!existsSync7(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
2900
+ if (!existsSync8(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
2207
2901
  mkdirSync5(globalDir(), { recursive: true });
2208
2902
  console.log("");
2209
2903
  console.log(" Welcome to Sisyphus. Run 'sisyphus setup' to get started.");