switchroom 0.19.29 → 0.19.30

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.
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
2120
2120
  });
2121
2121
 
2122
2122
  // src/build-info.ts
2123
- var VERSION = "0.19.29", COMMIT_SHA = "c618451c";
2123
+ var VERSION = "0.19.30", COMMIT_SHA = "efe8e2a7";
2124
2124
 
2125
2125
  // src/cli/resolve-version.ts
2126
2126
  import { existsSync, readFileSync } from "node:fs";
@@ -50860,6 +50860,209 @@ var init_doctor_drift = __esm(() => {
50860
50860
  init_write_compose();
50861
50861
  });
50862
50862
 
50863
+ // src/cli/component-versions.ts
50864
+ function versionFromImageRef(ref) {
50865
+ const at = ref.indexOf("@");
50866
+ const withoutDigest = at >= 0 ? ref.slice(0, at) : ref;
50867
+ const slash = withoutDigest.lastIndexOf("/");
50868
+ const colon = withoutDigest.indexOf(":", slash + 1);
50869
+ if (colon < 0) {
50870
+ return { version: null, detail: `image ref has no tag (${ref})` };
50871
+ }
50872
+ const tag = withoutDigest.slice(colon + 1);
50873
+ if (SEMVER_TAG_RE.test(tag))
50874
+ return { version: tag };
50875
+ return { version: null, detail: `non-semver tag "${tag}"` };
50876
+ }
50877
+ function isSwitchroomReleaseImage(_name, imageRef) {
50878
+ return /\/switchroom-[a-z0-9-]+(:|@|$)/.test(imageRef);
50879
+ }
50880
+ function collectContainerComponents(exec) {
50881
+ const r = exec("docker", [
50882
+ "ps",
50883
+ "--filter",
50884
+ "name=switchroom-",
50885
+ "--format",
50886
+ "{{.Names}}\t{{.Image}}"
50887
+ ]);
50888
+ if (r.status !== 0)
50889
+ return [];
50890
+ const out = [];
50891
+ for (const line of r.stdout.split(`
50892
+ `)) {
50893
+ const [name, imageRef] = line.trim().split("\t");
50894
+ if (!name || !imageRef)
50895
+ continue;
50896
+ if (!isSwitchroomReleaseImage(name, imageRef))
50897
+ continue;
50898
+ const { version: version2, detail } = versionFromImageRef(imageRef);
50899
+ out.push({
50900
+ name,
50901
+ kind: "container",
50902
+ version: version2,
50903
+ imageRef,
50904
+ ...detail ? { detail } : {}
50905
+ });
50906
+ }
50907
+ return out.sort((a, b) => a.name.localeCompare(b.name));
50908
+ }
50909
+ function normalizeSemverTag(v) {
50910
+ if (!v)
50911
+ return null;
50912
+ const tag = `v${v.trim().replace(/^v/, "")}`;
50913
+ return SEMVER_TAG_RE.test(tag) ? tag : null;
50914
+ }
50915
+ function resolveDriftTarget(components, releasePin) {
50916
+ const pin = normalizeSemverTag(releasePin);
50917
+ if (pin)
50918
+ return { target: pin, targetSource: "release.pin" };
50919
+ let highest = null;
50920
+ for (const c of components) {
50921
+ if (!c.version)
50922
+ continue;
50923
+ if (highest === null) {
50924
+ highest = c.version;
50925
+ continue;
50926
+ }
50927
+ const cmp = compareReleaseTags(c.version, highest);
50928
+ if (cmp !== null && cmp > 0)
50929
+ highest = c.version;
50930
+ }
50931
+ return highest ? { target: highest, targetSource: "highest-deployed" } : { target: null, targetSource: "none" };
50932
+ }
50933
+ function detectComponentDrift(components, releasePin) {
50934
+ const { target, targetSource } = resolveDriftTarget(components, releasePin);
50935
+ const report = {
50936
+ target,
50937
+ targetSource,
50938
+ behind: [],
50939
+ ahead: [],
50940
+ current: [],
50941
+ unknown: []
50942
+ };
50943
+ for (const c of components) {
50944
+ if (!c.version || !target) {
50945
+ report.unknown.push(c);
50946
+ continue;
50947
+ }
50948
+ const cmp = compareReleaseTags(c.version, target);
50949
+ if (cmp === null)
50950
+ report.unknown.push(c);
50951
+ else if (cmp < 0)
50952
+ report.behind.push(c);
50953
+ else if (cmp > 0)
50954
+ report.ahead.push(c);
50955
+ else
50956
+ report.current.push(c);
50957
+ }
50958
+ return report;
50959
+ }
50960
+ function collectComponents(cliVersion, exec) {
50961
+ const cli = {
50962
+ name: "cli (host)",
50963
+ kind: "cli",
50964
+ version: normalizeSemverTag(cliVersion),
50965
+ ...normalizeSemverTag(cliVersion) ? {} : { detail: `unparseable CLI version "${cliVersion}"` }
50966
+ };
50967
+ return [cli, ...collectContainerComponents(exec)];
50968
+ }
50969
+ function formatComponentDrift(report) {
50970
+ const lines = [];
50971
+ if (!report.target) {
50972
+ lines.push("Component versions: no comparable version found (no release.pin and no semver-tagged component).");
50973
+ } else {
50974
+ const src = report.targetSource === "release.pin" ? "release.pin" : "highest deployed version (no release.pin set)";
50975
+ lines.push(`Component versions \u2014 target ${report.target} (from ${src}):`);
50976
+ }
50977
+ for (const c of report.behind) {
50978
+ lines.push(` BEHIND ${c.name} \u2014 ${c.version} (target ${report.target})`);
50979
+ }
50980
+ for (const c of report.ahead) {
50981
+ lines.push(` ahead ${c.name} \u2014 ${c.version}`);
50982
+ }
50983
+ for (const c of report.unknown) {
50984
+ lines.push(` unknown ${c.name} \u2014 ${c.detail ?? "version not readable"}`);
50985
+ }
50986
+ for (const c of report.current) {
50987
+ lines.push(` ok ${c.name} \u2014 ${c.version}`);
50988
+ }
50989
+ return lines.join(`
50990
+ `);
50991
+ }
50992
+ var SEMVER_TAG_RE;
50993
+ var init_component_versions = __esm(() => {
50994
+ SEMVER_TAG_RE = /^v\d+\.\d+\.\d+$/;
50995
+ });
50996
+
50997
+ // src/cli/doctor-component-versions.ts
50998
+ import { spawnSync as spawnSync12 } from "node:child_process";
50999
+ function fixFor(c, target) {
51000
+ if (c.kind === "cli") {
51001
+ return `switchroom update (self-updates the host CLI to ${target} first, then everything else)`;
51002
+ }
51003
+ if (c.name === "switchroom-web") {
51004
+ return `switchroom webd install --tag ${target} (run host-side: webd install fails inside hostd when the hostd compose predates SWITCHROOM_HOSTD_OPERATOR_UID)`;
51005
+ }
51006
+ if (c.name === "switchroom-hindsight-autoheal" || c.name === "switchroom-hostd") {
51007
+ return `switchroom hostd install --tag ${target} (regenerates the hostd compose project \u2014 both hostd and the autoheal sidecar)`;
51008
+ }
51009
+ return `switchroom update`;
51010
+ }
51011
+ function runComponentVersionChecks(config, opts = {}) {
51012
+ const exec = opts.exec ?? defaultExec2;
51013
+ const components = collectComponents(opts.cliVersion ?? SWITCHROOM_VERSION, exec);
51014
+ const report = detectComponentDrift(components, config.release?.pin);
51015
+ if (!report.target) {
51016
+ return [
51017
+ {
51018
+ name: "component versions",
51019
+ status: "skip",
51020
+ detail: "no release.pin set and no semver-tagged switchroom container running \u2014 nothing to compare."
51021
+ }
51022
+ ];
51023
+ }
51024
+ const results = [];
51025
+ const src = report.targetSource === "release.pin" ? `release.pin ${report.target}` : `${report.target} (highest deployed \u2014 no release.pin set)`;
51026
+ if (report.behind.length === 0) {
51027
+ results.push({
51028
+ name: "component versions",
51029
+ status: "ok",
51030
+ detail: `all ${report.current.length} comparable component(s) on ${src}`
51031
+ });
51032
+ }
51033
+ for (const c of report.behind) {
51034
+ results.push({
51035
+ name: `component behind: ${c.name}`,
51036
+ status: "warn",
51037
+ detail: `on ${c.version}, expected ${report.target} (${src})`,
51038
+ fix: fixFor(c, report.target)
51039
+ });
51040
+ }
51041
+ for (const c of report.ahead) {
51042
+ results.push({
51043
+ name: `component ahead: ${c.name}`,
51044
+ status: "warn",
51045
+ detail: `on ${c.version}, ahead of ${src} \u2014 a roll may be mid-flight, or release.pin is stale`
51046
+ });
51047
+ }
51048
+ for (const c of report.unknown) {
51049
+ results.push({
51050
+ name: `component version unknown: ${c.name}`,
51051
+ status: "skip",
51052
+ detail: c.detail ?? "version not readable"
51053
+ });
51054
+ }
51055
+ return results;
51056
+ }
51057
+ var defaultExec2 = (cmd, args) => {
51058
+ const r = spawnSync12(cmd, args, { encoding: "utf-8", timeout: 1e4 });
51059
+ return { status: r.status ?? 1, stdout: r.stdout ?? "" };
51060
+ };
51061
+ var init_doctor_component_versions = __esm(() => {
51062
+ init_resolve_version();
51063
+ init_component_versions();
51064
+ });
51065
+
50863
51066
  // src/cli/doctor-scaffold-wiring.ts
50864
51067
  import { join as join70, resolve as resolve43 } from "node:path";
50865
51068
  function readJson2(d, path7) {
@@ -52614,7 +52817,7 @@ var init_doctor_fix_session_model = __esm(() => {
52614
52817
  });
52615
52818
 
52616
52819
  // src/cli/doctor-claude-cli.ts
52617
- import { spawnSync as spawnSync12 } from "node:child_process";
52820
+ import { spawnSync as spawnSync13 } from "node:child_process";
52618
52821
  import { existsSync as existsSync76, readFileSync as readFileSync70 } from "node:fs";
52619
52822
  import { dirname as dirname30, join as join80 } from "node:path";
52620
52823
  function parseClaudeCliVersion(raw) {
@@ -52679,7 +52882,7 @@ function parseClaudeCliProbeOutput(r) {
52679
52882
  return { state: "read", bin, raw };
52680
52883
  }
52681
52884
  function probeAgentClaudeCli(agentName) {
52682
- const r = spawnSync12("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", buildClaudeCliProbeScript()], { stdio: "pipe", timeout: 3000 });
52885
+ const r = spawnSync13("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", buildClaudeCliProbeScript()], { stdio: "pipe", timeout: 3000 });
52683
52886
  return parseClaudeCliProbeOutput(r);
52684
52887
  }
