switchroom 0.19.25 → 0.19.26

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.
Files changed (35) hide show
  1. package/dist/agent-scheduler/index.js +6 -2
  2. package/dist/auth-broker/index.js +9 -2
  3. package/dist/cli/notion-write-pretool.mjs +6 -2
  4. package/dist/cli/switchroom.js +908 -527
  5. package/dist/host-control/main.js +10 -3
  6. package/dist/vault/approvals/kernel-server.js +10 -2
  7. package/dist/vault/broker/server.js +10 -2
  8. package/package.json +1 -1
  9. package/profiles/_base/cron-session.sh.hbs +6 -0
  10. package/profiles/_base/start.sh.hbs +40 -4
  11. package/telegram-plugin/dist/gateway/gateway.js +275 -109
  12. package/telegram-plugin/gateway/gateway.ts +53 -52
  13. package/telegram-plugin/gateway/periodic-sweep-guard.ts +86 -0
  14. package/telegram-plugin/gateway/status-pin-retarget.ts +144 -0
  15. package/telegram-plugin/status-no-truncate.ts +49 -0
  16. package/telegram-plugin/status-pin-driver.ts +28 -0
  17. package/telegram-plugin/status-pin.ts +33 -4
  18. package/telegram-plugin/tests/card-type-distinguishability.test.ts +268 -0
  19. package/telegram-plugin/tests/periodic-sweep-guard.test.ts +151 -0
  20. package/telegram-plugin/tests/pinned-card-collapse.test.ts +29 -18
  21. package/telegram-plugin/tests/status-pin-retarget.test.ts +216 -0
  22. package/telegram-plugin/tests/status-pin-shutdown-wiring.test.ts +94 -0
  23. package/telegram-plugin/tests/status-pin-store.test.ts +87 -21
  24. package/telegram-plugin/tests/status-pin.test.ts +128 -2
  25. package/telegram-plugin/tests/worker-activity-feed.test.ts +10 -10
  26. package/telegram-plugin/tests/worker-feed-coalesce.test.ts +37 -21
  27. package/telegram-plugin/tests/worker-visibility-prose-silent-harness.test.ts +1 -1
  28. package/telegram-plugin/tier-downgrade.ts +3 -2
  29. package/telegram-plugin/tool-activity-summary.ts +61 -18
  30. package/telegram-plugin/uat/assertions.ts +21 -2
  31. package/telegram-plugin/uat/feed-matcher.test.ts +29 -0
  32. package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-channel.test.ts +9 -2
  33. package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-dm.test.ts +9 -2
  34. package/telegram-plugin/worker-activity-feed.ts +38 -17
  35. package/vendor/hindsight-memory/scripts/tests/test_recall_request_timeout.py +241 -0
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
2120
2120
  });
2121
2121
 
2122
2122
  // src/build-info.ts
2123
- var VERSION = "0.19.25", COMMIT_SHA = "d6ed6bdd";
2123
+ var VERSION = "0.19.26", COMMIT_SHA = "f9314b89";
2124
2124
 
2125
2125
  // src/cli/resolve-version.ts
2126
2126
  import { existsSync, readFileSync } from "node:fs";
