sisyphi 1.1.38 → 1.1.39

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
@@ -2254,9 +2254,56 @@ var init_history = __esm({
2254
2254
  }
2255
2255
  });
2256
2256
 
2257
+ // src/shared/platform.ts
2258
+ import { execSync as execSync3 } from "child_process";
2259
+ import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
2260
+ function detectPlatform() {
2261
+ if (cachedPlatform) return cachedPlatform;
2262
+ if (process.platform === "darwin") {
2263
+ cachedPlatform = "darwin";
2264
+ } else if (process.platform === "win32") {
2265
+ cachedPlatform = "win32";
2266
+ } else if (process.platform === "linux") {
2267
+ cachedPlatform = isWsl() ? "wsl" : "linux";
2268
+ } else {
2269
+ cachedPlatform = "unknown";
2270
+ }
2271
+ return cachedPlatform;
2272
+ }
2273
+ function isWsl() {
2274
+ if (process.env["WSL_DISTRO_NAME"] || process.env["WSL_INTEROP"]) return true;
2275
+ try {
2276
+ if (existsSync8("/proc/version")) {
2277
+ const v = readFileSync10("/proc/version", "utf-8").toLowerCase();
2278
+ if (v.includes("microsoft") || v.includes("wsl")) return true;
2279
+ }
2280
+ } catch {
2281
+ }
2282
+ return false;
2283
+ }
2284
+ function hasCommand(cmd) {
2285
+ const cached = cmdCache.get(cmd);
2286
+ if (cached !== void 0) return cached;
2287
+ try {
2288
+ execSync3(`command -v ${cmd}`, { stdio: "pipe", shell: "/bin/sh" });
2289
+ cmdCache.set(cmd, true);
2290
+ return true;
2291
+ } catch {
2292
+ cmdCache.set(cmd, false);
2293
+ return false;
2294
+ }
2295
+ }
2296
+ var cachedPlatform, cmdCache;
2297
+ var init_platform = __esm({
2298
+ "src/shared/platform.ts"() {
2299
+ "use strict";
2300
+ cmdCache = /* @__PURE__ */ new Map();
2301
+ }
2302
+ });
2303
+
2257
2304
  // src/daemon/notify.ts
2258
2305
  import { spawn, execFile } from "child_process";
2259
- import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, existsSync as existsSync8 } from "fs";
2306
+ import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, existsSync as existsSync9 } from "fs";
2260
2307
  import { join as join11 } from "path";
2261
2308
  import { homedir as homedir5 } from "os";
