switchroom 0.18.17 → 0.18.19

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 (67) hide show
  1. package/dist/agent-scheduler/index.js +13 -0
  2. package/dist/auth-broker/index.js +13 -0
  3. package/dist/cli/notion-write-pretool.mjs +13 -0
  4. package/dist/cli/switchroom.js +605 -479
  5. package/dist/host-control/main.js +17 -1
  6. package/dist/vault/approvals/kernel-server.js +13 -0
  7. package/dist/vault/broker/server.js +13 -0
  8. package/package.json +1 -1
  9. package/telegram-plugin/bridge/bridge.ts +7 -1
  10. package/telegram-plugin/dist/bridge/bridge.js +26 -1
  11. package/telegram-plugin/dist/gateway/gateway.js +1544 -619
  12. package/telegram-plugin/dist/server.js +32 -1
  13. package/telegram-plugin/fleet-fallback-resume.ts +26 -3
  14. package/telegram-plugin/format.ts +137 -213
  15. package/telegram-plugin/gateway/approval-hold.ts +49 -0
  16. package/telegram-plugin/gateway/bridge-dead-watchdog.ts +61 -18
  17. package/telegram-plugin/gateway/gateway.ts +399 -85
  18. package/telegram-plugin/gateway/linear-activity.ts +20 -4
  19. package/telegram-plugin/gateway/outbound-send-path.ts +9 -7
  20. package/telegram-plugin/gateway/premium-recovery-wiring.ts +122 -0
  21. package/telegram-plugin/gateway/session-model-file.ts +103 -0
  22. package/telegram-plugin/gateway/tier-downgrade-wiring.ts +121 -0
  23. package/telegram-plugin/gateway/unhandled-rejection-policy.ts +14 -1
  24. package/telegram-plugin/llm-error-present.ts +474 -0
  25. package/telegram-plugin/operator-events.ts +7 -1
  26. package/telegram-plugin/permission-title.ts +172 -10
  27. package/telegram-plugin/premium-recovery.ts +101 -0
  28. package/telegram-plugin/raw-error-scrub.ts +73 -0
  29. package/telegram-plugin/retry-api-call.ts +8 -2
  30. package/telegram-plugin/send-gate-degraded.test.ts +152 -1
  31. package/telegram-plugin/send-gate-observability.test.ts +140 -0
  32. package/telegram-plugin/send-gate-observability.ts +65 -20
  33. package/telegram-plugin/send-gate.test.ts +143 -1
  34. package/telegram-plugin/send-gate.ts +212 -19
  35. package/telegram-plugin/session-tail.ts +16 -0
  36. package/telegram-plugin/shared/local-time.ts +69 -0
  37. package/telegram-plugin/stream-reply-handler.ts +5 -14
  38. package/telegram-plugin/tests/approval-hold-harness.ts +6 -6
  39. package/telegram-plugin/tests/approval-hold-outcome.test.ts +10 -2
  40. package/telegram-plugin/tests/bridge-dead-watchdog.test.ts +61 -0
  41. package/telegram-plugin/tests/fleet-fallback-resume.test.ts +39 -0
  42. package/telegram-plugin/tests/flood-windows-persistence.test.ts +3 -2
  43. package/telegram-plugin/tests/format-consistency.test.ts +68 -53
  44. package/telegram-plugin/tests/formatting-parse-regression.test.ts +5 -6
  45. package/telegram-plugin/tests/formatting-torture-set.ts +1 -1
  46. package/telegram-plugin/tests/gateway-outbound-redact.test.ts +26 -0
  47. package/telegram-plugin/tests/linear-create-issue.test.ts +30 -2
  48. package/telegram-plugin/tests/llm-error-present.test.ts +481 -0
  49. package/telegram-plugin/tests/outbound-send-path.test.ts +4 -3
  50. package/telegram-plugin/tests/paragraph-normalizer.test.ts +42 -100
  51. package/telegram-plugin/tests/permission-title.test.ts +167 -4
  52. package/telegram-plugin/tests/premium-recovery-wiring.test.ts +150 -0
  53. package/telegram-plugin/tests/premium-recovery.test.ts +165 -0
  54. package/telegram-plugin/tests/reaction-gate-routing.test.ts +6 -1
  55. package/telegram-plugin/tests/retry-api-call.test.ts +21 -0
  56. package/telegram-plugin/tests/stream-reply-handler.test.ts +9 -12
  57. package/telegram-plugin/tests/telegram-format.test.ts +86 -31
  58. package/telegram-plugin/tests/tier-downgrade-wiring.test.ts +165 -0
  59. package/telegram-plugin/tests/tier-downgrade.test.ts +141 -0
  60. package/telegram-plugin/tests/turn-flush-safety.test.ts +17 -21
  61. package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +27 -1
  62. package/telegram-plugin/tests/worker-activity-feed.test.ts +5 -2
  63. package/telegram-plugin/tests/worker-feed-coalesce.test.ts +492 -0
  64. package/telegram-plugin/tier-downgrade.ts +198 -0
  65. package/telegram-plugin/tool-activity-summary.ts +99 -0
  66. package/telegram-plugin/turn-flush-safety.ts +4 -3
  67. package/telegram-plugin/worker-activity-feed.ts +509 -409
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
2120
2120
  });
2121
2121
 
2122
2122
  // src/build-info.ts
2123
- var VERSION = "0.18.17", COMMIT_SHA = "372a4968";
2123
+ var VERSION = "0.18.19", COMMIT_SHA = "34c72776";
2124
2124
 
2125
2125
  // src/cli/resolve-version.ts
2126
2126
  import { existsSync, readFileSync } from "node:fs";
