sisyphi 1.1.0 → 1.1.8
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 +767 -177
- package/dist/cli.js.map +1 -1
- package/dist/templates/begin.md +22 -0
- package/dist/tui.js +59 -29
- package/dist/tui.js.map +1 -1
- package/package.json +2 -2
- package/templates/begin.md +22 -0
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,9 @@ import {
|
|
|
20
20
|
|
|
21
21
|
// src/cli/index.ts
|
|
22
22
|
import { Command } from "commander";
|
|
23
|
-
import { existsSync as
|
|
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
26
|
|
|
25
27
|
// src/cli/commands/start.ts
|
|
26
28
|
import { execSync as execSync5 } from "child_process";
|
|
@@ -642,24 +644,6 @@ function registerSpawn(program2) {
|
|
|
642
644
|
}
|
|
643
645
|
|
|
644
646
|
// src/cli/commands/submit.ts
|
|
645
|
-
import { execSync as execSync6 } from "child_process";
|
|
646
|
-
function isInWorktree() {
|
|
647
|
-
try {
|
|
648
|
-
const gitDir = execSync6("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
|
|
649
|
-
const commonDir = execSync6("git rev-parse --git-common-dir", { encoding: "utf-8" }).trim();
|
|
650
|
-
return gitDir !== commonDir;
|
|
651
|
-
} catch {
|
|
652
|
-
return false;
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
function getUncommittedChanges() {
|
|
656
|
-
try {
|
|
657
|
-
const status = execSync6("git status --porcelain", { encoding: "utf-8" }).trim();
|
|
658
|
-
return status || null;
|
|
659
|
-
} catch {
|
|
660
|
-
return null;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
647
|
function registerSubmit(program2) {
|
|
664
648
|
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) => {
|
|
665
649
|
assertTmux();
|
|
@@ -669,17 +653,6 @@ function registerSubmit(program2) {
|
|
|
669
653
|
console.error("Error: provide --session or set SISYPHUS_SESSION_ID (and SISYPHUS_AGENT_ID) environment variables");
|
|
670
654
|
process.exit(1);
|
|
671
655
|
}
|
|
672
|
-
if (isInWorktree()) {
|
|
673
|
-
const changes = getUncommittedChanges();
|
|
674
|
-
if (changes) {
|
|
675
|
-
console.error("Error: uncommitted changes in worktree. Commit your changes before submitting.");
|
|
676
|
-
console.error('\nCommit first:\n git add -A && git commit -m "description of changes"\n');
|
|
677
|
-
console.error("Or discard:\n git checkout -- .\n");
|
|
678
|
-
console.error("Uncommitted changes:");
|
|
679
|
-
console.error(changes);
|
|
680
|
-
process.exit(1);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
656
|
const report = opts.report ?? await readStdin();
|
|
684
657
|
if (!report) {
|
|
685
658
|
console.error("Error: provide --report or pipe content via stdin");
|
|
@@ -1192,7 +1165,210 @@ function registerSetupKeybind(program2) {
|
|
|
1192
1165
|
|
|
1193
1166
|
// src/cli/commands/doctor.ts
|
|
1194
1167
|
import { execSync as execSync7 } from "child_process";
|
|
1195
|
-
import { existsSync as
|
|
1168
|
+
import { existsSync as existsSync5, statSync } from "fs";
|
|
1169
|
+
|
|
1170
|
+
// 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";
|
|
1173
|
+
import { homedir as homedir3 } from "os";
|
|
1174
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
1175
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1176
|
+
function detectTerminal() {
|
|
1177
|
+
const termProgram = process.env["TERM_PROGRAM"] || "";
|
|
1178
|
+
const isIterm = termProgram === "iTerm.app" || !!process.env["ITERM_SESSION_ID"];
|
|
1179
|
+
return { name: termProgram || "unknown", isIterm };
|
|
1180
|
+
}
|
|
1181
|
+
function isTmuxAvailable() {
|
|
1182
|
+
try {
|
|
1183
|
+
execSync6("which tmux", { stdio: "pipe" });
|
|
1184
|
+
return true;
|
|
1185
|
+
} catch {
|
|
1186
|
+
return false;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
function isBrewAvailable() {
|
|
1190
|
+
try {
|
|
1191
|
+
execSync6("which brew", { stdio: "pipe" });
|
|
1192
|
+
return true;
|
|
1193
|
+
} catch {
|
|
1194
|
+
return false;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
function tryAutoInstallTmux() {
|
|
1198
|
+
if (!isBrewAvailable()) return false;
|
|
1199
|
+
try {
|
|
1200
|
+
console.log(" Installing tmux via Homebrew...");
|
|
1201
|
+
execSync6("brew install tmux", { stdio: "inherit" });
|
|
1202
|
+
return isTmuxAvailable();
|
|
1203
|
+
} catch {
|
|
1204
|
+
return false;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
function checkItermOptionKey() {
|
|
1208
|
+
if (process.platform !== "darwin") {
|
|
1209
|
+
return { checked: false, allCorrect: true, incorrectProfiles: [] };
|
|
1210
|
+
}
|
|
1211
|
+
const plistPath2 = join5(homedir3(), "Library", "Preferences", "com.googlecode.iterm2.plist");
|
|
1212
|
+
if (!existsSync4(plistPath2)) {
|
|
1213
|
+
return { checked: false, allCorrect: false, incorrectProfiles: [] };
|
|
1214
|
+
}
|
|
1215
|
+
try {
|
|
1216
|
+
const json = execSync6(
|
|
1217
|
+
`plutil -extract "New Bookmarks" json -o - "${plistPath2}"`,
|
|
1218
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
1219
|
+
);
|
|
1220
|
+
const profiles = JSON.parse(json);
|
|
1221
|
+
const currentProfile = process.env["ITERM_PROFILE"];
|
|
1222
|
+
const incorrect = [];
|
|
1223
|
+
for (const profile of profiles) {
|
|
1224
|
+
const name = profile["Name"] || "Unnamed";
|
|
1225
|
+
if (currentProfile && name !== currentProfile) continue;
|
|
1226
|
+
if (profile["Right Option Key Sends"] !== 2) {
|
|
1227
|
+
incorrect.push(name);
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
return { checked: true, allCorrect: incorrect.length === 0, incorrectProfiles: incorrect };
|
|
1231
|
+
} catch {
|
|
1232
|
+
return { checked: false, allCorrect: false, incorrectProfiles: [] };
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
function hasExistingTmuxConf() {
|
|
1236
|
+
return existsSync4(join5(homedir3(), ".tmux.conf")) || existsSync4(join5(homedir3(), ".config", "tmux", "tmux.conf"));
|
|
1237
|
+
}
|
|
1238
|
+
var TMUX_DEFAULTS = `# Sensible tmux defaults (installed by sisyphus)
|
|
1239
|
+
# Customize freely \u2014 sisyphus won't overwrite this file.
|
|
1240
|
+
|
|
1241
|
+
# Enable mouse (click panes, scroll, resize)
|
|
1242
|
+
set -g mouse on
|
|
1243
|
+
|
|
1244
|
+
# Scrollback history
|
|
1245
|
+
set -g history-limit 50000
|
|
1246
|
+
|
|
1247
|
+
# 256 color + true color support
|
|
1248
|
+
set -g default-terminal "tmux-256color"
|
|
1249
|
+
set -as terminal-overrides ",*:Tc"
|
|
1250
|
+
|
|
1251
|
+
# Low escape delay (keeps Option/Meta keybindings responsive)
|
|
1252
|
+
set -sg escape-time 10
|
|
1253
|
+
|
|
1254
|
+
# Window numbering from 1
|
|
1255
|
+
set -g base-index 1
|
|
1256
|
+
setw -g pane-base-index 1
|
|
1257
|
+
|
|
1258
|
+
# Renumber windows when one closes
|
|
1259
|
+
set -g renumber-windows on
|
|
1260
|
+
|
|
1261
|
+
# Clipboard integration
|
|
1262
|
+
set -g set-clipboard on
|
|
1263
|
+
|
|
1264
|
+
# Focus events (for editors)
|
|
1265
|
+
set -g focus-events on
|
|
1266
|
+
`;
|
|
1267
|
+
function writeTmuxDefaults() {
|
|
1268
|
+
const confPath = join5(homedir3(), ".tmux.conf");
|
|
1269
|
+
writeFileSync3(confPath, TMUX_DEFAULTS, "utf8");
|
|
1270
|
+
}
|
|
1271
|
+
function isNvimAvailable() {
|
|
1272
|
+
try {
|
|
1273
|
+
execSync6("which nvim", { stdio: "pipe" });
|
|
1274
|
+
return true;
|
|
1275
|
+
} catch {
|
|
1276
|
+
return false;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
function getNvimVersion() {
|
|
1280
|
+
try {
|
|
1281
|
+
return execSync6("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "") || "unknown";
|
|
1282
|
+
} catch {
|
|
1283
|
+
return "unknown";
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
function hasLazyVimConfig() {
|
|
1287
|
+
return existsSync4(join5(homedir3(), ".config", "nvim", "lazy-lock.json"));
|
|
1288
|
+
}
|
|
1289
|
+
function tryAutoInstallNvim() {
|
|
1290
|
+
if (isNvimAvailable()) {
|
|
1291
|
+
return { installed: true, autoInstalled: false, version: getNvimVersion(), lazyVimInstalled: hasLazyVimConfig() };
|
|
1292
|
+
}
|
|
1293
|
+
if (!isBrewAvailable()) {
|
|
1294
|
+
return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false };
|
|
1295
|
+
}
|
|
1296
|
+
try {
|
|
1297
|
+
console.log(" Installing neovim via Homebrew...");
|
|
1298
|
+
execSync6("brew install neovim", { stdio: "inherit" });
|
|
1299
|
+
} catch {
|
|
1300
|
+
return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false };
|
|
1301
|
+
}
|
|
1302
|
+
if (!isNvimAvailable()) {
|
|
1303
|
+
return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false };
|
|
1304
|
+
}
|
|
1305
|
+
const nvimConfigDir = join5(homedir3(), ".config", "nvim");
|
|
1306
|
+
let lazyVimInstalled = false;
|
|
1307
|
+
if (!existsSync4(nvimConfigDir)) {
|
|
1308
|
+
try {
|
|
1309
|
+
console.log(" Cloning LazyVim starter config...");
|
|
1310
|
+
execSync6(`git clone https://github.com/LazyVim/starter ${nvimConfigDir}`, { stdio: "inherit" });
|
|
1311
|
+
const gitDir = join5(nvimConfigDir, ".git");
|
|
1312
|
+
if (existsSync4(gitDir)) {
|
|
1313
|
+
execSync6(`rm -rf "${gitDir}"`, { stdio: "pipe" });
|
|
1314
|
+
}
|
|
1315
|
+
lazyVimInstalled = true;
|
|
1316
|
+
} catch {
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return { installed: true, autoInstalled: true, version: getNvimVersion(), lazyVimInstalled };
|
|
1320
|
+
}
|
|
1321
|
+
function beginCommandPath() {
|
|
1322
|
+
return join5(homedir3(), ".claude", "commands", "sisyphus", "begin.md");
|
|
1323
|
+
}
|
|
1324
|
+
function bundledBeginCommandPath() {
|
|
1325
|
+
const distDir = dirname2(fileURLToPath2(import.meta.url));
|
|
1326
|
+
return join5(distDir, "templates", "begin.md");
|
|
1327
|
+
}
|
|
1328
|
+
function isBeginCommandInstalled() {
|
|
1329
|
+
return existsSync4(beginCommandPath());
|
|
1330
|
+
}
|
|
1331
|
+
function installBeginCommand() {
|
|
1332
|
+
const dest = beginCommandPath();
|
|
1333
|
+
if (existsSync4(dest)) {
|
|
1334
|
+
return { installed: true, autoInstalled: false, path: dest };
|
|
1335
|
+
}
|
|
1336
|
+
const src = bundledBeginCommandPath();
|
|
1337
|
+
if (!existsSync4(src)) {
|
|
1338
|
+
return { installed: false, autoInstalled: false, path: dest };
|
|
1339
|
+
}
|
|
1340
|
+
try {
|
|
1341
|
+
mkdirSync3(dirname2(dest), { recursive: true });
|
|
1342
|
+
writeFileSync3(dest, readFileSync4(src, "utf-8"), "utf8");
|
|
1343
|
+
return { installed: true, autoInstalled: true, path: dest };
|
|
1344
|
+
} catch {
|
|
1345
|
+
return { installed: false, autoInstalled: false, path: dest };
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
function runOnboarding() {
|
|
1349
|
+
const terminal = detectTerminal();
|
|
1350
|
+
const tmuxAlreadyInstalled = isTmuxAvailable();
|
|
1351
|
+
let tmuxInstalled = tmuxAlreadyInstalled;
|
|
1352
|
+
let tmuxAutoInstalled = false;
|
|
1353
|
+
let tmuxDefaultsWritten = false;
|
|
1354
|
+
if (!tmuxAlreadyInstalled && process.platform === "darwin") {
|
|
1355
|
+
tmuxAutoInstalled = tryAutoInstallTmux();
|
|
1356
|
+
tmuxInstalled = tmuxAutoInstalled;
|
|
1357
|
+
if (tmuxAutoInstalled && !hasExistingTmuxConf()) {
|
|
1358
|
+
writeTmuxDefaults();
|
|
1359
|
+
tmuxDefaultsWritten = true;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
let itermOptionKey = { checked: false, allCorrect: true, incorrectProfiles: [] };
|
|
1363
|
+
if (terminal.isIterm) {
|
|
1364
|
+
itermOptionKey = checkItermOptionKey();
|
|
1365
|
+
}
|
|
1366
|
+
const nvim = tryAutoInstallNvim();
|
|
1367
|
+
const command = installBeginCommand();
|
|
1368
|
+
return { tmuxInstalled, tmuxAutoInstalled, terminal, itermOptionKey, tmuxDefaultsWritten, nvim, command };
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
// src/cli/commands/doctor.ts
|
|
1196
1372
|
function checkNodeVersion() {
|
|
1197
1373
|
const major = parseInt(process.versions.node.split(".")[0], 10);
|
|
1198
1374
|
if (major < 22) {
|
|
@@ -1249,7 +1425,7 @@ function checkDaemonInstalled() {
|
|
|
1249
1425
|
};
|
|
1250
1426
|
}
|
|
1251
1427
|
const pid = daemonPidPath();
|
|
1252
|
-
if (
|
|
1428
|
+
if (existsSync5(pid)) {
|
|
1253
1429
|
return { name: "Daemon setup", status: "ok", detail: `PID file found at ${pid}` };
|
|
1254
1430
|
}
|
|
1255
1431
|
return {
|
|
@@ -1261,7 +1437,7 @@ function checkDaemonInstalled() {
|
|
|
1261
1437
|
}
|
|
1262
1438
|
function checkDaemonRunning() {
|
|
1263
1439
|
const pid = daemonPidPath();
|
|
1264
|
-
if (!
|
|
1440
|
+
if (!existsSync5(pid)) {
|
|
1265
1441
|
const fix = process.platform === "darwin" ? "launchctl load -w ~/Library/LaunchAgents/com.sisyphus.daemon.plist" : "sisyphusd & \u2014 or check if the process is running";
|
|
1266
1442
|
return {
|
|
1267
1443
|
name: "Daemon process",
|
|
@@ -1299,7 +1475,7 @@ function checkTmux() {
|
|
|
1299
1475
|
}
|
|
1300
1476
|
function checkCycleScript() {
|
|
1301
1477
|
const path = cycleScriptPath();
|
|
1302
|
-
if (!
|
|
1478
|
+
if (!existsSync5(path)) {
|
|
1303
1479
|
return {
|
|
1304
1480
|
name: "Cycle script",
|
|
1305
1481
|
status: "fail",
|
|
@@ -1324,7 +1500,7 @@ function checkCycleScript() {
|
|
|
1324
1500
|
function checkTmuxKeybind() {
|
|
1325
1501
|
const existing = getExistingBinding(DEFAULT_KEY);
|
|
1326
1502
|
if (existing === null) {
|
|
1327
|
-
if (
|
|
1503
|
+
if (existsSync5(sisyphusTmuxConfPath())) {
|
|
1328
1504
|
return {
|
|
1329
1505
|
name: `Tmux keybind (${DEFAULT_KEY})`,
|
|
1330
1506
|
status: "warn",
|
|
@@ -1350,25 +1526,85 @@ function checkTmuxKeybind() {
|
|
|
1350
1526
|
}
|
|
1351
1527
|
function checkGlobalDir() {
|
|
1352
1528
|
const dir = globalDir();
|
|
1353
|
-
if (
|
|
1529
|
+
if (existsSync5(dir)) {
|
|
1354
1530
|
return { name: "Data directory", status: "ok", detail: dir };
|
|
1355
1531
|
}
|
|
1356
1532
|
return { name: "Data directory", status: "warn", detail: `${dir} does not exist (created on first use)` };
|
|
1357
1533
|
}
|
|
1534
|
+
function checkTerminal() {
|
|
1535
|
+
if (process.platform !== "darwin") {
|
|
1536
|
+
return { name: "Terminal", status: "ok", detail: "Non-macOS (skipped)" };
|
|
1537
|
+
}
|
|
1538
|
+
const terminal = detectTerminal();
|
|
1539
|
+
if (terminal.isIterm) {
|
|
1540
|
+
return { name: "Terminal", status: "ok", detail: terminal.name };
|
|
1541
|
+
}
|
|
1542
|
+
return {
|
|
1543
|
+
name: "Terminal",
|
|
1544
|
+
status: "warn",
|
|
1545
|
+
detail: terminal.name ? terminal.name : "unknown",
|
|
1546
|
+
fix: "iTerm2 recommended for best experience: https://iterm2.com"
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
function checkItermRightOptionKey() {
|
|
1550
|
+
if (process.platform !== "darwin") return null;
|
|
1551
|
+
const terminal = detectTerminal();
|
|
1552
|
+
if (!terminal.isIterm) return null;
|
|
1553
|
+
const result = checkItermOptionKey();
|
|
1554
|
+
if (!result.checked) return null;
|
|
1555
|
+
if (result.allCorrect) {
|
|
1556
|
+
return { name: "Right Option Key", status: "ok", detail: "Esc+" };
|
|
1557
|
+
}
|
|
1558
|
+
const profiles = result.incorrectProfiles.map((p) => `"${p}"`).join(", ");
|
|
1559
|
+
return {
|
|
1560
|
+
name: "Right Option Key",
|
|
1561
|
+
status: "warn",
|
|
1562
|
+
detail: `Not Esc+ for ${profiles}`,
|
|
1563
|
+
fix: "iTerm2 \u2192 Settings \u2192 Profiles \u2192 Keys \u2192 Right Option Key \u2192 Esc+"
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
function checkBeginCommand() {
|
|
1567
|
+
if (isBeginCommandInstalled()) {
|
|
1568
|
+
return { name: "/begin command", status: "ok", detail: "Installed" };
|
|
1569
|
+
}
|
|
1570
|
+
return {
|
|
1571
|
+
name: "/begin command",
|
|
1572
|
+
status: "warn",
|
|
1573
|
+
detail: "Not installed",
|
|
1574
|
+
fix: "sisyphus setup"
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
function checkNvim() {
|
|
1578
|
+
if (!isNvimAvailable()) {
|
|
1579
|
+
const fix = process.platform === "darwin" ? "brew install neovim" : "Install neovim from https://neovim.io";
|
|
1580
|
+
return { name: "nvim", status: "warn", detail: "Not installed", fix };
|
|
1581
|
+
}
|
|
1582
|
+
try {
|
|
1583
|
+
const version = execSync7("nvim --version", { encoding: "utf-8", stdio: "pipe" }).split("\n")[0]?.replace("NVIM ", "");
|
|
1584
|
+
return { name: "nvim", status: "ok", detail: version ?? "installed" };
|
|
1585
|
+
} catch {
|
|
1586
|
+
return { name: "nvim", status: "ok", detail: "installed" };
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1358
1589
|
var SYMBOLS = { ok: "\u2713", warn: "!", fail: "\u2717" };
|
|
1359
1590
|
function registerDoctor(program2) {
|
|
1360
1591
|
program2.command("doctor").description("Check sisyphus installation health").action(async () => {
|
|
1592
|
+
const itermCheck = checkItermRightOptionKey();
|
|
1361
1593
|
const checks = [
|
|
1362
1594
|
checkNodeVersion(),
|
|
1363
1595
|
checkClaudeCli(),
|
|
1364
1596
|
checkGit(),
|
|
1365
1597
|
checkTmux(),
|
|
1366
1598
|
checkTmuxVersion(),
|
|
1599
|
+
checkTerminal(),
|
|
1600
|
+
...itermCheck ? [itermCheck] : [],
|
|
1367
1601
|
checkGlobalDir(),
|
|
1368
1602
|
checkDaemonInstalled(),
|
|
1369
1603
|
checkDaemonRunning(),
|
|
1370
1604
|
checkCycleScript(),
|
|
1371
|
-
checkTmuxKeybind()
|
|
1605
|
+
checkTmuxKeybind(),
|
|
1606
|
+
checkBeginCommand(),
|
|
1607
|
+
checkNvim()
|
|
1372
1608
|
];
|
|
1373
1609
|
let hasIssues = false;
|
|
1374
1610
|
for (const c of checks) {
|
|
@@ -1398,135 +1634,400 @@ function registerCompanionContext(program2) {
|
|
|
1398
1634
|
}
|
|
1399
1635
|
|
|
1400
1636
|
// src/cli/commands/getting-started.ts
|
|
1637
|
+
function isClaudeCode() {
|
|
1638
|
+
return !!process.env["CLAUDECODE"];
|
|
1639
|
+
}
|
|
1640
|
+
function printNonClaudeMessage() {
|
|
1641
|
+
console.log(`
|
|
1642
|
+
\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
|
|
1643
|
+
\u2551 sisyphus getting-started \u2014 Interactive Tutorial \u2551
|
|
1644
|
+
\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
|
|
1645
|
+
|
|
1646
|
+
This command provides an interactive tutorial best experienced
|
|
1647
|
+
inside Claude Code. Claude will walk you through setup step by step.
|
|
1648
|
+
|
|
1649
|
+
To start:
|
|
1650
|
+
1. Open Claude Code: claude
|
|
1651
|
+
2. Run: sisyphus getting-started
|
|
1652
|
+
|
|
1653
|
+
If you just want the quick reference, run:
|
|
1654
|
+
sisyphus --help
|
|
1655
|
+
sisyphus doctor
|
|
1656
|
+
`);
|
|
1657
|
+
}
|
|
1658
|
+
function printStep0() {
|
|
1659
|
+
const tmuxInstalled = isTmuxInstalled();
|
|
1660
|
+
const inTmux = !!process.env["TMUX"];
|
|
1661
|
+
let tmuxSession = null;
|
|
1662
|
+
if (process.env["TMUX"]) {
|
|
1663
|
+
const segments = process.env["TMUX"].split(",");
|
|
1664
|
+
const pathPart = segments[0];
|
|
1665
|
+
if (pathPart) {
|
|
1666
|
+
const parts = pathPart.split("/");
|
|
1667
|
+
tmuxSession = parts[parts.length - 1] || null;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
console.log(`
|
|
1671
|
+
<claude-instructions>
|
|
1672
|
+
# Sisyphus Getting Started \u2014 Step 0: Entry & Tmux Gate
|
|
1673
|
+
|
|
1674
|
+
## Environment Data
|
|
1675
|
+
- tmuxInstalled: ${tmuxInstalled}
|
|
1676
|
+
- inTmux: ${inTmux}
|
|
1677
|
+
- tmuxSession: ${tmuxSession ?? "none"}
|
|
1678
|
+
|
|
1679
|
+
## Tutorial Overview
|
|
1680
|
+
|
|
1681
|
+
This tutorial has 5 steps. Share this overview so the user knows what's coming and can skip ahead:
|
|
1682
|
+
|
|
1683
|
+
| Step | Topic | Command |
|
|
1684
|
+
|------|-------|---------|
|
|
1685
|
+
| 0 | Entry & tmux gate (you are here) | \`sisyphus getting-started\` |
|
|
1686
|
+
| 1 | Tmux basics \u2014 sessions, panes, navigation | \`--tutorial 1\` |
|
|
1687
|
+
| 2 | Nvim basics \u2014 open, save, quit (optional) | \`--tutorial 2\` |
|
|
1688
|
+
| 3 | Sisyphus concepts \u2014 session model & keybinds | \`--tutorial 3\` |
|
|
1689
|
+
| 4 | Live demo \u2014 launch and observe a real session | \`--tutorial 4\` |
|
|
1690
|
+
|
|
1691
|
+
Tell the user they can skip to any step with \`sisyphus getting-started --tutorial <N>\`.
|
|
1692
|
+
|
|
1693
|
+
## Instructions for Claude
|
|
1694
|
+
|
|
1695
|
+
You are guiding a user through the Sisyphus interactive tutorial.
|
|
1696
|
+
|
|
1697
|
+
### First: Ask if they want the tutorial
|
|
1698
|
+
|
|
1699
|
+
Ask the user if they'd like the interactive walkthrough. If they decline, give this quick summary and stop:
|
|
1700
|
+
|
|
1701
|
+
> Sisyphus is a multi-agent orchestrator for Claude Code. Start a session with \`sisyphus start "task"\`,
|
|
1702
|
+
> monitor with \`sisyphus dashboard\`, and check health with \`sisyphus doctor\`.
|
|
1703
|
+
|
|
1704
|
+
### If they want the tutorial:
|
|
1705
|
+
|
|
1706
|
+
**Case 1: tmux is NOT installed (tmuxInstalled: false)**
|
|
1707
|
+
- 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."
|
|
1708
|
+
- Show this ASCII diagram:
|
|
1709
|
+
|
|
1710
|
+
\`\`\`
|
|
1711
|
+
Regular terminal: With tmux:
|
|
1712
|
+
\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
|
|
1713
|
+
\u2502 \u2502 \u2502 Agent\u2502 Agent\u2502
|
|
1714
|
+
\u2502 one view \u2502 \u2502 001 \u2502 002 \u2502
|
|
1715
|
+
\u2502 \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2524
|
|
1716
|
+
\u2502 \u2502 \u2502 Agent\u2502 Orch \u2502
|
|
1717
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 003 \u2502 \u2502
|
|
1718
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
1719
|
+
\`\`\`
|
|
1720
|
+
|
|
1721
|
+
- Tell them to install tmux:
|
|
1722
|
+
- macOS: \`brew install tmux\`
|
|
1723
|
+
- Linux: \`apt install tmux\` or their package manager
|
|
1724
|
+
- After install, re-run: \`sisyphus getting-started --tutorial 0\` to verify
|
|
1725
|
+
|
|
1726
|
+
**Case 2: tmux installed but NOT in a tmux session (inTmux: false)**
|
|
1727
|
+
- Tell the user they need to be inside a tmux session for the tutorial
|
|
1728
|
+
- Have them run: \`tmux new-session\`
|
|
1729
|
+
- Then resume the conversation with Claude in the new tmux session: \`claude\`
|
|
1730
|
+
- Then re-run: \`sisyphus getting-started --tutorial 0\` to verify
|
|
1731
|
+
|
|
1732
|
+
**Case 3: In tmux (inTmux: true)**
|
|
1733
|
+
- Tell the user they're all set \u2014 tmux is running
|
|
1734
|
+
- Proceed by running: \`sisyphus getting-started --tutorial 1\`
|
|
1735
|
+
</claude-instructions>
|
|
1736
|
+
`);
|
|
1737
|
+
}
|
|
1738
|
+
function printStep1() {
|
|
1739
|
+
console.log(`
|
|
1740
|
+
<claude-instructions>
|
|
1741
|
+
# Sisyphus Getting Started \u2014 Step 1: Tmux Basics
|
|
1742
|
+
|
|
1743
|
+
## Instructions for Claude
|
|
1744
|
+
|
|
1745
|
+
Teach the user tmux fundamentals. Be conversational and encouraging.
|
|
1746
|
+
|
|
1747
|
+
### 1. Explain the concepts with diagrams
|
|
1748
|
+
|
|
1749
|
+
**Sessions, Windows, and Panes:**
|
|
1750
|
+
|
|
1751
|
+
\`\`\`
|
|
1752
|
+
tmux session "work"
|
|
1753
|
+
\u251C\u2500\u2500 window 1: "code"
|
|
1754
|
+
\u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
1755
|
+
\u2502 \u2502 pane 1 \u2502 pane 2 \u2502
|
|
1756
|
+
\u2502 \u2502 (editor)\u2502 (tests) \u2502
|
|
1757
|
+
\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
1758
|
+
\u2514\u2500\u2500 window 2: "servers"
|
|
1759
|
+
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
1760
|
+
\u2502 pane 1 \u2502
|
|
1761
|
+
\u2502 (dev server) \u2502
|
|
1762
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
1763
|
+
\`\`\`
|
|
1764
|
+
|
|
1765
|
+
- **Session**: A collection of windows. Persists even if you close the terminal.
|
|
1766
|
+
- **Window**: Like a tab. Each window fills the screen.
|
|
1767
|
+
- **Pane**: A split within a window. Sisyphus puts each agent in its own pane.
|
|
1768
|
+
|
|
1769
|
+
### 2. Hands-on: Create a test split
|
|
1770
|
+
|
|
1771
|
+
Run this command for the user:
|
|
1772
|
+
\`\`\`
|
|
1773
|
+
tmux split-window -h
|
|
1774
|
+
\`\`\`
|
|
1775
|
+
|
|
1776
|
+
Tell them: "I just split your terminal. You should see two panes side by side."
|
|
1777
|
+
|
|
1778
|
+
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
|
|
1782
|
+
|
|
1783
|
+
Ask them to try navigating between panes.
|
|
1784
|
+
|
|
1785
|
+
### 3. Clean up the test pane
|
|
1786
|
+
|
|
1787
|
+
Once they confirm they can navigate, close the extra pane:
|
|
1788
|
+
\`\`\`
|
|
1789
|
+
tmux kill-pane -t {the other pane}
|
|
1790
|
+
\`\`\`
|
|
1791
|
+
|
|
1792
|
+
Or tell them they can type \`exit\` in the extra pane to close it.
|
|
1793
|
+
|
|
1794
|
+
### 4. Teach essential commands
|
|
1795
|
+
|
|
1796
|
+
- **Detach**: \`Ctrl-b d\` \u2014 leaves tmux running in background, returns to normal terminal
|
|
1797
|
+
- **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.
|
|
1799
|
+
|
|
1800
|
+
### 5. Verification
|
|
1801
|
+
|
|
1802
|
+
Ask the user to confirm: "Can you navigate between panes with Ctrl-b + arrow keys?"
|
|
1803
|
+
|
|
1804
|
+
Once confirmed, proceed:
|
|
1805
|
+
\`\`\`
|
|
1806
|
+
sisyphus getting-started --tutorial 2
|
|
1807
|
+
\`\`\`
|
|
1808
|
+
</claude-instructions>
|
|
1809
|
+
`);
|
|
1810
|
+
}
|
|
1811
|
+
function printStep2() {
|
|
1812
|
+
const nvimInstalled = isNvimAvailable();
|
|
1813
|
+
console.log(`
|
|
1814
|
+
<claude-instructions>
|
|
1815
|
+
# Sisyphus Getting Started \u2014 Step 2: Nvim Basics
|
|
1816
|
+
|
|
1817
|
+
## Environment Data
|
|
1818
|
+
- nvimInstalled: ${nvimInstalled}
|
|
1819
|
+
|
|
1820
|
+
## Instructions for Claude
|
|
1821
|
+
|
|
1822
|
+
This step is OPTIONAL. Nvim is useful for reviewing agent work in tmux panes but not required.
|
|
1823
|
+
|
|
1824
|
+
### If nvim is NOT installed (nvimInstalled: false)
|
|
1825
|
+
|
|
1826
|
+
Ask the user: "Neovim is handy for reviewing files in tmux panes. Want me to install it, or skip this step?"
|
|
1827
|
+
|
|
1828
|
+
- **Install**: Run \`brew install neovim\` (macOS) or suggest their package manager
|
|
1829
|
+
- **Skip**: That's fine \u2014 they can use \`cat\`, \`less\`, or any editor they prefer. Proceed to step 3.
|
|
1830
|
+
|
|
1831
|
+
### If nvim IS installed (nvimInstalled: true)
|
|
1832
|
+
|
|
1833
|
+
Teach exactly 3 things \u2014 no more:
|
|
1834
|
+
|
|
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)
|
|
1838
|
+
|
|
1839
|
+
### Hands-on exercise
|
|
1840
|
+
|
|
1841
|
+
Create a temporary file for practice:
|
|
1842
|
+
\`\`\`
|
|
1843
|
+
echo "Hello from the sisyphus tutorial!" > /tmp/sisyphus-tutorial-test.txt
|
|
1844
|
+
\`\`\`
|
|
1845
|
+
|
|
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\`
|
|
1850
|
+
|
|
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."
|
|
1852
|
+
|
|
1853
|
+
### Verification
|
|
1854
|
+
|
|
1855
|
+
Ask if they were able to open and close the file (or if they skipped).
|
|
1856
|
+
|
|
1857
|
+
Proceed:
|
|
1858
|
+
\`\`\`
|
|
1859
|
+
sisyphus getting-started --tutorial 3
|
|
1860
|
+
\`\`\`
|
|
1861
|
+
</claude-instructions>
|
|
1862
|
+
`);
|
|
1863
|
+
}
|
|
1864
|
+
function printStep3() {
|
|
1865
|
+
console.log(`
|
|
1866
|
+
<claude-instructions>
|
|
1867
|
+
# Sisyphus Getting Started \u2014 Step 3: Sisyphus Concepts & Keybinds
|
|
1868
|
+
|
|
1869
|
+
## Instructions for Claude
|
|
1870
|
+
|
|
1871
|
+
### 1. Explain the session model
|
|
1872
|
+
|
|
1873
|
+
This is the KEY concept. Use the diagram and be clear:
|
|
1874
|
+
|
|
1875
|
+
\`\`\`
|
|
1876
|
+
YOUR tmux session ("work") Sisyphus tmux session ("sisyphus-abc123")
|
|
1877
|
+
\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
|
+
\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
|
|
1880
|
+
\u2502 + dashboard \u2502 \u2502 \u2502 \u2502 \u2502
|
|
1881
|
+
\u2502 \u2502 \u2502 Plans & \u2502 Writes \u2502 Writes \u2502
|
|
1882
|
+
\u2502 \u2502 \u2502 assigns \u2502 code \u2502 tests \u2502
|
|
1883
|
+
\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
|
|
1884
|
+
\`\`\`
|
|
1885
|
+
|
|
1886
|
+
Key points:
|
|
1887
|
+
- Sisyphus creates its OWN tmux session \u2014 it doesn't clutter yours
|
|
1888
|
+
- The **orchestrator** (yellow pane) plans work and spawns agents
|
|
1889
|
+
- **Agents** (colored panes) work in parallel on subtasks
|
|
1890
|
+
- Your session stays clean \u2014 you get a **dashboard** for monitoring
|
|
1891
|
+
- You can jump between your session and the sisyphus session to observe
|
|
1892
|
+
|
|
1893
|
+
### 2. Teach keybinds
|
|
1894
|
+
|
|
1895
|
+
Two keybinds to remember:
|
|
1896
|
+
|
|
1897
|
+
| Keybind | Action |
|
|
1898
|
+
|---------|--------|
|
|
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.
|
|
1903
|
+
|
|
1904
|
+
### 3. Verify keybinds are installed
|
|
1905
|
+
|
|
1906
|
+
Run \`sisyphus doctor\` and check the output. Look for:
|
|
1907
|
+
- "Cycle script" \u2014 should be \u2713
|
|
1908
|
+
- "Tmux keybind" \u2014 should be \u2713
|
|
1909
|
+
|
|
1910
|
+
If either is missing, run: \`sisyphus setup-keybind\`
|
|
1911
|
+
|
|
1912
|
+
### 4. Test the keybind
|
|
1913
|
+
|
|
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.
|
|
1915
|
+
|
|
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
|
|
1920
|
+
|
|
1921
|
+
### 5. Verification
|
|
1922
|
+
|
|
1923
|
+
Confirm:
|
|
1924
|
+
- 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
|
|
1927
|
+
|
|
1928
|
+
Proceed:
|
|
1929
|
+
\`\`\`
|
|
1930
|
+
sisyphus getting-started --tutorial 4
|
|
1931
|
+
\`\`\`
|
|
1932
|
+
</claude-instructions>
|
|
1933
|
+
`);
|
|
1934
|
+
}
|
|
1935
|
+
function printStep4() {
|
|
1936
|
+
console.log(`
|
|
1937
|
+
<claude-instructions>
|
|
1938
|
+
# Sisyphus Getting Started \u2014 Step 4: Demo Session
|
|
1939
|
+
|
|
1940
|
+
## Instructions for Claude
|
|
1941
|
+
|
|
1942
|
+
This is the grand finale \u2014 a live demo session.
|
|
1943
|
+
|
|
1944
|
+
### 1. Health check
|
|
1945
|
+
|
|
1946
|
+
Run \`sisyphus doctor\` first. If any checks are failing, help the user fix them before proceeding.
|
|
1947
|
+
All core checks (tmux, daemon, keybinds) should be \u2713.
|
|
1948
|
+
|
|
1949
|
+
### 2. Launch the demo
|
|
1950
|
+
|
|
1951
|
+
Run this command:
|
|
1952
|
+
\`\`\`
|
|
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."
|
|
1954
|
+
\`\`\`
|
|
1955
|
+
|
|
1956
|
+
### 3. Walk through what's happening
|
|
1957
|
+
|
|
1958
|
+
Guide the user through observing the session in real-time:
|
|
1959
|
+
|
|
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"
|
|
1964
|
+
|
|
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"
|
|
1969
|
+
|
|
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
|
|
1973
|
+
|
|
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
|
|
1979
|
+
|
|
1980
|
+
### 4. After completion
|
|
1981
|
+
|
|
1982
|
+
Once the session shows "completed":
|
|
1983
|
+
|
|
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/"
|
|
1987
|
+
|
|
1988
|
+
### 5. Congratulations!
|
|
1989
|
+
|
|
1990
|
+
Wrap up with:
|
|
1991
|
+
|
|
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"\`
|
|
2000
|
+
>
|
|
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.
|
|
2005
|
+
</claude-instructions>
|
|
2006
|
+
`);
|
|
2007
|
+
}
|
|
2008
|
+
var STEPS = [printStep0, printStep1, printStep2, printStep3, printStep4];
|
|
1401
2009
|
function registerGettingStarted(program2) {
|
|
1402
|
-
program2.command("getting-started").description("
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
" Sisyphus is a multi-agent orchestration daemon for Claude Code.",
|
|
1412
|
-
" It breaks large tasks into subtasks, spawns parallel Claude agents,",
|
|
1413
|
-
" and coordinates their work across multiple cycles \u2014 autonomously.",
|
|
1414
|
-
"",
|
|
1415
|
-
" \u2500\u2500\u2500 Tmux \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1416
|
-
""
|
|
1417
|
-
];
|
|
1418
|
-
if (!hasTmux) {
|
|
1419
|
-
lines.push(
|
|
1420
|
-
" \u26A0 tmux is not installed. Sisyphus requires tmux.",
|
|
1421
|
-
" Install it: brew install tmux (macOS)",
|
|
1422
|
-
" apt install tmux (Linux)",
|
|
1423
|
-
""
|
|
1424
|
-
);
|
|
1425
|
-
} else if (!inTmux) {
|
|
1426
|
-
lines.push(
|
|
1427
|
-
" \u26A0 You are not inside a tmux session.",
|
|
1428
|
-
" Sisyphus spawns agent panes inside tmux, so you should",
|
|
1429
|
-
" start a tmux session before running sisyphus:",
|
|
1430
|
-
"",
|
|
1431
|
-
" tmux new-session",
|
|
1432
|
-
""
|
|
1433
|
-
);
|
|
1434
|
-
} else {
|
|
1435
|
-
lines.push(
|
|
1436
|
-
" \u2713 You are inside tmux. Good to go.",
|
|
1437
|
-
""
|
|
1438
|
-
);
|
|
2010
|
+
program2.command("getting-started").description("Interactive tutorial (best with Claude Code)").option("--tutorial <step>", "Tutorial step (0-4)", parseInt).action((opts) => {
|
|
2011
|
+
if (opts.tutorial !== void 0) {
|
|
2012
|
+
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.`);
|
|
2015
|
+
process.exit(1);
|
|
2016
|
+
}
|
|
2017
|
+
STEPS[step]();
|
|
2018
|
+
return;
|
|
1439
2019
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
" If you could do it with a single Claude Code session in plan",
|
|
1446
|
-
" mode, it's too small for sisyphus.",
|
|
1447
|
-
"",
|
|
1448
|
-
" Good tasks:",
|
|
1449
|
-
' \u2022 "Implement this feature" @path/to/requirements.md',
|
|
1450
|
-
' \u2022 "Do a deep dive on all SEO/AEO optimizations and',
|
|
1451
|
-
' systematically apply them across the site"',
|
|
1452
|
-
" \u2022 Large-scale refactors spanning dozens of files",
|
|
1453
|
-
" \u2022 Full feature builds from written requirements",
|
|
1454
|
-
"",
|
|
1455
|
-
" Too small for sisyphus:",
|
|
1456
|
-
' \u2022 "Add these 3 UI components to the page"',
|
|
1457
|
-
' \u2022 "Fix this bug in auth.ts"',
|
|
1458
|
-
" \u2022 Anything a single Claude session handles comfortably",
|
|
1459
|
-
"",
|
|
1460
|
-
" Tasks don't need to be hyper-specific \u2014 broad but meaningful",
|
|
1461
|
-
" tasks work great because the orchestrator will plan the approach.",
|
|
1462
|
-
" What matters is SCALE, not specificity.",
|
|
1463
|
-
"",
|
|
1464
|
-
" For best results, write requirements and reference them directly:",
|
|
1465
|
-
"",
|
|
1466
|
-
' sisyphus start "Implement this @path/to/requirements.md"',
|
|
1467
|
-
"",
|
|
1468
|
-
" \u2500\u2500\u2500 How It Works \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1469
|
-
"",
|
|
1470
|
-
' 1. You run: sisyphus start "task description"',
|
|
1471
|
-
" 2. An orchestrator Claude reviews the task and creates a roadmap",
|
|
1472
|
-
" 3. It spawns agent Claude instances in parallel tmux panes",
|
|
1473
|
-
" 4. Agents work independently, then submit reports when done",
|
|
1474
|
-
" 5. The orchestrator respawns with fresh context, reviews progress,",
|
|
1475
|
-
" and kicks off the next cycle of work",
|
|
1476
|
-
" 6. This repeats until the orchestrator marks the task complete",
|
|
1477
|
-
"",
|
|
1478
|
-
" The orchestrator is stateless \u2014 it gets killed after each cycle",
|
|
1479
|
-
" and respawned fresh with the full session state. This means it",
|
|
1480
|
-
" never runs out of context, no matter how many cycles a task takes.",
|
|
1481
|
-
"",
|
|
1482
|
-
" \u2500\u2500\u2500 Monitoring (Important!) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1483
|
-
"",
|
|
1484
|
-
" Sisyphus sessions should be actively monitored. Agents can get",
|
|
1485
|
-
" stuck waiting for input, fail to submit reports, or take the",
|
|
1486
|
-
" roadmap in a direction you don't want. The dashboard is your",
|
|
1487
|
-
" primary tool for staying on top of things:",
|
|
1488
|
-
"",
|
|
1489
|
-
" sisyphus dashboard",
|
|
1490
|
-
"",
|
|
1491
|
-
" Key dashboard actions:",
|
|
1492
|
-
" m \u2014 Message the orchestrator to steer direction",
|
|
1493
|
-
" w \u2014 Jump directly into the sisyphus tmux session",
|
|
1494
|
-
" to see exactly what agents are doing",
|
|
1495
|
-
"",
|
|
1496
|
-
" Use `m` to course-correct from the dashboard, and `w` when you",
|
|
1497
|
-
" need the most granular view of agent activity.",
|
|
1498
|
-
"",
|
|
1499
|
-
" \u2500\u2500\u2500 Commands \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1500
|
-
"",
|
|
1501
|
-
" Start & monitor:",
|
|
1502
|
-
' sisyphus start "task" Start a session',
|
|
1503
|
-
' sisyphus start "task @requirements.md" Start referencing requirements',
|
|
1504
|
-
" sisyphus status [id] Show session status",
|
|
1505
|
-
" sisyphus list List all sessions",
|
|
1506
|
-
" sisyphus dashboard Open TUI dashboard",
|
|
1507
|
-
"",
|
|
1508
|
-
" Control:",
|
|
1509
|
-
' sisyphus resume <id> "instructions" Resume with new direction',
|
|
1510
|
-
" sisyphus kill <id> Stop a session",
|
|
1511
|
-
"",
|
|
1512
|
-
" Health:",
|
|
1513
|
-
" sisyphus doctor Check installation health",
|
|
1514
|
-
" tail -f ~/.sisyphus/daemon.log Watch daemon logs",
|
|
1515
|
-
"",
|
|
1516
|
-
" \u2500\u2500\u2500 Next Steps \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1517
|
-
"",
|
|
1518
|
-
" 1. Run `sisyphus doctor` to check your setup",
|
|
1519
|
-
" 2. Start a tmux session: `tmux new-session`",
|
|
1520
|
-
' 3. Try it: `sisyphus start "your task description"`',
|
|
1521
|
-
""
|
|
1522
|
-
);
|
|
1523
|
-
console.log(lines.join("\n"));
|
|
2020
|
+
if (!isClaudeCode()) {
|
|
2021
|
+
printNonClaudeMessage();
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
printStep0();
|
|
1524
2025
|
});
|
|
1525
2026
|
}
|
|
1526
2027
|
|
|
1527
2028
|
// src/cli/commands/init.ts
|
|
1528
|
-
import { existsSync as
|
|
1529
|
-
import { join as
|
|
2029
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2030
|
+
import { join as join6 } from "path";
|
|
1530
2031
|
var DEFAULT_CONFIG = {};
|
|
1531
2032
|
var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
|
|
1532
2033
|
|
|
@@ -1537,19 +2038,19 @@ var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
|
|
|
1537
2038
|
function registerInit(program2) {
|
|
1538
2039
|
program2.command("init").description("Initialize sisyphus configuration for this project").option("--orchestrator", "Also create a custom orchestrator prompt template").action((opts) => {
|
|
1539
2040
|
const cwd = process.cwd();
|
|
1540
|
-
const sisDir =
|
|
1541
|
-
const configPath =
|
|
1542
|
-
if (
|
|
2041
|
+
const sisDir = join6(cwd, ".sisyphus");
|
|
2042
|
+
const configPath = join6(sisDir, "config.json");
|
|
2043
|
+
if (existsSync6(configPath)) {
|
|
1543
2044
|
console.log(`Already initialized: ${configPath}`);
|
|
1544
2045
|
return;
|
|
1545
2046
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
2047
|
+
mkdirSync4(sisDir, { recursive: true });
|
|
2048
|
+
writeFileSync4(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n", "utf-8");
|
|
1548
2049
|
console.log(`Created ${configPath}`);
|
|
1549
2050
|
if (opts.orchestrator) {
|
|
1550
|
-
const orchPath =
|
|
1551
|
-
if (!
|
|
1552
|
-
|
|
2051
|
+
const orchPath = join6(sisDir, "orchestrator.md");
|
|
2052
|
+
if (!existsSync6(orchPath)) {
|
|
2053
|
+
writeFileSync4(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
|
|
1553
2054
|
console.log(`Created ${orchPath}`);
|
|
1554
2055
|
}
|
|
1555
2056
|
}
|
|
@@ -1562,6 +2063,94 @@ function registerInit(program2) {
|
|
|
1562
2063
|
});
|
|
1563
2064
|
}
|
|
1564
2065
|
|
|
2066
|
+
// src/cli/commands/setup.ts
|
|
2067
|
+
import { execSync as execSync8 } from "child_process";
|
|
2068
|
+
function getTmuxVersion() {
|
|
2069
|
+
try {
|
|
2070
|
+
return execSync8("tmux -V", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
2071
|
+
} catch {
|
|
2072
|
+
return "installed";
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
function printResults(result, daemonOk, keybindMsg) {
|
|
2076
|
+
console.log("");
|
|
2077
|
+
console.log("Setting up Sisyphus...");
|
|
2078
|
+
console.log("");
|
|
2079
|
+
if (result.tmuxInstalled) {
|
|
2080
|
+
const detail = getTmuxVersion();
|
|
2081
|
+
console.log(` \u2713 tmux: ${detail}${result.tmuxAutoInstalled ? " (just installed)" : ""}`);
|
|
2082
|
+
} else {
|
|
2083
|
+
const hint = process.platform === "darwin" ? "Install Homebrew (https://brew.sh) then: brew install tmux" : "apt install tmux (Debian/Ubuntu) or your package manager";
|
|
2084
|
+
console.log(` \u2717 tmux: Not installed \u2014 ${hint}`);
|
|
2085
|
+
}
|
|
2086
|
+
if (result.tmuxDefaultsWritten) {
|
|
2087
|
+
console.log(" \u2713 tmux config: Sensible defaults written to ~/.tmux.conf");
|
|
2088
|
+
}
|
|
2089
|
+
if (process.platform === "darwin") {
|
|
2090
|
+
if (result.terminal.isIterm) {
|
|
2091
|
+
console.log(` \u2713 Terminal: ${result.terminal.name}`);
|
|
2092
|
+
} else {
|
|
2093
|
+
const name = result.terminal.name ? result.terminal.name : "unknown";
|
|
2094
|
+
console.log(` \u26A0 Terminal: ${name} \u2014 iTerm2 recommended (https://iterm2.com)`);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
if (result.itermOptionKey.checked) {
|
|
2098
|
+
if (result.itermOptionKey.allCorrect) {
|
|
2099
|
+
console.log(" \u2713 Right Option Key: Esc+");
|
|
2100
|
+
} else {
|
|
2101
|
+
const profiles = result.itermOptionKey.incorrectProfiles.map((p) => `"${p}"`).join(", ");
|
|
2102
|
+
console.log(` \u26A0 Right Option Key: Not set to Esc+ for ${profiles}`);
|
|
2103
|
+
console.log(" Fix: iTerm2 \u2192 Settings \u2192 Profiles \u2192 Keys \u2192 Right Option Key \u2192 Esc+");
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
if (daemonOk) {
|
|
2107
|
+
console.log(" \u2713 Daemon: Running");
|
|
2108
|
+
} else {
|
|
2109
|
+
console.log(" \u2717 Daemon: Failed to start");
|
|
2110
|
+
}
|
|
2111
|
+
console.log(` \u2713 Keybindings: ${keybindMsg}`);
|
|
2112
|
+
if (result.command.installed) {
|
|
2113
|
+
console.log(` \u2713 /begin command: ${result.command.path}${result.command.autoInstalled ? " (just installed)" : ""}`);
|
|
2114
|
+
} else {
|
|
2115
|
+
console.log(" \u2717 /begin command: Failed to install");
|
|
2116
|
+
}
|
|
2117
|
+
if (result.nvim.installed) {
|
|
2118
|
+
const extra = result.nvim.autoInstalled ? " (just installed)" : "";
|
|
2119
|
+
console.log(` \u2713 Editor: nvim ${result.nvim.version}${extra}`);
|
|
2120
|
+
if (result.nvim.lazyVimInstalled) {
|
|
2121
|
+
console.log(" \u2713 LazyVim: Starter config installed to ~/.config/nvim/");
|
|
2122
|
+
}
|
|
2123
|
+
} else {
|
|
2124
|
+
console.log(" \u26A0 Editor: nvim not installed");
|
|
2125
|
+
if (process.platform === "darwin") {
|
|
2126
|
+
console.log(" Install: brew install neovim");
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
console.log("");
|
|
2130
|
+
console.log("Run 'sisyphus getting-started' for a usage guide.");
|
|
2131
|
+
console.log("");
|
|
2132
|
+
}
|
|
2133
|
+
function registerSetup(program2) {
|
|
2134
|
+
program2.command("setup").description("One-time setup: install dependencies, daemon, keybindings, and commands").action(async () => {
|
|
2135
|
+
const result = runOnboarding();
|
|
2136
|
+
let daemonOk = false;
|
|
2137
|
+
try {
|
|
2138
|
+
await ensureDaemonInstalled();
|
|
2139
|
+
daemonOk = true;
|
|
2140
|
+
} catch {
|
|
2141
|
+
daemonOk = isInstalled();
|
|
2142
|
+
}
|
|
2143
|
+
const keybindResult = setupTmuxKeybind();
|
|
2144
|
+
let keybindMsg;
|
|
2145
|
+
if (keybindResult.status === "installed" || keybindResult.status === "already-installed") {
|
|
2146
|
+
keybindMsg = `${DEFAULT_KEY} (cycle), ${DEFAULT_HOME_KEY} (dashboard)`;
|
|
2147
|
+
} else {
|
|
2148
|
+
keybindMsg = keybindResult.message;
|
|
2149
|
+
}
|
|
2150
|
+
printResults(result, daemonOk, keybindMsg);
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
|
|
1565
2154
|
// src/cli/index.ts
|
|
1566
2155
|
var nodeVersion = parseInt(process.versions.node.split(".")[0], 10);
|
|
1567
2156
|
if (nodeVersion < 22) {
|
|
@@ -1569,7 +2158,11 @@ if (nodeVersion < 22) {
|
|
|
1569
2158
|
process.exit(1);
|
|
1570
2159
|
}
|
|
1571
2160
|
var program = new Command();
|
|
1572
|
-
program.name("sisyphus").description("tmux-integrated orchestration daemon for Claude Code").version(
|
|
2161
|
+
program.name("sisyphus").description("tmux-integrated orchestration daemon for Claude Code").version(
|
|
2162
|
+
JSON.parse(
|
|
2163
|
+
readFileSync5(join7(dirname3(fileURLToPath3(import.meta.url)), "..", "package.json"), "utf-8")
|
|
2164
|
+
).version
|
|
2165
|
+
);
|
|
1573
2166
|
program.configureHelp({
|
|
1574
2167
|
sortSubcommands: false
|
|
1575
2168
|
});
|
|
@@ -1596,6 +2189,7 @@ registerDoctor(program);
|
|
|
1596
2189
|
registerCompanionContext(program);
|
|
1597
2190
|
registerGettingStarted(program);
|
|
1598
2191
|
registerInit(program);
|
|
2192
|
+
registerSetup(program);
|
|
1599
2193
|
program.addHelpText("after", `
|
|
1600
2194
|
Examples:
|
|
1601
2195
|
$ sisyphus start "Implement auth system" Start a new session
|
|
@@ -1608,15 +2202,11 @@ Run 'sisyphus getting-started' for a complete usage guide.
|
|
|
1608
2202
|
`);
|
|
1609
2203
|
var args = process.argv.slice(2);
|
|
1610
2204
|
var firstArg = args[0];
|
|
1611
|
-
var skipWelcome = ["doctor", "getting-started", "help", "--help", "-h", "init", "uninstall", "--version", "-V"];
|
|
1612
|
-
if (!
|
|
1613
|
-
|
|
1614
|
-
console.log("");
|
|
1615
|
-
console.log(" Welcome to Sisyphus \u2014 multi-agent orchestration for Claude Code.");
|
|
2205
|
+
var skipWelcome = ["doctor", "getting-started", "help", "--help", "-h", "init", "setup", "uninstall", "--version", "-V"];
|
|
2206
|
+
if (!existsSync7(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
|
|
2207
|
+
mkdirSync5(globalDir(), { recursive: true });
|
|
1616
2208
|
console.log("");
|
|
1617
|
-
console.log("
|
|
1618
|
-
console.log(" sisyphus doctor Check your setup");
|
|
1619
|
-
console.log(" sisyphus getting-started Learn the basics");
|
|
2209
|
+
console.log(" Welcome to Sisyphus. Run 'sisyphus setup' to get started.");
|
|
1620
2210
|
console.log("");
|
|
1621
2211
|
}
|
|
1622
2212
|
program.parseAsync(process.argv).catch((err) => {
|