sisyphi 1.2.19 → 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 +508 -281
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +193 -287
- package/dist/daemon.js.map +1 -1
- package/dist/templates/agent-plugin/agents/spec.md +18 -7
- package/dist/tui.js +98 -79
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/templates/agent-plugin/agents/spec.md +18 -7
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"() {
|
|
@@ -3308,7 +3335,7 @@ var init_help_inject = __esm({
|
|
|
3308
3335
|
});
|
|
3309
3336
|
|
|
3310
3337
|
// src/shared/digest-verbs.ts
|
|
3311
|
-
import { existsSync as existsSync39, readFileSync as
|
|
3338
|
+
import { existsSync as existsSync39, readFileSync as readFileSync38 } from "fs";
|
|
3312
3339
|
var init_digest_verbs = __esm({
|
|
3313
3340
|
"src/shared/digest-verbs.ts"() {
|
|
3314
3341
|
"use strict";
|
|
@@ -3324,8 +3351,8 @@ var init_colors = __esm({
|
|
|
3324
3351
|
});
|
|
3325
3352
|
|
|
3326
3353
|
// src/daemon/orchestrator-modes.ts
|
|
3327
|
-
import { existsSync as existsSync40, readdirSync as readdirSync10, readFileSync as
|
|
3328
|
-
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";
|
|
3329
3356
|
import { homedir as homedir14 } from "os";
|
|
3330
3357
|
var init_orchestrator_modes = __esm({
|
|
3331
3358
|
"src/daemon/orchestrator-modes.ts"() {
|
|
@@ -3336,8 +3363,8 @@ var init_orchestrator_modes = __esm({
|
|
|
3336
3363
|
});
|
|
3337
3364
|
|
|
3338
3365
|
// src/daemon/lib/render-plugin.ts
|
|
3339
|
-
import { readdirSync as readdirSync11, readFileSync as
|
|
3340
|
-
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";
|
|
3341
3368
|
var init_render_plugin = __esm({
|
|
3342
3369
|
"src/daemon/lib/render-plugin.ts"() {
|
|
3343
3370
|
"use strict";
|
|
@@ -3418,10 +3445,10 @@ var init_orphan_sweep = __esm({
|
|
|
3418
3445
|
});
|
|
3419
3446
|
|
|
3420
3447
|
// src/daemon/agent.ts
|
|
3421
|
-
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";
|
|
3422
3449
|
import { execSync as execSync21 } from "child_process";
|
|
3423
3450
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
3424
|
-
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";
|
|
3425
3452
|
var init_agent = __esm({
|
|
3426
3453
|
"src/daemon/agent.ts"() {
|
|
3427
3454
|
"use strict";
|
|
@@ -3499,25 +3526,11 @@ var init_pane_monitor = __esm({
|
|
|
3499
3526
|
}
|
|
3500
3527
|
});
|
|
3501
3528
|
|
|
3502
|
-
// src/daemon/mode-notify.ts
|
|
3503
|
-
import { existsSync as existsSync44 } from "fs";
|
|
3504
|
-
import { ulid as ulid3 } from "ulid";
|
|
3505
|
-
var init_mode_notify = __esm({
|
|
3506
|
-
"src/daemon/mode-notify.ts"() {
|
|
3507
|
-
"use strict";
|
|
3508
|
-
init_ask_store();
|
|
3509
|
-
init_state();
|
|
3510
|
-
init_orchestrator_modes();
|
|
3511
|
-
init_paths();
|
|
3512
|
-
init_types();
|
|
3513
|
-
}
|
|
3514
|
-
});
|
|
3515
|
-
|
|
3516
3529
|
// src/daemon/orchestrator.ts
|
|
3517
|
-
import { existsSync as
|
|
3530
|
+
import { existsSync as existsSync44, readdirSync as readdirSync13, readFileSync as readFileSync42, writeFileSync as writeFileSync24 } from "fs";
|
|
3518
3531
|
import { execSync as execSync22 } from "child_process";
|
|
3519
3532
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
3520
|
-
import { resolve as resolve16, join as
|
|
3533
|
+
import { resolve as resolve16, join as join36, relative as relative3 } from "path";
|
|
3521
3534
|
var init_orchestrator = __esm({
|
|
3522
3535
|
"src/daemon/orchestrator.ts"() {
|
|
3523
3536
|
"use strict";
|
|
@@ -3539,13 +3552,12 @@ var init_orchestrator = __esm({
|
|
|
3539
3552
|
init_tmux2();
|
|
3540
3553
|
init_pane_registry();
|
|
3541
3554
|
init_pane_monitor();
|
|
3542
|
-
init_mode_notify();
|
|
3543
3555
|
init_plugins();
|
|
3544
3556
|
}
|
|
3545
3557
|
});
|
|
3546
3558
|
|
|
3547
3559
|
// src/daemon/status-dots.ts
|
|
3548
|
-
import { readFileSync as
|
|
3560
|
+
import { readFileSync as readFileSync43 } from "fs";
|
|
3549
3561
|
var COMPLETED_TTL_MS;
|
|
3550
3562
|
var init_status_dots = __esm({
|
|
3551
3563
|
"src/daemon/status-dots.ts"() {
|
|
@@ -3557,9 +3569,17 @@ var init_status_dots = __esm({
|
|
|
3557
3569
|
}
|
|
3558
3570
|
});
|
|
3559
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
|
+
|
|
3560
3580
|
// src/daemon/uploader.ts
|
|
3561
3581
|
import { rm } from "fs/promises";
|
|
3562
|
-
import { tmpdir as
|
|
3582
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
3563
3583
|
var init_uploader = __esm({
|
|
3564
3584
|
"src/daemon/uploader.ts"() {
|
|
3565
3585
|
"use strict";
|
|
@@ -3598,7 +3618,7 @@ var init_format = __esm({
|
|
|
3598
3618
|
|
|
3599
3619
|
// src/daemon/session-manager.ts
|
|
3600
3620
|
import { v4 as uuidv4 } from "uuid";
|
|
3601
|
-
import { existsSync as
|
|
3621
|
+
import { existsSync as existsSync45, readFileSync as readFileSync44, readdirSync as readdirSync14, rmSync as rmSync9 } from "fs";
|
|
3602
3622
|
var init_session_manager = __esm({
|
|
3603
3623
|
"src/daemon/session-manager.ts"() {
|
|
3604
3624
|
"use strict";
|
|
@@ -3621,6 +3641,7 @@ var init_session_manager = __esm({
|
|
|
3621
3641
|
init_companion_render();
|
|
3622
3642
|
init_companion_commentary();
|
|
3623
3643
|
init_companion_popup();
|
|
3644
|
+
init_mode_transition();
|
|
3624
3645
|
init_history();
|
|
3625
3646
|
init_uploader();
|
|
3626
3647
|
init_upload();
|
|
@@ -3635,7 +3656,7 @@ var init_session_manager = __esm({
|
|
|
3635
3656
|
// src/daemon/transcript-digest.ts
|
|
3636
3657
|
import { openSync, fstatSync, readSync, closeSync } from "fs";
|
|
3637
3658
|
import { homedir as homedir15 } from "os";
|
|
3638
|
-
import { join as
|
|
3659
|
+
import { join as join37 } from "path";
|
|
3639
3660
|
var BYTE_CAP, TAIL_BYTES;
|
|
3640
3661
|
var init_transcript_digest = __esm({
|
|
3641
3662
|
"src/daemon/transcript-digest.ts"() {
|
|
@@ -3649,7 +3670,7 @@ var init_transcript_digest = __esm({
|
|
|
3649
3670
|
import {
|
|
3650
3671
|
closeSync as closeSync2,
|
|
3651
3672
|
constants,
|
|
3652
|
-
existsSync as
|
|
3673
|
+
existsSync as existsSync46,
|
|
3653
3674
|
fstatSync as fstatSync2,
|
|
3654
3675
|
lstatSync as lstatSync2,
|
|
3655
3676
|
openSync as openSync2,
|
|
@@ -3685,17 +3706,17 @@ var init_ask_visual = __esm({
|
|
|
3685
3706
|
|
|
3686
3707
|
// src/daemon/server.ts
|
|
3687
3708
|
import { createServer } from "net";
|
|
3688
|
-
import { unlinkSync as
|
|
3689
|
-
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";
|
|
3690
3711
|
import { scanInbox } from "@crouton-kit/humanloop";
|
|
3691
3712
|
function registryPath() {
|
|
3692
|
-
return
|
|
3713
|
+
return join38(globalDir(), "session-registry.json");
|
|
3693
3714
|
}
|
|
3694
3715
|
function loadSessionRegistry() {
|
|
3695
3716
|
const p = registryPath();
|
|
3696
|
-
if (!
|
|
3717
|
+
if (!existsSync47(p)) return {};
|
|
3697
3718
|
try {
|
|
3698
|
-
return JSON.parse(
|
|
3719
|
+
return JSON.parse(readFileSync46(p, "utf-8"));
|
|
3699
3720
|
} catch (err) {
|
|
3700
3721
|
console.warn("[sisyphus] Failed to parse session registry:", err instanceof Error ? err.message : err);
|
|
3701
3722
|
return {};
|
|
@@ -3726,8 +3747,8 @@ var init_server = __esm({
|
|
|
3726
3747
|
|
|
3727
3748
|
// src/cli/index.ts
|
|
3728
3749
|
import { Command } from "commander";
|
|
3729
|
-
import { existsSync as
|
|
3730
|
-
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";
|
|
3731
3752
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
3732
3753
|
|
|
3733
3754
|
// src/cli/commands/start.ts
|
|
@@ -5833,6 +5854,10 @@ function assertTmux() {
|
|
|
5833
5854
|
throw new Error("Not running inside a tmux pane. Sisyphus requires tmux.");
|
|
5834
5855
|
}
|
|
5835
5856
|
}
|
|
5857
|
+
function getTmuxSession() {
|
|
5858
|
+
assertTmux();
|
|
5859
|
+
return execSync4('tmux display-message -p "#{session_name}"', { encoding: "utf8" }).trim();
|
|
5860
|
+
}
|
|
5836
5861
|
function getTmuxSessionInfo() {
|
|
5837
5862
|
assertTmux();
|
|
5838
5863
|
const out = execSync4('tmux display-message -p "#{session_id}|#{session_name}"', { encoding: "utf8" }).trim();
|
|
@@ -5849,6 +5874,27 @@ function getCurrentTmuxSessionHome(sessionId) {
|
|
|
5849
5874
|
return "";
|
|
5850
5875
|
}
|
|
5851
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
|
+
}
|
|
5852
5898
|
|
|
5853
5899
|
// src/cli/commands/start.ts
|
|
5854
5900
|
init_shell();
|
|
@@ -6102,6 +6148,27 @@ function openDashboardWindow(tmuxSession, cwd) {
|
|
|
6102
6148
|
}
|
|
6103
6149
|
} catch {
|
|
6104
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
|
+
}
|
|
6105
6172
|
const tuiPath = join6(import.meta.dirname, "tui.js");
|
|
6106
6173
|
const windowId = execSync6(
|
|
6107
6174
|
`tmux new-window -t ${shellQuote(tmuxSession + ":")} -n "sisyphus-dashboard" -c ${shellQuote(cwd)} -P -F "#{window_id}"`,
|
|
@@ -6570,8 +6637,8 @@ Exit codes: 0 ok | 2 usage (missing content or --session) | 3 not_found.`
|
|
|
6570
6637
|
}
|
|
6571
6638
|
|
|
6572
6639
|
// src/cli/commands/ask.ts
|
|
6573
|
-
import { existsSync as existsSync12, readFileSync as
|
|
6574
|
-
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";
|
|
6575
6642
|
import { ulid } from "ulid";
|
|
6576
6643
|
|
|
6577
6644
|
// src/shared/ask-schema.ts
|
|
@@ -6634,6 +6701,125 @@ init_state();
|
|
|
6634
6701
|
init_types();
|
|
6635
6702
|
init_exec();
|
|
6636
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
|
|
6637
6823
|
import { approveDeck, notifyDeck, launchReview, display } from "@crouton-kit/humanloop";
|
|
6638
6824
|
var ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
6639
6825
|
function validateAskId(askId) {
|
|
@@ -7025,7 +7211,7 @@ async function markAnswered(cwd, sessionId, askId) {
|
|
|
7025
7211
|
function waitForOutput(cwd, sessionId, askId, initialPpid) {
|
|
7026
7212
|
const outputPath = askOutputPath(cwd, sessionId, askId);
|
|
7027
7213
|
if (existsSync12(outputPath)) {
|
|
7028
|
-
return Promise.resolve(JSON.parse(
|
|
7214
|
+
return Promise.resolve(JSON.parse(readFileSync13(outputPath, "utf-8")));
|
|
7029
7215
|
}
|
|
7030
7216
|
return new Promise((res, _rej) => {
|
|
7031
7217
|
let ppidWatcher;
|
|
@@ -7037,7 +7223,7 @@ function waitForOutput(cwd, sessionId, askId, initialPpid) {
|
|
|
7037
7223
|
const onChange = () => {
|
|
7038
7224
|
if (!existsSync12(outputPath)) return;
|
|
7039
7225
|
try {
|
|
7040
|
-
const out = JSON.parse(
|
|
7226
|
+
const out = JSON.parse(readFileSync13(outputPath, "utf-8"));
|
|
7041
7227
|
cleanup();
|
|
7042
7228
|
res(out);
|
|
7043
7229
|
} catch (err) {
|
|
@@ -7064,22 +7250,86 @@ function waitForOutput(cwd, sessionId, askId, initialPpid) {
|
|
|
7064
7250
|
process.once("SIGINT", onSigint);
|
|
7065
7251
|
});
|
|
7066
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
|
+
}
|
|
7067
7266
|
function maybeSpawnAskPane(cwd, sessionId, askId, kind) {
|
|
7068
7267
|
if (kind === "review") return;
|
|
7069
7268
|
const callerPane = process.env.TMUX_PANE;
|
|
7070
7269
|
if (!callerPane) return;
|
|
7071
7270
|
if (process.env.SISYPHUS_DISABLE_ASK_PANE === "1") return;
|
|
7072
|
-
const tuiPath =
|
|
7271
|
+
const tuiPath = join14(import.meta.dirname, "tui.js");
|
|
7073
7272
|
const cmd = `node ${shellQuote(tuiPath)} --cwd ${shellQuote(cwd)} --session-id ${shellQuote(sessionId)} --ask ${shellQuote(askId)}`;
|
|
7074
|
-
execSafe(`tmux split-window -
|
|
7273
|
+
execSafe(`tmux split-window -h -t ${shellQuote(callerPane)} -c ${shellQuote(cwd)} ${shellQuote(cmd)}`);
|
|
7274
|
+
focusAgentPane(callerPane);
|
|
7075
7275
|
}
|
|
7076
7276
|
function maybeSpawnReviewPane(cwd, sessionId, askId) {
|
|
7077
7277
|
const callerPane = process.env.TMUX_PANE;
|
|
7078
7278
|
if (!callerPane) return;
|
|
7079
7279
|
if (process.env.SISYPHUS_DISABLE_ASK_PANE === "1") return;
|
|
7080
|
-
const cliPath =
|
|
7280
|
+
const cliPath = join14(import.meta.dirname, "cli.js");
|
|
7081
7281
|
const cmd = `node ${shellQuote(cliPath)} ask review open ${shellQuote(askId)} --session ${shellQuote(sessionId)}`;
|
|
7082
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
|
+
}
|
|
7083
7333
|
}
|
|
7084
7334
|
async function submitDeck(deck, opts, options) {
|
|
7085
7335
|
const blocking = options?.blocking !== false;
|
|
@@ -7089,6 +7339,8 @@ async function submitDeck(deck, opts, options) {
|
|
|
7089
7339
|
const claudeSessionId = resolveClaudeSessionId(cwd, sessionId, askedBy);
|
|
7090
7340
|
const askId = mintAskId();
|
|
7091
7341
|
const q0 = deck.interactions[0];
|
|
7342
|
+
const askTitle = deck.title !== void 0 ? deck.title : q0?.title;
|
|
7343
|
+
const suppressTerminalNotification = blocking ? shouldSuppressBanner() : false;
|
|
7092
7344
|
createAsk(cwd, sessionId, {
|
|
7093
7345
|
askId,
|
|
7094
7346
|
askedBy,
|
|
@@ -7096,15 +7348,16 @@ async function submitDeck(deck, opts, options) {
|
|
|
7096
7348
|
pid: process.pid,
|
|
7097
7349
|
claudeSessionId,
|
|
7098
7350
|
cwd,
|
|
7099
|
-
title:
|
|
7351
|
+
title: askTitle,
|
|
7100
7352
|
subtitle: q0?.subtitle,
|
|
7101
|
-
kind: options?.kindOverride !== void 0 ? options.kindOverride : q0?.kind
|
|
7353
|
+
kind: options?.kindOverride !== void 0 ? options.kindOverride : q0?.kind,
|
|
7354
|
+
suppressTerminalNotification
|
|
7102
7355
|
});
|
|
7103
7356
|
writeDecisions(cwd, sessionId, askId, deck);
|
|
7104
7357
|
if (!blocking) {
|
|
7105
7358
|
return { askId };
|
|
7106
7359
|
}
|
|
7107
|
-
|
|
7360
|
+
await triageAsk(cwd, sessionId, askId, q0?.kind, askTitle);
|
|
7108
7361
|
const output = await waitForOutput(cwd, sessionId, askId, initialPpid);
|
|
7109
7362
|
await markAnswered(cwd, sessionId, askId);
|
|
7110
7363
|
return { askId, output };
|
|
@@ -7157,6 +7410,8 @@ async function submitReview(absFile, opts, options) {
|
|
|
7157
7410
|
const initialPpid = process.ppid;
|
|
7158
7411
|
const claudeSessionId = resolveClaudeSessionId(cwd, sessionId, askedBy);
|
|
7159
7412
|
const askId = mintAskId();
|
|
7413
|
+
const reviewTitle = `Review ${basename4(absFile)}`;
|
|
7414
|
+
const suppressTerminalNotification = blocking ? shouldSuppressBanner() : false;
|
|
7160
7415
|
createAsk(cwd, sessionId, {
|
|
7161
7416
|
askId,
|
|
7162
7417
|
askedBy,
|
|
@@ -7164,12 +7419,13 @@ async function submitReview(absFile, opts, options) {
|
|
|
7164
7419
|
pid: process.pid,
|
|
7165
7420
|
claudeSessionId,
|
|
7166
7421
|
cwd,
|
|
7167
|
-
title:
|
|
7168
|
-
kind: "review"
|
|
7422
|
+
title: reviewTitle,
|
|
7423
|
+
kind: "review",
|
|
7424
|
+
suppressTerminalNotification
|
|
7169
7425
|
});
|
|
7170
7426
|
writeReview(cwd, sessionId, askId, { file: absFile });
|
|
7171
7427
|
if (!blocking) return { askId };
|
|
7172
|
-
|
|
7428
|
+
await triageAsk(cwd, sessionId, askId, "review", reviewTitle);
|
|
7173
7429
|
const output = await waitForOutput(cwd, sessionId, askId, initialPpid);
|
|
7174
7430
|
await markAnswered(cwd, sessionId, askId);
|
|
7175
7431
|
return { askId, output };
|
|
@@ -7201,7 +7457,7 @@ async function reviewOpen(askId, opts) {
|
|
|
7201
7457
|
if (!reviewData) exitUsage("missing-review-pointer", `review.json missing for ${askId}`, { received: askId });
|
|
7202
7458
|
const draftPath = askReviewDraftPath(cwd, sessionId, askId);
|
|
7203
7459
|
const flagPath = askReviewSubmitFlagPath(cwd, sessionId, askId);
|
|
7204
|
-
if (existsSync12(flagPath))
|
|
7460
|
+
if (existsSync12(flagPath)) unlinkSync5(flagPath);
|
|
7205
7461
|
const result = await launchReview(reviewData.file, {
|
|
7206
7462
|
output: draftPath,
|
|
7207
7463
|
submitFlagPath: flagPath,
|
|
@@ -7227,7 +7483,7 @@ async function reviewSubmit(askId, opts) {
|
|
|
7227
7483
|
let comments = [];
|
|
7228
7484
|
if (existsSync12(draftPath)) {
|
|
7229
7485
|
try {
|
|
7230
|
-
const draft = JSON.parse(
|
|
7486
|
+
const draft = JSON.parse(readFileSync13(draftPath, "utf-8"));
|
|
7231
7487
|
if (Array.isArray(draft.comments)) comments = draft.comments;
|
|
7232
7488
|
} catch {
|
|
7233
7489
|
}
|
|
@@ -7344,7 +7600,7 @@ async function peek(askId, opts) {
|
|
|
7344
7600
|
if (meta.completedAt) result.completedAt = meta.completedAt;
|
|
7345
7601
|
try {
|
|
7346
7602
|
if (existsSync12(outputPath)) {
|
|
7347
|
-
result.output = JSON.parse(
|
|
7603
|
+
result.output = JSON.parse(readFileSync13(outputPath, "utf-8"));
|
|
7348
7604
|
}
|
|
7349
7605
|
} catch (err) {
|
|
7350
7606
|
if (!(err instanceof SyntaxError)) throw err;
|
|
@@ -7756,13 +8012,13 @@ Exit codes: 0 ok | 2 usage (bad state) | 3 not_found.`
|
|
|
7756
8012
|
|
|
7757
8013
|
// src/tui/lib/context.ts
|
|
7758
8014
|
init_paths();
|
|
7759
|
-
import { readFileSync as
|
|
8015
|
+
import { readFileSync as readFileSync15, readdirSync as readdirSync4 } from "fs";
|
|
7760
8016
|
|
|
7761
8017
|
// src/tui/lib/reports.ts
|
|
7762
|
-
import { readFileSync as
|
|
8018
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
7763
8019
|
function loadReportContent(report) {
|
|
7764
8020
|
try {
|
|
7765
|
-
return
|
|
8021
|
+
return readFileSync14(report.filePath, "utf-8");
|
|
7766
8022
|
} catch {
|
|
7767
8023
|
return report.summary;
|
|
7768
8024
|
}
|
|
@@ -7779,7 +8035,7 @@ function resolveReports(reports) {
|
|
|
7779
8035
|
// src/tui/lib/context.ts
|
|
7780
8036
|
function readFileSafe(filePath) {
|
|
7781
8037
|
try {
|
|
7782
|
-
return
|
|
8038
|
+
return readFileSync15(filePath, "utf-8");
|
|
7783
8039
|
} catch {
|
|
7784
8040
|
return null;
|
|
7785
8041
|
}
|
|
@@ -7958,7 +8214,7 @@ Exit codes: 0 ok | 3 not_found (unknown session).`
|
|
|
7958
8214
|
|
|
7959
8215
|
// src/cli/commands/spawn.ts
|
|
7960
8216
|
import { existsSync as existsSync13 } from "fs";
|
|
7961
|
-
import { join as
|
|
8217
|
+
import { join as join15 } from "path";
|
|
7962
8218
|
function registerSpawn(program2) {
|
|
7963
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(
|
|
7964
8220
|
"after",
|
|
@@ -8035,7 +8291,7 @@ Exit codes: 0 ok | 2 usage | 3 not_found (unknown session) | 5 conflict.`
|
|
|
8035
8291
|
});
|
|
8036
8292
|
}
|
|
8037
8293
|
if (opts.repo && opts.repo !== ".") {
|
|
8038
|
-
const repoPath =
|
|
8294
|
+
const repoPath = join15(sisyphusCwd, opts.repo);
|
|
8039
8295
|
if (!existsSync13(repoPath)) {
|
|
8040
8296
|
exitError({
|
|
8041
8297
|
code: "repo_not_found",
|
|
@@ -8221,7 +8477,7 @@ Exit codes: 0 ok | 2 usage | 3 not_found | 60 transient.`
|
|
|
8221
8477
|
}
|
|
8222
8478
|
|
|
8223
8479
|
// src/cli/commands/await.ts
|
|
8224
|
-
import { existsSync as existsSync16, readFileSync as
|
|
8480
|
+
import { existsSync as existsSync16, readFileSync as readFileSync18 } from "fs";
|
|
8225
8481
|
var AWAIT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
|
|
8226
8482
|
function registerAwait(program2) {
|
|
8227
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(
|
|
@@ -8261,7 +8517,7 @@ Exit codes: 0 ok | 2 usage (missing --session) | 3 not_found (unknown agent) | 6
|
|
|
8261
8517
|
let report = "";
|
|
8262
8518
|
if (reportPath && existsSync16(reportPath)) {
|
|
8263
8519
|
try {
|
|
8264
|
-
report =
|
|
8520
|
+
report = readFileSync18(reportPath, "utf-8");
|
|
8265
8521
|
} catch (err) {
|
|
8266
8522
|
process.stderr.write(`Warning: could not read report at ${reportPath}: ${err instanceof Error ? err.message : err}
|
|
8267
8523
|
`);
|
|
@@ -8466,15 +8722,15 @@ import { execSync as execSync10 } from "child_process";
|
|
|
8466
8722
|
|
|
8467
8723
|
// src/cli/onboard.ts
|
|
8468
8724
|
import { execSync as execSync9 } from "child_process";
|
|
8469
|
-
import { existsSync as existsSync17, readFileSync as
|
|
8725
|
+
import { existsSync as existsSync17, readFileSync as readFileSync19, writeFileSync as writeFileSync10 } from "fs";
|
|
8470
8726
|
import { homedir as homedir9 } from "os";
|
|
8471
|
-
import { dirname as dirname5, join as
|
|
8727
|
+
import { dirname as dirname5, join as join18 } from "path";
|
|
8472
8728
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8473
8729
|
init_platform();
|
|
8474
8730
|
|
|
8475
8731
|
// src/shared/clipboard.ts
|
|
8476
8732
|
init_platform();
|
|
8477
|
-
import { execFileSync as
|
|
8733
|
+
import { execFileSync as execFileSync3, spawnSync } from "child_process";
|
|
8478
8734
|
function detectClipboard() {
|
|
8479
8735
|
const platform = detectPlatform();
|
|
8480
8736
|
if (platform === "darwin") {
|
|
@@ -8565,7 +8821,7 @@ function checkItermOptionKey() {
|
|
|
8565
8821
|
if (process.platform !== "darwin") {
|
|
8566
8822
|
return { checked: false, allCorrect: true, incorrectProfiles: [] };
|
|
8567
8823
|
}
|
|
8568
|
-
const plistPath2 =
|
|
8824
|
+
const plistPath2 = join18(homedir9(), "Library", "Preferences", "com.googlecode.iterm2.plist");
|
|
8569
8825
|
if (!existsSync17(plistPath2)) {
|
|
8570
8826
|
return { checked: false, allCorrect: false, incorrectProfiles: [] };
|
|
8571
8827
|
}
|
|
@@ -8590,7 +8846,7 @@ function checkItermOptionKey() {
|
|
|
8590
8846
|
}
|
|
8591
8847
|
}
|
|
8592
8848
|
function hasExistingTmuxConf() {
|
|
8593
|
-
return existsSync17(
|
|
8849
|
+
return existsSync17(join18(homedir9(), ".tmux.conf")) || existsSync17(join18(homedir9(), ".config", "tmux", "tmux.conf"));
|
|
8594
8850
|
}
|
|
8595
8851
|
var SISYPHUS_DEFAULTS_MARKER = "# sisyphus-managed \u2014 do not edit";
|
|
8596
8852
|
function buildTmuxDefaults() {
|
|
@@ -8702,8 +8958,8 @@ source-file -q ${sisyphusConf} ${SISYPHUS_DEFAULTS_MARKER}
|
|
|
8702
8958
|
`;
|
|
8703
8959
|
}
|
|
8704
8960
|
function writeTmuxDefaults() {
|
|
8705
|
-
const confPath =
|
|
8706
|
-
|
|
8961
|
+
const confPath = join18(homedir9(), ".tmux.conf");
|
|
8962
|
+
writeFileSync10(confPath, buildTmuxDefaults(), "utf8");
|
|
8707
8963
|
}
|
|
8708
8964
|
function isNvimAvailable() {
|
|
8709
8965
|
try {
|
|
@@ -8721,21 +8977,21 @@ function getNvimVersion() {
|
|
|
8721
8977
|
}
|
|
8722
8978
|
}
|
|
8723
8979
|
function hasLazyVimConfig() {
|
|
8724
|
-
return existsSync17(
|
|
8980
|
+
return existsSync17(join18(homedir9(), ".config", "nvim", "lazy-lock.json"));
|
|
8725
8981
|
}
|
|
8726
8982
|
function bundledBaleiaPluginPath() {
|
|
8727
8983
|
const distDir = dirname5(fileURLToPath2(import.meta.url));
|
|
8728
|
-
return
|
|
8984
|
+
return join18(distDir, "templates", "baleia.lua");
|
|
8729
8985
|
}
|
|
8730
8986
|
function installBaleiaPlugin() {
|
|
8731
|
-
const pluginsDir =
|
|
8987
|
+
const pluginsDir = join18(homedir9(), ".config", "nvim", "lua", "plugins");
|
|
8732
8988
|
if (!existsSync17(pluginsDir)) return false;
|
|
8733
|
-
const dest =
|
|
8989
|
+
const dest = join18(pluginsDir, "sisyphus-baleia.lua");
|
|
8734
8990
|
if (existsSync17(dest)) return true;
|
|
8735
8991
|
const src = bundledBaleiaPluginPath();
|
|
8736
8992
|
if (!existsSync17(src)) return false;
|
|
8737
8993
|
try {
|
|
8738
|
-
|
|
8994
|
+
writeFileSync10(dest, readFileSync19(src, "utf-8"), "utf8");
|
|
8739
8995
|
return true;
|
|
8740
8996
|
} catch {
|
|
8741
8997
|
return false;
|
|
@@ -8758,7 +9014,7 @@ function tryAutoInstallNvim() {
|
|
|
8758
9014
|
if (!isNvimAvailable()) {
|
|
8759
9015
|
return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false, baleiaInstalled: false };
|
|
8760
9016
|
}
|
|
8761
|
-
const nvimConfigDir =
|
|
9017
|
+
const nvimConfigDir = join18(homedir9(), ".config", "nvim");
|
|
8762
9018
|
let lazyVimInstalled = false;
|
|
8763
9019
|
if (!existsSync17(nvimConfigDir)) {
|
|
8764
9020
|
const cloneCmd = [
|
|
@@ -8776,7 +9032,7 @@ function tryAutoInstallNvim() {
|
|
|
8776
9032
|
stdio: "inherit",
|
|
8777
9033
|
env: { ...process.env, GIT_LFS_SKIP_SMUDGE: "1" }
|
|
8778
9034
|
});
|
|
8779
|
-
const gitDir =
|
|
9035
|
+
const gitDir = join18(nvimConfigDir, ".git");
|
|
8780
9036
|
if (existsSync17(gitDir)) {
|
|
8781
9037
|
execSync9(`rm -rf "${gitDir}"`, { stdio: "pipe" });
|
|
8782
9038
|
}
|
|
@@ -9063,7 +9319,7 @@ Exit codes: 0 ok | 1 conflict or requires-force`
|
|
|
9063
9319
|
|
|
9064
9320
|
// src/cli/commands/check-keybinds.ts
|
|
9065
9321
|
import { execSync as execSync11 } from "child_process";
|
|
9066
|
-
import { readFileSync as
|
|
9322
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
9067
9323
|
function isTmuxInstalled2() {
|
|
9068
9324
|
try {
|
|
9069
9325
|
execSync11("which tmux", { stdio: "pipe" });
|
|
@@ -9116,7 +9372,7 @@ function runCheck() {
|
|
|
9116
9372
|
let userConfAlreadySources = false;
|
|
9117
9373
|
if (userConfPath !== null) {
|
|
9118
9374
|
try {
|
|
9119
|
-
userConfAlreadySources =
|
|
9375
|
+
userConfAlreadySources = readFileSync20(userConfPath, "utf-8").includes(sisyphusConfPath);
|
|
9120
9376
|
} catch {
|
|
9121
9377
|
}
|
|
9122
9378
|
}
|
|
@@ -9287,9 +9543,9 @@ Exit codes: 0 ok`
|
|
|
9287
9543
|
// src/cli/commands/check-statusbar.ts
|
|
9288
9544
|
init_paths();
|
|
9289
9545
|
import { execSync as execSync12 } from "child_process";
|
|
9290
|
-
import { existsSync as existsSync18, readFileSync as
|
|
9546
|
+
import { existsSync as existsSync18, readFileSync as readFileSync21 } from "fs";
|
|
9291
9547
|
import { homedir as homedir10 } from "os";
|
|
9292
|
-
import { join as
|
|
9548
|
+
import { join as join19 } from "path";
|
|
9293
9549
|
var SISYPHUS_LEFT_TOKEN = "@sisyphus_left";
|
|
9294
9550
|
var SISYPHUS_RIGHT_TOKEN = "@sisyphus_right";
|
|
9295
9551
|
var TMUX_DEFAULT_STATUS_LEFT = "[#S] ";
|
|
@@ -9314,7 +9570,7 @@ function isDaemonRunning() {
|
|
|
9314
9570
|
const pidFile = daemonPidPath();
|
|
9315
9571
|
if (!existsSync18(pidFile)) return false;
|
|
9316
9572
|
try {
|
|
9317
|
-
const pid = parseInt(
|
|
9573
|
+
const pid = parseInt(readFileSync21(pidFile, "utf-8").trim(), 10);
|
|
9318
9574
|
if (Number.isNaN(pid) || pid <= 0) return false;
|
|
9319
9575
|
process.kill(pid, 0);
|
|
9320
9576
|
return true;
|
|
@@ -9357,8 +9613,8 @@ function probeTmuxOptions(serverRunning) {
|
|
|
9357
9613
|
};
|
|
9358
9614
|
}
|
|
9359
9615
|
function findUserTmuxConf() {
|
|
9360
|
-
const xdg =
|
|
9361
|
-
const dotfile =
|
|
9616
|
+
const xdg = join19(homedir10(), ".config", "tmux", "tmux.conf");
|
|
9617
|
+
const dotfile = join19(homedir10(), ".tmux.conf");
|
|
9362
9618
|
if (existsSync18(xdg)) return xdg;
|
|
9363
9619
|
if (existsSync18(dotfile)) return dotfile;
|
|
9364
9620
|
return null;
|
|
@@ -9370,21 +9626,21 @@ function probeUserConf() {
|
|
|
9370
9626
|
}
|
|
9371
9627
|
let contents = "";
|
|
9372
9628
|
try {
|
|
9373
|
-
contents =
|
|
9629
|
+
contents = readFileSync21(path, "utf-8");
|
|
9374
9630
|
} catch {
|
|
9375
9631
|
return { path, setsStatusLeft: false, setsStatusRight: false, sourcesSisyphusManaged: false };
|
|
9376
9632
|
}
|
|
9377
9633
|
const lines = contents.split("\n").filter((line) => !line.trim().startsWith("#"));
|
|
9378
9634
|
const setsStatusLeft = lines.some((line) => /^\s*(set|set-option)\s+-g(?:\s+-\w+)*\s+status-left\b/.test(line));
|
|
9379
9635
|
const setsStatusRight = lines.some((line) => /^\s*(set|set-option)\s+-g(?:\s+-\w+)*\s+status-right\b/.test(line));
|
|
9380
|
-
const sourcesSisyphusManaged = contents.includes(
|
|
9636
|
+
const sourcesSisyphusManaged = contents.includes(join19(homedir10(), ".sisyphus", "tmux.conf"));
|
|
9381
9637
|
return { path, setsStatusLeft, setsStatusRight, sourcesSisyphusManaged };
|
|
9382
9638
|
}
|
|
9383
9639
|
function loadGlobalSisyphusConfig() {
|
|
9384
9640
|
const path = globalConfigPath();
|
|
9385
9641
|
if (!existsSync18(path)) return null;
|
|
9386
9642
|
try {
|
|
9387
|
-
const parsed = JSON.parse(
|
|
9643
|
+
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
9388
9644
|
return parsed.statusBar === void 0 ? null : parsed.statusBar;
|
|
9389
9645
|
} catch {
|
|
9390
9646
|
return null;
|
|
@@ -9724,7 +9980,7 @@ init_paths();
|
|
|
9724
9980
|
import { execSync as execSync14 } from "child_process";
|
|
9725
9981
|
import { existsSync as existsSync19, statSync as statSync4 } from "fs";
|
|
9726
9982
|
import { homedir as homedir11 } from "os";
|
|
9727
|
-
import { join as
|
|
9983
|
+
import { join as join20 } from "path";
|
|
9728
9984
|
init_platform();
|
|
9729
9985
|
init_plugins();
|
|
9730
9986
|
function checkNodeVersion() {
|
|
@@ -9958,7 +10214,7 @@ function checkNvim() {
|
|
|
9958
10214
|
}
|
|
9959
10215
|
function checkNotifyBinary() {
|
|
9960
10216
|
if (process.platform === "darwin") {
|
|
9961
|
-
const binary =
|
|
10217
|
+
const binary = join20(homedir11(), ".sisyphus", "SisyphusNotify.app", "Contents", "MacOS", "sisyphus-notify");
|
|
9962
10218
|
if (existsSync19(binary)) {
|
|
9963
10219
|
return { name: "Notifications", status: "ok", detail: "SisyphusNotify.app built" };
|
|
9964
10220
|
}
|
|
@@ -10072,13 +10328,13 @@ init_version();
|
|
|
10072
10328
|
init_platform();
|
|
10073
10329
|
init_paths();
|
|
10074
10330
|
init_state();
|
|
10075
|
-
import { execFileSync as
|
|
10076
|
-
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";
|
|
10077
10333
|
import os from "os";
|
|
10078
10334
|
var REPO = "crouton-labs/sisyphus";
|
|
10079
10335
|
function tryCmd(bin, args2) {
|
|
10080
10336
|
try {
|
|
10081
|
-
const out =
|
|
10337
|
+
const out = execFileSync4(bin, args2, {
|
|
10082
10338
|
encoding: "utf-8",
|
|
10083
10339
|
stdio: ["ignore", "pipe", "ignore"],
|
|
10084
10340
|
timeout: 5e3
|
|
@@ -10134,7 +10390,7 @@ function tailLog(lines) {
|
|
|
10134
10390
|
const path = daemonLogPath();
|
|
10135
10391
|
if (!existsSync20(path)) return null;
|
|
10136
10392
|
try {
|
|
10137
|
-
const all =
|
|
10393
|
+
const all = readFileSync23(path, "utf-8").split("\n");
|
|
10138
10394
|
return all.slice(-lines).join("\n").trim() || null;
|
|
10139
10395
|
} catch {
|
|
10140
10396
|
return null;
|
|
@@ -10423,8 +10679,8 @@ Exit codes: 0 ok | 1 filing error | 2 usage | 60 cloud unreachable (retry-safe)`
|
|
|
10423
10679
|
}
|
|
10424
10680
|
|
|
10425
10681
|
// src/cli/commands/init.ts
|
|
10426
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as
|
|
10427
|
-
import { join as
|
|
10682
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync11 } from "fs";
|
|
10683
|
+
import { join as join21 } from "path";
|
|
10428
10684
|
var DEFAULT_CONFIG2 = {};
|
|
10429
10685
|
var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
|
|
10430
10686
|
|
|
@@ -10453,19 +10709,19 @@ Effects
|
|
|
10453
10709
|
Exit codes: 0 ok`
|
|
10454
10710
|
).action((opts) => {
|
|
10455
10711
|
const cwd = process.cwd();
|
|
10456
|
-
const sisDir =
|
|
10457
|
-
const configPath =
|
|
10712
|
+
const sisDir = join21(cwd, ".sisyphus");
|
|
10713
|
+
const configPath = join21(sisDir, "config.json");
|
|
10458
10714
|
if (existsSync21(configPath)) {
|
|
10459
10715
|
console.log(`Already initialized: ${configPath}`);
|
|
10460
10716
|
return;
|
|
10461
10717
|
}
|
|
10462
10718
|
mkdirSync9(sisDir, { recursive: true });
|
|
10463
|
-
|
|
10719
|
+
writeFileSync11(configPath, JSON.stringify(DEFAULT_CONFIG2, null, 2) + "\n", "utf-8");
|
|
10464
10720
|
console.log(`Created ${configPath}`);
|
|
10465
10721
|
if (opts.orchestrator) {
|
|
10466
|
-
const orchPath =
|
|
10722
|
+
const orchPath = join21(sisDir, "orchestrator.md");
|
|
10467
10723
|
if (!existsSync21(orchPath)) {
|
|
10468
|
-
|
|
10724
|
+
writeFileSync11(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
|
|
10469
10725
|
console.log(`Created ${orchPath}`);
|
|
10470
10726
|
}
|
|
10471
10727
|
}
|
|
@@ -10525,7 +10781,7 @@ Exit codes: 0 ok`
|
|
|
10525
10781
|
|
|
10526
10782
|
// src/cli/commands/configure-upload.ts
|
|
10527
10783
|
init_paths();
|
|
10528
|
-
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";
|
|
10529
10785
|
import { createInterface as createInterface3 } from "readline";
|
|
10530
10786
|
import { dirname as dirname6 } from "path";
|
|
10531
10787
|
async function readUrlFromInput(interactive) {
|
|
@@ -10606,7 +10862,7 @@ Exit codes: 0 ok | 1 validation or write error`
|
|
|
10606
10862
|
let existing = {};
|
|
10607
10863
|
if (existsSync22(configPath)) {
|
|
10608
10864
|
try {
|
|
10609
|
-
existing = JSON.parse(
|
|
10865
|
+
existing = JSON.parse(readFileSync24(configPath, "utf-8"));
|
|
10610
10866
|
} catch {
|
|
10611
10867
|
console.error(`Error: ${configPath} could not be parsed \u2014 fix or delete it first`);
|
|
10612
10868
|
process.exit(1);
|
|
@@ -10614,7 +10870,7 @@ Exit codes: 0 ok | 1 validation or write error`
|
|
|
10614
10870
|
}
|
|
10615
10871
|
const merged = { ...existing, upload: { url, token } };
|
|
10616
10872
|
mkdirSync10(dirname6(configPath), { recursive: true });
|
|
10617
|
-
|
|
10873
|
+
writeFileSync12(configPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
10618
10874
|
chmodSync2(configPath, 384);
|
|
10619
10875
|
console.log(`\u2713 upload configured (${configPath})`);
|
|
10620
10876
|
});
|
|
@@ -10623,10 +10879,10 @@ Exit codes: 0 ok | 1 validation or write error`
|
|
|
10623
10879
|
// src/cli/commands/getting-started.ts
|
|
10624
10880
|
import { execSync as execSync15 } from "child_process";
|
|
10625
10881
|
import { existsSync as existsSync23 } from "fs";
|
|
10626
|
-
import { dirname as dirname7, join as
|
|
10882
|
+
import { dirname as dirname7, join as join22 } from "path";
|
|
10627
10883
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
10628
10884
|
function templatePath(name) {
|
|
10629
|
-
return
|
|
10885
|
+
return join22(dirname7(fileURLToPath3(import.meta.url)), "templates", name);
|
|
10630
10886
|
}
|
|
10631
10887
|
function isClaudeCode() {
|
|
10632
10888
|
return !!process.env["CLAUDECODE"];
|
|
@@ -10993,7 +11249,7 @@ sis ui guide --tutorial 4
|
|
|
10993
11249
|
`);
|
|
10994
11250
|
}
|
|
10995
11251
|
function printStep4() {
|
|
10996
|
-
const demoPath =
|
|
11252
|
+
const demoPath = join22(process.cwd(), "tmp", "sisyphus-tutorial-demo");
|
|
10997
11253
|
console.log(`
|
|
10998
11254
|
<claude-instructions>
|
|
10999
11255
|
# Sisyphus Getting Started \u2014 Step 4: Demo Session
|
|
@@ -11037,7 +11293,7 @@ Have the user confirm they understand these keybinds before proceeding.
|
|
|
11037
11293
|
|
|
11038
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:
|
|
11039
11295
|
\`\`\`
|
|
11040
|
-
mkdir -p ${
|
|
11296
|
+
mkdir -p ${join22(process.cwd(), "tmp")}
|
|
11041
11297
|
rm -rf ${demoPath}
|
|
11042
11298
|
cp -r ${templatePath("tutorial-demo")} ${demoPath}
|
|
11043
11299
|
git -C ${demoPath} init
|
|
@@ -11115,7 +11371,7 @@ sis ui guide --tutorial 5
|
|
|
11115
11371
|
`);
|
|
11116
11372
|
}
|
|
11117
11373
|
function printStep5() {
|
|
11118
|
-
const demoPath =
|
|
11374
|
+
const demoPath = join22(process.cwd(), "tmp", "sisyphus-tutorial-demo");
|
|
11119
11375
|
const demoExists = existsSync23(demoPath);
|
|
11120
11376
|
let recentCommits = "";
|
|
11121
11377
|
let topLevelFiles = "";
|
|
@@ -11220,9 +11476,9 @@ If they say yes, run:
|
|
|
11220
11476
|
rm -rf ${demoPath}
|
|
11221
11477
|
\`\`\`
|
|
11222
11478
|
|
|
11223
|
-
Then check whether \`${
|
|
11479
|
+
Then check whether \`${join22(process.cwd(), "tmp")}\` is now empty, and if so remove it too:
|
|
11224
11480
|
\`\`\`
|
|
11225
|
-
rmdir ${
|
|
11481
|
+
rmdir ${join22(process.cwd(), "tmp")} 2>/dev/null || true
|
|
11226
11482
|
\`\`\`
|
|
11227
11483
|
|
|
11228
11484
|
If they say no or want to explore first, leave it. They can clean up later with the same
|
|
@@ -11655,7 +11911,7 @@ Exit codes: 0 ok | 2 usage (invalid --tutorial value)`
|
|
|
11655
11911
|
|
|
11656
11912
|
// src/cli/commands/history.ts
|
|
11657
11913
|
init_paths();
|
|
11658
|
-
import { readdirSync as readdirSync7, readFileSync as
|
|
11914
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync25, existsSync as existsSync24 } from "fs";
|
|
11659
11915
|
import { resolve as resolve8 } from "path";
|
|
11660
11916
|
function loadAllSummaries() {
|
|
11661
11917
|
const base = historyBaseDir();
|
|
@@ -11665,7 +11921,7 @@ function loadAllSummaries() {
|
|
|
11665
11921
|
const summaryPath = historySessionSummaryPath(name);
|
|
11666
11922
|
if (existsSync24(summaryPath)) {
|
|
11667
11923
|
try {
|
|
11668
|
-
const raw =
|
|
11924
|
+
const raw = readFileSync25(summaryPath, "utf-8");
|
|
11669
11925
|
results.push({ id: name, summary: JSON.parse(raw) });
|
|
11670
11926
|
continue;
|
|
11671
11927
|
} catch {
|
|
@@ -11682,7 +11938,7 @@ function buildLiveSummary(sessionId) {
|
|
|
11682
11938
|
if (!existsSync24(eventsPath)) return null;
|
|
11683
11939
|
let cwd = null;
|
|
11684
11940
|
try {
|
|
11685
|
-
const lines =
|
|
11941
|
+
const lines = readFileSync25(eventsPath, "utf-8").split("\n");
|
|
11686
11942
|
for (const line of lines) {
|
|
11687
11943
|
if (!line.trim()) continue;
|
|
11688
11944
|
try {
|
|
@@ -11703,7 +11959,7 @@ function buildLiveSummary(sessionId) {
|
|
|
11703
11959
|
if (!existsSync24(sPath)) return null;
|
|
11704
11960
|
let session2;
|
|
11705
11961
|
try {
|
|
11706
|
-
session2 = JSON.parse(
|
|
11962
|
+
session2 = JSON.parse(readFileSync25(sPath, "utf-8"));
|
|
11707
11963
|
} catch {
|
|
11708
11964
|
return null;
|
|
11709
11965
|
}
|
|
@@ -11765,7 +12021,7 @@ function buildLiveSummary(sessionId) {
|
|
|
11765
12021
|
function loadEvents(sessionId) {
|
|
11766
12022
|
const eventsPath = historyEventsPath(sessionId);
|
|
11767
12023
|
if (!existsSync24(eventsPath)) return [];
|
|
11768
|
-
const lines =
|
|
12024
|
+
const lines = readFileSync25(eventsPath, "utf-8").split("\n").filter((l) => l.trim());
|
|
11769
12025
|
const events = [];
|
|
11770
12026
|
for (const line of lines) {
|
|
11771
12027
|
try {
|
|
@@ -11780,7 +12036,7 @@ function findSession(idOrName) {
|
|
|
11780
12036
|
const summaryPath = historySessionSummaryPath(idOrName);
|
|
11781
12037
|
if (existsSync24(summaryPath)) {
|
|
11782
12038
|
try {
|
|
11783
|
-
return { id: idOrName, summary: JSON.parse(
|
|
12039
|
+
return { id: idOrName, summary: JSON.parse(readFileSync25(summaryPath, "utf-8")) };
|
|
11784
12040
|
} catch {
|
|
11785
12041
|
}
|
|
11786
12042
|
}
|
|
@@ -12028,7 +12284,7 @@ Exit codes: 0 ok | 2 usage | 1 export_failed.`).action(async (sessionIdArg, opts
|
|
|
12028
12284
|
|
|
12029
12285
|
// src/cli/commands/upload.ts
|
|
12030
12286
|
import { rmSync as rmSync6 } from "fs";
|
|
12031
|
-
import { tmpdir } from "os";
|
|
12287
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
12032
12288
|
init_config();
|
|
12033
12289
|
init_session_export();
|
|
12034
12290
|
init_upload();
|
|
@@ -12084,7 +12340,7 @@ Exit codes: 0 ok | 1 upload or export error | 2 usage`
|
|
|
12084
12340
|
}
|
|
12085
12341
|
let zipPath;
|
|
12086
12342
|
try {
|
|
12087
|
-
zipPath = await exportSessionToZip(sessionId, cwd, { reveal: false, outputDir:
|
|
12343
|
+
zipPath = await exportSessionToZip(sessionId, cwd, { reveal: false, outputDir: tmpdir2() });
|
|
12088
12344
|
} catch (err) {
|
|
12089
12345
|
exitError({
|
|
12090
12346
|
code: "export_failed",
|
|
@@ -12149,7 +12405,7 @@ Exit codes: 0 ok | 1 upload or export error | 2 usage`
|
|
|
12149
12405
|
// src/cli/commands/scratch.ts
|
|
12150
12406
|
import { execSync as execSync16 } from "child_process";
|
|
12151
12407
|
init_shell();
|
|
12152
|
-
function
|
|
12408
|
+
function findHomeSession2(cwd) {
|
|
12153
12409
|
const normalizedCwd = cwd.replace(/\/+$/, "");
|
|
12154
12410
|
let output;
|
|
12155
12411
|
try {
|
|
@@ -12197,7 +12453,7 @@ Effects
|
|
|
12197
12453
|
Exit codes: 0 ok | 2 usage.`).action((promptParts, opts) => {
|
|
12198
12454
|
assertTmux();
|
|
12199
12455
|
const cwd = opts.cwd ?? process.env["SISYPHUS_CWD"] ?? process.cwd();
|
|
12200
|
-
const homeSession =
|
|
12456
|
+
const homeSession = findHomeSession2(cwd);
|
|
12201
12457
|
if (!homeSession) {
|
|
12202
12458
|
const current = execSync16('tmux display-message -p "#{session_name}"', {
|
|
12203
12459
|
encoding: "utf-8"
|
|
@@ -12225,15 +12481,15 @@ function openScratchWindow(tmuxSession, cwd, prompt) {
|
|
|
12225
12481
|
|
|
12226
12482
|
// src/cli/commands/review.ts
|
|
12227
12483
|
init_paths();
|
|
12228
|
-
import { join as
|
|
12229
|
-
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";
|
|
12230
12486
|
var _statusCheck = ["draft", "question", "approved", "rejected", "deferred"];
|
|
12231
12487
|
function resolveContextArtifact(file, opts, filename, notFoundMessage) {
|
|
12232
12488
|
const cwd = opts.cwd || process.env.SISYPHUS_CWD || process.cwd();
|
|
12233
12489
|
if (file) return resolve9(file);
|
|
12234
12490
|
const sessionId = opts.sessionId || process.env.SISYPHUS_SESSION_ID;
|
|
12235
12491
|
if (sessionId) {
|
|
12236
|
-
const target =
|
|
12492
|
+
const target = join24(contextDir(cwd, sessionId), filename);
|
|
12237
12493
|
if (!existsSync26(target)) {
|
|
12238
12494
|
exitUsage("file-not-found", `File not found: ${target}`, { received: target });
|
|
12239
12495
|
}
|
|
@@ -12243,7 +12499,7 @@ function resolveContextArtifact(file, opts, filename, notFoundMessage) {
|
|
|
12243
12499
|
if (existsSync26(dir)) {
|
|
12244
12500
|
const sessions = readdirSync8(dir);
|
|
12245
12501
|
for (const session2 of sessions.reverse()) {
|
|
12246
|
-
const candidate =
|
|
12502
|
+
const candidate = join24(dir, session2, "context", filename);
|
|
12247
12503
|
if (existsSync26(candidate)) return candidate;
|
|
12248
12504
|
}
|
|
12249
12505
|
}
|
|
@@ -12302,12 +12558,12 @@ Exit codes: 0 ok | 2 usage.`).action(async (file, opts) => {
|
|
|
12302
12558
|
if (!existsSync26(targetPath)) {
|
|
12303
12559
|
exitUsage("file-not-found", `File not found: ${targetPath}`, { received: targetPath });
|
|
12304
12560
|
}
|
|
12305
|
-
const parsed = JSON.parse(
|
|
12561
|
+
const parsed = JSON.parse(readFileSync27(targetPath, "utf-8"));
|
|
12306
12562
|
const rendered = renderRequirementsMarkdown(parsed);
|
|
12307
|
-
const outPath =
|
|
12563
|
+
const outPath = join24(dirname8(targetPath), "requirements.md");
|
|
12308
12564
|
const tmpPath = outPath + ".tmp";
|
|
12309
12565
|
if (existsSync26(outPath)) {
|
|
12310
|
-
const existing =
|
|
12566
|
+
const existing = readFileSync27(outPath, "utf-8");
|
|
12311
12567
|
if (existing !== rendered) {
|
|
12312
12568
|
if (!opts.force) {
|
|
12313
12569
|
exitUsage("conflict", `${outPath} has been hand-edited (differs from rendered output)`, {
|
|
@@ -12320,7 +12576,7 @@ Exit codes: 0 ok | 2 usage.`).action(async (file, opts) => {
|
|
|
12320
12576
|
`);
|
|
12321
12577
|
}
|
|
12322
12578
|
}
|
|
12323
|
-
|
|
12579
|
+
writeFileSync14(tmpPath, rendered, "utf-8");
|
|
12324
12580
|
renameSync3(tmpPath, outPath);
|
|
12325
12581
|
emitJsonOk({ path: resolve9(outPath) });
|
|
12326
12582
|
return;
|
|
@@ -12684,8 +12940,8 @@ function renderRequirementsMarkdown(json) {
|
|
|
12684
12940
|
}
|
|
12685
12941
|
|
|
12686
12942
|
// src/cli/commands/companion.ts
|
|
12687
|
-
import { basename as basename7, dirname as dirname11, join as
|
|
12688
|
-
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";
|
|
12689
12945
|
init_paths();
|
|
12690
12946
|
init_companion_types();
|
|
12691
12947
|
init_companion_memory();
|
|
@@ -12788,10 +13044,10 @@ Effects
|
|
|
12788
13044
|
Writes/updates ~/.sisyphus/companion-context-cache/<session-id>.json on each call that produces output.
|
|
12789
13045
|
|
|
12790
13046
|
Exit codes: 0 ok.`).action((opts) => {
|
|
12791
|
-
const cachePath =
|
|
13047
|
+
const cachePath = join29(globalDir(), "companion-context-cache", `${opts.sessionId}.json`);
|
|
12792
13048
|
let prev = {};
|
|
12793
13049
|
try {
|
|
12794
|
-
prev = JSON.parse(
|
|
13050
|
+
prev = JSON.parse(readFileSync32(cachePath, "utf-8"));
|
|
12795
13051
|
} catch {
|
|
12796
13052
|
prev = {};
|
|
12797
13053
|
}
|
|
@@ -12805,7 +13061,7 @@ Exit codes: 0 ok.`).action((opts) => {
|
|
|
12805
13061
|
process.stdout.write(renderFullContext(next));
|
|
12806
13062
|
}
|
|
12807
13063
|
mkdirSync15(dirname11(cachePath), { recursive: true });
|
|
12808
|
-
|
|
13064
|
+
writeFileSync19(cachePath, JSON.stringify(next), "utf-8");
|
|
12809
13065
|
});
|
|
12810
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", `
|
|
12811
13067
|
companion pane: open (or focus) the companion side pane next to the dashboard.
|
|
@@ -12909,7 +13165,7 @@ Exit codes: 0 ok | 3 not_found.`
|
|
|
12909
13165
|
}));
|
|
12910
13166
|
emitJsonOk({ ...companionData, badges });
|
|
12911
13167
|
} else {
|
|
12912
|
-
emitJsonOk(companionData);
|
|
13168
|
+
emitJsonOk({ ...companionData });
|
|
12913
13169
|
}
|
|
12914
13170
|
});
|
|
12915
13171
|
}
|
|
@@ -12919,7 +13175,7 @@ init_runner();
|
|
|
12919
13175
|
init_creds();
|
|
12920
13176
|
init_tailscale();
|
|
12921
13177
|
import { homedir as homedir13 } from "os";
|
|
12922
|
-
import { join as
|
|
13178
|
+
import { join as join30 } from "path";
|
|
12923
13179
|
function assertArch(raw) {
|
|
12924
13180
|
if (raw === "arm" || raw === "x86") return raw;
|
|
12925
13181
|
throw new Error(`Invalid --arch: ${raw}. Must be 'arm' or 'x86'.`);
|
|
@@ -12965,7 +13221,7 @@ Exit codes: 0 ok | 1 error (bad credentials or API failure).`).action(async () =
|
|
|
12965
13221
|
const sub = deploy.command(provider).description(`${provider} commands.`).addHelpText("before", `
|
|
12966
13222
|
deploy ${provider}: per-provider box lifecycle. up | down | status | ssh | logs | update.
|
|
12967
13223
|
`);
|
|
12968
|
-
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", `
|
|
12969
13225
|
deploy ${provider} up: provision the ${provider} box via Terraform (init \u2192 plan \u2192 apply).
|
|
12970
13226
|
|
|
12971
13227
|
Input
|
|
@@ -13112,14 +13368,14 @@ init_runner2();
|
|
|
13112
13368
|
|
|
13113
13369
|
// src/cli/cloud/handoff.ts
|
|
13114
13370
|
import { spawn as spawn5 } from "child_process";
|
|
13115
|
-
import { existsSync as existsSync36, readFileSync as
|
|
13371
|
+
import { existsSync as existsSync36, readFileSync as readFileSync36, writeFileSync as writeFileSync20 } from "fs";
|
|
13116
13372
|
init_exec();
|
|
13117
13373
|
init_paths();
|
|
13118
13374
|
init_shell();
|
|
13119
13375
|
init_runner();
|
|
13120
13376
|
init_ssh_exec();
|
|
13121
13377
|
init_provider_pick();
|
|
13122
|
-
import { join as
|
|
13378
|
+
import { join as join32 } from "path";
|
|
13123
13379
|
async function cloudHandoff(sessionId, opts) {
|
|
13124
13380
|
const cwd = process.env["SISYPHUS_CWD"] ?? process.cwd();
|
|
13125
13381
|
const request = {
|
|
@@ -13164,7 +13420,7 @@ async function waitForSentOrError(cwd, sessionId) {
|
|
|
13164
13420
|
if (!existsSync36(path)) continue;
|
|
13165
13421
|
let session2;
|
|
13166
13422
|
try {
|
|
13167
|
-
session2 = JSON.parse(
|
|
13423
|
+
session2 = JSON.parse(readFileSync36(path, "utf-8"));
|
|
13168
13424
|
} catch (err) {
|
|
13169
13425
|
void err;
|
|
13170
13426
|
continue;
|
|
@@ -13245,16 +13501,16 @@ async function cloudReclaim(sessionId, opts) {
|
|
|
13245
13501
|
await rsyncDown(target, `${remoteRepoDir}/`, `${cwd}/`, { withDelete: false, excludeSisyphus: true, update: true });
|
|
13246
13502
|
for (const name of ["config.json", "orchestrator.md", "orchestrator-settings.json"]) {
|
|
13247
13503
|
const remotePath = `${remoteRepoDir}/.sisyphus/${name}`;
|
|
13248
|
-
const localPath =
|
|
13504
|
+
const localPath = join32(projectDir(cwd), name);
|
|
13249
13505
|
const probe = runOnBox(provider, `test -f ${shellQuote(remotePath.replace(/^~\//, ""))} && echo y || echo n`);
|
|
13250
13506
|
if (probe.stdout.trim() !== "y") continue;
|
|
13251
13507
|
await rsyncDown(target, remotePath, localPath, { withDelete: false });
|
|
13252
13508
|
}
|
|
13253
13509
|
const localStatePath = statePath(cwd, sessionId);
|
|
13254
|
-
const merged = JSON.parse(
|
|
13510
|
+
const merged = JSON.parse(readFileSync36(localStatePath, "utf-8"));
|
|
13255
13511
|
merged.cwd = cwd;
|
|
13256
13512
|
merged.handoff = local.handoff;
|
|
13257
|
-
|
|
13513
|
+
writeFileSync20(localStatePath, JSON.stringify(merged, null, 2));
|
|
13258
13514
|
const reclaimMessage = `Session reclaimed from cloud (${provider}:${repo}). Resuming locally.`;
|
|
13259
13515
|
console.log(`\u2192 local sis resume`);
|
|
13260
13516
|
const resumeResp = await sendRequest({
|
|
@@ -13287,7 +13543,7 @@ function readLocalSession(cwd, sessionId) {
|
|
|
13287
13543
|
received: sessionId
|
|
13288
13544
|
});
|
|
13289
13545
|
}
|
|
13290
|
-
return JSON.parse(
|
|
13546
|
+
return JSON.parse(readFileSync36(path, "utf-8"));
|
|
13291
13547
|
}
|
|
13292
13548
|
async function waitForBoxPaused(provider, remoteSessionDir) {
|
|
13293
13549
|
const POLL_INTERVAL_MS = 2e3;
|
|
@@ -13571,7 +13827,7 @@ function attachNotify(diagnostic2) {
|
|
|
13571
13827
|
// src/cli/commands/tmux-sessions.ts
|
|
13572
13828
|
init_paths();
|
|
13573
13829
|
import { execSync as execSync18 } from "child_process";
|
|
13574
|
-
import { readFileSync as
|
|
13830
|
+
import { readFileSync as readFileSync37, existsSync as existsSync37 } from "fs";
|
|
13575
13831
|
var DOT_MAP = {
|
|
13576
13832
|
"orchestrator:processing": { icon: "\u25CF", color: "#d4ad6a" },
|
|
13577
13833
|
"orchestrator:idle": { icon: "\u25CF", color: "#d47766" },
|
|
@@ -13584,7 +13840,7 @@ function readManifest() {
|
|
|
13584
13840
|
const p = sessionsManifestPath();
|
|
13585
13841
|
if (!existsSync37(p)) return null;
|
|
13586
13842
|
try {
|
|
13587
|
-
return JSON.parse(
|
|
13843
|
+
return JSON.parse(readFileSync37(p, "utf-8"));
|
|
13588
13844
|
} catch {
|
|
13589
13845
|
return null;
|
|
13590
13846
|
}
|
|
@@ -13625,7 +13881,7 @@ init_server();
|
|
|
13625
13881
|
init_ask_store();
|
|
13626
13882
|
init_state();
|
|
13627
13883
|
init_paths();
|
|
13628
|
-
import { existsSync as
|
|
13884
|
+
import { existsSync as existsSync48 } from "fs";
|
|
13629
13885
|
var HEARTBEAT_ASKED_BY2 = "system:heartbeat";
|
|
13630
13886
|
var ORPHAN_ASKED_BY2 = "system:orphan-handler";
|
|
13631
13887
|
function isHeartbeatZombie(cwd, sessionId, askId) {
|
|
@@ -13633,14 +13889,14 @@ function isHeartbeatZombie(cwd, sessionId, askId) {
|
|
|
13633
13889
|
if (!meta) return false;
|
|
13634
13890
|
if (meta.askedBy !== HEARTBEAT_ASKED_BY2) return false;
|
|
13635
13891
|
if (meta.status === "answered") return false;
|
|
13636
|
-
if (
|
|
13892
|
+
if (existsSync48(askOutputPath(cwd, sessionId, askId))) return false;
|
|
13637
13893
|
for (const candidateId of listAsks(cwd, sessionId)) {
|
|
13638
13894
|
if (candidateId === askId) continue;
|
|
13639
13895
|
const candidateMeta = readMeta(cwd, sessionId, candidateId);
|
|
13640
13896
|
if (!candidateMeta) continue;
|
|
13641
13897
|
if (candidateMeta.heartbeatAskId !== askId) continue;
|
|
13642
13898
|
if (candidateMeta.status === "answered") return true;
|
|
13643
|
-
if (
|
|
13899
|
+
if (existsSync48(askOutputPath(cwd, sessionId, candidateId))) return true;
|
|
13644
13900
|
}
|
|
13645
13901
|
return false;
|
|
13646
13902
|
}
|
|
@@ -13649,7 +13905,7 @@ function isOrphanZombie(cwd, sessionId, askId) {
|
|
|
13649
13905
|
if (!meta) return false;
|
|
13650
13906
|
if (meta.askedBy !== ORPHAN_ASKED_BY2) return false;
|
|
13651
13907
|
if (meta.status === "answered") return false;
|
|
13652
|
-
if (
|
|
13908
|
+
if (existsSync48(askOutputPath(cwd, sessionId, askId))) return false;
|
|
13653
13909
|
if (meta.orphanTarget?.kind !== "agent") return false;
|
|
13654
13910
|
let session2;
|
|
13655
13911
|
try {
|
|
@@ -13662,27 +13918,6 @@ function isOrphanZombie(cwd, sessionId, askId) {
|
|
|
13662
13918
|
if (!agent2) return false;
|
|
13663
13919
|
return agent2.status !== "running";
|
|
13664
13920
|
}
|
|
13665
|
-
function isModeGateZombie(cwd, sessionId, askId) {
|
|
13666
|
-
const meta = readMeta(cwd, sessionId, askId);
|
|
13667
|
-
if (!meta) return false;
|
|
13668
|
-
if (meta.modeTransition !== true) return false;
|
|
13669
|
-
if (meta.status === "answered") return false;
|
|
13670
|
-
if (existsSync49(askOutputPath(cwd, sessionId, askId))) return false;
|
|
13671
|
-
const deck = readDecisions(cwd, sessionId, askId);
|
|
13672
|
-
if (!deck) return false;
|
|
13673
|
-
const source = deck.source;
|
|
13674
|
-
const chain = source?.modeChain;
|
|
13675
|
-
if (!chain || chain.length === 0) return false;
|
|
13676
|
-
let currentMode;
|
|
13677
|
-
try {
|
|
13678
|
-
const session2 = getSession(cwd, sessionId);
|
|
13679
|
-
currentMode = session2.orchestratorCycles[session2.orchestratorCycles.length - 1]?.mode;
|
|
13680
|
-
} catch {
|
|
13681
|
-
return false;
|
|
13682
|
-
}
|
|
13683
|
-
if (!currentMode) return false;
|
|
13684
|
-
return !chain.some((e) => e.mode === currentMode);
|
|
13685
|
-
}
|
|
13686
13921
|
async function resolveZombie(cwd, sessionId, entry) {
|
|
13687
13922
|
const { askId, kind } = entry;
|
|
13688
13923
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -13700,11 +13935,6 @@ async function resolveZombie(cwd, sessionId, entry) {
|
|
|
13700
13935
|
selectedOptionId = "dismiss";
|
|
13701
13936
|
freetext = "auto-resolved: agent superseded by replacement (clean-zombies sweep)";
|
|
13702
13937
|
break;
|
|
13703
|
-
case "mode-gate":
|
|
13704
|
-
interactionId = "mode-transition";
|
|
13705
|
-
selectedOptionId = "ack";
|
|
13706
|
-
freetext = "auto-resolved: session advanced past mode-transition (clean-zombies sweep)";
|
|
13707
|
-
break;
|
|
13708
13938
|
}
|
|
13709
13939
|
writeOutput(cwd, sessionId, askId, [{
|
|
13710
13940
|
id: interactionId,
|
|
@@ -13723,14 +13953,12 @@ async function sweepSession(cwd, sessionId) {
|
|
|
13723
13953
|
const meta = readMeta(cwd, sessionId, askId);
|
|
13724
13954
|
const agentId = meta?.orphanTarget?.kind === "agent" ? meta.orphanTarget.agentId : "unknown";
|
|
13725
13955
|
zombies.push({ sessionId, askId, kind: "orphan", reason: `agent ${agentId} is no longer running` });
|
|
13726
|
-
} else if (isModeGateZombie(cwd, sessionId, askId)) {
|
|
13727
|
-
zombies.push({ sessionId, askId, kind: "mode-gate", reason: "session advanced past mode-transition" });
|
|
13728
13956
|
}
|
|
13729
13957
|
}
|
|
13730
13958
|
return zombies;
|
|
13731
13959
|
}
|
|
13732
13960
|
function registerCleanZombies(program2) {
|
|
13733
|
-
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(
|
|
13734
13962
|
"after",
|
|
13735
13963
|
`
|
|
13736
13964
|
clean-zombies: sweep all sessions and dismiss stale ask records.
|
|
@@ -13743,8 +13971,8 @@ Output (stdout, plain text \u2014 human-readable maintenance summary; not for ag
|
|
|
13743
13971
|
|
|
13744
13972
|
Effects
|
|
13745
13973
|
Mutates ask records: writes response.json and updates status to "answered" for each
|
|
13746
|
-
zombie found. Dismisses heartbeat asks whose original was answered
|
|
13747
|
-
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.
|
|
13748
13976
|
|
|
13749
13977
|
Exit codes: 0 ok`
|
|
13750
13978
|
).action(async () => {
|
|
@@ -13757,7 +13985,7 @@ Exit codes: 0 ok`
|
|
|
13757
13985
|
const allZombies = [];
|
|
13758
13986
|
const sessionsSweept = /* @__PURE__ */ new Set();
|
|
13759
13987
|
for (const [sessionId, cwd] of entries) {
|
|
13760
|
-
if (!
|
|
13988
|
+
if (!existsSync48(statePath(cwd, sessionId))) continue;
|
|
13761
13989
|
try {
|
|
13762
13990
|
const zombies = await sweepSession(cwd, sessionId);
|
|
13763
13991
|
if (zombies.length > 0) {
|
|
@@ -13773,8 +14001,7 @@ Exit codes: 0 ok`
|
|
|
13773
14001
|
}
|
|
13774
14002
|
const summary = {
|
|
13775
14003
|
heartbeats: allZombies.filter((z4) => z4.kind === "heartbeat").length,
|
|
13776
|
-
orphans: allZombies.filter((z4) => z4.kind === "orphan").length
|
|
13777
|
-
modeGates: allZombies.filter((z4) => z4.kind === "mode-gate").length
|
|
14004
|
+
orphans: allZombies.filter((z4) => z4.kind === "orphan").length
|
|
13778
14005
|
};
|
|
13779
14006
|
const total = allZombies.length;
|
|
13780
14007
|
if (total === 0) {
|
|
@@ -13782,7 +14009,7 @@ Exit codes: 0 ok`
|
|
|
13782
14009
|
return;
|
|
13783
14010
|
}
|
|
13784
14011
|
for (const [sessionId, cwd] of entries) {
|
|
13785
|
-
if (!
|
|
14012
|
+
if (!existsSync48(statePath(cwd, sessionId))) continue;
|
|
13786
14013
|
const sessionZombies = allZombies.filter((z4) => z4.sessionId === sessionId);
|
|
13787
14014
|
for (const zombie of sessionZombies) {
|
|
13788
14015
|
try {
|
|
@@ -13795,7 +14022,7 @@ Exit codes: 0 ok`
|
|
|
13795
14022
|
}
|
|
13796
14023
|
}
|
|
13797
14024
|
}
|
|
13798
|
-
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"}.`);
|
|
13799
14026
|
});
|
|
13800
14027
|
}
|
|
13801
14028
|
|
|
@@ -13824,7 +14051,7 @@ var sessionCounts = null;
|
|
|
13824
14051
|
var program = new Command();
|
|
13825
14052
|
program.name("sis").description("tmux-integrated orchestration daemon for Claude Code").helpOption("-h, --help", "print -h for any node or leaf").version(
|
|
13826
14053
|
JSON.parse(
|
|
13827
|
-
|
|
14054
|
+
readFileSync47(join39(dirname16(fileURLToPath6(import.meta.url)), "..", "package.json"), "utf-8")
|
|
13828
14055
|
).version,
|
|
13829
14056
|
"--version",
|
|
13830
14057
|
"output the version number"
|
|
@@ -13936,7 +14163,7 @@ registerHomeInit(diagnostic);
|
|
|
13936
14163
|
var args = process.argv.slice(2);
|
|
13937
14164
|
var firstArg = args[0];
|
|
13938
14165
|
var skipWelcome = ["session", "start", "dashboard", "agent", "orch", "ask", "ui", "segment", "admin", "feedback", "help", "--help", "--version"];
|
|
13939
|
-
if (!
|
|
14166
|
+
if (!existsSync49(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
|
|
13940
14167
|
mkdirSync21(globalDir(), { recursive: true });
|
|
13941
14168
|
console.log("");
|
|
13942
14169
|
console.log(" Welcome to Sisyphus. Run 'sis admin install setup' to get started.");
|