sisyphi 1.0.6 → 1.0.7

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 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 fmt = `#[fg=${color},bold] #{pane_title} #[fg=${color}]#{pane_current_path} #[default]`;
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 agentBlocks = lastCycle.agentsSpawned.map((id) => {
668
+ const agentLines = lastCycle.agentsSpawned.map((id) => {
660
669
  const agent = agentMap.get(id);
661
- if (!agent) return `<agent-${id} status="unknown">
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
- let reportContent = "(no reports)";
667
- if (reportToUse) {
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
- <last-cycle>
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 ["hooks.json", "require-submit.sh", "intercept-send-message.sh"]) {
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", hook: { type: "command", command: "bash hooks/intercept-send-message.sh" } }
1062
+ ],
1063
+ Stop: [
1064
+ { hook: { type: "command", command: "bash hooks/require-submit.sh" } }
1065
+ ]
1066
+ };
1067
+ const normalizedType = agentType?.replace(/^sisyphus:/, "") ?? "";
1068
+ if (normalizedType === "plan") {
1069
+ hooksConfig.UserPromptSubmit = [
1070
+ { hook: { type: "command", command: "bash 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
+ { hook: { type: "command", command: "bash 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) {
@@ -1500,6 +1517,17 @@ function pruneOldSessions(cwd) {
1500
1517
  console.error("[sisyphus] Session pruning failed:", err);
1501
1518
  }
1502
1519
  }
1520
+ async function reopenWindow(sessionId, cwd) {
1521
+ const session = getSession(cwd, sessionId);
1522
+ const tmuxName = session.tmuxSessionName ?? `sisyphus-${session.name ?? sessionId.slice(0, 8)}`;
1523
+ if (sessionExists(tmuxName) && session.tmuxWindowId) {
1524
+ return { tmuxSessionName: tmuxName, tmuxWindowId: session.tmuxWindowId };
1525
+ }
1526
+ const created = createSession2(tmuxName, "main", cwd);
1527
+ setSessionOption(tmuxName, "@sisyphus_cwd", cwd.replace(/\/+$/, ""));
1528
+ await updateSessionTmux(cwd, sessionId, tmuxName, created.windowId);
1529
+ return { tmuxSessionName: tmuxName, tmuxWindowId: created.windowId };
1530
+ }
1503
1531
  async function resumeSession(sessionId, cwd, message) {
1504
1532
  const session = getSession(cwd, sessionId);
1505
1533
  const tmuxName = session.tmuxSessionName ?? `sisyphus-${sessionId.slice(0, 8)}`;
@@ -2018,6 +2046,23 @@ async function handleRequest(req) {
2018
2046
  const result = await handleRollback(req.sessionId, tracking.cwd, req.toCycle);
2019
2047
  return { ok: true, data: result };
2020
2048
  }
2049
+ case "reopen-window": {
2050
+ let tracking = sessionTrackingMap.get(req.sessionId);
2051
+ if (!tracking) {
2052
+ const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
2053
+ if (existsSync8(stateFile)) {
2054
+ tracking = { cwd: req.cwd, messageCounter: 0 };
2055
+ sessionTrackingMap.set(req.sessionId, tracking);
2056
+ persistSessionRegistry();
2057
+ } else {
2058
+ return unknownSessionError(req.sessionId);
2059
+ }
2060
+ }
2061
+ const result = await reopenWindow(req.sessionId, tracking.cwd);
2062
+ tracking.tmuxSession = result.tmuxSessionName;
2063
+ tracking.windowId = result.tmuxWindowId;
2064
+ return { ok: true, data: result };
2065
+ }
2021
2066
  case "delete": {
2022
2067
  const activeTracking = sessionTrackingMap.get(req.sessionId);
2023
2068
  if (activeTracking) {
@@ -2138,7 +2183,7 @@ function stopServer() {
2138
2183
 
2139
2184
  // src/daemon/updater.ts
2140
2185
  import { execSync as execSync3 } from "child_process";
2141
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2 } from "fs";
2186
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2, lstatSync } from "fs";
2142
2187
  import { resolve as resolve4 } from "path";
2143
2188
  import { get } from "https";
2144
2189
  function isNewer(latest, current) {
@@ -2228,8 +2273,19 @@ function clearUpdating() {
2228
2273
  } catch {
2229
2274
  }
2230
2275
  }
2276
+ function isLinkedInstall() {
2277
+ try {
2278
+ const nodeDir = resolve4(process.execPath, "..");
2279
+ const globalPrefix = execSync3("npm prefix -g", { timeout: 5e3, encoding: "utf-8", env: { ...process.env, PATH: `${nodeDir}:${process.env.PATH ?? ""}` } }).trim();
2280
+ const globalPkgDir = resolve4(globalPrefix, "lib", "node_modules", "sisyphi");
2281
+ return lstatSync(globalPkgDir).isSymbolicLink();
2282
+ } catch {
2283
+ return false;
2284
+ }
2285
+ }
2231
2286
  async function checkAndApply() {
2232
2287
  clearUpdating();
2288
+ if (isLinkedInstall()) return;
2233
2289
  try {
2234
2290
  const update = await checkForUpdate();
2235
2291
  if (!update) return;