switchroom 0.18.7 → 0.18.8

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 (54) hide show
  1. package/dist/cli/switchroom.js +905 -758
  2. package/dist/host-control/main.js +1 -1
  3. package/package.json +1 -1
  4. package/profiles/_base/start.sh.hbs +111 -34
  5. package/skills/switchroom-runtime/SKILL.md +2 -0
  6. package/telegram-plugin/dist/gateway/gateway.js +1403 -657
  7. package/telegram-plugin/flood-circuit-breaker.ts +123 -0
  8. package/telegram-plugin/gateway/activity-card-store.ts +63 -18
  9. package/telegram-plugin/gateway/boot-card.ts +27 -0
  10. package/telegram-plugin/gateway/busy-ack.ts +106 -0
  11. package/telegram-plugin/gateway/gateway.ts +564 -85
  12. package/telegram-plugin/gateway/mental-model-propose-diff.ts +61 -5
  13. package/telegram-plugin/gateway/model-command.ts +23 -11
  14. package/telegram-plugin/gateway/session-model-file.ts +198 -0
  15. package/telegram-plugin/gateway/status-pin-store.ts +82 -22
  16. package/telegram-plugin/gateway/worker-pin-reaper.ts +114 -0
  17. package/telegram-plugin/hooks/hooks.json +10 -10
  18. package/telegram-plugin/hooks/run-hook.sh +84 -0
  19. package/telegram-plugin/model-unavailable.ts +26 -0
  20. package/telegram-plugin/pty-partial-handler.ts +39 -0
  21. package/telegram-plugin/render/rich-render.ts +79 -1
  22. package/telegram-plugin/retry-api-call.ts +62 -0
  23. package/telegram-plugin/shared/bot-runtime.ts +8 -1
  24. package/telegram-plugin/silence-poke.ts +14 -0
  25. package/telegram-plugin/stream-controller.ts +156 -38
  26. package/telegram-plugin/tests/activity-card-store.test.ts +47 -2
  27. package/telegram-plugin/tests/approval-card-restart-outcome.test.ts +218 -0
  28. package/telegram-plugin/tests/boot-card-flood-suppress.test.ts +111 -0
  29. package/telegram-plugin/tests/busy-ack-wiring.test.ts +118 -0
  30. package/telegram-plugin/tests/busy-ack.test.ts +121 -0
  31. package/telegram-plugin/tests/flood-circuit-breaker.test.ts +74 -0
  32. package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +177 -25
  33. package/telegram-plugin/tests/mental-model-name-entity-corruption.test.ts +119 -0
  34. package/telegram-plugin/tests/model-command.test.ts +2 -2
  35. package/telegram-plugin/tests/model-unavailable.test.ts +41 -0
  36. package/telegram-plugin/tests/pty-partial-handler.test.ts +56 -0
  37. package/telegram-plugin/tests/render/render-outbound-chunks.test.ts +98 -0
  38. package/telegram-plugin/tests/retry-api-call.test.ts +59 -0
  39. package/telegram-plugin/tests/run-hook-wrapper.test.ts +132 -0
  40. package/telegram-plugin/tests/session-model-file.test.ts +132 -0
  41. package/telegram-plugin/tests/slot-banner-boot-recovery.test.ts +3 -3
  42. package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +3 -3
  43. package/telegram-plugin/tests/status-pin-store.test.ts +62 -6
  44. package/telegram-plugin/tests/stream-controller-chunk-cap.test.ts +122 -0
  45. package/telegram-plugin/tests/voice-send.test.ts +308 -0
  46. package/telegram-plugin/tests/worker-pin-reaper.test.ts +132 -0
  47. package/telegram-plugin/uat/scenarios/jtbd-deliberate-restart-resumes-dm.test.ts +118 -0
  48. package/telegram-plugin/uat/scenarios/jtbd-midflight-busy-ack-dm.test.ts +201 -0
  49. package/telegram-plugin/uat/scenarios/jtbd-worker-pin-lifecycle-dm.test.ts +208 -0
  50. package/telegram-plugin/uat/scenarios/vault-card-survives-gateway-restart-dm.test.ts +140 -0
  51. package/telegram-plugin/uat/scenarios/vault-deny-resumes-turn-dm.test.ts +84 -0
  52. package/telegram-plugin/uat/scenarios/vault-timeout-wakes-agent-dm.test.ts +91 -0
  53. package/telegram-plugin/voice-ondemand.ts +25 -1
  54. package/telegram-plugin/voice-send.ts +154 -0