@@ -13726,13 +13726,15 @@ var init_schema = __esm(() => {
13726
13726
  recall: exports_external.object({
13727
13727
  max_memories: exports_external.number().int().min(0).optional().describe("Cap on the number of memories injected into the prompt by " + "auto-recall, regardless of token budget. Plugin default is 12. " + "0 disables the cap (all memories Hindsight returns are injected)."),
13728
13728
  cache_ttl_secs: exports_external.number().int().min(0).optional().describe("Per-session recall cache TTL in seconds. When > 0, identical " + "(prompt, bank) within the same session reuse the cached recall " + "result instead of round-tripping to Hindsight. 0 disables. " + "Default is 600 (10 min) for switchroom-managed agents."),
13729
+ hook_timeout_seconds: exports_external.number().int().min(1).optional().describe("Ceiling (seconds) Claude Code gives the UserPromptSubmit recall " + "hook before killing it. Stamped into the installed plugin's " + "hooks/hooks.json, so it survives `switchroom apply` reinstalling " + "the plugin. Default 12. Raising it lets slow banks finish at the " + "cost of pre-turn dead air; `parallel_deadline_seconds` and " + "`request_timeout_seconds` are both kept under it. A value below " + "3s is raised to 3s and reported: the fan-out deadline must be at " + "least 1s AND still leave 2s of post-deadline headroom, so a " + "lower ceiling admits no usable envelope at all."),
13730
+ parallel_deadline_seconds: exports_external.number().int().min(1).optional().describe("Shared deadline (seconds) for the whole parallel multi-bank " + "recall fan-out. Slots unfinished when it elapses are abandoned " + "and reported as timed out. Defaults to `hook_timeout_seconds` " + "minus 2s of headroom for block formatting, cache write and " + "stdout flush, so a straggler bank can never push the hook past " + "its ceiling. Set explicitly to override that derivation; a value " + "that would leave less than 2s under the hook ceiling \u2014 including " + "one set EQUAL to it \u2014 is clamped back to `hook_timeout_seconds` " + "minus 2, and the clamp is reported. Equality is not allowed: at " + "zero headroom the hook is killed mid-write and the turn loses " + "both the memories and the recall_log row explaining why."),
13729
13731
  query_max_tokens: exports_external.number().int().min(0).optional().describe("Cap on the number of DISTINCT BM25 terms the recall hook may " + "put on the wire. `recallMaxQueryChars` bounds characters, which " + "is not the cost driver: Hindsight OR-joins every query token " + "into one tsquery and Postgres native FTS ranks the entire " + "matched set before the top-60 heapsort, so cost tracks TERMS. " + "An 800-char composed query is ~96 distinct terms and matched " + "119,510 of 135,565 rows on the `overlord` bank (14.0s for the " + "3-arm " + "BM25 UNION), past the per-bank timeout \u2014 96.8% of that agent's " + "own-bank recalls returned nothing. Plugin default is 24 " + "(measured 48,433 rows / 2.7s on the same bank). Terms are " + "chosen recency-first (the latest turn beats prior context), " + "then by selectivity. 0 disables shaping (rollback lever)."),
13730
13732
  query_stop_terms: exports_external.array(exports_external.string().min(1).regex(/^[\w./-]+$/)).optional().describe("Extra terms dropped from the BM25 recall query, on top of the " + "built-in English stopword list. For BANK-SPECIFIC " + "high-document-frequency words a generic stoplist cannot know " + "about: on `overlord`, `switchroom` matches 20% of the bank and " + "`agent` another 20%, purely because that is what the corpus is " + "about, and each such term drags tens of thousands of rows into " + "the ranking. Defaults to []."),
13731
- request_timeout_seconds: exports_external.number().int().min(1).optional().describe("Per-bank HTTP read timeout, in seconds, for one recall " + "request. Plugin default is 12, matching the UserPromptSubmit " + "hook ceiling; the shared `recallParallelDeadlineSeconds` (10) " + "is the tighter outer guard in the default configuration, so " + "this is a per-request safety net. Was a hardcoded 8 in the " + "plugin before #3757."),
13733
+ request_timeout_seconds: exports_external.number().int().min(1).optional().describe("Per-bank HTTP read timeout, in seconds, for one recall " + "request. Even parallelised, each bank carries its own deadline " + "so ONE hung bank returns empty instead of consuming the shared " + "deadline and starving its siblings. The plugin default is 12 " + "(raised from a hardcoded 8 in #3757, which fired on 96.8% of " + "one agent's own-bank recalls). Switchroom defaults it to the " + "effective `parallel_deadline_seconds` instead \u2014 10 at the " + "shipped ceiling \u2014 because the shared fan-out deadline is " + "already the tighter outer guard, so a per-bank value above it " + "can never bind. An explicitly configured value above the " + "effective deadline is clamped down to it, and the clamp is " + "reported."),
13732
13734
  own_bank_min_slots: exports_external.number().int().min(0).optional().describe("Slots inside `max_memories` reserved as a FLOOR for the agent's " + "own bank when recall fans out to more than one bank. The merged " + "set is sorted globally by relevance and head-sliced, which is " + "winner-take-all across banks: when both banks return more " + "candidates than the cap, one bank's score distribution can fill " + "every slot and the agent gets a dossier about its operator with " + "none of its own session memory. A floor, not a quota: at most " + "this many slots, only if the own bank returned that many, and " + "only up to HALF the cap shared with `additional_bank_min_slots` " + "\u2014 the rest is always won on pure relevance, so composition still " + "moves with the scores. Fixes score-based crowd-out only; a " + "timed-out bank returns no candidates and reservation is a no-op " + "there. 0 disables (default). Switchroom-managed agents use 2 " + "against the fleet-deployed cap of 6."),
13733
13735
  additional_bank_min_slots: exports_external.number().int().min(0).optional().describe("Slots inside `max_memories` reserved as a FLOOR for the " + "additional (profile / shared / sender) banks. Symmetric with " + "`own_bank_min_slots` \u2014 same floor-not-quota semantics, and the " + "two share the same half-of-cap reservation budget. When they sum " + "above that budget the own-bank floor is honoured first. 0 " + "disables (default). Switchroom-managed agents use 1 against the " + "fleet-deployed cap of 6. Observe `injected_own_bank_count` / " + "`injected_additional_bank_count` via " + "`switchroom memory recall-log`."),
13734
13736
  types: exports_external.array(exports_external.string()).optional().describe("Hindsight fact types to recall. Switchroom default is " + '["world", "experience", "observation"] \u2014 the synthesized ' + "`observation` tier is on by default. Set to " + '["world", "experience"] to opt out of observation-backed ' + "recall for this agent (or fleet-wide under defaults)."),
13735
- additional_banks: exports_external.array(exports_external.string()).optional().describe("Extra Hindsight banks to recall from on every turn, merged into " + "the agent's own bank results \u2014 e.g. a shared operator/household " + "profile bank authored via `switchroom memory profile`. Each is " + "recalled with an 8s timeout and is non-fatal on failure. Stays " + "within the single tenant: all banks are the operator's data, in " + "the operator's Hindsight instance (see the `single-tenant` " + "invariant). Defaults to [] (no extra banks)."),
13737
+ additional_banks: exports_external.array(exports_external.string()).optional().describe("Extra Hindsight banks to recall from on every turn, merged into " + "the agent's own bank results \u2014 e.g. a shared operator/household " + "profile bank authored via `switchroom memory profile`. Each is " + "recalled with the `request_timeout_seconds` per-bank timeout " + "(defaults to the effective `parallel_deadline_seconds`, 10s at " + "the shipped ceiling) and is non-fatal on failure. Stays " + "within the single tenant: all banks are the operator's data, in " + "the operator's Hindsight instance (see the `single-tenant` " + "invariant). Defaults to [] (no extra banks)."),
13736
13738
  sender_banks: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Per-speaker recall routing: a map of Telegram sender \u2192 extra " + "recall bank. When a message arrives, the agent also recalls the " + "speaker's bank (matched by Telegram username \u2014 a leading @ is " + "optional \u2014 or numeric user_id), merged " + "into its own results \u2014 so each trusted user gets their own " + "profile context. Additive recall scoping within the single " + "tenant: never an access boundary (who may drive an agent stays " + "the per-agent user assignment in `access.allowFrom`). Author the " + "banks via `switchroom memory profile`."),
13737
13739
  skip_trivial: exports_external.boolean().optional().describe("Skip recall on plausibly-stateless trivial turns (time/date/" + "greeting). Switchroom default true \u2014 saves the recall arm + " + "injected tokens on turns that never need memory, guarded so it " + "never skips a turn that references user/project/session state. " + "Set false to always run recall."),
13738
13740
  topic_filter_mode: exports_external.enum(["soft-preamble", "hard-filter"]).optional().describe("Supergroup-mode cross-topic memory behaviour. Default " + "(unset) \u2192 soft-preamble: recall returns memories from all " + "topics, and a 'Current topic: \u2026' preamble tells the model " + "to self-scope. hard-filter: drop any recalled memory whose " + "metadata.thread_id differs from the active inbound's topic. " + "Flip to hard-filter when the recall_log shows binding " + "failures (model surfacing the right memory but applying " + "it to the wrong topic).")
@@ -14091,6 +14093,8 @@ var init_schema = __esm(() => {
14091
14093
  recall: exports_external.object({
14092
14094
  max_memories: exports_external.number().int().min(0).optional(),
14093
14095
  cache_ttl_secs: exports_external.number().int().min(0).optional(),
14096
+ hook_timeout_seconds: exports_external.number().int().min(1).optional(),
14097
+ parallel_deadline_seconds: exports_external.number().int().min(1).optional(),
14094
14098
  query_max_tokens: exports_external.number().int().min(0).optional(),
14095
14099
  query_stop_terms: exports_external.array(exports_external.string().min(1).regex(/^[\w./-]+$/)).optional(),
14096
14100
  request_timeout_seconds: exports_external.number().int().min(1).optional(),
@@ -17992,6 +17996,106 @@ var init_generation_stamp = __esm(() => {
17992
17996
  STAMPED_FILES = ["start.sh", "CLAUDE.md", ".mcp.json"];
17993
17997
  });
17994
17998
 
17999
+ // src/setup/hindsight-recall-tunables.ts
18000
+ function resolveHindsightRecallTunables(recall) {
18001
+ const clamps = [];
18002
+ const usable = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? Math.floor(v) : undefined;
18003
+ let hookTimeoutSeconds = usable(recall?.hook_timeout_seconds) ?? DEFAULT_RECALL_HOOK_TIMEOUT_SECONDS;
18004
+ if (hookTimeoutSeconds < MIN_RECALL_HOOK_TIMEOUT_SECONDS) {
18005
+ clamps.push(`memory.recall.hook_timeout_seconds ${hookTimeoutSeconds}s is below the minimum ` + `${MIN_RECALL_HOOK_TIMEOUT_SECONDS}s \u2014 raised to ${MIN_RECALL_HOOK_TIMEOUT_SECONDS}s ` + `(the fan-out deadline must be at least 1s AND leave ` + `${RECALL_DEADLINE_HEADROOM_SECONDS}s of post-deadline headroom, so a lower ` + `ceiling admits no usable envelope at all)`);
18006
+ hookTimeoutSeconds = MIN_RECALL_HOOK_TIMEOUT_SECONDS;
18007
+ }
18008
+ const maxDeadline = hookTimeoutSeconds - RECALL_DEADLINE_HEADROOM_SECONDS;
18009
+ let parallelDeadlineSeconds = usable(recall?.parallel_deadline_seconds) ?? maxDeadline;
18010
+ if (parallelDeadlineSeconds > maxDeadline) {
18011
+ clamps.push(`memory.recall.parallel_deadline_seconds ${parallelDeadlineSeconds}s leaves less ` + `than ${RECALL_DEADLINE_HEADROOM_SECONDS}s under hook_timeout_seconds ` + `${hookTimeoutSeconds}s \u2014 clamped to ${maxDeadline}s (Claude Code SIGKILLs the ` + `hook at the ceiling, so a deadline at or past it loses both the memories and ` + `the recall_log row that would have explained why)`);
18012
+ parallelDeadlineSeconds = maxDeadline;
18013
+ }
18014
+ let requestTimeoutSeconds = usable(recall?.request_timeout_seconds) ?? Math.min(DEFAULT_RECALL_REQUEST_TIMEOUT_SECONDS, parallelDeadlineSeconds);
18015
+ if (requestTimeoutSeconds > parallelDeadlineSeconds) {
18016
+ clamps.push(`memory.recall.request_timeout_seconds ${requestTimeoutSeconds}s exceeds the ` + `effective parallel deadline ${parallelDeadlineSeconds}s \u2014 clamped to ` + `${parallelDeadlineSeconds}s (a per-bank timeout past the shared deadline can ` + `never fire)`);
18017
+ requestTimeoutSeconds = parallelDeadlineSeconds;
18018
+ }
18019
+ return {
18020
+ hookTimeoutSeconds,
18021
+ parallelDeadlineSeconds,
18022
+ requestTimeoutSeconds,
18023
+ clamps
18024
+ };
18025
+ }
18026
+ function renderHindsightHooksOverrides(raw, tunables) {
18027
+ let parsed;
18028
+ try {
18029
+ parsed = JSON.parse(raw);
18030
+ } catch {
18031
+ return null;
18032
+ }
18033
+ if (parsed == null || typeof parsed !== "object")
18034
+ return null;
18035
+ const root = parsed;
18036
+ const hooks = root.hooks;
18037
+ if (hooks == null || typeof hooks !== "object")
18038
+ return null;
18039
+ const matchers = hooks[MANAGED_HOOK_EVENT];
18040
+ if (!Array.isArray(matchers))
18041
+ return null;
18042
+ let stamped = false;
18043
+ for (const matcher of matchers) {
18044
+ if (matcher == null || typeof matcher !== "object")
18045
+ continue;
18046
+ const inner = matcher.hooks;
18047
+ if (!Array.isArray(inner))
18048
+ continue;
18049
+ for (const hook of inner) {
18050
+ if (hook == null || typeof hook !== "object")
18051
+ continue;
18052
+ const h = hook;
18053
+ if (typeof h.command !== "string")
18054
+ continue;
18055
+ if (!h.command.includes(RECALL_HOOK_COMMAND_MARKER))
18056
+ continue;
18057
+ h.timeout = tunables.hookTimeoutSeconds;
18058
+ stamped = true;
18059
+ }
18060
+ }
18061
+ if (!stamped)
18062
+ return null;
18063
+ return JSON.stringify(root, null, 2) + `
18064
+ `;
18065
+ }
18066
+ function readHooksRecallTimeout(raw) {
18067
+ let parsed;
18068
+ try {
18069
+ parsed = JSON.parse(raw);
18070
+ } catch {
18071
+ return null;
18072
+ }
18073
+ const hooks = parsed?.hooks;
18074
+ if (hooks == null || typeof hooks !== "object")
18075
+ return null;
18076
+ const matchers = hooks[MANAGED_HOOK_EVENT];
18077
+ if (!Array.isArray(matchers))
18078
+ return null;
18079
+ for (const matcher of matchers) {
18080
+ const inner = matcher?.hooks;
18081
+ if (!Array.isArray(inner))
18082
+ continue;
18083
+ for (const hook of inner) {
18084
+ const h = hook;
18085
+ if (h == null || typeof h.command !== "string")
18086
+ continue;
18087
+ if (!h.command.includes(RECALL_HOOK_COMMAND_MARKER))
18088
+ continue;
18089
+ return typeof h.timeout === "number" ? h.timeout : null;
18090
+ }
18091
+ }
18092
+ return null;
18093
+ }
18094
+ var DEFAULT_RECALL_HOOK_TIMEOUT_SECONDS = 12, RECALL_DEADLINE_HEADROOM_SECONDS = 2, MIN_RECALL_HOOK_TIMEOUT_SECONDS, DEFAULT_RECALL_REQUEST_TIMEOUT_SECONDS = 12, MANAGED_HOOK_EVENT = "UserPromptSubmit", RECALL_HOOK_COMMAND_MARKER = "recall.py";
18095
+ var init_hindsight_recall_tunables = __esm(() => {
18096
+ MIN_RECALL_HOOK_TIMEOUT_SECONDS = RECALL_DEADLINE_HEADROOM_SECONDS + 1;
18097
+ });
18098
+
17995
18099
  // src/scheduler/cron-routing.ts
17996
18100
  function isKnownCheapModel(model) {
17997
18101
  return model !== undefined && CHEAP_MODEL_RE.test(model);
@@ -27805,7 +27909,7 @@ function writeFileSyncIfChanged(filePath, content, mode) {
27805
27909
  writeFileSync6(filePath, content, mode !== undefined ? { encoding: "utf-8", mode } : "utf-8");
27806
27910
  return true;
27807
27911
  }
27808
- function dirContentEquals(src, dest, topLevelOverrides) {
27912
+ function dirContentEquals(src, dest, contentOverrides, relBase = "") {
27809
27913
  if (!existsSync18(src) || !existsSync18(dest))
27810
27914
  return false;
27811
27915
  let srcEntries;
@@ -27833,16 +27937,17 @@ function dirContentEquals(src, dest, topLevelOverrides) {
27833
27937
  } catch {
27834
27938
  return false;
27835
27939
  }
27940
+ const rel = relBase === "" ? entry : `${relBase}/${entry}`;
27836
27941
  if (sStat.isDirectory() !== dStat.isDirectory())
27837
27942
  return false;
27838
27943
  if (sStat.isDirectory()) {
27839
- if (!dirContentEquals(s, d))
27944
+ if (!dirContentEquals(s, d, contentOverrides, rel))
27840
27945
  return false;
27841
27946
  } else {
27842
27947
  if ((sStat.mode & 64) !== (dStat.mode & 64))
27843
27948
  return false;
27844
27949
  try {
27845
- const expected = topLevelOverrides?.[entry];
27950
+ const expected = contentOverrides?.[rel];
27846
27951
  if (expected !== undefined) {
27847
27952
  if (readFileSync15(d, "utf-8") !== expected)
27848
27953
  return false;
@@ -28010,7 +28115,7 @@ Thumbs.db
28010
28115
  function resolveHindsightVendorPath() {
28011
28116
  return resolve11(import.meta.dirname, "../../vendor/hindsight-memory");
28012
28117
  }
28013
- function installHindsightPlugin(agentName, agentDir, switchroomConfig) {
28118
+ function installHindsightPlugin(agentName, agentDir, switchroomConfig, resolvedAgentConfig) {
28014
28119
  if (!switchroomConfig)
28015
28120
  return null;
28016
28121
  const memory = switchroomConfig.memory;
@@ -28030,21 +28135,31 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig) {
28030
28135
  const destPath = join12(agentDir, ".claude", "plugins", "hindsight-memory");
28031
28136
  const additionalBanks = resolveUsers(switchroomConfig, agentName).additionalBanks;
28032
28137
  const retainConfig = resolveHindsightRetainConfig(switchroomConfig, agentName);
28033
- let expectedSettings = null;
28138
+ const recallTunables = resolveHindsightRecallTunables(resolveHindsightRecallConfig(switchroomConfig, agentName, resolvedAgentConfig));
28139
+ const contentOverrides = {};
28034
28140
  try {
28035
28141
  const vendorSettingsPath = join12(sourcePath, "settings.json");
28036
28142
  if (existsSync18(vendorSettingsPath)) {
28037
- expectedSettings = renderHindsightSettingsOverrides(readFileSync15(vendorSettingsPath, "utf-8"), additionalBanks, retainConfig);
28143
+ const expectedSettings = renderHindsightSettingsOverrides(readFileSync15(vendorSettingsPath, "utf-8"), additionalBanks, retainConfig, recallTunables);
28144
+ if (expectedSettings != null)
28145
+ contentOverrides["settings.json"] = expectedSettings;
28146
+ }
28147
+ const vendorHooksPath = join12(sourcePath, "hooks", "hooks.json");
28148
+ if (existsSync18(vendorHooksPath)) {
28149
+ const expectedHooks = renderHindsightHooksOverrides(readFileSync15(vendorHooksPath, "utf-8"), recallTunables);
28150
+ if (expectedHooks != null)
28151
+ contentOverrides["hooks/hooks.json"] = expectedHooks;
28038
28152
  }
28039
28153
  } catch {}
28040
- const upToDate = dirContentEquals(sourcePath, destPath, expectedSettings != null ? { "settings.json": expectedSettings } : undefined);
28154
+ const upToDate = dirContentEquals(sourcePath, destPath, contentOverrides);
28041
28155
  if (!upToDate) {
28042
28156
  if (existsSync18(destPath)) {
28043
28157
  rmSync4(destPath, { recursive: true, force: true });
28044
28158
  }
28045
28159
  copyDirRecursive2(sourcePath, destPath);
28046
28160
  }
28047
- applyHindsightSettingsOverrides(destPath, additionalBanks, retainConfig);
28161
+ applyHindsightSettingsOverrides(destPath, additionalBanks, retainConfig, recallTunables);
28162
+ applyHindsightHooksOverrides(destPath, recallTunables);
28048
28163
  const bankId = agentMemory?.collection ?? agentName;
28049
28164
  const mcpUrl = memory.config?.url ?? HINDSIGHT_DEFAULT_MCP_URL;
28050
28165
  const apiBaseUrl = mcpUrl.replace(/\/mcp\/?$/, "").replace(/\/$/, "");
@@ -28058,7 +28173,31 @@ function resolveHindsightRetainConfig(switchroomConfig, agentName) {
28058
28173
  overlapTurns: retain?.overlap_turns ?? HINDSIGHT_DEFAULT_RETAIN_OVERLAP_TURNS
28059
28174
  };
28060
28175
  }
28061
- function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks, retainConfig) {
28176
+ function resolveHindsightRecallConfig(switchroomConfig, agentName, resolvedAgentConfig) {
28177
+ if (resolvedAgentConfig) {
28178
+ return resolvedAgentConfig.memory?.recall;
28179
+ }
28180
+ if (!switchroomConfig)
28181
+ return;
28182
+ const resolved = resolveAgentConfig(switchroomConfig.defaults, switchroomConfig.profiles, switchroomConfig.agents[agentName] ?? {});
28183
+ return resolved?.memory?.recall;
28184
+ }
28185
+ function applyHindsightHooksOverrides(pluginDestPath, tunables) {
28186
+ const hooksPath = join12(pluginDestPath, "hooks", "hooks.json");
28187
+ if (!existsSync18(hooksPath))
28188
+ return;
28189
+ let raw;
28190
+ try {
28191
+ raw = readFileSync15(hooksPath, "utf-8");
28192
+ } catch {
28193
+ return;
28194
+ }
28195
+ const next = renderHindsightHooksOverrides(raw, tunables);
28196
+ if (next == null)
28197
+ return;
28198
+ writeFileSyncIfChanged(hooksPath, next);
28199
+ }
28200
+ function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks, retainConfig, recallTunables) {
28062
28201
  const settingsPath = join12(pluginDestPath, "settings.json");
28063
28202
  if (!existsSync18(settingsPath))
28064
28203
  return;
@@ -28068,12 +28207,12 @@ function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks, retain
28068
28207
  } catch {
28069
28208
  return;
28070
28209
  }
28071
- const next = renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig);
28210
+ const next = renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, recallTunables);
28072
28211
  if (next == null)
28073
28212
  return;
28074
28213
  writeFileSyncIfChanged(settingsPath, next);
28075
28214
  }
28076
- function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig) {
28215
+ function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig, recallTunables) {
28077
28216
  let settings;
28078
28217
  try {
28079
28218
  settings = JSON.parse(raw);
@@ -28084,6 +28223,8 @@ function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig) {
28084
28223
  settings.retainMode = "chunked";
28085
28224
  settings.retainOverlapTurns = retainConfig.overlapTurns;
28086
28225
  settings.recallMaxMemories = 8;
28226
+ settings.recallParallelDeadlineSeconds = recallTunables.parallelDeadlineSeconds;
28227
+ settings.recallRequestTimeoutSeconds = recallTunables.requestTimeoutSeconds;
28087
28228
  settings.recallTypes = ["world", "experience", "observation"];
28088
28229
  settings.recallOwnBankMinSlots = HINDSIGHT_OWN_BANK_MIN_SLOTS_DEFAULT;
28089
28230
  settings.recallAdditionalBankMinSlots = HINDSIGHT_ADDITIONAL_BANK_MIN_SLOTS_DEFAULT;
@@ -28122,6 +28263,7 @@ function buildWorkspaceContext(args) {
28122
28263
  hindsightApiBaseUrl,
28123
28264
  hindsightRecallMaxMemories,
28124
28265
  hindsightRecallCacheTtlSecs,
28266
+ hindsightRecallParallelDeadlineSeconds,
28125
28267
  hindsightRecallQueryMaxTokens,
28126
28268
  hindsightRecallQueryStopTermsJson,
28127
28269
  hindsightRecallRequestTimeoutSeconds,
@@ -28171,6 +28313,7 @@ function buildWorkspaceContext(args) {
28171
28313
  hindsightApiBaseUrlQ: shellSingleQuote(hindsightApiBaseUrl),
28172
28314
  hindsightRecallMaxMemories,
28173
28315
  hindsightRecallCacheTtlSecs,
28316
+ hindsightRecallParallelDeadlineSeconds,
28174
28317
  hindsightRecallQueryMaxTokens,
28175
28318
  hindsightRecallQueryStopTermsJson,
28176
28319
  hindsightRecallRequestTimeoutSeconds,
@@ -28429,10 +28572,12 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
28429
28572
  const hindsightApiBaseUrl = switchroomConfig?.memory?.config?.url ? switchroomConfig.memory.config.url.replace(/\/mcp\/?$/, "").replace(/\/$/, "") : HINDSIGHT_DEFAULT_API_BASE_URL;
28430
28573
  const hindsightRecallMaxMemories = agentConfig.memory?.recall?.max_memories;
28431
28574
  const hindsightRecallCacheTtlSecs = agentConfig.memory?.recall?.cache_ttl_secs;
28575
+ const hindsightRecallTunables = resolveHindsightRecallTunables(agentConfig.memory?.recall);
28576
+ const hindsightRecallParallelDeadlineSeconds = hindsightRecallTunables.parallelDeadlineSeconds;
28577
+ const hindsightRecallRequestTimeoutSeconds = hindsightRecallTunables.requestTimeoutSeconds;
28432
28578
  const hindsightRecallQueryMaxTokens = agentConfig.memory?.recall?.query_max_tokens ?? HINDSIGHT_RECALL_QUERY_MAX_TOKENS_DEFAULT;
28433
28579
  const rawRecallQueryStopTerms = agentConfig.memory?.recall?.query_stop_terms;
28434
28580
  const hindsightRecallQueryStopTermsJson = JSON.stringify(rawRecallQueryStopTerms ?? []);
28435
- const hindsightRecallRequestTimeoutSeconds = agentConfig.memory?.recall?.request_timeout_seconds ?? HINDSIGHT_RECALL_REQUEST_TIMEOUT_SECONDS_DEFAULT;
28436
28581
  const hindsightRecallOwnBankMinSlots = agentConfig.memory?.recall?.own_bank_min_slots ?? HINDSIGHT_OWN_BANK_MIN_SLOTS_DEFAULT;
28437
28582
  const hindsightRecallAdditionalBankMinSlots = agentConfig.memory?.recall?.additional_bank_min_slots ?? HINDSIGHT_ADDITIONAL_BANK_MIN_SLOTS_DEFAULT;
28438
28583
  const hindsightRecallTypes = agentConfig.memory?.recall?.types?.length ? agentConfig.memory.recall.types.join(",") : undefined;
@@ -28461,6 +28606,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
28461
28606
  hindsightApiBaseUrl,
28462
28607
  hindsightRecallMaxMemories,
28463
28608
  hindsightRecallCacheTtlSecs,
28609
+ hindsightRecallParallelDeadlineSeconds,
28464
28610
  hindsightRecallQueryMaxTokens,
28465
28611
  hindsightRecallQueryStopTermsJson,
28466
28612
  hindsightRecallRequestTimeoutSeconds,
@@ -28547,7 +28693,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
28547
28693
  }
28548
28694
  retractStaleIntegrationKeys(integration, new Set(entries.map((e) => e.key)), settings.mcpServers, site1Protected);
28549
28695
  }
28550
- installHindsightPlugin(name, agentDir, switchroomConfig);
28696
+ installHindsightPlugin(name, agentDir, switchroomConfig, agentConfig);
28551
28697
  const hindsightOn = isHindsightEnabled(switchroomConfig) && switchroomConfig.agents[name]?.memory?.auto_recall !== false;
28552
28698
  if (hindsightOn) {
28553
28699
  settings.autoMemoryEnabled = false;
@@ -29394,10 +29540,12 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
29394
29540
  const hindsightApiBaseUrl = switchroomConfig.memory?.config?.url ? switchroomConfig.memory.config.url.replace(/\/mcp\/?$/, "").replace(/\/$/, "") : HINDSIGHT_DEFAULT_API_BASE_URL;
29395
29541
  const hindsightRecallMaxMemories = agentConfig.memory?.recall?.max_memories;
29396
29542
  const hindsightRecallCacheTtlSecs = agentConfig.memory?.recall?.cache_ttl_secs;
29543
+ const hindsightRecallTunables = resolveHindsightRecallTunables(agentConfig.memory?.recall);
29544
+ const hindsightRecallParallelDeadlineSeconds = hindsightRecallTunables.parallelDeadlineSeconds;
29545
+ const hindsightRecallRequestTimeoutSeconds = hindsightRecallTunables.requestTimeoutSeconds;
29397
29546
  const hindsightRecallQueryMaxTokens = agentConfig.memory?.recall?.query_max_tokens ?? HINDSIGHT_RECALL_QUERY_MAX_TOKENS_DEFAULT;
29398
29547
  const rawRecallQueryStopTerms = agentConfig.memory?.recall?.query_stop_terms;
29399
29548
  const hindsightRecallQueryStopTermsJson = JSON.stringify(rawRecallQueryStopTerms ?? []);
29400
- const hindsightRecallRequestTimeoutSeconds = agentConfig.memory?.recall?.request_timeout_seconds ?? HINDSIGHT_RECALL_REQUEST_TIMEOUT_SECONDS_DEFAULT;
29401
29549
  const hindsightRecallOwnBankMinSlots = agentConfig.memory?.recall?.own_bank_min_slots ?? HINDSIGHT_OWN_BANK_MIN_SLOTS_DEFAULT;
29402
29550
  const hindsightRecallAdditionalBankMinSlots = agentConfig.memory?.recall?.additional_bank_min_slots ?? HINDSIGHT_ADDITIONAL_BANK_MIN_SLOTS_DEFAULT;
29403
29551
  const hindsightRecallTypes = agentConfig.memory?.recall?.types?.length ? agentConfig.memory.recall.types.join(",") : undefined;
@@ -29439,6 +29587,7 @@ function reconcileAgentInner(name, agentConfigRaw, agentsDir, telegramConfig, sw
29439
29587
  hindsightApiBaseUrlQ: shellSingleQuote(hindsightApiBaseUrl),
29440
29588
  hindsightRecallMaxMemories,
29441
29589
  hindsightRecallCacheTtlSecs,
29590
+ hindsightRecallParallelDeadlineSeconds,
29442
29591
  hindsightRecallQueryMaxTokens,
29443
29592
  hindsightRecallQueryStopTermsJson,
29444
29593
  hindsightRecallRequestTimeoutSeconds,
@@ -29633,7 +29782,7 @@ ${baseAppend}` : TELEGRAM_FORMATTING_FLOOR_CARD;
29633
29782
  }
29634
29783
  settings.mcpServers = mcpServers;
29635
29784
  if (!options.skipProfileTemplates) {
29636
- installHindsightPlugin(name, agentDir, switchroomConfig);
29785
+ installHindsightPlugin(name, agentDir, switchroomConfig, agentConfig);
29637
29786
  }
29638
29787
  if (hindsightEnabled) {
29639
29788
  settings.autoMemoryEnabled = false;
@@ -29857,6 +30006,7 @@ ${body}
29857
30006
  hindsightApiBaseUrl,
29858
30007
  hindsightRecallMaxMemories,
29859
30008
  hindsightRecallCacheTtlSecs,
30009
+ hindsightRecallParallelDeadlineSeconds,
29860
30010
  hindsightRecallQueryMaxTokens,
29861
30011
  hindsightRecallQueryStopTermsJson,
29862
30012
  hindsightRecallRequestTimeoutSeconds,
@@ -30191,7 +30341,7 @@ function buildAccessJson2(agentConfig, telegramConfig, resolvedTopicId, userId)
30191
30341
  return JSON.stringify(access, null, 2) + `
30192
30342
  `;
30193
30343
  }
30194
- var REPO_ROOT, HINDSIGHT_RECALL_QUERY_MAX_TOKENS_DEFAULT = 24, HINDSIGHT_RECALL_REQUEST_TIMEOUT_SECONDS_DEFAULT = 12, SANDBOX_GUIDANCE = `## Sandbox: you're running in a switchroom container
30344
+ var REPO_ROOT, HINDSIGHT_RECALL_QUERY_MAX_TOKENS_DEFAULT = 24, SANDBOX_GUIDANCE = `## Sandbox: you're running in a switchroom container
30195
30345
 
30196
30346
  Your container has \`read_only: true\` rootfs. Most paths are read-only.
30197
30347
 
@@ -30556,6 +30706,7 @@ var init_scaffold = __esm(() => {
30556
30706
  init_atomic();
30557
30707
  init_agent_owned_tree();
30558
30708
  init_generation_stamp();
30709
+ init_hindsight_recall_tunables();
30559
30710
  init_schema();
30560
30711
  init_cron_routing();
30561
30712
  init_tier_selector();
@@ -30575,6 +30726,7 @@ var init_scaffold = __esm(() => {
30575
30726
  init_vault();
30576
30727
  init_onboarding();
30577
30728
  init_hindsight();
30729
+ init_hindsight_recall_tunables();
30578
30730
  init_bare_clone();
30579
30731
  init_agent_worktree();
30580
30732
  REPO_ROOT = resolve11(import.meta.dirname, "../..");
@@ -43778,25 +43930,34 @@ var init_doctor_status = __esm(() => {
43778
43930
  });
43779
43931
 
43780
43932
  // src/config/thinking-effort-risk.ts
43933
+ function isRiskyThinkingEffort(effort) {
43934
+ if (!effort)
43935
+ return false;
43936
+ return RISKY_EFFORTS.has(effort.trim().toLowerCase());
43937
+ }
43938
+ function emitsAdaptiveThinking(model) {
43939
+ if (!model)
43940
+ return false;
43941
+ const m = model.trim().toLowerCase();
43942
+ return m === "opus" || m.startsWith("claude-opus-");
43943
+ }
43781
43944
  function isAdaptiveThinkingOpus(model) {
43782
43945
  if (!model)
43783
43946
  return false;
43784
43947
  const m = model.trim().toLowerCase();
43785
- return m === "opus" || m.startsWith("claude-opus-4") || m === "claude-opus-5" || m.startsWith("claude-opus-5-");
43948
+ return m.startsWith("claude-opus-4");
43786
43949
  }
43787
43950
  function assessThinkingEffortRisk(model, effort) {
43788
- if (!effort)
43789
- return { risky: false };
43790
- if (!RISKY_EFFORTS.has(effort.trim().toLowerCase()))
43951
+ if (!isRiskyThinkingEffort(effort))
43791
43952
  return { risky: false };
43792
43953
  if (!isAdaptiveThinkingOpus(model))
43793
43954
  return { risky: false };
43794
43955
  return {
43795
43956
  risky: true,
43796
- reason: `thinking_effort '${effort}' on adaptive-thinking model '${model}' can trigger ` + `'400 thinking/redacted_thinking blocks cannot be modified' errors when work runs ` + `through concurrent sub-agents (issue #1978). Pin 'thinking_effort: low' (the safe ` + `floor) unless the bundled claude CLI includes the concurrent-agent thinking-block ` + `merge fix.`
43957
+ reason: `thinking_effort '${effort}' on pinned Opus 4.x model '${model}' could trigger ` + `'400 thinking/redacted_thinking blocks cannot be modified' errors when work runs ` + `through concurrent sub-agents (issue #1978). The upstream claude-CLI fix shipped in ` + `${CLAUDE_CLI_THINKING_MERGE_FIX_VERSION}, but the Opus 4.x reproduction has not been ` + `re-tested since, so pin 'thinking_effort: low' or move the agent to a current Opus model.`
43797
43958
  };
43798
43959
  }
43799
- var RISKY_EFFORTS;
43960
+ var CLAUDE_CLI_THINKING_MERGE_FIX_VERSION = "2.1.156", RISKY_EFFORTS;
43800
43961
  var init_thinking_effort_risk = __esm(() => {
43801
43962
  RISKY_EFFORTS = new Set(["medium", "high", "xhigh", "max"]);
43802
43963
  });
@@ -49401,6 +49562,83 @@ function detectHookScriptDrift(name, opts = {}) {
49401
49562
  }
49402
49563
  ];
49403
49564
  }
49565
+ function detectHindsightRecallTunableDrift(name, agentConfig, agentDir, config) {
49566
+ if (!isHindsightEnabled(config))
49567
+ return [];
49568
+ const resolved = resolveAgentConfig(config.defaults, config.profiles, agentConfig);
49569
+ if (resolved.memory?.auto_recall === false)
49570
+ return [];
49571
+ const pluginDir = join67(agentDir, ".claude", "plugins", "hindsight-memory");
49572
+ if (!existsSync72(pluginDir))
49573
+ return [];
49574
+ const expected = resolveHindsightRecallTunables(resolved.memory?.recall);
49575
+ const findings = [];
49576
+ const mismatches = [];
49577
+ const hooksPath = join67(pluginDir, "hooks", "hooks.json");
49578
+ if (existsSync72(hooksPath)) {
49579
+ let installedHookTimeout = null;
49580
+ try {
49581
+ installedHookTimeout = readHooksRecallTimeout(readFileSync66(hooksPath, "utf-8"));
49582
+ } catch {
49583
+ installedHookTimeout = null;
49584
+ }
49585
+ if (installedHookTimeout === null) {
49586
+ mismatches.push("hooks/hooks.json has no readable UserPromptSubmit recall-hook timeout");
49587
+ } else if (installedHookTimeout !== expected.hookTimeoutSeconds) {
49588
+ mismatches.push(`hook ceiling is ${installedHookTimeout}s, expected ` + `${expected.hookTimeoutSeconds}s (memory.recall.hook_timeout_seconds)`);
49589
+ }
49590
+ }
49591
+ const settingsPath = join67(pluginDir, "settings.json");
49592
+ if (existsSync72(settingsPath)) {
49593
+ let settings = null;
49594
+ try {
49595
+ settings = JSON.parse(readFileSync66(settingsPath, "utf-8"));
49596
+ } catch {
49597
+ settings = null;
49598
+ }
49599
+ if (settings === null) {
49600
+ mismatches.push("settings.json is unreadable or malformed");
49601
+ } else {
49602
+ const checks = [
49603
+ [
49604
+ "recallParallelDeadlineSeconds",
49605
+ "memory.recall.parallel_deadline_seconds",
49606
+ expected.parallelDeadlineSeconds
49607
+ ],
49608
+ [
49609
+ "recallRequestTimeoutSeconds",
49610
+ "memory.recall.request_timeout_seconds",
49611
+ expected.requestTimeoutSeconds
49612
+ ]
49613
+ ];
49614
+ for (const [key, yamlKey, want] of checks) {
49615
+ const got = settings[key];
49616
+ if (got === undefined) {
49617
+ mismatches.push(`settings.json is missing \`${key}\` (expected ${want})`);
49618
+ } else if (got !== want) {
49619
+ mismatches.push(`\`${key}\` is ${JSON.stringify(got)}, expected ${want} (${yamlKey})`);
49620
+ }
49621
+ }
49622
+ }
49623
+ }
49624
+ if (mismatches.length > 0) {
49625
+ findings.push({
49626
+ surface: "memory-tunables",
49627
+ agent: name,
49628
+ detail: `installed hindsight plugin disagrees with switchroom.yaml: ` + mismatches.join("; "),
49629
+ fix: "Run `switchroom apply` to re-stamp the plugin, then restart the agent " + "(`switchroom agent restart <name>`). If it recurs immediately after an " + "apply, the stamping in installHindsightPlugin has regressed \u2014 these " + "values have silently reverted three times before, which is why this " + "check exists."
49630
+ });
49631
+ }
49632
+ if (expected.clamps.length > 0) {
49633
+ findings.push({
49634
+ surface: "memory-tunables",
49635
+ agent: name,
49636
+ detail: `recall tunable clamped: ${expected.clamps.join("; ")}`,
49637
+ fix: "Adjust the offending `memory.recall.*` value in switchroom.yaml so the " + "nested deadlines hold (per-bank timeout <= parallel deadline <= hook " + "ceiling), or raise the outer bound."
49638
+ });
49639
+ }
49640
+ return findings;
49641
+ }
49404
49642
  function writeDriftReport(agentDir, findings) {
49405
49643
  try {
49406
49644
  const report = {
@@ -49423,6 +49661,7 @@ function detectAgentDrift(name, agentConfigRaw, agentsDir, config, configPath, o
49423
49661
  currentVersion: opts.currentVersion
49424
49662
  }));
49425
49663
  findings.push(...detectSkillsDrift(name, agentDir));
49664
+ findings.push(...detectHindsightRecallTunableDrift(name, agentConfig, agentDir, config));
49426
49665
  if (!opts.skipContainerProbes) {
49427
49666
  findings.push(...detectHookScriptDrift(name, {
49428
49667
  binDir: opts.binDir,
@@ -49439,6 +49678,7 @@ var BUNDLED_POOL_SEGMENT = "/.switchroom/skills/_bundled/", defaultExec = (cmd,
49439
49678
  var init_drift = __esm(() => {
49440
49679
  init_merge();
49441
49680
  init_hindsight2();
49681
+ init_hindsight_recall_tunables();
49442
49682
  init_scaffold();
49443
49683
  init_lifecycle();
49444
49684
  init_generation_stamp();
@@ -51279,6 +51519,142 @@ var init_doctor_fix_session_model = __esm(() => {
51279
51519
  init_loader();
51280
51520
  });
51281
51521
 
51522
+ // src/cli/doctor-claude-cli.ts
51523
+ import { spawnSync as spawnSync12 } from "node:child_process";
51524
+ import { existsSync as existsSync77, readFileSync as readFileSync68 } from "node:fs";
51525
+ import { dirname as dirname29, join as join78 } from "node:path";
51526
+ function parseClaudeCliVersion(raw) {
51527
+ const m = raw.trim().match(/^v?(\d+)\.(\d+)\.(\d+)/);
51528
+ if (!m)
51529
+ return null;
51530
+ return [Number(m[1]), Number(m[2]), Number(m[3])];
51531
+ }
51532
+ function claudeCliMeetsFloor(raw, floor) {
51533
+ const got = parseClaudeCliVersion(raw);
51534
+ const want = parseClaudeCliVersion(floor);
51535
+ if (!got || !want)
51536
+ return null;
51537
+ for (let i = 0;i < 3; i++) {
51538
+ if (got[i] > want[i])
51539
+ return true;
51540
+ if (got[i] < want[i])
51541
+ return false;
51542
+ }
51543
+ return true;
51544
+ }
51545
+ function locateRepoFile(relative4) {
51546
+ let dir = import.meta.dirname;
51547
+ for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
51548
+ const candidate = join78(dir, relative4);
51549
+ if (existsSync77(candidate))
51550
+ return candidate;
51551
+ dir = dirname29(dir);
51552
+ }
51553
+ return null;
51554
+ }
51555
+ function readPinnedClaudeCliVersion(dockerfilePath) {
51556
+ const path7 = dockerfilePath === undefined ? locateRepoFile(join78("docker", "Dockerfile.base")) : dockerfilePath;
51557
+ if (!path7)
51558
+ return null;
51559
+ let text;
51560
+ try {
51561
+ text = readFileSync68(path7, "utf-8");
51562
+ } catch {
51563
+ return null;
51564
+ }
51565
+ const m = text.match(/^\s*ARG\s+CLAUDE_CODE_VERSION=([0-9][0-9.]*)\s*$/m);
51566
+ return m ? m[1] : null;
51567
+ }
51568
+ function buildClaudeCliProbeScript() {
51569
+ return 'P="$HOME/.local/bin:$HOME/bin:$HOME/.npm-global/bin:$PATH"; ' + 'B=$(PATH="$P" command -v claude 2>/dev/null); ' + 'if [ -z "$B" ]; then echo "BIN= VER="; exit 0; fi; ' + 'V=$("$B" --version 2>/dev/null | head -n1 | cut -d" " -f1); ' + 'echo "BIN=$B VER=$V"';
51570
+ }
51571
+ function parseClaudeCliProbeOutput(r) {
51572
+ if (r.error || r.status !== 0) {
51573
+ const stderr = (r.stderr?.toString() ?? "").trim();
51574
+ const msg = r.error?.message ?? (stderr !== "" ? stderr : `docker exec exited ${r.status ?? "on timeout"}`);
51575
+ return { state: "unreachable", msg };
51576
+ }
51577
+ const out = (r.stdout?.toString() ?? "").trim();
51578
+ const bin = out.match(/BIN=(\S*)/)?.[1] ?? "";
51579
+ const raw = out.match(/VER=(\S*)/)?.[1] ?? "";
51580
+ if (bin === "")
51581
+ return { state: "missing" };
51582
+ if (raw === "") {
51583
+ return { state: "unreachable", msg: `\`${bin} --version\` produced no output` };
51584
+ }
51585
+ return { state: "read", bin, raw };
51586
+ }
51587
+ function probeAgentClaudeCli(agentName) {
51588
+ const r = spawnSync12("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", buildClaudeCliProbeScript()], { stdio: "pipe", timeout: 3000 });
51589
+ return parseClaudeCliProbeOutput(r);
51590
+ }
51591
+ function assessClaudeCliFloor(input) {
51592
+ const { agentName, probe: probe2, exposed, pinned } = input;
51593
+ const floor = input.floor ?? CLAUDE_CLI_THINKING_MERGE_FIX_VERSION;
51594
+ const name = `${agentName}: claude CLI floor`;
51595
+ const restart = `Restart the agent onto the current image: \`switchroom update\` then ` + `\`switchroom agent restart ${agentName} --force\`` + (pinned ? ` (docker/Dockerfile.base pins ${pinned})` : "") + `.`;
51596
+ if (probe2.state === "unreachable") {
51597
+ return {
51598
+ name,
51599
+ status: "skip",
51600
+ detail: `could not read the running claude CLI in switchroom-${agentName}: ${probe2.msg}`
51601
+ };
51602
+ }
51603
+ if (probe2.state === "missing") {
51604
+ return {
51605
+ name,
51606
+ status: "warn",
51607
+ detail: `no \`claude\` on the session PATH inside switchroom-${agentName}`,
51608
+ fix: restart
51609
+ };
51610
+ }
51611
+ const meets = claudeCliMeetsFloor(probe2.raw, floor);
51612
+ if (meets === null) {
51613
+ return {
51614
+ name,
51615
+ status: "skip",
51616
+ detail: `unparseable version from ${probe2.bin}: ${probe2.raw}`
51617
+ };
51618
+ }
51619
+ if (meets) {
51620
+ return {
51621
+ name,
51622
+ status: "ok",
51623
+ detail: `${probe2.raw} at ${probe2.bin} (>= ${floor} floor)`
51624
+ };
51625
+ }
51626
+ const exposure = exposed ? ` This agent's model and thinking_effort are in the #1978 exposure shape, ` + `which the config-time guard no longer covers on the assumption of a ` + `>= ${floor} CLI.` : ` This agent's model and thinking_effort are not in the #1978 exposure shape.`;
51627
+ return {
51628
+ name,
51629
+ status: "warn",
51630
+ detail: `${probe2.raw} at ${probe2.bin} is BELOW the ${floor} floor \u2014 ` + `the claude-code build that fixed the #1978 thinking-block merge 400.` + exposure,
51631
+ fix: exposed ? `${restart} Until then set \`thinking_effort: low\` for ${agentName}; ` + `on a pre-${floor} CLI, medium+ effort on an Opus model can 400 with ` + `'thinking blocks cannot be modified' when work runs through concurrent sub-agents.` : restart
51632
+ };
51633
+ }
51634
+ function runClaudeCliVersionChecks(config, deps = {}) {
51635
+ if (deps.fast)
51636
+ return [];
51637
+ const agents = Object.entries(config.agents ?? {});
51638
+ if (agents.length === 0)
51639
+ return [];
51640
+ const pinned = readPinnedClaudeCliVersion(deps.dockerfilePath);
51641
+ const probe2 = deps.probe ?? probeAgentClaudeCli;
51642
+ return agents.map(([agentName, agentConfig]) => {
51643
+ const resolved = resolveAgentConfig(config.defaults, config.profiles, agentConfig);
51644
+ const exposed = emitsAdaptiveThinking(resolved.model) && isRiskyThinkingEffort(resolved.thinking_effort);
51645
+ return assessClaudeCliFloor({
51646
+ agentName,
51647
+ probe: probe2(agentName),
51648
+ exposed,
51649
+ pinned
51650
+ });
51651
+ });
51652
+ }
51653
+ var init_doctor_claude_cli = __esm(() => {
51654
+ init_merge();
51655
+ init_thinking_effort_risk();
51656
+ });
51657
+
51282
51658
  // src/cli/doctor.ts
51283
51659
  var exports_doctor = {};
51284
51660
  __export(exports_doctor, {
@@ -51330,28 +51706,28 @@ __export(exports_doctor, {
51330
51706
  PENDING_RETAINS_EVICTION_WINDOW_DAYS: () => PENDING_RETAINS_EVICTION_WINDOW_DAYS,
51331
51707
  MFF_VAULT_KEY: () => MFF_VAULT_KEY
51332
51708
  });
51333
- import { spawnSync as spawnSync12 } from "node:child_process";
51709
+ import { spawnSync as spawnSync13 } from "node:child_process";
51334
51710
  import { Socket as Socket3 } from "node:net";
51335
51711
  import {
51336
51712
  accessSync as accessSync3,
51337
51713
  constants as fsConstants6,
51338
- existsSync as existsSync77,
51714
+ existsSync as existsSync78,
51339
51715
  lstatSync as lstatSync11,
51340
51716
  mkdirSync as mkdirSync41,
51341
- readFileSync as readFileSync68,
51717
+ readFileSync as readFileSync69,
51342
51718
  readdirSync as readdirSync26,
51343
51719
  statSync as statSync43
51344
51720
  } from "node:fs";
51345
- import { dirname as dirname29, join as join78, resolve as resolve44 } from "node:path";
51721
+ import { dirname as dirname30, join as join79, resolve as resolve44 } from "node:path";
51346
51722
  import { createPublicKey, createPrivateKey } from "node:crypto";
51347
51723
  function findInNvm(bin) {
51348
- const nvmRoot = join78(process.env.HOME ?? "", ".nvm", "versions", "node");
51349
- if (!existsSync77(nvmRoot))
51724
+ const nvmRoot = join79(process.env.HOME ?? "", ".nvm", "versions", "node");
51725
+ if (!existsSync78(nvmRoot))
51350
51726
  return null;
51351
51727
  try {
51352
51728
  const versions = readdirSync26(nvmRoot).sort().reverse();
51353
51729
  for (const v of versions) {
51354
- const candidate = join78(nvmRoot, v, "bin", bin);
51730
+ const candidate = join79(nvmRoot, v, "bin", bin);
51355
51731
  try {
51356
51732
  const s = statSync43(candidate);
51357
51733
  if (s.isFile() || s.isSymbolicLink()) {
@@ -51378,7 +51754,7 @@ function whichOnPath(bin) {
51378
51754
  for (const dir of pathEnv.split(":")) {
51379
51755
  if (!dir)
51380
51756
  continue;
51381
- const candidate = join78(dir, bin);
51757
+ const candidate = join79(dir, bin);
51382
51758
  if (isX(candidate))
51383
51759
  return candidate;
51384
51760
  }
@@ -51462,7 +51838,7 @@ function readVersion(bin, parser) {
51462
51838
  const path7 = which(bin);
51463
51839
  if (!path7)
51464
51840
  return null;
51465
- const proc = spawnSync12(path7, ["--version"], {
51841
+ const proc = spawnSync13(path7, ["--version"], {
51466
51842
  stdio: ["ignore", "pipe", "pipe"]
51467
51843
  });
51468
51844
  if (proc.error || proc.status !== 0)
@@ -51544,21 +51920,21 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
51544
51920
  if (envBrowsersPath && envBrowsersPath.length > 0) {
51545
51921
  cacheLocations.push(envBrowsersPath);
51546
51922
  }
51547
- cacheLocations.push(join78(homeDir, ".cache", "ms-playwright"));
51923
+ cacheLocations.push(join79(homeDir, ".cache", "ms-playwright"));
51548
51924
  for (const cacheDir of cacheLocations) {
51549
- if (!existsSync77(cacheDir))
51925
+ if (!existsSync78(cacheDir))
51550
51926
  continue;
51551
51927
  try {
51552
51928
  const entries = readdirSync26(cacheDir).filter((e) => e.startsWith("chromium"));
51553
51929
  for (const entry of entries) {
51554
51930
  const candidates2 = [
51555
- join78(cacheDir, entry, "chrome-linux64", "chrome"),
51556
- join78(cacheDir, entry, "chrome-linux", "chrome"),
51557
- join78(cacheDir, entry, "chrome-linux64", "headless_shell"),
51558
- join78(cacheDir, entry, "chrome-linux", "headless_shell")
51931
+ join79(cacheDir, entry, "chrome-linux64", "chrome"),
51932
+ join79(cacheDir, entry, "chrome-linux", "chrome"),
51933
+ join79(cacheDir, entry, "chrome-linux64", "headless_shell"),
51934
+ join79(cacheDir, entry, "chrome-linux", "headless_shell")
51559
51935
  ];
51560
51936
  for (const path7 of candidates2) {
51561
- if (existsSync77(path7))
51937
+ if (existsSync78(path7))
51562
51938
  return path7;
51563
51939
  }
51564
51940
  }
@@ -51643,8 +52019,8 @@ function checkConfig(config, configPath) {
51643
52019
  results.push({
51644
52020
  name: "thinking_effort \u00d7 adaptive model",
51645
52021
  status: effortRisks.length > 0 ? "warn" : "ok",
51646
- detail: effortRisks.length > 0 ? `${effortRisks.length} agent(s) on Opus (4.x/5) with thinking_effort > low: ${effortRisks.join(", ")}` : "no risky model/effort combos",
51647
- fix: effortRisks.length > 0 ? "Pin `thinking_effort: low` for Opus 4.x / Opus 5 agents \u2014 medium+ can 400 on 'thinking blocks cannot be modified' with concurrent sub-agents (issue #1978). Removing the field is NOT a fix (Opus defaults effort=high when unset)." : undefined
52022
+ detail: effortRisks.length > 0 ? `${effortRisks.length} agent(s) on pinned Opus 4.x with thinking_effort > low: ${effortRisks.join(", ")}` : "no risky model/effort combos",
52023
+ fix: effortRisks.length > 0 ? "Pin `thinking_effort: low` for pinned Opus 4.x agents, or move them to a current Opus model. medium+ could 400 on 'thinking blocks cannot be modified' with concurrent sub-agents (issue #1978); the upstream fix shipped in claude-code " + `${CLAUDE_CLI_THINKING_MERGE_FIX_VERSION} but the Opus 4.x reproduction was never re-tested. Removing the field is NOT a fix (Opus defaults effort=high when unset).` : undefined
51648
52024
  });
51649
52025
  return results;
51650
52026
  }
@@ -51687,7 +52063,7 @@ function checkDeployMounts(opts) {
51687
52063
  const home2 = opts?.home ?? process.env.HOME ?? "/root";
51688
52064
  const { pathKind } = opts?.deps ?? DEFAULT_DEPLOY_MOUNTS_DEPS;
51689
52065
  const results = [];
51690
- const dockerComposePlugin = join78(home2, ".docker", "cli-plugins", "docker-compose");
52066
+ const dockerComposePlugin = join79(home2, ".docker", "cli-plugins", "docker-compose");
51691
52067
  const pluginKind = pathKind(dockerComposePlugin);
51692
52068
  if (pluginKind === "dir") {
51693
52069
  results.push({
@@ -51725,8 +52101,8 @@ function checkDeployMounts(opts) {
51725
52101
  function checkLegacyState() {
51726
52102
  const results = [];
51727
52103
  const h = process.env.HOME ?? "/root";
51728
- const clerkDir = join78(h, LEGACY_STATE_DIR);
51729
- const clerkPresent = existsSync77(clerkDir);
52104
+ const clerkDir = join79(h, LEGACY_STATE_DIR);
52105
+ const clerkPresent = existsSync78(clerkDir);
51730
52106
  results.push({
51731
52107
  name: "legacy ~/.clerk state",
51732
52108
  status: clerkPresent ? "warn" : "ok",
@@ -51735,7 +52111,7 @@ function checkLegacyState() {
51735
52111
  fix: "Legacy state detected. Run `mv ~/.clerk ~/.switchroom` and rename " + "any top-level `clerk:` key in switchroom.yaml to `switchroom:`. " + "This back-compat shim is REMOVED in v0.13.0 \u2014 no automatic " + "migration exists."
51736
52112
  } : {}
51737
52113
  });
51738
- const legacySock = join78(h, ".switchroom", "vault-broker.sock");
52114
+ const legacySock = join79(h, ".switchroom", "vault-broker.sock");
51739
52115
  let sockStat = null;
51740
52116
  try {
51741
52117
  sockStat = lstatSync11(legacySock);
@@ -51754,7 +52130,7 @@ function probeVaultBrokerSocketPair(agentName) {
51754
52130
  const dataPath = `/run/switchroom/broker/${agentName}/sock`;
51755
52131
  const unlockPath = `/run/switchroom/broker/${agentName}/unlock`;
51756
52132
  const script = `D=0; U=0; ` + `test -S '${dataPath}' && D=1; ` + `test -S '${unlockPath}' && U=1; ` + `echo "D=$D U=$U"`;
51757
- const r = spawnSync12("docker", ["exec", "switchroom-vault-broker", "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
52133
+ const r = spawnSync13("docker", ["exec", "switchroom-vault-broker", "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
51758
52134
  if (r.error || r.status === null)
51759
52135
  return "unreachable";
51760
52136
  if (r.status !== 0) {
@@ -51856,7 +52232,7 @@ function checkVault(config) {
51856
52232
  detail: "Approval auth: passphrase (two-factor)"
51857
52233
  };
51858
52234
  const pairsResult = checkVaultBrokerSocketPairs(config);
51859
- if (!existsSync77(vaultPath)) {
52235
+ if (!existsSync78(vaultPath)) {
51860
52236
  return [
51861
52237
  postureResult,
51862
52238
  {
@@ -51969,7 +52345,7 @@ function checkHindsightConsumer(config, opts) {
51969
52345
  }
51970
52346
  function probeAuthBrokerSocket(consumerName) {
51971
52347
  const containerPath = `/run/switchroom/auth-broker/${consumerName}/sock`;
51972
- const r = spawnSync12("docker", ["exec", "switchroom-auth-broker", "test", "-S", containerPath], { stdio: "pipe", timeout: 3000 });
52348
+ const r = spawnSync13("docker", ["exec", "switchroom-auth-broker", "test", "-S", containerPath], { stdio: "pipe", timeout: 3000 });
51973
52349
  if (r.error || r.status === null)
51974
52350
  return "unreachable";
51975
52351
  if (r.status === 0)
@@ -52164,7 +52540,7 @@ async function checkHindsight(config) {
52164
52540
  function probePendingRetainsQueue(agentName) {
52165
52541
  const base = "/state/agent/home/.hindsight";
52166
52542
  const script = buildPendingRetainsProbeScript(base);
52167
- const r = spawnSync12("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
52543
+ const r = spawnSync13("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
52168
52544
  return parsePendingRetainsProbeOutput(r);
52169
52545
  }
52170
52546
  function buildPendingRetainsProbeScript(base, now = Date.now()) {
@@ -52312,7 +52688,7 @@ function classifyReadError(err) {
52312
52688
  }
52313
52689
  function tryReadHostFile(path7) {
52314
52690
  try {
52315
- return { kind: "ok", content: readFileSync68(path7, "utf-8") };
52691
+ return { kind: "ok", content: readFileSync69(path7, "utf-8") };
52316
52692
  } catch (err) {
52317
52693
  const kind = classifyReadError(err);
52318
52694
  const error = err?.message ?? String(err);
@@ -52324,11 +52700,11 @@ function tryReadHostFile(path7) {
52324
52700
  }
52325
52701
  }
52326
52702
  function parseEnvFile(path7) {
52327
- if (!existsSync77(path7))
52703
+ if (!existsSync78(path7))
52328
52704
  return {};
52329
52705
  let content;
52330
52706
  try {
52331
- content = readFileSync68(path7, "utf-8");
52707
+ content = readFileSync69(path7, "utf-8");
52332
52708
  } catch {
52333
52709
  return {};
52334
52710
  }
@@ -52383,7 +52759,7 @@ async function checkTelegram(config) {
52383
52759
  const plugin = agentConfig.channels?.telegram?.plugin ?? "switchroom";
52384
52760
  if (plugin !== "switchroom")
52385
52761
  continue;
52386
- const envPath = join78(agentsDir, name, "telegram", ".env");
52762
+ const envPath = join79(agentsDir, name, "telegram", ".env");
52387
52763
  const read = tryReadHostFile(envPath);
52388
52764
  if (read.kind === "eacces") {
52389
52765
  results.push({
@@ -52435,7 +52811,7 @@ async function checkTelegram(config) {
52435
52811
  }
52436
52812
  function checkStartShStale(agentName, startShPath) {
52437
52813
  const label = `${agentName}: start.sh scheduler block`;
52438
- if (!existsSync77(startShPath)) {
52814
+ if (!existsSync78(startShPath)) {
52439
52815
  return {
52440
52816
  name: label,
52441
52817
  status: "warn",
@@ -52445,7 +52821,7 @@ function checkStartShStale(agentName, startShPath) {
52445
52821
  }
52446
52822
  let content;
52447
52823
  try {
52448
- content = readFileSync68(startShPath, "utf-8");
52824
+ content = readFileSync69(startShPath, "utf-8");
52449
52825
  } catch (err) {
52450
52826
  return {
52451
52827
  name: label,
@@ -52466,7 +52842,7 @@ function checkStartShStale(agentName, startShPath) {
52466
52842
  }
52467
52843
  function checkStartShSessionModelCarrier(agentName, startShPath) {
52468
52844
  const label = `${agentName}: start.sh /model session carrier`;
52469
- if (!existsSync77(startShPath)) {
52845
+ if (!existsSync78(startShPath)) {
52470
52846
  return {
52471
52847
  name: label,
52472
52848
  status: "warn",
@@ -52476,7 +52852,7 @@ function checkStartShSessionModelCarrier(agentName, startShPath) {
52476
52852
  }
52477
52853
  let content;
52478
52854
  try {
52479
- content = readFileSync68(startShPath, "utf-8");
52855
+ content = readFileSync69(startShPath, "utf-8");
52480
52856
  } catch (err) {
52481
52857
  return {
52482
52858
  name: label,
@@ -52513,7 +52889,7 @@ function checkStartShSessionModelCarrier(agentName, startShPath) {
52513
52889
  }
52514
52890
  function checkLeakedHomeSwitchroom(agentName, agentDir) {
52515
52891
  const label = `${agentName}: $HOME/.switchroom symlink (#910)`;
52516
- const path7 = join78(agentDir, "home", ".switchroom");
52892
+ const path7 = join79(agentDir, "home", ".switchroom");
52517
52893
  let stats;
52518
52894
  try {
52519
52895
  stats = lstatSync11(path7);
@@ -52550,8 +52926,8 @@ function checkLeakedHomeSwitchroom(agentName, agentDir) {
52550
52926
  }
52551
52927
  function checkRepoHygiene(repoRoot) {
52552
52928
  const results = [];
52553
- const exportDir = join78(repoRoot, "clerk-export");
52554
- if (existsSync77(exportDir)) {
52929
+ const exportDir = join79(repoRoot, "clerk-export");
52930
+ if (existsSync78(exportDir)) {
52555
52931
  results.push({
52556
52932
  name: "repo hygiene: clerk-export/ on disk (#1072)",
52557
52933
  status: "warn",
@@ -52559,8 +52935,8 @@ function checkRepoHygiene(repoRoot) {
52559
52935
  fix: `Run scripts/migrate-clerk-export-to-vault.sh to move the bundle ` + `into the vault, then delete the on-disk copy.`
52560
52936
  });
52561
52937
  }
52562
- const knownTarball = join78(repoRoot, "clerk-export-with-secrets.tar.gz");
52563
- if (existsSync77(knownTarball)) {
52938
+ const knownTarball = join79(repoRoot, "clerk-export-with-secrets.tar.gz");
52939
+ if (existsSync78(knownTarball)) {
52564
52940
  results.push({
52565
52941
  name: "repo hygiene: clerk-export-with-secrets.tar.gz on disk (#1072)",
52566
52942
  status: "warn",
@@ -52577,7 +52953,7 @@ function checkRepoHygiene(repoRoot) {
52577
52953
  results.push({
52578
52954
  name: `repo hygiene: ${name} on disk (#1072)`,
52579
52955
  status: "warn",
52580
- detail: `${join78(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
52956
+ detail: `${join79(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
52581
52957
  fix: `Inspect, migrate any secrets into the vault, then delete the ` + `archive.`
52582
52958
  });
52583
52959
  }
@@ -52600,12 +52976,12 @@ function checkRepoHygiene(repoRoot) {
52600
52976
  }
52601
52977
  function isSwitchroomCheckout(dir) {
52602
52978
  try {
52603
- if (!existsSync77(join78(dir, ".git")))
52979
+ if (!existsSync78(join79(dir, ".git")))
52604
52980
  return false;
52605
- const pkgPath = join78(dir, "package.json");
52606
- if (!existsSync77(pkgPath))
52981
+ const pkgPath = join79(dir, "package.json");
52982
+ if (!existsSync78(pkgPath))
52607
52983
  return false;
52608
- const pkg = JSON.parse(readFileSync68(pkgPath, "utf-8"));
52984
+ const pkg = JSON.parse(readFileSync69(pkgPath, "utf-8"));
52609
52985
  return pkg.name === "switchroom";
52610
52986
  } catch {
52611
52987
  return false;
@@ -52618,7 +52994,7 @@ function checkAgents(config, configPath) {
52618
52994
  const authStatuses = getAllAuthStatuses(config);
52619
52995
  for (const [name, agentConfig] of Object.entries(config.agents)) {
52620
52996
  const agentDir = resolve44(agentsDir, name);
52621
- if (!existsSync77(agentDir)) {
52997
+ if (!existsSync78(agentDir)) {
52622
52998
  results.push({
52623
52999
  name: `${name}: scaffold`,
52624
53000
  status: "fail",
@@ -52639,8 +53015,8 @@ function checkAgents(config, configPath) {
52639
53015
  fix: `Rotate the bot token (e.g. via \`switchroom vault\`), then run ` + `\`switchroom agent unquarantine ${name}\` and \`switchroom agent restart ${name}\``
52640
53016
  });
52641
53017
  }
52642
- results.push(checkStartShStale(name, join78(agentDir, "start.sh")));
52643
- results.push(checkStartShSessionModelCarrier(name, join78(agentDir, "start.sh")));
53018
+ results.push(checkStartShStale(name, join79(agentDir, "start.sh")));
53019
+ results.push(checkStartShSessionModelCarrier(name, join79(agentDir, "start.sh")));
52644
53020
  results.push(checkLeakedHomeSwitchroom(name, agentDir));
52645
53021
  const status = statuses[name];
52646
53022
  const active = status?.active ?? "unknown";
@@ -52717,8 +53093,8 @@ function checkAgents(config, configPath) {
52717
53093
  }
52718
53094
  }
52719
53095
  if (agentConfig.channels?.telegram?.plugin === "switchroom") {
52720
- const mcpJsonPath = join78(agentDir, ".mcp.json");
52721
- if (!existsSync77(mcpJsonPath)) {
53096
+ const mcpJsonPath = join79(agentDir, ".mcp.json");
53097
+ if (!existsSync78(mcpJsonPath)) {
52722
53098
  results.push({
52723
53099
  name: `${name}: .mcp.json`,
52724
53100
  status: "fail",
@@ -52727,7 +53103,7 @@ function checkAgents(config, configPath) {
52727
53103
  });
52728
53104
  } else {
52729
53105
  try {
52730
- const mcp = JSON.parse(readFileSync68(mcpJsonPath, "utf-8"));
53106
+ const mcp = JSON.parse(readFileSync69(mcpJsonPath, "utf-8"));
52731
53107
  const hasSwitchroomTelegram = !!mcp.mcpServers?.["switchroom-telegram"];
52732
53108
  const memoryEnabled = isHindsightEnabled(config);
52733
53109
  const hasHindsight = !!mcp.mcpServers?.hindsight;
@@ -52804,7 +53180,7 @@ function mffEnvPath(config) {
52804
53180
  return agent ? resolve44(home2, ".switchroom/credentials", agent, "my-family-finance/.env") : resolve44(home2, ".switchroom/credentials/my-family-finance/.env");
52805
53181
  }
52806
53182
  function mffEnvState(envPath) {
52807
- if (!existsSync77(envPath))
53183
+ if (!existsSync78(envPath))
52808
53184
  return "absent";
52809
53185
  try {
52810
53186
  accessSync3(envPath, fsConstants6.R_OK);
@@ -52822,7 +53198,7 @@ function checkMffVaultKeyPresent(passphrase, vaultPath) {
52822
53198
  fix: "Export SWITCHROOM_VAULT_PASSPHRASE to enable MFF vault probes"
52823
53199
  };
52824
53200
  }
52825
- if (!existsSync77(vaultPath)) {
53201
+ if (!existsSync78(vaultPath)) {
52826
53202
  return {
52827
53203
  name: "mff: vault key present",
52828
53204
  status: "fail",
@@ -52875,7 +53251,7 @@ function deriveEd25519PublicKeyBytes(keyMaterial) {
52875
53251
  }
52876
53252
  }
52877
53253
  function checkMffVaultKeyFormat(passphrase, vaultPath) {
52878
- if (!passphrase || !existsSync77(vaultPath)) {
53254
+ if (!passphrase || !existsSync78(vaultPath)) {
52879
53255
  return {
52880
53256
  name: "mff: vault key format",
52881
53257
  status: "warn",
@@ -53018,9 +53394,9 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
53018
53394
  detail: "skipped (MFF_API_URL not set)"
53019
53395
  };
53020
53396
  }
53021
- const credDir = dirname29(envPath);
53022
- const authScript = join78(credDir, "claude-auth.py");
53023
- if (!existsSync77(authScript)) {
53397
+ const credDir = dirname30(envPath);
53398
+ const authScript = join79(credDir, "claude-auth.py");
53399
+ if (!existsSync78(authScript)) {
53024
53400
  return {
53025
53401
  name: "mff: auth flow",
53026
53402
  status: "warn",
@@ -53031,7 +53407,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
53031
53407
  const python3 = which("python3") ?? "python3";
53032
53408
  let token;
53033
53409
  try {
53034
- const result = spawnSync12(python3, [authScript, "--quiet"], {
53410
+ const result = spawnSync13(python3, [authScript, "--quiet"], {
53035
53411
  timeout: timeoutMs,
53036
53412
  encoding: "utf-8",
53037
53413
  env: { ...process.env, ...env2 }
@@ -53222,11 +53598,11 @@ function runDockerSection(config) {
53222
53598
  let composeYaml;
53223
53599
  let dockerfileAgent;
53224
53600
  try {
53225
- composeYaml = readFileSync68(composePath, "utf8");
53601
+ composeYaml = readFileSync69(composePath, "utf8");
53226
53602
  } catch {}
53227
53603
  const dockerfilePath = resolve44(process.env.HOME ?? "", ".switchroom", "docker", "Dockerfile.agent");
53228
53604
  try {
53229
- dockerfileAgent = readFileSync68(dockerfilePath, "utf8");
53605
+ dockerfileAgent = readFileSync69(dockerfilePath, "utf8");
53230
53606
  } catch {}
53231
53607
  return runDockerChecks({
53232
53608
  config,
@@ -53346,7 +53722,7 @@ function registerDoctorCommand(program3) {
53346
53722
  resolveSecret: (ref) => {
53347
53723
  if (!isVaultReference(ref))
53348
53724
  return ref;
53349
- if (!passphrase || !existsSync77(vaultPath))
53725
+ if (!passphrase || !existsSync78(vaultPath))
53350
53726
  return null;
53351
53727
  try {
53352
53728
  return getStringSecret(passphrase, vaultPath, parseVaultReference(ref));
@@ -53362,7 +53738,7 @@ function registerDoctorCommand(program3) {
53362
53738
  resolveSecret: (ref) => {
53363
53739
  if (!isVaultReference(ref))
53364
53740
  return ref;
53365
- if (!passphrase || !existsSync77(vaultPath))
53741
+ if (!passphrase || !existsSync78(vaultPath))
53366
53742
  return null;
53367
53743
  try {
53368
53744
  return getStringSecret(passphrase, vaultPath, parseVaultReference(ref));
@@ -53393,6 +53769,10 @@ function registerDoctorCommand(program3) {
53393
53769
  title: "Agent liveness (in-agent via hostd)",
53394
53770
  results: await runAgentSmokeChecks(config, { fast: opts.fast })
53395
53771
  },
53772
+ {
53773
+ title: "Claude CLI floor (#1978)",
53774
+ results: runClaudeCliVersionChecks(config, { fast: opts.fast })
53775
+ },
53396
53776
  {
53397
53777
  title: "Google Drive",
53398
53778
  results: [
@@ -53528,6 +53908,7 @@ var init_doctor = __esm(() => {
53528
53908
  init_doctor_vault_broker_durability();
53529
53909
  init_doctor_timezone();
53530
53910
  init_doctor_fix_session_model();
53911
+ init_doctor_claude_cli();
53531
53912
  DEFAULT_DEPLOY_MOUNTS_DEPS = { pathKind: defaultPathKind };
53532
53913
  MANIFEST_WARN_ONLY = new Set([
53533
53914
  "@playwright/mcp",
@@ -66463,9 +66844,9 @@ __export(exports_server, {
66463
66844
  dispatchTool: () => dispatchTool,
66464
66845
  TOOLS: () => TOOLS
66465
66846
  });
66466
- import { spawnSync as spawnSync19 } from "node:child_process";
66847
+ import { spawnSync as spawnSync20 } from "node:child_process";
66467
66848
  function execCli(args, stdin) {
66468
- const r = spawnSync19(CLI_BIN, args, {
66849
+ const r = spawnSync20(CLI_BIN, args, {
66469
66850
  encoding: "utf-8",
66470
66851
  env: process.env,
66471
66852
  timeout: 15000,
@@ -66974,7 +67355,7 @@ __export(exports_server2, {
66974
67355
  TOOLS: () => TOOLS2
66975
67356
  });
66976
67357
  import { randomBytes as randomBytes16 } from "node:crypto";
66977
- import { existsSync as existsSync104 } from "node:fs";
67358
+ import { existsSync as existsSync105 } from "node:fs";
66978
67359
  function selfSocketPath() {
66979
67360
  return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
66980
67361
  }
@@ -67001,7 +67382,7 @@ async function dispatchTool2(name, args) {
67001
67382
  return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
67002
67383
  }
67003
67384
  const sockPath = selfSocketPath();
67004
- if (!existsSync104(sockPath)) {
67385
+ if (!existsSync105(sockPath)) {
67005
67386
  return errorText2(`hostd MCP: socket not bound at ${sockPath}. The host-control ` + `daemon is either not installed (run \`switchroom hostd install\`) ` + `or this agent isn't admin-flagged in switchroom.yaml. RFC C ` + `bind-mounts the per-agent socket only when host_control.enabled ` + `is true AND the agent has admin: true.`);
67006
67387
  }
67007
67388
  let req;
@@ -67252,13 +67633,13 @@ function resolveAuditLogPath() {
67252
67633
  if (process.env.HOSTD_AUDIT_LOG_PATH)
67253
67634
  return process.env.HOSTD_AUDIT_LOG_PATH;
67254
67635
  const bindMounted = "/host-home/.switchroom/host-control-audit.log";
67255
- if (existsSync104(bindMounted))
67636
+ if (existsSync105(bindMounted))
67256
67637
  return bindMounted;
67257
67638
  return defaultAuditLogPath2();
67258
67639
  }
67259
67640
  function getLastUpdateApplyStatus() {
67260
67641
  const path10 = resolveAuditLogPath();
67261
- if (!existsSync104(path10)) {
67642
+ if (!existsSync105(path10)) {
67262
67643
  return errorText2(`get_status: audit log not found at ${path10}. No update_apply has run yet?`);
67263
67644
  }
67264
67645
  let raw;
@@ -67977,11 +68358,11 @@ var init_test_agents = __esm(() => {
67977
68358
  });
67978
68359
 
67979
68360
  // src/litellm/header-passthrough-guard.ts
67980
- import { existsSync as existsSync107, readdirSync as readdirSync41 } from "node:fs";
68361
+ import { existsSync as existsSync108, readdirSync as readdirSync41 } from "node:fs";
67981
68362
  function discoverLiveLitellmConfigPath(opts) {
67982
68363
  const servicesDir = opts?.servicesDir ?? COOLIFY_SERVICES_DIR;
67983
68364
  const readdir2 = opts?.readdirFn ?? ((p) => readdirSync41(p));
67984
- const exists = opts?.existsFn ?? existsSync107;
68365
+ const exists = opts?.existsFn ?? existsSync108;
67985
68366
  let entries;
67986
68367
  try {
67987
68368
  entries = readdir2(servicesDir);
@@ -68071,7 +68452,7 @@ var init_header_passthrough_guard = __esm(() => {
68071
68452
  });
68072
68453
 
68073
68454
  // src/fleet-health/litellm-config-sensor.ts
68074
- import { readFileSync as readFileSync93, existsSync as existsSync108 } from "node:fs";
68455
+ import { readFileSync as readFileSync94, existsSync as existsSync109 } from "node:fs";
68075
68456
  function resolveLitellmConfigPath(explicit, discoverFn = () => discoverLiveLitellmConfigPath()) {
68076
68457
  if (explicit)
68077
68458
  return explicit;
@@ -68082,8 +68463,8 @@ function resolveLitellmConfigPath(explicit, discoverFn = () => discoverLiveLitel
68082
68463
  }
68083
68464
  function scanLitellmConfig(opts = {}) {
68084
68465
  const path10 = resolveLitellmConfigPath(opts.path, opts.discoverFn);
68085
- const exists = opts.existsFn ?? existsSync108;
68086
- const read = opts.readFn ?? ((p) => readFileSync93(p, "utf-8"));
68466
+ const exists = opts.existsFn ?? existsSync109;
68467
+ const read = opts.readFn ?? ((p) => readFileSync94(p, "utf-8"));
68087
68468
  const log = opts.log ?? (() => {});
68088
68469
  const nowIso = opts.nowIso ?? new Date().toISOString();
68089
68470
  if (path10 === null) {
@@ -68158,20 +68539,20 @@ __export(exports_scan, {
68158
68539
  ledgerPathForBase: () => ledgerPathForBase
68159
68540
  });
68160
68541
  import {
68161
- readFileSync as readFileSync94,
68542
+ readFileSync as readFileSync95,
68162
68543
  readdirSync as readdirSync42,
68163
- existsSync as existsSync109,
68544
+ existsSync as existsSync110,
68164
68545
  mkdirSync as mkdirSync61,
68165
68546
  writeFileSync as writeFileSync45
68166
68547
  } from "node:fs";
68167
- import { resolve as resolve61, dirname as dirname39 } from "node:path";
68548
+ import { resolve as resolve61, dirname as dirname40 } from "node:path";
68168
68549
  import { homedir as homedir60 } from "node:os";
68169
68550
  function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir60()) {
68170
68551
  return resolve61(home2, ".switchroom");
68171
68552
  }
68172
68553
  function listAgents(base) {
68173
68554
  const dir = resolve61(base, "agents");
68174
- if (!existsSync109(dir))
68555
+ if (!existsSync110(dir))
68175
68556
  return [];
68176
68557
  try {
68177
68558
  return readdirSync42(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
@@ -68200,16 +68581,16 @@ function runScan(opts = {}) {
68200
68581
  let gwText = "";
68201
68582
  let sawArtifact = false;
68202
68583
  try {
68203
- if (existsSync109(turnsPath)) {
68204
- turnsText = readFileSync94(turnsPath, "utf-8");
68584
+ if (existsSync110(turnsPath)) {
68585
+ turnsText = readFileSync95(turnsPath, "utf-8");
68205
68586
  sawArtifact = true;
68206
68587
  }
68207
68588
  } catch (e) {
68208
68589
  log(`fleet-health: WARN skipping ${agent} turns.jsonl unreadable: ${String(e)}`);
68209
68590
  }
68210
68591
  try {
68211
- if (existsSync109(gwPath)) {
68212
- gwText = readFileSync94(gwPath, "utf-8");
68592
+ if (existsSync110(gwPath)) {
68593
+ gwText = readFileSync95(gwPath, "utf-8");
68213
68594
  sawArtifact = true;
68214
68595
  }
68215
68596
  } catch (e) {
@@ -68251,19 +68632,19 @@ function runScan(opts = {}) {
68251
68632
  function readLedgerIfPresent(base) {
68252
68633
  const path10 = ledgerPathForBase(base);
68253
68634
  try {
68254
- if (!existsSync109(path10))
68635
+ if (!existsSync110(path10))
68255
68636
  return null;
68256
- return JSON.parse(readFileSync94(path10, "utf-8"));
68637
+ return JSON.parse(readFileSync95(path10, "utf-8"));
68257
68638
  } catch {
68258
68639
  return null;
68259
68640
  }
68260
68641
  }
68261
68642
  function ledgerPathForBase(base) {
68262
- return fleetHealthLedgerPath(dirname39(base));
68643
+ return fleetHealthLedgerPath(dirname40(base));
68263
68644
  }
68264
68645
  function writeLedger(base, ledger) {
68265
68646
  const path10 = ledgerPathForBase(base);
68266
- mkdirSync61(dirname39(path10), { recursive: true });
68647
+ mkdirSync61(dirname40(path10), { recursive: true });
68267
68648
  writeFileSync45(path10, JSON.stringify(ledger, null, 2) + `
68268
68649
  `, "utf-8");
68269
68650
  return path10;
@@ -88798,9 +89179,9 @@ init_source();
88798
89179
  init_loader();
88799
89180
  init_lifecycle();
88800
89181
  init_compose_env();
88801
- import { existsSync as existsSync79, mkdirSync as mkdirSync43, readFileSync as readFileSync70, realpathSync as realpathSync6, statSync as statSync45, chownSync as chownSync8 } from "node:fs";
88802
- import { spawnSync as spawnSync13 } from "node:child_process";
88803
- import { join as join80, dirname as dirname30, resolve as resolve45 } from "node:path";
89182
+ import { existsSync as existsSync80, mkdirSync as mkdirSync43, readFileSync as readFileSync71, realpathSync as realpathSync6, statSync as statSync45, chownSync as chownSync8 } from "node:fs";
89183
+ import { spawnSync as spawnSync14 } from "node:child_process";
89184
+ import { join as join81, dirname as dirname31, resolve as resolve45 } from "node:path";
88804
89185
  import { homedir as homedir43 } from "node:os";
88805
89186
 
88806
89187
  // src/cli/release-yaml.ts
@@ -88931,27 +89312,27 @@ init_scaffold_integration();
88931
89312
  // src/cli/sync-bundled-skills.ts
88932
89313
  import {
88933
89314
  cpSync as cpSync2,
88934
- existsSync as existsSync78,
89315
+ existsSync as existsSync79,
88935
89316
  mkdirSync as mkdirSync42,
88936
- readFileSync as readFileSync69,
89317
+ readFileSync as readFileSync70,
88937
89318
  readdirSync as readdirSync27,
88938
89319
  renameSync as renameSync18,
88939
89320
  rmSync as rmSync13,
88940
89321
  writeFileSync as writeFileSync29
88941
89322
  } from "node:fs";
88942
- import { join as join79 } from "node:path";
89323
+ import { join as join80 } from "node:path";
88943
89324
  var BUNDLED_SKILL_MANIFEST_NAME = ".switchroom-manifest.json";
88944
89325
  function listSkillDirs(dir) {
88945
- if (!existsSync78(dir))
89326
+ if (!existsSync79(dir))
88946
89327
  return [];
88947
89328
  return readdirSync27(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
88948
89329
  }
88949
89330
  function readBundledSkillManifest(poolDir) {
88950
- const path7 = join79(poolDir, BUNDLED_SKILL_MANIFEST_NAME);
88951
- if (!existsSync78(path7))
89331
+ const path7 = join80(poolDir, BUNDLED_SKILL_MANIFEST_NAME);
89332
+ if (!existsSync79(path7))
88952
89333
  return { firstRun: true };
88953
89334
  try {
88954
- const parsed = JSON.parse(readFileSync69(path7, "utf8"));
89335
+ const parsed = JSON.parse(readFileSync70(path7, "utf8"));
88955
89336
  if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.skills) || !parsed.skills.every((s) => typeof s === "string")) {
88956
89337
  return { corrupt: true };
88957
89338
  }
@@ -88962,7 +89343,7 @@ function readBundledSkillManifest(poolDir) {
88962
89343
  }
88963
89344
  }
88964
89345
  function stageAndSwap(srcSkill, destSkill, poolDir, name) {
88965
- const staging = join79(poolDir, `.tmp-${name}-${process.pid}-${Date.now()}`);
89346
+ const staging = join80(poolDir, `.tmp-${name}-${process.pid}-${Date.now()}`);
88966
89347
  try {
88967
89348
  rmSync13(staging, { recursive: true, force: true });
88968
89349
  cpSync2(srcSkill, staging, { recursive: true, dereference: false });
@@ -88991,11 +89372,11 @@ function syncBundledSkills(opts) {
88991
89372
  const shipped = listSkillDirs(source).sort();
88992
89373
  const shippedSet = new Set(shipped);
88993
89374
  for (const name of shipped) {
88994
- const destSkill = join79(dest, name);
88995
- const existed = existsSync78(destSkill);
89375
+ const destSkill = join80(dest, name);
89376
+ const existed = existsSync79(destSkill);
88996
89377
  let transferred = false;
88997
89378
  if (existed && !priorSkills.has(name) && !result.firstRun) {
88998
- const backup = join79(dest, `${name}.operator-backup-${process.pid}-${Date.now()}`);
89379
+ const backup = join80(dest, `${name}.operator-backup-${process.pid}-${Date.now()}`);
88999
89380
  try {
89000
89381
  renameSync18(destSkill, backup);
89001
89382
  transferred = true;
@@ -89009,7 +89390,7 @@ function syncBundledSkills(opts) {
89009
89390
  continue;
89010
89391
  }
89011
89392
  }
89012
- stageAndSwap(join79(source, name), destSkill, dest, name);
89393
+ stageAndSwap(join80(source, name), destSkill, dest, name);
89013
89394
  if (!transferred && (priorSkills.has(name) || existed))
89014
89395
  result.updated.push(name);
89015
89396
  else
@@ -89019,8 +89400,8 @@ function syncBundledSkills(opts) {
89019
89400
  for (const name of priorSkills) {
89020
89401
  if (shippedSet.has(name))
89021
89402
  continue;
89022
- const target = join79(dest, name);
89023
- if (existsSync78(target)) {
89403
+ const target = join80(dest, name);
89404
+ if (existsSync79(target)) {
89024
89405
  rmSync13(target, { recursive: true, force: true });
89025
89406
  result.removed.push(name);
89026
89407
  }
@@ -89038,8 +89419,8 @@ function syncBundledSkills(opts) {
89038
89419
  skills: shipped,
89039
89420
  updatedAt: new Date().toISOString()
89040
89421
  };
89041
- const manifestPath = join79(dest, BUNDLED_SKILL_MANIFEST_NAME);
89042
- const manifestTmp = join79(dest, `${BUNDLED_SKILL_MANIFEST_NAME}.tmp-${process.pid}-${Date.now()}`);
89422
+ const manifestPath = join80(dest, BUNDLED_SKILL_MANIFEST_NAME);
89423
+ const manifestTmp = join80(dest, `${BUNDLED_SKILL_MANIFEST_NAME}.tmp-${process.pid}-${Date.now()}`);
89043
89424
  writeFileSync29(manifestTmp, JSON.stringify(manifest, null, 2) + `
89044
89425
  `, "utf8");
89045
89426
  renameSync18(manifestTmp, manifestPath);
@@ -89051,7 +89432,7 @@ init_resolve_version();
89051
89432
  function defaultPersistPin(configPath) {
89052
89433
  return (pin) => {
89053
89434
  const path7 = configPath ?? findConfigFile();
89054
- const before = readFileSync70(path7, "utf8");
89435
+ const before = readFileSync71(path7, "utf8");
89055
89436
  const after = setReleasePinInConfig(before, pin);
89056
89437
  if (after === before)
89057
89438
  return;
@@ -89065,18 +89446,18 @@ function defaultPersistPin(configPath) {
89065
89446
  } catch {}
89066
89447
  };
89067
89448
  }
89068
- var DEFAULT_COMPOSE_PATH2 = join80(homedir43(), ".switchroom", "compose", "docker-compose.yml");
89449
+ var DEFAULT_COMPOSE_PATH2 = join81(homedir43(), ".switchroom", "compose", "docker-compose.yml");
89069
89450
  function runningFromSwitchroomCheckout(scriptPath) {
89070
- let dir = dirname30(scriptPath);
89451
+ let dir = dirname31(scriptPath);
89071
89452
  for (let i = 0;i < 12; i++) {
89072
- if (existsSync79(join80(dir, ".git"))) {
89453
+ if (existsSync80(join81(dir, ".git"))) {
89073
89454
  try {
89074
- const pkg = JSON.parse(readFileSync70(join80(dir, "package.json"), "utf-8"));
89455
+ const pkg = JSON.parse(readFileSync71(join81(dir, "package.json"), "utf-8"));
89075
89456
  if (pkg.name === "switchroom")
89076
89457
  return true;
89077
89458
  } catch {}
89078
89459
  }
89079
- const parent = dirname30(dir);
89460
+ const parent = dirname31(dir);
89080
89461
  if (parent === dir)
89081
89462
  break;
89082
89463
  dir = parent;
@@ -89156,7 +89537,7 @@ function planUpdate(opts) {
89156
89537
  steps.push({
89157
89538
  name: "pull-images",
89158
89539
  description: "Pull broker / kernel / agent images from GHCR",
89159
- skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync79(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
89540
+ skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync80(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
89160
89541
  run: () => {
89161
89542
  const r = runner("docker", [
89162
89543
  "compose",
@@ -89282,14 +89663,14 @@ function planUpdate(opts) {
89282
89663
  return;
89283
89664
  }
89284
89665
  const source = resolve45(import.meta.dirname, "../../skills");
89285
- const dest = join80(homedir43(), ".switchroom", "skills", "_bundled");
89286
- if (!existsSync79(source)) {
89666
+ const dest = join81(homedir43(), ".switchroom", "skills", "_bundled");
89667
+ if (!existsSync80(source)) {
89287
89668
  process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
89288
89669
  `);
89289
89670
  return;
89290
89671
  }
89291
89672
  try {
89292
- mkdirSync43(dirname30(dest), { recursive: true });
89673
+ mkdirSync43(dirname31(dest), { recursive: true });
89293
89674
  const r = syncBundledSkills({
89294
89675
  source,
89295
89676
  dest,
@@ -89314,11 +89695,11 @@ function planUpdate(opts) {
89314
89695
  run: () => {
89315
89696
  if (opts.syncBundledSkillsFn)
89316
89697
  return;
89317
- const dest = join80(homedir43(), ".switchroom", "skills", "_bundled");
89318
- if (!existsSync79(dest)) {
89698
+ const dest = join81(homedir43(), ".switchroom", "skills", "_bundled");
89699
+ if (!existsSync80(dest)) {
89319
89700
  return;
89320
89701
  }
89321
- const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !existsSync79(join80(dest, key)));
89702
+ const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !existsSync80(join81(dest, key)));
89322
89703
  if (missing.length > 0) {
89323
89704
  throw new Error(`verify-bundled-skills: builtin default skill(s) missing from the pool after sync: ` + `${missing.join(", ")}. These ship in the CLI package and must exist in ${dest}. ` + `This is a broken sync or a packaging regression \u2014 the pool is not converged.`);
89324
89705
  }
@@ -89353,7 +89734,7 @@ function planUpdate(opts) {
89353
89734
  description: "docker compose up -d --remove-orphans (recreates services with new images / compose)",
89354
89735
  run: () => {
89355
89736
  try {
89356
- const composeText = readFileSync70(composePath, "utf8");
89737
+ const composeText = readFileSync71(composePath, "utf8");
89357
89738
  const pf = validateBindSources(composeText);
89358
89739
  if (!pf.ok)
89359
89740
  throw new Error(formatPreflightError(pf));
@@ -89387,7 +89768,7 @@ function planUpdate(opts) {
89387
89768
  return steps;
89388
89769
  }
89389
89770
  function defaultRunner2(cmd, args) {
89390
- const r = spawnSync13(cmd, args, { stdio: "inherit" });
89771
+ const r = spawnSync14(cmd, args, { stdio: "inherit" });
89391
89772
  return { status: r.status ?? 1 };
89392
89773
  }
89393
89774
  function writeMarkerInPreferredLocation(agent, reason, runner) {
@@ -89430,12 +89811,12 @@ function defaultStatusProbe(composePath) {
89430
89811
  try {
89431
89812
  cliBuiltAt = new Date(statSync45(scriptPath).mtimeMs).toISOString();
89432
89813
  } catch {}
89433
- let dir = dirname30(scriptPath);
89814
+ let dir = dirname31(scriptPath);
89434
89815
  for (let i = 0;i < 8; i++) {
89435
- const pkgPath = join80(dir, "package.json");
89436
- if (existsSync79(pkgPath)) {
89816
+ const pkgPath = join81(dir, "package.json");
89817
+ if (existsSync80(pkgPath)) {
89437
89818
  try {
89438
- const pkg = JSON.parse(readFileSync70(pkgPath, "utf-8"));
89819
+ const pkg = JSON.parse(readFileSync71(pkgPath, "utf-8"));
89439
89820
  if (typeof pkg.version === "string")
89440
89821
  cliVersion = pkg.version;
89441
89822
  } catch (err) {
@@ -89443,7 +89824,7 @@ function defaultStatusProbe(composePath) {
89443
89824
  }
89444
89825
  break;
89445
89826
  }
89446
- const parent = dirname30(dir);
89827
+ const parent = dirname31(dir);
89447
89828
  if (parent === dir)
89448
89829
  break;
89449
89830
  dir = parent;
@@ -89456,13 +89837,13 @@ function defaultStatusProbe(composePath) {
89456
89837
  warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
89457
89838
  }
89458
89839
  const services = [];
89459
- if (!existsSync79(composePath)) {
89840
+ if (!existsSync80(composePath)) {
89460
89841
  warnings.push(`compose file not found at ${composePath}; service status unknown`);
89461
89842
  return { cliVersion, cliBuiltAt, services, warnings };
89462
89843
  }
89463
89844
  let serviceList = [];
89464
89845
  try {
89465
- const r = spawnSync13("docker", ["compose", "-p", "switchroom", "-f", composePath, "config", "--services"], { encoding: "utf-8", timeout: 1e4 });
89846
+ const r = spawnSync14("docker", ["compose", "-p", "switchroom", "-f", composePath, "config", "--services"], { encoding: "utf-8", timeout: 1e4 });
89466
89847
  if (r.status !== 0) {
89467
89848
  warnings.push(`docker compose config --services failed: ${r.stderr?.trim() ?? r.error?.message ?? "unknown"}`);
89468
89849
  return { cliVersion, cliBuiltAt, services, warnings };
@@ -89479,7 +89860,7 @@ function defaultStatusProbe(composePath) {
89479
89860
  let containerCreatedAt = null;
89480
89861
  let status = "<unknown>";
89481
89862
  try {
89482
- const r = spawnSync13("docker", ["inspect", "-f", "{{.Config.Image}}|{{.Created}}|{{.State.Status}}", containerName2], { encoding: "utf-8", timeout: 5000 });
89863
+ const r = spawnSync14("docker", ["inspect", "-f", "{{.Config.Image}}|{{.Created}}|{{.State.Status}}", containerName2], { encoding: "utf-8", timeout: 5000 });
89483
89864
  if (r.status === 0) {
89484
89865
  const [img, created, st] = r.stdout.trim().split("|");
89485
89866
  image = img ?? null;
@@ -89495,7 +89876,7 @@ function defaultStatusProbe(composePath) {
89495
89876
  let imagePulledAt = null;
89496
89877
  if (image) {
89497
89878
  try {
89498
- const r = spawnSync13("docker", ["image", "inspect", "-f", "{{.Id}}|{{.Created}}|{{.Metadata.LastTagTime}}", image], { encoding: "utf-8", timeout: 5000 });
89879
+ const r = spawnSync14("docker", ["image", "inspect", "-f", "{{.Id}}|{{.Created}}|{{.Metadata.LastTagTime}}", image], { encoding: "utf-8", timeout: 5000 });
89499
89880
  if (r.status === 0) {
89500
89881
  const [id, created, lastTag] = r.stdout.trim().split("|");
89501
89882
  imageDigestShort = id?.replace(/^sha256:/, "").slice(0, 12) ?? null;
@@ -89651,14 +90032,14 @@ function registerUpdateCommand(program3) {
89651
90032
 
89652
90033
  // src/cli/rollout.ts
89653
90034
  init_helpers();
89654
- import { spawnSync as spawnSync15 } from "node:child_process";
89655
- import { readFileSync as readFileSync72, chownSync as chownSync9, statSync as statSync47 } from "node:fs";
90035
+ import { spawnSync as spawnSync16 } from "node:child_process";
90036
+ import { readFileSync as readFileSync73, chownSync as chownSync9, statSync as statSync47 } from "node:fs";
89656
90037
  import { homedir as homedir45 } from "node:os";
89657
90038
 
89658
90039
  // src/cli/rollout-pin-journal.ts
89659
90040
  import {
89660
- existsSync as existsSync80,
89661
- readFileSync as readFileSync71,
90041
+ existsSync as existsSync81,
90042
+ readFileSync as readFileSync72,
89662
90043
  writeFileSync as writeFileSync30,
89663
90044
  renameSync as renameSync19,
89664
90045
  unlinkSync as unlinkSync14,
@@ -89667,7 +90048,7 @@ import {
89667
90048
  } from "node:fs";
89668
90049
  import { homedir as homedir44 } from "node:os";
89669
90050
  import { createHash as createHash16 } from "node:crypto";
89670
- import { join as join81, basename as basename9, resolve as resolve46, dirname as dirname31 } from "node:path";
90051
+ import { join as join82, basename as basename9, resolve as resolve46, dirname as dirname32 } from "node:path";
89671
90052
  init_flock();
89672
90053
  var PIN_JOURNAL_MAX_AGE_MS = 15 * 60 * 1000;
89673
90054
  var STATE_DIR_NAME = ".switchroom";
@@ -89676,16 +90057,16 @@ function pinJournalDir(configPath) {
89676
90057
  if (override && override.trim().length > 0)
89677
90058
  return override.trim();
89678
90059
  if (configPath) {
89679
- const dir = dirname31(resolve46(configPath));
90060
+ const dir = dirname32(resolve46(configPath));
89680
90061
  if (basename9(dir) === STATE_DIR_NAME)
89681
90062
  return dir;
89682
90063
  }
89683
- return join81(homedir44(), STATE_DIR_NAME);
90064
+ return join82(homedir44(), STATE_DIR_NAME);
89684
90065
  }
89685
90066
  function pinJournalPath(configPath) {
89686
90067
  const abs = resolve46(configPath);
89687
90068
  const key = createHash16("sha256").update(abs).digest("hex").slice(0, 12);
89688
- return join81(pinJournalDir(abs), `.rollout-pin-journal.${basename9(abs)}.${key}.json`);
90069
+ return join82(pinJournalDir(abs), `.rollout-pin-journal.${basename9(abs)}.${key}.json`);
89689
90070
  }
89690
90071
  function isPidAlive(pid) {
89691
90072
  if (!Number.isInteger(pid) || pid <= 0)
@@ -89719,9 +90100,9 @@ function readPinJournal(configPath, warn = (m) => process.stderr.write(m)) {
89719
90100
  const p = pinJournalPath(configPath);
89720
90101
  let raw;
89721
90102
  try {
89722
- raw = readFileSync71(p, "utf8");
90103
+ raw = readFileSync72(p, "utf8");
89723
90104
  } catch (e) {
89724
- if (existsSync80(p)) {
90105
+ if (existsSync81(p)) {
89725
90106
  warn(`\u26a0\ufe0f rollout pin journal: ${p} exists but could not be read ` + `(${e.message}). A provisional \`release.pin\` may be ` + `uncommitted \u2014 verify it host-side before the next reconcile.
89726
90107
  `);
89727
90108
  }
@@ -89772,7 +90153,7 @@ function beginPinPersist(configPath, pin, opts = {}) {
89772
90153
  warn(`\u26a0\ufe0f rollout pin journal: overwriting an ABANDONED journal at ${p} ` + `(pid ${existing.pid} recorded ${existing.at}, provisional pin ` + `${existing.pin}). Its roll never committed or reverted; \`release.pin\` ` + `in ${configPath} may still name that unproven build \u2014 verify it.
89773
90154
  `);
89774
90155
  }
89775
- const priorPin = getReleasePinFromConfig(readFileSync71(configPath, "utf8"));
90156
+ const priorPin = getReleasePinFromConfig(readFileSync72(configPath, "utf8"));
89776
90157
  const journal = {
89777
90158
  v: 1,
89778
90159
  configPath,
@@ -89781,7 +90162,7 @@ function beginPinPersist(configPath, pin, opts = {}) {
89781
90162
  pid: process.pid,
89782
90163
  at: new Date().toISOString()
89783
90164
  };
89784
- mkdirSync44(dirname31(p), { recursive: true });
90165
+ mkdirSync44(dirname32(p), { recursive: true });
89785
90166
  const tmp = `${p}.${process.pid}.tmp`;
89786
90167
  writeFileSync30(tmp, JSON.stringify(journal), { encoding: "utf8", mode: 384 });
89787
90168
  renameSync19(tmp, p);
@@ -89793,7 +90174,7 @@ function commitPinPersist(configPath) {
89793
90174
  unlinkSync14(p);
89794
90175
  return null;
89795
90176
  } catch (e) {
89796
- if (!existsSync80(p))
90177
+ if (!existsSync81(p))
89797
90178
  return null;
89798
90179
  return `rollout pin journal: FAILED to clear ${p} after a SUCCESSFUL roll ` + `(${e.message}). Delete it host-side \u2014 while it exists, ` + `recovery may revert a proven \`release.pin\`.`;
89799
90180
  }
@@ -89815,7 +90196,7 @@ function rollbackPinPersist(configPath, opts = {}) {
89815
90196
  }
89816
90197
  }
89817
90198
  try {
89818
- const current = readFileSync71(configPath, "utf8");
90199
+ const current = readFileSync72(configPath, "utf8");
89819
90200
  const next = journal.priorPin ? setReleasePinInConfig(current, journal.priorPin) : deleteReleasePinInConfig(current);
89820
90201
  let mode = 384;
89821
90202
  try {
@@ -89871,10 +90252,10 @@ init_audit_reader();
89871
90252
  init_hindsight();
89872
90253
 
89873
90254
  // src/cli/deploy-version-guard.ts
89874
- import { spawnSync as spawnSync14 } from "node:child_process";
90255
+ import { spawnSync as spawnSync15 } from "node:child_process";
89875
90256
  var DOCKER_INSPECT_TIMEOUT_MS = 60 * 1000;
89876
90257
  var defaultRunner3 = (args) => {
89877
- const r = spawnSync14("docker", args, {
90258
+ const r = spawnSync15("docker", args, {
89878
90259
  encoding: "utf8",
89879
90260
  timeout: DOCKER_INSPECT_TIMEOUT_MS,
89880
90261
  killSignal: "SIGKILL"
@@ -90248,7 +90629,7 @@ function isSpawnTimeout(r, killSignal) {
90248
90629
  var ROLLOUT_KILL_SIGNAL = "SIGKILL";
90249
90630
  function createRolloutDeps(params) {
90250
90631
  const { configPath, scriptPath, hostdCtx } = params;
90251
- const spawn6 = params.spawn ?? spawnSync15;
90632
+ const spawn6 = params.spawn ?? spawnSync16;
90252
90633
  const warn = params.warn ?? ((line) => process.stderr.write(line));
90253
90634
  const dockerRun = (args) => {
90254
90635
  let r;
@@ -90323,7 +90704,7 @@ function createRolloutDeps(params) {
90323
90704
  return r.ok ? r.stdout : null;
90324
90705
  }),
90325
90706
  persistPin: (pin) => {
90326
- const before = readFileSync72(configPath, "utf8");
90707
+ const before = readFileSync73(configPath, "utf8");
90327
90708
  const after = setReleasePinInConfig(before, pin);
90328
90709
  if (after === before)
90329
90710
  return false;
@@ -90502,8 +90883,8 @@ init_helpers();
90502
90883
  init_lifecycle();
90503
90884
  init_resolve_version();
90504
90885
  import { execSync as execSync3 } from "node:child_process";
90505
- import { existsSync as existsSync81, readFileSync as readFileSync73 } from "node:fs";
90506
- import { dirname as dirname32, join as join82 } from "node:path";
90886
+ import { existsSync as existsSync82, readFileSync as readFileSync74 } from "node:fs";
90887
+ import { dirname as dirname33, join as join83 } from "node:path";
90507
90888
  function getClaudeCodeVersion() {
90508
90889
  try {
90509
90890
  const out = execSync3("claude --version 2>/dev/null", {
@@ -90553,16 +90934,16 @@ function formatUptime3(timestamp) {
90553
90934
  function locateSwitchroomInstallDir() {
90554
90935
  let dir = import.meta.dirname;
90555
90936
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
90556
- const pkgPath = join82(dir, "package.json");
90557
- if (existsSync81(pkgPath)) {
90937
+ const pkgPath = join83(dir, "package.json");
90938
+ if (existsSync82(pkgPath)) {
90558
90939
  try {
90559
- const pkg = JSON.parse(readFileSync73(pkgPath, "utf-8"));
90560
- if (pkg.name === "switchroom" && existsSync81(join82(dir, ".git"))) {
90940
+ const pkg = JSON.parse(readFileSync74(pkgPath, "utf-8"));
90941
+ if (pkg.name === "switchroom" && existsSync82(join83(dir, ".git"))) {
90561
90942
  return dir;
90562
90943
  }
90563
90944
  } catch {}
90564
90945
  }
90565
- dir = dirname32(dir);
90946
+ dir = dirname33(dir);
90566
90947
  }
90567
90948
  return null;
90568
90949
  }
@@ -90735,18 +91116,18 @@ import { resolve as resolve48 } from "node:path";
90735
91116
 
90736
91117
  // src/agents/session-retention.ts
90737
91118
  import {
90738
- existsSync as existsSync82,
91119
+ existsSync as existsSync83,
90739
91120
  readdirSync as readdirSync28,
90740
91121
  statSync as statSync48,
90741
91122
  unlinkSync as unlinkSync15
90742
91123
  } from "node:fs";
90743
- import { join as join83 } from "node:path";
91124
+ import { join as join84 } from "node:path";
90744
91125
  var DEFAULT_SESSION_RETENTION_MAX_COUNT = 20;
90745
91126
  var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
90746
91127
  var MIN_KEEP = 2;
90747
91128
  function collectSessionJsonl(claudeConfigDir) {
90748
- const projects = join83(claudeConfigDir, "projects");
90749
- if (!existsSync82(projects))
91129
+ const projects = join84(claudeConfigDir, "projects");
91130
+ if (!existsSync83(projects))
90750
91131
  return [];
90751
91132
  const found = [];
90752
91133
  const walk2 = (dir) => {
@@ -90757,7 +91138,7 @@ function collectSessionJsonl(claudeConfigDir) {
90757
91138
  return;
90758
91139
  }
90759
91140
  for (const name of entries) {
90760
- const full = join83(dir, name);
91141
+ const full = join84(dir, name);
90761
91142
  let st;
90762
91143
  try {
90763
91144
  st = statSync48(full);
@@ -90877,18 +91258,18 @@ function registerHandoffCommand(program3) {
90877
91258
  // src/issues/store.ts
90878
91259
  import {
90879
91260
  closeSync as closeSync15,
90880
- existsSync as existsSync83,
91261
+ existsSync as existsSync84,
90881
91262
  mkdirSync as mkdirSync45,
90882
91263
  openSync as openSync15,
90883
91264
  readdirSync as readdirSync29,
90884
- readFileSync as readFileSync74,
91265
+ readFileSync as readFileSync75,
90885
91266
  renameSync as renameSync20,
90886
91267
  statSync as statSync49,
90887
91268
  unlinkSync as unlinkSync16,
90888
91269
  writeFileSync as writeFileSync31,
90889
91270
  writeSync as writeSync9
90890
91271
  } from "node:fs";
90891
- import { join as join84 } from "node:path";
91272
+ import { join as join85 } from "node:path";
90892
91273
  import { randomBytes as randomBytes13 } from "node:crypto";
90893
91274
  import { execSync as execSync4 } from "node:child_process";
90894
91275
 
@@ -91319,12 +91700,12 @@ function redactedMarker(ruleId) {
91319
91700
  var ISSUES_FILE = "issues.jsonl";
91320
91701
  var ISSUES_LOCK = "issues.lock";
91321
91702
  function readAll(stateDir) {
91322
- const path7 = join84(stateDir, ISSUES_FILE);
91323
- if (!existsSync83(path7))
91703
+ const path7 = join85(stateDir, ISSUES_FILE);
91704
+ if (!existsSync84(path7))
91324
91705
  return [];
91325
91706
  let raw;
91326
91707
  try {
91327
- raw = readFileSync74(path7, "utf-8");
91708
+ raw = readFileSync75(path7, "utf-8");
91328
91709
  } catch {
91329
91710
  return [];
91330
91711
  }
@@ -91397,7 +91778,7 @@ function record(stateDir, input, nowFn = Date.now) {
91397
91778
  });
91398
91779
  }
91399
91780
  function resolve49(stateDir, fingerprint, nowFn = Date.now) {
91400
- if (!existsSync83(join84(stateDir, ISSUES_FILE)))
91781
+ if (!existsSync84(join85(stateDir, ISSUES_FILE)))
91401
91782
  return 0;
91402
91783
  return withLock(stateDir, () => {
91403
91784
  const all = readAll(stateDir);
@@ -91415,7 +91796,7 @@ function resolve49(stateDir, fingerprint, nowFn = Date.now) {
91415
91796
  });
91416
91797
  }
91417
91798
  function resolveAllBySource(stateDir, source, nowFn = Date.now) {
91418
- if (!existsSync83(join84(stateDir, ISSUES_FILE)))
91799
+ if (!existsSync84(join85(stateDir, ISSUES_FILE)))
91419
91800
  return 0;
91420
91801
  return withLock(stateDir, () => {
91421
91802
  const all = readAll(stateDir);
@@ -91433,7 +91814,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
91433
91814
  });
91434
91815
  }
91435
91816
  function prune(stateDir, opts = {}) {
91436
- if (!existsSync83(join84(stateDir, ISSUES_FILE)))
91817
+ if (!existsSync84(join85(stateDir, ISSUES_FILE)))
91437
91818
  return 0;
91438
91819
  return withLock(stateDir, () => {
91439
91820
  const all = readAll(stateDir);
@@ -91466,7 +91847,7 @@ function ensureDir(stateDir) {
91466
91847
  mkdirSync45(stateDir, { recursive: true });
91467
91848
  }
91468
91849
  function writeAll(stateDir, events) {
91469
- const path7 = join84(stateDir, ISSUES_FILE);
91850
+ const path7 = join85(stateDir, ISSUES_FILE);
91470
91851
  sweepOrphanTmpFiles(stateDir);
91471
91852
  const tmp = `${path7}.tmp-${process.pid}-${randomBytes13(4).toString("hex")}`;
91472
91853
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
@@ -91488,7 +91869,7 @@ function sweepOrphanTmpFiles(stateDir) {
91488
91869
  for (const entry of entries) {
91489
91870
  if (!entry.startsWith(TMP_PREFIX))
91490
91871
  continue;
91491
- const tmpPath = join84(stateDir, entry);
91872
+ const tmpPath = join85(stateDir, entry);
91492
91873
  try {
91493
91874
  const stat = statSync49(tmpPath);
91494
91875
  if (stat.mtimeMs < cutoff) {
@@ -91500,7 +91881,7 @@ function sweepOrphanTmpFiles(stateDir) {
91500
91881
  var LOCK_RETRY_MS = 25;
91501
91882
  var LOCK_TIMEOUT_MS = 1e4;
91502
91883
  function withLock(stateDir, fn) {
91503
- const lockPath = join84(stateDir, ISSUES_LOCK);
91884
+ const lockPath = join85(stateDir, ISSUES_LOCK);
91504
91885
  const startedAt = Date.now();
91505
91886
  let fd = null;
91506
91887
  while (fd === null) {
@@ -91535,7 +91916,7 @@ function withLock(stateDir, fn) {
91535
91916
  function tryStealStaleLock(lockPath) {
91536
91917
  let pidStr;
91537
91918
  try {
91538
- pidStr = readFileSync74(lockPath, "utf-8").trim();
91919
+ pidStr = readFileSync75(lockPath, "utf-8").trim();
91539
91920
  } catch {
91540
91921
  return true;
91541
91922
  }
@@ -91783,20 +92164,20 @@ function relTime(deltaMs) {
91783
92164
 
91784
92165
  // src/cli/deps.ts
91785
92166
  init_source();
91786
- import { existsSync as existsSync86 } from "node:fs";
92167
+ import { existsSync as existsSync87 } from "node:fs";
91787
92168
  import { homedir as homedir48 } from "node:os";
91788
- import { join as join87, resolve as resolve50 } from "node:path";
92169
+ import { join as join88, resolve as resolve50 } from "node:path";
91789
92170
 
91790
92171
  // src/deps/python.ts
91791
92172
  import { createHash as createHash17 } from "node:crypto";
91792
92173
  import {
91793
- existsSync as existsSync84,
92174
+ existsSync as existsSync85,
91794
92175
  mkdirSync as mkdirSync46,
91795
- readFileSync as readFileSync75,
92176
+ readFileSync as readFileSync76,
91796
92177
  rmSync as rmSync14,
91797
92178
  writeFileSync as writeFileSync32
91798
92179
  } from "node:fs";
91799
- import { dirname as dirname33, join as join85 } from "node:path";
92180
+ import { dirname as dirname34, join as join86 } from "node:path";
91800
92181
  import { homedir as homedir46 } from "node:os";
91801
92182
  import { execFileSync as execFileSync24 } from "node:child_process";
91802
92183
 
@@ -91809,26 +92190,26 @@ class PythonEnvError extends Error {
91809
92190
  }
91810
92191
  }
91811
92192
  function defaultPythonCacheRoot() {
91812
- return join85(homedir46(), ".switchroom", "deps", "python");
92193
+ return join86(homedir46(), ".switchroom", "deps", "python");
91813
92194
  }
91814
92195
  function hashFile(path7) {
91815
- return createHash17("sha256").update(readFileSync75(path7)).digest("hex");
92196
+ return createHash17("sha256").update(readFileSync76(path7)).digest("hex");
91816
92197
  }
91817
92198
  function ensurePythonEnv(opts) {
91818
92199
  const { skillName, requirementsPath, force = false } = opts;
91819
92200
  const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
91820
92201
  const hostPython = opts.pythonBin ?? "python3";
91821
- if (!existsSync84(requirementsPath)) {
92202
+ if (!existsSync85(requirementsPath)) {
91822
92203
  throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
91823
92204
  }
91824
- const venvDir = join85(cacheRoot, skillName);
91825
- const stampPath = join85(venvDir, ".requirements.sha256");
91826
- const binDir = join85(venvDir, "bin");
91827
- const pythonBin = join85(binDir, "python");
91828
- const pipBin = join85(binDir, "pip");
92205
+ const venvDir = join86(cacheRoot, skillName);
92206
+ const stampPath = join86(venvDir, ".requirements.sha256");
92207
+ const binDir = join86(venvDir, "bin");
92208
+ const pythonBin = join86(binDir, "python");
92209
+ const pipBin = join86(binDir, "pip");
91829
92210
  const targetHash = hashFile(requirementsPath);
91830
- if (!force && existsSync84(stampPath) && existsSync84(pythonBin)) {
91831
- const existingHash = readFileSync75(stampPath, "utf8").trim();
92211
+ if (!force && existsSync85(stampPath) && existsSync85(pythonBin)) {
92212
+ const existingHash = readFileSync76(stampPath, "utf8").trim();
91832
92213
  if (existingHash === targetHash) {
91833
92214
  return {
91834
92215
  skillName,
@@ -91840,10 +92221,10 @@ function ensurePythonEnv(opts) {
91840
92221
  };
91841
92222
  }
91842
92223
  }
91843
- if (existsSync84(venvDir)) {
92224
+ if (existsSync85(venvDir)) {
91844
92225
  rmSync14(venvDir, { recursive: true, force: true });
91845
92226
  }
91846
- mkdirSync46(dirname33(venvDir), { recursive: true });
92227
+ mkdirSync46(dirname34(venvDir), { recursive: true });
91847
92228
  try {
91848
92229
  execFileSync24(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
91849
92230
  } catch (err) {
@@ -91878,13 +92259,13 @@ function ensurePythonEnv(opts) {
91878
92259
  import { createHash as createHash18 } from "node:crypto";
91879
92260
  import {
91880
92261
  copyFileSync as copyFileSync11,
91881
- existsSync as existsSync85,
92262
+ existsSync as existsSync86,
91882
92263
  mkdirSync as mkdirSync47,
91883
- readFileSync as readFileSync76,
92264
+ readFileSync as readFileSync77,
91884
92265
  rmSync as rmSync15,
91885
92266
  writeFileSync as writeFileSync33
91886
92267
  } from "node:fs";
91887
- import { dirname as dirname34, join as join86 } from "node:path";
92268
+ import { dirname as dirname35, join as join87 } from "node:path";
91888
92269
  import { homedir as homedir47 } from "node:os";
91889
92270
  import { execFileSync as execFileSync25 } from "node:child_process";
91890
92271
 
@@ -91908,23 +92289,23 @@ var LOCKFILES_FOR = {
91908
92289
  npm: ["package-lock.json"]
91909
92290
  };
91910
92291
  function defaultNodeCacheRoot() {
91911
- return join86(homedir47(), ".switchroom", "deps", "node");
92292
+ return join87(homedir47(), ".switchroom", "deps", "node");
91912
92293
  }
91913
92294
  function hashDepInputs(packageJsonPath) {
91914
- const sourceDir = dirname34(packageJsonPath);
92295
+ const sourceDir = dirname35(packageJsonPath);
91915
92296
  const hasher = createHash18("sha256");
91916
92297
  hasher.update(`package.json
91917
92298
  `);
91918
- hasher.update(readFileSync76(packageJsonPath));
92299
+ hasher.update(readFileSync77(packageJsonPath));
91919
92300
  for (const lockName of ALL_LOCKFILES) {
91920
- const lockPath = join86(sourceDir, lockName);
91921
- if (existsSync85(lockPath)) {
92301
+ const lockPath = join87(sourceDir, lockName);
92302
+ if (existsSync86(lockPath)) {
91922
92303
  hasher.update(`
91923
92304
  `);
91924
92305
  hasher.update(lockName);
91925
92306
  hasher.update(`
91926
92307
  `);
91927
- hasher.update(readFileSync76(lockPath));
92308
+ hasher.update(readFileSync77(lockPath));
91928
92309
  }
91929
92310
  }
91930
92311
  return hasher.digest("hex");
@@ -91933,17 +92314,17 @@ function ensureNodeEnv(opts) {
91933
92314
  const { skillName, packageJsonPath, force = false } = opts;
91934
92315
  const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
91935
92316
  const installer = opts.installer ?? "bun";
91936
- if (!existsSync85(packageJsonPath)) {
92317
+ if (!existsSync86(packageJsonPath)) {
91937
92318
  throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
91938
92319
  }
91939
- const sourceDir = dirname34(packageJsonPath);
91940
- const envDir = join86(cacheRoot, skillName);
91941
- const stampPath = join86(envDir, ".package.sha256");
91942
- const nodeModulesDir = join86(envDir, "node_modules");
91943
- const binDir = join86(nodeModulesDir, ".bin");
92320
+ const sourceDir = dirname35(packageJsonPath);
92321
+ const envDir = join87(cacheRoot, skillName);
92322
+ const stampPath = join87(envDir, ".package.sha256");
92323
+ const nodeModulesDir = join87(envDir, "node_modules");
92324
+ const binDir = join87(nodeModulesDir, ".bin");
91944
92325
  const targetHash = hashDepInputs(packageJsonPath);
91945
- if (!force && existsSync85(stampPath) && existsSync85(nodeModulesDir)) {
91946
- const existingHash = readFileSync76(stampPath, "utf8").trim();
92326
+ if (!force && existsSync86(stampPath) && existsSync86(nodeModulesDir)) {
92327
+ const existingHash = readFileSync77(stampPath, "utf8").trim();
91947
92328
  if (existingHash === targetHash) {
91948
92329
  return {
91949
92330
  skillName,
@@ -91954,16 +92335,16 @@ function ensureNodeEnv(opts) {
91954
92335
  };
91955
92336
  }
91956
92337
  }
91957
- if (existsSync85(envDir)) {
92338
+ if (existsSync86(envDir)) {
91958
92339
  rmSync15(envDir, { recursive: true, force: true });
91959
92340
  }
91960
92341
  mkdirSync47(envDir, { recursive: true });
91961
- copyFileSync11(packageJsonPath, join86(envDir, "package.json"));
92342
+ copyFileSync11(packageJsonPath, join87(envDir, "package.json"));
91962
92343
  let copiedLockfile = false;
91963
92344
  for (const lockName of LOCKFILES_FOR[installer]) {
91964
- const lockPath = join86(sourceDir, lockName);
91965
- if (existsSync85(lockPath)) {
91966
- copyFileSync11(lockPath, join86(envDir, lockName));
92345
+ const lockPath = join87(sourceDir, lockName);
92346
+ if (existsSync86(lockPath)) {
92347
+ copyFileSync11(lockPath, join87(envDir, lockName));
91967
92348
  copiedLockfile = true;
91968
92349
  }
91969
92350
  }
@@ -91998,22 +92379,22 @@ function registerDepsCommand(program3) {
91998
92379
  const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
91999
92380
  deps.command("rebuild <skill>").description("Rebuild the Python venv and/or Node node_modules cache for a skill").option("-p, --python", "Rebuild only the Python env").option("-n, --node", "Rebuild only the Node env").action(async (skill, opts) => {
92000
92381
  const skillsRoot = builtinSkillsRoot();
92001
- if (!existsSync86(skillsRoot)) {
92382
+ if (!existsSync87(skillsRoot)) {
92002
92383
  console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
92003
92384
  process.exit(1);
92004
92385
  }
92005
- const skillDir = join87(skillsRoot, skill);
92006
- if (!existsSync86(skillDir)) {
92386
+ const skillDir = join88(skillsRoot, skill);
92387
+ if (!existsSync87(skillDir)) {
92007
92388
  console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
92008
92389
  process.exit(1);
92009
92390
  }
92010
- const requirementsPath = join87(skillDir, "requirements.txt");
92011
- const packageJsonPath = join87(skillDir, "package.json");
92012
- const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync86(requirementsPath));
92013
- const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync86(packageJsonPath));
92391
+ const requirementsPath = join88(skillDir, "requirements.txt");
92392
+ const packageJsonPath = join88(skillDir, "package.json");
92393
+ const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync87(requirementsPath));
92394
+ const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync87(packageJsonPath));
92014
92395
  let did = 0;
92015
92396
  if (wantPython) {
92016
- if (!existsSync86(requirementsPath)) {
92397
+ if (!existsSync87(requirementsPath)) {
92017
92398
  console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
92018
92399
  process.exit(1);
92019
92400
  }
@@ -92037,7 +92418,7 @@ function registerDepsCommand(program3) {
92037
92418
  }
92038
92419
  }
92039
92420
  if (wantNode) {
92040
- if (!existsSync86(packageJsonPath)) {
92421
+ if (!existsSync87(packageJsonPath)) {
92041
92422
  console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
92042
92423
  process.exit(1);
92043
92424
  }
@@ -92070,9 +92451,9 @@ function registerDepsCommand(program3) {
92070
92451
  // src/cli/workspace.ts
92071
92452
  init_helpers();
92072
92453
  init_loader();
92073
- import { existsSync as existsSync87 } from "node:fs";
92454
+ import { existsSync as existsSync88 } from "node:fs";
92074
92455
  import { resolve as resolve51, sep as sep4 } from "node:path";
92075
- import { spawnSync as spawnSync16 } from "node:child_process";
92456
+ import { spawnSync as spawnSync17 } from "node:child_process";
92076
92457
 
92077
92458
  // src/agents/workspace.ts
92078
92459
  import { readFile as readFile2, stat } from "node:fs/promises";
@@ -92785,7 +93166,7 @@ function registerWorkspaceCommand(program3) {
92785
93166
  process.exit(1);
92786
93167
  }
92787
93168
  const editor = process.env["EDITOR"] ?? process.env["VISUAL"] ?? "vi";
92788
- const child = spawnSync16(editor, [target], { stdio: "inherit" });
93169
+ const child = spawnSync17(editor, [target], { stdio: "inherit" });
92789
93170
  if (child.status !== 0 && child.status !== null) {
92790
93171
  process.exit(child.status);
92791
93172
  }
@@ -92847,12 +93228,12 @@ function registerWorkspaceCommand(program3) {
92847
93228
  if (!dir)
92848
93229
  return;
92849
93230
  const gitDir = resolve51(dir, ".git");
92850
- if (!existsSync87(gitDir)) {
93231
+ if (!existsSync88(gitDir)) {
92851
93232
  process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
92852
93233
  `);
92853
93234
  return;
92854
93235
  }
92855
- const statusResult = spawnSync16("git", ["status", "--short"], {
93236
+ const statusResult = spawnSync17("git", ["status", "--short"], {
92856
93237
  cwd: dir,
92857
93238
  encoding: "utf-8"
92858
93239
  });
@@ -92867,7 +93248,7 @@ function registerWorkspaceCommand(program3) {
92867
93248
  return;
92868
93249
  }
92869
93250
  const message = opts.message || `checkpoint: ${new Date().toISOString()}`;
92870
- const addResult = spawnSync16("git", ["add", "-A"], {
93251
+ const addResult = spawnSync17("git", ["add", "-A"], {
92871
93252
  cwd: dir,
92872
93253
  encoding: "utf-8"
92873
93254
  });
@@ -92876,7 +93257,7 @@ function registerWorkspaceCommand(program3) {
92876
93257
  `);
92877
93258
  process.exit(1);
92878
93259
  }
92879
- const commitResult = spawnSync16("git", ["commit", "-m", message], {
93260
+ const commitResult = spawnSync17("git", ["commit", "-m", message], {
92880
93261
  cwd: dir,
92881
93262
  encoding: "utf-8"
92882
93263
  });
@@ -92885,7 +93266,7 @@ function registerWorkspaceCommand(program3) {
92885
93266
  `);
92886
93267
  process.exit(1);
92887
93268
  }
92888
- const shaResult = spawnSync16("git", ["rev-parse", "--short", "HEAD"], {
93269
+ const shaResult = spawnSync17("git", ["rev-parse", "--short", "HEAD"], {
92889
93270
  cwd: dir,
92890
93271
  encoding: "utf-8"
92891
93272
  });
@@ -92901,12 +93282,12 @@ function registerWorkspaceCommand(program3) {
92901
93282
  if (!dir)
92902
93283
  return;
92903
93284
  const gitDir = resolve51(dir, ".git");
92904
- if (!existsSync87(gitDir)) {
93285
+ if (!existsSync88(gitDir)) {
92905
93286
  process.stdout.write(`Workspace is not a git repository.
92906
93287
  `);
92907
93288
  return;
92908
93289
  }
92909
- const child = spawnSync16("git", ["status", "--short"], {
93290
+ const child = spawnSync17("git", ["status", "--short"], {
92910
93291
  cwd: dir,
92911
93292
  stdio: "inherit"
92912
93293
  });
@@ -92926,7 +93307,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
92926
93307
  const agentsDir = resolveAgentsDir(config);
92927
93308
  const agentDir = resolve51(agentsDir, agentName);
92928
93309
  const dir = resolveAgentWorkspaceDir(agentDir);
92929
- if (!existsSync87(dir)) {
93310
+ if (!existsSync88(dir)) {
92930
93311
  process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
92931
93312
  `);
92932
93313
  return;
@@ -92962,8 +93343,8 @@ function safeParseInt(value, fallback) {
92962
93343
  init_helpers();
92963
93344
  init_loader();
92964
93345
  init_merge();
92965
- import { copyFileSync as copyFileSync12, existsSync as existsSync88, readFileSync as readFileSync77, writeFileSync as writeFileSync34 } from "node:fs";
92966
- import { join as join88, resolve as resolve52 } from "node:path";
93346
+ import { copyFileSync as copyFileSync12, existsSync as existsSync89, readFileSync as readFileSync78, writeFileSync as writeFileSync34 } from "node:fs";
93347
+ import { join as join89, resolve as resolve52 } from "node:path";
92967
93348
  init_scaffold();
92968
93349
  init_profiles();
92969
93350
  init_schema();
@@ -92980,7 +93361,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
92980
93361
  const agentsDir = resolveAgentsDir(config);
92981
93362
  const agentDir = resolve52(agentsDir, agentName);
92982
93363
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
92983
- if (!existsSync88(workspaceDir)) {
93364
+ if (!existsSync89(workspaceDir)) {
92984
93365
  console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
92985
93366
  process.exit(1);
92986
93367
  }
@@ -92989,7 +93370,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
92989
93370
  profileName,
92990
93371
  profilePath,
92991
93372
  workspaceDir,
92992
- soulPath: join88(workspaceDir, "SOUL.md"),
93373
+ soulPath: join89(workspaceDir, "SOUL.md"),
92993
93374
  soul: merged.soul
92994
93375
  };
92995
93376
  }
@@ -93006,11 +93387,11 @@ function registerSoulCommand(program3) {
93006
93387
  const t = resolveSoulTargetOrExit(program3, agentName);
93007
93388
  if (!t)
93008
93389
  return;
93009
- if (!existsSync88(t.soulPath)) {
93390
+ if (!existsSync89(t.soulPath)) {
93010
93391
  console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
93011
93392
  process.exit(1);
93012
93393
  }
93013
- process.stdout.write(readFileSync77(t.soulPath, "utf-8"));
93394
+ process.stdout.write(readFileSync78(t.soulPath, "utf-8"));
93014
93395
  }));
93015
93396
  cmd.command("reset <agent>").description("Re-seed SOUL.md from the agent's current profile " + "(backs the existing file up to SOUL.md.bak first)").option("-y, --yes", "Skip the confirmation prompt").action(withConfigError(async (agentName, opts) => {
93016
93397
  const t = resolveSoulTargetOrExit(program3, agentName);
@@ -93021,7 +93402,7 @@ function registerSoulCommand(program3) {
93021
93402
  console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
93022
93403
  process.exit(1);
93023
93404
  }
93024
- const exists = existsSync88(t.soulPath);
93405
+ const exists = existsSync89(t.soulPath);
93025
93406
  if (exists && !opts.yes) {
93026
93407
  if (!isInteractive()) {
93027
93408
  console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
@@ -93036,7 +93417,7 @@ function registerSoulCommand(program3) {
93036
93417
  let backupPath;
93037
93418
  if (exists) {
93038
93419
  backupPath = `${t.soulPath}.bak`;
93039
- if (existsSync88(backupPath)) {
93420
+ if (existsSync89(backupPath)) {
93040
93421
  backupPath = `${t.soulPath}.bak.${Date.now()}`;
93041
93422
  }
93042
93423
  copyFileSync12(t.soulPath, backupPath);
@@ -93055,8 +93436,8 @@ function registerSoulCommand(program3) {
93055
93436
  // src/cli/debug.ts
93056
93437
  init_helpers();
93057
93438
  init_loader();
93058
- import { existsSync as existsSync89, readFileSync as readFileSync78, readdirSync as readdirSync30, statSync as statSync50 } from "node:fs";
93059
- import { resolve as resolve53, join as join89 } from "node:path";
93439
+ import { existsSync as existsSync90, readFileSync as readFileSync79, readdirSync as readdirSync30, statSync as statSync50 } from "node:fs";
93440
+ import { resolve as resolve53, join as join90 } from "node:path";
93060
93441
  import { createHash as createHash19 } from "node:crypto";
93061
93442
  init_merge();
93062
93443
  init_hindsight2();
@@ -93067,11 +93448,11 @@ function estimateTokens(bytes) {
93067
93448
  return Math.round(bytes / 3.7);
93068
93449
  }
93069
93450
  function readMcpServerNames(agentDir) {
93070
- const mcpPath = join89(agentDir, ".mcp.json");
93071
- if (!existsSync89(mcpPath))
93451
+ const mcpPath = join90(agentDir, ".mcp.json");
93452
+ if (!existsSync90(mcpPath))
93072
93453
  return [];
93073
93454
  try {
93074
- const parsed = JSON.parse(readFileSync78(mcpPath, "utf-8"));
93455
+ const parsed = JSON.parse(readFileSync79(mcpPath, "utf-8"));
93075
93456
  return Object.keys(parsed.mcpServers ?? {});
93076
93457
  } catch {
93077
93458
  return null;
@@ -93081,8 +93462,8 @@ function sha256(content) {
93081
93462
  return createHash19("sha256").update(content).digest("hex").slice(0, 16);
93082
93463
  }
93083
93464
  function findLatestTranscriptJsonl(claudeConfigDir) {
93084
- const projectsDir = join89(claudeConfigDir, "projects");
93085
- if (!existsSync89(projectsDir))
93465
+ const projectsDir = join90(claudeConfigDir, "projects");
93466
+ if (!existsSync90(projectsDir))
93086
93467
  return;
93087
93468
  try {
93088
93469
  const entries = readdirSync30(projectsDir, { withFileTypes: true });
@@ -93090,9 +93471,9 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
93090
93471
  for (const entry of entries) {
93091
93472
  if (!entry.isDirectory())
93092
93473
  continue;
93093
- const projectPath = join89(projectsDir, entry.name);
93094
- const transcriptPath = join89(projectPath, "transcript.jsonl");
93095
- if (!existsSync89(transcriptPath))
93474
+ const projectPath = join90(projectsDir, entry.name);
93475
+ const transcriptPath = join90(projectPath, "transcript.jsonl");
93476
+ if (!existsSync90(transcriptPath))
93096
93477
  continue;
93097
93478
  const stat3 = statSync50(transcriptPath);
93098
93479
  if (!latest || stat3.mtimeMs > latest.mtime) {
@@ -93106,7 +93487,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
93106
93487
  }
93107
93488
  function extractLatestUserMessage(transcriptPath) {
93108
93489
  try {
93109
- const content = readFileSync78(transcriptPath, "utf-8");
93490
+ const content = readFileSync79(transcriptPath, "utf-8");
93110
93491
  const lines = content.trim().split(`
93111
93492
  `).filter(Boolean);
93112
93493
  for (let i = lines.length - 1;i >= 0; i--) {
@@ -93155,16 +93536,16 @@ function registerDebugCommand(program3) {
93155
93536
  }
93156
93537
  const agentsDir = resolveAgentsDir(config);
93157
93538
  const agentDir = resolve53(agentsDir, agentName);
93158
- if (!existsSync89(agentDir)) {
93539
+ if (!existsSync90(agentDir)) {
93159
93540
  console.error(`Agent directory not found: ${agentDir}`);
93160
93541
  process.exit(1);
93161
93542
  }
93162
93543
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
93163
- const claudeConfigDir = join89(agentDir, ".claude");
93164
- const claudeMdPath = join89(agentDir, "CLAUDE.md");
93165
- const soulMdPath = join89(agentDir, "SOUL.md");
93166
- const workspaceSoulMdPath = join89(workspaceDir, "SOUL.md");
93167
- const handoffPath = join89(agentDir, ".handoff.md");
93544
+ const claudeConfigDir = join90(agentDir, ".claude");
93545
+ const claudeMdPath = join90(agentDir, "CLAUDE.md");
93546
+ const soulMdPath = join90(agentDir, "SOUL.md");
93547
+ const workspaceSoulMdPath = join90(workspaceDir, "SOUL.md");
93548
+ const handoffPath = join90(agentDir, ".handoff.md");
93168
93549
  const lastN = parseInt(opts.last, 10);
93169
93550
  if (isNaN(lastN) || lastN < 1) {
93170
93551
  console.error("--last must be a positive integer");
@@ -93210,7 +93591,7 @@ function registerDebugCommand(program3) {
93210
93591
  }
93211
93592
  console.log(`=== Append System Prompt (per-session) ===
93212
93593
  `);
93213
- const handoffContent = existsSync89(handoffPath) ? readFileSync78(handoffPath, "utf-8") : "";
93594
+ const handoffContent = existsSync90(handoffPath) ? readFileSync79(handoffPath, "utf-8") : "";
93214
93595
  if (handoffContent.trim().length > 0) {
93215
93596
  console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
93216
93597
  console.log(handoffContent);
@@ -93221,7 +93602,7 @@ function registerDebugCommand(program3) {
93221
93602
  }
93222
93603
  console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
93223
93604
  `);
93224
- const claudeMdContent = existsSync89(claudeMdPath) ? readFileSync78(claudeMdPath, "utf-8") : "";
93605
+ const claudeMdContent = existsSync90(claudeMdPath) ? readFileSync79(claudeMdPath, "utf-8") : "";
93225
93606
  if (claudeMdContent.trim().length > 0) {
93226
93607
  console.log(`(${formatBytes(claudeMdContent.length)})`);
93227
93608
  console.log(claudeMdContent);
@@ -93232,7 +93613,7 @@ function registerDebugCommand(program3) {
93232
93613
  }
93233
93614
  console.log(`=== Persona (SOUL.md) ===
93234
93615
  `);
93235
- const soulMdContent = existsSync89(soulMdPath) ? readFileSync78(soulMdPath, "utf-8") : existsSync89(workspaceSoulMdPath) ? readFileSync78(workspaceSoulMdPath, "utf-8") : "";
93616
+ const soulMdContent = existsSync90(soulMdPath) ? readFileSync79(soulMdPath, "utf-8") : existsSync90(workspaceSoulMdPath) ? readFileSync79(workspaceSoulMdPath, "utf-8") : "";
93236
93617
  if (soulMdContent.trim().length > 0) {
93237
93618
  console.log(`(${formatBytes(soulMdContent.length)})`);
93238
93619
  console.log(soulMdContent);
@@ -93293,11 +93674,11 @@ function registerDebugCommand(program3) {
93293
93674
  const soulMdBytes = soulMdContent.length;
93294
93675
  const perTurnBytes = dynamicResult.concatenated.length;
93295
93676
  const userBytes = userMessage?.text.length ?? 0;
93296
- const fleetDir = join89(agentsDir, "..", "fleet");
93297
- const fleetInvPath = join89(fleetDir, "switchroom-invariants.md");
93298
- const fleetClaudePath = join89(fleetDir, "CLAUDE.md");
93299
- const fleetInvBytes = existsSync89(fleetInvPath) ? readFileSync78(fleetInvPath, "utf-8").length : 0;
93300
- const fleetClaudeBytes = existsSync89(fleetClaudePath) ? readFileSync78(fleetClaudePath, "utf-8").length : 0;
93677
+ const fleetDir = join90(agentsDir, "..", "fleet");
93678
+ const fleetInvPath = join90(fleetDir, "switchroom-invariants.md");
93679
+ const fleetClaudePath = join90(fleetDir, "CLAUDE.md");
93680
+ const fleetInvBytes = existsSync90(fleetInvPath) ? readFileSync79(fleetInvPath, "utf-8").length : 0;
93681
+ const fleetClaudeBytes = existsSync90(fleetClaudePath) ? readFileSync79(fleetClaudePath, "utf-8").length : 0;
93301
93682
  const fleetBytes = fleetInvBytes + fleetClaudeBytes;
93302
93683
  const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
93303
93684
  console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
@@ -93330,8 +93711,8 @@ init_source();
93330
93711
 
93331
93712
  // src/worktree/claim.ts
93332
93713
  import { execFileSync as execFileSync26 } from "node:child_process";
93333
- import { closeSync as closeSync16, mkdirSync as mkdirSync49, openSync as openSync16, existsSync as existsSync91, unlinkSync as unlinkSync18 } from "node:fs";
93334
- import { join as join91, resolve as resolve55 } from "node:path";
93714
+ import { closeSync as closeSync16, mkdirSync as mkdirSync49, openSync as openSync16, existsSync as existsSync92, unlinkSync as unlinkSync18 } from "node:fs";
93715
+ import { join as join92, resolve as resolve55 } from "node:path";
93335
93716
  import { homedir as homedir50 } from "node:os";
93336
93717
  import { randomBytes as randomBytes14 } from "node:crypto";
93337
93718
 
@@ -93339,19 +93720,19 @@ import { randomBytes as randomBytes14 } from "node:crypto";
93339
93720
  import {
93340
93721
  mkdirSync as mkdirSync48,
93341
93722
  writeFileSync as writeFileSync35,
93342
- readFileSync as readFileSync79,
93723
+ readFileSync as readFileSync80,
93343
93724
  readdirSync as readdirSync31,
93344
93725
  unlinkSync as unlinkSync17,
93345
- existsSync as existsSync90,
93726
+ existsSync as existsSync91,
93346
93727
  renameSync as renameSync21
93347
93728
  } from "node:fs";
93348
- import { join as join90, resolve as resolve54 } from "node:path";
93729
+ import { join as join91, resolve as resolve54 } from "node:path";
93349
93730
  import { homedir as homedir49 } from "node:os";
93350
93731
  function registryDir() {
93351
- return resolve54(process.env.SWITCHROOM_WORKTREE_DIR ?? join90(homedir49(), ".switchroom", "worktrees"));
93732
+ return resolve54(process.env.SWITCHROOM_WORKTREE_DIR ?? join91(homedir49(), ".switchroom", "worktrees"));
93352
93733
  }
93353
93734
  function recordPath(id) {
93354
- return join90(registryDir(), `${id}.json`);
93735
+ return join91(registryDir(), `${id}.json`);
93355
93736
  }
93356
93737
  function ensureDir2() {
93357
93738
  mkdirSync48(registryDir(), { recursive: true });
@@ -93367,7 +93748,7 @@ function writeRecord(record2) {
93367
93748
  function readRecord(id) {
93368
93749
  const path10 = recordPath(id);
93369
93750
  try {
93370
- const raw = readFileSync79(path10, "utf8");
93751
+ const raw = readFileSync80(path10, "utf8");
93371
93752
  return JSON.parse(raw);
93372
93753
  } catch {
93373
93754
  return null;
@@ -93402,7 +93783,7 @@ function acquireRepoLock(repoPath) {
93402
93783
  const lockDir = registryDir();
93403
93784
  mkdirSync49(lockDir, { recursive: true });
93404
93785
  const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
93405
- const lockPath = join91(lockDir, `.lock-${lockName}`);
93786
+ const lockPath = join92(lockDir, `.lock-${lockName}`);
93406
93787
  const deadline = Date.now() + 5000;
93407
93788
  let fd = null;
93408
93789
  while (fd === null) {
@@ -93429,7 +93810,7 @@ function acquireRepoLock(repoPath) {
93429
93810
  }
93430
93811
  var DEFAULT_CONCURRENCY = 5;
93431
93812
  function worktreesBaseDir() {
93432
- return resolve55(process.env.SWITCHROOM_WORKTREE_BASE ?? join91(homedir50(), ".switchroom", "worktree-checkouts"));
93813
+ return resolve55(process.env.SWITCHROOM_WORKTREE_BASE ?? join92(homedir50(), ".switchroom", "worktree-checkouts"));
93433
93814
  }
93434
93815
  function shortId() {
93435
93816
  return randomBytes14(4).toString("hex");
@@ -93451,12 +93832,12 @@ function resolveRepoPath(repo, codeRepos) {
93451
93832
  }
93452
93833
  function expandHome(p) {
93453
93834
  if (p.startsWith("~/"))
93454
- return join91(homedir50(), p.slice(2));
93835
+ return join92(homedir50(), p.slice(2));
93455
93836
  return p;
93456
93837
  }
93457
93838
  async function claimWorktree(input, codeRepos) {
93458
93839
  const repoPath = resolveRepoPath(input.repo, codeRepos);
93459
- if (!existsSync91(repoPath)) {
93840
+ if (!existsSync92(repoPath)) {
93460
93841
  throw new Error(`Repository path does not exist: ${repoPath}`);
93461
93842
  }
93462
93843
  let concurrencyCap = DEFAULT_CONCURRENCY;
@@ -93479,7 +93860,7 @@ async function claimWorktree(input, codeRepos) {
93479
93860
  branch = `task/${taskSuffix}-${id}`;
93480
93861
  const baseDir = worktreesBaseDir();
93481
93862
  mkdirSync49(baseDir, { recursive: true });
93482
- worktreePath = join91(baseDir, `${id}-${taskSuffix}`);
93863
+ worktreePath = join92(baseDir, `${id}-${taskSuffix}`);
93483
93864
  const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
93484
93865
  const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
93485
93866
  const now = new Date().toISOString();
@@ -93512,7 +93893,7 @@ async function claimWorktree(input, codeRepos) {
93512
93893
 
93513
93894
  // src/worktree/release.ts
93514
93895
  import { execFileSync as execFileSync27 } from "node:child_process";
93515
- import { existsSync as existsSync92 } from "node:fs";
93896
+ import { existsSync as existsSync93 } from "node:fs";
93516
93897
  function releaseWorktree(input) {
93517
93898
  const { id } = input;
93518
93899
  const record2 = readRecord(id);
@@ -93520,7 +93901,7 @@ function releaseWorktree(input) {
93520
93901
  return { released: true };
93521
93902
  }
93522
93903
  let gitSuccess = true;
93523
- if (existsSync92(record2.path)) {
93904
+ if (existsSync93(record2.path)) {
93524
93905
  try {
93525
93906
  execFileSync27("git", ["worktree", "remove", "--force", record2.path], {
93526
93907
  cwd: record2.repo,
@@ -93559,7 +93940,7 @@ function listWorktrees() {
93559
93940
 
93560
93941
  // src/worktree/reaper.ts
93561
93942
  import { execFileSync as execFileSync28 } from "node:child_process";
93562
- import { existsSync as existsSync93 } from "node:fs";
93943
+ import { existsSync as existsSync94 } from "node:fs";
93563
93944
  var STALE_THRESHOLD_MS = 10 * 60 * 1000;
93564
93945
  function reapSkipReasonText(action) {
93565
93946
  switch (action) {
@@ -93620,7 +94001,7 @@ function planReaper(nowMs, deps = {}) {
93620
94001
  const plan = [];
93621
94002
  for (const record2 of listRecords()) {
93622
94003
  const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
93623
- const worktreeExists = existsSync93(record2.path);
94004
+ const worktreeExists = existsSync94(record2.path);
93624
94005
  if (!worktreeExists) {
93625
94006
  plan.push({
93626
94007
  record: record2,
@@ -93701,8 +94082,8 @@ function runReaper(nowMs, deps = {}) {
93701
94082
  // src/worktree/gc.ts
93702
94083
  import { execFileSync as execFileSync29 } from "node:child_process";
93703
94084
  import {
93704
- existsSync as existsSync94,
93705
- readFileSync as readFileSync80,
94085
+ existsSync as existsSync95,
94086
+ readFileSync as readFileSync81,
93706
94087
  readdirSync as readdirSync32,
93707
94088
  statSync as statSync51,
93708
94089
  renameSync as renameSync22,
@@ -93710,7 +94091,7 @@ import {
93710
94091
  rmSync as rmSync16
93711
94092
  } from "node:fs";
93712
94093
  import { homedir as homedir51 } from "node:os";
93713
- import { join as join92, resolve as resolve56 } from "node:path";
94094
+ import { join as join93, resolve as resolve56 } from "node:path";
93714
94095
  function parseGitdirPointer(dotGitFileContents) {
93715
94096
  const m = /^gitdir:\s*(.+?)\s*$/m.exec(dotGitFileContents);
93716
94097
  return m ? m[1] : null;
@@ -93825,17 +94206,17 @@ function defaultPrSignal(repo, branch, exec) {
93825
94206
  }
93826
94207
  }
93827
94208
  function trashRoot() {
93828
- return resolve56(process.env.SWITCHROOM_WORKTREE_TRASH ?? join92(homedir51(), ".switchroom", "worktree-gc-trash"));
94209
+ return resolve56(process.env.SWITCHROOM_WORKTREE_TRASH ?? join93(homedir51(), ".switchroom", "worktree-gc-trash"));
93829
94210
  }
93830
94211
  function planGc(roots, deps = {}) {
93831
- const exists = deps.existsSync ?? existsSync94;
94212
+ const exists = deps.existsSync ?? existsSync95;
93832
94213
  const readDir = deps.readDir ?? ((p) => readdirSync32(p));
93833
- const readFile4 = deps.readFile ?? ((p) => readFileSync80(p, "utf8"));
94214
+ const readFile4 = deps.readFile ?? ((p) => readFileSync81(p, "utf8"));
93834
94215
  const stat3 = deps.stat ?? ((p) => statSync51(p));
93835
94216
  const exec = deps.exec ?? defaultExec2;
93836
94217
  const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
93837
94218
  const stamp = deps.dateStamp ?? "undated";
93838
- const trash = join92(trashRoot(), stamp);
94219
+ const trash = join93(trashRoot(), stamp);
93839
94220
  let claimed;
93840
94221
  try {
93841
94222
  claimed = new Set(listRecords().map((r) => resolve56(r.path)));
@@ -93871,10 +94252,10 @@ function planGc(roots, deps = {}) {
93871
94252
  continue;
93872
94253
  }
93873
94254
  for (const name of entries) {
93874
- const dir = join92(root, name);
94255
+ const dir = join93(root, name);
93875
94256
  if (isEphemeralPath(dir))
93876
94257
  continue;
93877
- const dotGit = join92(dir, ".git");
94258
+ const dotGit = join93(dir, ".git");
93878
94259
  if (!exists(dotGit))
93879
94260
  continue;
93880
94261
  let st;
@@ -93903,7 +94284,7 @@ function planGc(roots, deps = {}) {
93903
94284
  ownerRepos.add(repoRoot);
93904
94285
  if (exists(ptr))
93905
94286
  continue;
93906
- orphans.push({ dir, owner: repoRoot, dest: join92(trash, name) });
94287
+ orphans.push({ dir, owner: repoRoot, dest: join93(trash, name) });
93907
94288
  }
93908
94289
  }
93909
94290
  const registered = [];
@@ -94007,14 +94388,14 @@ function selectPurgeTargets(entries, olderThanDays) {
94007
94388
  return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
94008
94389
  }
94009
94390
  function listTrashEntries(nowMs, deps = {}) {
94010
- const exists = deps.existsSync ?? existsSync94;
94391
+ const exists = deps.existsSync ?? existsSync95;
94011
94392
  const readDir = deps.readDir ?? ((p) => readdirSync32(p));
94012
94393
  const root = trashRoot();
94013
94394
  if (!exists(root))
94014
94395
  return [];
94015
94396
  const out = [];
94016
94397
  for (const stamp of readDir(root)) {
94017
- const stampDir = join92(root, stamp);
94398
+ const stampDir = join93(root, stamp);
94018
94399
  let names;
94019
94400
  try {
94020
94401
  names = readDir(stampDir);
@@ -94022,7 +94403,7 @@ function listTrashEntries(nowMs, deps = {}) {
94022
94403
  continue;
94023
94404
  }
94024
94405
  for (const name of names) {
94025
- const p = join92(stampDir, name);
94406
+ const p = join93(stampDir, name);
94026
94407
  let mtimeMs = nowMs;
94027
94408
  try {
94028
94409
  mtimeMs = statSync51(p).mtimeMs;
@@ -94046,7 +94427,7 @@ function purgeTrash(paths) {
94046
94427
  return { deleted, errors: errors2 };
94047
94428
  }
94048
94429
  function defaultRoots() {
94049
- return [join92(homedir51(), "code")];
94430
+ return [join93(homedir51(), "code")];
94050
94431
  }
94051
94432
 
94052
94433
  // src/cli/worktree.ts
@@ -94274,7 +94655,7 @@ import {
94274
94655
  rmSync as rmSync17,
94275
94656
  writeFileSync as writeFileSync36
94276
94657
  } from "node:fs";
94277
- import { join as join93 } from "node:path";
94658
+ import { join as join94 } from "node:path";
94278
94659
  function encodeCredentialsFilename(email) {
94279
94660
  const SAFE = new Set([
94280
94661
  ..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
@@ -94464,16 +94845,16 @@ function resolveCredentialsDir(env2) {
94464
94845
  if (explicit && explicit.length > 0)
94465
94846
  return explicit;
94466
94847
  const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
94467
- return join93(stateBase, "google-workspace-mcp", "credentials");
94848
+ return join94(stateBase, "google-workspace-mcp", "credentials");
94468
94849
  }
94469
94850
  function writeSeedFile(dir, email, seed) {
94470
94851
  mkdirSync51(dir, { recursive: true, mode: 448 });
94471
94852
  chmodSync14(dir, 448);
94472
94853
  for (const name of readdirSync33(dir)) {
94473
- rmSync17(join93(dir, name), { force: true, recursive: true });
94854
+ rmSync17(join94(dir, name), { force: true, recursive: true });
94474
94855
  }
94475
94856
  const filename = encodeCredentialsFilename(email);
94476
- const filePath = join93(dir, filename);
94857
+ const filePath = join94(dir, filename);
94477
94858
  writeFileSync36(filePath, JSON.stringify(seed), { mode: 384 });
94478
94859
  chmodSync14(filePath, 384);
94479
94860
  return filePath;
@@ -94633,7 +95014,7 @@ function registerDriveMcpLauncherCommand(program3) {
94633
95014
  init_scaffold_integration();
94634
95015
  import { spawn as spawn6 } from "node:child_process";
94635
95016
  import { writeFileSync as writeFileSync37, mkdirSync as mkdirSync52 } from "node:fs";
94636
- import { dirname as dirname35, join as join94 } from "node:path";
95017
+ import { dirname as dirname36, join as join95 } from "node:path";
94637
95018
  var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
94638
95019
  var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
94639
95020
  var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
@@ -94669,7 +95050,7 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
94669
95050
  function writeRefreshHeartbeat(agentName, data, account) {
94670
95051
  const path10 = heartbeatPath(agentName, account);
94671
95052
  try {
94672
- mkdirSync52(dirname35(path10), { recursive: true });
95053
+ mkdirSync52(dirname36(path10), { recursive: true });
94673
95054
  writeFileSync37(path10, JSON.stringify(data, null, 2), { mode: 420 });
94674
95055
  } catch {}
94675
95056
  }
@@ -94678,7 +95059,7 @@ function heartbeatPath(agentName, account) {
94678
95059
  const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
94679
95060
  if (override) {
94680
95061
  const base = slug ? `m365-launcher-${agentName}-${slug}` : `m365-launcher-${agentName}`;
94681
- return join94(override, `${base}.heartbeat.json`);
95062
+ return join95(override, `${base}.heartbeat.json`);
94682
95063
  }
94683
95064
  return slug ? `/state/agent/m365-launcher-${slug}.heartbeat.json` : "/state/agent/m365-launcher.heartbeat.json";
94684
95065
  }
@@ -94910,8 +95291,8 @@ function registerM365McpLauncherCommand(program3) {
94910
95291
  // src/cli/notion-mcp-launcher.ts
94911
95292
  init_scaffold_integration();
94912
95293
  import { spawn as spawn7 } from "node:child_process";
94913
- import { existsSync as existsSync95, mkdirSync as mkdirSync53, writeFileSync as writeFileSync38 } from "node:fs";
94914
- import { dirname as dirname36 } from "node:path";
95294
+ import { existsSync as existsSync96, mkdirSync as mkdirSync53, writeFileSync as writeFileSync38 } from "node:fs";
95295
+ import { dirname as dirname37 } from "node:path";
94915
95296
  var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
94916
95297
  var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
94917
95298
  var DEFAULT_VAULT_KEY = "notion/integration-token";
@@ -94921,8 +95302,8 @@ function buildNotionMcpArgs(opts) {
94921
95302
  }
94922
95303
  function defaultWriteHeartbeat(path10, contents) {
94923
95304
  try {
94924
- const dir = dirname36(path10);
94925
- if (!existsSync95(dir))
95305
+ const dir = dirname37(path10);
95306
+ if (!existsSync96(dir))
94926
95307
  mkdirSync53(dir, { recursive: true });
94927
95308
  writeFileSync38(path10, contents);
94928
95309
  } catch {}
@@ -95034,9 +95415,9 @@ function registerNotionMcpLauncherCommand(program3) {
95034
95415
 
95035
95416
  // src/cli/hindsight-mcp-shim.ts
95036
95417
  init_hindsight();
95037
- import { mkdirSync as mkdirSync54, readFileSync as readFileSync81, renameSync as renameSync23, writeFileSync as writeFileSync39 } from "node:fs";
95418
+ import { mkdirSync as mkdirSync54, readFileSync as readFileSync82, renameSync as renameSync23, writeFileSync as writeFileSync39 } from "node:fs";
95038
95419
  import { tmpdir as tmpdir5 } from "node:os";
95039
- import { join as join95 } from "node:path";
95420
+ import { join as join96 } from "node:path";
95040
95421
  import { createInterface as createInterface6 } from "node:readline";
95041
95422
  var SHIM_SUPPORTED_PROTOCOL_VERSIONS = [
95042
95423
  "2025-06-18",
@@ -95262,12 +95643,12 @@ class HindsightShim {
95262
95643
  `));
95263
95644
  }
95264
95645
  get cachePath() {
95265
- return join95(this.opts.cacheDir, TOOLS_CACHE_FILENAME);
95646
+ return join96(this.opts.cacheDir, TOOLS_CACHE_FILENAME);
95266
95647
  }
95267
95648
  writeCache(result) {
95268
95649
  try {
95269
95650
  mkdirSync54(this.opts.cacheDir, { recursive: true });
95270
- const tmp = join95(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
95651
+ const tmp = join96(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
95271
95652
  writeFileSync39(tmp, JSON.stringify(result, null, 2) + `
95272
95653
  `);
95273
95654
  renameSync23(tmp, this.cachePath);
@@ -95277,7 +95658,7 @@ class HindsightShim {
95277
95658
  }
95278
95659
  readCache() {
95279
95660
  try {
95280
- const parsed = JSON.parse(readFileSync81(this.cachePath, "utf-8"));
95661
+ const parsed = JSON.parse(readFileSync82(this.cachePath, "utf-8"));
95281
95662
  if (Array.isArray(parsed.tools))
95282
95663
  return parsed;
95283
95664
  return null;
@@ -95440,7 +95821,7 @@ function resolveShimOptionsFromEnv(env2) {
95440
95821
  return {
95441
95822
  url: env2.HINDSIGHT_MCP_URL || HINDSIGHT_DEFAULT_MCP_URL,
95442
95823
  bankId: env2.HINDSIGHT_BANK_ID || "",
95443
- cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR || join95(home2, ".hindsight-shim")
95824
+ cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR || join96(home2, ".hindsight-shim")
95444
95825
  };
95445
95826
  }
95446
95827
  function registerHindsightMcpShimCommand(program3) {
@@ -95453,7 +95834,7 @@ function registerHindsightMcpShimCommand(program3) {
95453
95834
 
95454
95835
  // src/cli/deliver-file.ts
95455
95836
  init_client2();
95456
- import { readFileSync as readFileSync82, statSync as statSync52 } from "node:fs";
95837
+ import { readFileSync as readFileSync83, statSync as statSync52 } from "node:fs";
95457
95838
  import { basename as basename13 } from "node:path";
95458
95839
 
95459
95840
  // src/delivery/onedrive.ts
@@ -95793,7 +96174,7 @@ async function defaultResolveProvider() {
95793
96174
  async function runDeliverFile(localPath, deps = {}) {
95794
96175
  const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
95795
96176
  const sizeOf = deps.fileSize ?? ((p) => statSync52(p).size);
95796
- const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync82(p)));
96177
+ const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync83(p)));
95797
96178
  const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
95798
96179
  let size;
95799
96180
  try {
@@ -96114,8 +96495,8 @@ function runRedactStdin() {
96114
96495
  }
96115
96496
 
96116
96497
  // src/cli/status-ask.ts
96117
- import { readFileSync as readFileSync83, existsSync as existsSync96, readdirSync as readdirSync34 } from "node:fs";
96118
- import { join as join96 } from "node:path";
96498
+ import { readFileSync as readFileSync84, existsSync as existsSync97, readdirSync as readdirSync34 } from "node:fs";
96499
+ import { join as join97 } from "node:path";
96119
96500
  import { homedir as homedir52 } from "node:os";
96120
96501
 
96121
96502
  // src/status-ask/report.ts
@@ -96390,7 +96771,7 @@ function runReport(opts) {
96390
96771
  for (const src of sources) {
96391
96772
  let content;
96392
96773
  try {
96393
- content = readFileSync83(src.path, "utf-8");
96774
+ content = readFileSync84(src.path, "utf-8");
96394
96775
  } catch (err) {
96395
96776
  process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
96396
96777
  `);
@@ -96437,7 +96818,7 @@ function runReport(opts) {
96437
96818
  function resolveSources(explicitPath) {
96438
96819
  if (explicitPath != null && explicitPath.trim() !== "") {
96439
96820
  const trimmed = explicitPath.trim();
96440
- if (!existsSync96(trimmed)) {
96821
+ if (!existsSync97(trimmed)) {
96441
96822
  process.stderr.write(`status-ask report: ${trimmed}: file not found
96442
96823
  `);
96443
96824
  process.exit(1);
@@ -96451,9 +96832,9 @@ function resolveSources(explicitPath) {
96451
96832
  const config = loadConfig();
96452
96833
  agentsDir = resolveAgentsDir(config);
96453
96834
  } catch {
96454
- agentsDir = join96(homedir52(), ".switchroom", "agents");
96835
+ agentsDir = join97(homedir52(), ".switchroom", "agents");
96455
96836
  }
96456
- if (!existsSync96(agentsDir))
96837
+ if (!existsSync97(agentsDir))
96457
96838
  return [];
96458
96839
  const sources = [];
96459
96840
  let entries;
@@ -96463,8 +96844,8 @@ function resolveSources(explicitPath) {
96463
96844
  return [];
96464
96845
  }
96465
96846
  for (const name of entries) {
96466
- const path10 = join96(agentsDir, name, "runtime-metrics.jsonl");
96467
- if (existsSync96(path10)) {
96847
+ const path10 = join97(agentsDir, name, "runtime-metrics.jsonl");
96848
+ if (existsSync97(path10)) {
96468
96849
  sources.push({ path: path10, agent: name });
96469
96850
  }
96470
96851
  }
@@ -96494,32 +96875,32 @@ var import_yaml21 = __toESM(require_dist(), 1);
96494
96875
  init_paths();
96495
96876
  import {
96496
96877
  closeSync as closeSync17,
96497
- existsSync as existsSync97,
96878
+ existsSync as existsSync98,
96498
96879
  fsyncSync as fsyncSync8,
96499
96880
  mkdirSync as mkdirSync55,
96500
96881
  openSync as openSync17,
96501
96882
  readdirSync as readdirSync35,
96502
- readFileSync as readFileSync84,
96883
+ readFileSync as readFileSync85,
96503
96884
  renameSync as renameSync24,
96504
96885
  statSync as statSync53,
96505
96886
  unlinkSync as unlinkSync19,
96506
96887
  writeSync as writeSync10
96507
96888
  } from "node:fs";
96508
- import { join as join97, resolve as resolve57 } from "node:path";
96889
+ import { join as join98, resolve as resolve57 } from "node:path";
96509
96890
  var STAGING_SUBDIR = ".staging";
96510
96891
  function overlayPathsFor(agent, opts = {}) {
96511
96892
  const base = opts.root ? resolve57(opts.root, agent) : resolve57(resolveDualPath(`~/.switchroom/agents/${agent}`));
96512
- const scheduleDir = join97(base, "schedule.d");
96513
- const scheduleStagingDir = join97(scheduleDir, STAGING_SUBDIR);
96514
- const skillsDir = join97(base, "skills.d");
96515
- const skillsStagingDir = join97(skillsDir, STAGING_SUBDIR);
96893
+ const scheduleDir = join98(base, "schedule.d");
96894
+ const scheduleStagingDir = join98(scheduleDir, STAGING_SUBDIR);
96895
+ const skillsDir = join98(base, "skills.d");
96896
+ const skillsStagingDir = join98(skillsDir, STAGING_SUBDIR);
96516
96897
  return {
96517
96898
  agentRoot: base,
96518
96899
  scheduleDir,
96519
96900
  scheduleStagingDir,
96520
96901
  skillsDir,
96521
96902
  skillsStagingDir,
96522
- lockPath: join97(base, ".lock"),
96903
+ lockPath: join98(base, ".lock"),
96523
96904
  stagingDir: scheduleStagingDir
96524
96905
  };
96525
96906
  }
@@ -96573,8 +96954,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
96573
96954
  const paths = overlayPathsFor(agent, opts);
96574
96955
  return withAgentLock(paths, () => {
96575
96956
  ensureDirs(paths);
96576
- const stagingPath = join97(paths.scheduleStagingDir, `${slug}.yaml`);
96577
- const finalPath = join97(paths.scheduleDir, `${slug}.yaml`);
96957
+ const stagingPath = join98(paths.scheduleStagingDir, `${slug}.yaml`);
96958
+ const finalPath = join98(paths.scheduleDir, `${slug}.yaml`);
96578
96959
  const fd = openSync17(stagingPath, "w", 384);
96579
96960
  try {
96580
96961
  writeSync10(fd, yamlText);
@@ -96590,8 +96971,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
96590
96971
  const paths = overlayPathsFor(agent, opts);
96591
96972
  return withAgentLock(paths, () => {
96592
96973
  ensureSkillsDirs(paths);
96593
- const stagingPath = join97(paths.skillsStagingDir, `${slug}.yaml`);
96594
- const finalPath = join97(paths.skillsDir, `${slug}.yaml`);
96974
+ const stagingPath = join98(paths.skillsStagingDir, `${slug}.yaml`);
96975
+ const finalPath = join98(paths.skillsDir, `${slug}.yaml`);
96595
96976
  const fd = openSync17(stagingPath, "w", 384);
96596
96977
  try {
96597
96978
  writeSync10(fd, yamlText);
@@ -96606,8 +96987,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
96606
96987
  function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
96607
96988
  const paths = overlayPathsFor(agent, opts);
96608
96989
  return withAgentLock(paths, () => {
96609
- const finalPath = join97(paths.skillsDir, `${slug}.yaml`);
96610
- if (!existsSync97(finalPath))
96990
+ const finalPath = join98(paths.skillsDir, `${slug}.yaml`);
96991
+ if (!existsSync98(finalPath))
96611
96992
  return false;
96612
96993
  unlinkSync19(finalPath);
96613
96994
  return true;
@@ -96615,15 +96996,15 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
96615
96996
  }
96616
96997
  function listSkillsOverlayEntries(agent, opts = {}) {
96617
96998
  const paths = overlayPathsFor(agent, opts);
96618
- if (!existsSync97(paths.skillsDir))
96999
+ if (!existsSync98(paths.skillsDir))
96619
97000
  return [];
96620
97001
  const out = [];
96621
97002
  for (const name of readdirSync35(paths.skillsDir)) {
96622
97003
  if (!/\.ya?ml$/i.test(name))
96623
97004
  continue;
96624
- const full = join97(paths.skillsDir, name);
97005
+ const full = join98(paths.skillsDir, name);
96625
97006
  try {
96626
- const raw = readFileSync84(full, "utf-8");
97007
+ const raw = readFileSync85(full, "utf-8");
96627
97008
  const slug = name.replace(/\.ya?ml$/i, "");
96628
97009
  out.push({ slug, path: full, raw });
96629
97010
  } catch {}
@@ -96633,8 +97014,8 @@ function listSkillsOverlayEntries(agent, opts = {}) {
96633
97014
  function deleteOverlayEntry(agent, slug, opts = {}) {
96634
97015
  const paths = overlayPathsFor(agent, opts);
96635
97016
  return withAgentLock(paths, () => {
96636
- const finalPath = join97(paths.scheduleDir, `${slug}.yaml`);
96637
- if (!existsSync97(finalPath))
97017
+ const finalPath = join98(paths.scheduleDir, `${slug}.yaml`);
97018
+ if (!existsSync98(finalPath))
96638
97019
  return false;
96639
97020
  unlinkSync19(finalPath);
96640
97021
  return true;
@@ -96642,15 +97023,15 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
96642
97023
  }
96643
97024
  function listOverlayEntries(agent, opts = {}) {
96644
97025
  const paths = overlayPathsFor(agent, opts);
96645
- if (!existsSync97(paths.scheduleDir))
97026
+ if (!existsSync98(paths.scheduleDir))
96646
97027
  return [];
96647
97028
  const out = [];
96648
97029
  for (const name of readdirSync35(paths.scheduleDir)) {
96649
97030
  if (!/\.ya?ml$/i.test(name))
96650
97031
  continue;
96651
- const full = join97(paths.scheduleDir, name);
97032
+ const full = join98(paths.scheduleDir, name);
96652
97033
  try {
96653
- const raw = readFileSync84(full, "utf-8");
97034
+ const raw = readFileSync85(full, "utf-8");
96654
97035
  const slug = name.replace(/\.ya?ml$/i, "");
96655
97036
  out.push({ slug, path: full, raw });
96656
97037
  } catch {}
@@ -96870,23 +97251,23 @@ function reconcileAgentCronOnly(agent) {
96870
97251
  // src/cli/agent-config-pending.ts
96871
97252
  import {
96872
97253
  closeSync as closeSync18,
96873
- existsSync as existsSync98,
97254
+ existsSync as existsSync99,
96874
97255
  fsyncSync as fsyncSync9,
96875
97256
  mkdirSync as mkdirSync56,
96876
97257
  openSync as openSync18,
96877
97258
  readdirSync as readdirSync36,
96878
- readFileSync as readFileSync85,
97259
+ readFileSync as readFileSync86,
96879
97260
  renameSync as renameSync25,
96880
97261
  unlinkSync as unlinkSync20,
96881
97262
  writeFileSync as writeFileSync40,
96882
97263
  writeSync as writeSync11
96883
97264
  } from "node:fs";
96884
- import { join as join98 } from "node:path";
97265
+ import { join as join99 } from "node:path";
96885
97266
  import { randomBytes as randomBytes15 } from "node:crypto";
96886
97267
  var STAGE_ID_PREFIX = "cap_";
96887
97268
  function pendingDir(agent, opts = {}) {
96888
97269
  const paths = overlayPathsFor(agent, opts);
96889
- return join98(paths.scheduleDir, ".pending");
97270
+ return join99(paths.scheduleDir, ".pending");
96890
97271
  }
96891
97272
  function ensurePendingDir(agent, opts = {}) {
96892
97273
  const dir = pendingDir(agent, opts);
@@ -96899,8 +97280,8 @@ function newStageId() {
96899
97280
  function stagePendingScheduleEntry(opts) {
96900
97281
  const dir = ensurePendingDir(opts.agent, { root: opts.root });
96901
97282
  const stageId = opts.stageId ?? newStageId();
96902
- const yamlPath = join98(dir, `${stageId}.yaml`);
96903
- const metaPath = join98(dir, `${stageId}.meta.json`);
97283
+ const yamlPath = join99(dir, `${stageId}.yaml`);
97284
+ const metaPath = join99(dir, `${stageId}.meta.json`);
96904
97285
  const meta = {
96905
97286
  v: 1,
96906
97287
  stage_id: stageId,
@@ -96927,19 +97308,19 @@ function stagePendingScheduleEntry(opts) {
96927
97308
  }
96928
97309
  function listPendingScheduleEntries(agent, opts = {}) {
96929
97310
  const dir = pendingDir(agent, opts);
96930
- if (!existsSync98(dir))
97311
+ if (!existsSync99(dir))
96931
97312
  return [];
96932
97313
  const out = [];
96933
97314
  for (const name of readdirSync36(dir).sort()) {
96934
97315
  if (!name.endsWith(".meta.json"))
96935
97316
  continue;
96936
97317
  const stageId = name.slice(0, -".meta.json".length);
96937
- const metaPath = join98(dir, name);
96938
- const yamlPath = join98(dir, `${stageId}.yaml`);
96939
- if (!existsSync98(yamlPath))
97318
+ const metaPath = join99(dir, name);
97319
+ const yamlPath = join99(dir, `${stageId}.yaml`);
97320
+ if (!existsSync99(yamlPath))
96940
97321
  continue;
96941
97322
  try {
96942
- const meta = JSON.parse(readFileSync85(metaPath, "utf-8"));
97323
+ const meta = JSON.parse(readFileSync86(metaPath, "utf-8"));
96943
97324
  if (meta?.v !== 1 || typeof meta.stage_id !== "string")
96944
97325
  continue;
96945
97326
  out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
@@ -96954,8 +97335,8 @@ function commitPendingScheduleEntry(opts) {
96954
97335
  return { committed: false, reason: "not_found" };
96955
97336
  const slug = match.meta.entry.name ?? match.stageId;
96956
97337
  const paths = overlayPathsFor(opts.agent, { root: opts.root });
96957
- const finalPath = join98(paths.scheduleDir, `${slug}.yaml`);
96958
- if (existsSync98(finalPath)) {
97338
+ const finalPath = join99(paths.scheduleDir, `${slug}.yaml`);
97339
+ if (existsSync99(finalPath)) {
96959
97340
  return { committed: false, reason: "slug_collision" };
96960
97341
  }
96961
97342
  renameSync25(match.yamlPath, finalPath);
@@ -96977,7 +97358,7 @@ function denyPendingScheduleEntry(opts) {
96977
97358
  }
96978
97359
 
96979
97360
  // src/cli/agent-config-write.ts
96980
- import { existsSync as existsSync99, readFileSync as readFileSync86 } from "node:fs";
97361
+ import { existsSync as existsSync100, readFileSync as readFileSync87 } from "node:fs";
96981
97362
  import { execFileSync as execFileSync30 } from "node:child_process";
96982
97363
 
96983
97364
  // src/scheduler/schedule-report.ts
@@ -97367,8 +97748,8 @@ function scheduleRemove(opts) {
97367
97748
  }
97368
97749
  let priorContent = null;
97369
97750
  try {
97370
- if (existsSync99(match.path))
97371
- priorContent = readFileSync86(match.path, "utf-8");
97751
+ if (existsSync100(match.path))
97752
+ priorContent = readFileSync87(match.path, "utf-8");
97372
97753
  } catch {}
97373
97754
  deleteOverlayEntry(agent, match.slug, { root: opts.root });
97374
97755
  const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
@@ -97571,7 +97952,7 @@ function registerAgentConfigWriteCommands(program3) {
97571
97952
  }
97572
97953
  let blob;
97573
97954
  if (opts.jsonl) {
97574
- blob = existsSync99(opts.jsonl) ? readFileSync86(opts.jsonl, "utf-8") : "";
97955
+ blob = existsSync100(opts.jsonl) ? readFileSync87(opts.jsonl, "utf-8") : "";
97575
97956
  } else {
97576
97957
  try {
97577
97958
  blob = execFileSync30("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
@@ -97603,11 +97984,11 @@ function registerAgentConfigWriteCommands(program3) {
97603
97984
 
97604
97985
  // src/cli/agent-config-skill-write.ts
97605
97986
  var import_yaml22 = __toESM(require_dist(), 1);
97606
- import { existsSync as existsSync100 } from "node:fs";
97987
+ import { existsSync as existsSync101 } from "node:fs";
97607
97988
  init_reconcile_default_skills();
97608
97989
  init_agent_config();
97609
97990
  var import_yaml23 = __toESM(require_dist(), 1);
97610
- import { join as join99 } from "node:path";
97991
+ import { join as join100 } from "node:path";
97611
97992
  var MAX_SKILLS_PER_AGENT = 20;
97612
97993
  var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
97613
97994
  function exitCodeFor2(code) {
@@ -97682,8 +98063,8 @@ function skillInstall(opts) {
97682
98063
  return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
97683
98064
  }
97684
98065
  const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
97685
- const skillPath = join99(poolDir, skillName);
97686
- if (!existsSync100(skillPath)) {
98066
+ const skillPath = join100(poolDir, skillName);
98067
+ if (!existsSync101(skillPath)) {
97687
98068
  return err("E_SKILL_NOT_FOUND", `bundled skill not found at ${skillPath}. The operator needs to ` + `place the skill at this path before the agent can opt in.`);
97688
98069
  }
97689
98070
  const yamlText = import_yaml22.stringify({ skills: [skillName] });
@@ -97847,12 +98228,12 @@ function registerAgentConfigSkillWriteCommands(program3) {
97847
98228
  // src/cli/skill.ts
97848
98229
  import {
97849
98230
  closeSync as closeSync19,
97850
- existsSync as existsSync101,
98231
+ existsSync as existsSync102,
97851
98232
  lstatSync as lstatSync12,
97852
98233
  mkdirSync as mkdirSync57,
97853
98234
  mkdtempSync as mkdtempSync5,
97854
98235
  openSync as openSync19,
97855
- readFileSync as readFileSync87,
98236
+ readFileSync as readFileSync88,
97856
98237
  readdirSync as readdirSync37,
97857
98238
  realpathSync as realpathSync7,
97858
98239
  renameSync as renameSync26,
@@ -97861,8 +98242,8 @@ import {
97861
98242
  writeFileSync as writeFileSync41
97862
98243
  } from "node:fs";
97863
98244
  import { tmpdir as tmpdir6, homedir as homedir53 } from "node:os";
97864
- import { dirname as dirname37, join as join100, relative as relative4, resolve as resolve58 } from "node:path";
97865
- import { spawnSync as spawnSync17 } from "node:child_process";
98245
+ import { dirname as dirname38, join as join101, relative as relative4, resolve as resolve58 } from "node:path";
98246
+ import { spawnSync as spawnSync18 } from "node:child_process";
97866
98247
 
97867
98248
  // src/cli/skill-common.ts
97868
98249
  var import_yaml24 = __toESM(require_dist(), 1);
@@ -98095,7 +98476,7 @@ function scanForClaudeP2(content) {
98095
98476
  function resolveSkillsPoolDir2(override) {
98096
98477
  const raw = override ?? "~/.switchroom/skills";
98097
98478
  if (raw.startsWith("~/")) {
98098
- return join100(homedir53(), raw.slice(2));
98479
+ return join101(homedir53(), raw.slice(2));
98099
98480
  }
98100
98481
  if (raw === "~")
98101
98482
  return homedir53();
@@ -98134,7 +98515,7 @@ function loadFromDir(dir) {
98134
98515
  const walk2 = (sub) => {
98135
98516
  const entries = readdirSync37(sub, { withFileTypes: true });
98136
98517
  for (const ent of entries) {
98137
- const full = join100(sub, ent.name);
98518
+ const full = join101(sub, ent.name);
98138
98519
  const rel = relative4(abs, full);
98139
98520
  if (ent.isSymbolicLink()) {
98140
98521
  fail3(`refusing to read symlink inside --from dir: ${rel}`);
@@ -98144,7 +98525,7 @@ function loadFromDir(dir) {
98144
98525
  continue;
98145
98526
  }
98146
98527
  if (ent.isFile()) {
98147
- const buf = readFileSync87(full);
98528
+ const buf = readFileSync88(full);
98148
98529
  files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
98149
98530
  }
98150
98531
  }
@@ -98155,7 +98536,7 @@ function loadFromDir(dir) {
98155
98536
  function loadFromTarball(tarPath) {
98156
98537
  const isGz = tarPath.endsWith(".gz") || tarPath.endsWith(".tgz");
98157
98538
  const listFlags = isGz ? ["-tzf"] : ["-tf"];
98158
- const list2 = spawnSync17("tar", [...listFlags, tarPath], {
98539
+ const list2 = spawnSync18("tar", [...listFlags, tarPath], {
98159
98540
  encoding: "utf-8",
98160
98541
  stdio: ["ignore", "pipe", "pipe"]
98161
98542
  });
@@ -98169,10 +98550,10 @@ function loadFromTarball(tarPath) {
98169
98550
  fail3(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
98170
98551
  }
98171
98552
  }
98172
- const staging = mkdtempSync5(join100(tmpdir6(), "skill-apply-extract-"));
98553
+ const staging = mkdtempSync5(join101(tmpdir6(), "skill-apply-extract-"));
98173
98554
  try {
98174
98555
  const flags = isGz ? ["-xzf"] : ["-xf"];
98175
- const r = spawnSync17("tar", [
98556
+ const r = spawnSync18("tar", [
98176
98557
  ...flags,
98177
98558
  tarPath,
98178
98559
  "-C",
@@ -98193,7 +98574,7 @@ function loadFromTarball(tarPath) {
98193
98574
  }
98194
98575
  }
98195
98576
  function loadSingleFile(filePath) {
98196
- const content = readFileSync87(filePath, "utf-8");
98577
+ const content = readFileSync88(filePath, "utf-8");
98197
98578
  return { "SKILL.md": content };
98198
98579
  }
98199
98580
  function loadFromStdin() {
@@ -98247,7 +98628,7 @@ function validatePayload(name, files) {
98247
98628
  if (errors2.length === 0) {
98248
98629
  for (const [path10, content] of Object.entries(files)) {
98249
98630
  if (SH_SCRIPT_RE2.test(path10)) {
98250
- const r = spawnSync17("bash", ["-n"], {
98631
+ const r = spawnSync18("bash", ["-n"], {
98251
98632
  input: content,
98252
98633
  encoding: "utf-8"
98253
98634
  });
@@ -98255,11 +98636,11 @@ function validatePayload(name, files) {
98255
98636
  errors2.push(`${path10} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
98256
98637
  }
98257
98638
  } else if (PY_SCRIPT_RE2.test(path10)) {
98258
- const tmp = mkdtempSync5(join100(tmpdir6(), "skill-apply-py-"));
98259
- const tmpPy = join100(tmp, "check.py");
98639
+ const tmp = mkdtempSync5(join101(tmpdir6(), "skill-apply-py-"));
98640
+ const tmpPy = join101(tmp, "check.py");
98260
98641
  try {
98261
98642
  writeFileSync41(tmpPy, content);
98262
- const r = spawnSync17("python3", ["-m", "py_compile", tmpPy], {
98643
+ const r = spawnSync18("python3", ["-m", "py_compile", tmpPy], {
98263
98644
  encoding: "utf-8"
98264
98645
  });
98265
98646
  if (r.status !== 0) {
@@ -98276,15 +98657,15 @@ function validatePayload(name, files) {
98276
98657
  function diffSummary(currentDir, files) {
98277
98658
  const lines = [];
98278
98659
  const currentFiles = {};
98279
- if (existsSync101(currentDir)) {
98660
+ if (existsSync102(currentDir)) {
98280
98661
  const walk2 = (sub) => {
98281
98662
  for (const ent of readdirSync37(sub, { withFileTypes: true })) {
98282
- const full = join100(sub, ent.name);
98663
+ const full = join101(sub, ent.name);
98283
98664
  const rel = relative4(currentDir, full);
98284
98665
  if (ent.isDirectory()) {
98285
98666
  walk2(full);
98286
98667
  } else if (ent.isFile()) {
98287
- currentFiles[rel.replace(/\\/g, "/")] = readFileSync87(full, "utf-8");
98668
+ currentFiles[rel.replace(/\\/g, "/")] = readFileSync88(full, "utf-8");
98288
98669
  }
98289
98670
  }
98290
98671
  };
@@ -98312,10 +98693,10 @@ function diffSummary(currentDir, files) {
98312
98693
  `);
98313
98694
  }
98314
98695
  function writePayload(poolDir, name, files) {
98315
- if (!existsSync101(poolDir)) {
98696
+ if (!existsSync102(poolDir)) {
98316
98697
  mkdirSync57(poolDir, { recursive: true, mode: 493 });
98317
98698
  }
98318
- const target = join100(poolDir, name);
98699
+ const target = join101(poolDir, name);
98319
98700
  let targetIsSymlink = false;
98320
98701
  try {
98321
98702
  const st = lstatSync12(target);
@@ -98326,12 +98707,12 @@ function writePayload(poolDir, name, files) {
98326
98707
  if (targetIsSymlink) {
98327
98708
  fail3(`refusing to overwrite symlink at ${target}; investigate manually`);
98328
98709
  }
98329
- const staging = mkdtempSync5(join100(poolDir, `.skill-apply-stage-${name}-`));
98710
+ const staging = mkdtempSync5(join101(poolDir, `.skill-apply-stage-${name}-`));
98330
98711
  let oldRename = null;
98331
98712
  try {
98332
98713
  for (const [path10, content] of Object.entries(files)) {
98333
- const full = join100(staging, path10);
98334
- mkdirSync57(dirname37(full), { recursive: true, mode: 493 });
98714
+ const full = join101(staging, path10);
98715
+ mkdirSync57(dirname38(full), { recursive: true, mode: 493 });
98335
98716
  const fd = openSync19(full, "wx");
98336
98717
  try {
98337
98718
  writeFileSync41(fd, content);
@@ -98361,9 +98742,9 @@ function writePayload(poolDir, name, files) {
98361
98742
  try {
98362
98743
  rmSync18(staging, { recursive: true, force: true });
98363
98744
  } catch {}
98364
- if (oldRename && existsSync101(oldRename)) {
98745
+ if (oldRename && existsSync102(oldRename)) {
98365
98746
  try {
98366
- if (existsSync101(target)) {
98747
+ if (existsSync102(target)) {
98367
98748
  rmSync18(target, { recursive: true, force: true });
98368
98749
  }
98369
98750
  renameSync26(oldRename, target);
@@ -98384,7 +98765,7 @@ function registerSkillCommand(program3) {
98384
98765
  files = loadFromStdin();
98385
98766
  } else {
98386
98767
  const fromPath = resolve58(opts.from);
98387
- if (!existsSync101(fromPath)) {
98768
+ if (!existsSync102(fromPath)) {
98388
98769
  fail3(`--from path does not exist: ${opts.from}`);
98389
98770
  }
98390
98771
  const st = statSync54(fromPath);
@@ -98408,7 +98789,7 @@ function registerSkillCommand(program3) {
98408
98789
  }
98409
98790
  const config = loadConfig();
98410
98791
  const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
98411
- const currentDir = join100(poolDir, name);
98792
+ const currentDir = join101(poolDir, name);
98412
98793
  console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
98413
98794
  console.log(source_default.bold("Diff vs current pool content:"));
98414
98795
  console.log(diffSummary(currentDir, files));
@@ -98422,7 +98803,7 @@ function registerSkillCommand(program3) {
98422
98803
  \u2713 Wrote ${name} to ${currentDir}`));
98423
98804
  const applyBin = process.argv[1] ?? "switchroom";
98424
98805
  console.log(source_default.gray(`Running \`switchroom apply --non-interactive\`...`));
98425
- const r = spawnSync17(process.argv0, [applyBin, "apply", "--non-interactive"], { stdio: "inherit" });
98806
+ const r = spawnSync18(process.argv0, [applyBin, "apply", "--non-interactive"], { stdio: "inherit" });
98426
98807
  if (r.status !== 0) {
98427
98808
  console.error(source_default.yellow(`(warning: \`switchroom apply\` exited ${r.status} \u2014 skill is ` + `in the pool but symlinks may not be refreshed. Re-run manually.)`));
98428
98809
  }
@@ -98440,12 +98821,12 @@ function sumBytes(files) {
98440
98821
  init_esm();
98441
98822
  import {
98442
98823
  closeSync as closeSync20,
98443
- existsSync as existsSync102,
98824
+ existsSync as existsSync103,
98444
98825
  lstatSync as lstatSync13,
98445
98826
  mkdirSync as mkdirSync58,
98446
98827
  mkdtempSync as mkdtempSync6,
98447
98828
  openSync as openSync20,
98448
- readFileSync as readFileSync88,
98829
+ readFileSync as readFileSync89,
98449
98830
  readdirSync as readdirSync38,
98450
98831
  renameSync as renameSync27,
98451
98832
  rmSync as rmSync19,
@@ -98453,9 +98834,9 @@ import {
98453
98834
  utimesSync,
98454
98835
  writeFileSync as writeFileSync42
98455
98836
  } from "node:fs";
98456
- import { dirname as dirname38, join as join101, relative as relative5, resolve as resolve59 } from "node:path";
98837
+ import { dirname as dirname39, join as join102, relative as relative5, resolve as resolve59 } from "node:path";
98457
98838
  import { homedir as homedir54, tmpdir as tmpdir7 } from "node:os";
98458
- import { spawnSync as spawnSync18 } from "node:child_process";
98839
+ import { spawnSync as spawnSync19 } from "node:child_process";
98459
98840
  init_helpers();
98460
98841
  init_agent_config();
98461
98842
  init_source();
@@ -98465,15 +98846,15 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
98465
98846
  var PERSONAL_SKILLS_SUBPATH = "personal-skills";
98466
98847
  function resolveConfigSkillsDir(agent) {
98467
98848
  const override = process.env.SWITCHROOM_CONFIG_DIR;
98468
- const candidate = override ? resolve59(override) : join101(homedir54(), ".switchroom-config");
98469
- if (!existsSync102(candidate))
98849
+ const candidate = override ? resolve59(override) : join102(homedir54(), ".switchroom-config");
98850
+ if (!existsSync103(candidate))
98470
98851
  return null;
98471
- return join101(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
98852
+ return join102(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
98472
98853
  }
98473
98854
  var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
98474
98855
  function sweepMirrorPriors(configSkillsRoot) {
98475
98856
  try {
98476
- if (!existsSync102(configSkillsRoot))
98857
+ if (!existsSync103(configSkillsRoot))
98477
98858
  return;
98478
98859
  const now = Date.now();
98479
98860
  for (const ent of readdirSync38(configSkillsRoot)) {
@@ -98486,7 +98867,7 @@ function sweepMirrorPriors(configSkillsRoot) {
98486
98867
  if (now - ts < MIRROR_PRIOR_TTL_MS)
98487
98868
  continue;
98488
98869
  try {
98489
- rmSync19(join101(configSkillsRoot, ent), { recursive: true, force: true });
98870
+ rmSync19(join102(configSkillsRoot, ent), { recursive: true, force: true });
98490
98871
  } catch {}
98491
98872
  }
98492
98873
  } catch {}
@@ -98495,7 +98876,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
98495
98876
  const configSkillsRoot = resolveConfigSkillsDir(agent);
98496
98877
  if (!configSkillsRoot)
98497
98878
  return;
98498
- const dest = join101(configSkillsRoot, name);
98879
+ const dest = join102(configSkillsRoot, name);
98499
98880
  try {
98500
98881
  if (liveSkillDir !== null) {
98501
98882
  try {
@@ -98509,32 +98890,32 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
98509
98890
  }
98510
98891
  if (liveSkillDir === null) {
98511
98892
  sweepMirrorPriors(configSkillsRoot);
98512
- if (existsSync102(dest)) {
98513
- const trash = join101(configSkillsRoot, `.${name}-trash-${Date.now()}`);
98893
+ if (existsSync103(dest)) {
98894
+ const trash = join102(configSkillsRoot, `.${name}-trash-${Date.now()}`);
98514
98895
  renameSync27(dest, trash);
98515
98896
  }
98516
98897
  return;
98517
98898
  }
98518
98899
  mkdirSync58(configSkillsRoot, { recursive: true, mode: 493 });
98519
98900
  sweepMirrorPriors(configSkillsRoot);
98520
- const staging = mkdtempSync6(join101(configSkillsRoot, `.${name}-staging-`));
98901
+ const staging = mkdtempSync6(join102(configSkillsRoot, `.${name}-staging-`));
98521
98902
  const walk2 = (src, dst) => {
98522
98903
  mkdirSync58(dst, { recursive: true, mode: 493 });
98523
98904
  for (const ent of readdirSync38(src, { withFileTypes: true })) {
98524
- const s = join101(src, ent.name);
98525
- const d = join101(dst, ent.name);
98905
+ const s = join102(src, ent.name);
98906
+ const d = join102(dst, ent.name);
98526
98907
  if (ent.isSymbolicLink())
98527
98908
  continue;
98528
98909
  if (ent.isDirectory())
98529
98910
  walk2(s, d);
98530
98911
  else if (ent.isFile()) {
98531
- writeFileSync42(d, readFileSync88(s));
98912
+ writeFileSync42(d, readFileSync89(s));
98532
98913
  }
98533
98914
  }
98534
98915
  };
98535
98916
  walk2(liveSkillDir, staging);
98536
- if (existsSync102(dest)) {
98537
- const prior = join101(configSkillsRoot, `.${name}-prior-${Date.now()}`);
98917
+ if (existsSync103(dest)) {
98918
+ const prior = join102(configSkillsRoot, `.${name}-prior-${Date.now()}`);
98538
98919
  renameSync27(dest, prior);
98539
98920
  }
98540
98921
  renameSync27(staging, dest);
@@ -98564,17 +98945,17 @@ function resolveAgent(opts) {
98564
98945
  function resolveAgentsRoot(opts) {
98565
98946
  if (opts.root)
98566
98947
  return resolve59(opts.root);
98567
- return join101(homedir54(), ".switchroom", "agents");
98948
+ return join102(homedir54(), ".switchroom", "agents");
98568
98949
  }
98569
98950
  function personalSkillDir(agentsRoot, agent, name) {
98570
- return join101(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
98951
+ return join102(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
98571
98952
  }
98572
98953
  function trashDir(agentsRoot, agent) {
98573
- return join101(agentsRoot, agent, ".claude", TRASH_DIRNAME);
98954
+ return join102(agentsRoot, agent, ".claude", TRASH_DIRNAME);
98574
98955
  }
98575
98956
  function countPersonalSkills(agentsRoot, agent) {
98576
- const skillsDir = join101(agentsRoot, agent, ".claude", "skills");
98577
- if (!existsSync102(skillsDir))
98957
+ const skillsDir = join102(agentsRoot, agent, ".claude", "skills");
98958
+ if (!existsSync103(skillsDir))
98578
98959
  return 0;
98579
98960
  let n = 0;
98580
98961
  for (const ent of readdirSync38(skillsDir, { withFileTypes: true })) {
@@ -98611,7 +98992,7 @@ function loadFromDir2(dir) {
98611
98992
  const files = {};
98612
98993
  const walk2 = (sub) => {
98613
98994
  for (const ent of readdirSync38(sub, { withFileTypes: true })) {
98614
- const full = join101(sub, ent.name);
98995
+ const full = join102(sub, ent.name);
98615
98996
  if (ent.isSymbolicLink()) {
98616
98997
  fail4(`refusing to read symlink in --from dir: ${relative5(abs, full)}`);
98617
98998
  }
@@ -98621,7 +99002,7 @@ function loadFromDir2(dir) {
98621
99002
  }
98622
99003
  if (ent.isFile()) {
98623
99004
  const rel = relative5(abs, full).replace(/\\/g, "/");
98624
- files[rel] = readFileSync88(full, "utf-8");
99005
+ files[rel] = readFileSync89(full, "utf-8");
98625
99006
  }
98626
99007
  }
98627
99008
  };
@@ -98659,16 +99040,16 @@ function behavioralValidate(files) {
98659
99040
  const errors2 = [];
98660
99041
  for (const [path10, content] of Object.entries(files)) {
98661
99042
  if (SH_SCRIPT_RE.test(path10)) {
98662
- const r = spawnSync18("bash", ["-n"], { input: content, encoding: "utf-8" });
99043
+ const r = spawnSync19("bash", ["-n"], { input: content, encoding: "utf-8" });
98663
99044
  if (r.status !== 0) {
98664
99045
  errors2.push(`${path10} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
98665
99046
  }
98666
99047
  } else if (PY_SCRIPT_RE.test(path10)) {
98667
- const tmp = mkdtempSync6(join101(tmpdir7(), "skill-personal-py-"));
98668
- const tmpPy = join101(tmp, "check.py");
99048
+ const tmp = mkdtempSync6(join102(tmpdir7(), "skill-personal-py-"));
99049
+ const tmpPy = join102(tmp, "check.py");
98669
99050
  try {
98670
99051
  writeFileSync42(tmpPy, content);
98671
- const r = spawnSync18("python3", ["-m", "py_compile", tmpPy], {
99052
+ const r = spawnSync19("python3", ["-m", "py_compile", tmpPy], {
98672
99053
  encoding: "utf-8"
98673
99054
  });
98674
99055
  if (r.status !== 0) {
@@ -98683,13 +99064,13 @@ function behavioralValidate(files) {
98683
99064
  }
98684
99065
  function sweepTrash(agentsRoot, agent) {
98685
99066
  const trash = trashDir(agentsRoot, agent);
98686
- if (!existsSync102(trash))
99067
+ if (!existsSync103(trash))
98687
99068
  return;
98688
99069
  const now = Date.now();
98689
99070
  for (const ent of readdirSync38(trash, { withFileTypes: true })) {
98690
99071
  if (!ent.isDirectory())
98691
99072
  continue;
98692
- const entPath = join101(trash, ent.name);
99073
+ const entPath = join102(trash, ent.name);
98693
99074
  try {
98694
99075
  const st = statSync55(entPath);
98695
99076
  if (now - st.mtimeMs > TRASH_TTL_MS) {
@@ -98709,13 +99090,13 @@ function writePersonalSkill(targetDir, files) {
98709
99090
  if (targetIsSymlink) {
98710
99091
  fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
98711
99092
  }
98712
- mkdirSync58(dirname38(targetDir), { recursive: true, mode: 493 });
98713
- const staging = mkdtempSync6(join101(dirname38(targetDir), `.skill-personal-stage-`));
99093
+ mkdirSync58(dirname39(targetDir), { recursive: true, mode: 493 });
99094
+ const staging = mkdtempSync6(join102(dirname39(targetDir), `.skill-personal-stage-`));
98714
99095
  let oldRename = null;
98715
99096
  try {
98716
99097
  for (const [path10, content] of Object.entries(files)) {
98717
- const full = join101(staging, path10);
98718
- mkdirSync58(dirname38(full), { recursive: true, mode: 493 });
99098
+ const full = join102(staging, path10);
99099
+ mkdirSync58(dirname39(full), { recursive: true, mode: 493 });
98719
99100
  const fd = openSync20(full, "wx");
98720
99101
  try {
98721
99102
  writeFileSync42(fd, content);
@@ -98745,9 +99126,9 @@ function writePersonalSkill(targetDir, files) {
98745
99126
  try {
98746
99127
  rmSync19(staging, { recursive: true, force: true });
98747
99128
  } catch {}
98748
- if (oldRename && existsSync102(oldRename)) {
99129
+ if (oldRename && existsSync103(oldRename)) {
98749
99130
  try {
98750
- if (existsSync102(targetDir)) {
99131
+ if (existsSync103(targetDir)) {
98751
99132
  rmSync19(targetDir, { recursive: true, force: true });
98752
99133
  }
98753
99134
  renameSync27(oldRename, targetDir);
@@ -98813,7 +99194,7 @@ function loadFiles(opts) {
98813
99194
  return loadFromStdin2();
98814
99195
  }
98815
99196
  const p = resolve59(opts.from);
98816
- if (!existsSync102(p)) {
99197
+ if (!existsSync103(p)) {
98817
99198
  fail4(`--from path does not exist: ${opts.from}`);
98818
99199
  }
98819
99200
  const st = statSync55(p);
@@ -98821,7 +99202,7 @@ function loadFiles(opts) {
98821
99202
  return loadFromDir2(p);
98822
99203
  }
98823
99204
  if (p.endsWith(".md")) {
98824
- return { "SKILL.md": readFileSync88(p, "utf-8") };
99205
+ return { "SKILL.md": readFileSync89(p, "utf-8") };
98825
99206
  }
98826
99207
  fail4(`--from must be a directory or a .md file. Got: ${opts.from}`);
98827
99208
  }
@@ -98861,10 +99242,10 @@ function editPersonalAction(name, opts) {
98861
99242
  }
98862
99243
  var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
98863
99244
  function defaultSharedRoot() {
98864
- return join101(homedir54(), ".switchroom", "skills");
99245
+ return join102(homedir54(), ".switchroom", "skills");
98865
99246
  }
98866
99247
  function defaultBundledRoot() {
98867
- return join101(homedir54(), ".switchroom", "skills", "_bundled");
99248
+ return join102(homedir54(), ".switchroom", "skills", "_bundled");
98868
99249
  }
98869
99250
  function resolveCloneSource(source, opts) {
98870
99251
  const m = CLONE_SOURCE_RE.exec(source);
@@ -98874,8 +99255,8 @@ function resolveCloneSource(source, opts) {
98874
99255
  const tier = m[1];
98875
99256
  const slug = m[2];
98876
99257
  const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
98877
- const dir = join101(root, slug);
98878
- if (!existsSync102(dir)) {
99258
+ const dir = join102(root, slug);
99259
+ if (!existsSync103(dir)) {
98879
99260
  fail4(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
98880
99261
  }
98881
99262
  const st = lstatSync13(dir);
@@ -98890,7 +99271,7 @@ function readSourceFiles(dir) {
98890
99271
  const skipped = [];
98891
99272
  const walk2 = (sub) => {
98892
99273
  for (const ent of readdirSync38(sub, { withFileTypes: true })) {
98893
- const full = join101(sub, ent.name);
99274
+ const full = join102(sub, ent.name);
98894
99275
  if (ent.isSymbolicLink()) {
98895
99276
  continue;
98896
99277
  }
@@ -98910,7 +99291,7 @@ function readSourceFiles(dir) {
98910
99291
  fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
98911
99292
  }
98912
99293
  } catch {}
98913
- files[rel] = readFileSync88(full, "utf-8");
99294
+ files[rel] = readFileSync89(full, "utf-8");
98914
99295
  }
98915
99296
  }
98916
99297
  };
@@ -99001,7 +99382,7 @@ function removePersonalAction(name, opts) {
99001
99382
  const trashRoot2 = trashDir(agentsRoot, agent);
99002
99383
  mkdirSync58(trashRoot2, { recursive: true, mode: 493 });
99003
99384
  const ts = Date.now();
99004
- const trashTarget = join101(trashRoot2, `${name}-${ts}`);
99385
+ const trashTarget = join102(trashRoot2, `${name}-${ts}`);
99005
99386
  renameSync27(target, trashTarget);
99006
99387
  const now = new Date(ts);
99007
99388
  utimesSync(trashTarget, now, now);
@@ -99020,16 +99401,16 @@ function listPersonalAction(opts) {
99020
99401
  const agent = resolveAgent(opts);
99021
99402
  const agentsRoot = resolveAgentsRoot(opts);
99022
99403
  sweepTrash(agentsRoot, agent);
99023
- const skillsDir = join101(agentsRoot, agent, ".claude", "skills");
99404
+ const skillsDir = join102(agentsRoot, agent, ".claude", "skills");
99024
99405
  const personal = [];
99025
- if (existsSync102(skillsDir)) {
99406
+ if (existsSync103(skillsDir)) {
99026
99407
  for (const ent of readdirSync38(skillsDir, { withFileTypes: true })) {
99027
99408
  if (!ent.isDirectory())
99028
99409
  continue;
99029
99410
  if (!ent.name.startsWith(PERSONAL_PREFIX))
99030
99411
  continue;
99031
99412
  const skillName = ent.name.slice(PERSONAL_PREFIX.length);
99032
- const skillPath = join101(skillsDir, ent.name);
99413
+ const skillPath = join102(skillsDir, ent.name);
99033
99414
  let fileCount = 0;
99034
99415
  let totalBytes = 0;
99035
99416
  const walk2 = (sub) => {
@@ -99037,10 +99418,10 @@ function listPersonalAction(opts) {
99037
99418
  if (e.isFile()) {
99038
99419
  fileCount += 1;
99039
99420
  try {
99040
- totalBytes += statSync55(join101(sub, e.name)).size;
99421
+ totalBytes += statSync55(join102(sub, e.name)).size;
99041
99422
  } catch {}
99042
99423
  } else if (e.isDirectory()) {
99043
- walk2(join101(sub, e.name));
99424
+ walk2(join102(sub, e.name));
99044
99425
  }
99045
99426
  }
99046
99427
  };
@@ -99079,11 +99460,11 @@ function registerSkillPersonalCommands(program3) {
99079
99460
  // src/cli/self-improve-propose-skill.ts
99080
99461
  import { createConnection as createConnection4 } from "node:net";
99081
99462
  import { homedir as homedir55 } from "node:os";
99082
- import { join as join102 } from "node:path";
99083
- import { readFileSync as readFileSync89 } from "node:fs";
99463
+ import { join as join103 } from "node:path";
99464
+ import { readFileSync as readFileSync90 } from "node:fs";
99084
99465
  var IPC_CONNECT_TIMEOUT_MS = 5000;
99085
99466
  function gatewaySocketPath() {
99086
- return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join102(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join102(homedir55(), ".claude", "channels", "telegram", "gateway.sock"));
99467
+ return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join103(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join103(homedir55(), ".claude", "channels", "telegram", "gateway.sock"));
99087
99468
  }
99088
99469
  function fail5(msg, code = 1) {
99089
99470
  console.error(msg);
@@ -99118,7 +99499,7 @@ function registerSelfImproveProposeSkillCommand(program3) {
99118
99499
  fail5("agent name required (--agent or $SWITCHROOM_AGENT_NAME)");
99119
99500
  let draft;
99120
99501
  try {
99121
- draft = JSON.parse(readFileSync89(opts.draft, "utf-8"));
99502
+ draft = JSON.parse(readFileSync90(opts.draft, "utf-8"));
99122
99503
  } catch (e) {
99123
99504
  fail5(`failed to read/parse --draft: ${e.message}`);
99124
99505
  }
@@ -99155,9 +99536,9 @@ function registerSelfImproveProposeSkillCommand(program3) {
99155
99536
  init_esm();
99156
99537
  init_helpers();
99157
99538
  var import_yaml25 = __toESM(require_dist(), 1);
99158
- import { existsSync as existsSync103, readdirSync as readdirSync39, readFileSync as readFileSync90, statSync as statSync56 } from "node:fs";
99539
+ import { existsSync as existsSync104, readdirSync as readdirSync39, readFileSync as readFileSync91, statSync as statSync56 } from "node:fs";
99159
99540
  import { homedir as homedir56 } from "node:os";
99160
- import { join as join103, resolve as resolve60 } from "node:path";
99541
+ import { join as join104, resolve as resolve60 } from "node:path";
99161
99542
  var PERSONAL_PREFIX2 = "personal-";
99162
99543
  var BUNDLED_SUBDIR = "_bundled";
99163
99544
  var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
@@ -99171,12 +99552,12 @@ function defaultBundledRoot2() {
99171
99552
  return resolve60(homedir56(), ".switchroom/skills/_bundled");
99172
99553
  }
99173
99554
  function readSkillFrontmatter(skillDir) {
99174
- const mdPath = join103(skillDir, "SKILL.md");
99175
- if (!existsSync103(mdPath))
99555
+ const mdPath = join104(skillDir, "SKILL.md");
99556
+ if (!existsSync104(mdPath))
99176
99557
  return null;
99177
99558
  let content;
99178
99559
  try {
99179
- content = readFileSync90(mdPath, "utf-8");
99560
+ content = readFileSync91(mdPath, "utf-8");
99180
99561
  } catch {
99181
99562
  return null;
99182
99563
  }
@@ -99204,7 +99585,7 @@ function readSkillFrontmatter(skillDir) {
99204
99585
  return { fm: parsed };
99205
99586
  }
99206
99587
  function statSkillMd(skillDir) {
99207
- const mdPath = join103(skillDir, "SKILL.md");
99588
+ const mdPath = join104(skillDir, "SKILL.md");
99208
99589
  try {
99209
99590
  const st = statSync56(mdPath);
99210
99591
  return { size: st.size, mtime: st.mtime.toISOString() };
@@ -99215,8 +99596,8 @@ function statSkillMd(skillDir) {
99215
99596
  function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
99216
99597
  if (!AGENT_NAME_RE3.test(agent))
99217
99598
  return [];
99218
- const skillsDir = join103(agentsRoot, agent, ".claude/skills");
99219
- if (!existsSync103(skillsDir))
99599
+ const skillsDir = join104(agentsRoot, agent, ".claude/skills");
99600
+ if (!existsSync104(skillsDir))
99220
99601
  return [];
99221
99602
  const out = [];
99222
99603
  let entries;
@@ -99228,7 +99609,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
99228
99609
  for (const ent of entries) {
99229
99610
  if (!ent.startsWith(PERSONAL_PREFIX2))
99230
99611
  continue;
99231
- const dirPath = join103(skillsDir, ent);
99612
+ const dirPath = join104(skillsDir, ent);
99232
99613
  try {
99233
99614
  if (!statSync56(dirPath).isDirectory())
99234
99615
  continue;
@@ -99254,7 +99635,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
99254
99635
  return out;
99255
99636
  }
99256
99637
  function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
99257
- if (!existsSync103(sharedRoot))
99638
+ if (!existsSync104(sharedRoot))
99258
99639
  return [];
99259
99640
  const out = [];
99260
99641
  let entries;
@@ -99268,7 +99649,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
99268
99649
  continue;
99269
99650
  if (ent.startsWith("."))
99270
99651
  continue;
99271
- const dirPath = join103(sharedRoot, ent);
99652
+ const dirPath = join104(sharedRoot, ent);
99272
99653
  try {
99273
99654
  if (!statSync56(dirPath).isDirectory())
99274
99655
  continue;
@@ -99292,7 +99673,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
99292
99673
  return out;
99293
99674
  }
99294
99675
  function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
99295
- if (!existsSync103(bundledRoot))
99676
+ if (!existsSync104(bundledRoot))
99296
99677
  return [];
99297
99678
  const out = [];
99298
99679
  let entries;
@@ -99304,7 +99685,7 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
99304
99685
  for (const ent of entries) {
99305
99686
  if (ent.startsWith("."))
99306
99687
  continue;
99307
- const dirPath = join103(bundledRoot, ent);
99688
+ const dirPath = join104(bundledRoot, ent);
99308
99689
  try {
99309
99690
  if (!statSync56(dirPath).isDirectory())
99310
99691
  continue;
@@ -99450,7 +99831,7 @@ init_source();
99450
99831
  init_helpers();
99451
99832
  init_operator_uid();
99452
99833
  import {
99453
- existsSync as existsSync105,
99834
+ existsSync as existsSync106,
99454
99835
  mkdirSync as mkdirSync59,
99455
99836
  readdirSync as readdirSync40,
99456
99837
  writeFileSync as writeFileSync43,
@@ -99460,14 +99841,14 @@ import {
99460
99841
  copyFileSync as copyFileSync13
99461
99842
  } from "node:fs";
99462
99843
  import { homedir as homedir57 } from "node:os";
99463
- import { join as join104 } from "node:path";
99464
- import { spawnSync as spawnSync21 } from "node:child_process";
99844
+ import { join as join105 } from "node:path";
99845
+ import { spawnSync as spawnSync22 } from "node:child_process";
99465
99846
 
99466
99847
  // src/cli/singleton-stale-cleanup.ts
99467
- import { spawnSync as spawnSync20 } from "node:child_process";
99848
+ import { spawnSync as spawnSync21 } from "node:child_process";
99468
99849
  function makeDockerRunner() {
99469
99850
  return (args) => {
99470
- const r = spawnSync20("docker", args, { encoding: "utf8" });
99851
+ const r = spawnSync21("docker", args, { encoding: "utf8" });
99471
99852
  return {
99472
99853
  ok: r.status === 0,
99473
99854
  stdout: r.stdout ?? "",
@@ -99809,7 +100190,7 @@ function resolveHostdHostHome(env2 = process.env, home2 = homedir57()) {
99809
100190
  return resolved;
99810
100191
  }
99811
100192
  function resolveHostdSkillsTarget(hostHome) {
99812
- const skillsPath = join104(hostHome, ".switchroom", "skills");
100193
+ const skillsPath = join105(hostHome, ".switchroom", "skills");
99813
100194
  let st;
99814
100195
  try {
99815
100196
  st = lstatSync14(skillsPath);
@@ -99826,21 +100207,21 @@ function resolveHostdSkillsTarget(hostHome) {
99826
100207
  console.warn(`switchroom hostd install: ~/.switchroom/skills is a symlink whose target ` + `does not resolve (dangling) \u2014 skipping the skills bind mount. Bundled ` + `skills will be unavailable to rollout/update until the symlink is fixed.`);
99827
100208
  return;
99828
100209
  }
99829
- if (!existsSync105(target)) {
100210
+ if (!existsSync106(target)) {
99830
100211
  console.warn(`switchroom hostd install: ~/.switchroom/skills resolves to "${target}", ` + `which does not exist \u2014 skipping the skills bind mount. Bundled skills ` + `will be unavailable to rollout/update until the symlink target exists.`);
99831
100212
  return;
99832
100213
  }
99833
100214
  return target;
99834
100215
  }
99835
100216
  function hostdDir() {
99836
- return join104(homedir57(), ".switchroom", "hostd");
100217
+ return join105(homedir57(), ".switchroom", "hostd");
99837
100218
  }
99838
100219
  function hostdComposePath() {
99839
- return join104(hostdDir(), "docker-compose.yml");
100220
+ return join105(hostdDir(), "docker-compose.yml");
99840
100221
  }
99841
100222
  function backupExistingCompose() {
99842
100223
  const p = hostdComposePath();
99843
- if (!existsSync105(p))
100224
+ if (!existsSync106(p))
99844
100225
  return null;
99845
100226
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
99846
100227
  const bak = `${p}.bak-${ts}`;
@@ -99848,7 +100229,7 @@ function backupExistingCompose() {
99848
100229
  return bak;
99849
100230
  }
99850
100231
  function runDocker(args) {
99851
- const r = spawnSync21("docker", args, { encoding: "utf8" });
100232
+ const r = spawnSync22("docker", args, { encoding: "utf8" });
99852
100233
  return {
99853
100234
  ok: r.status === 0,
99854
100235
  stdout: r.stdout ?? "",
@@ -99938,7 +100319,7 @@ function doStatus() {
99938
100319
  const composeYml = hostdComposePath();
99939
100320
  console.log(source_default.bold("switchroom-hostd"));
99940
100321
  console.log("");
99941
- if (!existsSync105(composeYml)) {
100322
+ if (!existsSync106(composeYml)) {
99942
100323
  console.log(source_default.yellow(" compose: not installed"));
99943
100324
  console.log(source_default.dim(" run `switchroom hostd install` to set up."));
99944
100325
  return;
@@ -99959,14 +100340,14 @@ function doStatus() {
99959
100340
  } else {
99960
100341
  console.log(source_default.green(` container: ${ps.stdout.trim()}`));
99961
100342
  }
99962
- if (existsSync105(dir)) {
100343
+ if (existsSync106(dir)) {
99963
100344
  const entries = [];
99964
100345
  try {
99965
100346
  for (const name of readdirSync40(dir)) {
99966
100347
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
99967
100348
  continue;
99968
- const sockPath = join104(dir, name, "sock");
99969
- if (existsSync105(sockPath)) {
100349
+ const sockPath = join105(dir, name, "sock");
100350
+ if (existsSync106(sockPath)) {
99970
100351
  const st = statSync57(sockPath);
99971
100352
  if ((st.mode & 61440) === 49152) {
99972
100353
  entries.push(`${name} \u2192 ${sockPath}`);
@@ -99985,7 +100366,7 @@ function doStatus() {
99985
100366
  }
99986
100367
  function doUninstall() {
99987
100368
  const composeYml = hostdComposePath();
99988
- if (!existsSync105(composeYml)) {
100369
+ if (!existsSync106(composeYml)) {
99989
100370
  console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
99990
100371
  return;
99991
100372
  }
@@ -100009,7 +100390,7 @@ function registerHostdCommand(program3) {
100009
100390
  hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
100010
100391
  hostd.command("audit").description("Tail and filter the hostd audit log (privileged-verb call history)").option("--tail <n>", "Number of matching entries to show (default: 50)", "50").option("--agent <name>", "Filter to a specific caller agent").option("--op <verb>", "Filter to a specific hostd verb (e.g. update_apply, agent_restart)").option("--error", "Show only failed (error/denied) entries").option("--verbose", "Show the captured stderr / error tail under each failed row").option("--path <file>", "Override audit log path (for debugging)").action((opts) => {
100011
100392
  const logPath = opts.path ?? defaultAuditLogPath2();
100012
- if (!existsSync105(logPath)) {
100393
+ if (!existsSync106(logPath)) {
100013
100394
  console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
100014
100395
  The log is created when hostd handles its first privileged-verb request.`));
100015
100396
  return;
@@ -100057,10 +100438,10 @@ The log is created when hostd handles its first privileged-verb request.`));
100057
100438
  init_source();
100058
100439
  init_helpers();
100059
100440
  init_operator_uid();
100060
- import { chownSync as chownSync10, existsSync as existsSync106, mkdirSync as mkdirSync60, writeFileSync as writeFileSync44, copyFileSync as copyFileSync14 } from "node:fs";
100441
+ import { chownSync as chownSync10, existsSync as existsSync107, mkdirSync as mkdirSync60, writeFileSync as writeFileSync44, copyFileSync as copyFileSync14 } from "node:fs";
100061
100442
  import { homedir as homedir58 } from "node:os";
100062
- import { join as join105 } from "node:path";
100063
- import { spawnSync as spawnSync22 } from "node:child_process";
100443
+ import { join as join106 } from "node:path";
100444
+ import { spawnSync as spawnSync23 } from "node:child_process";
100064
100445
  function resolveWebImageTag(explicitTag, release) {
100065
100446
  if (explicitTag)
100066
100447
  return explicitTag;
@@ -100159,14 +100540,14 @@ services:
100159
100540
  `;
100160
100541
  }
100161
100542
  function webdDir() {
100162
- return join105(homedir58(), ".switchroom", "web");
100543
+ return join106(homedir58(), ".switchroom", "web");
100163
100544
  }
100164
100545
  function webdComposePath() {
100165
- return join105(webdDir(), "docker-compose.yml");
100546
+ return join106(webdDir(), "docker-compose.yml");
100166
100547
  }
100167
100548
  function backupExistingCompose2() {
100168
100549
  const p = webdComposePath();
100169
- if (!existsSync106(p))
100550
+ if (!existsSync107(p))
100170
100551
  return null;
100171
100552
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
100172
100553
  const bak = `${p}.bak-${ts}`;
@@ -100174,7 +100555,7 @@ function backupExistingCompose2() {
100174
100555
  return bak;
100175
100556
  }
100176
100557
  function runDocker2(args) {
100177
- const r = spawnSync22("docker", args, { encoding: "utf8" });
100558
+ const r = spawnSync23("docker", args, { encoding: "utf8" });
100178
100559
  return {
100179
100560
  ok: r.status === 0,
100180
100561
  stdout: r.stdout ?? "",
@@ -100268,7 +100649,7 @@ function doStatus2() {
100268
100649
  const composeYml = webdComposePath();
100269
100650
  console.log(source_default.bold("switchroom-web"));
100270
100651
  console.log("");
100271
- if (!existsSync106(composeYml)) {
100652
+ if (!existsSync107(composeYml)) {
100272
100653
  console.log(source_default.yellow(" compose: not installed"));
100273
100654
  console.log(source_default.dim(" run `switchroom webd install` to set up."));
100274
100655
  return;
@@ -100292,7 +100673,7 @@ function doStatus2() {
100292
100673
  }
100293
100674
  function doUninstall2() {
100294
100675
  const composeYml = webdComposePath();
100295
- if (!existsSync106(composeYml)) {
100676
+ if (!existsSync107(composeYml)) {
100296
100677
  console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
100297
100678
  return;
100298
100679
  }
@@ -100322,9 +100703,9 @@ function registerWebdCommand(program3) {
100322
100703
  // src/cli/host-repair.ts
100323
100704
  init_source();
100324
100705
  import { homedir as homedir59 } from "node:os";
100325
- import { join as join106 } from "node:path";
100706
+ import { join as join107 } from "node:path";
100326
100707
  var ARTIFACT_ALLOWLIST = {
100327
- dockerComposePluginDir: (home2) => join106(home2, ".docker", "cli-plugins", "docker-compose"),
100708
+ dockerComposePluginDir: (home2) => join107(home2, ".docker", "cli-plugins", "docker-compose"),
100328
100709
  stateSentinel: "/state"
100329
100710
  };
100330
100711
  function isStateBogusAutoDir(probe2) {
@@ -100567,13 +100948,13 @@ init_helpers();
100567
100948
  init_scan();
100568
100949
 
100569
100950
  // src/fleet-health/gh-sync.ts
100570
- import { spawnSync as spawnSync23 } from "node:child_process";
100951
+ import { spawnSync as spawnSync24 } from "node:child_process";
100571
100952
  var LABEL = "fleet-health";
100572
100953
  function defaultGhDeps(log) {
100573
100954
  return {
100574
100955
  log,
100575
100956
  run: (args) => {
100576
- const r = spawnSync23("gh", args, { encoding: "utf-8" });
100957
+ const r = spawnSync24("gh", args, { encoding: "utf-8" });
100577
100958
  return {
100578
100959
  ok: r.status === 0,
100579
100960
  stdout: (r.stdout ?? "").trim(),
@@ -100778,9 +101159,9 @@ function printDeepDiveBrief(targets) {
100778
101159
 
100779
101160
  // src/cli/hindsight-watch.ts
100780
101161
  init_source();
100781
- import { existsSync as existsSync111, readdirSync as readdirSync45 } from "node:fs";
101162
+ import { existsSync as existsSync112, readdirSync as readdirSync45 } from "node:fs";
100782
101163
  import { homedir as homedir62 } from "node:os";
100783
- import { join as join108, resolve as resolve63 } from "node:path";
101164
+ import { join as join109, resolve as resolve63 } from "node:path";
100784
101165
 
100785
101166
  // src/host-control/config-degraded.ts
100786
101167
  import { connect as connect3 } from "node:net";
@@ -100848,8 +101229,8 @@ function postOperatorNoticeViaGateways(candidates, text, log, connectTimeoutMs =
100848
101229
  init_install_cron();
100849
101230
 
100850
101231
  // src/hindsight-watch/probe.ts
100851
- import { spawnSync as spawnSync24 } from "node:child_process";
100852
- import { readFileSync as readFileSync95, readdirSync as readdirSync44, statSync as statSync59 } from "node:fs";
101232
+ import { spawnSync as spawnSync25 } from "node:child_process";
101233
+ import { readFileSync as readFileSync96, readdirSync as readdirSync44, statSync as statSync59 } from "node:fs";
100853
101234
  import { homedir as homedir61 } from "node:os";
100854
101235
  import { resolve as resolve62 } from "node:path";
100855
101236
 
@@ -100977,16 +101358,16 @@ function readLlmSignals(series) {
100977
101358
  }
100978
101359
 
100979
101360
  // src/hindsight-watch/recall-log.ts
100980
- import { closeSync as closeSync21, existsSync as existsSync110, openSync as openSync21, readdirSync as readdirSync43, readSync as readSync5, statSync as statSync58 } from "node:fs";
100981
- import { join as join107 } from "node:path";
101361
+ import { closeSync as closeSync21, existsSync as existsSync111, openSync as openSync21, readdirSync as readdirSync43, readSync as readSync5, statSync as statSync58 } from "node:fs";
101362
+ import { join as join108 } from "node:path";
100982
101363
  var RECALL_WINDOW_ROWS = 200;
100983
101364
  var RECALL_MAX_TAIL_BYTES = 1024 * 1024;
100984
101365
  var RECALL_MAX_ROW_AGE_MS = 24 * 60 * 60 * 1000;
100985
101366
  function recallLogPath2(agentDir) {
100986
- return join107(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
101367
+ return join108(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
100987
101368
  }
100988
101369
  function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
100989
- if (!existsSync110(path10))
101370
+ if (!existsSync111(path10))
100990
101371
  return [];
100991
101372
  let text;
100992
101373
  let truncatedHead = false;
@@ -101131,7 +101512,7 @@ function probeRecallLogs(agentsDir, now, windowRows = RECALL_WINDOW_ROWS, maxAge
101131
101512
  const all = [];
101132
101513
  let agents = 0;
101133
101514
  for (const name of names) {
101134
- const rows = withinWindow(readRecallLogTail2(recallLogPath2(join107(agentsDir, name)), windowRows), now, maxAgeMs);
101515
+ const rows = withinWindow(readRecallLogTail2(recallLogPath2(join108(agentsDir, name)), windowRows), now, maxAgeMs);
101135
101516
  if (rows.length > 0)
101136
101517
  agents++;
101137
101518
  all.push(...rows);
@@ -101198,7 +101579,7 @@ async function probeMetrics(url = DEFAULT_METRICS_URL, fetchImpl = fetch) {
101198
101579
  }
101199
101580
  }
101200
101581
  var defaultRunner4 = (cmd, args) => {
101201
- const r = spawnSync24(cmd, args, { stdio: "pipe", timeout: 1e4, encoding: "utf8" });
101582
+ const r = spawnSync25(cmd, args, { stdio: "pipe", timeout: 1e4, encoding: "utf8" });
101202
101583
  if (r.error)
101203
101584
  return { status: null, stdout: "", stderr: r.error.message };
101204
101585
  return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
@@ -101226,7 +101607,7 @@ function readDropCount(hindsightDir) {
101226
101607
  try {
101227
101608
  if (statSync59(path10).size > DROPS_LEDGER_MAX_BYTES)
101228
101609
  return 0;
101229
- parsed = JSON.parse(readFileSync95(path10, "utf8"));
101610
+ parsed = JSON.parse(readFileSync96(path10, "utf8"));
101230
101611
  } catch {
101231
101612
  return 0;
101232
101613
  }
@@ -101926,7 +102307,7 @@ function registerHindsightWatchCommand(program3) {
101926
102307
  process.exitCode = runInstallCron(opts.cronUser);
101927
102308
  return;
101928
102309
  }
101929
- const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ?? join108(process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir62(), ".switchroom", "agents");
102310
+ const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ?? join109(process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir62(), ".switchroom", "agents");
101930
102311
  const statePath = opts.state ?? defaultStatePath2();
101931
102312
  const log = (m) => {
101932
102313
  process.stderr.write(`${m}
@@ -102018,7 +102399,7 @@ async function notifyOperator(agentsDir, text, log) {
102018
102399
  try {
102019
102400
  for (const name of readdirSync45(agentsDir).sort()) {
102020
102401
  const sock = resolve63(agentsDir, name, "telegram", "gateway.sock");
102021
- if (existsSync111(sock))
102402
+ if (existsSync112(sock))
102022
102403
  candidates.push({ agent: name, sock });
102023
102404
  }
102024
102405
  } catch (e) {