@@ -13818,6 +13818,19 @@ var init_schema = __esm(() => {
13818
13818
  approval_timeout_minutes: exports_external.number().int().nonnegative().optional().describe("Operator approval-card lifetime (minutes) for the tool-use 'Allow " + "once' card and the vault grant decision wait. After this long with " + "no operator tap, the card auto-denies (a TIMEOUT, not a denial \u2014 the " + "agent is told not to retry). Default 60. hostd-gated verbs " + "(mcp__hostd__*) keep their own longer window; the hostd " + "config-propose card is not governed by this key."),
13819
13819
  sub_agent_tick_interval_ms: exports_external.number().int().nonnegative().optional().describe("Heartbeat tick interval (ms) for sub-agent rendering. Forces a " + "re-render of the elapsed-time counter while sub-agents are running, " + "even during silent stretches between tool calls. Default 10000 (10 s). " + "Set to 0 to disable the elapsed-ticker path."),
13820
13820
  edit_budget_threshold: exports_external.number().int().nonnegative().optional().describe("Telegram API edit budget per minute before the progress-card driver " + "falls back to a slower coalesce window. When a chat accumulates more " + "than this many card edits in the trailing 60 s, the driver switches " + "to a wider coalesce interval until the rate drops back. Default 18. " + "Increase if your gateway frequently bumps the Telegram edit-rate ceiling " + "with many parallel sub-agents; decrease for a more conservative buffer."),
13821
+ send_gate: exports_external.object({
13822
+ enabled: exports_external.boolean().optional().describe("Master switch for the deterministic outbound send gate " + "(telegram-plugin/send-gate.ts) \u2014 the token-bucket scheduler every " + "Bot API call transits so per-surface throttles can't add up past a " + "flood ceiling. ON by default. Precedence: the operator break-glass " + "env var SWITCHROOM_TELEGRAM_SEND_GATE (0/false/off/no) ALWAYS wins " + "when explicitly set; this key only decides when that env var is " + "unset. Omit to keep the gate on."),
13823
+ global_per_sec: exports_external.number().positive().optional().describe("Global bucket sustained rate (Bot API calls/sec across ALL chats). " + "Default 25 (headroom under Telegram's ~30/s). Must be > 0 \u2014 a zero " + "or negative rate would wedge all outbound sends. Omit to keep 25."),
13824
+ global_burst: exports_external.number().int().positive().describe("Global bucket burst capacity. Default 4; worst-case 1s window " + "admits global_burst + global_per_sec = 29 < 30, a real margin under " + "the ceiling. Must be an integer >= 1 (a 0 capacity never admits a " + "token and wedges sends). Omit to keep 4.").optional(),
13825
+ per_chat_per_sec: exports_external.number().positive().optional().describe("Per-chat sustained rate (calls/sec to a single chat). Default 1. " + "Must be > 0 (zero/negative wedges that chat). Omit to keep 1."),
13826
+ per_chat_burst: exports_external.number().int().positive().optional().describe("Per-chat burst capacity. Default 3. Must be an integer >= 1 " + "(0 wedges the chat's bucket). Omit to keep 3."),
13827
+ per_group_per_min: exports_external.number().positive().optional().describe("Per-group sustained rate (calls/min to a single group/supergroup). " + "Default 18 (headroom under Telegram's ~20/min group ceiling). Must " + "be > 0. Omit to keep 18."),
13828
+ per_group_burst: exports_external.number().int().positive().optional().describe("Per-group burst capacity. Default 2. Must be an integer >= 1 " + "(0 wedges the group's bucket). Omit to keep 2."),
13829
+ edit_floor_ms: exports_external.number().int().nonnegative().optional().describe("Minimum ms between successive edits of the SAME message_id " + "(last-write-wins coalescing enforces this floor). Default 1500 " + "(Telegram's ~1 edit/sec/message practical ceiling). 0 disables the " + "floor. Must be an integer >= 0. Omit to keep 1500.")
13830
+ }).optional().describe("Tunable rate limits for the deterministic outbound send gate " + "(telegram-plugin/send-gate.ts). Every key is optional and defaults to " + "the send gate's built-in value, so omitting the whole block reproduces " + "today's exact behaviour \u2014 this is pure operator tuning, no default is " + "changed. Cascades from defaults.channels.telegram.send_gate."),
13831
+ worker_feed: exports_external.object({
13832
+ max_rows: exports_external.number().int().positive().optional().describe("Max live-worker rows rendered in the COMBINED worker-activity feed " + "(2+ background workers in one chat/thread coalesce into ONE message; " + "telegram-plugin/worker-activity-feed.ts) before a compact " + "'+M more working\u2026' spill line \u2014 keeps the coalesced body compact and " + "legible (and under the rich-message wire ceiling). Default 8. A " + "single-worker chat renders the full \uD83D\uDEE0 Worker card and ignores this. " + "Must be an integer >= 1. Omit to keep 8.")
13833
+ }).optional().describe("Tuning for the coalesced worker-activity feed " + "(telegram-plugin/worker-activity-feed.ts). Cascades from " + "defaults.channels.telegram.worker_feed."),
13821
13834
  stickers: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Sticker aliases for the `send_sticker` MCP tool (#576). Maps a " + "short alias name (e.g. 'happy', 'thinking') to a Telegram file_id. " + "Operator-curated \u2014 capture file_ids from inbound stickers the user " + "sends and add them here. The agent calls send_sticker(chat_id, " + "alias='happy') and the gateway resolves to the file_id at send " + "time. Aliases enable persona-flavored expressiveness without " + "exposing raw file_ids in the agent prompt. Personal-assistant / " + "health-coach personas benefit; coding agents typically don't " + "configure any."),
13822
13835
  voice_in: exports_external.object({
13823
13836
  enabled: exports_external.boolean().optional().describe("Master switch for voice-message transcription."),
@@ -17116,18 +17129,18 @@ function alignAgentTreeOwnershipIfRoot(name, agentDir) {
17116
17129
  }
17117
17130
  return uid;
17118
17131
  }
17119
- function findAgentUnreadablePaths(candidates, agentUid) {
17132
+ function findAgentUnreadablePaths(candidates, agentUid, fs = { lstatSync, statSync: statSync3 }) {
17120
17133
  const bad = [];
17121
17134
  for (const p of candidates) {
17122
17135
  let ls;
17123
17136
  try {
17124
- ls = lstatSync(p);
17137
+ ls = fs.lstatSync(p);
17125
17138
  } catch {
17126
17139
  continue;
17127
17140
  }
17128
17141
  if (ls.isSymbolicLink())
17129
17142
  continue;
17130
- const st = statSync3(p);
17143
+ const st = fs.statSync(p);
17131
17144
  if (st.uid === agentUid)
17132
17145
  continue;
17133
17146
  const otherReadable = st.isDirectory() ? (st.mode & 5) === 5 : (st.mode & 4) === 4;
@@ -26686,6 +26699,37 @@ function channelsToEnv(agent) {
26686
26699
  if (tg.edit_budget_threshold !== undefined) {
26687
26700
  out.SWITCHROOM_TG_EDIT_BUDGET_THRESHOLD = String(tg.edit_budget_threshold);
26688
26701
  }
26702
+ const sg = tg.send_gate;
26703
+ if (sg) {
26704
+ if (sg.enabled !== undefined) {
26705
+ out.SWITCHROOM_TG_SEND_GATE_ENABLED = sg.enabled ? "1" : "0";
26706
+ }
26707
+ if (sg.global_per_sec !== undefined) {
26708
+ out.SWITCHROOM_TG_SEND_GATE_GLOBAL_PER_SEC = String(sg.global_per_sec);
26709
+ }
26710
+ if (sg.global_burst !== undefined) {
26711
+ out.SWITCHROOM_TG_SEND_GATE_GLOBAL_BURST = String(sg.global_burst);
26712
+ }
26713
+ if (sg.per_chat_per_sec !== undefined) {
26714
+ out.SWITCHROOM_TG_SEND_GATE_PER_CHAT_PER_SEC = String(sg.per_chat_per_sec);
26715
+ }
26716
+ if (sg.per_chat_burst !== undefined) {
26717
+ out.SWITCHROOM_TG_SEND_GATE_PER_CHAT_BURST = String(sg.per_chat_burst);
26718
+ }
26719
+ if (sg.per_group_per_min !== undefined) {
26720
+ out.SWITCHROOM_TG_SEND_GATE_PER_GROUP_PER_MIN = String(sg.per_group_per_min);
26721
+ }
26722
+ if (sg.per_group_burst !== undefined) {
26723
+ out.SWITCHROOM_TG_SEND_GATE_PER_GROUP_BURST = String(sg.per_group_burst);
26724
+ }
26725
+ if (sg.edit_floor_ms !== undefined) {
26726
+ out.SWITCHROOM_TG_SEND_GATE_EDIT_FLOOR_MS = String(sg.edit_floor_ms);
26727
+ }
26728
+ }
26729
+ const wf = tg?.worker_feed;
26730
+ if (wf?.max_rows !== undefined) {
26731
+ out.SWITCHROOM_TG_WORKER_FEED_MAX_ROWS = String(wf.max_rows);
26732
+ }
26689
26733
  if (tg.clear_status_on_completion !== undefined) {
26690
26734
  out.SWITCHROOM_TG_CLEAR_STATUS_ON_COMPLETION = tg.clear_status_on_completion ? "1" : "0";
26691
26735
  }
@@ -29769,6 +29813,7 @@ function generateCompose(opts) {
29769
29813
  const hostControlEnabled = config.host_control?.enabled !== false;
29770
29814
  const hostHomeForChecks = opts.homeDir ?? process.env.HOME ?? "";
29771
29815
  const probeHome = opts.probeHomeDir ?? hostHomeForChecks;
29816
+ const precreateHostDirs = opts.precreateHostDirs ?? (opts.homeDir !== undefined || opts.probeHomeDir !== undefined);
29772
29817
  const switchroomConfigPath = resolveConfigMountSource(opts.switchroomConfigPath, homePrefix);
29773
29818
  const bundledSkillsPoolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
29774
29819
  const voiceEngine = opts.voiceEngine ?? loadHostCapabilities()?.voice.engine ?? "cloud";
@@ -29973,7 +30018,7 @@ function generateCompose(opts) {
29973
30018
  telemetryDisabled,
29974
30019
  posthogKeyOverride,
29975
30020
  posthogHostOverride
29976
- }, bundledSkillsPoolDir, hostControlEnabled, opts.operatorUid, voiceEngine);
30021
+ }, bundledSkillsPoolDir, hostControlEnabled, opts.operatorUid, voiceEngine, precreateHostDirs);
29977
30022
  }
29978
30023
  lines.push(`volumes:`);
29979
30024
  for (const a of describeAgents(config, opts.litellmConfirmedAgents)) {
@@ -30007,7 +30052,7 @@ function generateCompose(opts) {
30007
30052
  return lines.join(`
30008
30053
  `);
30009
30054
  }
30010
- function emitAgentService(lines, a, imageTag, buildMode, buildContext, homePrefix, hostHomeForChecks, probeHome, switchroomConfigPath, containerNamePrefix, posthog, bundledSkillsPoolDir, hostControlEnabled, operatorUid, voiceEngine) {
30055
+ function emitAgentService(lines, a, imageTag, buildMode, buildContext, homePrefix, hostHomeForChecks, probeHome, switchroomConfigPath, containerNamePrefix, posthog, bundledSkillsPoolDir, hostControlEnabled, operatorUid, voiceEngine, precreateHostDirs) {
30011
30056
  lines.push(` agent-${a.name}:`);
30012
30057
  emitImageOrBuild(lines, "agent", imageTag, buildMode, buildContext);
30013
30058
  lines.push(` container_name: ${containerNamePrefix}-${a.name}`);
@@ -30170,22 +30215,30 @@ function emitAgentService(lines, a, imageTag, buildMode, buildContext, homePrefi
30170
30215
  if (conditionalMountPresent(`${probeHome}/.switchroom/credentials/${a.name}`, hostHomeForChecks, probeHome)) {
30171
30216
  lines.push(` - ${homePrefix}/.switchroom/credentials/${a.name}:${homePrefix}/.switchroom/credentials:ro`);
30172
30217
  }
30173
- try {
30174
- mkdirSync12(`${probeHome}/.switchroom/audit/${a.name}`, { recursive: true });
30175
- } catch {}
30176
- try {
30177
- const blockedDir = `${probeHome}/.switchroom/blocked-approvals`;
30178
- mkdirSync12(blockedDir, { recursive: true });
30179
- chmodSync3(blockedDir, 1023);
30180
- } catch {}
30181
- try {
30182
- mkdirSync12(`${probeHome}/.switchroom/agents/${a.name}/schedule.d`, { recursive: true });
30183
- } catch {}
30184
- lines.push(` - ${homePrefix}/.switchroom/audit/${a.name}:${homePrefix}/.switchroom/audit/${a.name}:rw`);
30185
- if (conditionalMountPresent(`${probeHome}/.switchroom-config`, hostHomeForChecks, probeHome)) {
30218
+ if (precreateHostDirs) {
30186
30219
  try {
30187
- mkdirSync12(`${probeHome}/.switchroom-config/agents/${a.name}/personal-skills`, { recursive: true });
30220
+ mkdirSync12(`${probeHome}/.switchroom/audit/${a.name}`, { recursive: true });
30188
30221
  } catch {}
30222
+ }
30223
+ if (precreateHostDirs) {
30224
+ try {
30225
+ const blockedDir = `${probeHome}/.switchroom/blocked-approvals`;
30226
+ mkdirSync12(blockedDir, { recursive: true });
30227
+ chmodSync3(blockedDir, 1023);
30228
+ } catch {}
30229
+ }
30230
+ if (precreateHostDirs) {
30231
+ try {
30232
+ mkdirSync12(`${probeHome}/.switchroom/agents/${a.name}/schedule.d`, { recursive: true });
30233
+ } catch {}
30234
+ }
30235
+ lines.push(` - ${homePrefix}/.switchroom/audit/${a.name}:${homePrefix}/.switchroom/audit/${a.name}:rw`);
30236
+ if (conditionalMountPresent(`${probeHome}/.switchroom-config`, hostHomeForChecks, probeHome)) {
30237
+ if (precreateHostDirs) {
30238
+ try {
30239
+ mkdirSync12(`${probeHome}/.switchroom-config/agents/${a.name}/personal-skills`, { recursive: true });
30240
+ } catch {}
30241
+ }
30189
30242
  lines.push(` - ${homePrefix}/.switchroom-config/agents/${a.name}/personal-skills:${homePrefix}/.switchroom-config/agents/${a.name}/personal-skills:rw`);
30190
30243
  }
30191
30244
  if (existsSync18(`${probeHome}/.switchroom/bin/webkite`)) {
@@ -43713,6 +43766,74 @@ function runCredentialsMigrationChecks(config, deps = {}) {
43713
43766
  }
43714
43767
  var init_doctor_credentials_migration = () => {};
43715
43768
 
43769
+ // src/cli/doctor-agent-dotfile-ownership.ts
43770
+ import { existsSync as existsSync62, lstatSync as lstatSync8, statSync as statSync36 } from "node:fs";
43771
+ import { join as join61 } from "node:path";
43772
+ function agentDotfileCandidates(agentDir) {
43773
+ return [
43774
+ join61(agentDir, ".claude", "settings.json"),
43775
+ join61(agentDir, ".claude.json"),
43776
+ join61(agentDir, ".claude", ".claude.json"),
43777
+ join61(agentDir, ".mcp.json"),
43778
+ join61(agentDir, ".claude-cron", ".mcp.json")
43779
+ ];
43780
+ }
43781
+ function runAgentDotfileOwnershipChecks(config, deps = {}) {
43782
+ const results = [];
43783
+ const agents = Object.keys(config.agents ?? {});
43784
+ if (agents.length === 0)
43785
+ return results;
43786
+ const exists = deps.existsSync ?? existsSync62;
43787
+ const scanFs = deps.scanFs ?? { lstatSync: lstatSync8, statSync: statSync36 };
43788
+ let agentsDir = deps.agentsDir;
43789
+ if (agentsDir === undefined) {
43790
+ try {
43791
+ agentsDir = resolveAgentsDir(config);
43792
+ } catch {
43793
+ agentsDir = undefined;
43794
+ }
43795
+ }
43796
+ if (agentsDir === undefined) {
43797
+ return [
43798
+ {
43799
+ name: "agent dotfile ownership",
43800
+ status: "warn",
43801
+ detail: "could not resolve the agents directory (no switchroom block) \u2014 skipped dotfile-ownership check"
43802
+ }
43803
+ ];
43804
+ }
43805
+ for (const name of agents) {
43806
+ const agentDir = join61(agentsDir, name);
43807
+ if (!exists(agentDir)) {
43808
+ continue;
43809
+ }
43810
+ const isRoot = config.agents[name]?.root === true;
43811
+ const runtimeUid = isRoot ? 0 : allocateAgentUid(name);
43812
+ const candidates = agentDotfileCandidates(agentDir);
43813
+ const unreadable = findAgentUnreadablePaths(candidates, runtimeUid, scanFs);
43814
+ if (unreadable.length === 0) {
43815
+ results.push({
43816
+ name: `dotfile ownership: ${name}`,
43817
+ status: "ok",
43818
+ detail: isRoot ? `runs as uid 0 (root-tier) \u2014 dotfiles readable` : `dotfiles readable by agent uid ${runtimeUid}`
43819
+ });
43820
+ continue;
43821
+ }
43822
+ results.push({
43823
+ name: `dotfile ownership: ${name}`,
43824
+ status: "fail",
43825
+ detail: `${unreadable.length} dotfile(s) not readable by the agent's runtime ` + `uid ${runtimeUid}: ${unreadable.join(", ")}. Claude Code loads no ` + `permission allowlist from an unreadable settings.json \u2192 the agent ` + `prompts the operator to approve every tool call (card storm).`,
43826
+ fix: `sudo chown -h -R ${runtimeUid}:${runtimeUid} ${agentDir} # then \`switchroom agent restart ${name}\``
43827
+ });
43828
+ }
43829
+ return results;
43830
+ }
43831
+ var init_doctor_agent_dotfile_ownership = __esm(() => {
43832
+ init_loader();
43833
+ init_agent_uid();
43834
+ init_agent_owned_tree();
43835
+ });
43836
+
43716
43837
  // src/cli/doctor-inlined-secrets.ts
43717
43838
  import { readFileSync as fsReadFileSync } from "node:fs";
43718
43839
  function isSecretShapedKey(key) {
@@ -43810,7 +43931,7 @@ var init_doctor_inlined_secrets = __esm(() => {
43810
43931
  // src/cli/doctor-approval-attribution.ts
43811
43932
  import { execFileSync as execFileSync19 } from "node:child_process";
43812
43933
  import { readFileSync as fsReadFileSync2 } from "node:fs";
43813
- import { join as join61 } from "node:path";
43934
+ import { join as join62 } from "node:path";
43814
43935
  function parseApproverSet(canonical) {
43815
43936
  try {
43816
43937
  const arr = JSON.parse(canonical);
@@ -43930,7 +44051,7 @@ function loadLiveAllowFromAccessFiles(agentNames, agentsDir, readFile2 = (p) =>
43930
44051
  let readAny = false;
43931
44052
  for (const name of agentNames) {
43932
44053
  try {
43933
- const raw = readFile2(join61(agentsDir, name, "telegram", "access.json"));
44054
+ const raw = readFile2(join62(agentsDir, name, "telegram", "access.json"));
43934
44055
  const parsed = JSON.parse(raw);
43935
44056
  readAny = true;
43936
44057
  if (Array.isArray(parsed.allowFrom)) {
@@ -43963,13 +44084,13 @@ var init_doctor_approval_attribution = __esm(() => {
43963
44084
  // src/cli/doctor-audit-integrity.ts
43964
44085
  import { readFileSync as fsReadFileSync3 } from "node:fs";
43965
44086
  import { homedir as homedir37 } from "node:os";
43966
- import { join as join62 } from "node:path";
44087
+ import { join as join63 } from "node:path";
43967
44088
  function rootWrittenLogs(home2) {
43968
44089
  return [
43969
- { label: "vault-broker", path: join62(home2, ".switchroom", "vault-audit.log") },
44090
+ { label: "vault-broker", path: join63(home2, ".switchroom", "vault-audit.log") },
43970
44091
  {
43971
44092
  label: "hostd",
43972
- path: join62(home2, ".switchroom", "host-control-audit.log")
44093
+ path: join63(home2, ".switchroom", "host-control-audit.log")
43973
44094
  }
43974
44095
  ];
43975
44096
  }
@@ -43978,7 +44099,7 @@ function runAuditIntegrityChecks(deps = {}) {
43978
44099
  const read = deps.readFileSync ?? ((p) => fsReadFileSync3(p, "utf8"));
43979
44100
  const readFailOpen = deps.readFailOpenState ?? readFailOpenState;
43980
44101
  const results = [];
43981
- const vaultAuditLog = join62(home2, ".switchroom", "vault-audit.log");
44102
+ const vaultAuditLog = join63(home2, ".switchroom", "vault-audit.log");
43982
44103
  const failOpen = readFailOpen(failOpenStatePath(vaultAuditLog));
43983
44104
  if (failOpen.failOpenCount > 0) {
43984
44105
  const when = failOpen.lastFailureTs ? ` (last at ${failOpen.lastFailureTs})` : ``;
@@ -44051,16 +44172,16 @@ var init_doctor_audit_integrity = __esm(() => {
44051
44172
  });
44052
44173
 
44053
44174
  // src/cli/doctor-agent-smoke.ts
44054
- import { existsSync as existsSync62 } from "node:fs";
44175
+ import { existsSync as existsSync63 } from "node:fs";
44055
44176
  import { homedir as homedir38 } from "node:os";
44056
- import { join as join63 } from "node:path";
44177
+ import { join as join64 } from "node:path";
44057
44178
  import { randomUUID as randomUUID5 } from "node:crypto";
44058
44179
  async function runAgentSmokeChecks(config, deps = {}) {
44059
44180
  if (deps.fast)
44060
44181
  return [];
44061
44182
  const home2 = deps.homeDir ?? homedir38();
44062
- const sock = deps.operatorSockPath ?? join63(home2, ".switchroom", "hostd", "operator", "sock");
44063
- if (!deps.hostdRequestImpl && !existsSync62(sock)) {
44183
+ const sock = deps.operatorSockPath ?? join64(home2, ".switchroom", "hostd", "operator", "sock");
44184
+ if (!deps.hostdRequestImpl && !existsSync63(sock)) {
44064
44185
  return [
44065
44186
  {
44066
44187
  name: "agent liveness",
@@ -44138,9 +44259,9 @@ var init_doctor_agent_smoke = __esm(() => {
44138
44259
 
44139
44260
  // src/cli/doctor-vault-broker-durability.ts
44140
44261
  import { execFileSync as execFileSync20 } from "node:child_process";
44141
- import { existsSync as existsSync63, statSync as statSync36 } from "node:fs";
44262
+ import { existsSync as existsSync64, statSync as statSync37 } from "node:fs";
44142
44263
  import { homedir as homedir39 } from "node:os";
44143
- import { join as join64 } from "node:path";
44264
+ import { join as join65 } from "node:path";
44144
44265
  function probeBindMountInode(hostPath, brokerContainerPath, opts) {
44145
44266
  const statHost = opts?.statHost ?? defaultStatHost;
44146
44267
  const statBroker = opts?.statBroker ?? defaultStatBroker;
@@ -44162,10 +44283,10 @@ function probeBindMountInode(hostPath, brokerContainerPath, opts) {
44162
44283
  };
44163
44284
  }
44164
44285
  function defaultStatHost(p) {
44165
- if (!existsSync63(p))
44286
+ if (!existsSync64(p))
44166
44287
  return null;
44167
44288
  try {
44168
- const s = statSync36(p, { bigint: true });
44289
+ const s = statSync37(p, { bigint: true });
44169
44290
  return { ino: s.ino, size: Number(s.size) };
44170
44291
  } catch {
44171
44292
  return null;
@@ -44289,16 +44410,16 @@ function runVaultBrokerDurabilityChecks(_config, opts) {
44289
44410
  probeBrokerUnlocked(opts?.statusProbe),
44290
44411
  probeAutoUnlockBlob(home2),
44291
44412
  probeMachineIdMount(),
44292
- formatBindMountResult("vault-broker: vault.enc bind mount", join64(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join64(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
44293
- formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join64(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join64(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
44294
- formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join64(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join64(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log")),
44413
+ formatBindMountResult("vault-broker: vault.enc bind mount", join65(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join65(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
44414
+ formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join65(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join65(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
44415
+ formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join65(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join65(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log")),
44295
44416
  probeKernelDbDurability(home2, {
44296
44417
  statBroker: opts?.kernelStatBroker
44297
44418
  })
44298
44419
  ];
44299
44420
  }
44300
44421
  function probeKernelDbDurability(home2, opts) {
44301
- const hostDir = join64(home2, ".switchroom", "approvals");
44422
+ const hostDir = join65(home2, ".switchroom", "approvals");
44302
44423
  const containerDir = "/state/approvals";
44303
44424
  const name = "approval-kernel: approvals bind mount (allow_always durability)";
44304
44425
  const kernelStat = opts?.statBroker ?? defaultKernelStatBroker;
@@ -44366,8 +44487,8 @@ function defaultKernelStatBroker(p) {
44366
44487
  return { kind: "ok-with-stat", ino: inoStr, size };
44367
44488
  }
44368
44489
  function probeAutoUnlockBlob(home2) {
44369
- const blobPath = join64(home2, ".switchroom", "vault-auto-unlock");
44370
- if (!existsSync63(blobPath)) {
44490
+ const blobPath = join65(home2, ".switchroom", "vault-auto-unlock");
44491
+ if (!existsSync64(blobPath)) {
44371
44492
  return {
44372
44493
  name: "vault-broker: auto-unlock blob",
44373
44494
  status: "warn",
@@ -44375,7 +44496,7 @@ function probeAutoUnlockBlob(home2) {
44375
44496
  fix: "Run `switchroom vault broker enable-auto-unlock` to seal the blob with the current passphrase + machine-id"
44376
44497
  };
44377
44498
  }
44378
- const sz = statSync36(blobPath).size;
44499
+ const sz = statSync37(blobPath).size;
44379
44500
  if (sz === 0) {
44380
44501
  return {
44381
44502
  name: "vault-broker: auto-unlock blob",
@@ -44391,7 +44512,7 @@ function probeAutoUnlockBlob(home2) {
44391
44512
  };
44392
44513
  }
44393
44514
  function probeMachineIdMount() {
44394
- const hostExists = existsSync63("/etc/machine-id");
44515
+ const hostExists = existsSync64("/etc/machine-id");
44395
44516
  if (!hostExists) {
44396
44517
  return {
44397
44518
  name: "vault-broker: machine-id passthrough",
@@ -44542,25 +44663,25 @@ import { Socket as Socket3 } from "node:net";
44542
44663
  import {
44543
44664
  accessSync as accessSync2,
44544
44665
  constants as fsConstants5,
44545
- existsSync as existsSync64,
44546
- lstatSync as lstatSync8,
44666
+ existsSync as existsSync65,
44667
+ lstatSync as lstatSync9,
44547
44668
  mkdirSync as mkdirSync33,
44548
44669
  readFileSync as readFileSync58,
44549
44670
  readdirSync as readdirSync22,
44550
- statSync as statSync37
44671
+ statSync as statSync38
44551
44672
  } from "node:fs";
44552
- import { dirname as dirname19, join as join65, resolve as resolve38 } from "node:path";
44673
+ import { dirname as dirname19, join as join66, resolve as resolve38 } from "node:path";
44553
44674
  import { createPublicKey, createPrivateKey } from "node:crypto";
44554
44675
  function findInNvm(bin) {
44555
- const nvmRoot = join65(process.env.HOME ?? "", ".nvm", "versions", "node");
44556
- if (!existsSync64(nvmRoot))
44676
+ const nvmRoot = join66(process.env.HOME ?? "", ".nvm", "versions", "node");
44677
+ if (!existsSync65(nvmRoot))
44557
44678
  return null;
44558
44679
  try {
44559
44680
  const versions = readdirSync22(nvmRoot).sort().reverse();
44560
44681
  for (const v of versions) {
44561
- const candidate = join65(nvmRoot, v, "bin", bin);
44682
+ const candidate = join66(nvmRoot, v, "bin", bin);
44562
44683
  try {
44563
- const s = statSync37(candidate);
44684
+ const s = statSync38(candidate);
44564
44685
  if (s.isFile() || s.isSymbolicLink()) {
44565
44686
  return candidate;
44566
44687
  }
@@ -44585,7 +44706,7 @@ function whichOnPath(bin) {
44585
44706
  for (const dir of pathEnv.split(":")) {
44586
44707
  if (!dir)
44587
44708
  continue;
44588
- const candidate = join65(dir, bin);
44709
+ const candidate = join66(dir, bin);
44589
44710
  if (isX(candidate))
44590
44711
  return candidate;
44591
44712
  }
@@ -44751,21 +44872,21 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
44751
44872
  if (envBrowsersPath && envBrowsersPath.length > 0) {
44752
44873
  cacheLocations.push(envBrowsersPath);
44753
44874
  }
44754
- cacheLocations.push(join65(homeDir, ".cache", "ms-playwright"));
44875
+ cacheLocations.push(join66(homeDir, ".cache", "ms-playwright"));
44755
44876
  for (const cacheDir of cacheLocations) {
44756
- if (!existsSync64(cacheDir))
44877
+ if (!existsSync65(cacheDir))
44757
44878
  continue;
44758
44879
  try {
44759
44880
  const entries = readdirSync22(cacheDir).filter((e) => e.startsWith("chromium"));
44760
44881
  for (const entry of entries) {
44761
44882
  const candidates2 = [
44762
- join65(cacheDir, entry, "chrome-linux64", "chrome"),
44763
- join65(cacheDir, entry, "chrome-linux", "chrome"),
44764
- join65(cacheDir, entry, "chrome-linux64", "headless_shell"),
44765
- join65(cacheDir, entry, "chrome-linux", "headless_shell")
44883
+ join66(cacheDir, entry, "chrome-linux64", "chrome"),
44884
+ join66(cacheDir, entry, "chrome-linux", "chrome"),
44885
+ join66(cacheDir, entry, "chrome-linux64", "headless_shell"),
44886
+ join66(cacheDir, entry, "chrome-linux", "headless_shell")
44766
44887
  ];
44767
44888
  for (const path4 of candidates2) {
44768
- if (existsSync64(path4))
44889
+ if (existsSync65(path4))
44769
44890
  return path4;
44770
44891
  }
44771
44892
  }
@@ -44884,7 +45005,7 @@ function checkUserDeclaredMcps(name, agentConfig, config, renderedMcpServers) {
44884
45005
  }
44885
45006
  function defaultPathKind(p) {
44886
45007
  try {
44887
- const s = lstatSync8(p);
45008
+ const s = lstatSync9(p);
44888
45009
  return s.isDirectory() ? "dir" : "file-or-symlink";
44889
45010
  } catch {
44890
45011
  return "absent";
@@ -44894,7 +45015,7 @@ function checkDeployMounts(opts) {
44894
45015
  const home2 = opts?.home ?? process.env.HOME ?? "/root";
44895
45016
  const { pathKind } = opts?.deps ?? DEFAULT_DEPLOY_MOUNTS_DEPS;
44896
45017
  const results = [];
44897
- const dockerComposePlugin = join65(home2, ".docker", "cli-plugins", "docker-compose");
45018
+ const dockerComposePlugin = join66(home2, ".docker", "cli-plugins", "docker-compose");
44898
45019
  const pluginKind = pathKind(dockerComposePlugin);
44899
45020
  if (pluginKind === "dir") {
44900
45021
  results.push({
@@ -44932,8 +45053,8 @@ function checkDeployMounts(opts) {
44932
45053
  function checkLegacyState() {
44933
45054
  const results = [];
44934
45055
  const h = process.env.HOME ?? "/root";
44935
- const clerkDir = join65(h, LEGACY_STATE_DIR);
44936
- const clerkPresent = existsSync64(clerkDir);
45056
+ const clerkDir = join66(h, LEGACY_STATE_DIR);
45057
+ const clerkPresent = existsSync65(clerkDir);
44937
45058
  results.push({
44938
45059
  name: "legacy ~/.clerk state",
44939
45060
  status: clerkPresent ? "warn" : "ok",
@@ -44942,10 +45063,10 @@ function checkLegacyState() {
44942
45063
  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."
44943
45064
  } : {}
44944
45065
  });
44945
- const legacySock = join65(h, ".switchroom", "vault-broker.sock");
45066
+ const legacySock = join66(h, ".switchroom", "vault-broker.sock");
44946
45067
  let sockStat = null;
44947
45068
  try {
44948
- sockStat = lstatSync8(legacySock);
45069
+ sockStat = lstatSync9(legacySock);
44949
45070
  } catch {}
44950
45071
  if (sockStat) {
44951
45072
  results.push({
@@ -45063,7 +45184,7 @@ function checkVault(config) {
45063
45184
  detail: "Approval auth: passphrase (two-factor)"
45064
45185
  };
45065
45186
  const pairsResult = checkVaultBrokerSocketPairs(config);
45066
- if (!existsSync64(vaultPath)) {
45187
+ if (!existsSync65(vaultPath)) {
45067
45188
  return [
45068
45189
  postureResult,
45069
45190
  {
@@ -45449,7 +45570,7 @@ function tryReadHostFile(path4) {
45449
45570
  }
45450
45571
  }
45451
45572
  function parseEnvFile(path4) {
45452
- if (!existsSync64(path4))
45573
+ if (!existsSync65(path4))
45453
45574
  return {};
45454
45575
  let content;
45455
45576
  try {
@@ -45508,7 +45629,7 @@ async function checkTelegram(config) {
45508
45629
  const plugin = agentConfig.channels?.telegram?.plugin ?? "switchroom";
45509
45630
  if (plugin !== "switchroom")
45510
45631
  continue;
45511
- const envPath = join65(agentsDir, name, "telegram", ".env");
45632
+ const envPath = join66(agentsDir, name, "telegram", ".env");
45512
45633
  const read = tryReadHostFile(envPath);
45513
45634
  if (read.kind === "eacces") {
45514
45635
  results.push({
@@ -45560,7 +45681,7 @@ async function checkTelegram(config) {
45560
45681
  }
45561
45682
  function checkStartShStale(agentName, startShPath) {
45562
45683
  const label = `${agentName}: start.sh scheduler block`;
45563
- if (!existsSync64(startShPath)) {
45684
+ if (!existsSync65(startShPath)) {
45564
45685
  return {
45565
45686
  name: label,
45566
45687
  status: "warn",
@@ -45591,10 +45712,10 @@ function checkStartShStale(agentName, startShPath) {
45591
45712
  }
45592
45713
  function checkLeakedHomeSwitchroom(agentName, agentDir) {
45593
45714
  const label = `${agentName}: $HOME/.switchroom symlink (#910)`;
45594
- const path4 = join65(agentDir, "home", ".switchroom");
45715
+ const path4 = join66(agentDir, "home", ".switchroom");
45595
45716
  let stats;
45596
45717
  try {
45597
- stats = lstatSync8(path4);
45718
+ stats = lstatSync9(path4);
45598
45719
  } catch (err) {
45599
45720
  if (err.code === "ENOENT") {
45600
45721
  return {
@@ -45628,8 +45749,8 @@ function checkLeakedHomeSwitchroom(agentName, agentDir) {
45628
45749
  }
45629
45750
  function checkRepoHygiene(repoRoot) {
45630
45751
  const results = [];
45631
- const exportDir = join65(repoRoot, "clerk-export");
45632
- if (existsSync64(exportDir)) {
45752
+ const exportDir = join66(repoRoot, "clerk-export");
45753
+ if (existsSync65(exportDir)) {
45633
45754
  results.push({
45634
45755
  name: "repo hygiene: clerk-export/ on disk (#1072)",
45635
45756
  status: "warn",
@@ -45637,8 +45758,8 @@ function checkRepoHygiene(repoRoot) {
45637
45758
  fix: `Run scripts/migrate-clerk-export-to-vault.sh to move the bundle ` + `into the vault, then delete the on-disk copy.`
45638
45759
  });
45639
45760
  }
45640
- const knownTarball = join65(repoRoot, "clerk-export-with-secrets.tar.gz");
45641
- if (existsSync64(knownTarball)) {
45761
+ const knownTarball = join66(repoRoot, "clerk-export-with-secrets.tar.gz");
45762
+ if (existsSync65(knownTarball)) {
45642
45763
  results.push({
45643
45764
  name: "repo hygiene: clerk-export-with-secrets.tar.gz on disk (#1072)",
45644
45765
  status: "warn",
@@ -45655,7 +45776,7 @@ function checkRepoHygiene(repoRoot) {
45655
45776
  results.push({
45656
45777
  name: `repo hygiene: ${name} on disk (#1072)`,
45657
45778
  status: "warn",
45658
- detail: `${join65(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
45779
+ detail: `${join66(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
45659
45780
  fix: `Inspect, migrate any secrets into the vault, then delete the ` + `archive.`
45660
45781
  });
45661
45782
  }
@@ -45678,10 +45799,10 @@ function checkRepoHygiene(repoRoot) {
45678
45799
  }
45679
45800
  function isSwitchroomCheckout(dir) {
45680
45801
  try {
45681
- if (!existsSync64(join65(dir, ".git")))
45802
+ if (!existsSync65(join66(dir, ".git")))
45682
45803
  return false;
45683
- const pkgPath = join65(dir, "package.json");
45684
- if (!existsSync64(pkgPath))
45804
+ const pkgPath = join66(dir, "package.json");
45805
+ if (!existsSync65(pkgPath))
45685
45806
  return false;
45686
45807
  const pkg = JSON.parse(readFileSync58(pkgPath, "utf-8"));
45687
45808
  return pkg.name === "switchroom";
@@ -45696,7 +45817,7 @@ function checkAgents(config, configPath) {
45696
45817
  const authStatuses = getAllAuthStatuses(config);
45697
45818
  for (const [name, agentConfig] of Object.entries(config.agents)) {
45698
45819
  const agentDir = resolve38(agentsDir, name);
45699
- if (!existsSync64(agentDir)) {
45820
+ if (!existsSync65(agentDir)) {
45700
45821
  results.push({
45701
45822
  name: `${name}: scaffold`,
45702
45823
  status: "fail",
@@ -45717,7 +45838,7 @@ function checkAgents(config, configPath) {
45717
45838
  fix: `Rotate the bot token (e.g. via \`switchroom vault\`), then run ` + `\`switchroom agent unquarantine ${name}\` and \`switchroom agent restart ${name}\``
45718
45839
  });
45719
45840
  }
45720
- results.push(checkStartShStale(name, join65(agentDir, "start.sh")));
45841
+ results.push(checkStartShStale(name, join66(agentDir, "start.sh")));
45721
45842
  results.push(checkLeakedHomeSwitchroom(name, agentDir));
45722
45843
  const status = statuses[name];
45723
45844
  const active = status?.active ?? "unknown";
@@ -45794,8 +45915,8 @@ function checkAgents(config, configPath) {
45794
45915
  }
45795
45916
  }
45796
45917
  if (agentConfig.channels?.telegram?.plugin === "switchroom") {
45797
- const mcpJsonPath = join65(agentDir, ".mcp.json");
45798
- if (!existsSync64(mcpJsonPath)) {
45918
+ const mcpJsonPath = join66(agentDir, ".mcp.json");
45919
+ if (!existsSync65(mcpJsonPath)) {
45799
45920
  results.push({
45800
45921
  name: `${name}: .mcp.json`,
45801
45922
  status: "fail",
@@ -45881,7 +46002,7 @@ function mffEnvPath(config) {
45881
46002
  return agent ? resolve38(home2, ".switchroom/credentials", agent, "my-family-finance/.env") : resolve38(home2, ".switchroom/credentials/my-family-finance/.env");
45882
46003
  }
45883
46004
  function mffEnvState(envPath) {
45884
- if (!existsSync64(envPath))
46005
+ if (!existsSync65(envPath))
45885
46006
  return "absent";
45886
46007
  try {
45887
46008
  accessSync2(envPath, fsConstants5.R_OK);
@@ -45899,7 +46020,7 @@ function checkMffVaultKeyPresent(passphrase, vaultPath) {
45899
46020
  fix: "Export SWITCHROOM_VAULT_PASSPHRASE to enable MFF vault probes"
45900
46021
  };
45901
46022
  }
45902
- if (!existsSync64(vaultPath)) {
46023
+ if (!existsSync65(vaultPath)) {
45903
46024
  return {
45904
46025
  name: "mff: vault key present",
45905
46026
  status: "fail",
@@ -45952,7 +46073,7 @@ function deriveEd25519PublicKeyBytes(keyMaterial) {
45952
46073
  }
45953
46074
  }
45954
46075
  function checkMffVaultKeyFormat(passphrase, vaultPath) {
45955
- if (!passphrase || !existsSync64(vaultPath)) {
46076
+ if (!passphrase || !existsSync65(vaultPath)) {
45956
46077
  return {
45957
46078
  name: "mff: vault key format",
45958
46079
  status: "warn",
@@ -46096,8 +46217,8 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
46096
46217
  };
46097
46218
  }
46098
46219
  const credDir = dirname19(envPath);
46099
- const authScript = join65(credDir, "claude-auth.py");
46100
- if (!existsSync64(authScript)) {
46220
+ const authScript = join66(credDir, "claude-auth.py");
46221
+ if (!existsSync65(authScript)) {
46101
46222
  return {
46102
46223
  name: "mff: auth flow",
46103
46224
  status: "warn",
@@ -46403,6 +46524,10 @@ function registerDoctorCommand(program3) {
46403
46524
  { title: "Memory (Hindsight)", results: await checkHindsight(config) },
46404
46525
  { title: "Telegram", results: await checkTelegram(config) },
46405
46526
  { title: "Agents", results: checkAgents(config, configPath) },
46527
+ {
46528
+ title: "Agent dotfile ownership (#3157)",
46529
+ results: runAgentDotfileOwnershipChecks(config)
46530
+ },
46406
46531
  { title: "Credentials", results: runCredentialsMigrationChecks(config) },
46407
46532
  { title: "Audit integrity (WS10-F4)", results: runAuditIntegrityChecks() },
46408
46533
  {
@@ -46525,6 +46650,7 @@ var init_doctor = __esm(() => {
46525
46650
  init_doctor_notion();
46526
46651
  init_doctor_credentials_migration();
46527
46652
  init_doctor_secret_access();
46653
+ init_doctor_agent_dotfile_ownership();
46528
46654
  init_doctor_inlined_secrets();
46529
46655
  init_doctor_approval_attribution();
46530
46656
  init_doctor_audit_integrity();
@@ -47116,7 +47242,7 @@ var init_fleet_defaults = __esm(() => {
47116
47242
 
47117
47243
  // src/agents/connection-health.ts
47118
47244
  import { mkdirSync as mkdirSync44, writeFileSync as writeFileSync27 } from "node:fs";
47119
- import { join as join80 } from "node:path";
47245
+ import { join as join81 } from "node:path";
47120
47246
  async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
47121
47247
  const reqs = computeMcpSecretRequirements(config).filter((r) => r.agent === agentName);
47122
47248
  if (reqs.length === 0)
@@ -47173,8 +47299,8 @@ async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
47173
47299
  return issues;
47174
47300
  }
47175
47301
  function writeConnectionHealthFile(agentDir, health, deps) {
47176
- const dir = join80(agentDir, ".claude");
47177
- const path7 = join80(dir, CONNECTION_HEALTH_FILENAME);
47302
+ const dir = join81(agentDir, ".claude");
47303
+ const path7 = join81(dir, CONNECTION_HEALTH_FILENAME);
47178
47304
  (deps?.mkdir ?? ((p, o) => mkdirSync44(p, o)))(dir, { recursive: true });
47179
47305
  (deps?.writeFile ?? ((p, d) => writeFileSync27(p, d)))(path7, JSON.stringify(health, null, 2) + `
47180
47306
  `);
@@ -47197,10 +47323,10 @@ var CONNECTION_HEALTH_FILENAME = "connection-health.json";
47197
47323
  var init_connection_health = () => {};
47198
47324
 
47199
47325
  // src/cli/update-prompt-hook.ts
47200
- import { existsSync as existsSync81, readFileSync as readFileSync70, writeFileSync as writeFileSync28, chmodSync as chmodSync11, mkdirSync as mkdirSync45 } from "node:fs";
47201
- import { join as join81 } from "node:path";
47326
+ import { existsSync as existsSync82, readFileSync as readFileSync70, writeFileSync as writeFileSync28, chmodSync as chmodSync11, mkdirSync as mkdirSync45 } from "node:fs";
47327
+ import { join as join82 } from "node:path";
47202
47328
  function containerHookCommand() {
47203
- return join81(CONTAINER_AGENT_DIR, ".claude", "hooks", HOOK_FILENAME);
47329
+ return join82(CONTAINER_AGENT_DIR, ".claude", "hooks", HOOK_FILENAME);
47204
47330
  }
47205
47331
  function updatePromptHookScript() {
47206
47332
  return `#!/bin/bash
@@ -47266,12 +47392,12 @@ exit 0
47266
47392
  `;
47267
47393
  }
47268
47394
  function installUpdatePromptHook(agentDir) {
47269
- const hooksDir = join81(agentDir, ".claude", "hooks");
47395
+ const hooksDir = join82(agentDir, ".claude", "hooks");
47270
47396
  mkdirSync45(hooksDir, { recursive: true });
47271
- const scriptPath = join81(hooksDir, HOOK_FILENAME);
47397
+ const scriptPath = join82(hooksDir, HOOK_FILENAME);
47272
47398
  const desired = updatePromptHookScript();
47273
47399
  let installed = false;
47274
- const existing = existsSync81(scriptPath) ? readFileSync70(scriptPath, "utf-8") : "";
47400
+ const existing = existsSync82(scriptPath) ? readFileSync70(scriptPath, "utf-8") : "";
47275
47401
  if (existing !== desired) {
47276
47402
  writeFileSync28(scriptPath, desired, { mode: 493 });
47277
47403
  chmodSync11(scriptPath, 493);
@@ -47281,8 +47407,8 @@ function installUpdatePromptHook(agentDir) {
47281
47407
  chmodSync11(scriptPath, 493);
47282
47408
  } catch {}
47283
47409
  }
47284
- const settingsPath = join81(agentDir, ".claude", "settings.json");
47285
- if (!existsSync81(settingsPath)) {
47410
+ const settingsPath = join82(agentDir, ".claude", "settings.json");
47411
+ if (!existsSync82(settingsPath)) {
47286
47412
  return { scriptPath, settingsPath, installed };
47287
47413
  }
47288
47414
  const raw = readFileSync70(settingsPath, "utf-8");
@@ -47710,7 +47836,7 @@ __export(exports_voice_sidecar_token, {
47710
47836
  VOICE_SIDECAR_TOKEN_ENV: () => VOICE_SIDECAR_TOKEN_ENV
47711
47837
  });
47712
47838
  import { randomBytes as randomBytes14 } from "node:crypto";
47713
- import { chmodSync as chmodSync12, chownSync as chownSync7, existsSync as existsSync83, mkdirSync as mkdirSync46, readFileSync as readFileSync71, rmSync as rmSync17, writeFileSync as writeFileSync29 } from "node:fs";
47839
+ import { chmodSync as chmodSync12, chownSync as chownSync7, existsSync as existsSync84, mkdirSync as mkdirSync46, readFileSync as readFileSync71, rmSync as rmSync17, writeFileSync as writeFileSync29 } from "node:fs";
47714
47840
  import { dirname as dirname28 } from "node:path";
47715
47841
  async function defaultResolveOrSeedToken(home2, writeErr) {
47716
47842
  const [{ getViaBrokerStructured: getViaBrokerStructured2, putViaBroker: putViaBroker2 }, { resolveOperatorVaultPassphrase }] = await Promise.all([
@@ -47745,7 +47871,7 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
47745
47871
  const envPath = composeEnvPath(composePath);
47746
47872
  if (engine !== "local") {
47747
47873
  try {
47748
- if (existsSync83(envPath)) {
47874
+ if (existsSync84(envPath)) {
47749
47875
  const body = readFileSync71(envPath, "utf-8");
47750
47876
  if (body.includes(`${VOICE_SIDECAR_TOKEN_ENV}=`))
47751
47877
  rmSync17(envPath);
@@ -47768,7 +47894,7 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
47768
47894
  mkdirSync46(dirname28(envPath), { recursive: true });
47769
47895
  let body = "";
47770
47896
  try {
47771
- if (existsSync83(envPath))
47897
+ if (existsSync84(envPath))
47772
47898
  body = readFileSync71(envPath, "utf-8");
47773
47899
  } catch {}
47774
47900
  const line = `${VOICE_SIDECAR_TOKEN_ENV}=${token}`;
@@ -47828,11 +47954,11 @@ __export(exports_apply, {
47828
47954
  DEFAULT_COMPOSE_PATH: () => DEFAULT_COMPOSE_PATH2,
47829
47955
  COMPOSE_PROJECT: () => COMPOSE_PROJECT2
47830
47956
  });
47831
- import { accessSync as accessSync3, chownSync as chownSync8, constants as fsConstants6, copyFileSync as copyFileSync12, existsSync as existsSync84, mkdirSync as mkdirSync47, readFileSync as readFileSync72, readdirSync as readdirSync29, renameSync as renameSync18, statSync as statSync46, writeFileSync as writeFileSync30 } from "node:fs";
47957
+ import { accessSync as accessSync3, chownSync as chownSync8, constants as fsConstants6, copyFileSync as copyFileSync12, existsSync as existsSync85, mkdirSync as mkdirSync47, readFileSync as readFileSync72, readdirSync as readdirSync29, renameSync as renameSync18, statSync as statSync47, writeFileSync as writeFileSync30 } from "node:fs";
47832
47958
  import { mkdir as mkdir2 } from "node:fs/promises";
47833
47959
  import { spawnSync as childSpawnSync } from "node:child_process";
47834
47960
  import readline from "node:readline";
47835
- import { dirname as dirname29, join as join83, resolve as resolve51 } from "node:path";
47961
+ import { dirname as dirname29, join as join84, resolve as resolve51 } from "node:path";
47836
47962
  import { homedir as homedir49 } from "node:os";
47837
47963
  import { execFileSync as execFileSync27 } from "node:child_process";
47838
47964
  function effectiveLiteLLMEnabled(config, agentResolvedLitellm) {
@@ -47844,7 +47970,7 @@ async function resolveOperatorVaultPassphrase(home2) {
47844
47970
  return envPass;
47845
47971
  try {
47846
47972
  const { readAutoUnlockFile: readAutoUnlockFile2 } = await Promise.resolve().then(() => (init_auto_unlock(), exports_auto_unlock));
47847
- const blobPath = join83(home2, ".switchroom", "vault-auto-unlock");
47973
+ const blobPath = join84(home2, ".switchroom", "vault-auto-unlock");
47848
47974
  const pass = readAutoUnlockFile2(blobPath);
47849
47975
  return pass && pass.length > 0 ? pass : null;
47850
47976
  } catch {
@@ -47932,7 +48058,7 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
47932
48058
  const oauthAccount = config.auth?.active;
47933
48059
  let pendingConfigEdits = false;
47934
48060
  let configText = null;
47935
- if (switchroomConfigPath && existsSync84(switchroomConfigPath)) {
48061
+ if (switchroomConfigPath && existsSync85(switchroomConfigPath)) {
47936
48062
  try {
47937
48063
  configText = readFileSync72(switchroomConfigPath, "utf-8");
47938
48064
  } catch (err) {
@@ -48089,7 +48215,7 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
48089
48215
  try {
48090
48216
  let cfgMode = 420;
48091
48217
  try {
48092
- cfgMode = statSync46(switchroomConfigPath).mode & 511;
48218
+ cfgMode = statSync47(switchroomConfigPath).mode & 511;
48093
48219
  } catch {}
48094
48220
  writeConfigFileSync(switchroomConfigPath, configText, cfgMode);
48095
48221
  writeOut(source_default.gray(` ~ litellm: granted read-ACL on per-agent keys (updated ${switchroomConfigPath})
@@ -48157,10 +48283,10 @@ function resolveVaultBindMountDir(homeDir, ctx) {
48157
48283
  if (isCustomPath && ctx.customVaultPath) {
48158
48284
  return dirname29(ctx.customVaultPath);
48159
48285
  }
48160
- return join83(homeDir, ".switchroom", "vault");
48286
+ return join84(homeDir, ".switchroom", "vault");
48161
48287
  }
48162
48288
  function inspectVaultBindMountDir(vaultDir) {
48163
- if (!existsSync84(vaultDir))
48289
+ if (!existsSync85(vaultDir))
48164
48290
  return { kind: "missing" };
48165
48291
  const entries = readdirSync29(vaultDir);
48166
48292
  const unknown = [];
@@ -48188,43 +48314,43 @@ function hasVaultRefs(value) {
48188
48314
  async function ensureHostMountSources(config) {
48189
48315
  const home2 = resolveHostHomeForCompose();
48190
48316
  const dirs = [
48191
- join83(home2, ".switchroom", "approvals"),
48192
- join83(home2, ".switchroom", "scheduler"),
48193
- join83(home2, ".switchroom", "logs"),
48194
- join83(home2, ".switchroom", "compose"),
48195
- join83(home2, ".switchroom", "broker-operator")
48317
+ join84(home2, ".switchroom", "approvals"),
48318
+ join84(home2, ".switchroom", "scheduler"),
48319
+ join84(home2, ".switchroom", "logs"),
48320
+ join84(home2, ".switchroom", "compose"),
48321
+ join84(home2, ".switchroom", "broker-operator")
48196
48322
  ];
48197
48323
  for (const name of Object.keys(config.agents)) {
48198
- dirs.push(join83(home2, ".switchroom", "agents", name));
48199
- dirs.push(join83(home2, ".switchroom", "logs", name));
48200
- dirs.push(join83(home2, ".claude", "projects", name));
48201
- dirs.push(join83(home2, ".switchroom", "audit", name));
48202
- if (existsSync84(join83(home2, ".switchroom-config"))) {
48203
- dirs.push(join83(home2, ".switchroom-config", "agents", name, "personal-skills"));
48324
+ dirs.push(join84(home2, ".switchroom", "agents", name));
48325
+ dirs.push(join84(home2, ".switchroom", "logs", name));
48326
+ dirs.push(join84(home2, ".claude", "projects", name));
48327
+ dirs.push(join84(home2, ".switchroom", "audit", name));
48328
+ if (existsSync85(join84(home2, ".switchroom-config"))) {
48329
+ dirs.push(join84(home2, ".switchroom-config", "agents", name, "personal-skills"));
48204
48330
  }
48205
48331
  }
48206
48332
  for (const dir of dirs) {
48207
48333
  await mkdir2(dir, { recursive: true });
48208
48334
  }
48209
- const autoUnlockPath = join83(home2, ".switchroom", "vault-auto-unlock");
48210
- if (!existsSync84(autoUnlockPath)) {
48335
+ const autoUnlockPath = join84(home2, ".switchroom", "vault-auto-unlock");
48336
+ if (!existsSync85(autoUnlockPath)) {
48211
48337
  writeFileSync30(autoUnlockPath, "", { mode: 384 });
48212
48338
  }
48213
- const auditLogPath = join83(home2, ".switchroom", "vault-audit.log");
48214
- if (!existsSync84(auditLogPath)) {
48339
+ const auditLogPath = join84(home2, ".switchroom", "vault-audit.log");
48340
+ if (!existsSync85(auditLogPath)) {
48215
48341
  writeFileSync30(auditLogPath, "", { mode: 420 });
48216
48342
  }
48217
- const grantsDbPath = join83(home2, ".switchroom", "vault-grants.db");
48218
- if (!existsSync84(grantsDbPath)) {
48343
+ const grantsDbPath = join84(home2, ".switchroom", "vault-grants.db");
48344
+ if (!existsSync85(grantsDbPath)) {
48219
48345
  writeFileSync30(grantsDbPath, "", { mode: 384 });
48220
48346
  }
48221
- const hostdAuditLogPath = join83(home2, ".switchroom", "host-control-audit.log");
48222
- if (!existsSync84(hostdAuditLogPath)) {
48347
+ const hostdAuditLogPath = join84(home2, ".switchroom", "host-control-audit.log");
48348
+ if (!existsSync85(hostdAuditLogPath)) {
48223
48349
  writeFileSync30(hostdAuditLogPath, "", { mode: 420 });
48224
48350
  }
48225
48351
  for (const name of Object.keys(config.agents)) {
48226
- const tokenPath = join83(home2, ".switchroom", "agents", name, ".vault-token");
48227
- if (!existsSync84(tokenPath)) {
48352
+ const tokenPath = join84(home2, ".switchroom", "agents", name, ".vault-token");
48353
+ if (!existsSync85(tokenPath)) {
48228
48354
  writeFileSync30(tokenPath, "", { mode: 384 });
48229
48355
  }
48230
48356
  try {
@@ -48232,16 +48358,16 @@ async function ensureHostMountSources(config) {
48232
48358
  chownSync8(tokenPath, uid, uid);
48233
48359
  } catch {}
48234
48360
  }
48235
- const fleetDir = join83(home2, ".switchroom", "fleet");
48361
+ const fleetDir = join84(home2, ".switchroom", "fleet");
48236
48362
  await mkdir2(fleetDir, { recursive: true });
48237
- const invariantsPath = join83(fleetDir, "switchroom-invariants.md");
48363
+ const invariantsPath = join84(fleetDir, "switchroom-invariants.md");
48238
48364
  const invariantsCanonical = renderFleetInvariants();
48239
- const invariantsCurrent = existsSync84(invariantsPath) ? readFileSync72(invariantsPath, "utf-8") : null;
48365
+ const invariantsCurrent = existsSync85(invariantsPath) ? readFileSync72(invariantsPath, "utf-8") : null;
48240
48366
  if (invariantsCurrent !== invariantsCanonical) {
48241
48367
  writeFileSync30(invariantsPath, invariantsCanonical, { mode: 420 });
48242
48368
  }
48243
- const fleetClaudePath = join83(fleetDir, "CLAUDE.md");
48244
- if (!existsSync84(fleetClaudePath)) {
48369
+ const fleetClaudePath = join84(fleetDir, "CLAUDE.md");
48370
+ if (!existsSync85(fleetClaudePath)) {
48245
48371
  writeFileSync30(fleetClaudePath, renderFleetDefaultsClaudeMd(), {
48246
48372
  mode: 420
48247
48373
  });
@@ -48271,7 +48397,7 @@ function isInAgentContainer(vaultPresent, composeV2Present, env2 = process.env)
48271
48397
  function runApplyPreflight(config, opts = {}) {
48272
48398
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
48273
48399
  const detect = opts.detectComposeV2 ?? detectComposeV2;
48274
- const vaultMissing = hasVaultRefs(config) && !existsSync84(vaultPath);
48400
+ const vaultMissing = hasVaultRefs(config) && !existsSync85(vaultPath);
48275
48401
  const composeErr = detect();
48276
48402
  if ((vaultMissing || composeErr) && isInAgentContainer(!vaultMissing, composeErr === null)) {
48277
48403
  throw new Error(IN_AGENT_CONTAINER_APPLY_MSG);
@@ -48285,7 +48411,7 @@ function runApplyPreflight(config, opts = {}) {
48285
48411
  detectAndReportLegacyGdriveSlots(vaultPath);
48286
48412
  }
48287
48413
  function detectAndReportLegacyGdriveSlots(vaultPath) {
48288
- if (!existsSync84(vaultPath))
48414
+ if (!existsSync85(vaultPath))
48289
48415
  return;
48290
48416
  const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
48291
48417
  if (!passphrase)
@@ -48326,8 +48452,8 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
48326
48452
  }
48327
48453
  function writeInstallTypeCache(homeDir = homedir49()) {
48328
48454
  const ctx = detectInstallType();
48329
- const dir = join83(homeDir, ".switchroom");
48330
- const out = join83(dir, "install-type.json");
48455
+ const dir = join84(homeDir, ".switchroom");
48456
+ const out = join84(dir, "install-type.json");
48331
48457
  const tmp = `${out}.tmp`;
48332
48458
  mkdirSync47(dir, { recursive: true });
48333
48459
  const payload = {
@@ -48394,17 +48520,17 @@ Applying switchroom config...
48394
48520
  writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
48395
48521
  `));
48396
48522
  try {
48397
- installUpdatePromptHook(join83(agentsDir, name));
48523
+ installUpdatePromptHook(join84(agentsDir, name));
48398
48524
  } catch (hookErr) {
48399
48525
  writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
48400
48526
  `));
48401
48527
  }
48402
- await refreshAgentConnectionHealth(config, name, join83(agentsDir, name), {
48528
+ await refreshAgentConnectionHealth(config, name, join84(agentsDir, name), {
48403
48529
  vaultAclReader: connHealthVaultAclReader
48404
48530
  });
48405
48531
  try {
48406
48532
  const uid = allocateAgentUid(name);
48407
- alignAgentUid(name, join83(agentsDir, name), uid, {
48533
+ alignAgentUid(name, join84(agentsDir, name), uid, {
48408
48534
  confirm: !options.nonInteractive,
48409
48535
  writeOut
48410
48536
  });
@@ -48449,7 +48575,7 @@ Applying switchroom config...
48449
48575
  for (const name of agentNames) {
48450
48576
  try {
48451
48577
  const uid = allocateAgentUid(name);
48452
- alignAgentUid(name, join83(agentsDir, name), uid, {
48578
+ alignAgentUid(name, join84(agentsDir, name), uid, {
48453
48579
  confirm: !options.nonInteractive,
48454
48580
  writeOut
48455
48581
  });
@@ -48787,7 +48913,7 @@ function copyExampleConfig2(name) {
48787
48913
  throw new Error(`Invalid example name: ${name} (must match /^[a-z0-9_-]+$/)`);
48788
48914
  }
48789
48915
  const dest = resolve51(process.cwd(), "switchroom.yaml");
48790
- if (existsSync84(dest)) {
48916
+ if (existsSync85(dest)) {
48791
48917
  console.error(source_default.yellow("switchroom.yaml already exists \u2014 skipping example copy"));
48792
48918
  return;
48793
48919
  }
@@ -48798,7 +48924,7 @@ function copyExampleConfig2(name) {
48798
48924
  return;
48799
48925
  }
48800
48926
  const exampleFile = resolve51(import.meta.dirname, `../../examples/${name}.yaml`);
48801
- if (!existsSync84(exampleFile)) {
48927
+ if (!existsSync85(exampleFile)) {
48802
48928
  throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
48803
48929
  }
48804
48930
  copyFileSync12(exampleFile, dest);
@@ -48809,8 +48935,8 @@ function findUnwritableAgentDirs(config, opts) {
48809
48935
  const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
48810
48936
  const unwritable = [];
48811
48937
  for (const name of targets) {
48812
- const startSh = join83(agentsDir, name, "start.sh");
48813
- if (!existsSync84(startSh))
48938
+ const startSh = join84(agentsDir, name, "start.sh");
48939
+ if (!existsSync85(startSh))
48814
48940
  continue;
48815
48941
  try {
48816
48942
  accessSync3(startSh, fsConstants6.W_OK);
@@ -49013,7 +49139,7 @@ var init_apply = __esm(() => {
49013
49139
  switchroom: switchroom_default,
49014
49140
  minimal: minimal_default
49015
49141
  };
49016
- DEFAULT_COMPOSE_PATH2 = join83(homedir49(), ".switchroom", "compose", "docker-compose.yml");
49142
+ DEFAULT_COMPOSE_PATH2 = join84(homedir49(), ".switchroom", "compose", "docker-compose.yml");
49017
49143
  IN_AGENT_CONTAINER_APPLY_MSG = "`switchroom apply`'s full per-agent scaffold cannot run from inside an " + "agent container \u2014 this is a host/hostd operation by construction " + "(no vault at the container HOME, and no `docker compose` v2 plugin here).\nTo roll the fleet to a new version, drive the hostd rollout (`mcp__hostd__rollout`): it runs a `--compose-only` apply plus a per-agent restart-reconcile, and each agent refreshes its own templates " + `on restart \u2014 the roll completes without any agent running a full apply.
49018
49144
  ` + "A full host-side `sudo switchroom apply` is only needed for structural changes (compose regeneration / new-agent scaffolding), and is run by the operator on the host, never from inside an agent.";
49019
49145
  SELF_ELEVATE_PRESERVED_ENV = [
@@ -62462,7 +62588,7 @@ __export(exports_server2, {
62462
62588
  TOOLS: () => TOOLS2
62463
62589
  });
62464
62590
  import { randomBytes as randomBytes16 } from "node:crypto";
62465
- import { existsSync as existsSync93, readFileSync as readFileSync81 } from "node:fs";
62591
+ import { existsSync as existsSync94, readFileSync as readFileSync81 } from "node:fs";
62466
62592
  function selfSocketPath() {
62467
62593
  return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
62468
62594
  }
@@ -62489,7 +62615,7 @@ async function dispatchTool2(name, args) {
62489
62615
  return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
62490
62616
  }
62491
62617
  const sockPath = selfSocketPath();
62492
- if (!existsSync93(sockPath)) {
62618
+ if (!existsSync94(sockPath)) {
62493
62619
  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.`);
62494
62620
  }
62495
62621
  let req;
@@ -62740,13 +62866,13 @@ function resolveAuditLogPath() {
62740
62866
  if (process.env.HOSTD_AUDIT_LOG_PATH)
62741
62867
  return process.env.HOSTD_AUDIT_LOG_PATH;
62742
62868
  const bindMounted = "/host-home/.switchroom/host-control-audit.log";
62743
- if (existsSync93(bindMounted))
62869
+ if (existsSync94(bindMounted))
62744
62870
  return bindMounted;
62745
62871
  return defaultAuditLogPath2();
62746
62872
  }
62747
62873
  function getLastUpdateApplyStatus() {
62748
62874
  const path8 = resolveAuditLogPath();
62749
- if (!existsSync93(path8)) {
62875
+ if (!existsSync94(path8)) {
62750
62876
  return errorText2(`get_status: audit log not found at ${path8}. No update_apply has run yet?`);
62751
62877
  }
62752
62878
  let raw;
@@ -63428,7 +63554,7 @@ __export(exports_scan, {
63428
63554
  import {
63429
63555
  readFileSync as readFileSync83,
63430
63556
  readdirSync as readdirSync37,
63431
- existsSync as existsSync96,
63557
+ existsSync as existsSync97,
63432
63558
  mkdirSync as mkdirSync54,
63433
63559
  writeFileSync as writeFileSync36
63434
63560
  } from "node:fs";
@@ -63439,7 +63565,7 @@ function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.en
63439
63565
  }
63440
63566
  function listAgents(base) {
63441
63567
  const dir = resolve56(base, "agents");
63442
- if (!existsSync96(dir))
63568
+ if (!existsSync97(dir))
63443
63569
  return [];
63444
63570
  try {
63445
63571
  return readdirSync37(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
@@ -63468,7 +63594,7 @@ function runScan(opts = {}) {
63468
63594
  let gwText = "";
63469
63595
  let sawArtifact = false;
63470
63596
  try {
63471
- if (existsSync96(turnsPath)) {
63597
+ if (existsSync97(turnsPath)) {
63472
63598
  turnsText = readFileSync83(turnsPath, "utf-8");
63473
63599
  sawArtifact = true;
63474
63600
  }
@@ -63476,7 +63602,7 @@ function runScan(opts = {}) {
63476
63602
  log(`fleet-health: WARN skipping ${agent} turns.jsonl unreadable: ${String(e)}`);
63477
63603
  }
63478
63604
  try {
63479
- if (existsSync96(gwPath)) {
63605
+ if (existsSync97(gwPath)) {
63480
63606
  gwText = readFileSync83(gwPath, "utf-8");
63481
63607
  sawArtifact = true;
63482
63608
  }
@@ -63515,7 +63641,7 @@ function runScan(opts = {}) {
63515
63641
  function readLedgerIfPresent(base) {
63516
63642
  const path8 = ledgerPathForBase(base);
63517
63643
  try {
63518
- if (!existsSync96(path8))
63644
+ if (!existsSync97(path8))
63519
63645
  return null;
63520
63646
  return JSON.parse(readFileSync83(path8, "utf-8"));
63521
63647
  } catch {
@@ -83249,9 +83375,9 @@ init_source();
83249
83375
  init_loader();
83250
83376
  init_lifecycle();
83251
83377
  init_compose_env();
83252
- import { cpSync as cpSync2, existsSync as existsSync65, mkdirSync as mkdirSync34, readFileSync as readFileSync59, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync39, chownSync as chownSync5 } from "node:fs";
83378
+ import { cpSync as cpSync2, existsSync as existsSync66, mkdirSync as mkdirSync34, readFileSync as readFileSync59, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync40, chownSync as chownSync5 } from "node:fs";
83253
83379
  import { spawnSync as spawnSync12 } from "node:child_process";
83254
- import { join as join66, dirname as dirname20, resolve as resolve39 } from "node:path";
83380
+ import { join as join67, dirname as dirname20, resolve as resolve39 } from "node:path";
83255
83381
  import { homedir as homedir40 } from "node:os";
83256
83382
 
83257
83383
  // src/cli/release-yaml.ts
@@ -83277,7 +83403,7 @@ init_operator_uid();
83277
83403
  init_atomic();
83278
83404
 
83279
83405
  // src/cli/preflight-mounts.ts
83280
- import { statSync as statSync38 } from "node:fs";
83406
+ import { statSync as statSync39 } from "node:fs";
83281
83407
  function parseHostBindSources(composeText) {
83282
83408
  const out = [];
83283
83409
  const FILE_HINT = /\.(ya?ml|db|log|toml|json|token|id)$|\/\.vault-token$|machine-id$|localtime$|vault-auto-unlock$|\/webkite$/;
@@ -83304,7 +83430,7 @@ function parseHostBindSources(composeText) {
83304
83430
  return out;
83305
83431
  }
83306
83432
  function validateBindSources(composeText, deps = {}) {
83307
- const stat = deps.stat ?? ((p) => statSync38(p));
83433
+ const stat = deps.stat ?? ((p) => statSync39(p));
83308
83434
  const sources = parseHostBindSources(composeText);
83309
83435
  const issues = [];
83310
83436
  const seen = new Set;
@@ -83349,7 +83475,7 @@ function defaultPersistPin(configPath) {
83349
83475
  const after = setReleasePinInConfig(before, pin);
83350
83476
  if (after === before)
83351
83477
  return;
83352
- writeConfigFileSync(path4, after, statSync39(path4).mode & 511);
83478
+ writeConfigFileSync(path4, after, statSync40(path4).mode & 511);
83353
83479
  try {
83354
83480
  if (typeof process.geteuid === "function" && process.geteuid() === 0) {
83355
83481
  const uid = resolveOperatorUid();
@@ -83359,13 +83485,13 @@ function defaultPersistPin(configPath) {
83359
83485
  } catch {}
83360
83486
  };
83361
83487
  }
83362
- var DEFAULT_COMPOSE_PATH = join66(homedir40(), ".switchroom", "compose", "docker-compose.yml");
83488
+ var DEFAULT_COMPOSE_PATH = join67(homedir40(), ".switchroom", "compose", "docker-compose.yml");
83363
83489
  function runningFromSwitchroomCheckout(scriptPath) {
83364
83490
  let dir = dirname20(scriptPath);
83365
83491
  for (let i = 0;i < 12; i++) {
83366
- if (existsSync65(join66(dir, ".git"))) {
83492
+ if (existsSync66(join67(dir, ".git"))) {
83367
83493
  try {
83368
- const pkg = JSON.parse(readFileSync59(join66(dir, "package.json"), "utf-8"));
83494
+ const pkg = JSON.parse(readFileSync59(join67(dir, "package.json"), "utf-8"));
83369
83495
  if (pkg.name === "switchroom")
83370
83496
  return true;
83371
83497
  } catch {}
@@ -83450,7 +83576,7 @@ function planUpdate(opts) {
83450
83576
  steps.push({
83451
83577
  name: "pull-images",
83452
83578
  description: "Pull broker / kernel / agent images from GHCR",
83453
- skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync65(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
83579
+ skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync66(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
83454
83580
  run: () => {
83455
83581
  const r = runner("docker", [
83456
83582
  "compose",
@@ -83576,14 +83702,14 @@ function planUpdate(opts) {
83576
83702
  return;
83577
83703
  }
83578
83704
  const source = resolve39(import.meta.dirname, "../../skills");
83579
- const dest = join66(homedir40(), ".switchroom", "skills", "_bundled");
83580
- if (!existsSync65(source)) {
83705
+ const dest = join67(homedir40(), ".switchroom", "skills", "_bundled");
83706
+ if (!existsSync66(source)) {
83581
83707
  process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
83582
83708
  `);
83583
83709
  return;
83584
83710
  }
83585
83711
  try {
83586
- if (existsSync65(dest)) {
83712
+ if (existsSync66(dest)) {
83587
83713
  rmSync12(dest, { recursive: true, force: true });
83588
83714
  }
83589
83715
  mkdirSync34(dirname20(dest), { recursive: true });
@@ -83697,12 +83823,12 @@ function defaultStatusProbe(composePath) {
83697
83823
  } catch {}
83698
83824
  if (scriptPath) {
83699
83825
  try {
83700
- cliBuiltAt = new Date(statSync39(scriptPath).mtimeMs).toISOString();
83826
+ cliBuiltAt = new Date(statSync40(scriptPath).mtimeMs).toISOString();
83701
83827
  } catch {}
83702
83828
  let dir = dirname20(scriptPath);
83703
83829
  for (let i = 0;i < 8; i++) {
83704
- const pkgPath = join66(dir, "package.json");
83705
- if (existsSync65(pkgPath)) {
83830
+ const pkgPath = join67(dir, "package.json");
83831
+ if (existsSync66(pkgPath)) {
83706
83832
  try {
83707
83833
  const pkg = JSON.parse(readFileSync59(pkgPath, "utf-8"));
83708
83834
  if (typeof pkg.version === "string")
@@ -83725,7 +83851,7 @@ function defaultStatusProbe(composePath) {
83725
83851
  warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
83726
83852
  }
83727
83853
  const services = [];
83728
- if (!existsSync65(composePath)) {
83854
+ if (!existsSync66(composePath)) {
83729
83855
  warnings.push(`compose file not found at ${composePath}; service status unknown`);
83730
83856
  return { cliVersion, cliBuiltAt, services, warnings };
83731
83857
  }
@@ -83921,7 +84047,7 @@ function registerUpdateCommand(program3) {
83921
84047
  // src/cli/rollout.ts
83922
84048
  init_helpers();
83923
84049
  import { spawnSync as spawnSync14 } from "node:child_process";
83924
- import { readFileSync as readFileSync60, chownSync as chownSync6, statSync as statSync40 } from "node:fs";
84050
+ import { readFileSync as readFileSync60, chownSync as chownSync6, statSync as statSync41 } from "node:fs";
83925
84051
  import { homedir as homedir41 } from "node:os";
83926
84052
  init_operator_uid();
83927
84053
  init_atomic();
@@ -84325,7 +84451,7 @@ function registerRolloutCommand(program3) {
84325
84451
  const after = setReleasePinInConfig(before, pin);
84326
84452
  if (after === before)
84327
84453
  return;
84328
- writeConfigFileSync(configPath, after, statSync40(configPath).mode & 511);
84454
+ writeConfigFileSync(configPath, after, statSync41(configPath).mode & 511);
84329
84455
  try {
84330
84456
  if (typeof process.geteuid === "function" && process.geteuid() === 0) {
84331
84457
  const uid = resolveOperatorUid();
@@ -84399,8 +84525,8 @@ init_helpers();
84399
84525
  init_lifecycle();
84400
84526
  init_resolve_version();
84401
84527
  import { execSync as execSync3 } from "node:child_process";
84402
- import { existsSync as existsSync66, readFileSync as readFileSync61 } from "node:fs";
84403
- import { dirname as dirname21, join as join67 } from "node:path";
84528
+ import { existsSync as existsSync67, readFileSync as readFileSync61 } from "node:fs";
84529
+ import { dirname as dirname21, join as join68 } from "node:path";
84404
84530
  function getClaudeCodeVersion() {
84405
84531
  try {
84406
84532
  const out = execSync3("claude --version 2>/dev/null", {
@@ -84450,11 +84576,11 @@ function formatUptime3(timestamp) {
84450
84576
  function locateSwitchroomInstallDir() {
84451
84577
  let dir = import.meta.dirname;
84452
84578
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
84453
- const pkgPath = join67(dir, "package.json");
84454
- if (existsSync66(pkgPath)) {
84579
+ const pkgPath = join68(dir, "package.json");
84580
+ if (existsSync67(pkgPath)) {
84455
84581
  try {
84456
84582
  const pkg = JSON.parse(readFileSync61(pkgPath, "utf-8"));
84457
- if (pkg.name === "switchroom" && existsSync66(join67(dir, ".git"))) {
84583
+ if (pkg.name === "switchroom" && existsSync67(join68(dir, ".git"))) {
84458
84584
  return dir;
84459
84585
  }
84460
84586
  } catch {}
@@ -84632,18 +84758,18 @@ import { resolve as resolve41 } from "node:path";
84632
84758
 
84633
84759
  // src/agents/session-retention.ts
84634
84760
  import {
84635
- existsSync as existsSync67,
84761
+ existsSync as existsSync68,
84636
84762
  readdirSync as readdirSync23,
84637
- statSync as statSync41,
84763
+ statSync as statSync42,
84638
84764
  unlinkSync as unlinkSync14
84639
84765
  } from "node:fs";
84640
- import { join as join68 } from "node:path";
84766
+ import { join as join69 } from "node:path";
84641
84767
  var DEFAULT_SESSION_RETENTION_MAX_COUNT = 20;
84642
84768
  var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
84643
84769
  var MIN_KEEP = 2;
84644
84770
  function collectSessionJsonl(claudeConfigDir) {
84645
- const projects = join68(claudeConfigDir, "projects");
84646
- if (!existsSync67(projects))
84771
+ const projects = join69(claudeConfigDir, "projects");
84772
+ if (!existsSync68(projects))
84647
84773
  return [];
84648
84774
  const found = [];
84649
84775
  const walk2 = (dir) => {
@@ -84654,10 +84780,10 @@ function collectSessionJsonl(claudeConfigDir) {
84654
84780
  return;
84655
84781
  }
84656
84782
  for (const name of entries) {
84657
- const full = join68(dir, name);
84783
+ const full = join69(dir, name);
84658
84784
  let st;
84659
84785
  try {
84660
- st = statSync41(full);
84786
+ st = statSync42(full);
84661
84787
  } catch {
84662
84788
  continue;
84663
84789
  }
@@ -84774,18 +84900,18 @@ function registerHandoffCommand(program3) {
84774
84900
  // src/issues/store.ts
84775
84901
  import {
84776
84902
  closeSync as closeSync12,
84777
- existsSync as existsSync68,
84903
+ existsSync as existsSync69,
84778
84904
  mkdirSync as mkdirSync35,
84779
84905
  openSync as openSync12,
84780
84906
  readdirSync as readdirSync24,
84781
84907
  readFileSync as readFileSync62,
84782
84908
  renameSync as renameSync15,
84783
- statSync as statSync42,
84909
+ statSync as statSync43,
84784
84910
  unlinkSync as unlinkSync15,
84785
84911
  writeFileSync as writeFileSync19,
84786
84912
  writeSync as writeSync9
84787
84913
  } from "node:fs";
84788
- import { join as join69 } from "node:path";
84914
+ import { join as join70 } from "node:path";
84789
84915
  import { randomBytes as randomBytes12 } from "node:crypto";
84790
84916
  import { execSync as execSync4 } from "node:child_process";
84791
84917
 
@@ -85216,8 +85342,8 @@ function redactedMarker(ruleId) {
85216
85342
  var ISSUES_FILE = "issues.jsonl";
85217
85343
  var ISSUES_LOCK = "issues.lock";
85218
85344
  function readAll(stateDir) {
85219
- const path4 = join69(stateDir, ISSUES_FILE);
85220
- if (!existsSync68(path4))
85345
+ const path4 = join70(stateDir, ISSUES_FILE);
85346
+ if (!existsSync69(path4))
85221
85347
  return [];
85222
85348
  let raw;
85223
85349
  try {
@@ -85294,7 +85420,7 @@ function record(stateDir, input, nowFn = Date.now) {
85294
85420
  });
85295
85421
  }
85296
85422
  function resolve42(stateDir, fingerprint, nowFn = Date.now) {
85297
- if (!existsSync68(join69(stateDir, ISSUES_FILE)))
85423
+ if (!existsSync69(join70(stateDir, ISSUES_FILE)))
85298
85424
  return 0;
85299
85425
  return withLock(stateDir, () => {
85300
85426
  const all = readAll(stateDir);
@@ -85312,7 +85438,7 @@ function resolve42(stateDir, fingerprint, nowFn = Date.now) {
85312
85438
  });
85313
85439
  }
85314
85440
  function resolveAllBySource(stateDir, source, nowFn = Date.now) {
85315
- if (!existsSync68(join69(stateDir, ISSUES_FILE)))
85441
+ if (!existsSync69(join70(stateDir, ISSUES_FILE)))
85316
85442
  return 0;
85317
85443
  return withLock(stateDir, () => {
85318
85444
  const all = readAll(stateDir);
@@ -85330,7 +85456,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
85330
85456
  });
85331
85457
  }
85332
85458
  function prune(stateDir, opts = {}) {
85333
- if (!existsSync68(join69(stateDir, ISSUES_FILE)))
85459
+ if (!existsSync69(join70(stateDir, ISSUES_FILE)))
85334
85460
  return 0;
85335
85461
  return withLock(stateDir, () => {
85336
85462
  const all = readAll(stateDir);
@@ -85363,7 +85489,7 @@ function ensureDir(stateDir) {
85363
85489
  mkdirSync35(stateDir, { recursive: true });
85364
85490
  }
85365
85491
  function writeAll(stateDir, events) {
85366
- const path4 = join69(stateDir, ISSUES_FILE);
85492
+ const path4 = join70(stateDir, ISSUES_FILE);
85367
85493
  sweepOrphanTmpFiles(stateDir);
85368
85494
  const tmp = `${path4}.tmp-${process.pid}-${randomBytes12(4).toString("hex")}`;
85369
85495
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
@@ -85385,9 +85511,9 @@ function sweepOrphanTmpFiles(stateDir) {
85385
85511
  for (const entry of entries) {
85386
85512
  if (!entry.startsWith(TMP_PREFIX))
85387
85513
  continue;
85388
- const tmpPath = join69(stateDir, entry);
85514
+ const tmpPath = join70(stateDir, entry);
85389
85515
  try {
85390
- const stat = statSync42(tmpPath);
85516
+ const stat = statSync43(tmpPath);
85391
85517
  if (stat.mtimeMs < cutoff) {
85392
85518
  unlinkSync15(tmpPath);
85393
85519
  }
@@ -85397,7 +85523,7 @@ function sweepOrphanTmpFiles(stateDir) {
85397
85523
  var LOCK_RETRY_MS = 25;
85398
85524
  var LOCK_TIMEOUT_MS = 1e4;
85399
85525
  function withLock(stateDir, fn) {
85400
- const lockPath = join69(stateDir, ISSUES_LOCK);
85526
+ const lockPath = join70(stateDir, ISSUES_LOCK);
85401
85527
  const startedAt = Date.now();
85402
85528
  let fd = null;
85403
85529
  while (fd === null) {
@@ -85680,20 +85806,20 @@ function relTime(deltaMs) {
85680
85806
 
85681
85807
  // src/cli/deps.ts
85682
85808
  init_source();
85683
- import { existsSync as existsSync71 } from "node:fs";
85809
+ import { existsSync as existsSync72 } from "node:fs";
85684
85810
  import { homedir as homedir44 } from "node:os";
85685
- import { join as join72, resolve as resolve43 } from "node:path";
85811
+ import { join as join73, resolve as resolve43 } from "node:path";
85686
85812
 
85687
85813
  // src/deps/python.ts
85688
85814
  import { createHash as createHash13 } from "node:crypto";
85689
85815
  import {
85690
- existsSync as existsSync69,
85816
+ existsSync as existsSync70,
85691
85817
  mkdirSync as mkdirSync36,
85692
85818
  readFileSync as readFileSync63,
85693
85819
  rmSync as rmSync13,
85694
85820
  writeFileSync as writeFileSync20
85695
85821
  } from "node:fs";
85696
- import { dirname as dirname22, join as join70 } from "node:path";
85822
+ import { dirname as dirname22, join as join71 } from "node:path";
85697
85823
  import { homedir as homedir42 } from "node:os";
85698
85824
  import { execFileSync as execFileSync21 } from "node:child_process";
85699
85825
 
@@ -85706,7 +85832,7 @@ class PythonEnvError extends Error {
85706
85832
  }
85707
85833
  }
85708
85834
  function defaultPythonCacheRoot() {
85709
- return join70(homedir42(), ".switchroom", "deps", "python");
85835
+ return join71(homedir42(), ".switchroom", "deps", "python");
85710
85836
  }
85711
85837
  function hashFile(path4) {
85712
85838
  return createHash13("sha256").update(readFileSync63(path4)).digest("hex");
@@ -85715,16 +85841,16 @@ function ensurePythonEnv(opts) {
85715
85841
  const { skillName, requirementsPath, force = false } = opts;
85716
85842
  const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
85717
85843
  const hostPython = opts.pythonBin ?? "python3";
85718
- if (!existsSync69(requirementsPath)) {
85844
+ if (!existsSync70(requirementsPath)) {
85719
85845
  throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
85720
85846
  }
85721
- const venvDir = join70(cacheRoot, skillName);
85722
- const stampPath = join70(venvDir, ".requirements.sha256");
85723
- const binDir = join70(venvDir, "bin");
85724
- const pythonBin = join70(binDir, "python");
85725
- const pipBin = join70(binDir, "pip");
85847
+ const venvDir = join71(cacheRoot, skillName);
85848
+ const stampPath = join71(venvDir, ".requirements.sha256");
85849
+ const binDir = join71(venvDir, "bin");
85850
+ const pythonBin = join71(binDir, "python");
85851
+ const pipBin = join71(binDir, "pip");
85726
85852
  const targetHash = hashFile(requirementsPath);
85727
- if (!force && existsSync69(stampPath) && existsSync69(pythonBin)) {
85853
+ if (!force && existsSync70(stampPath) && existsSync70(pythonBin)) {
85728
85854
  const existingHash = readFileSync63(stampPath, "utf8").trim();
85729
85855
  if (existingHash === targetHash) {
85730
85856
  return {
@@ -85737,7 +85863,7 @@ function ensurePythonEnv(opts) {
85737
85863
  };
85738
85864
  }
85739
85865
  }
85740
- if (existsSync69(venvDir)) {
85866
+ if (existsSync70(venvDir)) {
85741
85867
  rmSync13(venvDir, { recursive: true, force: true });
85742
85868
  }
85743
85869
  mkdirSync36(dirname22(venvDir), { recursive: true });
@@ -85775,13 +85901,13 @@ function ensurePythonEnv(opts) {
85775
85901
  import { createHash as createHash14 } from "node:crypto";
85776
85902
  import {
85777
85903
  copyFileSync as copyFileSync10,
85778
- existsSync as existsSync70,
85904
+ existsSync as existsSync71,
85779
85905
  mkdirSync as mkdirSync37,
85780
85906
  readFileSync as readFileSync64,
85781
85907
  rmSync as rmSync14,
85782
85908
  writeFileSync as writeFileSync21
85783
85909
  } from "node:fs";
85784
- import { dirname as dirname23, join as join71 } from "node:path";
85910
+ import { dirname as dirname23, join as join72 } from "node:path";
85785
85911
  import { homedir as homedir43 } from "node:os";
85786
85912
  import { execFileSync as execFileSync22 } from "node:child_process";
85787
85913
 
@@ -85805,7 +85931,7 @@ var LOCKFILES_FOR = {
85805
85931
  npm: ["package-lock.json"]
85806
85932
  };
85807
85933
  function defaultNodeCacheRoot() {
85808
- return join71(homedir43(), ".switchroom", "deps", "node");
85934
+ return join72(homedir43(), ".switchroom", "deps", "node");
85809
85935
  }
85810
85936
  function hashDepInputs(packageJsonPath) {
85811
85937
  const sourceDir = dirname23(packageJsonPath);
@@ -85814,8 +85940,8 @@ function hashDepInputs(packageJsonPath) {
85814
85940
  `);
85815
85941
  hasher.update(readFileSync64(packageJsonPath));
85816
85942
  for (const lockName of ALL_LOCKFILES) {
85817
- const lockPath = join71(sourceDir, lockName);
85818
- if (existsSync70(lockPath)) {
85943
+ const lockPath = join72(sourceDir, lockName);
85944
+ if (existsSync71(lockPath)) {
85819
85945
  hasher.update(`
85820
85946
  `);
85821
85947
  hasher.update(lockName);
@@ -85830,16 +85956,16 @@ function ensureNodeEnv(opts) {
85830
85956
  const { skillName, packageJsonPath, force = false } = opts;
85831
85957
  const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
85832
85958
  const installer = opts.installer ?? "bun";
85833
- if (!existsSync70(packageJsonPath)) {
85959
+ if (!existsSync71(packageJsonPath)) {
85834
85960
  throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
85835
85961
  }
85836
85962
  const sourceDir = dirname23(packageJsonPath);
85837
- const envDir = join71(cacheRoot, skillName);
85838
- const stampPath = join71(envDir, ".package.sha256");
85839
- const nodeModulesDir = join71(envDir, "node_modules");
85840
- const binDir = join71(nodeModulesDir, ".bin");
85963
+ const envDir = join72(cacheRoot, skillName);
85964
+ const stampPath = join72(envDir, ".package.sha256");
85965
+ const nodeModulesDir = join72(envDir, "node_modules");
85966
+ const binDir = join72(nodeModulesDir, ".bin");
85841
85967
  const targetHash = hashDepInputs(packageJsonPath);
85842
- if (!force && existsSync70(stampPath) && existsSync70(nodeModulesDir)) {
85968
+ if (!force && existsSync71(stampPath) && existsSync71(nodeModulesDir)) {
85843
85969
  const existingHash = readFileSync64(stampPath, "utf8").trim();
85844
85970
  if (existingHash === targetHash) {
85845
85971
  return {
@@ -85851,16 +85977,16 @@ function ensureNodeEnv(opts) {
85851
85977
  };
85852
85978
  }
85853
85979
  }
85854
- if (existsSync70(envDir)) {
85980
+ if (existsSync71(envDir)) {
85855
85981
  rmSync14(envDir, { recursive: true, force: true });
85856
85982
  }
85857
85983
  mkdirSync37(envDir, { recursive: true });
85858
- copyFileSync10(packageJsonPath, join71(envDir, "package.json"));
85984
+ copyFileSync10(packageJsonPath, join72(envDir, "package.json"));
85859
85985
  let copiedLockfile = false;
85860
85986
  for (const lockName of LOCKFILES_FOR[installer]) {
85861
- const lockPath = join71(sourceDir, lockName);
85862
- if (existsSync70(lockPath)) {
85863
- copyFileSync10(lockPath, join71(envDir, lockName));
85987
+ const lockPath = join72(sourceDir, lockName);
85988
+ if (existsSync71(lockPath)) {
85989
+ copyFileSync10(lockPath, join72(envDir, lockName));
85864
85990
  copiedLockfile = true;
85865
85991
  }
85866
85992
  }
@@ -85895,22 +86021,22 @@ function registerDepsCommand(program3) {
85895
86021
  const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
85896
86022
  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) => {
85897
86023
  const skillsRoot = builtinSkillsRoot();
85898
- if (!existsSync71(skillsRoot)) {
86024
+ if (!existsSync72(skillsRoot)) {
85899
86025
  console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
85900
86026
  process.exit(1);
85901
86027
  }
85902
- const skillDir = join72(skillsRoot, skill);
85903
- if (!existsSync71(skillDir)) {
86028
+ const skillDir = join73(skillsRoot, skill);
86029
+ if (!existsSync72(skillDir)) {
85904
86030
  console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
85905
86031
  process.exit(1);
85906
86032
  }
85907
- const requirementsPath = join72(skillDir, "requirements.txt");
85908
- const packageJsonPath = join72(skillDir, "package.json");
85909
- const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync71(requirementsPath));
85910
- const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync71(packageJsonPath));
86033
+ const requirementsPath = join73(skillDir, "requirements.txt");
86034
+ const packageJsonPath = join73(skillDir, "package.json");
86035
+ const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync72(requirementsPath));
86036
+ const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync72(packageJsonPath));
85911
86037
  let did = 0;
85912
86038
  if (wantPython) {
85913
- if (!existsSync71(requirementsPath)) {
86039
+ if (!existsSync72(requirementsPath)) {
85914
86040
  console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
85915
86041
  process.exit(1);
85916
86042
  }
@@ -85934,7 +86060,7 @@ function registerDepsCommand(program3) {
85934
86060
  }
85935
86061
  }
85936
86062
  if (wantNode) {
85937
- if (!existsSync71(packageJsonPath)) {
86063
+ if (!existsSync72(packageJsonPath)) {
85938
86064
  console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
85939
86065
  process.exit(1);
85940
86066
  }
@@ -85967,7 +86093,7 @@ function registerDepsCommand(program3) {
85967
86093
  // src/cli/workspace.ts
85968
86094
  init_helpers();
85969
86095
  init_loader();
85970
- import { existsSync as existsSync72 } from "node:fs";
86096
+ import { existsSync as existsSync73 } from "node:fs";
85971
86097
  import { resolve as resolve44, sep as sep3 } from "node:path";
85972
86098
  import { spawnSync as spawnSync15 } from "node:child_process";
85973
86099
 
@@ -86744,7 +86870,7 @@ function registerWorkspaceCommand(program3) {
86744
86870
  if (!dir)
86745
86871
  return;
86746
86872
  const gitDir = resolve44(dir, ".git");
86747
- if (!existsSync72(gitDir)) {
86873
+ if (!existsSync73(gitDir)) {
86748
86874
  process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
86749
86875
  `);
86750
86876
  return;
@@ -86798,7 +86924,7 @@ function registerWorkspaceCommand(program3) {
86798
86924
  if (!dir)
86799
86925
  return;
86800
86926
  const gitDir = resolve44(dir, ".git");
86801
- if (!existsSync72(gitDir)) {
86927
+ if (!existsSync73(gitDir)) {
86802
86928
  process.stdout.write(`Workspace is not a git repository.
86803
86929
  `);
86804
86930
  return;
@@ -86823,7 +86949,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
86823
86949
  const agentsDir = resolveAgentsDir(config);
86824
86950
  const agentDir = resolve44(agentsDir, agentName);
86825
86951
  const dir = resolveAgentWorkspaceDir(agentDir);
86826
- if (!existsSync72(dir)) {
86952
+ if (!existsSync73(dir)) {
86827
86953
  process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
86828
86954
  `);
86829
86955
  return;
@@ -86859,8 +86985,8 @@ function safeParseInt(value, fallback) {
86859
86985
  init_helpers();
86860
86986
  init_loader();
86861
86987
  init_merge();
86862
- import { copyFileSync as copyFileSync11, existsSync as existsSync73, readFileSync as readFileSync65, writeFileSync as writeFileSync22 } from "node:fs";
86863
- import { join as join73, resolve as resolve45 } from "node:path";
86988
+ import { copyFileSync as copyFileSync11, existsSync as existsSync74, readFileSync as readFileSync65, writeFileSync as writeFileSync22 } from "node:fs";
86989
+ import { join as join74, resolve as resolve45 } from "node:path";
86864
86990
  init_scaffold();
86865
86991
  init_profiles();
86866
86992
  init_schema();
@@ -86877,7 +87003,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
86877
87003
  const agentsDir = resolveAgentsDir(config);
86878
87004
  const agentDir = resolve45(agentsDir, agentName);
86879
87005
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
86880
- if (!existsSync73(workspaceDir)) {
87006
+ if (!existsSync74(workspaceDir)) {
86881
87007
  console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
86882
87008
  process.exit(1);
86883
87009
  }
@@ -86886,7 +87012,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
86886
87012
  profileName,
86887
87013
  profilePath,
86888
87014
  workspaceDir,
86889
- soulPath: join73(workspaceDir, "SOUL.md"),
87015
+ soulPath: join74(workspaceDir, "SOUL.md"),
86890
87016
  soul: merged.soul
86891
87017
  };
86892
87018
  }
@@ -86903,7 +87029,7 @@ function registerSoulCommand(program3) {
86903
87029
  const t = resolveSoulTargetOrExit(program3, agentName);
86904
87030
  if (!t)
86905
87031
  return;
86906
- if (!existsSync73(t.soulPath)) {
87032
+ if (!existsSync74(t.soulPath)) {
86907
87033
  console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
86908
87034
  process.exit(1);
86909
87035
  }
@@ -86918,7 +87044,7 @@ function registerSoulCommand(program3) {
86918
87044
  console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
86919
87045
  process.exit(1);
86920
87046
  }
86921
- const exists = existsSync73(t.soulPath);
87047
+ const exists = existsSync74(t.soulPath);
86922
87048
  if (exists && !opts.yes) {
86923
87049
  if (!isInteractive()) {
86924
87050
  console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
@@ -86933,7 +87059,7 @@ function registerSoulCommand(program3) {
86933
87059
  let backupPath;
86934
87060
  if (exists) {
86935
87061
  backupPath = `${t.soulPath}.bak`;
86936
- if (existsSync73(backupPath)) {
87062
+ if (existsSync74(backupPath)) {
86937
87063
  backupPath = `${t.soulPath}.bak.${Date.now()}`;
86938
87064
  }
86939
87065
  copyFileSync11(t.soulPath, backupPath);
@@ -86952,8 +87078,8 @@ function registerSoulCommand(program3) {
86952
87078
  // src/cli/debug.ts
86953
87079
  init_helpers();
86954
87080
  init_loader();
86955
- import { existsSync as existsSync74, readFileSync as readFileSync66, readdirSync as readdirSync25, statSync as statSync43 } from "node:fs";
86956
- import { resolve as resolve46, join as join74 } from "node:path";
87081
+ import { existsSync as existsSync75, readFileSync as readFileSync66, readdirSync as readdirSync25, statSync as statSync44 } from "node:fs";
87082
+ import { resolve as resolve46, join as join75 } from "node:path";
86957
87083
  import { createHash as createHash15 } from "node:crypto";
86958
87084
  init_merge();
86959
87085
  init_hindsight2();
@@ -86964,8 +87090,8 @@ function estimateTokens(bytes) {
86964
87090
  return Math.round(bytes / 3.7);
86965
87091
  }
86966
87092
  function readMcpServerNames(agentDir) {
86967
- const mcpPath = join74(agentDir, ".mcp.json");
86968
- if (!existsSync74(mcpPath))
87093
+ const mcpPath = join75(agentDir, ".mcp.json");
87094
+ if (!existsSync75(mcpPath))
86969
87095
  return [];
86970
87096
  try {
86971
87097
  const parsed = JSON.parse(readFileSync66(mcpPath, "utf-8"));
@@ -86978,8 +87104,8 @@ function sha256(content) {
86978
87104
  return createHash15("sha256").update(content).digest("hex").slice(0, 16);
86979
87105
  }
86980
87106
  function findLatestTranscriptJsonl(claudeConfigDir) {
86981
- const projectsDir = join74(claudeConfigDir, "projects");
86982
- if (!existsSync74(projectsDir))
87107
+ const projectsDir = join75(claudeConfigDir, "projects");
87108
+ if (!existsSync75(projectsDir))
86983
87109
  return;
86984
87110
  try {
86985
87111
  const entries = readdirSync25(projectsDir, { withFileTypes: true });
@@ -86987,11 +87113,11 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
86987
87113
  for (const entry of entries) {
86988
87114
  if (!entry.isDirectory())
86989
87115
  continue;
86990
- const projectPath = join74(projectsDir, entry.name);
86991
- const transcriptPath = join74(projectPath, "transcript.jsonl");
86992
- if (!existsSync74(transcriptPath))
87116
+ const projectPath = join75(projectsDir, entry.name);
87117
+ const transcriptPath = join75(projectPath, "transcript.jsonl");
87118
+ if (!existsSync75(transcriptPath))
86993
87119
  continue;
86994
- const stat3 = statSync43(transcriptPath);
87120
+ const stat3 = statSync44(transcriptPath);
86995
87121
  if (!latest || stat3.mtimeMs > latest.mtime) {
86996
87122
  latest = { path: transcriptPath, mtime: stat3.mtimeMs };
86997
87123
  }
@@ -87052,16 +87178,16 @@ function registerDebugCommand(program3) {
87052
87178
  }
87053
87179
  const agentsDir = resolveAgentsDir(config);
87054
87180
  const agentDir = resolve46(agentsDir, agentName);
87055
- if (!existsSync74(agentDir)) {
87181
+ if (!existsSync75(agentDir)) {
87056
87182
  console.error(`Agent directory not found: ${agentDir}`);
87057
87183
  process.exit(1);
87058
87184
  }
87059
87185
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
87060
- const claudeConfigDir = join74(agentDir, ".claude");
87061
- const claudeMdPath = join74(agentDir, "CLAUDE.md");
87062
- const soulMdPath = join74(agentDir, "SOUL.md");
87063
- const workspaceSoulMdPath = join74(workspaceDir, "SOUL.md");
87064
- const handoffPath = join74(agentDir, ".handoff.md");
87186
+ const claudeConfigDir = join75(agentDir, ".claude");
87187
+ const claudeMdPath = join75(agentDir, "CLAUDE.md");
87188
+ const soulMdPath = join75(agentDir, "SOUL.md");
87189
+ const workspaceSoulMdPath = join75(workspaceDir, "SOUL.md");
87190
+ const handoffPath = join75(agentDir, ".handoff.md");
87065
87191
  const lastN = parseInt(opts.last, 10);
87066
87192
  if (isNaN(lastN) || lastN < 1) {
87067
87193
  console.error("--last must be a positive integer");
@@ -87107,7 +87233,7 @@ function registerDebugCommand(program3) {
87107
87233
  }
87108
87234
  console.log(`=== Append System Prompt (per-session) ===
87109
87235
  `);
87110
- const handoffContent = existsSync74(handoffPath) ? readFileSync66(handoffPath, "utf-8") : "";
87236
+ const handoffContent = existsSync75(handoffPath) ? readFileSync66(handoffPath, "utf-8") : "";
87111
87237
  if (handoffContent.trim().length > 0) {
87112
87238
  console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
87113
87239
  console.log(handoffContent);
@@ -87118,7 +87244,7 @@ function registerDebugCommand(program3) {
87118
87244
  }
87119
87245
  console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
87120
87246
  `);
87121
- const claudeMdContent = existsSync74(claudeMdPath) ? readFileSync66(claudeMdPath, "utf-8") : "";
87247
+ const claudeMdContent = existsSync75(claudeMdPath) ? readFileSync66(claudeMdPath, "utf-8") : "";
87122
87248
  if (claudeMdContent.trim().length > 0) {
87123
87249
  console.log(`(${formatBytes(claudeMdContent.length)})`);
87124
87250
  console.log(claudeMdContent);
@@ -87129,7 +87255,7 @@ function registerDebugCommand(program3) {
87129
87255
  }
87130
87256
  console.log(`=== Persona (SOUL.md) ===
87131
87257
  `);
87132
- const soulMdContent = existsSync74(soulMdPath) ? readFileSync66(soulMdPath, "utf-8") : existsSync74(workspaceSoulMdPath) ? readFileSync66(workspaceSoulMdPath, "utf-8") : "";
87258
+ const soulMdContent = existsSync75(soulMdPath) ? readFileSync66(soulMdPath, "utf-8") : existsSync75(workspaceSoulMdPath) ? readFileSync66(workspaceSoulMdPath, "utf-8") : "";
87133
87259
  if (soulMdContent.trim().length > 0) {
87134
87260
  console.log(`(${formatBytes(soulMdContent.length)})`);
87135
87261
  console.log(soulMdContent);
@@ -87190,11 +87316,11 @@ function registerDebugCommand(program3) {
87190
87316
  const soulMdBytes = soulMdContent.length;
87191
87317
  const perTurnBytes = dynamicResult.concatenated.length;
87192
87318
  const userBytes = userMessage?.text.length ?? 0;
87193
- const fleetDir = join74(agentsDir, "..", "fleet");
87194
- const fleetInvPath = join74(fleetDir, "switchroom-invariants.md");
87195
- const fleetClaudePath = join74(fleetDir, "CLAUDE.md");
87196
- const fleetInvBytes = existsSync74(fleetInvPath) ? readFileSync66(fleetInvPath, "utf-8").length : 0;
87197
- const fleetClaudeBytes = existsSync74(fleetClaudePath) ? readFileSync66(fleetClaudePath, "utf-8").length : 0;
87319
+ const fleetDir = join75(agentsDir, "..", "fleet");
87320
+ const fleetInvPath = join75(fleetDir, "switchroom-invariants.md");
87321
+ const fleetClaudePath = join75(fleetDir, "CLAUDE.md");
87322
+ const fleetInvBytes = existsSync75(fleetInvPath) ? readFileSync66(fleetInvPath, "utf-8").length : 0;
87323
+ const fleetClaudeBytes = existsSync75(fleetClaudePath) ? readFileSync66(fleetClaudePath, "utf-8").length : 0;
87198
87324
  const fleetBytes = fleetInvBytes + fleetClaudeBytes;
87199
87325
  const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
87200
87326
  console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
@@ -87227,8 +87353,8 @@ init_source();
87227
87353
 
87228
87354
  // src/worktree/claim.ts
87229
87355
  import { execFileSync as execFileSync23 } from "node:child_process";
87230
- import { closeSync as closeSync13, mkdirSync as mkdirSync39, openSync as openSync13, existsSync as existsSync76, unlinkSync as unlinkSync17 } from "node:fs";
87231
- import { join as join76, resolve as resolve48 } from "node:path";
87356
+ import { closeSync as closeSync13, mkdirSync as mkdirSync39, openSync as openSync13, existsSync as existsSync77, unlinkSync as unlinkSync17 } from "node:fs";
87357
+ import { join as join77, resolve as resolve48 } from "node:path";
87232
87358
  import { homedir as homedir46 } from "node:os";
87233
87359
  import { randomBytes as randomBytes13 } from "node:crypto";
87234
87360
 
@@ -87239,16 +87365,16 @@ import {
87239
87365
  readFileSync as readFileSync67,
87240
87366
  readdirSync as readdirSync26,
87241
87367
  unlinkSync as unlinkSync16,
87242
- existsSync as existsSync75,
87368
+ existsSync as existsSync76,
87243
87369
  renameSync as renameSync16
87244
87370
  } from "node:fs";
87245
- import { join as join75, resolve as resolve47 } from "node:path";
87371
+ import { join as join76, resolve as resolve47 } from "node:path";
87246
87372
  import { homedir as homedir45 } from "node:os";
87247
87373
  function registryDir() {
87248
- return resolve47(process.env.SWITCHROOM_WORKTREE_DIR ?? join75(homedir45(), ".switchroom", "worktrees"));
87374
+ return resolve47(process.env.SWITCHROOM_WORKTREE_DIR ?? join76(homedir45(), ".switchroom", "worktrees"));
87249
87375
  }
87250
87376
  function recordPath(id) {
87251
- return join75(registryDir(), `${id}.json`);
87377
+ return join76(registryDir(), `${id}.json`);
87252
87378
  }
87253
87379
  function ensureDir2() {
87254
87380
  mkdirSync38(registryDir(), { recursive: true });
@@ -87299,7 +87425,7 @@ function acquireRepoLock(repoPath) {
87299
87425
  const lockDir = registryDir();
87300
87426
  mkdirSync39(lockDir, { recursive: true });
87301
87427
  const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
87302
- const lockPath = join76(lockDir, `.lock-${lockName}`);
87428
+ const lockPath = join77(lockDir, `.lock-${lockName}`);
87303
87429
  const deadline = Date.now() + 5000;
87304
87430
  let fd = null;
87305
87431
  while (fd === null) {
@@ -87326,7 +87452,7 @@ function acquireRepoLock(repoPath) {
87326
87452
  }
87327
87453
  var DEFAULT_CONCURRENCY = 5;
87328
87454
  function worktreesBaseDir() {
87329
- return resolve48(process.env.SWITCHROOM_WORKTREE_BASE ?? join76(homedir46(), ".switchroom", "worktree-checkouts"));
87455
+ return resolve48(process.env.SWITCHROOM_WORKTREE_BASE ?? join77(homedir46(), ".switchroom", "worktree-checkouts"));
87330
87456
  }
87331
87457
  function shortId() {
87332
87458
  return randomBytes13(4).toString("hex");
@@ -87348,12 +87474,12 @@ function resolveRepoPath(repo, codeRepos) {
87348
87474
  }
87349
87475
  function expandHome(p) {
87350
87476
  if (p.startsWith("~/"))
87351
- return join76(homedir46(), p.slice(2));
87477
+ return join77(homedir46(), p.slice(2));
87352
87478
  return p;
87353
87479
  }
87354
87480
  async function claimWorktree(input, codeRepos) {
87355
87481
  const repoPath = resolveRepoPath(input.repo, codeRepos);
87356
- if (!existsSync76(repoPath)) {
87482
+ if (!existsSync77(repoPath)) {
87357
87483
  throw new Error(`Repository path does not exist: ${repoPath}`);
87358
87484
  }
87359
87485
  let concurrencyCap = DEFAULT_CONCURRENCY;
@@ -87376,7 +87502,7 @@ async function claimWorktree(input, codeRepos) {
87376
87502
  branch = `task/${taskSuffix}-${id}`;
87377
87503
  const baseDir = worktreesBaseDir();
87378
87504
  mkdirSync39(baseDir, { recursive: true });
87379
- worktreePath = join76(baseDir, `${id}-${taskSuffix}`);
87505
+ worktreePath = join77(baseDir, `${id}-${taskSuffix}`);
87380
87506
  const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
87381
87507
  const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
87382
87508
  const now = new Date().toISOString();
@@ -87409,7 +87535,7 @@ async function claimWorktree(input, codeRepos) {
87409
87535
 
87410
87536
  // src/worktree/release.ts
87411
87537
  import { execFileSync as execFileSync24 } from "node:child_process";
87412
- import { existsSync as existsSync77 } from "node:fs";
87538
+ import { existsSync as existsSync78 } from "node:fs";
87413
87539
  function releaseWorktree(input) {
87414
87540
  const { id } = input;
87415
87541
  const record2 = readRecord(id);
@@ -87417,7 +87543,7 @@ function releaseWorktree(input) {
87417
87543
  return { released: true };
87418
87544
  }
87419
87545
  let gitSuccess = true;
87420
- if (existsSync77(record2.path)) {
87546
+ if (existsSync78(record2.path)) {
87421
87547
  try {
87422
87548
  execFileSync24("git", ["worktree", "remove", "--force", record2.path], {
87423
87549
  cwd: record2.repo,
@@ -87456,7 +87582,7 @@ function listWorktrees() {
87456
87582
 
87457
87583
  // src/worktree/reaper.ts
87458
87584
  import { execFileSync as execFileSync25 } from "node:child_process";
87459
- import { existsSync as existsSync78 } from "node:fs";
87585
+ import { existsSync as existsSync79 } from "node:fs";
87460
87586
  var STALE_THRESHOLD_MS = 10 * 60 * 1000;
87461
87587
  function reapSkipReasonText(action) {
87462
87588
  switch (action) {
@@ -87517,7 +87643,7 @@ function planReaper(nowMs, deps = {}) {
87517
87643
  const plan = [];
87518
87644
  for (const record2 of listRecords()) {
87519
87645
  const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
87520
- const worktreeExists = existsSync78(record2.path);
87646
+ const worktreeExists = existsSync79(record2.path);
87521
87647
  if (!worktreeExists) {
87522
87648
  plan.push({
87523
87649
  record: record2,
@@ -87598,16 +87724,16 @@ function runReaper(nowMs, deps = {}) {
87598
87724
  // src/worktree/gc.ts
87599
87725
  import { execFileSync as execFileSync26 } from "node:child_process";
87600
87726
  import {
87601
- existsSync as existsSync79,
87727
+ existsSync as existsSync80,
87602
87728
  readFileSync as readFileSync68,
87603
87729
  readdirSync as readdirSync27,
87604
- statSync as statSync44,
87730
+ statSync as statSync45,
87605
87731
  renameSync as renameSync17,
87606
87732
  mkdirSync as mkdirSync40,
87607
87733
  rmSync as rmSync15
87608
87734
  } from "node:fs";
87609
87735
  import { homedir as homedir47 } from "node:os";
87610
- import { join as join77, resolve as resolve49 } from "node:path";
87736
+ import { join as join78, resolve as resolve49 } from "node:path";
87611
87737
  function parseGitdirPointer(dotGitFileContents) {
87612
87738
  const m = /^gitdir:\s*(.+?)\s*$/m.exec(dotGitFileContents);
87613
87739
  return m ? m[1] : null;
@@ -87722,17 +87848,17 @@ function defaultPrSignal(repo, branch, exec) {
87722
87848
  }
87723
87849
  }
87724
87850
  function trashRoot() {
87725
- return resolve49(process.env.SWITCHROOM_WORKTREE_TRASH ?? join77(homedir47(), ".switchroom", "worktree-gc-trash"));
87851
+ return resolve49(process.env.SWITCHROOM_WORKTREE_TRASH ?? join78(homedir47(), ".switchroom", "worktree-gc-trash"));
87726
87852
  }
87727
87853
  function planGc(roots, deps = {}) {
87728
- const exists = deps.existsSync ?? existsSync79;
87854
+ const exists = deps.existsSync ?? existsSync80;
87729
87855
  const readDir = deps.readDir ?? ((p) => readdirSync27(p));
87730
87856
  const readFile4 = deps.readFile ?? ((p) => readFileSync68(p, "utf8"));
87731
- const stat3 = deps.stat ?? ((p) => statSync44(p));
87857
+ const stat3 = deps.stat ?? ((p) => statSync45(p));
87732
87858
  const exec = deps.exec ?? defaultExec;
87733
87859
  const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
87734
87860
  const stamp = deps.dateStamp ?? "undated";
87735
- const trash = join77(trashRoot(), stamp);
87861
+ const trash = join78(trashRoot(), stamp);
87736
87862
  let claimed;
87737
87863
  try {
87738
87864
  claimed = new Set(listRecords().map((r) => resolve49(r.path)));
@@ -87768,10 +87894,10 @@ function planGc(roots, deps = {}) {
87768
87894
  continue;
87769
87895
  }
87770
87896
  for (const name of entries) {
87771
- const dir = join77(root, name);
87897
+ const dir = join78(root, name);
87772
87898
  if (isEphemeralPath(dir))
87773
87899
  continue;
87774
- const dotGit = join77(dir, ".git");
87900
+ const dotGit = join78(dir, ".git");
87775
87901
  if (!exists(dotGit))
87776
87902
  continue;
87777
87903
  let st;
@@ -87800,7 +87926,7 @@ function planGc(roots, deps = {}) {
87800
87926
  ownerRepos.add(repoRoot);
87801
87927
  if (exists(ptr))
87802
87928
  continue;
87803
- orphans.push({ dir, owner: repoRoot, dest: join77(trash, name) });
87929
+ orphans.push({ dir, owner: repoRoot, dest: join78(trash, name) });
87804
87930
  }
87805
87931
  }
87806
87932
  const registered = [];
@@ -87904,14 +88030,14 @@ function selectPurgeTargets(entries, olderThanDays) {
87904
88030
  return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
87905
88031
  }
87906
88032
  function listTrashEntries(nowMs, deps = {}) {
87907
- const exists = deps.existsSync ?? existsSync79;
88033
+ const exists = deps.existsSync ?? existsSync80;
87908
88034
  const readDir = deps.readDir ?? ((p) => readdirSync27(p));
87909
88035
  const root = trashRoot();
87910
88036
  if (!exists(root))
87911
88037
  return [];
87912
88038
  const out = [];
87913
88039
  for (const stamp of readDir(root)) {
87914
- const stampDir = join77(root, stamp);
88040
+ const stampDir = join78(root, stamp);
87915
88041
  let names;
87916
88042
  try {
87917
88043
  names = readDir(stampDir);
@@ -87919,10 +88045,10 @@ function listTrashEntries(nowMs, deps = {}) {
87919
88045
  continue;
87920
88046
  }
87921
88047
  for (const name of names) {
87922
- const p = join77(stampDir, name);
88048
+ const p = join78(stampDir, name);
87923
88049
  let mtimeMs = nowMs;
87924
88050
  try {
87925
- mtimeMs = statSync44(p).mtimeMs;
88051
+ mtimeMs = statSync45(p).mtimeMs;
87926
88052
  } catch {}
87927
88053
  out.push({ path: p, ageDays: (nowMs - mtimeMs) / 86400000 });
87928
88054
  }
@@ -87943,7 +88069,7 @@ function purgeTrash(paths) {
87943
88069
  return { deleted, errors: errors2 };
87944
88070
  }
87945
88071
  function defaultRoots() {
87946
- return [join77(homedir47(), "code")];
88072
+ return [join78(homedir47(), "code")];
87947
88073
  }
87948
88074
 
87949
88075
  // src/cli/worktree.ts
@@ -88171,7 +88297,7 @@ import {
88171
88297
  rmSync as rmSync16,
88172
88298
  writeFileSync as writeFileSync24
88173
88299
  } from "node:fs";
88174
- import { join as join78 } from "node:path";
88300
+ import { join as join79 } from "node:path";
88175
88301
  function encodeCredentialsFilename(email) {
88176
88302
  const SAFE = new Set([
88177
88303
  ..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
@@ -88361,16 +88487,16 @@ function resolveCredentialsDir(env2) {
88361
88487
  if (explicit && explicit.length > 0)
88362
88488
  return explicit;
88363
88489
  const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
88364
- return join78(stateBase, "google-workspace-mcp", "credentials");
88490
+ return join79(stateBase, "google-workspace-mcp", "credentials");
88365
88491
  }
88366
88492
  function writeSeedFile(dir, email, seed) {
88367
88493
  mkdirSync41(dir, { recursive: true, mode: 448 });
88368
88494
  chmodSync10(dir, 448);
88369
88495
  for (const name of readdirSync28(dir)) {
88370
- rmSync16(join78(dir, name), { force: true, recursive: true });
88496
+ rmSync16(join79(dir, name), { force: true, recursive: true });
88371
88497
  }
88372
88498
  const filename = encodeCredentialsFilename(email);
88373
- const filePath = join78(dir, filename);
88499
+ const filePath = join79(dir, filename);
88374
88500
  writeFileSync24(filePath, JSON.stringify(seed), { mode: 384 });
88375
88501
  chmodSync10(filePath, 384);
88376
88502
  return filePath;
@@ -88530,7 +88656,7 @@ function registerDriveMcpLauncherCommand(program3) {
88530
88656
  init_scaffold_integration();
88531
88657
  import { spawn as spawn5 } from "node:child_process";
88532
88658
  import { writeFileSync as writeFileSync25, mkdirSync as mkdirSync42 } from "node:fs";
88533
- import { dirname as dirname24, join as join79 } from "node:path";
88659
+ import { dirname as dirname24, join as join80 } from "node:path";
88534
88660
  var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
88535
88661
  var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
88536
88662
  var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
@@ -88570,7 +88696,7 @@ function writeRefreshHeartbeat(agentName, data) {
88570
88696
  function heartbeatPath(agentName) {
88571
88697
  const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
88572
88698
  if (override) {
88573
- return join79(override, `m365-launcher-${agentName}.heartbeat.json`);
88699
+ return join80(override, `m365-launcher-${agentName}.heartbeat.json`);
88574
88700
  }
88575
88701
  return "/state/agent/m365-launcher.heartbeat.json";
88576
88702
  }
@@ -88802,7 +88928,7 @@ function registerM365McpLauncherCommand(program3) {
88802
88928
  // src/cli/notion-mcp-launcher.ts
88803
88929
  init_scaffold_integration();
88804
88930
  import { spawn as spawn6 } from "node:child_process";
88805
- import { existsSync as existsSync80, mkdirSync as mkdirSync43, writeFileSync as writeFileSync26 } from "node:fs";
88931
+ import { existsSync as existsSync81, mkdirSync as mkdirSync43, writeFileSync as writeFileSync26 } from "node:fs";
88806
88932
  import { dirname as dirname25 } from "node:path";
88807
88933
  var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
88808
88934
  var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
@@ -88814,7 +88940,7 @@ function buildNotionMcpArgs(opts) {
88814
88940
  function defaultWriteHeartbeat(path7, contents) {
88815
88941
  try {
88816
88942
  const dir = dirname25(path7);
88817
- if (!existsSync80(dir))
88943
+ if (!existsSync81(dir))
88818
88944
  mkdirSync43(dir, { recursive: true });
88819
88945
  writeFileSync26(path7, contents);
88820
88946
  } catch {}
@@ -88926,7 +89052,7 @@ function registerNotionMcpLauncherCommand(program3) {
88926
89052
 
88927
89053
  // src/cli/deliver-file.ts
88928
89054
  init_client2();
88929
- import { readFileSync as readFileSync69, statSync as statSync45 } from "node:fs";
89055
+ import { readFileSync as readFileSync69, statSync as statSync46 } from "node:fs";
88930
89056
  import { basename as basename11 } from "node:path";
88931
89057
 
88932
89058
  // src/delivery/onedrive.ts
@@ -89265,7 +89391,7 @@ async function defaultResolveProvider() {
89265
89391
  }
89266
89392
  async function runDeliverFile(localPath, deps = {}) {
89267
89393
  const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
89268
- const sizeOf = deps.fileSize ?? ((p) => statSync45(p).size);
89394
+ const sizeOf = deps.fileSize ?? ((p) => statSync46(p).size);
89269
89395
  const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync69(p)));
89270
89396
  const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
89271
89397
  let size;
@@ -89587,8 +89713,8 @@ function runRedactStdin() {
89587
89713
  }
89588
89714
 
89589
89715
  // src/cli/status-ask.ts
89590
- import { readFileSync as readFileSync73, existsSync as existsSync85, readdirSync as readdirSync30 } from "node:fs";
89591
- import { join as join84 } from "node:path";
89716
+ import { readFileSync as readFileSync73, existsSync as existsSync86, readdirSync as readdirSync30 } from "node:fs";
89717
+ import { join as join85 } from "node:path";
89592
89718
  import { homedir as homedir50 } from "node:os";
89593
89719
 
89594
89720
  // src/status-ask/report.ts
@@ -89910,7 +90036,7 @@ function runReport(opts) {
89910
90036
  function resolveSources(explicitPath) {
89911
90037
  if (explicitPath != null && explicitPath.trim() !== "") {
89912
90038
  const trimmed = explicitPath.trim();
89913
- if (!existsSync85(trimmed)) {
90039
+ if (!existsSync86(trimmed)) {
89914
90040
  process.stderr.write(`status-ask report: ${trimmed}: file not found
89915
90041
  `);
89916
90042
  process.exit(1);
@@ -89924,9 +90050,9 @@ function resolveSources(explicitPath) {
89924
90050
  const config = loadConfig();
89925
90051
  agentsDir = resolveAgentsDir(config);
89926
90052
  } catch {
89927
- agentsDir = join84(homedir50(), ".switchroom", "agents");
90053
+ agentsDir = join85(homedir50(), ".switchroom", "agents");
89928
90054
  }
89929
- if (!existsSync85(agentsDir))
90055
+ if (!existsSync86(agentsDir))
89930
90056
  return [];
89931
90057
  const sources = [];
89932
90058
  let entries;
@@ -89936,8 +90062,8 @@ function resolveSources(explicitPath) {
89936
90062
  return [];
89937
90063
  }
89938
90064
  for (const name of entries) {
89939
- const path8 = join84(agentsDir, name, "runtime-metrics.jsonl");
89940
- if (existsSync85(path8)) {
90065
+ const path8 = join85(agentsDir, name, "runtime-metrics.jsonl");
90066
+ if (existsSync86(path8)) {
89941
90067
  sources.push({ path: path8, agent: name });
89942
90068
  }
89943
90069
  }
@@ -89967,32 +90093,32 @@ var import_yaml21 = __toESM(require_dist(), 1);
89967
90093
  init_paths();
89968
90094
  import {
89969
90095
  closeSync as closeSync14,
89970
- existsSync as existsSync86,
90096
+ existsSync as existsSync87,
89971
90097
  fsyncSync as fsyncSync8,
89972
90098
  mkdirSync as mkdirSync48,
89973
90099
  openSync as openSync14,
89974
90100
  readdirSync as readdirSync31,
89975
90101
  readFileSync as readFileSync74,
89976
90102
  renameSync as renameSync19,
89977
- statSync as statSync47,
90103
+ statSync as statSync48,
89978
90104
  unlinkSync as unlinkSync18,
89979
90105
  writeSync as writeSync10
89980
90106
  } from "node:fs";
89981
- import { join as join85, resolve as resolve52 } from "node:path";
90107
+ import { join as join86, resolve as resolve52 } from "node:path";
89982
90108
  var STAGING_SUBDIR = ".staging";
89983
90109
  function overlayPathsFor(agent, opts = {}) {
89984
90110
  const base = opts.root ? resolve52(opts.root, agent) : resolve52(resolveDualPath(`~/.switchroom/agents/${agent}`));
89985
- const scheduleDir = join85(base, "schedule.d");
89986
- const scheduleStagingDir = join85(scheduleDir, STAGING_SUBDIR);
89987
- const skillsDir = join85(base, "skills.d");
89988
- const skillsStagingDir = join85(skillsDir, STAGING_SUBDIR);
90111
+ const scheduleDir = join86(base, "schedule.d");
90112
+ const scheduleStagingDir = join86(scheduleDir, STAGING_SUBDIR);
90113
+ const skillsDir = join86(base, "skills.d");
90114
+ const skillsStagingDir = join86(skillsDir, STAGING_SUBDIR);
89989
90115
  return {
89990
90116
  agentRoot: base,
89991
90117
  scheduleDir,
89992
90118
  scheduleStagingDir,
89993
90119
  skillsDir,
89994
90120
  skillsStagingDir,
89995
- lockPath: join85(base, ".lock"),
90121
+ lockPath: join86(base, ".lock"),
89996
90122
  stagingDir: scheduleStagingDir
89997
90123
  };
89998
90124
  }
@@ -90018,7 +90144,7 @@ function withAgentLock(paths, fn) {
90018
90144
  if (e.code !== "EEXIST")
90019
90145
  throw err;
90020
90146
  try {
90021
- const age = Date.now() - statSync47(paths.lockPath).mtimeMs;
90147
+ const age = Date.now() - statSync48(paths.lockPath).mtimeMs;
90022
90148
  if (age > 30000) {
90023
90149
  unlinkSync18(paths.lockPath);
90024
90150
  continue;
@@ -90046,8 +90172,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
90046
90172
  const paths = overlayPathsFor(agent, opts);
90047
90173
  return withAgentLock(paths, () => {
90048
90174
  ensureDirs(paths);
90049
- const stagingPath = join85(paths.scheduleStagingDir, `${slug}.yaml`);
90050
- const finalPath = join85(paths.scheduleDir, `${slug}.yaml`);
90175
+ const stagingPath = join86(paths.scheduleStagingDir, `${slug}.yaml`);
90176
+ const finalPath = join86(paths.scheduleDir, `${slug}.yaml`);
90051
90177
  const fd = openSync14(stagingPath, "w", 384);
90052
90178
  try {
90053
90179
  writeSync10(fd, yamlText);
@@ -90063,8 +90189,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
90063
90189
  const paths = overlayPathsFor(agent, opts);
90064
90190
  return withAgentLock(paths, () => {
90065
90191
  ensureSkillsDirs(paths);
90066
- const stagingPath = join85(paths.skillsStagingDir, `${slug}.yaml`);
90067
- const finalPath = join85(paths.skillsDir, `${slug}.yaml`);
90192
+ const stagingPath = join86(paths.skillsStagingDir, `${slug}.yaml`);
90193
+ const finalPath = join86(paths.skillsDir, `${slug}.yaml`);
90068
90194
  const fd = openSync14(stagingPath, "w", 384);
90069
90195
  try {
90070
90196
  writeSync10(fd, yamlText);
@@ -90079,8 +90205,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
90079
90205
  function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
90080
90206
  const paths = overlayPathsFor(agent, opts);
90081
90207
  return withAgentLock(paths, () => {
90082
- const finalPath = join85(paths.skillsDir, `${slug}.yaml`);
90083
- if (!existsSync86(finalPath))
90208
+ const finalPath = join86(paths.skillsDir, `${slug}.yaml`);
90209
+ if (!existsSync87(finalPath))
90084
90210
  return false;
90085
90211
  unlinkSync18(finalPath);
90086
90212
  return true;
@@ -90088,13 +90214,13 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
90088
90214
  }
90089
90215
  function listSkillsOverlayEntries(agent, opts = {}) {
90090
90216
  const paths = overlayPathsFor(agent, opts);
90091
- if (!existsSync86(paths.skillsDir))
90217
+ if (!existsSync87(paths.skillsDir))
90092
90218
  return [];
90093
90219
  const out = [];
90094
90220
  for (const name of readdirSync31(paths.skillsDir)) {
90095
90221
  if (!/\.ya?ml$/i.test(name))
90096
90222
  continue;
90097
- const full = join85(paths.skillsDir, name);
90223
+ const full = join86(paths.skillsDir, name);
90098
90224
  try {
90099
90225
  const raw = readFileSync74(full, "utf-8");
90100
90226
  const slug = name.replace(/\.ya?ml$/i, "");
@@ -90106,8 +90232,8 @@ function listSkillsOverlayEntries(agent, opts = {}) {
90106
90232
  function deleteOverlayEntry(agent, slug, opts = {}) {
90107
90233
  const paths = overlayPathsFor(agent, opts);
90108
90234
  return withAgentLock(paths, () => {
90109
- const finalPath = join85(paths.scheduleDir, `${slug}.yaml`);
90110
- if (!existsSync86(finalPath))
90235
+ const finalPath = join86(paths.scheduleDir, `${slug}.yaml`);
90236
+ if (!existsSync87(finalPath))
90111
90237
  return false;
90112
90238
  unlinkSync18(finalPath);
90113
90239
  return true;
@@ -90115,13 +90241,13 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
90115
90241
  }
90116
90242
  function listOverlayEntries(agent, opts = {}) {
90117
90243
  const paths = overlayPathsFor(agent, opts);
90118
- if (!existsSync86(paths.scheduleDir))
90244
+ if (!existsSync87(paths.scheduleDir))
90119
90245
  return [];
90120
90246
  const out = [];
90121
90247
  for (const name of readdirSync31(paths.scheduleDir)) {
90122
90248
  if (!/\.ya?ml$/i.test(name))
90123
90249
  continue;
90124
- const full = join85(paths.scheduleDir, name);
90250
+ const full = join86(paths.scheduleDir, name);
90125
90251
  try {
90126
90252
  const raw = readFileSync74(full, "utf-8");
90127
90253
  const slug = name.replace(/\.ya?ml$/i, "");
@@ -90343,7 +90469,7 @@ function reconcileAgentCronOnly(agent) {
90343
90469
  // src/cli/agent-config-pending.ts
90344
90470
  import {
90345
90471
  closeSync as closeSync15,
90346
- existsSync as existsSync87,
90472
+ existsSync as existsSync88,
90347
90473
  fsyncSync as fsyncSync9,
90348
90474
  mkdirSync as mkdirSync49,
90349
90475
  openSync as openSync15,
@@ -90354,12 +90480,12 @@ import {
90354
90480
  writeFileSync as writeFileSync31,
90355
90481
  writeSync as writeSync11
90356
90482
  } from "node:fs";
90357
- import { join as join86 } from "node:path";
90483
+ import { join as join87 } from "node:path";
90358
90484
  import { randomBytes as randomBytes15 } from "node:crypto";
90359
90485
  var STAGE_ID_PREFIX = "cap_";
90360
90486
  function pendingDir(agent, opts = {}) {
90361
90487
  const paths = overlayPathsFor(agent, opts);
90362
- return join86(paths.scheduleDir, ".pending");
90488
+ return join87(paths.scheduleDir, ".pending");
90363
90489
  }
90364
90490
  function ensurePendingDir(agent, opts = {}) {
90365
90491
  const dir = pendingDir(agent, opts);
@@ -90372,8 +90498,8 @@ function newStageId() {
90372
90498
  function stagePendingScheduleEntry(opts) {
90373
90499
  const dir = ensurePendingDir(opts.agent, { root: opts.root });
90374
90500
  const stageId = opts.stageId ?? newStageId();
90375
- const yamlPath = join86(dir, `${stageId}.yaml`);
90376
- const metaPath = join86(dir, `${stageId}.meta.json`);
90501
+ const yamlPath = join87(dir, `${stageId}.yaml`);
90502
+ const metaPath = join87(dir, `${stageId}.meta.json`);
90377
90503
  const meta = {
90378
90504
  v: 1,
90379
90505
  stage_id: stageId,
@@ -90400,16 +90526,16 @@ function stagePendingScheduleEntry(opts) {
90400
90526
  }
90401
90527
  function listPendingScheduleEntries(agent, opts = {}) {
90402
90528
  const dir = pendingDir(agent, opts);
90403
- if (!existsSync87(dir))
90529
+ if (!existsSync88(dir))
90404
90530
  return [];
90405
90531
  const out = [];
90406
90532
  for (const name of readdirSync32(dir).sort()) {
90407
90533
  if (!name.endsWith(".meta.json"))
90408
90534
  continue;
90409
90535
  const stageId = name.slice(0, -".meta.json".length);
90410
- const metaPath = join86(dir, name);
90411
- const yamlPath = join86(dir, `${stageId}.yaml`);
90412
- if (!existsSync87(yamlPath))
90536
+ const metaPath = join87(dir, name);
90537
+ const yamlPath = join87(dir, `${stageId}.yaml`);
90538
+ if (!existsSync88(yamlPath))
90413
90539
  continue;
90414
90540
  try {
90415
90541
  const meta = JSON.parse(readFileSync75(metaPath, "utf-8"));
@@ -90427,8 +90553,8 @@ function commitPendingScheduleEntry(opts) {
90427
90553
  return { committed: false, reason: "not_found" };
90428
90554
  const slug = match.meta.entry.name ?? match.stageId;
90429
90555
  const paths = overlayPathsFor(opts.agent, { root: opts.root });
90430
- const finalPath = join86(paths.scheduleDir, `${slug}.yaml`);
90431
- if (existsSync87(finalPath)) {
90556
+ const finalPath = join87(paths.scheduleDir, `${slug}.yaml`);
90557
+ if (existsSync88(finalPath)) {
90432
90558
  return { committed: false, reason: "slug_collision" };
90433
90559
  }
90434
90560
  renameSync20(match.yamlPath, finalPath);
@@ -90450,7 +90576,7 @@ function denyPendingScheduleEntry(opts) {
90450
90576
  }
90451
90577
 
90452
90578
  // src/cli/agent-config-write.ts
90453
- import { existsSync as existsSync88, readFileSync as readFileSync76 } from "node:fs";
90579
+ import { existsSync as existsSync89, readFileSync as readFileSync76 } from "node:fs";
90454
90580
  import { execFileSync as execFileSync28 } from "node:child_process";
90455
90581
 
90456
90582
  // src/scheduler/schedule-report.ts
@@ -90840,7 +90966,7 @@ function scheduleRemove(opts) {
90840
90966
  }
90841
90967
  let priorContent = null;
90842
90968
  try {
90843
- if (existsSync88(match.path))
90969
+ if (existsSync89(match.path))
90844
90970
  priorContent = readFileSync76(match.path, "utf-8");
90845
90971
  } catch {}
90846
90972
  deleteOverlayEntry(agent, match.slug, { root: opts.root });
@@ -91044,7 +91170,7 @@ function registerAgentConfigWriteCommands(program3) {
91044
91170
  }
91045
91171
  let blob;
91046
91172
  if (opts.jsonl) {
91047
- blob = existsSync88(opts.jsonl) ? readFileSync76(opts.jsonl, "utf-8") : "";
91173
+ blob = existsSync89(opts.jsonl) ? readFileSync76(opts.jsonl, "utf-8") : "";
91048
91174
  } else {
91049
91175
  try {
91050
91176
  blob = execFileSync28("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
@@ -91076,11 +91202,11 @@ function registerAgentConfigWriteCommands(program3) {
91076
91202
 
91077
91203
  // src/cli/agent-config-skill-write.ts
91078
91204
  var import_yaml22 = __toESM(require_dist(), 1);
91079
- import { existsSync as existsSync89 } from "node:fs";
91205
+ import { existsSync as existsSync90 } from "node:fs";
91080
91206
  init_reconcile_default_skills();
91081
91207
  init_agent_config();
91082
91208
  var import_yaml23 = __toESM(require_dist(), 1);
91083
- import { join as join87 } from "node:path";
91209
+ import { join as join88 } from "node:path";
91084
91210
  var MAX_SKILLS_PER_AGENT = 20;
91085
91211
  var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
91086
91212
  function exitCodeFor2(code) {
@@ -91155,8 +91281,8 @@ function skillInstall(opts) {
91155
91281
  return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
91156
91282
  }
91157
91283
  const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
91158
- const skillPath = join87(poolDir, skillName);
91159
- if (!existsSync89(skillPath)) {
91284
+ const skillPath = join88(poolDir, skillName);
91285
+ if (!existsSync90(skillPath)) {
91160
91286
  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.`);
91161
91287
  }
91162
91288
  const yamlText = import_yaml22.stringify({ skills: [skillName] });
@@ -91320,8 +91446,8 @@ function registerAgentConfigSkillWriteCommands(program3) {
91320
91446
  // src/cli/skill.ts
91321
91447
  import {
91322
91448
  closeSync as closeSync16,
91323
- existsSync as existsSync90,
91324
- lstatSync as lstatSync10,
91449
+ existsSync as existsSync91,
91450
+ lstatSync as lstatSync11,
91325
91451
  mkdirSync as mkdirSync50,
91326
91452
  mkdtempSync as mkdtempSync5,
91327
91453
  openSync as openSync16,
@@ -91330,11 +91456,11 @@ import {
91330
91456
  realpathSync as realpathSync7,
91331
91457
  renameSync as renameSync21,
91332
91458
  rmSync as rmSync18,
91333
- statSync as statSync48,
91459
+ statSync as statSync49,
91334
91460
  writeFileSync as writeFileSync32
91335
91461
  } from "node:fs";
91336
91462
  import { tmpdir as tmpdir5, homedir as homedir51 } from "node:os";
91337
- import { dirname as dirname30, join as join88, relative as relative2, resolve as resolve53 } from "node:path";
91463
+ import { dirname as dirname30, join as join89, relative as relative2, resolve as resolve53 } from "node:path";
91338
91464
  import { spawnSync as spawnSync16 } from "node:child_process";
91339
91465
 
91340
91466
  // src/cli/skill-common.ts
@@ -91568,7 +91694,7 @@ function scanForClaudeP2(content) {
91568
91694
  function resolveSkillsPoolDir2(override) {
91569
91695
  const raw = override ?? "~/.switchroom/skills";
91570
91696
  if (raw.startsWith("~/")) {
91571
- return join88(homedir51(), raw.slice(2));
91697
+ return join89(homedir51(), raw.slice(2));
91572
91698
  }
91573
91699
  if (raw === "~")
91574
91700
  return homedir51();
@@ -91600,14 +91726,14 @@ function isTarballPath(p) {
91600
91726
  }
91601
91727
  function loadFromDir(dir) {
91602
91728
  const abs = realpathSync7(dir);
91603
- if (!statSync48(abs).isDirectory()) {
91729
+ if (!statSync49(abs).isDirectory()) {
91604
91730
  fail3(`--from path is not a directory: ${dir}`);
91605
91731
  }
91606
91732
  const files = {};
91607
91733
  const walk2 = (sub) => {
91608
91734
  const entries = readdirSync33(sub, { withFileTypes: true });
91609
91735
  for (const ent of entries) {
91610
- const full = join88(sub, ent.name);
91736
+ const full = join89(sub, ent.name);
91611
91737
  const rel = relative2(abs, full);
91612
91738
  if (ent.isSymbolicLink()) {
91613
91739
  fail3(`refusing to read symlink inside --from dir: ${rel}`);
@@ -91642,7 +91768,7 @@ function loadFromTarball(tarPath) {
91642
91768
  fail3(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
91643
91769
  }
91644
91770
  }
91645
- const staging = mkdtempSync5(join88(tmpdir5(), "skill-apply-extract-"));
91771
+ const staging = mkdtempSync5(join89(tmpdir5(), "skill-apply-extract-"));
91646
91772
  try {
91647
91773
  const flags = isGz ? ["-xzf"] : ["-xf"];
91648
91774
  const r = spawnSync16("tar", [
@@ -91728,8 +91854,8 @@ function validatePayload(name, files) {
91728
91854
  errors2.push(`${path8} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
91729
91855
  }
91730
91856
  } else if (PY_SCRIPT_RE2.test(path8)) {
91731
- const tmp = mkdtempSync5(join88(tmpdir5(), "skill-apply-py-"));
91732
- const tmpPy = join88(tmp, "check.py");
91857
+ const tmp = mkdtempSync5(join89(tmpdir5(), "skill-apply-py-"));
91858
+ const tmpPy = join89(tmp, "check.py");
91733
91859
  try {
91734
91860
  writeFileSync32(tmpPy, content);
91735
91861
  const r = spawnSync16("python3", ["-m", "py_compile", tmpPy], {
@@ -91749,10 +91875,10 @@ function validatePayload(name, files) {
91749
91875
  function diffSummary(currentDir, files) {
91750
91876
  const lines = [];
91751
91877
  const currentFiles = {};
91752
- if (existsSync90(currentDir)) {
91878
+ if (existsSync91(currentDir)) {
91753
91879
  const walk2 = (sub) => {
91754
91880
  for (const ent of readdirSync33(sub, { withFileTypes: true })) {
91755
- const full = join88(sub, ent.name);
91881
+ const full = join89(sub, ent.name);
91756
91882
  const rel = relative2(currentDir, full);
91757
91883
  if (ent.isDirectory()) {
91758
91884
  walk2(full);
@@ -91785,13 +91911,13 @@ function diffSummary(currentDir, files) {
91785
91911
  `);
91786
91912
  }
91787
91913
  function writePayload(poolDir, name, files) {
91788
- if (!existsSync90(poolDir)) {
91914
+ if (!existsSync91(poolDir)) {
91789
91915
  mkdirSync50(poolDir, { recursive: true, mode: 493 });
91790
91916
  }
91791
- const target = join88(poolDir, name);
91917
+ const target = join89(poolDir, name);
91792
91918
  let targetIsSymlink = false;
91793
91919
  try {
91794
- const st = lstatSync10(target);
91920
+ const st = lstatSync11(target);
91795
91921
  if (st.isSymbolicLink()) {
91796
91922
  targetIsSymlink = true;
91797
91923
  }
@@ -91799,11 +91925,11 @@ function writePayload(poolDir, name, files) {
91799
91925
  if (targetIsSymlink) {
91800
91926
  fail3(`refusing to overwrite symlink at ${target}; investigate manually`);
91801
91927
  }
91802
- const staging = mkdtempSync5(join88(poolDir, `.skill-apply-stage-${name}-`));
91928
+ const staging = mkdtempSync5(join89(poolDir, `.skill-apply-stage-${name}-`));
91803
91929
  let oldRename = null;
91804
91930
  try {
91805
91931
  for (const [path8, content] of Object.entries(files)) {
91806
- const full = join88(staging, path8);
91932
+ const full = join89(staging, path8);
91807
91933
  mkdirSync50(dirname30(full), { recursive: true, mode: 493 });
91808
91934
  const fd = openSync16(full, "wx");
91809
91935
  try {
@@ -91818,7 +91944,7 @@ function writePayload(poolDir, name, files) {
91818
91944
  }
91819
91945
  let targetExists = false;
91820
91946
  try {
91821
- lstatSync10(target);
91947
+ lstatSync11(target);
91822
91948
  targetExists = true;
91823
91949
  } catch {}
91824
91950
  if (targetExists) {
@@ -91834,9 +91960,9 @@ function writePayload(poolDir, name, files) {
91834
91960
  try {
91835
91961
  rmSync18(staging, { recursive: true, force: true });
91836
91962
  } catch {}
91837
- if (oldRename && existsSync90(oldRename)) {
91963
+ if (oldRename && existsSync91(oldRename)) {
91838
91964
  try {
91839
- if (existsSync90(target)) {
91965
+ if (existsSync91(target)) {
91840
91966
  rmSync18(target, { recursive: true, force: true });
91841
91967
  }
91842
91968
  renameSync21(oldRename, target);
@@ -91857,10 +91983,10 @@ function registerSkillCommand(program3) {
91857
91983
  files = loadFromStdin();
91858
91984
  } else {
91859
91985
  const fromPath = resolve53(opts.from);
91860
- if (!existsSync90(fromPath)) {
91986
+ if (!existsSync91(fromPath)) {
91861
91987
  fail3(`--from path does not exist: ${opts.from}`);
91862
91988
  }
91863
- const st = statSync48(fromPath);
91989
+ const st = statSync49(fromPath);
91864
91990
  if (st.isDirectory()) {
91865
91991
  files = loadFromDir(fromPath);
91866
91992
  } else if (isTarballPath(fromPath)) {
@@ -91881,7 +92007,7 @@ function registerSkillCommand(program3) {
91881
92007
  }
91882
92008
  const config = loadConfig();
91883
92009
  const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
91884
- const currentDir = join88(poolDir, name);
92010
+ const currentDir = join89(poolDir, name);
91885
92011
  console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
91886
92012
  console.log(source_default.bold("Diff vs current pool content:"));
91887
92013
  console.log(diffSummary(currentDir, files));
@@ -91913,8 +92039,8 @@ function sumBytes(files) {
91913
92039
  init_esm();
91914
92040
  import {
91915
92041
  closeSync as closeSync17,
91916
- existsSync as existsSync91,
91917
- lstatSync as lstatSync11,
92042
+ existsSync as existsSync92,
92043
+ lstatSync as lstatSync12,
91918
92044
  mkdirSync as mkdirSync51,
91919
92045
  mkdtempSync as mkdtempSync6,
91920
92046
  openSync as openSync17,
@@ -91922,11 +92048,11 @@ import {
91922
92048
  readdirSync as readdirSync34,
91923
92049
  renameSync as renameSync22,
91924
92050
  rmSync as rmSync19,
91925
- statSync as statSync49,
92051
+ statSync as statSync50,
91926
92052
  utimesSync,
91927
92053
  writeFileSync as writeFileSync33
91928
92054
  } from "node:fs";
91929
- import { dirname as dirname31, join as join89, relative as relative3, resolve as resolve54 } from "node:path";
92055
+ import { dirname as dirname31, join as join90, relative as relative3, resolve as resolve54 } from "node:path";
91930
92056
  import { homedir as homedir52, tmpdir as tmpdir6 } from "node:os";
91931
92057
  import { spawnSync as spawnSync17 } from "node:child_process";
91932
92058
  init_helpers();
@@ -91938,15 +92064,15 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
91938
92064
  var PERSONAL_SKILLS_SUBPATH = "personal-skills";
91939
92065
  function resolveConfigSkillsDir(agent) {
91940
92066
  const override = process.env.SWITCHROOM_CONFIG_DIR;
91941
- const candidate = override ? resolve54(override) : join89(homedir52(), ".switchroom-config");
91942
- if (!existsSync91(candidate))
92067
+ const candidate = override ? resolve54(override) : join90(homedir52(), ".switchroom-config");
92068
+ if (!existsSync92(candidate))
91943
92069
  return null;
91944
- return join89(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
92070
+ return join90(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
91945
92071
  }
91946
92072
  var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
91947
92073
  function sweepMirrorPriors(configSkillsRoot) {
91948
92074
  try {
91949
- if (!existsSync91(configSkillsRoot))
92075
+ if (!existsSync92(configSkillsRoot))
91950
92076
  return;
91951
92077
  const now = Date.now();
91952
92078
  for (const ent of readdirSync34(configSkillsRoot)) {
@@ -91959,7 +92085,7 @@ function sweepMirrorPriors(configSkillsRoot) {
91959
92085
  if (now - ts < MIRROR_PRIOR_TTL_MS)
91960
92086
  continue;
91961
92087
  try {
91962
- rmSync19(join89(configSkillsRoot, ent), { recursive: true, force: true });
92088
+ rmSync19(join90(configSkillsRoot, ent), { recursive: true, force: true });
91963
92089
  } catch {}
91964
92090
  }
91965
92091
  } catch {}
@@ -91968,11 +92094,11 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
91968
92094
  const configSkillsRoot = resolveConfigSkillsDir(agent);
91969
92095
  if (!configSkillsRoot)
91970
92096
  return;
91971
- const dest = join89(configSkillsRoot, name);
92097
+ const dest = join90(configSkillsRoot, name);
91972
92098
  try {
91973
92099
  if (liveSkillDir !== null) {
91974
92100
  try {
91975
- const st = lstatSync11(liveSkillDir);
92101
+ const st = lstatSync12(liveSkillDir);
91976
92102
  if (st.isSymbolicLink()) {
91977
92103
  process.stderr.write(source_default.yellow(`warning: refusing to mirror ${liveSkillDir} \u2014 source is a symlink
91978
92104
  `));
@@ -91982,20 +92108,20 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
91982
92108
  }
91983
92109
  if (liveSkillDir === null) {
91984
92110
  sweepMirrorPriors(configSkillsRoot);
91985
- if (existsSync91(dest)) {
91986
- const trash = join89(configSkillsRoot, `.${name}-trash-${Date.now()}`);
92111
+ if (existsSync92(dest)) {
92112
+ const trash = join90(configSkillsRoot, `.${name}-trash-${Date.now()}`);
91987
92113
  renameSync22(dest, trash);
91988
92114
  }
91989
92115
  return;
91990
92116
  }
91991
92117
  mkdirSync51(configSkillsRoot, { recursive: true, mode: 493 });
91992
92118
  sweepMirrorPriors(configSkillsRoot);
91993
- const staging = mkdtempSync6(join89(configSkillsRoot, `.${name}-staging-`));
92119
+ const staging = mkdtempSync6(join90(configSkillsRoot, `.${name}-staging-`));
91994
92120
  const walk2 = (src, dst) => {
91995
92121
  mkdirSync51(dst, { recursive: true, mode: 493 });
91996
92122
  for (const ent of readdirSync34(src, { withFileTypes: true })) {
91997
- const s = join89(src, ent.name);
91998
- const d = join89(dst, ent.name);
92123
+ const s = join90(src, ent.name);
92124
+ const d = join90(dst, ent.name);
91999
92125
  if (ent.isSymbolicLink())
92000
92126
  continue;
92001
92127
  if (ent.isDirectory())
@@ -92006,8 +92132,8 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
92006
92132
  }
92007
92133
  };
92008
92134
  walk2(liveSkillDir, staging);
92009
- if (existsSync91(dest)) {
92010
- const prior = join89(configSkillsRoot, `.${name}-prior-${Date.now()}`);
92135
+ if (existsSync92(dest)) {
92136
+ const prior = join90(configSkillsRoot, `.${name}-prior-${Date.now()}`);
92011
92137
  renameSync22(dest, prior);
92012
92138
  }
92013
92139
  renameSync22(staging, dest);
@@ -92037,17 +92163,17 @@ function resolveAgent(opts) {
92037
92163
  function resolveAgentsRoot(opts) {
92038
92164
  if (opts.root)
92039
92165
  return resolve54(opts.root);
92040
- return join89(homedir52(), ".switchroom", "agents");
92166
+ return join90(homedir52(), ".switchroom", "agents");
92041
92167
  }
92042
92168
  function personalSkillDir(agentsRoot, agent, name) {
92043
- return join89(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
92169
+ return join90(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
92044
92170
  }
92045
92171
  function trashDir(agentsRoot, agent) {
92046
- return join89(agentsRoot, agent, ".claude", TRASH_DIRNAME);
92172
+ return join90(agentsRoot, agent, ".claude", TRASH_DIRNAME);
92047
92173
  }
92048
92174
  function countPersonalSkills(agentsRoot, agent) {
92049
- const skillsDir = join89(agentsRoot, agent, ".claude", "skills");
92050
- if (!existsSync91(skillsDir))
92175
+ const skillsDir = join90(agentsRoot, agent, ".claude", "skills");
92176
+ if (!existsSync92(skillsDir))
92051
92177
  return 0;
92052
92178
  let n = 0;
92053
92179
  for (const ent of readdirSync34(skillsDir, { withFileTypes: true })) {
@@ -92078,13 +92204,13 @@ function readStdinSync2() {
92078
92204
  }
92079
92205
  function loadFromDir2(dir) {
92080
92206
  const abs = resolve54(dir);
92081
- if (!statSync49(abs).isDirectory()) {
92207
+ if (!statSync50(abs).isDirectory()) {
92082
92208
  fail4(`--from path is not a directory: ${dir}`);
92083
92209
  }
92084
92210
  const files = {};
92085
92211
  const walk2 = (sub) => {
92086
92212
  for (const ent of readdirSync34(sub, { withFileTypes: true })) {
92087
- const full = join89(sub, ent.name);
92213
+ const full = join90(sub, ent.name);
92088
92214
  if (ent.isSymbolicLink()) {
92089
92215
  fail4(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
92090
92216
  }
@@ -92137,8 +92263,8 @@ function behavioralValidate(files) {
92137
92263
  errors2.push(`${path8} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
92138
92264
  }
92139
92265
  } else if (PY_SCRIPT_RE.test(path8)) {
92140
- const tmp = mkdtempSync6(join89(tmpdir6(), "skill-personal-py-"));
92141
- const tmpPy = join89(tmp, "check.py");
92266
+ const tmp = mkdtempSync6(join90(tmpdir6(), "skill-personal-py-"));
92267
+ const tmpPy = join90(tmp, "check.py");
92142
92268
  try {
92143
92269
  writeFileSync33(tmpPy, content);
92144
92270
  const r = spawnSync17("python3", ["-m", "py_compile", tmpPy], {
@@ -92156,15 +92282,15 @@ function behavioralValidate(files) {
92156
92282
  }
92157
92283
  function sweepTrash(agentsRoot, agent) {
92158
92284
  const trash = trashDir(agentsRoot, agent);
92159
- if (!existsSync91(trash))
92285
+ if (!existsSync92(trash))
92160
92286
  return;
92161
92287
  const now = Date.now();
92162
92288
  for (const ent of readdirSync34(trash, { withFileTypes: true })) {
92163
92289
  if (!ent.isDirectory())
92164
92290
  continue;
92165
- const entPath = join89(trash, ent.name);
92291
+ const entPath = join90(trash, ent.name);
92166
92292
  try {
92167
- const st = statSync49(entPath);
92293
+ const st = statSync50(entPath);
92168
92294
  if (now - st.mtimeMs > TRASH_TTL_MS) {
92169
92295
  rmSync19(entPath, { recursive: true, force: true });
92170
92296
  }
@@ -92174,7 +92300,7 @@ function sweepTrash(agentsRoot, agent) {
92174
92300
  function writePersonalSkill(targetDir, files) {
92175
92301
  let targetIsSymlink = false;
92176
92302
  try {
92177
- const st = lstatSync11(targetDir);
92303
+ const st = lstatSync12(targetDir);
92178
92304
  if (st.isSymbolicLink()) {
92179
92305
  targetIsSymlink = true;
92180
92306
  }
@@ -92183,11 +92309,11 @@ function writePersonalSkill(targetDir, files) {
92183
92309
  fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
92184
92310
  }
92185
92311
  mkdirSync51(dirname31(targetDir), { recursive: true, mode: 493 });
92186
- const staging = mkdtempSync6(join89(dirname31(targetDir), `.skill-personal-stage-`));
92312
+ const staging = mkdtempSync6(join90(dirname31(targetDir), `.skill-personal-stage-`));
92187
92313
  let oldRename = null;
92188
92314
  try {
92189
92315
  for (const [path8, content] of Object.entries(files)) {
92190
- const full = join89(staging, path8);
92316
+ const full = join90(staging, path8);
92191
92317
  mkdirSync51(dirname31(full), { recursive: true, mode: 493 });
92192
92318
  const fd = openSync17(full, "wx");
92193
92319
  try {
@@ -92202,7 +92328,7 @@ function writePersonalSkill(targetDir, files) {
92202
92328
  }
92203
92329
  let targetExists = false;
92204
92330
  try {
92205
- lstatSync11(targetDir);
92331
+ lstatSync12(targetDir);
92206
92332
  targetExists = true;
92207
92333
  } catch {}
92208
92334
  if (targetExists) {
@@ -92218,9 +92344,9 @@ function writePersonalSkill(targetDir, files) {
92218
92344
  try {
92219
92345
  rmSync19(staging, { recursive: true, force: true });
92220
92346
  } catch {}
92221
- if (oldRename && existsSync91(oldRename)) {
92347
+ if (oldRename && existsSync92(oldRename)) {
92222
92348
  try {
92223
- if (existsSync91(targetDir)) {
92349
+ if (existsSync92(targetDir)) {
92224
92350
  rmSync19(targetDir, { recursive: true, force: true });
92225
92351
  }
92226
92352
  renameSync22(oldRename, targetDir);
@@ -92237,7 +92363,7 @@ function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
92237
92363
  const target = personalSkillDir(agentsRoot, agent, name);
92238
92364
  const exists = (() => {
92239
92365
  try {
92240
- lstatSync11(target);
92366
+ lstatSync12(target);
92241
92367
  return true;
92242
92368
  } catch {
92243
92369
  return false;
@@ -92286,10 +92412,10 @@ function loadFiles(opts) {
92286
92412
  return loadFromStdin2();
92287
92413
  }
92288
92414
  const p = resolve54(opts.from);
92289
- if (!existsSync91(p)) {
92415
+ if (!existsSync92(p)) {
92290
92416
  fail4(`--from path does not exist: ${opts.from}`);
92291
92417
  }
92292
- const st = statSync49(p);
92418
+ const st = statSync50(p);
92293
92419
  if (st.isDirectory()) {
92294
92420
  return loadFromDir2(p);
92295
92421
  }
@@ -92334,10 +92460,10 @@ function editPersonalAction(name, opts) {
92334
92460
  }
92335
92461
  var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
92336
92462
  function defaultSharedRoot() {
92337
- return join89(homedir52(), ".switchroom", "skills");
92463
+ return join90(homedir52(), ".switchroom", "skills");
92338
92464
  }
92339
92465
  function defaultBundledRoot() {
92340
- return join89(homedir52(), ".switchroom", "skills", "_bundled");
92466
+ return join90(homedir52(), ".switchroom", "skills", "_bundled");
92341
92467
  }
92342
92468
  function resolveCloneSource(source, opts) {
92343
92469
  const m = CLONE_SOURCE_RE.exec(source);
@@ -92347,11 +92473,11 @@ function resolveCloneSource(source, opts) {
92347
92473
  const tier = m[1];
92348
92474
  const slug = m[2];
92349
92475
  const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
92350
- const dir = join89(root, slug);
92351
- if (!existsSync91(dir)) {
92476
+ const dir = join90(root, slug);
92477
+ if (!existsSync92(dir)) {
92352
92478
  fail4(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
92353
92479
  }
92354
- const st = lstatSync11(dir);
92480
+ const st = lstatSync12(dir);
92355
92481
  if (st.isSymbolicLink()) {
92356
92482
  fail4(`clone source ${JSON.stringify(source)} is a symlink at ${dir}; ` + `point clone at the canonical pool path instead`);
92357
92483
  }
@@ -92363,7 +92489,7 @@ function readSourceFiles(dir) {
92363
92489
  const skipped = [];
92364
92490
  const walk2 = (sub) => {
92365
92491
  for (const ent of readdirSync34(sub, { withFileTypes: true })) {
92366
- const full = join89(sub, ent.name);
92492
+ const full = join90(sub, ent.name);
92367
92493
  if (ent.isSymbolicLink()) {
92368
92494
  continue;
92369
92495
  }
@@ -92378,7 +92504,7 @@ function readSourceFiles(dir) {
92378
92504
  continue;
92379
92505
  }
92380
92506
  try {
92381
- const st = lstatSync11(full);
92507
+ const st = lstatSync12(full);
92382
92508
  if (st.size > CLONE_MAX_FILE_BYTES) {
92383
92509
  fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
92384
92510
  }
@@ -92460,7 +92586,7 @@ function removePersonalAction(name, opts) {
92460
92586
  }
92461
92587
  const target = personalSkillDir(agentsRoot, agent, name);
92462
92588
  try {
92463
- const st = lstatSync11(target);
92589
+ const st = lstatSync12(target);
92464
92590
  if (st.isSymbolicLink()) {
92465
92591
  fail4(`refusing to remove symlink at ${target}; investigate manually`);
92466
92592
  }
@@ -92474,7 +92600,7 @@ function removePersonalAction(name, opts) {
92474
92600
  const trashRoot2 = trashDir(agentsRoot, agent);
92475
92601
  mkdirSync51(trashRoot2, { recursive: true, mode: 493 });
92476
92602
  const ts = Date.now();
92477
- const trashTarget = join89(trashRoot2, `${name}-${ts}`);
92603
+ const trashTarget = join90(trashRoot2, `${name}-${ts}`);
92478
92604
  renameSync22(target, trashTarget);
92479
92605
  const now = new Date(ts);
92480
92606
  utimesSync(trashTarget, now, now);
@@ -92493,16 +92619,16 @@ function listPersonalAction(opts) {
92493
92619
  const agent = resolveAgent(opts);
92494
92620
  const agentsRoot = resolveAgentsRoot(opts);
92495
92621
  sweepTrash(agentsRoot, agent);
92496
- const skillsDir = join89(agentsRoot, agent, ".claude", "skills");
92622
+ const skillsDir = join90(agentsRoot, agent, ".claude", "skills");
92497
92623
  const personal = [];
92498
- if (existsSync91(skillsDir)) {
92624
+ if (existsSync92(skillsDir)) {
92499
92625
  for (const ent of readdirSync34(skillsDir, { withFileTypes: true })) {
92500
92626
  if (!ent.isDirectory())
92501
92627
  continue;
92502
92628
  if (!ent.name.startsWith(PERSONAL_PREFIX))
92503
92629
  continue;
92504
92630
  const skillName = ent.name.slice(PERSONAL_PREFIX.length);
92505
- const skillPath = join89(skillsDir, ent.name);
92631
+ const skillPath = join90(skillsDir, ent.name);
92506
92632
  let fileCount = 0;
92507
92633
  let totalBytes = 0;
92508
92634
  const walk2 = (sub) => {
@@ -92510,10 +92636,10 @@ function listPersonalAction(opts) {
92510
92636
  if (e.isFile()) {
92511
92637
  fileCount += 1;
92512
92638
  try {
92513
- totalBytes += statSync49(join89(sub, e.name)).size;
92639
+ totalBytes += statSync50(join90(sub, e.name)).size;
92514
92640
  } catch {}
92515
92641
  } else if (e.isDirectory()) {
92516
- walk2(join89(sub, e.name));
92642
+ walk2(join90(sub, e.name));
92517
92643
  }
92518
92644
  }
92519
92645
  };
@@ -92552,11 +92678,11 @@ function registerSkillPersonalCommands(program3) {
92552
92678
  // src/cli/self-improve-propose-skill.ts
92553
92679
  import { createConnection as createConnection4 } from "node:net";
92554
92680
  import { homedir as homedir53 } from "node:os";
92555
- import { join as join90 } from "node:path";
92681
+ import { join as join91 } from "node:path";
92556
92682
  import { readFileSync as readFileSync79 } from "node:fs";
92557
92683
  var IPC_CONNECT_TIMEOUT_MS = 5000;
92558
92684
  function gatewaySocketPath() {
92559
- return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join90(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join90(homedir53(), ".claude", "channels", "telegram", "gateway.sock"));
92685
+ return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join91(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join91(homedir53(), ".claude", "channels", "telegram", "gateway.sock"));
92560
92686
  }
92561
92687
  function fail5(msg, code = 1) {
92562
92688
  console.error(msg);
@@ -92628,9 +92754,9 @@ function registerSelfImproveProposeSkillCommand(program3) {
92628
92754
  init_esm();
92629
92755
  init_helpers();
92630
92756
  var import_yaml25 = __toESM(require_dist(), 1);
92631
- import { existsSync as existsSync92, readdirSync as readdirSync35, readFileSync as readFileSync80, statSync as statSync50 } from "node:fs";
92757
+ import { existsSync as existsSync93, readdirSync as readdirSync35, readFileSync as readFileSync80, statSync as statSync51 } from "node:fs";
92632
92758
  import { homedir as homedir54 } from "node:os";
92633
- import { join as join91, resolve as resolve55 } from "node:path";
92759
+ import { join as join92, resolve as resolve55 } from "node:path";
92634
92760
  var PERSONAL_PREFIX2 = "personal-";
92635
92761
  var BUNDLED_SUBDIR = "_bundled";
92636
92762
  var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
@@ -92644,8 +92770,8 @@ function defaultBundledRoot2() {
92644
92770
  return resolve55(homedir54(), ".switchroom/skills/_bundled");
92645
92771
  }
92646
92772
  function readSkillFrontmatter(skillDir) {
92647
- const mdPath = join91(skillDir, "SKILL.md");
92648
- if (!existsSync92(mdPath))
92773
+ const mdPath = join92(skillDir, "SKILL.md");
92774
+ if (!existsSync93(mdPath))
92649
92775
  return null;
92650
92776
  let content;
92651
92777
  try {
@@ -92677,9 +92803,9 @@ function readSkillFrontmatter(skillDir) {
92677
92803
  return { fm: parsed };
92678
92804
  }
92679
92805
  function statSkillMd(skillDir) {
92680
- const mdPath = join91(skillDir, "SKILL.md");
92806
+ const mdPath = join92(skillDir, "SKILL.md");
92681
92807
  try {
92682
- const st = statSync50(mdPath);
92808
+ const st = statSync51(mdPath);
92683
92809
  return { size: st.size, mtime: st.mtime.toISOString() };
92684
92810
  } catch {
92685
92811
  return null;
@@ -92688,8 +92814,8 @@ function statSkillMd(skillDir) {
92688
92814
  function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
92689
92815
  if (!AGENT_NAME_RE3.test(agent))
92690
92816
  return [];
92691
- const skillsDir = join91(agentsRoot, agent, ".claude/skills");
92692
- if (!existsSync92(skillsDir))
92817
+ const skillsDir = join92(agentsRoot, agent, ".claude/skills");
92818
+ if (!existsSync93(skillsDir))
92693
92819
  return [];
92694
92820
  const out = [];
92695
92821
  let entries;
@@ -92701,9 +92827,9 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
92701
92827
  for (const ent of entries) {
92702
92828
  if (!ent.startsWith(PERSONAL_PREFIX2))
92703
92829
  continue;
92704
- const dirPath = join91(skillsDir, ent);
92830
+ const dirPath = join92(skillsDir, ent);
92705
92831
  try {
92706
- if (!statSync50(dirPath).isDirectory())
92832
+ if (!statSync51(dirPath).isDirectory())
92707
92833
  continue;
92708
92834
  } catch {
92709
92835
  continue;
@@ -92727,7 +92853,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
92727
92853
  return out;
92728
92854
  }
92729
92855
  function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
92730
- if (!existsSync92(sharedRoot))
92856
+ if (!existsSync93(sharedRoot))
92731
92857
  return [];
92732
92858
  const out = [];
92733
92859
  let entries;
@@ -92741,9 +92867,9 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
92741
92867
  continue;
92742
92868
  if (ent.startsWith("."))
92743
92869
  continue;
92744
- const dirPath = join91(sharedRoot, ent);
92870
+ const dirPath = join92(sharedRoot, ent);
92745
92871
  try {
92746
- if (!statSync50(dirPath).isDirectory())
92872
+ if (!statSync51(dirPath).isDirectory())
92747
92873
  continue;
92748
92874
  } catch {
92749
92875
  continue;
@@ -92765,7 +92891,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
92765
92891
  return out;
92766
92892
  }
92767
92893
  function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
92768
- if (!existsSync92(bundledRoot))
92894
+ if (!existsSync93(bundledRoot))
92769
92895
  return [];
92770
92896
  const out = [];
92771
92897
  let entries;
@@ -92777,9 +92903,9 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
92777
92903
  for (const ent of entries) {
92778
92904
  if (ent.startsWith("."))
92779
92905
  continue;
92780
- const dirPath = join91(bundledRoot, ent);
92906
+ const dirPath = join92(bundledRoot, ent);
92781
92907
  try {
92782
- if (!statSync50(dirPath).isDirectory())
92908
+ if (!statSync51(dirPath).isDirectory())
92783
92909
  continue;
92784
92910
  } catch {
92785
92911
  continue;
@@ -92923,18 +93049,18 @@ init_source();
92923
93049
  init_helpers();
92924
93050
  init_operator_uid();
92925
93051
  import {
92926
- existsSync as existsSync94,
93052
+ existsSync as existsSync95,
92927
93053
  mkdirSync as mkdirSync52,
92928
93054
  readdirSync as readdirSync36,
92929
93055
  readFileSync as readFileSync82,
92930
93056
  writeFileSync as writeFileSync34,
92931
- statSync as statSync51,
92932
- lstatSync as lstatSync12,
93057
+ statSync as statSync52,
93058
+ lstatSync as lstatSync13,
92933
93059
  realpathSync as realpathSync8,
92934
93060
  copyFileSync as copyFileSync13
92935
93061
  } from "node:fs";
92936
93062
  import { homedir as homedir55 } from "node:os";
92937
- import { join as join92 } from "node:path";
93063
+ import { join as join93 } from "node:path";
92938
93064
  import { spawnSync as spawnSync20 } from "node:child_process";
92939
93065
 
92940
93066
  // src/cli/singleton-stale-cleanup.ts
@@ -93283,10 +93409,10 @@ function resolveHostdHostHome(env2 = process.env, home2 = homedir55()) {
93283
93409
  return resolved;
93284
93410
  }
93285
93411
  function resolveHostdSkillsTarget(hostHome) {
93286
- const skillsPath = join92(hostHome, ".switchroom", "skills");
93412
+ const skillsPath = join93(hostHome, ".switchroom", "skills");
93287
93413
  let st;
93288
93414
  try {
93289
- st = lstatSync12(skillsPath);
93415
+ st = lstatSync13(skillsPath);
93290
93416
  } catch {
93291
93417
  return;
93292
93418
  }
@@ -93300,21 +93426,21 @@ function resolveHostdSkillsTarget(hostHome) {
93300
93426
  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.`);
93301
93427
  return;
93302
93428
  }
93303
- if (!existsSync94(target)) {
93429
+ if (!existsSync95(target)) {
93304
93430
  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.`);
93305
93431
  return;
93306
93432
  }
93307
93433
  return target;
93308
93434
  }
93309
93435
  function hostdDir() {
93310
- return join92(homedir55(), ".switchroom", "hostd");
93436
+ return join93(homedir55(), ".switchroom", "hostd");
93311
93437
  }
93312
93438
  function hostdComposePath() {
93313
- return join92(hostdDir(), "docker-compose.yml");
93439
+ return join93(hostdDir(), "docker-compose.yml");
93314
93440
  }
93315
93441
  function backupExistingCompose() {
93316
93442
  const p = hostdComposePath();
93317
- if (!existsSync94(p))
93443
+ if (!existsSync95(p))
93318
93444
  return null;
93319
93445
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
93320
93446
  const bak = `${p}.bak-${ts}`;
@@ -93412,7 +93538,7 @@ function doStatus() {
93412
93538
  const composeYml = hostdComposePath();
93413
93539
  console.log(source_default.bold("switchroom-hostd"));
93414
93540
  console.log("");
93415
- if (!existsSync94(composeYml)) {
93541
+ if (!existsSync95(composeYml)) {
93416
93542
  console.log(source_default.yellow(" compose: not installed"));
93417
93543
  console.log(source_default.dim(" run `switchroom hostd install` to set up."));
93418
93544
  return;
@@ -93433,15 +93559,15 @@ function doStatus() {
93433
93559
  } else {
93434
93560
  console.log(source_default.green(` container: ${ps.stdout.trim()}`));
93435
93561
  }
93436
- if (existsSync94(dir)) {
93562
+ if (existsSync95(dir)) {
93437
93563
  const entries = [];
93438
93564
  try {
93439
93565
  for (const name of readdirSync36(dir)) {
93440
93566
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
93441
93567
  continue;
93442
- const sockPath = join92(dir, name, "sock");
93443
- if (existsSync94(sockPath)) {
93444
- const st = statSync51(sockPath);
93568
+ const sockPath = join93(dir, name, "sock");
93569
+ if (existsSync95(sockPath)) {
93570
+ const st = statSync52(sockPath);
93445
93571
  if ((st.mode & 61440) === 49152) {
93446
93572
  entries.push(`${name} \u2192 ${sockPath}`);
93447
93573
  }
@@ -93459,7 +93585,7 @@ function doStatus() {
93459
93585
  }
93460
93586
  function doUninstall() {
93461
93587
  const composeYml = hostdComposePath();
93462
- if (!existsSync94(composeYml)) {
93588
+ if (!existsSync95(composeYml)) {
93463
93589
  console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
93464
93590
  return;
93465
93591
  }
@@ -93483,7 +93609,7 @@ function registerHostdCommand(program3) {
93483
93609
  hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
93484
93610
  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) => {
93485
93611
  const logPath = opts.path ?? defaultAuditLogPath2();
93486
- if (!existsSync94(logPath)) {
93612
+ if (!existsSync95(logPath)) {
93487
93613
  console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
93488
93614
  The log is created when hostd handles its first privileged-verb request.`));
93489
93615
  return;
@@ -93531,9 +93657,9 @@ The log is created when hostd handles its first privileged-verb request.`));
93531
93657
  init_source();
93532
93658
  init_helpers();
93533
93659
  init_operator_uid();
93534
- import { chownSync as chownSync9, existsSync as existsSync95, mkdirSync as mkdirSync53, writeFileSync as writeFileSync35, copyFileSync as copyFileSync14 } from "node:fs";
93660
+ import { chownSync as chownSync9, existsSync as existsSync96, mkdirSync as mkdirSync53, writeFileSync as writeFileSync35, copyFileSync as copyFileSync14 } from "node:fs";
93535
93661
  import { homedir as homedir56 } from "node:os";
93536
- import { join as join93 } from "node:path";
93662
+ import { join as join94 } from "node:path";
93537
93663
  import { spawnSync as spawnSync21 } from "node:child_process";
93538
93664
  function resolveWebImageTag(explicitTag, release) {
93539
93665
  if (explicitTag)
@@ -93628,14 +93754,14 @@ services:
93628
93754
  `;
93629
93755
  }
93630
93756
  function webdDir() {
93631
- return join93(homedir56(), ".switchroom", "web");
93757
+ return join94(homedir56(), ".switchroom", "web");
93632
93758
  }
93633
93759
  function webdComposePath() {
93634
- return join93(webdDir(), "docker-compose.yml");
93760
+ return join94(webdDir(), "docker-compose.yml");
93635
93761
  }
93636
93762
  function backupExistingCompose2() {
93637
93763
  const p = webdComposePath();
93638
- if (!existsSync95(p))
93764
+ if (!existsSync96(p))
93639
93765
  return null;
93640
93766
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
93641
93767
  const bak = `${p}.bak-${ts}`;
@@ -93734,7 +93860,7 @@ function doStatus2() {
93734
93860
  const composeYml = webdComposePath();
93735
93861
  console.log(source_default.bold("switchroom-web"));
93736
93862
  console.log("");
93737
- if (!existsSync95(composeYml)) {
93863
+ if (!existsSync96(composeYml)) {
93738
93864
  console.log(source_default.yellow(" compose: not installed"));
93739
93865
  console.log(source_default.dim(" run `switchroom webd install` to set up."));
93740
93866
  return;
@@ -93758,7 +93884,7 @@ function doStatus2() {
93758
93884
  }
93759
93885
  function doUninstall2() {
93760
93886
  const composeYml = webdComposePath();
93761
- if (!existsSync95(composeYml)) {
93887
+ if (!existsSync96(composeYml)) {
93762
93888
  console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
93763
93889
  return;
93764
93890
  }
@@ -93788,9 +93914,9 @@ function registerWebdCommand(program3) {
93788
93914
  // src/cli/host-repair.ts
93789
93915
  init_source();
93790
93916
  import { homedir as homedir57 } from "node:os";
93791
- import { join as join94 } from "node:path";
93917
+ import { join as join95 } from "node:path";
93792
93918
  var ARTIFACT_ALLOWLIST = {
93793
- dockerComposePluginDir: (home2) => join94(home2, ".docker", "cli-plugins", "docker-compose"),
93919
+ dockerComposePluginDir: (home2) => join95(home2, ".docker", "cli-plugins", "docker-compose"),
93794
93920
  stateSentinel: "/state"
93795
93921
  };
93796
93922
  function isStateBogusAutoDir(probe2) {
@@ -93944,11 +94070,11 @@ function applyMountRepairs(items, deps) {
93944
94070
  function registerHostCommand(program3) {
93945
94071
  const host = program3.command("host").description("Host-level maintenance operations for switchroom");
93946
94072
  host.command("repair-mounts").description("Detect and remove known auto-dir artifacts left by a container-context deploy " + "(2026-06-23 outage class). Default: dry-run. Pass --yes to apply.").option("--yes", "Actually perform the removals (default is dry-run)").action(async (opts) => {
93947
- const { rmdirSync: rmdirSync2, rmSync: rmSync20, lstatSync: lstatSync13, readdirSync: readdirSync37 } = await import("node:fs");
94073
+ const { rmdirSync: rmdirSync2, rmSync: rmSync20, lstatSync: lstatSync14, readdirSync: readdirSync37 } = await import("node:fs");
93948
94074
  const probe2 = {
93949
94075
  lstat(path8) {
93950
94076
  try {
93951
- return lstatSync13(path8);
94077
+ return lstatSync14(path8);
93952
94078
  } catch {
93953
94079
  return null;
93954
94080
  }