sisyphi 1.1.7 → 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 +763 -179
- package/dist/cli.js.map +1 -1
- package/dist/templates/begin.md +22 -0
- package/package.json +1 -1
- package/templates/begin.md +22 -0
package/dist/cli.js
CHANGED
|
@@ -20,9 +20,9 @@ import {
|
|
|
20
20
|
|
|
21
21
|
// src/cli/index.ts
|
|
22
22
|
import { Command } from "commander";
|
|
23
|
-
import { existsSync as
|
|
24
|
-
import { dirname as
|
|
25
|
-
import { fileURLToPath 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";
|
|
26
26
|
|
|
27
27
|
// src/cli/commands/start.ts
|
|
28
28
|
import { execSync as execSync5 } from "child_process";
|
|
@@ -644,24 +644,6 @@ function registerSpawn(program2) {
|
|
|
644
644
|
}
|
|
645
645
|
|
|
646
646
|
// src/cli/commands/submit.ts
|
|
647
|
-
import { execSync as execSync6 } from "child_process";
|
|
648
|
-
function isInWorktree() {
|
|
649
|
-
try {
|
|
650
|
-
const gitDir = execSync6("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
|
|
651
|
-
const commonDir = execSync6("git rev-parse --git-common-dir", { encoding: "utf-8" }).trim();
|
|
652
|
-
return gitDir !== commonDir;
|
|
653
|
-
} catch {
|
|
654
|
-
return false;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
function getUncommittedChanges() {
|
|
658
|
-
try {
|
|
659
|
-
const status = execSync6("git status --porcelain", { encoding: "utf-8" }).trim();
|
|
660
|
-
return status || null;
|
|
661
|
-
} catch {
|
|
662
|
-
return null;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
647
|
function registerSubmit(program2) {
|
|
666
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) => {
|
|
667
649
|
assertTmux();
|
|
@@ -671,17 +653,6 @@ function registerSubmit(program2) {
|
|
|
671
653
|
console.error("Error: provide --session or set SISYPHUS_SESSION_ID (and SISYPHUS_AGENT_ID) environment variables");
|
|
672
654
|
process.exit(1);
|
|
673
655
|
}
|
|
674
|
-
if (isInWorktree()) {
|
|
675
|
-
const changes = getUncommittedChanges();
|
|
676
|
-
if (changes) {
|
|
677
|
-
console.error("Error: uncommitted changes in worktree. Commit your changes before submitting.");
|
|
678
|
-
console.error('\nCommit first:\n git add -A && git commit -m "description of changes"\n');
|
|
679
|
-
console.error("Or discard:\n git checkout -- .\n");
|
|
680
|
-
console.error("Uncommitted changes:");
|
|
681
|
-
console.error(changes);
|
|
682
|
-
process.exit(1);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
656
|
const report = opts.report ?? await readStdin();
|
|
686
657
|
if (!report) {
|
|
687
658
|
console.error("Error: provide --report or pipe content via stdin");
|
|
@@ -1194,7 +1165,210 @@ function registerSetupKeybind(program2) {
|
|
|
1194
1165
|
|
|
1195
1166
|
// src/cli/commands/doctor.ts
|
|
1196
1167
|
import { execSync as execSync7 } from "child_process";
|
|
1197
|
-
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
|
|
1198
1372
|
function checkNodeVersion() {
|
|
1199
1373
|
const major = parseInt(process.versions.node.split(".")[0], 10);
|
|
1200
1374
|
if (major < 22) {
|
|
@@ -1251,7 +1425,7 @@ function checkDaemonInstalled() {
|
|
|
1251
1425
|
};
|
|
1252
1426
|
}
|
|
1253
1427
|
const pid = daemonPidPath();
|
|
1254
|
-
if (
|
|
1428
|
+
if (existsSync5(pid)) {
|
|
1255
1429
|
return { name: "Daemon setup", status: "ok", detail: `PID file found at ${pid}` };
|
|
1256
1430
|
}
|
|
1257
1431
|
return {
|
|
@@ -1263,7 +1437,7 @@ function checkDaemonInstalled() {
|
|
|
1263
1437
|
}
|
|
1264
1438
|
function checkDaemonRunning() {
|
|
1265
1439
|
const pid = daemonPidPath();
|
|
1266
|
-
if (!
|
|
1440
|
+
if (!existsSync5(pid)) {
|
|
1267
1441
|
const fix = process.platform === "darwin" ? "launchctl load -w ~/Library/LaunchAgents/com.sisyphus.daemon.plist" : "sisyphusd & \u2014 or check if the process is running";
|
|
1268
1442
|
return {
|
|
1269
1443
|
name: "Daemon process",
|
|
@@ -1301,7 +1475,7 @@ function checkTmux() {
|
|
|
1301
1475
|
}
|
|
1302
1476
|
function checkCycleScript() {
|
|
1303
1477
|
const path = cycleScriptPath();
|
|
1304
|
-
if (!
|
|
1478
|
+
if (!existsSync5(path)) {
|
|
1305
1479
|
return {
|
|
1306
1480
|
name: "Cycle script",
|
|
1307
1481
|
status: "fail",
|
|
@@ -1326,7 +1500,7 @@ function checkCycleScript() {
|
|
|
1326
1500
|
function checkTmuxKeybind() {
|
|
1327
1501
|
const existing = getExistingBinding(DEFAULT_KEY);
|
|
1328
1502
|
if (existing === null) {
|
|
1329
|
-
if (
|
|
1503
|
+
if (existsSync5(sisyphusTmuxConfPath())) {
|
|
1330
1504
|
return {
|
|
1331
1505
|
name: `Tmux keybind (${DEFAULT_KEY})`,
|
|
1332
1506
|
status: "warn",
|
|
@@ -1352,25 +1526,85 @@ function checkTmuxKeybind() {
|
|
|
1352
1526
|
}
|
|
1353
1527
|
function checkGlobalDir() {
|
|
1354
1528
|
const dir = globalDir();
|
|
1355
|
-
if (
|
|
1529
|
+
if (existsSync5(dir)) {
|
|
1356
1530
|
return { name: "Data directory", status: "ok", detail: dir };
|
|
1357
1531
|
}
|
|
1358
1532
|
return { name: "Data directory", status: "warn", detail: `${dir} does not exist (created on first use)` };
|
|
1359
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
|
+
}
|
|
1360
1589
|
var SYMBOLS = { ok: "\u2713", warn: "!", fail: "\u2717" };
|
|
1361
1590
|
function registerDoctor(program2) {
|
|
1362
1591
|
program2.command("doctor").description("Check sisyphus installation health").action(async () => {
|
|
1592
|
+
const itermCheck = checkItermRightOptionKey();
|
|
1363
1593
|
const checks = [
|
|
1364
1594
|
checkNodeVersion(),
|
|
1365
1595
|
checkClaudeCli(),
|
|
1366
1596
|
checkGit(),
|
|
1367
1597
|
checkTmux(),
|
|
1368
1598
|
checkTmuxVersion(),
|
|
1599
|
+
checkTerminal(),
|
|
1600
|
+
...itermCheck ? [itermCheck] : [],
|
|
1369
1601
|
checkGlobalDir(),
|
|
1370
1602
|
checkDaemonInstalled(),
|
|
1371
1603
|
checkDaemonRunning(),
|
|
1372
1604
|
checkCycleScript(),
|
|
1373
|
-
checkTmuxKeybind()
|
|
1605
|
+
checkTmuxKeybind(),
|
|
1606
|
+
checkBeginCommand(),
|
|
1607
|
+
checkNvim()
|
|
1374
1608
|
];
|
|
1375
1609
|
let hasIssues = false;
|
|
1376
1610
|
for (const c of checks) {
|
|
@@ -1400,135 +1634,400 @@ function registerCompanionContext(program2) {
|
|
|
1400
1634
|
}
|
|
1401
1635
|
|
|
1402
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];
|
|
1403
2009
|
function registerGettingStarted(program2) {
|
|
1404
|
-
program2.command("getting-started").description("
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
" Sisyphus is a multi-agent orchestration daemon for Claude Code.",
|
|
1414
|
-
" It breaks large tasks into subtasks, spawns parallel Claude agents,",
|
|
1415
|
-
" and coordinates their work across multiple cycles \u2014 autonomously.",
|
|
1416
|
-
"",
|
|
1417
|
-
" \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",
|
|
1418
|
-
""
|
|
1419
|
-
];
|
|
1420
|
-
if (!hasTmux) {
|
|
1421
|
-
lines.push(
|
|
1422
|
-
" \u26A0 tmux is not installed. Sisyphus requires tmux.",
|
|
1423
|
-
" Install it: brew install tmux (macOS)",
|
|
1424
|
-
" apt install tmux (Linux)",
|
|
1425
|
-
""
|
|
1426
|
-
);
|
|
1427
|
-
} else if (!inTmux) {
|
|
1428
|
-
lines.push(
|
|
1429
|
-
" \u26A0 You are not inside a tmux session.",
|
|
1430
|
-
" Sisyphus spawns agent panes inside tmux, so you should",
|
|
1431
|
-
" start a tmux session before running sisyphus:",
|
|
1432
|
-
"",
|
|
1433
|
-
" tmux new-session",
|
|
1434
|
-
""
|
|
1435
|
-
);
|
|
1436
|
-
} else {
|
|
1437
|
-
lines.push(
|
|
1438
|
-
" \u2713 You are inside tmux. Good to go.",
|
|
1439
|
-
""
|
|
1440
|
-
);
|
|
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;
|
|
1441
2019
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
" If you could do it with a single Claude Code session in plan",
|
|
1448
|
-
" mode, it's too small for sisyphus.",
|
|
1449
|
-
"",
|
|
1450
|
-
" Good tasks:",
|
|
1451
|
-
' \u2022 "Implement this feature" @path/to/requirements.md',
|
|
1452
|
-
' \u2022 "Do a deep dive on all SEO/AEO optimizations and',
|
|
1453
|
-
' systematically apply them across the site"',
|
|
1454
|
-
" \u2022 Large-scale refactors spanning dozens of files",
|
|
1455
|
-
" \u2022 Full feature builds from written requirements",
|
|
1456
|
-
"",
|
|
1457
|
-
" Too small for sisyphus:",
|
|
1458
|
-
' \u2022 "Add these 3 UI components to the page"',
|
|
1459
|
-
' \u2022 "Fix this bug in auth.ts"',
|
|
1460
|
-
" \u2022 Anything a single Claude session handles comfortably",
|
|
1461
|
-
"",
|
|
1462
|
-
" Tasks don't need to be hyper-specific \u2014 broad but meaningful",
|
|
1463
|
-
" tasks work great because the orchestrator will plan the approach.",
|
|
1464
|
-
" What matters is SCALE, not specificity.",
|
|
1465
|
-
"",
|
|
1466
|
-
" For best results, write requirements and reference them directly:",
|
|
1467
|
-
"",
|
|
1468
|
-
' sisyphus start "Implement this @path/to/requirements.md"',
|
|
1469
|
-
"",
|
|
1470
|
-
" \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",
|
|
1471
|
-
"",
|
|
1472
|
-
' 1. You run: sisyphus start "task description"',
|
|
1473
|
-
" 2. An orchestrator Claude reviews the task and creates a roadmap",
|
|
1474
|
-
" 3. It spawns agent Claude instances in parallel tmux panes",
|
|
1475
|
-
" 4. Agents work independently, then submit reports when done",
|
|
1476
|
-
" 5. The orchestrator respawns with fresh context, reviews progress,",
|
|
1477
|
-
" and kicks off the next cycle of work",
|
|
1478
|
-
" 6. This repeats until the orchestrator marks the task complete",
|
|
1479
|
-
"",
|
|
1480
|
-
" The orchestrator is stateless \u2014 it gets killed after each cycle",
|
|
1481
|
-
" and respawned fresh with the full session state. This means it",
|
|
1482
|
-
" never runs out of context, no matter how many cycles a task takes.",
|
|
1483
|
-
"",
|
|
1484
|
-
" \u2500\u2500\u2500 Monitoring (Important!) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1485
|
-
"",
|
|
1486
|
-
" Sisyphus sessions should be actively monitored. Agents can get",
|
|
1487
|
-
" stuck waiting for input, fail to submit reports, or take the",
|
|
1488
|
-
" roadmap in a direction you don't want. The dashboard is your",
|
|
1489
|
-
" primary tool for staying on top of things:",
|
|
1490
|
-
"",
|
|
1491
|
-
" sisyphus dashboard",
|
|
1492
|
-
"",
|
|
1493
|
-
" Key dashboard actions:",
|
|
1494
|
-
" m \u2014 Message the orchestrator to steer direction",
|
|
1495
|
-
" w \u2014 Jump directly into the sisyphus tmux session",
|
|
1496
|
-
" to see exactly what agents are doing",
|
|
1497
|
-
"",
|
|
1498
|
-
" Use `m` to course-correct from the dashboard, and `w` when you",
|
|
1499
|
-
" need the most granular view of agent activity.",
|
|
1500
|
-
"",
|
|
1501
|
-
" \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",
|
|
1502
|
-
"",
|
|
1503
|
-
" Start & monitor:",
|
|
1504
|
-
' sisyphus start "task" Start a session',
|
|
1505
|
-
' sisyphus start "task @requirements.md" Start referencing requirements',
|
|
1506
|
-
" sisyphus status [id] Show session status",
|
|
1507
|
-
" sisyphus list List all sessions",
|
|
1508
|
-
" sisyphus dashboard Open TUI dashboard",
|
|
1509
|
-
"",
|
|
1510
|
-
" Control:",
|
|
1511
|
-
' sisyphus resume <id> "instructions" Resume with new direction',
|
|
1512
|
-
" sisyphus kill <id> Stop a session",
|
|
1513
|
-
"",
|
|
1514
|
-
" Health:",
|
|
1515
|
-
" sisyphus doctor Check installation health",
|
|
1516
|
-
" tail -f ~/.sisyphus/daemon.log Watch daemon logs",
|
|
1517
|
-
"",
|
|
1518
|
-
" \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",
|
|
1519
|
-
"",
|
|
1520
|
-
" 1. Run `sisyphus doctor` to check your setup",
|
|
1521
|
-
" 2. Start a tmux session: `tmux new-session`",
|
|
1522
|
-
' 3. Try it: `sisyphus start "your task description"`',
|
|
1523
|
-
""
|
|
1524
|
-
);
|
|
1525
|
-
console.log(lines.join("\n"));
|
|
2020
|
+
if (!isClaudeCode()) {
|
|
2021
|
+
printNonClaudeMessage();
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
printStep0();
|
|
1526
2025
|
});
|
|
1527
2026
|
}
|
|
1528
2027
|
|
|
1529
2028
|
// src/cli/commands/init.ts
|
|
1530
|
-
import { existsSync as
|
|
1531
|
-
import { join as
|
|
2029
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2030
|
+
import { join as join6 } from "path";
|
|
1532
2031
|
var DEFAULT_CONFIG = {};
|
|
1533
2032
|
var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
|
|
1534
2033
|
|
|
@@ -1539,19 +2038,19 @@ var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
|
|
|
1539
2038
|
function registerInit(program2) {
|
|
1540
2039
|
program2.command("init").description("Initialize sisyphus configuration for this project").option("--orchestrator", "Also create a custom orchestrator prompt template").action((opts) => {
|
|
1541
2040
|
const cwd = process.cwd();
|
|
1542
|
-
const sisDir =
|
|
1543
|
-
const configPath =
|
|
1544
|
-
if (
|
|
2041
|
+
const sisDir = join6(cwd, ".sisyphus");
|
|
2042
|
+
const configPath = join6(sisDir, "config.json");
|
|
2043
|
+
if (existsSync6(configPath)) {
|
|
1545
2044
|
console.log(`Already initialized: ${configPath}`);
|
|
1546
2045
|
return;
|
|
1547
2046
|
}
|
|
1548
|
-
|
|
1549
|
-
|
|
2047
|
+
mkdirSync4(sisDir, { recursive: true });
|
|
2048
|
+
writeFileSync4(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n", "utf-8");
|
|
1550
2049
|
console.log(`Created ${configPath}`);
|
|
1551
2050
|
if (opts.orchestrator) {
|
|
1552
|
-
const orchPath =
|
|
1553
|
-
if (!
|
|
1554
|
-
|
|
2051
|
+
const orchPath = join6(sisDir, "orchestrator.md");
|
|
2052
|
+
if (!existsSync6(orchPath)) {
|
|
2053
|
+
writeFileSync4(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
|
|
1555
2054
|
console.log(`Created ${orchPath}`);
|
|
1556
2055
|
}
|
|
1557
2056
|
}
|
|
@@ -1564,6 +2063,94 @@ function registerInit(program2) {
|
|
|
1564
2063
|
});
|
|
1565
2064
|
}
|
|
1566
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
|
+
|
|
1567
2154
|
// src/cli/index.ts
|
|
1568
2155
|
var nodeVersion = parseInt(process.versions.node.split(".")[0], 10);
|
|
1569
2156
|
if (nodeVersion < 22) {
|
|
@@ -1573,7 +2160,7 @@ if (nodeVersion < 22) {
|
|
|
1573
2160
|
var program = new Command();
|
|
1574
2161
|
program.name("sisyphus").description("tmux-integrated orchestration daemon for Claude Code").version(
|
|
1575
2162
|
JSON.parse(
|
|
1576
|
-
|
|
2163
|
+
readFileSync5(join7(dirname3(fileURLToPath3(import.meta.url)), "..", "package.json"), "utf-8")
|
|
1577
2164
|
).version
|
|
1578
2165
|
);
|
|
1579
2166
|
program.configureHelp({
|
|
@@ -1602,6 +2189,7 @@ registerDoctor(program);
|
|
|
1602
2189
|
registerCompanionContext(program);
|
|
1603
2190
|
registerGettingStarted(program);
|
|
1604
2191
|
registerInit(program);
|
|
2192
|
+
registerSetup(program);
|
|
1605
2193
|
program.addHelpText("after", `
|
|
1606
2194
|
Examples:
|
|
1607
2195
|
$ sisyphus start "Implement auth system" Start a new session
|
|
@@ -1614,15 +2202,11 @@ Run 'sisyphus getting-started' for a complete usage guide.
|
|
|
1614
2202
|
`);
|
|
1615
2203
|
var args = process.argv.slice(2);
|
|
1616
2204
|
var firstArg = args[0];
|
|
1617
|
-
var skipWelcome = ["doctor", "getting-started", "help", "--help", "-h", "init", "uninstall", "--version", "-V"];
|
|
1618
|
-
if (!
|
|
1619
|
-
|
|
1620
|
-
console.log("");
|
|
1621
|
-
console.log(" Welcome to Sisyphus \u2014 multi-agent orchestration for Claude Code.");
|
|
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 });
|
|
1622
2208
|
console.log("");
|
|
1623
|
-
console.log("
|
|
1624
|
-
console.log(" sisyphus doctor Check your setup");
|
|
1625
|
-
console.log(" sisyphus getting-started Learn the basics");
|
|
2209
|
+
console.log(" Welcome to Sisyphus. Run 'sisyphus setup' to get started.");
|
|
1626
2210
|
console.log("");
|
|
1627
2211
|
}
|
|
1628
2212
|
program.parseAsync(process.argv).catch((err) => {
|