sisyphi 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/daemon.js +84 -25
- package/dist/daemon.js.map +1 -1
- package/dist/templates/agent-plugin/hooks/CLAUDE.md +57 -0
- package/dist/templates/agent-plugin/hooks/hooks.json +12 -8
- package/dist/templates/agent-plugin/hooks/plan-user-prompt.sh +16 -0
- package/dist/templates/agent-plugin/hooks/spec-user-prompt.sh +19 -0
- package/dist/templates/companion-plugin/hooks/hooks.json +6 -4
- package/dist/templates/orchestrator-planning.md +8 -0
- package/dist/tui.js +22 -5
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/templates/agent-plugin/hooks/CLAUDE.md +57 -0
- package/templates/agent-plugin/hooks/hooks.json +12 -8
- package/templates/agent-plugin/hooks/plan-user-prompt.sh +16 -0
- package/templates/agent-plugin/hooks/spec-user-prompt.sh +19 -0
- package/templates/companion-plugin/hooks/hooks.json +6 -4
- package/templates/orchestrator-planning.md +8 -0
package/dist/daemon.js
CHANGED
|
@@ -509,6 +509,7 @@ function createSession2(sessionName, windowName, cwd) {
|
|
|
509
509
|
exec(`tmux new-session -d -s "${sessionName}" -n "${windowName}" -c ${shellQuote(cwd)}`);
|
|
510
510
|
const windowId = exec(`tmux display-message -t "${sessionName}:${windowName}" -p "#{window_id}"`);
|
|
511
511
|
const initialPaneId = exec(`tmux display-message -t "${sessionName}:${windowName}" -p "#{pane_id}"`);
|
|
512
|
+
configureSessionDefaults(sessionName, windowId);
|
|
512
513
|
return { windowId, initialPaneId };
|
|
513
514
|
}
|
|
514
515
|
function paneExists(paneTarget) {
|
|
@@ -535,7 +536,8 @@ function setPaneTitle(paneTarget, title) {
|
|
|
535
536
|
execSafe(`tmux select-pane -t "${paneTarget}" -T ${shellQuote(title)}`);
|
|
536
537
|
}
|
|
537
538
|
function setPaneStyle(paneTarget, color) {
|
|
538
|
-
const
|
|
539
|
+
const gitBranch = `#(cd #{pane_current_path} && git branch --show-current 2>/dev/null || echo 'n/a')`;
|
|
540
|
+
const fmt = `#[fg=${color},bold] #{pane_title} #[fg=${color}]#{pane_current_path} | ${gitBranch} #[default]`;
|
|
539
541
|
execSafe(`tmux set -p -t "${paneTarget}" pane-border-format ${shellQuote(fmt)}`);
|
|
540
542
|
execSafe(`tmux set -p -t "${paneTarget}" @pane_color "${color}"`);
|
|
541
543
|
execSafe(`tmux set -w -t "${paneTarget}" pane-border-style "fg=#{?#{@pane_color},#{@pane_color},default}"`);
|
|
@@ -544,6 +546,13 @@ function setPaneStyle(paneTarget, color) {
|
|
|
544
546
|
function selectLayout(windowTarget, layout = "even-horizontal") {
|
|
545
547
|
execSafe(`tmux select-layout -t "${windowTarget}" ${layout}`);
|
|
546
548
|
}
|
|
549
|
+
function configureSessionDefaults(sessionName, windowId) {
|
|
550
|
+
execSafe(`tmux set -w -t "${windowId}" pane-border-status top`);
|
|
551
|
+
execSafe(`tmux set -w -t "${windowId}" allow-rename off`);
|
|
552
|
+
execSafe(`tmux set -w -t "${windowId}" automatic-rename off`);
|
|
553
|
+
execSafe(`tmux set-hook -t "${sessionName}" after-kill-pane "select-layout even-horizontal"`);
|
|
554
|
+
execSafe(`tmux set-hook -t "${sessionName}" pane-exited "select-layout even-horizontal"`);
|
|
555
|
+
}
|
|
547
556
|
|
|
548
557
|
// src/daemon/pane-registry.ts
|
|
549
558
|
var paneMap = /* @__PURE__ */ new Map();
|
|
@@ -656,31 +665,18 @@ ${lines.join("\n")}
|
|
|
656
665
|
const lastCycle = session.orchestratorCycles[session.orchestratorCycles.length - 1];
|
|
657
666
|
if (lastCycle && lastCycle.agentsSpawned.length > 0) {
|
|
658
667
|
const agentMap = new Map(session.agents.map((a) => [a.id, a]));
|
|
659
|
-
const
|
|
668
|
+
const agentLines = lastCycle.agentsSpawned.map((id) => {
|
|
660
669
|
const agent = agentMap.get(id);
|
|
661
|
-
if (!agent) return
|
|
662
|
-
(no agent data)
|
|
663
|
-
</agent-${id}>`;
|
|
670
|
+
if (!agent) return `- **${id}**: unknown (no agent data)`;
|
|
664
671
|
const finalReport = agent.reports.find((r) => r.type === "final");
|
|
665
672
|
const reportToUse = finalReport ?? agent.reports[agent.reports.length - 1];
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
try {
|
|
669
|
-
reportContent = readFileSync3(reportToUse.filePath, "utf-8");
|
|
670
|
-
} catch {
|
|
671
|
-
reportContent = `(could not read report: ${reportToUse.filePath})`;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
return `<agent-${id} name="${agent.name}" status="${agent.status}">
|
|
675
|
-
${reportContent}
|
|
676
|
-
</agent-${id}>`;
|
|
673
|
+
const reportRef = reportToUse ? `@${reportToUse.filePath}` : "(no reports)";
|
|
674
|
+
return `- **${id}** (${agent.name}) [${agent.status}]: ${reportRef}`;
|
|
677
675
|
}).join("\n");
|
|
678
676
|
mostRecentCycleSection = `
|
|
679
677
|
### Most Recent Cycle
|
|
680
678
|
|
|
681
|
-
|
|
682
|
-
${agentBlocks}
|
|
683
|
-
</last-cycle>
|
|
679
|
+
${agentLines}
|
|
684
680
|
`;
|
|
685
681
|
}
|
|
686
682
|
const roadmapRef = existsSync4(roadmapFile) ? `@${roadmapFile}` : "(empty)";
|
|
@@ -1057,9 +1053,30 @@ function createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig) {
|
|
|
1057
1053
|
copyFileSync2(agentConfig.filePath, `${base}/agents/${shortName}.md`);
|
|
1058
1054
|
}
|
|
1059
1055
|
const srcHooks = resolve3(import.meta.dirname, "../templates/agent-plugin/hooks");
|
|
1060
|
-
for (const f of ["
|
|
1056
|
+
for (const f of ["require-submit.sh", "intercept-send-message.sh"]) {
|
|
1061
1057
|
copyFileSync2(`${srcHooks}/${f}`, `${base}/hooks/${f}`);
|
|
1062
1058
|
}
|
|
1059
|
+
const hooksConfig = {
|
|
1060
|
+
PreToolUse: [
|
|
1061
|
+
{ matcher: "SendMessage", hooks: [{ type: "command", command: "bash ${CLAUDE_PLUGIN_ROOT}/hooks/intercept-send-message.sh" }] }
|
|
1062
|
+
],
|
|
1063
|
+
Stop: [
|
|
1064
|
+
{ hooks: [{ type: "command", command: "bash ${CLAUDE_PLUGIN_ROOT}/hooks/require-submit.sh" }] }
|
|
1065
|
+
]
|
|
1066
|
+
};
|
|
1067
|
+
const normalizedType = agentType?.replace(/^sisyphus:/, "") ?? "";
|
|
1068
|
+
if (normalizedType === "plan") {
|
|
1069
|
+
hooksConfig.UserPromptSubmit = [
|
|
1070
|
+
{ hooks: [{ type: "command", command: "bash ${CLAUDE_PLUGIN_ROOT}/hooks/plan-user-prompt.sh" }] }
|
|
1071
|
+
];
|
|
1072
|
+
copyFileSync2(`${srcHooks}/plan-user-prompt.sh`, `${base}/hooks/plan-user-prompt.sh`);
|
|
1073
|
+
} else if (normalizedType === "spec-draft") {
|
|
1074
|
+
hooksConfig.UserPromptSubmit = [
|
|
1075
|
+
{ hooks: [{ type: "command", command: "bash ${CLAUDE_PLUGIN_ROOT}/hooks/spec-user-prompt.sh" }] }
|
|
1076
|
+
];
|
|
1077
|
+
copyFileSync2(`${srcHooks}/spec-user-prompt.sh`, `${base}/hooks/spec-user-prompt.sh`);
|
|
1078
|
+
}
|
|
1079
|
+
writeFileSync4(`${base}/hooks/hooks.json`, JSON.stringify({ hooks: hooksConfig }, null, 2), "utf-8");
|
|
1063
1080
|
return base;
|
|
1064
1081
|
}
|
|
1065
1082
|
function setupAgentPane(opts) {
|
|
@@ -1303,14 +1320,17 @@ async function handleAgentSubmit(cwd, sessionId, agentId, report) {
|
|
|
1303
1320
|
});
|
|
1304
1321
|
const session = getSession(cwd, sessionId);
|
|
1305
1322
|
const agent = session.agents.find((a) => a.id === agentId);
|
|
1323
|
+
const allDone = allAgentsDone(session);
|
|
1306
1324
|
if (agent?.paneId) {
|
|
1307
1325
|
unregisterAgentPane(sessionId, agentId);
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1326
|
+
if (!allDone) {
|
|
1327
|
+
try {
|
|
1328
|
+
killPane(agent.paneId);
|
|
1329
|
+
} catch {
|
|
1330
|
+
}
|
|
1311
1331
|
}
|
|
1312
1332
|
}
|
|
1313
|
-
return
|
|
1333
|
+
return allDone;
|
|
1314
1334
|
}
|
|
1315
1335
|
async function handleAgentKilled(cwd, sessionId, agentId, reason) {
|
|
1316
1336
|
unregisterAgentPane(sessionId, agentId);
|
|
@@ -1500,6 +1520,17 @@ function pruneOldSessions(cwd) {
|
|
|
1500
1520
|
console.error("[sisyphus] Session pruning failed:", err);
|
|
1501
1521
|
}
|
|
1502
1522
|
}
|
|
1523
|
+
async function reopenWindow(sessionId, cwd) {
|
|
1524
|
+
const session = getSession(cwd, sessionId);
|
|
1525
|
+
const tmuxName = session.tmuxSessionName ?? `sisyphus-${session.name ?? sessionId.slice(0, 8)}`;
|
|
1526
|
+
if (sessionExists(tmuxName) && session.tmuxWindowId) {
|
|
1527
|
+
return { tmuxSessionName: tmuxName, tmuxWindowId: session.tmuxWindowId };
|
|
1528
|
+
}
|
|
1529
|
+
const created = createSession2(tmuxName, "main", cwd);
|
|
1530
|
+
setSessionOption(tmuxName, "@sisyphus_cwd", cwd.replace(/\/+$/, ""));
|
|
1531
|
+
await updateSessionTmux(cwd, sessionId, tmuxName, created.windowId);
|
|
1532
|
+
return { tmuxSessionName: tmuxName, tmuxWindowId: created.windowId };
|
|
1533
|
+
}
|
|
1503
1534
|
async function resumeSession(sessionId, cwd, message) {
|
|
1504
1535
|
const session = getSession(cwd, sessionId);
|
|
1505
1536
|
const tmuxName = session.tmuxSessionName ?? `sisyphus-${sessionId.slice(0, 8)}`;
|
|
@@ -2018,6 +2049,23 @@ async function handleRequest(req) {
|
|
|
2018
2049
|
const result = await handleRollback(req.sessionId, tracking.cwd, req.toCycle);
|
|
2019
2050
|
return { ok: true, data: result };
|
|
2020
2051
|
}
|
|
2052
|
+
case "reopen-window": {
|
|
2053
|
+
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
2054
|
+
if (!tracking) {
|
|
2055
|
+
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
2056
|
+
if (existsSync8(stateFile)) {
|
|
2057
|
+
tracking = { cwd: req.cwd, messageCounter: 0 };
|
|
2058
|
+
sessionTrackingMap.set(req.sessionId, tracking);
|
|
2059
|
+
persistSessionRegistry();
|
|
2060
|
+
} else {
|
|
2061
|
+
return unknownSessionError(req.sessionId);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
const result = await reopenWindow(req.sessionId, tracking.cwd);
|
|
2065
|
+
tracking.tmuxSession = result.tmuxSessionName;
|
|
2066
|
+
tracking.windowId = result.tmuxWindowId;
|
|
2067
|
+
return { ok: true, data: result };
|
|
2068
|
+
}
|
|
2021
2069
|
case "delete": {
|
|
2022
2070
|
const activeTracking = sessionTrackingMap.get(req.sessionId);
|
|
2023
2071
|
if (activeTracking) {
|
|
@@ -2138,7 +2186,7 @@ function stopServer() {
|
|
|
2138
2186
|
|
|
2139
2187
|
// src/daemon/updater.ts
|
|
2140
2188
|
import { execSync as execSync3 } from "child_process";
|
|
2141
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2 } from "fs";
|
|
2189
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2, lstatSync } from "fs";
|
|
2142
2190
|
import { resolve as resolve4 } from "path";
|
|
2143
2191
|
import { get } from "https";
|
|
2144
2192
|
function isNewer(latest, current) {
|
|
@@ -2228,8 +2276,19 @@ function clearUpdating() {
|
|
|
2228
2276
|
} catch {
|
|
2229
2277
|
}
|
|
2230
2278
|
}
|
|
2279
|
+
function isLinkedInstall() {
|
|
2280
|
+
try {
|
|
2281
|
+
const nodeDir = resolve4(process.execPath, "..");
|
|
2282
|
+
const globalPrefix = execSync3("npm prefix -g", { timeout: 5e3, encoding: "utf-8", env: { ...process.env, PATH: `${nodeDir}:${process.env.PATH ?? ""}` } }).trim();
|
|
2283
|
+
const globalPkgDir = resolve4(globalPrefix, "lib", "node_modules", "sisyphi");
|
|
2284
|
+
return lstatSync(globalPkgDir).isSymbolicLink();
|
|
2285
|
+
} catch {
|
|
2286
|
+
return false;
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2231
2289
|
async function checkAndApply() {
|
|
2232
2290
|
clearUpdating();
|
|
2291
|
+
if (isLinkedInstall()) return;
|
|
2233
2292
|
try {
|
|
2234
2293
|
const update = await checkForUpdate();
|
|
2235
2294
|
if (!update) return;
|