sisyphi 1.2.19 → 1.2.21
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 +528 -283
- 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();
|
|
@@ -5910,12 +5956,30 @@ function registerStart(parent, root) {
|
|
|
5910
5956
|
if (root) buildStartCommand(root, true);
|
|
5911
5957
|
}
|
|
5912
5958
|
function buildStartCommand(target, hidden) {
|
|
5913
|
-
target.command("start", { hidden }).description("Start a new sisyphus session").argument("[task]", "Task description for the orchestrator (omit when using --stdin)").option("--context <context>", "Background context for the orchestrator").option("--name <name>", "Human-readable name for the session").option("--effort <tier>", "Pipeline effort tier (low|medium|high|xhigh)").option("--stdin", "Read the task description from stdin (avoids shell escaping for long prompts)").option("--context-stdin", "Read the context from stdin (mutually exclusive with --stdin)").option("--accept-cwd-mismatch", "Proceed even when invocation cwd differs from the current tmux session's home (linking will be inconsistent)").addHelpText("after", `
|
|
5959
|
+
target.command("start", { hidden }).description("Start a new sisyphus session").argument("[task]", "Task description for the orchestrator (omit when using --stdin)").option("-c, --context <context>", "Background context for the orchestrator").option("--name <name>", "Human-readable name for the session").option("--effort <tier>", "Pipeline effort tier (low|medium|high|xhigh)").option("--stdin", "Read the task description from stdin (avoids shell escaping for long prompts)").option("--context-stdin", "Read the context from stdin (mutually exclusive with --stdin)").option("--accept-cwd-mismatch", "Proceed even when invocation cwd differs from the current tmux session's home (linking will be inconsistent)").addHelpText("after", `
|
|
5914
5960
|
session lifecycle start: launch a new orchestrated sisyphus session.
|
|
5915
5961
|
|
|
5962
|
+
Writing the handoff
|
|
5963
|
+
task the goal \u2014 what to build or fix and what "done" looks like. This is the
|
|
5964
|
+
persistent objective the orchestrator re-reads every cycle; keep it focused.
|
|
5965
|
+
-c/--context background that informs the work but isn't the goal itself: relevant file
|
|
5966
|
+
paths, constraints, specs, adjacent concerns, prior findings. Rendered
|
|
5967
|
+
apart from the task so the orchestrator references it without conflating it.
|
|
5968
|
+
Keep context factual, not diagnostic \u2014 point at files, areas, and constraints; don't
|
|
5969
|
+
speculate on root causes or fixes, which biases the orchestrator down the wrong path.
|
|
5970
|
+
|
|
5971
|
+
Example
|
|
5972
|
+
sis start "Fix the JWT refresh bug \u2014 app shows a blank screen on token expiry instead of redirecting to login" -c "Auth system lives in src/auth/. Key files: interceptor.ts (HTTP interceptor), token-store.ts (token persistence), refresh.ts (refresh flow). Tests in src/auth/__tests__/. Don't break the logout flow."
|
|
5973
|
+
|
|
5974
|
+
Long task or context? Pipe via stdin to avoid shell escaping:
|
|
5975
|
+
cat task.md | sis start --stdin -c "short context here"
|
|
5976
|
+
cat ctx.md | sis start "short task" --context-stdin
|
|
5977
|
+
The same --stdin / --context-stdin pattern exists on \`agent spawn\`, \`orch message\`,
|
|
5978
|
+
\`orch tell\`, \`session resume\`, and agent-side \`agent submit\` / \`agent report\` / \`orch yield\`.
|
|
5979
|
+
|
|
5916
5980
|
Input
|
|
5917
5981
|
[task] optional positional \u2014 task description string; omit when using --stdin or pass \`-\` to read stdin
|
|
5918
|
-
--context <context>
|
|
5982
|
+
-c, --context <context> optional \u2014 background context injected alongside the task; also readable via --context-stdin
|
|
5919
5983
|
--name <name> optional \u2014 human-readable label for the session
|
|
5920
5984
|
--effort <tier> optional \u2014 pipeline effort tier: low | medium | high | xhigh
|
|
5921
5985
|
--stdin optional \u2014 read task from stdin instead of positional; mutually exclusive with --context-stdin
|
|
@@ -6102,6 +6166,27 @@ function openDashboardWindow(tmuxSession, cwd) {
|
|
|
6102
6166
|
}
|
|
6103
6167
|
} catch {
|
|
6104
6168
|
}
|
|
6169
|
+
try {
|
|
6170
|
+
const windows = execSync6(
|
|
6171
|
+
`tmux list-windows -t ${shellQuote(tmuxSession)} -F "#{window_id} #{window_name}"`,
|
|
6172
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6173
|
+
).trim();
|
|
6174
|
+
for (const line of windows.split("\n").filter(Boolean)) {
|
|
6175
|
+
const spaceIdx = line.indexOf(" ");
|
|
6176
|
+
if (spaceIdx < 0) continue;
|
|
6177
|
+
const wid = line.slice(0, spaceIdx);
|
|
6178
|
+
const wname = line.slice(spaceIdx + 1);
|
|
6179
|
+
if (wname === "sisyphus-dashboard") {
|
|
6180
|
+
execSync6(
|
|
6181
|
+
`tmux set-option -t ${shellQuote(tmuxSession)} @sisyphus_dashboard ${shellQuote(wid)}`,
|
|
6182
|
+
{ stdio: "pipe" }
|
|
6183
|
+
);
|
|
6184
|
+
execSync6(`tmux select-window -t ${shellQuote(wid)}`, { stdio: "pipe" });
|
|
6185
|
+
return false;
|
|
6186
|
+
}
|
|
6187
|
+
}
|
|
6188
|
+
} catch {
|
|
6189
|
+
}
|
|
6105
6190
|
const tuiPath = join6(import.meta.dirname, "tui.js");
|
|
6106
6191
|
const windowId = execSync6(
|
|
6107
6192
|
`tmux new-window -t ${shellQuote(tmuxSession + ":")} -n "sisyphus-dashboard" -c ${shellQuote(cwd)} -P -F "#{window_id}"`,
|
|
@@ -6570,8 +6655,8 @@ Exit codes: 0 ok | 2 usage (missing content or --session) | 3 not_found.`
|
|
|
6570
6655
|
}
|
|
6571
6656
|
|
|
6572
6657
|
// src/cli/commands/ask.ts
|
|
6573
|
-
import { existsSync as existsSync12, readFileSync as
|
|
6574
|
-
import { basename as basename4, join as
|
|
6658
|
+
import { existsSync as existsSync12, readFileSync as readFileSync13, unlinkSync as unlinkSync5, watchFile, unwatchFile } from "fs";
|
|
6659
|
+
import { basename as basename4, join as join14, resolve as resolve5 } from "path";
|
|
6575
6660
|
import { ulid } from "ulid";
|
|
6576
6661
|
|
|
6577
6662
|
// src/shared/ask-schema.ts
|
|
@@ -6634,6 +6719,125 @@ init_state();
|
|
|
6634
6719
|
init_types();
|
|
6635
6720
|
init_exec();
|
|
6636
6721
|
init_shell();
|
|
6722
|
+
init_platform();
|
|
6723
|
+
|
|
6724
|
+
// src/cli/commands/ask-popup.ts
|
|
6725
|
+
init_exec();
|
|
6726
|
+
init_shell();
|
|
6727
|
+
import { writeFileSync as writeFileSync8, readFileSync as readFileSync12, unlinkSync as unlinkSync4 } from "fs";
|
|
6728
|
+
import { tmpdir } from "os";
|
|
6729
|
+
import { join as join13 } from "path";
|
|
6730
|
+
var POPUP_WIDTH = 46;
|
|
6731
|
+
var INNER_WIDTH = POPUP_WIDTH - 6;
|
|
6732
|
+
var POPUP_TIMEOUT = 10;
|
|
6733
|
+
var ACCENT_COLOR = "colour214";
|
|
6734
|
+
function wrapText(text, width) {
|
|
6735
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
6736
|
+
const lines = [];
|
|
6737
|
+
let current = "";
|
|
6738
|
+
for (const word of words) {
|
|
6739
|
+
if (current && current.length + 1 + word.length > width) {
|
|
6740
|
+
lines.push(current);
|
|
6741
|
+
current = word;
|
|
6742
|
+
} else {
|
|
6743
|
+
current = current ? `${current} ${word}` : word;
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
if (current) lines.push(current);
|
|
6747
|
+
return lines.length > 0 ? lines : [""];
|
|
6748
|
+
}
|
|
6749
|
+
function hasAttachedClient() {
|
|
6750
|
+
const out = execSafe('tmux list-clients -F "#{client_name}"');
|
|
6751
|
+
return !!out && out.split("\n").filter(Boolean).length > 0;
|
|
6752
|
+
}
|
|
6753
|
+
function showAskTriagePopup(opts) {
|
|
6754
|
+
const scriptPath2 = join13(tmpdir(), `sisyphus-ask-${opts.askId}.sh`);
|
|
6755
|
+
const resultPath = join13(tmpdir(), `sisyphus-ask-${opts.askId}.result`);
|
|
6756
|
+
try {
|
|
6757
|
+
const safeTitle = opts.title.trim().length > 0 ? opts.title : "Question pending";
|
|
6758
|
+
const titleLines = wrapText(safeTitle, INNER_WIDTH);
|
|
6759
|
+
const bodyLines = [
|
|
6760
|
+
"",
|
|
6761
|
+
...titleLines.map((l) => ` ${l}`),
|
|
6762
|
+
"",
|
|
6763
|
+
" [1] Dismiss",
|
|
6764
|
+
" [2] Open in dashboard",
|
|
6765
|
+
" [3] Open in agent",
|
|
6766
|
+
"",
|
|
6767
|
+
` (auto-dismiss in ${POPUP_TIMEOUT}s)`,
|
|
6768
|
+
""
|
|
6769
|
+
];
|
|
6770
|
+
const content = bodyLines.join("\n") + "\n";
|
|
6771
|
+
const height = bodyLines.length + 2;
|
|
6772
|
+
const script = `#!/bin/sh
|
|
6773
|
+
printf '\\033[?25l'
|
|
6774
|
+
stty -echo 2>/dev/null
|
|
6775
|
+
RESULT_FILE=${shellQuote(resultPath)}
|
|
6776
|
+
printf '\\033[2J\\033[H'
|
|
6777
|
+
cat <<'__SISYPHUS_ASK_EOF__'
|
|
6778
|
+
${content}__SISYPHUS_ASK_EOF__
|
|
6779
|
+
while IFS= read -r -n1 -t ${POPUP_TIMEOUT} k; do
|
|
6780
|
+
case "$k" in
|
|
6781
|
+
1|d|D|q|Q|'') printf 'dismiss' > "$RESULT_FILE"; break ;;
|
|
6782
|
+
2|b|B) printf 'dashboard' > "$RESULT_FILE"; break ;;
|
|
6783
|
+
3|a|A) printf 'agent' > "$RESULT_FILE"; break ;;
|
|
6784
|
+
esac
|
|
6785
|
+
done
|
|
6786
|
+
if [ ! -f "$RESULT_FILE" ]; then printf 'dismiss' > "$RESULT_FILE"; fi
|
|
6787
|
+
`;
|
|
6788
|
+
writeFileSync8(scriptPath2, script, { mode: 493 });
|
|
6789
|
+
try {
|
|
6790
|
+
unlinkSync4(resultPath);
|
|
6791
|
+
} catch {
|
|
6792
|
+
}
|
|
6793
|
+
const popupTitle = ` \u2691 ${opts.sessionLabel} `;
|
|
6794
|
+
const clientsRaw = execSafe('tmux list-clients -F "#{client_name} #{client_width}"');
|
|
6795
|
+
if (!clientsRaw) return "dismiss";
|
|
6796
|
+
const lines = clientsRaw.split("\n").filter(Boolean);
|
|
6797
|
+
if (lines.length === 0) return "dismiss";
|
|
6798
|
+
let raw = null;
|
|
6799
|
+
for (const line of lines) {
|
|
6800
|
+
const lastSpace = line.lastIndexOf(" ");
|
|
6801
|
+
const client = line.slice(0, lastSpace);
|
|
6802
|
+
const clientWidth = parseInt(line.slice(lastSpace + 1), 10);
|
|
6803
|
+
if (!clientWidth) continue;
|
|
6804
|
+
const x = Math.max(0, clientWidth - POPUP_WIDTH - 1);
|
|
6805
|
+
const args2 = [
|
|
6806
|
+
`-c ${shellQuote(client)}`,
|
|
6807
|
+
"-E -b double",
|
|
6808
|
+
`-T ${shellQuote(popupTitle)}`,
|
|
6809
|
+
`-S "fg=${ACCENT_COLOR}"`,
|
|
6810
|
+
`-s "fg=${ACCENT_COLOR}"`,
|
|
6811
|
+
`-x ${x} -y 2`,
|
|
6812
|
+
`-w ${POPUP_WIDTH} -h ${height}`,
|
|
6813
|
+
shellQuote(scriptPath2)
|
|
6814
|
+
].join(" ");
|
|
6815
|
+
execSafe(`tmux display-popup ${args2}`);
|
|
6816
|
+
try {
|
|
6817
|
+
raw = readFileSync12(resultPath, "utf8").trim();
|
|
6818
|
+
} catch {
|
|
6819
|
+
raw = null;
|
|
6820
|
+
}
|
|
6821
|
+
if (raw) break;
|
|
6822
|
+
}
|
|
6823
|
+
if (raw === "dashboard") return "dashboard";
|
|
6824
|
+
if (raw === "agent") return "agent";
|
|
6825
|
+
return "dismiss";
|
|
6826
|
+
} catch {
|
|
6827
|
+
return "dismiss";
|
|
6828
|
+
} finally {
|
|
6829
|
+
try {
|
|
6830
|
+
unlinkSync4(resultPath);
|
|
6831
|
+
} catch {
|
|
6832
|
+
}
|
|
6833
|
+
try {
|
|
6834
|
+
unlinkSync4(scriptPath2);
|
|
6835
|
+
} catch {
|
|
6836
|
+
}
|
|
6837
|
+
}
|
|
6838
|
+
}
|
|
6839
|
+
|
|
6840
|
+
// src/cli/commands/ask.ts
|
|
6637
6841
|
import { approveDeck, notifyDeck, launchReview, display } from "@crouton-kit/humanloop";
|
|
6638
6842
|
var ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
6639
6843
|
function validateAskId(askId) {
|
|
@@ -7025,7 +7229,7 @@ async function markAnswered(cwd, sessionId, askId) {
|
|
|
7025
7229
|
function waitForOutput(cwd, sessionId, askId, initialPpid) {
|
|
7026
7230
|
const outputPath = askOutputPath(cwd, sessionId, askId);
|
|
7027
7231
|
if (existsSync12(outputPath)) {
|
|
7028
|
-
return Promise.resolve(JSON.parse(
|
|
7232
|
+
return Promise.resolve(JSON.parse(readFileSync13(outputPath, "utf-8")));
|
|
7029
7233
|
}
|
|
7030
7234
|
return new Promise((res, _rej) => {
|
|
7031
7235
|
let ppidWatcher;
|
|
@@ -7037,7 +7241,7 @@ function waitForOutput(cwd, sessionId, askId, initialPpid) {
|
|
|
7037
7241
|
const onChange = () => {
|
|
7038
7242
|
if (!existsSync12(outputPath)) return;
|
|
7039
7243
|
try {
|
|
7040
|
-
const out = JSON.parse(
|
|
7244
|
+
const out = JSON.parse(readFileSync13(outputPath, "utf-8"));
|
|
7041
7245
|
cleanup();
|
|
7042
7246
|
res(out);
|
|
7043
7247
|
} catch (err) {
|
|
@@ -7064,22 +7268,86 @@ function waitForOutput(cwd, sessionId, askId, initialPpid) {
|
|
|
7064
7268
|
process.once("SIGINT", onSigint);
|
|
7065
7269
|
});
|
|
7066
7270
|
}
|
|
7271
|
+
function focusAgentPane(callerPane) {
|
|
7272
|
+
const sess = execSafe(`tmux display-message -p -t ${shellQuote(callerPane)} "#{session_name}"`)?.trim();
|
|
7273
|
+
const win = execSafe(`tmux display-message -p -t ${shellQuote(callerPane)} "#{window_id}"`)?.trim();
|
|
7274
|
+
if (sess) switchAllClientsToSession(sess);
|
|
7275
|
+
if (win) execSafe(`tmux select-window -t ${shellQuote(win)}`);
|
|
7276
|
+
}
|
|
7277
|
+
function switchAllClientsToSession(session2) {
|
|
7278
|
+
const clients = execSafe('tmux list-clients -F "#{client_name}"');
|
|
7279
|
+
if (!clients) return;
|
|
7280
|
+
for (const c of clients.split("\n").filter(Boolean)) {
|
|
7281
|
+
execSafe(`tmux switch-client -c ${shellQuote(c)} -t ${shellQuote(session2)}`);
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7067
7284
|
function maybeSpawnAskPane(cwd, sessionId, askId, kind) {
|
|
7068
7285
|
if (kind === "review") return;
|
|
7069
7286
|
const callerPane = process.env.TMUX_PANE;
|
|
7070
7287
|
if (!callerPane) return;
|
|
7071
7288
|
if (process.env.SISYPHUS_DISABLE_ASK_PANE === "1") return;
|
|
7072
|
-
const tuiPath =
|
|
7289
|
+
const tuiPath = join14(import.meta.dirname, "tui.js");
|
|
7073
7290
|
const cmd = `node ${shellQuote(tuiPath)} --cwd ${shellQuote(cwd)} --session-id ${shellQuote(sessionId)} --ask ${shellQuote(askId)}`;
|
|
7074
|
-
execSafe(`tmux split-window -
|
|
7291
|
+
execSafe(`tmux split-window -h -t ${shellQuote(callerPane)} -c ${shellQuote(cwd)} ${shellQuote(cmd)}`);
|
|
7292
|
+
focusAgentPane(callerPane);
|
|
7075
7293
|
}
|
|
7076
7294
|
function maybeSpawnReviewPane(cwd, sessionId, askId) {
|
|
7077
7295
|
const callerPane = process.env.TMUX_PANE;
|
|
7078
7296
|
if (!callerPane) return;
|
|
7079
7297
|
if (process.env.SISYPHUS_DISABLE_ASK_PANE === "1") return;
|
|
7080
|
-
const cliPath =
|
|
7298
|
+
const cliPath = join14(import.meta.dirname, "cli.js");
|
|
7081
7299
|
const cmd = `node ${shellQuote(cliPath)} ask review open ${shellQuote(askId)} --session ${shellQuote(sessionId)}`;
|
|
7082
7300
|
execSafe(`tmux split-window -h -t ${shellQuote(callerPane)} -c ${shellQuote(cwd)} ${shellQuote(cmd)}`);
|
|
7301
|
+
focusAgentPane(callerPane);
|
|
7302
|
+
}
|
|
7303
|
+
function sessionLabel(cwd, sessionId) {
|
|
7304
|
+
try {
|
|
7305
|
+
if (existsSync12(statePath(cwd, sessionId))) {
|
|
7306
|
+
const s = getSession(cwd, sessionId);
|
|
7307
|
+
if (s.name) return s.name;
|
|
7308
|
+
}
|
|
7309
|
+
} catch {
|
|
7310
|
+
}
|
|
7311
|
+
return sessionId.slice(0, 8);
|
|
7312
|
+
}
|
|
7313
|
+
function shouldSuppressBanner() {
|
|
7314
|
+
const popupWillShow = !!process.env.TMUX_PANE && process.env.SISYPHUS_DISABLE_ASK_PANE !== "1" && hasAttachedClient();
|
|
7315
|
+
return popupWillShow && isTerminalFrontmost();
|
|
7316
|
+
}
|
|
7317
|
+
async function triageAsk(cwd, sessionId, askId, kind, title) {
|
|
7318
|
+
const callerPane = process.env.TMUX_PANE;
|
|
7319
|
+
if (!callerPane) return;
|
|
7320
|
+
if (process.env.SISYPHUS_DISABLE_ASK_PANE === "1") return;
|
|
7321
|
+
const choice = showAskTriagePopup({
|
|
7322
|
+
askId,
|
|
7323
|
+
sessionLabel: sessionLabel(cwd, sessionId),
|
|
7324
|
+
title: title !== void 0 ? title : "Question pending"
|
|
7325
|
+
});
|
|
7326
|
+
if (choice === "dismiss") return;
|
|
7327
|
+
if (choice === "agent") {
|
|
7328
|
+
if (kind === "review") maybeSpawnReviewPane(cwd, sessionId, askId);
|
|
7329
|
+
else maybeSpawnAskPane(cwd, sessionId, askId, kind);
|
|
7330
|
+
return;
|
|
7331
|
+
}
|
|
7332
|
+
await openInDashboard(cwd, sessionId, askId, kind);
|
|
7333
|
+
}
|
|
7334
|
+
async function openInDashboard(cwd, sessionId, askId, kind) {
|
|
7335
|
+
try {
|
|
7336
|
+
assertTmux();
|
|
7337
|
+
const tmuxSession = findHomeSession(cwd) ?? getTmuxSession();
|
|
7338
|
+
if (kind === "review") {
|
|
7339
|
+
openDashboardWindow(tmuxSession, cwd);
|
|
7340
|
+
switchAllClientsToSession(tmuxSession);
|
|
7341
|
+
const cliPath = join14(import.meta.dirname, "cli.js");
|
|
7342
|
+
const cmd = `node ${shellQuote(cliPath)} ask review open ${shellQuote(askId)} --session ${shellQuote(sessionId)}`;
|
|
7343
|
+
execSafe(`tmux display-popup -E -w 90% -h 90% -d ${shellQuote(cwd)} ${shellQuote(cmd)}`);
|
|
7344
|
+
return;
|
|
7345
|
+
}
|
|
7346
|
+
await rawSend({ type: "focus-set", cwd, sessionId, askId });
|
|
7347
|
+
openDashboardWindow(tmuxSession, cwd);
|
|
7348
|
+
switchAllClientsToSession(tmuxSession);
|
|
7349
|
+
} catch {
|
|
7350
|
+
}
|
|
7083
7351
|
}
|
|
7084
7352
|
async function submitDeck(deck, opts, options) {
|
|
7085
7353
|
const blocking = options?.blocking !== false;
|
|
@@ -7089,6 +7357,8 @@ async function submitDeck(deck, opts, options) {
|
|
|
7089
7357
|
const claudeSessionId = resolveClaudeSessionId(cwd, sessionId, askedBy);
|
|
7090
7358
|
const askId = mintAskId();
|
|
7091
7359
|
const q0 = deck.interactions[0];
|
|
7360
|
+
const askTitle = deck.title !== void 0 ? deck.title : q0?.title;
|
|
7361
|
+
const suppressTerminalNotification = blocking ? shouldSuppressBanner() : false;
|
|
7092
7362
|
createAsk(cwd, sessionId, {
|
|
7093
7363
|
askId,
|
|
7094
7364
|
askedBy,
|
|
@@ -7096,15 +7366,16 @@ async function submitDeck(deck, opts, options) {
|
|
|
7096
7366
|
pid: process.pid,
|
|
7097
7367
|
claudeSessionId,
|
|
7098
7368
|
cwd,
|
|
7099
|
-
title:
|
|
7369
|
+
title: askTitle,
|
|
7100
7370
|
subtitle: q0?.subtitle,
|
|
7101
|
-
kind: options?.kindOverride !== void 0 ? options.kindOverride : q0?.kind
|
|
7371
|
+
kind: options?.kindOverride !== void 0 ? options.kindOverride : q0?.kind,
|
|
7372
|
+
suppressTerminalNotification
|
|
7102
7373
|
});
|
|
7103
7374
|
writeDecisions(cwd, sessionId, askId, deck);
|
|
7104
7375
|
if (!blocking) {
|
|
7105
7376
|
return { askId };
|
|
7106
7377
|
}
|
|
7107
|
-
|
|
7378
|
+
await triageAsk(cwd, sessionId, askId, q0?.kind, askTitle);
|
|
7108
7379
|
const output = await waitForOutput(cwd, sessionId, askId, initialPpid);
|
|
7109
7380
|
await markAnswered(cwd, sessionId, askId);
|
|
7110
7381
|
return { askId, output };
|
|
@@ -7157,6 +7428,8 @@ async function submitReview(absFile, opts, options) {
|
|
|
7157
7428
|
const initialPpid = process.ppid;
|
|
7158
7429
|
const claudeSessionId = resolveClaudeSessionId(cwd, sessionId, askedBy);
|
|
7159
7430
|
const askId = mintAskId();
|
|
7431
|
+
const reviewTitle = `Review ${basename4(absFile)}`;
|
|
7432
|
+
const suppressTerminalNotification = blocking ? shouldSuppressBanner() : false;
|
|
7160
7433
|
createAsk(cwd, sessionId, {
|
|
7161
7434
|
askId,
|
|
7162
7435
|
askedBy,
|
|
@@ -7164,12 +7437,13 @@ async function submitReview(absFile, opts, options) {
|
|
|
7164
7437
|
pid: process.pid,
|
|
7165
7438
|
claudeSessionId,
|
|
7166
7439
|
cwd,
|
|
7167
|
-
title:
|
|
7168
|
-
kind: "review"
|
|
7440
|
+
title: reviewTitle,
|
|
7441
|
+
kind: "review",
|
|
7442
|
+
suppressTerminalNotification
|
|
7169
7443
|
});
|
|
7170
7444
|
writeReview(cwd, sessionId, askId, { file: absFile });
|
|
7171
7445
|
if (!blocking) return { askId };
|
|
7172
|
-
|
|
7446
|
+
await triageAsk(cwd, sessionId, askId, "review", reviewTitle);
|
|
7173
7447
|
const output = await waitForOutput(cwd, sessionId, askId, initialPpid);
|
|
7174
7448
|
await markAnswered(cwd, sessionId, askId);
|
|
7175
7449
|
return { askId, output };
|
|
@@ -7201,7 +7475,7 @@ async function reviewOpen(askId, opts) {
|
|
|
7201
7475
|
if (!reviewData) exitUsage("missing-review-pointer", `review.json missing for ${askId}`, { received: askId });
|
|
7202
7476
|
const draftPath = askReviewDraftPath(cwd, sessionId, askId);
|
|
7203
7477
|
const flagPath = askReviewSubmitFlagPath(cwd, sessionId, askId);
|
|
7204
|
-
if (existsSync12(flagPath))
|
|
7478
|
+
if (existsSync12(flagPath)) unlinkSync5(flagPath);
|
|
7205
7479
|
const result = await launchReview(reviewData.file, {
|
|
7206
7480
|
output: draftPath,
|
|
7207
7481
|
submitFlagPath: flagPath,
|
|
@@ -7227,7 +7501,7 @@ async function reviewSubmit(askId, opts) {
|
|
|
7227
7501
|
let comments = [];
|
|
7228
7502
|
if (existsSync12(draftPath)) {
|
|
7229
7503
|
try {
|
|
7230
|
-
const draft = JSON.parse(
|
|
7504
|
+
const draft = JSON.parse(readFileSync13(draftPath, "utf-8"));
|
|
7231
7505
|
if (Array.isArray(draft.comments)) comments = draft.comments;
|
|
7232
7506
|
} catch {
|
|
7233
7507
|
}
|
|
@@ -7344,7 +7618,7 @@ async function peek(askId, opts) {
|
|
|
7344
7618
|
if (meta.completedAt) result.completedAt = meta.completedAt;
|
|
7345
7619
|
try {
|
|
7346
7620
|
if (existsSync12(outputPath)) {
|
|
7347
|
-
result.output = JSON.parse(
|
|
7621
|
+
result.output = JSON.parse(readFileSync13(outputPath, "utf-8"));
|
|
7348
7622
|
}
|
|
7349
7623
|
} catch (err) {
|
|
7350
7624
|
if (!(err instanceof SyntaxError)) throw err;
|
|
@@ -7756,13 +8030,13 @@ Exit codes: 0 ok | 2 usage (bad state) | 3 not_found.`
|
|
|
7756
8030
|
|
|
7757
8031
|
// src/tui/lib/context.ts
|
|
7758
8032
|
init_paths();
|
|
7759
|
-
import { readFileSync as
|
|
8033
|
+
import { readFileSync as readFileSync15, readdirSync as readdirSync4 } from "fs";
|
|
7760
8034
|
|
|
7761
8035
|
// src/tui/lib/reports.ts
|
|
7762
|
-
import { readFileSync as
|
|
8036
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
7763
8037
|
function loadReportContent(report) {
|
|
7764
8038
|
try {
|
|
7765
|
-
return
|
|
8039
|
+
return readFileSync14(report.filePath, "utf-8");
|
|
7766
8040
|
} catch {
|
|
7767
8041
|
return report.summary;
|
|
7768
8042
|
}
|
|
@@ -7779,7 +8053,7 @@ function resolveReports(reports) {
|
|
|
7779
8053
|
// src/tui/lib/context.ts
|
|
7780
8054
|
function readFileSafe(filePath) {
|
|
7781
8055
|
try {
|
|
7782
|
-
return
|
|
8056
|
+
return readFileSync15(filePath, "utf-8");
|
|
7783
8057
|
} catch {
|
|
7784
8058
|
return null;
|
|
7785
8059
|
}
|
|
@@ -7958,7 +8232,7 @@ Exit codes: 0 ok | 3 not_found (unknown session).`
|
|
|
7958
8232
|
|
|
7959
8233
|
// src/cli/commands/spawn.ts
|
|
7960
8234
|
import { existsSync as existsSync13 } from "fs";
|
|
7961
|
-
import { join as
|
|
8235
|
+
import { join as join15 } from "path";
|
|
7962
8236
|
function registerSpawn(program2) {
|
|
7963
8237
|
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
8238
|
"after",
|
|
@@ -8035,7 +8309,7 @@ Exit codes: 0 ok | 2 usage | 3 not_found (unknown session) | 5 conflict.`
|
|
|
8035
8309
|
});
|
|
8036
8310
|
}
|
|
8037
8311
|
if (opts.repo && opts.repo !== ".") {
|
|
8038
|
-
const repoPath =
|
|
8312
|
+
const repoPath = join15(sisyphusCwd, opts.repo);
|
|
8039
8313
|
if (!existsSync13(repoPath)) {
|
|
8040
8314
|
exitError({
|
|
8041
8315
|
code: "repo_not_found",
|
|
@@ -8221,7 +8495,7 @@ Exit codes: 0 ok | 2 usage | 3 not_found | 60 transient.`
|
|
|
8221
8495
|
}
|
|
8222
8496
|
|
|
8223
8497
|
// src/cli/commands/await.ts
|
|
8224
|
-
import { existsSync as existsSync16, readFileSync as
|
|
8498
|
+
import { existsSync as existsSync16, readFileSync as readFileSync18 } from "fs";
|
|
8225
8499
|
var AWAIT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
|
|
8226
8500
|
function registerAwait(program2) {
|
|
8227
8501
|
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 +8535,7 @@ Exit codes: 0 ok | 2 usage (missing --session) | 3 not_found (unknown agent) | 6
|
|
|
8261
8535
|
let report = "";
|
|
8262
8536
|
if (reportPath && existsSync16(reportPath)) {
|
|
8263
8537
|
try {
|
|
8264
|
-
report =
|
|
8538
|
+
report = readFileSync18(reportPath, "utf-8");
|
|
8265
8539
|
} catch (err) {
|
|
8266
8540
|
process.stderr.write(`Warning: could not read report at ${reportPath}: ${err instanceof Error ? err.message : err}
|
|
8267
8541
|
`);
|
|
@@ -8466,15 +8740,15 @@ import { execSync as execSync10 } from "child_process";
|
|
|
8466
8740
|
|
|
8467
8741
|
// src/cli/onboard.ts
|
|
8468
8742
|
import { execSync as execSync9 } from "child_process";
|
|
8469
|
-
import { existsSync as existsSync17, readFileSync as
|
|
8743
|
+
import { existsSync as existsSync17, readFileSync as readFileSync19, writeFileSync as writeFileSync10 } from "fs";
|
|
8470
8744
|
import { homedir as homedir9 } from "os";
|
|
8471
|
-
import { dirname as dirname5, join as
|
|
8745
|
+
import { dirname as dirname5, join as join18 } from "path";
|
|
8472
8746
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8473
8747
|
init_platform();
|
|
8474
8748
|
|
|
8475
8749
|
// src/shared/clipboard.ts
|
|
8476
8750
|
init_platform();
|
|
8477
|
-
import { execFileSync as
|
|
8751
|
+
import { execFileSync as execFileSync3, spawnSync } from "child_process";
|
|
8478
8752
|
function detectClipboard() {
|
|
8479
8753
|
const platform = detectPlatform();
|
|
8480
8754
|
if (platform === "darwin") {
|
|
@@ -8565,7 +8839,7 @@ function checkItermOptionKey() {
|
|
|
8565
8839
|
if (process.platform !== "darwin") {
|
|
8566
8840
|
return { checked: false, allCorrect: true, incorrectProfiles: [] };
|
|
8567
8841
|
}
|
|
8568
|
-
const plistPath2 =
|
|
8842
|
+
const plistPath2 = join18(homedir9(), "Library", "Preferences", "com.googlecode.iterm2.plist");
|
|
8569
8843
|
if (!existsSync17(plistPath2)) {
|
|
8570
8844
|
return { checked: false, allCorrect: false, incorrectProfiles: [] };
|
|
8571
8845
|
}
|
|
@@ -8590,7 +8864,7 @@ function checkItermOptionKey() {
|
|
|
8590
8864
|
}
|
|
8591
8865
|
}
|
|
8592
8866
|
function hasExistingTmuxConf() {
|
|
8593
|
-
return existsSync17(
|
|
8867
|
+
return existsSync17(join18(homedir9(), ".tmux.conf")) || existsSync17(join18(homedir9(), ".config", "tmux", "tmux.conf"));
|
|
8594
8868
|
}
|
|
8595
8869
|
var SISYPHUS_DEFAULTS_MARKER = "# sisyphus-managed \u2014 do not edit";
|
|
8596
8870
|
function buildTmuxDefaults() {
|
|
@@ -8702,8 +8976,8 @@ source-file -q ${sisyphusConf} ${SISYPHUS_DEFAULTS_MARKER}
|
|
|
8702
8976
|
`;
|
|
8703
8977
|
}
|
|
8704
8978
|
function writeTmuxDefaults() {
|
|
8705
|
-
const confPath =
|
|
8706
|
-
|
|
8979
|
+
const confPath = join18(homedir9(), ".tmux.conf");
|
|
8980
|
+
writeFileSync10(confPath, buildTmuxDefaults(), "utf8");
|
|
8707
8981
|
}
|
|
8708
8982
|
function isNvimAvailable() {
|
|
8709
8983
|
try {
|
|
@@ -8721,21 +8995,21 @@ function getNvimVersion() {
|
|
|
8721
8995
|
}
|
|
8722
8996
|
}
|
|
8723
8997
|
function hasLazyVimConfig() {
|
|
8724
|
-
return existsSync17(
|
|
8998
|
+
return existsSync17(join18(homedir9(), ".config", "nvim", "lazy-lock.json"));
|
|
8725
8999
|
}
|
|
8726
9000
|
function bundledBaleiaPluginPath() {
|
|
8727
9001
|
const distDir = dirname5(fileURLToPath2(import.meta.url));
|
|
8728
|
-
return
|
|
9002
|
+
return join18(distDir, "templates", "baleia.lua");
|
|
8729
9003
|
}
|
|
8730
9004
|
function installBaleiaPlugin() {
|
|
8731
|
-
const pluginsDir =
|
|
9005
|
+
const pluginsDir = join18(homedir9(), ".config", "nvim", "lua", "plugins");
|
|
8732
9006
|
if (!existsSync17(pluginsDir)) return false;
|
|
8733
|
-
const dest =
|
|
9007
|
+
const dest = join18(pluginsDir, "sisyphus-baleia.lua");
|
|
8734
9008
|
if (existsSync17(dest)) return true;
|
|
8735
9009
|
const src = bundledBaleiaPluginPath();
|
|
8736
9010
|
if (!existsSync17(src)) return false;
|
|
8737
9011
|
try {
|
|
8738
|
-
|
|
9012
|
+
writeFileSync10(dest, readFileSync19(src, "utf-8"), "utf8");
|
|
8739
9013
|
return true;
|
|
8740
9014
|
} catch {
|
|
8741
9015
|
return false;
|
|
@@ -8758,7 +9032,7 @@ function tryAutoInstallNvim() {
|
|
|
8758
9032
|
if (!isNvimAvailable()) {
|
|
8759
9033
|
return { installed: false, autoInstalled: false, version: "", lazyVimInstalled: false, baleiaInstalled: false };
|
|
8760
9034
|
}
|
|
8761
|
-
const nvimConfigDir =
|
|
9035
|
+
const nvimConfigDir = join18(homedir9(), ".config", "nvim");
|
|
8762
9036
|
let lazyVimInstalled = false;
|
|
8763
9037
|
if (!existsSync17(nvimConfigDir)) {
|
|
8764
9038
|
const cloneCmd = [
|
|
@@ -8776,7 +9050,7 @@ function tryAutoInstallNvim() {
|
|
|
8776
9050
|
stdio: "inherit",
|
|
8777
9051
|
env: { ...process.env, GIT_LFS_SKIP_SMUDGE: "1" }
|
|
8778
9052
|
});
|
|
8779
|
-
const gitDir =
|
|
9053
|
+
const gitDir = join18(nvimConfigDir, ".git");
|
|
8780
9054
|
if (existsSync17(gitDir)) {
|
|
8781
9055
|
execSync9(`rm -rf "${gitDir}"`, { stdio: "pipe" });
|
|
8782
9056
|
}
|
|
@@ -9063,7 +9337,7 @@ Exit codes: 0 ok | 1 conflict or requires-force`
|
|
|
9063
9337
|
|
|
9064
9338
|
// src/cli/commands/check-keybinds.ts
|
|
9065
9339
|
import { execSync as execSync11 } from "child_process";
|
|
9066
|
-
import { readFileSync as
|
|
9340
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
9067
9341
|
function isTmuxInstalled2() {
|
|
9068
9342
|
try {
|
|
9069
9343
|
execSync11("which tmux", { stdio: "pipe" });
|
|
@@ -9116,7 +9390,7 @@ function runCheck() {
|
|
|
9116
9390
|
let userConfAlreadySources = false;
|
|
9117
9391
|
if (userConfPath !== null) {
|
|
9118
9392
|
try {
|
|
9119
|
-
userConfAlreadySources =
|
|
9393
|
+
userConfAlreadySources = readFileSync20(userConfPath, "utf-8").includes(sisyphusConfPath);
|
|
9120
9394
|
} catch {
|
|
9121
9395
|
}
|
|
9122
9396
|
}
|
|
@@ -9287,9 +9561,9 @@ Exit codes: 0 ok`
|
|
|
9287
9561
|
// src/cli/commands/check-statusbar.ts
|
|
9288
9562
|
init_paths();
|
|
9289
9563
|
import { execSync as execSync12 } from "child_process";
|
|
9290
|
-
import { existsSync as existsSync18, readFileSync as
|
|
9564
|
+
import { existsSync as existsSync18, readFileSync as readFileSync21 } from "fs";
|
|
9291
9565
|
import { homedir as homedir10 } from "os";
|
|
9292
|
-
import { join as
|
|
9566
|
+
import { join as join19 } from "path";
|
|
9293
9567
|
var SISYPHUS_LEFT_TOKEN = "@sisyphus_left";
|
|
9294
9568
|
var SISYPHUS_RIGHT_TOKEN = "@sisyphus_right";
|
|
9295
9569
|
var TMUX_DEFAULT_STATUS_LEFT = "[#S] ";
|
|
@@ -9314,7 +9588,7 @@ function isDaemonRunning() {
|
|
|
9314
9588
|
const pidFile = daemonPidPath();
|
|
9315
9589
|
if (!existsSync18(pidFile)) return false;
|
|
9316
9590
|
try {
|
|
9317
|
-
const pid = parseInt(
|
|
9591
|
+
const pid = parseInt(readFileSync21(pidFile, "utf-8").trim(), 10);
|
|
9318
9592
|
if (Number.isNaN(pid) || pid <= 0) return false;
|
|
9319
9593
|
process.kill(pid, 0);
|
|
9320
9594
|
return true;
|
|
@@ -9357,8 +9631,8 @@ function probeTmuxOptions(serverRunning) {
|
|
|
9357
9631
|
};
|
|
9358
9632
|
}
|
|
9359
9633
|
function findUserTmuxConf() {
|
|
9360
|
-
const xdg =
|
|
9361
|
-
const dotfile =
|
|
9634
|
+
const xdg = join19(homedir10(), ".config", "tmux", "tmux.conf");
|
|
9635
|
+
const dotfile = join19(homedir10(), ".tmux.conf");
|
|
9362
9636
|
if (existsSync18(xdg)) return xdg;
|
|
9363
9637
|
if (existsSync18(dotfile)) return dotfile;
|
|
9364
9638
|
return null;
|
|
@@ -9370,21 +9644,21 @@ function probeUserConf() {
|
|
|
9370
9644
|
}
|
|
9371
9645
|
let contents = "";
|
|
9372
9646
|
try {
|
|
9373
|
-
contents =
|
|
9647
|
+
contents = readFileSync21(path, "utf-8");
|
|
9374
9648
|
} catch {
|
|
9375
9649
|
return { path, setsStatusLeft: false, setsStatusRight: false, sourcesSisyphusManaged: false };
|
|
9376
9650
|
}
|
|
9377
9651
|
const lines = contents.split("\n").filter((line) => !line.trim().startsWith("#"));
|
|
9378
9652
|
const setsStatusLeft = lines.some((line) => /^\s*(set|set-option)\s+-g(?:\s+-\w+)*\s+status-left\b/.test(line));
|
|
9379
9653
|
const setsStatusRight = lines.some((line) => /^\s*(set|set-option)\s+-g(?:\s+-\w+)*\s+status-right\b/.test(line));
|
|
9380
|
-
const sourcesSisyphusManaged = contents.includes(
|
|
9654
|
+
const sourcesSisyphusManaged = contents.includes(join19(homedir10(), ".sisyphus", "tmux.conf"));
|
|
9381
9655
|
return { path, setsStatusLeft, setsStatusRight, sourcesSisyphusManaged };
|
|
9382
9656
|
}
|
|
9383
9657
|
function loadGlobalSisyphusConfig() {
|
|
9384
9658
|
const path = globalConfigPath();
|
|
9385
9659
|
if (!existsSync18(path)) return null;
|
|
9386
9660
|
try {
|
|
9387
|
-
const parsed = JSON.parse(
|
|
9661
|
+
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
9388
9662
|
return parsed.statusBar === void 0 ? null : parsed.statusBar;
|
|
9389
9663
|
} catch {
|
|
9390
9664
|
return null;
|
|
@@ -9724,7 +9998,7 @@ init_paths();
|
|
|
9724
9998
|
import { execSync as execSync14 } from "child_process";
|
|
9725
9999
|
import { existsSync as existsSync19, statSync as statSync4 } from "fs";
|
|
9726
10000
|
import { homedir as homedir11 } from "os";
|
|
9727
|
-
import { join as
|
|
10001
|
+
import { join as join20 } from "path";
|
|
9728
10002
|
init_platform();
|
|
9729
10003
|
init_plugins();
|
|
9730
10004
|
function checkNodeVersion() {
|
|
@@ -9958,7 +10232,7 @@ function checkNvim() {
|
|
|
9958
10232
|
}
|
|
9959
10233
|
function checkNotifyBinary() {
|
|
9960
10234
|
if (process.platform === "darwin") {
|
|
9961
|
-
const binary =
|
|
10235
|
+
const binary = join20(homedir11(), ".sisyphus", "SisyphusNotify.app", "Contents", "MacOS", "sisyphus-notify");
|
|
9962
10236
|
if (existsSync19(binary)) {
|
|
9963
10237
|
return { name: "Notifications", status: "ok", detail: "SisyphusNotify.app built" };
|
|
9964
10238
|
}
|
|
@@ -10072,13 +10346,13 @@ init_version();
|
|
|
10072
10346
|
init_platform();
|
|
10073
10347
|
init_paths();
|
|
10074
10348
|
init_state();
|
|
10075
|
-
import { execFileSync as
|
|
10076
|
-
import { existsSync as existsSync20, readFileSync as
|
|
10349
|
+
import { execFileSync as execFileSync4, spawnSync as spawnSync2 } from "child_process";
|
|
10350
|
+
import { existsSync as existsSync20, readFileSync as readFileSync23 } from "fs";
|
|
10077
10351
|
import os from "os";
|
|
10078
10352
|
var REPO = "crouton-labs/sisyphus";
|
|
10079
10353
|
function tryCmd(bin, args2) {
|
|
10080
10354
|
try {
|
|
10081
|
-
const out =
|
|
10355
|
+
const out = execFileSync4(bin, args2, {
|
|
10082
10356
|
encoding: "utf-8",
|
|
10083
10357
|
stdio: ["ignore", "pipe", "ignore"],
|
|
10084
10358
|
timeout: 5e3
|
|
@@ -10134,7 +10408,7 @@ function tailLog(lines) {
|
|
|
10134
10408
|
const path = daemonLogPath();
|
|
10135
10409
|
if (!existsSync20(path)) return null;
|
|
10136
10410
|
try {
|
|
10137
|
-
const all =
|
|
10411
|
+
const all = readFileSync23(path, "utf-8").split("\n");
|
|
10138
10412
|
return all.slice(-lines).join("\n").trim() || null;
|
|
10139
10413
|
} catch {
|
|
10140
10414
|
return null;
|
|
@@ -10423,8 +10697,8 @@ Exit codes: 0 ok | 1 filing error | 2 usage | 60 cloud unreachable (retry-safe)`
|
|
|
10423
10697
|
}
|
|
10424
10698
|
|
|
10425
10699
|
// src/cli/commands/init.ts
|
|
10426
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as
|
|
10427
|
-
import { join as
|
|
10700
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync11 } from "fs";
|
|
10701
|
+
import { join as join21 } from "path";
|
|
10428
10702
|
var DEFAULT_CONFIG2 = {};
|
|
10429
10703
|
var ORCHESTRATOR_TEMPLATE = `# Custom Orchestrator Prompt
|
|
10430
10704
|
|
|
@@ -10453,19 +10727,19 @@ Effects
|
|
|
10453
10727
|
Exit codes: 0 ok`
|
|
10454
10728
|
).action((opts) => {
|
|
10455
10729
|
const cwd = process.cwd();
|
|
10456
|
-
const sisDir =
|
|
10457
|
-
const configPath =
|
|
10730
|
+
const sisDir = join21(cwd, ".sisyphus");
|
|
10731
|
+
const configPath = join21(sisDir, "config.json");
|
|
10458
10732
|
if (existsSync21(configPath)) {
|
|
10459
10733
|
console.log(`Already initialized: ${configPath}`);
|
|
10460
10734
|
return;
|
|
10461
10735
|
}
|
|
10462
10736
|
mkdirSync9(sisDir, { recursive: true });
|
|
10463
|
-
|
|
10737
|
+
writeFileSync11(configPath, JSON.stringify(DEFAULT_CONFIG2, null, 2) + "\n", "utf-8");
|
|
10464
10738
|
console.log(`Created ${configPath}`);
|
|
10465
10739
|
if (opts.orchestrator) {
|
|
10466
|
-
const orchPath =
|
|
10740
|
+
const orchPath = join21(sisDir, "orchestrator.md");
|
|
10467
10741
|
if (!existsSync21(orchPath)) {
|
|
10468
|
-
|
|
10742
|
+
writeFileSync11(orchPath, ORCHESTRATOR_TEMPLATE, "utf-8");
|
|
10469
10743
|
console.log(`Created ${orchPath}`);
|
|
10470
10744
|
}
|
|
10471
10745
|
}
|
|
@@ -10525,7 +10799,7 @@ Exit codes: 0 ok`
|
|
|
10525
10799
|
|
|
10526
10800
|
// src/cli/commands/configure-upload.ts
|
|
10527
10801
|
init_paths();
|
|
10528
|
-
import { chmodSync as chmodSync2, existsSync as existsSync22, mkdirSync as mkdirSync10, readFileSync as
|
|
10802
|
+
import { chmodSync as chmodSync2, existsSync as existsSync22, mkdirSync as mkdirSync10, readFileSync as readFileSync24, writeFileSync as writeFileSync12 } from "fs";
|
|
10529
10803
|
import { createInterface as createInterface3 } from "readline";
|
|
10530
10804
|
import { dirname as dirname6 } from "path";
|
|
10531
10805
|
async function readUrlFromInput(interactive) {
|
|
@@ -10606,7 +10880,7 @@ Exit codes: 0 ok | 1 validation or write error`
|
|
|
10606
10880
|
let existing = {};
|
|
10607
10881
|
if (existsSync22(configPath)) {
|
|
10608
10882
|
try {
|
|
10609
|
-
existing = JSON.parse(
|
|
10883
|
+
existing = JSON.parse(readFileSync24(configPath, "utf-8"));
|
|
10610
10884
|
} catch {
|
|
10611
10885
|
console.error(`Error: ${configPath} could not be parsed \u2014 fix or delete it first`);
|
|
10612
10886
|
process.exit(1);
|
|
@@ -10614,7 +10888,7 @@ Exit codes: 0 ok | 1 validation or write error`
|
|
|
10614
10888
|
}
|
|
10615
10889
|
const merged = { ...existing, upload: { url, token } };
|
|
10616
10890
|
mkdirSync10(dirname6(configPath), { recursive: true });
|
|
10617
|
-
|
|
10891
|
+
writeFileSync12(configPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
10618
10892
|
chmodSync2(configPath, 384);
|
|
10619
10893
|
console.log(`\u2713 upload configured (${configPath})`);
|
|
10620
10894
|
});
|
|
@@ -10623,10 +10897,10 @@ Exit codes: 0 ok | 1 validation or write error`
|
|
|
10623
10897
|
// src/cli/commands/getting-started.ts
|
|
10624
10898
|
import { execSync as execSync15 } from "child_process";
|
|
10625
10899
|
import { existsSync as existsSync23 } from "fs";
|
|
10626
|
-
import { dirname as dirname7, join as
|
|
10900
|
+
import { dirname as dirname7, join as join22 } from "path";
|
|
10627
10901
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
10628
10902
|
function templatePath(name) {
|
|
10629
|
-
return
|
|
10903
|
+
return join22(dirname7(fileURLToPath3(import.meta.url)), "templates", name);
|
|
10630
10904
|
}
|
|
10631
10905
|
function isClaudeCode() {
|
|
10632
10906
|
return !!process.env["CLAUDECODE"];
|
|
@@ -10993,7 +11267,7 @@ sis ui guide --tutorial 4
|
|
|
10993
11267
|
`);
|
|
10994
11268
|
}
|
|
10995
11269
|
function printStep4() {
|
|
10996
|
-
const demoPath =
|
|
11270
|
+
const demoPath = join22(process.cwd(), "tmp", "sisyphus-tutorial-demo");
|
|
10997
11271
|
console.log(`
|
|
10998
11272
|
<claude-instructions>
|
|
10999
11273
|
# Sisyphus Getting Started \u2014 Step 4: Demo Session
|
|
@@ -11037,7 +11311,7 @@ Have the user confirm they understand these keybinds before proceeding.
|
|
|
11037
11311
|
|
|
11038
11312
|
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
11313
|
\`\`\`
|
|
11040
|
-
mkdir -p ${
|
|
11314
|
+
mkdir -p ${join22(process.cwd(), "tmp")}
|
|
11041
11315
|
rm -rf ${demoPath}
|
|
11042
11316
|
cp -r ${templatePath("tutorial-demo")} ${demoPath}
|
|
11043
11317
|
git -C ${demoPath} init
|
|
@@ -11115,7 +11389,7 @@ sis ui guide --tutorial 5
|
|
|
11115
11389
|
`);
|
|
11116
11390
|
}
|
|
11117
11391
|
function printStep5() {
|
|
11118
|
-
const demoPath =
|
|
11392
|
+
const demoPath = join22(process.cwd(), "tmp", "sisyphus-tutorial-demo");
|
|
11119
11393
|
const demoExists = existsSync23(demoPath);
|
|
11120
11394
|
let recentCommits = "";
|
|
11121
11395
|
let topLevelFiles = "";
|
|
@@ -11220,9 +11494,9 @@ If they say yes, run:
|
|
|
11220
11494
|
rm -rf ${demoPath}
|
|
11221
11495
|
\`\`\`
|
|
11222
11496
|
|
|
11223
|
-
Then check whether \`${
|
|
11497
|
+
Then check whether \`${join22(process.cwd(), "tmp")}\` is now empty, and if so remove it too:
|
|
11224
11498
|
\`\`\`
|
|
11225
|
-
rmdir ${
|
|
11499
|
+
rmdir ${join22(process.cwd(), "tmp")} 2>/dev/null || true
|
|
11226
11500
|
\`\`\`
|
|
11227
11501
|
|
|
11228
11502
|
If they say no or want to explore first, leave it. They can clean up later with the same
|
|
@@ -11655,7 +11929,7 @@ Exit codes: 0 ok | 2 usage (invalid --tutorial value)`
|
|
|
11655
11929
|
|
|
11656
11930
|
// src/cli/commands/history.ts
|
|
11657
11931
|
init_paths();
|
|
11658
|
-
import { readdirSync as readdirSync7, readFileSync as
|
|
11932
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync25, existsSync as existsSync24 } from "fs";
|
|
11659
11933
|
import { resolve as resolve8 } from "path";
|
|
11660
11934
|
function loadAllSummaries() {
|
|
11661
11935
|
const base = historyBaseDir();
|
|
@@ -11665,7 +11939,7 @@ function loadAllSummaries() {
|
|
|
11665
11939
|
const summaryPath = historySessionSummaryPath(name);
|
|
11666
11940
|
if (existsSync24(summaryPath)) {
|
|
11667
11941
|
try {
|
|
11668
|
-
const raw =
|
|
11942
|
+
const raw = readFileSync25(summaryPath, "utf-8");
|
|
11669
11943
|
results.push({ id: name, summary: JSON.parse(raw) });
|
|
11670
11944
|
continue;
|
|
11671
11945
|
} catch {
|
|
@@ -11682,7 +11956,7 @@ function buildLiveSummary(sessionId) {
|
|
|
11682
11956
|
if (!existsSync24(eventsPath)) return null;
|
|
11683
11957
|
let cwd = null;
|
|
11684
11958
|
try {
|
|
11685
|
-
const lines =
|
|
11959
|
+
const lines = readFileSync25(eventsPath, "utf-8").split("\n");
|
|
11686
11960
|
for (const line of lines) {
|
|
11687
11961
|
if (!line.trim()) continue;
|
|
11688
11962
|
try {
|
|
@@ -11703,7 +11977,7 @@ function buildLiveSummary(sessionId) {
|
|
|
11703
11977
|
if (!existsSync24(sPath)) return null;
|
|
11704
11978
|
let session2;
|
|
11705
11979
|
try {
|
|
11706
|
-
session2 = JSON.parse(
|
|
11980
|
+
session2 = JSON.parse(readFileSync25(sPath, "utf-8"));
|
|
11707
11981
|
} catch {
|
|
11708
11982
|
return null;
|
|
11709
11983
|
}
|
|
@@ -11765,7 +12039,7 @@ function buildLiveSummary(sessionId) {
|
|
|
11765
12039
|
function loadEvents(sessionId) {
|
|
11766
12040
|
const eventsPath = historyEventsPath(sessionId);
|
|
11767
12041
|
if (!existsSync24(eventsPath)) return [];
|
|
11768
|
-
const lines =
|
|
12042
|
+
const lines = readFileSync25(eventsPath, "utf-8").split("\n").filter((l) => l.trim());
|
|
11769
12043
|
const events = [];
|
|
11770
12044
|
for (const line of lines) {
|
|
11771
12045
|
try {
|
|
@@ -11780,7 +12054,7 @@ function findSession(idOrName) {
|
|
|
11780
12054
|
const summaryPath = historySessionSummaryPath(idOrName);
|
|
11781
12055
|
if (existsSync24(summaryPath)) {
|
|
11782
12056
|
try {
|
|
11783
|
-
return { id: idOrName, summary: JSON.parse(
|
|
12057
|
+
return { id: idOrName, summary: JSON.parse(readFileSync25(summaryPath, "utf-8")) };
|
|
11784
12058
|
} catch {
|
|
11785
12059
|
}
|
|
11786
12060
|
}
|
|
@@ -12028,7 +12302,7 @@ Exit codes: 0 ok | 2 usage | 1 export_failed.`).action(async (sessionIdArg, opts
|
|
|
12028
12302
|
|
|
12029
12303
|
// src/cli/commands/upload.ts
|
|
12030
12304
|
import { rmSync as rmSync6 } from "fs";
|
|
12031
|
-
import { tmpdir } from "os";
|
|
12305
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
12032
12306
|
init_config();
|
|
12033
12307
|
init_session_export();
|
|
12034
12308
|
init_upload();
|
|
@@ -12084,7 +12358,7 @@ Exit codes: 0 ok | 1 upload or export error | 2 usage`
|
|
|
12084
12358
|
}
|
|
12085
12359
|
let zipPath;
|
|
12086
12360
|
try {
|
|
12087
|
-
zipPath = await exportSessionToZip(sessionId, cwd, { reveal: false, outputDir:
|
|
12361
|
+
zipPath = await exportSessionToZip(sessionId, cwd, { reveal: false, outputDir: tmpdir2() });
|
|
12088
12362
|
} catch (err) {
|
|
12089
12363
|
exitError({
|
|
12090
12364
|
code: "export_failed",
|
|
@@ -12149,7 +12423,7 @@ Exit codes: 0 ok | 1 upload or export error | 2 usage`
|
|
|
12149
12423
|
// src/cli/commands/scratch.ts
|
|
12150
12424
|
import { execSync as execSync16 } from "child_process";
|
|
12151
12425
|
init_shell();
|
|
12152
|
-
function
|
|
12426
|
+
function findHomeSession2(cwd) {
|
|
12153
12427
|
const normalizedCwd = cwd.replace(/\/+$/, "");
|
|
12154
12428
|
let output;
|
|
12155
12429
|
try {
|
|
@@ -12197,7 +12471,7 @@ Effects
|
|
|
12197
12471
|
Exit codes: 0 ok | 2 usage.`).action((promptParts, opts) => {
|
|
12198
12472
|
assertTmux();
|
|
12199
12473
|
const cwd = opts.cwd ?? process.env["SISYPHUS_CWD"] ?? process.cwd();
|
|
12200
|
-
const homeSession =
|
|
12474
|
+
const homeSession = findHomeSession2(cwd);
|
|
12201
12475
|
if (!homeSession) {
|
|
12202
12476
|
const current = execSync16('tmux display-message -p "#{session_name}"', {
|
|
12203
12477
|
encoding: "utf-8"
|
|
@@ -12225,15 +12499,15 @@ function openScratchWindow(tmuxSession, cwd, prompt) {
|
|
|
12225
12499
|
|
|
12226
12500
|
// src/cli/commands/review.ts
|
|
12227
12501
|
init_paths();
|
|
12228
|
-
import { join as
|
|
12229
|
-
import { existsSync as existsSync26, readFileSync as
|
|
12502
|
+
import { join as join24, resolve as resolve9, dirname as dirname8 } from "path";
|
|
12503
|
+
import { existsSync as existsSync26, readFileSync as readFileSync27, writeFileSync as writeFileSync14, renameSync as renameSync3, readdirSync as readdirSync8 } from "fs";
|
|
12230
12504
|
var _statusCheck = ["draft", "question", "approved", "rejected", "deferred"];
|
|
12231
12505
|
function resolveContextArtifact(file, opts, filename, notFoundMessage) {
|
|
12232
12506
|
const cwd = opts.cwd || process.env.SISYPHUS_CWD || process.cwd();
|
|
12233
12507
|
if (file) return resolve9(file);
|
|
12234
12508
|
const sessionId = opts.sessionId || process.env.SISYPHUS_SESSION_ID;
|
|
12235
12509
|
if (sessionId) {
|
|
12236
|
-
const target =
|
|
12510
|
+
const target = join24(contextDir(cwd, sessionId), filename);
|
|
12237
12511
|
if (!existsSync26(target)) {
|
|
12238
12512
|
exitUsage("file-not-found", `File not found: ${target}`, { received: target });
|
|
12239
12513
|
}
|
|
@@ -12243,7 +12517,7 @@ function resolveContextArtifact(file, opts, filename, notFoundMessage) {
|
|
|
12243
12517
|
if (existsSync26(dir)) {
|
|
12244
12518
|
const sessions = readdirSync8(dir);
|
|
12245
12519
|
for (const session2 of sessions.reverse()) {
|
|
12246
|
-
const candidate =
|
|
12520
|
+
const candidate = join24(dir, session2, "context", filename);
|
|
12247
12521
|
if (existsSync26(candidate)) return candidate;
|
|
12248
12522
|
}
|
|
12249
12523
|
}
|
|
@@ -12302,12 +12576,12 @@ Exit codes: 0 ok | 2 usage.`).action(async (file, opts) => {
|
|
|
12302
12576
|
if (!existsSync26(targetPath)) {
|
|
12303
12577
|
exitUsage("file-not-found", `File not found: ${targetPath}`, { received: targetPath });
|
|
12304
12578
|
}
|
|
12305
|
-
const parsed = JSON.parse(
|
|
12579
|
+
const parsed = JSON.parse(readFileSync27(targetPath, "utf-8"));
|
|
12306
12580
|
const rendered = renderRequirementsMarkdown(parsed);
|
|
12307
|
-
const outPath =
|
|
12581
|
+
const outPath = join24(dirname8(targetPath), "requirements.md");
|
|
12308
12582
|
const tmpPath = outPath + ".tmp";
|
|
12309
12583
|
if (existsSync26(outPath)) {
|
|
12310
|
-
const existing =
|
|
12584
|
+
const existing = readFileSync27(outPath, "utf-8");
|
|
12311
12585
|
if (existing !== rendered) {
|
|
12312
12586
|
if (!opts.force) {
|
|
12313
12587
|
exitUsage("conflict", `${outPath} has been hand-edited (differs from rendered output)`, {
|
|
@@ -12320,7 +12594,7 @@ Exit codes: 0 ok | 2 usage.`).action(async (file, opts) => {
|
|
|
12320
12594
|
`);
|
|
12321
12595
|
}
|
|
12322
12596
|
}
|
|
12323
|
-
|
|
12597
|
+
writeFileSync14(tmpPath, rendered, "utf-8");
|
|
12324
12598
|
renameSync3(tmpPath, outPath);
|
|
12325
12599
|
emitJsonOk({ path: resolve9(outPath) });
|
|
12326
12600
|
return;
|
|
@@ -12684,8 +12958,8 @@ function renderRequirementsMarkdown(json) {
|
|
|
12684
12958
|
}
|
|
12685
12959
|
|
|
12686
12960
|
// src/cli/commands/companion.ts
|
|
12687
|
-
import { basename as basename7, dirname as dirname11, join as
|
|
12688
|
-
import { mkdirSync as mkdirSync15, readFileSync as
|
|
12961
|
+
import { basename as basename7, dirname as dirname11, join as join29 } from "path";
|
|
12962
|
+
import { mkdirSync as mkdirSync15, readFileSync as readFileSync32, writeFileSync as writeFileSync19 } from "fs";
|
|
12689
12963
|
init_paths();
|
|
12690
12964
|
init_companion_types();
|
|
12691
12965
|
init_companion_memory();
|
|
@@ -12788,10 +13062,10 @@ Effects
|
|
|
12788
13062
|
Writes/updates ~/.sisyphus/companion-context-cache/<session-id>.json on each call that produces output.
|
|
12789
13063
|
|
|
12790
13064
|
Exit codes: 0 ok.`).action((opts) => {
|
|
12791
|
-
const cachePath =
|
|
13065
|
+
const cachePath = join29(globalDir(), "companion-context-cache", `${opts.sessionId}.json`);
|
|
12792
13066
|
let prev = {};
|
|
12793
13067
|
try {
|
|
12794
|
-
prev = JSON.parse(
|
|
13068
|
+
prev = JSON.parse(readFileSync32(cachePath, "utf-8"));
|
|
12795
13069
|
} catch {
|
|
12796
13070
|
prev = {};
|
|
12797
13071
|
}
|
|
@@ -12805,7 +13079,7 @@ Exit codes: 0 ok.`).action((opts) => {
|
|
|
12805
13079
|
process.stdout.write(renderFullContext(next));
|
|
12806
13080
|
}
|
|
12807
13081
|
mkdirSync15(dirname11(cachePath), { recursive: true });
|
|
12808
|
-
|
|
13082
|
+
writeFileSync19(cachePath, JSON.stringify(next), "utf-8");
|
|
12809
13083
|
});
|
|
12810
13084
|
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
13085
|
companion pane: open (or focus) the companion side pane next to the dashboard.
|
|
@@ -12909,7 +13183,7 @@ Exit codes: 0 ok | 3 not_found.`
|
|
|
12909
13183
|
}));
|
|
12910
13184
|
emitJsonOk({ ...companionData, badges });
|
|
12911
13185
|
} else {
|
|
12912
|
-
emitJsonOk(companionData);
|
|
13186
|
+
emitJsonOk({ ...companionData });
|
|
12913
13187
|
}
|
|
12914
13188
|
});
|
|
12915
13189
|
}
|
|
@@ -12919,7 +13193,7 @@ init_runner();
|
|
|
12919
13193
|
init_creds();
|
|
12920
13194
|
init_tailscale();
|
|
12921
13195
|
import { homedir as homedir13 } from "os";
|
|
12922
|
-
import { join as
|
|
13196
|
+
import { join as join30 } from "path";
|
|
12923
13197
|
function assertArch(raw) {
|
|
12924
13198
|
if (raw === "arm" || raw === "x86") return raw;
|
|
12925
13199
|
throw new Error(`Invalid --arch: ${raw}. Must be 'arm' or 'x86'.`);
|
|
@@ -12965,7 +13239,7 @@ Exit codes: 0 ok | 1 error (bad credentials or API failure).`).action(async () =
|
|
|
12965
13239
|
const sub = deploy.command(provider).description(`${provider} commands.`).addHelpText("before", `
|
|
12966
13240
|
deploy ${provider}: per-provider box lifecycle. up | down | status | ssh | logs | update.
|
|
12967
13241
|
`);
|
|
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.",
|
|
13242
|
+
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
13243
|
deploy ${provider} up: provision the ${provider} box via Terraform (init \u2192 plan \u2192 apply).
|
|
12970
13244
|
|
|
12971
13245
|
Input
|
|
@@ -13112,14 +13386,14 @@ init_runner2();
|
|
|
13112
13386
|
|
|
13113
13387
|
// src/cli/cloud/handoff.ts
|
|
13114
13388
|
import { spawn as spawn5 } from "child_process";
|
|
13115
|
-
import { existsSync as existsSync36, readFileSync as
|
|
13389
|
+
import { existsSync as existsSync36, readFileSync as readFileSync36, writeFileSync as writeFileSync20 } from "fs";
|
|
13116
13390
|
init_exec();
|
|
13117
13391
|
init_paths();
|
|
13118
13392
|
init_shell();
|
|
13119
13393
|
init_runner();
|
|
13120
13394
|
init_ssh_exec();
|
|
13121
13395
|
init_provider_pick();
|
|
13122
|
-
import { join as
|
|
13396
|
+
import { join as join32 } from "path";
|
|
13123
13397
|
async function cloudHandoff(sessionId, opts) {
|
|
13124
13398
|
const cwd = process.env["SISYPHUS_CWD"] ?? process.cwd();
|
|
13125
13399
|
const request = {
|
|
@@ -13164,7 +13438,7 @@ async function waitForSentOrError(cwd, sessionId) {
|
|
|
13164
13438
|
if (!existsSync36(path)) continue;
|
|
13165
13439
|
let session2;
|
|
13166
13440
|
try {
|
|
13167
|
-
session2 = JSON.parse(
|
|
13441
|
+
session2 = JSON.parse(readFileSync36(path, "utf-8"));
|
|
13168
13442
|
} catch (err) {
|
|
13169
13443
|
void err;
|
|
13170
13444
|
continue;
|
|
@@ -13245,16 +13519,16 @@ async function cloudReclaim(sessionId, opts) {
|
|
|
13245
13519
|
await rsyncDown(target, `${remoteRepoDir}/`, `${cwd}/`, { withDelete: false, excludeSisyphus: true, update: true });
|
|
13246
13520
|
for (const name of ["config.json", "orchestrator.md", "orchestrator-settings.json"]) {
|
|
13247
13521
|
const remotePath = `${remoteRepoDir}/.sisyphus/${name}`;
|
|
13248
|
-
const localPath =
|
|
13522
|
+
const localPath = join32(projectDir(cwd), name);
|
|
13249
13523
|
const probe = runOnBox(provider, `test -f ${shellQuote(remotePath.replace(/^~\//, ""))} && echo y || echo n`);
|
|
13250
13524
|
if (probe.stdout.trim() !== "y") continue;
|
|
13251
13525
|
await rsyncDown(target, remotePath, localPath, { withDelete: false });
|
|
13252
13526
|
}
|
|
13253
13527
|
const localStatePath = statePath(cwd, sessionId);
|
|
13254
|
-
const merged = JSON.parse(
|
|
13528
|
+
const merged = JSON.parse(readFileSync36(localStatePath, "utf-8"));
|
|
13255
13529
|
merged.cwd = cwd;
|
|
13256
13530
|
merged.handoff = local.handoff;
|
|
13257
|
-
|
|
13531
|
+
writeFileSync20(localStatePath, JSON.stringify(merged, null, 2));
|
|
13258
13532
|
const reclaimMessage = `Session reclaimed from cloud (${provider}:${repo}). Resuming locally.`;
|
|
13259
13533
|
console.log(`\u2192 local sis resume`);
|
|
13260
13534
|
const resumeResp = await sendRequest({
|
|
@@ -13287,7 +13561,7 @@ function readLocalSession(cwd, sessionId) {
|
|
|
13287
13561
|
received: sessionId
|
|
13288
13562
|
});
|
|
13289
13563
|
}
|
|
13290
|
-
return JSON.parse(
|
|
13564
|
+
return JSON.parse(readFileSync36(path, "utf-8"));
|
|
13291
13565
|
}
|
|
13292
13566
|
async function waitForBoxPaused(provider, remoteSessionDir) {
|
|
13293
13567
|
const POLL_INTERVAL_MS = 2e3;
|
|
@@ -13571,7 +13845,7 @@ function attachNotify(diagnostic2) {
|
|
|
13571
13845
|
// src/cli/commands/tmux-sessions.ts
|
|
13572
13846
|
init_paths();
|
|
13573
13847
|
import { execSync as execSync18 } from "child_process";
|
|
13574
|
-
import { readFileSync as
|
|
13848
|
+
import { readFileSync as readFileSync37, existsSync as existsSync37 } from "fs";
|
|
13575
13849
|
var DOT_MAP = {
|
|
13576
13850
|
"orchestrator:processing": { icon: "\u25CF", color: "#d4ad6a" },
|
|
13577
13851
|
"orchestrator:idle": { icon: "\u25CF", color: "#d47766" },
|
|
@@ -13584,7 +13858,7 @@ function readManifest() {
|
|
|
13584
13858
|
const p = sessionsManifestPath();
|
|
13585
13859
|
if (!existsSync37(p)) return null;
|
|
13586
13860
|
try {
|
|
13587
|
-
return JSON.parse(
|
|
13861
|
+
return JSON.parse(readFileSync37(p, "utf-8"));
|
|
13588
13862
|
} catch {
|
|
13589
13863
|
return null;
|
|
13590
13864
|
}
|
|
@@ -13625,7 +13899,7 @@ init_server();
|
|
|
13625
13899
|
init_ask_store();
|
|
13626
13900
|
init_state();
|
|
13627
13901
|
init_paths();
|
|
13628
|
-
import { existsSync as
|
|
13902
|
+
import { existsSync as existsSync48 } from "fs";
|
|
13629
13903
|
var HEARTBEAT_ASKED_BY2 = "system:heartbeat";
|
|
13630
13904
|
var ORPHAN_ASKED_BY2 = "system:orphan-handler";
|
|
13631
13905
|
function isHeartbeatZombie(cwd, sessionId, askId) {
|
|
@@ -13633,14 +13907,14 @@ function isHeartbeatZombie(cwd, sessionId, askId) {
|
|
|
13633
13907
|
if (!meta) return false;
|
|
13634
13908
|
if (meta.askedBy !== HEARTBEAT_ASKED_BY2) return false;
|
|
13635
13909
|
if (meta.status === "answered") return false;
|
|
13636
|
-
if (
|
|
13910
|
+
if (existsSync48(askOutputPath(cwd, sessionId, askId))) return false;
|
|
13637
13911
|
for (const candidateId of listAsks(cwd, sessionId)) {
|
|
13638
13912
|
if (candidateId === askId) continue;
|
|
13639
13913
|
const candidateMeta = readMeta(cwd, sessionId, candidateId);
|
|
13640
13914
|
if (!candidateMeta) continue;
|
|
13641
13915
|
if (candidateMeta.heartbeatAskId !== askId) continue;
|
|
13642
13916
|
if (candidateMeta.status === "answered") return true;
|
|
13643
|
-
if (
|
|
13917
|
+
if (existsSync48(askOutputPath(cwd, sessionId, candidateId))) return true;
|
|
13644
13918
|
}
|
|
13645
13919
|
return false;
|
|
13646
13920
|
}
|
|
@@ -13649,7 +13923,7 @@ function isOrphanZombie(cwd, sessionId, askId) {
|
|
|
13649
13923
|
if (!meta) return false;
|
|
13650
13924
|
if (meta.askedBy !== ORPHAN_ASKED_BY2) return false;
|
|
13651
13925
|
if (meta.status === "answered") return false;
|
|
13652
|
-
if (
|
|
13926
|
+
if (existsSync48(askOutputPath(cwd, sessionId, askId))) return false;
|
|
13653
13927
|
if (meta.orphanTarget?.kind !== "agent") return false;
|
|
13654
13928
|
let session2;
|
|
13655
13929
|
try {
|
|
@@ -13662,27 +13936,6 @@ function isOrphanZombie(cwd, sessionId, askId) {
|
|
|
13662
13936
|
if (!agent2) return false;
|
|
13663
13937
|
return agent2.status !== "running";
|
|
13664
13938
|
}
|
|
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
13939
|
async function resolveZombie(cwd, sessionId, entry) {
|
|
13687
13940
|
const { askId, kind } = entry;
|
|
13688
13941
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -13700,11 +13953,6 @@ async function resolveZombie(cwd, sessionId, entry) {
|
|
|
13700
13953
|
selectedOptionId = "dismiss";
|
|
13701
13954
|
freetext = "auto-resolved: agent superseded by replacement (clean-zombies sweep)";
|
|
13702
13955
|
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
13956
|
}
|
|
13709
13957
|
writeOutput(cwd, sessionId, askId, [{
|
|
13710
13958
|
id: interactionId,
|
|
@@ -13723,14 +13971,12 @@ async function sweepSession(cwd, sessionId) {
|
|
|
13723
13971
|
const meta = readMeta(cwd, sessionId, askId);
|
|
13724
13972
|
const agentId = meta?.orphanTarget?.kind === "agent" ? meta.orphanTarget.agentId : "unknown";
|
|
13725
13973
|
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
13974
|
}
|
|
13729
13975
|
}
|
|
13730
13976
|
return zombies;
|
|
13731
13977
|
}
|
|
13732
13978
|
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
|
|
13979
|
+
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
13980
|
"after",
|
|
13735
13981
|
`
|
|
13736
13982
|
clean-zombies: sweep all sessions and dismiss stale ask records.
|
|
@@ -13743,8 +13989,8 @@ Output (stdout, plain text \u2014 human-readable maintenance summary; not for ag
|
|
|
13743
13989
|
|
|
13744
13990
|
Effects
|
|
13745
13991
|
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
|
|
13992
|
+
zombie found. Dismisses heartbeat asks whose original was answered and orphan asks whose
|
|
13993
|
+
target agent is no longer running.
|
|
13748
13994
|
|
|
13749
13995
|
Exit codes: 0 ok`
|
|
13750
13996
|
).action(async () => {
|
|
@@ -13757,7 +14003,7 @@ Exit codes: 0 ok`
|
|
|
13757
14003
|
const allZombies = [];
|
|
13758
14004
|
const sessionsSweept = /* @__PURE__ */ new Set();
|
|
13759
14005
|
for (const [sessionId, cwd] of entries) {
|
|
13760
|
-
if (!
|
|
14006
|
+
if (!existsSync48(statePath(cwd, sessionId))) continue;
|
|
13761
14007
|
try {
|
|
13762
14008
|
const zombies = await sweepSession(cwd, sessionId);
|
|
13763
14009
|
if (zombies.length > 0) {
|
|
@@ -13773,8 +14019,7 @@ Exit codes: 0 ok`
|
|
|
13773
14019
|
}
|
|
13774
14020
|
const summary = {
|
|
13775
14021
|
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
|
|
14022
|
+
orphans: allZombies.filter((z4) => z4.kind === "orphan").length
|
|
13778
14023
|
};
|
|
13779
14024
|
const total = allZombies.length;
|
|
13780
14025
|
if (total === 0) {
|
|
@@ -13782,7 +14027,7 @@ Exit codes: 0 ok`
|
|
|
13782
14027
|
return;
|
|
13783
14028
|
}
|
|
13784
14029
|
for (const [sessionId, cwd] of entries) {
|
|
13785
|
-
if (!
|
|
14030
|
+
if (!existsSync48(statePath(cwd, sessionId))) continue;
|
|
13786
14031
|
const sessionZombies = allZombies.filter((z4) => z4.sessionId === sessionId);
|
|
13787
14032
|
for (const zombie of sessionZombies) {
|
|
13788
14033
|
try {
|
|
@@ -13795,7 +14040,7 @@ Exit codes: 0 ok`
|
|
|
13795
14040
|
}
|
|
13796
14041
|
}
|
|
13797
14042
|
}
|
|
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"}
|
|
14043
|
+
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
14044
|
});
|
|
13800
14045
|
}
|
|
13801
14046
|
|
|
@@ -13824,7 +14069,7 @@ var sessionCounts = null;
|
|
|
13824
14069
|
var program = new Command();
|
|
13825
14070
|
program.name("sis").description("tmux-integrated orchestration daemon for Claude Code").helpOption("-h, --help", "print -h for any node or leaf").version(
|
|
13826
14071
|
JSON.parse(
|
|
13827
|
-
|
|
14072
|
+
readFileSync47(join39(dirname16(fileURLToPath6(import.meta.url)), "..", "package.json"), "utf-8")
|
|
13828
14073
|
).version,
|
|
13829
14074
|
"--version",
|
|
13830
14075
|
"output the version number"
|
|
@@ -13936,7 +14181,7 @@ registerHomeInit(diagnostic);
|
|
|
13936
14181
|
var args = process.argv.slice(2);
|
|
13937
14182
|
var firstArg = args[0];
|
|
13938
14183
|
var skipWelcome = ["session", "start", "dashboard", "agent", "orch", "ask", "ui", "segment", "admin", "feedback", "help", "--help", "--version"];
|
|
13939
|
-
if (!
|
|
14184
|
+
if (!existsSync49(globalDir()) && firstArg && !skipWelcome.includes(firstArg)) {
|
|
13940
14185
|
mkdirSync21(globalDir(), { recursive: true });
|
|
13941
14186
|
console.log("");
|
|
13942
14187
|
console.log(" Welcome to Sisyphus. Run 'sis admin install setup' to get started.");
|