sisyphi 1.1.38 → 1.1.40

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
@@ -336,7 +336,7 @@ var init_config = __esm({
336
336
  },
337
337
  companionPopup: true,
338
338
  requiredPlugins: [
339
- { name: "devcore", marketplace: "crouton-kit" }
339
+ { name: "devcore", marketplace: "crouton-kit", owner: "crouton-labs" }
340
340
  ]
341
341
  };
342
342
  }
@@ -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 {
@@ -7406,14 +7470,28 @@ function formatDuration2(startOrMs, endIso) {
7406
7470
  if (minutes > 0) return `${minutes}m${seconds}s`;
7407
7471
  return `${seconds}s`;
7408
7472
  }
7473
+ function wrap(open, close = "\x1B[0m") {
7474
+ return (s) => COLOR_ENABLED ? `${open}${s}${close}` : s;
7475
+ }
7476
+ var COLOR_ENABLED, bold, dim, red, green, yellow, cyan, gray, magenta, white;
7409
7477
  var init_format = __esm({
7410
7478
  "src/shared/format.ts"() {
7411
7479
  "use strict";
7480
+ COLOR_ENABLED = process.env["FORCE_COLOR"] === "1" || process.stdout.isTTY === true && process.env["NO_COLOR"] === void 0 && process.env["TERM"] !== "dumb";
7481
+ bold = wrap("\x1B[1m");
7482
+ dim = wrap("\x1B[2m");
7483
+ red = wrap("\x1B[31m");
7484
+ green = wrap("\x1B[32m");
7485
+ yellow = wrap("\x1B[33m");
7486
+ cyan = wrap("\x1B[36m");
7487
+ gray = wrap("\x1B[90m");
7488
+ magenta = wrap("\x1B[35m");
7489
+ white = wrap("\x1B[37m");
7412
7490
  }
7413
7491
  });
7414
7492
 
7415
7493
  // src/cli/deploy/creds.ts
7416
- import { chmodSync, existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync19 } from "fs";
7494
+ import { chmodSync, existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync20 } from "fs";
7417
7495
  import { createInterface } from "readline";
7418
7496
  function isValidProvider(value) {
7419
7497
  return PROVIDERS.includes(value);
@@ -7454,12 +7532,12 @@ var init_pricing = __esm({
7454
7532
  });
7455
7533
 
7456
7534
  // src/cli/deploy/runtime.ts
7457
- import { existsSync as existsSync19, readFileSync as readFileSync20, unlinkSync as unlinkSync3 } from "fs";
7535
+ import { existsSync as existsSync20, readFileSync as readFileSync21, unlinkSync as unlinkSync3 } from "fs";
7458
7536
  function readRuntimeState(provider) {
7459
7537
  const path = deployRuntimePath(provider);
7460
- if (!existsSync19(path)) return null;
7538
+ if (!existsSync20(path)) return null;
7461
7539
  try {
7462
- return JSON.parse(readFileSync20(path, "utf-8"));
7540
+ return JSON.parse(readFileSync21(path, "utf-8"));
7463
7541
  } catch {
7464
7542
  return null;
7465
7543
  }
@@ -7481,15 +7559,15 @@ var init_tailnet = __esm({
7481
7559
  });
7482
7560
 
7483
7561
  // src/cli/deploy/templates.ts
7484
- import { existsSync as existsSync20 } from "fs";
7562
+ import { existsSync as existsSync21 } from "fs";
7485
7563
  import { dirname as dirname6, resolve as resolve9 } from "path";
7486
7564
  import { fileURLToPath } from "url";
7487
7565
  function deployRoot() {
7488
7566
  const here = dirname6(fileURLToPath(import.meta.url));
7489
7567
  const bundled = resolve9(here, "..", "deploy");
7490
- if (existsSync20(bundled)) return bundled;
7568
+ if (existsSync21(bundled)) return bundled;
7491
7569
  const sourceRoot = resolve9(here, "..", "..", "..", "deploy");
7492
- if (existsSync20(sourceRoot)) return sourceRoot;
7570
+ if (existsSync21(sourceRoot)) return sourceRoot;
7493
7571
  throw new Error(
7494
7572
  `Could not locate deploy/ templates. Looked at:
7495
7573
  ${bundled}
@@ -7518,7 +7596,7 @@ var init_tailscale = __esm({
7518
7596
 
7519
7597
  // src/cli/deploy/runner.ts
7520
7598
  import { spawn as spawn2, spawnSync } from "child_process";
7521
- import { copyFileSync as copyFileSync5, existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync21 } from "fs";
7599
+ import { copyFileSync as copyFileSync5, existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync22 } from "fs";
7522
7600
  function readOutputs(provider) {
7523
7601
  const result = spawnSync("terraform", ["output", "-json", `-state=${deployStatePath(provider)}`], {
7524
7602
  cwd: providerModuleDir(provider),
@@ -7544,7 +7622,7 @@ function readOutputs(provider) {
7544
7622
  }
7545
7623
  }
7546
7624
  function isProvisioned(provider) {
7547
- if (!existsSync21(deployStatePath(provider))) return false;
7625
+ if (!existsSync22(deployStatePath(provider))) return false;
7548
7626
  return readOutputs(provider) !== null;
7549
7627
  }
7550
7628
  function effectiveSshTarget(provider) {
@@ -7663,7 +7741,7 @@ var init_grove = __esm({
7663
7741
 
7664
7742
  // src/cli/cloud/repo.ts
7665
7743
  import { spawnSync as spawnSync3 } from "child_process";
7666
- import { existsSync as existsSync22 } from "fs";
7744
+ import { existsSync as existsSync23 } from "fs";
7667
7745
  import { basename as basename5, join as join18 } from "path";
7668
7746
  function captureGit(args, cwd) {
7669
7747
  const result = spawnSync3("git", args, {
@@ -7704,10 +7782,10 @@ function buildRsyncArgs(localDir, remoteTarget) {
7704
7782
  ];
7705
7783
  }
7706
7784
  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";
7785
+ if (existsSync23(join18(toplevel, "pnpm-lock.yaml"))) return "pnpm";
7786
+ if (existsSync23(join18(toplevel, "bun.lockb"))) return "bun";
7787
+ if (existsSync23(join18(toplevel, "yarn.lock"))) return "yarn";
7788
+ if (existsSync23(join18(toplevel, "package-lock.json"))) return "npm";
7711
7789
  return null;
7712
7790
  }
7713
7791
  function packageManagerInstallCmd(pm) {
@@ -7908,7 +7986,7 @@ __export(cloud_handoff_exports, {
7908
7986
  triggerForceHandoff: () => triggerForceHandoff
7909
7987
  });
7910
7988
  import { spawn as spawn5 } from "child_process";
7911
- import { existsSync as existsSync23 } from "fs";
7989
+ import { existsSync as existsSync24 } from "fs";
7912
7990
  import { join as join19 } from "path";
7913
7991
  function runRsync2(args) {
7914
7992
  return new Promise((resolve13) => {
@@ -7948,7 +8026,7 @@ async function syncSessionState(cwd, sessionId, repo, target, provider) {
7948
8026
  const candidates = ["config.json", "orchestrator.md", "orchestrator-settings.json"];
7949
8027
  for (const name of candidates) {
7950
8028
  const localPath = join19(localProject, name);
7951
- if (!existsSync23(localPath)) continue;
8029
+ if (!existsSync24(localPath)) continue;
7952
8030
  const remotePath = `${boxRepoPath(repo)}/.sisyphus/${name}`;
7953
8031
  const args = [
7954
8032
  "-avz",
@@ -8115,14 +8193,14 @@ var init_cloud_handoff = __esm({
8115
8193
 
8116
8194
  // src/daemon/session-manager.ts
8117
8195
  import { v4 as uuidv4 } from "uuid";
8118
- import { existsSync as existsSync24, readFileSync as readFileSync22, readdirSync as readdirSync11, rmSync as rmSync6 } from "fs";
8196
+ import { existsSync as existsSync25, readFileSync as readFileSync23, readdirSync as readdirSync11, rmSync as rmSync6 } from "fs";
8119
8197
  function truncate(s, max) {
8120
8198
  return s.length <= max ? s : s.slice(0, max) + "...";
8121
8199
  }
8122
8200
  function readGoal(cwd, sessionId, fallback) {
8123
8201
  try {
8124
8202
  const p = goalPath(cwd, sessionId);
8125
- if (existsSync24(p)) return readFileSync22(p, "utf-8").trim();
8203
+ if (existsSync25(p)) return readFileSync23(p, "utf-8").trim();
8126
8204
  } catch {
8127
8205
  }
8128
8206
  return fallback;
@@ -8361,7 +8439,7 @@ It is the other session's responsibility. You do not need to monitor it.
8361
8439
  function pruneOldSessions(cwd) {
8362
8440
  try {
8363
8441
  const dir = sessionsDir(cwd);
8364
- if (!existsSync24(dir)) return;
8442
+ if (!existsSync25(dir)) return;
8365
8443
  const entries = readdirSync11(dir, { withFileTypes: true });
8366
8444
  const candidates = [];
8367
8445
  for (const entry of entries) {
@@ -8499,7 +8577,7 @@ function getSessionStatus(cwd, sessionId) {
8499
8577
  }
8500
8578
  function listSessions(cwd) {
8501
8579
  const dir = sessionsDir(cwd);
8502
- if (!existsSync24(dir)) return [];
8580
+ if (!existsSync25(dir)) return [];
8503
8581
  const entries = readdirSync11(dir, { withFileTypes: true });
8504
8582
  const sessions = [];
8505
8583
  for (const entry of entries) {
@@ -8572,8 +8650,8 @@ function onAllAgentsDone2(sessionId, cwd, windowId) {
8572
8650
  Agents: ${truncate(spawnedThisCycle, 200)}`;
8573
8651
  try {
8574
8652
  const logPath2 = cycleLogPath(cwd, sessionId, cycleNumber);
8575
- if (existsSync24(logPath2)) {
8576
- const log = readFileSync22(logPath2, "utf-8").trim();
8653
+ if (existsSync25(logPath2)) {
8654
+ const log = readFileSync23(logPath2, "utf-8").trim();
8577
8655
  if (log) cycleCtx += `
8578
8656
  Cycle log: ${truncate(log, 200)}`;
8579
8657
  }
@@ -9205,7 +9283,7 @@ import { spawn as spawn6 } from "child_process";
9205
9283
  import {
9206
9284
  closeSync as closeSync2,
9207
9285
  constants,
9208
- existsSync as existsSync25,
9286
+ existsSync as existsSync26,
9209
9287
  fstatSync as fstatSync2,
9210
9288
  lstatSync,
9211
9289
  openSync as openSync2,
@@ -9226,7 +9304,7 @@ async function generateVisualForQuestion(opts) {
9226
9304
  if (!question) return { ok: false, error: `qid ${opts.qid} not found in decisions` };
9227
9305
  const mdPath = askVisualMarkdownPath(opts.cwd, opts.sessionId, opts.askId, opts.qid);
9228
9306
  const ansiPath = askVisualAnsiPath(opts.cwd, opts.sessionId, opts.askId, opts.qid);
9229
- if (!opts.force && existsSync25(mdPath) && existsSync25(ansiPath)) {
9307
+ if (!opts.force && existsSync26(mdPath) && existsSync26(ansiPath)) {
9230
9308
  return { ok: true, markdownPath: mdPath, ansiPath, turns: 0 };
9231
9309
  }
9232
9310
  if (opts.force) {
@@ -9300,7 +9378,7 @@ function buildUserPrompt(q, askedBy, ctx) {
9300
9378
  }
9301
9379
  function readSystemPrompt() {
9302
9380
  if (cachedSystemPrompt !== void 0) return cachedSystemPrompt;
9303
- const found = SYSTEM_PROMPT_CANDIDATES.find((p) => existsSync25(p));
9381
+ const found = SYSTEM_PROMPT_CANDIDATES.find((p) => existsSync26(p));
9304
9382
  if (found === void 0) {
9305
9383
  throw new Error(
9306
9384
  `termrender-haiku-system.md not found in any candidate location: ${SYSTEM_PROMPT_CANDIDATES.join(", ")}`
@@ -9476,7 +9554,7 @@ var init_ask_visual = __esm({
9476
9554
 
9477
9555
  // src/daemon/server.ts
9478
9556
  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";
9557
+ 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
9558
  import { join as join21 } from "path";
9481
9559
  function setCompositor(c) {
9482
9560
  compositor = c;
@@ -9495,9 +9573,9 @@ function persistSessionRegistry() {
9495
9573
  }
9496
9574
  function loadSessionRegistry() {
9497
9575
  const p = registryPath();
9498
- if (!existsSync26(p)) return {};
9576
+ if (!existsSync27(p)) return {};
9499
9577
  try {
9500
- return JSON.parse(readFileSync24(p, "utf-8"));
9578
+ return JSON.parse(readFileSync25(p, "utf-8"));
9501
9579
  } catch (err) {
9502
9580
  console.warn("[sisyphus] Failed to parse session registry:", err instanceof Error ? err.message : err);
9503
9581
  return {};
@@ -9544,7 +9622,7 @@ function collectAllSessionIds() {
9544
9622
  scannedCwds.add(cwd);
9545
9623
  try {
9546
9624
  const dir = sessionsDir(cwd);
9547
- if (!existsSync26(dir)) continue;
9625
+ if (!existsSync27(dir)) continue;
9548
9626
  for (const entry of readdirSync12(dir, { withFileTypes: true })) {
9549
9627
  if (entry.isDirectory() && !idToCwd.has(entry.name)) {
9550
9628
  idToCwd.set(entry.name, cwd);
@@ -9745,7 +9823,7 @@ async function handleRequest(req) {
9745
9823
  let tracking = sessionTrackingMap.get(req.sessionId);
9746
9824
  if (!tracking) {
9747
9825
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9748
- if (existsSync26(stateFile)) {
9826
+ if (existsSync27(stateFile)) {
9749
9827
  tracking = { cwd: req.cwd, messageCounter: 0 };
9750
9828
  sessionTrackingMap.set(req.sessionId, tracking);
9751
9829
  persistSessionRegistry();
@@ -9760,7 +9838,7 @@ async function handleRequest(req) {
9760
9838
  }
9761
9839
  case "clear-orphan": {
9762
9840
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9763
- if (!existsSync26(stateFile)) {
9841
+ if (!existsSync27(stateFile)) {
9764
9842
  return { ok: false, error: `Unknown session: ${req.sessionId}. No state.json at ${stateFile}.` };
9765
9843
  }
9766
9844
  await Promise.all([
@@ -9811,7 +9889,7 @@ async function handleRequest(req) {
9811
9889
  let tracking = sessionTrackingMap.get(req.sessionId);
9812
9890
  if (!tracking) {
9813
9891
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9814
- if (existsSync26(stateFile)) {
9892
+ if (existsSync27(stateFile)) {
9815
9893
  registerSessionCwd(req.sessionId, req.cwd);
9816
9894
  tracking = sessionTrackingMap.get(req.sessionId);
9817
9895
  } else {
@@ -9825,7 +9903,7 @@ async function handleRequest(req) {
9825
9903
  let tracking = sessionTrackingMap.get(req.sessionId);
9826
9904
  if (!tracking) {
9827
9905
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9828
- if (existsSync26(stateFile)) {
9906
+ if (existsSync27(stateFile)) {
9829
9907
  registerSessionCwd(req.sessionId, req.cwd);
9830
9908
  tracking = sessionTrackingMap.get(req.sessionId);
9831
9909
  } else {
@@ -9842,7 +9920,7 @@ async function handleRequest(req) {
9842
9920
  let tracking = sessionTrackingMap.get(req.sessionId);
9843
9921
  if (!tracking) {
9844
9922
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9845
- if (existsSync26(stateFile)) {
9923
+ if (existsSync27(stateFile)) {
9846
9924
  tracking = { cwd: req.cwd, messageCounter: 0 };
9847
9925
  sessionTrackingMap.set(req.sessionId, tracking);
9848
9926
  persistSessionRegistry();
@@ -9912,7 +9990,7 @@ async function handleRequest(req) {
9912
9990
  }
9913
9991
  case "set-upload-status": {
9914
9992
  const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
9915
- if (!existsSync26(stateFile)) {
9993
+ if (!existsSync27(stateFile)) {
9916
9994
  return unknownSessionError(req.sessionId);
9917
9995
  }
9918
9996
  try {
@@ -10207,7 +10285,7 @@ async function handleRequest(req) {
10207
10285
  function startServer() {
10208
10286
  return new Promise((resolve13, reject) => {
10209
10287
  const sock = socketPath();
10210
- if (existsSync26(sock)) {
10288
+ if (existsSync27(sock)) {
10211
10289
  unlinkSync4(sock);
10212
10290
  }
10213
10291
  server = createServer((conn) => {
@@ -10262,7 +10340,7 @@ function stopServer() {
10262
10340
  }
10263
10341
  server.close(() => {
10264
10342
  const sock = socketPath();
10265
- if (existsSync26(sock)) {
10343
+ if (existsSync27(sock)) {
10266
10344
  unlinkSync4(sock);
10267
10345
  }
10268
10346
  server = null;
@@ -10300,8 +10378,8 @@ init_paths();
10300
10378
  init_config();
10301
10379
  init_server();
10302
10380
  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";
10381
+ import { mkdirSync as mkdirSync15, readFileSync as readFileSync28, writeFileSync as writeFileSync17, unlinkSync as unlinkSync6, existsSync as existsSync31 } from "fs";
10382
+ import { execSync as execSync8 } from "child_process";
10305
10383
  import { setTimeout as sleep } from "timers/promises";
10306
10384
 
10307
10385
  // src/daemon/heartbeat-asks.ts
@@ -10310,7 +10388,7 @@ init_state();
10310
10388
  init_server();
10311
10389
  init_paths();
10312
10390
  import { ulid as ulid3 } from "ulid";
10313
- import { existsSync as existsSync27 } from "fs";
10391
+ import { existsSync as existsSync28 } from "fs";
10314
10392
  var HEARTBEAT_ASKED_BY2 = "system:heartbeat";
10315
10393
  var HEARTBEAT_THRESHOLD_MS = 60 * 60 * 1e3;
10316
10394
  var HEARTBEAT_SCAN_INTERVAL_MS = 15 * 60 * 1e3;
@@ -10393,7 +10471,7 @@ async function scanSessionForStaleAsks(cwd, sessionId) {
10393
10471
  async function scanAllSessionsForStaleAsks() {
10394
10472
  const reg = loadSessionRegistry();
10395
10473
  for (const [sessionId, cwd] of Object.entries(reg)) {
10396
- if (!existsSync27(statePath(cwd, sessionId))) continue;
10474
+ if (!existsSync28(statePath(cwd, sessionId))) continue;
10397
10475
  try {
10398
10476
  await scanSessionForStaleAsks(cwd, sessionId);
10399
10477
  } catch (err) {
@@ -10458,15 +10536,15 @@ init_status_dots();
10458
10536
  init_companion();
10459
10537
  init_exec();
10460
10538
  init_shell();
10461
- import { readFileSync as readFileSync25, existsSync as existsSync28 } from "fs";
10539
+ import { readFileSync as readFileSync26, existsSync as existsSync29 } from "fs";
10462
10540
  import { homedir as homedir8 } from "os";
10463
10541
  import { join as join22 } from "path";
10464
10542
  var STATUS_BAR_BG = "#1d1e21";
10465
10543
  var SESSION_ORDER_PATH = join22(homedir8(), ".config", "tmux", "session-order");
10466
10544
  function getSessionOrder() {
10467
10545
  try {
10468
- if (!existsSync28(SESSION_ORDER_PATH)) return [];
10469
- return readFileSync25(SESSION_ORDER_PATH, "utf-8").split("\n").filter(Boolean);
10546
+ if (!existsSync29(SESSION_ORDER_PATH)) return [];
10547
+ return readFileSync26(SESSION_ORDER_PATH, "utf-8").split("\n").filter(Boolean);
10470
10548
  } catch {
10471
10549
  return [];
10472
10550
  }
@@ -11020,7 +11098,7 @@ init_state();
11020
11098
  // src/daemon/updater.ts
11021
11099
  init_paths();
11022
11100
  init_version();
11023
- import { execSync as execSync6 } from "child_process";
11101
+ import { execSync as execSync7 } from "child_process";
11024
11102
  import { writeFileSync as writeFileSync16, unlinkSync as unlinkSync5, lstatSync as lstatSync2 } from "fs";
11025
11103
  import { resolve as resolve11 } from "path";
11026
11104
  import { get } from "https";
@@ -11072,8 +11150,8 @@ function applyUpdate(expectedVersion) {
11072
11150
  try {
11073
11151
  const nodeDir = resolve11(process.execPath, "..");
11074
11152
  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", {
11153
+ execSync7("npm install -g sisyphi", { timeout: 15e3, stdio: "pipe", env });
11154
+ const result = execSync7("npm ls -g sisyphi --json --depth=0", {
11077
11155
  timeout: 5e3,
11078
11156
  encoding: "utf-8",
11079
11157
  env
@@ -11105,7 +11183,7 @@ function clearUpdating() {
11105
11183
  function isLinkedInstall() {
11106
11184
  try {
11107
11185
  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();
11186
+ const globalPrefix = execSync7("npm prefix -g", { timeout: 5e3, encoding: "utf-8", env: { ...process.env, PATH: `${nodeDir}:${process.env.PATH ?? ""}` } }).trim();
11109
11187
  const globalPkgDir = resolve11(globalPrefix, "lib", "node_modules", "sisyphi");
11110
11188
  return lstatSync2(globalPkgDir).isSymbolicLink();
11111
11189
  } catch {
@@ -11148,7 +11226,7 @@ function stopPeriodicUpdateCheck() {
11148
11226
  }
11149
11227
 
11150
11228
  // 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";
11229
+ 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
11230
  import { join as join23, resolve as resolve12 } from "path";
11153
11231
  import { homedir as homedir9 } from "os";
11154
11232
  var PLUGIN_NAME = "sisyphus-tmux";
@@ -11162,7 +11240,7 @@ function copyDir(src, dest) {
11162
11240
  copyDir(srcPath, destPath);
11163
11241
  } else {
11164
11242
  const srcMtime = statSync4(srcPath).mtimeMs;
11165
- const destMtime = existsSync29(destPath) ? statSync4(destPath).mtimeMs : 0;
11243
+ const destMtime = existsSync30(destPath) ? statSync4(destPath).mtimeMs : 0;
11166
11244
  if (srcMtime > destMtime) {
11167
11245
  copyFileSync6(srcPath, destPath);
11168
11246
  }
@@ -11172,16 +11250,16 @@ function copyDir(src, dest) {
11172
11250
  function pluginNeedsUpdate(sourceDir) {
11173
11251
  const srcHooks = join23(sourceDir, "hooks", "hooks.json");
11174
11252
  const destHooks = join23(INSTALL_DIR, "hooks", "hooks.json");
11175
- if (!existsSync29(destHooks)) return true;
11253
+ if (!existsSync30(destHooks)) return true;
11176
11254
  try {
11177
- return readFileSync26(srcHooks, "utf-8") !== readFileSync26(destHooks, "utf-8");
11255
+ return readFileSync27(srcHooks, "utf-8") !== readFileSync27(destHooks, "utf-8");
11178
11256
  } catch {
11179
11257
  return true;
11180
11258
  }
11181
11259
  }
11182
11260
  function installPlugin() {
11183
11261
  const sourceDir = resolve12(import.meta.dirname, "../templates/sisyphus-tmux-plugin");
11184
- if (!existsSync29(sourceDir)) {
11262
+ if (!existsSync30(sourceDir)) {
11185
11263
  console.error(`[plugin-install] Source dir not found: ${sourceDir}`);
11186
11264
  return;
11187
11265
  }
@@ -11189,7 +11267,7 @@ function installPlugin() {
11189
11267
  try {
11190
11268
  copyDir(sourceDir, INSTALL_DIR);
11191
11269
  const hookScript = join23(INSTALL_DIR, "hooks", "tmux-state.sh");
11192
- if (existsSync29(hookScript)) {
11270
+ if (existsSync30(hookScript)) {
11193
11271
  try {
11194
11272
  chmodSync3(hookScript, 493);
11195
11273
  } catch {
@@ -11255,7 +11333,7 @@ function ensureDirs() {
11255
11333
  function readPid() {
11256
11334
  const pidFile = daemonPidPath();
11257
11335
  try {
11258
- const pid = parseInt(readFileSync27(pidFile, "utf-8").trim(), 10);
11336
+ const pid = parseInt(readFileSync28(pidFile, "utf-8").trim(), 10);
11259
11337
  return pid && isProcessAlive(pid) ? pid : null;
11260
11338
  } catch {
11261
11339
  return null;
@@ -11271,7 +11349,7 @@ function acquirePidLock() {
11271
11349
  }
11272
11350
  function isLaunchdManaged() {
11273
11351
  try {
11274
- execSync7("launchctl list com.sisyphus.daemon", { stdio: "pipe" });
11352
+ execSync8("launchctl list com.sisyphus.daemon", { stdio: "pipe" });
11275
11353
  return true;
11276
11354
  } catch {
11277
11355
  return false;
@@ -11326,11 +11404,11 @@ async function recoverSessions() {
11326
11404
  let recovered = 0;
11327
11405
  for (const [sessionId, cwd] of entries) {
11328
11406
  const stateFile = statePath(cwd, sessionId);
11329
- if (!existsSync30(stateFile)) {
11407
+ if (!existsSync31(stateFile)) {
11330
11408
  continue;
11331
11409
  }
11332
11410
  try {
11333
- const session = JSON.parse(readFileSync27(stateFile, "utf-8"));
11411
+ const session = JSON.parse(readFileSync28(stateFile, "utf-8"));
11334
11412
  if (session.status === "active" || session.status === "paused") {
11335
11413
  registerSessionCwd(sessionId, cwd);
11336
11414
  resetAgentCounterFromState(sessionId, session.agents ?? []);