2262
2309
  function ensureSwitchScript() {
@@ -2276,7 +2323,7 @@ function ensureNotifyProcess() {
2276
2323
  return notifyProcess;
2277
2324
  }
2278
2325
  const binary = getNotifyBinary();
2279
- if (!existsSync8(binary)) {
2326
+ if (!existsSync9(binary)) {
2280
2327
  return null;
2281
2328
  }
2282
2329
  notifyProcess = spawn(binary, [], {
@@ -2294,6 +2341,13 @@ function ensureNotifyProcess() {
2294
2341
  notifyProcess.stderr?.unref();
2295
2342
  return notifyProcess;
2296
2343
  }
2344
+ function sendLinuxNotification(title, msg, level) {
2345
+ if (!hasCommand("notify-send")) return false;
2346
+ const urgency = level === "urgent" ? "critical" : "normal";
2347
+ execFile("notify-send", ["--app-name=Sisyphus", `--urgency=${urgency}`, title, msg], () => {
2348
+ });
2349
+ return true;
2350
+ }
2297
2351
  function sendTerminalNotification(titleOrOpts, message, tmuxSession, level) {
2298
2352
  let title;
2299
2353
  let msg;
@@ -2303,77 +2357,87 @@ function sendTerminalNotification(titleOrOpts, message, tmuxSession, level) {
2303
2357
  title = titleOrOpts.title;
2304
2358
  msg = titleOrOpts.message;
2305
2359
  tmuxSess = titleOrOpts.tmuxSession;
2306
- lvl = titleOrOpts.level ?? "urgent";
2360
+ lvl = titleOrOpts.level === void 0 ? "urgent" : titleOrOpts.level;
2307
2361
  } else {
2308
2362
  title = titleOrOpts;
2309
2363
  msg = message;
2310
2364
  tmuxSess = tmuxSession;
2311
- lvl = level ?? "urgent";
2365
+ lvl = level === void 0 ? "urgent" : level;
2312
2366
  }
2313
2367
  if (tmuxSess) ensureSwitchScript();
2314
- const proc = ensureNotifyProcess();
2315
- if (proc?.stdin?.writable) {
2316
- const payload = { title, message: msg, level: lvl };
2317
- if (tmuxSess) payload.tmuxSession = tmuxSess;
2318
- proc.stdin.write(JSON.stringify(payload) + "\n");
2368
+ const platform = detectPlatform();
2369
+ if (platform === "darwin") {
2370
+ const proc = ensureNotifyProcess();
2371
+ if (proc?.stdin?.writable) {
2372
+ const payload = { title, message: msg, level: lvl };
2373
+ if (tmuxSess) payload.tmuxSession = tmuxSess;
2374
+ proc.stdin.write(JSON.stringify(payload) + "\n");
2375
+ return;
2376
+ }
2377
+ const tnArgs = ["-title", title, "-message", msg];
2378
+ if (lvl === "urgent") tnArgs.push("-sound", "default");
2379
+ execFile("terminal-notifier", tnArgs, (err) => {
2380
+ if (err) {
2381
+ const soundClause = lvl === "urgent" ? ' sound name "default"' : "";
2382
+ execFile("osascript", [
2383
+ "-e",
2384
+ `display notification "${escapeAppleScript(msg)}" with title "${escapeAppleScript(title)}"${soundClause}`
2385
+ ], () => {
2386
+ });
2387
+ }
2388
+ });
2389
+ return;
2390
+ }
2391
+ if (platform === "linux" || platform === "wsl") {
2392
+ sendLinuxNotification(title, msg, lvl);
2319
2393
  return;
2320
2394
  }
2321
- const tnArgs = ["-title", title, "-message", msg];
2322
- if (lvl === "urgent") tnArgs.push("-sound", "default");
2323
- execFile("terminal-notifier", tnArgs, (err) => {
2324
- if (err) {
2325
- const soundClause = lvl === "urgent" ? ' sound name "default"' : "";
2326
- execFile("osascript", [
2327
- "-e",
2328
- `display notification "${escapeAppleScript(msg)}" with title "${escapeAppleScript(title)}"${soundClause}`
2329
- ], () => {
2330
- });
2331
- }
2332
- });
2333
2395
  }
2334
2396
  var TMUX_SOCKET, SWITCH_SCRIPT, notifyProcess;
2335
2397
  var init_notify = __esm({
2336
2398
  "src/daemon/notify.ts"() {
2337
2399
  "use strict";
2338
2400
  init_shell();
2401
+ init_platform();
2339
2402
  TMUX_SOCKET = `/tmp/tmux-${process.getuid?.() ?? 0}/default`;
2340
2403
  SWITCH_SCRIPT = [
2341
2404
  "#!/bin/bash",
2342
2405
  'SESSION="$1"',
2343
2406
  `TMUX_SOCKET="${TMUX_SOCKET}"`,
2344
- "TMUX=/opt/homebrew/bin/tmux",
2345
2407
  "",
2346
2408
  "# Find any attached client (user is likely on a different session)",
2347
- `CLIENT_TTY=$("$TMUX" -S "$TMUX_SOCKET" list-clients -F '#{client_tty}' 2>/dev/null | head -1)`,
2409
+ `CLIENT_TTY=$(tmux -S "$TMUX_SOCKET" list-clients -F '#{client_tty}' 2>/dev/null | head -1)`,
2348
2410
  '[ -z "$CLIENT_TTY" ] && exit 0',
2349
2411
  "",
2350
2412
  "# Switch that client to the target session",
2351
- '"$TMUX" -S "$TMUX_SOCKET" switch-client -c "$CLIENT_TTY" -t "$SESSION" 2>/dev/null',
2352
- '"$TMUX" -S "$TMUX_SOCKET" select-window -t "$SESSION" 2>/dev/null',
2413
+ 'tmux -S "$TMUX_SOCKET" switch-client -c "$CLIENT_TTY" -t "$SESSION" 2>/dev/null',
2414
+ 'tmux -S "$TMUX_SOCKET" select-window -t "$SESSION" 2>/dev/null',
2353
2415
  "",
2354
- "# Bring iTerm2 to front and select the tab with this client",
2355
- `TTY_SHORT=$(echo "$CLIENT_TTY" | sed 's|/dev/||')`,
2356
- 'osascript -e "',
2357
- ' tell application \\"iTerm2\\"',
2358
- " activate",
2359
- " repeat with w in windows",
2360
- " tell w",
2361
- " repeat with t in tabs",
2362
- " tell t",
2363
- " repeat with s in sessions",
2364
- " tell s",
2365
- ' if tty contains \\"$TTY_SHORT\\" then',
2366
- " select t",
2367
- " return",
2368
- " end if",
2369
- " end tell",
2370
- " end repeat",
2371
- " end tell",
2372
- " end repeat",
2373
- " end tell",
2374
- " end repeat",
2375
- " end tell",
2376
- `" 2>/dev/null || osascript -e 'tell application "iTerm2" to activate' 2>/dev/null`,
2416
+ "# macOS-only: bring iTerm2 to front and select the tab with this client",
2417
+ 'if [ "$(uname -s)" = "Darwin" ] && command -v osascript >/dev/null 2>&1; then',
2418
+ ` TTY_SHORT=$(echo "$CLIENT_TTY" | sed 's|/dev/||')`,
2419
+ ' osascript -e "',
2420
+ ' tell application \\"iTerm2\\"',
2421
+ " activate",
2422
+ " repeat with w in windows",
2423
+ " tell w",
2424
+ " repeat with t in tabs",
2425
+ " tell t",
2426
+ " repeat with s in sessions",
2427
+ " tell s",
2428
+ ' if tty contains \\"$TTY_SHORT\\" then',
2429
+ " select t",
2430
+ " return",
2431
+ " end if",
2432
+ " end tell",
2433
+ " end repeat",
2434
+ " end tell",
2435
+ " end repeat",
2436
+ " end tell",
2437
+ " end repeat",
2438
+ " end tell",
2439
+ ` " 2>/dev/null || osascript -e 'tell application "iTerm2" to activate' 2>/dev/null`,
2440
+ "fi",
2377
2441
  ""
2378
2442
  ].join("\n");
2379
2443
  notifyProcess = null;
@@ -2381,7 +2445,7 @@ var init_notify = __esm({
2381
2445
  });
2382
2446
 
2383
2447
  // src/daemon/ask-store.ts
2384
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync10, readdirSync as readdirSync7 } from "fs";
2448
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync11, readdirSync as readdirSync7 } from "fs";
2385
2449
  function maybeNotifyOnAskCreated(cwd, sessionId, meta) {
2386
2450
  if (process.env.NODE_ENV === "test" || process.env.SISYPHUS_DISABLE_NOTIFY === "1") return;
2387
2451
  const isActionable = meta.kind !== void 0 && ACTIONABLE_KINDS.has(meta.kind);
@@ -2432,7 +2496,7 @@ function writeDecisions(cwd, sessionId, askId, deck) {
2432
2496
  function readDecisions(cwd, sessionId, askId) {
2433
2497
  const p = askDecisionsPath(cwd, sessionId, askId);
2434
2498
  try {
2435
- return JSON.parse(readFileSync10(p, { encoding: "utf-8" }));
2499
+ return JSON.parse(readFileSync11(p, { encoding: "utf-8" }));
2436
2500
  } catch (_e) {
2437
2501
  return null;
2438
2502
  }
@@ -2445,10 +2509,10 @@ function writeOutput(cwd, sessionId, askId, responses, completedAt) {
2445
2509
  }
2446
2510
  function readMeta(cwd, sessionId, askId) {
2447
2511
  const p = askMetaPath(cwd, sessionId, askId);
2448
- if (!existsSync9(p)) {
2512
+ if (!existsSync10(p)) {
2449
2513
  return null;
2450
2514
  }
2451
- return JSON.parse(readFileSync10(p, "utf-8"));
2515
+ return JSON.parse(readFileSync11(p, "utf-8"));
2452
2516
  }
2453
2517
  async function updateMeta(cwd, sessionId, askId, patch) {
2454
2518
  return withLock(askId, () => {
@@ -2463,7 +2527,7 @@ async function updateMeta(cwd, sessionId, askId, patch) {
2463
2527
  }
2464
2528
  function listAsks(cwd, sessionId) {
2465
2529
  const dir = askDir(cwd, sessionId);
2466
- if (!existsSync9(dir)) {
2530
+ if (!existsSync10(dir)) {
2467
2531
  return [];
2468
2532
  }
2469
2533
  return readdirSync7(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
@@ -2479,7 +2543,7 @@ function buildAutoResponses(deck) {
2479
2543
  }
2480
2544
  async function autoResolveAsk(cwd, sessionId, askId, deck) {
2481
2545
  try {
2482
- if (existsSync9(askOutputPath(cwd, sessionId, askId))) return false;
2546
+ if (existsSync10(askOutputPath(cwd, sessionId, askId))) return false;
2483
2547
  const d = deck ?? readDecisions(cwd, sessionId, askId);
2484
2548
  if (!d) return false;
2485
2549
  const responses = buildAutoResponses(d);
@@ -2512,7 +2576,7 @@ function listOpenAsksFor(cwd, sessionId, askedBy) {
2512
2576
  if (meta.orphaned) continue;
2513
2577
  if (!meta.blocking) continue;
2514
2578
  if (meta.status !== "pending" && meta.status !== "in-progress") continue;
2515
- if (existsSync9(askOutputPath(cwd, sessionId, askId))) continue;
2579
+ if (existsSync10(askOutputPath(cwd, sessionId, askId))) continue;
2516
2580
  out.push({ askId, status: meta.status, ...meta.title !== void 0 ? { title: meta.title } : {} });
2517
2581
  }
2518
2582
  return out;
@@ -2781,8 +2845,8 @@ var init_process = __esm({
2781
2845
  });
2782
2846
 
2783
2847
  // src/daemon/orphan-sweep.ts
2784
- import { existsSync as existsSync10 } from "fs";
2785
- import { execSync as execSync3 } from "child_process";
2848
+ import { existsSync as existsSync11 } from "fs";
2849
+ import { execSync as execSync4 } from "child_process";
2786
2850
  function probePidLstart(pid, expectedLstart, psRunner = defaultPsRunner) {
2787
2851
  try {
2788
2852
  const lstart = psRunner(pid, execEnv());
@@ -2816,7 +2880,7 @@ async function capturePanePidLstart(paneId) {
2816
2880
  async function sweepOrphans(registry) {
2817
2881
  const reg = registry ?? loadSessionRegistry();
2818
2882
  for (const [sessionId, cwd] of Object.entries(reg)) {
2819
- if (!existsSync10(statePath(cwd, sessionId))) continue;
2883
+ if (!existsSync11(statePath(cwd, sessionId))) continue;
2820
2884
  try {
2821
2885
  await sweepSessionAgents(cwd, sessionId);
2822
2886
  await sweepSessionAsks(cwd, sessionId);
@@ -2879,13 +2943,13 @@ var init_orphan_sweep = __esm({
2879
2943
  init_paths();
2880
2944
  init_orphan_asks();
2881
2945
  init_process();
2882
- defaultPsRunner = (pid, env) => execSync3(`ps -o lstart= -p ${pid}`, { encoding: "utf-8", env, stdio: ["ignore", "pipe", "ignore"] }).trim();
2946
+ defaultPsRunner = (pid, env) => execSync4(`ps -o lstart= -p ${pid}`, { encoding: "utf-8", env, stdio: ["ignore", "pipe", "ignore"] }).trim();
2883
2947
  }
2884
2948
  });
2885
2949
 
2886
2950
  // src/daemon/agent.ts
2887
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, readdirSync as readdirSync8, existsSync as existsSync11, unlinkSync } from "fs";
2888
- import { execSync as execSync4 } from "child_process";
2951
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, readdirSync as readdirSync8, existsSync as existsSync12, unlinkSync } from "fs";
2952
+ import { execSync as execSync5 } from "child_process";
2889
2953
  import { randomUUID as randomUUID3 } from "crypto";
2890
2954
  import { resolve as resolve5, relative as relative2, dirname as dirname3, join as join12 } from "path";
2891
2955
  function resetAgentCounterFromState(sessionId, agents) {
@@ -2906,7 +2970,7 @@ function renderAgentSuffix(sessionId, instruction, contextDirRel) {
2906
2970
  const templatePath = resolve5(import.meta.dirname, "../templates/agent-suffix.md");
2907
2971
  let template;
2908
2972
  try {
2909
- template = readFileSync11(templatePath, "utf-8");
2973
+ template = readFileSync12(templatePath, "utf-8");
2910
2974
  } catch {
2911
2975
  template = `# Sisyphus Agent
2912
2976
  Session: {{SESSION_ID}}
@@ -2930,10 +2994,10 @@ function createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig) {
2930
2994
  if (agentConfig?.filePath && agentType && agentType !== "worker") {
2931
2995
  const shortName = agentType.replace(/^sisyphus:/, "");
2932
2996
  const subAgentDir = join12(dirname3(agentConfig.filePath), shortName);
2933
- if (existsSync11(subAgentDir)) {
2997
+ if (existsSync12(subAgentDir)) {
2934
2998
  for (const f of readdirSync8(subAgentDir)) {
2935
2999
  if (f.endsWith(".md") && f !== "CLAUDE.md") {
2936
- writeFileSync9(`${base}/agents/${f}`, substituteEnvVars(readFileSync11(join12(subAgentDir, f), "utf-8")), "utf-8");
3000
+ writeFileSync9(`${base}/agents/${f}`, substituteEnvVars(readFileSync12(join12(subAgentDir, f), "utf-8")), "utf-8");
2937
3001
  }
2938
3002
  }
2939
3003
  }
@@ -3030,7 +3094,7 @@ ${instruction}`);
3030
3094
  const sessionIdFlag = claudeSessionId ? ` --session-id "${claudeSessionId}"` : "";
3031
3095
  const promptFlag = agentConfig?.frontmatter.systemPrompt === "replace" ? "--system-prompt" : "--append-system-prompt";
3032
3096
  const siblingSettingsPath = agentConfig?.filePath ? agentConfig.filePath.replace(/\.md$/, ".settings.json") : null;
3033
- const settingsFlag = siblingSettingsPath && existsSync11(siblingSettingsPath) ? ` --settings "${siblingSettingsPath}"` : "";
3097
+ const settingsFlag = siblingSettingsPath && existsSync12(siblingSettingsPath) ? ` --settings "${siblingSettingsPath}"` : "";
3034
3098
  mainCmd = `claude${permFlag} --effort ${effort}${modelFlag} --plugin-dir "${pluginPath}"${sessionIdFlag}${extraPluginFlags ? ` ${extraPluginFlags}` : ""}${settingsFlag} --name ${shellQuote(agentTitle)} ${promptFlag} "$(cat '${suffixFilePath}')" ${shellQuote(instruction)}`;
3035
3099
  resumeArgs = `${permFlag.trimStart()} --effort ${effort}${modelFlag} --plugin-dir "${pluginPath}"${extraPluginFlags ? ` ${extraPluginFlags}` : ""}${settingsFlag}`;
3036
3100
  }
@@ -3055,14 +3119,14 @@ async function spawnAgent(opts) {
3055
3119
  const color = (agentConfig?.frontmatter.color ? normalizeTmuxColor(agentConfig.frontmatter.color) : null) ?? getNextColor(sessionId);
3056
3120
  let cliToCheck = provider === "openai" ? "codex" : "claude";
3057
3121
  try {
3058
- execSync4(`which ${cliToCheck}`, { stdio: "pipe", env: execEnv() });
3122
+ execSync5(`which ${cliToCheck}`, { stdio: "pipe", env: execEnv() });
3059
3123
  } catch {
3060
3124
  const fallback = agentConfig?.frontmatter.fallbackModel;
3061
3125
  if (fallback) {
3062
3126
  const fallbackProvider = detectProvider(fallback);
3063
3127
  const fallbackCli = fallbackProvider === "openai" ? "codex" : "claude";
3064
3128
  try {
3065
- execSync4(`which ${fallbackCli}`, { stdio: "pipe", env: execEnv() });
3129
+ execSync5(`which ${fallbackCli}`, { stdio: "pipe", env: execEnv() });
3066
3130
  } catch {
3067
3131
  throw new Error(`Neither ${cliToCheck} (model: ${agentConfig?.frontmatter.model}) nor ${fallbackCli} (fallback: ${fallback}) CLI found on PATH. Run \`sis admin doctor\` to diagnose.`);
3068
3132
  }
@@ -3219,9 +3283,9 @@ async function handleAgentReport(cwd, sessionId, agentId, content) {
3219
3283
  }
3220
3284
  function gcBgTasks(cwd, sessionId, agentId) {
3221
3285
  const file = `${sessionDir(cwd, sessionId)}/runtime/bg-tasks/${agentId}.txt`;
3222
- if (!existsSync11(file)) return;
3286
+ if (!existsSync12(file)) return;
3223
3287
  try {
3224
- const leftover = readFileSync11(file, "utf-8").split("\n").map((s) => s.trim()).filter(Boolean);
3288
+ const leftover = readFileSync12(file, "utf-8").split("\n").map((s) => s.trim()).filter(Boolean);
3225
3289
  if (leftover.length > 0) {
3226
3290
  console.warn(`[bg-tasks] ${agentId} exited with ${leftover.length} untracked background task(s): ${leftover.join(", ")}`);
3227
3291
  emitHistoryEvent(sessionId, "bg-tasks-leftover", { agentId, leftover });
@@ -3494,7 +3558,7 @@ var init_companion_types = __esm({
3494
3558
  });
3495
3559
 
3496
3560
  // src/daemon/companion-memory.ts
3497
- import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync12, renameSync as renameSync3, writeFileSync as writeFileSync10 } from "fs";
3561
+ import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync13, renameSync as renameSync3, writeFileSync as writeFileSync10 } from "fs";
3498
3562
  import { dirname as dirname4, join as join13 } from "path";
3499
3563
  import { randomUUID as randomUUID4 } from "crypto";
3500
3564
  import { z } from "zod";
@@ -3527,10 +3591,10 @@ function fillDefaults(state) {
3527
3591
  }
3528
3592
  function loadMemoryStrict() {
3529
3593
  const path = resolvedMemoryPath();
3530
- if (!existsSync12(path)) return defaultMemoryState();
3594
+ if (!existsSync13(path)) return defaultMemoryState();
3531
3595
  let raw;
3532
3596
  try {
3533
- raw = readFileSync12(path, "utf-8");
3597
+ raw = readFileSync13(path, "utf-8");
3534
3598
  } catch (err) {
3535
3599
  throw new MemoryStoreParseError(err);
3536
3600
  }
@@ -3971,7 +4035,7 @@ var init_companion_memory = __esm({
3971
4035
  });
3972
4036
 
3973
4037
  // src/daemon/companion.ts
3974
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync13, renameSync as renameSync4, writeFileSync as writeFileSync11 } from "fs";
4038
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync14, renameSync as renameSync4, writeFileSync as writeFileSync11 } from "fs";
3975
4039
  import { randomUUID as randomUUID5 } from "crypto";
3976
4040
  import { dirname as dirname5, join as join14 } from "path";
3977
4041
  function welfordUpdate(stats, value) {
@@ -3995,12 +4059,12 @@ function zScore(value, stats, metric) {
3995
4059
  }
3996
4060
  function loadCompanion() {
3997
4061
  const path = companionPath();
3998
- if (!existsSync13(path)) {
4062
+ if (!existsSync14(path)) {
3999
4063
  const state2 = createDefaultCompanion();
4000
4064
  saveCompanion(state2);
4001
4065
  return state2;
4002
4066
  }
4003
- const raw = readFileSync13(path, "utf-8");
4067
+ const raw = readFileSync14(path, "utf-8");
4004
4068
  const state = JSON.parse(raw);
4005
4069
  return normalizeCompanion(state);
4006
4070
  }
@@ -5739,7 +5803,7 @@ var init_companion_render = __esm({
5739
5803
  });
5740
5804
 
5741
5805
  // src/daemon/companion-popup.ts
5742
- import { writeFileSync as writeFileSync12, readFileSync as readFileSync14, unlinkSync as unlinkSync2, existsSync as existsSync14 } from "fs";
5806
+ import { writeFileSync as writeFileSync12, readFileSync as readFileSync15, unlinkSync as unlinkSync2, existsSync as existsSync15 } from "fs";
5743
5807
  import { tmpdir } from "os";
5744
5808
  import { join as join15, resolve as resolve6 } from "path";
5745
5809
  function wrapText(text, width) {
@@ -5783,7 +5847,7 @@ function showCommentaryPopupQueue(pages) {
5783
5847
  if (contentHeight > maxContentHeight) maxContentHeight = contentHeight;
5784
5848
  writeFileSync12(`${POPUP_TMP_PREFIX}-${i}.txt`, content);
5785
5849
  }
5786
- const whipAvailable = existsSync14(WHIP_ANIMATION_PATH);
5850
+ const whipAvailable = existsSync15(WHIP_ANIMATION_PATH);
5787
5851
  if (whipAvailable && maxContentHeight < WHIP_ANIMATION_ROWS + 2) {
5788
5852
  maxContentHeight = WHIP_ANIMATION_ROWS + 2;
5789
5853
  }
@@ -5857,7 +5921,7 @@ fi
5857
5921
  }
5858
5922
  let raw;
5859
5923
  try {
5860
- raw = readFileSync14(POPUP_RESULT_PREFIX, "utf8").trim();
5924
+ raw = readFileSync15(POPUP_RESULT_PREFIX, "utf8").trim();
5861
5925
  } catch {
5862
5926
  return null;
5863
5927
  } finally {
@@ -6328,7 +6392,7 @@ var init_pane_monitor = __esm({
6328
6392
  });
6329
6393
 
6330
6394
  // src/daemon/mode-notify.ts
6331
- import { existsSync as existsSync15 } from "fs";
6395
+ import { existsSync as existsSync16 } from "fs";
6332
6396
  import { ulid as ulid2 } from "ulid";
6333
6397
  function capitalize(s) {
6334
6398
  return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
@@ -6350,7 +6414,7 @@ function findOpenModeTransitionAsk(cwd, sessionId) {
6350
6414
  if (meta.modeTransition !== true) continue;
6351
6415
  if (meta.status === "answered") continue;
6352
6416
  if (meta.orphaned === true) continue;
6353
- if (existsSync15(askOutputPath(cwd, sessionId, askId))) continue;
6417
+ if (existsSync16(askOutputPath(cwd, sessionId, askId))) continue;
6354
6418
  return askId;
6355
6419
  }
6356
6420
  return null;
@@ -6464,14 +6528,14 @@ var init_mode_notify = __esm({
6464
6528
  });
6465
6529
 
6466
6530
  // src/daemon/orchestrator.ts
6467
- import { existsSync as existsSync16, readdirSync as readdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
6468
- import { execSync as execSync5 } from "child_process";
6531
+ import { existsSync as existsSync17, readdirSync as readdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "fs";
6532
+ import { execSync as execSync6 } from "child_process";
6469
6533
  import { randomUUID as randomUUID6 } from "crypto";
6470
6534
  import { resolve as resolve7, join as join16, relative as relative3 } from "path";
6471
6535
  function detectRepos(cwd) {
6472
6536
  const config = loadConfig(cwd);
6473
6537
  const repos = [];
6474
- if (existsSync16(join16(cwd, ".git"))) {
6538
+ if (existsSync17(join16(cwd, ".git"))) {
6475
6539
  try {
6476
6540
  repos.push(getRepoInfo(cwd, "."));
6477
6541
  } catch {
@@ -6483,7 +6547,7 @@ function detectRepos(cwd) {
6483
6547
  if (!entry.isDirectory()) continue;
6484
6548
  if (entry.name.startsWith(".")) continue;
6485
6549
  const childPath = join16(cwd, entry.name);
6486
- if (existsSync16(join16(childPath, ".git"))) {
6550
+ if (existsSync17(join16(childPath, ".git"))) {
6487
6551
  try {
6488
6552
  repos.push(getRepoInfo(childPath, entry.name));
6489
6553
  } catch {
@@ -6521,14 +6585,14 @@ function resolveOrchestratorSettings(cwd, sessionId) {
6521
6585
  const bundled = resolve7(import.meta.dirname, "../templates/orchestrator-settings.json");
6522
6586
  const projectSettings = projectOrchestratorSettingsPath(cwd);
6523
6587
  const userSettings = userOrchestratorSettingsPath();
6524
- const hasProject = existsSync16(projectSettings);
6525
- const hasUser = existsSync16(userSettings);
6588
+ const hasProject = existsSync17(projectSettings);
6589
+ const hasUser = existsSync17(userSettings);
6526
6590
  if (!hasProject && !hasUser) return bundled;
6527
6591
  let merged = {};
6528
6592
  for (const path of [bundled, hasUser ? userSettings : null, hasProject ? projectSettings : null]) {
6529
- if (!path || !existsSync16(path)) continue;
6593
+ if (!path || !existsSync17(path)) continue;
6530
6594
  try {
6531
- const parsed = JSON.parse(readFileSync15(path, "utf-8"));
6595
+ const parsed = JSON.parse(readFileSync16(path, "utf-8"));
6532
6596
  merged = { ...merged, ...parsed };
6533
6597
  } catch (err) {
6534
6598
  console.warn(`[sisyphus] Failed to parse settings layer ${path}: ${err instanceof Error ? err.message : err}`);
@@ -6540,21 +6604,21 @@ function resolveOrchestratorSettings(cwd, sessionId) {
6540
6604
  }
6541
6605
  function loadOrchestratorPrompt(cwd, sessionId, mode) {
6542
6606
  const projectPath = projectOrchestratorPromptPath(cwd);
6543
- if (existsSync16(projectPath)) {
6544
- return readFileSync15(projectPath, "utf-8");
6607
+ if (existsSync17(projectPath)) {
6608
+ return readFileSync16(projectPath, "utf-8");
6545
6609
  }
6546
6610
  const userPath = userOrchestratorPromptPath();
6547
- if (existsSync16(userPath)) {
6548
- return readFileSync15(userPath, "utf-8");
6611
+ if (existsSync17(userPath)) {
6612
+ return readFileSync16(userPath, "utf-8");
6549
6613
  }
6550
6614
  const basePath = resolve7(import.meta.dirname, "../templates/orchestrator-base.md");
6551
- const base = readFileSync15(basePath, "utf-8");
6615
+ const base = readFileSync16(basePath, "utf-8");
6552
6616
  const modes = discoverOrchestratorModes(cwd);
6553
6617
  const selected = modes.find((m) => m.name === mode) ?? modes.find((m) => m.name === "discovery");
6554
6618
  if (!selected) {
6555
6619
  throw new Error(`Unknown orchestrator mode '${mode}' and no fallback found. Available: ${modes.map((m) => m.name).join(", ")}`);
6556
6620
  }
6557
- const modeContent = readFileSync15(selected.filePath, "utf-8");
6621
+ const modeContent = readFileSync16(selected.filePath, "utf-8");
6558
6622
  const modeBody = extractAgentBody(modeContent);
6559
6623
  return base + "\n\n" + modeBody;
6560
6624
  }
@@ -6572,7 +6636,7 @@ function buildCompletionContent(session) {
6572
6636
  lines.push("");
6573
6637
  }
6574
6638
  const logsDirPath = logsDir(session.cwd, session.id);
6575
- if (existsSync16(logsDirPath)) {
6639
+ if (existsSync17(logsDirPath)) {
6576
6640
  const logFiles = readdirSync10(logsDirPath).filter((f) => f.startsWith("cycle-") && f.endsWith(".md")).sort();
6577
6641
  if (logFiles.length > 0) {
6578
6642
  lines.push("### Cycle Logs\n");
@@ -6597,7 +6661,7 @@ function buildCompletionContent(session) {
6597
6661
  while (j < entries.length && entries[j].idle && entries[j].mode === e.mode) j++;
6598
6662
  const runEntries = entries.slice(i, j);
6599
6663
  if (runEntries.length === 1) {
6600
- const content = readFileSync15(join16(logsDirPath, e.file), "utf-8").trim();
6664
+ const content = readFileSync16(join16(logsDirPath, e.file), "utf-8").trim();
6601
6665
  if (content) {
6602
6666
  lines.push(content);
6603
6667
  lines.push("");
@@ -6613,7 +6677,7 @@ function buildCompletionContent(session) {
6613
6677
  }
6614
6678
  i = j;
6615
6679
  } else {
6616
- const content = readFileSync15(join16(logsDirPath, e.file), "utf-8").trim();
6680
+ const content = readFileSync16(join16(logsDirPath, e.file), "utf-8").trim();
6617
6681
  if (content) {
6618
6682
  lines.push(content);
6619
6683
  lines.push("");
@@ -6624,7 +6688,7 @@ function buildCompletionContent(session) {
6624
6688
  }
6625
6689
  }
6626
6690
  const reportsDirPath = reportsDir(session.cwd, session.id);
6627
- if (existsSync16(reportsDirPath)) {
6691
+ if (existsSync17(reportsDirPath)) {
6628
6692
  const reportFiles = readdirSync10(reportsDirPath).filter((f) => f.endsWith(".md"));
6629
6693
  if (reportFiles.length > 0) {
6630
6694
  lines.push("### Detailed Reports\n");
@@ -6650,7 +6714,7 @@ ${session.context}
6650
6714
  }
6651
6715
  } else {
6652
6716
  let ctxFiles = [];
6653
- if (existsSync16(ctxDir)) {
6717
+ if (existsSync17(ctxDir)) {
6654
6718
  ctxFiles = readdirSync10(ctxDir).filter((f) => f !== "CLAUDE.md");
6655
6719
  }
6656
6720
  if (ctxFiles.length > 0) {
@@ -6692,10 +6756,10 @@ ${agentLines}
6692
6756
  }
6693
6757
  }
6694
6758
  const strategyFile = strategyPath(session.cwd, session.id);
6695
- const strategyRef = existsSync16(strategyFile) ? `@${relative3(session.cwd, strategyFile)}` : "(empty)";
6696
- const roadmapRef = existsSync16(roadmapFile) ? `@${relative3(session.cwd, roadmapFile)}` : "(empty)";
6759
+ const strategyRef = existsSync17(strategyFile) ? `@${relative3(session.cwd, strategyFile)}` : "(empty)";
6760
+ const roadmapRef = existsSync17(roadmapFile) ? `@${relative3(session.cwd, roadmapFile)}` : "(empty)";
6697
6761
  const digestFile = digestPath(session.cwd, session.id);
6698
- const digestRef = existsSync16(digestFile) ? `@${relative3(session.cwd, digestFile)}` : "(not yet created)";
6762
+ const digestRef = existsSync17(digestFile) ? `@${relative3(session.cwd, digestFile)}` : "(not yet created)";
6699
6763
  const repos = detectRepos(session.cwd);
6700
6764
  let repositoriesSection = "\n\n## Repositories\n";
6701
6765
  if (repos.length === 0) {
@@ -6722,7 +6786,7 @@ ${agentLines}
6722
6786
  }
6723
6787
  }
6724
6788
  const goalFile = goalPath(session.cwd, session.id);
6725
- const goalContent = existsSync16(goalFile) ? readFileSync15(goalFile, "utf-8").trim() : session.task;
6789
+ const goalContent = existsSync17(goalFile) ? readFileSync16(goalFile, "utf-8").trim() : session.task;
6726
6790
  const modeContent = modeContentBuilders[mode]?.(session) ?? "";
6727
6791
  return `## Goal
6728
6792
 
@@ -6747,7 +6811,7 @@ ${digestRef}
6747
6811
  }
6748
6812
  async function spawnOrchestrator(sessionId, cwd, windowId, message, forceMode) {
6749
6813
  try {
6750
- execSync5("which claude", { stdio: "pipe", env: EXEC_ENV });
6814
+ execSync6("which claude", { stdio: "pipe", env: EXEC_ENV });
6751
6815
  } catch {
6752
6816
  throw new Error("Claude CLI not found on PATH. Run `sis admin doctor` to diagnose.");
6753
6817
  }
@@ -6965,7 +7029,7 @@ var init_orchestrator = __esm({
6965
7029
  });
6966
7030
 
6967
7031
  // src/daemon/status-dots.ts
6968
- import { readFileSync as readFileSync16 } from "fs";
7032
+ import { readFileSync as readFileSync17 } from "fs";
6969
7033
  function renderDots(dots) {
6970
7034
  const sorted = [...dots].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
6971
7035
  return sorted.map((d) => {
@@ -6976,7 +7040,7 @@ function renderDots(dots) {
6976
7040
  function readClaudeState(paneId) {
6977
7041
  const numericId = paneId.replace("%", "");
6978
7042
  try {
6979
- const content = readFileSync16(`${CLAUDE_STATE_DIR}/${numericId}`, "utf-8").trim();
7043
+ const content = readFileSync17(`${CLAUDE_STATE_DIR}/${numericId}`, "utf-8").trim();
6980
7044
  if (content === "idle" || content === "processing" || content === "stopped") {
6981
7045
  return content;
6982
7046
  }
@@ -7158,7 +7222,7 @@ var init_manifest = __esm({
7158
7222
  // src/shared/session-export.ts
7159
7223
  import { execFile as execFile2 } from "child_process";
7160
7224
  import { promisify } from "util";
7161
- import { existsSync as existsSync17, readFileSync as readFileSync17, mkdirSync as mkdirSync10, symlinkSync, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "fs";
7225
+ import { existsSync as existsSync18, readFileSync as readFileSync18, mkdirSync as mkdirSync10, symlinkSync, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "fs";
7162
7226
  import { homedir as homedir6 } from "os";
7163
7227
  import { join as join17 } from "path";
7164
7228
  function sanitizeName(name) {
@@ -7170,7 +7234,7 @@ function buildOutputPath(label, dir) {
7170
7234
  const base = `sisyphus-${label}-${date}`;
7171
7235
  let candidate = join17(dir, `${base}.zip`);
7172
7236
  let counter = 1;
7173
- while (existsSync17(candidate)) {
7237
+ while (existsSync18(candidate)) {
7174
7238
  counter++;
7175
7239
  candidate = join17(dir, `${base}-${counter}.zip`);
7176
7240
  }
@@ -7233,16 +7297,16 @@ async function exportSessionToZip(sessionId, cwd, options) {
7233
7297
  const reveal = options?.reveal ?? true;
7234
7298
  const sessDir = sessionDir(cwd, sessionId);
7235
7299
  const histDir = historySessionDir(sessionId);
7236
- const sessExists = existsSync17(sessDir);
7237
- const histExists = existsSync17(histDir);
7300
+ const sessExists = existsSync18(sessDir);
7301
+ const histExists = existsSync18(histDir);
7238
7302
  if (!sessExists && !histExists) {
7239
7303
  throw new Error(`No data found for session ${sessionId}`);
7240
7304
  }
7241
7305
  let label = sessionId.slice(0, 8);
7242
7306
  const stPath = statePath(cwd, sessionId);
7243
- if (existsSync17(stPath)) {
7307
+ if (existsSync18(stPath)) {
7244
7308
  try {
7245
- const state = JSON.parse(readFileSync17(stPath, "utf-8"));
7309
+ const state = JSON.parse(readFileSync18(stPath, "utf-8"));
7246
7310
  if (state.name) {
7247
7311
  label = sanitizeName(state.name);
7248
7312
  }
@@ -7363,12 +7427,12 @@ var init_uploader = __esm({
7363
7427
  });
7364
7428
 
7365
7429
  // src/shared/version.ts
7366
- import { readFileSync as readFileSync18 } from "fs";
7430
+ import { readFileSync as readFileSync19 } from "fs";
7367
7431
  import { resolve as resolve8 } from "path";
7368
7432
  function readSisyphusVersion() {
7369
7433
  for (const rel of ["../package.json", "../../package.json"]) {
7370
7434
  try {
7371
- const raw = readFileSync18(resolve8(import.meta.dirname, rel), "utf-8");
7435
+ const raw = readFileSync19(resolve8(import.meta.dirname, rel), "utf-8");
7372
7436
  const pkg = JSON.parse(raw);
7373
7437
  if (pkg.name === "sisyphi" && pkg.version) return pkg.version;
7374
7438
  } catch {
@@ -7413,7 +7477,7 @@ var init_format = __esm({
7413
7477
  });
7414
7478
 
7415
7479
  // src/cli/deploy/creds.ts
7416
- import { chmodSync, existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync19 } from "fs";
7480
+ import { chmodSync, existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync20 } from "fs";
7417
7481
  import { createInterface } from "readline";
7418
7482
  function isValidProvider(value) {
7419
7483
  return PROVIDERS.includes(value);
@@ -7454,12 +7518,12 @@ var init_pricing = __esm({
7454
7518
  });
7455
7519
 
7456
7520
  // src/cli/deploy/runtime.ts
7457
- import { existsSync as existsSync19, readFileSync as readFileSync20, unlinkSync as unlinkSync3 } from "fs";
7521
+ import { existsSync as existsSync20, readFileSync as readFileSync21, unlinkSync as unlinkSync3 } from "fs";
7458
7522
  function readRuntimeState(provider) {
7459
7523
  const path = deployRuntimePath(provider);
7460
- if (!existsSync19(path)) return null;
7524
+ if (!existsSync20(path)) return null;
7461
7525
  try {
7462
- return JSON.parse(readFileSync20(path, "utf-8"));
7526
+ return JSON.parse(readFileSync21(path, "utf-8"));
7463
7527
  } catch {
7464
7528
  return null;
7465
7529
  }
@@ -7481,15 +7545,15 @@ var init_tailnet = __esm({
7481
7545
  });
7482
7546
 
7483
7547
  // src/cli/deploy/templates.ts
7484
- import { existsSync as existsSync20 } from "fs";
7548
+ import { existsSync as existsSync21 } from "fs";
7485
7549
  import { dirname as dirname6, resolve as resolve9 } from "path";
7486
7550
  import { fileURLToPath } from "url";
7487
7551
  function deployRoot() {
7488
7552
  const here = dirname6(fileURLToPath(import.meta.url));
7489
7553
  const bundled = resolve9(here, "..", "deploy");
7490
- if (existsSync20(bundled)) return bundled;
7554
+ if (existsSync21(bundled)) return bundled;
7491
7555
  const sourceRoot = resolve9(here, "..", "..", "..", "deploy");
7492
- if (existsSync20(sourceRoot)) return sourceRoot;
7556
+ if (existsSync21(sourceRoot)) return sourceRoot;
7493
7557
  throw new Error(
7494
7558
  `Could not locate deploy/ templates. Looked at:
7495
7559
  ${bundled}
@@ -7518,7 +7582,7 @@ var init_tailscale = __esm({
7518
7582
 
7519
7583
  // src/cli/deploy/runner.ts
7520
7584
  import { spawn as spawn2, spawnSync } from "child_process";
7521
- import { copyFileSync as copyFileSync5, existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync21 } from "fs";
7585
+ import { copyFileSync as copyFileSync5, existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync22 } from "fs";
7522
7586
  function readOutputs(provider) {
7523
7587
  const result = spawnSync("terraform", ["output", "-json", `-state=${deployStatePath(provider)}`], {
7524
7588
  cwd: providerModuleDir(provider),
@@ -7544,7 +7608,7 @@ function readOutputs(provider) {
7544
7608
  }
7545
7609
  }
7546
7610
  function isProvisioned(provider) {
7547
- if (!existsSync21(deployStatePath(provider))) return false;
7611
+ if (!existsSync22(deployStatePath(provider))) return false;
7548
7612
  return readOutputs(provider) !== null;
7549
7613
  }
7550
7614
  function effectiveSshTarget(provider) {
@@ -7663,7 +7727,7 @@ var init_grove = __esm({
7663
7727
 
7664
7728
  // src/cli/cloud/repo.ts
7665
7729
  import { spawnSync as spawnSync3 } from "child_process";
7666
- import { existsSync as existsSync22 } from "fs";
7730
+ import { existsSync as existsSync23 } from "fs";
7667
7731
  import { basename as basename5, join as join18 } from "path";
7668
7732
  function captureGit(args, cwd) {
7669
7733
  const result = spawnSync3("git", args, {
@@ -7704,10 +7768,10 @@ function buildRsyncArgs(localDir, remoteTarget) {
7704
7768
  ];
7705
7769
  }
7706
7770
  function detectPackageManager(toplevel) {
7707
- if (existsSync22(join18(toplevel, "pnpm-lock.yaml"))) return "pnpm";
7708
- if (existsSync22(join18(toplevel, "bun.lockb"))) return "bun";
7709
- if (existsSync22(join18(toplevel, "yarn.lock"))) return "yarn";
7710
- if (existsSync22(join18(toplevel, "package-lock.json"))) return "npm";
7771
+ if (existsSync23(join18(toplevel, "pnpm-lock.yaml"))) return "pnpm";
7772
+ if (existsSync23(join18(toplevel, "bun.lockb"))) return "bun";
7773
+ if (existsSync23(join18(toplevel, "yarn.lock"))) return "yarn";
7774
+ if (existsSync23(join18(toplevel, "package-lock.json"))) return "npm";
7711
7775
  return null;
7712
7776
  }
7713
7777
  function packageManagerInstallCmd(pm) {
@@ -7908,7 +7972,7 @@ __export(cloud_handoff_exports, {
7908
7972
  triggerForceHandoff: () => triggerForceHandoff
7909
7973
  });
7910
7974
  import { spawn as spawn5 } from "child_process";
7911
- import { existsSync as existsSync23 } from "fs";
7975
+ import { existsSync as existsSync24 } from "fs";
7912
7976
  import { join as join19 } from "path";
7913
7977
  function runRsync2(args) {
7914
7978
  return new Promise((resolve13) => {
@@ -7948,7 +8012,7 @@ async function syncSessionState(cwd, sessionId, repo, target, provider) {
7948
8012
  const candidates = ["config.json", "orchestrator.md", "orchestrator-settings.json"];
7949
8013
  for (const name of candidates) {
7950
8014
  const localPath = join19(localProject, name);
7951
- if (!existsSync23(localPath)) continue;
8015
+ if (!existsSync24(localPath)) continue;
7952
8016
  const remotePath = `${boxRepoPath(repo)}/.sisyphus/${name}`;
7953
8017
  const args = [
7954
8018
  "-avz",
@@ -8115,14 +8179,14 @@ var init_cloud_handoff = __esm({
8115
8179
 
8116
8180
  // src/daemon/session-manager.ts
8117
8181
  import { v4 as uuidv4 } from "uuid";
8118
- import { existsSync as existsSync24, readFileSync as readFileSync22, readdirSync as readdirSync11, rmSync as rmSync6 } from "fs";
8182
+ import { existsSync as existsSync25, readFileSync as readFileSync23, readdirSync as readdirSync11, rmSync as rmSync6 } from "fs";
8119
8183
  function truncate(s, max) {
8120
8184
  return s.length <= max ? s : s.slice(0, max) + "...";
8121
8185
  }
8122
8186
  function readGoal(cwd, sessionId, fallback) {
8123
8187
  try {
8124
8188
  const p = goalPath(cwd, sessionId);
8125
- if (existsSync24(p)) return readFileSync22(p, "utf-8").trim();
8189
+ if (existsSync25(p)) return readFileSync23(p, "utf-8").trim();
8126
8190
  } catch {
8127
8191
  }
8128
8192
  return fallback;
@@ -8361,7 +8425,7 @@ It is the other session's responsibility. You do not need to monitor it.
8361
8425
  function pruneOldSessions(cwd) {
8362
8426
  try {
8363
8427
  const dir = sessionsDir(cwd);
8364
- if (!existsSync24(dir)) return;
8428
+ if (!existsSync25(dir)) return;
8365
8429
  const entries = readdirSync11(dir, { withFileTypes: true });
8366
8430
  const candidates = [];
8367
8431
  for (const entry of entries) {
@@ -8499,7 +8563,7 @@ function getSessionStatus(cwd, sessionId) {
8499
8563
  }
8500
8564
  function listSessions(cwd) {
8501
8565
  const dir = sessionsDir(cwd);
8502
- if (!existsSync24(dir)) return [];
8566
+ if (!existsSync25(dir)) return [];
8503
8567
  const entries = readdirSync11(dir, { withFileTypes: true });
8504
8568
  const sessions = [];
8505
8569
  for (const entry of entries) {
@@ -8572,8 +8636,8 @@ function onAllAgentsDone2(sessionId, cwd, windowId) {
8572
8636
  Agents: ${truncate(spawnedThisCycle, 200)}`;
8573
8637
  try {
8574
8638
  const logPath2 = cycleLogPath(cwd, sessionId, cycleNumber);
8575
- if (existsSync24(logPath2)) {
8576
- const log = readFileSync22(logPath2, "utf-8").trim();
8639
+ if (existsSync25(logPath2)) {
8640
+ const log = readFileSync23(logPath2, "utf-8").trim();
8577
8641
  if (log) cycleCtx += `
8578
8642
  Cycle log: ${truncate(log, 200)}`;
8579
8643
  }
@@ -9205,7 +9269,7 @@ import { spawn as spawn6 } from "child_process";
9205
9269
  import {
9206
9270
  closeSync as closeSync2,
9207
9271
  constants,
9208
- existsSync as existsSync25,
9272
+ existsSync as existsSync26,
9209
9273
  fstatSync as fstatSync2,
9210
9274
  lstatSync,
9211
9275
  openSync as openSync2,
@@ -9226,7 +9290,7 @@ async function generateVisualForQuestion(opts) {
9226
9290
  if (!question) return { ok: false, error: `qid ${opts.qid} not found in decisions` };
9227
9291
  const mdPath = askVisualMarkdownPath(opts.cwd, opts.sessionId, opts.askId, opts.qid);
9228
9292
  const ansiPath = askVisualAnsiPath(opts.cwd, opts.sessionId, opts.askId, opts.qid);
9229
- if (!opts.force && existsSync25(mdPath) && existsSync25(ansiPath)) {
9293
+ if (!opts.force && existsSync26(mdPath) && existsSync26(ansiPath)) {
9230
9294
  return { ok: true, markdownPath: mdPath, ansiPath, turns: 0 };
9231
9295
  }
9232
9296
  if (opts.force) {
@@ -9300,7 +9364,7 @@ function buildUserPrompt(q, askedBy, ctx) {
9300
9364
  }
9301
9365
  function readSystemPrompt() {
9302
9366
  if (cachedSystemPrompt !== void 0) return cachedSystemPrompt;
9303
- const found = SYSTEM_PROMPT_CANDIDATES.find((p) => existsSync25(p));
9367
+ const found = SYSTEM_PROMPT_CANDIDATES.find((p) => existsSync26(p));
9304
9368
  if (found === void 0) {
9305
9369
  throw new Error(
9306
9370
  `termrender-haiku-system.md not found in any candidate location: ${SYSTEM_PROMPT_CANDIDATES.join(", ")}`
@@ -9476,7 +9540,7 @@ var init_ask_visual = __esm({
9476
9540
 
9477
9541
  // src/daemon/server.ts
9478
9542
  import { createServer } from "net";
9479
- import { unlinkSync as unlinkSync4, existsSync as existsSync26, writeFileSync as writeFileSync15, readFileSync as readFileSync24, mkdirSync as mkdirSync13, readdirSync as readdirSync12, rmSync as rmSync8, chmodSync as chmodSync2 } from "fs";
9543
+ import { unlinkSync as unlinkSync4, existsSync as existsSync27, writeFileSync as writeFileSync15, readFileSync as readFileSync25, mkdirSync as mkdirSync13, readdirSync as readdirSync12, rmSync as rmSync8, chmodSync as chmodSync2 } from "fs";
9480
9544
  import { join as join21 } from "path";
9481
9545
  function setCompositor(c) {
9482
9546
  compositor = c;
@@ -9495,9 +9559,9 @@ function persistSessionRegistry() {
9495
9559
  }
9496
9560
  function loadSessionRegistry() {
9497
9561
  const p = registryPath();
9498
- if (!existsSync26(p)) return {};
9562
+ if (!existsSync27(p)) return {};
9499
9563
  try {
9500
- return JSON.parse(readFileSync24(p, "utf-8"));
9564
+ return JSON.parse(readFileSync25(p, "utf-8"));
9501
9565
  } catch (err) {
9502
9566
  console.warn("[sisyphus] Failed to parse session registry:", err instanceof Error ? err.message : err);
9503
9567
  return {};
@@ -9544,7 +9608,7 @@ function collectAllSessionIds() {
9544
9608
  scannedCwds.add(cwd);
9545
9609
  try {
9546
9610
  const dir = sessionsDir(cwd);
9547
- if (!existsSync26(dir)) continue;
9611
+ if (!existsSync27(dir)) continue;
9548
9612
  for (const entry of readdirSync12(dir, { withFileTypes: true })) {
9549
9613
  if (entry.isDirectory() && !idToCwd.has(entry.name)) {
9550
9614
  idToCwd.set(entry.name, cwd);
@@ -9745,7 +9809,7 @@ async function handleRequest(req) {
9745
9809
  let tracking = sessionTrackingMap.get(req.sessionId);
9746
9810
  if (!tracking) {
9747
9811
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9748
- if (existsSync26(stateFile)) {
9812
+ if (existsSync27(stateFile)) {
9749
9813
  tracking = { cwd: req.cwd, messageCounter: 0 };
9750
9814
  sessionTrackingMap.set(req.sessionId, tracking);
9751
9815
  persistSessionRegistry();
@@ -9760,7 +9824,7 @@ async function handleRequest(req) {
9760
9824
  }
9761
9825
  case "clear-orphan": {
9762
9826
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9763
- if (!existsSync26(stateFile)) {
9827
+ if (!existsSync27(stateFile)) {
9764
9828
  return { ok: false, error: `Unknown session: ${req.sessionId}. No state.json at ${stateFile}.` };
9765
9829
  }
9766
9830
  await Promise.all([
@@ -9811,7 +9875,7 @@ async function handleRequest(req) {
9811
9875
  let tracking = sessionTrackingMap.get(req.sessionId);
9812
9876
  if (!tracking) {
9813
9877
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9814
- if (existsSync26(stateFile)) {
9878
+ if (existsSync27(stateFile)) {
9815
9879
  registerSessionCwd(req.sessionId, req.cwd);
9816
9880
  tracking = sessionTrackingMap.get(req.sessionId);
9817
9881
  } else {
@@ -9825,7 +9889,7 @@ async function handleRequest(req) {
9825
9889
  let tracking = sessionTrackingMap.get(req.sessionId);
9826
9890
  if (!tracking) {
9827
9891
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9828
- if (existsSync26(stateFile)) {
9892
+ if (existsSync27(stateFile)) {
9829
9893
  registerSessionCwd(req.sessionId, req.cwd);
9830
9894
  tracking = sessionTrackingMap.get(req.sessionId);
9831
9895
  } else {
@@ -9842,7 +9906,7 @@ async function handleRequest(req) {
9842
9906
  let tracking = sessionTrackingMap.get(req.sessionId);
9843
9907
  if (!tracking) {
9844
9908
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9845
- if (existsSync26(stateFile)) {
9909
+ if (existsSync27(stateFile)) {
9846
9910
  tracking = { cwd: req.cwd, messageCounter: 0 };
9847
9911
  sessionTrackingMap.set(req.sessionId, tracking);
9848
9912
  persistSessionRegistry();
@@ -9912,7 +9976,7 @@ async function handleRequest(req) {
9912
9976
  }
9913
9977
  case "set-upload-status": {
9914
9978
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9915
- if (!existsSync26(stateFile)) {
9979
+ if (!existsSync27(stateFile)) {
9916
9980
  return unknownSessionError(req.sessionId);
9917
9981
  }
9918
9982
  try {
@@ -10207,7 +10271,7 @@ async function handleRequest(req) {
10207
10271
  function startServer() {
10208
10272
  return new Promise((resolve13, reject) => {
10209
10273
  const sock = socketPath();
10210
- if (existsSync26(sock)) {
10274
+ if (existsSync27(sock)) {
10211
10275
  unlinkSync4(sock);
10212
10276
  }
10213
10277
  server = createServer((conn) => {
@@ -10262,7 +10326,7 @@ function stopServer() {
10262
10326
  }
10263
10327
  server.close(() => {
10264
10328
  const sock = socketPath();
10265
- if (existsSync26(sock)) {
10329
+ if (existsSync27(sock)) {
10266
10330
  unlinkSync4(sock);
10267
10331
  }
10268
10332
  server = null;
@@ -10300,8 +10364,8 @@ init_paths();
10300
10364
  init_config();
10301
10365
  init_server();
10302
10366
  init_orphan_sweep();
10303
- import { mkdirSync as mkdirSync15, readFileSync as readFileSync27, writeFileSync as writeFileSync17, unlinkSync as unlinkSync6, existsSync as existsSync30 } from "fs";
10304
- import { execSync as execSync7 } from "child_process";
10367
+ import { mkdirSync as mkdirSync15, readFileSync as readFileSync28, writeFileSync as writeFileSync17, unlinkSync as unlinkSync6, existsSync as existsSync31 } from "fs";
10368
+ import { execSync as execSync8 } from "child_process";
10305
10369
  import { setTimeout as sleep } from "timers/promises";
10306
10370
 
10307
10371
  // src/daemon/heartbeat-asks.ts
@@ -10310,7 +10374,7 @@ init_state();
10310
10374
  init_server();
10311
10375
  init_paths();
10312
10376
  import { ulid as ulid3 } from "ulid";
10313
- import { existsSync as existsSync27 } from "fs";
10377
+ import { existsSync as existsSync28 } from "fs";
10314
10378
  var HEARTBEAT_ASKED_BY2 = "system:heartbeat";
10315
10379
  var HEARTBEAT_THRESHOLD_MS = 60 * 60 * 1e3;
10316
10380
  var HEARTBEAT_SCAN_INTERVAL_MS = 15 * 60 * 1e3;
@@ -10393,7 +10457,7 @@ async function scanSessionForStaleAsks(cwd, sessionId) {
10393
10457
  async function scanAllSessionsForStaleAsks() {
10394
10458
  const reg = loadSessionRegistry();
10395
10459
  for (const [sessionId, cwd] of Object.entries(reg)) {
10396
- if (!existsSync27(statePath(cwd, sessionId))) continue;
10460
+ if (!existsSync28(statePath(cwd, sessionId))) continue;
10397
10461
  try {
10398
10462
  await scanSessionForStaleAsks(cwd, sessionId);
10399
10463
  } catch (err) {
@@ -10458,15 +10522,15 @@ init_status_dots();
10458
10522
  init_companion();
10459
10523
  init_exec();
10460
10524
  init_shell();
10461
- import { readFileSync as readFileSync25, existsSync as existsSync28 } from "fs";
10525
+ import { readFileSync as readFileSync26, existsSync as existsSync29 } from "fs";
10462
10526
  import { homedir as homedir8 } from "os";
10463
10527
  import { join as join22 } from "path";
10464
10528
  var STATUS_BAR_BG = "#1d1e21";
10465
10529
  var SESSION_ORDER_PATH = join22(homedir8(), ".config", "tmux", "session-order");
10466
10530
  function getSessionOrder() {
10467
10531
  try {
10468
- if (!existsSync28(SESSION_ORDER_PATH)) return [];
10469
- return readFileSync25(SESSION_ORDER_PATH, "utf-8").split("\n").filter(Boolean);
10532
+ if (!existsSync29(SESSION_ORDER_PATH)) return [];
10533
+ return readFileSync26(SESSION_ORDER_PATH, "utf-8").split("\n").filter(Boolean);
10470
10534
  } catch {
10471
10535
  return [];
10472
10536
  }
@@ -11020,7 +11084,7 @@ init_state();
11020
11084
  // src/daemon/updater.ts
11021
11085
  init_paths();
11022
11086
  init_version();
11023
- import { execSync as execSync6 } from "child_process";
11087
+ import { execSync as execSync7 } from "child_process";
11024
11088
  import { writeFileSync as writeFileSync16, unlinkSync as unlinkSync5, lstatSync as lstatSync2 } from "fs";
11025
11089
  import { resolve as resolve11 } from "path";
11026
11090
  import { get } from "https";
@@ -11072,8 +11136,8 @@ function applyUpdate(expectedVersion) {
11072
11136
  try {
11073
11137
  const nodeDir = resolve11(process.execPath, "..");
11074
11138
  const env = { ...process.env, PATH: `${nodeDir}:${process.env.PATH ?? ""}` };
11075
- execSync6("npm install -g sisyphi", { timeout: 15e3, stdio: "pipe", env });
11076
- const result = execSync6("npm ls -g sisyphi --json --depth=0", {
11139
+ execSync7("npm install -g sisyphi", { timeout: 15e3, stdio: "pipe", env });
11140
+ const result = execSync7("npm ls -g sisyphi --json --depth=0", {
11077
11141
  timeout: 5e3,
11078
11142
  encoding: "utf-8",
11079
11143
  env
@@ -11105,7 +11169,7 @@ function clearUpdating() {
11105
11169
  function isLinkedInstall() {
11106
11170
  try {
11107
11171
  const nodeDir = resolve11(process.execPath, "..");
11108
- const globalPrefix = execSync6("npm prefix -g", { timeout: 5e3, encoding: "utf-8", env: { ...process.env, PATH: `${nodeDir}:${process.env.PATH ?? ""}` } }).trim();
11172
+ const globalPrefix = execSync7("npm prefix -g", { timeout: 5e3, encoding: "utf-8", env: { ...process.env, PATH: `${nodeDir}:${process.env.PATH ?? ""}` } }).trim();
11109
11173
  const globalPkgDir = resolve11(globalPrefix, "lib", "node_modules", "sisyphi");
11110
11174
  return lstatSync2(globalPkgDir).isSymbolicLink();
11111
11175
  } catch {
@@ -11148,7 +11212,7 @@ function stopPeriodicUpdateCheck() {
11148
11212
  }
11149
11213
 
11150
11214
  // src/daemon/plugin-install.ts
11151
- import { copyFileSync as copyFileSync6, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync4, existsSync as existsSync29, readFileSync as readFileSync26, chmodSync as chmodSync3 } from "fs";
11215
+ import { copyFileSync as copyFileSync6, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync4, existsSync as existsSync30, readFileSync as readFileSync27, chmodSync as chmodSync3 } from "fs";
11152
11216
  import { join as join23, resolve as resolve12 } from "path";
11153
11217
  import { homedir as homedir9 } from "os";
11154
11218
  var PLUGIN_NAME = "sisyphus-tmux";
@@ -11162,7 +11226,7 @@ function copyDir(src, dest) {
11162
11226
  copyDir(srcPath, destPath);
11163
11227
  } else {
11164
11228
  const srcMtime = statSync4(srcPath).mtimeMs;
11165
- const destMtime = existsSync29(destPath) ? statSync4(destPath).mtimeMs : 0;
11229
+ const destMtime = existsSync30(destPath) ? statSync4(destPath).mtimeMs : 0;
11166
11230
  if (srcMtime > destMtime) {
11167
11231
  copyFileSync6(srcPath, destPath);
11168
11232
  }
@@ -11172,16 +11236,16 @@ function copyDir(src, dest) {
11172
11236
  function pluginNeedsUpdate(sourceDir) {
11173
11237
  const srcHooks = join23(sourceDir, "hooks", "hooks.json");
11174
11238
  const destHooks = join23(INSTALL_DIR, "hooks", "hooks.json");
11175
- if (!existsSync29(destHooks)) return true;
11239
+ if (!existsSync30(destHooks)) return true;
11176
11240
  try {
11177
- return readFileSync26(srcHooks, "utf-8") !== readFileSync26(destHooks, "utf-8");
11241
+ return readFileSync27(srcHooks, "utf-8") !== readFileSync27(destHooks, "utf-8");
11178
11242
  } catch {
11179
11243
  return true;
11180
11244
  }
11181
11245
  }
11182
11246
  function installPlugin() {
11183
11247
  const sourceDir = resolve12(import.meta.dirname, "../templates/sisyphus-tmux-plugin");
11184
- if (!existsSync29(sourceDir)) {
11248
+ if (!existsSync30(sourceDir)) {
11185
11249
  console.error(`[plugin-install] Source dir not found: ${sourceDir}`);
11186
11250
  return;
11187
11251
  }
@@ -11189,7 +11253,7 @@ function installPlugin() {
11189
11253
  try {
11190
11254
  copyDir(sourceDir, INSTALL_DIR);
11191
11255
  const hookScript = join23(INSTALL_DIR, "hooks", "tmux-state.sh");
11192
- if (existsSync29(hookScript)) {
11256
+ if (existsSync30(hookScript)) {
11193
11257
  try {
11194
11258
  chmodSync3(hookScript, 493);
11195
11259
  } catch {
@@ -11255,7 +11319,7 @@ function ensureDirs() {
11255
11319
  function readPid() {
11256
11320
  const pidFile = daemonPidPath();
11257
11321
  try {
11258
- const pid = parseInt(readFileSync27(pidFile, "utf-8").trim(), 10);
11322
+ const pid = parseInt(readFileSync28(pidFile, "utf-8").trim(), 10);
11259
11323
  return pid && isProcessAlive(pid) ? pid : null;
11260
11324
  } catch {
11261
11325
  return null;
@@ -11271,7 +11335,7 @@ function acquirePidLock() {
11271
11335
  }
11272
11336
  function isLaunchdManaged() {
11273
11337
  try {
11274
- execSync7("launchctl list com.sisyphus.daemon", { stdio: "pipe" });
11338
+ execSync8("launchctl list com.sisyphus.daemon", { stdio: "pipe" });
11275
11339
  return true;
11276
11340
  } catch {
11277
11341
  return false;
@@ -11326,11 +11390,11 @@ async function recoverSessions() {
11326
11390
  let recovered = 0;
11327
11391
  for (const [sessionId, cwd] of entries) {
11328
11392
  const stateFile = statePath(cwd, sessionId);
11329
- if (!existsSync30(stateFile)) {
11393
+ if (!existsSync31(stateFile)) {
11330
11394
  continue;
11331
11395
  }
11332
11396
  try {
11333
- const session = JSON.parse(readFileSync27(stateFile, "utf-8"));
11397
+ const session = JSON.parse(readFileSync28(stateFile, "utf-8"));
11334
11398
  if (session.status === "active" || session.status === "paused") {
11335
11399
  registerSessionCwd(sessionId, cwd);
11336
11400
  resetAgentCounterFromState(sessionId, session.agents ?? []);