sisyphi 1.2.18 → 1.2.20
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 +673 -439
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +225 -287
- package/dist/daemon.js.map +1 -1
- package/dist/templates/agent-plugin/agents/review/CLAUDE.md +0 -1
- package/dist/templates/agent-plugin/agents/spec.md +18 -7
- package/dist/templates/agent-suffix.md +9 -1
- package/dist/templates/orchestrator-base.md +13 -0
- package/dist/templates/orchestrator-plugin/hooks/goal-length-guard.sh +60 -0
- package/dist/templates/orchestrator-plugin/hooks/goal-read-advisory.sh +54 -0
- package/dist/templates/orchestrator-plugin/hooks/hooks.json +20 -0
- package/dist/tui.js +98 -79
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/templates/agent-plugin/agents/review/CLAUDE.md +0 -1
- package/templates/agent-plugin/agents/spec.md +18 -7
- package/templates/agent-suffix.md +9 -1
- package/templates/orchestrator-base.md +13 -0
- package/templates/orchestrator-plugin/hooks/goal-length-guard.sh +60 -0
- package/templates/orchestrator-plugin/hooks/goal-read-advisory.sh +54 -0
- package/templates/orchestrator-plugin/hooks/hooks.json +20 -0
package/dist/cli.js
CHANGED
|
@@ -87,8 +87,8 @@ function askReviewSubmitFlagPath(cwd, sessionId, askId) {
|
|
|
87
87
|
function askVisualsDir(cwd, sessionId, askId) {
|
|
88
88
|
return join(askEntryDir(cwd, sessionId, askId), "visuals");
|
|
89
89
|
}
|
|
90
|
-
function tmuxSessionName(cwd,
|
|
91
|
-
return `ssyph_${basename(cwd)}_${
|
|
90
|
+
function tmuxSessionName(cwd, sessionLabel2) {
|
|
91
|
+
return `ssyph_${basename(cwd)}_${sessionLabel2}`;
|
|
92
92
|
}
|
|
93
93
|
function sessionsManifestPath() {
|
|
94
94
|
return join(globalDir(), "sessions-manifest.json");
|
|
@@ -422,7 +422,7 @@ var init_state = __esm({
|
|
|
422
422
|
});
|
|
423
423
|
|
|
424
424
|
// src/shared/platform.ts
|
|
425
|
-
import { execSync as execSync7 } from "child_process";
|
|
425
|
+
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
426
426
|
import { existsSync as existsSync9, readFileSync as readFileSync10 } from "fs";
|
|
427
427
|
function detectPlatform() {
|
|
428
428
|
if (cachedPlatform) return cachedPlatform;
|
|
@@ -467,6 +467,20 @@ function hasCommand(cmd) {
|
|
|
467
467
|
return false;
|
|
468
468
|
}
|
|
469
469
|
}
|
|
470
|
+
function isTerminalFrontmost() {
|
|
471
|
+
if (detectPlatform() !== "darwin") return false;
|
|
472
|
+
try {
|
|
473
|
+
const raw = execFileSync2(
|
|
474
|
+
"osascript",
|
|
475
|
+
["-e", 'tell application "System Events" to get name of first application process whose frontmost is true'],
|
|
476
|
+
{ timeout: 1e3, stdio: ["ignore", "pipe", "ignore"] }
|
|
477
|
+
);
|
|
478
|
+
const name = raw.toString().trim().toLowerCase();
|
|
479
|
+
return TERMINAL_APP_NAMES.has(name);
|
|
480
|
+
} catch {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
470
484
|
function platformLabel() {
|
|
471
485
|
switch (detectPlatform()) {
|
|
472
486
|
case "darwin":
|
|
@@ -483,11 +497,24 @@ function platformLabel() {
|
|
|
483
497
|
return "unknown";
|
|
484
498
|
}
|
|
485
499
|
}
|
|
486
|
-
var cachedPlatform, cmdCache;
|
|
500
|
+
var cachedPlatform, cmdCache, TERMINAL_APP_NAMES;
|
|
487
501
|
var init_platform = __esm({
|
|
488
502
|
"src/shared/platform.ts"() {
|
|
489
503
|
"use strict";
|
|
490
504
|
cmdCache = /* @__PURE__ */ new Map();
|
|
505
|
+
TERMINAL_APP_NAMES = /* @__PURE__ */ new Set([
|
|
506
|
+
"iterm2",
|
|
507
|
+
"iterm",
|
|
508
|
+
"terminal",
|
|
509
|
+
"apple_terminal",
|
|
510
|
+
"alacritty",
|
|
511
|
+
"kitty",
|
|
512
|
+
"wezterm",
|
|
513
|
+
"ghostty",
|
|
514
|
+
"hyper",
|
|
515
|
+
"warp",
|
|
516
|
+
"tmux"
|
|
517
|
+
]);
|
|
491
518
|
}
|
|
492
519
|
});
|
|
493
520
|
|
|
@@ -635,13 +662,14 @@ var init_notify = __esm({
|
|
|
635
662
|
});
|
|
636
663
|
|
|
637
664
|
// src/daemon/ask-store.ts
|
|
638
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync11, readdirSync as readdirSync3 } from "fs";
|
|
665
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
639
666
|
import { basename as basename3 } from "path";
|
|
640
|
-
function maybeNotifyOnAskCreated(cwd, sessionId, meta) {
|
|
667
|
+
function maybeNotifyOnAskCreated(cwd, sessionId, meta, suppress = false) {
|
|
641
668
|
if (process.env.NODE_ENV === "test" || process.env.SISYPHUS_DISABLE_NOTIFY === "1") return;
|
|
642
669
|
const isActionable = meta.kind !== void 0 && ACTIONABLE_KINDS.has(meta.kind);
|
|
643
670
|
const isHeartbeat = meta.askedBy === HEARTBEAT_ASKED_BY;
|
|
644
671
|
if (!isActionable && !isHeartbeat) return;
|
|
672
|
+
if (suppress) return;
|
|
645
673
|
try {
|
|
646
674
|
const config = loadConfig(cwd);
|
|
647
675
|
if (config.notifications?.enabled === false) return;
|
|
@@ -667,8 +695,7 @@ function createAsk(cwd, sessionId, params) {
|
|
|
667
695
|
...params.title !== void 0 ? { title: params.title } : {},
|
|
668
696
|
...params.subtitle !== void 0 ? { subtitle: params.subtitle } : {},
|
|
669
697
|
...params.kind !== void 0 ? { kind: params.kind } : {},
|
|
670
|
-
...params.orphanTarget !== void 0 ? { orphanTarget: params.orphanTarget } : {}
|
|
671
|
-
...params.modeTransition !== void 0 ? { modeTransition: params.modeTransition } : {}
|
|
698
|
+
...params.orphanTarget !== void 0 ? { orphanTarget: params.orphanTarget } : {}
|
|
672
699
|
};
|
|
673
700
|
atomicWrite(askMetaPath(cwd, sessionId, params.askId), JSON.stringify(meta, null, 2));
|
|
674
701
|
emitHistoryEvent(sessionId, "ask-issued", {
|
|
@@ -677,7 +704,7 @@ function createAsk(cwd, sessionId, params) {
|
|
|
677
704
|
blocking: params.blocking,
|
|
678
705
|
askedAt
|
|
679
706
|
});
|
|
680
|
-
maybeNotifyOnAskCreated(cwd, sessionId, meta);
|
|
707
|
+
maybeNotifyOnAskCreated(cwd, sessionId, meta, params.suppressTerminalNotification === true);
|
|
681
708
|
return meta;
|
|
682
709
|
}
|
|
683
710
|
function writeDecisions(cwd, sessionId, askId, deck) {
|
|
@@ -898,9 +925,9 @@ var init_exec = __esm({
|
|
|
898
925
|
});
|
|
899
926
|
|
|
900
927
|
// src/daemon/frontmatter.ts
|
|
901
|
-
import { readFileSync as
|
|
928
|
+
import { readFileSync as readFileSync16, existsSync as existsSync14, readdirSync as readdirSync5 } from "fs";
|
|
902
929
|
import { homedir as homedir8 } from "os";
|
|
903
|
-
import { join as
|
|
930
|
+
import { join as join16, basename as basename5 } from "path";
|
|
904
931
|
function parseAgentFrontmatter(content) {
|
|
905
932
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
906
933
|
if (!match) return {};
|
|
@@ -950,7 +977,7 @@ function discoverAgentTypes(pluginDir, cwd) {
|
|
|
950
977
|
if (seen.has(qualifiedName)) continue;
|
|
951
978
|
seen.add(qualifiedName);
|
|
952
979
|
try {
|
|
953
|
-
const content =
|
|
980
|
+
const content = readFileSync16(join16(dir, file), "utf-8");
|
|
954
981
|
const fm = parseAgentFrontmatter(content);
|
|
955
982
|
results.push({ qualifiedName, source, description: fm.description, model: fm.model });
|
|
956
983
|
} catch {
|
|
@@ -958,14 +985,14 @@ function discoverAgentTypes(pluginDir, cwd) {
|
|
|
958
985
|
}
|
|
959
986
|
}
|
|
960
987
|
}
|
|
961
|
-
scanDir(
|
|
962
|
-
scanDir(
|
|
963
|
-
scanDir(
|
|
964
|
-
scanDir(
|
|
965
|
-
scanDir(
|
|
988
|
+
scanDir(join16(projectAgentPluginDir(cwd), "agents"), null, "project-sis");
|
|
989
|
+
scanDir(join16(userAgentPluginDir(), "agents"), null, "user-sis");
|
|
990
|
+
scanDir(join16(cwd, ".claude", "agents"), null, "project");
|
|
991
|
+
scanDir(join16(homedir8(), ".claude", "agents"), null, "user");
|
|
992
|
+
scanDir(join16(pluginDir, "agents"), "sisyphus", "bundled");
|
|
966
993
|
try {
|
|
967
|
-
const registryPath2 =
|
|
968
|
-
const registry = JSON.parse(
|
|
994
|
+
const registryPath2 = join16(homedir8(), ".claude", "plugins", "installed_plugins.json");
|
|
995
|
+
const registry = JSON.parse(readFileSync16(registryPath2, "utf-8"));
|
|
969
996
|
const pluginEntries = registry.plugins ?? registry;
|
|
970
997
|
for (const key of Object.keys(pluginEntries)) {
|
|
971
998
|
const atIdx = key.indexOf("@");
|
|
@@ -974,7 +1001,7 @@ function discoverAgentTypes(pluginDir, cwd) {
|
|
|
974
1001
|
const entry = pluginEntries[key];
|
|
975
1002
|
const installPath = Array.isArray(entry) ? entry[0]?.installPath : entry?.installPath;
|
|
976
1003
|
if (installPath) {
|
|
977
|
-
scanDir(
|
|
1004
|
+
scanDir(join16(installPath, "agents"), namespace, "plugin");
|
|
978
1005
|
}
|
|
979
1006
|
}
|
|
980
1007
|
} catch {
|
|
@@ -996,8 +1023,8 @@ var init_effort_render = __esm({
|
|
|
996
1023
|
});
|
|
997
1024
|
|
|
998
1025
|
// src/daemon/extensions.ts
|
|
999
|
-
import { existsSync as existsSync15, readFileSync as
|
|
1000
|
-
import { resolve as resolve6, join as
|
|
1026
|
+
import { existsSync as existsSync15, readFileSync as readFileSync17, readdirSync as readdirSync6, copyFileSync as copyFileSync2, mkdirSync as mkdirSync8, statSync as statSync3, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
1027
|
+
import { resolve as resolve6, join as join17, basename as basename6, relative } from "path";
|
|
1001
1028
|
function resolveBundledTemplateDir(kind) {
|
|
1002
1029
|
const built = resolve6(import.meta.dirname, "../templates", kind);
|
|
1003
1030
|
if (existsSync15(built)) return built;
|
|
@@ -1014,12 +1041,12 @@ var init_extensions = __esm({
|
|
|
1014
1041
|
});
|
|
1015
1042
|
|
|
1016
1043
|
// src/shared/version.ts
|
|
1017
|
-
import { readFileSync as
|
|
1044
|
+
import { readFileSync as readFileSync22 } from "fs";
|
|
1018
1045
|
import { resolve as resolve7 } from "path";
|
|
1019
1046
|
function readSisyphusVersion() {
|
|
1020
1047
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
1021
1048
|
try {
|
|
1022
|
-
const raw =
|
|
1049
|
+
const raw = readFileSync22(resolve7(import.meta.dirname, rel), "utf-8");
|
|
1023
1050
|
const pkg = JSON.parse(raw);
|
|
1024
1051
|
if (pkg.name === "sisyphi" && pkg.version) return pkg.version;
|
|
1025
1052
|
} catch {
|
|
@@ -1086,9 +1113,9 @@ var init_upload = __esm({
|
|
|
1086
1113
|
// src/shared/session-export.ts
|
|
1087
1114
|
import { execFile as execFile2 } from "child_process";
|
|
1088
1115
|
import { promisify } from "util";
|
|
1089
|
-
import { existsSync as existsSync25, readFileSync as
|
|
1116
|
+
import { existsSync as existsSync25, readFileSync as readFileSync26, mkdirSync as mkdirSync11, symlinkSync, rmSync as rmSync5, writeFileSync as writeFileSync13 } from "fs";
|
|
1090
1117
|
import { homedir as homedir12 } from "os";
|
|
1091
|
-
import { join as
|
|
1118
|
+
import { join as join23 } from "path";
|
|
1092
1119
|
function sanitizeName(name) {
|
|
1093
1120
|
return name.replace(/[^a-zA-Z0-9-_]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
1094
1121
|
}
|
|
@@ -1096,11 +1123,11 @@ function buildOutputPath(label, dir) {
|
|
|
1096
1123
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1097
1124
|
mkdirSync11(dir, { recursive: true });
|
|
1098
1125
|
const base = `sisyphus-${label}-${date}`;
|
|
1099
|
-
let candidate =
|
|
1126
|
+
let candidate = join23(dir, `${base}.zip`);
|
|
1100
1127
|
let counter = 1;
|
|
1101
1128
|
while (existsSync25(candidate)) {
|
|
1102
1129
|
counter++;
|
|
1103
|
-
candidate =
|
|
1130
|
+
candidate = join23(dir, `${base}-${counter}.zip`);
|
|
1104
1131
|
}
|
|
1105
1132
|
return candidate;
|
|
1106
1133
|
}
|
|
@@ -1170,24 +1197,24 @@ async function exportSessionToZip(sessionId, cwd, options) {
|
|
|
1170
1197
|
const stPath = statePath(cwd, sessionId);
|
|
1171
1198
|
if (existsSync25(stPath)) {
|
|
1172
1199
|
try {
|
|
1173
|
-
const state = JSON.parse(
|
|
1200
|
+
const state = JSON.parse(readFileSync26(stPath, "utf-8"));
|
|
1174
1201
|
if (state.name) {
|
|
1175
1202
|
label = sanitizeName(state.name);
|
|
1176
1203
|
}
|
|
1177
1204
|
} catch {
|
|
1178
1205
|
}
|
|
1179
1206
|
}
|
|
1180
|
-
const dir = options?.outputDir ??
|
|
1207
|
+
const dir = options?.outputDir ?? join23(homedir12(), "Downloads");
|
|
1181
1208
|
const outputPath = buildOutputPath(label, dir);
|
|
1182
1209
|
const tmpDir = `/tmp/sisyphus-export-${sessionId.slice(0, 8)}-${Date.now()}`;
|
|
1183
1210
|
try {
|
|
1184
1211
|
mkdirSync11(tmpDir, { recursive: true });
|
|
1185
|
-
|
|
1212
|
+
writeFileSync13(join23(tmpDir, "CLAUDE.md"), generateGuide(), "utf-8");
|
|
1186
1213
|
if (sessExists) {
|
|
1187
|
-
symlinkSync(sessDir,
|
|
1214
|
+
symlinkSync(sessDir, join23(tmpDir, "session"));
|
|
1188
1215
|
}
|
|
1189
1216
|
if (histExists) {
|
|
1190
|
-
symlinkSync(histDir,
|
|
1217
|
+
symlinkSync(histDir, join23(tmpDir, "history"));
|
|
1191
1218
|
}
|
|
1192
1219
|
const parts = ["CLAUDE.md", sessExists ? "session/" : "", histExists ? "history/" : ""].filter(Boolean);
|
|
1193
1220
|
await execFileAsync("zip", ["-rq", outputPath, ...parts], { cwd: tmpDir });
|
|
@@ -1398,9 +1425,9 @@ var init_companion_normalize = __esm({
|
|
|
1398
1425
|
});
|
|
1399
1426
|
|
|
1400
1427
|
// src/daemon/companion.ts
|
|
1401
|
-
import { existsSync as existsSync27, mkdirSync as mkdirSync12, readFileSync as
|
|
1428
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync12, readFileSync as readFileSync28, renameSync as renameSync4, writeFileSync as writeFileSync15 } from "fs";
|
|
1402
1429
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
1403
|
-
import { dirname as dirname9, join as
|
|
1430
|
+
import { dirname as dirname9, join as join25 } from "path";
|
|
1404
1431
|
function loadCompanion() {
|
|
1405
1432
|
const path = companionPath();
|
|
1406
1433
|
if (!existsSync27(path)) {
|
|
@@ -1408,7 +1435,7 @@ function loadCompanion() {
|
|
|
1408
1435
|
saveCompanion(state2);
|
|
1409
1436
|
return state2;
|
|
1410
1437
|
}
|
|
1411
|
-
const raw =
|
|
1438
|
+
const raw = readFileSync28(path, "utf-8");
|
|
1412
1439
|
const state = JSON.parse(raw);
|
|
1413
1440
|
return normalizeCompanion(state);
|
|
1414
1441
|
}
|
|
@@ -1416,8 +1443,8 @@ function saveCompanion(state) {
|
|
|
1416
1443
|
const path = companionPath();
|
|
1417
1444
|
const dir = dirname9(path);
|
|
1418
1445
|
mkdirSync12(dir, { recursive: true });
|
|
1419
|
-
const tmp =
|
|
1420
|
-
|
|
1446
|
+
const tmp = join25(dir, `.companion.${randomUUID3()}.tmp`);
|
|
1447
|
+
writeFileSync15(tmp, JSON.stringify(state, null, 2), "utf-8");
|
|
1421
1448
|
renameSync4(tmp, path);
|
|
1422
1449
|
}
|
|
1423
1450
|
function createDefaultCompanion() {
|
|
@@ -1470,8 +1497,8 @@ var init_companion = __esm({
|
|
|
1470
1497
|
});
|
|
1471
1498
|
|
|
1472
1499
|
// src/daemon/companion-memory.ts
|
|
1473
|
-
import { existsSync as existsSync28, mkdirSync as mkdirSync13, readFileSync as
|
|
1474
|
-
import { dirname as dirname10, join as
|
|
1500
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync13, readFileSync as readFileSync29, renameSync as renameSync5, writeFileSync as writeFileSync16 } from "fs";
|
|
1501
|
+
import { dirname as dirname10, join as join26 } from "path";
|
|
1475
1502
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
1476
1503
|
import { z } from "zod";
|
|
1477
1504
|
function isSafeObservationText(text) {
|
|
@@ -1501,7 +1528,7 @@ function loadMemoryStrict() {
|
|
|
1501
1528
|
if (!existsSync28(path)) return defaultMemoryState();
|
|
1502
1529
|
let raw;
|
|
1503
1530
|
try {
|
|
1504
|
-
raw =
|
|
1531
|
+
raw = readFileSync29(path, "utf-8");
|
|
1505
1532
|
} catch (err) {
|
|
1506
1533
|
throw new MemoryStoreParseError(err);
|
|
1507
1534
|
}
|
|
@@ -1597,10 +1624,10 @@ var init_companion_render = __esm({
|
|
|
1597
1624
|
});
|
|
1598
1625
|
|
|
1599
1626
|
// src/daemon/companion-popup.ts
|
|
1600
|
-
import { writeFileSync as
|
|
1601
|
-
import { tmpdir as
|
|
1602
|
-
import { join as
|
|
1603
|
-
function
|
|
1627
|
+
import { writeFileSync as writeFileSync17, readFileSync as readFileSync30, unlinkSync as unlinkSync6, existsSync as existsSync29 } from "fs";
|
|
1628
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
1629
|
+
import { join as join27, resolve as resolve10 } from "path";
|
|
1630
|
+
function wrapText2(text, width) {
|
|
1604
1631
|
const words = text.split(" ");
|
|
1605
1632
|
const lines = [];
|
|
1606
1633
|
let current = "";
|
|
@@ -1630,16 +1657,16 @@ function showCommentaryPopupQueue(pages) {
|
|
|
1630
1657
|
const defaultTitle = ` (${face}) `;
|
|
1631
1658
|
let maxContentHeight = 0;
|
|
1632
1659
|
for (let i = 0; i < pages.length; i++) {
|
|
1633
|
-
const lines =
|
|
1660
|
+
const lines = wrapText2(pages[i].text, INNER_WIDTH2);
|
|
1634
1661
|
const isLast = i === pages.length - 1;
|
|
1635
1662
|
const hint = isLast ? "[0:ok 1:good 2:bad 3:whip]" : "[enter:next 0-3:rate]";
|
|
1636
|
-
const hintPad = Math.max(0, Math.floor((
|
|
1663
|
+
const hintPad = Math.max(0, Math.floor((INNER_WIDTH2 - hint.length) / 2));
|
|
1637
1664
|
const hintLine = " ".repeat(hintPad + 2) + hint;
|
|
1638
1665
|
const content = "\n\n" + lines.map((l) => ` ${l}`).join("\n") + "\n\n" + hintLine + "\n";
|
|
1639
1666
|
const contentLineCount = content.split("\n").length - 1;
|
|
1640
1667
|
const contentHeight = Math.max(contentLineCount + 2, 5);
|
|
1641
1668
|
if (contentHeight > maxContentHeight) maxContentHeight = contentHeight;
|
|
1642
|
-
|
|
1669
|
+
writeFileSync17(`${POPUP_TMP_PREFIX}-${i}.txt`, content);
|
|
1643
1670
|
}
|
|
1644
1671
|
const whipAvailable = existsSync29(WHIP_ANIMATION_PATH);
|
|
1645
1672
|
if (whipAvailable && maxContentHeight < WHIP_ANIMATION_ROWS + 2) {
|
|
@@ -1688,9 +1715,9 @@ if [ ! -f "$RESULT_FILE" ]; then
|
|
|
1688
1715
|
printf 'neutral' > "$RESULT_FILE"
|
|
1689
1716
|
fi
|
|
1690
1717
|
`;
|
|
1691
|
-
|
|
1718
|
+
writeFileSync17(POPUP_SCRIPT, script, { mode: 493 });
|
|
1692
1719
|
try {
|
|
1693
|
-
|
|
1720
|
+
unlinkSync6(POPUP_RESULT_PREFIX);
|
|
1694
1721
|
} catch {
|
|
1695
1722
|
}
|
|
1696
1723
|
const clientsRaw = execSafe('tmux list-clients -F "#{client_name} #{client_width}"');
|
|
@@ -1700,27 +1727,27 @@ fi
|
|
|
1700
1727
|
const client = line.slice(0, lastSpace);
|
|
1701
1728
|
const clientWidth = parseInt(line.slice(lastSpace + 1), 10);
|
|
1702
1729
|
if (!clientWidth) continue;
|
|
1703
|
-
const x = Math.max(0, clientWidth -
|
|
1730
|
+
const x = Math.max(0, clientWidth - POPUP_WIDTH2 - 1);
|
|
1704
1731
|
const args2 = [
|
|
1705
1732
|
`-c ${shellQuote(client)}`,
|
|
1706
1733
|
"-E -b rounded",
|
|
1707
1734
|
`-T ${shellQuote(initialTitle)}`,
|
|
1708
1735
|
`-S "fg=${moodColor}"`,
|
|
1709
1736
|
`-s "fg=${moodColor}"`,
|
|
1710
|
-
`-x ${x} -y
|
|
1711
|
-
`-w ${
|
|
1737
|
+
`-x ${x} -y 2`,
|
|
1738
|
+
`-w ${POPUP_WIDTH2} -h ${maxContentHeight}`,
|
|
1712
1739
|
shellQuote(POPUP_SCRIPT)
|
|
1713
1740
|
].join(" ");
|
|
1714
1741
|
execSafe(`tmux display-popup ${args2}`);
|
|
1715
1742
|
}
|
|
1716
1743
|
let raw;
|
|
1717
1744
|
try {
|
|
1718
|
-
raw =
|
|
1745
|
+
raw = readFileSync30(POPUP_RESULT_PREFIX, "utf8").trim();
|
|
1719
1746
|
} catch {
|
|
1720
1747
|
return null;
|
|
1721
1748
|
} finally {
|
|
1722
1749
|
try {
|
|
1723
|
-
|
|
1750
|
+
unlinkSync6(POPUP_RESULT_PREFIX);
|
|
1724
1751
|
} catch {
|
|
1725
1752
|
}
|
|
1726
1753
|
}
|
|
@@ -1734,7 +1761,7 @@ fi
|
|
|
1734
1761
|
}
|
|
1735
1762
|
return null;
|
|
1736
1763
|
}
|
|
1737
|
-
var
|
|
1764
|
+
var POPUP_WIDTH2, INNER_WIDTH2, POPUP_DURATION, POPUP_TMP_PREFIX, POPUP_SCRIPT, POPUP_RESULT_PREFIX, WHIP_ANIMATION_PATH, WHIP_ANIMATION_ROWS;
|
|
1738
1765
|
var init_companion_popup = __esm({
|
|
1739
1766
|
"src/daemon/companion-popup.ts"() {
|
|
1740
1767
|
"use strict";
|
|
@@ -1743,12 +1770,12 @@ var init_companion_popup = __esm({
|
|
|
1743
1770
|
init_config();
|
|
1744
1771
|
init_exec();
|
|
1745
1772
|
init_shell();
|
|
1746
|
-
|
|
1747
|
-
|
|
1773
|
+
POPUP_WIDTH2 = 38;
|
|
1774
|
+
INNER_WIDTH2 = POPUP_WIDTH2 - 6;
|
|
1748
1775
|
POPUP_DURATION = 15;
|
|
1749
|
-
POPUP_TMP_PREFIX =
|
|
1750
|
-
POPUP_SCRIPT =
|
|
1751
|
-
POPUP_RESULT_PREFIX =
|
|
1776
|
+
POPUP_TMP_PREFIX = join27(tmpdir3(), "sisyphus-popup");
|
|
1777
|
+
POPUP_SCRIPT = join27(tmpdir3(), "sisyphus-popup.sh");
|
|
1778
|
+
POPUP_RESULT_PREFIX = join27(tmpdir3(), "sisyphus-popup-result");
|
|
1752
1779
|
WHIP_ANIMATION_PATH = resolve10(import.meta.dirname, "../templates/whip-animation.sh");
|
|
1753
1780
|
WHIP_ANIMATION_ROWS = 12;
|
|
1754
1781
|
}
|
|
@@ -1776,9 +1803,9 @@ __export(tmux_exports, {
|
|
|
1776
1803
|
windowExists: () => windowExists
|
|
1777
1804
|
});
|
|
1778
1805
|
import { execSync as execSync17 } from "child_process";
|
|
1779
|
-
import { join as
|
|
1780
|
-
import { readFileSync as
|
|
1781
|
-
import { tmpdir as
|
|
1806
|
+
import { join as join28 } from "path";
|
|
1807
|
+
import { readFileSync as readFileSync31, writeFileSync as writeFileSync18, mkdtempSync, rmSync as rmSync7, cpSync as cpSync3, existsSync as existsSync30, mkdirSync as mkdirSync14 } from "fs";
|
|
1808
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
1782
1809
|
function getWindowId() {
|
|
1783
1810
|
const pane = process.env["TMUX_PANE"];
|
|
1784
1811
|
if (pane) {
|
|
@@ -1829,8 +1856,8 @@ function registerDashboardWindow(cwd) {
|
|
|
1829
1856
|
}
|
|
1830
1857
|
}
|
|
1831
1858
|
function setupCompanionPlugin() {
|
|
1832
|
-
const srcDir =
|
|
1833
|
-
const destDir =
|
|
1859
|
+
const srcDir = join28(import.meta.dirname, "templates", "companion-plugin");
|
|
1860
|
+
const destDir = join28(globalDir(), "companion-plugin");
|
|
1834
1861
|
if (!existsSync30(destDir)) mkdirSync14(destDir, { recursive: true });
|
|
1835
1862
|
cpSync3(srcDir, destDir, { recursive: true });
|
|
1836
1863
|
return destDir;
|
|
@@ -1858,18 +1885,18 @@ function openCompanionPane(cwd) {
|
|
|
1858
1885
|
return;
|
|
1859
1886
|
}
|
|
1860
1887
|
const pluginDir = setupCompanionPlugin();
|
|
1861
|
-
const templatePath2 =
|
|
1888
|
+
const templatePath2 = join28(import.meta.dirname, "templates", "dashboard-claude.md");
|
|
1862
1889
|
let template;
|
|
1863
1890
|
try {
|
|
1864
|
-
template =
|
|
1891
|
+
template = readFileSync31(templatePath2, "utf-8");
|
|
1865
1892
|
} catch {
|
|
1866
1893
|
template = `You are a Sisyphus dashboard companion. Help the user manage multi-agent sessions.
|
|
1867
1894
|
Project: ${cwd}
|
|
1868
1895
|
Run \`sis session inspect list\` and \`sis session inspect status\` to see current state.`;
|
|
1869
1896
|
}
|
|
1870
1897
|
const rendered = template.replace(/\{\{CWD\}\}/g, cwd);
|
|
1871
|
-
const promptPath =
|
|
1872
|
-
|
|
1898
|
+
const promptPath = join28(globalDir(), "dashboard-companion-prompt.md");
|
|
1899
|
+
writeFileSync18(promptPath, rendered, "utf-8");
|
|
1873
1900
|
const pathEnv = augmentedPath();
|
|
1874
1901
|
const claudeCmd = `SISYPHUS_COMPANION_CWD=${shellQuote(cwd)} PATH=${shellQuote(pathEnv)} claude --dangerously-skip-permissions --plugin-dir ${shellQuote(pluginDir)} --append-system-prompt "$(cat ${shellQuote(promptPath)})"`;
|
|
1875
1902
|
const result = exec2(
|
|
@@ -1884,12 +1911,12 @@ function switchToSession(sessionName) {
|
|
|
1884
1911
|
execSafe(`tmux switch-client -t ${shellQuote(sessionName)}`);
|
|
1885
1912
|
}
|
|
1886
1913
|
function editInPopup(cwd, editor, opts) {
|
|
1887
|
-
const tmpDir = mkdtempSync(
|
|
1888
|
-
const filePath =
|
|
1914
|
+
const tmpDir = mkdtempSync(join28(tmpdir4(), "sisyphus-"));
|
|
1915
|
+
const filePath = join28(tmpDir, "input.md");
|
|
1889
1916
|
try {
|
|
1890
|
-
|
|
1917
|
+
writeFileSync18(filePath, opts?.content ? opts.content : "", "utf-8");
|
|
1891
1918
|
openEditorPopup(cwd, editor, filePath, opts?.size);
|
|
1892
|
-
const result =
|
|
1919
|
+
const result = readFileSync31(filePath, "utf-8").trim();
|
|
1893
1920
|
return result || null;
|
|
1894
1921
|
} finally {
|
|
1895
1922
|
rmSync7(tmpDir, { recursive: true, force: true });
|
|
@@ -1897,8 +1924,8 @@ function editInPopup(cwd, editor, opts) {
|
|
|
1897
1924
|
}
|
|
1898
1925
|
function promptInPopup(prompt, opts) {
|
|
1899
1926
|
const { w = "50%", h = "3" } = opts ?? {};
|
|
1900
|
-
const tmpDir = mkdtempSync(
|
|
1901
|
-
const outFile =
|
|
1927
|
+
const tmpDir = mkdtempSync(join28(tmpdir4(), "sisyphus-"));
|
|
1928
|
+
const outFile = join28(tmpDir, "result");
|
|
1902
1929
|
try {
|
|
1903
1930
|
const script = `printf ${shellQuote(prompt + " ")} && read -r line && printf '%s' "$line" > ${shellQuote(outFile)}`;
|
|
1904
1931
|
execSync17(
|
|
@@ -1906,7 +1933,7 @@ function promptInPopup(prompt, opts) {
|
|
|
1906
1933
|
{ stdio: "inherit", env: EXEC_ENV }
|
|
1907
1934
|
);
|
|
1908
1935
|
if (!existsSync30(outFile)) return null;
|
|
1909
|
-
const result =
|
|
1936
|
+
const result = readFileSync31(outFile, "utf-8").trim();
|
|
1910
1937
|
return result || null;
|
|
1911
1938
|
} finally {
|
|
1912
1939
|
rmSync7(tmpDir, { recursive: true, force: true });
|
|
@@ -1937,10 +1964,10 @@ function openClaudeResumePopup(cwd, claudeSessionId, resumeEnv, resumeArgs) {
|
|
|
1937
1964
|
{ stdio: "inherit", env: EXEC_ENV }
|
|
1938
1965
|
);
|
|
1939
1966
|
}
|
|
1940
|
-
function openClaudeResumeSession(cwd, sessionId, claudeSessionId,
|
|
1941
|
-
const sessionName = tmuxSessionName(cwd,
|
|
1967
|
+
function openClaudeResumeSession(cwd, sessionId, claudeSessionId, sessionLabel2, resumeEnv, resumeArgs, cycleNum, mode) {
|
|
1968
|
+
const sessionName = tmuxSessionName(cwd, sessionLabel2);
|
|
1942
1969
|
const cycleLabel = cycleNum != null ? `c${cycleNum}` : "";
|
|
1943
|
-
const paneTitle = cycleLabel ? `ssph:orch ${
|
|
1970
|
+
const paneTitle = cycleLabel ? `ssph:orch ${sessionLabel2} ${cycleLabel}` : `ssph:orch ${sessionLabel2}`;
|
|
1944
1971
|
const existing = execSafe('tmux list-sessions -F "#{session_id}|#{session_name}"');
|
|
1945
1972
|
const existingLine = existing?.split("\n").find((line) => line.slice(line.indexOf("|") + 1) === sessionName);
|
|
1946
1973
|
if (existingLine) {
|
|
@@ -1948,7 +1975,7 @@ function openClaudeResumeSession(cwd, sessionId, claudeSessionId, sessionLabel,
|
|
|
1948
1975
|
execSafe(`tmux set-option -t ${shellQuote(existingSessId)} @sisyphus_cwd ${shellQuote(cwd.replace(/\/+$/, ""))}`);
|
|
1949
1976
|
execSafe(`tmux set-option -t ${shellQuote(existingSessId)} @sisyphus_session_id ${shellQuote(sessionId)}`);
|
|
1950
1977
|
const firstPaneId2 = execSafe(`tmux list-panes -t ${shellQuote(existingSessId)} -F '#{pane_id}'`)?.split("\n")[0];
|
|
1951
|
-
if (firstPaneId2) applyOrchestratorPaneStyle(firstPaneId2, paneTitle,
|
|
1978
|
+
if (firstPaneId2) applyOrchestratorPaneStyle(firstPaneId2, paneTitle, sessionLabel2, cycleLabel, mode);
|
|
1952
1979
|
return sessionName;
|
|
1953
1980
|
}
|
|
1954
1981
|
const pathEnv = augmentedPath();
|
|
@@ -1964,14 +1991,14 @@ function openClaudeResumeSession(cwd, sessionId, claudeSessionId, sessionLabel,
|
|
|
1964
1991
|
execSafe(`tmux set -w -t ${shellQuote(newSessId + ":")} pane-border-status top`);
|
|
1965
1992
|
execSafe(`tmux set -w -t ${shellQuote(newSessId + ":")} allow-rename off`);
|
|
1966
1993
|
execSafe(`tmux set -w -t ${shellQuote(newSessId + ":")} automatic-rename off`);
|
|
1967
|
-
if (firstPaneId) applyOrchestratorPaneStyle(firstPaneId, paneTitle,
|
|
1994
|
+
if (firstPaneId) applyOrchestratorPaneStyle(firstPaneId, paneTitle, sessionLabel2, cycleLabel, mode);
|
|
1968
1995
|
return sessionName;
|
|
1969
1996
|
}
|
|
1970
|
-
function applyOrchestratorPaneStyle(paneId, title,
|
|
1997
|
+
function applyOrchestratorPaneStyle(paneId, title, sessionLabel2, cycleLabel, mode) {
|
|
1971
1998
|
const color = "yellow";
|
|
1972
1999
|
execSafe(`tmux select-pane -t ${shellQuote(paneId)} -T ${shellQuote(title)}`);
|
|
1973
2000
|
execSafe(`tmux set -p -t ${shellQuote(paneId)} @pane_role ${shellQuote("orch")}`);
|
|
1974
|
-
execSafe(`tmux set -p -t ${shellQuote(paneId)} @pane_session ${shellQuote(
|
|
2001
|
+
execSafe(`tmux set -p -t ${shellQuote(paneId)} @pane_session ${shellQuote(sessionLabel2)}`);
|
|
1975
2002
|
if (cycleLabel) execSafe(`tmux set -p -t ${shellQuote(paneId)} @pane_cycle ${shellQuote(cycleLabel)}`);
|
|
1976
2003
|
if (mode) execSafe(`tmux set -p -t ${shellQuote(paneId)} @pane_mode ${shellQuote(mode)}`);
|
|
1977
2004
|
const gitBranch = `#(cd #{pane_current_path} && git branch --show-current 2>/dev/null)`;
|
|
@@ -2024,7 +2051,7 @@ __export(creds_exports, {
|
|
|
2024
2051
|
readTailscaleEnv: () => readTailscaleEnv,
|
|
2025
2052
|
writeTailscaleEnv: () => writeTailscaleEnv
|
|
2026
2053
|
});
|
|
2027
|
-
import { chmodSync as chmodSync3, existsSync as existsSync31, mkdirSync as mkdirSync16, readFileSync as
|
|
2054
|
+
import { chmodSync as chmodSync3, existsSync as existsSync31, mkdirSync as mkdirSync16, readFileSync as readFileSync33 } from "fs";
|
|
2028
2055
|
import { createInterface as createInterface4 } from "readline";
|
|
2029
2056
|
function isValidProvider(value) {
|
|
2030
2057
|
return PROVIDERS.includes(value);
|
|
@@ -2058,7 +2085,7 @@ function serializeEnvFile(values) {
|
|
|
2058
2085
|
}
|
|
2059
2086
|
function readEnvFile(path) {
|
|
2060
2087
|
if (!existsSync31(path)) return null;
|
|
2061
|
-
return parseEnvFile(
|
|
2088
|
+
return parseEnvFile(readFileSync33(path, "utf-8"));
|
|
2062
2089
|
}
|
|
2063
2090
|
function writeEnvFile(path, values) {
|
|
2064
2091
|
ensureDeployDir();
|
|
@@ -2188,12 +2215,12 @@ var init_pricing = __esm({
|
|
|
2188
2215
|
});
|
|
2189
2216
|
|
|
2190
2217
|
// src/cli/deploy/runtime.ts
|
|
2191
|
-
import { existsSync as existsSync32, readFileSync as
|
|
2218
|
+
import { existsSync as existsSync32, readFileSync as readFileSync34, unlinkSync as unlinkSync7 } from "fs";
|
|
2192
2219
|
function readRuntimeState(provider) {
|
|
2193
2220
|
const path = deployRuntimePath(provider);
|
|
2194
2221
|
if (!existsSync32(path)) return null;
|
|
2195
2222
|
try {
|
|
2196
|
-
return JSON.parse(
|
|
2223
|
+
return JSON.parse(readFileSync34(path, "utf-8"));
|
|
2197
2224
|
} catch {
|
|
2198
2225
|
return null;
|
|
2199
2226
|
}
|
|
@@ -2203,7 +2230,7 @@ function writeRuntimeState(provider, state) {
|
|
|
2203
2230
|
}
|
|
2204
2231
|
function clearRuntimeState(provider) {
|
|
2205
2232
|
const path = deployRuntimePath(provider);
|
|
2206
|
-
if (existsSync32(path))
|
|
2233
|
+
if (existsSync32(path)) unlinkSync7(path);
|
|
2207
2234
|
}
|
|
2208
2235
|
var init_runtime = __esm({
|
|
2209
2236
|
"src/cli/deploy/runtime.ts"() {
|
|
@@ -2447,7 +2474,7 @@ var init_tailscale = __esm({
|
|
|
2447
2474
|
|
|
2448
2475
|
// src/cli/deploy/runner.ts
|
|
2449
2476
|
import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
|
|
2450
|
-
import { copyFileSync as copyFileSync3, existsSync as existsSync34, mkdirSync as mkdirSync17, readFileSync as
|
|
2477
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync34, mkdirSync as mkdirSync17, readFileSync as readFileSync35 } from "fs";
|
|
2451
2478
|
function runTerraform(provider, args2, extraEnv) {
|
|
2452
2479
|
ensureProviderStateDir(provider);
|
|
2453
2480
|
ensureTerraformInstalled();
|
|
@@ -2485,7 +2512,7 @@ function readSshPubkey(path) {
|
|
|
2485
2512
|
or pass --ssh-key <path>.`
|
|
2486
2513
|
);
|
|
2487
2514
|
}
|
|
2488
|
-
return
|
|
2515
|
+
return readFileSync35(path, "utf-8").trim();
|
|
2489
2516
|
}
|
|
2490
2517
|
function readOutputs(provider) {
|
|
2491
2518
|
const result = spawnSync5("terraform", ["output", "-json", `-state=${deployStatePath(provider)}`], {
|
|
@@ -2800,7 +2827,7 @@ var init_grove = __esm({
|
|
|
2800
2827
|
// src/cli/cloud/repo.ts
|
|
2801
2828
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
2802
2829
|
import { existsSync as existsSync35 } from "fs";
|
|
2803
|
-
import { basename as basename8, join as
|
|
2830
|
+
import { basename as basename8, join as join31 } from "path";
|
|
2804
2831
|
function captureGit(args2, cwd) {
|
|
2805
2832
|
const result = spawnSync7("git", args2, {
|
|
2806
2833
|
encoding: "utf-8",
|
|
@@ -2840,10 +2867,10 @@ function buildRsyncArgs(localDir, remoteTarget) {
|
|
|
2840
2867
|
];
|
|
2841
2868
|
}
|
|
2842
2869
|
function detectPackageManager(toplevel) {
|
|
2843
|
-
if (existsSync35(
|
|
2844
|
-
if (existsSync35(
|
|
2845
|
-
if (existsSync35(
|
|
2846
|
-
if (existsSync35(
|
|
2870
|
+
if (existsSync35(join31(toplevel, "pnpm-lock.yaml"))) return "pnpm";
|
|
2871
|
+
if (existsSync35(join31(toplevel, "bun.lockb"))) return "bun";
|
|
2872
|
+
if (existsSync35(join31(toplevel, "yarn.lock"))) return "yarn";
|
|
2873
|
+
if (existsSync35(join31(toplevel, "package-lock.json"))) return "npm";
|
|
2847
2874
|
return null;
|
|
2848
2875
|
}
|
|
2849
2876
|
function packageManagerInstallCmd(pm) {
|
|
@@ -3126,7 +3153,7 @@ var init_protocol = __esm({
|
|
|
3126
3153
|
});
|
|
3127
3154
|
|
|
3128
3155
|
// src/daemon/spawn-helpers.ts
|
|
3129
|
-
import { writeFileSync as
|
|
3156
|
+
import { writeFileSync as writeFileSync21, existsSync as existsSync38 } from "fs";
|
|
3130
3157
|
import { resolve as resolve13 } from "path";
|
|
3131
3158
|
var init_spawn_helpers = __esm({
|
|
3132
3159
|
"src/daemon/spawn-helpers.ts"() {
|
|
@@ -3135,17 +3162,180 @@ var init_spawn_helpers = __esm({
|
|
|
3135
3162
|
}
|
|
3136
3163
|
});
|
|
3137
3164
|
|
|
3165
|
+
// src/cli/help-rubric.ts
|
|
3166
|
+
function commandPath(cmd) {
|
|
3167
|
+
const parts = [];
|
|
3168
|
+
let cur = cmd;
|
|
3169
|
+
while (cur !== null) {
|
|
3170
|
+
parts.push(cur.name());
|
|
3171
|
+
cur = cur.parent;
|
|
3172
|
+
}
|
|
3173
|
+
parts.reverse();
|
|
3174
|
+
parts.shift();
|
|
3175
|
+
return parts.join(" ");
|
|
3176
|
+
}
|
|
3177
|
+
function subcommandRubric(cmd) {
|
|
3178
|
+
if (cmd.name() === "help") {
|
|
3179
|
+
return cmd.summary() || cmd.description();
|
|
3180
|
+
}
|
|
3181
|
+
const p = commandPath(cmd);
|
|
3182
|
+
let key = p;
|
|
3183
|
+
const deployProviderMatch = p.match(/^deploy (hetzner|aws) (.+)$/);
|
|
3184
|
+
if (deployProviderMatch) {
|
|
3185
|
+
key = `deploy * ${deployProviderMatch[2]}`;
|
|
3186
|
+
}
|
|
3187
|
+
const r = RUBRICS[key];
|
|
3188
|
+
if (r) {
|
|
3189
|
+
return `${r.short} | use when ${r.useWhen}`;
|
|
3190
|
+
}
|
|
3191
|
+
return cmd.summary() || cmd.description();
|
|
3192
|
+
}
|
|
3193
|
+
var RUBRICS, CONCEPTS_BLOCK, ROOT_AFTER_HELP;
|
|
3194
|
+
var init_help_rubric = __esm({
|
|
3195
|
+
"src/cli/help-rubric.ts"() {
|
|
3196
|
+
"use strict";
|
|
3197
|
+
RUBRICS = {
|
|
3198
|
+
"session": { short: "Manage tracked orchestration sessions", useWhen: "acting on a unit of orchestrated work" },
|
|
3199
|
+
"agent": { short: "Operate on worker agents", useWhen: "directing or reading a spawned agent" },
|
|
3200
|
+
"orch": { short: "Talk to / steer the orchestrator", useWhen: "guiding the session's orchestrator" },
|
|
3201
|
+
"ask": { short: "Human-in-the-loop question I/O", useWhen: "answering or polling a blocking ask" },
|
|
3202
|
+
"ui": { short: "Interactive surfaces", useWhen: "a human wants the dashboard, guide, or scratch" },
|
|
3203
|
+
"segment": { short: "Status-line segment registration", useWhen: "wiring tmux status indicators" },
|
|
3204
|
+
"admin": { short: "Install, verify, and report", useWhen: "setting up or diagnosing the install" },
|
|
3205
|
+
"companion": { short: "Companion-pane helper", useWhen: "driving the companion Claude pane" },
|
|
3206
|
+
"deploy": { short: "Provision cloud boxes (Terraform)", useWhen: "standing up or tearing down infra" },
|
|
3207
|
+
"cloud": { short: "Per-repo workflow on a deployed box", useWhen: "syncing work to/from the box" },
|
|
3208
|
+
"feedback": { short: "Report a problem with sisyphus itself", useWhen: "the user complains about the tool or workflow" },
|
|
3209
|
+
"session lifecycle": { short: "Start/stop/advance a session", useWhen: "changing whether a session runs" },
|
|
3210
|
+
"session inspect": { short: "Read session state", useWhen: "you need status/history/context without mutating" },
|
|
3211
|
+
"session config": { short: "Change a session's settings", useWhen: "adjusting task, effort, or dangerous mode" },
|
|
3212
|
+
"session recover": { short: "Repair or relocate a session", useWhen: "a session is stuck, lost, or needs rollback" },
|
|
3213
|
+
"session scratch": { short: "Open a standalone (non-sisyphus) Claude", useWhen: "you want a throwaway Claude in this repo" },
|
|
3214
|
+
"session lifecycle start": { short: "Start a new session", useWhen: "beginning work on a new task" },
|
|
3215
|
+
"session lifecycle complete": { short: "Mark the current cycle complete", useWhen: "the orchestrator finished its work" },
|
|
3216
|
+
"session lifecycle continue": { short: "Continue past completion into a new cycle", useWhen: "more work follows completion" },
|
|
3217
|
+
"session lifecycle resume": { short: "Resume a paused/handed-off session", useWhen: "bringing a stopped session back" },
|
|
3218
|
+
"session lifecycle kill": { short: "Stop a session, keep its state", useWhen: "halting work but preserving the record" },
|
|
3219
|
+
"session lifecycle delete": { short: "Remove a session and its state", useWhen: "discarding a session permanently" },
|
|
3220
|
+
"session inspect status": { short: "Show live session status", useWhen: "checking what is running now" },
|
|
3221
|
+
"session inspect list": { short: "List sessions", useWhen: "enumerating sessions in this repo" },
|
|
3222
|
+
"session inspect history": { short: "Show past sessions/cycles", useWhen: "reviewing prior runs" },
|
|
3223
|
+
"session inspect context": { short: "Print the orchestrator's context", useWhen: "auditing what the orchestrator sees" },
|
|
3224
|
+
"session inspect export": { short: "Export session transcript/state", useWhen: "archiving or sharing a session" },
|
|
3225
|
+
"session inspect requirements": { short: "Show/export the session requirements", useWhen: "reviewing the locked requirements doc" },
|
|
3226
|
+
"session config task": { short: "Set the session's task text", useWhen: "retargeting what the session works on" },
|
|
3227
|
+
"session config effort": { short: "Set the effort tier", useWhen: "tuning model effort for a session" },
|
|
3228
|
+
"session config dangerous": { short: "Toggle dangerous (skip-permissions) mode", useWhen: "allowing unattended tool use" },
|
|
3229
|
+
"session recover rollback": { short: "Roll back to an earlier cycle", useWhen: "a cycle went wrong" },
|
|
3230
|
+
"session recover reconnect": { short: "Reattach the daemon to a live session", useWhen: "the tmux/daemon link dropped" },
|
|
3231
|
+
"session recover quiesce": { short: "Pause a session at the next safe point", useWhen: "you need it to stop cleanly" },
|
|
3232
|
+
"session recover clone": { short: "Clone a session toward a new goal", useWhen: "forking work from an existing session" },
|
|
3233
|
+
"agent spawn": { short: "Spawn a worker agent", useWhen: "delegating a scoped sub-task" },
|
|
3234
|
+
"agent submit": { short: "Submit this agent's result upstream", useWhen: "from inside an agent, its work is done" },
|
|
3235
|
+
"agent report": { short: "Post a progress report", useWhen: "from inside an agent, updating mid-task" },
|
|
3236
|
+
"agent await": { short: "Block until a spawned agent submits", useWhen: "you need an agent's result before continuing" },
|
|
3237
|
+
"agent ctl": { short: "Agent process control", useWhen: "killing or restarting an agent process" },
|
|
3238
|
+
"agent io": { short: "Agent message I/O", useWhen: "sending text to or reading an agent" },
|
|
3239
|
+
"agent ctl kill": { short: "Kill an agent", useWhen: "an agent must stop immediately" },
|
|
3240
|
+
"agent ctl restart": { short: "Restart an agent", useWhen: "an agent is wedged and should start over" },
|
|
3241
|
+
"agent io tell": { short: "Send text to an agent", useWhen: "injecting instructions into a running agent" },
|
|
3242
|
+
"agent io read": { short: "Read an agent's transcript", useWhen: "inspecting what an agent produced" },
|
|
3243
|
+
"orch yield": { short: "Yield control back to the human/parent", useWhen: "the orchestrator is done or blocked" },
|
|
3244
|
+
"orch tell": { short: "Send text to the orchestrator", useWhen: "injecting guidance into the orchestrator" },
|
|
3245
|
+
"orch message": { short: "Queue a message for the orchestrator", useWhen: "leaving async input for the next turn" },
|
|
3246
|
+
"orch read": { short: "Read the orchestrator's transcript", useWhen: "inspecting orchestrator output" },
|
|
3247
|
+
"ask submit": { short: "Submit an ask deck for a human", useWhen: "an agent needs a human decision" },
|
|
3248
|
+
"ask poll": { short: "Block until an ask is answered", useWhen: "you need the human's answer before continuing" },
|
|
3249
|
+
"ask peek": { short: "Read an ask's state without blocking", useWhen: "checking ask status non-blocking" },
|
|
3250
|
+
"ui dashboard": { short: "Open the TUI dashboard", useWhen: "a human wants the monitoring UI" },
|
|
3251
|
+
"ui guide": { short: "Print the full usage guide", useWhen: "a human needs end-to-end docs" },
|
|
3252
|
+
"segment register": { short: "Register a status-line segment", useWhen: "adding a tmux status indicator" },
|
|
3253
|
+
"segment unregister": { short: "Remove a status-line segment", useWhen: "tearing one down" },
|
|
3254
|
+
"admin install": { short: "Install / uninstall sisyphus bits", useWhen: "first setup or removal" },
|
|
3255
|
+
"admin check": { short: "Verify the installation", useWhen: "diagnosing a broken or partial install" },
|
|
3256
|
+
"admin report": { short: "Diagnostics & telemetry", useWhen: "filing a bug or uploading session data" },
|
|
3257
|
+
"admin install setup": { short: "Run first-time setup", useWhen: "installing sisyphus on this machine" },
|
|
3258
|
+
"admin install setup-keybind": { short: "Install the tmux keybind", useWhen: "wiring the dashboard hotkey" },
|
|
3259
|
+
"admin install init": { short: "Initialize repo-local config", useWhen: "onboarding a new repo" },
|
|
3260
|
+
"admin install uninstall": { short: "Remove sisyphus", useWhen: "fully removing the install" },
|
|
3261
|
+
"admin check doctor": { short: "Run install diagnostics", useWhen: "something is not working and you want a health check" },
|
|
3262
|
+
"admin check check-keybinds": { short: "Verify tmux keybinds", useWhen: "the dashboard hotkey does not fire" },
|
|
3263
|
+
"admin check check-statusbar": { short: "Verify status-line wiring", useWhen: "status segments do not render" },
|
|
3264
|
+
"admin report bug": { short: "File a bug report", useWhen: "reporting a defect with context" },
|
|
3265
|
+
"admin report upload": { short: "Upload session data", useWhen: "sharing a session for support" },
|
|
3266
|
+
"admin report configure-upload": { short: "Configure the upload target", useWhen: "setting where uploads go" },
|
|
3267
|
+
"admin clean-zombies": { short: "Sweep stale zombie processes", useWhen: "cleaning up after a crashed daemon or hung agent" },
|
|
3268
|
+
"cloud box": { short: "Provision / operate the box for this repo", useWhen: "working with the box-side environment" },
|
|
3269
|
+
"cloud handoff": { short: "Move a session between local and box", useWhen: "relocating in-flight work" },
|
|
3270
|
+
"cloud box sync": { short: "Rsync this repo to the box", useWhen: "pushing local code to the box" },
|
|
3271
|
+
"cloud box install": { short: "Run the package install on the box", useWhen: "box deps are missing/stale" },
|
|
3272
|
+
"cloud box session": { short: "Create/refresh the box tmux home", useWhen: "the box-side session is absent" },
|
|
3273
|
+
"cloud box attach": { short: "Attach to the box session", useWhen: "you want to work on the box interactively" },
|
|
3274
|
+
"cloud box status": { short: "Print box-side status", useWhen: "checking box state for this repo" },
|
|
3275
|
+
"cloud box login": { short: "Run claude auth login on the box", useWhen: "the box needs Claude credentials" },
|
|
3276
|
+
"cloud box up": { short: "Sync+install+session in one shot", useWhen: "bringing the box up from cold" },
|
|
3277
|
+
"cloud handoff push": { short: "Hand a live session off to the box", useWhen: "moving local work to the cloud" },
|
|
3278
|
+
"cloud handoff pull": { short: "Reclaim a handed-off session locally", useWhen: "bringing cloud work back home" },
|
|
3279
|
+
"agent types": { short: "Agent-type catalog", useWhen: "discovering installable agent types" },
|
|
3280
|
+
"agent types list": { short: "List agent types", useWhen: "enumerating spawnable agent types" },
|
|
3281
|
+
"deploy list": { short: "List deploy providers", useWhen: "discovering supported providers" },
|
|
3282
|
+
"companion profile": { short: "Print companion profile", useWhen: "reading the combined memory + context + badges blob" },
|
|
3283
|
+
"companion memory": { short: "Show accumulated companion observations", useWhen: "reviewing what the companion noticed" },
|
|
3284
|
+
"companion context": { short: "Emit per-prompt companion context", useWhen: "the companion plugin hook needs context" },
|
|
3285
|
+
"companion pane": { short: "Open/focus the side claude pane", useWhen: "you want the companion pane by the dashboard" },
|
|
3286
|
+
"companion popup-test": { short: "Show a test commentary popup", useWhen: "validating feedback-key handling" },
|
|
3287
|
+
"deploy auth": { short: "Configure provider auth", useWhen: "setting up Tailscale/provider credentials" },
|
|
3288
|
+
"deploy hetzner": { short: "Hetzner box lifecycle", useWhen: "targeting Hetzner" },
|
|
3289
|
+
"deploy aws": { short: "AWS box lifecycle", useWhen: "targeting AWS" },
|
|
3290
|
+
"deploy auth tailscale": { short: "Configure Tailscale OAuth", useWhen: "joining the box to your tailnet" },
|
|
3291
|
+
"deploy * up": { short: "Provision the box", useWhen: "standing the box up" },
|
|
3292
|
+
"deploy * down": { short: "Destroy the box", useWhen: "tearing the box down" },
|
|
3293
|
+
"deploy * status": { short: "Print box outputs", useWhen: "checking IP/cost/instance" },
|
|
3294
|
+
"deploy * ssh": { short: "SSH/mosh into the box", useWhen: "getting a shell on the box" },
|
|
3295
|
+
"deploy * logs": { short: "Tail cloud-init + daemon logs", useWhen: "diagnosing a box-side failure" },
|
|
3296
|
+
"deploy * update": { short: "Upgrade sisyphus on the box", useWhen: "pulling the latest daemon onto the box" }
|
|
3297
|
+
};
|
|
3298
|
+
CONCEPTS_BLOCK = `
|
|
3299
|
+
Concepts
|
|
3300
|
+
session a tracked unit of orchestrated work on one task
|
|
3301
|
+
agent a worker Claude spawned to execute a scoped sub-task
|
|
3302
|
+
orchestrator the Claude that owns a session: decomposes, spawns, advances
|
|
3303
|
+
cycle one discovery\u2192plan\u2192implement\u2192validate iteration; the rollback unit
|
|
3304
|
+
ask a blocking question surfaced for a human to answer
|
|
3305
|
+
mode the session's current phase, driving orchestrator behavior
|
|
3306
|
+
`;
|
|
3307
|
+
ROOT_AFTER_HELP = `
|
|
3308
|
+
I/O contract: flags and positional args on input, JSON on stdout (JSONL for streams).
|
|
3309
|
+
|
|
3310
|
+
Exit codes:
|
|
3311
|
+
0 success
|
|
3312
|
+
1 permanent error (fallback)
|
|
3313
|
+
2 usage error (bad args/shape)
|
|
3314
|
+
3 not found
|
|
3315
|
+
4 ambiguous (multiple matches \u2014 see error.candidates)
|
|
3316
|
+
5 conflict (already-exists, wrong-state)
|
|
3317
|
+
60 transient (retry-safe: daemon down, timeout, lock contention)
|
|
3318
|
+
|
|
3319
|
+
Errors:
|
|
3320
|
+
{"ok": false,
|
|
3321
|
+
"error": {"code": "<stable-enum>", "kind": "<usage|not_found|ambiguous|conflict|transient|permanent>",
|
|
3322
|
+
"message": "...", "received"?: ..., "expected"?: ..., "next"?: "...", "candidates"?: [...]}}
|
|
3323
|
+
`;
|
|
3324
|
+
}
|
|
3325
|
+
});
|
|
3326
|
+
|
|
3138
3327
|
// src/daemon/help-inject.ts
|
|
3139
3328
|
import { execSync as execSync19 } from "child_process";
|
|
3140
3329
|
var init_help_inject = __esm({
|
|
3141
3330
|
"src/daemon/help-inject.ts"() {
|
|
3142
3331
|
"use strict";
|
|
3143
3332
|
init_spawn_helpers();
|
|
3333
|
+
init_help_rubric();
|
|
3144
3334
|
}
|
|
3145
3335
|
});
|
|
3146
3336
|
|
|
3147
3337
|
// src/shared/digest-verbs.ts
|
|
3148
|
-
import { existsSync as existsSync39, readFileSync as
|
|
3338
|
+
import { existsSync as existsSync39, readFileSync as readFileSync38 } from "fs";
|
|
3149
3339
|
var init_digest_verbs = __esm({
|
|
3150
3340
|
"src/shared/digest-verbs.ts"() {
|
|
3151
3341
|
"use strict";
|
|
@@ -3161,8 +3351,8 @@ var init_colors = __esm({
|
|
|
3161
3351
|
});
|
|
3162
3352
|
|
|
3163
3353
|
// src/daemon/orchestrator-modes.ts
|
|
3164
|
-
import { existsSync as existsSync40, readdirSync as readdirSync10, readFileSync as
|
|
3165
|
-
import { resolve as resolve14, join as
|
|
3354
|
+
import { existsSync as existsSync40, readdirSync as readdirSync10, readFileSync as readFileSync39 } from "fs";
|
|
3355
|
+
import { resolve as resolve14, join as join33 } from "path";
|
|
3166
3356
|
import { homedir as homedir14 } from "os";
|
|
3167
3357
|
var init_orchestrator_modes = __esm({
|
|
3168
3358
|
"src/daemon/orchestrator-modes.ts"() {
|
|
@@ -3173,8 +3363,8 @@ var init_orchestrator_modes = __esm({
|
|
|
3173
3363
|
});
|
|
3174
3364
|
|
|
3175
3365
|
// src/daemon/lib/render-plugin.ts
|
|
3176
|
-
import { readdirSync as readdirSync11, readFileSync as
|
|
3177
|
-
import { join as
|
|
3366
|
+
import { readdirSync as readdirSync11, readFileSync as readFileSync40, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18, copyFileSync as copyFileSync4, rmSync as rmSync8, existsSync as existsSync41 } from "fs";
|
|
3367
|
+
import { join as join34 } from "path";
|
|
3178
3368
|
var init_render_plugin = __esm({
|
|
3179
3369
|
"src/daemon/lib/render-plugin.ts"() {
|
|
3180
3370
|
"use strict";
|
|
@@ -3255,10 +3445,10 @@ var init_orphan_sweep = __esm({
|
|
|
3255
3445
|
});
|
|
3256
3446
|
|
|
3257
3447
|
// src/daemon/agent.ts
|
|
3258
|
-
import { readFileSync as
|
|
3448
|
+
import { readFileSync as readFileSync41, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, readdirSync as readdirSync12, existsSync as existsSync43, unlinkSync as unlinkSync8 } from "fs";
|
|
3259
3449
|
import { execSync as execSync21 } from "child_process";
|
|
3260
3450
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
3261
|
-
import { resolve as resolve15, relative as relative2, dirname as dirname13, join as
|
|
3451
|
+
import { resolve as resolve15, relative as relative2, dirname as dirname13, join as join35 } from "path";
|
|
3262
3452
|
var init_agent = __esm({
|
|
3263
3453
|
"src/daemon/agent.ts"() {
|
|
3264
3454
|
"use strict";
|
|
@@ -3336,25 +3526,11 @@ var init_pane_monitor = __esm({
|
|
|
3336
3526
|
}
|
|
3337
3527
|
});
|
|
3338
3528
|
|
|
3339
|
-
// src/daemon/mode-notify.ts
|
|
3340
|
-
import { existsSync as existsSync44 } from "fs";
|
|
3341
|
-
import { ulid as ulid3 } from "ulid";
|
|
3342
|
-
var init_mode_notify = __esm({
|
|
3343
|
-
"src/daemon/mode-notify.ts"() {
|
|
3344
|
-
"use strict";
|
|
3345
|
-
init_ask_store();
|
|
3346
|
-
init_state();
|
|
3347
|
-
init_orchestrator_modes();
|
|
3348
|
-
init_paths();
|
|
3349
|
-
init_types();
|
|
3350
|
-
}
|
|
3351
|
-
});
|
|
3352
|
-
|
|
3353
3529
|
// src/daemon/orchestrator.ts
|
|
3354
|
-
import { existsSync as
|
|
3530
|
+
import { existsSync as existsSync44, readdirSync as readdirSync13, readFileSync as readFileSync42, writeFileSync as writeFileSync24 } from "fs";
|
|
3355
3531
|
import { execSync as execSync22 } from "child_process";
|
|
3356
3532
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
3357
|
-
import { resolve as resolve16, join as
|
|
3533
|
+
import { resolve as resolve16, join as join36, relative as relative3 } from "path";
|
|
3358
3534
|
var init_orchestrator = __esm({
|
|
3359
3535
|
"src/daemon/orchestrator.ts"() {
|
|
3360
3536
|
"use strict";
|
|
@@ -3376,13 +3552,12 @@ var init_orchestrator = __esm({
|
|
|
3376
3552
|
init_tmux2();
|
|
3377
3553
|
init_pane_registry();
|
|
3378
3554
|
init_pane_monitor();
|
|
3379
|
-
init_mode_notify();
|
|
3380
3555
|
init_plugins();
|
|
3381
3556
|
}
|
|
3382
3557
|
});
|
|
3383
3558
|
|
|
3384
3559
|
// src/daemon/status-dots.ts
|
|
3385
|
-
import { readFileSync as
|
|
3560
|
+
import { readFileSync as readFileSync43 } from "fs";
|
|
3386
3561
|
var COMPLETED_TTL_MS;
|
|
3387
3562
|
var init_status_dots = __esm({
|
|
3388
3563
|
"src/daemon/status-dots.ts"() {
|
|
@@ -3394,9 +3569,17 @@ var init_status_dots = __esm({
|
|
|
3394
3569
|
}
|
|
3395
3570
|
});
|
|
3396
3571
|
|
|
3572
|
+
// src/daemon/mode-transition.ts
|
|
3573
|
+
var init_mode_transition = __esm({
|
|
3574
|
+
"src/daemon/mode-transition.ts"() {
|
|
3575
|
+
"use strict";
|
|
3576
|
+
init_orchestrator_modes();
|
|
3577
|
+
}
|
|
3578
|
+
});
|
|
3579
|
+
|
|
3397
3580
|
// src/daemon/uploader.ts
|
|
3398
3581
|
import { rm } from "fs/promises";
|
|
3399
|
-
import { tmpdir as
|
|
3582
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
3400
3583
|
var init_uploader = __esm({
|
|
3401
3584
|
"src/daemon/uploader.ts"() {
|
|
3402
3585
|
"use strict";
|
|
@@ -3435,7 +3618,7 @@ var init_format = __esm({
|
|
|
3435
3618
|
|
|
3436
3619
|
// src/daemon/session-manager.ts
|
|
3437
3620
|
import { v4 as uuidv4 } from "uuid";
|
|
3438
|
-
import { existsSync as
|
|
3621
|
+
import { existsSync as existsSync45, readFileSync as readFileSync44, readdirSync as readdirSync14, rmSync as rmSync9 } from "fs";
|
|
3439
3622
|
var init_session_manager = __esm({
|
|
3440
3623
|
"src/daemon/session-manager.ts"() {
|
|
3441
3624
|
"use strict";
|
|
@@ -3458,6 +3641,7 @@ var init_session_manager = __esm({
|
|
|
3458
3641
|
init_companion_render();
|
|
3459
3642
|
init_companion_commentary();
|
|
3460
3643
|
init_companion_popup();
|
|
3644
|
+
init_mode_transition();
|
|
3461
3645
|
init_history();
|
|
3462
3646
|
init_uploader();
|
|
3463
3647
|
init_upload();
|
|
@@ -3472,7 +3656,7 @@ var init_session_manager = __esm({
|
|
|
3472
3656
|
// src/daemon/transcript-digest.ts
|
|
3473
3657
|
import { openSync, fstatSync, readSync, closeSync } from "fs";
|
|
3474
3658
|
import { homedir as homedir15 } from "os";
|
|
3475
|
-
import { join as
|
|
3659
|
+
import { join as join37 } from "path";
|
|
3476
3660
|
var BYTE_CAP, TAIL_BYTES;
|
|
3477
3661
|
var init_transcript_digest = __esm({
|
|
3478
3662
|
"src/daemon/transcript-digest.ts"() {
|
|
@@ -3486,7 +3670,7 @@ var init_transcript_digest = __esm({
|
|
|
3486
3670
|
import {
|
|
3487
3671
|
closeSync as closeSync2,
|
|
3488
3672
|
constants,
|
|
3489
|
-
existsSync as
|
|
3673
|
+
existsSync as existsSync46,
|
|
3490
3674
|
fstatSync as fstatSync2,
|
|
3491
3675
|
lstatSync as lstatSync2,
|
|
3492
3676
|
openSync as openSync2,
|
|
@@ -3522,17 +3706,17 @@ var init_ask_visual = __esm({
|
|
|
3522
3706
|
|
|
3523
3707
|
// src/daemon/server.ts
|
|
3524
3708
|
import { createServer } from "net";
|
|
3525
|
-
import { unlinkSync as
|
|
3526
|
-
import { join as
|
|
3709
|
+
import { unlinkSync as unlinkSync9, existsSync as existsSync47, writeFileSync as writeFileSync25, readFileSync as readFileSync46, mkdirSync as mkdirSync20, readdirSync as readdirSync15, rmSync as rmSync11, chmodSync as chmodSync4 } from "fs";
|
|
3710
|
+
import { join as join38, basename as basename10, dirname as dirname15 } from "path";
|
|
3527
3711
|
import { scanInbox } from "@crouton-kit/humanloop";
|
|
3528
3712
|
function registryPath() {
|
|
3529
|
-
return
|
|
3713
|
+
return join38(globalDir(), "session-registry.json");
|
|
3530
3714
|
}
|
|
3531
3715
|
function loadSessionRegistry() {
|
|
3532
3716
|
const p = registryPath();
|
|
3533
|
-
if (!
|
|
3717
|
+
if (!existsSync47(p)) return {};
|
|
3534
3718
|
try {
|
|
3535
|
-
return JSON.parse(
|
|
3719
|
+
return JSON.parse(readFileSync46(p, "utf-8"));
|
|
3536
3720
|
} catch (err) {
|
|
3537
3721
|
console.warn("[sisyphus] Failed to parse session registry:", err instanceof Error ? err.message : err);
|
|
3538
3722
|
return {};
|
|
@@ -3563,8 +3747,8 @@ var init_server = __esm({
|
|
|
3563
3747
|
|
|
3564
3748
|
// src/cli/index.ts
|
|
3565
3749
|
import { Command } from "commander";
|
|
3566
|
-
import { existsSync as
|
|
3567
|
-
import { dirname as dirname16, join as
|
|
3750
|
+
import { existsSync as existsSync49, mkdirSync as mkdirSync21, readFileSync as readFileSync47 } from "fs";
|
|
3751
|
+
import { dirname as dirname16, join as join39 } from "path";
|
|
3568
3752
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
3569
3753
|
|
|
3570
3754
|
// src/cli/commands/start.ts
|
|
@@ -5670,6 +5854,10 @@ function assertTmux() {
|
|
|
5670
5854
|
throw new Error("Not running inside a tmux pane. Sisyphus requires tmux.");
|
|
5671
5855
|
}
|
|
5672
5856
|
}
|
|
5857
|
+
function getTmuxSession() {
|
|
5858
|
+
assertTmux();
|
|
5859
|
+
return execSync4('tmux display-message -p "#{session_name}"', { encoding: "utf8" }).trim();
|
|
5860
|
+
}
|
|
5673
5861
|
function getTmuxSessionInfo() {
|
|
5674
5862
|
assertTmux();
|
|
5675
5863
|
const out = execSync4('tmux display-message -p "#{session_id}|#{session_name}"', { encoding: "utf8" }).trim();
|
|
@@ -5686,6 +5874,27 @@ function getCurrentTmuxSessionHome(sessionId) {
|
|
|
5686
5874
|
return "";
|
|
5687
5875
|
}
|
|
5688
5876
|
}
|
|
5877
|
+
function findHomeSession(cwd) {
|
|
5878
|
+
const normalizedCwd = cwd.replace(/\/+$/, "");
|
|
5879
|
+
let output;
|
|
5880
|
+
try {
|
|
5881
|
+
output = execSync4('tmux list-sessions -F "#{session_id}|#{session_name}"', {
|
|
5882
|
+
encoding: "utf-8",
|
|
5883
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5884
|
+
}).trim();
|
|
5885
|
+
} catch {
|
|
5886
|
+
return null;
|
|
5887
|
+
}
|
|
5888
|
+
for (const line of output.split("\n").filter(Boolean)) {
|
|
5889
|
+
const pipeIdx = line.indexOf("|");
|
|
5890
|
+
if (pipeIdx < 0) continue;
|
|
5891
|
+
const sessId = line.slice(0, pipeIdx);
|
|
5892
|
+
const name = line.slice(pipeIdx + 1);
|
|
5893
|
+
if (name.startsWith("ssyph_")) continue;
|
|
5894
|
+
if (getCurrentTmuxSessionHome(sessId) === normalizedCwd) return sessId;
|
|
5895
|
+
}
|
|
5896
|
+
return null;
|
|
5897
|
+
}
|
|
5689
5898
|
|
|
5690
5899
|
// src/cli/commands/start.ts
|
|
5691
5900
|
init_shell();
|
|
@@ -5939,6 +6148,27 @@ function openDashboardWindow(tmuxSession, cwd) {
|
|
|
5939
6148
|
}
|
|
5940
6149
|
} catch {
|
|
5941
6150
|
}
|
|
6151
|
+
try {
|
|
6152
|
+
const windows = execSync6(
|
|
6153
|
+
`tmux list-windows -t ${shellQuote(tmuxSession)} -F "#{window_id} #{window_name}"`,
|
|
6154
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6155
|
+
).trim();
|
|
6156
|
+
for (const line of windows.split("\n").filter(Boolean)) {
|
|
6157
|
+
const spaceIdx = line.indexOf(" ");
|
|
6158
|
+
if (spaceIdx < 0) continue;
|
|
6159
|
+
const wid = line.slice(0, spaceIdx);
|
|
6160
|
+
const wname = line.slice(spaceIdx + 1);
|
|
6161
|
+
if (wname === "sisyphus-dashboard") {
|
|
6162
|
+
execSync6(
|
|
6163
|
+
`tmux set-option -t ${shellQuote(tmuxSession)} @sisyphus_dashboard ${shellQuote(wid)}`,
|
|
6164
|
+
{ stdio: "pipe" }
|
|
6165
|
+
);
|
|
6166
|
+
execSync6(`tmux select-window -t ${shellQuote(wid)}`, { stdio: "pipe" });
|
|
6167
|
+
return false;
|
|
6168
|
+
}
|
|
6169
|
+
}
|
|
6170
|
+
} catch {
|
|
6171
|
+
}
|
|
5942
6172
|
const tuiPath = join6(import.meta.dirname, "tui.js");
|
|
5943
6173
|
const windowId = execSync6(
|
|
5944
6174
|
`tmux new-window -t ${shellQuote(tmuxSession + ":")} -n "sisyphus-dashboard" -c ${shellQuote(cwd)} -P -F "#{window_id}"`,
|
|
@@ -6407,8 +6637,8 @@ Exit codes: 0 ok | 2 usage (missing content or --session) | 3 not_found.`
|
|
|
6407
6637
|
}
|
|
6408
6638
|
|
|
6409
6639
|
// src/cli/commands/ask.ts
|
|
6410
|
-
import { existsSync as existsSync12, readFileSync as
|
|
6411
|
-
import { basename as basename4, join as
|
|
6640
|
+
import { existsSync as existsSync12, readFileSync as readFileSync13, unlinkSync as unlinkSync5, watchFile, unwatchFile } from "fs";
|
|
6641
|
+
import { basename as basename4, join as join14, resolve as resolve5 } from "path";
|
|
6412
6642
|
import { ulid } from "ulid";
|
|
6413
6643
|
|
|
6414
6644
|
// src/shared/ask-schema.ts
|
|
@@ -6471,6 +6701,125 @@ init_state();
|
|
|
6471
6701
|
init_types();
|
|
6472
6702
|
init_exec();
|
|
6473
6703
|
init_shell();
|
|
6704
|
+
init_platform();
|
|
6705
|
+
|
|
6706
|
+
// src/cli/commands/ask-popup.ts
|
|
6707
|
+
init_exec();
|
|
6708
|
+
init_shell();
|
|
6709
|
+
import { writeFileSync as writeFileSync8, readFileSync as readFileSync12, unlinkSync as unlinkSync4 } from "fs";
|
|
6710
|
+
import { tmpdir } from "os";
|
|
6711
|
+
import { join as join13 } from "path";
|
|
6712
|
+
var POPUP_WIDTH = 46;
|
|
6713
|
+
var INNER_WIDTH = POPUP_WIDTH - 6;
|
|
6714
|
+
var POPUP_TIMEOUT = 10;
|
|
6715
|
+
var ACCENT_COLOR = "colour214";
|
|
6716
|
+
function wrapText(text, width) {
|
|
6717
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
6718
|
+
const lines = [];
|
|
6719
|
+
let current = "";
|
|
6720
|
+
for (const word of words) {
|
|
6721
|
+
if (current && current.length + 1 + word.length > width) {
|
|
6722
|
+
lines.push(current);
|
|
6723
|
+
current = word;
|
|
6724
|
+
} else {
|
|
6725
|
+
current = current ? `${current} ${word}` : word;
|
|
6726
|
+
}
|
|
6727
|
+
}
|
|
6728
|
+
if (current) lines.push(current);
|
|
6729
|
+
return lines.length > 0 ? lines : [""];
|
|
6730
|
+
}
|
|
6731
|
+
function hasAttachedClient() {
|
|
6732
|
+
const out = execSafe('tmux list-clients -F "#{client_name}"');
|
|
6733
|
+
return !!out && out.split("\n").filter(Boolean).length > 0;
|
|
6734
|
+
}
|
|
6735
|
+
function showAskTriagePopup(opts) {
|
|
6736
|
+
const scriptPath2 = join13(tmpdir(), `sisyphus-ask-${opts.askId}.sh`);
|
|
6737
|
+
const resultPath = join13(tmpdir(), `sisyphus-ask-${opts.askId}.result`);
|
|
6738
|
+
try {
|
|
6739
|
+
const safeTitle = opts.title.trim().length > 0 ? opts.title : "Question pending";
|
|
6740
|
+
const titleLines = wrapText(safeTitle, INNER_WIDTH);
|
|
6741
|
+
const bodyLines = [
|
|
6742
|
+
"",
|
|
6743
|
+
...titleLines.map((l) => ` ${l}`),
|
|
6744
|
+
"",
|
|
6745
|
+
" [1] Dismiss",
|
|
6746
|
+
" [2] Open in dashboard",
|
|
6747
|
+
" [3] Open in agent",
|
|
6748
|
+
"",
|
|
6749
|
+
` (auto-dismiss in ${POPUP_TIMEOUT}s)`,
|
|
6750
|
+
""
|
|
6751
|
+
];
|
|
6752
|
+
const content = bodyLines.join("\n") + "\n";
|
|
6753
|
+
const height = bodyLines.length + 2;
|
|
6754
|
+
const script = `#!/bin/sh
|
|
6755
|
+
printf '\\033[?25l'
|
|
6756
|
+
stty -echo 2>/dev/null
|
|
6757
|
+
RESULT_FILE=${shellQuote(resultPath)}
|
|
6758
|
+
printf '\\033[2J\\033[H'
|
|
6759
|
+
cat <<'__SISYPHUS_ASK_EOF__'
|
|
6760
|
+
${content}__SISYPHUS_ASK_EOF__
|
|
6761
|
+
while IFS= read -r -n1 -t ${POPUP_TIMEOUT} k; do
|
|
6762
|
+
case "$k" in
|
|
6763
|
+
1|d|D|q|Q|'') printf 'dismiss' > "$RESULT_FILE"; break ;;
|
|
6764
|
+
2|b|B) printf 'dashboard' > "$RESULT_FILE"; break ;;
|
|
6765
|
+
3|a|A) printf 'agent' > "$RESULT_FILE"; break ;;
|
|
6766
|
+
esac
|
|
6767
|
+
done
|
|
6768
|
+
if [ ! -f "$RESULT_FILE" ]; then printf 'dismiss' > "$RESULT_FILE"; fi
|
|
6769
|
+
`;
|
|
6770
|
+
writeFileSync8(scriptPath2, script, { mode: 493 });
|
|
6771
|
+
try {
|
|
6772
|
+
unlinkSync4(resultPath);
|
|
6773
|
+
} catch {
|
|
6774
|
+
}
|
|
6775
|
+
const popupTitle = ` \u2691 ${opts.sessionLabel} `;
|
|
6776
|
+
const clientsRaw = execSafe('tmux list-clients -F "#{client_name} #{client_width}"');
|
|
6777
|
+
if (!clientsRaw) return "dismiss";
|
|
6778
|
+
const lines = clientsRaw.split("\n").filter(Boolean);
|
|
6779
|
+
if (lines.length === 0) return "dismiss";
|
|
6780
|
+
let raw = null;
|
|
6781
|
+
for (const line of lines) {
|
|
6782
|
+
const lastSpace = line.lastIndexOf(" ");
|
|
6783
|
+
const client = line.slice(0, lastSpace);
|
|
6784
|
+
const clientWidth = parseInt(line.slice(lastSpace + 1), 10);
|
|
6785
|
+
if (!clientWidth) continue;
|
|
6786
|
+
const x = Math.max(0, clientWidth - POPUP_WIDTH - 1);
|
|
6787
|
+
const args2 = [
|
|
6788
|
+
`-c ${shellQuote(client)}`,
|
|
6789
|
+
"-E -b double",
|
|
6790
|
+
`-T ${shellQuote(popupTitle)}`,
|
|
6791
|
+
`-S "fg=${ACCENT_COLOR}"`,
|
|
6792
|
+
`-s "fg=${ACCENT_COLOR}"`,
|
|
6793
|
+
`-x ${x} -y 2`,
|
|
6794
|
+
`-w ${POPUP_WIDTH} -h ${height}`,
|
|
6795
|
+
shellQuote(scriptPath2)
|
|
6796
|
+
].join(" ");
|
|
6797
|
+
execSafe(`tmux display-popup ${args2}`);
|
|
6798
|
+
try {
|
|
6799
|
+
raw = readFileSync12(resultPath, "utf8").trim();
|
|
6800
|
+
} catch {
|
|
6801
|
+
raw = null;
|
|
6802
|
+
}
|
|
6803
|
+
if (raw) break;
|
|
6804
|
+
}
|
|
6805
|
+
if (raw === "dashboard") return "dashboard";
|
|
6806
|
+
if (raw === "agent") return "agent";
|
|
6807
|
+
return "dismiss";
|
|
6808
|
+
} catch {
|
|
6809
|
+
return "dismiss";
|
|
6810
|
+
} finally {
|
|
6811
|
+
try {
|
|
6812
|
+
unlinkSync4(resultPath);
|
|
6813
|
+
} catch {
|
|
6814
|
+
}
|
|
6815
|
+
try {
|
|
6816
|
+
unlinkSync4(scriptPath2);
|
|
6817
|
+
} catch {
|
|
6818
|
+
}
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
|
|
6822
|
+
// src/cli/commands/ask.ts
|
|
6474
6823
|
import { approveDeck, notifyDeck, launchReview, display } from "@crouton-kit/humanloop";
|
|
6475
6824
|
var ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
6476
6825
|
function validateAskId(askId) {
|
|
@@ -6862,7 +7211,7 @@ async function markAnswered(cwd, sessionId, askId) {
|
|
|
6862
7211
|
function waitForOutput(cwd, sessionId, askId, initialPpid) {
|
|
6863
7212
|
const outputPath = askOutputPath(cwd, sessionId, askId);
|
|
6864
7213
|
if (existsSync12(outputPath)) {
|
|
6865
|
-
return Promise.resolve(JSON.parse(
|
|
7214
|
+
return Promise.resolve(JSON.parse(readFileSync13(outputPath, "utf-8")));
|
|
6866
7215
|
}
|
|
6867
7216
|
return new Promise((res, _rej) => {
|
|
6868
7217
|
let ppidWatcher;
|
|
@@ -6874,7 +7223,7 @@ function waitForOutput(cwd, sessionId, askId, initialPpid) {
|
|
|
6874
7223
|
const onChange = () => {
|
|
6875
7224
|
if (!existsSync12(outputPath)) return;
|
|
6876
7225
|
try {
|
|
6877
|
-
const out = JSON.parse(
|
|
7226
|
+
const out = JSON.parse(readFileSync13(outputPath, "utf-8"));
|
|
6878
7227
|
cleanup();
|
|
6879
7228
|
res(out);
|
|
6880
7229
|
} catch (err) {
|
|
@@ -6901,22 +7250,86 @@ function waitForOutput(cwd, sessionId, askId, initialPpid) {
|
|
|
6901
7250
|
process.once("SIGINT", onSigint);
|
|
6902
7251
|
});
|
|
6903
7252
|
}
|
|
7253
|
+
function focusAgentPane(callerPane) {
|
|
7254
|
+
const sess = execSafe(`tmux display-message -p -t ${shellQuote(callerPane)} "#{session_name}"`)?.trim();
|
|
7255
|
+
const win = execSafe(`tmux display-message -p -t ${shellQuote(callerPane)} "#{window_id}"`)?.trim();
|
|
7256
|
+
if (sess) switchAllClientsToSession(sess);
|
|
7257
|
+
if (win) execSafe(`tmux select-window -t ${shellQuote(win)}`);
|
|
7258
|
+
}
|
|
7259
|
+
function switchAllClientsToSession(session2) {
|
|
7260
|
+
const clients = execSafe('tmux list-clients -F "#{client_name}"');
|
|
7261
|
+
if (!clients) return;
|
|
7262
|
+
for (const c of clients.split("\n").filter(Boolean)) {
|
|
7263
|
+
execSafe(`tmux switch-client -c ${shellQuote(c)} -t ${shellQuote(session2)}`);
|
|
7264
|
+
}
|
|
7265
|
+
}
|
|
6904
7266
|
function maybeSpawnAskPane(cwd, sessionId, askId, kind) {
|
|
6905
7267
|
if (kind === "review") return;
|
|
6906
7268
|
const callerPane = process.env.TMUX_PANE;
|
|
6907
7269
|
if (!callerPane) return;
|
|
6908
7270
|
if (process.env.SISYPHUS_DISABLE_ASK_PANE === "1") return;
|
|
6909
|
-
const tuiPath =
|
|
7271
|
+
const tuiPath = join14(import.meta.dirname, "tui.js");
|
|
6910
7272
|
const cmd = `node ${shellQuote(tuiPath)} --cwd ${shellQuote(cwd)} --session-id ${shellQuote(sessionId)} --ask ${shellQuote(askId)}`;
|
|
6911
|
-
execSafe(`tmux split-window -
|
|
7273
|
+
execSafe(`tmux split-window -h -t ${shellQuote(callerPane)} -c ${shellQuote(cwd)} ${shellQuote(cmd)}`);
|
|
7274
|
+
focusAgentPane(callerPane);
|
|
6912
7275
|
}
|
|
6913
7276
|
function maybeSpawnReviewPane(cwd, sessionId, askId) {
|
|
6914
7277
|
const callerPane = process.env.TMUX_PANE;
|
|
6915
7278
|
if (!callerPane) return;
|
|
6916
7279
|
if (process.env.SISYPHUS_DISABLE_ASK_PANE === "1") return;
|
|
6917
|
-
const cliPath =
|
|
7280
|
+
const cliPath = join14(import.meta.dirname, "cli.js");
|
|
6918
7281
|
const cmd = `node ${shellQuote(cliPath)} ask review open ${shellQuote(askId)} --session ${shellQuote(sessionId)}`;
|
|
6919
7282
|
execSafe(`tmux split-window -h -t ${shellQuote(callerPane)} -c ${shellQuote(cwd)} ${shellQuote(cmd)}`);
|
|
7283
|
+
focusAgentPane(callerPane);
|
|
7284
|
+
}
|
|
7285
|
+
function sessionLabel(cwd, sessionId) {
|
|
7286
|
+
try {
|
|
7287
|
+
if (existsSync12(statePath(cwd, sessionId))) {
|
|
7288
|
+
const s = getSession(cwd, sessionId);
|
|
7289
|
+
if (s.name) return s.name;
|
|
7290
|
+
}
|
|
7291
|
+
} catch {
|
|
7292
|
+
}
|
|
7293
|
+
return sessionId.slice(0, 8);
|
|
7294
|
+
}
|
|
7295
|
+
function shouldSuppressBanner() {
|
|
7296
|
+
const popupWillShow = !!process.env.TMUX_PANE && process.env.SISYPHUS_DISABLE_ASK_PANE !== "1" && hasAttachedClient();
|
|
7297
|
+
return popupWillShow && isTerminalFrontmost();
|
|
7298
|
+
}
|
|
7299
|
+
async function triageAsk(cwd, sessionId, askId, kind, title) {
|
|
7300
|
+
const callerPane = process.env.TMUX_PANE;
|
|
7301
|
+
if (!callerPane) return;
|
|
7302
|
+
if (process.env.SISYPHUS_DISABLE_ASK_PANE === "1") return;
|
|
7303
|
+
const choice = showAskTriagePopup({
|
|
7304
|
+
askId,
|
|
7305
|
+
sessionLabel: sessionLabel(cwd, sessionId),
|
|
7306
|
+
title: title !== void 0 ? title : "Question pending"
|
|
7307
|
+
});
|
|
7308
|
+
if (choice === "dismiss") return;
|
|
7309
|
+
if (choice === "agent") {
|
|
7310
|
+
if (kind === "review") maybeSpawnReviewPane(cwd, sessionId, askId);
|
|
7311
|
+
else maybeSpawnAskPane(cwd, sessionId, askId, kind);
|
|
7312
|
+
return;
|
|
7313
|
+
}
|
|
7314
|
+
await openInDashboard(cwd, sessionId, askId, kind);
|
|
7315
|
+
}
|
|
7316
|
+
async function openInDashboard(cwd, sessionId, askId, kind) {
|
|
7317
|
+
try {
|
|
7318
|
+
assertTmux();
|
|
7319
|
+
const tmuxSession = findHomeSession(cwd) ?? getTmuxSession();
|
|
7320
|
+
if (kind === "review") {
|
|
7321
|
+
openDashboardWindow(tmuxSession, cwd);
|
|
7322
|
+
switchAllClientsToSession(tmuxSession);
|
|
7323
|
+
const cliPath = join14(import.meta.dirname, "cli.js");
|
|
7324
|
+
const cmd = `node ${shellQuote(cliPath)} ask review open ${shellQuote(askId)} --session ${shellQuote(sessionId)}`;
|
|
7325
|
+
execSafe(`tmux display-popup -E -w 90% -h 90% -d ${shellQuote(cwd)} ${shellQuote(cmd)}`);
|
|
7326
|
+
return;
|
|
7327
|
+
}
|
|
7328
|
+
await rawSend({ type: "focus-set", cwd, sessionId, askId });
|
|
7329
|
+
openDashboardWindow(tmuxSession, cwd);
|
|
7330
|
+
switchAllClientsToSession(tmuxSession);
|
|
7331
|
+
} catch {
|
|
7332
|
+
}
|
|
6920
7333
|
}
|
|
6921
7334
|
async function submitDeck(deck, opts, options) {
|
|
6922
7335
|
const blocking = options?.blocking !== false;
|
|
@@ -6926,6 +7339,8 @@ async function submitDeck(deck, opts, options) {
|
|
|
6926
7339
|
const claudeSessionId = resolveClaudeSessionId(cwd, sessionId, askedBy);
|
|
6927
7340
|
const askId = mintAskId();
|
|
6928
7341
|
const q0 = deck.interactions[0];
|
|
7342
|
+
const askTitle = deck.title !== void 0 ? deck.title : q0?.title;
|
|
7343
|
+
const suppressTerminalNotification = blocking ? shouldSuppressBanner() : false;
|
|
6929
7344
|
createAsk(cwd, sessionId, {
|
|
6930
7345
|
askId,
|
|
6931
7346
|
askedBy,
|
|
@@ -6933,15 +7348,16 @@ async function submitDeck(deck, opts, options) {
|
|
|
6933
7348
|
pid: process.pid,
|
|
6934
7349
|
claudeSessionId,
|
|
6935
7350
|
cwd,
|
|
6936
|
-
title:
|
|
7351
|
+
title: askTitle,
|
|
6937
7352
|
subtitle: q0?.subtitle,
|
|
6938
|
-
kind: options?.kindOverride !== void 0 ? options.kindOverride : q0?.kind
|
|
7353
|
+
kind: options?.kindOverride !== void 0 ? options.kindOverride : q0?.kind,
|
|
7354
|
+
suppressTerminalNotification
|
|
6939
7355
|
});
|
|
6940
7356
|
writeDecisions(cwd, sessionId, askId, deck);
|
|
6941
7357
|
if (!blocking) {
|
|
6942
7358
|
return { askId };
|
|
6943
7359
|
}
|
|
6944
|
-
|
|
7360
|
+
await triageAsk(cwd, sessionId, askId, q0?.kind, askTitle);
|
|
6945
7361
|
const output = await waitForOutput(cwd, sessionId, askId, initialPpid);
|
|
6946
7362
|
await markAnswered(cwd, sessionId, askId);
|
|
6947
7363
|
return { askId, output };
|
|
@@ -6994,6 +7410,8 @@ async function submitReview(absFile, opts, options) {
|
|
|
6994
7410
|
const initialPpid = process.ppid;
|
|
6995
7411
|
const claudeSessionId = resolveClaudeSessionId(cwd, sessionId, askedBy);
|
|
6996
7412
|
const askId = mintAskId();
|
|
7413
|
+
const reviewTitle = `Review ${basename4(absFile)}`;
|
|
7414
|
+
const suppressTerminalNotification = blocking ? shouldSuppressBanner() : false;
|
|
6997
7415
|
createAsk(cwd, sessionId, {
|
|
6998
7416
|
askId,
|
|
6999
7417
|
askedBy,
|
|
@@ -7001,12 +7419,13 @@ async function submitReview(absFile, opts, options) {
|
|
|
7001
7419
|
pid: process.pid,
|
|
7002
7420
|
claudeSessionId,
|
|
7003
7421
|
cwd,
|
|
7004
|
-
title:
|
|
7005
|
-
kind: "review"
|
|
7422
|
+
title: reviewTitle,
|
|
7423
|
+
kind: "review",
|
|
7424
|
+
suppressTerminalNotification
|
|
7006
7425
|
});
|
|
7007
7426
|
writeReview(cwd, sessionId, askId, { file: absFile });
|
|
7008
7427
|
if (!blocking) return { askId };
|
|
7009
|
-
|
|
7428
|
+
await triageAsk(cwd, sessionId, askId, "review", reviewTitle);
|
|
7010
7429
|
const output = await waitForOutput(cwd, sessionId, askId, initialPpid);
|
|
7011
7430
|
await markAnswered(cwd, sessionId, askId);
|
|
7012
7431
|
return { askId, output };
|
|
@@ -7038,7 +7457,7 @@ async function reviewOpen(askId, opts) {
|
|
|
7038
7457
|
if (!reviewData) exitUsage("missing-review-pointer", `review.json missing for ${askId}`, { received: askId });
|
|
7039
7458
|
const draftPath = askReviewDraftPath(cwd, sessionId, askId);
|
|
7040
7459
|
const flagPath = askReviewSubmitFlagPath(cwd, sessionId, askId);
|
|
7041
|
-
if (existsSync12(flagPath))
|
|
7460
|
+
if (existsSync12(flagPath)) unlinkSync5(flagPath);
|
|
7042
7461
|
const result = await launchReview(reviewData.file, {
|
|
7043
7462
|
output: draftPath,
|
|
7044
7463
|
submitFlagPath: flagPath,
|
|
@@ -7064,7 +7483,7 @@ async function reviewSubmit(askId, opts) {
|
|
|
7064
7483
|
let comments = [];
|
|
7065
7484
|
if (existsSync12(draftPath)) {
|
|
7066
7485
|
try {
|
|
7067
|
-
const draft = JSON.parse(
|
|
7486
|
+
const draft = JSON.parse(readFileSync13(draftPath, "utf-8"));
|
|
7068
7487
|
if (Array.isArray(draft.comments)) comments = draft.comments;
|
|
7069
7488
|
} catch {
|
|
7070
7489
|
}
|
|
@@ -7181,7 +7600,7 @@ async function peek(askId, opts) {
|
|
|
7181
7600
|
if (meta.completedAt) result.completedAt = meta.completedAt;
|
|
7182
7601
|
try {
|
|
7183
7602
|
if (existsSync12(outputPath)) {
|
|
7184
|
-
result.output = JSON.parse(
|
|
7603
|
+
result.output = JSON.parse(readFileSync13(outputPath, "utf-8"));
|
|
7185
7604
|
}
|
|
7186
7605
|
} catch (err) {
|
|
7187
7606
|
if (!(err instanceof SyntaxError)) throw err;
|
|
@@ -7593,13 +8012,13 @@ Exit codes: 0 ok | 2 usage (bad state) | 3 not_found.`
|
|
|
7593
8012
|
|
|
7594
8013
|
// src/tui/lib/context.ts
|
|
7595
8014
|
init_paths();
|
|
7596
|
-
import { readFileSync as
|
|
8015
|
+
import { readFileSync as readFileSync15, readdirSync as readdirSync4 } from "fs";
|
|
7597
8016
|
|
|
7598
8017
|
// src/tui/lib/reports.ts
|
|
7599
|
-
import { readFileSync as
|
|
8018
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
7600
8019
|
function loadReportContent(report) {
|
|
7601
8020
|
try {
|
|
7602
|
-
return
|
|
8021
|
+
return readFileSync14(report.filePath, "utf-8");
|
|
7603
8022
|
} catch {
|
|
7604
8023
|
return report.summary;
|
|
7605
8024
|
}
|
|
@@ -7616,7 +8035,7 @@ function resolveReports(reports) {
|
|
|
7616
8035
|
// src/tui/lib/context.ts
|
|
7617
8036
|
function readFileSafe(filePath) {
|
|
7618
8037
|
try {
|
|
7619
|
-
return
|
|
8038
|
+
return readFileSync15(filePath, "utf-8");
|
|
7620
8039
|
} catch {
|
|
7621
8040
|
return null;
|
|
7622
8041
|
}
|
|
@@ -7795,7 +8214,7 @@ Exit codes: 0 ok | 3 not_found (unknown session).`
|
|
|
7795
8214
|
|
|
7796
8215
|
// src/cli/commands/spawn.ts
|
|
7797
8216
|
import { existsSync as existsSync13 } from "fs";
|
|
7798
|
-
import { join as
|
|
8217
|
+
import { join as join15 } from "path";
|
|
7799
8218
|
function registerSpawn(program2) {
|
|
7800
8219
|
program2.command("spawn").description("Spawn a new agent (orchestrator only)").option("--agent-type <type>", "Agent type (e.g. sisyphus:debug, devcore:programmer)").option("--name <name>", "Agent name").option("--stdin", "Force-read instruction from stdin (avoids shell escaping for long prompts)").option("--repo <name>", "Repo subdirectory to use for this agent").option("--session <sessionId>", "Session ID (defaults to SISYPHUS_SESSION_ID env var)").addHelpText(
|
|
7801
8220
|
"after",
|
|
@@ -7872,7 +8291,7 @@ Exit codes: 0 ok | 2 usage | 3 not_found (unknown session) | 5 conflict.`
|
|
|
7872
8291
|
});
|
|
7873
8292
|
}
|
|
7874
8293
|
if (opts.repo && opts.repo !== ".") {
|
|
7875
|
-
const repoPath =
|
|
8294
|
+
const repoPath = join15(sisyphusCwd, opts.repo);
|
|
7876
8295
|
if (!existsSync13(repoPath)) {
|
|
7877
8296
|
exitError({
|
|
7878
8297
|
code: "repo_not_found",
|
|
@@ -8058,7 +8477,7 @@ Exit codes: 0 ok | 2 usage | 3 not_found | 60 transient.`
|
|
|
8058
8477
|
}
|
|
8059
8478
|
|
|
8060
8479
|
// src/cli/commands/await.ts
|
|
8061
|
-
import { existsSync as existsSync16, readFileSync as
|
|
8480
|
+
import { existsSync as existsSync16, readFileSync as readFileSync18 } from "fs";
|
|
8062
8481
|
var AWAIT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
|
|
8063
8482
|
function registerAwait(program2) {
|
|
8064
8483
|
program2.command("await").description("Block until an agent reaches a terminal status, then print its final report inline. Marks the agent as consumed-inline so its report is suppressed from the next cycle.").argument("<agentId>", "Agent ID to await").option("--session <sessionId>", "Session ID (defaults to SISYPHUS_SESSION_ID env var)").addHelpText(
|
|
@@ -8098,7 +8517,7 @@ Exit codes: 0 ok | 2 usage (missing --session) | 3 not_found (unknown agent) | 6
|
|
|
8098
8517
|
let report = "";
|
|
8099
8518
|
if (reportPath && existsSync16(reportPath)) {
|
|
8100
8519
|
try {
|
|
8101
|
-
report =
|
|
8520
|
+
report = readFileSync18(reportPath, "utf-8");
|
|
8102
8521
|
} catch (err) {
|
|
8103
8522
|
process.stderr.write(`Warning: could not read report at ${reportPath}: ${err instanceof Error ? err.message : err}
|
|
8104
8523
|
`);
|
|
@@ -8303,15 +8722,15 @@ import { execSync as execSync10 } from "child_process";
|
|
|
8303
8722
|
|
|
8304
8723
|
// src/cli/onboard.ts
|
|
8305
8724
|
import { execSync as execSync9 } from "child_process";
|
|
8306
|
-
import { existsSync as existsSync17, readFileSync as
|
|
8725
|
+
import { existsSync as existsSync17, readFileSync as readFileSync19, writeFileSync as writeFileSync10 } from "fs";
|
|
8307
8726
|
import { homedir as homedir9 } from "os";
|
|
8308
|
-
import { dirname as dirname5, join as
|
|
8727
|
+
import { dirname as dirname5, join as join18 } from "path";
|
|
8309
8728
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8310
8729
|
init_platform();
|
|
8311
8730
|
|
|
8312
8731
|
// src/shared/clipboard.ts
|
|
8313
8732
|
init_platform();
|
|
8314
|
-
import { execFileSync as
|
|
8733
|
+
import { execFileSync as execFileSync3, spawnSync } from "child_process";
|
|
8315
8734
|
function detectClipboard() {
|
|
8316
8735
|
const platform = detectPlatform();
|
|
8317
8736
|
if (platform === "darwin") {
|
|
@@ -8402,7 +8821,7 @@ function checkItermOptionKey() {
|
|
|
8402
8821
|
if (process.platform !== "darwin") {
|
|
8403
8822
|
return { checked: false, allCorrect: true, incorrectProfiles: [] };
|
|
8404
8823
|
}
|
|
8405
|
-
const plistPath2 =
|
|
8824
|
+
const plistPath2 = join18(homedir9(), "Library", "Preferences", "com.googlecode.iterm2.plist");
|
|
8406
8825
|
if (!existsSync17(plistPath2)) {
|
|
8407
8826
|
return { checked: false, allCorrect: false, incorrectProfiles: [] };
|
|
8408
8827
|
}
|
|
@@ -8427,7 +8846,7 @@ function checkItermOptionKey() {
|
|
|
8427
8846
|
}
|
|
8428
8847
|
}
|
|
8429
8848
|
function hasExistingTmuxConf() {
|
|
8430
|
-
return existsSync17(
|
|
8849
|
+
return existsSync17(join18(homedir9(), ".tmux.conf")) || existsSync17(join18(homedir9(), ".config", "tmux", "tmux.conf"));
|
|
8431
8850
|
}
|
|
8432
8851
|
var SISYPHUS_DEFAULTS_MARKER = "# sisyphus-managed \u2014 do not edit";
|
|
8433
8852
|
function buildTmuxDefaults() {
|
|
@@ -8539,8 +8958,8 @@ source-file -q ${sisyphusConf} ${SISYPHUS_DEFAULTS_MARKER}
|
|
|
8539
8958
|
`;
|
|
8540
8959
|
}
|
|
8541
8960
|
function writeTmuxDefaults() {
|
|
8542
|
-
const confPath =
|
|
8543
|
-
|
|
8961
|
+
const confPath = join18(homedir9(), ".tmux.conf");
|
|
8962
|
+
writeFileSync10(confPath, buildTmuxDefaults(), "utf8");
|
|
8544
8963
|
}
|
|
8545
8964
|
function isNvimAvailable() {
|
|
8546
8965
|
try {
|
|
@@ -8558,21 +8977,21 @@ function getNvimVersion() {
|
|
|
8558
8977
|
}
|
|
8559
8978
|
}
|
|
8560
8979
|
function hasLazyVimConfig() {
|
|
8561
|
-
return existsSync17(
|
|
8980
|
+
return existsSync17(join18(homedir9(), ".config", "nvim", "lazy-lock.json"));
|
|
8562
8981
|
}
|
|
8563
8982
|
function bundledBaleiaPluginPath() {
|
|
8564
8983
|
const distDir = dirname5(fileURLToPath2(import.meta.url));
|
|
8565
|
-
return
|
|
8984
|
+
return join18(distDir, "templates", "baleia.lua");
|
|
8566
8985
|
}
|
|
8567
8986
|
function installBaleiaPlugin() {
|
|
8568
|
-
const pluginsDir =
|
|
8987
|
+
const pluginsDir = join18(homedir9(), ".config", "nvim", "lua", "plugins");
|
|
8569
8988
|
if (!existsSync17(pluginsDir)) return false;
|
|
8570
|
-
const dest =
|
|
8989
|
+
const dest = join18(pluginsDir, "sisyphus-baleia.lua");
|
|
8571
8990
|
if (existsSync17(dest)) return true;
|
|
8572
8991
|
const src = bundledBaleiaPluginPath();
|
|
8573
8992
|
if (!existsSync17(src)) return false;
|
|
8574
8993
|
try {
|
|
8575
|
-
|
|
8994
|
+
writeFileSync10(dest, readFileSync19(src, "utf-8"), "utf8");
|
|
8576
8995
|
return true;
|
|
8577
8996
|
} catch {
|
|
8578
8997
|
return false;
|
|
@@ -8595,7 +9014,7 @@ function tryAutoInstallNvim() {
|
|
|
8595
9014
|
if (!isNvimAvailable()) {
|
|
8596
9015
|
return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false, baleiaInstalled: false };
|
|
8597
9016
|
}
|
|
8598
|
-
const nvimConfigDir =
|
|
9017
|
+
const nvimConfigDir = join18(homedir9(), ".config", "nvim");
|
|
8599
9018
|
let lazyVimInstalled = false;
|
|
8600
9019
|
if (!existsSync17(nvimConfigDir)) {
|
|
8601
9020
|
const cloneCmd = [
|
|
@@ -8613,7 +9032,7 @@ function tryAutoInstallNvim() {
|
|
|
8613
9032
|
stdio: "inherit",
|
|
8614
9033
|
env: { ...process.env, GIT_LFS_SKIP_SMUDGE: "1" }
|
|
8615
9034
|
});
|
|
8616
|
-
const gitDir =
|
|
9035
|
+
const gitDir = join18(nvimConfigDir, ".git");
|
|
8617
9036
|
if (existsSync17(gitDir)) {
|
|
8618
9037
|
execSync9(`rm -rf "${gitDir}"`, { stdio: "pipe" });
|
|
8619
9038
|
}
|
|
@@ -8900,7 +9319,7 @@ Exit codes: 0 ok | 1 conflict or requires-force`
|
|
|
8900
9319
|
|
|
8901
9320
|
// src/cli/commands/check-keybinds.ts
|
|
8902
9321
|
import { execSync as execSync11 } from "child_process";
|
|
8903
|
-
import { readFileSync as
|
|
9322
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
8904
9323
|
function isTmuxInstalled2() {
|
|
8905
9324
|
try {
|
|
8906
9325
|
execSync11("which tmux", { stdio: "pipe" });
|
|
@@ -8953,7 +9372,7 @@ function runCheck() {
|
|
|
8953
9372
|
let userConfAlreadySources = false;
|
|
8954
9373
|
if (userConfPath !== null) {
|
|
8955
9374
|
try {
|
|
8956
|
-
userConfAlreadySources =
|
|
9375
|
+
userConfAlreadySources = readFileSync20(userConfPath, "utf-8").includes(sisyphusConfPath);
|
|
8957
9376
|
} catch {
|
|
8958
9377
|
}
|
|
8959
9378
|
}
|
|
@@ -9124,9 +9543,9 @@ Exit codes: 0 ok`
|
|
|
9124
9543
|
// src/cli/commands/check-statusbar.ts
|
|
9125
9544
|
init_paths();
|
|
9126
9545
|
import { execSync as execSync12 } from "child_process";
|
|
9127
|
-
import { existsSync as existsSync18, readFileSync as
|
|
9546
|
+
import { existsSync as existsSync18, readFileSync as readFileSync21 } from "fs";
|
|
9128
9547
|
import { homedir as homedir10 } from "os";
|
|
9129
|
-
import { join as
|
|
9548
|
+
import { join as join19 } from "path";
|
|
9130
9549
|
var SISYPHUS_LEFT_TOKEN = "@sisyphus_left";
|
|
9131
9550
|
var SISYPHUS_RIGHT_TOKEN = "@sisyphus_right";
|
|
9132
9551
|
var TMUX_DEFAULT_STATUS_LEFT = "[#S] ";
|
|
@@ -9151,7 +9570,7 @@ function isDaemonRunning() {
|
|
|
9151
9570
|
const pidFile = daemonPidPath();
|
|
9152
9571
|
if (!existsSync18(pidFile)) return false;
|
|
9153
9572
|
try {
|
|
9154
|
-
const pid = parseInt(
|
|
9573
|
+
const pid = parseInt(readFileSync21(pidFile, "utf-8").trim(), 10);
|
|
9155
9574
|
if (Number.isNaN(pid) || pid <= 0) return false;
|
|
9156
9575
|
process.kill(pid, 0);
|
|
9157
9576
|
return true;
|
|
@@ -9194,8 +9613,8 @@ function probeTmuxOptions(serverRunning) {
|
|
|
9194
9613
|
};
|
|
9195
9614
|
}
|
|
9196
9615
|
function findUserTmuxConf() {
|
|
9197
|
-
const xdg =
|
|
9198
|
-
const dotfile =
|
|
9616
|
+
const xdg = join19(homedir10(), ".config", "tmux", "tmux.conf");
|
|
9617
|
+
const dotfile = join19(homedir10(), ".tmux.conf");
|
|
9199
9618
|
if (existsSync18(xdg)) return xdg;
|
|
9200
9619
|
if (existsSync18(dotfile)) return dotfile;
|
|
9201
9620
|
return null;
|
|
@@ -9207,21 +9626,21 @@ function probeUserConf() {
|
|
|
9207
9626
|
}
|
|
9208
9627
|
let contents = "";
|
|
9209
9628
|
try {
|
|
9210
|
-
contents =
|
|
9629
|
+
contents = readFileSync21(path, "utf-8");
|
|
9211
9630
|
} catch {
|
|
9212
9631
|
return { path, setsStatusLeft: false, setsStatusRight: false, sourcesSisyphusManaged: false };
|
|
9213
9632
|
}
|
|
9214
9633
|
const lines = contents.split("\n").filter((line) => !line.trim().startsWith("#"));
|
|
9215
9634
|
const setsStatusLeft = lines.some((line) => /^\s*(set|set-option)\s+-g(?:\s+-\w+)*\s+status-left\b/.test(line));
|
|
9216
9635
|
const setsStatusRight = lines.some((line) => /^\s*(set|set-option)\s+-g(?:\s+-\w+)*\s+status-right\b/.test(line));
|
|
9217
|
-
const sourcesSisyphusManaged = contents.includes(
|
|
9636
|
+
const sourcesSisyphusManaged = contents.includes(join19(homedir10(), ".sisyphus", "tmux.conf"));
|
|
9218
9637
|
return { path, setsStatusLeft, setsStatusRight, sourcesSisyphusManaged };
|
|
9219
9638
|
}
|
|
9220
9639
|
function loadGlobalSisyphusConfig() {
|
|
9221
9640
|
const path = globalConfigPath();
|
|
9222
9641
|
if (!existsSync18(path)) return null;
|
|
9223
9642
|
try {
|
|
9224
|
-
const parsed = JSON.parse(
|
|
9643
|
+
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
9225
9644
|
return parsed.statusBar === void 0 ? null : parsed.statusBar;
|
|
9226
9645
|
} catch {
|
|
9227
9646
|
return null;
|
|
@@ -9561,7 +9980,7 @@ init_paths();
|
|
|
9561
9980
|
import { execSync as execSync14 } from "child_process";
|
|
9562
9981
|
import { existsSync as existsSync19, statSync as statSync4 } from "fs";
|
|
9563
9982
|
import { homedir as homedir11 } from "os";
|
|
9564
|
-
import { join as
|
|
9983
|
+
import { join as join20 } from "path";
|
|
9565
9984
|
init_platform();
|
|
9566
9985
|
init_plugins();
|
|
9567
9986
|
function checkNodeVersion() {
|
|
@@ -9795,7 +10214,7 @@ function checkNvim() {
|
|
|
9795
10214
|
}
|
|
9796
10215
|
function checkNotifyBinary() {
|
|
9797
10216
|
if (process.platform === "darwin") {
|
|
9798
|
-
const binary =
|
|
10217
|
+
const binary = join20(homedir11(), ".sisyphus", "SisyphusNotify.app", "Contents", "MacOS", "sisyphus-notify");
|
|
9799
10218
|
if (existsSync19(binary)) {
|
|
9800
10219
|
return { name: "Notifications", status: "ok", detail: "SisyphusNotify.app built" };
|
|
9801
10220
|
}
|
|
@@ -9909,13 +10328,13 @@ init_version();
|
|
|
9909
10328
|
init_platform();
|
|
9910
10329
|
init_paths();
|
|
9911
10330
|
init_state();
|
|
9912
|
-
import { execFileSync as
|
|
9913
|
-
import { existsSync as existsSync20, readFileSync as
|
|
10331
|
+
import { execFileSync as execFileSync4, spawnSync as spawnSync2 } from "child_process";
|
|
10332
|
+
import { existsSync as existsSync20, readFileSync as readFileSync23 } from "fs";
|
|
9914
10333
|
import os from "os";
|
|
9915
10334
|
var REPO = "crouton-labs/sisyphus";
|
|
9916
10335
|
function tryCmd(bin, args2) {
|
|
9917
10336
|
try {
|
|
9918
|
-
const out =
|
|
10337
|
+
const out = execFileSync4(bin, args2, {
|
|
9919
10338
|
encoding: "utf-8",
|
|
9920
10339
|
stdio: ["ignore", "pipe", "ignore"],
|
|
9921
10340
|
timeout: 5e3
|
|
@@ -9971,7 +10390,7 @@ function tailLog(lines) {
|
|
|
9971
10390
|
const path = daemonLogPath();
|
|
9972
10391
|
if (!existsSync20(path)) return null;
|
|
9973
10392
|
try {
|
|
9974
|
-
const all =
|
|
10393
|
+
const all = readFileSync23(path, "utf-8").split("\n");
|
|
9975
10394
|
return all.slice(-lines).join("\n").trim() || null;
|
|
9976
10395
|
} catch {
|
|
9977
10396
|
return null;
|
|
@@ -10260,8 +10679,8 @@ Exit codes: 0 ok | 1 filing error | 2 usage | 60 cloud unreachable (retry-safe)`
|
|
|
10260
10679
|
}
|
|
10261
10680
|
|
|
10262
10681
|
// src/cli/commands/init.ts
|
|
10263
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as
|
|
10264
|
-
import { join as
|
|
10682
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync11 } from "fs";
|
|
10683
|
+
import { join as join21 } from "path";
|
|
10265
10684
|
var DEFAULT_CONFIG2 = {};
|
|
10266
10685
|
var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
|
|
10267
10686
|
|
|
@@ -10290,19 +10709,19 @@ Effects
|
|
|
10290
10709
|
Exit codes: 0 ok`
|
|
10291
10710
|
).action((opts) => {
|
|
10292
10711
|
const cwd = process.cwd();
|
|
10293
|
-
const sisDir =
|
|
10294
|
-
const configPath =
|
|
10712
|
+
const sisDir = join21(cwd, ".sisyphus");
|
|
10713
|
+
const configPath = join21(sisDir, "config.json");
|
|
10295
10714
|
if (existsSync21(configPath)) {
|
|
10296
10715
|
console.log(`Already initialized: ${configPath}`);
|
|
10297
10716
|
return;
|
|
10298
10717
|
}
|
|
10299
10718
|
mkdirSync9(sisDir, { recursive: true });
|
|
10300
|
-
|
|
10719
|
+
writeFileSync11(configPath, JSON.stringify(DEFAULT_CONFIG2, null, 2) + "\n", "utf-8");
|
|
10301
10720
|
console.log(`Created ${configPath}`);
|
|
10302
10721
|
if (opts.orchestrator) {
|
|
10303
|
-
const orchPath =
|
|
10722
|
+
const orchPath = join21(sisDir, "orchestrator.md");
|
|
10304
10723
|
if (!existsSync21(orchPath)) {
|
|
10305
|
-
|
|
10724
|
+
writeFileSync11(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
|
|
10306
10725
|
console.log(`Created ${orchPath}`);
|
|
10307
10726
|
}
|
|
10308
10727
|
}
|
|
@@ -10362,7 +10781,7 @@ Exit codes: 0 ok`
|
|
|
10362
10781
|
|
|
10363
10782
|
// src/cli/commands/configure-upload.ts
|
|
10364
10783
|
init_paths();
|
|
10365
|
-
import { chmodSync as chmodSync2, existsSync as existsSync22, mkdirSync as mkdirSync10, readFileSync as
|
|
10784
|
+
import { chmodSync as chmodSync2, existsSync as existsSync22, mkdirSync as mkdirSync10, readFileSync as readFileSync24, writeFileSync as writeFileSync12 } from "fs";
|
|
10366
10785
|
import { createInterface as createInterface3 } from "readline";
|
|
10367
10786
|
import { dirname as dirname6 } from "path";
|
|
10368
10787
|
async function readUrlFromInput(interactive) {
|
|
@@ -10443,7 +10862,7 @@ Exit codes: 0 ok | 1 validation or write error`
|
|
|
10443
10862
|
let existing = {};
|
|
10444
10863
|
if (existsSync22(configPath)) {
|
|
10445
10864
|
try {
|
|
10446
|
-
existing = JSON.parse(
|
|
10865
|
+
existing = JSON.parse(readFileSync24(configPath, "utf-8"));
|
|
10447
10866
|
} catch {
|
|
10448
10867
|
console.error(`Error: ${configPath} could not be parsed \u2014 fix or delete it first`);
|
|
10449
10868
|
process.exit(1);
|
|
@@ -10451,7 +10870,7 @@ Exit codes: 0 ok | 1 validation or write error`
|
|
|
10451
10870
|
}
|
|
10452
10871
|
const merged = { ...existing, upload: { url, token } };
|
|
10453
10872
|
mkdirSync10(dirname6(configPath), { recursive: true });
|
|
10454
|
-
|
|
10873
|
+
writeFileSync12(configPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
10455
10874
|
chmodSync2(configPath, 384);
|
|
10456
10875
|
console.log(`\u2713 upload configured (${configPath})`);
|
|
10457
10876
|
});
|
|
@@ -10460,10 +10879,10 @@ Exit codes: 0 ok | 1 validation or write error`
|
|
|
10460
10879
|
// src/cli/commands/getting-started.ts
|
|
10461
10880
|
import { execSync as execSync15 } from "child_process";
|
|
10462
10881
|
import { existsSync as existsSync23 } from "fs";
|
|
10463
|
-
import { dirname as dirname7, join as
|
|
10882
|
+
import { dirname as dirname7, join as join22 } from "path";
|
|
10464
10883
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
10465
10884
|
function templatePath(name) {
|
|
10466
|
-
return
|
|
10885
|
+
return join22(dirname7(fileURLToPath3(import.meta.url)), "templates", name);
|
|
10467
10886
|
}
|
|
10468
10887
|
function isClaudeCode() {
|
|
10469
10888
|
return !!process.env["CLAUDECODE"];
|
|
@@ -10830,7 +11249,7 @@ sis ui guide --tutorial 4
|
|
|
10830
11249
|
`);
|
|
10831
11250
|
}
|
|
10832
11251
|
function printStep4() {
|
|
10833
|
-
const demoPath =
|
|
11252
|
+
const demoPath = join22(process.cwd(), "tmp", "sisyphus-tutorial-demo");
|
|
10834
11253
|
console.log(`
|
|
10835
11254
|
<claude-instructions>
|
|
10836
11255
|
# Sisyphus Getting Started \u2014 Step 4: Demo Session
|
|
@@ -10874,7 +11293,7 @@ Have the user confirm they understand these keybinds before proceeding.
|
|
|
10874
11293
|
|
|
10875
11294
|
First, copy the demo todo app into ./tmp/ under the user's current directory and init a git repo (sisyphus needs git). Run from the user's current shell \u2014 don't \`cd\` away first:
|
|
10876
11295
|
\`\`\`
|
|
10877
|
-
mkdir -p ${
|
|
11296
|
+
mkdir -p ${join22(process.cwd(), "tmp")}
|
|
10878
11297
|
rm -rf ${demoPath}
|
|
10879
11298
|
cp -r ${templatePath("tutorial-demo")} ${demoPath}
|
|
10880
11299
|
git -C ${demoPath} init
|
|
@@ -10952,7 +11371,7 @@ sis ui guide --tutorial 5
|
|
|
10952
11371
|
`);
|
|
10953
11372
|
}
|
|
10954
11373
|
function printStep5() {
|
|
10955
|
-
const demoPath =
|
|
11374
|
+
const demoPath = join22(process.cwd(), "tmp", "sisyphus-tutorial-demo");
|
|
10956
11375
|
const demoExists = existsSync23(demoPath);
|
|
10957
11376
|
let recentCommits = "";
|
|
10958
11377
|
let topLevelFiles = "";
|
|
@@ -11057,9 +11476,9 @@ If they say yes, run:
|
|
|
11057
11476
|
rm -rf ${demoPath}
|
|
11058
11477
|
\`\`\`
|
|
11059
11478
|
|
|
11060
|
-
Then check whether \`${
|
|
11479
|
+
Then check whether \`${join22(process.cwd(), "tmp")}\` is now empty, and if so remove it too:
|
|
11061
11480
|
\`\`\`
|
|
11062
|
-
rmdir ${
|
|
11481
|
+
rmdir ${join22(process.cwd(), "tmp")} 2>/dev/null || true
|
|
11063
11482
|
\`\`\`
|
|
11064
11483
|
|
|
11065
11484
|
If they say no or want to explore first, leave it. They can clean up later with the same
|
|
@@ -11492,7 +11911,7 @@ Exit codes: 0 ok | 2 usage (invalid --tutorial value)`
|
|
|
11492
11911
|
|
|
11493
11912
|
// src/cli/commands/history.ts
|
|
11494
11913
|
init_paths();
|
|
11495
|
-
import { readdirSync as readdirSync7, readFileSync as
|
|
11914
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync25, existsSync as existsSync24 } from "fs";
|
|
11496
11915
|
import { resolve as resolve8 } from "path";
|
|
11497
11916
|
function loadAllSummaries() {
|
|
11498
11917
|
const base = historyBaseDir();
|
|
@@ -11502,7 +11921,7 @@ function loadAllSummaries() {
|
|
|
11502
11921
|
const summaryPath = historySessionSummaryPath(name);
|
|
11503
11922
|
if (existsSync24(summaryPath)) {
|
|
11504
11923
|
try {
|
|
11505
|
-
const raw =
|
|
11924
|
+
const raw = readFileSync25(summaryPath, "utf-8");
|
|
11506
11925
|
results.push({ id: name, summary: JSON.parse(raw) });
|
|
11507
11926
|
continue;
|
|
11508
11927
|
} catch {
|
|
@@ -11519,7 +11938,7 @@ function buildLiveSummary(sessionId) {
|
|
|
11519
11938
|
if (!existsSync24(eventsPath)) return null;
|
|
11520
11939
|
let cwd = null;
|
|
11521
11940
|
try {
|
|
11522
|
-
const lines =
|
|
11941
|
+
const lines = readFileSync25(eventsPath, "utf-8").split("\n");
|
|
11523
11942
|
for (const line of lines) {
|
|
11524
11943
|
if (!line.trim()) continue;
|
|
11525
11944
|
try {
|
|
@@ -11540,7 +11959,7 @@ function buildLiveSummary(sessionId) {
|
|
|
11540
11959
|
if (!existsSync24(sPath)) return null;
|
|
11541
11960
|
let session2;
|
|
11542
11961
|
try {
|
|
11543
|
-
session2 = JSON.parse(
|
|
11962
|
+
session2 = JSON.parse(readFileSync25(sPath, "utf-8"));
|
|
11544
11963
|
} catch {
|
|
11545
11964
|
return null;
|
|
11546
11965
|
}
|
|
@@ -11602,7 +12021,7 @@ function buildLiveSummary(sessionId) {
|
|
|
11602
12021
|
function loadEvents(sessionId) {
|
|
11603
12022
|
const eventsPath = historyEventsPath(sessionId);
|
|
11604
12023
|
if (!existsSync24(eventsPath)) return [];
|
|
11605
|
-
const lines =
|
|
12024
|
+
const lines = readFileSync25(eventsPath, "utf-8").split("\n").filter((l) => l.trim());
|
|
11606
12025
|
const events = [];
|
|
11607
12026
|
for (const line of lines) {
|
|
11608
12027
|
try {
|
|
@@ -11617,7 +12036,7 @@ function findSession(idOrName) {
|
|
|
11617
12036
|
const summaryPath = historySessionSummaryPath(idOrName);
|
|
11618
12037
|
if (existsSync24(summaryPath)) {
|
|
11619
12038
|
try {
|
|
11620
|
-
return { id: idOrName, summary: JSON.parse(
|
|
12039
|
+
return { id: idOrName, summary: JSON.parse(readFileSync25(summaryPath, "utf-8")) };
|
|
11621
12040
|
} catch {
|
|
11622
12041
|
}
|
|
11623
12042
|
}
|
|
@@ -11865,7 +12284,7 @@ Exit codes: 0 ok | 2 usage | 1 export_failed.`).action(async (sessionIdArg, opts
|
|
|
11865
12284
|
|
|
11866
12285
|
// src/cli/commands/upload.ts
|
|
11867
12286
|
import { rmSync as rmSync6 } from "fs";
|
|
11868
|
-
import { tmpdir } from "os";
|
|
12287
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
11869
12288
|
init_config();
|
|
11870
12289
|
init_session_export();
|
|
11871
12290
|
init_upload();
|
|
@@ -11921,7 +12340,7 @@ Exit codes: 0 ok | 1 upload or export error | 2 usage`
|
|
|
11921
12340
|
}
|
|
11922
12341
|
let zipPath;
|
|
11923
12342
|
try {
|
|
11924
|
-
zipPath = await exportSessionToZip(sessionId, cwd, { reveal: false, outputDir:
|
|
12343
|
+
zipPath = await exportSessionToZip(sessionId, cwd, { reveal: false, outputDir: tmpdir2() });
|
|
11925
12344
|
} catch (err) {
|
|
11926
12345
|
exitError({
|
|
11927
12346
|
code: "export_failed",
|
|
@@ -11986,7 +12405,7 @@ Exit codes: 0 ok | 1 upload or export error | 2 usage`
|
|
|
11986
12405
|
// src/cli/commands/scratch.ts
|
|
11987
12406
|
import { execSync as execSync16 } from "child_process";
|
|
11988
12407
|
init_shell();
|
|
11989
|
-
function
|
|
12408
|
+
function findHomeSession2(cwd) {
|
|
11990
12409
|
const normalizedCwd = cwd.replace(/\/+$/, "");
|
|
11991
12410
|
let output;
|
|
11992
12411
|
try {
|
|
@@ -12034,7 +12453,7 @@ Effects
|
|
|
12034
12453
|
Exit codes: 0 ok | 2 usage.`).action((promptParts, opts) => {
|
|
12035
12454
|
assertTmux();
|
|
12036
12455
|
const cwd = opts.cwd ?? process.env["SISYPHUS_CWD"] ?? process.cwd();
|
|
12037
|
-
const homeSession =
|
|
12456
|
+
const homeSession = findHomeSession2(cwd);
|
|
12038
12457
|
if (!homeSession) {
|
|
12039
12458
|
const current = execSync16('tmux display-message -p "#{session_name}"', {
|
|
12040
12459
|
encoding: "utf-8"
|
|
@@ -12062,15 +12481,15 @@ function openScratchWindow(tmuxSession, cwd, prompt) {
|
|
|
12062
12481
|
|
|
12063
12482
|
// src/cli/commands/review.ts
|
|
12064
12483
|
init_paths();
|
|
12065
|
-
import { join as
|
|
12066
|
-
import { existsSync as existsSync26, readFileSync as
|
|
12484
|
+
import { join as join24, resolve as resolve9, dirname as dirname8 } from "path";
|
|
12485
|
+
import { existsSync as existsSync26, readFileSync as readFileSync27, writeFileSync as writeFileSync14, renameSync as renameSync3, readdirSync as readdirSync8 } from "fs";
|
|
12067
12486
|
var _statusCheck = ["draft", "question", "approved", "rejected", "deferred"];
|
|
12068
12487
|
function resolveContextArtifact(file, opts, filename, notFoundMessage) {
|
|
12069
12488
|
const cwd = opts.cwd || process.env.SISYPHUS_CWD || process.cwd();
|
|
12070
12489
|
if (file) return resolve9(file);
|
|
12071
12490
|
const sessionId = opts.sessionId || process.env.SISYPHUS_SESSION_ID;
|
|
12072
12491
|
if (sessionId) {
|
|
12073
|
-
const target =
|
|
12492
|
+
const target = join24(contextDir(cwd, sessionId), filename);
|
|
12074
12493
|
if (!existsSync26(target)) {
|
|
12075
12494
|
exitUsage("file-not-found", `File not found: ${target}`, { received: target });
|
|
12076
12495
|
}
|
|
@@ -12080,7 +12499,7 @@ function resolveContextArtifact(file, opts, filename, notFoundMessage) {
|
|
|
12080
12499
|
if (existsSync26(dir)) {
|
|
12081
12500
|
const sessions = readdirSync8(dir);
|
|
12082
12501
|
for (const session2 of sessions.reverse()) {
|
|
12083
|
-
const candidate =
|
|
12502
|
+
const candidate = join24(dir, session2, "context", filename);
|
|
12084
12503
|
if (existsSync26(candidate)) return candidate;
|
|
12085
12504
|
}
|
|
12086
12505
|
}
|
|
@@ -12139,12 +12558,12 @@ Exit codes: 0 ok | 2 usage.`).action(async (file, opts) => {
|
|
|
12139
12558
|
if (!existsSync26(targetPath)) {
|
|
12140
12559
|
exitUsage("file-not-found", `File not found: ${targetPath}`, { received: targetPath });
|
|
12141
12560
|
}
|
|
12142
|
-
const parsed = JSON.parse(
|
|
12561
|
+
const parsed = JSON.parse(readFileSync27(targetPath, "utf-8"));
|
|
12143
12562
|
const rendered = renderRequirementsMarkdown(parsed);
|
|
12144
|
-
const outPath =
|
|
12563
|
+
const outPath = join24(dirname8(targetPath), "requirements.md");
|
|
12145
12564
|
const tmpPath = outPath + ".tmp";
|
|
12146
12565
|
if (existsSync26(outPath)) {
|
|
12147
|
-
const existing =
|
|
12566
|
+
const existing = readFileSync27(outPath, "utf-8");
|
|
12148
12567
|
if (existing !== rendered) {
|
|
12149
12568
|
if (!opts.force) {
|
|
12150
12569
|
exitUsage("conflict", `${outPath} has been hand-edited (differs from rendered output)`, {
|
|
@@ -12157,7 +12576,7 @@ Exit codes: 0 ok | 2 usage.`).action(async (file, opts) => {
|
|
|
12157
12576
|
`);
|
|
12158
12577
|
}
|
|
12159
12578
|
}
|
|
12160
|
-
|
|
12579
|
+
writeFileSync14(tmpPath, rendered, "utf-8");
|
|
12161
12580
|
renameSync3(tmpPath, outPath);
|
|
12162
12581
|
emitJsonOk({ path: resolve9(outPath) });
|
|
12163
12582
|
return;
|
|
@@ -12521,8 +12940,8 @@ function renderRequirementsMarkdown(json) {
|
|
|
12521
12940
|
}
|
|
12522
12941
|
|
|
12523
12942
|
// src/cli/commands/companion.ts
|
|
12524
|
-
import { basename as basename7, dirname as dirname11, join as
|
|
12525
|
-
import { mkdirSync as mkdirSync15, readFileSync as
|
|
12943
|
+
import { basename as basename7, dirname as dirname11, join as join29 } from "path";
|
|
12944
|
+
import { mkdirSync as mkdirSync15, readFileSync as readFileSync32, writeFileSync as writeFileSync19 } from "fs";
|
|
12526
12945
|
init_paths();
|
|
12527
12946
|
init_companion_types();
|
|
12528
12947
|
init_companion_memory();
|
|
@@ -12625,10 +13044,10 @@ Effects
|
|
|
12625
13044
|
Writes/updates ~/.sisyphus/companion-context-cache/<session-id>.json on each call that produces output.
|
|
12626
13045
|
|
|
12627
13046
|
Exit codes: 0 ok.`).action((opts) => {
|
|
12628
|
-
const cachePath =
|
|
13047
|
+
const cachePath = join29(globalDir(), "companion-context-cache", `${opts.sessionId}.json`);
|
|
12629
13048
|
let prev = {};
|
|
12630
13049
|
try {
|
|
12631
|
-
prev = JSON.parse(
|
|
13050
|
+
prev = JSON.parse(readFileSync32(cachePath, "utf-8"));
|
|
12632
13051
|
} catch {
|
|
12633
13052
|
prev = {};
|
|
12634
13053
|
}
|
|
@@ -12642,7 +13061,7 @@ Exit codes: 0 ok.`).action((opts) => {
|
|
|
12642
13061
|
process.stdout.write(renderFullContext(next));
|
|
12643
13062
|
}
|
|
12644
13063
|
mkdirSync15(dirname11(cachePath), { recursive: true });
|
|
12645
|
-
|
|
13064
|
+
writeFileSync19(cachePath, JSON.stringify(next), "utf-8");
|
|
12646
13065
|
});
|
|
12647
13066
|
companion.command("pane").description("Open (or focus) a side claude pane next to the dashboard").option("--cwd <path>", "Project directory (default: current directory)").addHelpText("after", `
|
|
12648
13067
|
companion pane: open (or focus) the companion side pane next to the dashboard.
|
|
@@ -12746,7 +13165,7 @@ Exit codes: 0 ok | 3 not_found.`
|
|
|
12746
13165
|
}));
|
|
12747
13166
|
emitJsonOk({ ...companionData, badges });
|
|
12748
13167
|
} else {
|
|
12749
|
-
emitJsonOk(companionData);
|
|
13168
|
+
emitJsonOk({ ...companionData });
|
|
12750
13169
|
}
|
|
12751
13170
|
});
|
|
12752
13171
|
}
|
|
@@ -12756,7 +13175,7 @@ init_runner();
|
|
|
12756
13175
|
init_creds();
|
|
12757
13176
|
init_tailscale();
|
|
12758
13177
|
import { homedir as homedir13 } from "os";
|
|
12759
|
-
import { join as
|
|
13178
|
+
import { join as join30 } from "path";
|
|
12760
13179
|
function assertArch(raw) {
|
|
12761
13180
|
if (raw === "arm" || raw === "x86") return raw;
|
|
12762
13181
|
throw new Error(`Invalid --arch: ${raw}. Must be 'arm' or 'x86'.`);
|
|
@@ -12802,7 +13221,7 @@ Exit codes: 0 ok | 1 error (bad credentials or API failure).`).action(async () =
|
|
|
12802
13221
|
const sub = deploy.command(provider).description(`${provider} commands.`).addHelpText("before", `
|
|
12803
13222
|
deploy ${provider}: per-provider box lifecycle. up | down | status | ssh | logs | update.
|
|
12804
13223
|
`);
|
|
12805
|
-
sub.command("up").description(`Provision the ${provider} box (terraform init \u2192 plan \u2192 apply).`).option("--region <region>", `Provider region (defaults: hetzner=nbg1, aws=us-east-1).`).option("--arch <arch>", "'arm' (default) or 'x86'. Picks the default --size and image.", "arm").option("--size <size>", "Instance type override (defaults follow --arch).").option("--ssh-key <path>", "Path to SSH public key.",
|
|
13224
|
+
sub.command("up").description(`Provision the ${provider} box (terraform init \u2192 plan \u2192 apply).`).option("--region <region>", `Provider region (defaults: hetzner=nbg1, aws=us-east-1).`).option("--arch <arch>", "'arm' (default) or 'x86'. Picks the default --size and image.", "arm").option("--size <size>", "Instance type override (defaults follow --arch).").option("--ssh-key <path>", "Path to SSH public key.", join30(homedir13(), ".ssh", "id_ed25519.pub")).option("--no-chromium", "Skip headless Chromium install.").option("--no-auto-update", "Skip the daily auto-update systemd timer.").option("--name <name>", "Box hostname / Tailscale node name.", "sisyphus").option("--yes", "Skip the re-provision confirmation prompt when state already exists.").addHelpText("after", `
|
|
12806
13225
|
deploy ${provider} up: provision the ${provider} box via Terraform (init \u2192 plan \u2192 apply).
|
|
12807
13226
|
|
|
12808
13227
|
Input
|
|
@@ -12949,14 +13368,14 @@ init_runner2();
|
|
|
12949
13368
|
|
|
12950
13369
|
// src/cli/cloud/handoff.ts
|
|
12951
13370
|
import { spawn as spawn5 } from "child_process";
|
|
12952
|
-
import { existsSync as existsSync36, readFileSync as
|
|
13371
|
+
import { existsSync as existsSync36, readFileSync as readFileSync36, writeFileSync as writeFileSync20 } from "fs";
|
|
12953
13372
|
init_exec();
|
|
12954
13373
|
init_paths();
|
|
12955
13374
|
init_shell();
|
|
12956
13375
|
init_runner();
|
|
12957
13376
|
init_ssh_exec();
|
|
12958
13377
|
init_provider_pick();
|
|
12959
|
-
import { join as
|
|
13378
|
+
import { join as join32 } from "path";
|
|
12960
13379
|
async function cloudHandoff(sessionId, opts) {
|
|
12961
13380
|
const cwd = process.env["SISYPHUS_CWD"] ?? process.cwd();
|
|
12962
13381
|
const request = {
|
|
@@ -13001,7 +13420,7 @@ async function waitForSentOrError(cwd, sessionId) {
|
|
|
13001
13420
|
if (!existsSync36(path)) continue;
|
|
13002
13421
|
let session2;
|
|
13003
13422
|
try {
|
|
13004
|
-
session2 = JSON.parse(
|
|
13423
|
+
session2 = JSON.parse(readFileSync36(path, "utf-8"));
|
|
13005
13424
|
} catch (err) {
|
|
13006
13425
|
void err;
|
|
13007
13426
|
continue;
|
|
@@ -13082,16 +13501,16 @@ async function cloudReclaim(sessionId, opts) {
|
|
|
13082
13501
|
await rsyncDown(target, `${remoteRepoDir}/`, `${cwd}/`, { withDelete: false, excludeSisyphus: true, update: true });
|
|
13083
13502
|
for (const name of ["config.json", "orchestrator.md", "orchestrator-settings.json"]) {
|
|
13084
13503
|
const remotePath = `${remoteRepoDir}/.sisyphus/${name}`;
|
|
13085
|
-
const localPath =
|
|
13504
|
+
const localPath = join32(projectDir(cwd), name);
|
|
13086
13505
|
const probe = runOnBox(provider, `test -f ${shellQuote(remotePath.replace(/^~\//, ""))} && echo y || echo n`);
|
|
13087
13506
|
if (probe.stdout.trim() !== "y") continue;
|
|
13088
13507
|
await rsyncDown(target, remotePath, localPath, { withDelete: false });
|
|
13089
13508
|
}
|
|
13090
13509
|
const localStatePath = statePath(cwd, sessionId);
|
|
13091
|
-
const merged = JSON.parse(
|
|
13510
|
+
const merged = JSON.parse(readFileSync36(localStatePath, "utf-8"));
|
|
13092
13511
|
merged.cwd = cwd;
|
|
13093
13512
|
merged.handoff = local.handoff;
|
|
13094
|
-
|
|
13513
|
+
writeFileSync20(localStatePath, JSON.stringify(merged, null, 2));
|
|
13095
13514
|
const reclaimMessage = `Session reclaimed from cloud (${provider}:${repo}). Resuming locally.`;
|
|
13096
13515
|
console.log(`\u2192 local sis resume`);
|
|
13097
13516
|
const resumeResp = await sendRequest({
|
|
@@ -13124,7 +13543,7 @@ function readLocalSession(cwd, sessionId) {
|
|
|
13124
13543
|
received: sessionId
|
|
13125
13544
|
});
|
|
13126
13545
|
}
|
|
13127
|
-
return JSON.parse(
|
|
13546
|
+
return JSON.parse(readFileSync36(path, "utf-8"));
|
|
13128
13547
|
}
|
|
13129
13548
|
async function waitForBoxPaused(provider, remoteSessionDir) {
|
|
13130
13549
|
const POLL_INTERVAL_MS = 2e3;
|
|
@@ -13408,7 +13827,7 @@ function attachNotify(diagnostic2) {
|
|
|
13408
13827
|
// src/cli/commands/tmux-sessions.ts
|
|
13409
13828
|
init_paths();
|
|
13410
13829
|
import { execSync as execSync18 } from "child_process";
|
|
13411
|
-
import { readFileSync as
|
|
13830
|
+
import { readFileSync as readFileSync37, existsSync as existsSync37 } from "fs";
|
|
13412
13831
|
var DOT_MAP = {
|
|
13413
13832
|
"orchestrator:processing": { icon: "\u25CF", color: "#d4ad6a" },
|
|
13414
13833
|
"orchestrator:idle": { icon: "\u25CF", color: "#d47766" },
|
|
@@ -13421,7 +13840,7 @@ function readManifest() {
|
|
|
13421
13840
|
const p = sessionsManifestPath();
|
|
13422
13841
|
if (!existsSync37(p)) return null;
|
|
13423
13842
|
try {
|
|
13424
|
-
return JSON.parse(
|
|
13843
|
+
return JSON.parse(readFileSync37(p, "utf-8"));
|
|
13425
13844
|
} catch {
|
|
13426
13845
|
return null;
|
|
13427
13846
|
}
|
|
@@ -13462,7 +13881,7 @@ init_server();
|
|
|
13462
13881
|
init_ask_store();
|
|
13463
13882
|
init_state();
|
|
13464
13883
|
init_paths();
|
|
13465
|
-
import { existsSync as
|
|
13884
|
+
import { existsSync as existsSync48 } from "fs";
|
|
13466
13885
|
var HEARTBEAT_ASKED_BY2 = "system:heartbeat";
|
|
13467
13886
|
var ORPHAN_ASKED_BY2 = "system:orphan-handler";
|
|
13468
13887
|
function isHeartbeatZombie(cwd, sessionId, askId) {
|
|
@@ -13470,14 +13889,14 @@ function isHeartbeatZombie(cwd, sessionId, askId) {
|
|
|
13470
13889
|
if (!meta) return false;
|
|
13471
13890
|
if (meta.askedBy !== HEARTBEAT_ASKED_BY2) return false;
|
|
13472
13891
|
if (meta.status === "answered") return false;
|
|
13473
|
-
if (
|
|
13892
|
+
if (existsSync48(askOutputPath(cwd, sessionId, askId))) return false;
|
|
13474
13893
|
for (const candidateId of listAsks(cwd, sessionId)) {
|
|
13475
13894
|
if (candidateId === askId) continue;
|
|
13476
13895
|
const candidateMeta = readMeta(cwd, sessionId, candidateId);
|
|
13477
13896
|
if (!candidateMeta) continue;
|
|
13478
13897
|
if (candidateMeta.heartbeatAskId !== askId) continue;
|
|
13479
13898
|
if (candidateMeta.status === "answered") return true;
|
|
13480
|
-
if (
|
|
13899
|
+
if (existsSync48(askOutputPath(cwd, sessionId, candidateId))) return true;
|
|
13481
13900
|
}
|
|
13482
13901
|
return false;
|
|
13483
13902
|
}
|
|
@@ -13486,7 +13905,7 @@ function isOrphanZombie(cwd, sessionId, askId) {
|
|
|
13486
13905
|
if (!meta) return false;
|
|
13487
13906
|
if (meta.askedBy !== ORPHAN_ASKED_BY2) return false;
|
|
13488
13907
|
if (meta.status === "answered") return false;
|
|
13489
|
-
if (
|
|
13908
|
+
if (existsSync48(askOutputPath(cwd, sessionId, askId))) return false;
|
|
13490
13909
|
if (meta.orphanTarget?.kind !== "agent") return false;
|
|
13491
13910
|
let session2;
|
|
13492
13911
|
try {
|
|
@@ -13499,27 +13918,6 @@ function isOrphanZombie(cwd, sessionId, askId) {
|
|
|
13499
13918
|
if (!agent2) return false;
|
|
13500
13919
|
return agent2.status !== "running";
|
|
13501
13920
|
}
|
|
13502
|
-
function isModeGateZombie(cwd, sessionId, askId) {
|
|
13503
|
-
const meta = readMeta(cwd, sessionId, askId);
|
|
13504
|
-
if (!meta) return false;
|
|
13505
|
-
if (meta.modeTransition !== true) return false;
|
|
13506
|
-
if (meta.status === "answered") return false;
|
|
13507
|
-
if (existsSync49(askOutputPath(cwd, sessionId, askId))) return false;
|
|
13508
|
-
const deck = readDecisions(cwd, sessionId, askId);
|
|
13509
|
-
if (!deck) return false;
|
|
13510
|
-
const source = deck.source;
|
|
13511
|
-
const chain = source?.modeChain;
|
|
13512
|
-
if (!chain || chain.length === 0) return false;
|
|
13513
|
-
let currentMode;
|
|
13514
|
-
try {
|
|
13515
|
-
const session2 = getSession(cwd, sessionId);
|
|
13516
|
-
currentMode = session2.orchestratorCycles[session2.orchestratorCycles.length - 1]?.mode;
|
|
13517
|
-
} catch {
|
|
13518
|
-
return false;
|
|
13519
|
-
}
|
|
13520
|
-
if (!currentMode) return false;
|
|
13521
|
-
return !chain.some((e) => e.mode === currentMode);
|
|
13522
|
-
}
|
|
13523
13921
|
async function resolveZombie(cwd, sessionId, entry) {
|
|
13524
13922
|
const { askId, kind } = entry;
|
|
13525
13923
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -13537,11 +13935,6 @@ async function resolveZombie(cwd, sessionId, entry) {
|
|
|
13537
13935
|
selectedOptionId = "dismiss";
|
|
13538
13936
|
freetext = "auto-resolved: agent superseded by replacement (clean-zombies sweep)";
|
|
13539
13937
|
break;
|
|
13540
|
-
case "mode-gate":
|
|
13541
|
-
interactionId = "mode-transition";
|
|
13542
|
-
selectedOptionId = "ack";
|
|
13543
|
-
freetext = "auto-resolved: session advanced past mode-transition (clean-zombies sweep)";
|
|
13544
|
-
break;
|
|
13545
13938
|
}
|
|
13546
13939
|
writeOutput(cwd, sessionId, askId, [{
|
|
13547
13940
|
id: interactionId,
|
|
@@ -13560,14 +13953,12 @@ async function sweepSession(cwd, sessionId) {
|
|
|
13560
13953
|
const meta = readMeta(cwd, sessionId, askId);
|
|
13561
13954
|
const agentId = meta?.orphanTarget?.kind === "agent" ? meta.orphanTarget.agentId : "unknown";
|
|
13562
13955
|
zombies.push({ sessionId, askId, kind: "orphan", reason: `agent ${agentId} is no longer running` });
|
|
13563
|
-
} else if (isModeGateZombie(cwd, sessionId, askId)) {
|
|
13564
|
-
zombies.push({ sessionId, askId, kind: "mode-gate", reason: "session advanced past mode-transition" });
|
|
13565
13956
|
}
|
|
13566
13957
|
}
|
|
13567
13958
|
return zombies;
|
|
13568
13959
|
}
|
|
13569
13960
|
function registerCleanZombies(program2) {
|
|
13570
|
-
program2.command("clean-zombies").description("Sweep all sessions for zombie asks (heartbeats whose original is answered, orphans whose agent is superseded
|
|
13961
|
+
program2.command("clean-zombies").description("Sweep all sessions for zombie asks (heartbeats whose original is answered, orphans whose agent is superseded) and dismiss them").addHelpText(
|
|
13571
13962
|
"after",
|
|
13572
13963
|
`
|
|
13573
13964
|
clean-zombies: sweep all sessions and dismiss stale ask records.
|
|
@@ -13580,8 +13971,8 @@ Output (stdout, plain text \u2014 human-readable maintenance summary; not for ag
|
|
|
13580
13971
|
|
|
13581
13972
|
Effects
|
|
13582
13973
|
Mutates ask records: writes response.json and updates status to "answered" for each
|
|
13583
|
-
zombie found. Dismisses heartbeat asks whose original was answered
|
|
13584
|
-
target agent is no longer running
|
|
13974
|
+
zombie found. Dismisses heartbeat asks whose original was answered and orphan asks whose
|
|
13975
|
+
target agent is no longer running.
|
|
13585
13976
|
|
|
13586
13977
|
Exit codes: 0 ok`
|
|
13587
13978
|
).action(async () => {
|
|
@@ -13594,7 +13985,7 @@ Exit codes: 0 ok`
|
|
|
13594
13985
|
const allZombies = [];
|
|
13595
13986
|
const sessionsSweept = /* @__PURE__ */ new Set();
|
|
13596
13987
|
for (const [sessionId, cwd] of entries) {
|
|
13597
|
-
if (!
|
|
13988
|
+
if (!existsSync48(statePath(cwd, sessionId))) continue;
|
|
13598
13989
|
try {
|
|
13599
13990
|
const zombies = await sweepSession(cwd, sessionId);
|
|
13600
13991
|
if (zombies.length > 0) {
|
|
@@ -13610,8 +14001,7 @@ Exit codes: 0 ok`
|
|
|
13610
14001
|
}
|
|
13611
14002
|
const summary = {
|
|
13612
14003
|
heartbeats: allZombies.filter((z4) => z4.kind === "heartbeat").length,
|
|
13613
|
-
orphans: allZombies.filter((z4) => z4.kind === "orphan").length
|
|
13614
|
-
modeGates: allZombies.filter((z4) => z4.kind === "mode-gate").length
|
|
14004
|
+
orphans: allZombies.filter((z4) => z4.kind === "orphan").length
|
|
13615
14005
|
};
|
|
13616
14006
|
const total = allZombies.length;
|
|
13617
14007
|
if (total === 0) {
|
|
@@ -13619,7 +14009,7 @@ Exit codes: 0 ok`
|
|
|
13619
14009
|
return;
|
|
13620
14010
|
}
|
|
13621
14011
|
for (const [sessionId, cwd] of entries) {
|
|
13622
|
-
if (!
|
|
14012
|
+
if (!existsSync48(statePath(cwd, sessionId))) continue;
|
|
13623
14013
|
const sessionZombies = allZombies.filter((z4) => z4.sessionId === sessionId);
|
|
13624
14014
|
for (const zombie of sessionZombies) {
|
|
13625
14015
|
try {
|
|
@@ -13632,153 +14022,13 @@ Exit codes: 0 ok`
|
|
|
13632
14022
|
}
|
|
13633
14023
|
}
|
|
13634
14024
|
}
|
|
13635
|
-
console.log(`Dismissed ${total} zombie${total === 1 ? "" : "s"} across ${sessionsSweept.size} session${sessionsSweept.size === 1 ? "" : "s"}: ${summary.heartbeats} heartbeat${summary.heartbeats === 1 ? "" : "s"}, ${summary.orphans} orphan${summary.orphans === 1 ? "" : "s"}
|
|
14025
|
+
console.log(`Dismissed ${total} zombie${total === 1 ? "" : "s"} across ${sessionsSweept.size} session${sessionsSweept.size === 1 ? "" : "s"}: ${summary.heartbeats} heartbeat${summary.heartbeats === 1 ? "" : "s"}, ${summary.orphans} orphan${summary.orphans === 1 ? "" : "s"}.`);
|
|
13636
14026
|
});
|
|
13637
14027
|
}
|
|
13638
14028
|
|
|
13639
14029
|
// src/cli/index.ts
|
|
13640
14030
|
init_paths();
|
|
13641
|
-
|
|
13642
|
-
// src/cli/help-rubric.ts
|
|
13643
|
-
function commandPath(cmd) {
|
|
13644
|
-
const parts = [];
|
|
13645
|
-
let cur = cmd;
|
|
13646
|
-
while (cur !== null) {
|
|
13647
|
-
parts.push(cur.name());
|
|
13648
|
-
cur = cur.parent;
|
|
13649
|
-
}
|
|
13650
|
-
parts.reverse();
|
|
13651
|
-
parts.shift();
|
|
13652
|
-
return parts.join(" ");
|
|
13653
|
-
}
|
|
13654
|
-
var RUBRICS = {
|
|
13655
|
-
"session": { short: "Manage tracked orchestration sessions", useWhen: "acting on a unit of orchestrated work" },
|
|
13656
|
-
"agent": { short: "Operate on worker agents", useWhen: "directing or reading a spawned agent" },
|
|
13657
|
-
"orch": { short: "Talk to / steer the orchestrator", useWhen: "guiding the session's orchestrator" },
|
|
13658
|
-
"ask": { short: "Human-in-the-loop question I/O", useWhen: "answering or polling a blocking ask" },
|
|
13659
|
-
"ui": { short: "Interactive surfaces", useWhen: "a human wants the dashboard, guide, or scratch" },
|
|
13660
|
-
"segment": { short: "Status-line segment registration", useWhen: "wiring tmux status indicators" },
|
|
13661
|
-
"admin": { short: "Install, verify, and report", useWhen: "setting up or diagnosing the install" },
|
|
13662
|
-
"companion": { short: "Companion-pane helper", useWhen: "driving the companion Claude pane" },
|
|
13663
|
-
"deploy": { short: "Provision cloud boxes (Terraform)", useWhen: "standing up or tearing down infra" },
|
|
13664
|
-
"cloud": { short: "Per-repo workflow on a deployed box", useWhen: "syncing work to/from the box" },
|
|
13665
|
-
"feedback": { short: "Report a problem with sisyphus itself", useWhen: "the user complains about the tool or workflow" },
|
|
13666
|
-
"session lifecycle": { short: "Start/stop/advance a session", useWhen: "changing whether a session runs" },
|
|
13667
|
-
"session inspect": { short: "Read session state", useWhen: "you need status/history/context without mutating" },
|
|
13668
|
-
"session config": { short: "Change a session's settings", useWhen: "adjusting task, effort, or dangerous mode" },
|
|
13669
|
-
"session recover": { short: "Repair or relocate a session", useWhen: "a session is stuck, lost, or needs rollback" },
|
|
13670
|
-
"session scratch": { short: "Open a standalone (non-sisyphus) Claude", useWhen: "you want a throwaway Claude in this repo" },
|
|
13671
|
-
"session lifecycle start": { short: "Start a new session", useWhen: "beginning work on a new task" },
|
|
13672
|
-
"session lifecycle complete": { short: "Mark the current cycle complete", useWhen: "the orchestrator finished its work" },
|
|
13673
|
-
"session lifecycle continue": { short: "Continue past completion into a new cycle", useWhen: "more work follows completion" },
|
|
13674
|
-
"session lifecycle resume": { short: "Resume a paused/handed-off session", useWhen: "bringing a stopped session back" },
|
|
13675
|
-
"session lifecycle kill": { short: "Stop a session, keep its state", useWhen: "halting work but preserving the record" },
|
|
13676
|
-
"session lifecycle delete": { short: "Remove a session and its state", useWhen: "discarding a session permanently" },
|
|
13677
|
-
"session inspect status": { short: "Show live session status", useWhen: "checking what is running now" },
|
|
13678
|
-
"session inspect list": { short: "List sessions", useWhen: "enumerating sessions in this repo" },
|
|
13679
|
-
"session inspect history": { short: "Show past sessions/cycles", useWhen: "reviewing prior runs" },
|
|
13680
|
-
"session inspect context": { short: "Print the orchestrator's context", useWhen: "auditing what the orchestrator sees" },
|
|
13681
|
-
"session inspect export": { short: "Export session transcript/state", useWhen: "archiving or sharing a session" },
|
|
13682
|
-
"session inspect requirements": { short: "Show/export the session requirements", useWhen: "reviewing the locked requirements doc" },
|
|
13683
|
-
"session config task": { short: "Set the session's task text", useWhen: "retargeting what the session works on" },
|
|
13684
|
-
"session config effort": { short: "Set the effort tier", useWhen: "tuning model effort for a session" },
|
|
13685
|
-
"session config dangerous": { short: "Toggle dangerous (skip-permissions) mode", useWhen: "allowing unattended tool use" },
|
|
13686
|
-
"session recover rollback": { short: "Roll back to an earlier cycle", useWhen: "a cycle went wrong" },
|
|
13687
|
-
"session recover reconnect": { short: "Reattach the daemon to a live session", useWhen: "the tmux/daemon link dropped" },
|
|
13688
|
-
"session recover quiesce": { short: "Pause a session at the next safe point", useWhen: "you need it to stop cleanly" },
|
|
13689
|
-
"session recover clone": { short: "Clone a session toward a new goal", useWhen: "forking work from an existing session" },
|
|
13690
|
-
"agent spawn": { short: "Spawn a worker agent", useWhen: "delegating a scoped sub-task" },
|
|
13691
|
-
"agent submit": { short: "Submit this agent's result upstream", useWhen: "from inside an agent, its work is done" },
|
|
13692
|
-
"agent report": { short: "Post a progress report", useWhen: "from inside an agent, updating mid-task" },
|
|
13693
|
-
"agent await": { short: "Block until a spawned agent submits", useWhen: "you need an agent's result before continuing" },
|
|
13694
|
-
"agent ctl": { short: "Agent process control", useWhen: "killing or restarting an agent process" },
|
|
13695
|
-
"agent io": { short: "Agent message I/O", useWhen: "sending text to or reading an agent" },
|
|
13696
|
-
"agent ctl kill": { short: "Kill an agent", useWhen: "an agent must stop immediately" },
|
|
13697
|
-
"agent ctl restart": { short: "Restart an agent", useWhen: "an agent is wedged and should start over" },
|
|
13698
|
-
"agent io tell": { short: "Send text to an agent", useWhen: "injecting instructions into a running agent" },
|
|
13699
|
-
"agent io read": { short: "Read an agent's transcript", useWhen: "inspecting what an agent produced" },
|
|
13700
|
-
"orch yield": { short: "Yield control back to the human/parent", useWhen: "the orchestrator is done or blocked" },
|
|
13701
|
-
"orch tell": { short: "Send text to the orchestrator", useWhen: "injecting guidance into the orchestrator" },
|
|
13702
|
-
"orch message": { short: "Queue a message for the orchestrator", useWhen: "leaving async input for the next turn" },
|
|
13703
|
-
"orch read": { short: "Read the orchestrator's transcript", useWhen: "inspecting orchestrator output" },
|
|
13704
|
-
"ask submit": { short: "Submit an ask deck for a human", useWhen: "an agent needs a human decision" },
|
|
13705
|
-
"ask poll": { short: "Block until an ask is answered", useWhen: "you need the human's answer before continuing" },
|
|
13706
|
-
"ask peek": { short: "Read an ask's state without blocking", useWhen: "checking ask status non-blocking" },
|
|
13707
|
-
"ui dashboard": { short: "Open the TUI dashboard", useWhen: "a human wants the monitoring UI" },
|
|
13708
|
-
"ui guide": { short: "Print the full usage guide", useWhen: "a human needs end-to-end docs" },
|
|
13709
|
-
"segment register": { short: "Register a status-line segment", useWhen: "adding a tmux status indicator" },
|
|
13710
|
-
"segment unregister": { short: "Remove a status-line segment", useWhen: "tearing one down" },
|
|
13711
|
-
"admin install": { short: "Install / uninstall sisyphus bits", useWhen: "first setup or removal" },
|
|
13712
|
-
"admin check": { short: "Verify the installation", useWhen: "diagnosing a broken or partial install" },
|
|
13713
|
-
"admin report": { short: "Diagnostics & telemetry", useWhen: "filing a bug or uploading session data" },
|
|
13714
|
-
"admin install setup": { short: "Run first-time setup", useWhen: "installing sisyphus on this machine" },
|
|
13715
|
-
"admin install setup-keybind": { short: "Install the tmux keybind", useWhen: "wiring the dashboard hotkey" },
|
|
13716
|
-
"admin install init": { short: "Initialize repo-local config", useWhen: "onboarding a new repo" },
|
|
13717
|
-
"admin install uninstall": { short: "Remove sisyphus", useWhen: "fully removing the install" },
|
|
13718
|
-
"admin check doctor": { short: "Run install diagnostics", useWhen: "something is not working and you want a health check" },
|
|
13719
|
-
"admin check check-keybinds": { short: "Verify tmux keybinds", useWhen: "the dashboard hotkey does not fire" },
|
|
13720
|
-
"admin check check-statusbar": { short: "Verify status-line wiring", useWhen: "status segments do not render" },
|
|
13721
|
-
"admin report bug": { short: "File a bug report", useWhen: "reporting a defect with context" },
|
|
13722
|
-
"admin report upload": { short: "Upload session data", useWhen: "sharing a session for support" },
|
|
13723
|
-
"admin report configure-upload": { short: "Configure the upload target", useWhen: "setting where uploads go" },
|
|
13724
|
-
"admin clean-zombies": { short: "Sweep stale zombie processes", useWhen: "cleaning up after a crashed daemon or hung agent" },
|
|
13725
|
-
"cloud box": { short: "Provision / operate the box for this repo", useWhen: "working with the box-side environment" },
|
|
13726
|
-
"cloud handoff": { short: "Move a session between local and box", useWhen: "relocating in-flight work" },
|
|
13727
|
-
"cloud box sync": { short: "Rsync this repo to the box", useWhen: "pushing local code to the box" },
|
|
13728
|
-
"cloud box install": { short: "Run the package install on the box", useWhen: "box deps are missing/stale" },
|
|
13729
|
-
"cloud box session": { short: "Create/refresh the box tmux home", useWhen: "the box-side session is absent" },
|
|
13730
|
-
"cloud box attach": { short: "Attach to the box session", useWhen: "you want to work on the box interactively" },
|
|
13731
|
-
"cloud box status": { short: "Print box-side status", useWhen: "checking box state for this repo" },
|
|
13732
|
-
"cloud box login": { short: "Run claude auth login on the box", useWhen: "the box needs Claude credentials" },
|
|
13733
|
-
"cloud box up": { short: "Sync+install+session in one shot", useWhen: "bringing the box up from cold" },
|
|
13734
|
-
"cloud handoff push": { short: "Hand a live session off to the box", useWhen: "moving local work to the cloud" },
|
|
13735
|
-
"cloud handoff pull": { short: "Reclaim a handed-off session locally", useWhen: "bringing cloud work back home" },
|
|
13736
|
-
"agent types": { short: "Agent-type catalog", useWhen: "discovering installable agent types" },
|
|
13737
|
-
"agent types list": { short: "List agent types", useWhen: "enumerating spawnable agent types" },
|
|
13738
|
-
"deploy list": { short: "List deploy providers", useWhen: "discovering supported providers" },
|
|
13739
|
-
"companion profile": { short: "Print companion profile", useWhen: "reading the combined memory + context + badges blob" },
|
|
13740
|
-
"companion memory": { short: "Show accumulated companion observations", useWhen: "reviewing what the companion noticed" },
|
|
13741
|
-
"companion context": { short: "Emit per-prompt companion context", useWhen: "the companion plugin hook needs context" },
|
|
13742
|
-
"companion pane": { short: "Open/focus the side claude pane", useWhen: "you want the companion pane by the dashboard" },
|
|
13743
|
-
"companion popup-test": { short: "Show a test commentary popup", useWhen: "validating feedback-key handling" },
|
|
13744
|
-
"deploy auth": { short: "Configure provider auth", useWhen: "setting up Tailscale/provider credentials" },
|
|
13745
|
-
"deploy hetzner": { short: "Hetzner box lifecycle", useWhen: "targeting Hetzner" },
|
|
13746
|
-
"deploy aws": { short: "AWS box lifecycle", useWhen: "targeting AWS" },
|
|
13747
|
-
"deploy auth tailscale": { short: "Configure Tailscale OAuth", useWhen: "joining the box to your tailnet" },
|
|
13748
|
-
"deploy * up": { short: "Provision the box", useWhen: "standing the box up" },
|
|
13749
|
-
"deploy * down": { short: "Destroy the box", useWhen: "tearing the box down" },
|
|
13750
|
-
"deploy * status": { short: "Print box outputs", useWhen: "checking IP/cost/instance" },
|
|
13751
|
-
"deploy * ssh": { short: "SSH/mosh into the box", useWhen: "getting a shell on the box" },
|
|
13752
|
-
"deploy * logs": { short: "Tail cloud-init + daemon logs", useWhen: "diagnosing a box-side failure" },
|
|
13753
|
-
"deploy * update": { short: "Upgrade sisyphus on the box", useWhen: "pulling the latest daemon onto the box" }
|
|
13754
|
-
};
|
|
13755
|
-
function subcommandRubric(cmd) {
|
|
13756
|
-
if (cmd.name() === "help") {
|
|
13757
|
-
return cmd.summary() || cmd.description();
|
|
13758
|
-
}
|
|
13759
|
-
const p = commandPath(cmd);
|
|
13760
|
-
let key = p;
|
|
13761
|
-
const deployProviderMatch = p.match(/^deploy (hetzner|aws) (.+)$/);
|
|
13762
|
-
if (deployProviderMatch) {
|
|
13763
|
-
key = `deploy * ${deployProviderMatch[2]}`;
|
|
13764
|
-
}
|
|
13765
|
-
const r = RUBRICS[key];
|
|
13766
|
-
if (r) {
|
|
13767
|
-
return `${r.short} | use when ${r.useWhen}`;
|
|
13768
|
-
}
|
|
13769
|
-
return cmd.summary() || cmd.description();
|
|
13770
|
-
}
|
|
13771
|
-
var CONCEPTS_BLOCK = `
|
|
13772
|
-
Concepts
|
|
13773
|
-
session a tracked unit of orchestrated work on one task
|
|
13774
|
-
agent a worker Claude spawned to execute a scoped sub-task
|
|
13775
|
-
orchestrator the Claude that owns a session: decomposes, spawns, advances
|
|
13776
|
-
cycle one discovery\u2192plan\u2192implement\u2192validate iteration; the rollback unit
|
|
13777
|
-
ask a blocking question surfaced for a human to answer
|
|
13778
|
-
mode the session's current phase, driving orchestrator behavior
|
|
13779
|
-
`;
|
|
13780
|
-
|
|
13781
|
-
// src/cli/index.ts
|
|
14031
|
+
init_help_rubric();
|
|
13782
14032
|
var nodeVersion = parseInt(process.versions.node.split(".")[0], 10);
|
|
13783
14033
|
if (nodeVersion < 22) {
|
|
13784
14034
|
console.error(`Sisyphus requires Node.js v22+ (current: v${process.versions.node})`);
|
|
@@ -13801,7 +14051,7 @@ var sessionCounts = null;
|
|
|
13801
14051
|
var program = new Command();
|
|
13802
14052
|
program.name("sis").description("tmux-integrated orchestration daemon for Claude Code").helpOption("-h, --help", "print -h for any node or leaf").version(
|
|
13803
14053
|
JSON.parse(
|
|
13804
|
-
|
|
14054
|
+
readFileSync47(join39(dirname16(fileURLToPath6(import.meta.url)), "..", "package.json"), "utf-8")
|
|
13805
14055
|
).version,
|
|
13806
14056
|
"--version",
|
|
13807
14057
|
"output the version number"
|
|
@@ -13811,23 +14061,7 @@ program.configureHelp({
|
|
|
13811
14061
|
subcommandDescription: (cmd) => subcommandRubric(cmd)
|
|
13812
14062
|
});
|
|
13813
14063
|
program.addHelpText("before", CONCEPTS_BLOCK);
|
|
13814
|
-
program.addHelpText("after",
|
|
13815
|
-
I/O contract: flags and positional args on input, JSON on stdout (JSONL for streams).
|
|
13816
|
-
|
|
13817
|
-
Exit codes:
|
|
13818
|
-
0 success
|
|
13819
|
-
1 permanent error (fallback)
|
|
13820
|
-
2 usage error (bad args/shape)
|
|
13821
|
-
3 not found
|
|
13822
|
-
4 ambiguous (multiple matches \u2014 see error.candidates)
|
|
13823
|
-
5 conflict (already-exists, wrong-state)
|
|
13824
|
-
60 transient (retry-safe: daemon down, timeout, lock contention)
|
|
13825
|
-
|
|
13826
|
-
Errors:
|
|
13827
|
-
{"ok": false,
|
|
13828
|
-
"error": {"code": "<stable-enum>", "kind": "<usage|not_found|ambiguous|conflict|transient|permanent>",
|
|
13829
|
-
"message": "...", "received"?: ..., "expected"?: ..., "next"?: "...", "candidates"?: [...]}}
|
|
13830
|
-
`);
|
|
14064
|
+
program.addHelpText("after", ROOT_AFTER_HELP);
|
|
13831
14065
|
if ((process.argv.includes("--help") || process.argv.includes("-h")) && process.argv.some((a) => a === "session")) {
|
|
13832
14066
|
try {
|
|
13833
14067
|
const resp = await rawSend2({ type: "list", cwd: process.cwd(), all: true }, 250);
|
|
@@ -13929,7 +14163,7 @@ registerHomeInit(diagnostic);
|
|
|
13929
14163
|
var args = process.argv.slice(2);
|
|
13930
14164
|
var firstArg = args[0];
|
|
13931
14165
|
var skipWelcome = ["session", "start", "dashboard", "agent", "orch", "ask", "ui", "segment", "admin", "feedback", "help", "--help", "--version"];
|
|
13932
|
-
if (!
|
|
14166
|
+
if (!existsSync49(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
|
|
13933
14167
|
mkdirSync21(globalDir(), { recursive: true });
|
|
13934
14168
|
console.log("");
|
|
13935
14169
|
console.log(" Welcome to Sisyphus. Run 'sis admin install setup' to get started.");
|