52685
52888
  function assessClaudeCliFloor(input) {
@@ -52802,7 +53005,7 @@ __export(exports_doctor, {
52802
53005
  PENDING_RETAINS_EVICTION_WINDOW_DAYS: () => PENDING_RETAINS_EVICTION_WINDOW_DAYS,
52803
53006
  MFF_VAULT_KEY: () => MFF_VAULT_KEY
52804
53007
  });
52805
- import { spawnSync as spawnSync13 } from "node:child_process";
53008
+ import { spawnSync as spawnSync14 } from "node:child_process";
52806
53009
  import { Socket as Socket3 } from "node:net";
52807
53010
  import {
52808
53011
  accessSync as accessSync3,
@@ -52948,7 +53151,7 @@ function readVersion(bin, parser) {
52948
53151
  const path7 = which(bin);
52949
53152
  if (!path7)
52950
53153
  return null;
52951
- const proc = spawnSync13(path7, ["--version"], {
53154
+ const proc = spawnSync14(path7, ["--version"], {
52952
53155
  stdio: ["ignore", "pipe", "pipe"]
52953
53156
  });
52954
53157
  if (proc.error || proc.status !== 0)
@@ -53240,7 +53443,7 @@ function probeVaultBrokerSocketPair(agentName) {
53240
53443
  const dataPath = `/run/switchroom/broker/${agentName}/sock`;
53241
53444
  const unlockPath = `/run/switchroom/broker/${agentName}/unlock`;
53242
53445
  const script = `D=0; U=0; ` + `test -S '${dataPath}' && D=1; ` + `test -S '${unlockPath}' && U=1; ` + `echo "D=$D U=$U"`;
53243
- const r = spawnSync13("docker", ["exec", "switchroom-vault-broker", "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
53446
+ const r = spawnSync14("docker", ["exec", "switchroom-vault-broker", "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
53244
53447
  if (r.error || r.status === null)
53245
53448
  return "unreachable";
53246
53449
  if (r.status !== 0) {
@@ -53490,7 +53693,7 @@ function checkHindsightConsumer(config, opts) {
53490
53693
  }
53491
53694
  function probeAuthBrokerSocket(consumerName) {
53492
53695
  const containerPath = `/run/switchroom/auth-broker/${consumerName}/sock`;
53493
- const r = spawnSync13("docker", ["exec", "switchroom-auth-broker", "test", "-S", containerPath], { stdio: "pipe", timeout: 3000 });
53696
+ const r = spawnSync14("docker", ["exec", "switchroom-auth-broker", "test", "-S", containerPath], { stdio: "pipe", timeout: 3000 });
53494
53697
  if (r.error || r.status === null)
53495
53698
  return "unreachable";
53496
53699
  if (r.status === 0)
@@ -53741,7 +53944,7 @@ async function checkHindsight(config) {
53741
53944
  function probePendingRetainsQueue(agentName) {
53742
53945
  const base = "/state/agent/home/.hindsight";
53743
53946
  const script = buildPendingRetainsProbeScript(base);
53744
- const r = spawnSync13("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
53947
+ const r = spawnSync14("docker", ["exec", `switchroom-${agentName}`, "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
53745
53948
  return parsePendingRetainsProbeOutput(r);
53746
53949
  }
53747
53950
  function buildPendingRetainsProbeScript(base, now = Date.now()) {
@@ -54608,7 +54811,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
54608
54811
  const python3 = which("python3") ?? "python3";
54609
54812
  let token;
54610
54813
  try {
54611
- const result = spawnSync13(python3, [authScript, "--quiet"], {
54814
+ const result = spawnSync14(python3, [authScript, "--quiet"], {
54612
54815
  timeout: timeoutMs,
54613
54816
  encoding: "utf-8",
54614
54817
  env: { ...process.env, ...env2 }
@@ -55037,6 +55240,22 @@ function registerDoctorCommand(program3) {
55037
55240
  })
55038
55241
  },
55039
55242
  { title: "Cron Session", results: runCronSessionChecks(config) },
55243
+ {
55244
+ title: "Component versions (#3919)",
55245
+ results: (() => {
55246
+ try {
55247
+ return runComponentVersionChecks(config);
55248
+ } catch (err) {
55249
+ return [
55250
+ {
55251
+ name: "component versions",
55252
+ status: "skip",
55253
+ detail: `not checkable: ${err?.message ?? String(err)}`
55254
+ }
55255
+ ];
55256
+ }
55257
+ })()
55258
+ },
55040
55259
  {
55041
55260
  title: "Generated-surface drift (KEN-130)",
55042
55261
  results: await runGeneratedSurfaceDriftChecks(config, configPath, {
@@ -55124,6 +55343,7 @@ var init_doctor = __esm(() => {
55124
55343
  init_doctor_cron_session();
55125
55344
  init_doctor_flood_pressure();
55126
55345
  init_doctor_drift();
55346
+ init_doctor_component_versions();
55127
55347
  init_doctor_microsoft();
55128
55348
  init_doctor_notion();
55129
55349
  init_doctor_credentials_migration();
@@ -68072,9 +68292,9 @@ __export(exports_server, {
68072
68292
  dispatchTool: () => dispatchTool,
68073
68293
  TOOLS: () => TOOLS
68074
68294
  });
68075
- import { spawnSync as spawnSync20 } from "node:child_process";
68295
+ import { spawnSync as spawnSync22 } from "node:child_process";
68076
68296
  function execCli(args, stdin) {
68077
- const r = spawnSync20(CLI_BIN, args, {
68297
+ const r = spawnSync22(CLI_BIN, args, {
68078
68298
  encoding: "utf-8",
68079
68299
  env: process.env,
68080
68300
  timeout: 15000,
@@ -68583,7 +68803,7 @@ __export(exports_server2, {
68583
68803
  TOOLS: () => TOOLS2
68584
68804
  });
68585
68805
  import { randomBytes as randomBytes16 } from "node:crypto";
68586
- import { existsSync as existsSync104 } from "node:fs";
68806
+ import { existsSync as existsSync105 } from "node:fs";
68587
68807
  function selfSocketPath() {
68588
68808
  return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
68589
68809
  }
@@ -68610,7 +68830,7 @@ async function dispatchTool2(name, args) {
68610
68830
  return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
68611
68831
  }
68612
68832
  const sockPath = selfSocketPath();
68613
- if (!existsSync104(sockPath)) {
68833
+ if (!existsSync105(sockPath)) {
68614
68834
  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.`);
68615
68835
  }
68616
68836
  let req;
@@ -68861,13 +69081,13 @@ function resolveAuditLogPath() {
68861
69081
  if (process.env.HOSTD_AUDIT_LOG_PATH)
68862
69082
  return process.env.HOSTD_AUDIT_LOG_PATH;
68863
69083
  const bindMounted = "/host-home/.switchroom/host-control-audit.log";
68864
- if (existsSync104(bindMounted))
69084
+ if (existsSync105(bindMounted))
68865
69085
  return bindMounted;
68866
69086
  return defaultAuditLogPath2();
68867
69087
  }
68868
69088
  function getLastUpdateApplyStatus() {
68869
69089
  const path10 = resolveAuditLogPath();
68870
- if (!existsSync104(path10)) {
69090
+ if (!existsSync105(path10)) {
68871
69091
  return errorText2(`get_status: audit log not found at ${path10}. No update_apply has run yet?`);
68872
69092
  }
68873
69093
  let raw;
@@ -69586,11 +69806,11 @@ var init_test_agents = __esm(() => {
69586
69806
  });
69587
69807
 
69588
69808
  // src/litellm/header-passthrough-guard.ts
69589
- import { existsSync as existsSync107, readdirSync as readdirSync41 } from "node:fs";
69809
+ import { existsSync as existsSync108, readdirSync as readdirSync41 } from "node:fs";
69590
69810
  function discoverLiveLitellmConfigPath(opts) {
69591
69811
  const servicesDir = opts?.servicesDir ?? COOLIFY_SERVICES_DIR;
69592
69812
  const readdir2 = opts?.readdirFn ?? ((p) => readdirSync41(p));
69593
- const exists = opts?.existsFn ?? existsSync107;
69813
+ const exists = opts?.existsFn ?? existsSync108;
69594
69814
  let entries;
69595
69815
  try {
69596
69816
  entries = readdir2(servicesDir);
@@ -69680,7 +69900,7 @@ var init_header_passthrough_guard = __esm(() => {
69680
69900
  });
69681
69901
 
69682
69902
  // src/fleet-health/litellm-config-sensor.ts
69683
- import { readFileSync as readFileSync96, existsSync as existsSync108 } from "node:fs";
69903
+ import { readFileSync as readFileSync96, existsSync as existsSync109 } from "node:fs";
69684
69904
  function resolveLitellmConfigPath(explicit, discoverFn = () => discoverLiveLitellmConfigPath()) {
69685
69905
  if (explicit)
69686
69906
  return explicit;
@@ -69691,7 +69911,7 @@ function resolveLitellmConfigPath(explicit, discoverFn = () => discoverLiveLitel
69691
69911
  }
69692
69912
  function scanLitellmConfig(opts = {}) {
69693
69913
  const path10 = resolveLitellmConfigPath(opts.path, opts.discoverFn);
69694
- const exists = opts.existsFn ?? existsSync108;
69914
+ const exists = opts.existsFn ?? existsSync109;
69695
69915
  const read = opts.readFn ?? ((p) => readFileSync96(p, "utf-8"));
69696
69916
  const log = opts.log ?? (() => {});
69697
69917
  const nowIso = opts.nowIso ?? new Date().toISOString();
@@ -69769,18 +69989,18 @@ __export(exports_scan, {
69769
69989
  import {
69770
69990
  readFileSync as readFileSync97,
69771
69991
  readdirSync as readdirSync42,
69772
- existsSync as existsSync109,
69773
- mkdirSync as mkdirSync62,
69992
+ existsSync as existsSync110,
69993
+ mkdirSync as mkdirSync63,
69774
69994
  writeFileSync as writeFileSync46
69775
69995
  } from "node:fs";
69776
- import { resolve as resolve61, dirname as dirname41 } from "node:path";
69996
+ import { resolve as resolve61, dirname as dirname42 } from "node:path";
69777
69997
  import { homedir as homedir60 } from "node:os";
69778
69998
  function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir60()) {
69779
69999
  return resolve61(home2, ".switchroom");
69780
70000
  }
69781
70001
  function listAgents(base) {
69782
70002
  const dir = resolve61(base, "agents");
69783
- if (!existsSync109(dir))
70003
+ if (!existsSync110(dir))
69784
70004
  return [];
69785
70005
  try {
69786
70006
  return readdirSync42(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
@@ -69809,7 +70029,7 @@ function runScan(opts = {}) {
69809
70029
  let gwText = "";
69810
70030
  let sawArtifact = false;
69811
70031
  try {
69812
- if (existsSync109(turnsPath)) {
70032
+ if (existsSync110(turnsPath)) {
69813
70033
  turnsText = readFileSync97(turnsPath, "utf-8");
69814
70034
  sawArtifact = true;
69815
70035
  }
@@ -69817,7 +70037,7 @@ function runScan(opts = {}) {
69817
70037
  log(`fleet-health: WARN skipping ${agent} turns.jsonl unreadable: ${String(e)}`);
69818
70038
  }
69819
70039
  try {
69820
- if (existsSync109(gwPath)) {
70040
+ if (existsSync110(gwPath)) {
69821
70041
  gwText = readFileSync97(gwPath, "utf-8");
69822
70042
  sawArtifact = true;
69823
70043
  }
@@ -69860,7 +70080,7 @@ function runScan(opts = {}) {
69860
70080
  function readLedgerIfPresent(base) {
69861
70081
  const path10 = ledgerPathForBase(base);
69862
70082
  try {
69863
- if (!existsSync109(path10))
70083
+ if (!existsSync110(path10))
69864
70084
  return null;
69865
70085
  return JSON.parse(readFileSync97(path10, "utf-8"));
69866
70086
  } catch {
@@ -69868,11 +70088,11 @@ function readLedgerIfPresent(base) {
69868
70088
  }
69869
70089
  }
69870
70090
  function ledgerPathForBase(base) {
69871
- return fleetHealthLedgerPath(dirname41(base));
70091
+ return fleetHealthLedgerPath(dirname42(base));
69872
70092
  }
69873
70093
  function writeLedger(base, ledger) {
69874
70094
  const path10 = ledgerPathForBase(base);
69875
- mkdirSync62(dirname41(path10), { recursive: true });
70095
+ mkdirSync63(dirname42(path10), { recursive: true });
69876
70096
  writeFileSync46(path10, JSON.stringify(ledger, null, 2) + `
69877
70097
  `, "utf-8");
69878
70098
  return path10;
@@ -90583,9 +90803,9 @@ init_source();
90583
90803
  init_loader();
90584
90804
  init_lifecycle();
90585
90805
  init_compose_env();
90586
- import { existsSync as existsSync79, mkdirSync as mkdirSync44, readFileSync as readFileSync73, realpathSync as realpathSync6, statSync as statSync45, chownSync as chownSync8 } from "node:fs";
90587
- import { spawnSync as spawnSync14 } from "node:child_process";
90588
- import { join as join83, dirname as dirname32, resolve as resolve45 } from "node:path";
90806
+ import { existsSync as existsSync80, mkdirSync as mkdirSync45, readFileSync as readFileSync73, realpathSync as realpathSync6, statSync as statSync45, chownSync as chownSync8 } from "node:fs";
90807
+ import { spawnSync as spawnSync16 } from "node:child_process";
90808
+ import { join as join83, dirname as dirname33, resolve as resolve45 } from "node:path";
90589
90809
  import { homedir as homedir43 } from "node:os";
90590
90810
 
90591
90811
  // src/cli/release-yaml.ts
@@ -90833,6 +91053,286 @@ function syncBundledSkills(opts) {
90833
91053
 
90834
91054
  // src/cli/update.ts
90835
91055
  init_resolve_version();
91056
+
91057
+ // src/cli/self-update.ts
91058
+ var SELF_UPDATE_ENV_SENTINEL = "SWITCHROOM_SELF_UPDATED";
91059
+ var VERSION_STORE_DIRNAME = ".switchroom-versions";
91060
+ var GITHUB_LATEST_RELEASE_URL = "https://api.github.com/repos/switchroom/switchroom/releases/latest";
91061
+ function detectInstallKind(p) {
91062
+ if (p.inContainer) {
91063
+ return {
91064
+ kind: "container",
91065
+ reason: "running inside a switchroom container \u2014 the CLI here comes from the " + "container image and is updated by pulling a new image, not by " + "replacing a file. Nothing to self-update."
91066
+ };
91067
+ }
91068
+ if (p.bundleDir.startsWith("/$bunfs")) {
91069
+ if (!p.execPath) {
91070
+ return {
91071
+ kind: "unknown",
91072
+ reason: "compiled binary but process.execPath is empty \u2014 cannot locate the " + "file to replace. Re-run install.sh to upgrade."
91073
+ };
91074
+ }
91075
+ return {
91076
+ kind: "static-binary",
91077
+ binaryPath: p.execPath,
91078
+ reason: `published static binary at ${p.execPath}`
91079
+ };
91080
+ }
91081
+ if (/[/\\]node_modules[/\\]switchroom[/\\]/.test(p.scriptPath)) {
91082
+ return {
91083
+ kind: "npm-global",
91084
+ reason: "installed via npm \u2014 self-update does not manage npm installs. " + "Upgrade with `npm i -g switchroom@latest`."
91085
+ };
91086
+ }
91087
+ if (isSwitchroomSourceCheckoutPath(p.scriptPath)) {
91088
+ return {
91089
+ kind: "source-checkout",
91090
+ reason: "running from a switchroom source checkout \u2014 self-update would clobber " + "your working tree. Upgrade with `git pull && bun install && npm run build`."
91091
+ };
91092
+ }
91093
+ return {
91094
+ kind: "unknown",
91095
+ reason: `could not identify how switchroom was installed (running from ` + `"${p.scriptPath}"). Not touching it. Upgrade with the method you used ` + `to install, or re-run install.sh.`
91096
+ };
91097
+ }
91098
+ function isSwitchroomSourceCheckoutPath(scriptPath) {
91099
+ return /[/\\](src|dist)[/\\]cli[/\\]switchroom(\.js|\.ts)?$/.test(scriptPath);
91100
+ }
91101
+ function releaseAssetName(platform, arch) {
91102
+ const os6 = platform === "linux" ? "linux" : platform === "darwin" ? "macos" : null;
91103
+ const cpu = arch === "x64" ? "amd64" : arch === "arm64" ? "arm64" : null;
91104
+ if (!os6 || !cpu)
91105
+ return null;
91106
+ return `switchroom-${os6}-${cpu}`;
91107
+ }
91108
+ function parseLatestReleaseTag(body) {
91109
+ try {
91110
+ const parsed = JSON.parse(body);
91111
+ if (parsed?.draft === true)
91112
+ return null;
91113
+ const tag = parsed?.tag_name;
91114
+ if (typeof tag !== "string" || !/^v\d+\.\d+\.\d+$/.test(tag))
91115
+ return null;
91116
+ return tag;
91117
+ } catch {
91118
+ return null;
91119
+ }
91120
+ }
91121
+ function expectedChecksum(checksumsText, asset) {
91122
+ for (const line of checksumsText.split(`
91123
+ `)) {
91124
+ const idx = line.indexOf(` ${asset}`);
91125
+ if (idx <= 0)
91126
+ continue;
91127
+ if (line.slice(idx + 2).trim() !== asset)
91128
+ continue;
91129
+ const hash2 = line.slice(0, idx).trim();
91130
+ if (/^[0-9a-f]{64}$/i.test(hash2))
91131
+ return hash2.toLowerCase();
91132
+ }
91133
+ return null;
91134
+ }
91135
+ function planSelfUpdate(opts) {
91136
+ const { detection, currentVersion, latestTag } = opts;
91137
+ if (detection.kind !== "static-binary" || !detection.binaryPath) {
91138
+ return { action: "skip", reason: detection.reason };
91139
+ }
91140
+ if (!latestTag) {
91141
+ return {
91142
+ action: "skip",
91143
+ reason: "could not resolve the latest published release from GitHub " + "(offline, rate-limited, or the API changed shape) \u2014 leaving the CLI as-is."
91144
+ };
91145
+ }
91146
+ const current = `v${currentVersion.trim().replace(/^v/, "")}`;
91147
+ const cmp = compareReleaseTags(current, latestTag);
91148
+ if (cmp === null) {
91149
+ return {
91150
+ action: "skip",
91151
+ reason: `local CLI version "${currentVersion}" is not a comparable semver \u2014 ` + `refusing to guess whether ${latestTag} is an upgrade.`
91152
+ };
91153
+ }
91154
+ if (cmp >= 0)
91155
+ return { action: "current", version: current };
91156
+ return {
91157
+ action: "update",
91158
+ from: current,
91159
+ to: latestTag,
91160
+ binaryPath: detection.binaryPath
91161
+ };
91162
+ }
91163
+ function versionStorePath(installDir, version2, sep4 = "/") {
91164
+ return `${installDir}${sep4}${VERSION_STORE_DIRNAME}${sep4}switchroom-${version2.replace(/^v/, "")}`;
91165
+ }
91166
+ function rollbackHint(installDir, previousVersion) {
91167
+ return `Rollback: cp ${versionStorePath(installDir, previousVersion)} ` + `${installDir}/switchroom`;
91168
+ }
91169
+ function alreadySelfUpdated(env2) {
91170
+ return env2[SELF_UPDATE_ENV_SENTINEL] === "1";
91171
+ }
91172
+ async function fetchLatestReleaseTag(io) {
91173
+ try {
91174
+ return parseLatestReleaseTag(await io.httpGetText(GITHUB_LATEST_RELEASE_URL));
91175
+ } catch {
91176
+ return null;
91177
+ }
91178
+ }
91179
+ async function performSelfUpdate(opts) {
91180
+ const { plan, assetName, io } = opts;
91181
+ const log = opts.log ?? (() => {});
91182
+ const installDir = io.dirname(plan.binaryPath);
91183
+ const store = `${installDir}/${VERSION_STORE_DIRNAME}`;
91184
+ const base = `https://github.com/switchroom/switchroom/releases/download/${plan.to}`;
91185
+ const staged = versionStorePath(installDir, plan.to);
91186
+ const tmp = `${staged}.download`;
91187
+ io.mkdirp(store);
91188
+ io.remove(tmp);
91189
+ log(`downloading ${assetName} ${plan.to}`);
91190
+ try {
91191
+ await io.httpDownload(`${base}/${assetName}`, tmp);
91192
+ } catch (err) {
91193
+ io.remove(tmp);
91194
+ throw new Error(`self-update: download of ${base}/${assetName} failed (${err.message}). ` + `The installed CLI is unchanged.`);
91195
+ }
91196
+ let checksums;
91197
+ try {
91198
+ checksums = await io.httpGetText(`${base}/switchroom-checksums.txt`);
91199
+ } catch (err) {
91200
+ io.remove(tmp);
91201
+ throw new Error(`self-update: could not fetch switchroom-checksums.txt for ${plan.to} ` + `(${err.message}) \u2014 refusing to install an unverified binary. ` + `The installed CLI is unchanged.`);
91202
+ }
91203
+ const expected = expectedChecksum(checksums, assetName);
91204
+ if (!expected) {
91205
+ io.remove(tmp);
91206
+ throw new Error(`self-update: release ${plan.to} has no checksum entry for ${assetName} \u2014 ` + `refusing to install an unverified binary. The installed CLI is unchanged.`);
91207
+ }
91208
+ const actual = io.sha256File(tmp).toLowerCase();
91209
+ if (actual !== expected) {
91210
+ io.remove(tmp);
91211
+ throw new Error(`self-update: SHA256 mismatch for ${assetName} (expected ${expected}, got ${actual}) \u2014 ` + `refusing to install. The installed CLI is unchanged.`);
91212
+ }
91213
+ io.chmodExec(tmp);
91214
+ const proved = io.probeBinaryVersion(tmp);
91215
+ if (!proved) {
91216
+ io.remove(tmp);
91217
+ throw new Error(`self-update: the downloaded ${plan.to} binary did not run cleanly on this host ` + `(\`switchroom version\` failed) \u2014 refusing to install it. The installed CLI is unchanged.`);
91218
+ }
91219
+ const previousArchive = versionStorePath(installDir, plan.from);
91220
+ try {
91221
+ if (!io.exists(previousArchive))
91222
+ io.copyFile(plan.binaryPath, previousArchive);
91223
+ } catch (err) {
91224
+ io.remove(tmp);
91225
+ throw new Error(`self-update: could not archive the current binary to ${previousArchive} ` + `(${err.message}) \u2014 refusing to swap without a rollback copy. ` + `The installed CLI is unchanged.`);
91226
+ }
91227
+ io.rename(tmp, staged);
91228
+ io.copyFile(staged, `${plan.binaryPath}.new`);
91229
+ io.chmodExec(`${plan.binaryPath}.new`);
91230
+ io.rename(`${plan.binaryPath}.new`, plan.binaryPath);
91231
+ log(rollbackHint(installDir, plan.from));
91232
+ return {
91233
+ replaced: true,
91234
+ newVersion: plan.to,
91235
+ binaryPath: plan.binaryPath,
91236
+ message: `host CLI ${plan.from} \u2192 ${plan.to} (verified sha256, ran clean). ` + rollbackHint(installDir, plan.from)
91237
+ };
91238
+ }
91239
+
91240
+ // src/cli/self-update-io.ts
91241
+ import { createHash as createHash16 } from "node:crypto";
91242
+ import {
91243
+ chmodSync as chmodSync15,
91244
+ closeSync as closeSync15,
91245
+ copyFileSync as copyFileSync11,
91246
+ createWriteStream,
91247
+ existsSync as existsSync79,
91248
+ mkdirSync as mkdirSync44,
91249
+ openSync as openSync15,
91250
+ readSync as readSync5,
91251
+ renameSync as renameSync19,
91252
+ rmSync as rmSync14
91253
+ } from "node:fs";
91254
+ import { dirname as dirname32 } from "node:path";
91255
+ import { Readable } from "node:stream";
91256
+ import { pipeline } from "node:stream/promises";
91257
+ import { spawnSync as spawnSync15 } from "node:child_process";
91258
+ var HTTP_TIMEOUT_MS = 60000;
91259
+ function defaultSelfUpdateIO() {
91260
+ return {
91261
+ async httpGetText(url) {
91262
+ const res = await fetch(url, {
91263
+ headers: { "user-agent": "switchroom-cli-self-update" },
91264
+ signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
91265
+ });
91266
+ if (!res.ok)
91267
+ throw new Error(`HTTP ${res.status} for ${url}`);
91268
+ return await res.text();
91269
+ },
91270
+ async httpDownload(url, dest) {
91271
+ const res = await fetch(url, {
91272
+ headers: { "user-agent": "switchroom-cli-self-update" },
91273
+ redirect: "follow",
91274
+ signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
91275
+ });
91276
+ if (!res.ok)
91277
+ throw new Error(`HTTP ${res.status} for ${url}`);
91278
+ if (!res.body)
91279
+ throw new Error(`empty response body for ${url}`);
91280
+ await pipeline(Readable.fromWeb(res.body), createWriteStream(dest));
91281
+ },
91282
+ sha256File(path7) {
91283
+ const hash2 = createHash16("sha256");
91284
+ const fd = openSync15(path7, "r");
91285
+ try {
91286
+ const buf = Buffer.allocUnsafe(1 << 20);
91287
+ for (;; ) {
91288
+ const n = readSync5(fd, buf, 0, buf.length, null);
91289
+ if (n <= 0)
91290
+ break;
91291
+ hash2.update(buf.subarray(0, n));
91292
+ }
91293
+ } finally {
91294
+ closeSync15(fd);
91295
+ }
91296
+ return hash2.digest("hex");
91297
+ },
91298
+ probeBinaryVersion(path7) {
91299
+ const r = spawnSync15(path7, ["version"], {
91300
+ encoding: "utf-8",
91301
+ timeout: 30000,
91302
+ env: { ...process.env, SWITCHROOM_SELF_UPDATED: "" }
91303
+ });
91304
+ if (r.status !== 0)
91305
+ return null;
91306
+ const out = `${r.stdout ?? ""}`.trim();
91307
+ const m = out.match(/\d+\.\d+\.\d+/);
91308
+ return m ? m[0] : null;
91309
+ },
91310
+ mkdirp(dir) {
91311
+ mkdirSync44(dir, { recursive: true });
91312
+ },
91313
+ copyFile(src, dest) {
91314
+ copyFileSync11(src, dest);
91315
+ },
91316
+ chmodExec(path7) {
91317
+ chmodSync15(path7, 493);
91318
+ },
91319
+ rename(src, dest) {
91320
+ renameSync19(src, dest);
91321
+ },
91322
+ remove(path7) {
91323
+ rmSync14(path7, { force: true });
91324
+ },
91325
+ exists(path7) {
91326
+ return existsSync79(path7);
91327
+ },
91328
+ dirname(path7) {
91329
+ return dirname32(path7);
91330
+ }
91331
+ };
91332
+ }
91333
+
91334
+ // src/cli/update.ts
91335
+ init_component_versions();
90836
91336
  function defaultPersistPin(configPath) {
90837
91337
  return (pin) => {
90838
91338
  const path7 = configPath ?? findConfigFile();
@@ -90852,16 +91352,16 @@ function defaultPersistPin(configPath) {
90852
91352
  }
90853
91353
  var DEFAULT_COMPOSE_PATH2 = join83(homedir43(), ".switchroom", "compose", "docker-compose.yml");
90854
91354
  function runningFromSwitchroomCheckout(scriptPath) {
90855
- let dir = dirname32(scriptPath);
91355
+ let dir = dirname33(scriptPath);
90856
91356
  for (let i = 0;i < 12; i++) {
90857
- if (existsSync79(join83(dir, ".git"))) {
91357
+ if (existsSync80(join83(dir, ".git"))) {
90858
91358
  try {
90859
91359
  const pkg = JSON.parse(readFileSync73(join83(dir, "package.json"), "utf-8"));
90860
91360
  if (pkg.name === "switchroom")
90861
91361
  return true;
90862
91362
  } catch {}
90863
91363
  }
90864
- const parent = dirname32(dir);
91364
+ const parent = dirname33(dir);
90865
91365
  if (parent === dir)
90866
91366
  break;
90867
91367
  dir = parent;
@@ -90906,6 +91406,61 @@ function planUpdate(opts) {
90906
91406
  const scriptPath = opts.scriptPath ?? process.argv[1] ?? "";
90907
91407
  const steps = [];
90908
91408
  const hostdContext = typeof opts.hostdContext === "boolean" ? opts.hostdContext : isHostdContext();
91409
+ steps.push({
91410
+ name: "self-update-cli",
91411
+ description: "Replace the host operator CLI binary with the latest published release (checksum-verified, run-proved, atomic swap + versioned rollback copy), then re-exec the new binary for the remaining steps",
91412
+ skipReason: opts.skipSelfUpdate ? "--skip-self-update flag set" : alreadySelfUpdated(process.env) ? "already re-exec'd from a freshly installed binary in this run" : undefined,
91413
+ run: async () => {
91414
+ const say = opts.stdout ?? ((t) => process.stdout.write(t));
91415
+ const io = opts.selfUpdateIO ?? defaultSelfUpdateIO();
91416
+ const detection = detectInstallKind(opts.installProbe ?? {
91417
+ bundleDir: import.meta.dirname,
91418
+ execPath: process.execPath,
91419
+ scriptPath,
91420
+ inContainer: process.env.SWITCHROOM_HOSTD_CONTEXT === "1" || existsSync80("/.dockerenv")
91421
+ });
91422
+ if (detection.kind !== "static-binary") {
91423
+ say(source_default.gray(` host CLI not self-updated: ${detection.reason}
91424
+ `));
91425
+ return;
91426
+ }
91427
+ const latestTag = opts.latestReleaseTagFn ? await opts.latestReleaseTagFn() : await fetchLatestReleaseTag(io);
91428
+ const plan = planSelfUpdate({
91429
+ detection,
91430
+ currentVersion: SWITCHROOM_VERSION,
91431
+ latestTag
91432
+ });
91433
+ if (plan.action === "skip") {
91434
+ say(source_default.gray(` host CLI not self-updated: ${plan.reason}
91435
+ `));
91436
+ return;
91437
+ }
91438
+ if (plan.action === "current") {
91439
+ say(source_default.gray(` host CLI already on ${plan.version}
91440
+ `));
91441
+ return;
91442
+ }
91443
+ const asset = releaseAssetName(process.platform, process.arch);
91444
+ if (!asset) {
91445
+ say(source_default.gray(` host CLI not self-updated: no published binary for ${process.platform}/${process.arch}
91446
+ `));
91447
+ return;
91448
+ }
91449
+ const result = await performSelfUpdate({
91450
+ plan,
91451
+ assetName: asset,
91452
+ io,
91453
+ log: (s) => say(source_default.gray(` ${s}
91454
+ `))
91455
+ });
91456
+ if (result.replaced && opts.selfUpdateSink) {
91457
+ opts.selfUpdateSink.replacedWith = result.newVersion;
91458
+ opts.selfUpdateSink.binaryPath = result.binaryPath;
91459
+ }
91460
+ say(source_default.green(` ${result.message}
91461
+ `));
91462
+ }
91463
+ });
90909
91464
  if (opts.pin) {
90910
91465
  const pin = opts.pin;
90911
91466
  const persist = opts.persistPinFn ?? defaultPersistPin(opts.configPath);
@@ -90941,7 +91496,7 @@ function planUpdate(opts) {
90941
91496
  steps.push({
90942
91497
  name: "pull-images",
90943
91498
  description: "Pull broker / kernel / agent images from GHCR",
90944
- skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync79(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
91499
+ skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync80(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
90945
91500
  run: () => {
90946
91501
  const r = runner("docker", [
90947
91502
  "compose",
@@ -91068,13 +91623,13 @@ function planUpdate(opts) {
91068
91623
  }
91069
91624
  const source = resolve45(import.meta.dirname, "../../skills");
91070
91625
  const dest = join83(homedir43(), ".switchroom", "skills", "_bundled");
91071
- if (!existsSync79(source)) {
91626
+ if (!existsSync80(source)) {
91072
91627
  process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
91073
91628
  `);
91074
91629
  return;
91075
91630
  }
91076
91631
  try {
91077
- mkdirSync44(dirname32(dest), { recursive: true });
91632
+ mkdirSync45(dirname33(dest), { recursive: true });
91078
91633
  const r = syncBundledSkills({
91079
91634
  source,
91080
91635
  dest,
@@ -91100,10 +91655,10 @@ function planUpdate(opts) {
91100
91655
  if (opts.syncBundledSkillsFn)
91101
91656
  return;
91102
91657
  const dest = join83(homedir43(), ".switchroom", "skills", "_bundled");
91103
- if (!existsSync79(dest)) {
91658
+ if (!existsSync80(dest)) {
91104
91659
  return;
91105
91660
  }
91106
- const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !existsSync79(join83(dest, key)));
91661
+ const missing = getBuiltinDefaultSkillEntries().map((e) => e.key).filter((key) => !existsSync80(join83(dest, key)));
91107
91662
  if (missing.length > 0) {
91108
91663
  throw new Error(`verify-bundled-skills: builtin default skill(s) missing from the pool after sync: ` + `${missing.join(", ")}. These ship in the CLI package and must exist in ${dest}. ` + `This is a broken sync or a packaging regression \u2014 the pool is not converged.`);
91109
91664
  }
@@ -91172,7 +91727,7 @@ function planUpdate(opts) {
91172
91727
  return steps;
91173
91728
  }
91174
91729
  function defaultRunner2(cmd, args) {
91175
- const r = spawnSync14(cmd, args, { stdio: "inherit" });
91730
+ const r = spawnSync16(cmd, args, { stdio: "inherit" });
91176
91731
  return { status: r.status ?? 1 };
91177
91732
  }
91178
91733
  function writeMarkerInPreferredLocation(agent, reason, runner) {
@@ -91215,10 +91770,10 @@ function defaultStatusProbe(composePath) {
91215
91770
  try {
91216
91771
  cliBuiltAt = new Date(statSync45(scriptPath).mtimeMs).toISOString();
91217
91772
  } catch {}
91218
- let dir = dirname32(scriptPath);
91773
+ let dir = dirname33(scriptPath);
91219
91774
  for (let i = 0;i < 8; i++) {
91220
91775
  const pkgPath = join83(dir, "package.json");
91221
- if (existsSync79(pkgPath)) {
91776
+ if (existsSync80(pkgPath)) {
91222
91777
  try {
91223
91778
  const pkg = JSON.parse(readFileSync73(pkgPath, "utf-8"));
91224
91779
  if (typeof pkg.version === "string")
@@ -91228,7 +91783,7 @@ function defaultStatusProbe(composePath) {
91228
91783
  }
91229
91784
  break;
91230
91785
  }
91231
- const parent = dirname32(dir);
91786
+ const parent = dirname33(dir);
91232
91787
  if (parent === dir)
91233
91788
  break;
91234
91789
  dir = parent;
@@ -91241,13 +91796,13 @@ function defaultStatusProbe(composePath) {
91241
91796
  warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
91242
91797
  }
91243
91798
  const services = [];
91244
- if (!existsSync79(composePath)) {
91799
+ if (!existsSync80(composePath)) {
91245
91800
  warnings.push(`compose file not found at ${composePath}; service status unknown`);
91246
91801
  return { cliVersion, cliBuiltAt, services, warnings };
91247
91802
  }
91248
91803
  let serviceList = [];
91249
91804
  try {
91250
- const r = spawnSync14("docker", ["compose", "-p", "switchroom", "-f", composePath, "config", "--services"], { encoding: "utf-8", timeout: 1e4 });
91805
+ const r = spawnSync16("docker", ["compose", "-p", "switchroom", "-f", composePath, "config", "--services"], { encoding: "utf-8", timeout: 1e4 });
91251
91806
  if (r.status !== 0) {
91252
91807
  warnings.push(`docker compose config --services failed: ${r.stderr?.trim() ?? r.error?.message ?? "unknown"}`);
91253
91808
  return { cliVersion, cliBuiltAt, services, warnings };
@@ -91264,7 +91819,7 @@ function defaultStatusProbe(composePath) {
91264
91819
  let containerCreatedAt = null;
91265
91820
  let status = "<unknown>";
91266
91821
  try {
91267
- const r = spawnSync14("docker", ["inspect", "-f", "{{.Config.Image}}|{{.Created}}|{{.State.Status}}", containerName2], { encoding: "utf-8", timeout: 5000 });
91822
+ const r = spawnSync16("docker", ["inspect", "-f", "{{.Config.Image}}|{{.Created}}|{{.State.Status}}", containerName2], { encoding: "utf-8", timeout: 5000 });
91268
91823
  if (r.status === 0) {
91269
91824
  const [img, created, st] = r.stdout.trim().split("|");
91270
91825
  image = img ?? null;
@@ -91280,7 +91835,7 @@ function defaultStatusProbe(composePath) {
91280
91835
  let imagePulledAt = null;
91281
91836
  if (image) {
91282
91837
  try {
91283
- const r = spawnSync14("docker", ["image", "inspect", "-f", "{{.Id}}|{{.Created}}|{{.Metadata.LastTagTime}}", image], { encoding: "utf-8", timeout: 5000 });
91838
+ const r = spawnSync16("docker", ["image", "inspect", "-f", "{{.Id}}|{{.Created}}|{{.Metadata.LastTagTime}}", image], { encoding: "utf-8", timeout: 5000 });
91284
91839
  if (r.status === 0) {
91285
91840
  const [id, created, lastTag] = r.stdout.trim().split("|");
91286
91841
  imageDigestShort = id?.replace(/^sha256:/, "").slice(0, 12) ?? null;
@@ -91344,6 +91899,47 @@ function formatStatusReport(rep) {
91344
91899
  return lines.join(`
91345
91900
  `);
91346
91901
  }
91902
+ function buildReexecArgs(opts) {
91903
+ const args = ["update", "--skip-self-update"];
91904
+ if (opts.skipImages)
91905
+ args.push("--skip-images");
91906
+ if (opts.channel)
91907
+ args.push("--channel", opts.channel);
91908
+ if (opts.pin)
91909
+ args.push("--pin", opts.pin);
91910
+ return args;
91911
+ }
91912
+ function defaultReexec(binaryPath, args) {
91913
+ const r = spawnSync16(binaryPath, args, {
91914
+ stdio: "inherit",
91915
+ env: { ...process.env, [SELF_UPDATE_ENV_SENTINEL]: "1" }
91916
+ });
91917
+ return { status: r.status ?? 1 };
91918
+ }
91919
+ function renderDriftPreamble(opts) {
91920
+ try {
91921
+ const exec = opts.componentExec ?? ((cmd, args) => {
91922
+ const r = spawnSync16(cmd, args, { encoding: "utf-8", timeout: 1e4 });
91923
+ return { status: r.status ?? 1, stdout: r.stdout ?? "" };
91924
+ });
91925
+ let pin;
91926
+ try {
91927
+ pin = loadConfig().release?.pin ?? undefined;
91928
+ } catch {
91929
+ pin = undefined;
91930
+ }
91931
+ const report = detectComponentDrift(collectComponents(SWITCHROOM_VERSION, exec), pin);
91932
+ const body = formatComponentDrift(report);
91933
+ return report.behind.length > 0 ? `${body}
91934
+
91935
+ ${report.behind.length} component(s) BEHIND ${report.target} \u2014 this update targets them.
91936
+ ` : `${body}
91937
+ `;
91938
+ } catch (err) {
91939
+ return `Component versions: not checkable (${err.message})
91940
+ `;
91941
+ }
91942
+ }
91347
91943
  async function runUpdate(opts) {
91348
91944
  const stdout = opts.stdout ?? ((s) => process.stdout.write(s));
91349
91945
  const stderr = opts.stderr ?? ((s) => process.stderr.write(s));
@@ -91373,11 +91969,14 @@ async function runUpdate(opts) {
91373
91969
  return 2;
91374
91970
  }
91375
91971
  }
91376
- const steps = planUpdate(opts);
91972
+ const selfUpdateSink = opts.selfUpdateSink ?? {};
91973
+ const steps = planUpdate({ ...opts, selfUpdateSink });
91377
91974
  if (opts.check) {
91378
91975
  stdout(source_default.bold(`switchroom update --check (dry-run)
91379
91976
 
91380
91977
  `));
91978
+ stdout(renderDriftPreamble(opts) + `
91979
+ `);
91381
91980
  for (const step of steps) {
91382
91981
  const status = step.skipReason ? source_default.gray(`[skip] ${step.skipReason}`) : source_default.green("[run]");
91383
91982
  stdout(` ${status} ${step.name} \u2014 ${step.description}
@@ -91397,7 +91996,15 @@ Dry-run only; nothing was changed. Re-run without --check to apply.
91397
91996
  stdout(source_default.bold(`\u25b8 ${step.name}
91398
91997
  `));
91399
91998
  try {
91400
- step.run();
91999
+ await step.run();
92000
+ if (step.name === "self-update-cli" && selfUpdateSink.binaryPath) {
92001
+ const reexec = opts.reexecFn ?? defaultReexec;
92002
+ stdout(source_default.bold(`
92003
+ \u25b8 re-exec ${selfUpdateSink.binaryPath} (${selfUpdateSink.replacedWith}) for the remaining steps
92004
+ `));
92005
+ const r = reexec(selfUpdateSink.binaryPath, buildReexecArgs(opts));
92006
+ return r.status;
92007
+ }
91401
92008
  } catch (err) {
91402
92009
  stderr(source_default.red(`\u2717 ${step.name} failed: ${err.message}
91403
92010
  `));
@@ -91420,7 +92027,7 @@ Dry-run only; nothing was changed. Re-run without --check to apply.
91420
92027
  return 0;
91421
92028
  }
91422
92029
  function registerUpdateCommand(program3) {
91423
- program3.command("update").description("Update switchroom on this host: pull images, refresh scaffolds, recreate containers. Wraps the full `pull && apply && up -d` flow.").option("--check", "Dry-run: print the steps that would execute, exit 0.").option("--skip-images", "Skip the docker image pull (offline mode).").option("--rebuild", "Source-checkout / maintainer only: git pull + bun install + npm run build before applying. REFUSED on a published install \u2014 use `npm i -g switchroom@latest && switchroom update` there.").option("--status", "Read-only snapshot: report local CLI version, image digest + pull time, container creation time per service. Does NOT invoke any update steps. Wired by Telegram /upgrade-status (#927).").option("--json", "Output as JSON (currently only honored under --status; other modes ignore).").addOption(new Option("--channel <c>", "Override the resolved release block for this update run: follow the named channel (dev|rc|latest). Mutually exclusive with --pin.").choices(["dev", "rc", "latest"]).conflicts("pin")).addOption(new Option("--pin <p>", "Override the resolved release block for this update run: pin to a specific build (sha-<7-40 hex> or v<semver>). Mutually exclusive with --channel.").conflicts("channel")).option("--force", "[legacy v0.6 no-op]").option("--no-restart", "[legacy v0.6 no-op]").option("--resume <file>", "[legacy v0.6 no-op]").option("--phase <phase>", "[legacy v0.6 no-op]").action(async (opts) => {
92030
+ program3.command("update").description("Update EVERY switchroom component on this host \u2014 the operator CLI itself (first), images, scaffolds, the hostd / web / hindsight singletons, and the agent fleet. Wraps the full `self-update && pull && apply && up -d` flow.").option("--check", "Dry-run: print the steps that would execute, exit 0.").option("--skip-images", "Skip the docker image pull (offline mode).").option("--skip-self-update", "Do NOT replace the host operator CLI binary. By default `update` updates every switchroom component INCLUDING itself (and does it first, because `apply` renders scaffolds from templates shipped inside the CLI).").option("--rebuild", "Source-checkout / maintainer only: git pull + bun install + npm run build before applying. REFUSED on a published install \u2014 use `npm i -g switchroom@latest && switchroom update` there.").option("--status", "Read-only snapshot: report local CLI version, image digest + pull time, container creation time per service. Does NOT invoke any update steps. Wired by Telegram /upgrade-status (#927).").option("--json", "Output as JSON (currently only honored under --status; other modes ignore).").addOption(new Option("--channel <c>", "Override the resolved release block for this update run: follow the named channel (dev|rc|latest). Mutually exclusive with --pin.").choices(["dev", "rc", "latest"]).conflicts("pin")).addOption(new Option("--pin <p>", "Override the resolved release block for this update run: pin to a specific build (sha-<7-40 hex> or v<semver>). Mutually exclusive with --channel.").conflicts("channel")).option("--force", "[legacy v0.6 no-op]").option("--no-restart", "[legacy v0.6 no-op]").option("--resume <file>", "[legacy v0.6 no-op]").option("--phase <phase>", "[legacy v0.6 no-op]").action(async (opts) => {
91424
92031
  if (opts.pin && !/^(sha-[0-9a-f]{7,40}|v\d+\.\d+\.\d+)$/.test(opts.pin)) {
91425
92032
  console.error(source_default.red(`--pin "${opts.pin}" is invalid. Expected sha-<7-40 hex> or v<semver>.`));
91426
92033
  process.exit(2);
@@ -91436,23 +92043,23 @@ function registerUpdateCommand(program3) {
91436
92043
 
91437
92044
  // src/cli/rollout.ts
91438
92045
  init_helpers();
91439
- import { spawnSync as spawnSync16 } from "node:child_process";
92046
+ import { spawnSync as spawnSync18 } from "node:child_process";
91440
92047
  import { readFileSync as readFileSync75, chownSync as chownSync9, statSync as statSync47 } from "node:fs";
91441
92048
  import { homedir as homedir45 } from "node:os";
91442
92049
 
91443
92050
  // src/cli/rollout-pin-journal.ts
91444
92051
  import {
91445
- existsSync as existsSync80,
92052
+ existsSync as existsSync81,
91446
92053
  readFileSync as readFileSync74,
91447
92054
  writeFileSync as writeFileSync31,
91448
- renameSync as renameSync19,
92055
+ renameSync as renameSync20,
91449
92056
  unlinkSync as unlinkSync15,
91450
92057
  statSync as statSync46,
91451
- mkdirSync as mkdirSync45
92058
+ mkdirSync as mkdirSync46
91452
92059
  } from "node:fs";
91453
92060
  import { homedir as homedir44 } from "node:os";
91454
- import { createHash as createHash16 } from "node:crypto";
91455
- import { join as join84, basename as basename9, resolve as resolve46, dirname as dirname33 } from "node:path";
92061
+ import { createHash as createHash17 } from "node:crypto";
92062
+ import { join as join84, basename as basename9, resolve as resolve46, dirname as dirname34 } from "node:path";
91456
92063
  init_flock();
91457
92064
  var PIN_JOURNAL_MAX_AGE_MS = 15 * 60 * 1000;
91458
92065
  var STATE_DIR_NAME = ".switchroom";
@@ -91461,7 +92068,7 @@ function pinJournalDir(configPath) {
91461
92068
  if (override && override.trim().length > 0)
91462
92069
  return override.trim();
91463
92070
  if (configPath) {
91464
- const dir = dirname33(resolve46(configPath));
92071
+ const dir = dirname34(resolve46(configPath));
91465
92072
  if (basename9(dir) === STATE_DIR_NAME)
91466
92073
  return dir;
91467
92074
  }
@@ -91469,7 +92076,7 @@ function pinJournalDir(configPath) {
91469
92076
  }
91470
92077
  function pinJournalPath(configPath) {
91471
92078
  const abs = resolve46(configPath);
91472
- const key = createHash16("sha256").update(abs).digest("hex").slice(0, 12);
92079
+ const key = createHash17("sha256").update(abs).digest("hex").slice(0, 12);
91473
92080
  return join84(pinJournalDir(abs), `.rollout-pin-journal.${basename9(abs)}.${key}.json`);
91474
92081
  }
91475
92082
  function isPidAlive(pid) {
@@ -91506,7 +92113,7 @@ function readPinJournal(configPath, warn = (m) => process.stderr.write(m)) {
91506
92113
  try {
91507
92114
  raw = readFileSync74(p, "utf8");
91508
92115
  } catch (e) {
91509
- if (existsSync80(p)) {
92116
+ if (existsSync81(p)) {
91510
92117
  warn(`\u26a0\ufe0f rollout pin journal: ${p} exists but could not be read ` + `(${e.message}). A provisional \`release.pin\` may be ` + `uncommitted \u2014 verify it host-side before the next reconcile.
91511
92118
  `);
91512
92119
  }
@@ -91566,10 +92173,10 @@ function beginPinPersist(configPath, pin, opts = {}) {
91566
92173
  pid: process.pid,
91567
92174
  at: new Date().toISOString()
91568
92175
  };
91569
- mkdirSync45(dirname33(p), { recursive: true });
92176
+ mkdirSync46(dirname34(p), { recursive: true });
91570
92177
  const tmp = `${p}.${process.pid}.tmp`;
91571
92178
  writeFileSync31(tmp, JSON.stringify(journal), { encoding: "utf8", mode: 384 });
91572
- renameSync19(tmp, p);
92179
+ renameSync20(tmp, p);
91573
92180
  return journal;
91574
92181
  }
91575
92182
  function commitPinPersist(configPath) {
@@ -91578,7 +92185,7 @@ function commitPinPersist(configPath) {
91578
92185
  unlinkSync15(p);
91579
92186
  return null;
91580
92187
  } catch (e) {
91581
- if (!existsSync80(p))
92188
+ if (!existsSync81(p))
91582
92189
  return null;
91583
92190
  return `rollout pin journal: FAILED to clear ${p} after a SUCCESSFUL roll ` + `(${e.message}). Delete it host-side \u2014 while it exists, ` + `recovery may revert a proven \`release.pin\`.`;
91584
92191
  }
@@ -91656,10 +92263,10 @@ init_audit_reader();
91656
92263
  init_hindsight();
91657
92264
 
91658
92265
  // src/cli/deploy-version-guard.ts
91659
- import { spawnSync as spawnSync15 } from "node:child_process";
92266
+ import { spawnSync as spawnSync17 } from "node:child_process";
91660
92267
  var DOCKER_INSPECT_TIMEOUT_MS = 60 * 1000;
91661
92268
  var defaultRunner3 = (args) => {
91662
- const r = spawnSync15("docker", args, {
92269
+ const r = spawnSync17("docker", args, {
91663
92270
  encoding: "utf8",
91664
92271
  timeout: DOCKER_INSPECT_TIMEOUT_MS,
91665
92272
  killSignal: "SIGKILL"
@@ -92033,7 +92640,7 @@ function isSpawnTimeout(r, killSignal) {
92033
92640
  var ROLLOUT_KILL_SIGNAL = "SIGKILL";
92034
92641
  function createRolloutDeps(params) {
92035
92642
  const { configPath, scriptPath, hostdCtx } = params;
92036
- const spawn6 = params.spawn ?? spawnSync16;
92643
+ const spawn6 = params.spawn ?? spawnSync18;
92037
92644
  const warn = params.warn ?? ((line) => process.stderr.write(line));
92038
92645
  const dockerRun = (args) => {
92039
92646
  let r;
@@ -92287,8 +92894,8 @@ init_helpers();
92287
92894
  init_lifecycle();
92288
92895
  init_resolve_version();
92289
92896
  import { execSync as execSync3 } from "node:child_process";
92290
- import { existsSync as existsSync81, readFileSync as readFileSync76 } from "node:fs";
92291
- import { dirname as dirname34, join as join85 } from "node:path";
92897
+ import { existsSync as existsSync82, readFileSync as readFileSync76 } from "node:fs";
92898
+ import { dirname as dirname35, join as join85 } from "node:path";
92292
92899
  function getClaudeCodeVersion() {
92293
92900
  try {
92294
92901
  const out = execSync3("claude --version 2>/dev/null", {
@@ -92339,15 +92946,15 @@ function locateSwitchroomInstallDir() {
92339
92946
  let dir = import.meta.dirname;
92340
92947
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
92341
92948
  const pkgPath = join85(dir, "package.json");
92342
- if (existsSync81(pkgPath)) {
92949
+ if (existsSync82(pkgPath)) {
92343
92950
  try {
92344
92951
  const pkg = JSON.parse(readFileSync76(pkgPath, "utf-8"));
92345
- if (pkg.name === "switchroom" && existsSync81(join85(dir, ".git"))) {
92952
+ if (pkg.name === "switchroom" && existsSync82(join85(dir, ".git"))) {
92346
92953
  return dir;
92347
92954
  }
92348
92955
  } catch {}
92349
92956
  }
92350
- dir = dirname34(dir);
92957
+ dir = dirname35(dir);
92351
92958
  }
92352
92959
  return null;
92353
92960
  }
@@ -92521,7 +93128,7 @@ init_merge();
92521
93128
 
92522
93129
  // src/agents/session-retention.ts
92523
93130
  import {
92524
- existsSync as existsSync82,
93131
+ existsSync as existsSync83,
92525
93132
  readdirSync as readdirSync28,
92526
93133
  statSync as statSync48,
92527
93134
  unlinkSync as unlinkSync16
@@ -92532,7 +93139,7 @@ var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
92532
93139
  var MIN_KEEP = 2;
92533
93140
  function collectSessionJsonl(claudeConfigDir) {
92534
93141
  const projects = join86(claudeConfigDir, "projects");
92535
- if (!existsSync82(projects))
93142
+ if (!existsSync83(projects))
92536
93143
  return [];
92537
93144
  const found = [];
92538
93145
  const walk2 = (dir) => {
@@ -92667,13 +93274,13 @@ function registerHandoffCommand(program3) {
92667
93274
 
92668
93275
  // src/issues/store.ts
92669
93276
  import {
92670
- closeSync as closeSync15,
92671
- existsSync as existsSync83,
92672
- mkdirSync as mkdirSync46,
92673
- openSync as openSync15,
93277
+ closeSync as closeSync16,
93278
+ existsSync as existsSync84,
93279
+ mkdirSync as mkdirSync47,
93280
+ openSync as openSync16,
92674
93281
  readdirSync as readdirSync29,
92675
93282
  readFileSync as readFileSync77,
92676
- renameSync as renameSync20,
93283
+ renameSync as renameSync21,
92677
93284
  statSync as statSync49,
92678
93285
  unlinkSync as unlinkSync17,
92679
93286
  writeFileSync as writeFileSync32,
@@ -93111,7 +93718,7 @@ var ISSUES_FILE = "issues.jsonl";
93111
93718
  var ISSUES_LOCK = "issues.lock";
93112
93719
  function readAll(stateDir) {
93113
93720
  const path7 = join87(stateDir, ISSUES_FILE);
93114
- if (!existsSync83(path7))
93721
+ if (!existsSync84(path7))
93115
93722
  return [];
93116
93723
  let raw;
93117
93724
  try {
@@ -93188,7 +93795,7 @@ function record(stateDir, input, nowFn = Date.now) {
93188
93795
  });
93189
93796
  }
93190
93797
  function resolve49(stateDir, fingerprint, nowFn = Date.now) {
93191
- if (!existsSync83(join87(stateDir, ISSUES_FILE)))
93798
+ if (!existsSync84(join87(stateDir, ISSUES_FILE)))
93192
93799
  return 0;
93193
93800
  return withLock(stateDir, () => {
93194
93801
  const all = readAll(stateDir);
@@ -93206,7 +93813,7 @@ function resolve49(stateDir, fingerprint, nowFn = Date.now) {
93206
93813
  });
93207
93814
  }
93208
93815
  function resolveAllBySource(stateDir, source, nowFn = Date.now) {
93209
- if (!existsSync83(join87(stateDir, ISSUES_FILE)))
93816
+ if (!existsSync84(join87(stateDir, ISSUES_FILE)))
93210
93817
  return 0;
93211
93818
  return withLock(stateDir, () => {
93212
93819
  const all = readAll(stateDir);
@@ -93224,7 +93831,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
93224
93831
  });
93225
93832
  }
93226
93833
  function prune(stateDir, opts = {}) {
93227
- if (!existsSync83(join87(stateDir, ISSUES_FILE)))
93834
+ if (!existsSync84(join87(stateDir, ISSUES_FILE)))
93228
93835
  return 0;
93229
93836
  return withLock(stateDir, () => {
93230
93837
  const all = readAll(stateDir);
@@ -93254,7 +93861,7 @@ function prune(stateDir, opts = {}) {
93254
93861
  });
93255
93862
  }
93256
93863
  function ensureDir(stateDir) {
93257
- mkdirSync46(stateDir, { recursive: true });
93864
+ mkdirSync47(stateDir, { recursive: true });
93258
93865
  }
93259
93866
  function writeAll(stateDir, events) {
93260
93867
  const path7 = join87(stateDir, ISSUES_FILE);
@@ -93264,7 +93871,7 @@ function writeAll(stateDir, events) {
93264
93871
  `) + `
93265
93872
  `;
93266
93873
  writeFileSync32(tmp, body, "utf-8");
93267
- renameSync20(tmp, path7);
93874
+ renameSync21(tmp, path7);
93268
93875
  }
93269
93876
  var ORPHAN_TMP_TTL_MS = 60000;
93270
93877
  var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
@@ -93296,7 +93903,7 @@ function withLock(stateDir, fn) {
93296
93903
  let fd = null;
93297
93904
  while (fd === null) {
93298
93905
  try {
93299
- fd = openSync15(lockPath, "wx");
93906
+ fd = openSync16(lockPath, "wx");
93300
93907
  try {
93301
93908
  writeSync9(fd, String(process.pid));
93302
93909
  } catch {}
@@ -93316,7 +93923,7 @@ function withLock(stateDir, fn) {
93316
93923
  return fn();
93317
93924
  } finally {
93318
93925
  try {
93319
- closeSync15(fd);
93926
+ closeSync16(fd);
93320
93927
  } catch {}
93321
93928
  try {
93322
93929
  unlinkSync17(lockPath);
@@ -93574,20 +94181,20 @@ function relTime(deltaMs) {
93574
94181
 
93575
94182
  // src/cli/deps.ts
93576
94183
  init_source();
93577
- import { existsSync as existsSync86 } from "node:fs";
94184
+ import { existsSync as existsSync87 } from "node:fs";
93578
94185
  import { homedir as homedir48 } from "node:os";
93579
94186
  import { join as join90, resolve as resolve50 } from "node:path";
93580
94187
 
93581
94188
  // src/deps/python.ts
93582
- import { createHash as createHash17 } from "node:crypto";
94189
+ import { createHash as createHash18 } from "node:crypto";
93583
94190
  import {
93584
- existsSync as existsSync84,
93585
- mkdirSync as mkdirSync47,
94191
+ existsSync as existsSync85,
94192
+ mkdirSync as mkdirSync48,
93586
94193
  readFileSync as readFileSync78,
93587
- rmSync as rmSync14,
94194
+ rmSync as rmSync15,
93588
94195
  writeFileSync as writeFileSync33
93589
94196
  } from "node:fs";
93590
- import { dirname as dirname35, join as join88 } from "node:path";
94197
+ import { dirname as dirname36, join as join88 } from "node:path";
93591
94198
  import { homedir as homedir46 } from "node:os";
93592
94199
  import { execFileSync as execFileSync24 } from "node:child_process";
93593
94200
 
@@ -93603,13 +94210,13 @@ function defaultPythonCacheRoot() {
93603
94210
  return join88(homedir46(), ".switchroom", "deps", "python");
93604
94211
  }
93605
94212
  function hashFile(path7) {
93606
- return createHash17("sha256").update(readFileSync78(path7)).digest("hex");
94213
+ return createHash18("sha256").update(readFileSync78(path7)).digest("hex");
93607
94214
  }
93608
94215
  function ensurePythonEnv(opts) {
93609
94216
  const { skillName, requirementsPath, force = false } = opts;
93610
94217
  const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
93611
94218
  const hostPython = opts.pythonBin ?? "python3";
93612
- if (!existsSync84(requirementsPath)) {
94219
+ if (!existsSync85(requirementsPath)) {
93613
94220
  throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
93614
94221
  }
93615
94222
  const venvDir = join88(cacheRoot, skillName);
@@ -93618,7 +94225,7 @@ function ensurePythonEnv(opts) {
93618
94225
  const pythonBin = join88(binDir, "python");
93619
94226
  const pipBin = join88(binDir, "pip");
93620
94227
  const targetHash = hashFile(requirementsPath);
93621
- if (!force && existsSync84(stampPath) && existsSync84(pythonBin)) {
94228
+ if (!force && existsSync85(stampPath) && existsSync85(pythonBin)) {
93622
94229
  const existingHash = readFileSync78(stampPath, "utf8").trim();
93623
94230
  if (existingHash === targetHash) {
93624
94231
  return {
@@ -93631,10 +94238,10 @@ function ensurePythonEnv(opts) {
93631
94238
  };
93632
94239
  }
93633
94240
  }
93634
- if (existsSync84(venvDir)) {
93635
- rmSync14(venvDir, { recursive: true, force: true });
94241
+ if (existsSync85(venvDir)) {
94242
+ rmSync15(venvDir, { recursive: true, force: true });
93636
94243
  }
93637
- mkdirSync47(dirname35(venvDir), { recursive: true });
94244
+ mkdirSync48(dirname36(venvDir), { recursive: true });
93638
94245
  try {
93639
94246
  execFileSync24(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
93640
94247
  } catch (err) {
@@ -93666,16 +94273,16 @@ function ensurePythonEnv(opts) {
93666
94273
  }
93667
94274
 
93668
94275
  // src/deps/node.ts
93669
- import { createHash as createHash18 } from "node:crypto";
94276
+ import { createHash as createHash19 } from "node:crypto";
93670
94277
  import {
93671
- copyFileSync as copyFileSync11,
93672
- existsSync as existsSync85,
93673
- mkdirSync as mkdirSync48,
94278
+ copyFileSync as copyFileSync12,
94279
+ existsSync as existsSync86,
94280
+ mkdirSync as mkdirSync49,
93674
94281
  readFileSync as readFileSync79,
93675
- rmSync as rmSync15,
94282
+ rmSync as rmSync16,
93676
94283
  writeFileSync as writeFileSync34
93677
94284
  } from "node:fs";
93678
- import { dirname as dirname36, join as join89 } from "node:path";
94285
+ import { dirname as dirname37, join as join89 } from "node:path";
93679
94286
  import { homedir as homedir47 } from "node:os";
93680
94287
  import { execFileSync as execFileSync25 } from "node:child_process";
93681
94288
 
@@ -93702,14 +94309,14 @@ function defaultNodeCacheRoot() {
93702
94309
  return join89(homedir47(), ".switchroom", "deps", "node");
93703
94310
  }
93704
94311
  function hashDepInputs(packageJsonPath) {
93705
- const sourceDir = dirname36(packageJsonPath);
93706
- const hasher = createHash18("sha256");
94312
+ const sourceDir = dirname37(packageJsonPath);
94313
+ const hasher = createHash19("sha256");
93707
94314
  hasher.update(`package.json
93708
94315
  `);
93709
94316
  hasher.update(readFileSync79(packageJsonPath));
93710
94317
  for (const lockName of ALL_LOCKFILES) {
93711
94318
  const lockPath = join89(sourceDir, lockName);
93712
- if (existsSync85(lockPath)) {
94319
+ if (existsSync86(lockPath)) {
93713
94320
  hasher.update(`
93714
94321
  `);
93715
94322
  hasher.update(lockName);
@@ -93724,16 +94331,16 @@ function ensureNodeEnv(opts) {
93724
94331
  const { skillName, packageJsonPath, force = false } = opts;
93725
94332
  const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
93726
94333
  const installer = opts.installer ?? "bun";
93727
- if (!existsSync85(packageJsonPath)) {
94334
+ if (!existsSync86(packageJsonPath)) {
93728
94335
  throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
93729
94336
  }
93730
- const sourceDir = dirname36(packageJsonPath);
94337
+ const sourceDir = dirname37(packageJsonPath);
93731
94338
  const envDir = join89(cacheRoot, skillName);
93732
94339
  const stampPath = join89(envDir, ".package.sha256");
93733
94340
  const nodeModulesDir = join89(envDir, "node_modules");
93734
94341
  const binDir = join89(nodeModulesDir, ".bin");
93735
94342
  const targetHash = hashDepInputs(packageJsonPath);
93736
- if (!force && existsSync85(stampPath) && existsSync85(nodeModulesDir)) {
94343
+ if (!force && existsSync86(stampPath) && existsSync86(nodeModulesDir)) {
93737
94344
  const existingHash = readFileSync79(stampPath, "utf8").trim();
93738
94345
  if (existingHash === targetHash) {
93739
94346
  return {
@@ -93745,16 +94352,16 @@ function ensureNodeEnv(opts) {
93745
94352
  };
93746
94353
  }
93747
94354
  }
93748
- if (existsSync85(envDir)) {
93749
- rmSync15(envDir, { recursive: true, force: true });
94355
+ if (existsSync86(envDir)) {
94356
+ rmSync16(envDir, { recursive: true, force: true });
93750
94357
  }
93751
- mkdirSync48(envDir, { recursive: true });
93752
- copyFileSync11(packageJsonPath, join89(envDir, "package.json"));
94358
+ mkdirSync49(envDir, { recursive: true });
94359
+ copyFileSync12(packageJsonPath, join89(envDir, "package.json"));
93753
94360
  let copiedLockfile = false;
93754
94361
  for (const lockName of LOCKFILES_FOR[installer]) {
93755
94362
  const lockPath = join89(sourceDir, lockName);
93756
- if (existsSync85(lockPath)) {
93757
- copyFileSync11(lockPath, join89(envDir, lockName));
94363
+ if (existsSync86(lockPath)) {
94364
+ copyFileSync12(lockPath, join89(envDir, lockName));
93758
94365
  copiedLockfile = true;
93759
94366
  }
93760
94367
  }
@@ -93789,22 +94396,22 @@ function registerDepsCommand(program3) {
93789
94396
  const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
93790
94397
  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) => {
93791
94398
  const skillsRoot = builtinSkillsRoot();
93792
- if (!existsSync86(skillsRoot)) {
94399
+ if (!existsSync87(skillsRoot)) {
93793
94400
  console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
93794
94401
  process.exit(1);
93795
94402
  }
93796
94403
  const skillDir = join90(skillsRoot, skill);
93797
- if (!existsSync86(skillDir)) {
94404
+ if (!existsSync87(skillDir)) {
93798
94405
  console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
93799
94406
  process.exit(1);
93800
94407
  }
93801
94408
  const requirementsPath = join90(skillDir, "requirements.txt");
93802
94409
  const packageJsonPath = join90(skillDir, "package.json");
93803
- const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync86(requirementsPath));
93804
- const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync86(packageJsonPath));
94410
+ const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync87(requirementsPath));
94411
+ const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync87(packageJsonPath));
93805
94412
  let did = 0;
93806
94413
  if (wantPython) {
93807
- if (!existsSync86(requirementsPath)) {
94414
+ if (!existsSync87(requirementsPath)) {
93808
94415
  console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
93809
94416
  process.exit(1);
93810
94417
  }
@@ -93828,7 +94435,7 @@ function registerDepsCommand(program3) {
93828
94435
  }
93829
94436
  }
93830
94437
  if (wantNode) {
93831
- if (!existsSync86(packageJsonPath)) {
94438
+ if (!existsSync87(packageJsonPath)) {
93832
94439
  console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
93833
94440
  process.exit(1);
93834
94441
  }
@@ -93861,9 +94468,9 @@ function registerDepsCommand(program3) {
93861
94468
  // src/cli/workspace.ts
93862
94469
  init_helpers();
93863
94470
  init_loader();
93864
- import { existsSync as existsSync87 } from "node:fs";
94471
+ import { existsSync as existsSync88 } from "node:fs";
93865
94472
  import { resolve as resolve51, sep as sep4 } from "node:path";
93866
- import { spawnSync as spawnSync17 } from "node:child_process";
94473
+ import { spawnSync as spawnSync19 } from "node:child_process";
93867
94474
 
93868
94475
  // src/agents/workspace.ts
93869
94476
  import { readFile as readFile2, stat } from "node:fs/promises";
@@ -94576,7 +95183,7 @@ function registerWorkspaceCommand(program3) {
94576
95183
  process.exit(1);
94577
95184
  }
94578
95185
  const editor = process.env["EDITOR"] ?? process.env["VISUAL"] ?? "vi";
94579
- const child = spawnSync17(editor, [target], { stdio: "inherit" });
95186
+ const child = spawnSync19(editor, [target], { stdio: "inherit" });
94580
95187
  if (child.status !== 0 && child.status !== null) {
94581
95188
  process.exit(child.status);
94582
95189
  }
@@ -94638,12 +95245,12 @@ function registerWorkspaceCommand(program3) {
94638
95245
  if (!dir)
94639
95246
  return;
94640
95247
  const gitDir = resolve51(dir, ".git");
94641
- if (!existsSync87(gitDir)) {
95248
+ if (!existsSync88(gitDir)) {
94642
95249
  process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
94643
95250
  `);
94644
95251
  return;
94645
95252
  }
94646
- const statusResult = spawnSync17("git", ["status", "--short"], {
95253
+ const statusResult = spawnSync19("git", ["status", "--short"], {
94647
95254
  cwd: dir,
94648
95255
  encoding: "utf-8"
94649
95256
  });
@@ -94658,7 +95265,7 @@ function registerWorkspaceCommand(program3) {
94658
95265
  return;
94659
95266
  }
94660
95267
  const message = opts.message || `checkpoint: ${new Date().toISOString()}`;
94661
- const addResult = spawnSync17("git", ["add", "-A"], {
95268
+ const addResult = spawnSync19("git", ["add", "-A"], {
94662
95269
  cwd: dir,
94663
95270
  encoding: "utf-8"
94664
95271
  });
@@ -94667,7 +95274,7 @@ function registerWorkspaceCommand(program3) {
94667
95274
  `);
94668
95275
  process.exit(1);
94669
95276
  }
94670
- const commitResult = spawnSync17("git", ["commit", "-m", message], {
95277
+ const commitResult = spawnSync19("git", ["commit", "-m", message], {
94671
95278
  cwd: dir,
94672
95279
  encoding: "utf-8"
94673
95280
  });
@@ -94676,7 +95283,7 @@ function registerWorkspaceCommand(program3) {
94676
95283
  `);
94677
95284
  process.exit(1);
94678
95285
  }
94679
- const shaResult = spawnSync17("git", ["rev-parse", "--short", "HEAD"], {
95286
+ const shaResult = spawnSync19("git", ["rev-parse", "--short", "HEAD"], {
94680
95287
  cwd: dir,
94681
95288
  encoding: "utf-8"
94682
95289
  });
@@ -94692,12 +95299,12 @@ function registerWorkspaceCommand(program3) {
94692
95299
  if (!dir)
94693
95300
  return;
94694
95301
  const gitDir = resolve51(dir, ".git");
94695
- if (!existsSync87(gitDir)) {
95302
+ if (!existsSync88(gitDir)) {
94696
95303
  process.stdout.write(`Workspace is not a git repository.
94697
95304
  `);
94698
95305
  return;
94699
95306
  }
94700
- const child = spawnSync17("git", ["status", "--short"], {
95307
+ const child = spawnSync19("git", ["status", "--short"], {
94701
95308
  cwd: dir,
94702
95309
  stdio: "inherit"
94703
95310
  });
@@ -94717,7 +95324,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
94717
95324
  const agentsDir = resolveAgentsDir(config);
94718
95325
  const agentDir = resolve51(agentsDir, agentName);
94719
95326
  const dir = resolveAgentWorkspaceDir(agentDir);
94720
- if (!existsSync87(dir)) {
95327
+ if (!existsSync88(dir)) {
94721
95328
  process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
94722
95329
  `);
94723
95330
  return;
@@ -94753,7 +95360,7 @@ function safeParseInt(value, fallback) {
94753
95360
  init_helpers();
94754
95361
  init_loader();
94755
95362
  init_merge();
94756
- import { copyFileSync as copyFileSync12, existsSync as existsSync88, readFileSync as readFileSync80, writeFileSync as writeFileSync35 } from "node:fs";
95363
+ import { copyFileSync as copyFileSync13, existsSync as existsSync89, readFileSync as readFileSync80, writeFileSync as writeFileSync35 } from "node:fs";
94757
95364
  import { join as join91, resolve as resolve52 } from "node:path";
94758
95365
  init_scaffold();
94759
95366
  init_profiles();
@@ -94771,7 +95378,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
94771
95378
  const agentsDir = resolveAgentsDir(config);
94772
95379
  const agentDir = resolve52(agentsDir, agentName);
94773
95380
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
94774
- if (!existsSync88(workspaceDir)) {
95381
+ if (!existsSync89(workspaceDir)) {
94775
95382
  console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
94776
95383
  process.exit(1);
94777
95384
  }
@@ -94797,7 +95404,7 @@ function registerSoulCommand(program3) {
94797
95404
  const t = resolveSoulTargetOrExit(program3, agentName);
94798
95405
  if (!t)
94799
95406
  return;
94800
- if (!existsSync88(t.soulPath)) {
95407
+ if (!existsSync89(t.soulPath)) {
94801
95408
  console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
94802
95409
  process.exit(1);
94803
95410
  }
@@ -94812,7 +95419,7 @@ function registerSoulCommand(program3) {
94812
95419
  console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
94813
95420
  process.exit(1);
94814
95421
  }
94815
- const exists = existsSync88(t.soulPath);
95422
+ const exists = existsSync89(t.soulPath);
94816
95423
  if (exists && !opts.yes) {
94817
95424
  if (!isInteractive()) {
94818
95425
  console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
@@ -94827,10 +95434,10 @@ function registerSoulCommand(program3) {
94827
95434
  let backupPath;
94828
95435
  if (exists) {
94829
95436
  backupPath = `${t.soulPath}.bak`;
94830
- if (existsSync88(backupPath)) {
95437
+ if (existsSync89(backupPath)) {
94831
95438
  backupPath = `${t.soulPath}.bak.${Date.now()}`;
94832
95439
  }
94833
- copyFileSync12(t.soulPath, backupPath);
95440
+ copyFileSync13(t.soulPath, backupPath);
94834
95441
  }
94835
95442
  writeFileSync35(t.soulPath, content, "utf-8");
94836
95443
  if (backupPath) {
@@ -94846,9 +95453,9 @@ function registerSoulCommand(program3) {
94846
95453
  // src/cli/debug.ts
94847
95454
  init_helpers();
94848
95455
  init_loader();
94849
- import { existsSync as existsSync89, readFileSync as readFileSync81, readdirSync as readdirSync30, statSync as statSync50 } from "node:fs";
95456
+ import { existsSync as existsSync90, readFileSync as readFileSync81, readdirSync as readdirSync30, statSync as statSync50 } from "node:fs";
94850
95457
  import { resolve as resolve53, join as join92 } from "node:path";
94851
- import { createHash as createHash19 } from "node:crypto";
95458
+ import { createHash as createHash20 } from "node:crypto";
94852
95459
  init_merge();
94853
95460
  init_hindsight2();
94854
95461
  function formatBytes(bytes) {
@@ -94859,7 +95466,7 @@ function estimateTokens(bytes) {
94859
95466
  }
94860
95467
  function readMcpServerNames(agentDir) {
94861
95468
  const mcpPath = join92(agentDir, ".mcp.json");
94862
- if (!existsSync89(mcpPath))
95469
+ if (!existsSync90(mcpPath))
94863
95470
  return [];
94864
95471
  try {
94865
95472
  const parsed = JSON.parse(readFileSync81(mcpPath, "utf-8"));
@@ -94869,11 +95476,11 @@ function readMcpServerNames(agentDir) {
94869
95476
  }
94870
95477
  }
94871
95478
  function sha256(content) {
94872
- return createHash19("sha256").update(content).digest("hex").slice(0, 16);
95479
+ return createHash20("sha256").update(content).digest("hex").slice(0, 16);
94873
95480
  }
94874
95481
  function findLatestTranscriptJsonl(claudeConfigDir) {
94875
95482
  const projectsDir = join92(claudeConfigDir, "projects");
94876
- if (!existsSync89(projectsDir))
95483
+ if (!existsSync90(projectsDir))
94877
95484
  return;
94878
95485
  try {
94879
95486
  const entries = readdirSync30(projectsDir, { withFileTypes: true });
@@ -94883,7 +95490,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
94883
95490
  continue;
94884
95491
  const projectPath = join92(projectsDir, entry.name);
94885
95492
  const transcriptPath = join92(projectPath, "transcript.jsonl");
94886
- if (!existsSync89(transcriptPath))
95493
+ if (!existsSync90(transcriptPath))
94887
95494
  continue;
94888
95495
  const stat3 = statSync50(transcriptPath);
94889
95496
  if (!latest || stat3.mtimeMs > latest.mtime) {
@@ -94946,7 +95553,7 @@ function registerDebugCommand(program3) {
94946
95553
  }
94947
95554
  const agentsDir = resolveAgentsDir(config);
94948
95555
  const agentDir = resolve53(agentsDir, agentName);
94949
- if (!existsSync89(agentDir)) {
95556
+ if (!existsSync90(agentDir)) {
94950
95557
  console.error(`Agent directory not found: ${agentDir}`);
94951
95558
  process.exit(1);
94952
95559
  }
@@ -95001,7 +95608,7 @@ function registerDebugCommand(program3) {
95001
95608
  }
95002
95609
  console.log(`=== Append System Prompt (per-session) ===
95003
95610
  `);
95004
- const handoffContent = existsSync89(handoffPath) ? readFileSync81(handoffPath, "utf-8") : "";
95611
+ const handoffContent = existsSync90(handoffPath) ? readFileSync81(handoffPath, "utf-8") : "";
95005
95612
  if (handoffContent.trim().length > 0) {
95006
95613
  console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
95007
95614
  console.log(handoffContent);
@@ -95012,7 +95619,7 @@ function registerDebugCommand(program3) {
95012
95619
  }
95013
95620
  console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
95014
95621
  `);
95015
- const claudeMdContent = existsSync89(claudeMdPath) ? readFileSync81(claudeMdPath, "utf-8") : "";
95622
+ const claudeMdContent = existsSync90(claudeMdPath) ? readFileSync81(claudeMdPath, "utf-8") : "";
95016
95623
  if (claudeMdContent.trim().length > 0) {
95017
95624
  console.log(`(${formatBytes(claudeMdContent.length)})`);
95018
95625
  console.log(claudeMdContent);
@@ -95023,7 +95630,7 @@ function registerDebugCommand(program3) {
95023
95630
  }
95024
95631
  console.log(`=== Persona (SOUL.md) ===
95025
95632
  `);
95026
- const soulMdContent = existsSync89(soulMdPath) ? readFileSync81(soulMdPath, "utf-8") : existsSync89(workspaceSoulMdPath) ? readFileSync81(workspaceSoulMdPath, "utf-8") : "";
95633
+ const soulMdContent = existsSync90(soulMdPath) ? readFileSync81(soulMdPath, "utf-8") : existsSync90(workspaceSoulMdPath) ? readFileSync81(workspaceSoulMdPath, "utf-8") : "";
95027
95634
  if (soulMdContent.trim().length > 0) {
95028
95635
  console.log(`(${formatBytes(soulMdContent.length)})`);
95029
95636
  console.log(soulMdContent);
@@ -95087,8 +95694,8 @@ function registerDebugCommand(program3) {
95087
95694
  const fleetDir = join92(agentsDir, "..", "fleet");
95088
95695
  const fleetInvPath = join92(fleetDir, "switchroom-invariants.md");
95089
95696
  const fleetClaudePath = join92(fleetDir, "CLAUDE.md");
95090
- const fleetInvBytes = existsSync89(fleetInvPath) ? readFileSync81(fleetInvPath, "utf-8").length : 0;
95091
- const fleetClaudeBytes = existsSync89(fleetClaudePath) ? readFileSync81(fleetClaudePath, "utf-8").length : 0;
95697
+ const fleetInvBytes = existsSync90(fleetInvPath) ? readFileSync81(fleetInvPath, "utf-8").length : 0;
95698
+ const fleetClaudeBytes = existsSync90(fleetClaudePath) ? readFileSync81(fleetClaudePath, "utf-8").length : 0;
95092
95699
  const fleetBytes = fleetInvBytes + fleetClaudeBytes;
95093
95700
  const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
95094
95701
  console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
@@ -95121,20 +95728,20 @@ init_source();
95121
95728
 
95122
95729
  // src/worktree/claim.ts
95123
95730
  import { execFileSync as execFileSync26 } from "node:child_process";
95124
- import { closeSync as closeSync16, mkdirSync as mkdirSync50, openSync as openSync16, existsSync as existsSync91, unlinkSync as unlinkSync19 } from "node:fs";
95731
+ import { closeSync as closeSync17, mkdirSync as mkdirSync51, openSync as openSync17, existsSync as existsSync92, unlinkSync as unlinkSync19 } from "node:fs";
95125
95732
  import { join as join94, resolve as resolve55 } from "node:path";
95126
95733
  import { homedir as homedir50 } from "node:os";
95127
95734
  import { randomBytes as randomBytes14 } from "node:crypto";
95128
95735
 
95129
95736
  // src/worktree/registry.ts
95130
95737
  import {
95131
- mkdirSync as mkdirSync49,
95738
+ mkdirSync as mkdirSync50,
95132
95739
  writeFileSync as writeFileSync36,
95133
95740
  readFileSync as readFileSync82,
95134
95741
  readdirSync as readdirSync31,
95135
95742
  unlinkSync as unlinkSync18,
95136
- existsSync as existsSync90,
95137
- renameSync as renameSync21
95743
+ existsSync as existsSync91,
95744
+ renameSync as renameSync22
95138
95745
  } from "node:fs";
95139
95746
  import { join as join93, resolve as resolve54 } from "node:path";
95140
95747
  import { homedir as homedir49 } from "node:os";
@@ -95145,7 +95752,7 @@ function recordPath(id) {
95145
95752
  return join93(registryDir(), `${id}.json`);
95146
95753
  }
95147
95754
  function ensureDir2() {
95148
- mkdirSync49(registryDir(), { recursive: true });
95755
+ mkdirSync50(registryDir(), { recursive: true });
95149
95756
  }
95150
95757
  function writeRecord(record2) {
95151
95758
  ensureDir2();
@@ -95153,7 +95760,7 @@ function writeRecord(record2) {
95153
95760
  const tmp = `${target}.tmp${process.pid}`;
95154
95761
  writeFileSync36(tmp, JSON.stringify(record2, null, 2) + `
95155
95762
  `, { mode: 384 });
95156
- renameSync21(tmp, target);
95763
+ renameSync22(tmp, target);
95157
95764
  }
95158
95765
  function readRecord(id) {
95159
95766
  const path10 = recordPath(id);
@@ -95191,14 +95798,14 @@ function countByRepo(repoPath) {
95191
95798
  // src/worktree/claim.ts
95192
95799
  function acquireRepoLock(repoPath) {
95193
95800
  const lockDir = registryDir();
95194
- mkdirSync50(lockDir, { recursive: true });
95801
+ mkdirSync51(lockDir, { recursive: true });
95195
95802
  const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
95196
95803
  const lockPath = join94(lockDir, `.lock-${lockName}`);
95197
95804
  const deadline = Date.now() + 5000;
95198
95805
  let fd = null;
95199
95806
  while (fd === null) {
95200
95807
  try {
95201
- fd = openSync16(lockPath, "wx");
95808
+ fd = openSync17(lockPath, "wx");
95202
95809
  } catch (err) {
95203
95810
  if (err.code !== "EEXIST")
95204
95811
  throw err;
@@ -95211,7 +95818,7 @@ function acquireRepoLock(repoPath) {
95211
95818
  }
95212
95819
  return () => {
95213
95820
  try {
95214
- closeSync16(fd);
95821
+ closeSync17(fd);
95215
95822
  } catch {}
95216
95823
  try {
95217
95824
  unlinkSync19(lockPath);
@@ -95247,7 +95854,7 @@ function expandHome(p) {
95247
95854
  }
95248
95855
  async function claimWorktree(input, codeRepos) {
95249
95856
  const repoPath = resolveRepoPath(input.repo, codeRepos);
95250
- if (!existsSync91(repoPath)) {
95857
+ if (!existsSync92(repoPath)) {
95251
95858
  throw new Error(`Repository path does not exist: ${repoPath}`);
95252
95859
  }
95253
95860
  let concurrencyCap = DEFAULT_CONCURRENCY;
@@ -95269,7 +95876,7 @@ async function claimWorktree(input, codeRepos) {
95269
95876
  const taskSuffix = input.taskName ? sanitizeTaskName(input.taskName) : "task";
95270
95877
  branch = `task/${taskSuffix}-${id}`;
95271
95878
  const baseDir = worktreesBaseDir();
95272
- mkdirSync50(baseDir, { recursive: true });
95879
+ mkdirSync51(baseDir, { recursive: true });
95273
95880
  worktreePath = join94(baseDir, `${id}-${taskSuffix}`);
95274
95881
  const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
95275
95882
  const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
@@ -95303,7 +95910,7 @@ async function claimWorktree(input, codeRepos) {
95303
95910
 
95304
95911
  // src/worktree/release.ts
95305
95912
  import { execFileSync as execFileSync27 } from "node:child_process";
95306
- import { existsSync as existsSync92 } from "node:fs";
95913
+ import { existsSync as existsSync93 } from "node:fs";
95307
95914
  function releaseWorktree(input) {
95308
95915
  const { id } = input;
95309
95916
  const record2 = readRecord(id);
@@ -95311,7 +95918,7 @@ function releaseWorktree(input) {
95311
95918
  return { released: true };
95312
95919
  }
95313
95920
  let gitSuccess = true;
95314
- if (existsSync92(record2.path)) {
95921
+ if (existsSync93(record2.path)) {
95315
95922
  try {
95316
95923
  execFileSync27("git", ["worktree", "remove", "--force", record2.path], {
95317
95924
  cwd: record2.repo,
@@ -95350,7 +95957,7 @@ function listWorktrees() {
95350
95957
 
95351
95958
  // src/worktree/reaper.ts
95352
95959
  import { execFileSync as execFileSync28 } from "node:child_process";
95353
- import { existsSync as existsSync93 } from "node:fs";
95960
+ import { existsSync as existsSync94 } from "node:fs";
95354
95961
  var STALE_THRESHOLD_MS = 10 * 60 * 1000;
95355
95962
  function reapSkipReasonText(action) {
95356
95963
  switch (action) {
@@ -95411,7 +96018,7 @@ function planReaper(nowMs, deps = {}) {
95411
96018
  const plan = [];
95412
96019
  for (const record2 of listRecords()) {
95413
96020
  const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
95414
- const worktreeExists = existsSync93(record2.path);
96021
+ const worktreeExists = existsSync94(record2.path);
95415
96022
  if (!worktreeExists) {
95416
96023
  plan.push({
95417
96024
  record: record2,
@@ -95492,13 +96099,13 @@ function runReaper(nowMs, deps = {}) {
95492
96099
  // src/worktree/gc.ts
95493
96100
  import { execFileSync as execFileSync29 } from "node:child_process";
95494
96101
  import {
95495
- existsSync as existsSync94,
96102
+ existsSync as existsSync95,
95496
96103
  readFileSync as readFileSync83,
95497
96104
  readdirSync as readdirSync32,
95498
96105
  statSync as statSync51,
95499
- renameSync as renameSync22,
95500
- mkdirSync as mkdirSync51,
95501
- rmSync as rmSync16
96106
+ renameSync as renameSync23,
96107
+ mkdirSync as mkdirSync52,
96108
+ rmSync as rmSync17
95502
96109
  } from "node:fs";
95503
96110
  import { homedir as homedir51 } from "node:os";
95504
96111
  import { join as join95, resolve as resolve56 } from "node:path";
@@ -95583,7 +96190,7 @@ function isEphemeralPath(path10) {
95583
96190
  }
95584
96191
  return false;
95585
96192
  }
95586
- var defaultExec2 = (file, args, cwd) => execFileSync29(file, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).toString();
96193
+ var defaultExec3 = (file, args, cwd) => execFileSync29(file, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).toString();
95587
96194
  function defaultPrSignal(repo, branch, exec) {
95588
96195
  try {
95589
96196
  const out = exec("gh", [
@@ -95619,11 +96226,11 @@ function trashRoot() {
95619
96226
  return resolve56(process.env.SWITCHROOM_WORKTREE_TRASH ?? join95(homedir51(), ".switchroom", "worktree-gc-trash"));
95620
96227
  }
95621
96228
  function planGc(roots, deps = {}) {
95622
- const exists = deps.existsSync ?? existsSync94;
96229
+ const exists = deps.existsSync ?? existsSync95;
95623
96230
  const readDir = deps.readDir ?? ((p) => readdirSync32(p));
95624
96231
  const readFile4 = deps.readFile ?? ((p) => readFileSync83(p, "utf8"));
95625
96232
  const stat3 = deps.stat ?? ((p) => statSync51(p));
95626
- const exec = deps.exec ?? defaultExec2;
96233
+ const exec = deps.exec ?? defaultExec3;
95627
96234
  const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
95628
96235
  const stamp = deps.dateStamp ?? "undated";
95629
96236
  const trash = join95(trashRoot(), stamp);
@@ -95748,11 +96355,11 @@ function planGc(roots, deps = {}) {
95748
96355
  };
95749
96356
  }
95750
96357
  function applyGc(plan, deps = {}) {
95751
- const exec = deps.exec ?? defaultExec2;
95752
- const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync51(p, { recursive: true }));
96358
+ const exec = deps.exec ?? defaultExec3;
96359
+ const mkdirp = deps.mkdirp ?? ((p) => void mkdirSync52(p, { recursive: true }));
95753
96360
  const move = deps.move ?? ((src, dest) => {
95754
96361
  try {
95755
- renameSync22(src, dest);
96362
+ renameSync23(src, dest);
95756
96363
  } catch {
95757
96364
  exec("mv", [src, dest]);
95758
96365
  }
@@ -95798,7 +96405,7 @@ function selectPurgeTargets(entries, olderThanDays) {
95798
96405
  return entries.filter((e) => e.ageDays >= olderThanDays).map((e) => e.path);
95799
96406
  }
95800
96407
  function listTrashEntries(nowMs, deps = {}) {
95801
- const exists = deps.existsSync ?? existsSync94;
96408
+ const exists = deps.existsSync ?? existsSync95;
95802
96409
  const readDir = deps.readDir ?? ((p) => readdirSync32(p));
95803
96410
  const root = trashRoot();
95804
96411
  if (!exists(root))
@@ -95828,7 +96435,7 @@ function purgeTrash(paths) {
95828
96435
  const errors2 = [];
95829
96436
  for (const p of paths) {
95830
96437
  try {
95831
- rmSync16(p, { recursive: true, force: true });
96438
+ rmSync17(p, { recursive: true, force: true });
95832
96439
  deleted.push(p);
95833
96440
  } catch (e) {
95834
96441
  errors2.push(`${p}: ${e.message}`);
@@ -96059,10 +96666,10 @@ init_drive();
96059
96666
  // src/cli/drive-mcp-launcher.ts
96060
96667
  init_scaffold_integration();
96061
96668
  import {
96062
- chmodSync as chmodSync15,
96063
- mkdirSync as mkdirSync52,
96669
+ chmodSync as chmodSync16,
96670
+ mkdirSync as mkdirSync53,
96064
96671
  readdirSync as readdirSync33,
96065
- rmSync as rmSync17,
96672
+ rmSync as rmSync18,
96066
96673
  writeFileSync as writeFileSync37
96067
96674
  } from "node:fs";
96068
96675
  import { join as join96 } from "node:path";
@@ -96258,15 +96865,15 @@ function resolveCredentialsDir(env2) {
96258
96865
  return join96(stateBase, "google-workspace-mcp", "credentials");
96259
96866
  }
96260
96867
  function writeSeedFile(dir, email, seed) {
96261
- mkdirSync52(dir, { recursive: true, mode: 448 });
96262
- chmodSync15(dir, 448);
96868
+ mkdirSync53(dir, { recursive: true, mode: 448 });
96869
+ chmodSync16(dir, 448);
96263
96870
  for (const name of readdirSync33(dir)) {
96264
- rmSync17(join96(dir, name), { force: true, recursive: true });
96871
+ rmSync18(join96(dir, name), { force: true, recursive: true });
96265
96872
  }
96266
96873
  const filename = encodeCredentialsFilename(email);
96267
96874
  const filePath = join96(dir, filename);
96268
96875
  writeFileSync37(filePath, JSON.stringify(seed), { mode: 384 });
96269
- chmodSync15(filePath, 384);
96876
+ chmodSync16(filePath, 384);
96270
96877
  return filePath;
96271
96878
  }
96272
96879
  async function fetchBrokerGoogleCreds() {
@@ -96423,8 +97030,8 @@ function registerDriveMcpLauncherCommand(program3) {
96423
97030
  // src/cli/m365-mcp-launcher.ts
96424
97031
  init_scaffold_integration();
96425
97032
  import { spawn as spawn6 } from "node:child_process";
96426
- import { writeFileSync as writeFileSync38, mkdirSync as mkdirSync53 } from "node:fs";
96427
- import { dirname as dirname37, join as join97 } from "node:path";
97033
+ import { writeFileSync as writeFileSync38, mkdirSync as mkdirSync54 } from "node:fs";
97034
+ import { dirname as dirname38, join as join97 } from "node:path";
96428
97035
  var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
96429
97036
  var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
96430
97037
  var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
@@ -96460,7 +97067,7 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
96460
97067
  function writeRefreshHeartbeat(agentName, data, account) {
96461
97068
  const path10 = heartbeatPath(agentName, account);
96462
97069
  try {
96463
- mkdirSync53(dirname37(path10), { recursive: true });
97070
+ mkdirSync54(dirname38(path10), { recursive: true });
96464
97071
  writeFileSync38(path10, JSON.stringify(data, null, 2), { mode: 420 });
96465
97072
  } catch {}
96466
97073
  }
@@ -96701,8 +97308,8 @@ function registerM365McpLauncherCommand(program3) {
96701
97308
  // src/cli/notion-mcp-launcher.ts
96702
97309
  init_scaffold_integration();
96703
97310
  import { spawn as spawn7 } from "node:child_process";
96704
- import { existsSync as existsSync95, mkdirSync as mkdirSync54, writeFileSync as writeFileSync39 } from "node:fs";
96705
- import { dirname as dirname38 } from "node:path";
97311
+ import { existsSync as existsSync96, mkdirSync as mkdirSync55, writeFileSync as writeFileSync39 } from "node:fs";
97312
+ import { dirname as dirname39 } from "node:path";
96706
97313
  var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
96707
97314
  var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
96708
97315
  var DEFAULT_VAULT_KEY = "notion/integration-token";
@@ -96712,9 +97319,9 @@ function buildNotionMcpArgs(opts) {
96712
97319
  }
96713
97320
  function defaultWriteHeartbeat(path10, contents) {
96714
97321
  try {
96715
- const dir = dirname38(path10);
96716
- if (!existsSync95(dir))
96717
- mkdirSync54(dir, { recursive: true });
97322
+ const dir = dirname39(path10);
97323
+ if (!existsSync96(dir))
97324
+ mkdirSync55(dir, { recursive: true });
96718
97325
  writeFileSync39(path10, contents);
96719
97326
  } catch {}
96720
97327
  }
@@ -96825,7 +97432,7 @@ function registerNotionMcpLauncherCommand(program3) {
96825
97432
 
96826
97433
  // src/cli/hindsight-mcp-shim.ts
96827
97434
  init_hindsight();
96828
- import { mkdirSync as mkdirSync55, readFileSync as readFileSync84, renameSync as renameSync23, writeFileSync as writeFileSync40 } from "node:fs";
97435
+ import { mkdirSync as mkdirSync56, readFileSync as readFileSync84, renameSync as renameSync24, writeFileSync as writeFileSync40 } from "node:fs";
96829
97436
  import { tmpdir as tmpdir5 } from "node:os";
96830
97437
  import { join as join98 } from "node:path";
96831
97438
  import { createInterface as createInterface6 } from "node:readline";
@@ -97057,11 +97664,11 @@ class HindsightShim {
97057
97664
  }
97058
97665
  writeCache(result) {
97059
97666
  try {
97060
- mkdirSync55(this.opts.cacheDir, { recursive: true });
97667
+ mkdirSync56(this.opts.cacheDir, { recursive: true });
97061
97668
  const tmp = join98(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
97062
97669
  writeFileSync40(tmp, JSON.stringify(result, null, 2) + `
97063
97670
  `);
97064
- renameSync23(tmp, this.cachePath);
97671
+ renameSync24(tmp, this.cachePath);
97065
97672
  } catch (err) {
97066
97673
  this.log(`[hindsight-shim] cache write failed: ${String(err)}`);
97067
97674
  }
@@ -97905,7 +98512,7 @@ function runRedactStdin() {
97905
98512
  }
97906
98513
 
97907
98514
  // src/cli/status-ask.ts
97908
- import { readFileSync as readFileSync86, existsSync as existsSync96, readdirSync as readdirSync34 } from "node:fs";
98515
+ import { readFileSync as readFileSync86, existsSync as existsSync97, readdirSync as readdirSync34 } from "node:fs";
97909
98516
  import { join as join99 } from "node:path";
97910
98517
  import { homedir as homedir52 } from "node:os";
97911
98518
 
@@ -98228,7 +98835,7 @@ function runReport(opts) {
98228
98835
  function resolveSources(explicitPath) {
98229
98836
  if (explicitPath != null && explicitPath.trim() !== "") {
98230
98837
  const trimmed = explicitPath.trim();
98231
- if (!existsSync96(trimmed)) {
98838
+ if (!existsSync97(trimmed)) {
98232
98839
  process.stderr.write(`status-ask report: ${trimmed}: file not found
98233
98840
  `);
98234
98841
  process.exit(1);
@@ -98244,7 +98851,7 @@ function resolveSources(explicitPath) {
98244
98851
  } catch {
98245
98852
  agentsDir = join99(homedir52(), ".switchroom", "agents");
98246
98853
  }
98247
- if (!existsSync96(agentsDir))
98854
+ if (!existsSync97(agentsDir))
98248
98855
  return [];
98249
98856
  const sources = [];
98250
98857
  let entries;
@@ -98255,7 +98862,7 @@ function resolveSources(explicitPath) {
98255
98862
  }
98256
98863
  for (const name of entries) {
98257
98864
  const path10 = join99(agentsDir, name, "runtime-metrics.jsonl");
98258
- if (existsSync96(path10)) {
98865
+ if (existsSync97(path10)) {
98259
98866
  sources.push({ path: path10, agent: name });
98260
98867
  }
98261
98868
  }
@@ -98284,14 +98891,14 @@ var import_yaml21 = __toESM(require_dist(), 1);
98284
98891
  // src/config/overlay-writer.ts
98285
98892
  init_paths();
98286
98893
  import {
98287
- closeSync as closeSync17,
98288
- existsSync as existsSync97,
98894
+ closeSync as closeSync18,
98895
+ existsSync as existsSync98,
98289
98896
  fsyncSync as fsyncSync8,
98290
- mkdirSync as mkdirSync56,
98291
- openSync as openSync17,
98897
+ mkdirSync as mkdirSync57,
98898
+ openSync as openSync18,
98292
98899
  readdirSync as readdirSync35,
98293
98900
  readFileSync as readFileSync87,
98294
- renameSync as renameSync24,
98901
+ renameSync as renameSync25,
98295
98902
  statSync as statSync53,
98296
98903
  unlinkSync as unlinkSync20,
98297
98904
  writeSync as writeSync10
@@ -98315,21 +98922,21 @@ function overlayPathsFor(agent, opts = {}) {
98315
98922
  };
98316
98923
  }
98317
98924
  function ensureDirs(paths) {
98318
- mkdirSync56(paths.scheduleDir, { recursive: true });
98319
- mkdirSync56(paths.scheduleStagingDir, { recursive: true });
98925
+ mkdirSync57(paths.scheduleDir, { recursive: true });
98926
+ mkdirSync57(paths.scheduleStagingDir, { recursive: true });
98320
98927
  }
98321
98928
  function ensureSkillsDirs(paths) {
98322
- mkdirSync56(paths.skillsDir, { recursive: true });
98323
- mkdirSync56(paths.skillsStagingDir, { recursive: true });
98929
+ mkdirSync57(paths.skillsDir, { recursive: true });
98930
+ mkdirSync57(paths.skillsStagingDir, { recursive: true });
98324
98931
  }
98325
98932
  function withAgentLock(paths, fn) {
98326
- mkdirSync56(paths.agentRoot, { recursive: true });
98933
+ mkdirSync57(paths.agentRoot, { recursive: true });
98327
98934
  const start = Date.now();
98328
98935
  const TIMEOUT_MS = 5000;
98329
98936
  let fd = null;
98330
98937
  while (Date.now() - start < TIMEOUT_MS) {
98331
98938
  try {
98332
- fd = openSync17(paths.lockPath, "wx");
98939
+ fd = openSync18(paths.lockPath, "wx");
98333
98940
  break;
98334
98941
  } catch (err) {
98335
98942
  const e = err;
@@ -98353,7 +98960,7 @@ function withAgentLock(paths, fn) {
98353
98960
  return fn();
98354
98961
  } finally {
98355
98962
  try {
98356
- closeSync17(fd);
98963
+ closeSync18(fd);
98357
98964
  } catch {}
98358
98965
  try {
98359
98966
  unlinkSync20(paths.lockPath);
@@ -98366,14 +98973,14 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
98366
98973
  ensureDirs(paths);
98367
98974
  const stagingPath = join100(paths.scheduleStagingDir, `${slug}.yaml`);
98368
98975
  const finalPath = join100(paths.scheduleDir, `${slug}.yaml`);
98369
- const fd = openSync17(stagingPath, "w", 384);
98976
+ const fd = openSync18(stagingPath, "w", 384);
98370
98977
  try {
98371
98978
  writeSync10(fd, yamlText);
98372
98979
  fsyncSync8(fd);
98373
98980
  } finally {
98374
- closeSync17(fd);
98981
+ closeSync18(fd);
98375
98982
  }
98376
- renameSync24(stagingPath, finalPath);
98983
+ renameSync25(stagingPath, finalPath);
98377
98984
  return finalPath;
98378
98985
  });
98379
98986
  }
@@ -98383,14 +98990,14 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
98383
98990
  ensureSkillsDirs(paths);
98384
98991
  const stagingPath = join100(paths.skillsStagingDir, `${slug}.yaml`);
98385
98992
  const finalPath = join100(paths.skillsDir, `${slug}.yaml`);
98386
- const fd = openSync17(stagingPath, "w", 384);
98993
+ const fd = openSync18(stagingPath, "w", 384);
98387
98994
  try {
98388
98995
  writeSync10(fd, yamlText);
98389
98996
  fsyncSync8(fd);
98390
98997
  } finally {
98391
- closeSync17(fd);
98998
+ closeSync18(fd);
98392
98999
  }
98393
- renameSync24(stagingPath, finalPath);
99000
+ renameSync25(stagingPath, finalPath);
98394
99001
  return finalPath;
98395
99002
  });
98396
99003
  }
@@ -98398,7 +99005,7 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
98398
99005
  const paths = overlayPathsFor(agent, opts);
98399
99006
  return withAgentLock(paths, () => {
98400
99007
  const finalPath = join100(paths.skillsDir, `${slug}.yaml`);
98401
- if (!existsSync97(finalPath))
99008
+ if (!existsSync98(finalPath))
98402
99009
  return false;
98403
99010
  unlinkSync20(finalPath);
98404
99011
  return true;
@@ -98406,7 +99013,7 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
98406
99013
  }
98407
99014
  function listSkillsOverlayEntries(agent, opts = {}) {
98408
99015
  const paths = overlayPathsFor(agent, opts);
98409
- if (!existsSync97(paths.skillsDir))
99016
+ if (!existsSync98(paths.skillsDir))
98410
99017
  return [];
98411
99018
  const out = [];
98412
99019
  for (const name of readdirSync35(paths.skillsDir)) {
@@ -98425,7 +99032,7 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
98425
99032
  const paths = overlayPathsFor(agent, opts);
98426
99033
  return withAgentLock(paths, () => {
98427
99034
  const finalPath = join100(paths.scheduleDir, `${slug}.yaml`);
98428
- if (!existsSync97(finalPath))
99035
+ if (!existsSync98(finalPath))
98429
99036
  return false;
98430
99037
  unlinkSync20(finalPath);
98431
99038
  return true;
@@ -98433,7 +99040,7 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
98433
99040
  }
98434
99041
  function listOverlayEntries(agent, opts = {}) {
98435
99042
  const paths = overlayPathsFor(agent, opts);
98436
- if (!existsSync97(paths.scheduleDir))
99043
+ if (!existsSync98(paths.scheduleDir))
98437
99044
  return [];
98438
99045
  const out = [];
98439
99046
  for (const name of readdirSync35(paths.scheduleDir)) {
@@ -98660,14 +99267,14 @@ function reconcileAgentCronOnly(agent) {
98660
99267
 
98661
99268
  // src/cli/agent-config-pending.ts
98662
99269
  import {
98663
- closeSync as closeSync18,
98664
- existsSync as existsSync98,
99270
+ closeSync as closeSync19,
99271
+ existsSync as existsSync99,
98665
99272
  fsyncSync as fsyncSync9,
98666
- mkdirSync as mkdirSync57,
98667
- openSync as openSync18,
99273
+ mkdirSync as mkdirSync58,
99274
+ openSync as openSync19,
98668
99275
  readdirSync as readdirSync36,
98669
99276
  readFileSync as readFileSync88,
98670
- renameSync as renameSync25,
99277
+ renameSync as renameSync26,
98671
99278
  unlinkSync as unlinkSync21,
98672
99279
  writeFileSync as writeFileSync41,
98673
99280
  writeSync as writeSync11
@@ -98681,7 +99288,7 @@ function pendingDir(agent, opts = {}) {
98681
99288
  }
98682
99289
  function ensurePendingDir(agent, opts = {}) {
98683
99290
  const dir = pendingDir(agent, opts);
98684
- mkdirSync57(dir, { recursive: true });
99291
+ mkdirSync58(dir, { recursive: true });
98685
99292
  return dir;
98686
99293
  }
98687
99294
  function newStageId() {
@@ -98703,14 +99310,14 @@ function stagePendingScheduleEntry(opts) {
98703
99310
  };
98704
99311
  const yamlTmp = `${yamlPath}.tmp-${process.pid}`;
98705
99312
  {
98706
- const fd = openSync18(yamlTmp, "w", 384);
99313
+ const fd = openSync19(yamlTmp, "w", 384);
98707
99314
  try {
98708
99315
  writeSync11(fd, opts.yamlText);
98709
99316
  fsyncSync9(fd);
98710
99317
  } finally {
98711
- closeSync18(fd);
99318
+ closeSync19(fd);
98712
99319
  }
98713
- renameSync25(yamlTmp, yamlPath);
99320
+ renameSync26(yamlTmp, yamlPath);
98714
99321
  }
98715
99322
  writeFileSync41(metaPath, JSON.stringify(meta, null, 2) + `
98716
99323
  `, { mode: 384 });
@@ -98718,7 +99325,7 @@ function stagePendingScheduleEntry(opts) {
98718
99325
  }
98719
99326
  function listPendingScheduleEntries(agent, opts = {}) {
98720
99327
  const dir = pendingDir(agent, opts);
98721
- if (!existsSync98(dir))
99328
+ if (!existsSync99(dir))
98722
99329
  return [];
98723
99330
  const out = [];
98724
99331
  for (const name of readdirSync36(dir).sort()) {
@@ -98727,7 +99334,7 @@ function listPendingScheduleEntries(agent, opts = {}) {
98727
99334
  const stageId = name.slice(0, -".meta.json".length);
98728
99335
  const metaPath = join101(dir, name);
98729
99336
  const yamlPath = join101(dir, `${stageId}.yaml`);
98730
- if (!existsSync98(yamlPath))
99337
+ if (!existsSync99(yamlPath))
98731
99338
  continue;
98732
99339
  try {
98733
99340
  const meta = JSON.parse(readFileSync88(metaPath, "utf-8"));
@@ -98746,10 +99353,10 @@ function commitPendingScheduleEntry(opts) {
98746
99353
  const slug = match.meta.entry.name ?? match.stageId;
98747
99354
  const paths = overlayPathsFor(opts.agent, { root: opts.root });
98748
99355
  const finalPath = join101(paths.scheduleDir, `${slug}.yaml`);
98749
- if (existsSync98(finalPath)) {
99356
+ if (existsSync99(finalPath)) {
98750
99357
  return { committed: false, reason: "slug_collision" };
98751
99358
  }
98752
- renameSync25(match.yamlPath, finalPath);
99359
+ renameSync26(match.yamlPath, finalPath);
98753
99360
  unlinkSync21(match.metaPath);
98754
99361
  return { committed: true, path: finalPath, slug };
98755
99362
  }
@@ -98768,7 +99375,7 @@ function denyPendingScheduleEntry(opts) {
98768
99375
  }
98769
99376
 
98770
99377
  // src/cli/agent-config-write.ts
98771
- import { existsSync as existsSync99, readFileSync as readFileSync89 } from "node:fs";
99378
+ import { existsSync as existsSync100, readFileSync as readFileSync89 } from "node:fs";
98772
99379
  import { execFileSync as execFileSync30 } from "node:child_process";
98773
99380
 
98774
99381
  // src/scheduler/schedule-report.ts
@@ -99158,7 +99765,7 @@ function scheduleRemove(opts) {
99158
99765
  }
99159
99766
  let priorContent = null;
99160
99767
  try {
99161
- if (existsSync99(match.path))
99768
+ if (existsSync100(match.path))
99162
99769
  priorContent = readFileSync89(match.path, "utf-8");
99163
99770
  } catch {}
99164
99771
  deleteOverlayEntry(agent, match.slug, { root: opts.root });
@@ -99362,7 +99969,7 @@ function registerAgentConfigWriteCommands(program3) {
99362
99969
  }
99363
99970
  let blob;
99364
99971
  if (opts.jsonl) {
99365
- blob = existsSync99(opts.jsonl) ? readFileSync89(opts.jsonl, "utf-8") : "";
99972
+ blob = existsSync100(opts.jsonl) ? readFileSync89(opts.jsonl, "utf-8") : "";
99366
99973
  } else {
99367
99974
  try {
99368
99975
  blob = execFileSync30("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
@@ -99394,7 +100001,7 @@ function registerAgentConfigWriteCommands(program3) {
99394
100001
 
99395
100002
  // src/cli/agent-config-skill-write.ts
99396
100003
  var import_yaml22 = __toESM(require_dist(), 1);
99397
- import { existsSync as existsSync100 } from "node:fs";
100004
+ import { existsSync as existsSync101 } from "node:fs";
99398
100005
  init_reconcile_default_skills();
99399
100006
  init_agent_config();
99400
100007
  var import_yaml23 = __toESM(require_dist(), 1);
@@ -99474,7 +100081,7 @@ function skillInstall(opts) {
99474
100081
  }
99475
100082
  const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
99476
100083
  const skillPath = join102(poolDir, skillName);
99477
- if (!existsSync100(skillPath)) {
100084
+ if (!existsSync101(skillPath)) {
99478
100085
  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.`);
99479
100086
  }
99480
100087
  const yamlText = import_yaml22.stringify({ skills: [skillName] });
@@ -99637,23 +100244,23 @@ function registerAgentConfigSkillWriteCommands(program3) {
99637
100244
 
99638
100245
  // src/cli/skill.ts
99639
100246
  import {
99640
- closeSync as closeSync19,
99641
- existsSync as existsSync101,
100247
+ closeSync as closeSync20,
100248
+ existsSync as existsSync102,
99642
100249
  lstatSync as lstatSync12,
99643
- mkdirSync as mkdirSync58,
100250
+ mkdirSync as mkdirSync59,
99644
100251
  mkdtempSync as mkdtempSync5,
99645
- openSync as openSync19,
100252
+ openSync as openSync20,
99646
100253
  readFileSync as readFileSync90,
99647
100254
  readdirSync as readdirSync37,
99648
100255
  realpathSync as realpathSync7,
99649
- renameSync as renameSync26,
99650
- rmSync as rmSync18,
100256
+ renameSync as renameSync27,
100257
+ rmSync as rmSync19,
99651
100258
  statSync as statSync54,
99652
100259
  writeFileSync as writeFileSync42
99653
100260
  } from "node:fs";
99654
100261
  import { tmpdir as tmpdir6, homedir as homedir53 } from "node:os";
99655
- import { dirname as dirname39, join as join103, relative as relative4, resolve as resolve58 } from "node:path";
99656
- import { spawnSync as spawnSync18 } from "node:child_process";
100262
+ import { dirname as dirname40, join as join103, relative as relative4, resolve as resolve58 } from "node:path";
100263
+ import { spawnSync as spawnSync20 } from "node:child_process";
99657
100264
 
99658
100265
  // src/cli/skill-common.ts
99659
100266
  var import_yaml24 = __toESM(require_dist(), 1);
@@ -99946,7 +100553,7 @@ function loadFromDir(dir) {
99946
100553
  function loadFromTarball(tarPath) {
99947
100554
  const isGz = tarPath.endsWith(".gz") || tarPath.endsWith(".tgz");
99948
100555
  const listFlags = isGz ? ["-tzf"] : ["-tf"];
99949
- const list2 = spawnSync18("tar", [...listFlags, tarPath], {
100556
+ const list2 = spawnSync20("tar", [...listFlags, tarPath], {
99950
100557
  encoding: "utf-8",
99951
100558
  stdio: ["ignore", "pipe", "pipe"]
99952
100559
  });
@@ -99963,7 +100570,7 @@ function loadFromTarball(tarPath) {
99963
100570
  const staging = mkdtempSync5(join103(tmpdir6(), "skill-apply-extract-"));
99964
100571
  try {
99965
100572
  const flags = isGz ? ["-xzf"] : ["-xf"];
99966
- const r = spawnSync18("tar", [
100573
+ const r = spawnSync20("tar", [
99967
100574
  ...flags,
99968
100575
  tarPath,
99969
100576
  "-C",
@@ -99979,7 +100586,7 @@ function loadFromTarball(tarPath) {
99979
100586
  return loadFromDir(staging);
99980
100587
  } finally {
99981
100588
  try {
99982
- rmSync18(staging, { recursive: true, force: true });
100589
+ rmSync19(staging, { recursive: true, force: true });
99983
100590
  } catch {}
99984
100591
  }
99985
100592
  }
@@ -100038,7 +100645,7 @@ function validatePayload(name, files) {
100038
100645
  if (errors2.length === 0) {
100039
100646
  for (const [path10, content] of Object.entries(files)) {
100040
100647
  if (SH_SCRIPT_RE2.test(path10)) {
100041
- const r = spawnSync18("bash", ["-n"], {
100648
+ const r = spawnSync20("bash", ["-n"], {
100042
100649
  input: content,
100043
100650
  encoding: "utf-8"
100044
100651
  });
@@ -100050,14 +100657,14 @@ function validatePayload(name, files) {
100050
100657
  const tmpPy = join103(tmp, "check.py");
100051
100658
  try {
100052
100659
  writeFileSync42(tmpPy, content);
100053
- const r = spawnSync18("python3", ["-m", "py_compile", tmpPy], {
100660
+ const r = spawnSync20("python3", ["-m", "py_compile", tmpPy], {
100054
100661
  encoding: "utf-8"
100055
100662
  });
100056
100663
  if (r.status !== 0) {
100057
100664
  errors2.push(`${path10} fails \`python3 -m py_compile\` syntax check: ${(r.stderr ?? "").trim()}`);
100058
100665
  }
100059
100666
  } finally {
100060
- rmSync18(tmp, { recursive: true, force: true });
100667
+ rmSync19(tmp, { recursive: true, force: true });
100061
100668
  }
100062
100669
  }
100063
100670
  }
@@ -100067,7 +100674,7 @@ function validatePayload(name, files) {
100067
100674
  function diffSummary(currentDir, files) {
100068
100675
  const lines = [];
100069
100676
  const currentFiles = {};
100070
- if (existsSync101(currentDir)) {
100677
+ if (existsSync102(currentDir)) {
100071
100678
  const walk2 = (sub) => {
100072
100679
  for (const ent of readdirSync37(sub, { withFileTypes: true })) {
100073
100680
  const full = join103(sub, ent.name);
@@ -100103,8 +100710,8 @@ function diffSummary(currentDir, files) {
100103
100710
  `);
100104
100711
  }
100105
100712
  function writePayload(poolDir, name, files) {
100106
- if (!existsSync101(poolDir)) {
100107
- mkdirSync58(poolDir, { recursive: true, mode: 493 });
100713
+ if (!existsSync102(poolDir)) {
100714
+ mkdirSync59(poolDir, { recursive: true, mode: 493 });
100108
100715
  }
100109
100716
  const target = join103(poolDir, name);
100110
100717
  let targetIsSymlink = false;
@@ -100122,12 +100729,12 @@ function writePayload(poolDir, name, files) {
100122
100729
  try {
100123
100730
  for (const [path10, content] of Object.entries(files)) {
100124
100731
  const full = join103(staging, path10);
100125
- mkdirSync58(dirname39(full), { recursive: true, mode: 493 });
100126
- const fd = openSync19(full, "wx");
100732
+ mkdirSync59(dirname40(full), { recursive: true, mode: 493 });
100733
+ const fd = openSync20(full, "wx");
100127
100734
  try {
100128
100735
  writeFileSync42(fd, content);
100129
100736
  } finally {
100130
- closeSync19(fd);
100737
+ closeSync20(fd);
100131
100738
  }
100132
100739
  if (SH_SCRIPT_RE2.test(path10) || PY_SCRIPT_RE2.test(path10)) {
100133
100740
  const fs7 = __require("node:fs");
@@ -100141,23 +100748,23 @@ function writePayload(poolDir, name, files) {
100141
100748
  } catch {}
100142
100749
  if (targetExists) {
100143
100750
  oldRename = `${target}.skill-apply-old-${Date.now()}`;
100144
- renameSync26(target, oldRename);
100751
+ renameSync27(target, oldRename);
100145
100752
  }
100146
- renameSync26(staging, target);
100753
+ renameSync27(staging, target);
100147
100754
  if (oldRename) {
100148
- rmSync18(oldRename, { recursive: true, force: true });
100755
+ rmSync19(oldRename, { recursive: true, force: true });
100149
100756
  oldRename = null;
100150
100757
  }
100151
100758
  } catch (err2) {
100152
100759
  try {
100153
- rmSync18(staging, { recursive: true, force: true });
100760
+ rmSync19(staging, { recursive: true, force: true });
100154
100761
  } catch {}
100155
- if (oldRename && existsSync101(oldRename)) {
100762
+ if (oldRename && existsSync102(oldRename)) {
100156
100763
  try {
100157
- if (existsSync101(target)) {
100158
- rmSync18(target, { recursive: true, force: true });
100764
+ if (existsSync102(target)) {
100765
+ rmSync19(target, { recursive: true, force: true });
100159
100766
  }
100160
- renameSync26(oldRename, target);
100767
+ renameSync27(oldRename, target);
100161
100768
  } catch {}
100162
100769
  }
100163
100770
  throw err2;
@@ -100175,7 +100782,7 @@ function registerSkillCommand(program3) {
100175
100782
  files = loadFromStdin();
100176
100783
  } else {
100177
100784
  const fromPath = resolve58(opts.from);
100178
- if (!existsSync101(fromPath)) {
100785
+ if (!existsSync102(fromPath)) {
100179
100786
  fail4(`--from path does not exist: ${opts.from}`);
100180
100787
  }
100181
100788
  const st = statSync54(fromPath);
@@ -100213,7 +100820,7 @@ function registerSkillCommand(program3) {
100213
100820
  \u2713 Wrote ${name} to ${currentDir}`));
100214
100821
  const applyBin = process.argv[1] ?? "switchroom";
100215
100822
  console.log(source_default.gray(`Running \`switchroom apply --non-interactive\`...`));
100216
- const r = spawnSync18(process.argv0, [applyBin, "apply", "--non-interactive"], { stdio: "inherit" });
100823
+ const r = spawnSync20(process.argv0, [applyBin, "apply", "--non-interactive"], { stdio: "inherit" });
100217
100824
  if (r.status !== 0) {
100218
100825
  console.error(source_default.yellow(`(warning: \`switchroom apply\` exited ${r.status} \u2014 skill is ` + `in the pool but symlinks may not be refreshed. Re-run manually.)`));
100219
100826
  }
@@ -100230,23 +100837,23 @@ function sumBytes(files) {
100230
100837
  // src/cli/skill-personal.ts
100231
100838
  init_esm();
100232
100839
  import {
100233
- closeSync as closeSync20,
100234
- existsSync as existsSync102,
100840
+ closeSync as closeSync21,
100841
+ existsSync as existsSync103,
100235
100842
  lstatSync as lstatSync13,
100236
- mkdirSync as mkdirSync59,
100843
+ mkdirSync as mkdirSync60,
100237
100844
  mkdtempSync as mkdtempSync6,
100238
- openSync as openSync20,
100845
+ openSync as openSync21,
100239
100846
  readFileSync as readFileSync91,
100240
100847
  readdirSync as readdirSync38,
100241
- renameSync as renameSync27,
100242
- rmSync as rmSync19,
100848
+ renameSync as renameSync28,
100849
+ rmSync as rmSync20,
100243
100850
  statSync as statSync55,
100244
100851
  utimesSync,
100245
100852
  writeFileSync as writeFileSync43
100246
100853
  } from "node:fs";
100247
- import { dirname as dirname40, join as join104, relative as relative5, resolve as resolve59 } from "node:path";
100854
+ import { dirname as dirname41, join as join104, relative as relative5, resolve as resolve59 } from "node:path";
100248
100855
  import { homedir as homedir54, tmpdir as tmpdir7 } from "node:os";
100249
- import { spawnSync as spawnSync19 } from "node:child_process";
100856
+ import { spawnSync as spawnSync21 } from "node:child_process";
100250
100857
  init_helpers();
100251
100858
  init_agent_config();
100252
100859
  init_source();
@@ -100257,14 +100864,14 @@ var PERSONAL_SKILLS_SUBPATH = "personal-skills";
100257
100864
  function resolveConfigSkillsDir(agent) {
100258
100865
  const override = process.env.SWITCHROOM_CONFIG_DIR;
100259
100866
  const candidate = override ? resolve59(override) : join104(homedir54(), ".switchroom-config");
100260
- if (!existsSync102(candidate))
100867
+ if (!existsSync103(candidate))
100261
100868
  return null;
100262
100869
  return join104(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
100263
100870
  }
100264
100871
  var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
100265
100872
  function sweepMirrorPriors(configSkillsRoot) {
100266
100873
  try {
100267
- if (!existsSync102(configSkillsRoot))
100874
+ if (!existsSync103(configSkillsRoot))
100268
100875
  return;
100269
100876
  const now = Date.now();
100270
100877
  for (const ent of readdirSync38(configSkillsRoot)) {
@@ -100277,7 +100884,7 @@ function sweepMirrorPriors(configSkillsRoot) {
100277
100884
  if (now - ts < MIRROR_PRIOR_TTL_MS)
100278
100885
  continue;
100279
100886
  try {
100280
- rmSync19(join104(configSkillsRoot, ent), { recursive: true, force: true });
100887
+ rmSync20(join104(configSkillsRoot, ent), { recursive: true, force: true });
100281
100888
  } catch {}
100282
100889
  }
100283
100890
  } catch {}
@@ -100300,17 +100907,17 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
100300
100907
  }
100301
100908
  if (liveSkillDir === null) {
100302
100909
  sweepMirrorPriors(configSkillsRoot);
100303
- if (existsSync102(dest)) {
100910
+ if (existsSync103(dest)) {
100304
100911
  const trash = join104(configSkillsRoot, `.${name}-trash-${Date.now()}`);
100305
- renameSync27(dest, trash);
100912
+ renameSync28(dest, trash);
100306
100913
  }
100307
100914
  return;
100308
100915
  }
100309
- mkdirSync59(configSkillsRoot, { recursive: true, mode: 493 });
100916
+ mkdirSync60(configSkillsRoot, { recursive: true, mode: 493 });
100310
100917
  sweepMirrorPriors(configSkillsRoot);
100311
100918
  const staging = mkdtempSync6(join104(configSkillsRoot, `.${name}-staging-`));
100312
100919
  const walk2 = (src, dst) => {
100313
- mkdirSync59(dst, { recursive: true, mode: 493 });
100920
+ mkdirSync60(dst, { recursive: true, mode: 493 });
100314
100921
  for (const ent of readdirSync38(src, { withFileTypes: true })) {
100315
100922
  const s = join104(src, ent.name);
100316
100923
  const d = join104(dst, ent.name);
@@ -100324,11 +100931,11 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
100324
100931
  }
100325
100932
  };
100326
100933
  walk2(liveSkillDir, staging);
100327
- if (existsSync102(dest)) {
100934
+ if (existsSync103(dest)) {
100328
100935
  const prior = join104(configSkillsRoot, `.${name}-prior-${Date.now()}`);
100329
- renameSync27(dest, prior);
100936
+ renameSync28(dest, prior);
100330
100937
  }
100331
- renameSync27(staging, dest);
100938
+ renameSync28(staging, dest);
100332
100939
  } catch (err2) {
100333
100940
  process.stderr.write(source_default.yellow(`warning: mirror to ${dest} failed (${err2.message ?? err2}); ` + `live copy still works, but this skill is not version-controlled until next successful sync.
100334
100941
  `));
@@ -100365,7 +100972,7 @@ function trashDir(agentsRoot, agent) {
100365
100972
  }
100366
100973
  function countPersonalSkills(agentsRoot, agent) {
100367
100974
  const skillsDir = join104(agentsRoot, agent, ".claude", "skills");
100368
- if (!existsSync102(skillsDir))
100975
+ if (!existsSync103(skillsDir))
100369
100976
  return 0;
100370
100977
  let n = 0;
100371
100978
  for (const ent of readdirSync38(skillsDir, { withFileTypes: true })) {
@@ -100450,7 +101057,7 @@ function behavioralValidate(files) {
100450
101057
  const errors2 = [];
100451
101058
  for (const [path10, content] of Object.entries(files)) {
100452
101059
  if (SH_SCRIPT_RE.test(path10)) {
100453
- const r = spawnSync19("bash", ["-n"], { input: content, encoding: "utf-8" });
101060
+ const r = spawnSync21("bash", ["-n"], { input: content, encoding: "utf-8" });
100454
101061
  if (r.status !== 0) {
100455
101062
  errors2.push(`${path10} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
100456
101063
  }
@@ -100459,14 +101066,14 @@ function behavioralValidate(files) {
100459
101066
  const tmpPy = join104(tmp, "check.py");
100460
101067
  try {
100461
101068
  writeFileSync43(tmpPy, content);
100462
- const r = spawnSync19("python3", ["-m", "py_compile", tmpPy], {
101069
+ const r = spawnSync21("python3", ["-m", "py_compile", tmpPy], {
100463
101070
  encoding: "utf-8"
100464
101071
  });
100465
101072
  if (r.status !== 0) {
100466
101073
  errors2.push(`${path10} fails \`python3 -m py_compile\`: ${(r.stderr ?? "").trim()}`);
100467
101074
  }
100468
101075
  } finally {
100469
- rmSync19(tmp, { recursive: true, force: true });
101076
+ rmSync20(tmp, { recursive: true, force: true });
100470
101077
  }
100471
101078
  }
100472
101079
  }
@@ -100474,7 +101081,7 @@ function behavioralValidate(files) {
100474
101081
  }
100475
101082
  function sweepTrash(agentsRoot, agent) {
100476
101083
  const trash = trashDir(agentsRoot, agent);
100477
- if (!existsSync102(trash))
101084
+ if (!existsSync103(trash))
100478
101085
  return;
100479
101086
  const now = Date.now();
100480
101087
  for (const ent of readdirSync38(trash, { withFileTypes: true })) {
@@ -100484,7 +101091,7 @@ function sweepTrash(agentsRoot, agent) {
100484
101091
  try {
100485
101092
  const st = statSync55(entPath);
100486
101093
  if (now - st.mtimeMs > TRASH_TTL_MS) {
100487
- rmSync19(entPath, { recursive: true, force: true });
101094
+ rmSync20(entPath, { recursive: true, force: true });
100488
101095
  }
100489
101096
  } catch {}
100490
101097
  }
@@ -100500,18 +101107,18 @@ function writePersonalSkill(targetDir, files) {
100500
101107
  if (targetIsSymlink) {
100501
101108
  fail5(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
100502
101109
  }
100503
- mkdirSync59(dirname40(targetDir), { recursive: true, mode: 493 });
100504
- const staging = mkdtempSync6(join104(dirname40(targetDir), `.skill-personal-stage-`));
101110
+ mkdirSync60(dirname41(targetDir), { recursive: true, mode: 493 });
101111
+ const staging = mkdtempSync6(join104(dirname41(targetDir), `.skill-personal-stage-`));
100505
101112
  let oldRename = null;
100506
101113
  try {
100507
101114
  for (const [path10, content] of Object.entries(files)) {
100508
101115
  const full = join104(staging, path10);
100509
- mkdirSync59(dirname40(full), { recursive: true, mode: 493 });
100510
- const fd = openSync20(full, "wx");
101116
+ mkdirSync60(dirname41(full), { recursive: true, mode: 493 });
101117
+ const fd = openSync21(full, "wx");
100511
101118
  try {
100512
101119
  writeFileSync43(fd, content);
100513
101120
  } finally {
100514
- closeSync20(fd);
101121
+ closeSync21(fd);
100515
101122
  }
100516
101123
  if (SH_SCRIPT_RE.test(path10) || PY_SCRIPT_RE.test(path10)) {
100517
101124
  const fs7 = __require("node:fs");
@@ -100525,23 +101132,23 @@ function writePersonalSkill(targetDir, files) {
100525
101132
  } catch {}
100526
101133
  if (targetExists) {
100527
101134
  oldRename = `${targetDir}.personal-old-${Date.now()}`;
100528
- renameSync27(targetDir, oldRename);
101135
+ renameSync28(targetDir, oldRename);
100529
101136
  }
100530
- renameSync27(staging, targetDir);
101137
+ renameSync28(staging, targetDir);
100531
101138
  if (oldRename) {
100532
- rmSync19(oldRename, { recursive: true, force: true });
101139
+ rmSync20(oldRename, { recursive: true, force: true });
100533
101140
  oldRename = null;
100534
101141
  }
100535
101142
  } catch (err2) {
100536
101143
  try {
100537
- rmSync19(staging, { recursive: true, force: true });
101144
+ rmSync20(staging, { recursive: true, force: true });
100538
101145
  } catch {}
100539
- if (oldRename && existsSync102(oldRename)) {
101146
+ if (oldRename && existsSync103(oldRename)) {
100540
101147
  try {
100541
- if (existsSync102(targetDir)) {
100542
- rmSync19(targetDir, { recursive: true, force: true });
101148
+ if (existsSync103(targetDir)) {
101149
+ rmSync20(targetDir, { recursive: true, force: true });
100543
101150
  }
100544
- renameSync27(oldRename, targetDir);
101151
+ renameSync28(oldRename, targetDir);
100545
101152
  } catch {}
100546
101153
  }
100547
101154
  throw err2;
@@ -100604,7 +101211,7 @@ function loadFiles(opts) {
100604
101211
  return loadFromStdin2();
100605
101212
  }
100606
101213
  const p = resolve59(opts.from);
100607
- if (!existsSync102(p)) {
101214
+ if (!existsSync103(p)) {
100608
101215
  fail5(`--from path does not exist: ${opts.from}`);
100609
101216
  }
100610
101217
  const st = statSync55(p);
@@ -100666,7 +101273,7 @@ function resolveCloneSource(source, opts) {
100666
101273
  const slug = m[2];
100667
101274
  const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
100668
101275
  const dir = join104(root, slug);
100669
- if (!existsSync102(dir)) {
101276
+ if (!existsSync103(dir)) {
100670
101277
  fail5(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
100671
101278
  }
100672
101279
  const st = lstatSync13(dir);
@@ -100790,10 +101397,10 @@ function removePersonalAction(name, opts) {
100790
101397
  throw err2;
100791
101398
  }
100792
101399
  const trashRoot2 = trashDir(agentsRoot, agent);
100793
- mkdirSync59(trashRoot2, { recursive: true, mode: 493 });
101400
+ mkdirSync60(trashRoot2, { recursive: true, mode: 493 });
100794
101401
  const ts = Date.now();
100795
101402
  const trashTarget = join104(trashRoot2, `${name}-${ts}`);
100796
- renameSync27(target, trashTarget);
101403
+ renameSync28(target, trashTarget);
100797
101404
  const now = new Date(ts);
100798
101405
  utimesSync(trashTarget, now, now);
100799
101406
  mirrorToConfigRepo(agent, name, null);
@@ -100813,7 +101420,7 @@ function listPersonalAction(opts) {
100813
101420
  sweepTrash(agentsRoot, agent);
100814
101421
  const skillsDir = join104(agentsRoot, agent, ".claude", "skills");
100815
101422
  const personal = [];
100816
- if (existsSync102(skillsDir)) {
101423
+ if (existsSync103(skillsDir)) {
100817
101424
  for (const ent of readdirSync38(skillsDir, { withFileTypes: true })) {
100818
101425
  if (!ent.isDirectory())
100819
101426
  continue;
@@ -100946,7 +101553,7 @@ function registerSelfImproveProposeSkillCommand(program3) {
100946
101553
  init_esm();
100947
101554
  init_helpers();
100948
101555
  var import_yaml25 = __toESM(require_dist(), 1);
100949
- import { existsSync as existsSync103, readdirSync as readdirSync39, readFileSync as readFileSync93, statSync as statSync56 } from "node:fs";
101556
+ import { existsSync as existsSync104, readdirSync as readdirSync39, readFileSync as readFileSync93, statSync as statSync56 } from "node:fs";
100950
101557
  import { homedir as homedir56 } from "node:os";
100951
101558
  import { join as join106, resolve as resolve60 } from "node:path";
100952
101559
  var PERSONAL_PREFIX2 = "personal-";
@@ -100963,7 +101570,7 @@ function defaultBundledRoot2() {
100963
101570
  }
100964
101571
  function readSkillFrontmatter(skillDir) {
100965
101572
  const mdPath = join106(skillDir, "SKILL.md");
100966
- if (!existsSync103(mdPath))
101573
+ if (!existsSync104(mdPath))
100967
101574
  return null;
100968
101575
  let content;
100969
101576
  try {
@@ -101007,7 +101614,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
101007
101614
  if (!AGENT_NAME_RE3.test(agent))
101008
101615
  return [];
101009
101616
  const skillsDir = join106(agentsRoot, agent, ".claude/skills");
101010
- if (!existsSync103(skillsDir))
101617
+ if (!existsSync104(skillsDir))
101011
101618
  return [];
101012
101619
  const out = [];
101013
101620
  let entries;
@@ -101045,7 +101652,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
101045
101652
  return out;
101046
101653
  }
101047
101654
  function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
101048
- if (!existsSync103(sharedRoot))
101655
+ if (!existsSync104(sharedRoot))
101049
101656
  return [];
101050
101657
  const out = [];
101051
101658
  let entries;
@@ -101083,7 +101690,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
101083
101690
  return out;
101084
101691
  }
101085
101692
  function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
101086
- if (!existsSync103(bundledRoot))
101693
+ if (!existsSync104(bundledRoot))
101087
101694
  return [];
101088
101695
  const out = [];
101089
101696
  let entries;
@@ -101241,24 +101848,24 @@ init_source();
101241
101848
  init_helpers();
101242
101849
  init_operator_uid();
101243
101850
  import {
101244
- existsSync as existsSync105,
101245
- mkdirSync as mkdirSync60,
101851
+ existsSync as existsSync106,
101852
+ mkdirSync as mkdirSync61,
101246
101853
  readdirSync as readdirSync40,
101247
101854
  writeFileSync as writeFileSync44,
101248
101855
  statSync as statSync57,
101249
101856
  lstatSync as lstatSync14,
101250
101857
  realpathSync as realpathSync8,
101251
- copyFileSync as copyFileSync13
101858
+ copyFileSync as copyFileSync14
101252
101859
  } from "node:fs";
101253
101860
  import { homedir as homedir57 } from "node:os";
101254
101861
  import { join as join107 } from "node:path";
101255
- import { spawnSync as spawnSync22 } from "node:child_process";
101862
+ import { spawnSync as spawnSync24 } from "node:child_process";
101256
101863
 
101257
101864
  // src/cli/singleton-stale-cleanup.ts
101258
- import { spawnSync as spawnSync21 } from "node:child_process";
101865
+ import { spawnSync as spawnSync23 } from "node:child_process";
101259
101866
  function makeDockerRunner() {
101260
101867
  return (args) => {
101261
- const r = spawnSync21("docker", args, { encoding: "utf8" });
101868
+ const r = spawnSync23("docker", args, { encoding: "utf8" });
101262
101869
  return {
101263
101870
  ok: r.status === 0,
101264
101871
  stdout: r.stdout ?? "",
@@ -101617,7 +102224,7 @@ function resolveHostdSkillsTarget(hostHome) {
101617
102224
  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.`);
101618
102225
  return;
101619
102226
  }
101620
- if (!existsSync105(target)) {
102227
+ if (!existsSync106(target)) {
101621
102228
  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.`);
101622
102229
  return;
101623
102230
  }
@@ -101631,15 +102238,15 @@ function hostdComposePath() {
101631
102238
  }
101632
102239
  function backupExistingCompose() {
101633
102240
  const p = hostdComposePath();
101634
- if (!existsSync105(p))
102241
+ if (!existsSync106(p))
101635
102242
  return null;
101636
102243
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
101637
102244
  const bak = `${p}.bak-${ts}`;
101638
- copyFileSync13(p, bak);
102245
+ copyFileSync14(p, bak);
101639
102246
  return bak;
101640
102247
  }
101641
102248
  function runDocker(args) {
101642
- const r = spawnSync22("docker", args, { encoding: "utf8" });
102249
+ const r = spawnSync24("docker", args, { encoding: "utf8" });
101643
102250
  return {
101644
102251
  ok: r.status === 0,
101645
102252
  stdout: r.stdout ?? "",
@@ -101664,7 +102271,7 @@ async function doInstall(opts, program3) {
101664
102271
  }
101665
102272
  const dir = hostdDir();
101666
102273
  const composePath = hostdComposePath();
101667
- mkdirSync60(dir, { recursive: true });
102274
+ mkdirSync61(dir, { recursive: true });
101668
102275
  const imageTag = resolveHostdImageTag(opts.tag, cfg.release);
101669
102276
  const guard = checkDowngrade({
101670
102277
  container: "switchroom-hostd",
@@ -101729,7 +102336,7 @@ function doStatus() {
101729
102336
  const composeYml = hostdComposePath();
101730
102337
  console.log(source_default.bold("switchroom-hostd"));
101731
102338
  console.log("");
101732
- if (!existsSync105(composeYml)) {
102339
+ if (!existsSync106(composeYml)) {
101733
102340
  console.log(source_default.yellow(" compose: not installed"));
101734
102341
  console.log(source_default.dim(" run `switchroom hostd install` to set up."));
101735
102342
  return;
@@ -101750,14 +102357,14 @@ function doStatus() {
101750
102357
  } else {
101751
102358
  console.log(source_default.green(` container: ${ps.stdout.trim()}`));
101752
102359
  }
101753
- if (existsSync105(dir)) {
102360
+ if (existsSync106(dir)) {
101754
102361
  const entries = [];
101755
102362
  try {
101756
102363
  for (const name of readdirSync40(dir)) {
101757
102364
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
101758
102365
  continue;
101759
102366
  const sockPath = join107(dir, name, "sock");
101760
- if (existsSync105(sockPath)) {
102367
+ if (existsSync106(sockPath)) {
101761
102368
  const st = statSync57(sockPath);
101762
102369
  if ((st.mode & 61440) === 49152) {
101763
102370
  entries.push(`${name} \u2192 ${sockPath}`);
@@ -101776,7 +102383,7 @@ function doStatus() {
101776
102383
  }
101777
102384
  function doUninstall() {
101778
102385
  const composeYml = hostdComposePath();
101779
- if (!existsSync105(composeYml)) {
102386
+ if (!existsSync106(composeYml)) {
101780
102387
  console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
101781
102388
  return;
101782
102389
  }
@@ -101800,7 +102407,7 @@ function registerHostdCommand(program3) {
101800
102407
  hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
101801
102408
  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) => {
101802
102409
  const logPath = opts.path ?? defaultAuditLogPath2();
101803
- if (!existsSync105(logPath)) {
102410
+ if (!existsSync106(logPath)) {
101804
102411
  console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
101805
102412
  The log is created when hostd handles its first privileged-verb request.`));
101806
102413
  return;
@@ -101848,10 +102455,10 @@ The log is created when hostd handles its first privileged-verb request.`));
101848
102455
  init_source();
101849
102456
  init_helpers();
101850
102457
  init_operator_uid();
101851
- import { chownSync as chownSync10, existsSync as existsSync106, mkdirSync as mkdirSync61, writeFileSync as writeFileSync45, copyFileSync as copyFileSync14 } from "node:fs";
102458
+ import { chownSync as chownSync10, existsSync as existsSync107, mkdirSync as mkdirSync62, writeFileSync as writeFileSync45, copyFileSync as copyFileSync15 } from "node:fs";
101852
102459
  import { homedir as homedir58 } from "node:os";
101853
102460
  import { join as join108 } from "node:path";
101854
- import { spawnSync as spawnSync23 } from "node:child_process";
102461
+ import { spawnSync as spawnSync25 } from "node:child_process";
101855
102462
  function resolveWebImageTag(explicitTag, release) {
101856
102463
  if (explicitTag)
101857
102464
  return explicitTag;
@@ -101957,15 +102564,15 @@ function webdComposePath() {
101957
102564
  }
101958
102565
  function backupExistingCompose2() {
101959
102566
  const p = webdComposePath();
101960
- if (!existsSync106(p))
102567
+ if (!existsSync107(p))
101961
102568
  return null;
101962
102569
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
101963
102570
  const bak = `${p}.bak-${ts}`;
101964
- copyFileSync14(p, bak);
102571
+ copyFileSync15(p, bak);
101965
102572
  return bak;
101966
102573
  }
101967
102574
  function runDocker2(args) {
101968
- const r = spawnSync23("docker", args, { encoding: "utf8" });
102575
+ const r = spawnSync25("docker", args, { encoding: "utf8" });
101969
102576
  return {
101970
102577
  ok: r.status === 0,
101971
102578
  stdout: r.stdout ?? "",
@@ -101982,7 +102589,7 @@ async function doInstall2(opts, program3) {
101982
102589
  }
101983
102590
  const dir = webdDir();
101984
102591
  const composePath = webdComposePath();
101985
- mkdirSync61(dir, { recursive: true });
102592
+ mkdirSync62(dir, { recursive: true });
101986
102593
  const cfg = getConfig(program3);
101987
102594
  const imageTag = resolveWebImageTag(opts.tag, cfg.release);
101988
102595
  const port = cfg.web_service?.port ?? 8080;
@@ -102059,7 +102666,7 @@ function doStatus2() {
102059
102666
  const composeYml = webdComposePath();
102060
102667
  console.log(source_default.bold("switchroom-web"));
102061
102668
  console.log("");
102062
- if (!existsSync106(composeYml)) {
102669
+ if (!existsSync107(composeYml)) {
102063
102670
  console.log(source_default.yellow(" compose: not installed"));
102064
102671
  console.log(source_default.dim(" run `switchroom webd install` to set up."));
102065
102672
  return;
@@ -102083,7 +102690,7 @@ function doStatus2() {
102083
102690
  }
102084
102691
  function doUninstall2() {
102085
102692
  const composeYml = webdComposePath();
102086
- if (!existsSync106(composeYml)) {
102693
+ if (!existsSync107(composeYml)) {
102087
102694
  console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
102088
102695
  return;
102089
102696
  }
@@ -102269,7 +102876,7 @@ function applyMountRepairs(items, deps) {
102269
102876
  function registerHostCommand(program3) {
102270
102877
  const host = program3.command("host").description("Host-level maintenance operations for switchroom");
102271
102878
  host.command("repair-mounts").description("Detect and remove known auto-dir artifacts left by a container-context deploy " + "(2026-06-23 outage class). Default: dry-run. Pass --yes to apply.").option("--yes", "Actually perform the removals (default is dry-run)").action(async (opts) => {
102272
- const { rmdirSync: rmdirSync2, rmSync: rmSync20, lstatSync: lstatSync15, readdirSync: readdirSync41 } = await import("node:fs");
102879
+ const { rmdirSync: rmdirSync2, rmSync: rmSync21, lstatSync: lstatSync15, readdirSync: readdirSync41 } = await import("node:fs");
102273
102880
  const probe2 = {
102274
102881
  lstat(path10) {
102275
102882
  try {
@@ -102328,7 +102935,7 @@ ${safeItems.length} item(s) would be removed. Run with --yes to apply.`));
102328
102935
  rmdirSync2(path10);
102329
102936
  },
102330
102937
  rmRf(path10) {
102331
- rmSync20(path10, { recursive: true, force: true });
102938
+ rmSync21(path10, { recursive: true, force: true });
102332
102939
  }
102333
102940
  };
102334
102941
  const results = applyMountRepairs(safeItems, applyDeps);
@@ -102358,13 +102965,13 @@ init_helpers();
102358
102965
  init_scan();
102359
102966
 
102360
102967
  // src/fleet-health/gh-sync.ts
102361
- import { spawnSync as spawnSync24 } from "node:child_process";
102968
+ import { spawnSync as spawnSync26 } from "node:child_process";
102362
102969
  var LABEL = "fleet-health";
102363
102970
  function defaultGhDeps(log) {
102364
102971
  return {
102365
102972
  log,
102366
102973
  run: (args) => {
102367
- const r = spawnSync24("gh", args, { encoding: "utf-8" });
102974
+ const r = spawnSync26("gh", args, { encoding: "utf-8" });
102368
102975
  return {
102369
102976
  ok: r.status === 0,
102370
102977
  stdout: (r.stdout ?? "").trim(),
@@ -102569,7 +103176,7 @@ function printDeepDiveBrief(targets) {
102569
103176
 
102570
103177
  // src/cli/hindsight-watch.ts
102571
103178
  init_source();
102572
- import { existsSync as existsSync111, readdirSync as readdirSync45 } from "node:fs";
103179
+ import { existsSync as existsSync112, readdirSync as readdirSync45 } from "node:fs";
102573
103180
  import { homedir as homedir62 } from "node:os";
102574
103181
  import { join as join111, resolve as resolve63 } from "node:path";
102575
103182
 
@@ -102639,7 +103246,7 @@ function postOperatorNoticeViaGateways(candidates, text, log, connectTimeoutMs =
102639
103246
  init_install_cron();
102640
103247
 
102641
103248
  // src/hindsight-watch/probe.ts
102642
- import { spawnSync as spawnSync25 } from "node:child_process";
103249
+ import { spawnSync as spawnSync27 } from "node:child_process";
102643
103250
  import { readFileSync as readFileSync98, readdirSync as readdirSync44, statSync as statSync59 } from "node:fs";
102644
103251
  import { homedir as homedir61 } from "node:os";
102645
103252
  import { resolve as resolve62 } from "node:path";
@@ -102768,7 +103375,7 @@ function readLlmSignals(series) {
102768
103375
  }
102769
103376
 
102770
103377
  // src/hindsight-watch/recall-log.ts
102771
- import { closeSync as closeSync21, existsSync as existsSync110, openSync as openSync21, readdirSync as readdirSync43, readSync as readSync5, statSync as statSync58 } from "node:fs";
103378
+ import { closeSync as closeSync22, existsSync as existsSync111, openSync as openSync22, readdirSync as readdirSync43, readSync as readSync6, statSync as statSync58 } from "node:fs";
102772
103379
  import { join as join110 } from "node:path";
102773
103380
  var RECALL_WINDOW_ROWS = 200;
102774
103381
  var RECALL_MAX_TAIL_BYTES = 1024 * 1024;
@@ -102777,7 +103384,7 @@ function recallLogPath2(agentDir) {
102777
103384
  return join110(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
102778
103385
  }
102779
103386
  function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
102780
- if (!existsSync110(path10))
103387
+ if (!existsSync111(path10))
102781
103388
  return [];
102782
103389
  let text;
102783
103390
  let truncatedHead = false;
@@ -102788,10 +103395,10 @@ function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
102788
103395
  truncatedHead = start > 0;
102789
103396
  const length = size - start;
102790
103397
  const buf = Buffer.alloc(length);
102791
- fd = openSync21(path10, "r");
103398
+ fd = openSync22(path10, "r");
102792
103399
  let read = 0;
102793
103400
  while (read < length) {
102794
- const n = readSync5(fd, buf, read, length - read, start + read);
103401
+ const n = readSync6(fd, buf, read, length - read, start + read);
102795
103402
  if (n <= 0)
102796
103403
  break;
102797
103404
  read += n;
@@ -102802,7 +103409,7 @@ function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
102802
103409
  } finally {
102803
103410
  if (fd !== undefined) {
102804
103411
  try {
102805
- closeSync21(fd);
103412
+ closeSync22(fd);
102806
103413
  } catch {}
102807
103414
  }
102808
103415
  }
@@ -102989,7 +103596,7 @@ async function probeMetrics(url = DEFAULT_METRICS_URL, fetchImpl = fetch) {
102989
103596
  }
102990
103597
  }
102991
103598
  var defaultRunner4 = (cmd, args) => {
102992
- const r = spawnSync25(cmd, args, { stdio: "pipe", timeout: 1e4, encoding: "utf8" });
103599
+ const r = spawnSync27(cmd, args, { stdio: "pipe", timeout: 1e4, encoding: "utf8" });
102993
103600
  if (r.error)
102994
103601
  return { status: null, stdout: "", stderr: r.error.message };
102995
103602
  return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
@@ -103809,7 +104416,7 @@ async function notifyOperator(agentsDir, text, log) {
103809
104416
  try {
103810
104417
  for (const name of readdirSync45(agentsDir).sort()) {
103811
104418
  const sock = resolve63(agentsDir, name, "telegram", "gateway.sock");
103812
- if (existsSync111(sock))
104419
+ if (existsSync112(sock))
103813
104420
  candidates.push({ agent: name, sock });
103814
104421
  }
103815
104422
  } catch (e) {
@@ -103825,13 +104432,13 @@ async function notifyOperator(agentsDir, text, log) {
103825
104432
 
103826
104433
  // src/cli/openrouter-watch.ts
103827
104434
  init_source();
103828
- import { existsSync as existsSync113, readdirSync as readdirSync46 } from "node:fs";
104435
+ import { existsSync as existsSync114, readdirSync as readdirSync46 } from "node:fs";
103829
104436
  import { homedir as homedir64 } from "node:os";
103830
104437
  import { join as join112, resolve as resolve65 } from "node:path";
103831
104438
 
103832
104439
  // src/openrouter/install-cron.ts
103833
- import { existsSync as existsSync112, mkdirSync as mkdirSync63, readFileSync as readFileSync99, renameSync as renameSync28, writeFileSync as writeFileSync47 } from "node:fs";
103834
- import { dirname as dirname42 } from "node:path";
104440
+ import { existsSync as existsSync113, mkdirSync as mkdirSync64, readFileSync as readFileSync99, renameSync as renameSync29, writeFileSync as writeFileSync47 } from "node:fs";
104441
+ import { dirname as dirname43 } from "node:path";
103835
104442
  var CRON_PATH2 = "/etc/cron.d/openrouter-watch";
103836
104443
  var CRON_SCHEDULE2 = "17 * * * *";
103837
104444
  var CRON_LOG_PATH2 = "/var/log/openrouter-watch.log";
@@ -103848,23 +104455,23 @@ function renderCron2(opts) {
103848
104455
  function installCron2(opts) {
103849
104456
  const path10 = opts.path ?? CRON_PATH2;
103850
104457
  const content = renderCron2(opts);
103851
- if (existsSync112(path10)) {
104458
+ if (existsSync113(path10)) {
103852
104459
  try {
103853
104460
  if (readFileSync99(path10, "utf8") === content) {
103854
104461
  return { status: "unchanged", path: path10, content };
103855
104462
  }
103856
104463
  } catch {}
103857
104464
  }
103858
- mkdirSync63(dirname42(path10), { recursive: true });
104465
+ mkdirSync64(dirname43(path10), { recursive: true });
103859
104466
  const tmp = `${path10}.${process.pid}.tmp`;
103860
104467
  writeFileSync47(tmp, content, { mode: 420 });
103861
- renameSync28(tmp, path10);
104468
+ renameSync29(tmp, path10);
103862
104469
  return { status: "installed", path: path10, content };
103863
104470
  }
103864
104471
  // src/openrouter/state.ts
103865
- import { mkdirSync as mkdirSync64, readFileSync as readFileSync100, renameSync as renameSync29, writeFileSync as writeFileSync48 } from "node:fs";
104472
+ import { mkdirSync as mkdirSync65, readFileSync as readFileSync100, renameSync as renameSync30, writeFileSync as writeFileSync48 } from "node:fs";
103866
104473
  import { homedir as homedir63 } from "node:os";
103867
- import { dirname as dirname43, resolve as resolve64 } from "node:path";
104474
+ import { dirname as dirname44, resolve as resolve64 } from "node:path";
103868
104475
  function defaultStatePath3(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir63()) {
103869
104476
  return resolve64(home2, ".switchroom", "openrouter-watch", "state.json");
103870
104477
  }
@@ -103891,12 +104498,12 @@ function loadState2(path10) {
103891
104498
  return out;
103892
104499
  }
103893
104500
  function saveState2(path10, state) {
103894
- mkdirSync64(dirname43(path10), { recursive: true, mode: 448 });
104501
+ mkdirSync65(dirname44(path10), { recursive: true, mode: 448 });
103895
104502
  const tmp = `${path10}.${process.pid}.tmp`;
103896
104503
  const payload = { v: 1, ...state };
103897
104504
  writeFileSync48(tmp, JSON.stringify(payload, null, 2) + `
103898
104505
  `, { mode: 384 });
103899
- renameSync29(tmp, path10);
104506
+ renameSync30(tmp, path10);
103900
104507
  }
103901
104508
 
103902
104509
  // src/openrouter/watch.ts
@@ -104081,7 +104688,7 @@ async function notifyOperator2(agentsDir, text, log) {
104081
104688
  try {
104082
104689
  for (const name of readdirSync46(agentsDir).sort()) {
104083
104690
  const sock = resolve65(agentsDir, name, "telegram", "gateway.sock");
104084
- if (existsSync113(sock))
104691
+ if (existsSync114(sock))
104085
104692
  candidates.push({ agent: name, sock });
104086
104693
  }
104087
104694
  } catch (e) {