@@ -40469,19 +40469,19 @@ var init_thinking_effort_risk = __esm(() => {
40469
40469
 
40470
40470
  // src/manifest.ts
40471
40471
  import {
40472
- existsSync as existsSync57,
40473
- readFileSync as readFileSync52,
40472
+ existsSync as existsSync58,
40473
+ readFileSync as readFileSync53,
40474
40474
  readdirSync as readdirSync19
40475
40475
  } from "node:fs";
40476
- import { dirname as dirname17, join as join50 } from "node:path";
40476
+ import { dirname as dirname18, join as join51 } from "node:path";
40477
40477
  import { execSync as execSync2 } from "node:child_process";
40478
40478
  function locateManifestPath() {
40479
40479
  let dir = import.meta.dirname;
40480
40480
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
40481
- const candidate = join50(dir, "dependencies.json");
40482
- if (existsSync57(candidate))
40481
+ const candidate = join51(dir, "dependencies.json");
40482
+ if (existsSync58(candidate))
40483
40483
  return candidate;
40484
- dir = dirname17(dir);
40484
+ dir = dirname18(dir);
40485
40485
  }
40486
40486
  return null;
40487
40487
  }
@@ -40492,7 +40492,7 @@ function loadManifest(manifestPath) {
40492
40492
  }
40493
40493
  let raw;
40494
40494
  try {
40495
- raw = readFileSync52(path4, "utf-8");
40495
+ raw = readFileSync53(path4, "utf-8");
40496
40496
  } catch (err) {
40497
40497
  throw new Error(`Failed to read manifest at ${path4}: ${err.message}`);
40498
40498
  }
@@ -40548,16 +40548,16 @@ function probeClaudeVersion() {
40548
40548
  }
40549
40549
  function probePlaywrightMcpVersion() {
40550
40550
  const home2 = process.env.HOME ?? "";
40551
- const npxCache = join50(home2, ".npm/_npx");
40552
- if (!existsSync57(npxCache))
40551
+ const npxCache = join51(home2, ".npm/_npx");
40552
+ if (!existsSync58(npxCache))
40553
40553
  return null;
40554
40554
  try {
40555
40555
  const entries = readdirSync19(npxCache);
40556
40556
  for (const entry of entries) {
40557
- const pkgPath = join50(npxCache, entry, "node_modules/@playwright/mcp/package.json");
40558
- if (existsSync57(pkgPath)) {
40557
+ const pkgPath = join51(npxCache, entry, "node_modules/@playwright/mcp/package.json");
40558
+ if (existsSync58(pkgPath)) {
40559
40559
  try {
40560
- const pkg = JSON.parse(readFileSync52(pkgPath, "utf-8"));
40560
+ const pkg = JSON.parse(readFileSync53(pkgPath, "utf-8"));
40561
40561
  if (pkg.version)
40562
40562
  return pkg.version;
40563
40563
  } catch {}
@@ -40859,7 +40859,7 @@ var init_doctor_memory = __esm(() => {
40859
40859
  });
40860
40860
 
40861
40861
  // src/cli/doctor-docker.ts
40862
- import { readFileSync as readFileSync53 } from "node:fs";
40862
+ import { readFileSync as readFileSync54 } from "node:fs";
40863
40863
  import { spawnSync as spawnSync8 } from "node:child_process";
40864
40864
  function imageTagOf(ref) {
40865
40865
  if (!ref)
@@ -40875,7 +40875,7 @@ function isDockerMode(opts) {
40875
40875
  return true;
40876
40876
  if (opts?.composePath) {
40877
40877
  try {
40878
- readFileSync53(opts.composePath, "utf8");
40878
+ readFileSync54(opts.composePath, "utf8");
40879
40879
  return true;
40880
40880
  } catch {}
40881
40881
  }
@@ -41157,11 +41157,11 @@ var init_doctor_docker = __esm(() => {
41157
41157
  });
41158
41158
 
41159
41159
  // src/cli/doctor-auth-broker.ts
41160
- import { existsSync as existsSync58, readFileSync as readFileSync54 } from "node:fs";
41160
+ import { existsSync as existsSync59, readFileSync as readFileSync55 } from "node:fs";
41161
41161
  import { createHash as createHash11 } from "node:crypto";
41162
41162
  import { spawnSync as spawnSync9 } from "node:child_process";
41163
- import { homedir as homedir28 } from "node:os";
41164
- import { join as join51 } from "node:path";
41163
+ import { homedir as homedir29 } from "node:os";
41164
+ import { join as join52 } from "node:path";
41165
41165
  function defaultDockerInspect(container, format) {
41166
41166
  try {
41167
41167
  const r = spawnSync9("docker", ["inspect", "-f", format, container], { encoding: "utf-8", timeout: 5000 });
@@ -41261,8 +41261,8 @@ function checkAuthBrokerPerAgentSockets(config, deps = {}) {
41261
41261
  }
41262
41262
  function checkAuthBrokerDrift(deps = {}) {
41263
41263
  const stateDir = resolveStateDir(deps);
41264
- const indexPath = join51(stateDir, "sha-index.json");
41265
- if (!existsSync58(indexPath)) {
41264
+ const indexPath = join52(stateDir, "sha-index.json");
41265
+ if (!existsSync59(indexPath)) {
41266
41266
  return {
41267
41267
  name: "auth-broker: drift",
41268
41268
  status: "ok",
@@ -41271,7 +41271,7 @@ function checkAuthBrokerDrift(deps = {}) {
41271
41271
  }
41272
41272
  let index;
41273
41273
  try {
41274
- index = JSON.parse(readFileSync54(indexPath, "utf-8"));
41274
+ index = JSON.parse(readFileSync55(indexPath, "utf-8"));
41275
41275
  } catch (err) {
41276
41276
  return {
41277
41277
  name: "auth-broker: drift",
@@ -41280,18 +41280,18 @@ function checkAuthBrokerDrift(deps = {}) {
41280
41280
  fix: "Inspect `~/.switchroom/state/auth-broker/sha-index.json` for corruption."
41281
41281
  };
41282
41282
  }
41283
- const home2 = deps.home ?? homedir28();
41283
+ const home2 = deps.home ?? homedir29();
41284
41284
  const divergent = [];
41285
41285
  const missingOnDisk = [];
41286
41286
  for (const [label, expected] of Object.entries(index)) {
41287
41287
  const credsPath = accountCredentialsPath(label, home2);
41288
- if (!existsSync58(credsPath)) {
41288
+ if (!existsSync59(credsPath)) {
41289
41289
  missingOnDisk.push(label);
41290
41290
  continue;
41291
41291
  }
41292
41292
  let got;
41293
41293
  try {
41294
- got = sha256Hex(readFileSync54(credsPath, "utf-8"));
41294
+ got = sha256Hex(readFileSync55(credsPath, "utf-8"));
41295
41295
  } catch (err) {
41296
41296
  divergent.push(`${label} (read failed: ${err.message})`);
41297
41297
  continue;
@@ -41322,8 +41322,8 @@ function checkAuthBrokerDrift(deps = {}) {
41322
41322
  }
41323
41323
  function checkAuthBrokerThresholdViolations(deps = {}) {
41324
41324
  const stateDir = resolveStateDir(deps);
41325
- const path4 = join51(stateDir, "threshold-violations.json");
41326
- if (!existsSync58(path4)) {
41325
+ const path4 = join52(stateDir, "threshold-violations.json");
41326
+ if (!existsSync59(path4)) {
41327
41327
  return {
41328
41328
  name: "auth-broker: threshold violations",
41329
41329
  status: "ok",
@@ -41332,7 +41332,7 @@ function checkAuthBrokerThresholdViolations(deps = {}) {
41332
41332
  }
41333
41333
  let violations;
41334
41334
  try {
41335
- violations = JSON.parse(readFileSync54(path4, "utf-8"));
41335
+ violations = JSON.parse(readFileSync55(path4, "utf-8"));
41336
41336
  } catch (err) {
41337
41337
  return {
41338
41338
  name: "auth-broker: threshold violations",
@@ -41366,9 +41366,9 @@ function checkAuthBrokerActiveAccount(config, deps = {}) {
41366
41366
  fix: "Run `switchroom auth use <label>` to pin a fleet-wide account, then `switchroom apply`. Without an active account every agent boot fails."
41367
41367
  };
41368
41368
  }
41369
- const home2 = deps.home ?? homedir28();
41369
+ const home2 = deps.home ?? homedir29();
41370
41370
  const dir = accountDir(active, home2);
41371
- if (!existsSync58(dir)) {
41371
+ if (!existsSync59(dir)) {
41372
41372
  return {
41373
41373
  name: "auth-broker: fleet active account",
41374
41374
  status: "fail",
@@ -41377,7 +41377,7 @@ function checkAuthBrokerActiveAccount(config, deps = {}) {
41377
41377
  };
41378
41378
  }
41379
41379
  const creds = accountCredentialsPath(active, home2);
41380
- if (!existsSync58(creds)) {
41380
+ if (!existsSync59(creds)) {
41381
41381
  return {
41382
41382
  name: "auth-broker: fleet active account",
41383
41383
  status: "fail",
@@ -41514,17 +41514,17 @@ var init_doctor_hostd = () => {};
41514
41514
  import {
41515
41515
  accessSync,
41516
41516
  constants as fsConstants4,
41517
- existsSync as existsSync59,
41517
+ existsSync as existsSync60,
41518
41518
  realpathSync as realpathSync5,
41519
- statSync as statSync24
41519
+ statSync as statSync25
41520
41520
  } from "node:fs";
41521
- import { userInfo, homedir as homedir29 } from "node:os";
41522
- import { join as join52 } from "node:path";
41521
+ import { userInfo, homedir as homedir30 } from "node:os";
41522
+ import { join as join53 } from "node:path";
41523
41523
  function resolveVaultPath2(config) {
41524
41524
  return config.vault?.path ? config.vault.path.replace(/^~/, process.env.HOME ?? "") : resolveStatePath("vault.enc");
41525
41525
  }
41526
41526
  function defaultStatVault(path4) {
41527
- if (!existsSync59(path4)) {
41527
+ if (!existsSync60(path4)) {
41528
41528
  return { exists: false, readable: false, uid: -1, mode: 0, realPath: path4 };
41529
41529
  }
41530
41530
  let real = path4;
@@ -41534,7 +41534,7 @@ function defaultStatVault(path4) {
41534
41534
  let uid = -1;
41535
41535
  let mode = 0;
41536
41536
  try {
41537
- const s = statSync24(real);
41537
+ const s = statSync25(real);
41538
41538
  uid = s.uid;
41539
41539
  mode = s.mode & 511;
41540
41540
  } catch {
@@ -41657,7 +41657,7 @@ async function runSecretAccessChecks(config, deps = {}) {
41657
41657
  };
41658
41658
  const passphrase = deps.passphrase ?? process.env.SWITCHROOM_VAULT_PASSPHRASE;
41659
41659
  if (!passphrase) {
41660
- const sock = deps.brokerOperatorSocket ?? join52(homedir29(), ".switchroom", "broker-operator", "sock");
41660
+ const sock = deps.brokerOperatorSocket ?? join53(homedir30(), ".switchroom", "broker-operator", "sock");
41661
41661
  const preflight = deps.preflight ?? ((a, k) => defaultPreflight(sock, a, k));
41662
41662
  for (const name of Object.keys(config.agents ?? {})) {
41663
41663
  const resolved = resolveAgentConfig(config.defaults, config.profiles, config.agents[name]);
@@ -41742,8 +41742,8 @@ import {
41742
41742
  existsSync as realExistsSync,
41743
41743
  readFileSync as realReadFileSync
41744
41744
  } from "node:fs";
41745
- import { join as join53, resolve as resolve34 } from "node:path";
41746
- import { homedir as homedir30 } from "node:os";
41745
+ import { join as join54, resolve as resolve34 } from "node:path";
41746
+ import { homedir as homedir31 } from "node:os";
41747
41747
  function resolveDeps(config, deps) {
41748
41748
  let agentsDir = deps.agentsDir;
41749
41749
  if (agentsDir === undefined) {
@@ -41866,8 +41866,8 @@ function checkScaffoldWiring(config, driveAgents, d) {
41866
41866
  });
41867
41867
  continue;
41868
41868
  }
41869
- const mcpPath = join53(agentDir, ".mcp.json");
41870
- const claudeJsonPath = join53(agentDir, ".claude", ".claude.json");
41869
+ const mcpPath = join54(agentDir, ".mcp.json");
41870
+ const claudeJsonPath = join54(agentDir, ".claude", ".claude.json");
41871
41871
  const mcpRead = readJson(d, mcpPath);
41872
41872
  const trustRead = readJson(d, claudeJsonPath);
41873
41873
  if (mcpRead.kind === "unreadable" || trustRead.kind === "unreadable") {
@@ -41980,7 +41980,7 @@ async function runDriveBrokerReachabilityChecks(config, deps = {}) {
41980
41980
  }
41981
41981
  ];
41982
41982
  }
41983
- const sock = deps.brokerOperatorSocket ?? join53(homedir30(), ".switchroom", "broker-operator", "sock");
41983
+ const sock = deps.brokerOperatorSocket ?? join54(homedir31(), ".switchroom", "broker-operator", "sock");
41984
41984
  const preflight = deps.preflight ?? ((a, k) => defaultPreflight(sock, a, k));
41985
41985
  const results = [];
41986
41986
  for (const agent of driveAgents) {
@@ -42034,8 +42034,8 @@ import {
42034
42034
  readSync as realReadSync,
42035
42035
  closeSync as realCloseSync
42036
42036
  } from "node:fs";
42037
- import { join as join54 } from "node:path";
42038
- import { homedir as homedir31 } from "node:os";
42037
+ import { join as join55 } from "node:path";
42038
+ import { homedir as homedir32 } from "node:os";
42039
42039
  function defaultReadHead(p, n) {
42040
42040
  let fd;
42041
42041
  try {
@@ -42063,7 +42063,7 @@ function resolveDeps2(config, deps) {
42063
42063
  }
42064
42064
  }
42065
42065
  return {
42066
- homeDir: deps.homeDir ?? homedir31(),
42066
+ homeDir: deps.homeDir ?? homedir32(),
42067
42067
  agentsDir,
42068
42068
  existsSync: deps.existsSync ?? ((p) => realExistsSync2(p)),
42069
42069
  readFileSync: deps.readFileSync ?? ((p) => realReadFileSync2(p, "utf-8")),
@@ -42092,7 +42092,7 @@ function runWebkiteChecks(config, deps = {}) {
42092
42092
  return [];
42093
42093
  const d = resolveDeps2(config, deps);
42094
42094
  const results = [];
42095
- const binPath = join54(d.homeDir, ".switchroom", "bin", "webkite");
42095
+ const binPath = join55(d.homeDir, ".switchroom", "bin", "webkite");
42096
42096
  if (!d.existsSync(binPath)) {
42097
42097
  results.push({
42098
42098
  name: "webkite: binary",
@@ -42122,12 +42122,12 @@ function runWebkiteChecks(config, deps = {}) {
42122
42122
  });
42123
42123
  }
42124
42124
  }
42125
- const cloakDir = join54(d.homeDir, ".cloakbrowser");
42125
+ const cloakDir = join55(d.homeDir, ".cloakbrowser");
42126
42126
  let chromeFound = false;
42127
42127
  if (d.existsSync(cloakDir)) {
42128
42128
  try {
42129
42129
  for (const entry of d.readdirSync(cloakDir)) {
42130
- if (entry.startsWith("chromium-") && d.existsSync(join54(cloakDir, entry, "chrome"))) {
42130
+ if (entry.startsWith("chromium-") && d.existsSync(join55(cloakDir, entry, "chrome"))) {
42131
42131
  chromeFound = true;
42132
42132
  break;
42133
42133
  }
@@ -42153,9 +42153,9 @@ function runWebkiteChecks(config, deps = {}) {
42153
42153
  return results;
42154
42154
  }
42155
42155
  for (const agent of enabledAgents) {
42156
- const agentDir = join54(d.agentsDir, agent);
42157
- const settingsPath = join54(agentDir, ".claude", "settings.json");
42158
- const mcpPath = join54(agentDir, ".mcp.json");
42156
+ const agentDir = join55(d.agentsDir, agent);
42157
+ const settingsPath = join55(agentDir, ".claude", "settings.json");
42158
+ const mcpPath = join55(agentDir, ".mcp.json");
42159
42159
  if (!d.existsSync(settingsPath) && !d.existsSync(mcpPath)) {
42160
42160
  continue;
42161
42161
  }
@@ -42285,7 +42285,7 @@ var init_doctor_cron_session = __esm(() => {
42285
42285
  });
42286
42286
 
42287
42287
  // src/cli/doctor-scaffold-wiring.ts
42288
- import { join as join55, resolve as resolve36 } from "node:path";
42288
+ import { join as join56, resolve as resolve36 } from "node:path";
42289
42289
  function readJson2(d, path4) {
42290
42290
  if (!d.existsSync(path4))
42291
42291
  return { kind: "absent" };
@@ -42328,8 +42328,8 @@ function checkIntegrationScaffoldWiring(args) {
42328
42328
  });
42329
42329
  continue;
42330
42330
  }
42331
- const mcpPath = join55(agentDir, ".mcp.json");
42332
- const claudeJsonPath = join55(agentDir, ".claude", ".claude.json");
42331
+ const mcpPath = join56(agentDir, ".mcp.json");
42332
+ const claudeJsonPath = join56(agentDir, ".claude", ".claude.json");
42333
42333
  const mcpRead = readJson2(deps, mcpPath);
42334
42334
  const trustRead = readJson2(deps, claudeJsonPath);
42335
42335
  if (mcpRead.kind === "unreadable" || trustRead.kind === "unreadable") {
@@ -42393,14 +42393,14 @@ import {
42393
42393
  existsSync as realExistsSync3,
42394
42394
  readFileSync as realReadFileSync3
42395
42395
  } from "node:fs";
42396
- import { join as join56 } from "node:path";
42397
- import { homedir as homedir32 } from "node:os";
42396
+ import { join as join57 } from "node:path";
42397
+ import { homedir as homedir33 } from "node:os";
42398
42398
  function resolveDeps3(deps) {
42399
- const home2 = deps.homeDir?.() ?? homedir32();
42399
+ const home2 = deps.homeDir?.() ?? homedir33();
42400
42400
  return {
42401
42401
  existsSync: deps.existsSync ?? realExistsSync3,
42402
42402
  readFileSync: deps.readFileSync ?? realReadFileSync3,
42403
- agentsDir: join56(home2, ".switchroom", "agents"),
42403
+ agentsDir: join57(home2, ".switchroom", "agents"),
42404
42404
  now: deps.now ?? Date.now
42405
42405
  };
42406
42406
  }
@@ -42483,7 +42483,7 @@ function checkOAuthClient2(config, anyAgentEnabled) {
42483
42483
  ];
42484
42484
  }
42485
42485
  function readHeartbeat(d, agentName) {
42486
- const path4 = join56(d.agentsDir, agentName, "m365-launcher.heartbeat.json");
42486
+ const path4 = join57(d.agentsDir, agentName, "m365-launcher.heartbeat.json");
42487
42487
  if (!d.existsSync(path4)) {
42488
42488
  return { error: "heartbeat file missing \u2014 launcher has not yet started" };
42489
42489
  }
@@ -42580,15 +42580,15 @@ import {
42580
42580
  readFileSync as realReadFileSync4,
42581
42581
  statSync as realStatSync2
42582
42582
  } from "node:fs";
42583
- import { join as join57 } from "node:path";
42584
- import { homedir as homedir33 } from "node:os";
42583
+ import { join as join58 } from "node:path";
42584
+ import { homedir as homedir34 } from "node:os";
42585
42585
  function resolveDeps4(deps) {
42586
- const home2 = deps.homeDir?.() ?? homedir33();
42586
+ const home2 = deps.homeDir?.() ?? homedir34();
42587
42587
  return {
42588
42588
  existsSync: deps.existsSync ?? realExistsSync4,
42589
42589
  readFileSync: deps.readFileSync ?? realReadFileSync4,
42590
42590
  statSync: deps.statSync ?? realStatSync2,
42591
- agentsDir: join57(home2, ".switchroom", "agents"),
42591
+ agentsDir: join58(home2, ".switchroom", "agents"),
42592
42592
  now: deps.now ?? Date.now,
42593
42593
  vaultAclReader: deps.vaultAclReader ?? (async () => ({ kind: "unreachable", msg: "no default reader wired" }))
42594
42594
  };
@@ -42713,7 +42713,7 @@ function checkLauncherHeartbeat2(notionAgents, d) {
42713
42713
  return [];
42714
42714
  const results = [];
42715
42715
  for (const name of notionAgents) {
42716
- const heartbeatPath = join57(d.agentsDir, name, "notion-launcher.heartbeat.json");
42716
+ const heartbeatPath = join58(d.agentsDir, name, "notion-launcher.heartbeat.json");
42717
42717
  if (!d.existsSync(heartbeatPath)) {
42718
42718
  results.push({
42719
42719
  name: `notion:launcher-heartbeat:${name}`,
@@ -42934,11 +42934,11 @@ import {
42934
42934
  readdirSync as realReaddirSync2,
42935
42935
  statSync as realStatSync3
42936
42936
  } from "node:fs";
42937
- import { homedir as homedir34 } from "node:os";
42938
- import { join as join58 } from "node:path";
42937
+ import { homedir as homedir35 } from "node:os";
42938
+ import { join as join59 } from "node:path";
42939
42939
  function runCredentialsMigrationChecks(config, deps = {}) {
42940
- const credDir = deps.credentialsDir ?? join58(homedir34(), ".switchroom", "credentials");
42941
- const existsSync60 = deps.existsSync ?? ((p) => realExistsSync5(p));
42940
+ const credDir = deps.credentialsDir ?? join59(homedir35(), ".switchroom", "credentials");
42941
+ const existsSync61 = deps.existsSync ?? ((p) => realExistsSync5(p));
42942
42942
  const readdirSync21 = deps.readdirSync ?? ((p) => realReaddirSync2(p));
42943
42943
  const isDirectory = deps.isDirectory ?? ((p) => {
42944
42944
  try {
@@ -42947,7 +42947,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
42947
42947
  return false;
42948
42948
  }
42949
42949
  });
42950
- if (!existsSync60(credDir))
42950
+ if (!existsSync61(credDir))
42951
42951
  return [];
42952
42952
  const agentNames = new Set(Object.keys(config.agents ?? {}));
42953
42953
  let entries;
@@ -42965,7 +42965,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
42965
42965
  const flat = [];
42966
42966
  const perAgentDirs = [];
42967
42967
  for (const e of entries) {
42968
- const full = join58(credDir, e);
42968
+ const full = join59(credDir, e);
42969
42969
  if (isDirectory(full) && agentNames.has(e)) {
42970
42970
  perAgentDirs.push(e);
42971
42971
  } else {
@@ -43088,19 +43088,19 @@ var init_doctor_inlined_secrets = __esm(() => {
43088
43088
 
43089
43089
  // src/cli/doctor-audit-integrity.ts
43090
43090
  import { readFileSync as fsReadFileSync2 } from "node:fs";
43091
- import { homedir as homedir35 } from "node:os";
43092
- import { join as join59 } from "node:path";
43091
+ import { homedir as homedir36 } from "node:os";
43092
+ import { join as join60 } from "node:path";
43093
43093
  function rootWrittenLogs(home2) {
43094
43094
  return [
43095
- { label: "vault-broker", path: join59(home2, ".switchroom", "vault-audit.log") },
43095
+ { label: "vault-broker", path: join60(home2, ".switchroom", "vault-audit.log") },
43096
43096
  {
43097
43097
  label: "hostd",
43098
- path: join59(home2, ".switchroom", "host-control-audit.log")
43098
+ path: join60(home2, ".switchroom", "host-control-audit.log")
43099
43099
  }
43100
43100
  ];
43101
43101
  }
43102
43102
  function runAuditIntegrityChecks(deps = {}) {
43103
- const home2 = deps.homeDir ?? homedir35();
43103
+ const home2 = deps.homeDir ?? homedir36();
43104
43104
  const read = deps.readFileSync ?? ((p) => fsReadFileSync2(p, "utf8"));
43105
43105
  const results = [];
43106
43106
  for (const { label, path: path4 } of rootWrittenLogs(home2)) {
@@ -43157,16 +43157,16 @@ var init_doctor_audit_integrity = __esm(() => {
43157
43157
  });
43158
43158
 
43159
43159
  // src/cli/doctor-agent-smoke.ts
43160
- import { existsSync as existsSync60 } from "node:fs";
43161
- import { homedir as homedir36 } from "node:os";
43162
- import { join as join60 } from "node:path";
43160
+ import { existsSync as existsSync61 } from "node:fs";
43161
+ import { homedir as homedir37 } from "node:os";
43162
+ import { join as join61 } from "node:path";
43163
43163
  import { randomUUID as randomUUID5 } from "node:crypto";
43164
43164
  async function runAgentSmokeChecks(config, deps = {}) {
43165
43165
  if (deps.fast)
43166
43166
  return [];
43167
- const home2 = deps.homeDir ?? homedir36();
43168
- const sock = deps.operatorSockPath ?? join60(home2, ".switchroom", "hostd", "operator", "sock");
43169
- if (!deps.hostdRequestImpl && !existsSync60(sock)) {
43167
+ const home2 = deps.homeDir ?? homedir37();
43168
+ const sock = deps.operatorSockPath ?? join61(home2, ".switchroom", "hostd", "operator", "sock");
43169
+ if (!deps.hostdRequestImpl && !existsSync61(sock)) {
43170
43170
  return [
43171
43171
  {
43172
43172
  name: "agent liveness",
@@ -43244,9 +43244,9 @@ var init_doctor_agent_smoke = __esm(() => {
43244
43244
 
43245
43245
  // src/cli/doctor-vault-broker-durability.ts
43246
43246
  import { execFileSync as execFileSync18 } from "node:child_process";
43247
- import { existsSync as existsSync61, statSync as statSync25 } from "node:fs";
43248
- import { homedir as homedir37 } from "node:os";
43249
- import { join as join61 } from "node:path";
43247
+ import { existsSync as existsSync62, statSync as statSync26 } from "node:fs";
43248
+ import { homedir as homedir38 } from "node:os";
43249
+ import { join as join62 } from "node:path";
43250
43250
  function probeBindMountInode(hostPath, brokerContainerPath, opts) {
43251
43251
  const statHost = opts?.statHost ?? defaultStatHost;
43252
43252
  const statBroker = opts?.statBroker ?? defaultStatBroker;
@@ -43268,10 +43268,10 @@ function probeBindMountInode(hostPath, brokerContainerPath, opts) {
43268
43268
  };
43269
43269
  }
43270
43270
  function defaultStatHost(p) {
43271
- if (!existsSync61(p))
43271
+ if (!existsSync62(p))
43272
43272
  return null;
43273
43273
  try {
43274
- const s = statSync25(p, { bigint: true });
43274
+ const s = statSync26(p, { bigint: true });
43275
43275
  return { ino: s.ino, size: Number(s.size) };
43276
43276
  } catch {
43277
43277
  return null;
@@ -43389,22 +43389,22 @@ function defaultBrokerStatusProbe() {
43389
43389
  }
43390
43390
  }
43391
43391
  function runVaultBrokerDurabilityChecks(_config, opts) {
43392
- const home2 = homedir37();
43392
+ const home2 = homedir38();
43393
43393
  const probe2 = opts?.inodeProbe ?? probeBindMountInode;
43394
43394
  return [
43395
43395
  probeBrokerUnlocked(opts?.statusProbe),
43396
43396
  probeAutoUnlockBlob(home2),
43397
43397
  probeMachineIdMount(),
43398
- formatBindMountResult("vault-broker: vault.enc bind mount", join61(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join61(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
43399
- formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join61(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join61(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
43400
- formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join61(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join61(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log")),
43398
+ formatBindMountResult("vault-broker: vault.enc bind mount", join62(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join62(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
43399
+ formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join62(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join62(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
43400
+ formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join62(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join62(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log")),
43401
43401
  probeKernelDbDurability(home2, {
43402
43402
  statBroker: opts?.kernelStatBroker
43403
43403
  })
43404
43404
  ];
43405
43405
  }
43406
43406
  function probeKernelDbDurability(home2, opts) {
43407
- const hostDir = join61(home2, ".switchroom", "approvals");
43407
+ const hostDir = join62(home2, ".switchroom", "approvals");
43408
43408
  const containerDir = "/state/approvals";
43409
43409
  const name = "approval-kernel: approvals bind mount (allow_always durability)";
43410
43410
  const kernelStat = opts?.statBroker ?? defaultKernelStatBroker;
@@ -43472,8 +43472,8 @@ function defaultKernelStatBroker(p) {
43472
43472
  return { kind: "ok-with-stat", ino: inoStr, size };
43473
43473
  }
43474
43474
  function probeAutoUnlockBlob(home2) {
43475
- const blobPath = join61(home2, ".switchroom", "vault-auto-unlock");
43476
- if (!existsSync61(blobPath)) {
43475
+ const blobPath = join62(home2, ".switchroom", "vault-auto-unlock");
43476
+ if (!existsSync62(blobPath)) {
43477
43477
  return {
43478
43478
  name: "vault-broker: auto-unlock blob",
43479
43479
  status: "warn",
@@ -43481,7 +43481,7 @@ function probeAutoUnlockBlob(home2) {
43481
43481
  fix: "Run `switchroom vault broker enable-auto-unlock` to seal the blob with the current passphrase + machine-id"
43482
43482
  };
43483
43483
  }
43484
- const sz = statSync25(blobPath).size;
43484
+ const sz = statSync26(blobPath).size;
43485
43485
  if (sz === 0) {
43486
43486
  return {
43487
43487
  name: "vault-broker: auto-unlock blob",
@@ -43497,7 +43497,7 @@ function probeAutoUnlockBlob(home2) {
43497
43497
  };
43498
43498
  }
43499
43499
  function probeMachineIdMount() {
43500
- const hostExists = existsSync61("/etc/machine-id");
43500
+ const hostExists = existsSync62("/etc/machine-id");
43501
43501
  if (!hostExists) {
43502
43502
  return {
43503
43503
  name: "vault-broker: machine-id passthrough",
@@ -43645,25 +43645,25 @@ import { execSync as execSync3, spawnSync as spawnSync11 } from "node:child_proc
43645
43645
  import {
43646
43646
  accessSync as accessSync2,
43647
43647
  constants as fsConstants5,
43648
- existsSync as existsSync62,
43648
+ existsSync as existsSync63,
43649
43649
  lstatSync as lstatSync7,
43650
- mkdirSync as mkdirSync32,
43651
- readFileSync as readFileSync55,
43650
+ mkdirSync as mkdirSync33,
43651
+ readFileSync as readFileSync56,
43652
43652
  readdirSync as readdirSync21,
43653
- statSync as statSync26
43653
+ statSync as statSync27
43654
43654
  } from "node:fs";
43655
- import { dirname as dirname18, join as join62, resolve as resolve37 } from "node:path";
43655
+ import { dirname as dirname19, join as join63, resolve as resolve37 } from "node:path";
43656
43656
  import { createPublicKey, createPrivateKey } from "node:crypto";
43657
43657
  function findInNvm(bin) {
43658
- const nvmRoot = join62(process.env.HOME ?? "", ".nvm", "versions", "node");
43659
- if (!existsSync62(nvmRoot))
43658
+ const nvmRoot = join63(process.env.HOME ?? "", ".nvm", "versions", "node");
43659
+ if (!existsSync63(nvmRoot))
43660
43660
  return null;
43661
43661
  try {
43662
43662
  const versions = readdirSync21(nvmRoot).sort().reverse();
43663
43663
  for (const v of versions) {
43664
- const candidate = join62(nvmRoot, v, "bin", bin);
43664
+ const candidate = join63(nvmRoot, v, "bin", bin);
43665
43665
  try {
43666
- const s = statSync26(candidate);
43666
+ const s = statSync27(candidate);
43667
43667
  if (s.isFile() || s.isSymbolicLink()) {
43668
43668
  return candidate;
43669
43669
  }
@@ -43826,21 +43826,21 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
43826
43826
  if (envBrowsersPath && envBrowsersPath.length > 0) {
43827
43827
  cacheLocations.push(envBrowsersPath);
43828
43828
  }
43829
- cacheLocations.push(join62(homeDir, ".cache", "ms-playwright"));
43829
+ cacheLocations.push(join63(homeDir, ".cache", "ms-playwright"));
43830
43830
  for (const cacheDir of cacheLocations) {
43831
- if (!existsSync62(cacheDir))
43831
+ if (!existsSync63(cacheDir))
43832
43832
  continue;
43833
43833
  try {
43834
43834
  const entries = readdirSync21(cacheDir).filter((e) => e.startsWith("chromium"));
43835
43835
  for (const entry of entries) {
43836
43836
  const candidates2 = [
43837
- join62(cacheDir, entry, "chrome-linux64", "chrome"),
43838
- join62(cacheDir, entry, "chrome-linux", "chrome"),
43839
- join62(cacheDir, entry, "chrome-linux64", "headless_shell"),
43840
- join62(cacheDir, entry, "chrome-linux", "headless_shell")
43837
+ join63(cacheDir, entry, "chrome-linux64", "chrome"),
43838
+ join63(cacheDir, entry, "chrome-linux", "chrome"),
43839
+ join63(cacheDir, entry, "chrome-linux64", "headless_shell"),
43840
+ join63(cacheDir, entry, "chrome-linux", "headless_shell")
43841
43841
  ];
43842
43842
  for (const path4 of candidates2) {
43843
- if (existsSync62(path4))
43843
+ if (existsSync63(path4))
43844
43844
  return path4;
43845
43845
  }
43846
43846
  }
@@ -43862,7 +43862,7 @@ function checkChromium() {
43862
43862
  }
43863
43863
  function checkDepsCacheWritable(depsRoot = resolvePath("~/.switchroom/deps")) {
43864
43864
  try {
43865
- mkdirSync32(depsRoot, { recursive: true });
43865
+ mkdirSync33(depsRoot, { recursive: true });
43866
43866
  accessSync2(depsRoot, fsConstants5.W_OK);
43867
43867
  return {
43868
43868
  name: "~/.switchroom/deps writable",
@@ -43969,7 +43969,7 @@ function checkDeployMounts(opts) {
43969
43969
  const home2 = opts?.home ?? process.env.HOME ?? "/root";
43970
43970
  const { pathKind } = opts?.deps ?? DEFAULT_DEPLOY_MOUNTS_DEPS;
43971
43971
  const results = [];
43972
- const dockerComposePlugin = join62(home2, ".docker", "cli-plugins", "docker-compose");
43972
+ const dockerComposePlugin = join63(home2, ".docker", "cli-plugins", "docker-compose");
43973
43973
  const pluginKind = pathKind(dockerComposePlugin);
43974
43974
  if (pluginKind === "dir") {
43975
43975
  results.push({
@@ -44007,8 +44007,8 @@ function checkDeployMounts(opts) {
44007
44007
  function checkLegacyState() {
44008
44008
  const results = [];
44009
44009
  const h = process.env.HOME ?? "/root";
44010
- const clerkDir = join62(h, LEGACY_STATE_DIR);
44011
- const clerkPresent = existsSync62(clerkDir);
44010
+ const clerkDir = join63(h, LEGACY_STATE_DIR);
44011
+ const clerkPresent = existsSync63(clerkDir);
44012
44012
  results.push({
44013
44013
  name: "legacy ~/.clerk state",
44014
44014
  status: clerkPresent ? "warn" : "ok",
@@ -44017,7 +44017,7 @@ function checkLegacyState() {
44017
44017
  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."
44018
44018
  } : {}
44019
44019
  });
44020
- const legacySock = join62(h, ".switchroom", "vault-broker.sock");
44020
+ const legacySock = join63(h, ".switchroom", "vault-broker.sock");
44021
44021
  let sockStat = null;
44022
44022
  try {
44023
44023
  sockStat = lstatSync7(legacySock);
@@ -44138,7 +44138,7 @@ function checkVault(config) {
44138
44138
  detail: "Approval auth: passphrase (two-factor)"
44139
44139
  };
44140
44140
  const pairsResult = checkVaultBrokerSocketPairs(config);
44141
- if (!existsSync62(vaultPath)) {
44141
+ if (!existsSync63(vaultPath)) {
44142
44142
  return [
44143
44143
  postureResult,
44144
44144
  {
@@ -44416,8 +44416,8 @@ async function checkHindsight(config) {
44416
44416
  }
44417
44417
  function checkPendingRetainsQueue(dir) {
44418
44418
  const home2 = process.env.HOME ?? "";
44419
- const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join62(home2, ".hindsight", "pending-retains");
44420
- if (!existsSync62(pendingDir)) {
44419
+ const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join63(home2, ".hindsight", "pending-retains");
44420
+ if (!existsSync63(pendingDir)) {
44421
44421
  return {
44422
44422
  name: "pending-retains queue",
44423
44423
  status: "ok",
@@ -44476,7 +44476,7 @@ function classifyReadError(err) {
44476
44476
  }
44477
44477
  function tryReadHostFile(path4) {
44478
44478
  try {
44479
- return { kind: "ok", content: readFileSync55(path4, "utf-8") };
44479
+ return { kind: "ok", content: readFileSync56(path4, "utf-8") };
44480
44480
  } catch (err) {
44481
44481
  const kind = classifyReadError(err);
44482
44482
  const error = err?.message ?? String(err);
@@ -44488,11 +44488,11 @@ function tryReadHostFile(path4) {
44488
44488
  }
44489
44489
  }
44490
44490
  function parseEnvFile(path4) {
44491
- if (!existsSync62(path4))
44491
+ if (!existsSync63(path4))
44492
44492
  return {};
44493
44493
  let content;
44494
44494
  try {
44495
- content = readFileSync55(path4, "utf-8");
44495
+ content = readFileSync56(path4, "utf-8");
44496
44496
  } catch {
44497
44497
  return {};
44498
44498
  }
@@ -44547,7 +44547,7 @@ async function checkTelegram(config) {
44547
44547
  const plugin = agentConfig.channels?.telegram?.plugin ?? "switchroom";
44548
44548
  if (plugin !== "switchroom")
44549
44549
  continue;
44550
- const envPath = join62(agentsDir, name, "telegram", ".env");
44550
+ const envPath = join63(agentsDir, name, "telegram", ".env");
44551
44551
  const read = tryReadHostFile(envPath);
44552
44552
  if (read.kind === "eacces") {
44553
44553
  results.push({
@@ -44599,7 +44599,7 @@ async function checkTelegram(config) {
44599
44599
  }
44600
44600
  function checkStartShStale(agentName, startShPath) {
44601
44601
  const label = `${agentName}: start.sh scheduler block`;
44602
- if (!existsSync62(startShPath)) {
44602
+ if (!existsSync63(startShPath)) {
44603
44603
  return {
44604
44604
  name: label,
44605
44605
  status: "warn",
@@ -44609,7 +44609,7 @@ function checkStartShStale(agentName, startShPath) {
44609
44609
  }
44610
44610
  let content;
44611
44611
  try {
44612
- content = readFileSync55(startShPath, "utf-8");
44612
+ content = readFileSync56(startShPath, "utf-8");
44613
44613
  } catch (err) {
44614
44614
  return {
44615
44615
  name: label,
@@ -44630,7 +44630,7 @@ function checkStartShStale(agentName, startShPath) {
44630
44630
  }
44631
44631
  function checkLeakedHomeSwitchroom(agentName, agentDir) {
44632
44632
  const label = `${agentName}: $HOME/.switchroom symlink (#910)`;
44633
- const path4 = join62(agentDir, "home", ".switchroom");
44633
+ const path4 = join63(agentDir, "home", ".switchroom");
44634
44634
  let stats;
44635
44635
  try {
44636
44636
  stats = lstatSync7(path4);
@@ -44667,8 +44667,8 @@ function checkLeakedHomeSwitchroom(agentName, agentDir) {
44667
44667
  }
44668
44668
  function checkRepoHygiene(repoRoot) {
44669
44669
  const results = [];
44670
- const exportDir = join62(repoRoot, "clerk-export");
44671
- if (existsSync62(exportDir)) {
44670
+ const exportDir = join63(repoRoot, "clerk-export");
44671
+ if (existsSync63(exportDir)) {
44672
44672
  results.push({
44673
44673
  name: "repo hygiene: clerk-export/ on disk (#1072)",
44674
44674
  status: "warn",
@@ -44676,8 +44676,8 @@ function checkRepoHygiene(repoRoot) {
44676
44676
  fix: `Run scripts/migrate-clerk-export-to-vault.sh to move the bundle ` + `into the vault, then delete the on-disk copy.`
44677
44677
  });
44678
44678
  }
44679
- const knownTarball = join62(repoRoot, "clerk-export-with-secrets.tar.gz");
44680
- if (existsSync62(knownTarball)) {
44679
+ const knownTarball = join63(repoRoot, "clerk-export-with-secrets.tar.gz");
44680
+ if (existsSync63(knownTarball)) {
44681
44681
  results.push({
44682
44682
  name: "repo hygiene: clerk-export-with-secrets.tar.gz on disk (#1072)",
44683
44683
  status: "warn",
@@ -44694,7 +44694,7 @@ function checkRepoHygiene(repoRoot) {
44694
44694
  results.push({
44695
44695
  name: `repo hygiene: ${name} on disk (#1072)`,
44696
44696
  status: "warn",
44697
- detail: `${join62(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
44697
+ detail: `${join63(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
44698
44698
  fix: `Inspect, migrate any secrets into the vault, then delete the ` + `archive.`
44699
44699
  });
44700
44700
  }
@@ -44717,12 +44717,12 @@ function checkRepoHygiene(repoRoot) {
44717
44717
  }
44718
44718
  function isSwitchroomCheckout(dir) {
44719
44719
  try {
44720
- if (!existsSync62(join62(dir, ".git")))
44720
+ if (!existsSync63(join63(dir, ".git")))
44721
44721
  return false;
44722
- const pkgPath = join62(dir, "package.json");
44723
- if (!existsSync62(pkgPath))
44722
+ const pkgPath = join63(dir, "package.json");
44723
+ if (!existsSync63(pkgPath))
44724
44724
  return false;
44725
- const pkg = JSON.parse(readFileSync55(pkgPath, "utf-8"));
44725
+ const pkg = JSON.parse(readFileSync56(pkgPath, "utf-8"));
44726
44726
  return pkg.name === "switchroom";
44727
44727
  } catch {
44728
44728
  return false;
@@ -44735,7 +44735,7 @@ function checkAgents(config, configPath) {
44735
44735
  const authStatuses = getAllAuthStatuses(config);
44736
44736
  for (const [name, agentConfig] of Object.entries(config.agents)) {
44737
44737
  const agentDir = resolve37(agentsDir, name);
44738
- if (!existsSync62(agentDir)) {
44738
+ if (!existsSync63(agentDir)) {
44739
44739
  results.push({
44740
44740
  name: `${name}: scaffold`,
44741
44741
  status: "fail",
@@ -44756,7 +44756,7 @@ function checkAgents(config, configPath) {
44756
44756
  fix: `Rotate the bot token (e.g. via \`switchroom vault\`), then run ` + `\`switchroom agent unquarantine ${name}\` and \`switchroom agent restart ${name}\``
44757
44757
  });
44758
44758
  }
44759
- results.push(checkStartShStale(name, join62(agentDir, "start.sh")));
44759
+ results.push(checkStartShStale(name, join63(agentDir, "start.sh")));
44760
44760
  results.push(checkLeakedHomeSwitchroom(name, agentDir));
44761
44761
  const status = statuses[name];
44762
44762
  const active = status?.active ?? "unknown";
@@ -44833,8 +44833,8 @@ function checkAgents(config, configPath) {
44833
44833
  }
44834
44834
  }
44835
44835
  if (agentConfig.channels?.telegram?.plugin === "switchroom") {
44836
- const mcpJsonPath = join62(agentDir, ".mcp.json");
44837
- if (!existsSync62(mcpJsonPath)) {
44836
+ const mcpJsonPath = join63(agentDir, ".mcp.json");
44837
+ if (!existsSync63(mcpJsonPath)) {
44838
44838
  results.push({
44839
44839
  name: `${name}: .mcp.json`,
44840
44840
  status: "fail",
@@ -44843,7 +44843,7 @@ function checkAgents(config, configPath) {
44843
44843
  });
44844
44844
  } else {
44845
44845
  try {
44846
- const mcp = JSON.parse(readFileSync55(mcpJsonPath, "utf-8"));
44846
+ const mcp = JSON.parse(readFileSync56(mcpJsonPath, "utf-8"));
44847
44847
  const hasSwitchroomTelegram = !!mcp.mcpServers?.["switchroom-telegram"];
44848
44848
  const memoryEnabled = isHindsightEnabled(config);
44849
44849
  const hasHindsight = !!mcp.mcpServers?.hindsight;
@@ -44920,7 +44920,7 @@ function mffEnvPath(config) {
44920
44920
  return agent ? resolve37(home2, ".switchroom/credentials", agent, "my-family-finance/.env") : resolve37(home2, ".switchroom/credentials/my-family-finance/.env");
44921
44921
  }
44922
44922
  function mffEnvState(envPath) {
44923
- if (!existsSync62(envPath))
44923
+ if (!existsSync63(envPath))
44924
44924
  return "absent";
44925
44925
  try {
44926
44926
  accessSync2(envPath, fsConstants5.R_OK);
@@ -44938,7 +44938,7 @@ function checkMffVaultKeyPresent(passphrase, vaultPath) {
44938
44938
  fix: "Export SWITCHROOM_VAULT_PASSPHRASE to enable MFF vault probes"
44939
44939
  };
44940
44940
  }
44941
- if (!existsSync62(vaultPath)) {
44941
+ if (!existsSync63(vaultPath)) {
44942
44942
  return {
44943
44943
  name: "mff: vault key present",
44944
44944
  status: "fail",
@@ -44991,7 +44991,7 @@ function deriveEd25519PublicKeyBytes(keyMaterial) {
44991
44991
  }
44992
44992
  }
44993
44993
  function checkMffVaultKeyFormat(passphrase, vaultPath) {
44994
- if (!passphrase || !existsSync62(vaultPath)) {
44994
+ if (!passphrase || !existsSync63(vaultPath)) {
44995
44995
  return {
44996
44996
  name: "mff: vault key format",
44997
44997
  status: "warn",
@@ -45134,9 +45134,9 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
45134
45134
  detail: "skipped (MFF_API_URL not set)"
45135
45135
  };
45136
45136
  }
45137
- const credDir = dirname18(envPath);
45138
- const authScript = join62(credDir, "claude-auth.py");
45139
- if (!existsSync62(authScript)) {
45137
+ const credDir = dirname19(envPath);
45138
+ const authScript = join63(credDir, "claude-auth.py");
45139
+ if (!existsSync63(authScript)) {
45140
45140
  return {
45141
45141
  name: "mff: auth flow",
45142
45142
  status: "warn",
@@ -45338,11 +45338,11 @@ function runDockerSection(config) {
45338
45338
  let composeYaml;
45339
45339
  let dockerfileAgent;
45340
45340
  try {
45341
- composeYaml = readFileSync55(composePath, "utf8");
45341
+ composeYaml = readFileSync56(composePath, "utf8");
45342
45342
  } catch {}
45343
45343
  const dockerfilePath = resolve37(process.env.HOME ?? "", ".switchroom", "docker", "Dockerfile.agent");
45344
45344
  try {
45345
- dockerfileAgent = readFileSync55(dockerfilePath, "utf8");
45345
+ dockerfileAgent = readFileSync56(dockerfilePath, "utf8");
45346
45346
  } catch {}
45347
45347
  return runDockerChecks({
45348
45348
  config,
@@ -48662,8 +48662,8 @@ agents:
48662
48662
  var init_minimal = () => {};
48663
48663
 
48664
48664
  // src/agents/connection-health.ts
48665
- import { mkdirSync as mkdirSync43, writeFileSync as writeFileSync35 } from "node:fs";
48666
- import { join as join77 } from "node:path";
48665
+ import { mkdirSync as mkdirSync44, writeFileSync as writeFileSync36 } from "node:fs";
48666
+ import { join as join78 } from "node:path";
48667
48667
  async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
48668
48668
  const reqs = computeMcpSecretRequirements(config).filter((r) => r.agent === agentName);
48669
48669
  if (reqs.length === 0)
@@ -48720,10 +48720,10 @@ async function computeAgentConnectionIssues(config, agentName, vaultAclReader) {
48720
48720
  return issues;
48721
48721
  }
48722
48722
  function writeConnectionHealthFile(agentDir, health, deps) {
48723
- const dir = join77(agentDir, ".claude");
48724
- const path7 = join77(dir, CONNECTION_HEALTH_FILENAME);
48725
- (deps?.mkdir ?? ((p, o) => mkdirSync43(p, o)))(dir, { recursive: true });
48726
- (deps?.writeFile ?? ((p, d) => writeFileSync35(p, d)))(path7, JSON.stringify(health, null, 2) + `
48723
+ const dir = join78(agentDir, ".claude");
48724
+ const path7 = join78(dir, CONNECTION_HEALTH_FILENAME);
48725
+ (deps?.mkdir ?? ((p, o) => mkdirSync44(p, o)))(dir, { recursive: true });
48726
+ (deps?.writeFile ?? ((p, d) => writeFileSync36(p, d)))(path7, JSON.stringify(health, null, 2) + `
48727
48727
  `);
48728
48728
  }
48729
48729
  async function refreshAgentConnectionHealth(config, agentName, agentDir, deps) {
@@ -48744,10 +48744,10 @@ var CONNECTION_HEALTH_FILENAME = "connection-health.json";
48744
48744
  var init_connection_health = () => {};
48745
48745
 
48746
48746
  // src/cli/update-prompt-hook.ts
48747
- import { existsSync as existsSync79, readFileSync as readFileSync67, writeFileSync as writeFileSync36, chmodSync as chmodSync10, mkdirSync as mkdirSync44 } from "node:fs";
48748
- import { join as join78 } from "node:path";
48747
+ import { existsSync as existsSync80, readFileSync as readFileSync68, writeFileSync as writeFileSync37, chmodSync as chmodSync10, mkdirSync as mkdirSync45 } from "node:fs";
48748
+ import { join as join79 } from "node:path";
48749
48749
  function containerHookCommand() {
48750
- return join78(CONTAINER_AGENT_DIR, ".claude", "hooks", HOOK_FILENAME);
48750
+ return join79(CONTAINER_AGENT_DIR, ".claude", "hooks", HOOK_FILENAME);
48751
48751
  }
48752
48752
  function updatePromptHookScript() {
48753
48753
  return `#!/bin/bash
@@ -48813,14 +48813,14 @@ exit 0
48813
48813
  `;
48814
48814
  }
48815
48815
  function installUpdatePromptHook(agentDir) {
48816
- const hooksDir = join78(agentDir, ".claude", "hooks");
48817
- mkdirSync44(hooksDir, { recursive: true });
48818
- const scriptPath = join78(hooksDir, HOOK_FILENAME);
48816
+ const hooksDir = join79(agentDir, ".claude", "hooks");
48817
+ mkdirSync45(hooksDir, { recursive: true });
48818
+ const scriptPath = join79(hooksDir, HOOK_FILENAME);
48819
48819
  const desired = updatePromptHookScript();
48820
48820
  let installed = false;
48821
- const existing = existsSync79(scriptPath) ? readFileSync67(scriptPath, "utf-8") : "";
48821
+ const existing = existsSync80(scriptPath) ? readFileSync68(scriptPath, "utf-8") : "";
48822
48822
  if (existing !== desired) {
48823
- writeFileSync36(scriptPath, desired, { mode: 493 });
48823
+ writeFileSync37(scriptPath, desired, { mode: 493 });
48824
48824
  chmodSync10(scriptPath, 493);
48825
48825
  installed = true;
48826
48826
  } else {
@@ -48828,11 +48828,11 @@ function installUpdatePromptHook(agentDir) {
48828
48828
  chmodSync10(scriptPath, 493);
48829
48829
  } catch {}
48830
48830
  }
48831
- const settingsPath = join78(agentDir, ".claude", "settings.json");
48832
- if (!existsSync79(settingsPath)) {
48831
+ const settingsPath = join79(agentDir, ".claude", "settings.json");
48832
+ if (!existsSync80(settingsPath)) {
48833
48833
  return { scriptPath, settingsPath, installed };
48834
48834
  }
48835
- const raw = readFileSync67(settingsPath, "utf-8");
48835
+ const raw = readFileSync68(settingsPath, "utf-8");
48836
48836
  let parsed;
48837
48837
  try {
48838
48838
  parsed = JSON.parse(raw);
@@ -48868,7 +48868,7 @@ function installUpdatePromptHook(agentDir) {
48868
48868
  if (mutated) {
48869
48869
  hooks.UserPromptSubmit = list2;
48870
48870
  parsed.hooks = hooks;
48871
- writeFileSync36(settingsPath, JSON.stringify(parsed, null, 2) + `
48871
+ writeFileSync37(settingsPath, JSON.stringify(parsed, null, 2) + `
48872
48872
  `, { mode: 384 });
48873
48873
  installed = true;
48874
48874
  } else if (!alreadyCorrect) {
@@ -48877,7 +48877,7 @@ function installUpdatePromptHook(agentDir) {
48877
48877
  });
48878
48878
  hooks.UserPromptSubmit = list2;
48879
48879
  parsed.hooks = hooks;
48880
- writeFileSync36(settingsPath, JSON.stringify(parsed, null, 2) + `
48880
+ writeFileSync37(settingsPath, JSON.stringify(parsed, null, 2) + `
48881
48881
  `, { mode: 384 });
48882
48882
  installed = true;
48883
48883
  }
@@ -49169,8 +49169,8 @@ __export(exports_voice_sidecar_token, {
49169
49169
  VOICE_SIDECAR_TOKEN_ENV: () => VOICE_SIDECAR_TOKEN_ENV
49170
49170
  });
49171
49171
  import { randomBytes as randomBytes14 } from "node:crypto";
49172
- import { chmodSync as chmodSync11, chownSync as chownSync7, existsSync as existsSync81, mkdirSync as mkdirSync45, readFileSync as readFileSync68, rmSync as rmSync17, writeFileSync as writeFileSync37 } from "node:fs";
49173
- import { dirname as dirname27 } from "node:path";
49172
+ import { chmodSync as chmodSync11, chownSync as chownSync7, existsSync as existsSync82, mkdirSync as mkdirSync46, readFileSync as readFileSync69, rmSync as rmSync17, writeFileSync as writeFileSync38 } from "node:fs";
49173
+ import { dirname as dirname28 } from "node:path";
49174
49174
  async function defaultResolveOrSeedToken(home2, writeErr) {
49175
49175
  const [{ getViaBrokerStructured: getViaBrokerStructured2, putViaBroker: putViaBroker2 }, { resolveOperatorVaultPassphrase }] = await Promise.all([
49176
49176
  Promise.resolve().then(() => (init_client(), exports_client)),
@@ -49204,8 +49204,8 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
49204
49204
  const envPath = composeEnvPath(composePath);
49205
49205
  if (engine !== "local") {
49206
49206
  try {
49207
- if (existsSync81(envPath)) {
49208
- const body = readFileSync68(envPath, "utf-8");
49207
+ if (existsSync82(envPath)) {
49208
+ const body = readFileSync69(envPath, "utf-8");
49209
49209
  if (body.includes(`${VOICE_SIDECAR_TOKEN_ENV}=`))
49210
49210
  rmSync17(envPath);
49211
49211
  }
@@ -49224,11 +49224,11 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
49224
49224
  if (!token)
49225
49225
  return;
49226
49226
  try {
49227
- mkdirSync45(dirname27(envPath), { recursive: true });
49227
+ mkdirSync46(dirname28(envPath), { recursive: true });
49228
49228
  let body = "";
49229
49229
  try {
49230
- if (existsSync81(envPath))
49231
- body = readFileSync68(envPath, "utf-8");
49230
+ if (existsSync82(envPath))
49231
+ body = readFileSync69(envPath, "utf-8");
49232
49232
  } catch {}
49233
49233
  const line = `${VOICE_SIDECAR_TOKEN_ENV}=${token}`;
49234
49234
  const keyRe = new RegExp(`^${VOICE_SIDECAR_TOKEN_ENV}=.*$`, "m");
@@ -49236,7 +49236,7 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
49236
49236
  `) ? body + `
49237
49237
  ` : body) + line + `
49238
49238
  `;
49239
- writeFileSync37(envPath, next, {
49239
+ writeFileSync38(envPath, next, {
49240
49240
  encoding: "utf-8",
49241
49241
  mode: 384
49242
49242
  });
@@ -49286,12 +49286,12 @@ __export(exports_apply, {
49286
49286
  DEFAULT_COMPOSE_PATH: () => DEFAULT_COMPOSE_PATH2,
49287
49287
  COMPOSE_PROJECT: () => COMPOSE_PROJECT2
49288
49288
  });
49289
- import { accessSync as accessSync3, chownSync as chownSync8, constants as fsConstants6, copyFileSync as copyFileSync12, existsSync as existsSync82, mkdirSync as mkdirSync46, readFileSync as readFileSync69, readdirSync as readdirSync28, renameSync as renameSync18, writeFileSync as writeFileSync38 } from "node:fs";
49289
+ import { accessSync as accessSync3, chownSync as chownSync8, constants as fsConstants6, copyFileSync as copyFileSync12, existsSync as existsSync83, mkdirSync as mkdirSync47, readFileSync as readFileSync70, readdirSync as readdirSync28, renameSync as renameSync18, writeFileSync as writeFileSync39 } from "node:fs";
49290
49290
  import { mkdir as mkdir2 } from "node:fs/promises";
49291
49291
  import { spawnSync as childSpawnSync } from "node:child_process";
49292
49292
  import readline from "node:readline";
49293
- import { dirname as dirname28, join as join80, resolve as resolve50 } from "node:path";
49294
- import { homedir as homedir47 } from "node:os";
49293
+ import { dirname as dirname29, join as join81, resolve as resolve50 } from "node:path";
49294
+ import { homedir as homedir48 } from "node:os";
49295
49295
  import { execFileSync as execFileSync25 } from "node:child_process";
49296
49296
  function effectiveLiteLLMEnabled(config, agentResolvedLitellm) {
49297
49297
  return agentResolvedLitellm?.enabled ?? config.litellm?.enabled ?? false;
@@ -49302,7 +49302,7 @@ async function resolveOperatorVaultPassphrase(home2) {
49302
49302
  return envPass;
49303
49303
  try {
49304
49304
  const { readAutoUnlockFile: readAutoUnlockFile2 } = await Promise.resolve().then(() => (init_auto_unlock(), exports_auto_unlock));
49305
- const blobPath = join80(home2, ".switchroom", "vault-auto-unlock");
49305
+ const blobPath = join81(home2, ".switchroom", "vault-auto-unlock");
49306
49306
  const pass = readAutoUnlockFile2(blobPath);
49307
49307
  return pass && pass.length > 0 ? pass : null;
49308
49308
  } catch {
@@ -49329,7 +49329,7 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
49329
49329
  Promise.resolve().then(() => (init_provision(), exports_provision)),
49330
49330
  Promise.resolve().then(() => (init_telegram_yaml(), exports_telegram_yaml))
49331
49331
  ]);
49332
- const passphrase = await resolveOperatorVaultPassphrase(ctx.home ?? homedir47());
49332
+ const passphrase = await resolveOperatorVaultPassphrase(ctx.home ?? homedir48());
49333
49333
  if (passphrase === null) {
49334
49334
  const { getViaBrokerStructured: getViaBrokerStructured3 } = await Promise.resolve().then(() => (init_client(), exports_client));
49335
49335
  const alreadyProvisioned = [];
@@ -49390,9 +49390,9 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
49390
49390
  const oauthAccount = config.auth?.active;
49391
49391
  let pendingConfigEdits = false;
49392
49392
  let configText = null;
49393
- if (switchroomConfigPath && existsSync82(switchroomConfigPath)) {
49393
+ if (switchroomConfigPath && existsSync83(switchroomConfigPath)) {
49394
49394
  try {
49395
- configText = readFileSync69(switchroomConfigPath, "utf-8");
49395
+ configText = readFileSync70(switchroomConfigPath, "utf-8");
49396
49396
  } catch (err) {
49397
49397
  ctx.writeErr(source_default.yellow(` ! litellm: could not read config for ACL grants (${err.message}); keys will be provisioned but agents may lack read-ACL.
49398
49398
  `));
@@ -49515,7 +49515,7 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
49515
49515
  }
49516
49516
  if (pendingConfigEdits && configText !== null && switchroomConfigPath) {
49517
49517
  try {
49518
- writeFileSync38(switchroomConfigPath, configText, "utf-8");
49518
+ writeFileSync39(switchroomConfigPath, configText, "utf-8");
49519
49519
  writeOut(source_default.gray(` ~ litellm: granted read-ACL on per-agent keys (updated ${switchroomConfigPath})
49520
49520
  `));
49521
49521
  } catch (err) {
@@ -49572,12 +49572,12 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
49572
49572
  function resolveVaultBindMountDir(homeDir, ctx) {
49573
49573
  const isCustomPath = ctx.migrationKind === "custom-path-skipped";
49574
49574
  if (isCustomPath && ctx.customVaultPath) {
49575
- return dirname28(ctx.customVaultPath);
49575
+ return dirname29(ctx.customVaultPath);
49576
49576
  }
49577
- return join80(homeDir, ".switchroom", "vault");
49577
+ return join81(homeDir, ".switchroom", "vault");
49578
49578
  }
49579
49579
  function inspectVaultBindMountDir(vaultDir) {
49580
- if (!existsSync82(vaultDir))
49580
+ if (!existsSync83(vaultDir))
49581
49581
  return { kind: "missing" };
49582
49582
  const entries = readdirSync28(vaultDir);
49583
49583
  const unknown = [];
@@ -49605,61 +49605,61 @@ function hasVaultRefs(value) {
49605
49605
  async function ensureHostMountSources(config) {
49606
49606
  const home2 = resolveHostHomeForCompose();
49607
49607
  const dirs = [
49608
- join80(home2, ".switchroom", "approvals"),
49609
- join80(home2, ".switchroom", "scheduler"),
49610
- join80(home2, ".switchroom", "logs"),
49611
- join80(home2, ".switchroom", "compose"),
49612
- join80(home2, ".switchroom", "broker-operator")
49608
+ join81(home2, ".switchroom", "approvals"),
49609
+ join81(home2, ".switchroom", "scheduler"),
49610
+ join81(home2, ".switchroom", "logs"),
49611
+ join81(home2, ".switchroom", "compose"),
49612
+ join81(home2, ".switchroom", "broker-operator")
49613
49613
  ];
49614
49614
  for (const name of Object.keys(config.agents)) {
49615
- dirs.push(join80(home2, ".switchroom", "agents", name));
49616
- dirs.push(join80(home2, ".switchroom", "logs", name));
49617
- dirs.push(join80(home2, ".claude", "projects", name));
49618
- dirs.push(join80(home2, ".switchroom", "audit", name));
49619
- if (existsSync82(join80(home2, ".switchroom-config"))) {
49620
- dirs.push(join80(home2, ".switchroom-config", "agents", name, "personal-skills"));
49615
+ dirs.push(join81(home2, ".switchroom", "agents", name));
49616
+ dirs.push(join81(home2, ".switchroom", "logs", name));
49617
+ dirs.push(join81(home2, ".claude", "projects", name));
49618
+ dirs.push(join81(home2, ".switchroom", "audit", name));
49619
+ if (existsSync83(join81(home2, ".switchroom-config"))) {
49620
+ dirs.push(join81(home2, ".switchroom-config", "agents", name, "personal-skills"));
49621
49621
  }
49622
49622
  }
49623
49623
  for (const dir of dirs) {
49624
49624
  await mkdir2(dir, { recursive: true });
49625
49625
  }
49626
- const autoUnlockPath = join80(home2, ".switchroom", "vault-auto-unlock");
49627
- if (!existsSync82(autoUnlockPath)) {
49628
- writeFileSync38(autoUnlockPath, "", { mode: 384 });
49626
+ const autoUnlockPath = join81(home2, ".switchroom", "vault-auto-unlock");
49627
+ if (!existsSync83(autoUnlockPath)) {
49628
+ writeFileSync39(autoUnlockPath, "", { mode: 384 });
49629
49629
  }
49630
- const auditLogPath = join80(home2, ".switchroom", "vault-audit.log");
49631
- if (!existsSync82(auditLogPath)) {
49632
- writeFileSync38(auditLogPath, "", { mode: 420 });
49630
+ const auditLogPath = join81(home2, ".switchroom", "vault-audit.log");
49631
+ if (!existsSync83(auditLogPath)) {
49632
+ writeFileSync39(auditLogPath, "", { mode: 420 });
49633
49633
  }
49634
- const grantsDbPath = join80(home2, ".switchroom", "vault-grants.db");
49635
- if (!existsSync82(grantsDbPath)) {
49636
- writeFileSync38(grantsDbPath, "", { mode: 384 });
49634
+ const grantsDbPath = join81(home2, ".switchroom", "vault-grants.db");
49635
+ if (!existsSync83(grantsDbPath)) {
49636
+ writeFileSync39(grantsDbPath, "", { mode: 384 });
49637
49637
  }
49638
- const hostdAuditLogPath = join80(home2, ".switchroom", "host-control-audit.log");
49639
- if (!existsSync82(hostdAuditLogPath)) {
49640
- writeFileSync38(hostdAuditLogPath, "", { mode: 420 });
49638
+ const hostdAuditLogPath = join81(home2, ".switchroom", "host-control-audit.log");
49639
+ if (!existsSync83(hostdAuditLogPath)) {
49640
+ writeFileSync39(hostdAuditLogPath, "", { mode: 420 });
49641
49641
  }
49642
49642
  for (const name of Object.keys(config.agents)) {
49643
- const tokenPath = join80(home2, ".switchroom", "agents", name, ".vault-token");
49644
- if (!existsSync82(tokenPath)) {
49645
- writeFileSync38(tokenPath, "", { mode: 384 });
49643
+ const tokenPath = join81(home2, ".switchroom", "agents", name, ".vault-token");
49644
+ if (!existsSync83(tokenPath)) {
49645
+ writeFileSync39(tokenPath, "", { mode: 384 });
49646
49646
  }
49647
49647
  try {
49648
49648
  const uid = allocateAgentUid(name);
49649
49649
  chownSync8(tokenPath, uid, uid);
49650
49650
  } catch {}
49651
49651
  }
49652
- const fleetDir = join80(home2, ".switchroom", "fleet");
49652
+ const fleetDir = join81(home2, ".switchroom", "fleet");
49653
49653
  await mkdir2(fleetDir, { recursive: true });
49654
- const invariantsPath = join80(fleetDir, "switchroom-invariants.md");
49654
+ const invariantsPath = join81(fleetDir, "switchroom-invariants.md");
49655
49655
  const invariantsCanonical = renderFleetInvariants();
49656
- const invariantsCurrent = existsSync82(invariantsPath) ? readFileSync69(invariantsPath, "utf-8") : null;
49656
+ const invariantsCurrent = existsSync83(invariantsPath) ? readFileSync70(invariantsPath, "utf-8") : null;
49657
49657
  if (invariantsCurrent !== invariantsCanonical) {
49658
- writeFileSync38(invariantsPath, invariantsCanonical, { mode: 420 });
49658
+ writeFileSync39(invariantsPath, invariantsCanonical, { mode: 420 });
49659
49659
  }
49660
- const fleetClaudePath = join80(fleetDir, "CLAUDE.md");
49661
- if (!existsSync82(fleetClaudePath)) {
49662
- writeFileSync38(fleetClaudePath, [
49660
+ const fleetClaudePath = join81(fleetDir, "CLAUDE.md");
49661
+ if (!existsSync83(fleetClaudePath)) {
49662
+ writeFileSync39(fleetClaudePath, [
49663
49663
  "# Switchroom fleet defaults",
49664
49664
  "",
49665
49665
  "Operator-owned fleet brain. Every agent reads this via",
@@ -49697,7 +49697,7 @@ function isInAgentContainer(vaultPresent, composeV2Present, env2 = process.env)
49697
49697
  function runApplyPreflight(config, opts = {}) {
49698
49698
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
49699
49699
  const detect = opts.detectComposeV2 ?? detectComposeV2;
49700
- const vaultMissing = hasVaultRefs(config) && !existsSync82(vaultPath);
49700
+ const vaultMissing = hasVaultRefs(config) && !existsSync83(vaultPath);
49701
49701
  const composeErr = detect();
49702
49702
  if ((vaultMissing || composeErr) && isInAgentContainer(!vaultMissing, composeErr === null)) {
49703
49703
  throw new Error(IN_AGENT_CONTAINER_APPLY_MSG);
@@ -49711,7 +49711,7 @@ function runApplyPreflight(config, opts = {}) {
49711
49711
  detectAndReportLegacyGdriveSlots(vaultPath);
49712
49712
  }
49713
49713
  function detectAndReportLegacyGdriveSlots(vaultPath) {
49714
- if (!existsSync82(vaultPath))
49714
+ if (!existsSync83(vaultPath))
49715
49715
  return;
49716
49716
  const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
49717
49717
  if (!passphrase)
@@ -49750,18 +49750,18 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
49750
49750
  `));
49751
49751
  }
49752
49752
  }
49753
- function writeInstallTypeCache(homeDir = homedir47()) {
49753
+ function writeInstallTypeCache(homeDir = homedir48()) {
49754
49754
  const ctx = detectInstallType();
49755
- const dir = join80(homeDir, ".switchroom");
49756
- const out = join80(dir, "install-type.json");
49755
+ const dir = join81(homeDir, ".switchroom");
49756
+ const out = join81(dir, "install-type.json");
49757
49757
  const tmp = `${out}.tmp`;
49758
- mkdirSync46(dir, { recursive: true });
49758
+ mkdirSync47(dir, { recursive: true });
49759
49759
  const payload = {
49760
49760
  install_type: ctx.install_type,
49761
49761
  detected_at: new Date().toISOString(),
49762
49762
  source_paths: ctx.source_paths
49763
49763
  };
49764
- writeFileSync38(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
49764
+ writeFileSync39(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
49765
49765
  renameSync18(tmp, out);
49766
49766
  return out;
49767
49767
  }
@@ -49820,17 +49820,17 @@ Applying switchroom config...
49820
49820
  writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
49821
49821
  `));
49822
49822
  try {
49823
- installUpdatePromptHook(join80(agentsDir, name));
49823
+ installUpdatePromptHook(join81(agentsDir, name));
49824
49824
  } catch (hookErr) {
49825
49825
  writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
49826
49826
  `));
49827
49827
  }
49828
- await refreshAgentConnectionHealth(config, name, join80(agentsDir, name), {
49828
+ await refreshAgentConnectionHealth(config, name, join81(agentsDir, name), {
49829
49829
  vaultAclReader: connHealthVaultAclReader
49830
49830
  });
49831
49831
  try {
49832
49832
  const uid = allocateAgentUid(name);
49833
- alignAgentUid(name, join80(agentsDir, name), uid, {
49833
+ alignAgentUid(name, join81(agentsDir, name), uid, {
49834
49834
  confirm: !options.nonInteractive,
49835
49835
  writeOut
49836
49836
  });
@@ -49867,7 +49867,7 @@ Applying switchroom config...
49867
49867
  writeOut,
49868
49868
  writeErr,
49869
49869
  failures,
49870
- home: homedir47()
49870
+ home: homedir48()
49871
49871
  });
49872
49872
  }
49873
49873
  await ensureHostMountSources(config);
@@ -49875,7 +49875,7 @@ Applying switchroom config...
49875
49875
  for (const name of agentNames) {
49876
49876
  try {
49877
49877
  const uid = allocateAgentUid(name);
49878
- alignAgentUid(name, join80(agentsDir, name), uid, {
49878
+ alignAgentUid(name, join81(agentsDir, name), uid, {
49879
49879
  confirm: !options.nonInteractive,
49880
49880
  writeOut
49881
49881
  });
@@ -49889,7 +49889,7 @@ Applying switchroom config...
49889
49889
  }
49890
49890
  const vaultPathConfigured = config.vault?.path;
49891
49891
  const customVaultPath = vaultPathConfigured ? resolvePath(vaultPathConfigured) : undefined;
49892
- const migrationResult = migrateVaultLayout(homedir47(), {
49892
+ const migrationResult = migrateVaultLayout(homedir48(), {
49893
49893
  customVaultPath
49894
49894
  });
49895
49895
  switch (migrationResult.kind) {
@@ -49915,7 +49915,7 @@ Applying switchroom config...
49915
49915
  writeErr(formatDivergentRecoveryMessage(migrationResult.details));
49916
49916
  process.exit(4);
49917
49917
  }
49918
- const postMigrationInspect = inspectVaultLayout(homedir47());
49918
+ const postMigrationInspect = inspectVaultLayout(homedir48());
49919
49919
  const acceptable = [
49920
49920
  "no-vault",
49921
49921
  "already-migrated",
@@ -49930,7 +49930,7 @@ Expected one of: ${acceptable.join(", ")}
49930
49930
  `));
49931
49931
  process.exit(5);
49932
49932
  }
49933
- const vaultDir = resolveVaultBindMountDir(homedir47(), {
49933
+ const vaultDir = resolveVaultBindMountDir(homedir48(), {
49934
49934
  migrationKind: migrationResult.kind,
49935
49935
  customVaultPath
49936
49936
  });
@@ -49961,7 +49961,7 @@ vault.enc.lock (PID-file flock from saveVault), and
49961
49961
  });
49962
49962
  if (!skipScaffold) {
49963
49963
  const { provisionVoiceSidecarToken: provisionVoiceSidecarToken2 } = await Promise.resolve().then(() => (init_voice_sidecar_token(), exports_voice_sidecar_token));
49964
- await provisionVoiceSidecarToken2(composePath, homedir47(), {
49964
+ await provisionVoiceSidecarToken2(composePath, homedir48(), {
49965
49965
  writeOut,
49966
49966
  writeErr,
49967
49967
  operatorUid
@@ -49977,7 +49977,7 @@ Wrote `) + displayComposePath + source_default.gray(` (${composeBytes} bytes)
49977
49977
  writeOut(source_default.gray(` (If pull returns 401, login to ghcr.io first: see docs/operators/install.md#ghcr-auth)
49978
49978
  `));
49979
49979
  if (process.geteuid?.() === 0 && operatorUid !== undefined) {
49980
- const restored = restoreOperatorOwnership(homedir47(), operatorUid);
49980
+ const restored = restoreOperatorOwnership(homedir48(), operatorUid);
49981
49981
  if (restored.length > 0) {
49982
49982
  writeOut(source_default.gray(` Restored operator ownership of ${restored.length} ~/.switchroom path(s)
49983
49983
  `));
@@ -50082,7 +50082,7 @@ Dry-run: validating switchroom config (no changes will be written)...
50082
50082
  }
50083
50083
  }
50084
50084
  try {
50085
- const probe2 = await probeVaultProvisioning(config, agentNames, homedir47());
50085
+ const probe2 = await probeVaultProvisioning(config, agentNames, homedir48());
50086
50086
  if (probe2.alreadyProvisioned.length > 0) {
50087
50087
  info.push(`litellm: ${probe2.alreadyProvisioned.length} agent(s) already provisioned (${probe2.alreadyProvisioned.join(", ")}).`);
50088
50088
  }
@@ -50213,18 +50213,18 @@ function copyExampleConfig2(name) {
50213
50213
  throw new Error(`Invalid example name: ${name} (must match /^[a-z0-9_-]+$/)`);
50214
50214
  }
50215
50215
  const dest = resolve50(process.cwd(), "switchroom.yaml");
50216
- if (existsSync82(dest)) {
50216
+ if (existsSync83(dest)) {
50217
50217
  console.error(source_default.yellow("switchroom.yaml already exists \u2014 skipping example copy"));
50218
50218
  return;
50219
50219
  }
50220
50220
  const embedded = EMBEDDED_EXAMPLES[name];
50221
50221
  if (embedded !== undefined) {
50222
- writeFileSync38(dest, embedded, { encoding: "utf8" });
50222
+ writeFileSync39(dest, embedded, { encoding: "utf8" });
50223
50223
  console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
50224
50224
  return;
50225
50225
  }
50226
50226
  const exampleFile = resolve50(import.meta.dirname, `../../examples/${name}.yaml`);
50227
- if (!existsSync82(exampleFile)) {
50227
+ if (!existsSync83(exampleFile)) {
50228
50228
  throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
50229
50229
  }
50230
50230
  copyFileSync12(exampleFile, dest);
@@ -50235,8 +50235,8 @@ function findUnwritableAgentDirs(config, opts) {
50235
50235
  const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
50236
50236
  const unwritable = [];
50237
50237
  for (const name of targets) {
50238
- const startSh = join80(agentsDir, name, "start.sh");
50239
- if (!existsSync82(startSh))
50238
+ const startSh = join81(agentsDir, name, "start.sh");
50239
+ if (!existsSync83(startSh))
50240
50240
  continue;
50241
50241
  try {
50242
50242
  accessSync3(startSh, fsConstants6.W_OK);
@@ -50437,7 +50437,7 @@ var init_apply = __esm(() => {
50437
50437
  switchroom: switchroom_default,
50438
50438
  minimal: minimal_default
50439
50439
  };
50440
- DEFAULT_COMPOSE_PATH2 = join80(homedir47(), ".switchroom", "compose", "docker-compose.yml");
50440
+ DEFAULT_COMPOSE_PATH2 = join81(homedir48(), ".switchroom", "compose", "docker-compose.yml");
50441
50441
  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.
50442
50442
  ` + "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.";
50443
50443
  SELF_ELEVATE_PRESERVED_ENV = [
@@ -63884,7 +63884,7 @@ __export(exports_server2, {
63884
63884
  TOOLS: () => TOOLS2
63885
63885
  });
63886
63886
  import { randomBytes as randomBytes16 } from "node:crypto";
63887
- import { existsSync as existsSync91, readFileSync as readFileSync78 } from "node:fs";
63887
+ import { existsSync as existsSync92, readFileSync as readFileSync79 } from "node:fs";
63888
63888
  function selfSocketPath() {
63889
63889
  return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
63890
63890
  }
@@ -63902,7 +63902,7 @@ async function dispatchTool2(name, args) {
63902
63902
  return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
63903
63903
  }
63904
63904
  const sockPath = selfSocketPath();
63905
- if (!existsSync91(sockPath)) {
63905
+ if (!existsSync92(sockPath)) {
63906
63906
  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.`);
63907
63907
  }
63908
63908
  let req;
@@ -64090,18 +64090,18 @@ function resolveAuditLogPath() {
64090
64090
  if (process.env.HOSTD_AUDIT_LOG_PATH)
64091
64091
  return process.env.HOSTD_AUDIT_LOG_PATH;
64092
64092
  const bindMounted = "/host-home/.switchroom/host-control-audit.log";
64093
- if (existsSync91(bindMounted))
64093
+ if (existsSync92(bindMounted))
64094
64094
  return bindMounted;
64095
64095
  return defaultAuditLogPath2();
64096
64096
  }
64097
64097
  function getLastUpdateApplyStatus() {
64098
64098
  const path8 = resolveAuditLogPath();
64099
- if (!existsSync91(path8)) {
64099
+ if (!existsSync92(path8)) {
64100
64100
  return errorText2(`get_status: audit log not found at ${path8}. No update_apply has run yet?`);
64101
64101
  }
64102
64102
  let raw;
64103
64103
  try {
64104
- raw = readFileSync78(path8, "utf-8");
64104
+ raw = readFileSync79(path8, "utf-8");
64105
64105
  } catch (err2) {
64106
64106
  return errorText2(`get_status: failed to read audit log at ${path8}: ${err2.message}`);
64107
64107
  }
@@ -64768,20 +64768,20 @@ __export(exports_scan, {
64768
64768
  ledgerPathForBase: () => ledgerPathForBase
64769
64769
  });
64770
64770
  import {
64771
- readFileSync as readFileSync80,
64771
+ readFileSync as readFileSync81,
64772
64772
  readdirSync as readdirSync36,
64773
- existsSync as existsSync94,
64774
- mkdirSync as mkdirSync53,
64775
- writeFileSync as writeFileSync44
64773
+ existsSync as existsSync95,
64774
+ mkdirSync as mkdirSync54,
64775
+ writeFileSync as writeFileSync45
64776
64776
  } from "node:fs";
64777
- import { resolve as resolve55, dirname as dirname31 } from "node:path";
64778
- import { homedir as homedir56 } from "node:os";
64779
- function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir56()) {
64777
+ import { resolve as resolve55, dirname as dirname32 } from "node:path";
64778
+ import { homedir as homedir57 } from "node:os";
64779
+ function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir57()) {
64780
64780
  return resolve55(home2, ".switchroom");
64781
64781
  }
64782
64782
  function listAgents(base) {
64783
64783
  const dir = resolve55(base, "agents");
64784
- if (!existsSync94(dir))
64784
+ if (!existsSync95(dir))
64785
64785
  return [];
64786
64786
  try {
64787
64787
  return readdirSync36(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
@@ -64810,16 +64810,16 @@ function runScan(opts = {}) {
64810
64810
  let gwText = "";
64811
64811
  let sawArtifact = false;
64812
64812
  try {
64813
- if (existsSync94(turnsPath)) {
64814
- turnsText = readFileSync80(turnsPath, "utf-8");
64813
+ if (existsSync95(turnsPath)) {
64814
+ turnsText = readFileSync81(turnsPath, "utf-8");
64815
64815
  sawArtifact = true;
64816
64816
  }
64817
64817
  } catch (e) {
64818
64818
  log(`fleet-health: WARN skipping ${agent} turns.jsonl unreadable: ${String(e)}`);
64819
64819
  }
64820
64820
  try {
64821
- if (existsSync94(gwPath)) {
64822
- gwText = readFileSync80(gwPath, "utf-8");
64821
+ if (existsSync95(gwPath)) {
64822
+ gwText = readFileSync81(gwPath, "utf-8");
64823
64823
  sawArtifact = true;
64824
64824
  }
64825
64825
  } catch (e) {
@@ -64857,20 +64857,20 @@ function runScan(opts = {}) {
64857
64857
  function readLedgerIfPresent(base) {
64858
64858
  const path8 = ledgerPathForBase(base);
64859
64859
  try {
64860
- if (!existsSync94(path8))
64860
+ if (!existsSync95(path8))
64861
64861
  return null;
64862
- return JSON.parse(readFileSync80(path8, "utf-8"));
64862
+ return JSON.parse(readFileSync81(path8, "utf-8"));
64863
64863
  } catch {
64864
64864
  return null;
64865
64865
  }
64866
64866
  }
64867
64867
  function ledgerPathForBase(base) {
64868
- return fleetHealthLedgerPath(dirname31(base));
64868
+ return fleetHealthLedgerPath(dirname32(base));
64869
64869
  }
64870
64870
  function writeLedger(base, ledger) {
64871
64871
  const path8 = ledgerPathForBase(base);
64872
- mkdirSync53(dirname31(path8), { recursive: true });
64873
- writeFileSync44(path8, JSON.stringify(ledger, null, 2) + `
64872
+ mkdirSync54(dirname32(path8), { recursive: true });
64873
+ writeFileSync45(path8, JSON.stringify(ledger, null, 2) + `
64874
64874
  `, "utf-8");
64875
64875
  return path8;
64876
64876
  }
@@ -64889,8 +64889,8 @@ import { existsSync, readFileSync } from "node:fs";
64889
64889
  import { dirname, join } from "node:path";
64890
64890
 
64891
64891
  // src/build-info.ts
64892
- var VERSION = "0.18.7";
64893
- var COMMIT_SHA = "21478348";
64892
+ var VERSION = "0.18.8";
64893
+ var COMMIT_SHA = "9255fe22";
64894
64894
 
64895
64895
  // src/cli/resolve-version.ts
64896
64896
  function readPackageVersion() {
@@ -79126,6 +79126,8 @@ Demoting memory ${source_default.cyan(memoryId)}`));
79126
79126
 
79127
79127
  // src/cli/web.ts
79128
79128
  init_source();
79129
+ import { join as join50 } from "node:path";
79130
+ import { homedir as homedir28 } from "node:os";
79129
79131
 
79130
79132
  // src/web/server.ts
79131
79133
  init_merge();
@@ -83126,10 +83128,100 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
83126
83128
 
83127
83129
  // src/cli/web.ts
83128
83130
  init_helpers();
83131
+ init_loader();
83132
+
83133
+ // src/web/startup-guard.ts
83134
+ import { existsSync as existsSync56, readFileSync as readFileSync51, writeFileSync as writeFileSync26, mkdirSync as mkdirSync31, statSync as statSync24, unlinkSync as unlinkSync13 } from "node:fs";
83135
+ import { dirname as dirname16 } from "node:path";
83136
+ function detectConfigMountFault(configPath, deps = {}) {
83137
+ const stat = deps.stat ?? ((p) => statSync24(p));
83138
+ let st;
83139
+ try {
83140
+ st = stat(configPath);
83141
+ } catch {
83142
+ return null;
83143
+ }
83144
+ if (!st.isDirectory())
83145
+ return null;
83146
+ return {
83147
+ kind: "directory-inode",
83148
+ path: configPath,
83149
+ message: `switchroom-web: the config bind source ${configPath} resolved to a DIRECTORY, ` + `not a file \u2014 reads fail with EISDIR and the server cannot start.
83150
+
83151
+ ` + `Cause: the single-file bind mount was (re)created while the host source was ` + `momentarily absent, so Docker auto-created an empty directory, OR a stale ` + `directory inode survived a plain \`docker restart\` inside the container's mount ` + `namespace (a restart does NOT re-resolve a single-file bind).
83152
+
83153
+ ` + `Remedy: RECREATE the container (not restart) so the bind re-resolves to the file:
83154
+ ` + ` docker compose -p switchroom-web -f ~/.switchroom/web/docker-compose.yml up -d --force-recreate
83155
+ ` + `and if the host source itself is a directory, remove it and restore the file first.`
83156
+ };
83157
+ }
83158
+ function nextCrashBackoff(prior, now, options = {}) {
83159
+ const baseMs = options.baseMs ?? 1000;
83160
+ const maxMs = options.maxMs ?? 60000;
83161
+ const resetAfterMs = options.resetAfterMs ?? 300000;
83162
+ const withinWindow = prior !== null && now - prior.lastTs <= resetAfterMs && now >= prior.lastTs;
83163
+ const count = withinWindow ? prior.count + 1 : 1;
83164
+ const exp = Math.min(count - 1, 30);
83165
+ const delayMs = count <= 1 ? 0 : Math.min(baseMs * 2 ** (exp - 1), maxMs);
83166
+ return { state: { count, lastTs: now }, delayMs };
83167
+ }
83168
+ function readCrashState(statePath) {
83169
+ try {
83170
+ if (!existsSync56(statePath))
83171
+ return null;
83172
+ const raw = JSON.parse(readFileSync51(statePath, "utf-8"));
83173
+ if (typeof raw.count !== "number" || typeof raw.lastTs !== "number")
83174
+ return null;
83175
+ return { count: raw.count, lastTs: raw.lastTs };
83176
+ } catch {
83177
+ return null;
83178
+ }
83179
+ }
83180
+ function writeCrashState(statePath, state) {
83181
+ try {
83182
+ mkdirSync31(dirname16(statePath), { recursive: true });
83183
+ writeFileSync26(statePath, JSON.stringify(state), { mode: 384 });
83184
+ } catch {}
83185
+ }
83186
+ function clearCrashState(statePath) {
83187
+ try {
83188
+ if (existsSync56(statePath))
83189
+ unlinkSync13(statePath);
83190
+ } catch {}
83191
+ }
83192
+
83193
+ // src/cli/web.ts
83129
83194
  init_posthog();
83195
+ async function handleWebStartupFailure(err, configPath, stateFile) {
83196
+ if (err instanceof ConfigError && configPath) {
83197
+ const fault = detectConfigMountFault(configPath);
83198
+ if (fault) {
83199
+ console.error(source_default.red(fault.message));
83200
+ }
83201
+ }
83202
+ const { state, delayMs } = nextCrashBackoff(readCrashState(stateFile), Date.now());
83203
+ writeCrashState(stateFile, state);
83204
+ if (delayMs > 0) {
83205
+ console.error(source_default.yellow(`switchroom-web: startup has failed ${state.count}\u00d7 in a row \u2014 backing off ` + `${Math.round(delayMs / 1000)}s before exit so \`restart: always\` doesn't ` + `crash-loop the host (issue #2915).`));
83206
+ await new Promise((r) => setTimeout(r, delayMs));
83207
+ }
83208
+ throw err;
83209
+ }
83210
+ function webCrashStatePath() {
83211
+ return join50(homedir28(), ".switchroom", "web", ".crashloop-state.json");
83212
+ }
83130
83213
  function registerWebCommand(program3) {
83131
83214
  program3.command("web").description("Start the web dashboard for monitoring agents").option("-p, --port <port>", "Port to listen on", "8080").option("-b, --bind <host>", "Host/IP to bind to (default: 127.0.0.1, localhost-only)", "127.0.0.1").action(withConfigError(async (opts) => {
83132
- const config = getConfig(program3);
83215
+ const configPath = getConfigPath(program3);
83216
+ const stateFile = webCrashStatePath();
83217
+ let config;
83218
+ try {
83219
+ config = getConfig(program3);
83220
+ } catch (err) {
83221
+ await handleWebStartupFailure(err, configPath, stateFile);
83222
+ throw err;
83223
+ }
83224
+ clearCrashState(stateFile);
83133
83225
  const port = parseInt(opts.port, 10);
83134
83226
  const hostname = opts.bind;
83135
83227
  if (isNaN(port) || port < 1 || port > 65535) {
@@ -83181,8 +83273,8 @@ Starting Switchroom dashboard...
83181
83273
  init_source();
83182
83274
  init_loader();
83183
83275
  init_scaffold();
83184
- import { existsSync as existsSync56, copyFileSync as copyFileSync9, readFileSync as readFileSync51, writeFileSync as writeFileSync26, mkdirSync as mkdirSync31 } from "node:fs";
83185
- import { resolve as resolve33, dirname as dirname16 } from "node:path";
83276
+ import { existsSync as existsSync57, copyFileSync as copyFileSync9, readFileSync as readFileSync52, writeFileSync as writeFileSync27, mkdirSync as mkdirSync32 } from "node:fs";
83277
+ import { resolve as resolve33, dirname as dirname17 } from "node:path";
83186
83278
  init_state();
83187
83279
  init_vault();
83188
83280
  init_manager();
@@ -83452,7 +83544,7 @@ async function stepConfigFile(configPath, nonInteractive) {
83452
83544
  existingConfig = null;
83453
83545
  }
83454
83546
  }
83455
- if (existingConfig && existsSync56(existingConfig)) {
83547
+ if (existingConfig && existsSync57(existingConfig)) {
83456
83548
  if (!nonInteractive) {
83457
83549
  const useExisting = await askYesNo(` Found ${source_default.cyan(existingConfig)}. Use it?`, true);
83458
83550
  if (!useExisting) {
@@ -83484,10 +83576,10 @@ async function copyExampleConfig(nonInteractive) {
83484
83576
  }
83485
83577
  const srcFile = resolve33(examplesDir, `${choice}.yaml`);
83486
83578
  const destFile = resolvePath("~/.switchroom/switchroom.yaml");
83487
- if (!existsSync56(srcFile)) {
83579
+ if (!existsSync57(srcFile)) {
83488
83580
  throw new ConfigError(`Example config not found: ${choice}.yaml`);
83489
83581
  }
83490
- mkdirSync31(dirname16(destFile), { recursive: true });
83582
+ mkdirSync32(dirname17(destFile), { recursive: true });
83491
83583
  copyFileSync9(srcFile, destFile);
83492
83584
  console.log(source_default.green(` Copied ${choice}.yaml -> ${destFile}`));
83493
83585
  console.log(source_default.yellow(` Edit ${destFile} to customize, then re-run switchroom setup.`));
@@ -83499,10 +83591,10 @@ async function copyExampleConfig(nonInteractive) {
83499
83591
  async function writeDetectedTimezone(destFile, nonInteractive, detect = detectServerTimezone, prompt = ask) {
83500
83592
  const detected = detect();
83501
83593
  if (detected !== undefined && detected !== "UTC" && isValidTimezone(detected)) {
83502
- const before2 = readFileSync51(destFile, "utf-8");
83594
+ const before2 = readFileSync52(destFile, "utf-8");
83503
83595
  const after2 = setSwitchroomTimezone(before2, detected);
83504
83596
  if (after2 !== before2)
83505
- writeFileSync26(destFile, after2);
83597
+ writeFileSync27(destFile, after2);
83506
83598
  console.log(source_default.green(` Detected timezone ${detected} \u2014 wrote switchroom.timezone.`));
83507
83599
  return;
83508
83600
  }
@@ -83520,10 +83612,10 @@ async function writeDetectedTimezone(destFile, nonInteractive, detect = detectSe
83520
83612
  console.error(source_default.yellow(` \u26a0 "${zone}" is not a valid IANA zone (expected Region/City like ` + '"Australia/Melbourne"). Skipping \u2014 agents will use UTC. ' + "Edit switchroom.timezone in switchroom.yaml by hand."));
83521
83613
  return;
83522
83614
  }
83523
- const before = readFileSync51(destFile, "utf-8");
83615
+ const before = readFileSync52(destFile, "utf-8");
83524
83616
  const after = setSwitchroomTimezone(before, zone);
83525
83617
  if (after !== before)
83526
- writeFileSync26(destFile, after);
83618
+ writeFileSync27(destFile, after);
83527
83619
  console.log(source_default.green(` Wrote switchroom.timezone: ${zone}.`));
83528
83620
  }
83529
83621
  async function stepBotToken(config, nonInteractive) {
@@ -83599,7 +83691,7 @@ async function resolveOrPromptToken(rawToken, label, config, nonInteractive) {
83599
83691
  try {
83600
83692
  const { openVault: openVault2 } = await Promise.resolve().then(() => (init_vault(), exports_vault));
83601
83693
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
83602
- if (existsSync56(vaultPath)) {
83694
+ if (existsSync57(vaultPath)) {
83603
83695
  const secrets = openVault2(passphrase, vaultPath);
83604
83696
  const key = rawToken.replace("vault:", "");
83605
83697
  const entry = secrets[key];
@@ -83627,7 +83719,7 @@ async function resolveOrPromptToken(rawToken, label, config, nonInteractive) {
83627
83719
  async function storeTokenInVault(config, vaultRef, token) {
83628
83720
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
83629
83721
  const key = vaultRef.replace("vault:", "");
83630
- if (!existsSync56(vaultPath)) {
83722
+ if (!existsSync57(vaultPath)) {
83631
83723
  console.log(source_default.gray(" Creating encrypted vault..."));
83632
83724
  let passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
83633
83725
  if (!passphrase) {
@@ -83813,7 +83905,7 @@ async function stepMemoryBackend(config, nonInteractive, switchroomConfigPath) {
83813
83905
  try {
83814
83906
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
83815
83907
  const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
83816
- if (passphrase && existsSync56(vaultPath)) {
83908
+ if (passphrase && existsSync57(vaultPath)) {
83817
83909
  const existing = getStringSecret(passphrase, vaultPath, "hindsight-api-key");
83818
83910
  if (existing) {
83819
83911
  console.log(source_default.gray(" Note: legacy 'hindsight-api-key' is in your vault but is no longer used. You can remove it with `switchroom vault rm hindsight-api-key`."));
@@ -84029,13 +84121,13 @@ async function stepAutoUnlock(config, switchroomConfigPath, nonInteractive) {
84029
84121
  return;
84030
84122
  }
84031
84123
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
84032
- if (!existsSync56(vaultPath)) {
84124
+ if (!existsSync57(vaultPath)) {
84033
84125
  console.log(source_default.gray(" Skipping (vault not created yet)."));
84034
84126
  return;
84035
84127
  }
84036
84128
  const credPathRaw = config.vault?.broker?.autoUnlockCredentialPath ?? "~/.switchroom/vault-auto-unlock";
84037
84129
  const credPath = resolvePath(credPathRaw);
84038
- if (config.vault?.broker?.autoUnlock === true && existsSync56(credPath)) {
84130
+ if (config.vault?.broker?.autoUnlock === true && existsSync57(credPath)) {
84039
84131
  console.log(source_default.green(` ${STEP_DONE} Already configured (${credPath})`));
84040
84132
  return;
84041
84133
  }
@@ -84101,12 +84193,12 @@ async function stepAutoUnlock(config, switchroomConfigPath, nonInteractive) {
84101
84193
  const choice = await askChoice(" Approval posture", [PASSPHRASE_CHOICE, TELEGRAM_ID_CHOICE]);
84102
84194
  if (choice === TELEGRAM_ID_CHOICE) {
84103
84195
  try {
84104
- const yamlPath = existsSync56(resolve33(process.cwd(), "switchroom.yaml")) ? resolve33(process.cwd(), "switchroom.yaml") : resolve33(process.cwd(), "switchroom.yml");
84105
- if (existsSync56(yamlPath)) {
84106
- const content = readFileSync51(yamlPath, "utf-8");
84196
+ const yamlPath = existsSync57(resolve33(process.cwd(), "switchroom.yaml")) ? resolve33(process.cwd(), "switchroom.yaml") : resolve33(process.cwd(), "switchroom.yml");
84197
+ if (existsSync57(yamlPath)) {
84198
+ const content = readFileSync52(yamlPath, "utf-8");
84107
84199
  const result = insertVaultBrokerApprovalAuth(content, "telegram-id");
84108
84200
  if (result.kind === "rewritten") {
84109
- writeFileSync26(yamlPath, result.content, "utf-8");
84201
+ writeFileSync27(yamlPath, result.content, "utf-8");
84110
84202
  console.log(source_default.green(` ${STEP_DONE} Set vault.broker.approvalAuth: telegram-id in ${yamlPath}`));
84111
84203
  } else if (result.kind === "already-set") {
84112
84204
  console.log(source_default.gray(" approvalAuth already set \u2014 leaving it alone."));
@@ -84137,8 +84229,8 @@ async function stepDangerousMode(config, nonInteractive) {
84137
84229
  resolve33(process.cwd(), "switchroom.yml")
84138
84230
  ];
84139
84231
  for (const configPath of configPaths) {
84140
- if (existsSync56(configPath)) {
84141
- let content = readFileSync51(configPath, "utf-8");
84232
+ if (existsSync57(configPath)) {
84233
+ let content = readFileSync52(configPath, "utf-8");
84142
84234
  const agentNames = Object.keys(config.agents);
84143
84235
  for (const name of agentNames) {
84144
84236
  const agentPattern = new RegExp(`(^ ${name}:\\s*\\n)`, "m");
@@ -84152,7 +84244,7 @@ async function stepDangerousMode(config, nonInteractive) {
84152
84244
  }
84153
84245
  config.agents[name].dangerous_mode = true;
84154
84246
  }
84155
- writeFileSync26(configPath, content, "utf-8");
84247
+ writeFileSync27(configPath, content, "utf-8");
84156
84248
  console.log(source_default.green(` ${STEP_DONE} Enabled dangerous_mode for all agents in ${configPath}`));
84157
84249
  break;
84158
84250
  }
@@ -84268,10 +84360,10 @@ init_source();
84268
84360
  init_loader();
84269
84361
  init_lifecycle();
84270
84362
  init_compose_env();
84271
- import { cpSync as cpSync2, existsSync as existsSync63, mkdirSync as mkdirSync33, readFileSync as readFileSync56, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync28, chownSync as chownSync5 } from "node:fs";
84363
+ import { cpSync as cpSync2, existsSync as existsSync64, mkdirSync as mkdirSync34, readFileSync as readFileSync57, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync29, chownSync as chownSync5 } from "node:fs";
84272
84364
  import { spawnSync as spawnSync12 } from "node:child_process";
84273
- import { join as join63, dirname as dirname19, resolve as resolve38 } from "node:path";
84274
- import { homedir as homedir38 } from "node:os";
84365
+ import { join as join64, dirname as dirname20, resolve as resolve38 } from "node:path";
84366
+ import { homedir as homedir39 } from "node:os";
84275
84367
 
84276
84368
  // src/cli/release-yaml.ts
84277
84369
  var import_yaml19 = __toESM(require_dist(), 1);
@@ -84296,7 +84388,7 @@ init_operator_uid();
84296
84388
  init_atomic();
84297
84389
 
84298
84390
  // src/cli/preflight-mounts.ts
84299
- import { statSync as statSync27 } from "node:fs";
84391
+ import { statSync as statSync28 } from "node:fs";
84300
84392
  function parseHostBindSources(composeText) {
84301
84393
  const out = [];
84302
84394
  const FILE_HINT = /\.(ya?ml|db|log|toml|json|token|id)$|\/\.vault-token$|machine-id$|localtime$|vault-auto-unlock$|\/webkite$/;
@@ -84323,7 +84415,7 @@ function parseHostBindSources(composeText) {
84323
84415
  return out;
84324
84416
  }
84325
84417
  function validateBindSources(composeText, deps = {}) {
84326
- const stat = deps.stat ?? ((p) => statSync27(p));
84418
+ const stat = deps.stat ?? ((p) => statSync28(p));
84327
84419
  const sources = parseHostBindSources(composeText);
84328
84420
  const issues = [];
84329
84421
  const seen = new Set;
@@ -84363,11 +84455,11 @@ ${lines.join(`
84363
84455
  function defaultPersistPin(configPath) {
84364
84456
  return (pin) => {
84365
84457
  const path4 = configPath ?? findConfigFile();
84366
- const before = readFileSync56(path4, "utf8");
84458
+ const before = readFileSync57(path4, "utf8");
84367
84459
  const after = setReleasePinInConfig(before, pin);
84368
84460
  if (after === before)
84369
84461
  return;
84370
- writeConfigFileSync(path4, after, statSync28(path4).mode & 511);
84462
+ writeConfigFileSync(path4, after, statSync29(path4).mode & 511);
84371
84463
  try {
84372
84464
  if (typeof process.geteuid === "function" && process.geteuid() === 0) {
84373
84465
  const uid = resolveOperatorUid();
@@ -84377,18 +84469,18 @@ function defaultPersistPin(configPath) {
84377
84469
  } catch {}
84378
84470
  };
84379
84471
  }
84380
- var DEFAULT_COMPOSE_PATH = join63(homedir38(), ".switchroom", "compose", "docker-compose.yml");
84472
+ var DEFAULT_COMPOSE_PATH = join64(homedir39(), ".switchroom", "compose", "docker-compose.yml");
84381
84473
  function runningFromSwitchroomCheckout(scriptPath) {
84382
- let dir = dirname19(scriptPath);
84474
+ let dir = dirname20(scriptPath);
84383
84475
  for (let i = 0;i < 12; i++) {
84384
- if (existsSync63(join63(dir, ".git"))) {
84476
+ if (existsSync64(join64(dir, ".git"))) {
84385
84477
  try {
84386
- const pkg = JSON.parse(readFileSync56(join63(dir, "package.json"), "utf-8"));
84478
+ const pkg = JSON.parse(readFileSync57(join64(dir, "package.json"), "utf-8"));
84387
84479
  if (pkg.name === "switchroom")
84388
84480
  return true;
84389
84481
  } catch {}
84390
84482
  }
84391
- const parent = dirname19(dir);
84483
+ const parent = dirname20(dir);
84392
84484
  if (parent === dir)
84393
84485
  break;
84394
84486
  dir = parent;
@@ -84468,7 +84560,7 @@ function planUpdate(opts) {
84468
84560
  steps.push({
84469
84561
  name: "pull-images",
84470
84562
  description: "Pull broker / kernel / agent images from GHCR",
84471
- skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync63(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
84563
+ skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync64(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
84472
84564
  run: () => {
84473
84565
  const r = runner("docker", [
84474
84566
  "compose",
@@ -84594,17 +84686,17 @@ function planUpdate(opts) {
84594
84686
  return;
84595
84687
  }
84596
84688
  const source = resolve38(import.meta.dirname, "../../skills");
84597
- const dest = join63(homedir38(), ".switchroom", "skills", "_bundled");
84598
- if (!existsSync63(source)) {
84689
+ const dest = join64(homedir39(), ".switchroom", "skills", "_bundled");
84690
+ if (!existsSync64(source)) {
84599
84691
  process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
84600
84692
  `);
84601
84693
  return;
84602
84694
  }
84603
84695
  try {
84604
- if (existsSync63(dest)) {
84696
+ if (existsSync64(dest)) {
84605
84697
  rmSync12(dest, { recursive: true, force: true });
84606
84698
  }
84607
- mkdirSync33(dirname19(dest), { recursive: true });
84699
+ mkdirSync34(dirname20(dest), { recursive: true });
84608
84700
  cpSync2(source, dest, { recursive: true, dereference: false });
84609
84701
  } catch (err) {
84610
84702
  throw new Error(`sync-bundled-skills failed: ${err.message}`);
@@ -84640,7 +84732,7 @@ function planUpdate(opts) {
84640
84732
  description: "docker compose up -d --remove-orphans (recreates services with new images / compose)",
84641
84733
  run: () => {
84642
84734
  try {
84643
- const composeText = readFileSync56(composePath, "utf8");
84735
+ const composeText = readFileSync57(composePath, "utf8");
84644
84736
  const pf = validateBindSources(composeText);
84645
84737
  if (!pf.ok)
84646
84738
  throw new Error(formatPreflightError(pf));
@@ -84715,14 +84807,14 @@ function defaultStatusProbe(composePath) {
84715
84807
  } catch {}
84716
84808
  if (scriptPath) {
84717
84809
  try {
84718
- cliBuiltAt = new Date(statSync28(scriptPath).mtimeMs).toISOString();
84810
+ cliBuiltAt = new Date(statSync29(scriptPath).mtimeMs).toISOString();
84719
84811
  } catch {}
84720
- let dir = dirname19(scriptPath);
84812
+ let dir = dirname20(scriptPath);
84721
84813
  for (let i = 0;i < 8; i++) {
84722
- const pkgPath = join63(dir, "package.json");
84723
- if (existsSync63(pkgPath)) {
84814
+ const pkgPath = join64(dir, "package.json");
84815
+ if (existsSync64(pkgPath)) {
84724
84816
  try {
84725
- const pkg = JSON.parse(readFileSync56(pkgPath, "utf-8"));
84817
+ const pkg = JSON.parse(readFileSync57(pkgPath, "utf-8"));
84726
84818
  if (typeof pkg.version === "string")
84727
84819
  cliVersion = pkg.version;
84728
84820
  } catch (err) {
@@ -84730,7 +84822,7 @@ function defaultStatusProbe(composePath) {
84730
84822
  }
84731
84823
  break;
84732
84824
  }
84733
- const parent = dirname19(dir);
84825
+ const parent = dirname20(dir);
84734
84826
  if (parent === dir)
84735
84827
  break;
84736
84828
  dir = parent;
@@ -84743,7 +84835,7 @@ function defaultStatusProbe(composePath) {
84743
84835
  warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
84744
84836
  }
84745
84837
  const services = [];
84746
- if (!existsSync63(composePath)) {
84838
+ if (!existsSync64(composePath)) {
84747
84839
  warnings.push(`compose file not found at ${composePath}; service status unknown`);
84748
84840
  return { cliVersion, cliBuiltAt, services, warnings };
84749
84841
  }
@@ -84939,8 +85031,8 @@ function registerUpdateCommand(program3) {
84939
85031
  // src/cli/rollout.ts
84940
85032
  init_helpers();
84941
85033
  import { spawnSync as spawnSync13 } from "node:child_process";
84942
- import { readFileSync as readFileSync57, chownSync as chownSync6, statSync as statSync29 } from "node:fs";
84943
- import { homedir as homedir39 } from "node:os";
85034
+ import { readFileSync as readFileSync58, chownSync as chownSync6, statSync as statSync30 } from "node:fs";
85035
+ import { homedir as homedir40 } from "node:os";
84944
85036
  init_operator_uid();
84945
85037
  init_atomic();
84946
85038
  init_audit_reader();
@@ -85169,10 +85261,10 @@ function executeRollout(steps, target, deps, execOpts = {}) {
85169
85261
  return { ok: true, rolled, warnings };
85170
85262
  }
85171
85263
  function resolveRollbackTarget(auditLogPath) {
85172
- const logPath = auditLogPath ?? defaultAuditLogPath2(homedir39());
85264
+ const logPath = auditLogPath ?? defaultAuditLogPath2(homedir40());
85173
85265
  let raw;
85174
85266
  try {
85175
- raw = readFileSync57(logPath, "utf8");
85267
+ raw = readFileSync58(logPath, "utf8");
85176
85268
  } catch {
85177
85269
  return null;
85178
85270
  }
@@ -85286,11 +85378,11 @@ function registerRolloutCommand(program3) {
85286
85378
  emitPhase: (phase) => process.stdout.write(encodeRolloutPhaseLine(phase) + `
85287
85379
  `),
85288
85380
  persistPin: (pin) => {
85289
- const before = readFileSync57(configPath, "utf8");
85381
+ const before = readFileSync58(configPath, "utf8");
85290
85382
  const after = setReleasePinInConfig(before, pin);
85291
85383
  if (after === before)
85292
85384
  return;
85293
- writeConfigFileSync(configPath, after, statSync29(configPath).mode & 511);
85385
+ writeConfigFileSync(configPath, after, statSync30(configPath).mode & 511);
85294
85386
  try {
85295
85387
  if (typeof process.geteuid === "function" && process.geteuid() === 0) {
85296
85388
  const uid = resolveOperatorUid();
@@ -85360,8 +85452,8 @@ init_source();
85360
85452
  init_helpers();
85361
85453
  init_lifecycle();
85362
85454
  import { execSync as execSync4 } from "node:child_process";
85363
- import { existsSync as existsSync64, readFileSync as readFileSync58 } from "node:fs";
85364
- import { dirname as dirname20, join as join64 } from "node:path";
85455
+ import { existsSync as existsSync65, readFileSync as readFileSync59 } from "node:fs";
85456
+ import { dirname as dirname21, join as join65 } from "node:path";
85365
85457
  function getClaudeCodeVersion() {
85366
85458
  try {
85367
85459
  const out = execSync4("claude --version 2>/dev/null", {
@@ -85411,16 +85503,16 @@ function formatUptime3(timestamp) {
85411
85503
  function locateSwitchroomInstallDir() {
85412
85504
  let dir = import.meta.dirname;
85413
85505
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
85414
- const pkgPath = join64(dir, "package.json");
85415
- if (existsSync64(pkgPath)) {
85506
+ const pkgPath = join65(dir, "package.json");
85507
+ if (existsSync65(pkgPath)) {
85416
85508
  try {
85417
- const pkg = JSON.parse(readFileSync58(pkgPath, "utf-8"));
85418
- if (pkg.name === "switchroom" && existsSync64(join64(dir, ".git"))) {
85509
+ const pkg = JSON.parse(readFileSync59(pkgPath, "utf-8"));
85510
+ if (pkg.name === "switchroom" && existsSync65(join65(dir, ".git"))) {
85419
85511
  return dir;
85420
85512
  }
85421
85513
  } catch {}
85422
85514
  }
85423
- dir = dirname20(dir);
85515
+ dir = dirname21(dir);
85424
85516
  }
85425
85517
  return null;
85426
85518
  }
@@ -85593,18 +85685,18 @@ import { resolve as resolve40 } from "node:path";
85593
85685
 
85594
85686
  // src/agents/session-retention.ts
85595
85687
  import {
85596
- existsSync as existsSync65,
85688
+ existsSync as existsSync66,
85597
85689
  readdirSync as readdirSync22,
85598
- statSync as statSync30,
85599
- unlinkSync as unlinkSync13
85690
+ statSync as statSync31,
85691
+ unlinkSync as unlinkSync14
85600
85692
  } from "node:fs";
85601
- import { join as join65 } from "node:path";
85693
+ import { join as join66 } from "node:path";
85602
85694
  var DEFAULT_SESSION_RETENTION_MAX_COUNT = 20;
85603
85695
  var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
85604
85696
  var MIN_KEEP = 2;
85605
85697
  function collectSessionJsonl(claudeConfigDir) {
85606
- const projects = join65(claudeConfigDir, "projects");
85607
- if (!existsSync65(projects))
85698
+ const projects = join66(claudeConfigDir, "projects");
85699
+ if (!existsSync66(projects))
85608
85700
  return [];
85609
85701
  const found = [];
85610
85702
  const walk2 = (dir) => {
@@ -85615,10 +85707,10 @@ function collectSessionJsonl(claudeConfigDir) {
85615
85707
  return;
85616
85708
  }
85617
85709
  for (const name of entries) {
85618
- const full = join65(dir, name);
85710
+ const full = join66(dir, name);
85619
85711
  let st;
85620
85712
  try {
85621
- st = statSync30(full);
85713
+ st = statSync31(full);
85622
85714
  } catch {
85623
85715
  continue;
85624
85716
  }
@@ -85665,7 +85757,7 @@ function pruneSessionJsonl(claudeConfigDir, opts = {}) {
85665
85757
  if (!eligible)
85666
85758
  continue;
85667
85759
  try {
85668
- unlinkSync13(f.path);
85760
+ unlinkSync14(f.path);
85669
85761
  deleted++;
85670
85762
  } catch (err) {
85671
85763
  onWarn(`session-retention: could not delete ${f.path}: ${err.message}`);
@@ -85735,18 +85827,18 @@ function registerHandoffCommand(program3) {
85735
85827
  // src/issues/store.ts
85736
85828
  import {
85737
85829
  closeSync as closeSync12,
85738
- existsSync as existsSync66,
85739
- mkdirSync as mkdirSync34,
85830
+ existsSync as existsSync67,
85831
+ mkdirSync as mkdirSync35,
85740
85832
  openSync as openSync12,
85741
85833
  readdirSync as readdirSync23,
85742
- readFileSync as readFileSync59,
85834
+ readFileSync as readFileSync60,
85743
85835
  renameSync as renameSync15,
85744
- statSync as statSync31,
85745
- unlinkSync as unlinkSync14,
85746
- writeFileSync as writeFileSync27,
85836
+ statSync as statSync32,
85837
+ unlinkSync as unlinkSync15,
85838
+ writeFileSync as writeFileSync28,
85747
85839
  writeSync as writeSync9
85748
85840
  } from "node:fs";
85749
- import { join as join66 } from "node:path";
85841
+ import { join as join67 } from "node:path";
85750
85842
  import { randomBytes as randomBytes12 } from "node:crypto";
85751
85843
  import { execSync as execSync5 } from "node:child_process";
85752
85844
 
@@ -86144,12 +86236,12 @@ function redactedMarker(ruleId) {
86144
86236
  var ISSUES_FILE = "issues.jsonl";
86145
86237
  var ISSUES_LOCK = "issues.lock";
86146
86238
  function readAll(stateDir) {
86147
- const path4 = join66(stateDir, ISSUES_FILE);
86148
- if (!existsSync66(path4))
86239
+ const path4 = join67(stateDir, ISSUES_FILE);
86240
+ if (!existsSync67(path4))
86149
86241
  return [];
86150
86242
  let raw;
86151
86243
  try {
86152
- raw = readFileSync59(path4, "utf-8");
86244
+ raw = readFileSync60(path4, "utf-8");
86153
86245
  } catch {
86154
86246
  return [];
86155
86247
  }
@@ -86222,7 +86314,7 @@ function record(stateDir, input, nowFn = Date.now) {
86222
86314
  });
86223
86315
  }
86224
86316
  function resolve41(stateDir, fingerprint, nowFn = Date.now) {
86225
- if (!existsSync66(join66(stateDir, ISSUES_FILE)))
86317
+ if (!existsSync67(join67(stateDir, ISSUES_FILE)))
86226
86318
  return 0;
86227
86319
  return withLock(stateDir, () => {
86228
86320
  const all = readAll(stateDir);
@@ -86240,7 +86332,7 @@ function resolve41(stateDir, fingerprint, nowFn = Date.now) {
86240
86332
  });
86241
86333
  }
86242
86334
  function resolveAllBySource(stateDir, source, nowFn = Date.now) {
86243
- if (!existsSync66(join66(stateDir, ISSUES_FILE)))
86335
+ if (!existsSync67(join67(stateDir, ISSUES_FILE)))
86244
86336
  return 0;
86245
86337
  return withLock(stateDir, () => {
86246
86338
  const all = readAll(stateDir);
@@ -86258,7 +86350,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
86258
86350
  });
86259
86351
  }
86260
86352
  function prune(stateDir, opts = {}) {
86261
- if (!existsSync66(join66(stateDir, ISSUES_FILE)))
86353
+ if (!existsSync67(join67(stateDir, ISSUES_FILE)))
86262
86354
  return 0;
86263
86355
  return withLock(stateDir, () => {
86264
86356
  const all = readAll(stateDir);
@@ -86288,16 +86380,16 @@ function prune(stateDir, opts = {}) {
86288
86380
  });
86289
86381
  }
86290
86382
  function ensureDir(stateDir) {
86291
- mkdirSync34(stateDir, { recursive: true });
86383
+ mkdirSync35(stateDir, { recursive: true });
86292
86384
  }
86293
86385
  function writeAll(stateDir, events) {
86294
- const path4 = join66(stateDir, ISSUES_FILE);
86386
+ const path4 = join67(stateDir, ISSUES_FILE);
86295
86387
  sweepOrphanTmpFiles(stateDir);
86296
86388
  const tmp = `${path4}.tmp-${process.pid}-${randomBytes12(4).toString("hex")}`;
86297
86389
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
86298
86390
  `) + `
86299
86391
  `;
86300
- writeFileSync27(tmp, body, "utf-8");
86392
+ writeFileSync28(tmp, body, "utf-8");
86301
86393
  renameSync15(tmp, path4);
86302
86394
  }
86303
86395
  var ORPHAN_TMP_TTL_MS = 60000;
@@ -86313,11 +86405,11 @@ function sweepOrphanTmpFiles(stateDir) {
86313
86405
  for (const entry of entries) {
86314
86406
  if (!entry.startsWith(TMP_PREFIX))
86315
86407
  continue;
86316
- const tmpPath = join66(stateDir, entry);
86408
+ const tmpPath = join67(stateDir, entry);
86317
86409
  try {
86318
- const stat = statSync31(tmpPath);
86410
+ const stat = statSync32(tmpPath);
86319
86411
  if (stat.mtimeMs < cutoff) {
86320
- unlinkSync14(tmpPath);
86412
+ unlinkSync15(tmpPath);
86321
86413
  }
86322
86414
  } catch {}
86323
86415
  }
@@ -86325,7 +86417,7 @@ function sweepOrphanTmpFiles(stateDir) {
86325
86417
  var LOCK_RETRY_MS = 25;
86326
86418
  var LOCK_TIMEOUT_MS = 1e4;
86327
86419
  function withLock(stateDir, fn) {
86328
- const lockPath = join66(stateDir, ISSUES_LOCK);
86420
+ const lockPath = join67(stateDir, ISSUES_LOCK);
86329
86421
  const startedAt = Date.now();
86330
86422
  let fd = null;
86331
86423
  while (fd === null) {
@@ -86353,27 +86445,27 @@ function withLock(stateDir, fn) {
86353
86445
  closeSync12(fd);
86354
86446
  } catch {}
86355
86447
  try {
86356
- unlinkSync14(lockPath);
86448
+ unlinkSync15(lockPath);
86357
86449
  } catch {}
86358
86450
  }
86359
86451
  }
86360
86452
  function tryStealStaleLock(lockPath) {
86361
86453
  let pidStr;
86362
86454
  try {
86363
- pidStr = readFileSync59(lockPath, "utf-8").trim();
86455
+ pidStr = readFileSync60(lockPath, "utf-8").trim();
86364
86456
  } catch {
86365
86457
  return true;
86366
86458
  }
86367
86459
  const pid = Number(pidStr);
86368
86460
  if (!Number.isFinite(pid) || pid <= 0) {
86369
86461
  try {
86370
- unlinkSync14(lockPath);
86462
+ unlinkSync15(lockPath);
86371
86463
  } catch {}
86372
86464
  return true;
86373
86465
  }
86374
86466
  if (pid === process.pid) {
86375
86467
  try {
86376
- unlinkSync14(lockPath);
86468
+ unlinkSync15(lockPath);
86377
86469
  } catch {}
86378
86470
  return true;
86379
86471
  }
@@ -86388,7 +86480,7 @@ function tryStealStaleLock(lockPath) {
86388
86480
  return false;
86389
86481
  }
86390
86482
  try {
86391
- unlinkSync14(lockPath);
86483
+ unlinkSync15(lockPath);
86392
86484
  } catch {}
86393
86485
  return true;
86394
86486
  }
@@ -86608,21 +86700,21 @@ function relTime(deltaMs) {
86608
86700
 
86609
86701
  // src/cli/deps.ts
86610
86702
  init_source();
86611
- import { existsSync as existsSync69 } from "node:fs";
86612
- import { homedir as homedir42 } from "node:os";
86613
- import { join as join69, resolve as resolve42 } from "node:path";
86703
+ import { existsSync as existsSync70 } from "node:fs";
86704
+ import { homedir as homedir43 } from "node:os";
86705
+ import { join as join70, resolve as resolve42 } from "node:path";
86614
86706
 
86615
86707
  // src/deps/python.ts
86616
86708
  import { createHash as createHash12 } from "node:crypto";
86617
86709
  import {
86618
- existsSync as existsSync67,
86619
- mkdirSync as mkdirSync35,
86620
- readFileSync as readFileSync60,
86710
+ existsSync as existsSync68,
86711
+ mkdirSync as mkdirSync36,
86712
+ readFileSync as readFileSync61,
86621
86713
  rmSync as rmSync13,
86622
- writeFileSync as writeFileSync28
86714
+ writeFileSync as writeFileSync29
86623
86715
  } from "node:fs";
86624
- import { dirname as dirname21, join as join67 } from "node:path";
86625
- import { homedir as homedir40 } from "node:os";
86716
+ import { dirname as dirname22, join as join68 } from "node:path";
86717
+ import { homedir as homedir41 } from "node:os";
86626
86718
  import { execFileSync as execFileSync19 } from "node:child_process";
86627
86719
 
86628
86720
  class PythonEnvError extends Error {
@@ -86634,26 +86726,26 @@ class PythonEnvError extends Error {
86634
86726
  }
86635
86727
  }
86636
86728
  function defaultPythonCacheRoot() {
86637
- return join67(homedir40(), ".switchroom", "deps", "python");
86729
+ return join68(homedir41(), ".switchroom", "deps", "python");
86638
86730
  }
86639
86731
  function hashFile(path4) {
86640
- return createHash12("sha256").update(readFileSync60(path4)).digest("hex");
86732
+ return createHash12("sha256").update(readFileSync61(path4)).digest("hex");
86641
86733
  }
86642
86734
  function ensurePythonEnv(opts) {
86643
86735
  const { skillName, requirementsPath, force = false } = opts;
86644
86736
  const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
86645
86737
  const hostPython = opts.pythonBin ?? "python3";
86646
- if (!existsSync67(requirementsPath)) {
86738
+ if (!existsSync68(requirementsPath)) {
86647
86739
  throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
86648
86740
  }
86649
- const venvDir = join67(cacheRoot, skillName);
86650
- const stampPath = join67(venvDir, ".requirements.sha256");
86651
- const binDir = join67(venvDir, "bin");
86652
- const pythonBin = join67(binDir, "python");
86653
- const pipBin = join67(binDir, "pip");
86741
+ const venvDir = join68(cacheRoot, skillName);
86742
+ const stampPath = join68(venvDir, ".requirements.sha256");
86743
+ const binDir = join68(venvDir, "bin");
86744
+ const pythonBin = join68(binDir, "python");
86745
+ const pipBin = join68(binDir, "pip");
86654
86746
  const targetHash = hashFile(requirementsPath);
86655
- if (!force && existsSync67(stampPath) && existsSync67(pythonBin)) {
86656
- const existingHash = readFileSync60(stampPath, "utf8").trim();
86747
+ if (!force && existsSync68(stampPath) && existsSync68(pythonBin)) {
86748
+ const existingHash = readFileSync61(stampPath, "utf8").trim();
86657
86749
  if (existingHash === targetHash) {
86658
86750
  return {
86659
86751
  skillName,
@@ -86665,10 +86757,10 @@ function ensurePythonEnv(opts) {
86665
86757
  };
86666
86758
  }
86667
86759
  }
86668
- if (existsSync67(venvDir)) {
86760
+ if (existsSync68(venvDir)) {
86669
86761
  rmSync13(venvDir, { recursive: true, force: true });
86670
86762
  }
86671
- mkdirSync35(dirname21(venvDir), { recursive: true });
86763
+ mkdirSync36(dirname22(venvDir), { recursive: true });
86672
86764
  try {
86673
86765
  execFileSync19(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
86674
86766
  } catch (err) {
@@ -86687,7 +86779,7 @@ function ensurePythonEnv(opts) {
86687
86779
  const e = err;
86688
86780
  throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
86689
86781
  }
86690
- writeFileSync28(stampPath, targetHash + `
86782
+ writeFileSync29(stampPath, targetHash + `
86691
86783
  `);
86692
86784
  return {
86693
86785
  skillName,
@@ -86703,14 +86795,14 @@ function ensurePythonEnv(opts) {
86703
86795
  import { createHash as createHash13 } from "node:crypto";
86704
86796
  import {
86705
86797
  copyFileSync as copyFileSync10,
86706
- existsSync as existsSync68,
86707
- mkdirSync as mkdirSync36,
86708
- readFileSync as readFileSync61,
86798
+ existsSync as existsSync69,
86799
+ mkdirSync as mkdirSync37,
86800
+ readFileSync as readFileSync62,
86709
86801
  rmSync as rmSync14,
86710
- writeFileSync as writeFileSync29
86802
+ writeFileSync as writeFileSync30
86711
86803
  } from "node:fs";
86712
- import { dirname as dirname22, join as join68 } from "node:path";
86713
- import { homedir as homedir41 } from "node:os";
86804
+ import { dirname as dirname23, join as join69 } from "node:path";
86805
+ import { homedir as homedir42 } from "node:os";
86714
86806
  import { execFileSync as execFileSync20 } from "node:child_process";
86715
86807
 
86716
86808
  class NodeEnvError extends Error {
@@ -86733,23 +86825,23 @@ var LOCKFILES_FOR = {
86733
86825
  npm: ["package-lock.json"]
86734
86826
  };
86735
86827
  function defaultNodeCacheRoot() {
86736
- return join68(homedir41(), ".switchroom", "deps", "node");
86828
+ return join69(homedir42(), ".switchroom", "deps", "node");
86737
86829
  }
86738
86830
  function hashDepInputs(packageJsonPath) {
86739
- const sourceDir = dirname22(packageJsonPath);
86831
+ const sourceDir = dirname23(packageJsonPath);
86740
86832
  const hasher = createHash13("sha256");
86741
86833
  hasher.update(`package.json
86742
86834
  `);
86743
- hasher.update(readFileSync61(packageJsonPath));
86835
+ hasher.update(readFileSync62(packageJsonPath));
86744
86836
  for (const lockName of ALL_LOCKFILES) {
86745
- const lockPath = join68(sourceDir, lockName);
86746
- if (existsSync68(lockPath)) {
86837
+ const lockPath = join69(sourceDir, lockName);
86838
+ if (existsSync69(lockPath)) {
86747
86839
  hasher.update(`
86748
86840
  `);
86749
86841
  hasher.update(lockName);
86750
86842
  hasher.update(`
86751
86843
  `);
86752
- hasher.update(readFileSync61(lockPath));
86844
+ hasher.update(readFileSync62(lockPath));
86753
86845
  }
86754
86846
  }
86755
86847
  return hasher.digest("hex");
@@ -86758,17 +86850,17 @@ function ensureNodeEnv(opts) {
86758
86850
  const { skillName, packageJsonPath, force = false } = opts;
86759
86851
  const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
86760
86852
  const installer = opts.installer ?? "bun";
86761
- if (!existsSync68(packageJsonPath)) {
86853
+ if (!existsSync69(packageJsonPath)) {
86762
86854
  throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
86763
86855
  }
86764
- const sourceDir = dirname22(packageJsonPath);
86765
- const envDir = join68(cacheRoot, skillName);
86766
- const stampPath = join68(envDir, ".package.sha256");
86767
- const nodeModulesDir = join68(envDir, "node_modules");
86768
- const binDir = join68(nodeModulesDir, ".bin");
86856
+ const sourceDir = dirname23(packageJsonPath);
86857
+ const envDir = join69(cacheRoot, skillName);
86858
+ const stampPath = join69(envDir, ".package.sha256");
86859
+ const nodeModulesDir = join69(envDir, "node_modules");
86860
+ const binDir = join69(nodeModulesDir, ".bin");
86769
86861
  const targetHash = hashDepInputs(packageJsonPath);
86770
- if (!force && existsSync68(stampPath) && existsSync68(nodeModulesDir)) {
86771
- const existingHash = readFileSync61(stampPath, "utf8").trim();
86862
+ if (!force && existsSync69(stampPath) && existsSync69(nodeModulesDir)) {
86863
+ const existingHash = readFileSync62(stampPath, "utf8").trim();
86772
86864
  if (existingHash === targetHash) {
86773
86865
  return {
86774
86866
  skillName,
@@ -86779,16 +86871,16 @@ function ensureNodeEnv(opts) {
86779
86871
  };
86780
86872
  }
86781
86873
  }
86782
- if (existsSync68(envDir)) {
86874
+ if (existsSync69(envDir)) {
86783
86875
  rmSync14(envDir, { recursive: true, force: true });
86784
86876
  }
86785
- mkdirSync36(envDir, { recursive: true });
86786
- copyFileSync10(packageJsonPath, join68(envDir, "package.json"));
86877
+ mkdirSync37(envDir, { recursive: true });
86878
+ copyFileSync10(packageJsonPath, join69(envDir, "package.json"));
86787
86879
  let copiedLockfile = false;
86788
86880
  for (const lockName of LOCKFILES_FOR[installer]) {
86789
- const lockPath = join68(sourceDir, lockName);
86790
- if (existsSync68(lockPath)) {
86791
- copyFileSync10(lockPath, join68(envDir, lockName));
86881
+ const lockPath = join69(sourceDir, lockName);
86882
+ if (existsSync69(lockPath)) {
86883
+ copyFileSync10(lockPath, join69(envDir, lockName));
86792
86884
  copiedLockfile = true;
86793
86885
  }
86794
86886
  }
@@ -86804,7 +86896,7 @@ function ensureNodeEnv(opts) {
86804
86896
  const e = err;
86805
86897
  throw new NodeEnvError(`Failed to install node deps for skill "${skillName}" with ${installer}: ${e.message}`, e.stderr?.toString());
86806
86898
  }
86807
- writeFileSync29(stampPath, targetHash + `
86899
+ writeFileSync30(stampPath, targetHash + `
86808
86900
  `);
86809
86901
  return {
86810
86902
  skillName,
@@ -86817,28 +86909,28 @@ function ensureNodeEnv(opts) {
86817
86909
 
86818
86910
  // src/cli/deps.ts
86819
86911
  function builtinSkillsRoot() {
86820
- return resolve42(homedir42(), ".switchroom/skills/_bundled");
86912
+ return resolve42(homedir43(), ".switchroom/skills/_bundled");
86821
86913
  }
86822
86914
  function registerDepsCommand(program3) {
86823
86915
  const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
86824
86916
  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) => {
86825
86917
  const skillsRoot = builtinSkillsRoot();
86826
- if (!existsSync69(skillsRoot)) {
86918
+ if (!existsSync70(skillsRoot)) {
86827
86919
  console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
86828
86920
  process.exit(1);
86829
86921
  }
86830
- const skillDir = join69(skillsRoot, skill);
86831
- if (!existsSync69(skillDir)) {
86922
+ const skillDir = join70(skillsRoot, skill);
86923
+ if (!existsSync70(skillDir)) {
86832
86924
  console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
86833
86925
  process.exit(1);
86834
86926
  }
86835
- const requirementsPath = join69(skillDir, "requirements.txt");
86836
- const packageJsonPath = join69(skillDir, "package.json");
86837
- const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync69(requirementsPath));
86838
- const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync69(packageJsonPath));
86927
+ const requirementsPath = join70(skillDir, "requirements.txt");
86928
+ const packageJsonPath = join70(skillDir, "package.json");
86929
+ const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync70(requirementsPath));
86930
+ const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync70(packageJsonPath));
86839
86931
  let did = 0;
86840
86932
  if (wantPython) {
86841
- if (!existsSync69(requirementsPath)) {
86933
+ if (!existsSync70(requirementsPath)) {
86842
86934
  console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
86843
86935
  process.exit(1);
86844
86936
  }
@@ -86862,7 +86954,7 @@ function registerDepsCommand(program3) {
86862
86954
  }
86863
86955
  }
86864
86956
  if (wantNode) {
86865
- if (!existsSync69(packageJsonPath)) {
86957
+ if (!existsSync70(packageJsonPath)) {
86866
86958
  console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
86867
86959
  process.exit(1);
86868
86960
  }
@@ -86895,7 +86987,7 @@ function registerDepsCommand(program3) {
86895
86987
  // src/cli/workspace.ts
86896
86988
  init_helpers();
86897
86989
  init_loader();
86898
- import { existsSync as existsSync70 } from "node:fs";
86990
+ import { existsSync as existsSync71 } from "node:fs";
86899
86991
  import { resolve as resolve43, sep as sep3 } from "node:path";
86900
86992
  import { spawnSync as spawnSync14 } from "node:child_process";
86901
86993
 
@@ -87672,7 +87764,7 @@ function registerWorkspaceCommand(program3) {
87672
87764
  if (!dir)
87673
87765
  return;
87674
87766
  const gitDir = resolve43(dir, ".git");
87675
- if (!existsSync70(gitDir)) {
87767
+ if (!existsSync71(gitDir)) {
87676
87768
  process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
87677
87769
  `);
87678
87770
  return;
@@ -87726,7 +87818,7 @@ function registerWorkspaceCommand(program3) {
87726
87818
  if (!dir)
87727
87819
  return;
87728
87820
  const gitDir = resolve43(dir, ".git");
87729
- if (!existsSync70(gitDir)) {
87821
+ if (!existsSync71(gitDir)) {
87730
87822
  process.stdout.write(`Workspace is not a git repository.
87731
87823
  `);
87732
87824
  return;
@@ -87751,7 +87843,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
87751
87843
  const agentsDir = resolveAgentsDir(config);
87752
87844
  const agentDir = resolve43(agentsDir, agentName);
87753
87845
  const dir = resolveAgentWorkspaceDir(agentDir);
87754
- if (!existsSync70(dir)) {
87846
+ if (!existsSync71(dir)) {
87755
87847
  process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
87756
87848
  `);
87757
87849
  return;
@@ -87787,8 +87879,8 @@ function safeParseInt(value, fallback) {
87787
87879
  init_helpers();
87788
87880
  init_loader();
87789
87881
  init_merge();
87790
- import { copyFileSync as copyFileSync11, existsSync as existsSync71, readFileSync as readFileSync62, writeFileSync as writeFileSync30 } from "node:fs";
87791
- import { join as join70, resolve as resolve44 } from "node:path";
87882
+ import { copyFileSync as copyFileSync11, existsSync as existsSync72, readFileSync as readFileSync63, writeFileSync as writeFileSync31 } from "node:fs";
87883
+ import { join as join71, resolve as resolve44 } from "node:path";
87792
87884
  init_scaffold();
87793
87885
  init_profiles();
87794
87886
  init_schema();
@@ -87805,7 +87897,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
87805
87897
  const agentsDir = resolveAgentsDir(config);
87806
87898
  const agentDir = resolve44(agentsDir, agentName);
87807
87899
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
87808
- if (!existsSync71(workspaceDir)) {
87900
+ if (!existsSync72(workspaceDir)) {
87809
87901
  console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
87810
87902
  process.exit(1);
87811
87903
  }
@@ -87814,7 +87906,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
87814
87906
  profileName,
87815
87907
  profilePath,
87816
87908
  workspaceDir,
87817
- soulPath: join70(workspaceDir, "SOUL.md"),
87909
+ soulPath: join71(workspaceDir, "SOUL.md"),
87818
87910
  soul: merged.soul
87819
87911
  };
87820
87912
  }
@@ -87831,11 +87923,11 @@ function registerSoulCommand(program3) {
87831
87923
  const t = resolveSoulTargetOrExit(program3, agentName);
87832
87924
  if (!t)
87833
87925
  return;
87834
- if (!existsSync71(t.soulPath)) {
87926
+ if (!existsSync72(t.soulPath)) {
87835
87927
  console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
87836
87928
  process.exit(1);
87837
87929
  }
87838
- process.stdout.write(readFileSync62(t.soulPath, "utf-8"));
87930
+ process.stdout.write(readFileSync63(t.soulPath, "utf-8"));
87839
87931
  }));
87840
87932
  cmd.command("reset <agent>").description("Re-seed SOUL.md from the agent's current profile " + "(backs the existing file up to SOUL.md.bak first)").option("-y, --yes", "Skip the confirmation prompt").action(withConfigError(async (agentName, opts) => {
87841
87933
  const t = resolveSoulTargetOrExit(program3, agentName);
@@ -87846,7 +87938,7 @@ function registerSoulCommand(program3) {
87846
87938
  console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
87847
87939
  process.exit(1);
87848
87940
  }
87849
- const exists = existsSync71(t.soulPath);
87941
+ const exists = existsSync72(t.soulPath);
87850
87942
  if (exists && !opts.yes) {
87851
87943
  if (!isInteractive()) {
87852
87944
  console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
@@ -87861,12 +87953,12 @@ function registerSoulCommand(program3) {
87861
87953
  let backupPath;
87862
87954
  if (exists) {
87863
87955
  backupPath = `${t.soulPath}.bak`;
87864
- if (existsSync71(backupPath)) {
87956
+ if (existsSync72(backupPath)) {
87865
87957
  backupPath = `${t.soulPath}.bak.${Date.now()}`;
87866
87958
  }
87867
87959
  copyFileSync11(t.soulPath, backupPath);
87868
87960
  }
87869
- writeFileSync30(t.soulPath, content, "utf-8");
87961
+ writeFileSync31(t.soulPath, content, "utf-8");
87870
87962
  if (backupPath) {
87871
87963
  console.log(`soul: re-seeded ${agentName}'s SOUL.md from profile ` + `"${t.profileName}".
87872
87964
  ` + ` Previous version saved to ${backupPath}`);
@@ -87880,8 +87972,8 @@ function registerSoulCommand(program3) {
87880
87972
  // src/cli/debug.ts
87881
87973
  init_helpers();
87882
87974
  init_loader();
87883
- import { existsSync as existsSync72, readFileSync as readFileSync63, readdirSync as readdirSync24, statSync as statSync32 } from "node:fs";
87884
- import { resolve as resolve45, join as join71 } from "node:path";
87975
+ import { existsSync as existsSync73, readFileSync as readFileSync64, readdirSync as readdirSync24, statSync as statSync33 } from "node:fs";
87976
+ import { resolve as resolve45, join as join72 } from "node:path";
87885
87977
  import { createHash as createHash14 } from "node:crypto";
87886
87978
  init_merge();
87887
87979
  init_hindsight2();
@@ -87892,11 +87984,11 @@ function estimateTokens(bytes) {
87892
87984
  return Math.round(bytes / 3.7);
87893
87985
  }
87894
87986
  function readMcpServerNames(agentDir) {
87895
- const mcpPath = join71(agentDir, ".mcp.json");
87896
- if (!existsSync72(mcpPath))
87987
+ const mcpPath = join72(agentDir, ".mcp.json");
87988
+ if (!existsSync73(mcpPath))
87897
87989
  return [];
87898
87990
  try {
87899
- const parsed = JSON.parse(readFileSync63(mcpPath, "utf-8"));
87991
+ const parsed = JSON.parse(readFileSync64(mcpPath, "utf-8"));
87900
87992
  return Object.keys(parsed.mcpServers ?? {});
87901
87993
  } catch {
87902
87994
  return null;
@@ -87906,8 +87998,8 @@ function sha256(content) {
87906
87998
  return createHash14("sha256").update(content).digest("hex").slice(0, 16);
87907
87999
  }
87908
88000
  function findLatestTranscriptJsonl(claudeConfigDir) {
87909
- const projectsDir = join71(claudeConfigDir, "projects");
87910
- if (!existsSync72(projectsDir))
88001
+ const projectsDir = join72(claudeConfigDir, "projects");
88002
+ if (!existsSync73(projectsDir))
87911
88003
  return;
87912
88004
  try {
87913
88005
  const entries = readdirSync24(projectsDir, { withFileTypes: true });
@@ -87915,11 +88007,11 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
87915
88007
  for (const entry of entries) {
87916
88008
  if (!entry.isDirectory())
87917
88009
  continue;
87918
- const projectPath = join71(projectsDir, entry.name);
87919
- const transcriptPath = join71(projectPath, "transcript.jsonl");
87920
- if (!existsSync72(transcriptPath))
88010
+ const projectPath = join72(projectsDir, entry.name);
88011
+ const transcriptPath = join72(projectPath, "transcript.jsonl");
88012
+ if (!existsSync73(transcriptPath))
87921
88013
  continue;
87922
- const stat3 = statSync32(transcriptPath);
88014
+ const stat3 = statSync33(transcriptPath);
87923
88015
  if (!latest || stat3.mtimeMs > latest.mtime) {
87924
88016
  latest = { path: transcriptPath, mtime: stat3.mtimeMs };
87925
88017
  }
@@ -87931,7 +88023,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
87931
88023
  }
87932
88024
  function extractLatestUserMessage(transcriptPath) {
87933
88025
  try {
87934
- const content = readFileSync63(transcriptPath, "utf-8");
88026
+ const content = readFileSync64(transcriptPath, "utf-8");
87935
88027
  const lines = content.trim().split(`
87936
88028
  `).filter(Boolean);
87937
88029
  for (let i = lines.length - 1;i >= 0; i--) {
@@ -87980,16 +88072,16 @@ function registerDebugCommand(program3) {
87980
88072
  }
87981
88073
  const agentsDir = resolveAgentsDir(config);
87982
88074
  const agentDir = resolve45(agentsDir, agentName);
87983
- if (!existsSync72(agentDir)) {
88075
+ if (!existsSync73(agentDir)) {
87984
88076
  console.error(`Agent directory not found: ${agentDir}`);
87985
88077
  process.exit(1);
87986
88078
  }
87987
88079
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
87988
- const claudeConfigDir = join71(agentDir, ".claude");
87989
- const claudeMdPath = join71(agentDir, "CLAUDE.md");
87990
- const soulMdPath = join71(agentDir, "SOUL.md");
87991
- const workspaceSoulMdPath = join71(workspaceDir, "SOUL.md");
87992
- const handoffPath = join71(agentDir, ".handoff.md");
88080
+ const claudeConfigDir = join72(agentDir, ".claude");
88081
+ const claudeMdPath = join72(agentDir, "CLAUDE.md");
88082
+ const soulMdPath = join72(agentDir, "SOUL.md");
88083
+ const workspaceSoulMdPath = join72(workspaceDir, "SOUL.md");
88084
+ const handoffPath = join72(agentDir, ".handoff.md");
87993
88085
  const lastN = parseInt(opts.last, 10);
87994
88086
  if (isNaN(lastN) || lastN < 1) {
87995
88087
  console.error("--last must be a positive integer");
@@ -88035,7 +88127,7 @@ function registerDebugCommand(program3) {
88035
88127
  }
88036
88128
  console.log(`=== Append System Prompt (per-session) ===
88037
88129
  `);
88038
- const handoffContent = existsSync72(handoffPath) ? readFileSync63(handoffPath, "utf-8") : "";
88130
+ const handoffContent = existsSync73(handoffPath) ? readFileSync64(handoffPath, "utf-8") : "";
88039
88131
  if (handoffContent.trim().length > 0) {
88040
88132
  console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
88041
88133
  console.log(handoffContent);
@@ -88046,7 +88138,7 @@ function registerDebugCommand(program3) {
88046
88138
  }
88047
88139
  console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
88048
88140
  `);
88049
- const claudeMdContent = existsSync72(claudeMdPath) ? readFileSync63(claudeMdPath, "utf-8") : "";
88141
+ const claudeMdContent = existsSync73(claudeMdPath) ? readFileSync64(claudeMdPath, "utf-8") : "";
88050
88142
  if (claudeMdContent.trim().length > 0) {
88051
88143
  console.log(`(${formatBytes(claudeMdContent.length)})`);
88052
88144
  console.log(claudeMdContent);
@@ -88057,7 +88149,7 @@ function registerDebugCommand(program3) {
88057
88149
  }
88058
88150
  console.log(`=== Persona (SOUL.md) ===
88059
88151
  `);
88060
- const soulMdContent = existsSync72(soulMdPath) ? readFileSync63(soulMdPath, "utf-8") : existsSync72(workspaceSoulMdPath) ? readFileSync63(workspaceSoulMdPath, "utf-8") : "";
88152
+ const soulMdContent = existsSync73(soulMdPath) ? readFileSync64(soulMdPath, "utf-8") : existsSync73(workspaceSoulMdPath) ? readFileSync64(workspaceSoulMdPath, "utf-8") : "";
88061
88153
  if (soulMdContent.trim().length > 0) {
88062
88154
  console.log(`(${formatBytes(soulMdContent.length)})`);
88063
88155
  console.log(soulMdContent);
@@ -88118,11 +88210,11 @@ function registerDebugCommand(program3) {
88118
88210
  const soulMdBytes = soulMdContent.length;
88119
88211
  const perTurnBytes = dynamicResult.concatenated.length;
88120
88212
  const userBytes = userMessage?.text.length ?? 0;
88121
- const fleetDir = join71(agentsDir, "..", "fleet");
88122
- const fleetInvPath = join71(fleetDir, "switchroom-invariants.md");
88123
- const fleetClaudePath = join71(fleetDir, "CLAUDE.md");
88124
- const fleetInvBytes = existsSync72(fleetInvPath) ? readFileSync63(fleetInvPath, "utf-8").length : 0;
88125
- const fleetClaudeBytes = existsSync72(fleetClaudePath) ? readFileSync63(fleetClaudePath, "utf-8").length : 0;
88213
+ const fleetDir = join72(agentsDir, "..", "fleet");
88214
+ const fleetInvPath = join72(fleetDir, "switchroom-invariants.md");
88215
+ const fleetClaudePath = join72(fleetDir, "CLAUDE.md");
88216
+ const fleetInvBytes = existsSync73(fleetInvPath) ? readFileSync64(fleetInvPath, "utf-8").length : 0;
88217
+ const fleetClaudeBytes = existsSync73(fleetClaudePath) ? readFileSync64(fleetClaudePath, "utf-8").length : 0;
88126
88218
  const fleetBytes = fleetInvBytes + fleetClaudeBytes;
88127
88219
  const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
88128
88220
  console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
@@ -88155,44 +88247,44 @@ init_source();
88155
88247
 
88156
88248
  // src/worktree/claim.ts
88157
88249
  import { execFileSync as execFileSync21 } from "node:child_process";
88158
- import { closeSync as closeSync13, mkdirSync as mkdirSync38, openSync as openSync13, existsSync as existsSync74, unlinkSync as unlinkSync16 } from "node:fs";
88159
- import { join as join73, resolve as resolve47 } from "node:path";
88160
- import { homedir as homedir44 } from "node:os";
88250
+ import { closeSync as closeSync13, mkdirSync as mkdirSync39, openSync as openSync13, existsSync as existsSync75, unlinkSync as unlinkSync17 } from "node:fs";
88251
+ import { join as join74, resolve as resolve47 } from "node:path";
88252
+ import { homedir as homedir45 } from "node:os";
88161
88253
  import { randomBytes as randomBytes13 } from "node:crypto";
88162
88254
 
88163
88255
  // src/worktree/registry.ts
88164
88256
  import {
88165
- mkdirSync as mkdirSync37,
88166
- writeFileSync as writeFileSync31,
88167
- readFileSync as readFileSync64,
88257
+ mkdirSync as mkdirSync38,
88258
+ writeFileSync as writeFileSync32,
88259
+ readFileSync as readFileSync65,
88168
88260
  readdirSync as readdirSync25,
88169
- unlinkSync as unlinkSync15,
88170
- existsSync as existsSync73,
88261
+ unlinkSync as unlinkSync16,
88262
+ existsSync as existsSync74,
88171
88263
  renameSync as renameSync16
88172
88264
  } from "node:fs";
88173
- import { join as join72, resolve as resolve46 } from "node:path";
88174
- import { homedir as homedir43 } from "node:os";
88265
+ import { join as join73, resolve as resolve46 } from "node:path";
88266
+ import { homedir as homedir44 } from "node:os";
88175
88267
  function registryDir() {
88176
- return resolve46(process.env.SWITCHROOM_WORKTREE_DIR ?? join72(homedir43(), ".switchroom", "worktrees"));
88268
+ return resolve46(process.env.SWITCHROOM_WORKTREE_DIR ?? join73(homedir44(), ".switchroom", "worktrees"));
88177
88269
  }
88178
88270
  function recordPath(id) {
88179
- return join72(registryDir(), `${id}.json`);
88271
+ return join73(registryDir(), `${id}.json`);
88180
88272
  }
88181
88273
  function ensureDir2() {
88182
- mkdirSync37(registryDir(), { recursive: true });
88274
+ mkdirSync38(registryDir(), { recursive: true });
88183
88275
  }
88184
88276
  function writeRecord(record2) {
88185
88277
  ensureDir2();
88186
88278
  const target = recordPath(record2.id);
88187
88279
  const tmp = `${target}.tmp${process.pid}`;
88188
- writeFileSync31(tmp, JSON.stringify(record2, null, 2) + `
88280
+ writeFileSync32(tmp, JSON.stringify(record2, null, 2) + `
88189
88281
  `, { mode: 384 });
88190
88282
  renameSync16(tmp, target);
88191
88283
  }
88192
88284
  function readRecord(id) {
88193
88285
  const path7 = recordPath(id);
88194
88286
  try {
88195
- const raw = readFileSync64(path7, "utf8");
88287
+ const raw = readFileSync65(path7, "utf8");
88196
88288
  return JSON.parse(raw);
88197
88289
  } catch {
88198
88290
  return null;
@@ -88201,7 +88293,7 @@ function readRecord(id) {
88201
88293
  function deleteRecord(id) {
88202
88294
  const path7 = recordPath(id);
88203
88295
  try {
88204
- unlinkSync15(path7);
88296
+ unlinkSync16(path7);
88205
88297
  } catch {}
88206
88298
  }
88207
88299
  function listRecords() {
@@ -88225,9 +88317,9 @@ function countByRepo(repoPath) {
88225
88317
  // src/worktree/claim.ts
88226
88318
  function acquireRepoLock(repoPath) {
88227
88319
  const lockDir = registryDir();
88228
- mkdirSync38(lockDir, { recursive: true });
88320
+ mkdirSync39(lockDir, { recursive: true });
88229
88321
  const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
88230
- const lockPath = join73(lockDir, `.lock-${lockName}`);
88322
+ const lockPath = join74(lockDir, `.lock-${lockName}`);
88231
88323
  const deadline = Date.now() + 5000;
88232
88324
  let fd = null;
88233
88325
  while (fd === null) {
@@ -88248,13 +88340,13 @@ function acquireRepoLock(repoPath) {
88248
88340
  closeSync13(fd);
88249
88341
  } catch {}
88250
88342
  try {
88251
- unlinkSync16(lockPath);
88343
+ unlinkSync17(lockPath);
88252
88344
  } catch {}
88253
88345
  };
88254
88346
  }
88255
88347
  var DEFAULT_CONCURRENCY = 5;
88256
88348
  function worktreesBaseDir() {
88257
- return resolve47(process.env.SWITCHROOM_WORKTREE_BASE ?? join73(homedir44(), ".switchroom", "worktree-checkouts"));
88349
+ return resolve47(process.env.SWITCHROOM_WORKTREE_BASE ?? join74(homedir45(), ".switchroom", "worktree-checkouts"));
88258
88350
  }
88259
88351
  function shortId() {
88260
88352
  return randomBytes13(4).toString("hex");
@@ -88276,12 +88368,12 @@ function resolveRepoPath(repo, codeRepos) {
88276
88368
  }
88277
88369
  function expandHome(p) {
88278
88370
  if (p.startsWith("~/"))
88279
- return join73(homedir44(), p.slice(2));
88371
+ return join74(homedir45(), p.slice(2));
88280
88372
  return p;
88281
88373
  }
88282
88374
  async function claimWorktree(input, codeRepos) {
88283
88375
  const repoPath = resolveRepoPath(input.repo, codeRepos);
88284
- if (!existsSync74(repoPath)) {
88376
+ if (!existsSync75(repoPath)) {
88285
88377
  throw new Error(`Repository path does not exist: ${repoPath}`);
88286
88378
  }
88287
88379
  let concurrencyCap = DEFAULT_CONCURRENCY;
@@ -88303,8 +88395,8 @@ async function claimWorktree(input, codeRepos) {
88303
88395
  const taskSuffix = input.taskName ? sanitizeTaskName(input.taskName) : "task";
88304
88396
  branch = `task/${taskSuffix}-${id}`;
88305
88397
  const baseDir = worktreesBaseDir();
88306
- mkdirSync38(baseDir, { recursive: true });
88307
- worktreePath = join73(baseDir, `${id}-${taskSuffix}`);
88398
+ mkdirSync39(baseDir, { recursive: true });
88399
+ worktreePath = join74(baseDir, `${id}-${taskSuffix}`);
88308
88400
  const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
88309
88401
  const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
88310
88402
  const now = new Date().toISOString();
@@ -88337,7 +88429,7 @@ async function claimWorktree(input, codeRepos) {
88337
88429
 
88338
88430
  // src/worktree/release.ts
88339
88431
  import { execFileSync as execFileSync22 } from "node:child_process";
88340
- import { existsSync as existsSync75 } from "node:fs";
88432
+ import { existsSync as existsSync76 } from "node:fs";
88341
88433
  function releaseWorktree(input) {
88342
88434
  const { id } = input;
88343
88435
  const record2 = readRecord(id);
@@ -88345,7 +88437,7 @@ function releaseWorktree(input) {
88345
88437
  return { released: true };
88346
88438
  }
88347
88439
  let gitSuccess = true;
88348
- if (existsSync75(record2.path)) {
88440
+ if (existsSync76(record2.path)) {
88349
88441
  try {
88350
88442
  execFileSync22("git", ["worktree", "remove", "--force", record2.path], {
88351
88443
  cwd: record2.repo,
@@ -88384,7 +88476,7 @@ function listWorktrees() {
88384
88476
 
88385
88477
  // src/worktree/reaper.ts
88386
88478
  import { execFileSync as execFileSync23 } from "node:child_process";
88387
- import { existsSync as existsSync76 } from "node:fs";
88479
+ import { existsSync as existsSync77 } from "node:fs";
88388
88480
  var STALE_THRESHOLD_MS = 10 * 60 * 1000;
88389
88481
  function isPathInUse(path7) {
88390
88482
  try {
@@ -88411,7 +88503,7 @@ function hasUncommittedChanges(repoPath, worktreePath) {
88411
88503
  function reapRecord(record2) {
88412
88504
  const { id, path: path7, repo, branch, ownerAgent } = record2;
88413
88505
  let warning = null;
88414
- if (existsSync76(path7)) {
88506
+ if (existsSync77(path7)) {
88415
88507
  if (hasUncommittedChanges(repo, path7)) {
88416
88508
  warning = `[worktree-reaper] Reaped worktree with uncommitted changes: ` + `id=${id} branch=${branch} agent=${ownerAgent ?? "unknown"} path=${path7}`;
88417
88509
  }
@@ -88432,7 +88524,7 @@ function runReaper(nowMs) {
88432
88524
  const warnings = [];
88433
88525
  for (const record2 of records) {
88434
88526
  const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
88435
- const worktreeExists = existsSync76(record2.path);
88527
+ const worktreeExists = existsSync77(record2.path);
88436
88528
  if (!worktreeExists) {
88437
88529
  deleteRecord(record2.id);
88438
88530
  reaped.push(record2.id);
@@ -88452,16 +88544,16 @@ function runReaper(nowMs) {
88452
88544
  // src/worktree/gc.ts
88453
88545
  import { execFileSync as execFileSync24 } from "node:child_process";
88454
88546
  import {
88455
- existsSync as existsSync77,
88456
- readFileSync as readFileSync65,
88547
+ existsSync as existsSync78,
88548
+ readFileSync as readFileSync66,
88457
88549
  readdirSync as readdirSync26,
88458
- statSync as statSync33,
88550
+ statSync as statSync34,
88459
88551
  renameSync as renameSync17,
88460
- mkdirSync as mkdirSync39,
88552
+ mkdirSync as mkdirSync40,
88461
88553
  rmSync as rmSync15
88462
88554
  } from "node:fs";
88463
- import { homedir as homedir45 } from "node:os";
88464
- import { join as join74, resolve as resolve48 } from "node:path";
88555
+ import { homedir as homedir46 } from "node:os";
88556
+ import { join as join75, resolve as resolve48 } from "node:path";
88465
88557
  function parseGitdirPointer(dotGitFileContents) {
88466
88558
  const m = /^gitdir:\s*(.+?)\s*$/m.exec(dotGitFileContents);
88467
88559
  return m ? m[1] : null;
@@ -88576,17 +88668,17 @@ function defaultPrSignal(repo, branch, exec) {
88576
88668
  }
88577
88669
  }
88578
88670
  function trashRoot() {
88579
- return resolve48(process.env.SWITCHROOM_WORKTREE_TRASH ?? join74(homedir45(), ".switchroom", "worktree-gc-trash"));
88671
+ return resolve48(process.env.SWITCHROOM_WORKTREE_TRASH ?? join75(homedir46(), ".switchroom", "worktree-gc-trash"));
88580
88672
  }
88581
88673
  function planGc(roots, deps = {}) {
88582
- const exists = deps.existsSync ?? existsSync77;
88674
+ const exists = deps.existsSync ?? existsSync78;
88583
88675
  const readDir = deps.readDir ?? ((p) => readdirSync26(p));
88584
- const readFile4 = deps.readFile ?? ((p) => readFileSync65(p, "utf8"));
88585
- const stat3 = deps.stat ?? ((p) => statSync33(p));
88676
+ const readFile4 = deps.readFile ?? ((p) => readFileSync66(p, "utf8"));
88677
+ const stat3 = deps.stat ?? ((p) => statSync34(p));
88586
88678
  const exec = deps.exec ?? defaultExec;
88587
88679
  const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
88588
88680
  const stamp = deps.dateStamp ?? "undated";
88589
- const trash = join74(trashRoot(), stamp);
88681
+ const trash = join75(trashRoot(), stamp);
88590
88682
  let claimed;
88591
88683
  try {
88592
88684
  claimed = new Set(listRecords().map((r) => resolve48(r.path)));
@@ -88622,10 +88714,10 @@ function planGc(roots, deps = {}) {
88622
88714
  continue;
88623
88715
  }
88624
88716
  for (const name of entries) {
88625
- const dir = join74(root, name);
88717
+ const dir = join75(root, name);
88626
88718
  if (isEphemeralPath(dir))
88627
88719
  continue;
88628
- const dotGit = join74(dir, ".git");
88720
+ const dotGit = join75(dir, ".git");
88629
88721
  if (!exists(dotGit))
88630
88722
  continue;
88631
88723
  let st;
@@ -88654,7 +88746,7 @@ function planGc(roots, deps = {}) {
88654
88746
  ownerRepos.add(repoRoot);
88655
88747
  if (exists(ptr))
88656
88748
  continue;
88657
- orphans.push({ dir, owner: repoRoot, dest: join74(trash, name) });
88749
+ orphans.push({ dir, owner: repoRoot, dest: join75(trash, name) });
88658
88750
  }
88659
88751
  }
88660
88752
  const registered = [];
@@ -88709,7 +88801,7 @@ function planGc(roots, deps = {}) {
88709
88801
  }
88710
88802
  function applyGc(plan, deps = {}) {
88711
88803
  const exec = deps.exec ?? defaultExec;
88712
- const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync39(p, { recursive: true }));
88804
+ const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync40(p, { recursive: true }));
88713
88805
  const move = deps.move ?? ((src, dest) => {
88714
88806
  try {
88715
88807
  renameSync17(src, dest);
@@ -88758,14 +88850,14 @@ function selectPurgeTargets(entries, olderThanDays) {
88758
88850
  return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
88759
88851
  }
88760
88852
  function listTrashEntries(nowMs, deps = {}) {
88761
- const exists = deps.existsSync ?? existsSync77;
88853
+ const exists = deps.existsSync ?? existsSync78;
88762
88854
  const readDir = deps.readDir ?? ((p) => readdirSync26(p));
88763
88855
  const root = trashRoot();
88764
88856
  if (!exists(root))
88765
88857
  return [];
88766
88858
  const out = [];
88767
88859
  for (const stamp of readDir(root)) {
88768
- const stampDir = join74(root, stamp);
88860
+ const stampDir = join75(root, stamp);
88769
88861
  let names;
88770
88862
  try {
88771
88863
  names = readDir(stampDir);
@@ -88773,10 +88865,10 @@ function listTrashEntries(nowMs, deps = {}) {
88773
88865
  continue;
88774
88866
  }
88775
88867
  for (const name of names) {
88776
- const p = join74(stampDir, name);
88868
+ const p = join75(stampDir, name);
88777
88869
  let mtimeMs = nowMs;
88778
88870
  try {
88779
- mtimeMs = statSync33(p).mtimeMs;
88871
+ mtimeMs = statSync34(p).mtimeMs;
88780
88872
  } catch {}
88781
88873
  out.push({ path: p, ageDays: (nowMs - mtimeMs) / 86400000 });
88782
88874
  }
@@ -88797,7 +88889,7 @@ function purgeTrash(paths) {
88797
88889
  return { deleted, errors: errors2 };
88798
88890
  }
88799
88891
  function defaultRoots() {
88800
- return [join74(homedir45(), "code")];
88892
+ return [join75(homedir46(), "code")];
88801
88893
  }
88802
88894
 
88803
88895
  // src/cli/worktree.ts
@@ -88986,12 +89078,12 @@ init_drive();
88986
89078
  init_scaffold_integration();
88987
89079
  import {
88988
89080
  chmodSync as chmodSync9,
88989
- mkdirSync as mkdirSync40,
89081
+ mkdirSync as mkdirSync41,
88990
89082
  readdirSync as readdirSync27,
88991
89083
  rmSync as rmSync16,
88992
- writeFileSync as writeFileSync32
89084
+ writeFileSync as writeFileSync33
88993
89085
  } from "node:fs";
88994
- import { join as join75 } from "node:path";
89086
+ import { join as join76 } from "node:path";
88995
89087
  function encodeCredentialsFilename(email) {
88996
89088
  const SAFE = new Set([
88997
89089
  ..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
@@ -89181,17 +89273,17 @@ function resolveCredentialsDir(env2) {
89181
89273
  if (explicit && explicit.length > 0)
89182
89274
  return explicit;
89183
89275
  const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
89184
- return join75(stateBase, "google-workspace-mcp", "credentials");
89276
+ return join76(stateBase, "google-workspace-mcp", "credentials");
89185
89277
  }
89186
89278
  function writeSeedFile(dir, email, seed) {
89187
- mkdirSync40(dir, { recursive: true, mode: 448 });
89279
+ mkdirSync41(dir, { recursive: true, mode: 448 });
89188
89280
  chmodSync9(dir, 448);
89189
89281
  for (const name of readdirSync27(dir)) {
89190
- rmSync16(join75(dir, name), { force: true, recursive: true });
89282
+ rmSync16(join76(dir, name), { force: true, recursive: true });
89191
89283
  }
89192
89284
  const filename = encodeCredentialsFilename(email);
89193
- const filePath = join75(dir, filename);
89194
- writeFileSync32(filePath, JSON.stringify(seed), { mode: 384 });
89285
+ const filePath = join76(dir, filename);
89286
+ writeFileSync33(filePath, JSON.stringify(seed), { mode: 384 });
89195
89287
  chmodSync9(filePath, 384);
89196
89288
  return filePath;
89197
89289
  }
@@ -89349,11 +89441,19 @@ function registerDriveMcpLauncherCommand(program3) {
89349
89441
  // src/cli/m365-mcp-launcher.ts
89350
89442
  init_scaffold_integration();
89351
89443
  import { spawn as spawn5 } from "node:child_process";
89352
- import { writeFileSync as writeFileSync33, mkdirSync as mkdirSync41 } from "node:fs";
89353
- import { dirname as dirname23, join as join76 } from "node:path";
89444
+ import { writeFileSync as writeFileSync34, mkdirSync as mkdirSync42 } from "node:fs";
89445
+ import { dirname as dirname24, join as join77 } from "node:path";
89354
89446
  var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
89355
89447
  var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
89356
89448
  var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
89449
+ var SOFTERIA_RESTART_BASE_MS = 1000;
89450
+ var SOFTERIA_RESTART_MAX_MS = 30 * 1000;
89451
+ var SOFTERIA_STABLE_MS = 60 * 1000;
89452
+ var SOFTERIA_MAX_RESTARTS = 6;
89453
+ function computeRestartBackoffMs(attempt) {
89454
+ const raw = SOFTERIA_RESTART_BASE_MS * 2 ** Math.max(0, attempt);
89455
+ return Math.min(raw, SOFTERIA_RESTART_MAX_MS);
89456
+ }
89357
89457
  function buildSofteriaArgs(opts = {}) {
89358
89458
  const pkg = `${MICROSOFT_WORKSPACE_MCP_PACKAGE}@${MICROSOFT_WORKSPACE_MCP_PINNED_VERSION}`;
89359
89459
  const args = ["-y", pkg];
@@ -89375,14 +89475,14 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
89375
89475
  function writeRefreshHeartbeat(agentName, data) {
89376
89476
  const path7 = heartbeatPath(agentName);
89377
89477
  try {
89378
- mkdirSync41(dirname23(path7), { recursive: true });
89379
- writeFileSync33(path7, JSON.stringify(data, null, 2), { mode: 420 });
89478
+ mkdirSync42(dirname24(path7), { recursive: true });
89479
+ writeFileSync34(path7, JSON.stringify(data, null, 2), { mode: 420 });
89380
89480
  } catch {}
89381
89481
  }
89382
89482
  function heartbeatPath(agentName) {
89383
89483
  const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
89384
89484
  if (override) {
89385
- return join76(override, `m365-launcher-${agentName}.heartbeat.json`);
89485
+ return join77(override, `m365-launcher-${agentName}.heartbeat.json`);
89386
89486
  }
89387
89487
  return "/state/agent/m365-launcher.heartbeat.json";
89388
89488
  }
@@ -89441,13 +89541,23 @@ async function runMs365McpLauncher(opts, rt) {
89441
89541
  let currentChild = null;
89442
89542
  let teardownStdio = null;
89443
89543
  let refreshTimer = null;
89544
+ let restartTimer = null;
89444
89545
  let restartingForRefresh = false;
89546
+ let shuttingDown = false;
89445
89547
  let resolveLauncher = null;
89548
+ let currentCreds = null;
89549
+ let restartAttempts = 0;
89550
+ let lastSpawnMs = 0;
89446
89551
  const exitLauncher = (code) => {
89552
+ shuttingDown = true;
89447
89553
  if (refreshTimer) {
89448
89554
  clearTimer(refreshTimer);
89449
89555
  refreshTimer = null;
89450
89556
  }
89557
+ if (restartTimer) {
89558
+ clearTimer(restartTimer);
89559
+ restartTimer = null;
89560
+ }
89451
89561
  if (resolveLauncher) {
89452
89562
  const r = resolveLauncher;
89453
89563
  resolveLauncher = null;
@@ -89457,6 +89567,7 @@ async function runMs365McpLauncher(opts, rt) {
89457
89567
  const launchChild = (accessToken) => {
89458
89568
  const env2 = buildSofteriaEnv(accessToken);
89459
89569
  const child = rt.spawnSofteria(env2);
89570
+ lastSpawnMs = now();
89460
89571
  teardownStdio = wireStdio(child);
89461
89572
  child.once("exit", (code, signal) => {
89462
89573
  if (teardownStdio) {
@@ -89467,6 +89578,35 @@ async function runMs365McpLauncher(opts, rt) {
89467
89578
  return;
89468
89579
  }
89469
89580
  const resolved = code ?? (signal ? 128 : 0);
89581
+ if (shuttingDown)
89582
+ return;
89583
+ const crashed = resolved !== 0;
89584
+ const tokenValid = currentCreds != null && currentCreds.expiresAt > now();
89585
+ if (crashed && tokenValid) {
89586
+ if (now() - lastSpawnMs >= SOFTERIA_STABLE_MS)
89587
+ restartAttempts = 0;
89588
+ if (restartAttempts < SOFTERIA_MAX_RESTARTS) {
89589
+ const delayMs = computeRestartBackoffMs(restartAttempts);
89590
+ restartAttempts += 1;
89591
+ currentChild = null;
89592
+ log(`m365-launcher: softeria exited (code=${resolved} signal=${signal}); re-spawning with cached token in ${Math.round(delayMs / 1000)}s (attempt ${restartAttempts}/${SOFTERIA_MAX_RESTARTS}, no broker call)`);
89593
+ restartTimer = setTimer(() => {
89594
+ restartTimer = null;
89595
+ if (shuttingDown)
89596
+ return;
89597
+ if (currentCreds != null && currentCreds.expiresAt > now()) {
89598
+ currentChild = launchChild(currentCreds.accessToken);
89599
+ } else {
89600
+ log("m365-launcher: cached token expired during backoff \u2014 exiting for fresh creds");
89601
+ exitLauncher(resolved);
89602
+ }
89603
+ }, delayMs);
89604
+ return;
89605
+ }
89606
+ log(`m365-launcher: softeria crash-looped ${restartAttempts}x within ${Math.round(SOFTERIA_STABLE_MS / 1000)}s \u2014 giving up, exiting`);
89607
+ exitLauncher(resolved);
89608
+ return;
89609
+ }
89470
89610
  log(`m365-launcher: softeria exited unexpectedly code=${resolved} signal=${signal}`);
89471
89611
  exitLauncher(resolved);
89472
89612
  });
@@ -89484,6 +89624,10 @@ async function runMs365McpLauncher(opts, rt) {
89484
89624
  refreshTimer = setTimer(async () => {
89485
89625
  try {
89486
89626
  log("m365-launcher: refreshing token + restarting softeria");
89627
+ if (restartTimer) {
89628
+ clearTimer(restartTimer);
89629
+ restartTimer = null;
89630
+ }
89487
89631
  restartingForRefresh = true;
89488
89632
  try {
89489
89633
  process.stdin.pause();
@@ -89493,6 +89637,8 @@ async function runMs365McpLauncher(opts, rt) {
89493
89637
  await killChild(currentChild);
89494
89638
  }
89495
89639
  restartingForRefresh = false;
89640
+ currentCreds = fresh;
89641
+ restartAttempts = 0;
89496
89642
  currentChild = launchChild(fresh.accessToken);
89497
89643
  try {
89498
89644
  process.stdin.resume();
@@ -89519,6 +89665,7 @@ async function runMs365McpLauncher(opts, rt) {
89519
89665
  log(`m365-launcher: initial broker call failed \u2014 ${msg}`);
89520
89666
  return 1;
89521
89667
  }
89668
+ currentCreds = initial;
89522
89669
  currentChild = launchChild(initial.accessToken);
89523
89670
  scheduleRefresh(initial.expiresAt);
89524
89671
  const onSignal = (signal) => {
@@ -89567,8 +89714,8 @@ function registerM365McpLauncherCommand(program3) {
89567
89714
  // src/cli/notion-mcp-launcher.ts
89568
89715
  init_scaffold_integration();
89569
89716
  import { spawn as spawn6 } from "node:child_process";
89570
- import { existsSync as existsSync78, mkdirSync as mkdirSync42, writeFileSync as writeFileSync34 } from "node:fs";
89571
- import { dirname as dirname24 } from "node:path";
89717
+ import { existsSync as existsSync79, mkdirSync as mkdirSync43, writeFileSync as writeFileSync35 } from "node:fs";
89718
+ import { dirname as dirname25 } from "node:path";
89572
89719
  var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
89573
89720
  var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
89574
89721
  var DEFAULT_VAULT_KEY = "notion/integration-token";
@@ -89578,10 +89725,10 @@ function buildNotionMcpArgs(opts) {
89578
89725
  }
89579
89726
  function defaultWriteHeartbeat(path7, contents) {
89580
89727
  try {
89581
- const dir = dirname24(path7);
89582
- if (!existsSync78(dir))
89583
- mkdirSync42(dir, { recursive: true });
89584
- writeFileSync34(path7, contents);
89728
+ const dir = dirname25(path7);
89729
+ if (!existsSync79(dir))
89730
+ mkdirSync43(dir, { recursive: true });
89731
+ writeFileSync35(path7, contents);
89585
89732
  } catch {}
89586
89733
  }
89587
89734
  async function runNotionMcpLauncher(opts, runtime) {
@@ -89691,7 +89838,7 @@ function registerNotionMcpLauncherCommand(program3) {
89691
89838
 
89692
89839
  // src/cli/deliver-file.ts
89693
89840
  init_client2();
89694
- import { readFileSync as readFileSync66, statSync as statSync34 } from "node:fs";
89841
+ import { readFileSync as readFileSync67, statSync as statSync35 } from "node:fs";
89695
89842
  import { basename as basename11 } from "node:path";
89696
89843
 
89697
89844
  // src/delivery/onedrive.ts
@@ -90030,8 +90177,8 @@ async function defaultResolveProvider() {
90030
90177
  }
90031
90178
  async function runDeliverFile(localPath, deps = {}) {
90032
90179
  const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
90033
- const sizeOf = deps.fileSize ?? ((p) => statSync34(p).size);
90034
- const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync66(p)));
90180
+ const sizeOf = deps.fileSize ?? ((p) => statSync35(p).size);
90181
+ const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync67(p)));
90035
90182
  const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
90036
90183
  let size;
90037
90184
  try {
@@ -90352,9 +90499,9 @@ function runRedactStdin() {
90352
90499
  }
90353
90500
 
90354
90501
  // src/cli/status-ask.ts
90355
- import { readFileSync as readFileSync70, existsSync as existsSync83, readdirSync as readdirSync29 } from "node:fs";
90356
- import { join as join81 } from "node:path";
90357
- import { homedir as homedir48 } from "node:os";
90502
+ import { readFileSync as readFileSync71, existsSync as existsSync84, readdirSync as readdirSync29 } from "node:fs";
90503
+ import { join as join82 } from "node:path";
90504
+ import { homedir as homedir49 } from "node:os";
90358
90505
 
90359
90506
  // src/status-ask/report.ts
90360
90507
  function parseJsonl(content) {
@@ -90628,7 +90775,7 @@ function runReport(opts) {
90628
90775
  for (const src of sources) {
90629
90776
  let content;
90630
90777
  try {
90631
- content = readFileSync70(src.path, "utf-8");
90778
+ content = readFileSync71(src.path, "utf-8");
90632
90779
  } catch (err) {
90633
90780
  process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
90634
90781
  `);
@@ -90675,7 +90822,7 @@ function runReport(opts) {
90675
90822
  function resolveSources(explicitPath) {
90676
90823
  if (explicitPath != null && explicitPath.trim() !== "") {
90677
90824
  const trimmed = explicitPath.trim();
90678
- if (!existsSync83(trimmed)) {
90825
+ if (!existsSync84(trimmed)) {
90679
90826
  process.stderr.write(`status-ask report: ${trimmed}: file not found
90680
90827
  `);
90681
90828
  process.exit(1);
@@ -90689,9 +90836,9 @@ function resolveSources(explicitPath) {
90689
90836
  const config = loadConfig();
90690
90837
  agentsDir = resolveAgentsDir(config);
90691
90838
  } catch {
90692
- agentsDir = join81(homedir48(), ".switchroom", "agents");
90839
+ agentsDir = join82(homedir49(), ".switchroom", "agents");
90693
90840
  }
90694
- if (!existsSync83(agentsDir))
90841
+ if (!existsSync84(agentsDir))
90695
90842
  return [];
90696
90843
  const sources = [];
90697
90844
  let entries;
@@ -90701,8 +90848,8 @@ function resolveSources(explicitPath) {
90701
90848
  return [];
90702
90849
  }
90703
90850
  for (const name of entries) {
90704
- const path8 = join81(agentsDir, name, "runtime-metrics.jsonl");
90705
- if (existsSync83(path8)) {
90851
+ const path8 = join82(agentsDir, name, "runtime-metrics.jsonl");
90852
+ if (existsSync84(path8)) {
90706
90853
  sources.push({ path: path8, agent: name });
90707
90854
  }
90708
90855
  }
@@ -90732,45 +90879,45 @@ var import_yaml21 = __toESM(require_dist(), 1);
90732
90879
  init_paths();
90733
90880
  import {
90734
90881
  closeSync as closeSync14,
90735
- existsSync as existsSync84,
90882
+ existsSync as existsSync85,
90736
90883
  fsyncSync as fsyncSync8,
90737
- mkdirSync as mkdirSync47,
90884
+ mkdirSync as mkdirSync48,
90738
90885
  openSync as openSync14,
90739
90886
  readdirSync as readdirSync30,
90740
- readFileSync as readFileSync71,
90887
+ readFileSync as readFileSync72,
90741
90888
  renameSync as renameSync19,
90742
- statSync as statSync35,
90743
- unlinkSync as unlinkSync17,
90889
+ statSync as statSync36,
90890
+ unlinkSync as unlinkSync18,
90744
90891
  writeSync as writeSync10
90745
90892
  } from "node:fs";
90746
- import { join as join82, resolve as resolve51 } from "node:path";
90893
+ import { join as join83, resolve as resolve51 } from "node:path";
90747
90894
  var STAGING_SUBDIR = ".staging";
90748
90895
  function overlayPathsFor(agent, opts = {}) {
90749
90896
  const base = opts.root ? resolve51(opts.root, agent) : resolve51(resolveDualPath(`~/.switchroom/agents/${agent}`));
90750
- const scheduleDir = join82(base, "schedule.d");
90751
- const scheduleStagingDir = join82(scheduleDir, STAGING_SUBDIR);
90752
- const skillsDir = join82(base, "skills.d");
90753
- const skillsStagingDir = join82(skillsDir, STAGING_SUBDIR);
90897
+ const scheduleDir = join83(base, "schedule.d");
90898
+ const scheduleStagingDir = join83(scheduleDir, STAGING_SUBDIR);
90899
+ const skillsDir = join83(base, "skills.d");
90900
+ const skillsStagingDir = join83(skillsDir, STAGING_SUBDIR);
90754
90901
  return {
90755
90902
  agentRoot: base,
90756
90903
  scheduleDir,
90757
90904
  scheduleStagingDir,
90758
90905
  skillsDir,
90759
90906
  skillsStagingDir,
90760
- lockPath: join82(base, ".lock"),
90907
+ lockPath: join83(base, ".lock"),
90761
90908
  stagingDir: scheduleStagingDir
90762
90909
  };
90763
90910
  }
90764
90911
  function ensureDirs(paths) {
90765
- mkdirSync47(paths.scheduleDir, { recursive: true });
90766
- mkdirSync47(paths.scheduleStagingDir, { recursive: true });
90912
+ mkdirSync48(paths.scheduleDir, { recursive: true });
90913
+ mkdirSync48(paths.scheduleStagingDir, { recursive: true });
90767
90914
  }
90768
90915
  function ensureSkillsDirs(paths) {
90769
- mkdirSync47(paths.skillsDir, { recursive: true });
90770
- mkdirSync47(paths.skillsStagingDir, { recursive: true });
90916
+ mkdirSync48(paths.skillsDir, { recursive: true });
90917
+ mkdirSync48(paths.skillsStagingDir, { recursive: true });
90771
90918
  }
90772
90919
  function withAgentLock(paths, fn) {
90773
- mkdirSync47(paths.agentRoot, { recursive: true });
90920
+ mkdirSync48(paths.agentRoot, { recursive: true });
90774
90921
  const start = Date.now();
90775
90922
  const TIMEOUT_MS = 5000;
90776
90923
  let fd = null;
@@ -90783,9 +90930,9 @@ function withAgentLock(paths, fn) {
90783
90930
  if (e.code !== "EEXIST")
90784
90931
  throw err;
90785
90932
  try {
90786
- const age = Date.now() - statSync35(paths.lockPath).mtimeMs;
90933
+ const age = Date.now() - statSync36(paths.lockPath).mtimeMs;
90787
90934
  if (age > 30000) {
90788
- unlinkSync17(paths.lockPath);
90935
+ unlinkSync18(paths.lockPath);
90789
90936
  continue;
90790
90937
  }
90791
90938
  } catch {}
@@ -90803,7 +90950,7 @@ function withAgentLock(paths, fn) {
90803
90950
  closeSync14(fd);
90804
90951
  } catch {}
90805
90952
  try {
90806
- unlinkSync17(paths.lockPath);
90953
+ unlinkSync18(paths.lockPath);
90807
90954
  } catch {}
90808
90955
  }
90809
90956
  }
@@ -90811,8 +90958,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
90811
90958
  const paths = overlayPathsFor(agent, opts);
90812
90959
  return withAgentLock(paths, () => {
90813
90960
  ensureDirs(paths);
90814
- const stagingPath = join82(paths.scheduleStagingDir, `${slug}.yaml`);
90815
- const finalPath = join82(paths.scheduleDir, `${slug}.yaml`);
90961
+ const stagingPath = join83(paths.scheduleStagingDir, `${slug}.yaml`);
90962
+ const finalPath = join83(paths.scheduleDir, `${slug}.yaml`);
90816
90963
  const fd = openSync14(stagingPath, "w", 384);
90817
90964
  try {
90818
90965
  writeSync10(fd, yamlText);
@@ -90828,8 +90975,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
90828
90975
  const paths = overlayPathsFor(agent, opts);
90829
90976
  return withAgentLock(paths, () => {
90830
90977
  ensureSkillsDirs(paths);
90831
- const stagingPath = join82(paths.skillsStagingDir, `${slug}.yaml`);
90832
- const finalPath = join82(paths.skillsDir, `${slug}.yaml`);
90978
+ const stagingPath = join83(paths.skillsStagingDir, `${slug}.yaml`);
90979
+ const finalPath = join83(paths.skillsDir, `${slug}.yaml`);
90833
90980
  const fd = openSync14(stagingPath, "w", 384);
90834
90981
  try {
90835
90982
  writeSync10(fd, yamlText);
@@ -90844,24 +90991,24 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
90844
90991
  function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
90845
90992
  const paths = overlayPathsFor(agent, opts);
90846
90993
  return withAgentLock(paths, () => {
90847
- const finalPath = join82(paths.skillsDir, `${slug}.yaml`);
90848
- if (!existsSync84(finalPath))
90994
+ const finalPath = join83(paths.skillsDir, `${slug}.yaml`);
90995
+ if (!existsSync85(finalPath))
90849
90996
  return false;
90850
- unlinkSync17(finalPath);
90997
+ unlinkSync18(finalPath);
90851
90998
  return true;
90852
90999
  });
90853
91000
  }
90854
91001
  function listSkillsOverlayEntries(agent, opts = {}) {
90855
91002
  const paths = overlayPathsFor(agent, opts);
90856
- if (!existsSync84(paths.skillsDir))
91003
+ if (!existsSync85(paths.skillsDir))
90857
91004
  return [];
90858
91005
  const out = [];
90859
91006
  for (const name of readdirSync30(paths.skillsDir)) {
90860
91007
  if (!/\.ya?ml$/i.test(name))
90861
91008
  continue;
90862
- const full = join82(paths.skillsDir, name);
91009
+ const full = join83(paths.skillsDir, name);
90863
91010
  try {
90864
- const raw = readFileSync71(full, "utf-8");
91011
+ const raw = readFileSync72(full, "utf-8");
90865
91012
  const slug = name.replace(/\.ya?ml$/i, "");
90866
91013
  out.push({ slug, path: full, raw });
90867
91014
  } catch {}
@@ -90871,24 +91018,24 @@ function listSkillsOverlayEntries(agent, opts = {}) {
90871
91018
  function deleteOverlayEntry(agent, slug, opts = {}) {
90872
91019
  const paths = overlayPathsFor(agent, opts);
90873
91020
  return withAgentLock(paths, () => {
90874
- const finalPath = join82(paths.scheduleDir, `${slug}.yaml`);
90875
- if (!existsSync84(finalPath))
91021
+ const finalPath = join83(paths.scheduleDir, `${slug}.yaml`);
91022
+ if (!existsSync85(finalPath))
90876
91023
  return false;
90877
- unlinkSync17(finalPath);
91024
+ unlinkSync18(finalPath);
90878
91025
  return true;
90879
91026
  });
90880
91027
  }
90881
91028
  function listOverlayEntries(agent, opts = {}) {
90882
91029
  const paths = overlayPathsFor(agent, opts);
90883
- if (!existsSync84(paths.scheduleDir))
91030
+ if (!existsSync85(paths.scheduleDir))
90884
91031
  return [];
90885
91032
  const out = [];
90886
91033
  for (const name of readdirSync30(paths.scheduleDir)) {
90887
91034
  if (!/\.ya?ml$/i.test(name))
90888
91035
  continue;
90889
- const full = join82(paths.scheduleDir, name);
91036
+ const full = join83(paths.scheduleDir, name);
90890
91037
  try {
90891
- const raw = readFileSync71(full, "utf-8");
91038
+ const raw = readFileSync72(full, "utf-8");
90892
91039
  const slug = name.replace(/\.ya?ml$/i, "");
90893
91040
  out.push({ slug, path: full, raw });
90894
91041
  } catch {}
@@ -91039,27 +91186,27 @@ function reconcileAgentCronOnly(agent) {
91039
91186
  // src/cli/agent-config-pending.ts
91040
91187
  import {
91041
91188
  closeSync as closeSync15,
91042
- existsSync as existsSync85,
91189
+ existsSync as existsSync86,
91043
91190
  fsyncSync as fsyncSync9,
91044
- mkdirSync as mkdirSync48,
91191
+ mkdirSync as mkdirSync49,
91045
91192
  openSync as openSync15,
91046
91193
  readdirSync as readdirSync31,
91047
- readFileSync as readFileSync72,
91194
+ readFileSync as readFileSync73,
91048
91195
  renameSync as renameSync20,
91049
- unlinkSync as unlinkSync18,
91050
- writeFileSync as writeFileSync39,
91196
+ unlinkSync as unlinkSync19,
91197
+ writeFileSync as writeFileSync40,
91051
91198
  writeSync as writeSync11
91052
91199
  } from "node:fs";
91053
- import { join as join83 } from "node:path";
91200
+ import { join as join84 } from "node:path";
91054
91201
  import { randomBytes as randomBytes15 } from "node:crypto";
91055
91202
  var STAGE_ID_PREFIX = "cap_";
91056
91203
  function pendingDir(agent, opts = {}) {
91057
91204
  const paths = overlayPathsFor(agent, opts);
91058
- return join83(paths.scheduleDir, ".pending");
91205
+ return join84(paths.scheduleDir, ".pending");
91059
91206
  }
91060
91207
  function ensurePendingDir(agent, opts = {}) {
91061
91208
  const dir = pendingDir(agent, opts);
91062
- mkdirSync48(dir, { recursive: true });
91209
+ mkdirSync49(dir, { recursive: true });
91063
91210
  return dir;
91064
91211
  }
91065
91212
  function newStageId() {
@@ -91068,8 +91215,8 @@ function newStageId() {
91068
91215
  function stagePendingScheduleEntry(opts) {
91069
91216
  const dir = ensurePendingDir(opts.agent, { root: opts.root });
91070
91217
  const stageId = opts.stageId ?? newStageId();
91071
- const yamlPath = join83(dir, `${stageId}.yaml`);
91072
- const metaPath = join83(dir, `${stageId}.meta.json`);
91218
+ const yamlPath = join84(dir, `${stageId}.yaml`);
91219
+ const metaPath = join84(dir, `${stageId}.meta.json`);
91073
91220
  const meta = {
91074
91221
  v: 1,
91075
91222
  stage_id: stageId,
@@ -91090,25 +91237,25 @@ function stagePendingScheduleEntry(opts) {
91090
91237
  }
91091
91238
  renameSync20(yamlTmp, yamlPath);
91092
91239
  }
91093
- writeFileSync39(metaPath, JSON.stringify(meta, null, 2) + `
91240
+ writeFileSync40(metaPath, JSON.stringify(meta, null, 2) + `
91094
91241
  `, { mode: 384 });
91095
91242
  return { stageId, yamlPath, metaPath };
91096
91243
  }
91097
91244
  function listPendingScheduleEntries(agent, opts = {}) {
91098
91245
  const dir = pendingDir(agent, opts);
91099
- if (!existsSync85(dir))
91246
+ if (!existsSync86(dir))
91100
91247
  return [];
91101
91248
  const out = [];
91102
91249
  for (const name of readdirSync31(dir).sort()) {
91103
91250
  if (!name.endsWith(".meta.json"))
91104
91251
  continue;
91105
91252
  const stageId = name.slice(0, -".meta.json".length);
91106
- const metaPath = join83(dir, name);
91107
- const yamlPath = join83(dir, `${stageId}.yaml`);
91108
- if (!existsSync85(yamlPath))
91253
+ const metaPath = join84(dir, name);
91254
+ const yamlPath = join84(dir, `${stageId}.yaml`);
91255
+ if (!existsSync86(yamlPath))
91109
91256
  continue;
91110
91257
  try {
91111
- const meta = JSON.parse(readFileSync72(metaPath, "utf-8"));
91258
+ const meta = JSON.parse(readFileSync73(metaPath, "utf-8"));
91112
91259
  if (meta?.v !== 1 || typeof meta.stage_id !== "string")
91113
91260
  continue;
91114
91261
  out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
@@ -91123,12 +91270,12 @@ function commitPendingScheduleEntry(opts) {
91123
91270
  return { committed: false, reason: "not_found" };
91124
91271
  const slug = match.meta.entry.name ?? match.stageId;
91125
91272
  const paths = overlayPathsFor(opts.agent, { root: opts.root });
91126
- const finalPath = join83(paths.scheduleDir, `${slug}.yaml`);
91127
- if (existsSync85(finalPath)) {
91273
+ const finalPath = join84(paths.scheduleDir, `${slug}.yaml`);
91274
+ if (existsSync86(finalPath)) {
91128
91275
  return { committed: false, reason: "slug_collision" };
91129
91276
  }
91130
91277
  renameSync20(match.yamlPath, finalPath);
91131
- unlinkSync18(match.metaPath);
91278
+ unlinkSync19(match.metaPath);
91132
91279
  return { committed: true, path: finalPath, slug };
91133
91280
  }
91134
91281
  function denyPendingScheduleEntry(opts) {
@@ -91137,16 +91284,16 @@ function denyPendingScheduleEntry(opts) {
91137
91284
  if (!match)
91138
91285
  return { denied: false, reason: "not_found" };
91139
91286
  try {
91140
- unlinkSync18(match.yamlPath);
91287
+ unlinkSync19(match.yamlPath);
91141
91288
  } catch {}
91142
91289
  try {
91143
- unlinkSync18(match.metaPath);
91290
+ unlinkSync19(match.metaPath);
91144
91291
  } catch {}
91145
91292
  return { denied: true };
91146
91293
  }
91147
91294
 
91148
91295
  // src/cli/agent-config-write.ts
91149
- import { existsSync as existsSync86, readFileSync as readFileSync73 } from "node:fs";
91296
+ import { existsSync as existsSync87, readFileSync as readFileSync74 } from "node:fs";
91150
91297
  import { execFileSync as execFileSync26 } from "node:child_process";
91151
91298
 
91152
91299
  // src/scheduler/schedule-report.ts
@@ -91558,8 +91705,8 @@ function scheduleRemove(opts) {
91558
91705
  }
91559
91706
  let priorContent = null;
91560
91707
  try {
91561
- if (existsSync86(match.path))
91562
- priorContent = readFileSync73(match.path, "utf-8");
91708
+ if (existsSync87(match.path))
91709
+ priorContent = readFileSync74(match.path, "utf-8");
91563
91710
  } catch {}
91564
91711
  deleteOverlayEntry(agent, match.slug, { root: opts.root });
91565
91712
  const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
@@ -91762,7 +91909,7 @@ function registerAgentConfigWriteCommands(program3) {
91762
91909
  }
91763
91910
  let blob;
91764
91911
  if (opts.jsonl) {
91765
- blob = existsSync86(opts.jsonl) ? readFileSync73(opts.jsonl, "utf-8") : "";
91912
+ blob = existsSync87(opts.jsonl) ? readFileSync74(opts.jsonl, "utf-8") : "";
91766
91913
  } else {
91767
91914
  try {
91768
91915
  blob = execFileSync26("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
@@ -91794,11 +91941,11 @@ function registerAgentConfigWriteCommands(program3) {
91794
91941
 
91795
91942
  // src/cli/agent-config-skill-write.ts
91796
91943
  var import_yaml22 = __toESM(require_dist(), 1);
91797
- import { existsSync as existsSync87 } from "node:fs";
91944
+ import { existsSync as existsSync88 } from "node:fs";
91798
91945
  init_reconcile_default_skills();
91799
91946
  init_agent_config();
91800
91947
  var import_yaml23 = __toESM(require_dist(), 1);
91801
- import { join as join84 } from "node:path";
91948
+ import { join as join85 } from "node:path";
91802
91949
  var MAX_SKILLS_PER_AGENT = 20;
91803
91950
  var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
91804
91951
  function exitCodeFor2(code) {
@@ -91873,8 +92020,8 @@ function skillInstall(opts) {
91873
92020
  return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
91874
92021
  }
91875
92022
  const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
91876
- const skillPath = join84(poolDir, skillName);
91877
- if (!existsSync87(skillPath)) {
92023
+ const skillPath = join85(poolDir, skillName);
92024
+ if (!existsSync88(skillPath)) {
91878
92025
  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.`);
91879
92026
  }
91880
92027
  const yamlText = import_yaml22.stringify({ skills: [skillName] });
@@ -92038,21 +92185,21 @@ function registerAgentConfigSkillWriteCommands(program3) {
92038
92185
  // src/cli/skill.ts
92039
92186
  import {
92040
92187
  closeSync as closeSync16,
92041
- existsSync as existsSync88,
92188
+ existsSync as existsSync89,
92042
92189
  lstatSync as lstatSync9,
92043
- mkdirSync as mkdirSync49,
92190
+ mkdirSync as mkdirSync50,
92044
92191
  mkdtempSync as mkdtempSync5,
92045
92192
  openSync as openSync16,
92046
- readFileSync as readFileSync74,
92193
+ readFileSync as readFileSync75,
92047
92194
  readdirSync as readdirSync32,
92048
92195
  realpathSync as realpathSync7,
92049
92196
  renameSync as renameSync21,
92050
92197
  rmSync as rmSync18,
92051
- statSync as statSync36,
92052
- writeFileSync as writeFileSync40
92198
+ statSync as statSync37,
92199
+ writeFileSync as writeFileSync41
92053
92200
  } from "node:fs";
92054
- import { tmpdir as tmpdir5, homedir as homedir49 } from "node:os";
92055
- import { dirname as dirname29, join as join85, relative as relative2, resolve as resolve52 } from "node:path";
92201
+ import { tmpdir as tmpdir5, homedir as homedir50 } from "node:os";
92202
+ import { dirname as dirname30, join as join86, relative as relative2, resolve as resolve52 } from "node:path";
92056
92203
  import { spawnSync as spawnSync15 } from "node:child_process";
92057
92204
 
92058
92205
  // src/cli/skill-common.ts
@@ -92286,10 +92433,10 @@ function scanForClaudeP2(content) {
92286
92433
  function resolveSkillsPoolDir2(override) {
92287
92434
  const raw = override ?? "~/.switchroom/skills";
92288
92435
  if (raw.startsWith("~/")) {
92289
- return join85(homedir49(), raw.slice(2));
92436
+ return join86(homedir50(), raw.slice(2));
92290
92437
  }
92291
92438
  if (raw === "~")
92292
- return homedir49();
92439
+ return homedir50();
92293
92440
  return resolve52(raw);
92294
92441
  }
92295
92442
  function readStdinSync() {
@@ -92318,14 +92465,14 @@ function isTarballPath(p) {
92318
92465
  }
92319
92466
  function loadFromDir(dir) {
92320
92467
  const abs = realpathSync7(dir);
92321
- if (!statSync36(abs).isDirectory()) {
92468
+ if (!statSync37(abs).isDirectory()) {
92322
92469
  fail3(`--from path is not a directory: ${dir}`);
92323
92470
  }
92324
92471
  const files = {};
92325
92472
  const walk2 = (sub) => {
92326
92473
  const entries = readdirSync32(sub, { withFileTypes: true });
92327
92474
  for (const ent of entries) {
92328
- const full = join85(sub, ent.name);
92475
+ const full = join86(sub, ent.name);
92329
92476
  const rel = relative2(abs, full);
92330
92477
  if (ent.isSymbolicLink()) {
92331
92478
  fail3(`refusing to read symlink inside --from dir: ${rel}`);
@@ -92335,7 +92482,7 @@ function loadFromDir(dir) {
92335
92482
  continue;
92336
92483
  }
92337
92484
  if (ent.isFile()) {
92338
- const buf = readFileSync74(full);
92485
+ const buf = readFileSync75(full);
92339
92486
  files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
92340
92487
  }
92341
92488
  }
@@ -92360,7 +92507,7 @@ function loadFromTarball(tarPath) {
92360
92507
  fail3(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
92361
92508
  }
92362
92509
  }
92363
- const staging = mkdtempSync5(join85(tmpdir5(), "skill-apply-extract-"));
92510
+ const staging = mkdtempSync5(join86(tmpdir5(), "skill-apply-extract-"));
92364
92511
  try {
92365
92512
  const flags = isGz ? ["-xzf"] : ["-xf"];
92366
92513
  const r = spawnSync15("tar", [
@@ -92384,7 +92531,7 @@ function loadFromTarball(tarPath) {
92384
92531
  }
92385
92532
  }
92386
92533
  function loadSingleFile(filePath) {
92387
- const content = readFileSync74(filePath, "utf-8");
92534
+ const content = readFileSync75(filePath, "utf-8");
92388
92535
  return { "SKILL.md": content };
92389
92536
  }
92390
92537
  function loadFromStdin() {
@@ -92446,10 +92593,10 @@ function validatePayload(name, files) {
92446
92593
  errors2.push(`${path8} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
92447
92594
  }
92448
92595
  } else if (PY_SCRIPT_RE2.test(path8)) {
92449
- const tmp = mkdtempSync5(join85(tmpdir5(), "skill-apply-py-"));
92450
- const tmpPy = join85(tmp, "check.py");
92596
+ const tmp = mkdtempSync5(join86(tmpdir5(), "skill-apply-py-"));
92597
+ const tmpPy = join86(tmp, "check.py");
92451
92598
  try {
92452
- writeFileSync40(tmpPy, content);
92599
+ writeFileSync41(tmpPy, content);
92453
92600
  const r = spawnSync15("python3", ["-m", "py_compile", tmpPy], {
92454
92601
  encoding: "utf-8"
92455
92602
  });
@@ -92467,15 +92614,15 @@ function validatePayload(name, files) {
92467
92614
  function diffSummary(currentDir, files) {
92468
92615
  const lines = [];
92469
92616
  const currentFiles = {};
92470
- if (existsSync88(currentDir)) {
92617
+ if (existsSync89(currentDir)) {
92471
92618
  const walk2 = (sub) => {
92472
92619
  for (const ent of readdirSync32(sub, { withFileTypes: true })) {
92473
- const full = join85(sub, ent.name);
92620
+ const full = join86(sub, ent.name);
92474
92621
  const rel = relative2(currentDir, full);
92475
92622
  if (ent.isDirectory()) {
92476
92623
  walk2(full);
92477
92624
  } else if (ent.isFile()) {
92478
- currentFiles[rel.replace(/\\/g, "/")] = readFileSync74(full, "utf-8");
92625
+ currentFiles[rel.replace(/\\/g, "/")] = readFileSync75(full, "utf-8");
92479
92626
  }
92480
92627
  }
92481
92628
  };
@@ -92503,10 +92650,10 @@ function diffSummary(currentDir, files) {
92503
92650
  `);
92504
92651
  }
92505
92652
  function writePayload(poolDir, name, files) {
92506
- if (!existsSync88(poolDir)) {
92507
- mkdirSync49(poolDir, { recursive: true, mode: 493 });
92653
+ if (!existsSync89(poolDir)) {
92654
+ mkdirSync50(poolDir, { recursive: true, mode: 493 });
92508
92655
  }
92509
- const target = join85(poolDir, name);
92656
+ const target = join86(poolDir, name);
92510
92657
  let targetIsSymlink = false;
92511
92658
  try {
92512
92659
  const st = lstatSync9(target);
@@ -92517,15 +92664,15 @@ function writePayload(poolDir, name, files) {
92517
92664
  if (targetIsSymlink) {
92518
92665
  fail3(`refusing to overwrite symlink at ${target}; investigate manually`);
92519
92666
  }
92520
- const staging = mkdtempSync5(join85(poolDir, `.skill-apply-stage-${name}-`));
92667
+ const staging = mkdtempSync5(join86(poolDir, `.skill-apply-stage-${name}-`));
92521
92668
  let oldRename = null;
92522
92669
  try {
92523
92670
  for (const [path8, content] of Object.entries(files)) {
92524
- const full = join85(staging, path8);
92525
- mkdirSync49(dirname29(full), { recursive: true, mode: 493 });
92671
+ const full = join86(staging, path8);
92672
+ mkdirSync50(dirname30(full), { recursive: true, mode: 493 });
92526
92673
  const fd = openSync16(full, "wx");
92527
92674
  try {
92528
- writeFileSync40(fd, content);
92675
+ writeFileSync41(fd, content);
92529
92676
  } finally {
92530
92677
  closeSync16(fd);
92531
92678
  }
@@ -92552,9 +92699,9 @@ function writePayload(poolDir, name, files) {
92552
92699
  try {
92553
92700
  rmSync18(staging, { recursive: true, force: true });
92554
92701
  } catch {}
92555
- if (oldRename && existsSync88(oldRename)) {
92702
+ if (oldRename && existsSync89(oldRename)) {
92556
92703
  try {
92557
- if (existsSync88(target)) {
92704
+ if (existsSync89(target)) {
92558
92705
  rmSync18(target, { recursive: true, force: true });
92559
92706
  }
92560
92707
  renameSync21(oldRename, target);
@@ -92575,10 +92722,10 @@ function registerSkillCommand(program3) {
92575
92722
  files = loadFromStdin();
92576
92723
  } else {
92577
92724
  const fromPath = resolve52(opts.from);
92578
- if (!existsSync88(fromPath)) {
92725
+ if (!existsSync89(fromPath)) {
92579
92726
  fail3(`--from path does not exist: ${opts.from}`);
92580
92727
  }
92581
- const st = statSync36(fromPath);
92728
+ const st = statSync37(fromPath);
92582
92729
  if (st.isDirectory()) {
92583
92730
  files = loadFromDir(fromPath);
92584
92731
  } else if (isTarballPath(fromPath)) {
@@ -92599,7 +92746,7 @@ function registerSkillCommand(program3) {
92599
92746
  }
92600
92747
  const config = loadConfig();
92601
92748
  const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
92602
- const currentDir = join85(poolDir, name);
92749
+ const currentDir = join86(poolDir, name);
92603
92750
  console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
92604
92751
  console.log(source_default.bold("Diff vs current pool content:"));
92605
92752
  console.log(diffSummary(currentDir, files));
@@ -92631,21 +92778,21 @@ function sumBytes(files) {
92631
92778
  init_esm();
92632
92779
  import {
92633
92780
  closeSync as closeSync17,
92634
- existsSync as existsSync89,
92781
+ existsSync as existsSync90,
92635
92782
  lstatSync as lstatSync10,
92636
- mkdirSync as mkdirSync50,
92783
+ mkdirSync as mkdirSync51,
92637
92784
  mkdtempSync as mkdtempSync6,
92638
92785
  openSync as openSync17,
92639
- readFileSync as readFileSync75,
92786
+ readFileSync as readFileSync76,
92640
92787
  readdirSync as readdirSync33,
92641
92788
  renameSync as renameSync22,
92642
92789
  rmSync as rmSync19,
92643
- statSync as statSync37,
92790
+ statSync as statSync38,
92644
92791
  utimesSync,
92645
- writeFileSync as writeFileSync41
92792
+ writeFileSync as writeFileSync42
92646
92793
  } from "node:fs";
92647
- import { dirname as dirname30, join as join86, relative as relative3, resolve as resolve53 } from "node:path";
92648
- import { homedir as homedir50, tmpdir as tmpdir6 } from "node:os";
92794
+ import { dirname as dirname31, join as join87, relative as relative3, resolve as resolve53 } from "node:path";
92795
+ import { homedir as homedir51, tmpdir as tmpdir6 } from "node:os";
92649
92796
  import { spawnSync as spawnSync16 } from "node:child_process";
92650
92797
  init_helpers();
92651
92798
  init_agent_config();
@@ -92656,15 +92803,15 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
92656
92803
  var PERSONAL_SKILLS_SUBPATH = "personal-skills";
92657
92804
  function resolveConfigSkillsDir(agent) {
92658
92805
  const override = process.env.SWITCHROOM_CONFIG_DIR;
92659
- const candidate = override ? resolve53(override) : join86(homedir50(), ".switchroom-config");
92660
- if (!existsSync89(candidate))
92806
+ const candidate = override ? resolve53(override) : join87(homedir51(), ".switchroom-config");
92807
+ if (!existsSync90(candidate))
92661
92808
  return null;
92662
- return join86(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
92809
+ return join87(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
92663
92810
  }
92664
92811
  var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
92665
92812
  function sweepMirrorPriors(configSkillsRoot) {
92666
92813
  try {
92667
- if (!existsSync89(configSkillsRoot))
92814
+ if (!existsSync90(configSkillsRoot))
92668
92815
  return;
92669
92816
  const now = Date.now();
92670
92817
  for (const ent of readdirSync33(configSkillsRoot)) {
@@ -92677,7 +92824,7 @@ function sweepMirrorPriors(configSkillsRoot) {
92677
92824
  if (now - ts < MIRROR_PRIOR_TTL_MS)
92678
92825
  continue;
92679
92826
  try {
92680
- rmSync19(join86(configSkillsRoot, ent), { recursive: true, force: true });
92827
+ rmSync19(join87(configSkillsRoot, ent), { recursive: true, force: true });
92681
92828
  } catch {}
92682
92829
  }
92683
92830
  } catch {}
@@ -92686,7 +92833,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
92686
92833
  const configSkillsRoot = resolveConfigSkillsDir(agent);
92687
92834
  if (!configSkillsRoot)
92688
92835
  return;
92689
- const dest = join86(configSkillsRoot, name);
92836
+ const dest = join87(configSkillsRoot, name);
92690
92837
  try {
92691
92838
  if (liveSkillDir !== null) {
92692
92839
  try {
@@ -92700,32 +92847,32 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
92700
92847
  }
92701
92848
  if (liveSkillDir === null) {
92702
92849
  sweepMirrorPriors(configSkillsRoot);
92703
- if (existsSync89(dest)) {
92704
- const trash = join86(configSkillsRoot, `.${name}-trash-${Date.now()}`);
92850
+ if (existsSync90(dest)) {
92851
+ const trash = join87(configSkillsRoot, `.${name}-trash-${Date.now()}`);
92705
92852
  renameSync22(dest, trash);
92706
92853
  }
92707
92854
  return;
92708
92855
  }
92709
- mkdirSync50(configSkillsRoot, { recursive: true, mode: 493 });
92856
+ mkdirSync51(configSkillsRoot, { recursive: true, mode: 493 });
92710
92857
  sweepMirrorPriors(configSkillsRoot);
92711
- const staging = mkdtempSync6(join86(configSkillsRoot, `.${name}-staging-`));
92858
+ const staging = mkdtempSync6(join87(configSkillsRoot, `.${name}-staging-`));
92712
92859
  const walk2 = (src, dst) => {
92713
- mkdirSync50(dst, { recursive: true, mode: 493 });
92860
+ mkdirSync51(dst, { recursive: true, mode: 493 });
92714
92861
  for (const ent of readdirSync33(src, { withFileTypes: true })) {
92715
- const s = join86(src, ent.name);
92716
- const d = join86(dst, ent.name);
92862
+ const s = join87(src, ent.name);
92863
+ const d = join87(dst, ent.name);
92717
92864
  if (ent.isSymbolicLink())
92718
92865
  continue;
92719
92866
  if (ent.isDirectory())
92720
92867
  walk2(s, d);
92721
92868
  else if (ent.isFile()) {
92722
- writeFileSync41(d, readFileSync75(s));
92869
+ writeFileSync42(d, readFileSync76(s));
92723
92870
  }
92724
92871
  }
92725
92872
  };
92726
92873
  walk2(liveSkillDir, staging);
92727
- if (existsSync89(dest)) {
92728
- const prior = join86(configSkillsRoot, `.${name}-prior-${Date.now()}`);
92874
+ if (existsSync90(dest)) {
92875
+ const prior = join87(configSkillsRoot, `.${name}-prior-${Date.now()}`);
92729
92876
  renameSync22(dest, prior);
92730
92877
  }
92731
92878
  renameSync22(staging, dest);
@@ -92752,17 +92899,17 @@ function resolveAgent(opts) {
92752
92899
  function resolveAgentsRoot(opts) {
92753
92900
  if (opts.root)
92754
92901
  return resolve53(opts.root);
92755
- return join86(homedir50(), ".switchroom", "agents");
92902
+ return join87(homedir51(), ".switchroom", "agents");
92756
92903
  }
92757
92904
  function personalSkillDir(agentsRoot, agent, name) {
92758
- return join86(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
92905
+ return join87(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
92759
92906
  }
92760
92907
  function trashDir(agentsRoot, agent) {
92761
- return join86(agentsRoot, agent, ".claude", TRASH_DIRNAME);
92908
+ return join87(agentsRoot, agent, ".claude", TRASH_DIRNAME);
92762
92909
  }
92763
92910
  function countPersonalSkills(agentsRoot, agent) {
92764
- const skillsDir = join86(agentsRoot, agent, ".claude", "skills");
92765
- if (!existsSync89(skillsDir))
92911
+ const skillsDir = join87(agentsRoot, agent, ".claude", "skills");
92912
+ if (!existsSync90(skillsDir))
92766
92913
  return 0;
92767
92914
  let n = 0;
92768
92915
  for (const ent of readdirSync33(skillsDir, { withFileTypes: true })) {
@@ -92793,13 +92940,13 @@ function readStdinSync2() {
92793
92940
  }
92794
92941
  function loadFromDir2(dir) {
92795
92942
  const abs = resolve53(dir);
92796
- if (!statSync37(abs).isDirectory()) {
92943
+ if (!statSync38(abs).isDirectory()) {
92797
92944
  fail4(`--from path is not a directory: ${dir}`);
92798
92945
  }
92799
92946
  const files = {};
92800
92947
  const walk2 = (sub) => {
92801
92948
  for (const ent of readdirSync33(sub, { withFileTypes: true })) {
92802
- const full = join86(sub, ent.name);
92949
+ const full = join87(sub, ent.name);
92803
92950
  if (ent.isSymbolicLink()) {
92804
92951
  fail4(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
92805
92952
  }
@@ -92809,7 +92956,7 @@ function loadFromDir2(dir) {
92809
92956
  }
92810
92957
  if (ent.isFile()) {
92811
92958
  const rel = relative3(abs, full).replace(/\\/g, "/");
92812
- files[rel] = readFileSync75(full, "utf-8");
92959
+ files[rel] = readFileSync76(full, "utf-8");
92813
92960
  }
92814
92961
  }
92815
92962
  };
@@ -92852,10 +92999,10 @@ function behavioralValidate(files) {
92852
92999
  errors2.push(`${path8} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
92853
93000
  }
92854
93001
  } else if (PY_SCRIPT_RE.test(path8)) {
92855
- const tmp = mkdtempSync6(join86(tmpdir6(), "skill-personal-py-"));
92856
- const tmpPy = join86(tmp, "check.py");
93002
+ const tmp = mkdtempSync6(join87(tmpdir6(), "skill-personal-py-"));
93003
+ const tmpPy = join87(tmp, "check.py");
92857
93004
  try {
92858
- writeFileSync41(tmpPy, content);
93005
+ writeFileSync42(tmpPy, content);
92859
93006
  const r = spawnSync16("python3", ["-m", "py_compile", tmpPy], {
92860
93007
  encoding: "utf-8"
92861
93008
  });
@@ -92871,15 +93018,15 @@ function behavioralValidate(files) {
92871
93018
  }
92872
93019
  function sweepTrash(agentsRoot, agent) {
92873
93020
  const trash = trashDir(agentsRoot, agent);
92874
- if (!existsSync89(trash))
93021
+ if (!existsSync90(trash))
92875
93022
  return;
92876
93023
  const now = Date.now();
92877
93024
  for (const ent of readdirSync33(trash, { withFileTypes: true })) {
92878
93025
  if (!ent.isDirectory())
92879
93026
  continue;
92880
- const entPath = join86(trash, ent.name);
93027
+ const entPath = join87(trash, ent.name);
92881
93028
  try {
92882
- const st = statSync37(entPath);
93029
+ const st = statSync38(entPath);
92883
93030
  if (now - st.mtimeMs > TRASH_TTL_MS) {
92884
93031
  rmSync19(entPath, { recursive: true, force: true });
92885
93032
  }
@@ -92897,16 +93044,16 @@ function writePersonalSkill(targetDir, files) {
92897
93044
  if (targetIsSymlink) {
92898
93045
  fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
92899
93046
  }
92900
- mkdirSync50(dirname30(targetDir), { recursive: true, mode: 493 });
92901
- const staging = mkdtempSync6(join86(dirname30(targetDir), `.skill-personal-stage-`));
93047
+ mkdirSync51(dirname31(targetDir), { recursive: true, mode: 493 });
93048
+ const staging = mkdtempSync6(join87(dirname31(targetDir), `.skill-personal-stage-`));
92902
93049
  let oldRename = null;
92903
93050
  try {
92904
93051
  for (const [path8, content] of Object.entries(files)) {
92905
- const full = join86(staging, path8);
92906
- mkdirSync50(dirname30(full), { recursive: true, mode: 493 });
93052
+ const full = join87(staging, path8);
93053
+ mkdirSync51(dirname31(full), { recursive: true, mode: 493 });
92907
93054
  const fd = openSync17(full, "wx");
92908
93055
  try {
92909
- writeFileSync41(fd, content);
93056
+ writeFileSync42(fd, content);
92910
93057
  } finally {
92911
93058
  closeSync17(fd);
92912
93059
  }
@@ -92933,9 +93080,9 @@ function writePersonalSkill(targetDir, files) {
92933
93080
  try {
92934
93081
  rmSync19(staging, { recursive: true, force: true });
92935
93082
  } catch {}
92936
- if (oldRename && existsSync89(oldRename)) {
93083
+ if (oldRename && existsSync90(oldRename)) {
92937
93084
  try {
92938
- if (existsSync89(targetDir)) {
93085
+ if (existsSync90(targetDir)) {
92939
93086
  rmSync19(targetDir, { recursive: true, force: true });
92940
93087
  }
92941
93088
  renameSync22(oldRename, targetDir);
@@ -93001,15 +93148,15 @@ function loadFiles(opts) {
93001
93148
  return loadFromStdin2();
93002
93149
  }
93003
93150
  const p = resolve53(opts.from);
93004
- if (!existsSync89(p)) {
93151
+ if (!existsSync90(p)) {
93005
93152
  fail4(`--from path does not exist: ${opts.from}`);
93006
93153
  }
93007
- const st = statSync37(p);
93154
+ const st = statSync38(p);
93008
93155
  if (st.isDirectory()) {
93009
93156
  return loadFromDir2(p);
93010
93157
  }
93011
93158
  if (p.endsWith(".md")) {
93012
- return { "SKILL.md": readFileSync75(p, "utf-8") };
93159
+ return { "SKILL.md": readFileSync76(p, "utf-8") };
93013
93160
  }
93014
93161
  fail4(`--from must be a directory or a .md file. Got: ${opts.from}`);
93015
93162
  }
@@ -93049,10 +93196,10 @@ function editPersonalAction(name, opts) {
93049
93196
  }
93050
93197
  var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
93051
93198
  function defaultSharedRoot() {
93052
- return join86(homedir50(), ".switchroom", "skills");
93199
+ return join87(homedir51(), ".switchroom", "skills");
93053
93200
  }
93054
93201
  function defaultBundledRoot() {
93055
- return join86(homedir50(), ".switchroom", "skills", "_bundled");
93202
+ return join87(homedir51(), ".switchroom", "skills", "_bundled");
93056
93203
  }
93057
93204
  function resolveCloneSource(source, opts) {
93058
93205
  const m = CLONE_SOURCE_RE.exec(source);
@@ -93062,8 +93209,8 @@ function resolveCloneSource(source, opts) {
93062
93209
  const tier = m[1];
93063
93210
  const slug = m[2];
93064
93211
  const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
93065
- const dir = join86(root, slug);
93066
- if (!existsSync89(dir)) {
93212
+ const dir = join87(root, slug);
93213
+ if (!existsSync90(dir)) {
93067
93214
  fail4(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
93068
93215
  }
93069
93216
  const st = lstatSync10(dir);
@@ -93078,7 +93225,7 @@ function readSourceFiles(dir) {
93078
93225
  const skipped = [];
93079
93226
  const walk2 = (sub) => {
93080
93227
  for (const ent of readdirSync33(sub, { withFileTypes: true })) {
93081
- const full = join86(sub, ent.name);
93228
+ const full = join87(sub, ent.name);
93082
93229
  if (ent.isSymbolicLink()) {
93083
93230
  continue;
93084
93231
  }
@@ -93098,7 +93245,7 @@ function readSourceFiles(dir) {
93098
93245
  fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
93099
93246
  }
93100
93247
  } catch {}
93101
- files[rel] = readFileSync75(full, "utf-8");
93248
+ files[rel] = readFileSync76(full, "utf-8");
93102
93249
  }
93103
93250
  }
93104
93251
  };
@@ -93187,9 +93334,9 @@ function removePersonalAction(name, opts) {
93187
93334
  throw err2;
93188
93335
  }
93189
93336
  const trashRoot2 = trashDir(agentsRoot, agent);
93190
- mkdirSync50(trashRoot2, { recursive: true, mode: 493 });
93337
+ mkdirSync51(trashRoot2, { recursive: true, mode: 493 });
93191
93338
  const ts = Date.now();
93192
- const trashTarget = join86(trashRoot2, `${name}-${ts}`);
93339
+ const trashTarget = join87(trashRoot2, `${name}-${ts}`);
93193
93340
  renameSync22(target, trashTarget);
93194
93341
  const now = new Date(ts);
93195
93342
  utimesSync(trashTarget, now, now);
@@ -93208,16 +93355,16 @@ function listPersonalAction(opts) {
93208
93355
  const agent = resolveAgent(opts);
93209
93356
  const agentsRoot = resolveAgentsRoot(opts);
93210
93357
  sweepTrash(agentsRoot, agent);
93211
- const skillsDir = join86(agentsRoot, agent, ".claude", "skills");
93358
+ const skillsDir = join87(agentsRoot, agent, ".claude", "skills");
93212
93359
  const personal = [];
93213
- if (existsSync89(skillsDir)) {
93360
+ if (existsSync90(skillsDir)) {
93214
93361
  for (const ent of readdirSync33(skillsDir, { withFileTypes: true })) {
93215
93362
  if (!ent.isDirectory())
93216
93363
  continue;
93217
93364
  if (!ent.name.startsWith(PERSONAL_PREFIX))
93218
93365
  continue;
93219
93366
  const skillName = ent.name.slice(PERSONAL_PREFIX.length);
93220
- const skillPath = join86(skillsDir, ent.name);
93367
+ const skillPath = join87(skillsDir, ent.name);
93221
93368
  let fileCount = 0;
93222
93369
  let totalBytes = 0;
93223
93370
  const walk2 = (sub) => {
@@ -93225,10 +93372,10 @@ function listPersonalAction(opts) {
93225
93372
  if (e.isFile()) {
93226
93373
  fileCount += 1;
93227
93374
  try {
93228
- totalBytes += statSync37(join86(sub, e.name)).size;
93375
+ totalBytes += statSync38(join87(sub, e.name)).size;
93229
93376
  } catch {}
93230
93377
  } else if (e.isDirectory()) {
93231
- walk2(join86(sub, e.name));
93378
+ walk2(join87(sub, e.name));
93232
93379
  }
93233
93380
  }
93234
93381
  };
@@ -93266,12 +93413,12 @@ function registerSkillPersonalCommands(program3) {
93266
93413
 
93267
93414
  // src/cli/self-improve-propose-skill.ts
93268
93415
  import { createConnection as createConnection4 } from "node:net";
93269
- import { homedir as homedir51 } from "node:os";
93270
- import { join as join87 } from "node:path";
93271
- import { readFileSync as readFileSync76 } from "node:fs";
93416
+ import { homedir as homedir52 } from "node:os";
93417
+ import { join as join88 } from "node:path";
93418
+ import { readFileSync as readFileSync77 } from "node:fs";
93272
93419
  var IPC_CONNECT_TIMEOUT_MS = 5000;
93273
93420
  function gatewaySocketPath() {
93274
- return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join87(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join87(homedir51(), ".claude", "channels", "telegram", "gateway.sock"));
93421
+ return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join88(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join88(homedir52(), ".claude", "channels", "telegram", "gateway.sock"));
93275
93422
  }
93276
93423
  function fail5(msg, code = 1) {
93277
93424
  console.error(msg);
@@ -93306,7 +93453,7 @@ function registerSelfImproveProposeSkillCommand(program3) {
93306
93453
  fail5("agent name required (--agent or $SWITCHROOM_AGENT_NAME)");
93307
93454
  let draft;
93308
93455
  try {
93309
- draft = JSON.parse(readFileSync76(opts.draft, "utf-8"));
93456
+ draft = JSON.parse(readFileSync77(opts.draft, "utf-8"));
93310
93457
  } catch (e) {
93311
93458
  fail5(`failed to read/parse --draft: ${e.message}`);
93312
93459
  }
@@ -93343,28 +93490,28 @@ function registerSelfImproveProposeSkillCommand(program3) {
93343
93490
  init_esm();
93344
93491
  init_helpers();
93345
93492
  var import_yaml25 = __toESM(require_dist(), 1);
93346
- import { existsSync as existsSync90, readdirSync as readdirSync34, readFileSync as readFileSync77, statSync as statSync38 } from "node:fs";
93347
- import { homedir as homedir52 } from "node:os";
93348
- import { join as join88, resolve as resolve54 } from "node:path";
93493
+ import { existsSync as existsSync91, readdirSync as readdirSync34, readFileSync as readFileSync78, statSync as statSync39 } from "node:fs";
93494
+ import { homedir as homedir53 } from "node:os";
93495
+ import { join as join89, resolve as resolve54 } from "node:path";
93349
93496
  var PERSONAL_PREFIX2 = "personal-";
93350
93497
  var BUNDLED_SUBDIR = "_bundled";
93351
93498
  var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
93352
93499
  function defaultAgentsRoot() {
93353
- return resolve54(homedir52(), ".switchroom/agents");
93500
+ return resolve54(homedir53(), ".switchroom/agents");
93354
93501
  }
93355
93502
  function defaultSharedRoot2() {
93356
- return resolve54(homedir52(), ".switchroom/skills");
93503
+ return resolve54(homedir53(), ".switchroom/skills");
93357
93504
  }
93358
93505
  function defaultBundledRoot2() {
93359
- return resolve54(homedir52(), ".switchroom/skills/_bundled");
93506
+ return resolve54(homedir53(), ".switchroom/skills/_bundled");
93360
93507
  }
93361
93508
  function readSkillFrontmatter(skillDir) {
93362
- const mdPath = join88(skillDir, "SKILL.md");
93363
- if (!existsSync90(mdPath))
93509
+ const mdPath = join89(skillDir, "SKILL.md");
93510
+ if (!existsSync91(mdPath))
93364
93511
  return null;
93365
93512
  let content;
93366
93513
  try {
93367
- content = readFileSync77(mdPath, "utf-8");
93514
+ content = readFileSync78(mdPath, "utf-8");
93368
93515
  } catch {
93369
93516
  return null;
93370
93517
  }
@@ -93392,9 +93539,9 @@ function readSkillFrontmatter(skillDir) {
93392
93539
  return { fm: parsed };
93393
93540
  }
93394
93541
  function statSkillMd(skillDir) {
93395
- const mdPath = join88(skillDir, "SKILL.md");
93542
+ const mdPath = join89(skillDir, "SKILL.md");
93396
93543
  try {
93397
- const st = statSync38(mdPath);
93544
+ const st = statSync39(mdPath);
93398
93545
  return { size: st.size, mtime: st.mtime.toISOString() };
93399
93546
  } catch {
93400
93547
  return null;
@@ -93403,8 +93550,8 @@ function statSkillMd(skillDir) {
93403
93550
  function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
93404
93551
  if (!AGENT_NAME_RE3.test(agent))
93405
93552
  return [];
93406
- const skillsDir = join88(agentsRoot, agent, ".claude/skills");
93407
- if (!existsSync90(skillsDir))
93553
+ const skillsDir = join89(agentsRoot, agent, ".claude/skills");
93554
+ if (!existsSync91(skillsDir))
93408
93555
  return [];
93409
93556
  const out = [];
93410
93557
  let entries;
@@ -93416,9 +93563,9 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
93416
93563
  for (const ent of entries) {
93417
93564
  if (!ent.startsWith(PERSONAL_PREFIX2))
93418
93565
  continue;
93419
- const dirPath = join88(skillsDir, ent);
93566
+ const dirPath = join89(skillsDir, ent);
93420
93567
  try {
93421
- if (!statSync38(dirPath).isDirectory())
93568
+ if (!statSync39(dirPath).isDirectory())
93422
93569
  continue;
93423
93570
  } catch {
93424
93571
  continue;
@@ -93442,7 +93589,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
93442
93589
  return out;
93443
93590
  }
93444
93591
  function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
93445
- if (!existsSync90(sharedRoot))
93592
+ if (!existsSync91(sharedRoot))
93446
93593
  return [];
93447
93594
  const out = [];
93448
93595
  let entries;
@@ -93456,9 +93603,9 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
93456
93603
  continue;
93457
93604
  if (ent.startsWith("."))
93458
93605
  continue;
93459
- const dirPath = join88(sharedRoot, ent);
93606
+ const dirPath = join89(sharedRoot, ent);
93460
93607
  try {
93461
- if (!statSync38(dirPath).isDirectory())
93608
+ if (!statSync39(dirPath).isDirectory())
93462
93609
  continue;
93463
93610
  } catch {
93464
93611
  continue;
@@ -93480,7 +93627,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
93480
93627
  return out;
93481
93628
  }
93482
93629
  function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
93483
- if (!existsSync90(bundledRoot))
93630
+ if (!existsSync91(bundledRoot))
93484
93631
  return [];
93485
93632
  const out = [];
93486
93633
  let entries;
@@ -93492,9 +93639,9 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
93492
93639
  for (const ent of entries) {
93493
93640
  if (ent.startsWith("."))
93494
93641
  continue;
93495
- const dirPath = join88(bundledRoot, ent);
93642
+ const dirPath = join89(bundledRoot, ent);
93496
93643
  try {
93497
- if (!statSync38(dirPath).isDirectory())
93644
+ if (!statSync39(dirPath).isDirectory())
93498
93645
  continue;
93499
93646
  } catch {
93500
93647
  continue;
@@ -93638,18 +93785,18 @@ init_source();
93638
93785
  init_helpers();
93639
93786
  init_operator_uid();
93640
93787
  import {
93641
- existsSync as existsSync92,
93642
- mkdirSync as mkdirSync51,
93788
+ existsSync as existsSync93,
93789
+ mkdirSync as mkdirSync52,
93643
93790
  readdirSync as readdirSync35,
93644
- readFileSync as readFileSync79,
93645
- writeFileSync as writeFileSync42,
93646
- statSync as statSync39,
93791
+ readFileSync as readFileSync80,
93792
+ writeFileSync as writeFileSync43,
93793
+ statSync as statSync40,
93647
93794
  lstatSync as lstatSync11,
93648
93795
  realpathSync as realpathSync8,
93649
93796
  copyFileSync as copyFileSync13
93650
93797
  } from "node:fs";
93651
- import { homedir as homedir53 } from "node:os";
93652
- import { join as join89 } from "node:path";
93798
+ import { homedir as homedir54 } from "node:os";
93799
+ import { join as join90 } from "node:path";
93653
93800
  import { spawnSync as spawnSync20 } from "node:child_process";
93654
93801
 
93655
93802
  // src/cli/deploy-version-guard.ts
@@ -93871,7 +94018,7 @@ networks:
93871
94018
  # operator surface; the daemon's stderr lands in \`docker logs switchroom-hostd\`.
93872
94019
  `;
93873
94020
  }
93874
- function resolveHostdHostHome(env2 = process.env, home2 = homedir53()) {
94021
+ function resolveHostdHostHome(env2 = process.env, home2 = homedir54()) {
93875
94022
  const fromEnv = env2.SWITCHROOM_HOST_HOME?.trim();
93876
94023
  const resolved = fromEnv && fromEnv.length > 0 ? fromEnv : home2;
93877
94024
  if (resolved === "/host-home" || resolved.startsWith("/host-home/")) {
@@ -93882,7 +94029,7 @@ function resolveHostdHostHome(env2 = process.env, home2 = homedir53()) {
93882
94029
  return resolved;
93883
94030
  }
93884
94031
  function resolveHostdSkillsTarget(hostHome) {
93885
- const skillsPath = join89(hostHome, ".switchroom", "skills");
94032
+ const skillsPath = join90(hostHome, ".switchroom", "skills");
93886
94033
  let st;
93887
94034
  try {
93888
94035
  st = lstatSync11(skillsPath);
@@ -93899,21 +94046,21 @@ function resolveHostdSkillsTarget(hostHome) {
93899
94046
  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.`);
93900
94047
  return;
93901
94048
  }
93902
- if (!existsSync92(target)) {
94049
+ if (!existsSync93(target)) {
93903
94050
  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.`);
93904
94051
  return;
93905
94052
  }
93906
94053
  return target;
93907
94054
  }
93908
94055
  function hostdDir() {
93909
- return join89(homedir53(), ".switchroom", "hostd");
94056
+ return join90(homedir54(), ".switchroom", "hostd");
93910
94057
  }
93911
94058
  function hostdComposePath() {
93912
- return join89(hostdDir(), "docker-compose.yml");
94059
+ return join90(hostdDir(), "docker-compose.yml");
93913
94060
  }
93914
94061
  function backupExistingCompose() {
93915
94062
  const p = hostdComposePath();
93916
- if (!existsSync92(p))
94063
+ if (!existsSync93(p))
93917
94064
  return null;
93918
94065
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
93919
94066
  const bak = `${p}.bak-${ts}`;
@@ -93946,7 +94093,7 @@ async function doInstall(opts, program3) {
93946
94093
  }
93947
94094
  const dir = hostdDir();
93948
94095
  const composePath = hostdComposePath();
93949
- mkdirSync51(dir, { recursive: true });
94096
+ mkdirSync52(dir, { recursive: true });
93950
94097
  const imageTag = resolveHostdImageTag(opts.tag, cfg.release);
93951
94098
  const guard = checkDowngrade({
93952
94099
  container: "switchroom-hostd",
@@ -93979,7 +94126,7 @@ async function doInstall(opts, program3) {
93979
94126
  const bak = backupExistingCompose();
93980
94127
  if (bak)
93981
94128
  console.log(source_default.dim(` Backed up existing compose to ${bak}`));
93982
- writeFileSync42(composePath, yaml, "utf8");
94129
+ writeFileSync43(composePath, yaml, "utf8");
93983
94130
  console.log(source_default.green(` \u2713 Wrote ${composePath}`));
93984
94131
  const adminAgents = Object.entries(cfg.agents ?? {}).filter(([, a]) => a?.admin === true).map(([name]) => name);
93985
94132
  console.log(source_default.dim(` agents served (one socket each): ${allAgents.length === 0 ? "(none)" : allAgents.join(", ")}`));
@@ -94011,7 +94158,7 @@ function doStatus() {
94011
94158
  const composeYml = hostdComposePath();
94012
94159
  console.log(source_default.bold("switchroom-hostd"));
94013
94160
  console.log("");
94014
- if (!existsSync92(composeYml)) {
94161
+ if (!existsSync93(composeYml)) {
94015
94162
  console.log(source_default.yellow(" compose: not installed"));
94016
94163
  console.log(source_default.dim(" run `switchroom hostd install` to set up."));
94017
94164
  return;
@@ -94032,15 +94179,15 @@ function doStatus() {
94032
94179
  } else {
94033
94180
  console.log(source_default.green(` container: ${ps.stdout.trim()}`));
94034
94181
  }
94035
- if (existsSync92(dir)) {
94182
+ if (existsSync93(dir)) {
94036
94183
  const entries = [];
94037
94184
  try {
94038
94185
  for (const name of readdirSync35(dir)) {
94039
94186
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
94040
94187
  continue;
94041
- const sockPath = join89(dir, name, "sock");
94042
- if (existsSync92(sockPath)) {
94043
- const st = statSync39(sockPath);
94188
+ const sockPath = join90(dir, name, "sock");
94189
+ if (existsSync93(sockPath)) {
94190
+ const st = statSync40(sockPath);
94044
94191
  if ((st.mode & 61440) === 49152) {
94045
94192
  entries.push(`${name} \u2192 ${sockPath}`);
94046
94193
  }
@@ -94058,7 +94205,7 @@ function doStatus() {
94058
94205
  }
94059
94206
  function doUninstall() {
94060
94207
  const composeYml = hostdComposePath();
94061
- if (!existsSync92(composeYml)) {
94208
+ if (!existsSync93(composeYml)) {
94062
94209
  console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
94063
94210
  return;
94064
94211
  }
@@ -94082,12 +94229,12 @@ function registerHostdCommand(program3) {
94082
94229
  hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
94083
94230
  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) => {
94084
94231
  const logPath = opts.path ?? defaultAuditLogPath2();
94085
- if (!existsSync92(logPath)) {
94232
+ if (!existsSync93(logPath)) {
94086
94233
  console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
94087
94234
  The log is created when hostd handles its first privileged-verb request.`));
94088
94235
  return;
94089
94236
  }
94090
- const raw = readFileSync79(logPath, "utf-8");
94237
+ const raw = readFileSync80(logPath, "utf-8");
94091
94238
  const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
94092
94239
  const filters = {
94093
94240
  agent: opts.agent,
@@ -94130,9 +94277,9 @@ The log is created when hostd handles its first privileged-verb request.`));
94130
94277
  init_source();
94131
94278
  init_helpers();
94132
94279
  init_operator_uid();
94133
- import { existsSync as existsSync93, mkdirSync as mkdirSync52, writeFileSync as writeFileSync43, copyFileSync as copyFileSync14 } from "node:fs";
94134
- import { homedir as homedir54 } from "node:os";
94135
- import { join as join90 } from "node:path";
94280
+ import { existsSync as existsSync94, mkdirSync as mkdirSync53, writeFileSync as writeFileSync44, copyFileSync as copyFileSync14 } from "node:fs";
94281
+ import { homedir as homedir55 } from "node:os";
94282
+ import { join as join91 } from "node:path";
94136
94283
  import { spawnSync as spawnSync21 } from "node:child_process";
94137
94284
  function resolveWebImageTag(explicitTag, release) {
94138
94285
  if (explicitTag)
@@ -94217,14 +94364,14 @@ services:
94217
94364
  `;
94218
94365
  }
94219
94366
  function webdDir() {
94220
- return join90(homedir54(), ".switchroom", "web");
94367
+ return join91(homedir55(), ".switchroom", "web");
94221
94368
  }
94222
94369
  function webdComposePath() {
94223
- return join90(webdDir(), "docker-compose.yml");
94370
+ return join91(webdDir(), "docker-compose.yml");
94224
94371
  }
94225
94372
  function backupExistingCompose2() {
94226
94373
  const p = webdComposePath();
94227
- if (!existsSync93(p))
94374
+ if (!existsSync94(p))
94228
94375
  return null;
94229
94376
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
94230
94377
  const bak = `${p}.bak-${ts}`;
@@ -94249,7 +94396,7 @@ async function doInstall2(opts, program3) {
94249
94396
  }
94250
94397
  const dir = webdDir();
94251
94398
  const composePath = webdComposePath();
94252
- mkdirSync52(dir, { recursive: true });
94399
+ mkdirSync53(dir, { recursive: true });
94253
94400
  const cfg = getConfig(program3);
94254
94401
  const imageTag = resolveWebImageTag(opts.tag, cfg.release);
94255
94402
  const guard = checkDowngrade({
@@ -94262,7 +94409,7 @@ async function doInstall2(opts, program3) {
94262
94409
  return;
94263
94410
  }
94264
94411
  const yaml = renderWebComposeFile({
94265
- hostHome: homedir54(),
94412
+ hostHome: homedir55(),
94266
94413
  imageTag,
94267
94414
  operatorUid
94268
94415
  });
@@ -94275,7 +94422,7 @@ async function doInstall2(opts, program3) {
94275
94422
  const bak = backupExistingCompose2();
94276
94423
  if (bak)
94277
94424
  console.log(source_default.dim(` Backed up existing compose to ${bak}`));
94278
- writeFileSync43(composePath, yaml, "utf8");
94425
+ writeFileSync44(composePath, yaml, "utf8");
94279
94426
  console.log(source_default.green(` \u2713 Wrote ${composePath}`));
94280
94427
  console.log(source_default.dim(` running as uid ${operatorUid} (operator), network_mode: host`));
94281
94428
  console.log(source_default.dim(` Pulling ghcr.io/switchroom/switchroom-web:${imageTag}\u2026`));
@@ -94308,7 +94455,7 @@ function doStatus2() {
94308
94455
  const composeYml = webdComposePath();
94309
94456
  console.log(source_default.bold("switchroom-web"));
94310
94457
  console.log("");
94311
- if (!existsSync93(composeYml)) {
94458
+ if (!existsSync94(composeYml)) {
94312
94459
  console.log(source_default.yellow(" compose: not installed"));
94313
94460
  console.log(source_default.dim(" run `switchroom webd install` to set up."));
94314
94461
  return;
@@ -94332,7 +94479,7 @@ function doStatus2() {
94332
94479
  }
94333
94480
  function doUninstall2() {
94334
94481
  const composeYml = webdComposePath();
94335
- if (!existsSync93(composeYml)) {
94482
+ if (!existsSync94(composeYml)) {
94336
94483
  console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
94337
94484
  return;
94338
94485
  }
@@ -94361,10 +94508,10 @@ function registerWebdCommand(program3) {
94361
94508
 
94362
94509
  // src/cli/host-repair.ts
94363
94510
  init_source();
94364
- import { homedir as homedir55 } from "node:os";
94365
- import { join as join91 } from "node:path";
94511
+ import { homedir as homedir56 } from "node:os";
94512
+ import { join as join92 } from "node:path";
94366
94513
  var ARTIFACT_ALLOWLIST = {
94367
- dockerComposePluginDir: (home2) => join91(home2, ".docker", "cli-plugins", "docker-compose"),
94514
+ dockerComposePluginDir: (home2) => join92(home2, ".docker", "cli-plugins", "docker-compose"),
94368
94515
  stateSentinel: "/state"
94369
94516
  };
94370
94517
  function isStateBogusAutoDir(probe2) {
@@ -94413,7 +94560,7 @@ function isStateBogusAutoDir(probe2) {
94413
94560
  return true;
94414
94561
  }
94415
94562
  function planMountRepairs(probe2, opts = {}) {
94416
- const home2 = opts.hostHome ?? homedir55();
94563
+ const home2 = opts.hostHome ?? homedir56();
94417
94564
  const items = [];
94418
94565
  const dockerComposePath = ARTIFACT_ALLOWLIST.dockerComposePluginDir(home2);
94419
94566
  const dockerComposeSt = probe2.lstat(dockerComposePath);