sisyphi 1.1.32 → 1.1.33
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/README.md +1 -0
- package/dist/cli.js +132 -67
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +692 -322
- package/dist/daemon.js.map +1 -1
- package/dist/templates/agent-plugin/hooks/hooks.json +51 -0
- package/dist/tui.js +103 -41
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/templates/agent-plugin/hooks/hooks.json +51 -0
package/dist/daemon.js
CHANGED
|
@@ -51,9 +51,12 @@ __export(paths_exports, {
|
|
|
51
51
|
legacyLogsPath: () => legacyLogsPath,
|
|
52
52
|
logsDir: () => logsDir,
|
|
53
53
|
messagesDir: () => messagesDir,
|
|
54
|
+
projectAgentPluginDir: () => projectAgentPluginDir,
|
|
54
55
|
projectConfigPath: () => projectConfigPath,
|
|
55
56
|
projectDir: () => projectDir,
|
|
57
|
+
projectOrchestratorPluginDir: () => projectOrchestratorPluginDir,
|
|
56
58
|
projectOrchestratorPromptPath: () => projectOrchestratorPromptPath,
|
|
59
|
+
projectOrchestratorSettingsPath: () => projectOrchestratorSettingsPath,
|
|
57
60
|
promptsDir: () => promptsDir,
|
|
58
61
|
reportFilePath: () => reportFilePath,
|
|
59
62
|
reportsDir: () => reportsDir,
|
|
@@ -69,7 +72,11 @@ __export(paths_exports, {
|
|
|
69
72
|
strategyPath: () => strategyPath,
|
|
70
73
|
tmuxSessionDisplayName: () => tmuxSessionDisplayName,
|
|
71
74
|
tmuxSessionName: () => tmuxSessionName,
|
|
72
|
-
tuiScratchDir: () => tuiScratchDir
|
|
75
|
+
tuiScratchDir: () => tuiScratchDir,
|
|
76
|
+
userAgentPluginDir: () => userAgentPluginDir,
|
|
77
|
+
userOrchestratorPluginDir: () => userOrchestratorPluginDir,
|
|
78
|
+
userOrchestratorPromptPath: () => userOrchestratorPromptPath,
|
|
79
|
+
userOrchestratorSettingsPath: () => userOrchestratorSettingsPath
|
|
73
80
|
});
|
|
74
81
|
import { homedir } from "os";
|
|
75
82
|
import { basename, join } from "path";
|
|
@@ -100,6 +107,27 @@ function projectConfigPath(cwd) {
|
|
|
100
107
|
function projectOrchestratorPromptPath(cwd) {
|
|
101
108
|
return join(projectDir(cwd), "orchestrator.md");
|
|
102
109
|
}
|
|
110
|
+
function userOrchestratorPromptPath() {
|
|
111
|
+
return join(globalDir(), "orchestrator.md");
|
|
112
|
+
}
|
|
113
|
+
function projectOrchestratorSettingsPath(cwd) {
|
|
114
|
+
return join(projectDir(cwd), "orchestrator-settings.json");
|
|
115
|
+
}
|
|
116
|
+
function userOrchestratorSettingsPath() {
|
|
117
|
+
return join(globalDir(), "orchestrator-settings.json");
|
|
118
|
+
}
|
|
119
|
+
function projectAgentPluginDir(cwd) {
|
|
120
|
+
return join(projectDir(cwd), "agent-plugin");
|
|
121
|
+
}
|
|
122
|
+
function userAgentPluginDir() {
|
|
123
|
+
return join(globalDir(), "agent-plugin");
|
|
124
|
+
}
|
|
125
|
+
function projectOrchestratorPluginDir(cwd) {
|
|
126
|
+
return join(projectDir(cwd), "orchestrator-plugin");
|
|
127
|
+
}
|
|
128
|
+
function userOrchestratorPluginDir() {
|
|
129
|
+
return join(globalDir(), "orchestrator-plugin");
|
|
130
|
+
}
|
|
103
131
|
function sessionsDir(cwd) {
|
|
104
132
|
return join(projectDir(cwd), "sessions");
|
|
105
133
|
}
|
|
@@ -255,7 +283,7 @@ var init_paths = __esm({
|
|
|
255
283
|
|
|
256
284
|
// src/daemon/index.ts
|
|
257
285
|
init_paths();
|
|
258
|
-
import { mkdirSync as
|
|
286
|
+
import { mkdirSync as mkdirSync13, readFileSync as readFileSync24, writeFileSync as writeFileSync17, unlinkSync as unlinkSync5, existsSync as existsSync24 } from "fs";
|
|
259
287
|
import { execSync as execSync7 } from "child_process";
|
|
260
288
|
import { setTimeout as sleep } from "timers/promises";
|
|
261
289
|
|
|
@@ -317,8 +345,8 @@ function loadConfig(cwd) {
|
|
|
317
345
|
// src/daemon/server.ts
|
|
318
346
|
init_paths();
|
|
319
347
|
import { createServer } from "net";
|
|
320
|
-
import { unlinkSync as unlinkSync3, existsSync as
|
|
321
|
-
import { join as
|
|
348
|
+
import { unlinkSync as unlinkSync3, existsSync as existsSync20, writeFileSync as writeFileSync15, readFileSync as readFileSync21, mkdirSync as mkdirSync11, readdirSync as readdirSync12, rmSync as rmSync8, chmodSync } from "fs";
|
|
349
|
+
import { join as join19 } from "path";
|
|
322
350
|
|
|
323
351
|
// src/shared/shell.ts
|
|
324
352
|
function shellQuote(s) {
|
|
@@ -337,7 +365,7 @@ function escapeAppleScript(s) {
|
|
|
337
365
|
|
|
338
366
|
// src/daemon/session-manager.ts
|
|
339
367
|
import { v4 as uuidv4 } from "uuid";
|
|
340
|
-
import { existsSync as
|
|
368
|
+
import { existsSync as existsSync18, readFileSync as readFileSync19, readdirSync as readdirSync11, rmSync as rmSync6 } from "fs";
|
|
341
369
|
|
|
342
370
|
// src/daemon/state.ts
|
|
343
371
|
import { copyFileSync, cpSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync3, readdirSync, rmSync, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
@@ -356,16 +384,16 @@ function atomicWrite(filePath, data) {
|
|
|
356
384
|
var locks = /* @__PURE__ */ new Map();
|
|
357
385
|
async function withLock(key, fn) {
|
|
358
386
|
const prev = locks.get(key) ?? Promise.resolve();
|
|
359
|
-
let
|
|
387
|
+
let resolve12;
|
|
360
388
|
const next = new Promise((r) => {
|
|
361
|
-
|
|
389
|
+
resolve12 = r;
|
|
362
390
|
});
|
|
363
391
|
locks.set(key, next);
|
|
364
392
|
await prev;
|
|
365
393
|
try {
|
|
366
394
|
return fn();
|
|
367
395
|
} finally {
|
|
368
|
-
|
|
396
|
+
resolve12();
|
|
369
397
|
if (locks.get(key) === next) {
|
|
370
398
|
locks.delete(key);
|
|
371
399
|
}
|
|
@@ -468,6 +496,13 @@ function getSession(cwd, sessionId) {
|
|
|
468
496
|
function saveSession(session) {
|
|
469
497
|
atomicWrite(statePath(session.cwd, session.id), JSON.stringify(session, null, 2));
|
|
470
498
|
}
|
|
499
|
+
function isSessionDangerous(cwd, sessionId) {
|
|
500
|
+
try {
|
|
501
|
+
return getSession(cwd, sessionId).dangerousMode === true;
|
|
502
|
+
} catch {
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
471
506
|
async function addAgent(cwd, sessionId, agent) {
|
|
472
507
|
return withSessionLock(sessionId, () => {
|
|
473
508
|
const session = getSession(cwd, sessionId);
|
|
@@ -836,10 +871,10 @@ async function createCloneState(sourceCwd, sourceId, cloneId, goal, context, con
|
|
|
836
871
|
}
|
|
837
872
|
|
|
838
873
|
// src/daemon/orchestrator.ts
|
|
839
|
-
import { existsSync as
|
|
874
|
+
import { existsSync as existsSync16, readdirSync as readdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
|
|
840
875
|
import { execSync as execSync5 } from "child_process";
|
|
841
876
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
842
|
-
import { resolve as
|
|
877
|
+
import { resolve as resolve7, join as join16, relative as relative3 } from "path";
|
|
843
878
|
|
|
844
879
|
// src/daemon/spawn-helpers.ts
|
|
845
880
|
import { writeFileSync as writeFileSync4, existsSync as existsSync3 } from "fs";
|
|
@@ -951,6 +986,7 @@ function resetColors(sessionId) {
|
|
|
951
986
|
}
|
|
952
987
|
|
|
953
988
|
// src/daemon/frontmatter.ts
|
|
989
|
+
init_paths();
|
|
954
990
|
import { readFileSync as readFileSync4, existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
|
|
955
991
|
import { homedir as homedir2 } from "os";
|
|
956
992
|
import { join as join5, basename as basename2 } from "path";
|
|
@@ -1025,6 +1061,8 @@ function resolveAgentTypePath(agentType, pluginDir, cwd) {
|
|
|
1025
1061
|
searchPaths.push(join5(installPath, "agents", `${name}.md`));
|
|
1026
1062
|
}
|
|
1027
1063
|
} else {
|
|
1064
|
+
searchPaths.push(join5(projectAgentPluginDir(cwd), "agents", `${name}.md`));
|
|
1065
|
+
searchPaths.push(join5(userAgentPluginDir(), "agents", `${name}.md`));
|
|
1028
1066
|
searchPaths.push(join5(cwd, ".claude", "agents", `${name}.md`));
|
|
1029
1067
|
searchPaths.push(join5(homedir2(), ".claude", "agents", `${name}.md`));
|
|
1030
1068
|
searchPaths.push(join5(pluginDir, "agents", `${name}.md`));
|
|
@@ -1059,6 +1097,8 @@ function discoverAgentTypes(pluginDir, cwd) {
|
|
|
1059
1097
|
}
|
|
1060
1098
|
}
|
|
1061
1099
|
}
|
|
1100
|
+
scanDir(join5(projectAgentPluginDir(cwd), "agents"), null, "project-sis");
|
|
1101
|
+
scanDir(join5(userAgentPluginDir(), "agents"), null, "user-sis");
|
|
1062
1102
|
scanDir(join5(cwd, ".claude", "agents"), null, "project");
|
|
1063
1103
|
scanDir(join5(homedir2(), ".claude", "agents"), null, "user");
|
|
1064
1104
|
scanDir(join5(pluginDir, "agents"), "sisyphus", "bundled");
|
|
@@ -1098,6 +1138,8 @@ function resolveAgentConfig(agentType, pluginDir, cwd) {
|
|
|
1098
1138
|
// src/daemon/orchestrator-modes.ts
|
|
1099
1139
|
import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "fs";
|
|
1100
1140
|
import { resolve as resolve3, join as join6 } from "path";
|
|
1141
|
+
init_paths();
|
|
1142
|
+
import { homedir as homedir3 } from "os";
|
|
1101
1143
|
function resolveTemplatesDir() {
|
|
1102
1144
|
const distLayout = resolve3(import.meta.dirname, "../templates");
|
|
1103
1145
|
if (existsSync5(distLayout)) return distLayout;
|
|
@@ -1105,18 +1147,46 @@ function resolveTemplatesDir() {
|
|
|
1105
1147
|
if (existsSync5(srcLayout)) return srcLayout;
|
|
1106
1148
|
return void 0;
|
|
1107
1149
|
}
|
|
1108
|
-
function
|
|
1109
|
-
const
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
);
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1150
|
+
function modeLayers(cwd) {
|
|
1151
|
+
const layers = [];
|
|
1152
|
+
const project = projectDir(cwd);
|
|
1153
|
+
if (existsSync5(project)) layers.push({ source: "project", dir: project });
|
|
1154
|
+
const user = join6(homedir3(), ".sisyphus");
|
|
1155
|
+
if (existsSync5(user)) layers.push({ source: "user", dir: user });
|
|
1156
|
+
const bundled = resolveTemplatesDir();
|
|
1157
|
+
if (bundled) layers.push({ source: "bundled", dir: bundled });
|
|
1158
|
+
return layers;
|
|
1159
|
+
}
|
|
1160
|
+
function discoverOrchestratorModes(cwd) {
|
|
1161
|
+
const layers = cwd ? modeLayers(cwd) : modeLayers(process.cwd());
|
|
1162
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1163
|
+
for (const layer of layers) {
|
|
1164
|
+
let files;
|
|
1165
|
+
try {
|
|
1166
|
+
files = readdirSync3(layer.dir);
|
|
1167
|
+
} catch {
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
const modeFiles = files.filter(
|
|
1171
|
+
(f) => f.startsWith("orchestrator-") && f.endsWith(".md") && f !== "orchestrator-base.md"
|
|
1172
|
+
);
|
|
1173
|
+
for (const file of modeFiles) {
|
|
1174
|
+
const filePath = join6(layer.dir, file);
|
|
1175
|
+
let content = "";
|
|
1176
|
+
try {
|
|
1177
|
+
content = readFileSync5(filePath, "utf-8");
|
|
1178
|
+
} catch {
|
|
1179
|
+
continue;
|
|
1180
|
+
}
|
|
1181
|
+
const fm = parseAgentFrontmatter(content);
|
|
1182
|
+
const name = fm.name ?? file.replace(/^orchestrator-/, "").replace(/\.md$/, "");
|
|
1183
|
+
if (seen.has(name)) continue;
|
|
1184
|
+
const entry = { name, filePath, source: layer.source };
|
|
1185
|
+
if (fm.description !== void 0) entry.description = fm.description;
|
|
1186
|
+
seen.set(name, entry);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return Array.from(seen.values());
|
|
1120
1190
|
}
|
|
1121
1191
|
|
|
1122
1192
|
// src/daemon/lib/effort-render.ts
|
|
@@ -1181,6 +1251,249 @@ function walk(src, dest, tier) {
|
|
|
1181
1251
|
}
|
|
1182
1252
|
}
|
|
1183
1253
|
|
|
1254
|
+
// src/daemon/extensions.ts
|
|
1255
|
+
init_paths();
|
|
1256
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, readdirSync as readdirSync5, copyFileSync as copyFileSync3, mkdirSync as mkdirSync3, statSync as statSync2, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
1257
|
+
import { resolve as resolve4, join as join8, basename as basename3, relative } from "path";
|
|
1258
|
+
function resolveBundledTemplateDir(kind) {
|
|
1259
|
+
const built = resolve4(import.meta.dirname, "../templates", kind);
|
|
1260
|
+
if (existsSync7(built)) return built;
|
|
1261
|
+
const src = resolve4(import.meta.dirname, "../../templates", kind);
|
|
1262
|
+
if (existsSync7(src)) return src;
|
|
1263
|
+
return void 0;
|
|
1264
|
+
}
|
|
1265
|
+
function agentPluginLayers(cwd) {
|
|
1266
|
+
const layers = [];
|
|
1267
|
+
const project = projectAgentPluginDir(cwd);
|
|
1268
|
+
if (existsSync7(project)) layers.push({ source: "project", root: project });
|
|
1269
|
+
const user = userAgentPluginDir();
|
|
1270
|
+
if (existsSync7(user)) layers.push({ source: "user", root: user });
|
|
1271
|
+
const bundled = resolveBundledTemplateDir("agent-plugin");
|
|
1272
|
+
if (bundled) layers.push({ source: "bundled", root: bundled });
|
|
1273
|
+
return layers;
|
|
1274
|
+
}
|
|
1275
|
+
function orchestratorPluginLayers(cwd) {
|
|
1276
|
+
const layers = [];
|
|
1277
|
+
const project = projectOrchestratorPluginDir(cwd);
|
|
1278
|
+
if (existsSync7(project)) layers.push({ source: "project", root: project });
|
|
1279
|
+
const user = userOrchestratorPluginDir();
|
|
1280
|
+
if (existsSync7(user)) layers.push({ source: "user", root: user });
|
|
1281
|
+
const bundled = resolveBundledTemplateDir("orchestrator-plugin");
|
|
1282
|
+
if (bundled) layers.push({ source: "bundled", root: bundled });
|
|
1283
|
+
return layers;
|
|
1284
|
+
}
|
|
1285
|
+
function readManifest(layerRoot) {
|
|
1286
|
+
const path = join8(layerRoot, "hooks", "hooks.json");
|
|
1287
|
+
if (!existsSync7(path)) return null;
|
|
1288
|
+
try {
|
|
1289
|
+
const raw = readFileSync7(path, "utf-8");
|
|
1290
|
+
const parsed = JSON.parse(raw);
|
|
1291
|
+
return parsed;
|
|
1292
|
+
} catch (err) {
|
|
1293
|
+
console.warn(`[sisyphus] Failed to parse hooks manifest at ${path}: ${err instanceof Error ? err.message : err}`);
|
|
1294
|
+
return null;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
function commandScriptName(command2) {
|
|
1298
|
+
const match = command2.match(/([\w.-]+\.[\w]+)\s*$/);
|
|
1299
|
+
return match ? match[1] : null;
|
|
1300
|
+
}
|
|
1301
|
+
function groupApplies(group, ctx) {
|
|
1302
|
+
const types = group.agentTypes ?? ["all"];
|
|
1303
|
+
const matchesType = types.includes("all") || types.includes(ctx.agentType);
|
|
1304
|
+
if (!matchesType) return false;
|
|
1305
|
+
if (group.condition === "non-interactive" && ctx.interactive) return false;
|
|
1306
|
+
return true;
|
|
1307
|
+
}
|
|
1308
|
+
function mergeHookManifests(layers, ctx) {
|
|
1309
|
+
const disabled = /* @__PURE__ */ new Set();
|
|
1310
|
+
for (const layer of layers) {
|
|
1311
|
+
const manifest = readManifest(layer.root);
|
|
1312
|
+
if (!manifest?.disable) continue;
|
|
1313
|
+
for (const name of manifest.disable) disabled.add(name);
|
|
1314
|
+
}
|
|
1315
|
+
const merged = {};
|
|
1316
|
+
for (const layer of layers) {
|
|
1317
|
+
const manifest = readManifest(layer.root);
|
|
1318
|
+
if (!manifest?.hooks) continue;
|
|
1319
|
+
for (const [event, groups] of Object.entries(manifest.hooks)) {
|
|
1320
|
+
if (!Array.isArray(groups)) continue;
|
|
1321
|
+
for (const group of groups) {
|
|
1322
|
+
if (!groupApplies(group, ctx)) continue;
|
|
1323
|
+
const filteredHooks = group.hooks.filter((h) => {
|
|
1324
|
+
const script = commandScriptName(h.command);
|
|
1325
|
+
return !script || !disabled.has(script);
|
|
1326
|
+
});
|
|
1327
|
+
if (filteredHooks.length === 0) continue;
|
|
1328
|
+
const cleaned = {
|
|
1329
|
+
...group.matcher !== void 0 && { matcher: group.matcher },
|
|
1330
|
+
hooks: filteredHooks
|
|
1331
|
+
};
|
|
1332
|
+
if (!merged[event]) merged[event] = [];
|
|
1333
|
+
merged[event].push(cleaned);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
return merged;
|
|
1338
|
+
}
|
|
1339
|
+
var defaultFilter = (name) => name !== "CLAUDE.md" && name !== "hooks.json";
|
|
1340
|
+
function copyLayered(layers, opts) {
|
|
1341
|
+
mkdirSync3(opts.destDir, { recursive: true });
|
|
1342
|
+
const filter = opts.filter ?? defaultFilter;
|
|
1343
|
+
const written = /* @__PURE__ */ new Set();
|
|
1344
|
+
for (const layer of layers) {
|
|
1345
|
+
const layerSubdir = join8(layer.root, opts.subdir);
|
|
1346
|
+
if (!existsSync7(layerSubdir)) continue;
|
|
1347
|
+
let entries;
|
|
1348
|
+
try {
|
|
1349
|
+
entries = readdirSync5(layerSubdir);
|
|
1350
|
+
} catch {
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
for (const name of entries) {
|
|
1354
|
+
if (!filter(name)) continue;
|
|
1355
|
+
if (opts.skipFiles?.has(name)) continue;
|
|
1356
|
+
if (written.has(name)) continue;
|
|
1357
|
+
const src = join8(layerSubdir, name);
|
|
1358
|
+
const dest = join8(opts.destDir, name);
|
|
1359
|
+
const stat = statSync2(src);
|
|
1360
|
+
if (stat.isDirectory()) {
|
|
1361
|
+
if (!opts.recurse) continue;
|
|
1362
|
+
copyDirRecursive(src, dest);
|
|
1363
|
+
} else if (stat.isFile()) {
|
|
1364
|
+
copyFileSync3(src, dest);
|
|
1365
|
+
}
|
|
1366
|
+
written.add(name);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
function copyDirRecursive(src, dest) {
|
|
1371
|
+
mkdirSync3(dest, { recursive: true });
|
|
1372
|
+
for (const name of readdirSync5(src)) {
|
|
1373
|
+
const s = join8(src, name);
|
|
1374
|
+
const d = join8(dest, name);
|
|
1375
|
+
const stat = statSync2(s);
|
|
1376
|
+
if (stat.isDirectory()) {
|
|
1377
|
+
copyDirRecursive(s, d);
|
|
1378
|
+
} else if (stat.isFile()) {
|
|
1379
|
+
copyFileSync3(s, d);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
function collectReferencedHookScripts(layers, ctx) {
|
|
1384
|
+
const scripts = /* @__PURE__ */ new Set();
|
|
1385
|
+
for (const layer of layers) {
|
|
1386
|
+
const manifest = readManifest(layer.root);
|
|
1387
|
+
if (!manifest?.hooks) continue;
|
|
1388
|
+
for (const groups of Object.values(manifest.hooks)) {
|
|
1389
|
+
if (!Array.isArray(groups)) continue;
|
|
1390
|
+
for (const group of groups) {
|
|
1391
|
+
if (!groupApplies(group, ctx)) continue;
|
|
1392
|
+
for (const h of group.hooks) {
|
|
1393
|
+
const name = commandScriptName(h.command);
|
|
1394
|
+
if (name) scripts.add(name);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
return scripts;
|
|
1400
|
+
}
|
|
1401
|
+
function collectDisabledHookScripts(layers) {
|
|
1402
|
+
const disabled = /* @__PURE__ */ new Set();
|
|
1403
|
+
for (const layer of layers) {
|
|
1404
|
+
const manifest = readManifest(layer.root);
|
|
1405
|
+
if (!manifest?.disable) continue;
|
|
1406
|
+
for (const name of manifest.disable) disabled.add(name);
|
|
1407
|
+
}
|
|
1408
|
+
return disabled;
|
|
1409
|
+
}
|
|
1410
|
+
function indexAvailableSkills(layers) {
|
|
1411
|
+
const index = /* @__PURE__ */ new Map();
|
|
1412
|
+
for (const layer of layers) {
|
|
1413
|
+
const skillsRoot = join8(layer.root, "skills");
|
|
1414
|
+
if (!existsSync7(skillsRoot)) continue;
|
|
1415
|
+
let entries;
|
|
1416
|
+
try {
|
|
1417
|
+
entries = readdirSync5(skillsRoot);
|
|
1418
|
+
} catch {
|
|
1419
|
+
continue;
|
|
1420
|
+
}
|
|
1421
|
+
for (const name of entries) {
|
|
1422
|
+
if (index.has(name)) continue;
|
|
1423
|
+
const path = join8(skillsRoot, name);
|
|
1424
|
+
try {
|
|
1425
|
+
if (statSync2(path).isDirectory()) index.set(name, path);
|
|
1426
|
+
} catch {
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
return index;
|
|
1431
|
+
}
|
|
1432
|
+
function copySkill(layers, skillName, destSkillsDir) {
|
|
1433
|
+
const index = indexAvailableSkills(layers);
|
|
1434
|
+
const src = index.get(skillName);
|
|
1435
|
+
if (!src) return false;
|
|
1436
|
+
copyDirRecursive(src, join8(destSkillsDir, skillName));
|
|
1437
|
+
return true;
|
|
1438
|
+
}
|
|
1439
|
+
var FILTERED_RENDER_EXTS = /* @__PURE__ */ new Set([".md"]);
|
|
1440
|
+
function isRenderable(name) {
|
|
1441
|
+
for (const ext of FILTERED_RENDER_EXTS) {
|
|
1442
|
+
if (name.endsWith(ext)) return true;
|
|
1443
|
+
}
|
|
1444
|
+
return false;
|
|
1445
|
+
}
|
|
1446
|
+
function collectOverlay(layers) {
|
|
1447
|
+
const overlay = /* @__PURE__ */ new Map();
|
|
1448
|
+
for (const layer of layers) {
|
|
1449
|
+
if (!existsSync7(layer.root)) continue;
|
|
1450
|
+
walkRelative(layer.root, "", (relPath, absPath) => {
|
|
1451
|
+
if (overlay.has(relPath)) return;
|
|
1452
|
+
overlay.set(relPath, { src: absPath, source: layer.source });
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
return overlay;
|
|
1456
|
+
}
|
|
1457
|
+
function walkRelative(root, prefix, visit) {
|
|
1458
|
+
const dir = prefix ? join8(root, prefix) : root;
|
|
1459
|
+
let entries;
|
|
1460
|
+
try {
|
|
1461
|
+
entries = readdirSync5(dir);
|
|
1462
|
+
} catch {
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
for (const name of entries) {
|
|
1466
|
+
const childPrefix = prefix ? join8(prefix, name) : name;
|
|
1467
|
+
const abs = join8(dir, name);
|
|
1468
|
+
let stat;
|
|
1469
|
+
try {
|
|
1470
|
+
stat = statSync2(abs);
|
|
1471
|
+
} catch {
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1474
|
+
if (stat.isDirectory()) {
|
|
1475
|
+
walkRelative(root, childPrefix, visit);
|
|
1476
|
+
} else if (stat.isFile()) {
|
|
1477
|
+
visit(childPrefix, abs);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
function renderLayeredPluginDir(layers, destDir, tier) {
|
|
1482
|
+
rmSync3(destDir, { recursive: true, force: true });
|
|
1483
|
+
mkdirSync3(destDir, { recursive: true });
|
|
1484
|
+
const overlay = collectOverlay(layers);
|
|
1485
|
+
for (const [relPath, entry] of overlay) {
|
|
1486
|
+
const destPath = join8(destDir, relPath);
|
|
1487
|
+
mkdirSync3(join8(destPath, ".."), { recursive: true });
|
|
1488
|
+
if (isRenderable(basename3(relPath))) {
|
|
1489
|
+
const rendered = renderEffortMarkers(readFileSync7(entry.src, "utf-8"), tier);
|
|
1490
|
+
writeFileSync6(destPath, rendered, "utf-8");
|
|
1491
|
+
} else {
|
|
1492
|
+
copyFileSync3(entry.src, destPath);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1184
1497
|
// src/daemon/tmux.ts
|
|
1185
1498
|
import { execSync as execSync2 } from "child_process";
|
|
1186
1499
|
var t = (target) => shellQuote(target);
|
|
@@ -1443,10 +1756,10 @@ function getSessionPanes(sessionId) {
|
|
|
1443
1756
|
}
|
|
1444
1757
|
|
|
1445
1758
|
// src/daemon/agent.ts
|
|
1446
|
-
import { readFileSync as
|
|
1759
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, readdirSync as readdirSync8, existsSync as existsSync11, unlinkSync } from "fs";
|
|
1447
1760
|
import { execSync as execSync4 } from "child_process";
|
|
1448
1761
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
1449
|
-
import { resolve as
|
|
1762
|
+
import { resolve as resolve5, relative as relative2, dirname as dirname3, join as join12 } from "path";
|
|
1450
1763
|
init_paths();
|
|
1451
1764
|
|
|
1452
1765
|
// src/daemon/haiku.ts
|
|
@@ -1602,17 +1915,17 @@ ${reportText.slice(0, 3e3)}`
|
|
|
1602
1915
|
}
|
|
1603
1916
|
|
|
1604
1917
|
// src/daemon/plugins.ts
|
|
1605
|
-
import { readFileSync as
|
|
1918
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
1606
1919
|
import { execFileSync } from "child_process";
|
|
1607
|
-
import { homedir as
|
|
1608
|
-
import { join as
|
|
1920
|
+
import { homedir as homedir4 } from "os";
|
|
1921
|
+
import { join as join9 } from "path";
|
|
1609
1922
|
function installedPluginsPath() {
|
|
1610
|
-
return
|
|
1923
|
+
return join9(homedir4(), ".claude", "plugins", "installed_plugins.json");
|
|
1611
1924
|
}
|
|
1612
1925
|
function resolveInstalledPlugin(name) {
|
|
1613
1926
|
let data;
|
|
1614
1927
|
try {
|
|
1615
|
-
data = JSON.parse(
|
|
1928
|
+
data = JSON.parse(readFileSync8(installedPluginsPath(), "utf-8"));
|
|
1616
1929
|
} catch {
|
|
1617
1930
|
return null;
|
|
1618
1931
|
}
|
|
@@ -1670,13 +1983,13 @@ function resolveAgentPluginDirs(plugins) {
|
|
|
1670
1983
|
|
|
1671
1984
|
// src/daemon/history.ts
|
|
1672
1985
|
init_paths();
|
|
1673
|
-
import { appendFileSync, mkdirSync as
|
|
1986
|
+
import { appendFileSync, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7, renameSync as renameSync2, readdirSync as readdirSync6, readFileSync as readFileSync9, rmSync as rmSync4, statSync as statSync3 } from "fs";
|
|
1674
1987
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1675
|
-
import { dirname as dirname2, join as
|
|
1988
|
+
import { dirname as dirname2, join as join10 } from "path";
|
|
1676
1989
|
var knownDirs = /* @__PURE__ */ new Set();
|
|
1677
1990
|
function ensureDir(sessionId) {
|
|
1678
1991
|
if (knownDirs.has(sessionId)) return;
|
|
1679
|
-
|
|
1992
|
+
mkdirSync4(historySessionDir(sessionId), { recursive: true });
|
|
1680
1993
|
knownDirs.add(sessionId);
|
|
1681
1994
|
}
|
|
1682
1995
|
function emitHistoryEvent(sessionId, event, data) {
|
|
@@ -1744,8 +2057,8 @@ function writeSessionSummary(session, extra) {
|
|
|
1744
2057
|
sentiment: extra?.sentiment ?? null
|
|
1745
2058
|
};
|
|
1746
2059
|
const filePath = historySessionSummaryPath(session.id);
|
|
1747
|
-
const tmp =
|
|
1748
|
-
|
|
2060
|
+
const tmp = join10(dirname2(filePath), `.session-${randomUUID2()}.tmp`);
|
|
2061
|
+
writeFileSync7(tmp, JSON.stringify(summary, null, 2), "utf-8");
|
|
1749
2062
|
renameSync2(tmp, filePath);
|
|
1750
2063
|
} catch (err) {
|
|
1751
2064
|
console.error(`[history] Failed to write session summary for ${session.id}:`, err);
|
|
@@ -1756,14 +2069,14 @@ function getRecentSentiments(count = 5, scanLimit = 30, overrideBaseDir) {
|
|
|
1756
2069
|
const base = overrideBaseDir ?? historyBaseDir();
|
|
1757
2070
|
let entries;
|
|
1758
2071
|
try {
|
|
1759
|
-
entries =
|
|
2072
|
+
entries = readdirSync6(base);
|
|
1760
2073
|
} catch {
|
|
1761
2074
|
return [];
|
|
1762
2075
|
}
|
|
1763
2076
|
const withMtime = [];
|
|
1764
2077
|
for (const name of entries) {
|
|
1765
2078
|
try {
|
|
1766
|
-
const st =
|
|
2079
|
+
const st = statSync3(join10(base, name));
|
|
1767
2080
|
if (st.isDirectory()) withMtime.push({ name, mtime: st.mtimeMs });
|
|
1768
2081
|
} catch {
|
|
1769
2082
|
continue;
|
|
@@ -1774,7 +2087,7 @@ function getRecentSentiments(count = 5, scanLimit = 30, overrideBaseDir) {
|
|
|
1774
2087
|
const limit = Math.min(withMtime.length, scanLimit);
|
|
1775
2088
|
for (let i = 0; i < limit && results.length < count; i++) {
|
|
1776
2089
|
try {
|
|
1777
|
-
const raw =
|
|
2090
|
+
const raw = readFileSync9(join10(base, withMtime[i].name, "session.json"), "utf-8");
|
|
1778
2091
|
const summary = JSON.parse(raw);
|
|
1779
2092
|
if (summary.sentiment && summary.completedAt) {
|
|
1780
2093
|
results.push({
|
|
@@ -1799,27 +2112,27 @@ function pruneHistory() {
|
|
|
1799
2112
|
const base = historyBaseDir();
|
|
1800
2113
|
let entries;
|
|
1801
2114
|
try {
|
|
1802
|
-
entries =
|
|
2115
|
+
entries = readdirSync6(base);
|
|
1803
2116
|
} catch {
|
|
1804
2117
|
return;
|
|
1805
2118
|
}
|
|
1806
2119
|
const sessions = [];
|
|
1807
2120
|
for (const name of entries) {
|
|
1808
|
-
const dir =
|
|
2121
|
+
const dir = join10(base, name);
|
|
1809
2122
|
try {
|
|
1810
|
-
const summaryPath =
|
|
1811
|
-
const raw =
|
|
2123
|
+
const summaryPath = join10(dir, "session.json");
|
|
2124
|
+
const raw = readFileSync9(summaryPath, "utf-8");
|
|
1812
2125
|
const summary = JSON.parse(raw);
|
|
1813
2126
|
sessions.push({ dir, startedAt: new Date(summary.startedAt ?? 0).getTime() });
|
|
1814
2127
|
} catch {
|
|
1815
2128
|
try {
|
|
1816
|
-
const eventsPath =
|
|
1817
|
-
const firstLine =
|
|
2129
|
+
const eventsPath = join10(dir, "events.jsonl");
|
|
2130
|
+
const firstLine = readFileSync9(eventsPath, "utf-8").split("\n")[0];
|
|
1818
2131
|
const firstEvent = JSON.parse(firstLine);
|
|
1819
2132
|
sessions.push({ dir, startedAt: new Date(firstEvent.ts ?? 0).getTime() });
|
|
1820
2133
|
} catch {
|
|
1821
2134
|
try {
|
|
1822
|
-
const st =
|
|
2135
|
+
const st = statSync3(dir);
|
|
1823
2136
|
sessions.push({ dir, startedAt: st.mtimeMs });
|
|
1824
2137
|
} catch {
|
|
1825
2138
|
continue;
|
|
@@ -1832,7 +2145,7 @@ function pruneHistory() {
|
|
|
1832
2145
|
const cutoff = Date.now() - PRUNE_KEEP_DAYS * 24 * 60 * 60 * 1e3;
|
|
1833
2146
|
for (let i = PRUNE_KEEP_COUNT; i < sessions.length; i++) {
|
|
1834
2147
|
if (sessions[i].startedAt < cutoff) {
|
|
1835
|
-
|
|
2148
|
+
rmSync4(sessions[i].dir, { recursive: true, force: true });
|
|
1836
2149
|
}
|
|
1837
2150
|
}
|
|
1838
2151
|
} catch {
|
|
@@ -1844,13 +2157,13 @@ import { ulid } from "ulid";
|
|
|
1844
2157
|
|
|
1845
2158
|
// src/daemon/ask-store.ts
|
|
1846
2159
|
init_paths();
|
|
1847
|
-
import { existsSync as
|
|
2160
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync10, readdirSync as readdirSync7 } from "fs";
|
|
1848
2161
|
|
|
1849
2162
|
// src/daemon/notify.ts
|
|
1850
2163
|
import { spawn, execFile } from "child_process";
|
|
1851
|
-
import { writeFileSync as
|
|
1852
|
-
import { join as
|
|
1853
|
-
import { homedir as
|
|
2164
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, existsSync as existsSync8 } from "fs";
|
|
2165
|
+
import { join as join11 } from "path";
|
|
2166
|
+
import { homedir as homedir5 } from "os";
|
|
1854
2167
|
var TMUX_SOCKET = `/tmp/tmux-${process.getuid?.() ?? 0}/default`;
|
|
1855
2168
|
var SWITCH_SCRIPT = [
|
|
1856
2169
|
"#!/bin/bash",
|
|
@@ -1892,24 +2205,24 @@ var SWITCH_SCRIPT = [
|
|
|
1892
2205
|
""
|
|
1893
2206
|
].join("\n");
|
|
1894
2207
|
function ensureSwitchScript() {
|
|
1895
|
-
const dir =
|
|
1896
|
-
const scriptPath =
|
|
2208
|
+
const dir = join11(homedir5(), ".sisyphus");
|
|
2209
|
+
const scriptPath = join11(dir, "notify-switch.sh");
|
|
1897
2210
|
try {
|
|
1898
|
-
|
|
1899
|
-
|
|
2211
|
+
mkdirSync5(dir, { recursive: true });
|
|
2212
|
+
writeFileSync8(scriptPath, SWITCH_SCRIPT, { mode: 493 });
|
|
1900
2213
|
} catch {
|
|
1901
2214
|
}
|
|
1902
2215
|
}
|
|
1903
2216
|
var notifyProcess = null;
|
|
1904
2217
|
function getNotifyBinary() {
|
|
1905
|
-
return
|
|
2218
|
+
return join11(homedir5(), ".sisyphus", "SisyphusNotify.app", "Contents", "MacOS", "sisyphus-notify");
|
|
1906
2219
|
}
|
|
1907
2220
|
function ensureNotifyProcess() {
|
|
1908
2221
|
if (notifyProcess && !notifyProcess.killed && notifyProcess.stdin?.writable) {
|
|
1909
2222
|
return notifyProcess;
|
|
1910
2223
|
}
|
|
1911
2224
|
const binary = getNotifyBinary();
|
|
1912
|
-
if (!
|
|
2225
|
+
if (!existsSync8(binary)) {
|
|
1913
2226
|
return null;
|
|
1914
2227
|
}
|
|
1915
2228
|
notifyProcess = spawn(binary, [], {
|
|
@@ -1989,7 +2302,7 @@ function maybeNotifyOnAskCreated(cwd, sessionId, meta) {
|
|
|
1989
2302
|
}
|
|
1990
2303
|
}
|
|
1991
2304
|
function createAsk(cwd, sessionId, params) {
|
|
1992
|
-
|
|
2305
|
+
mkdirSync6(askVisualsDir(cwd, sessionId, params.askId), { recursive: true });
|
|
1993
2306
|
const askedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1994
2307
|
const meta = {
|
|
1995
2308
|
askId: params.askId,
|
|
@@ -2018,11 +2331,12 @@ function createAsk(cwd, sessionId, params) {
|
|
|
2018
2331
|
}
|
|
2019
2332
|
function writeDecisions(cwd, sessionId, askId, deck) {
|
|
2020
2333
|
atomicWrite(askDecisionsPath(cwd, sessionId, askId), JSON.stringify(deck, null, 2));
|
|
2334
|
+
void maybeAutoResolveAsk(cwd, sessionId, askId, deck);
|
|
2021
2335
|
}
|
|
2022
2336
|
function readDecisions(cwd, sessionId, askId) {
|
|
2023
2337
|
const p = askDecisionsPath(cwd, sessionId, askId);
|
|
2024
2338
|
try {
|
|
2025
|
-
return JSON.parse(
|
|
2339
|
+
return JSON.parse(readFileSync10(p, { encoding: "utf-8" }));
|
|
2026
2340
|
} catch (_e) {
|
|
2027
2341
|
return null;
|
|
2028
2342
|
}
|
|
@@ -2035,10 +2349,10 @@ function writeOutput(cwd, sessionId, askId, responses, completedAt) {
|
|
|
2035
2349
|
}
|
|
2036
2350
|
function readMeta(cwd, sessionId, askId) {
|
|
2037
2351
|
const p = askMetaPath(cwd, sessionId, askId);
|
|
2038
|
-
if (!
|
|
2352
|
+
if (!existsSync9(p)) {
|
|
2039
2353
|
return null;
|
|
2040
2354
|
}
|
|
2041
|
-
return JSON.parse(
|
|
2355
|
+
return JSON.parse(readFileSync10(p, "utf-8"));
|
|
2042
2356
|
}
|
|
2043
2357
|
async function updateMeta(cwd, sessionId, askId, patch) {
|
|
2044
2358
|
return withLock(askId, () => {
|
|
@@ -2053,10 +2367,44 @@ async function updateMeta(cwd, sessionId, askId, patch) {
|
|
|
2053
2367
|
}
|
|
2054
2368
|
function listAsks(cwd, sessionId) {
|
|
2055
2369
|
const dir = askDir(cwd, sessionId);
|
|
2056
|
-
if (!
|
|
2370
|
+
if (!existsSync9(dir)) {
|
|
2057
2371
|
return [];
|
|
2058
2372
|
}
|
|
2059
|
-
return
|
|
2373
|
+
return readdirSync7(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2374
|
+
}
|
|
2375
|
+
function buildAutoResponses(deck) {
|
|
2376
|
+
const out = [];
|
|
2377
|
+
for (const interaction of deck.interactions) {
|
|
2378
|
+
const first = interaction.options[0];
|
|
2379
|
+
if (!first) continue;
|
|
2380
|
+
out.push({ id: interaction.id, selectedOptionId: first.id });
|
|
2381
|
+
}
|
|
2382
|
+
return out;
|
|
2383
|
+
}
|
|
2384
|
+
async function autoResolveAsk(cwd, sessionId, askId, deck) {
|
|
2385
|
+
try {
|
|
2386
|
+
if (existsSync9(askOutputPath(cwd, sessionId, askId))) return false;
|
|
2387
|
+
const d = deck ?? readDecisions(cwd, sessionId, askId);
|
|
2388
|
+
if (!d) return false;
|
|
2389
|
+
const responses = buildAutoResponses(d);
|
|
2390
|
+
if (responses.length === 0) return false;
|
|
2391
|
+
writeOutput(cwd, sessionId, askId, responses);
|
|
2392
|
+
await updateMeta(cwd, sessionId, askId, {
|
|
2393
|
+
status: "answered",
|
|
2394
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2395
|
+
});
|
|
2396
|
+
return true;
|
|
2397
|
+
} catch (err) {
|
|
2398
|
+
console.warn(`[sisyphus] dangerous-mode auto-resolve failed for ask ${askId}:`, err instanceof Error ? err.message : err);
|
|
2399
|
+
return false;
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
async function maybeAutoResolveAsk(cwd, sessionId, askId, deck) {
|
|
2403
|
+
try {
|
|
2404
|
+
if (!isSessionDangerous(cwd, sessionId)) return;
|
|
2405
|
+
await autoResolveAsk(cwd, sessionId, askId, deck);
|
|
2406
|
+
} catch {
|
|
2407
|
+
}
|
|
2060
2408
|
}
|
|
2061
2409
|
function listOpenAsksFor(cwd, sessionId, askedBy) {
|
|
2062
2410
|
const out = [];
|
|
@@ -2067,7 +2415,7 @@ function listOpenAsksFor(cwd, sessionId, askedBy) {
|
|
|
2067
2415
|
if (meta.orphaned) continue;
|
|
2068
2416
|
if (!meta.blocking) continue;
|
|
2069
2417
|
if (meta.status !== "pending" && meta.status !== "in-progress") continue;
|
|
2070
|
-
if (
|
|
2418
|
+
if (existsSync9(askOutputPath(cwd, sessionId, askId))) continue;
|
|
2071
2419
|
out.push({ askId, status: meta.status, ...meta.title !== void 0 ? { title: meta.title } : {} });
|
|
2072
2420
|
}
|
|
2073
2421
|
return out;
|
|
@@ -2266,7 +2614,7 @@ async function resolveOrchestratorOrphanAsks(cwd, sessionId, selectedOptionId) {
|
|
|
2266
2614
|
}
|
|
2267
2615
|
|
|
2268
2616
|
// src/daemon/orphan-sweep.ts
|
|
2269
|
-
import { existsSync as
|
|
2617
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2270
2618
|
import { execSync as execSync3 } from "child_process";
|
|
2271
2619
|
init_paths();
|
|
2272
2620
|
|
|
@@ -2315,7 +2663,7 @@ async function capturePanePidLstart(paneId) {
|
|
|
2315
2663
|
async function sweepOrphans(registry) {
|
|
2316
2664
|
const reg = registry ?? loadSessionRegistry();
|
|
2317
2665
|
for (const [sessionId, cwd] of Object.entries(reg)) {
|
|
2318
|
-
if (!
|
|
2666
|
+
if (!existsSync10(statePath(cwd, sessionId))) continue;
|
|
2319
2667
|
try {
|
|
2320
2668
|
await sweepSessionAgents(cwd, sessionId);
|
|
2321
2669
|
await sweepSessionAsks(cwd, sessionId);
|
|
@@ -2384,10 +2732,10 @@ function substituteSisyphusVars(text, sessionId, agentId, sesDir) {
|
|
|
2384
2732
|
return text.replace(/\$SISYPHUS_SESSION_DIR/g, sesDir).replace(/\$SISYPHUS_SESSION_ID/g, sessionId).replace(/\$SISYPHUS_AGENT_ID/g, agentId);
|
|
2385
2733
|
}
|
|
2386
2734
|
function renderAgentSuffix(sessionId, instruction, contextDirRel) {
|
|
2387
|
-
const templatePath =
|
|
2735
|
+
const templatePath = resolve5(import.meta.dirname, "../templates/agent-suffix.md");
|
|
2388
2736
|
let template;
|
|
2389
2737
|
try {
|
|
2390
|
-
template =
|
|
2738
|
+
template = readFileSync11(templatePath, "utf-8");
|
|
2391
2739
|
} catch {
|
|
2392
2740
|
template = `# Sisyphus Agent
|
|
2393
2741
|
Session: {{SESSION_ID}}
|
|
@@ -2397,10 +2745,11 @@ Task: {{INSTRUCTION}}`;
|
|
|
2397
2745
|
}
|
|
2398
2746
|
function createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig) {
|
|
2399
2747
|
const base = `${promptsDir(cwd, sessionId)}/${agentId}-plugin`;
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2748
|
+
mkdirSync7(`${base}/.claude-plugin`, { recursive: true });
|
|
2749
|
+
mkdirSync7(`${base}/agents`, { recursive: true });
|
|
2750
|
+
mkdirSync7(`${base}/hooks`, { recursive: true });
|
|
2751
|
+
mkdirSync7(`${base}/skills`, { recursive: true });
|
|
2752
|
+
writeFileSync9(
|
|
2404
2753
|
`${base}/.claude-plugin/plugin.json`,
|
|
2405
2754
|
JSON.stringify({ name: `sisyphus-agent-${agentId}`, version: "1.0.0" }),
|
|
2406
2755
|
"utf-8"
|
|
@@ -2409,65 +2758,39 @@ function createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig) {
|
|
|
2409
2758
|
const substituteEnvVars = (text) => substituteSisyphusVars(text, sessionId, agentId, sesDir);
|
|
2410
2759
|
if (agentConfig?.filePath && agentType && agentType !== "worker") {
|
|
2411
2760
|
const shortName = agentType.replace(/^sisyphus:/, "");
|
|
2412
|
-
const subAgentDir =
|
|
2413
|
-
if (
|
|
2414
|
-
for (const f of
|
|
2761
|
+
const subAgentDir = join12(dirname3(agentConfig.filePath), shortName);
|
|
2762
|
+
if (existsSync11(subAgentDir)) {
|
|
2763
|
+
for (const f of readdirSync8(subAgentDir)) {
|
|
2415
2764
|
if (f.endsWith(".md") && f !== "CLAUDE.md") {
|
|
2416
|
-
|
|
2765
|
+
writeFileSync9(`${base}/agents/${f}`, substituteEnvVars(readFileSync11(join12(subAgentDir, f), "utf-8")), "utf-8");
|
|
2417
2766
|
}
|
|
2418
2767
|
}
|
|
2419
2768
|
}
|
|
2420
2769
|
}
|
|
2421
|
-
const
|
|
2422
|
-
for (const f of ["require-submit.sh", "intercept-send-message.sh", "register-bg-task.sh", "ask-background-guard.sh"]) {
|
|
2423
|
-
copyFileSync3(`${srcHooks}/${f}`, `${base}/hooks/${f}`);
|
|
2424
|
-
}
|
|
2425
|
-
const hooksConfig = {
|
|
2426
|
-
PreToolUse: [
|
|
2427
|
-
{ matcher: "SendMessage", hooks: [{ type: "command", command: "bash ${CLAUDE_PLUGIN_ROOT}/hooks/intercept-send-message.sh" }] },
|
|
2428
|
-
{ matcher: "Bash", hooks: [{ type: "command", command: "bash ${CLAUDE_PLUGIN_ROOT}/hooks/ask-background-guard.sh" }] }
|
|
2429
|
-
],
|
|
2430
|
-
PostToolUse: [
|
|
2431
|
-
{ matcher: "Task", hooks: [{ type: "command", command: "bash ${CLAUDE_PLUGIN_ROOT}/hooks/register-bg-task.sh" }] }
|
|
2432
|
-
]
|
|
2433
|
-
};
|
|
2434
|
-
if (!agentConfig?.frontmatter.interactive) {
|
|
2435
|
-
hooksConfig.Stop = [
|
|
2436
|
-
{ hooks: [{ type: "command", command: "bash ${CLAUDE_PLUGIN_ROOT}/hooks/require-submit.sh" }] }
|
|
2437
|
-
];
|
|
2438
|
-
}
|
|
2770
|
+
const layers = agentPluginLayers(cwd);
|
|
2439
2771
|
const normalizedType = agentType?.replace(/^sisyphus:/, "") ?? "";
|
|
2440
|
-
const
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
"spec": "spec-user-prompt.sh",
|
|
2444
|
-
"review": "review-user-prompt.sh",
|
|
2445
|
-
"review-plan": "review-plan-user-prompt.sh",
|
|
2446
|
-
"debug": "debug-user-prompt.sh",
|
|
2447
|
-
"operator": "operator-user-prompt.sh",
|
|
2448
|
-
"test-spec": "test-spec-user-prompt.sh",
|
|
2449
|
-
"explore": "explore-user-prompt.sh"
|
|
2772
|
+
const filterCtx = {
|
|
2773
|
+
agentType: normalizedType,
|
|
2774
|
+
interactive: agentConfig?.frontmatter.interactive === true
|
|
2450
2775
|
};
|
|
2451
|
-
const
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
}
|
|
2468
|
-
copyFileSync3(`${srcHooks}/plan-write-path.sh`, `${base}/hooks/plan-write-path.sh`);
|
|
2776
|
+
const referenced = collectReferencedHookScripts(layers, filterCtx);
|
|
2777
|
+
const disabled = collectDisabledHookScripts(layers);
|
|
2778
|
+
const skipFiles = new Set(disabled);
|
|
2779
|
+
copyLayered(layers, {
|
|
2780
|
+
subdir: "hooks",
|
|
2781
|
+
destDir: `${base}/hooks`,
|
|
2782
|
+
filter: (name) => name !== "CLAUDE.md" && name !== "hooks.json" && (referenced.has(name) || !name.endsWith(".sh")),
|
|
2783
|
+
skipFiles
|
|
2784
|
+
});
|
|
2785
|
+
const mergedHooks = mergeHookManifests(layers, filterCtx);
|
|
2786
|
+
writeFileSync9(`${base}/hooks/hooks.json`, JSON.stringify({ hooks: mergedHooks }, null, 2), "utf-8");
|
|
2787
|
+
const requestedSkills = agentConfig?.frontmatter.skills ?? [];
|
|
2788
|
+
for (const skillName of requestedSkills) {
|
|
2789
|
+
const ok = copySkill(layers, skillName, `${base}/skills`);
|
|
2790
|
+
if (!ok) {
|
|
2791
|
+
console.warn(`[sisyphus] Agent ${agentId} (${normalizedType}) requested skill '${skillName}' but no layer provides it.`);
|
|
2792
|
+
}
|
|
2469
2793
|
}
|
|
2470
|
-
writeFileSync8(`${base}/hooks/hooks.json`, JSON.stringify({ hooks: hooksConfig }, null, 2), "utf-8");
|
|
2471
2794
|
return base;
|
|
2472
2795
|
}
|
|
2473
2796
|
function setupAgentPane(opts) {
|
|
@@ -2480,9 +2803,9 @@ function setupAgentPane(opts) {
|
|
|
2480
2803
|
const agentTitle = `ssph:${sessionLabel} ${paneLabel} c${cycleNum}`;
|
|
2481
2804
|
setPaneTitle(paneId, agentTitle);
|
|
2482
2805
|
setPaneStyle(paneId, color, { role: paneLabel, session: sessionLabel, cycle: `c${cycleNum}` });
|
|
2483
|
-
const ctxDirRel =
|
|
2806
|
+
const ctxDirRel = relative2(paneCwd, contextDir(cwd, sessionId));
|
|
2484
2807
|
if (agentType === "plan") {
|
|
2485
|
-
|
|
2808
|
+
mkdirSync7(join12(contextDir(cwd, sessionId), agentId), { recursive: true });
|
|
2486
2809
|
}
|
|
2487
2810
|
const sesDir = sessionDir(cwd, sessionId);
|
|
2488
2811
|
const substitute = (text) => substituteSisyphusVars(text, sessionId, agentId, sesDir);
|
|
@@ -2496,7 +2819,7 @@ function setupAgentPane(opts) {
|
|
|
2496
2819
|
}
|
|
2497
2820
|
systemParts.push(suffix);
|
|
2498
2821
|
const suffixFilePath = `${promptsDir(cwd, sessionId)}/${agentId}-system.md`;
|
|
2499
|
-
|
|
2822
|
+
writeFileSync9(suffixFilePath, systemParts.join("\n\n"), "utf-8");
|
|
2500
2823
|
const bannerCmd = resolveBannerCmd();
|
|
2501
2824
|
const npmBinDir = resolveNpmBinDir();
|
|
2502
2825
|
const envExports = buildEnvExports([
|
|
@@ -2517,7 +2840,7 @@ function setupAgentPane(opts) {
|
|
|
2517
2840
|
parts.push(`## Task
|
|
2518
2841
|
|
|
2519
2842
|
${instruction}`);
|
|
2520
|
-
|
|
2843
|
+
writeFileSync9(codexPromptPath, parts.join("\n\n"), "utf-8");
|
|
2521
2844
|
const model = agentConfig?.frontmatter.model ?? "codex-mini";
|
|
2522
2845
|
mainCmd = `codex -m ${shellQuote(model)} --dangerously-bypass-approvals-and-sandbox "$(cat '${codexPromptPath}')"`;
|
|
2523
2846
|
} else {
|
|
@@ -2536,7 +2859,7 @@ ${instruction}`);
|
|
|
2536
2859
|
const sessionIdFlag = claudeSessionId ? ` --session-id "${claudeSessionId}"` : "";
|
|
2537
2860
|
const promptFlag = agentConfig?.frontmatter.systemPrompt === "replace" ? "--system-prompt" : "--append-system-prompt";
|
|
2538
2861
|
const siblingSettingsPath = agentConfig?.filePath ? agentConfig.filePath.replace(/\.md$/, ".settings.json") : null;
|
|
2539
|
-
const settingsFlag = siblingSettingsPath &&
|
|
2862
|
+
const settingsFlag = siblingSettingsPath && existsSync11(siblingSettingsPath) ? ` --settings "${siblingSettingsPath}"` : "";
|
|
2540
2863
|
mainCmd = `claude${permFlag} --effort ${effort}${modelFlag} --plugin-dir "${pluginPath}"${sessionIdFlag}${extraPluginFlags ? ` ${extraPluginFlags}` : ""}${settingsFlag} --name ${shellQuote(agentTitle)} ${promptFlag} "$(cat '${suffixFilePath}')" ${shellQuote(instruction)}`;
|
|
2541
2864
|
resumeArgs = `${permFlag.trimStart()} --effort ${effort}${modelFlag} --plugin-dir "${pluginPath}"${extraPluginFlags ? ` ${extraPluginFlags}` : ""}${settingsFlag}`;
|
|
2542
2865
|
}
|
|
@@ -2555,7 +2878,7 @@ async function spawnAgent(opts) {
|
|
|
2555
2878
|
const count = (agentCounters.get(sessionId) ?? 0) + 1;
|
|
2556
2879
|
agentCounters.set(sessionId, count);
|
|
2557
2880
|
const agentId = `agent-${String(count).padStart(3, "0")}`;
|
|
2558
|
-
const bundledPluginPath =
|
|
2881
|
+
const bundledPluginPath = resolve5(import.meta.dirname, "../templates/agent-plugin");
|
|
2559
2882
|
const agentConfig = resolveAgentConfig(agentType, bundledPluginPath, cwd);
|
|
2560
2883
|
let provider = detectProvider(agentConfig?.frontmatter.model);
|
|
2561
2884
|
const color = (agentConfig?.frontmatter.color ? normalizeTmuxColor(agentConfig.frontmatter.color) : null) ?? getNextColor(sessionId);
|
|
@@ -2579,7 +2902,7 @@ async function spawnAgent(opts) {
|
|
|
2579
2902
|
}
|
|
2580
2903
|
}
|
|
2581
2904
|
const repo = opts.repo !== void 0 ? opts.repo : ".";
|
|
2582
|
-
const repoRoot = repo === "." ? cwd :
|
|
2905
|
+
const repoRoot = repo === "." ? cwd : join12(cwd, repo);
|
|
2583
2906
|
const paneCwd = repoRoot;
|
|
2584
2907
|
const claudeSessionId = provider !== "openai" ? randomUUID3() : void 0;
|
|
2585
2908
|
const { paneId, fullCmd, resumeEnv, resumeArgs } = setupAgentPane({
|
|
@@ -2640,12 +2963,12 @@ async function restartAgent(sessionId, cwd, agentId, windowId) {
|
|
|
2640
2963
|
});
|
|
2641
2964
|
}
|
|
2642
2965
|
const { instruction, agentType, name, color } = agent;
|
|
2643
|
-
const bundledPluginPath =
|
|
2966
|
+
const bundledPluginPath = resolve5(import.meta.dirname, "../templates/agent-plugin");
|
|
2644
2967
|
const agentConfig = resolveAgentConfig(agentType, bundledPluginPath, cwd);
|
|
2645
2968
|
const provider = detectProvider(agentConfig?.frontmatter.model);
|
|
2646
2969
|
let paneCwd = cwd;
|
|
2647
2970
|
if (agent.repo !== ".") {
|
|
2648
|
-
paneCwd =
|
|
2971
|
+
paneCwd = join12(cwd, agent.repo);
|
|
2649
2972
|
}
|
|
2650
2973
|
if (agent.paneId) {
|
|
2651
2974
|
try {
|
|
@@ -2696,7 +3019,7 @@ async function restartAgent(sessionId, cwd, agentId, windowId) {
|
|
|
2696
3019
|
function nextReportNumber(cwd, sessionId, agentId) {
|
|
2697
3020
|
const dir = reportsDir(cwd, sessionId);
|
|
2698
3021
|
try {
|
|
2699
|
-
const files =
|
|
3022
|
+
const files = readdirSync8(dir).filter((f) => f.startsWith(`${agentId}-`) && !f.endsWith("-final.md"));
|
|
2700
3023
|
return String(files.length + 1).padStart(3, "0");
|
|
2701
3024
|
} catch {
|
|
2702
3025
|
return "001";
|
|
@@ -2704,10 +3027,10 @@ function nextReportNumber(cwd, sessionId, agentId) {
|
|
|
2704
3027
|
}
|
|
2705
3028
|
async function handleAgentReport(cwd, sessionId, agentId, content) {
|
|
2706
3029
|
const dir = reportsDir(cwd, sessionId);
|
|
2707
|
-
|
|
3030
|
+
mkdirSync7(dir, { recursive: true });
|
|
2708
3031
|
const num = nextReportNumber(cwd, sessionId, agentId);
|
|
2709
3032
|
const filePath = reportFilePath(cwd, sessionId, agentId, num);
|
|
2710
|
-
|
|
3033
|
+
writeFileSync9(filePath, content, "utf-8");
|
|
2711
3034
|
const entry = {
|
|
2712
3035
|
type: "update",
|
|
2713
3036
|
filePath,
|
|
@@ -2725,9 +3048,9 @@ async function handleAgentReport(cwd, sessionId, agentId, content) {
|
|
|
2725
3048
|
}
|
|
2726
3049
|
function gcBgTasks(cwd, sessionId, agentId) {
|
|
2727
3050
|
const file = `${sessionDir(cwd, sessionId)}/runtime/bg-tasks/${agentId}.txt`;
|
|
2728
|
-
if (!
|
|
3051
|
+
if (!existsSync11(file)) return;
|
|
2729
3052
|
try {
|
|
2730
|
-
const leftover =
|
|
3053
|
+
const leftover = readFileSync11(file, "utf-8").split("\n").map((s) => s.trim()).filter(Boolean);
|
|
2731
3054
|
if (leftover.length > 0) {
|
|
2732
3055
|
console.warn(`[bg-tasks] ${agentId} exited with ${leftover.length} untracked background task(s): ${leftover.join(", ")}`);
|
|
2733
3056
|
emitHistoryEvent(sessionId, "bg-tasks-leftover", { agentId, leftover });
|
|
@@ -2739,9 +3062,9 @@ function gcBgTasks(cwd, sessionId, agentId) {
|
|
|
2739
3062
|
}
|
|
2740
3063
|
async function handleAgentSubmit(cwd, sessionId, agentId, report) {
|
|
2741
3064
|
const dir = reportsDir(cwd, sessionId);
|
|
2742
|
-
|
|
3065
|
+
mkdirSync7(dir, { recursive: true });
|
|
2743
3066
|
const filePath = reportFilePath(cwd, sessionId, agentId, "final");
|
|
2744
|
-
|
|
3067
|
+
writeFileSync9(filePath, report, "utf-8");
|
|
2745
3068
|
const entry = {
|
|
2746
3069
|
type: "final",
|
|
2747
3070
|
filePath,
|
|
@@ -2811,7 +3134,7 @@ async function handleAwait(cwd, sessionId, agentId) {
|
|
|
2811
3134
|
const POLL_MS = 250;
|
|
2812
3135
|
let agent = initialAgent;
|
|
2813
3136
|
while (agent.status === "running") {
|
|
2814
|
-
await new Promise((
|
|
3137
|
+
await new Promise((resolve12) => setTimeout(resolve12, POLL_MS));
|
|
2815
3138
|
const session = getSession(cwd, sessionId);
|
|
2816
3139
|
const found = session.agents.find((a) => a.id === agentId);
|
|
2817
3140
|
if (!found) return null;
|
|
@@ -2832,9 +3155,9 @@ var respawningSessions = /* @__PURE__ */ new Set();
|
|
|
2832
3155
|
|
|
2833
3156
|
// src/daemon/companion.ts
|
|
2834
3157
|
init_paths();
|
|
2835
|
-
import { existsSync as
|
|
3158
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync13, renameSync as renameSync4, writeFileSync as writeFileSync11 } from "fs";
|
|
2836
3159
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
2837
|
-
import { dirname as dirname5, join as
|
|
3160
|
+
import { dirname as dirname5, join as join14 } from "path";
|
|
2838
3161
|
|
|
2839
3162
|
// src/shared/companion-normalize.ts
|
|
2840
3163
|
function emptyStats() {
|
|
@@ -2965,8 +3288,8 @@ var ACHIEVEMENTS = [
|
|
|
2965
3288
|
|
|
2966
3289
|
// src/daemon/companion-memory.ts
|
|
2967
3290
|
init_paths();
|
|
2968
|
-
import { existsSync as
|
|
2969
|
-
import { dirname as dirname4, join as
|
|
3291
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync12, renameSync as renameSync3, writeFileSync as writeFileSync10 } from "fs";
|
|
3292
|
+
import { dirname as dirname4, join as join13 } from "path";
|
|
2970
3293
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
2971
3294
|
import { z } from "zod";
|
|
2972
3295
|
var MAX_OBSERVATIONS = 200;
|
|
@@ -3003,10 +3326,10 @@ function fillDefaults(state) {
|
|
|
3003
3326
|
}
|
|
3004
3327
|
function loadMemoryStrict() {
|
|
3005
3328
|
const path = resolvedMemoryPath();
|
|
3006
|
-
if (!
|
|
3329
|
+
if (!existsSync12(path)) return defaultMemoryState();
|
|
3007
3330
|
let raw;
|
|
3008
3331
|
try {
|
|
3009
|
-
raw =
|
|
3332
|
+
raw = readFileSync12(path, "utf-8");
|
|
3010
3333
|
} catch (err) {
|
|
3011
3334
|
throw new MemoryStoreParseError(err);
|
|
3012
3335
|
}
|
|
@@ -3039,9 +3362,9 @@ function loadMemory() {
|
|
|
3039
3362
|
function saveMemory(store) {
|
|
3040
3363
|
const path = resolvedMemoryPath();
|
|
3041
3364
|
const dir = dirname4(path);
|
|
3042
|
-
|
|
3043
|
-
const tmp =
|
|
3044
|
-
|
|
3365
|
+
mkdirSync8(dir, { recursive: true });
|
|
3366
|
+
const tmp = join13(dir, `.companion-memory.${randomUUID4()}.tmp`);
|
|
3367
|
+
writeFileSync10(tmp, JSON.stringify(store, null, 2), "utf-8");
|
|
3045
3368
|
renameSync3(tmp, path);
|
|
3046
3369
|
}
|
|
3047
3370
|
function appendObservations(records, detectorUpdates) {
|
|
@@ -3468,21 +3791,21 @@ function zScore(value, stats, metric) {
|
|
|
3468
3791
|
}
|
|
3469
3792
|
function loadCompanion() {
|
|
3470
3793
|
const path = companionPath();
|
|
3471
|
-
if (!
|
|
3794
|
+
if (!existsSync13(path)) {
|
|
3472
3795
|
const state2 = createDefaultCompanion();
|
|
3473
3796
|
saveCompanion(state2);
|
|
3474
3797
|
return state2;
|
|
3475
3798
|
}
|
|
3476
|
-
const raw =
|
|
3799
|
+
const raw = readFileSync13(path, "utf-8");
|
|
3477
3800
|
const state = JSON.parse(raw);
|
|
3478
3801
|
return normalizeCompanion(state);
|
|
3479
3802
|
}
|
|
3480
3803
|
function saveCompanion(state) {
|
|
3481
3804
|
const path = companionPath();
|
|
3482
3805
|
const dir = dirname5(path);
|
|
3483
|
-
|
|
3484
|
-
const tmp =
|
|
3485
|
-
|
|
3806
|
+
mkdirSync9(dir, { recursive: true });
|
|
3807
|
+
const tmp = join14(dir, `.companion.${randomUUID5()}.tmp`);
|
|
3808
|
+
writeFileSync11(tmp, JSON.stringify(state, null, 2), "utf-8");
|
|
3486
3809
|
renameSync4(tmp, path);
|
|
3487
3810
|
}
|
|
3488
3811
|
var MAX_COMMENTARY_HISTORY = 1e3;
|
|
@@ -4069,7 +4392,7 @@ async function runPostSessionObservations(companion, session, prev) {
|
|
|
4069
4392
|
}
|
|
4070
4393
|
|
|
4071
4394
|
// src/daemon/companion-commentary.ts
|
|
4072
|
-
import { basename as
|
|
4395
|
+
import { basename as basename4 } from "path";
|
|
4073
4396
|
import { z as z2 } from "zod";
|
|
4074
4397
|
var COMMENTARY_JSON_SCHEMA = {
|
|
4075
4398
|
type: "object",
|
|
@@ -4880,9 +5203,9 @@ Generate a single agent nickname. One word only. No quotes, no explanation.`;
|
|
|
4880
5203
|
}
|
|
4881
5204
|
|
|
4882
5205
|
// src/daemon/companion-popup.ts
|
|
4883
|
-
import { writeFileSync as
|
|
5206
|
+
import { writeFileSync as writeFileSync12, readFileSync as readFileSync14, unlinkSync as unlinkSync2, existsSync as existsSync14 } from "fs";
|
|
4884
5207
|
import { tmpdir } from "os";
|
|
4885
|
-
import { join as
|
|
5208
|
+
import { join as join15, resolve as resolve6 } from "path";
|
|
4886
5209
|
|
|
4887
5210
|
// src/shared/companion-render.ts
|
|
4888
5211
|
import stringWidth from "string-width";
|
|
@@ -5178,10 +5501,10 @@ function applyColor(result, fields, facePart, mood, opts) {
|
|
|
5178
5501
|
var POPUP_WIDTH = 38;
|
|
5179
5502
|
var INNER_WIDTH = POPUP_WIDTH - 6;
|
|
5180
5503
|
var POPUP_DURATION = 15;
|
|
5181
|
-
var POPUP_TMP_PREFIX =
|
|
5182
|
-
var POPUP_SCRIPT =
|
|
5183
|
-
var POPUP_RESULT_PREFIX =
|
|
5184
|
-
var WHIP_ANIMATION_PATH =
|
|
5504
|
+
var POPUP_TMP_PREFIX = join15(tmpdir(), "sisyphus-popup");
|
|
5505
|
+
var POPUP_SCRIPT = join15(tmpdir(), "sisyphus-popup.sh");
|
|
5506
|
+
var POPUP_RESULT_PREFIX = join15(tmpdir(), "sisyphus-popup-result");
|
|
5507
|
+
var WHIP_ANIMATION_PATH = resolve6(import.meta.dirname, "../templates/whip-animation.sh");
|
|
5185
5508
|
var WHIP_ANIMATION_ROWS = 12;
|
|
5186
5509
|
function wrapText(text, width) {
|
|
5187
5510
|
const words = text.split(" ");
|
|
@@ -5222,9 +5545,9 @@ function showCommentaryPopupQueue(pages) {
|
|
|
5222
5545
|
const contentLineCount = content.split("\n").length - 1;
|
|
5223
5546
|
const contentHeight = Math.max(contentLineCount + 2, 5);
|
|
5224
5547
|
if (contentHeight > maxContentHeight) maxContentHeight = contentHeight;
|
|
5225
|
-
|
|
5548
|
+
writeFileSync12(`${POPUP_TMP_PREFIX}-${i}.txt`, content);
|
|
5226
5549
|
}
|
|
5227
|
-
const whipAvailable =
|
|
5550
|
+
const whipAvailable = existsSync14(WHIP_ANIMATION_PATH);
|
|
5228
5551
|
if (whipAvailable && maxContentHeight < WHIP_ANIMATION_ROWS + 2) {
|
|
5229
5552
|
maxContentHeight = WHIP_ANIMATION_ROWS + 2;
|
|
5230
5553
|
}
|
|
@@ -5271,7 +5594,7 @@ if [ ! -f "$RESULT_FILE" ]; then
|
|
|
5271
5594
|
printf 'neutral' > "$RESULT_FILE"
|
|
5272
5595
|
fi
|
|
5273
5596
|
`;
|
|
5274
|
-
|
|
5597
|
+
writeFileSync12(POPUP_SCRIPT, script, { mode: 493 });
|
|
5275
5598
|
try {
|
|
5276
5599
|
unlinkSync2(POPUP_RESULT_PREFIX);
|
|
5277
5600
|
} catch {
|
|
@@ -5298,7 +5621,7 @@ fi
|
|
|
5298
5621
|
}
|
|
5299
5622
|
let raw;
|
|
5300
5623
|
try {
|
|
5301
|
-
raw =
|
|
5624
|
+
raw = readFileSync14(POPUP_RESULT_PREFIX, "utf8").trim();
|
|
5302
5625
|
} catch {
|
|
5303
5626
|
return null;
|
|
5304
5627
|
} finally {
|
|
@@ -5734,7 +6057,7 @@ async function pollSession(sessionId, cwd, windowId, increment, sessionCache) {
|
|
|
5734
6057
|
}
|
|
5735
6058
|
|
|
5736
6059
|
// src/daemon/mode-notify.ts
|
|
5737
|
-
import { existsSync as
|
|
6060
|
+
import { existsSync as existsSync15 } from "fs";
|
|
5738
6061
|
import { ulid as ulid2 } from "ulid";
|
|
5739
6062
|
init_paths();
|
|
5740
6063
|
function capitalize(s) {
|
|
@@ -5757,7 +6080,7 @@ function findOpenModeTransitionAsk(cwd, sessionId) {
|
|
|
5757
6080
|
if (meta.modeTransition !== true) continue;
|
|
5758
6081
|
if (meta.status === "answered") continue;
|
|
5759
6082
|
if (meta.orphaned === true) continue;
|
|
5760
|
-
if (
|
|
6083
|
+
if (existsSync15(askOutputPath(cwd, sessionId, askId))) continue;
|
|
5761
6084
|
return askId;
|
|
5762
6085
|
}
|
|
5763
6086
|
return null;
|
|
@@ -5776,9 +6099,9 @@ function buildNextChain(prevChain, prevMode, nextMode, prevModeStats) {
|
|
|
5776
6099
|
}
|
|
5777
6100
|
return [{ mode: "unknown" }, { mode: nextMode }];
|
|
5778
6101
|
}
|
|
5779
|
-
function renderBody(chain) {
|
|
6102
|
+
function renderBody(chain, cwd) {
|
|
5780
6103
|
const current = chain[chain.length - 1];
|
|
5781
|
-
const description = discoverOrchestratorModes().find((m) => m.name === current.mode)?.description?.trim();
|
|
6104
|
+
const description = discoverOrchestratorModes(cwd).find((m) => m.name === current.mode)?.description?.trim();
|
|
5782
6105
|
const lines = [];
|
|
5783
6106
|
if (description) {
|
|
5784
6107
|
lines.push(`**${capitalize(current.mode)}** \u2014 ${description}`);
|
|
@@ -5812,7 +6135,7 @@ async function emitModeTransitionNotify(cwd, sessionId, prevMode, nextMode, prev
|
|
|
5812
6135
|
const subtitle = chain.map((e) => e.mode).join(" \u2192 ");
|
|
5813
6136
|
const title = "Mode change";
|
|
5814
6137
|
const deckTitle = `Mode: ${subtitle}`;
|
|
5815
|
-
const body = renderBody(chain);
|
|
6138
|
+
const body = renderBody(chain, cwd);
|
|
5816
6139
|
const interaction = {
|
|
5817
6140
|
id: "mode-transition",
|
|
5818
6141
|
title,
|
|
@@ -5864,19 +6187,19 @@ async function emitModeTransitionNotify(cwd, sessionId, prevMode, nextMode, prev
|
|
|
5864
6187
|
function detectRepos(cwd) {
|
|
5865
6188
|
const config = loadConfig(cwd);
|
|
5866
6189
|
const repos = [];
|
|
5867
|
-
if (
|
|
6190
|
+
if (existsSync16(join16(cwd, ".git"))) {
|
|
5868
6191
|
try {
|
|
5869
6192
|
repos.push(getRepoInfo(cwd, "."));
|
|
5870
6193
|
} catch {
|
|
5871
6194
|
}
|
|
5872
6195
|
}
|
|
5873
6196
|
try {
|
|
5874
|
-
const entries =
|
|
6197
|
+
const entries = readdirSync10(cwd, { withFileTypes: true });
|
|
5875
6198
|
for (const entry of entries) {
|
|
5876
6199
|
if (!entry.isDirectory()) continue;
|
|
5877
6200
|
if (entry.name.startsWith(".")) continue;
|
|
5878
|
-
const childPath =
|
|
5879
|
-
if (
|
|
6201
|
+
const childPath = join16(cwd, entry.name);
|
|
6202
|
+
if (existsSync16(join16(childPath, ".git"))) {
|
|
5880
6203
|
try {
|
|
5881
6204
|
repos.push(getRepoInfo(childPath, entry.name));
|
|
5882
6205
|
} catch {
|
|
@@ -5912,19 +6235,44 @@ function getOrchestratorPaneId(sessionId) {
|
|
|
5912
6235
|
function setOrchestratorPaneId(sessionId, paneId) {
|
|
5913
6236
|
sessionOrchestratorPane.set(sessionId, paneId);
|
|
5914
6237
|
}
|
|
6238
|
+
function resolveOrchestratorSettings(cwd, sessionId) {
|
|
6239
|
+
const bundled = resolve7(import.meta.dirname, "../templates/orchestrator-settings.json");
|
|
6240
|
+
const projectSettings = projectOrchestratorSettingsPath(cwd);
|
|
6241
|
+
const userSettings = userOrchestratorSettingsPath();
|
|
6242
|
+
const hasProject = existsSync16(projectSettings);
|
|
6243
|
+
const hasUser = existsSync16(userSettings);
|
|
6244
|
+
if (!hasProject && !hasUser) return bundled;
|
|
6245
|
+
let merged = {};
|
|
6246
|
+
for (const path of [bundled, hasUser ? userSettings : null, hasProject ? projectSettings : null]) {
|
|
6247
|
+
if (!path || !existsSync16(path)) continue;
|
|
6248
|
+
try {
|
|
6249
|
+
const parsed = JSON.parse(readFileSync15(path, "utf-8"));
|
|
6250
|
+
merged = { ...merged, ...parsed };
|
|
6251
|
+
} catch (err) {
|
|
6252
|
+
console.warn(`[sisyphus] Failed to parse settings layer ${path}: ${err instanceof Error ? err.message : err}`);
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6255
|
+
const out = join16(promptsDir(cwd, sessionId), "orchestrator-settings.merged.json");
|
|
6256
|
+
writeFileSync13(out, JSON.stringify(merged, null, 2), "utf-8");
|
|
6257
|
+
return out;
|
|
6258
|
+
}
|
|
5915
6259
|
function loadOrchestratorPrompt(cwd, sessionId, mode) {
|
|
5916
6260
|
const projectPath = projectOrchestratorPromptPath(cwd);
|
|
5917
|
-
if (
|
|
5918
|
-
return
|
|
6261
|
+
if (existsSync16(projectPath)) {
|
|
6262
|
+
return readFileSync15(projectPath, "utf-8");
|
|
5919
6263
|
}
|
|
5920
|
-
const
|
|
5921
|
-
|
|
5922
|
-
|
|
6264
|
+
const userPath = userOrchestratorPromptPath();
|
|
6265
|
+
if (existsSync16(userPath)) {
|
|
6266
|
+
return readFileSync15(userPath, "utf-8");
|
|
6267
|
+
}
|
|
6268
|
+
const basePath = resolve7(import.meta.dirname, "../templates/orchestrator-base.md");
|
|
6269
|
+
const base = readFileSync15(basePath, "utf-8");
|
|
6270
|
+
const modes = discoverOrchestratorModes(cwd);
|
|
5923
6271
|
const selected = modes.find((m) => m.name === mode) ?? modes.find((m) => m.name === "discovery");
|
|
5924
6272
|
if (!selected) {
|
|
5925
6273
|
throw new Error(`Unknown orchestrator mode '${mode}' and no fallback found. Available: ${modes.map((m) => m.name).join(", ")}`);
|
|
5926
6274
|
}
|
|
5927
|
-
const modeContent =
|
|
6275
|
+
const modeContent = readFileSync15(selected.filePath, "utf-8");
|
|
5928
6276
|
const modeBody = extractAgentBody(modeContent);
|
|
5929
6277
|
return base + "\n\n" + modeBody;
|
|
5930
6278
|
}
|
|
@@ -5945,8 +6293,8 @@ function buildCompletionContent(session) {
|
|
|
5945
6293
|
lines.push("");
|
|
5946
6294
|
}
|
|
5947
6295
|
const logsDirPath = logsDir(session.cwd, session.id);
|
|
5948
|
-
if (
|
|
5949
|
-
const logFiles =
|
|
6296
|
+
if (existsSync16(logsDirPath)) {
|
|
6297
|
+
const logFiles = readdirSync10(logsDirPath).filter((f) => f.startsWith("cycle-") && f.endsWith(".md")).sort();
|
|
5950
6298
|
if (logFiles.length > 0) {
|
|
5951
6299
|
lines.push("### Cycle Logs\n");
|
|
5952
6300
|
const cycleByNum = new Map(session.orchestratorCycles.map((c) => [c.cycle, c]));
|
|
@@ -5970,7 +6318,7 @@ function buildCompletionContent(session) {
|
|
|
5970
6318
|
while (j < entries.length && entries[j].idle && entries[j].mode === e.mode) j++;
|
|
5971
6319
|
const runEntries = entries.slice(i, j);
|
|
5972
6320
|
if (runEntries.length === 1) {
|
|
5973
|
-
const content =
|
|
6321
|
+
const content = readFileSync15(join16(logsDirPath, e.file), "utf-8").trim();
|
|
5974
6322
|
if (content) {
|
|
5975
6323
|
lines.push(content);
|
|
5976
6324
|
lines.push("");
|
|
@@ -5986,7 +6334,7 @@ function buildCompletionContent(session) {
|
|
|
5986
6334
|
}
|
|
5987
6335
|
i = j;
|
|
5988
6336
|
} else {
|
|
5989
|
-
const content =
|
|
6337
|
+
const content = readFileSync15(join16(logsDirPath, e.file), "utf-8").trim();
|
|
5990
6338
|
if (content) {
|
|
5991
6339
|
lines.push(content);
|
|
5992
6340
|
lines.push("");
|
|
@@ -5997,11 +6345,11 @@ function buildCompletionContent(session) {
|
|
|
5997
6345
|
}
|
|
5998
6346
|
}
|
|
5999
6347
|
const reportsDirPath = reportsDir(session.cwd, session.id);
|
|
6000
|
-
if (
|
|
6001
|
-
const reportFiles =
|
|
6348
|
+
if (existsSync16(reportsDirPath)) {
|
|
6349
|
+
const reportFiles = readdirSync10(reportsDirPath).filter((f) => f.endsWith(".md"));
|
|
6002
6350
|
if (reportFiles.length > 0) {
|
|
6003
6351
|
lines.push("### Detailed Reports\n");
|
|
6004
|
-
lines.push(`Full agent reports: @${
|
|
6352
|
+
lines.push(`Full agent reports: @${relative3(session.cwd, reportsDirPath)}
|
|
6005
6353
|
`);
|
|
6006
6354
|
}
|
|
6007
6355
|
}
|
|
@@ -6023,21 +6371,21 @@ ${session.context}
|
|
|
6023
6371
|
}
|
|
6024
6372
|
} else {
|
|
6025
6373
|
let ctxFiles = [];
|
|
6026
|
-
if (
|
|
6027
|
-
ctxFiles =
|
|
6374
|
+
if (existsSync16(ctxDir)) {
|
|
6375
|
+
ctxFiles = readdirSync10(ctxDir).filter((f) => f !== "CLAUDE.md");
|
|
6028
6376
|
}
|
|
6029
6377
|
if (ctxFiles.length > 0) {
|
|
6030
6378
|
contextSection = `
|
|
6031
6379
|
## Context
|
|
6032
6380
|
|
|
6033
|
-
@${
|
|
6381
|
+
@${relative3(session.cwd, ctxDir)}
|
|
6034
6382
|
`;
|
|
6035
6383
|
}
|
|
6036
6384
|
}
|
|
6037
6385
|
const messages = session.messages ?? [];
|
|
6038
6386
|
const messagesSection = messages.length > 0 ? "\n### Messages\n\n" + messages.map((m) => {
|
|
6039
6387
|
const sourceLabel = m.source.type === "agent" ? `agent:${m.source.agentId}` : m.source.type === "system" && m.source.detail ? `system:${m.source.detail}` : m.source.type;
|
|
6040
|
-
const fileRef = m.filePath ? ` \u2192 ${
|
|
6388
|
+
const fileRef = m.filePath ? ` \u2192 ${relative3(session.cwd, m.filePath)}` : "";
|
|
6041
6389
|
return `- [${sourceLabel} @ ${m.timestamp}] "${m.summary}"${fileRef}`;
|
|
6042
6390
|
}).join("\n") + "\n" : "";
|
|
6043
6391
|
let mostRecentCycleSection = "";
|
|
@@ -6054,7 +6402,7 @@ ${session.context}
|
|
|
6054
6402
|
if (!agent) return `- **${id}**: unknown (no agent data)`;
|
|
6055
6403
|
const finalReport = agent.reports.find((r) => r.type === "final");
|
|
6056
6404
|
const reportToUse = finalReport ?? agent.reports[agent.reports.length - 1];
|
|
6057
|
-
const reportRef = reportToUse ? `@${
|
|
6405
|
+
const reportRef = reportToUse ? `@${relative3(session.cwd, reportToUse.filePath)}` : "(no reports)";
|
|
6058
6406
|
return `- **${id}** (${agent.name}) [${agent.status}]: ${reportRef}`;
|
|
6059
6407
|
}).join("\n");
|
|
6060
6408
|
mostRecentCycleSection = `
|
|
@@ -6065,10 +6413,10 @@ ${agentLines}
|
|
|
6065
6413
|
}
|
|
6066
6414
|
}
|
|
6067
6415
|
const strategyFile = strategyPath(session.cwd, session.id);
|
|
6068
|
-
const strategyRef =
|
|
6069
|
-
const roadmapRef =
|
|
6416
|
+
const strategyRef = existsSync16(strategyFile) ? `@${relative3(session.cwd, strategyFile)}` : "(empty)";
|
|
6417
|
+
const roadmapRef = existsSync16(roadmapFile) ? `@${relative3(session.cwd, roadmapFile)}` : "(empty)";
|
|
6070
6418
|
const digestFile = digestPath(session.cwd, session.id);
|
|
6071
|
-
const digestRef =
|
|
6419
|
+
const digestRef = existsSync16(digestFile) ? `@${relative3(session.cwd, digestFile)}` : "(not yet created)";
|
|
6072
6420
|
const repos = detectRepos(session.cwd);
|
|
6073
6421
|
let repositoriesSection = "\n\n## Repositories\n";
|
|
6074
6422
|
if (repos.length === 0) {
|
|
@@ -6095,7 +6443,7 @@ ${agentLines}
|
|
|
6095
6443
|
}
|
|
6096
6444
|
}
|
|
6097
6445
|
const goalFile = goalPath(session.cwd, session.id);
|
|
6098
|
-
const goalContent =
|
|
6446
|
+
const goalContent = existsSync16(goalFile) ? readFileSync15(goalFile, "utf-8").trim() : session.task;
|
|
6099
6447
|
const modeContent = modeContentBuilders[mode]?.(session) ?? "";
|
|
6100
6448
|
return `## Goal
|
|
6101
6449
|
|
|
@@ -6103,7 +6451,7 @@ ${goalContent}
|
|
|
6103
6451
|
${contextSection}${messagesSection}
|
|
6104
6452
|
### Cycle Log
|
|
6105
6453
|
|
|
6106
|
-
Write your cycle summary to: ${
|
|
6454
|
+
Write your cycle summary to: ${relative3(session.cwd, logFile)}
|
|
6107
6455
|
${mostRecentCycleSection}${modeContent}
|
|
6108
6456
|
## Strategy
|
|
6109
6457
|
|
|
@@ -6129,7 +6477,7 @@ async function spawnOrchestrator(sessionId, cwd, windowId, message, forceMode) {
|
|
|
6129
6477
|
const mode = forceMode ?? (lastCycle?.mode ?? "discovery");
|
|
6130
6478
|
const basePrompt = loadOrchestratorPrompt(cwd, sessionId, mode);
|
|
6131
6479
|
const formattedState = formatStateForOrchestrator(session, mode);
|
|
6132
|
-
const agentPluginPath =
|
|
6480
|
+
const agentPluginPath = resolve7(import.meta.dirname, "../templates/agent-plugin");
|
|
6133
6481
|
const agentTypes = discoverAgentTypes(agentPluginPath, session.cwd).filter((t2) => t2.source === "bundled");
|
|
6134
6482
|
const agentTypeLines = agentTypes.length > 0 ? agentTypes.map((t2) => {
|
|
6135
6483
|
const modelTag = t2.model ? ` (${t2.model})` : "";
|
|
@@ -6138,7 +6486,7 @@ async function spawnOrchestrator(sessionId, cwd, windowId, message, forceMode) {
|
|
|
6138
6486
|
}).join("\n") : " (none)";
|
|
6139
6487
|
const sesDir = sessionDir(cwd, sessionId);
|
|
6140
6488
|
const substituteEnvVars = (text) => text.replace(/\$SISYPHUS_SESSION_DIR/g, sesDir).replace(/\$SISYPHUS_SESSION_ID/g, sessionId);
|
|
6141
|
-
const modes = discoverOrchestratorModes();
|
|
6489
|
+
const modes = discoverOrchestratorModes(cwd);
|
|
6142
6490
|
const modeLines = modes.map((m) => {
|
|
6143
6491
|
const desc = m.description ? ` \u2014 ${m.description}` : "";
|
|
6144
6492
|
return `- \`${m.name}\`${desc}`;
|
|
@@ -6150,7 +6498,7 @@ async function spawnOrchestrator(sessionId, cwd, windowId, message, forceMode) {
|
|
|
6150
6498
|
const systemPrompt = renderEffortMarkers(substitutedPrompt, sessionEffort);
|
|
6151
6499
|
const cycleNum = session.orchestratorCycles.length + 1;
|
|
6152
6500
|
const promptFilePath = `${promptsDir(cwd, sessionId)}/orchestrator-system-${cycleNum}.md`;
|
|
6153
|
-
|
|
6501
|
+
writeFileSync13(promptFilePath, systemPrompt, "utf-8");
|
|
6154
6502
|
sessionWindowMap.set(sessionId, windowId);
|
|
6155
6503
|
const npmBinDir = resolveNpmBinDir();
|
|
6156
6504
|
const envExports = buildEnvExports([
|
|
@@ -6187,14 +6535,19 @@ ${continuationText}`;
|
|
|
6187
6535
|
The previous cycle waited ~${hours}h for a \`sis ask\` answer. Repository state and any in-flight context may have drifted during that window. Briefly verify before acting on the answer.`;
|
|
6188
6536
|
}
|
|
6189
6537
|
const userPromptFilePath = `${promptsDir(cwd, sessionId)}/orchestrator-user-${cycleNum}.md`;
|
|
6190
|
-
|
|
6538
|
+
writeFileSync13(userPromptFilePath, substituteEnvVars(userPrompt), "utf-8");
|
|
6191
6539
|
if (session.messages && session.messages.length > 0) {
|
|
6192
6540
|
await drainMessages(cwd, sessionId, session.messages.length);
|
|
6193
6541
|
}
|
|
6194
|
-
const
|
|
6195
|
-
const
|
|
6196
|
-
|
|
6197
|
-
|
|
6542
|
+
const pluginPath = join16(sesDir, ".orchestrator-plugin");
|
|
6543
|
+
const orchLayers = orchestratorPluginLayers(cwd);
|
|
6544
|
+
if (orchLayers.length === 0) {
|
|
6545
|
+
const pluginSrcPath = resolve7(import.meta.dirname, "../templates/orchestrator-plugin");
|
|
6546
|
+
renderPluginDir(pluginSrcPath, pluginPath, sessionEffort);
|
|
6547
|
+
} else {
|
|
6548
|
+
renderLayeredPluginDir(orchLayers, pluginPath, sessionEffort);
|
|
6549
|
+
}
|
|
6550
|
+
const settingsPath = resolveOrchestratorSettings(cwd, sessionId);
|
|
6198
6551
|
const config = loadConfig(cwd);
|
|
6199
6552
|
const effort = config.orchestratorEffort ?? "xhigh";
|
|
6200
6553
|
const model = config.model;
|
|
@@ -6307,7 +6660,7 @@ function cleanupSessionMaps(sessionId) {
|
|
|
6307
6660
|
init_paths();
|
|
6308
6661
|
|
|
6309
6662
|
// src/daemon/status-dots.ts
|
|
6310
|
-
import { readFileSync as
|
|
6663
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
6311
6664
|
var CLAUDE_STATE_DIR = "/tmp/claude-tmux-state";
|
|
6312
6665
|
var DOT_MAP = {
|
|
6313
6666
|
"orchestrator:processing": { icon: "\u25CF", color: "#d4ad6a" },
|
|
@@ -6333,7 +6686,7 @@ function renderDots(dots) {
|
|
|
6333
6686
|
function readClaudeState(paneId) {
|
|
6334
6687
|
const numericId = paneId.replace("%", "");
|
|
6335
6688
|
try {
|
|
6336
|
-
const content =
|
|
6689
|
+
const content = readFileSync16(`${CLAUDE_STATE_DIR}/${numericId}`, "utf-8").trim();
|
|
6337
6690
|
if (content === "idle" || content === "processing" || content === "stopped") {
|
|
6338
6691
|
return content;
|
|
6339
6692
|
}
|
|
@@ -6491,21 +6844,21 @@ function buildManifest(args) {
|
|
|
6491
6844
|
init_paths();
|
|
6492
6845
|
import { execFile as execFile2 } from "child_process";
|
|
6493
6846
|
import { promisify } from "util";
|
|
6494
|
-
import { existsSync as
|
|
6495
|
-
import { homedir as
|
|
6496
|
-
import { join as
|
|
6847
|
+
import { existsSync as existsSync17, readFileSync as readFileSync17, mkdirSync as mkdirSync10, symlinkSync, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "fs";
|
|
6848
|
+
import { homedir as homedir6 } from "os";
|
|
6849
|
+
import { join as join17 } from "path";
|
|
6497
6850
|
function sanitizeName(name) {
|
|
6498
6851
|
return name.replace(/[^a-zA-Z0-9-_]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
6499
6852
|
}
|
|
6500
6853
|
function buildOutputPath(label, dir) {
|
|
6501
6854
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6502
|
-
|
|
6855
|
+
mkdirSync10(dir, { recursive: true });
|
|
6503
6856
|
const base = `sisyphus-${label}-${date}`;
|
|
6504
|
-
let candidate =
|
|
6857
|
+
let candidate = join17(dir, `${base}.zip`);
|
|
6505
6858
|
let counter = 1;
|
|
6506
|
-
while (
|
|
6859
|
+
while (existsSync17(candidate)) {
|
|
6507
6860
|
counter++;
|
|
6508
|
-
candidate =
|
|
6861
|
+
candidate = join17(dir, `${base}-${counter}.zip`);
|
|
6509
6862
|
}
|
|
6510
6863
|
return candidate;
|
|
6511
6864
|
}
|
|
@@ -6567,38 +6920,38 @@ async function exportSessionToZip(sessionId, cwd, options) {
|
|
|
6567
6920
|
const reveal = options?.reveal ?? true;
|
|
6568
6921
|
const sessDir = sessionDir(cwd, sessionId);
|
|
6569
6922
|
const histDir = historySessionDir(sessionId);
|
|
6570
|
-
const sessExists =
|
|
6571
|
-
const histExists =
|
|
6923
|
+
const sessExists = existsSync17(sessDir);
|
|
6924
|
+
const histExists = existsSync17(histDir);
|
|
6572
6925
|
if (!sessExists && !histExists) {
|
|
6573
6926
|
throw new Error(`No data found for session ${sessionId}`);
|
|
6574
6927
|
}
|
|
6575
6928
|
let label = sessionId.slice(0, 8);
|
|
6576
6929
|
const stPath = statePath(cwd, sessionId);
|
|
6577
|
-
if (
|
|
6930
|
+
if (existsSync17(stPath)) {
|
|
6578
6931
|
try {
|
|
6579
|
-
const state = JSON.parse(
|
|
6932
|
+
const state = JSON.parse(readFileSync17(stPath, "utf-8"));
|
|
6580
6933
|
if (state.name) {
|
|
6581
6934
|
label = sanitizeName(state.name);
|
|
6582
6935
|
}
|
|
6583
6936
|
} catch {
|
|
6584
6937
|
}
|
|
6585
6938
|
}
|
|
6586
|
-
const dir = options?.outputDir ??
|
|
6939
|
+
const dir = options?.outputDir ?? join17(homedir6(), "Downloads");
|
|
6587
6940
|
const outputPath = buildOutputPath(label, dir);
|
|
6588
6941
|
const tmpDir = `/tmp/sisyphus-export-${sessionId.slice(0, 8)}-${Date.now()}`;
|
|
6589
6942
|
try {
|
|
6590
|
-
|
|
6591
|
-
|
|
6943
|
+
mkdirSync10(tmpDir, { recursive: true });
|
|
6944
|
+
writeFileSync14(join17(tmpDir, "CLAUDE.md"), generateGuide(), "utf-8");
|
|
6592
6945
|
if (sessExists) {
|
|
6593
|
-
symlinkSync(sessDir,
|
|
6946
|
+
symlinkSync(sessDir, join17(tmpDir, "session"));
|
|
6594
6947
|
}
|
|
6595
6948
|
if (histExists) {
|
|
6596
|
-
symlinkSync(histDir,
|
|
6949
|
+
symlinkSync(histDir, join17(tmpDir, "history"));
|
|
6597
6950
|
}
|
|
6598
6951
|
const parts = ["CLAUDE.md", sessExists ? "session/" : "", histExists ? "history/" : ""].filter(Boolean);
|
|
6599
6952
|
await execFileAsync("zip", ["-rq", outputPath, ...parts], { cwd: tmpDir });
|
|
6600
6953
|
} finally {
|
|
6601
|
-
|
|
6954
|
+
rmSync5(tmpDir, { recursive: true, force: true });
|
|
6602
6955
|
}
|
|
6603
6956
|
if (reveal) {
|
|
6604
6957
|
try {
|
|
@@ -6672,12 +7025,12 @@ async function runSessionUploadAndPersist(args) {
|
|
|
6672
7025
|
}
|
|
6673
7026
|
|
|
6674
7027
|
// src/shared/version.ts
|
|
6675
|
-
import { readFileSync as
|
|
6676
|
-
import { resolve as
|
|
7028
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
7029
|
+
import { resolve as resolve8 } from "path";
|
|
6677
7030
|
function readSisyphusVersion() {
|
|
6678
7031
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
6679
7032
|
try {
|
|
6680
|
-
const raw =
|
|
7033
|
+
const raw = readFileSync18(resolve8(import.meta.dirname, rel), "utf-8");
|
|
6681
7034
|
const pkg = JSON.parse(raw);
|
|
6682
7035
|
if (pkg.name === "sisyphi" && pkg.version) return pkg.version;
|
|
6683
7036
|
} catch {
|
|
@@ -6718,7 +7071,7 @@ function truncate(s, max) {
|
|
|
6718
7071
|
function readGoal(cwd, sessionId, fallback) {
|
|
6719
7072
|
try {
|
|
6720
7073
|
const p = goalPath(cwd, sessionId);
|
|
6721
|
-
if (
|
|
7074
|
+
if (existsSync18(p)) return readFileSync19(p, "utf-8").trim();
|
|
6722
7075
|
} catch {
|
|
6723
7076
|
}
|
|
6724
7077
|
return fallback;
|
|
@@ -6959,8 +7312,8 @@ var PRUNE_KEEP_DAYS2 = 7;
|
|
|
6959
7312
|
function pruneOldSessions(cwd) {
|
|
6960
7313
|
try {
|
|
6961
7314
|
const dir = sessionsDir(cwd);
|
|
6962
|
-
if (!
|
|
6963
|
-
const entries =
|
|
7315
|
+
if (!existsSync18(dir)) return;
|
|
7316
|
+
const entries = readdirSync11(dir, { withFileTypes: true });
|
|
6964
7317
|
const candidates = [];
|
|
6965
7318
|
for (const entry of entries) {
|
|
6966
7319
|
if (!entry.isDirectory()) continue;
|
|
@@ -6983,7 +7336,7 @@ function pruneOldSessions(cwd) {
|
|
|
6983
7336
|
}
|
|
6984
7337
|
for (const c of candidates) {
|
|
6985
7338
|
if (keep.has(c.id)) continue;
|
|
6986
|
-
|
|
7339
|
+
rmSync6(sessionDir(cwd, c.id), { recursive: true, force: true });
|
|
6987
7340
|
}
|
|
6988
7341
|
} catch (err) {
|
|
6989
7342
|
console.error("[sisyphus] Session pruning failed:", err);
|
|
@@ -7097,8 +7450,8 @@ function getSessionStatus(cwd, sessionId) {
|
|
|
7097
7450
|
}
|
|
7098
7451
|
function listSessions(cwd) {
|
|
7099
7452
|
const dir = sessionsDir(cwd);
|
|
7100
|
-
if (!
|
|
7101
|
-
const entries =
|
|
7453
|
+
if (!existsSync18(dir)) return [];
|
|
7454
|
+
const entries = readdirSync11(dir, { withFileTypes: true });
|
|
7102
7455
|
const sessions = [];
|
|
7103
7456
|
for (const entry of entries) {
|
|
7104
7457
|
if (!entry.isDirectory()) continue;
|
|
@@ -7165,8 +7518,8 @@ function onAllAgentsDone2(sessionId, cwd, windowId) {
|
|
|
7165
7518
|
Agents: ${truncate(spawnedThisCycle, 200)}`;
|
|
7166
7519
|
try {
|
|
7167
7520
|
const logPath = cycleLogPath(cwd, sessionId, cycleNumber);
|
|
7168
|
-
if (
|
|
7169
|
-
const log =
|
|
7521
|
+
if (existsSync18(logPath)) {
|
|
7522
|
+
const log = readFileSync19(logPath, "utf-8").trim();
|
|
7170
7523
|
if (log) cycleCtx += `
|
|
7171
7524
|
Cycle log: ${truncate(log, 200)}`;
|
|
7172
7525
|
}
|
|
@@ -7633,7 +7986,7 @@ import { spawn as spawn2 } from "child_process";
|
|
|
7633
7986
|
import {
|
|
7634
7987
|
closeSync as closeSync2,
|
|
7635
7988
|
constants,
|
|
7636
|
-
existsSync as
|
|
7989
|
+
existsSync as existsSync19,
|
|
7637
7990
|
fstatSync as fstatSync2,
|
|
7638
7991
|
lstatSync,
|
|
7639
7992
|
openSync as openSync2,
|
|
@@ -7641,15 +7994,15 @@ import {
|
|
|
7641
7994
|
writeSync
|
|
7642
7995
|
} from "fs";
|
|
7643
7996
|
import * as fs from "fs";
|
|
7644
|
-
import { resolve as
|
|
7997
|
+
import { resolve as resolve9, dirname as dirname6, isAbsolute, sep } from "path";
|
|
7645
7998
|
import { fileURLToPath } from "url";
|
|
7646
7999
|
import { z as z3 } from "zod";
|
|
7647
8000
|
import { tool } from "@r-cli/sdk";
|
|
7648
8001
|
|
|
7649
8002
|
// src/daemon/transcript-digest.ts
|
|
7650
8003
|
import { openSync, fstatSync, readSync, closeSync } from "fs";
|
|
7651
|
-
import { homedir as
|
|
7652
|
-
import { join as
|
|
8004
|
+
import { homedir as homedir7 } from "os";
|
|
8005
|
+
import { join as join18 } from "path";
|
|
7653
8006
|
var BYTE_CAP = 8 * 1024;
|
|
7654
8007
|
var TAIL_BYTES = 64 * 1024;
|
|
7655
8008
|
var TOOL_USE_INPUT_CAP = 200;
|
|
@@ -7662,8 +8015,8 @@ function digestTranscript(opts) {
|
|
|
7662
8015
|
warnOnce("transcript-digest: no claudeSessionId; falling back to empty context");
|
|
7663
8016
|
return "";
|
|
7664
8017
|
}
|
|
7665
|
-
const home = opts.homeDir ??
|
|
7666
|
-
const path =
|
|
8018
|
+
const home = opts.homeDir ?? homedir7();
|
|
8019
|
+
const path = join18(home, ".claude", "projects", encodeCwd(opts.cwd), `${opts.claudeSessionId}.jsonl`);
|
|
7667
8020
|
let raw;
|
|
7668
8021
|
let fd;
|
|
7669
8022
|
try {
|
|
@@ -7746,11 +8099,11 @@ init_paths();
|
|
|
7746
8099
|
var READ_FILE_CAP = 50 * 1024;
|
|
7747
8100
|
var ANSI_CAP = 256 * 1024;
|
|
7748
8101
|
var SYSTEM_PROMPT_CANDIDATES = [
|
|
7749
|
-
|
|
8102
|
+
resolve9(dirname6(fileURLToPath(import.meta.url)), "templates/termrender-haiku-system.md"),
|
|
7750
8103
|
// dist/templates/
|
|
7751
|
-
|
|
8104
|
+
resolve9(dirname6(fileURLToPath(import.meta.url)), "../templates/termrender-haiku-system.md"),
|
|
7752
8105
|
// <sisyphus>/templates/ from dist/
|
|
7753
|
-
|
|
8106
|
+
resolve9(dirname6(fileURLToPath(import.meta.url)), "../../templates/termrender-haiku-system.md")
|
|
7754
8107
|
// src/daemon/ → <sisyphus>/templates/
|
|
7755
8108
|
];
|
|
7756
8109
|
var cachedSystemPrompt;
|
|
@@ -7763,7 +8116,7 @@ async function generateVisualForQuestion(opts) {
|
|
|
7763
8116
|
if (!question) return { ok: false, error: `qid ${opts.qid} not found in decisions` };
|
|
7764
8117
|
const mdPath = askVisualMarkdownPath(opts.cwd, opts.sessionId, opts.askId, opts.qid);
|
|
7765
8118
|
const ansiPath = askVisualAnsiPath(opts.cwd, opts.sessionId, opts.askId, opts.qid);
|
|
7766
|
-
if (!opts.force &&
|
|
8119
|
+
if (!opts.force && existsSync19(mdPath) && existsSync19(ansiPath)) {
|
|
7767
8120
|
return { ok: true, markdownPath: mdPath, ansiPath, turns: 0 };
|
|
7768
8121
|
}
|
|
7769
8122
|
if (opts.force) {
|
|
@@ -7837,7 +8190,7 @@ function buildUserPrompt(q, askedBy, ctx) {
|
|
|
7837
8190
|
}
|
|
7838
8191
|
function readSystemPrompt() {
|
|
7839
8192
|
if (cachedSystemPrompt !== void 0) return cachedSystemPrompt;
|
|
7840
|
-
const found = SYSTEM_PROMPT_CANDIDATES.find((p) =>
|
|
8193
|
+
const found = SYSTEM_PROMPT_CANDIDATES.find((p) => existsSync19(p));
|
|
7841
8194
|
if (found === void 0) {
|
|
7842
8195
|
throw new Error(
|
|
7843
8196
|
`termrender-haiku-system.md not found in any candidate location: ${SYSTEM_PROMPT_CANDIDATES.join(", ")}`
|
|
@@ -7850,7 +8203,7 @@ function readFileHandler(realSessionCwd, requestedPath) {
|
|
|
7850
8203
|
if (isAbsolute(requestedPath)) {
|
|
7851
8204
|
return Promise.resolve(errorResult("path must be relative to session cwd"));
|
|
7852
8205
|
}
|
|
7853
|
-
const joined =
|
|
8206
|
+
const joined = resolve9(realSessionCwd, requestedPath);
|
|
7854
8207
|
let realPath;
|
|
7855
8208
|
try {
|
|
7856
8209
|
const r = { p: fs.realpathSync(joined, { encoding: "utf-8" }) };
|
|
@@ -7907,23 +8260,23 @@ function errorResult(msg) {
|
|
|
7907
8260
|
return { content: [{ type: "text", text: msg }], isError: true };
|
|
7908
8261
|
}
|
|
7909
8262
|
function spawnAsync(cmd, args, input, timeoutMs) {
|
|
7910
|
-
return new Promise((
|
|
8263
|
+
return new Promise((resolve12) => {
|
|
7911
8264
|
const chunks = [];
|
|
7912
8265
|
const errChunks = [];
|
|
7913
8266
|
const proc = spawn2(cmd, args, { stdio: ["pipe", "pipe", "pipe"], env: execEnv() });
|
|
7914
8267
|
const timer = setTimeout(() => {
|
|
7915
8268
|
proc.kill();
|
|
7916
|
-
|
|
8269
|
+
resolve12({ stdout: "", stderr: "", status: null, error: new Error(`timed out after ${timeoutMs}ms`) });
|
|
7917
8270
|
}, timeoutMs);
|
|
7918
8271
|
proc.stdout.on("data", (d) => chunks.push(d));
|
|
7919
8272
|
proc.stderr.on("data", (d) => errChunks.push(d));
|
|
7920
8273
|
proc.on("error", (err) => {
|
|
7921
8274
|
clearTimeout(timer);
|
|
7922
|
-
|
|
8275
|
+
resolve12({ stdout: "", stderr: "", status: null, error: err });
|
|
7923
8276
|
});
|
|
7924
8277
|
proc.on("close", (code) => {
|
|
7925
8278
|
clearTimeout(timer);
|
|
7926
|
-
|
|
8279
|
+
resolve12({ stdout: Buffer.concat(chunks).toString("utf-8"), stderr: Buffer.concat(errChunks).toString("utf-8"), status: code });
|
|
7927
8280
|
});
|
|
7928
8281
|
proc.stdin.end(input, "utf-8");
|
|
7929
8282
|
});
|
|
@@ -7998,22 +8351,22 @@ function setCompositor(c) {
|
|
|
7998
8351
|
}
|
|
7999
8352
|
var sessionTrackingMap = /* @__PURE__ */ new Map();
|
|
8000
8353
|
function registryPath() {
|
|
8001
|
-
return
|
|
8354
|
+
return join19(globalDir(), "session-registry.json");
|
|
8002
8355
|
}
|
|
8003
8356
|
function persistSessionRegistry() {
|
|
8004
8357
|
const dir = globalDir();
|
|
8005
|
-
|
|
8358
|
+
mkdirSync11(dir, { recursive: true });
|
|
8006
8359
|
const registry = {};
|
|
8007
8360
|
for (const [id, tracking] of sessionTrackingMap) {
|
|
8008
8361
|
registry[id] = tracking.cwd;
|
|
8009
8362
|
}
|
|
8010
|
-
|
|
8363
|
+
writeFileSync15(registryPath(), JSON.stringify(registry, null, 2), "utf-8");
|
|
8011
8364
|
}
|
|
8012
8365
|
function loadSessionRegistry() {
|
|
8013
8366
|
const p = registryPath();
|
|
8014
|
-
if (!
|
|
8367
|
+
if (!existsSync20(p)) return {};
|
|
8015
8368
|
try {
|
|
8016
|
-
return JSON.parse(
|
|
8369
|
+
return JSON.parse(readFileSync21(p, "utf-8"));
|
|
8017
8370
|
} catch (err) {
|
|
8018
8371
|
console.warn("[sisyphus] Failed to parse session registry:", err instanceof Error ? err.message : err);
|
|
8019
8372
|
return {};
|
|
@@ -8060,8 +8413,8 @@ function collectAllSessionIds() {
|
|
|
8060
8413
|
scannedCwds.add(cwd);
|
|
8061
8414
|
try {
|
|
8062
8415
|
const dir = sessionsDir(cwd);
|
|
8063
|
-
if (!
|
|
8064
|
-
for (const entry of
|
|
8416
|
+
if (!existsSync20(dir)) continue;
|
|
8417
|
+
for (const entry of readdirSync12(dir, { withFileTypes: true })) {
|
|
8065
8418
|
if (entry.isDirectory() && !idToCwd.has(entry.name)) {
|
|
8066
8419
|
idToCwd.set(entry.name, cwd);
|
|
8067
8420
|
}
|
|
@@ -8261,7 +8614,7 @@ async function handleRequest(req) {
|
|
|
8261
8614
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
8262
8615
|
if (!tracking) {
|
|
8263
8616
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
8264
|
-
if (
|
|
8617
|
+
if (existsSync20(stateFile)) {
|
|
8265
8618
|
tracking = { cwd: req.cwd, messageCounter: 0 };
|
|
8266
8619
|
sessionTrackingMap.set(req.sessionId, tracking);
|
|
8267
8620
|
persistSessionRegistry();
|
|
@@ -8276,7 +8629,7 @@ async function handleRequest(req) {
|
|
|
8276
8629
|
}
|
|
8277
8630
|
case "clear-orphan": {
|
|
8278
8631
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
8279
|
-
if (!
|
|
8632
|
+
if (!existsSync20(stateFile)) {
|
|
8280
8633
|
return { ok: false, error: `Unknown session: ${req.sessionId}. No state.json at ${stateFile}.` };
|
|
8281
8634
|
}
|
|
8282
8635
|
await Promise.all([
|
|
@@ -8327,7 +8680,7 @@ async function handleRequest(req) {
|
|
|
8327
8680
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
8328
8681
|
if (!tracking) {
|
|
8329
8682
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
8330
|
-
if (
|
|
8683
|
+
if (existsSync20(stateFile)) {
|
|
8331
8684
|
registerSessionCwd(req.sessionId, req.cwd);
|
|
8332
8685
|
tracking = sessionTrackingMap.get(req.sessionId);
|
|
8333
8686
|
} else {
|
|
@@ -8341,7 +8694,7 @@ async function handleRequest(req) {
|
|
|
8341
8694
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
8342
8695
|
if (!tracking) {
|
|
8343
8696
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
8344
|
-
if (
|
|
8697
|
+
if (existsSync20(stateFile)) {
|
|
8345
8698
|
registerSessionCwd(req.sessionId, req.cwd);
|
|
8346
8699
|
tracking = sessionTrackingMap.get(req.sessionId);
|
|
8347
8700
|
} else {
|
|
@@ -8358,7 +8711,7 @@ async function handleRequest(req) {
|
|
|
8358
8711
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
8359
8712
|
if (!tracking) {
|
|
8360
8713
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
8361
|
-
if (
|
|
8714
|
+
if (existsSync20(stateFile)) {
|
|
8362
8715
|
tracking = { cwd: req.cwd, messageCounter: 0 };
|
|
8363
8716
|
sessionTrackingMap.set(req.sessionId, tracking);
|
|
8364
8717
|
persistSessionRegistry();
|
|
@@ -8382,7 +8735,7 @@ async function handleRequest(req) {
|
|
|
8382
8735
|
persistSessionRegistry();
|
|
8383
8736
|
}
|
|
8384
8737
|
const { sessionDir: sessionDir2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
8385
|
-
|
|
8738
|
+
rmSync8(sessionDir2(req.cwd, req.sessionId), { recursive: true, force: true });
|
|
8386
8739
|
return { ok: true };
|
|
8387
8740
|
}
|
|
8388
8741
|
case "pane-exited": {
|
|
@@ -8409,9 +8762,26 @@ async function handleRequest(req) {
|
|
|
8409
8762
|
await updateSession(tracking.cwd, req.sessionId, { effort: req.effort });
|
|
8410
8763
|
return { ok: true };
|
|
8411
8764
|
}
|
|
8765
|
+
case "set-dangerous-mode": {
|
|
8766
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
8767
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
8768
|
+
await updateSession(tracking.cwd, req.sessionId, { dangerousMode: req.enabled });
|
|
8769
|
+
let flushed = 0;
|
|
8770
|
+
if (req.enabled) {
|
|
8771
|
+
for (const askId of listAsks(tracking.cwd, req.sessionId)) {
|
|
8772
|
+
const meta = readMeta(tracking.cwd, req.sessionId, askId);
|
|
8773
|
+
if (!meta) continue;
|
|
8774
|
+
if (meta.status !== "pending" && meta.status !== "in-progress") continue;
|
|
8775
|
+
if (meta.orphaned) continue;
|
|
8776
|
+
const ok = await autoResolveAsk(tracking.cwd, req.sessionId, askId);
|
|
8777
|
+
if (ok) flushed += 1;
|
|
8778
|
+
}
|
|
8779
|
+
}
|
|
8780
|
+
return { ok: true, data: { enabled: req.enabled, flushed } };
|
|
8781
|
+
}
|
|
8412
8782
|
case "set-upload-status": {
|
|
8413
8783
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
8414
|
-
if (!
|
|
8784
|
+
if (!existsSync20(stateFile)) {
|
|
8415
8785
|
return unknownSessionError(req.sessionId);
|
|
8416
8786
|
}
|
|
8417
8787
|
try {
|
|
@@ -8434,19 +8804,19 @@ async function handleRequest(req) {
|
|
|
8434
8804
|
const source = req.source ?? { type: "user" };
|
|
8435
8805
|
const summary = req.content.length > 200 ? req.content.slice(0, 200) + "..." : req.content;
|
|
8436
8806
|
if (req.agentId) {
|
|
8437
|
-
const dir =
|
|
8438
|
-
|
|
8439
|
-
const filePath2 =
|
|
8440
|
-
|
|
8807
|
+
const dir = join19(messagesDir(tracking.cwd, req.sessionId), req.agentId);
|
|
8808
|
+
mkdirSync11(dir, { recursive: true });
|
|
8809
|
+
const filePath2 = join19(dir, `${id}.md`);
|
|
8810
|
+
writeFileSync15(filePath2, req.content, "utf-8");
|
|
8441
8811
|
emitHistoryEvent(req.sessionId, "message", { source: source.type, agentId: req.agentId, content: req.content });
|
|
8442
8812
|
return { ok: true };
|
|
8443
8813
|
}
|
|
8444
8814
|
let filePath;
|
|
8445
8815
|
if (req.content.length > 200) {
|
|
8446
8816
|
const dir = messagesDir(tracking.cwd, req.sessionId);
|
|
8447
|
-
|
|
8448
|
-
filePath =
|
|
8449
|
-
|
|
8817
|
+
mkdirSync11(dir, { recursive: true });
|
|
8818
|
+
filePath = join19(dir, `${id}.md`);
|
|
8819
|
+
writeFileSync15(filePath, req.content, "utf-8");
|
|
8450
8820
|
}
|
|
8451
8821
|
await appendMessage(tracking.cwd, req.sessionId, {
|
|
8452
8822
|
id,
|
|
@@ -8622,9 +8992,9 @@ async function handleRequest(req) {
|
|
|
8622
8992
|
}
|
|
8623
8993
|
}
|
|
8624
8994
|
function startServer() {
|
|
8625
|
-
return new Promise((
|
|
8995
|
+
return new Promise((resolve12, reject) => {
|
|
8626
8996
|
const sock = socketPath();
|
|
8627
|
-
if (
|
|
8997
|
+
if (existsSync20(sock)) {
|
|
8628
8998
|
unlinkSync3(sock);
|
|
8629
8999
|
}
|
|
8630
9000
|
server = createServer((conn) => {
|
|
@@ -8667,23 +9037,23 @@ function startServer() {
|
|
|
8667
9037
|
} catch {
|
|
8668
9038
|
}
|
|
8669
9039
|
console.log(`[sisyphus] Daemon listening on ${sock}`);
|
|
8670
|
-
|
|
9040
|
+
resolve12(server);
|
|
8671
9041
|
});
|
|
8672
9042
|
});
|
|
8673
9043
|
}
|
|
8674
9044
|
function stopServer() {
|
|
8675
|
-
return new Promise((
|
|
9045
|
+
return new Promise((resolve12) => {
|
|
8676
9046
|
if (!server) {
|
|
8677
|
-
|
|
9047
|
+
resolve12();
|
|
8678
9048
|
return;
|
|
8679
9049
|
}
|
|
8680
9050
|
server.close(() => {
|
|
8681
9051
|
const sock = socketPath();
|
|
8682
|
-
if (
|
|
9052
|
+
if (existsSync20(sock)) {
|
|
8683
9053
|
unlinkSync3(sock);
|
|
8684
9054
|
}
|
|
8685
9055
|
server = null;
|
|
8686
|
-
|
|
9056
|
+
resolve12();
|
|
8687
9057
|
});
|
|
8688
9058
|
});
|
|
8689
9059
|
}
|
|
@@ -8691,7 +9061,7 @@ function stopServer() {
|
|
|
8691
9061
|
// src/daemon/heartbeat-asks.ts
|
|
8692
9062
|
import { ulid as ulid3 } from "ulid";
|
|
8693
9063
|
init_paths();
|
|
8694
|
-
import { existsSync as
|
|
9064
|
+
import { existsSync as existsSync21 } from "fs";
|
|
8695
9065
|
var HEARTBEAT_ASKED_BY2 = "system:heartbeat";
|
|
8696
9066
|
var HEARTBEAT_THRESHOLD_MS = 60 * 60 * 1e3;
|
|
8697
9067
|
var HEARTBEAT_SCAN_INTERVAL_MS = 15 * 60 * 1e3;
|
|
@@ -8774,7 +9144,7 @@ async function scanSessionForStaleAsks(cwd, sessionId) {
|
|
|
8774
9144
|
async function scanAllSessionsForStaleAsks() {
|
|
8775
9145
|
const reg = loadSessionRegistry();
|
|
8776
9146
|
for (const [sessionId, cwd] of Object.entries(reg)) {
|
|
8777
|
-
if (!
|
|
9147
|
+
if (!existsSync21(statePath(cwd, sessionId))) continue;
|
|
8778
9148
|
try {
|
|
8779
9149
|
await scanSessionForStaleAsks(cwd, sessionId);
|
|
8780
9150
|
} catch (err) {
|
|
@@ -8829,15 +9199,15 @@ var DEFAULT_STATUS_BAR_CONFIG = {
|
|
|
8829
9199
|
};
|
|
8830
9200
|
|
|
8831
9201
|
// src/daemon/segments/compositor.ts
|
|
8832
|
-
import { readFileSync as
|
|
8833
|
-
import { homedir as
|
|
8834
|
-
import { join as
|
|
9202
|
+
import { readFileSync as readFileSync22, existsSync as existsSync22 } from "fs";
|
|
9203
|
+
import { homedir as homedir8 } from "os";
|
|
9204
|
+
import { join as join20 } from "path";
|
|
8835
9205
|
var STATUS_BAR_BG = "#1d1e21";
|
|
8836
|
-
var SESSION_ORDER_PATH =
|
|
9206
|
+
var SESSION_ORDER_PATH = join20(homedir8(), ".config", "tmux", "session-order");
|
|
8837
9207
|
function getSessionOrder() {
|
|
8838
9208
|
try {
|
|
8839
|
-
if (!
|
|
8840
|
-
return
|
|
9209
|
+
if (!existsSync22(SESSION_ORDER_PATH)) return [];
|
|
9210
|
+
return readFileSync22(SESSION_ORDER_PATH, "utf-8").split("\n").filter(Boolean);
|
|
8841
9211
|
} catch {
|
|
8842
9212
|
return [];
|
|
8843
9213
|
}
|
|
@@ -9374,8 +9744,8 @@ function writeEmptyManifest() {
|
|
|
9374
9744
|
// src/daemon/updater.ts
|
|
9375
9745
|
init_paths();
|
|
9376
9746
|
import { execSync as execSync6 } from "child_process";
|
|
9377
|
-
import { writeFileSync as
|
|
9378
|
-
import { resolve as
|
|
9747
|
+
import { writeFileSync as writeFileSync16, unlinkSync as unlinkSync4, lstatSync as lstatSync2 } from "fs";
|
|
9748
|
+
import { resolve as resolve10 } from "path";
|
|
9379
9749
|
import { get } from "https";
|
|
9380
9750
|
function isNewer(latest, current) {
|
|
9381
9751
|
const a = latest.split(".").map(Number);
|
|
@@ -9389,10 +9759,10 @@ function isNewer(latest, current) {
|
|
|
9389
9759
|
return false;
|
|
9390
9760
|
}
|
|
9391
9761
|
function checkForUpdate() {
|
|
9392
|
-
return new Promise((
|
|
9762
|
+
return new Promise((resolve12) => {
|
|
9393
9763
|
const timeout = setTimeout(() => {
|
|
9394
9764
|
console.error("[sisyphus] Update check timed out (5s)");
|
|
9395
|
-
|
|
9765
|
+
resolve12(null);
|
|
9396
9766
|
}, 5e3);
|
|
9397
9767
|
const req = get("https://registry.npmjs.org/sisyphi/latest", (res) => {
|
|
9398
9768
|
let data = "";
|
|
@@ -9404,26 +9774,26 @@ function checkForUpdate() {
|
|
|
9404
9774
|
try {
|
|
9405
9775
|
const { version: latest } = JSON.parse(data);
|
|
9406
9776
|
if (latest && isNewer(latest, getSisyphusVersion())) {
|
|
9407
|
-
|
|
9777
|
+
resolve12({ current: getSisyphusVersion(), latest });
|
|
9408
9778
|
} else {
|
|
9409
|
-
|
|
9779
|
+
resolve12(null);
|
|
9410
9780
|
}
|
|
9411
9781
|
} catch (err) {
|
|
9412
9782
|
console.error("[sisyphus] Failed to parse registry response:", err);
|
|
9413
|
-
|
|
9783
|
+
resolve12(null);
|
|
9414
9784
|
}
|
|
9415
9785
|
});
|
|
9416
9786
|
});
|
|
9417
9787
|
req.on("error", (err) => {
|
|
9418
9788
|
clearTimeout(timeout);
|
|
9419
9789
|
console.error("[sisyphus] Update check failed:", err.message);
|
|
9420
|
-
|
|
9790
|
+
resolve12(null);
|
|
9421
9791
|
});
|
|
9422
9792
|
});
|
|
9423
9793
|
}
|
|
9424
9794
|
function applyUpdate(expectedVersion) {
|
|
9425
9795
|
try {
|
|
9426
|
-
const nodeDir =
|
|
9796
|
+
const nodeDir = resolve10(process.execPath, "..");
|
|
9427
9797
|
const env = { ...process.env, PATH: `${nodeDir}:${process.env.PATH ?? ""}` };
|
|
9428
9798
|
execSync6("npm install -g sisyphi", { timeout: 15e3, stdio: "pipe", env });
|
|
9429
9799
|
const result = execSync6("npm ls -g sisyphi --json --depth=0", {
|
|
@@ -9445,7 +9815,7 @@ function applyUpdate(expectedVersion) {
|
|
|
9445
9815
|
}
|
|
9446
9816
|
function markUpdating(version) {
|
|
9447
9817
|
try {
|
|
9448
|
-
|
|
9818
|
+
writeFileSync16(daemonUpdatingPath(), version, "utf-8");
|
|
9449
9819
|
} catch {
|
|
9450
9820
|
}
|
|
9451
9821
|
}
|
|
@@ -9457,9 +9827,9 @@ function clearUpdating() {
|
|
|
9457
9827
|
}
|
|
9458
9828
|
function isLinkedInstall() {
|
|
9459
9829
|
try {
|
|
9460
|
-
const nodeDir =
|
|
9830
|
+
const nodeDir = resolve10(process.execPath, "..");
|
|
9461
9831
|
const globalPrefix = execSync6("npm prefix -g", { timeout: 5e3, encoding: "utf-8", env: { ...process.env, PATH: `${nodeDir}:${process.env.PATH ?? ""}` } }).trim();
|
|
9462
|
-
const globalPkgDir =
|
|
9832
|
+
const globalPkgDir = resolve10(globalPrefix, "lib", "node_modules", "sisyphi");
|
|
9463
9833
|
return lstatSync2(globalPkgDir).isSymbolicLink();
|
|
9464
9834
|
} catch {
|
|
9465
9835
|
return false;
|
|
@@ -9501,48 +9871,48 @@ function stopPeriodicUpdateCheck() {
|
|
|
9501
9871
|
}
|
|
9502
9872
|
|
|
9503
9873
|
// src/daemon/plugin-install.ts
|
|
9504
|
-
import { copyFileSync as
|
|
9505
|
-
import { join as
|
|
9506
|
-
import { homedir as
|
|
9874
|
+
import { copyFileSync as copyFileSync5, mkdirSync as mkdirSync12, readdirSync as readdirSync13, statSync as statSync4, existsSync as existsSync23, readFileSync as readFileSync23, chmodSync as chmodSync2 } from "fs";
|
|
9875
|
+
import { join as join21, resolve as resolve11 } from "path";
|
|
9876
|
+
import { homedir as homedir9 } from "os";
|
|
9507
9877
|
var PLUGIN_NAME = "sisyphus-tmux";
|
|
9508
|
-
var INSTALL_DIR =
|
|
9878
|
+
var INSTALL_DIR = join21(homedir9(), ".claude", "plugins", PLUGIN_NAME);
|
|
9509
9879
|
function copyDir(src, dest) {
|
|
9510
|
-
|
|
9511
|
-
for (const entry of
|
|
9512
|
-
const srcPath =
|
|
9513
|
-
const destPath =
|
|
9514
|
-
if (
|
|
9880
|
+
mkdirSync12(dest, { recursive: true });
|
|
9881
|
+
for (const entry of readdirSync13(src)) {
|
|
9882
|
+
const srcPath = join21(src, entry);
|
|
9883
|
+
const destPath = join21(dest, entry);
|
|
9884
|
+
if (statSync4(srcPath).isDirectory()) {
|
|
9515
9885
|
copyDir(srcPath, destPath);
|
|
9516
9886
|
} else {
|
|
9517
|
-
const srcMtime =
|
|
9518
|
-
const destMtime =
|
|
9887
|
+
const srcMtime = statSync4(srcPath).mtimeMs;
|
|
9888
|
+
const destMtime = existsSync23(destPath) ? statSync4(destPath).mtimeMs : 0;
|
|
9519
9889
|
if (srcMtime > destMtime) {
|
|
9520
|
-
|
|
9890
|
+
copyFileSync5(srcPath, destPath);
|
|
9521
9891
|
}
|
|
9522
9892
|
}
|
|
9523
9893
|
}
|
|
9524
9894
|
}
|
|
9525
9895
|
function pluginNeedsUpdate(sourceDir) {
|
|
9526
|
-
const srcHooks =
|
|
9527
|
-
const destHooks =
|
|
9528
|
-
if (!
|
|
9896
|
+
const srcHooks = join21(sourceDir, "hooks", "hooks.json");
|
|
9897
|
+
const destHooks = join21(INSTALL_DIR, "hooks", "hooks.json");
|
|
9898
|
+
if (!existsSync23(destHooks)) return true;
|
|
9529
9899
|
try {
|
|
9530
|
-
return
|
|
9900
|
+
return readFileSync23(srcHooks, "utf-8") !== readFileSync23(destHooks, "utf-8");
|
|
9531
9901
|
} catch {
|
|
9532
9902
|
return true;
|
|
9533
9903
|
}
|
|
9534
9904
|
}
|
|
9535
9905
|
function installPlugin() {
|
|
9536
|
-
const sourceDir =
|
|
9537
|
-
if (!
|
|
9906
|
+
const sourceDir = resolve11(import.meta.dirname, "../templates/sisyphus-tmux-plugin");
|
|
9907
|
+
if (!existsSync23(sourceDir)) {
|
|
9538
9908
|
console.error(`[plugin-install] Source dir not found: ${sourceDir}`);
|
|
9539
9909
|
return;
|
|
9540
9910
|
}
|
|
9541
9911
|
if (!pluginNeedsUpdate(sourceDir)) return;
|
|
9542
9912
|
try {
|
|
9543
9913
|
copyDir(sourceDir, INSTALL_DIR);
|
|
9544
|
-
const hookScript =
|
|
9545
|
-
if (
|
|
9914
|
+
const hookScript = join21(INSTALL_DIR, "hooks", "tmux-state.sh");
|
|
9915
|
+
if (existsSync23(hookScript)) {
|
|
9546
9916
|
try {
|
|
9547
9917
|
chmodSync2(hookScript, 493);
|
|
9548
9918
|
} catch {
|
|
@@ -9566,12 +9936,12 @@ var origError = console.error.bind(console);
|
|
|
9566
9936
|
console.log = (...args) => origLog(`[${ts()}]`, ...args);
|
|
9567
9937
|
console.error = (...args) => origError(`[${ts()}]`, ...args);
|
|
9568
9938
|
function ensureDirs() {
|
|
9569
|
-
|
|
9939
|
+
mkdirSync13(globalDir(), { recursive: true });
|
|
9570
9940
|
}
|
|
9571
9941
|
function readPid() {
|
|
9572
9942
|
const pidFile = daemonPidPath();
|
|
9573
9943
|
try {
|
|
9574
|
-
const pid = parseInt(
|
|
9944
|
+
const pid = parseInt(readFileSync24(pidFile, "utf-8").trim(), 10);
|
|
9575
9945
|
return pid && isProcessAlive(pid) ? pid : null;
|
|
9576
9946
|
} catch {
|
|
9577
9947
|
return null;
|
|
@@ -9583,7 +9953,7 @@ function acquirePidLock() {
|
|
|
9583
9953
|
console.error(`[sisyphus] Daemon already running (pid ${pid}). Use 'sisyphusd restart' or 'sisyphusd stop' first.`);
|
|
9584
9954
|
process.exit(0);
|
|
9585
9955
|
}
|
|
9586
|
-
|
|
9956
|
+
writeFileSync17(daemonPidPath(), String(process.pid), "utf-8");
|
|
9587
9957
|
}
|
|
9588
9958
|
function isLaunchdManaged() {
|
|
9589
9959
|
try {
|
|
@@ -9642,11 +10012,11 @@ async function recoverSessions() {
|
|
|
9642
10012
|
let recovered = 0;
|
|
9643
10013
|
for (const [sessionId, cwd] of entries) {
|
|
9644
10014
|
const stateFile = statePath(cwd, sessionId);
|
|
9645
|
-
if (!
|
|
10015
|
+
if (!existsSync24(stateFile)) {
|
|
9646
10016
|
continue;
|
|
9647
10017
|
}
|
|
9648
10018
|
try {
|
|
9649
|
-
const session = JSON.parse(
|
|
10019
|
+
const session = JSON.parse(readFileSync24(stateFile, "utf-8"));
|
|
9650
10020
|
if (session.status === "active" || session.status === "paused") {
|
|
9651
10021
|
registerSessionCwd(sessionId, cwd);
|
|
9652
10022
|
resetAgentCounterFromState(sessionId, session.agents ?? []);
|