switchroom 0.21.1 → 0.21.4

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.21.1", COMMIT_SHA = "6aef7b24";
2123
+ var VERSION = "0.21.4", COMMIT_SHA = "aa63e74c";
2124
2124
 
2125
2125
  // src/cli/resolve-version.ts
2126
2126
  import { existsSync, readFileSync } from "node:fs";
@@ -63125,6 +63125,17 @@ function rollbackHint(installDir, previousVersion) {
63125
63125
  function alreadySelfUpdated(env2) {
63126
63126
  return env2[SELF_UPDATE_ENV_SENTINEL] === "1";
63127
63127
  }
63128
+ function describeBinaryProbeFailure(opts) {
63129
+ const { probe: probe2, path: path7, subject } = opts;
63130
+ switch (probe2.kind) {
63131
+ case "not-executable":
63132
+ return `could not EXECUTE ${subject} at ${path7} (${probe2.detail}). This is a ` + `property of WHERE it was staged, not of the artifact \u2014 the usual causes ` + `are a staging directory mounted \`noexec\`, a lost execute bit, or an ` + `architecture this kernel cannot run. The download's sha256 already ` + `matched the release's checksums file, so re-downloading will not help.`;
63133
+ case "ran-but-failed":
63134
+ return `${subject} at ${path7} ran but exited non-zero for \`--version\` ` + `(${probe2.detail}) \u2014 the artifact itself is faulty.`;
63135
+ case "no-version":
63136
+ return `${subject} at ${path7} ran and exited 0 but printed no parseable version ` + `(${probe2.detail}) \u2014 the artifact itself is faulty.`;
63137
+ }
63138
+ }
63128
63139
  async function fetchLatestReleaseTag(io) {
63129
63140
  try {
63130
63141
  return parseLatestReleaseTag(await io.httpGetText(GITHUB_LATEST_RELEASE_URL));
@@ -63278,10 +63289,14 @@ async function performSelfUpdate(opts) {
63278
63289
  throw new Error(`self-update: SHA256 mismatch for ${assetName} (expected ${expected}, got ${actual}) \u2014 ` + `refusing to install. The installed CLI is unchanged.`);
63279
63290
  }
63280
63291
  io.chmodExec(tmp);
63281
- const proved = io.probeBinaryVersion(tmp);
63282
- if (!proved) {
63292
+ const proved = io.probeBinary(tmp);
63293
+ if (!proved.ok) {
63283
63294
  io.remove(tmp);
63284
- 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.`);
63295
+ throw new Error(`self-update: ${describeBinaryProbeFailure({
63296
+ probe: proved,
63297
+ path: tmp,
63298
+ subject: `the downloaded ${plan.to} binary`
63299
+ })} Refusing to install it. The installed CLI is unchanged.`);
63285
63300
  }
63286
63301
  const payload = await installAssetPayload({
63287
63302
  tag: plan.to,
@@ -63459,10 +63474,13 @@ function serializeStamp(stamp) {
63459
63474
  `;
63460
63475
  }
63461
63476
  function refreshHostCliStamp(input, io = {}) {
63477
+ const stamp = buildHostCliStamp(input, io);
63478
+ if (!stamp)
63479
+ return { status: "skipped", reason: "running in a container" };
63480
+ return writeHostCliStamp(stamp, io);
63481
+ }
63482
+ function writeHostCliStamp(stamp, io = {}) {
63462
63483
  try {
63463
- const stamp = buildHostCliStamp(input, io);
63464
- if (!stamp)
63465
- return { status: "skipped", reason: "running in a container" };
63466
63484
  const dir = resolveStampDir(io);
63467
63485
  if (!dir)
63468
63486
  return { status: "skipped", reason: "no switchroom home found" };
@@ -64917,6 +64935,7 @@ async function defaultLoadDecisions() {
64917
64935
  "exec",
64918
64936
  "switchroom-vault-broker",
64919
64937
  "sqlite3",
64938
+ "-readonly",
64920
64939
  "-separator",
64921
64940
  SEP2,
64922
64941
  GRANTS_DB_CONTAINER_PATH,
@@ -84374,7 +84393,8 @@ function detectGatewayFindings(agent, logText, logName = `logs/${agent}/gateway-
84374
84393
  const gw_hits = {
84375
84394
  "duplicate-delivery-represent": 0,
84376
84395
  "represent-escalation": 0,
84377
- "reply-delivery-failure": 0
84396
+ "reply-delivery-failure": 0,
84397
+ "orphaned-db-handle": 0
84378
84398
  };
84379
84399
  const lines = logText.split(`
84380
84400
  `);
@@ -84432,7 +84452,8 @@ var init_detect = __esm(() => {
84432
84452
  GATEWAY_SIGNATURES = {
84433
84453
  "duplicate-delivery-represent": /represent duplicate-send/,
84434
84454
  "represent-escalation": /obligation escalation/,
84435
- "reply-delivery-failure": /tg-post method=sendRichMessage[^\n]*status=err(?![a-z])/
84455
+ "reply-delivery-failure": /tg-post method=sendRichMessage[^\n]*status=err(?![a-z])/,
84456
+ "orphaned-db-handle": /orphaned-db-sweep DETECTED \d+ deleted-inode DB handle/
84436
84457
  };
84437
84458
  });
84438
84459
 
@@ -84496,6 +84517,12 @@ var init_mapping = __esm(() => {
84496
84517
  job_spec: "talk-to-agents-from-anywhere",
84497
84518
  signature: "reply-delivery-failure:sendRichMessage-err"
84498
84519
  },
84520
+ "orphaned-db-handle": {
84521
+ failure_mode: "success-theater",
84522
+ severity: 3,
84523
+ job_spec: "survive-reboots-and-real-life",
84524
+ signature: "orphaned-db-handle:deleted-inode-writes"
84525
+ },
84499
84526
  "hang-long-stalled": {
84500
84527
  failure_mode: "partial",
84501
84528
  severity: 2,
@@ -84740,10 +84767,10 @@ var init_test_agents = __esm(() => {
84740
84767
  });
84741
84768
 
84742
84769
  // src/litellm/header-passthrough-guard.ts
84743
- import { existsSync as existsSync124, readdirSync as readdirSync47 } from "node:fs";
84770
+ import { existsSync as existsSync124, readdirSync as readdirSync48 } from "node:fs";
84744
84771
  function discoverLiveLitellmConfigPath(opts) {
84745
84772
  const servicesDir = opts?.servicesDir ?? COOLIFY_SERVICES_DIR;
84746
- const readdir2 = opts?.readdirFn ?? ((p) => readdirSync47(p));
84773
+ const readdir2 = opts?.readdirFn ?? ((p) => readdirSync48(p));
84747
84774
  const exists = opts?.existsFn ?? existsSync124;
84748
84775
  let entries;
84749
84776
  try {
@@ -84964,7 +84991,7 @@ var init_passthrough_mount_guard = __esm(() => {
84964
84991
  // src/fleet-health/litellm-config-sensor.ts
84965
84992
  import { execFileSync as execFileSync35 } from "node:child_process";
84966
84993
  import { readFileSync as readFileSync111, existsSync as existsSync125 } from "node:fs";
84967
- import { dirname as dirname51, join as join126 } from "node:path";
84994
+ import { dirname as dirname52, join as join127 } from "node:path";
84968
84995
  function resolveLiveLitellmPythonVersion() {
84969
84996
  try {
84970
84997
  const ps = execFileSync35("docker", ["ps", "--format", "{{.Names}}\t{{.Image}}"], {
@@ -85048,7 +85075,7 @@ function scanLitellmConfig(opts = {}) {
85048
85075
  ts: nowIso
85049
85076
  });
85050
85077
  }
85051
- const composePath = join126(dirname51(path10), LIVE_COMPOSE_BASENAME);
85078
+ const composePath = join127(dirname52(path10), LIVE_COMPOSE_BASENAME);
85052
85079
  let composeText = null;
85053
85080
  if (exists(composePath)) {
85054
85081
  try {
@@ -85152,12 +85179,12 @@ __export(exports_scan, {
85152
85179
  });
85153
85180
  import {
85154
85181
  readFileSync as readFileSync112,
85155
- readdirSync as readdirSync48,
85182
+ readdirSync as readdirSync49,
85156
85183
  existsSync as existsSync126,
85157
85184
  mkdirSync as mkdirSync71,
85158
85185
  writeFileSync as writeFileSync53
85159
85186
  } from "node:fs";
85160
- import { resolve as resolve64, dirname as dirname52 } from "node:path";
85187
+ import { resolve as resolve64, dirname as dirname53 } from "node:path";
85161
85188
  import { homedir as homedir68 } from "node:os";
85162
85189
  function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir68()) {
85163
85190
  return resolve64(home2, ".switchroom");
@@ -85167,7 +85194,7 @@ function listAgents(base) {
85167
85194
  if (!existsSync126(dir))
85168
85195
  return [];
85169
85196
  try {
85170
- return readdirSync48(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
85197
+ return readdirSync49(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
85171
85198
  } catch {
85172
85199
  return [];
85173
85200
  }
@@ -85254,11 +85281,11 @@ function readLedgerIfPresent(base) {
85254
85281
  }
85255
85282
  }
85256
85283
  function ledgerPathForBase(base) {
85257
- return fleetHealthLedgerPath(dirname52(base));
85284
+ return fleetHealthLedgerPath(dirname53(base));
85258
85285
  }
85259
85286
  function writeLedger(base, ledger) {
85260
85287
  const path10 = ledgerPathForBase(base);
85261
- mkdirSync71(dirname52(path10), { recursive: true });
85288
+ mkdirSync71(dirname53(path10), { recursive: true });
85262
85289
  writeFileSync53(path10, JSON.stringify(ledger, null, 2) + `
85263
85290
  `, "utf-8");
85264
85291
  return path10;
@@ -86285,6 +86312,7 @@ function readLastMessages(historyDbPath) {
86285
86312
  } catch {}
86286
86313
  try {
86287
86314
  const out = execFileSync11("sqlite3", [
86315
+ "-readonly",
86288
86316
  historyDbPath,
86289
86317
  "SELECT role, MAX(ts) FROM messages GROUP BY role;"
86290
86318
  ], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
@@ -92341,7 +92369,7 @@ function scrubSqlite(path2, values, dryRun) {
92341
92369
  for (const { value } of values) {
92342
92370
  const esc = value.replace(/'/g, "''");
92343
92371
  try {
92344
- const out = execFileSync17("sqlite3", [path2, `SELECT COUNT(*) FROM messages WHERE text LIKE '%${esc}%'`], { encoding: "utf8", timeout: 1e4 });
92372
+ const out = execFileSync17("sqlite3", ["-readonly", path2, `SELECT COUNT(*) FROM messages WHERE text LIKE '%${esc}%'`], { encoding: "utf8", timeout: 1e4 });
92345
92373
  const n = parseInt(out.trim(), 10);
92346
92374
  if (!Number.isNaN(n))
92347
92375
  matches += n;
@@ -106606,7 +106634,7 @@ function readTopicsFromHistory(dbPath, chatId, limit) {
106606
106634
  fail2("`switchroom telegram topics` requires the Bun runtime (history DB uses bun:sqlite). " + "Run via `bun run dev` or via the installed `switchroom` CLI (bun-bundled).");
106607
106635
  }
106608
106636
  const { Database: Database2 } = metaRequire("bun:sqlite");
106609
- const db = new Database2(dbPath, { create: false });
106637
+ const db = new Database2(dbPath, { create: false, readonly: true });
106610
106638
  try {
106611
106639
  const aggregateRows = db.prepare(`
106612
106640
  SELECT
@@ -115303,6 +115331,11 @@ import { Readable } from "node:stream";
115303
115331
  import { pipeline } from "node:stream/promises";
115304
115332
  import { spawnSync as spawnSync18 } from "node:child_process";
115305
115333
  var HTTP_TIMEOUT_MS = 60000;
115334
+ function firstLine2(s) {
115335
+ const line = `${s ?? ""}`.split(`
115336
+ `).map((l) => l.trim()).find((l) => l.length > 0) ?? "";
115337
+ return line.length > 200 ? `${line.slice(0, 197)}...` : line;
115338
+ }
115306
115339
  function defaultSelfUpdateIO() {
115307
115340
  return {
115308
115341
  async httpGetText(url) {
@@ -115342,17 +115375,40 @@ function defaultSelfUpdateIO() {
115342
115375
  }
115343
115376
  return hash2.digest("hex");
115344
115377
  },
115345
- probeBinaryVersion(path7) {
115346
- const r = spawnSync18(path7, ["version"], {
115378
+ probeBinary(path7) {
115379
+ const r = spawnSync18(path7, ["--version"], {
115347
115380
  encoding: "utf-8",
115348
115381
  timeout: 30000,
115349
115382
  env: { ...process.env, SWITCHROOM_SELF_UPDATED: "" }
115350
115383
  });
115351
- if (r.status !== 0)
115352
- return null;
115384
+ if (r.error) {
115385
+ const code = r.error.code;
115386
+ return {
115387
+ ok: false,
115388
+ kind: "not-executable",
115389
+ detail: code ? `${code}: ${r.error.message}` : r.error.message
115390
+ };
115391
+ }
115392
+ if (r.signal) {
115393
+ return { ok: false, kind: "not-executable", detail: `killed by signal ${r.signal}` };
115394
+ }
115395
+ if (r.status !== 0) {
115396
+ return {
115397
+ ok: false,
115398
+ kind: "ran-but-failed",
115399
+ detail: `exit ${r.status}${firstLine2(r.stderr) ? `: ${firstLine2(r.stderr)}` : ""}`
115400
+ };
115401
+ }
115353
115402
  const out = `${r.stdout ?? ""}`.trim();
115354
115403
  const m = out.match(/\d+\.\d+\.\d+/);
115355
- return m ? m[0] : null;
115404
+ if (!m) {
115405
+ return {
115406
+ ok: false,
115407
+ kind: "no-version",
115408
+ detail: out ? `printed ${JSON.stringify(firstLine2(out))}` : "printed nothing"
115409
+ };
115410
+ }
115411
+ return { ok: true, version: m[0] };
115356
115412
  },
115357
115413
  mkdirp(dir) {
115358
115414
  mkdirSync49(dir, { recursive: true });
@@ -116419,7 +116475,7 @@ function registerUpdateCommand(program3) {
116419
116475
  // src/cli/rollout.ts
116420
116476
  init_helpers();
116421
116477
  import { spawnSync as spawnSync22 } from "node:child_process";
116422
- import { readFileSync as readFileSync85, chownSync as chownSync12, statSync as statSync53 } from "node:fs";
116478
+ import { readFileSync as readFileSync85, chownSync as chownSync12, statSync as statSync54 } from "node:fs";
116423
116479
  import { homedir as homedir51 } from "node:os";
116424
116480
 
116425
116481
  // src/cli/rollout-pin-journal.ts
@@ -116633,6 +116689,363 @@ function recoverPinJournal(configPath, opts = {}) {
116633
116689
  // src/cli/rollout.ts
116634
116690
  init_operator_uid();
116635
116691
  init_host_cli_stamp();
116692
+
116693
+ // src/cli/host-cli-upgrade.ts
116694
+ init_self_update();
116695
+ import { lchownSync, lstatSync as lstatSync14, readdirSync as readdirSync32, statSync as statSync53 } from "node:fs";
116696
+ import { basename as basename10, dirname as dirname42, join as join96 } from "node:path";
116697
+ init_shipped_assets();
116698
+ var HOST_CLI_UPGRADE_SENTINEL = "SWITCHROOM_HOST_CLI_UPGRADE:";
116699
+ function encodeHostCliUpgradeResult(r) {
116700
+ return `${HOST_CLI_UPGRADE_SENTINEL}${JSON.stringify(r)}`;
116701
+ }
116702
+ function parseHostCliUpgradeResult(logs) {
116703
+ const lines = logs.split(`
116704
+ `);
116705
+ for (let i2 = lines.length - 1;i2 >= 0; i2--) {
116706
+ const line = lines[i2]?.trim() ?? "";
116707
+ if (!line.startsWith(HOST_CLI_UPGRADE_SENTINEL))
116708
+ continue;
116709
+ try {
116710
+ const o = JSON.parse(line.slice(HOST_CLI_UPGRADE_SENTINEL.length));
116711
+ if (typeof o !== "object" || o === null)
116712
+ return null;
116713
+ if (typeof o.ok !== "boolean")
116714
+ return null;
116715
+ return {
116716
+ ok: o.ok,
116717
+ ...typeof o.version === "string" ? { version: o.version } : {},
116718
+ ...typeof o.binaryPath === "string" ? { binaryPath: o.binaryPath } : {},
116719
+ ...typeof o.error === "string" ? { error: o.error } : {}
116720
+ };
116721
+ } catch {
116722
+ return null;
116723
+ }
116724
+ }
116725
+ return null;
116726
+ }
116727
+ var SEMVER_TAG_RE2 = /^v\d+\.\d+\.\d+$/;
116728
+ function defaultIo() {
116729
+ return {
116730
+ isFile: (p) => {
116731
+ try {
116732
+ return statSync53(p).isFile();
116733
+ } catch {
116734
+ return false;
116735
+ }
116736
+ },
116737
+ kind: (p) => {
116738
+ try {
116739
+ const st = lstatSync14(p);
116740
+ if (st.isSymbolicLink())
116741
+ return "symlink";
116742
+ if (st.isDirectory())
116743
+ return "dir";
116744
+ if (st.isFile())
116745
+ return "file";
116746
+ return "other";
116747
+ } catch {
116748
+ return;
116749
+ }
116750
+ },
116751
+ owner: (p) => {
116752
+ try {
116753
+ const st = lstatSync14(p);
116754
+ return { uid: st.uid, gid: st.gid };
116755
+ } catch {
116756
+ return;
116757
+ }
116758
+ },
116759
+ chown: (p, uid, gid) => lchownSync(p, uid, gid),
116760
+ list: (d) => {
116761
+ try {
116762
+ return readdirSync32(d);
116763
+ } catch {
116764
+ return [];
116765
+ }
116766
+ },
116767
+ selfUpdate: defaultSelfUpdateIO(),
116768
+ platform: process.platform,
116769
+ arch: process.arch,
116770
+ getuid: () => process.getuid?.()
116771
+ };
116772
+ }
116773
+ function handBackOwnership(paths, owner, io) {
116774
+ const failures = [];
116775
+ const seen = new Set;
116776
+ const stack = [...paths];
116777
+ while (stack.length > 0) {
116778
+ const p = stack.pop();
116779
+ if (p === undefined || seen.has(p))
116780
+ continue;
116781
+ seen.add(p);
116782
+ const kind = io.kind(p);
116783
+ if (kind === undefined)
116784
+ continue;
116785
+ try {
116786
+ io.chown(p, owner.uid, owner.gid);
116787
+ } catch (err) {
116788
+ failures.push(`${p}: ${err.message}`);
116789
+ }
116790
+ if (kind !== "dir")
116791
+ continue;
116792
+ for (const child of io.list(p))
116793
+ stack.push(join96(p, child));
116794
+ }
116795
+ return failures;
116796
+ }
116797
+ async function runHostCliUpgrade(opts, io = defaultIo(), log = () => {}) {
116798
+ const { binary, pin, from } = opts;
116799
+ if (!SEMVER_TAG_RE2.test(pin)) {
116800
+ return { ok: false, error: `--pin must be a vX.Y.Z release tag, got "${pin}"` };
116801
+ }
116802
+ if (!binary.startsWith("/")) {
116803
+ return { ok: false, error: `--binary must be an absolute path, got "${binary}"` };
116804
+ }
116805
+ if (basename10(binary) !== "switchroom") {
116806
+ return {
116807
+ ok: false,
116808
+ error: `--binary must name a file called \`switchroom\`, got ` + `"${basename10(binary)}" \u2014 refusing to overwrite an unrelated file.`
116809
+ };
116810
+ }
116811
+ if (!io.isFile(binary)) {
116812
+ return {
116813
+ ok: false,
116814
+ error: `${binary} is not an existing regular file \u2014 the host bindir is either ` + `not mounted or the recorded host CLI path is stale.`
116815
+ };
116816
+ }
116817
+ const asset = releaseAssetName(io.platform, io.arch);
116818
+ if (!asset) {
116819
+ return {
116820
+ ok: false,
116821
+ error: `no published binary for ${io.platform}/${io.arch}`
116822
+ };
116823
+ }
116824
+ const priorOwner = io.owner(binary);
116825
+ const handBack = () => {
116826
+ try {
116827
+ if (!priorOwner || priorOwner.uid === io.getuid())
116828
+ return;
116829
+ const installRoot = payloadInstallRoot(binary);
116830
+ const failures = handBackOwnership([
116831
+ binary,
116832
+ join96(dirname42(binary), ".switchroom-versions"),
116833
+ dirname42(installRoot),
116834
+ installRoot,
116835
+ payloadVersionDir(installRoot, pin)
116836
+ ], priorOwner, io);
116837
+ if (failures.length > 0) {
116838
+ log(`warning: could not hand ownership back to uid ${priorOwner.uid}: ` + `${failures.join("; ")}
116839
+ `);
116840
+ }
116841
+ } catch (err) {
116842
+ log(`warning: ownership handoff failed: ${err.message}
116843
+ `);
116844
+ }
116845
+ };
116846
+ try {
116847
+ let result;
116848
+ try {
116849
+ result = await performSelfUpdate({
116850
+ plan: {
116851
+ action: "update",
116852
+ from: `v${from.replace(/^v/, "")}`,
116853
+ to: pin,
116854
+ binaryPath: binary
116855
+ },
116856
+ assetName: asset,
116857
+ io: io.selfUpdate,
116858
+ log
116859
+ });
116860
+ } catch (err) {
116861
+ return { ok: false, error: err.message };
116862
+ }
116863
+ const proven = io.selfUpdate.probeBinary(binary);
116864
+ if (!proven.ok) {
116865
+ return {
116866
+ ok: false,
116867
+ error: `swapped ${binary} but ${describeBinaryProbeFailure({
116868
+ probe: proven,
116869
+ path: binary,
116870
+ subject: "the installed binary"
116871
+ })} ${result.message}`
116872
+ };
116873
+ }
116874
+ if (`v${proven.version.replace(/^v/, "")}` !== pin) {
116875
+ return {
116876
+ ok: false,
116877
+ error: `swapped ${binary} but it reports ${proven.version}, not ${pin} \u2014 ` + `the install did not land. ${result.message}`
116878
+ };
116879
+ }
116880
+ return { ok: true, version: pin, binaryPath: binary };
116881
+ } finally {
116882
+ handBack();
116883
+ }
116884
+ }
116885
+ function registerHostCliUpgradeCommand(program3) {
116886
+ program3.command("host-cli-upgrade").description("INTERNAL (#4585): replace a bind-mounted HOST switchroom binary with a " + "published release, checksum-verified. Spawned by rollout inside a " + "short-lived helper container; not for interactive use.").requiredOption("--binary <path>", "Path of the host binary to replace").requiredOption("--pin <version>", "Target release tag, vX.Y.Z").requiredOption("--from <version>", "Version currently installed").action(async (opts) => {
116887
+ const result = await runHostCliUpgrade(opts, defaultIo(), (s) => process.stderr.write(s.endsWith(`
116888
+ `) ? s : `${s}
116889
+ `));
116890
+ process.stdout.write(`${encodeHostCliUpgradeResult(result)}
116891
+ `);
116892
+ if (!result.ok) {
116893
+ process.stderr.write(`host-cli-upgrade failed: ${result.error}
116894
+ `);
116895
+ process.exitCode = 1;
116896
+ }
116897
+ });
116898
+ }
116899
+
116900
+ // src/cli/host-cli-heal.ts
116901
+ var HEAL_PREFIX_MOUNT = "/hostcli";
116902
+ var HEAL_HELPER_CONTAINER = "switchroom-hostcli-heal";
116903
+ var HOSTD_CONTAINER_NAME = "switchroom-hostd";
116904
+ var HOST_CLI_HEAL_TIMEOUT_MS = (() => {
116905
+ const raw = process.env.SWITCHROOM_HOST_CLI_HEAL_TIMEOUT_MS;
116906
+ const n = raw ? Number(raw) : NaN;
116907
+ return Number.isFinite(n) && n > 0 ? n : 10 * 60 * 1000;
116908
+ })();
116909
+ function planHostCliHeal(opts) {
116910
+ const { stamp, target, hostdCtx } = opts;
116911
+ if (!hostdCtx) {
116912
+ return {
116913
+ action: "skip",
116914
+ reason: "not running in hostd context \u2014 on the host shell the operator can run " + "the install command directly"
116915
+ };
116916
+ }
116917
+ if (!stamp)
116918
+ return { action: "skip", reason: "no host-cli.json stamp to act on" };
116919
+ if (!/^v\d+\.\d+\.\d+$/.test(target)) {
116920
+ return { action: "skip", reason: `target "${target}" is not a vX.Y.Z release tag` };
116921
+ }
116922
+ if (stamp.installKind !== "static-binary") {
116923
+ return {
116924
+ action: "skip",
116925
+ reason: `the host CLI is a ${stamp.installKind} install \u2014 only a static-binary ` + `install can be replaced by swapping one file, so an operator must run ` + `the upgrade host-side`
116926
+ };
116927
+ }
116928
+ const path7 = stamp.path;
116929
+ if (!path7.startsWith("/") || path7.includes("/..")) {
116930
+ return { action: "skip", reason: `recorded host CLI path "${path7}" is not a plain absolute path` };
116931
+ }
116932
+ const installDir = path7.slice(0, path7.lastIndexOf("/"));
116933
+ const name = path7.slice(path7.lastIndexOf("/") + 1);
116934
+ if (name !== "switchroom") {
116935
+ return { action: "skip", reason: `recorded host CLI path "${path7}" is not named \`switchroom\`` };
116936
+ }
116937
+ const prefixHostPath = installDir.slice(0, installDir.lastIndexOf("/"));
116938
+ const dirName = installDir.slice(installDir.lastIndexOf("/") + 1);
116939
+ if (prefixHostPath.length === 0 || dirName.length === 0) {
116940
+ return {
116941
+ action: "skip",
116942
+ reason: `recorded host CLI path "${path7}" has no install prefix to bind \u2014 ` + `refusing to mount the host root`
116943
+ };
116944
+ }
116945
+ return {
116946
+ action: "heal",
116947
+ prefixHostPath,
116948
+ containerBinaryPath: `${HEAL_PREFIX_MOUNT}/${dirName}/switchroom`,
116949
+ from: stamp.version,
116950
+ target
116951
+ };
116952
+ }
116953
+ function healHelperArgs(opts) {
116954
+ const { plan, helperImage } = opts;
116955
+ return [
116956
+ "run",
116957
+ "-d",
116958
+ "--name",
116959
+ opts.containerName ?? HEAL_HELPER_CONTAINER,
116960
+ "--label",
116961
+ "switchroom.hostcli-heal=true",
116962
+ "-v",
116963
+ `${plan.prefixHostPath}:${HEAL_PREFIX_MOUNT}:rw`,
116964
+ helperImage,
116965
+ "switchroom",
116966
+ "host-cli-upgrade",
116967
+ "--binary",
116968
+ plan.containerBinaryPath,
116969
+ "--pin",
116970
+ plan.target,
116971
+ "--from",
116972
+ plan.from
116973
+ ];
116974
+ }
116975
+ function resolveHelperImage(docker) {
116976
+ const r = docker(["inspect", "--format", "{{.Config.Image}}", HOSTD_CONTAINER_NAME]);
116977
+ if (!r.ok)
116978
+ return null;
116979
+ const ref = r.stdout.trim().split(`
116980
+ `)[0]?.trim() ?? "";
116981
+ return ref.length > 0 ? ref : null;
116982
+ }
116983
+ function runHostCliHeal(opts) {
116984
+ const { plan, docker } = opts;
116985
+ const name = opts.containerName ?? HEAL_HELPER_CONTAINER;
116986
+ const log = opts.log ?? (() => {});
116987
+ const image = resolveHelperImage(docker);
116988
+ if (!image) {
116989
+ return {
116990
+ ok: false,
116991
+ message: `could not resolve the image of the running \`${HOSTD_CONTAINER_NAME}\` ` + `container, so there is no image to run the upgrade helper from`
116992
+ };
116993
+ }
116994
+ docker(["rm", "-f", name]);
116995
+ log(`\u21bb host CLI ${plan.from} \u2192 ${plan.target}: running the upgrade helper ` + `(${image}, binding ${plan.prefixHostPath})
116996
+ `);
116997
+ const started = docker(healHelperArgs({ plan, helperImage: image, containerName: name }));
116998
+ if (!started.ok) {
116999
+ return {
117000
+ ok: false,
117001
+ message: `could not start the upgrade helper: ${oneLine(started.stderr) || "docker run failed"}`
117002
+ };
117003
+ }
117004
+ try {
117005
+ return waitForHelper(docker, name, plan);
117006
+ } catch (err) {
117007
+ return { ok: false, message: `the upgrade helper could not be waited on: ${err.message}` };
117008
+ } finally {
117009
+ try {
117010
+ docker(["rm", "-f", name]);
117011
+ } catch {}
117012
+ }
117013
+ }
117014
+ function waitForHelper(docker, name, plan) {
117015
+ const waited = docker(["wait", name]);
117016
+ const logs = docker(["logs", name]);
117017
+ const blob = `${logs.stdout}
117018
+ ${logs.stderr}`;
117019
+ const parsed = parseHostCliUpgradeResult(blob);
117020
+ if (!waited.ok) {
117021
+ return {
117022
+ ok: false,
117023
+ message: `waiting on the upgrade helper failed (${oneLine(waited.stderr) || "docker wait failed"})` + (parsed?.error ? `; helper reported: ${parsed.error}` : "")
117024
+ };
117025
+ }
117026
+ const code = waited.stdout.trim().split(`
117027
+ `).pop()?.trim() ?? "";
117028
+ if (code !== "0" || !parsed || !parsed.ok) {
117029
+ return {
117030
+ ok: false,
117031
+ message: `the upgrade helper exited ${code || "?"}: ` + (parsed?.error ?? (oneLine(blob) || "no diagnostic on stdout/stderr"))
117032
+ };
117033
+ }
117034
+ if (!parsed.version) {
117035
+ return { ok: false, message: "the upgrade helper reported success without a version" };
117036
+ }
117037
+ return {
117038
+ ok: true,
117039
+ version: parsed.version,
117040
+ message: `host CLI upgraded ${plan.from} \u2192 ${parsed.version} at ${plan.containerBinaryPath.replace(HEAL_PREFIX_MOUNT, plan.prefixHostPath)}`
117041
+ };
117042
+ }
117043
+ function oneLine(s) {
117044
+ const flat = s.replace(/\s+/g, " ").trim();
117045
+ return flat.length > 400 ? `${flat.slice(0, 397)}...` : flat;
117046
+ }
117047
+
117048
+ // src/cli/rollout.ts
116636
117049
  init_self_invoke();
116637
117050
  init_atomic();
116638
117051
 
@@ -117281,7 +117694,7 @@ function createRolloutDeps(params) {
117281
117694
  if (after === before)
117282
117695
  return false;
117283
117696
  beginPinPersist(configPath, pin);
117284
- writeConfigPreservingOwnership(configPath, after, statSync53(configPath).mode & 511);
117697
+ writeConfigPreservingOwnership(configPath, after, statSync54(configPath).mode & 511);
117285
117698
  return true;
117286
117699
  },
117287
117700
  commitPin: () => {
@@ -117309,8 +117722,21 @@ function createRolloutDeps(params) {
117309
117722
  } : {}
117310
117723
  };
117311
117724
  }
117312
- function registerRolloutCommand(program3) {
117725
+ function registerRolloutCommand(program3, testHooks) {
117313
117726
  const hostdCtx = isHostdContext2();
117727
+ const hostCliHealDocker = testHooks?.hostCliHealDocker;
117728
+ const defaultHostCliHealDocker = (args) => {
117729
+ try {
117730
+ const r = spawnSync22("docker", args, {
117731
+ encoding: "utf8",
117732
+ timeout: HOST_CLI_HEAL_TIMEOUT_MS,
117733
+ killSignal: ROLLOUT_KILL_SIGNAL
117734
+ });
117735
+ return { ok: r.status === 0, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
117736
+ } catch (err) {
117737
+ return { ok: false, stdout: "", stderr: err.message };
117738
+ }
117739
+ };
117314
117740
  program3.command("rollout").description("Deploy a pinned version to the fleet, safely (staggered restart + " + "per-agent version assert + web/hostd refresh)." + (hostdCtx ? "" : " Run with sudo.")).option("--pin <version>", "Version to roll (e.g. v0.15.18). Defaults to release.pin from config.").option("--agents <list>", "Comma-separated subset of agents to roll (default: all configured).").option("--skip-web", "Skip the web refresh (host-shell path: also the hostd + hindsight " + "refresh). On the hostd/agent path only the web refresh is skipped \u2014 " + "hindsight is still recreated and hostd stays deferred regardless.").option("--allow-downgrade", "Permit rolling to an older semver tag (operator-approved rollback path). " + "When set, the downgrade guard is relaxed so --pin may be older than the " + "current release.pin. All other safety rails apply unchanged.").option("--allow-stale-host-cli", "Roll the fleet even though the HOST operator CLI is observably behind " + "the target. Escape hatch for the host-CLI-first gate: the host CLI is " + "then left drifted and every later host-shell command runs old code. " + "HOST-SHELL ONLY: deliberately not exposed as an `mcp__hostd__rollout` " + "parameter, so an agent cannot roll past the gate on its own \u2014 " + "overriding requires the same host shell needed to actually fix it.").option("--dry-run", "Print the plan and exit without changing anything.").action(async (opts) => {
117315
117741
  if (!opts.dryRun) {
117316
117742
  const note = recoverPinJournal(getConfigPath(program3));
@@ -117360,29 +117786,6 @@ function registerRolloutCommand(program3) {
117360
117786
  process.exitCode = 2;
117361
117787
  return;
117362
117788
  }
117363
- const hostCliStamp = readHostCliStamp();
117364
- if (!opts.allowStaleHostCli && shouldRefuseStaleHostCli(hostCliStamp, target)) {
117365
- const observed = hostCliStamp?.version ?? "unknown";
117366
- const refusal = `rollout refused: the HOST operator CLI is v${observed}, OLDER than ` + `the target ${target}. The host CLI must be upgraded FIRST \u2014 it is ` + `what runs \`switchroom apply\`, \`vault\`, \`doctor\` and the next ` + `roll host-side, and leaving it behind is how it silently drifted ` + `five releases. Run this on the host, then re-run the roll: ` + `${hostCliInstallCommand(hostCliStamp, target)}. ` + `(Observed from ${HOST_CLI_STAMP_FILENAME}, refreshed by every ` + `host-context switchroom command \u2014 if you already upgraded, run any ` + `switchroom command on the host to refresh it, or pass ` + `--allow-stale-host-cli to roll anyway.) Nothing was changed.`;
117367
- process.stderr.write(refusal + `
117368
- `);
117369
- if (hostdCtx) {
117370
- process.stdout.write(encodeRolloutResultLine({
117371
- ok: false,
117372
- rolled: [],
117373
- failedStep: PREFLIGHT_HOST_CLI_STALE_STEP,
117374
- got: observed,
117375
- warnings: [refusal]
117376
- }) + `
117377
- `);
117378
- }
117379
- process.exitCode = 2;
117380
- return;
117381
- }
117382
- if (opts.allowStaleHostCli && shouldRefuseStaleHostCli(hostCliStamp, target)) {
117383
- process.stderr.write(`\u26a0\ufe0f --allow-stale-host-cli: rolling past a HOST operator CLI on ` + `v${hostCliStamp?.version} (target ${target}). Fix it host-side ` + `with: ${hostCliInstallCommand(hostCliStamp, target)}
117384
- `);
117385
- }
117386
117789
  if (shouldRefuseDowngrade(hostdCtx, resolvedPin, config.release?.pin, opts.allowDowngrade)) {
117387
117790
  const current = config.release?.pin;
117388
117791
  process.stderr.write(`rollout (hostd path) refuses a DOWNGRADE: ${resolvedPin} is older ` + `than the current pin ${current}. Pass --allow-downgrade to ` + `authorize an operator-approved rollback to a known-good earlier ` + `tag. Nothing was changed.
@@ -117410,6 +117813,63 @@ function registerRolloutCommand(program3) {
117410
117813
  process.exitCode = 2;
117411
117814
  return;
117412
117815
  }
117816
+ let hostCliStamp = readHostCliStamp();
117817
+ let healNote;
117818
+ let healDeferredByDryRun = false;
117819
+ if (!opts.allowStaleHostCli && shouldRefuseStaleHostCli(hostCliStamp, target)) {
117820
+ const plan = planHostCliHeal({ stamp: hostCliStamp, target, hostdCtx });
117821
+ if (plan.action === "skip") {
117822
+ healNote = `host CLI self-heal not attempted: ${plan.reason}.`;
117823
+ } else if (opts.dryRun) {
117824
+ healDeferredByDryRun = true;
117825
+ healNote = `would upgrade the HOST CLI ${plan.from} \u2192 ${plan.target} first, in a ` + `short-lived helper container binding ${plan.prefixHostPath}`;
117826
+ process.stdout.write(`\u21bb dry-run: ${healNote}
117827
+ `);
117828
+ } else {
117829
+ const outcome = runHostCliHeal({
117830
+ plan,
117831
+ docker: hostCliHealDocker ?? defaultHostCliHealDocker,
117832
+ log: (line) => process.stderr.write(line)
117833
+ });
117834
+ if (outcome.ok && outcome.version) {
117835
+ const updated = {
117836
+ ...hostCliStamp,
117837
+ version: outcome.version.replace(/^v/, "")
117838
+ };
117839
+ const w = writeHostCliStamp(updated, testHooks?.hostCliStampIo ?? {});
117840
+ hostCliStamp = updated;
117841
+ healNote = `${outcome.message}` + (w.status === "skipped" ? ` (proceeding on the PROVEN version; ${HOST_CLI_STAMP_FILENAME} ` + `could not be refreshed: ${w.reason} \u2014 the next roll will ` + `re-observe the old version and heal again)` : "");
117842
+ process.stderr.write(`\u2705 ${healNote}
117843
+ `);
117844
+ } else {
117845
+ healNote = `host CLI self-heal FAILED: ${outcome.message}.`;
117846
+ process.stderr.write(`\u26a0\ufe0f ${healNote}
117847
+ `);
117848
+ }
117849
+ }
117850
+ }
117851
+ if (!opts.allowStaleHostCli && !healDeferredByDryRun && shouldRefuseStaleHostCli(hostCliStamp, target)) {
117852
+ const observed = hostCliStamp?.version ?? "unknown";
117853
+ const refusal = `rollout refused: the HOST operator CLI is v${observed}, OLDER than ` + `the target ${target}. The host CLI must be upgraded FIRST \u2014 it is ` + `what runs \`switchroom apply\`, \`vault\`, \`doctor\` and the next ` + `roll host-side, and leaving it behind is how it silently drifted ` + `five releases. ` + (healNote ? `${healNote} ` : "") + `An operator must run this on the host, then re-run the roll: ` + `${hostCliInstallCommand(hostCliStamp, target)}. ` + `(Observed from ${HOST_CLI_STAMP_FILENAME}, refreshed by every ` + `host-context switchroom command \u2014 if you already upgraded, run any ` + `switchroom command on the host to refresh it, or pass ` + `--allow-stale-host-cli to roll anyway.) Nothing was changed.`;
117854
+ process.stderr.write(refusal + `
117855
+ `);
117856
+ if (hostdCtx) {
117857
+ process.stdout.write(encodeRolloutResultLine({
117858
+ ok: false,
117859
+ rolled: [],
117860
+ failedStep: PREFLIGHT_HOST_CLI_STALE_STEP,
117861
+ got: observed,
117862
+ warnings: [refusal]
117863
+ }) + `
117864
+ `);
117865
+ }
117866
+ process.exitCode = 2;
117867
+ return;
117868
+ }
117869
+ if (opts.allowStaleHostCli && shouldRefuseStaleHostCli(hostCliStamp, target)) {
117870
+ process.stderr.write(`\u26a0\ufe0f --allow-stale-host-cli: rolling past a HOST operator CLI on ` + `v${hostCliStamp?.version} (target ${target}). Fix it host-side ` + `with: ${hostCliInstallCommand(hostCliStamp, target)}
117871
+ `);
117872
+ }
117413
117873
  const steps = planRollout(requested, {
117414
117874
  skipWeb: opts.skipWeb,
117415
117875
  pinToPersist: resolvedPin ?? undefined,
@@ -117429,7 +117889,7 @@ function registerRolloutCommand(program3) {
117429
117889
  hindsightApiUrl = rawUrl ? rawUrl.replace(/\/mcp\/?$/, "").replace(/\/$/, "") : HINDSIGHT_DEFAULT_API_BASE_URL;
117430
117890
  resolveBankId = (agent) => config.agents?.[agent]?.memory?.collection ?? agent;
117431
117891
  }
117432
- const deps = createRolloutDeps({
117892
+ const deps = testHooks?.rolloutDeps ?? createRolloutDeps({
117433
117893
  configPath,
117434
117894
  scriptPath,
117435
117895
  hostdCtx,
@@ -117519,7 +117979,7 @@ init_lifecycle();
117519
117979
  init_resolve_version();
117520
117980
  import { execSync as execSync3 } from "node:child_process";
117521
117981
  import { existsSync as existsSync93, readFileSync as readFileSync86 } from "node:fs";
117522
- import { dirname as dirname42, join as join96 } from "node:path";
117982
+ import { dirname as dirname43, join as join97 } from "node:path";
117523
117983
  function getClaudeCodeVersion() {
117524
117984
  try {
117525
117985
  const out = execSync3("claude --version 2>/dev/null", {
@@ -117569,16 +118029,16 @@ function formatUptime3(timestamp) {
117569
118029
  function locateSwitchroomInstallDir() {
117570
118030
  let dir = import.meta.dirname;
117571
118031
  for (let i2 = 0;i2 < 10 && dir && dir !== "/"; i2++) {
117572
- const pkgPath = join96(dir, "package.json");
118032
+ const pkgPath = join97(dir, "package.json");
117573
118033
  if (existsSync93(pkgPath)) {
117574
118034
  try {
117575
118035
  const pkg = JSON.parse(readFileSync86(pkgPath, "utf-8"));
117576
- if (pkg.name === "switchroom" && existsSync93(join96(dir, ".git"))) {
118036
+ if (pkg.name === "switchroom" && existsSync93(join97(dir, ".git"))) {
117577
118037
  return dir;
117578
118038
  }
117579
118039
  } catch {}
117580
118040
  }
117581
- dir = dirname42(dir);
118041
+ dir = dirname43(dir);
117582
118042
  }
117583
118043
  return null;
117584
118044
  }
@@ -117755,31 +118215,31 @@ init_merge();
117755
118215
  // src/agents/session-retention.ts
117756
118216
  import {
117757
118217
  existsSync as existsSync94,
117758
- readdirSync as readdirSync32,
117759
- statSync as statSync54,
118218
+ readdirSync as readdirSync33,
118219
+ statSync as statSync55,
117760
118220
  unlinkSync as unlinkSync18
117761
118221
  } from "node:fs";
117762
- import { join as join97 } from "node:path";
118222
+ import { join as join98 } from "node:path";
117763
118223
  var DEFAULT_SESSION_RETENTION_MAX_COUNT = 20;
117764
118224
  var DEFAULT_SESSION_RETENTION_MAX_AGE_DAYS = 30;
117765
118225
  var MIN_KEEP = 2;
117766
118226
  function collectSessionJsonl(claudeConfigDir) {
117767
- const projects = join97(claudeConfigDir, "projects");
118227
+ const projects = join98(claudeConfigDir, "projects");
117768
118228
  if (!existsSync94(projects))
117769
118229
  return [];
117770
118230
  const found = [];
117771
118231
  const walk2 = (dir) => {
117772
118232
  let entries;
117773
118233
  try {
117774
- entries = readdirSync32(dir);
118234
+ entries = readdirSync33(dir);
117775
118235
  } catch {
117776
118236
  return;
117777
118237
  }
117778
118238
  for (const name of entries) {
117779
- const full = join97(dir, name);
118239
+ const full = join98(dir, name);
117780
118240
  let st;
117781
118241
  try {
117782
- st = statSync54(full);
118242
+ st = statSync55(full);
117783
118243
  } catch {
117784
118244
  continue;
117785
118245
  }
@@ -117900,15 +118360,15 @@ import {
117900
118360
  existsSync as existsSync95,
117901
118361
  mkdirSync as mkdirSync53,
117902
118362
  openSync as openSync17,
117903
- readdirSync as readdirSync33,
118363
+ readdirSync as readdirSync34,
117904
118364
  readFileSync as readFileSync87,
117905
118365
  renameSync as renameSync25,
117906
- statSync as statSync55,
118366
+ statSync as statSync56,
117907
118367
  unlinkSync as unlinkSync19,
117908
118368
  writeFileSync as writeFileSync38,
117909
118369
  writeSync as writeSync9
117910
118370
  } from "node:fs";
117911
- import { join as join98 } from "node:path";
118371
+ import { join as join99 } from "node:path";
117912
118372
  import { randomBytes as randomBytes15 } from "node:crypto";
117913
118373
  import { execSync as execSync4 } from "node:child_process";
117914
118374
 
@@ -117935,7 +118395,7 @@ function computeFingerprint(source, code) {
117935
118395
  var ISSUES_FILE = "issues.jsonl";
117936
118396
  var ISSUES_LOCK = "issues.lock";
117937
118397
  function readAll(stateDir) {
117938
- const path7 = join98(stateDir, ISSUES_FILE);
118398
+ const path7 = join99(stateDir, ISSUES_FILE);
117939
118399
  if (!existsSync95(path7))
117940
118400
  return [];
117941
118401
  let raw;
@@ -118013,7 +118473,7 @@ function record(stateDir, input, nowFn = Date.now) {
118013
118473
  });
118014
118474
  }
118015
118475
  function resolve51(stateDir, fingerprint, nowFn = Date.now) {
118016
- if (!existsSync95(join98(stateDir, ISSUES_FILE)))
118476
+ if (!existsSync95(join99(stateDir, ISSUES_FILE)))
118017
118477
  return 0;
118018
118478
  return withLock(stateDir, () => {
118019
118479
  const all = readAll(stateDir);
@@ -118031,7 +118491,7 @@ function resolve51(stateDir, fingerprint, nowFn = Date.now) {
118031
118491
  });
118032
118492
  }
118033
118493
  function resolveAllBySource(stateDir, source, nowFn = Date.now) {
118034
- if (!existsSync95(join98(stateDir, ISSUES_FILE)))
118494
+ if (!existsSync95(join99(stateDir, ISSUES_FILE)))
118035
118495
  return 0;
118036
118496
  return withLock(stateDir, () => {
118037
118497
  const all = readAll(stateDir);
@@ -118049,7 +118509,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
118049
118509
  });
118050
118510
  }
118051
118511
  function prune(stateDir, opts = {}) {
118052
- if (!existsSync95(join98(stateDir, ISSUES_FILE)))
118512
+ if (!existsSync95(join99(stateDir, ISSUES_FILE)))
118053
118513
  return 0;
118054
118514
  return withLock(stateDir, () => {
118055
118515
  const all = readAll(stateDir);
@@ -118082,7 +118542,7 @@ function ensureDir(stateDir) {
118082
118542
  mkdirSync53(stateDir, { recursive: true });
118083
118543
  }
118084
118544
  function writeAll(stateDir, events) {
118085
- const path7 = join98(stateDir, ISSUES_FILE);
118545
+ const path7 = join99(stateDir, ISSUES_FILE);
118086
118546
  sweepOrphanTmpFiles(stateDir);
118087
118547
  const tmp = `${path7}.tmp-${process.pid}-${randomBytes15(4).toString("hex")}`;
118088
118548
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
@@ -118096,7 +118556,7 @@ var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
118096
118556
  function sweepOrphanTmpFiles(stateDir) {
118097
118557
  let entries;
118098
118558
  try {
118099
- entries = readdirSync33(stateDir);
118559
+ entries = readdirSync34(stateDir);
118100
118560
  } catch {
118101
118561
  return;
118102
118562
  }
@@ -118104,9 +118564,9 @@ function sweepOrphanTmpFiles(stateDir) {
118104
118564
  for (const entry of entries) {
118105
118565
  if (!entry.startsWith(TMP_PREFIX))
118106
118566
  continue;
118107
- const tmpPath = join98(stateDir, entry);
118567
+ const tmpPath = join99(stateDir, entry);
118108
118568
  try {
118109
- const stat = statSync55(tmpPath);
118569
+ const stat = statSync56(tmpPath);
118110
118570
  if (stat.mtimeMs < cutoff) {
118111
118571
  unlinkSync19(tmpPath);
118112
118572
  }
@@ -118116,7 +118576,7 @@ function sweepOrphanTmpFiles(stateDir) {
118116
118576
  var LOCK_RETRY_MS = 25;
118117
118577
  var LOCK_TIMEOUT_MS = 1e4;
118118
118578
  function withLock(stateDir, fn) {
118119
- const lockPath = join98(stateDir, ISSUES_LOCK);
118579
+ const lockPath = join99(stateDir, ISSUES_LOCK);
118120
118580
  const startedAt = Date.now();
118121
118581
  let fd = null;
118122
118582
  while (fd === null) {
@@ -118401,7 +118861,7 @@ function relTime(deltaMs) {
118401
118861
  init_source();
118402
118862
  import { existsSync as existsSync98 } from "node:fs";
118403
118863
  import { homedir as homedir54 } from "node:os";
118404
- import { join as join101, resolve as resolve52 } from "node:path";
118864
+ import { join as join102, resolve as resolve52 } from "node:path";
118405
118865
 
118406
118866
  // src/deps/python.ts
118407
118867
  import { createHash as createHash18 } from "node:crypto";
@@ -118412,7 +118872,7 @@ import {
118412
118872
  rmSync as rmSync16,
118413
118873
  writeFileSync as writeFileSync39
118414
118874
  } from "node:fs";
118415
- import { dirname as dirname43, join as join99 } from "node:path";
118875
+ import { dirname as dirname44, join as join100 } from "node:path";
118416
118876
  import { homedir as homedir52 } from "node:os";
118417
118877
  import { execFileSync as execFileSync28 } from "node:child_process";
118418
118878
 
@@ -118425,7 +118885,7 @@ class PythonEnvError extends Error {
118425
118885
  }
118426
118886
  }
118427
118887
  function defaultPythonCacheRoot() {
118428
- return join99(homedir52(), ".switchroom", "deps", "python");
118888
+ return join100(homedir52(), ".switchroom", "deps", "python");
118429
118889
  }
118430
118890
  function hashFile(path7) {
118431
118891
  return createHash18("sha256").update(readFileSync88(path7)).digest("hex");
@@ -118437,11 +118897,11 @@ function ensurePythonEnv(opts) {
118437
118897
  if (!existsSync96(requirementsPath)) {
118438
118898
  throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
118439
118899
  }
118440
- const venvDir = join99(cacheRoot, skillName);
118441
- const stampPath = join99(venvDir, ".requirements.sha256");
118442
- const binDir = join99(venvDir, "bin");
118443
- const pythonBin = join99(binDir, "python");
118444
- const pipBin = join99(binDir, "pip");
118900
+ const venvDir = join100(cacheRoot, skillName);
118901
+ const stampPath = join100(venvDir, ".requirements.sha256");
118902
+ const binDir = join100(venvDir, "bin");
118903
+ const pythonBin = join100(binDir, "python");
118904
+ const pipBin = join100(binDir, "pip");
118445
118905
  const targetHash = hashFile(requirementsPath);
118446
118906
  if (!force && existsSync96(stampPath) && existsSync96(pythonBin)) {
118447
118907
  const existingHash = readFileSync88(stampPath, "utf8").trim();
@@ -118459,7 +118919,7 @@ function ensurePythonEnv(opts) {
118459
118919
  if (existsSync96(venvDir)) {
118460
118920
  rmSync16(venvDir, { recursive: true, force: true });
118461
118921
  }
118462
- mkdirSync54(dirname43(venvDir), { recursive: true });
118922
+ mkdirSync54(dirname44(venvDir), { recursive: true });
118463
118923
  try {
118464
118924
  execFileSync28(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
118465
118925
  } catch (err) {
@@ -118500,7 +118960,7 @@ import {
118500
118960
  rmSync as rmSync17,
118501
118961
  writeFileSync as writeFileSync40
118502
118962
  } from "node:fs";
118503
- import { dirname as dirname44, join as join100 } from "node:path";
118963
+ import { dirname as dirname45, join as join101 } from "node:path";
118504
118964
  import { homedir as homedir53 } from "node:os";
118505
118965
  import { execFileSync as execFileSync29 } from "node:child_process";
118506
118966
 
@@ -118524,16 +118984,16 @@ var LOCKFILES_FOR = {
118524
118984
  npm: ["package-lock.json"]
118525
118985
  };
118526
118986
  function defaultNodeCacheRoot() {
118527
- return join100(homedir53(), ".switchroom", "deps", "node");
118987
+ return join101(homedir53(), ".switchroom", "deps", "node");
118528
118988
  }
118529
118989
  function hashDepInputs(packageJsonPath) {
118530
- const sourceDir = dirname44(packageJsonPath);
118990
+ const sourceDir = dirname45(packageJsonPath);
118531
118991
  const hasher = createHash19("sha256");
118532
118992
  hasher.update(`package.json
118533
118993
  `);
118534
118994
  hasher.update(readFileSync89(packageJsonPath));
118535
118995
  for (const lockName of ALL_LOCKFILES) {
118536
- const lockPath = join100(sourceDir, lockName);
118996
+ const lockPath = join101(sourceDir, lockName);
118537
118997
  if (existsSync97(lockPath)) {
118538
118998
  hasher.update(`
118539
118999
  `);
@@ -118552,11 +119012,11 @@ function ensureNodeEnv(opts) {
118552
119012
  if (!existsSync97(packageJsonPath)) {
118553
119013
  throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
118554
119014
  }
118555
- const sourceDir = dirname44(packageJsonPath);
118556
- const envDir = join100(cacheRoot, skillName);
118557
- const stampPath = join100(envDir, ".package.sha256");
118558
- const nodeModulesDir = join100(envDir, "node_modules");
118559
- const binDir = join100(nodeModulesDir, ".bin");
119015
+ const sourceDir = dirname45(packageJsonPath);
119016
+ const envDir = join101(cacheRoot, skillName);
119017
+ const stampPath = join101(envDir, ".package.sha256");
119018
+ const nodeModulesDir = join101(envDir, "node_modules");
119019
+ const binDir = join101(nodeModulesDir, ".bin");
118560
119020
  const targetHash = hashDepInputs(packageJsonPath);
118561
119021
  if (!force && existsSync97(stampPath) && existsSync97(nodeModulesDir)) {
118562
119022
  const existingHash = readFileSync89(stampPath, "utf8").trim();
@@ -118574,12 +119034,12 @@ function ensureNodeEnv(opts) {
118574
119034
  rmSync17(envDir, { recursive: true, force: true });
118575
119035
  }
118576
119036
  mkdirSync55(envDir, { recursive: true });
118577
- copyFileSync13(packageJsonPath, join100(envDir, "package.json"));
119037
+ copyFileSync13(packageJsonPath, join101(envDir, "package.json"));
118578
119038
  let copiedLockfile = false;
118579
119039
  for (const lockName of LOCKFILES_FOR[installer]) {
118580
- const lockPath = join100(sourceDir, lockName);
119040
+ const lockPath = join101(sourceDir, lockName);
118581
119041
  if (existsSync97(lockPath)) {
118582
- copyFileSync13(lockPath, join100(envDir, lockName));
119042
+ copyFileSync13(lockPath, join101(envDir, lockName));
118583
119043
  copiedLockfile = true;
118584
119044
  }
118585
119045
  }
@@ -118618,13 +119078,13 @@ function registerDepsCommand(program3) {
118618
119078
  console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
118619
119079
  process.exit(1);
118620
119080
  }
118621
- const skillDir = join101(skillsRoot, skill);
119081
+ const skillDir = join102(skillsRoot, skill);
118622
119082
  if (!existsSync98(skillDir)) {
118623
119083
  console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
118624
119084
  process.exit(1);
118625
119085
  }
118626
- const requirementsPath = join101(skillDir, "requirements.txt");
118627
- const packageJsonPath = join101(skillDir, "package.json");
119086
+ const requirementsPath = join102(skillDir, "requirements.txt");
119087
+ const packageJsonPath = join102(skillDir, "package.json");
118628
119088
  const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync98(requirementsPath));
118629
119089
  const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync98(packageJsonPath));
118630
119090
  let did = 0;
@@ -119579,7 +120039,7 @@ init_helpers();
119579
120039
  init_loader();
119580
120040
  init_merge();
119581
120041
  import { copyFileSync as copyFileSync14, existsSync as existsSync100, readFileSync as readFileSync90, writeFileSync as writeFileSync41 } from "node:fs";
119582
- import { join as join102, resolve as resolve54 } from "node:path";
120042
+ import { join as join103, resolve as resolve54 } from "node:path";
119583
120043
  init_scaffold();
119584
120044
  init_profiles();
119585
120045
  init_schema();
@@ -119605,7 +120065,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
119605
120065
  profileName,
119606
120066
  profilePath,
119607
120067
  workspaceDir,
119608
- soulPath: join102(workspaceDir, "SOUL.md"),
120068
+ soulPath: join103(workspaceDir, "SOUL.md"),
119609
120069
  soul: merged.soul
119610
120070
  };
119611
120071
  }
@@ -119671,8 +120131,8 @@ function registerSoulCommand(program3) {
119671
120131
  // src/cli/debug.ts
119672
120132
  init_helpers();
119673
120133
  init_loader();
119674
- import { existsSync as existsSync101, readFileSync as readFileSync91, readdirSync as readdirSync34, statSync as statSync56 } from "node:fs";
119675
- import { resolve as resolve55, join as join103 } from "node:path";
120134
+ import { existsSync as existsSync101, readFileSync as readFileSync91, readdirSync as readdirSync35, statSync as statSync57 } from "node:fs";
120135
+ import { resolve as resolve55, join as join104 } from "node:path";
119676
120136
  import { createHash as createHash20 } from "node:crypto";
119677
120137
  init_merge();
119678
120138
  init_hindsight2();
@@ -119683,7 +120143,7 @@ function estimateTokens(bytes) {
119683
120143
  return Math.round(bytes / 3.7);
119684
120144
  }
119685
120145
  function readMcpServerNames(agentDir) {
119686
- const mcpPath = join103(agentDir, ".mcp.json");
120146
+ const mcpPath = join104(agentDir, ".mcp.json");
119687
120147
  if (!existsSync101(mcpPath))
119688
120148
  return [];
119689
120149
  try {
@@ -119697,20 +120157,20 @@ function sha2562(content) {
119697
120157
  return createHash20("sha256").update(content).digest("hex").slice(0, 16);
119698
120158
  }
119699
120159
  function findLatestTranscriptJsonl(claudeConfigDir) {
119700
- const projectsDir = join103(claudeConfigDir, "projects");
120160
+ const projectsDir = join104(claudeConfigDir, "projects");
119701
120161
  if (!existsSync101(projectsDir))
119702
120162
  return;
119703
120163
  try {
119704
- const entries = readdirSync34(projectsDir, { withFileTypes: true });
120164
+ const entries = readdirSync35(projectsDir, { withFileTypes: true });
119705
120165
  let latest;
119706
120166
  for (const entry of entries) {
119707
120167
  if (!entry.isDirectory())
119708
120168
  continue;
119709
- const projectPath = join103(projectsDir, entry.name);
119710
- const transcriptPath = join103(projectPath, "transcript.jsonl");
120169
+ const projectPath = join104(projectsDir, entry.name);
120170
+ const transcriptPath = join104(projectPath, "transcript.jsonl");
119711
120171
  if (!existsSync101(transcriptPath))
119712
120172
  continue;
119713
- const stat3 = statSync56(transcriptPath);
120173
+ const stat3 = statSync57(transcriptPath);
119714
120174
  if (!latest || stat3.mtimeMs > latest.mtime) {
119715
120175
  latest = { path: transcriptPath, mtime: stat3.mtimeMs };
119716
120176
  }
@@ -119776,11 +120236,11 @@ function registerDebugCommand(program3) {
119776
120236
  process.exit(1);
119777
120237
  }
119778
120238
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
119779
- const claudeConfigDir = join103(agentDir, ".claude");
119780
- const claudeMdPath = join103(agentDir, "CLAUDE.md");
119781
- const soulMdPath = join103(agentDir, "SOUL.md");
119782
- const workspaceSoulMdPath = join103(workspaceDir, "SOUL.md");
119783
- const handoffPath = join103(agentDir, ".handoff.md");
120239
+ const claudeConfigDir = join104(agentDir, ".claude");
120240
+ const claudeMdPath = join104(agentDir, "CLAUDE.md");
120241
+ const soulMdPath = join104(agentDir, "SOUL.md");
120242
+ const workspaceSoulMdPath = join104(workspaceDir, "SOUL.md");
120243
+ const handoffPath = join104(agentDir, ".handoff.md");
119784
120244
  const lastN = parseInt(opts.last, 10);
119785
120245
  if (isNaN(lastN) || lastN < 1) {
119786
120246
  console.error("--last must be a positive integer");
@@ -119909,9 +120369,9 @@ function registerDebugCommand(program3) {
119909
120369
  const soulMdBytes = soulMdContent.length;
119910
120370
  const perTurnBytes = dynamicResult.concatenated.length;
119911
120371
  const userBytes = userMessage?.text.length ?? 0;
119912
- const fleetDir = join103(agentsDir, "..", "fleet");
119913
- const fleetInvPath = join103(fleetDir, "switchroom-invariants.md");
119914
- const fleetClaudePath = join103(fleetDir, "CLAUDE.md");
120372
+ const fleetDir = join104(agentsDir, "..", "fleet");
120373
+ const fleetInvPath = join104(fleetDir, "switchroom-invariants.md");
120374
+ const fleetClaudePath = join104(fleetDir, "CLAUDE.md");
119915
120375
  const fleetInvBytes = existsSync101(fleetInvPath) ? readFileSync91(fleetInvPath, "utf-8").length : 0;
119916
120376
  const fleetClaudeBytes = existsSync101(fleetClaudePath) ? readFileSync91(fleetClaudePath, "utf-8").length : 0;
119917
120377
  const fleetBytes = fleetInvBytes + fleetClaudeBytes;
@@ -119947,7 +120407,7 @@ init_source();
119947
120407
  // src/worktree/claim.ts
119948
120408
  import { execFileSync as execFileSync30 } from "node:child_process";
119949
120409
  import { closeSync as closeSync18, mkdirSync as mkdirSync57, openSync as openSync18, existsSync as existsSync103, unlinkSync as unlinkSync21 } from "node:fs";
119950
- import { join as join105, resolve as resolve57 } from "node:path";
120410
+ import { join as join106, resolve as resolve57 } from "node:path";
119951
120411
  import { homedir as homedir56 } from "node:os";
119952
120412
  import { randomBytes as randomBytes16 } from "node:crypto";
119953
120413
 
@@ -119956,18 +120416,18 @@ import {
119956
120416
  mkdirSync as mkdirSync56,
119957
120417
  writeFileSync as writeFileSync42,
119958
120418
  readFileSync as readFileSync92,
119959
- readdirSync as readdirSync35,
120419
+ readdirSync as readdirSync36,
119960
120420
  unlinkSync as unlinkSync20,
119961
120421
  existsSync as existsSync102,
119962
120422
  renameSync as renameSync26
119963
120423
  } from "node:fs";
119964
- import { join as join104, resolve as resolve56 } from "node:path";
120424
+ import { join as join105, resolve as resolve56 } from "node:path";
119965
120425
  import { homedir as homedir55 } from "node:os";
119966
120426
  function registryDir() {
119967
- return resolve56(process.env.SWITCHROOM_WORKTREE_DIR ?? join104(homedir55(), ".switchroom", "worktrees"));
120427
+ return resolve56(process.env.SWITCHROOM_WORKTREE_DIR ?? join105(homedir55(), ".switchroom", "worktrees"));
119968
120428
  }
119969
120429
  function recordPath(id) {
119970
- return join104(registryDir(), `${id}.json`);
120430
+ return join105(registryDir(), `${id}.json`);
119971
120431
  }
119972
120432
  function ensureDir2() {
119973
120433
  mkdirSync56(registryDir(), { recursive: true });
@@ -119999,7 +120459,7 @@ function listRecords() {
119999
120459
  ensureDir2();
120000
120460
  const dir = registryDir();
120001
120461
  const records = [];
120002
- for (const entry of readdirSync35(dir)) {
120462
+ for (const entry of readdirSync36(dir)) {
120003
120463
  if (!entry.endsWith(".json"))
120004
120464
  continue;
120005
120465
  const id = entry.slice(0, -5);
@@ -120018,7 +120478,7 @@ function acquireRepoLock(repoPath) {
120018
120478
  const lockDir = registryDir();
120019
120479
  mkdirSync57(lockDir, { recursive: true });
120020
120480
  const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
120021
- const lockPath = join105(lockDir, `.lock-${lockName}`);
120481
+ const lockPath = join106(lockDir, `.lock-${lockName}`);
120022
120482
  const deadline = Date.now() + 5000;
120023
120483
  let fd = null;
120024
120484
  while (fd === null) {
@@ -120045,7 +120505,7 @@ function acquireRepoLock(repoPath) {
120045
120505
  }
120046
120506
  var DEFAULT_CONCURRENCY = 5;
120047
120507
  function worktreesBaseDir() {
120048
- return resolve57(process.env.SWITCHROOM_WORKTREE_BASE ?? join105(homedir56(), ".switchroom", "worktree-checkouts"));
120508
+ return resolve57(process.env.SWITCHROOM_WORKTREE_BASE ?? join106(homedir56(), ".switchroom", "worktree-checkouts"));
120049
120509
  }
120050
120510
  function shortId() {
120051
120511
  return randomBytes16(4).toString("hex");
@@ -120067,7 +120527,7 @@ function resolveRepoPath(repo, codeRepos) {
120067
120527
  }
120068
120528
  function expandHome(p) {
120069
120529
  if (p.startsWith("~/"))
120070
- return join105(homedir56(), p.slice(2));
120530
+ return join106(homedir56(), p.slice(2));
120071
120531
  return p;
120072
120532
  }
120073
120533
  async function claimWorktree(input, codeRepos) {
@@ -120095,7 +120555,7 @@ async function claimWorktree(input, codeRepos) {
120095
120555
  branch = `task/${taskSuffix}-${id}`;
120096
120556
  const baseDir = worktreesBaseDir();
120097
120557
  mkdirSync57(baseDir, { recursive: true });
120098
- worktreePath = join105(baseDir, `${id}-${taskSuffix}`);
120558
+ worktreePath = join106(baseDir, `${id}-${taskSuffix}`);
120099
120559
  const ambientOwner = process.env.SWITCHROOM_AGENT_NAME;
120100
120560
  const ownerAgent = input.ownerAgent ?? (ambientOwner != null && ambientOwner !== "" ? ambientOwner : undefined);
120101
120561
  const now2 = new Date().toISOString();
@@ -120319,14 +120779,14 @@ import { execFileSync as execFileSync33 } from "node:child_process";
120319
120779
  import {
120320
120780
  existsSync as existsSync106,
120321
120781
  readFileSync as readFileSync93,
120322
- readdirSync as readdirSync36,
120323
- statSync as statSync57,
120782
+ readdirSync as readdirSync37,
120783
+ statSync as statSync58,
120324
120784
  renameSync as renameSync27,
120325
120785
  mkdirSync as mkdirSync58,
120326
120786
  rmSync as rmSync18
120327
120787
  } from "node:fs";
120328
120788
  import { homedir as homedir57 } from "node:os";
120329
- import { join as join106, resolve as resolve58 } from "node:path";
120789
+ import { join as join107, resolve as resolve58 } from "node:path";
120330
120790
  function parseGitdirPointer(dotGitFileContents) {
120331
120791
  const m = /^gitdir:\s*(.+?)\s*$/m.exec(dotGitFileContents);
120332
120792
  return m ? m[1] : null;
@@ -120485,7 +120945,7 @@ function defaultNewestTrackedMtimeMs(dir, exec) {
120485
120945
  let newest = 0;
120486
120946
  for (const f of files) {
120487
120947
  try {
120488
- const m = statSync57(join106(dir, f)).mtimeMs;
120948
+ const m = statSync58(join107(dir, f)).mtimeMs;
120489
120949
  if (m > newest)
120490
120950
  newest = m;
120491
120951
  } catch {}
@@ -120493,17 +120953,17 @@ function defaultNewestTrackedMtimeMs(dir, exec) {
120493
120953
  return newest || Date.now();
120494
120954
  }
120495
120955
  function trashRoot() {
120496
- return resolve58(process.env.SWITCHROOM_WORKTREE_TRASH ?? join106(homedir57(), ".switchroom", "worktree-gc-trash"));
120956
+ return resolve58(process.env.SWITCHROOM_WORKTREE_TRASH ?? join107(homedir57(), ".switchroom", "worktree-gc-trash"));
120497
120957
  }
120498
120958
  function planGc(roots, deps = {}, taskTreeRoots = []) {
120499
120959
  const exists = deps.existsSync ?? existsSync106;
120500
- const readDir = deps.readDir ?? ((p) => readdirSync36(p));
120960
+ const readDir = deps.readDir ?? ((p) => readdirSync37(p));
120501
120961
  const readFile4 = deps.readFile ?? ((p) => readFileSync93(p, "utf8"));
120502
- const stat3 = deps.stat ?? ((p) => statSync57(p));
120962
+ const stat3 = deps.stat ?? ((p) => statSync58(p));
120503
120963
  const exec = deps.exec ?? defaultExec3;
120504
120964
  const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
120505
120965
  const stamp = deps.dateStamp ?? "undated";
120506
- const trash = join106(trashRoot(), stamp);
120966
+ const trash = join107(trashRoot(), stamp);
120507
120967
  const probeInUse = deps.probeInUse ?? probePathInUse;
120508
120968
  const newestMtime = deps.newestTrackedMtimeMs ?? ((dir) => defaultNewestTrackedMtimeMs(dir, exec));
120509
120969
  const idleDays = deps.idleDays ?? 14;
@@ -120544,10 +121004,10 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
120544
121004
  continue;
120545
121005
  }
120546
121006
  for (const name of entries) {
120547
- const dir = join106(root, name);
121007
+ const dir = join107(root, name);
120548
121008
  if (isEphemeralPath(dir))
120549
121009
  continue;
120550
- const dotGit = join106(dir, ".git");
121010
+ const dotGit = join107(dir, ".git");
120551
121011
  if (!exists(dotGit))
120552
121012
  continue;
120553
121013
  let st;
@@ -120576,7 +121036,7 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
120576
121036
  ownerRepos.add(repoRoot);
120577
121037
  if (exists(ptr))
120578
121038
  continue;
120579
- orphans.push({ dir, owner: repoRoot, dest: join106(trash, name) });
121039
+ orphans.push({ dir, owner: repoRoot, dest: join107(trash, name) });
120580
121040
  }
120581
121041
  }
120582
121042
  const registered = [];
@@ -120631,10 +121091,10 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
120631
121091
  continue;
120632
121092
  }
120633
121093
  for (const name of entries) {
120634
- const dir = join106(root, name);
121094
+ const dir = join107(root, name);
120635
121095
  if (isEphemeralPath(dir))
120636
121096
  continue;
120637
- const dotGit = join106(dir, ".git");
121097
+ const dotGit = join107(dir, ".git");
120638
121098
  if (!exists(dotGit))
120639
121099
  continue;
120640
121100
  let shape;
@@ -120739,7 +121199,7 @@ function planGc(roots, deps = {}, taskTreeRoots = []) {
120739
121199
  verdict,
120740
121200
  prSignal: sig,
120741
121201
  idle,
120742
- dest: join106(trash, name),
121202
+ dest: join107(trash, name),
120743
121203
  willAct
120744
121204
  });
120745
121205
  }
@@ -120816,13 +121276,13 @@ function selectPurgeTargets(entries, olderThanDays) {
120816
121276
  }
120817
121277
  function listTrashEntries(nowMs, deps = {}) {
120818
121278
  const exists = deps.existsSync ?? existsSync106;
120819
- const readDir = deps.readDir ?? ((p) => readdirSync36(p));
121279
+ const readDir = deps.readDir ?? ((p) => readdirSync37(p));
120820
121280
  const root = trashRoot();
120821
121281
  if (!exists(root))
120822
121282
  return [];
120823
121283
  const out = [];
120824
121284
  for (const stamp of readDir(root)) {
120825
- const stampDir = join106(root, stamp);
121285
+ const stampDir = join107(root, stamp);
120826
121286
  let names;
120827
121287
  try {
120828
121288
  names = readDir(stampDir);
@@ -120830,10 +121290,10 @@ function listTrashEntries(nowMs, deps = {}) {
120830
121290
  continue;
120831
121291
  }
120832
121292
  for (const name of names) {
120833
- const p = join106(stampDir, name);
121293
+ const p = join107(stampDir, name);
120834
121294
  let mtimeMs = nowMs;
120835
121295
  try {
120836
- mtimeMs = statSync57(p).mtimeMs;
121296
+ mtimeMs = statSync58(p).mtimeMs;
120837
121297
  } catch {}
120838
121298
  out.push({ path: p, ageDays: (nowMs - mtimeMs) / 86400000 });
120839
121299
  }
@@ -120854,20 +121314,20 @@ function purgeTrash(paths) {
120854
121314
  return { deleted, errors: errors2 };
120855
121315
  }
120856
121316
  function defaultRoots() {
120857
- return [join106(homedir57(), "code")];
121317
+ return [join107(homedir57(), "code")];
120858
121318
  }
120859
121319
  function defaultTaskTreeRoots() {
120860
- const agentsDir = join106(homedir57(), ".switchroom", "agents");
121320
+ const agentsDir = join107(homedir57(), ".switchroom", "agents");
120861
121321
  let names;
120862
121322
  try {
120863
- names = readdirSync36(agentsDir);
121323
+ names = readdirSync37(agentsDir);
120864
121324
  } catch {
120865
121325
  return [];
120866
121326
  }
120867
121327
  const roots = [];
120868
121328
  for (const name of names.sort()) {
120869
121329
  for (const sub of ["home/work", "home/workspace"]) {
120870
- const r = join106(agentsDir, name, sub);
121330
+ const r = join107(agentsDir, name, sub);
120871
121331
  if (existsSync106(r))
120872
121332
  roots.push(r);
120873
121333
  }
@@ -121128,11 +121588,11 @@ init_scaffold_integration();
121128
121588
  import {
121129
121589
  chmodSync as chmodSync17,
121130
121590
  mkdirSync as mkdirSync59,
121131
- readdirSync as readdirSync37,
121591
+ readdirSync as readdirSync38,
121132
121592
  rmSync as rmSync19,
121133
121593
  writeFileSync as writeFileSync43
121134
121594
  } from "node:fs";
121135
- import { join as join107 } from "node:path";
121595
+ import { join as join108 } from "node:path";
121136
121596
  function encodeCredentialsFilename(email) {
121137
121597
  const SAFE = new Set([
121138
121598
  ..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
@@ -121396,16 +121856,16 @@ function resolveCredentialsDir(env2) {
121396
121856
  if (explicit && explicit.length > 0)
121397
121857
  return explicit;
121398
121858
  const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
121399
- return join107(stateBase, "google-workspace-mcp", "credentials");
121859
+ return join108(stateBase, "google-workspace-mcp", "credentials");
121400
121860
  }
121401
121861
  function writeSeedFile(dir, email, seed) {
121402
121862
  mkdirSync59(dir, { recursive: true, mode: 448 });
121403
121863
  chmodSync17(dir, 448);
121404
- for (const name of readdirSync37(dir)) {
121405
- rmSync19(join107(dir, name), { force: true, recursive: true });
121864
+ for (const name of readdirSync38(dir)) {
121865
+ rmSync19(join108(dir, name), { force: true, recursive: true });
121406
121866
  }
121407
121867
  const filename = encodeCredentialsFilename(email);
121408
- const filePath = join107(dir, filename);
121868
+ const filePath = join108(dir, filename);
121409
121869
  writeFileSync43(filePath, JSON.stringify(seed), { mode: 384 });
121410
121870
  chmodSync17(filePath, 384);
121411
121871
  return filePath;
@@ -121602,7 +122062,7 @@ function registerDriveMcpLauncherCommand(program3) {
121602
122062
  init_scaffold_integration();
121603
122063
  import { spawn as spawn6 } from "node:child_process";
121604
122064
  import { writeFileSync as writeFileSync44, mkdirSync as mkdirSync60 } from "node:fs";
121605
- import { dirname as dirname45, join as join108 } from "node:path";
122065
+ import { dirname as dirname46, join as join109 } from "node:path";
121606
122066
  var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
121607
122067
  var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
121608
122068
  var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
@@ -121638,7 +122098,7 @@ function computeRefreshDelayMs(expiresAt, now2, leadMs = DEFAULT_REFRESH_LEAD_MS
121638
122098
  function writeRefreshHeartbeat(agentName, data, account) {
121639
122099
  const path10 = heartbeatPath(agentName, account);
121640
122100
  try {
121641
- mkdirSync60(dirname45(path10), { recursive: true });
122101
+ mkdirSync60(dirname46(path10), { recursive: true });
121642
122102
  writeFileSync44(path10, JSON.stringify(data, null, 2), { mode: 420 });
121643
122103
  } catch {}
121644
122104
  }
@@ -121647,7 +122107,7 @@ function heartbeatPath(agentName, account) {
121647
122107
  const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
121648
122108
  if (override) {
121649
122109
  const base = slug ? `m365-launcher-${agentName}-${slug}` : `m365-launcher-${agentName}`;
121650
- return join108(override, `${base}.heartbeat.json`);
122110
+ return join109(override, `${base}.heartbeat.json`);
121651
122111
  }
121652
122112
  return slug ? `/state/agent/m365-launcher-${slug}.heartbeat.json` : "/state/agent/m365-launcher.heartbeat.json";
121653
122113
  }
@@ -121880,7 +122340,7 @@ function registerM365McpLauncherCommand(program3) {
121880
122340
  init_scaffold_integration();
121881
122341
  import { spawn as spawn7 } from "node:child_process";
121882
122342
  import { existsSync as existsSync107, mkdirSync as mkdirSync61, writeFileSync as writeFileSync45 } from "node:fs";
121883
- import { dirname as dirname46 } from "node:path";
122343
+ import { dirname as dirname47 } from "node:path";
121884
122344
  var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
121885
122345
  var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
121886
122346
  var DEFAULT_VAULT_KEY = "notion/integration-token";
@@ -121890,7 +122350,7 @@ function buildNotionMcpArgs(opts) {
121890
122350
  }
121891
122351
  function defaultWriteHeartbeat(path10, contents) {
121892
122352
  try {
121893
- const dir = dirname46(path10);
122353
+ const dir = dirname47(path10);
121894
122354
  if (!existsSync107(dir))
121895
122355
  mkdirSync61(dir, { recursive: true });
121896
122356
  writeFileSync45(path10, contents);
@@ -122005,7 +122465,7 @@ function registerNotionMcpLauncherCommand(program3) {
122005
122465
  init_hindsight_directive_admin();
122006
122466
  import { mkdirSync as mkdirSync62, readFileSync as readFileSync94, renameSync as renameSync28, writeFileSync as writeFileSync46 } from "node:fs";
122007
122467
  import { tmpdir as tmpdir5 } from "node:os";
122008
- import { join as join109 } from "node:path";
122468
+ import { join as join110 } from "node:path";
122009
122469
  import { createInterface as createInterface7 } from "node:readline";
122010
122470
 
122011
122471
  // src/memory/hindsight-knowledge-admin.ts
@@ -122593,12 +123053,12 @@ class HindsightShim {
122593
123053
  `));
122594
123054
  }
122595
123055
  get cachePath() {
122596
- return join109(this.opts.cacheDir, TOOLS_CACHE_FILENAME);
123056
+ return join110(this.opts.cacheDir, TOOLS_CACHE_FILENAME);
122597
123057
  }
122598
123058
  writeCache(result) {
122599
123059
  try {
122600
123060
  mkdirSync62(this.opts.cacheDir, { recursive: true });
122601
- const tmp = join109(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
123061
+ const tmp = join110(this.opts.cacheDir, `.${TOOLS_CACHE_FILENAME}.${process.pid}.tmp`);
122602
123062
  writeFileSync46(tmp, JSON.stringify(result, null, 2) + `
122603
123063
  `);
122604
123064
  renameSync28(tmp, this.cachePath);
@@ -122882,7 +123342,7 @@ function resolveShimOptionsFromEnv(env2) {
122882
123342
  return {
122883
123343
  url: env2.HINDSIGHT_MCP_URL || HINDSIGHT_DEFAULT_MCP_URL,
122884
123344
  bankId: env2.HINDSIGHT_BANK_ID || "",
122885
- cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR || join109(home2, ".hindsight-shim")
123345
+ cacheDir: env2.HINDSIGHT_SHIM_CACHE_DIR || join110(home2, ".hindsight-shim")
122886
123346
  };
122887
123347
  }
122888
123348
  function registerHindsightMcpShimCommand(program3) {
@@ -122895,11 +123355,11 @@ function registerHindsightMcpShimCommand(program3) {
122895
123355
 
122896
123356
  // src/cli/deliver-file.ts
122897
123357
  init_client2();
122898
- import { readFileSync as readFileSync95, statSync as statSync58 } from "node:fs";
122899
- import { basename as basename13 } from "node:path";
123358
+ import { readFileSync as readFileSync95, statSync as statSync59 } from "node:fs";
123359
+ import { basename as basename14 } from "node:path";
122900
123360
 
122901
123361
  // src/delivery/onedrive.ts
122902
- import { basename as basename11 } from "node:path";
123362
+ import { basename as basename12 } from "node:path";
122903
123363
  var GRAPH = "https://graph.microsoft.com/v1.0";
122904
123364
  var ONEDRIVE_INLINE_MAX_BYTES = 4 * 1024 * 1024;
122905
123365
  function authHeaders2(token) {
@@ -123022,14 +123482,14 @@ async function createShareLink(deps, item, scopes = ["anonymous", "organization"
123022
123482
  async function deliverToOneDrive(args) {
123023
123483
  const deps = { accessToken: args.accessToken, fetchImpl: args.fetchImpl };
123024
123484
  const folder = await ensureSwitchroomFolder(deps, args.agentName);
123025
- const filename = basename11(args.localPath);
123485
+ const filename = basename12(args.localPath);
123026
123486
  const item = await uploadFile(deps, folder.id, filename, args.bytes);
123027
123487
  const link = await createShareLink(deps, item, args.linkScopes);
123028
123488
  return { itemId: item.id, link, folderPath: `Switchroom/${args.agentName}` };
123029
123489
  }
123030
123490
 
123031
123491
  // src/delivery/gdrive.ts
123032
- import { basename as basename12 } from "node:path";
123492
+ import { basename as basename13 } from "node:path";
123033
123493
  var DRIVE = "https://www.googleapis.com/drive/v3";
123034
123494
  var UPLOAD = "https://www.googleapis.com/upload/drive/v3";
123035
123495
  var FOLDER_MIME = "application/vnd.google-apps.folder";
@@ -123177,7 +123637,7 @@ async function createShareLink2(deps, file, scopes = ["anyone"]) {
123177
123637
  async function deliverToGoogleDrive(args) {
123178
123638
  const deps = { accessToken: args.accessToken, fetchImpl: args.fetchImpl };
123179
123639
  const folder = await ensureSwitchroomFolder2(deps, args.agentName);
123180
- const filename = basename12(args.localPath);
123640
+ const filename = basename13(args.localPath);
123181
123641
  const file = await uploadFile2(deps, folder.id, filename, args.bytes);
123182
123642
  const link = await createShareLink2(deps, file, args.linkScopes);
123183
123643
  return { itemId: file.id, link, folderPath: `Switchroom/${args.agentName}` };
@@ -123234,7 +123694,7 @@ async function defaultResolveProvider() {
123234
123694
  }
123235
123695
  async function runDeliverFile(localPath, deps = {}) {
123236
123696
  const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
123237
- const sizeOf = deps.fileSize ?? ((p) => statSync58(p).size);
123697
+ const sizeOf = deps.fileSize ?? ((p) => statSync59(p).size);
123238
123698
  const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync95(p)));
123239
123699
  const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
123240
123700
  let size;
@@ -123256,7 +123716,7 @@ async function runDeliverFile(localPath, deps = {}) {
123256
123716
  try {
123257
123717
  const bytes = read(localPath);
123258
123718
  const out = await provider.deliver({ agentName, localPath, bytes });
123259
- return { ok: true, provider: provider.name, link: out.link, folderPath: out.folderPath, filename: basename13(localPath) };
123719
+ return { ok: true, provider: provider.name, link: out.link, folderPath: out.folderPath, filename: basename14(localPath) };
123260
123720
  } catch (err) {
123261
123721
  return { ok: false, provider: provider.name, error: `upload failed: ${err.message}` };
123262
123722
  }
@@ -123556,8 +124016,8 @@ function runRedactStdin() {
123556
124016
  }
123557
124017
 
123558
124018
  // src/cli/status-ask.ts
123559
- import { readFileSync as readFileSync96, existsSync as existsSync108, readdirSync as readdirSync38 } from "node:fs";
123560
- import { join as join110 } from "node:path";
124019
+ import { readFileSync as readFileSync96, existsSync as existsSync108, readdirSync as readdirSync39 } from "node:fs";
124020
+ import { join as join111 } from "node:path";
123561
124021
  import { homedir as homedir58 } from "node:os";
123562
124022
 
123563
124023
  // src/status-ask/report.ts
@@ -123893,19 +124353,19 @@ function resolveSources(explicitPath) {
123893
124353
  const config = loadConfig();
123894
124354
  agentsDir = resolveAgentsDir(config);
123895
124355
  } catch {
123896
- agentsDir = join110(homedir58(), ".switchroom", "agents");
124356
+ agentsDir = join111(homedir58(), ".switchroom", "agents");
123897
124357
  }
123898
124358
  if (!existsSync108(agentsDir))
123899
124359
  return [];
123900
124360
  const sources = [];
123901
124361
  let entries;
123902
124362
  try {
123903
- entries = readdirSync38(agentsDir);
124363
+ entries = readdirSync39(agentsDir);
123904
124364
  } catch {
123905
124365
  return [];
123906
124366
  }
123907
124367
  for (const name of entries) {
123908
- const path10 = join110(agentsDir, name, "runtime-metrics.jsonl");
124368
+ const path10 = join111(agentsDir, name, "runtime-metrics.jsonl");
123909
124369
  if (existsSync108(path10)) {
123910
124370
  sources.push({ path: path10, agent: name });
123911
124371
  }
@@ -123941,28 +124401,28 @@ import {
123941
124401
  fsyncSync as fsyncSync8,
123942
124402
  mkdirSync as mkdirSync63,
123943
124403
  openSync as openSync19,
123944
- readdirSync as readdirSync39,
124404
+ readdirSync as readdirSync40,
123945
124405
  readFileSync as readFileSync97,
123946
124406
  renameSync as renameSync29,
123947
- statSync as statSync59,
124407
+ statSync as statSync60,
123948
124408
  unlinkSync as unlinkSync22,
123949
124409
  writeSync as writeSync10
123950
124410
  } from "node:fs";
123951
- import { join as join111, resolve as resolve59 } from "node:path";
124411
+ import { join as join112, resolve as resolve59 } from "node:path";
123952
124412
  var STAGING_SUBDIR = ".staging";
123953
124413
  function overlayPathsFor(agent, opts = {}) {
123954
124414
  const base = opts.root ? resolve59(opts.root, agent) : resolve59(resolveDualPath(`~/.switchroom/agents/${agent}`));
123955
- const scheduleDir = join111(base, "schedule.d");
123956
- const scheduleStagingDir = join111(scheduleDir, STAGING_SUBDIR);
123957
- const skillsDir = join111(base, "skills.d");
123958
- const skillsStagingDir = join111(skillsDir, STAGING_SUBDIR);
124415
+ const scheduleDir = join112(base, "schedule.d");
124416
+ const scheduleStagingDir = join112(scheduleDir, STAGING_SUBDIR);
124417
+ const skillsDir = join112(base, "skills.d");
124418
+ const skillsStagingDir = join112(skillsDir, STAGING_SUBDIR);
123959
124419
  return {
123960
124420
  agentRoot: base,
123961
124421
  scheduleDir,
123962
124422
  scheduleStagingDir,
123963
124423
  skillsDir,
123964
124424
  skillsStagingDir,
123965
- lockPath: join111(base, ".lock"),
124425
+ lockPath: join112(base, ".lock"),
123966
124426
  stagingDir: scheduleStagingDir
123967
124427
  };
123968
124428
  }
@@ -123988,7 +124448,7 @@ function withAgentLock(paths, fn) {
123988
124448
  if (e.code !== "EEXIST")
123989
124449
  throw err;
123990
124450
  try {
123991
- const age = Date.now() - statSync59(paths.lockPath).mtimeMs;
124451
+ const age = Date.now() - statSync60(paths.lockPath).mtimeMs;
123992
124452
  if (age > 30000) {
123993
124453
  unlinkSync22(paths.lockPath);
123994
124454
  continue;
@@ -124021,7 +124481,7 @@ function alignStagedOwnerToDir(stagingPath, targetDir) {
124021
124481
  const euid = overlayWriterRuntime.geteuid();
124022
124482
  if (euid === undefined)
124023
124483
  return;
124024
- const dirStat = statSync59(targetDir);
124484
+ const dirStat = statSync60(targetDir);
124025
124485
  if (dirStat.uid === euid)
124026
124486
  return;
124027
124487
  overlayWriterRuntime.chown(stagingPath, dirStat.uid, dirStat.gid);
@@ -124031,8 +124491,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
124031
124491
  const paths = overlayPathsFor(agent, opts);
124032
124492
  return withAgentLock(paths, () => {
124033
124493
  ensureDirs(paths);
124034
- const stagingPath = join111(paths.scheduleStagingDir, `${slug}.yaml`);
124035
- const finalPath = join111(paths.scheduleDir, `${slug}.yaml`);
124494
+ const stagingPath = join112(paths.scheduleStagingDir, `${slug}.yaml`);
124495
+ const finalPath = join112(paths.scheduleDir, `${slug}.yaml`);
124036
124496
  const fd = openSync19(stagingPath, "w", 384);
124037
124497
  try {
124038
124498
  writeSync10(fd, yamlText);
@@ -124049,8 +124509,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
124049
124509
  const paths = overlayPathsFor(agent, opts);
124050
124510
  return withAgentLock(paths, () => {
124051
124511
  ensureSkillsDirs(paths);
124052
- const stagingPath = join111(paths.skillsStagingDir, `${slug}.yaml`);
124053
- const finalPath = join111(paths.skillsDir, `${slug}.yaml`);
124512
+ const stagingPath = join112(paths.skillsStagingDir, `${slug}.yaml`);
124513
+ const finalPath = join112(paths.skillsDir, `${slug}.yaml`);
124054
124514
  const fd = openSync19(stagingPath, "w", 384);
124055
124515
  try {
124056
124516
  writeSync10(fd, yamlText);
@@ -124066,7 +124526,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
124066
124526
  function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
124067
124527
  const paths = overlayPathsFor(agent, opts);
124068
124528
  return withAgentLock(paths, () => {
124069
- const finalPath = join111(paths.skillsDir, `${slug}.yaml`);
124529
+ const finalPath = join112(paths.skillsDir, `${slug}.yaml`);
124070
124530
  if (!existsSync109(finalPath))
124071
124531
  return false;
124072
124532
  unlinkSync22(finalPath);
@@ -124078,10 +124538,10 @@ function listSkillsOverlayEntries(agent, opts = {}) {
124078
124538
  if (!existsSync109(paths.skillsDir))
124079
124539
  return [];
124080
124540
  const out = [];
124081
- for (const name of readdirSync39(paths.skillsDir)) {
124541
+ for (const name of readdirSync40(paths.skillsDir)) {
124082
124542
  if (!/\.ya?ml$/i.test(name))
124083
124543
  continue;
124084
- const full = join111(paths.skillsDir, name);
124544
+ const full = join112(paths.skillsDir, name);
124085
124545
  try {
124086
124546
  const raw = readFileSync97(full, "utf-8");
124087
124547
  const slug = name.replace(/\.ya?ml$/i, "");
@@ -124093,7 +124553,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
124093
124553
  function deleteOverlayEntry(agent, slug, opts = {}) {
124094
124554
  const paths = overlayPathsFor(agent, opts);
124095
124555
  return withAgentLock(paths, () => {
124096
- const finalPath = join111(paths.scheduleDir, `${slug}.yaml`);
124556
+ const finalPath = join112(paths.scheduleDir, `${slug}.yaml`);
124097
124557
  if (!existsSync109(finalPath))
124098
124558
  return false;
124099
124559
  unlinkSync22(finalPath);
@@ -124105,10 +124565,10 @@ function listOverlayEntries(agent, opts = {}) {
124105
124565
  if (!existsSync109(paths.scheduleDir))
124106
124566
  return [];
124107
124567
  const out = [];
124108
- for (const name of readdirSync39(paths.scheduleDir)) {
124568
+ for (const name of readdirSync40(paths.scheduleDir)) {
124109
124569
  if (!/\.ya?ml$/i.test(name))
124110
124570
  continue;
124111
- const full = join111(paths.scheduleDir, name);
124571
+ const full = join112(paths.scheduleDir, name);
124112
124572
  try {
124113
124573
  const raw = readFileSync97(full, "utf-8");
124114
124574
  const slug = name.replace(/\.ya?ml$/i, "");
@@ -124334,19 +124794,19 @@ import {
124334
124794
  fsyncSync as fsyncSync9,
124335
124795
  mkdirSync as mkdirSync64,
124336
124796
  openSync as openSync20,
124337
- readdirSync as readdirSync40,
124797
+ readdirSync as readdirSync41,
124338
124798
  readFileSync as readFileSync98,
124339
124799
  renameSync as renameSync30,
124340
124800
  unlinkSync as unlinkSync23,
124341
124801
  writeFileSync as writeFileSync47,
124342
124802
  writeSync as writeSync11
124343
124803
  } from "node:fs";
124344
- import { join as join112 } from "node:path";
124804
+ import { join as join113 } from "node:path";
124345
124805
  import { randomBytes as randomBytes17 } from "node:crypto";
124346
124806
  var STAGE_ID_PREFIX = "cap_";
124347
124807
  function pendingDir(agent, opts = {}) {
124348
124808
  const paths = overlayPathsFor(agent, opts);
124349
- return join112(paths.scheduleDir, ".pending");
124809
+ return join113(paths.scheduleDir, ".pending");
124350
124810
  }
124351
124811
  function ensurePendingDir(agent, opts = {}) {
124352
124812
  const dir = pendingDir(agent, opts);
@@ -124359,8 +124819,8 @@ function newStageId() {
124359
124819
  function stagePendingScheduleEntry(opts) {
124360
124820
  const dir = ensurePendingDir(opts.agent, { root: opts.root });
124361
124821
  const stageId = opts.stageId ?? newStageId();
124362
- const yamlPath = join112(dir, `${stageId}.yaml`);
124363
- const metaPath = join112(dir, `${stageId}.meta.json`);
124822
+ const yamlPath = join113(dir, `${stageId}.yaml`);
124823
+ const metaPath = join113(dir, `${stageId}.meta.json`);
124364
124824
  const meta = {
124365
124825
  v: 1,
124366
124826
  stage_id: stageId,
@@ -124390,12 +124850,12 @@ function listPendingScheduleEntries(agent, opts = {}) {
124390
124850
  if (!existsSync110(dir))
124391
124851
  return [];
124392
124852
  const out = [];
124393
- for (const name of readdirSync40(dir).sort()) {
124853
+ for (const name of readdirSync41(dir).sort()) {
124394
124854
  if (!name.endsWith(".meta.json"))
124395
124855
  continue;
124396
124856
  const stageId = name.slice(0, -".meta.json".length);
124397
- const metaPath = join112(dir, name);
124398
- const yamlPath = join112(dir, `${stageId}.yaml`);
124857
+ const metaPath = join113(dir, name);
124858
+ const yamlPath = join113(dir, `${stageId}.yaml`);
124399
124859
  if (!existsSync110(yamlPath))
124400
124860
  continue;
124401
124861
  try {
@@ -124414,7 +124874,7 @@ function commitPendingScheduleEntry(opts) {
124414
124874
  return { committed: false, reason: "not_found" };
124415
124875
  const slug = match.meta.entry.name ?? match.stageId;
124416
124876
  const paths = overlayPathsFor(opts.agent, { root: opts.root });
124417
- const finalPath = join112(paths.scheduleDir, `${slug}.yaml`);
124877
+ const finalPath = join113(paths.scheduleDir, `${slug}.yaml`);
124418
124878
  if (existsSync110(finalPath)) {
124419
124879
  return { committed: false, reason: "slug_collision" };
124420
124880
  }
@@ -125067,7 +125527,7 @@ import { existsSync as existsSync112 } from "node:fs";
125067
125527
  init_reconcile_default_skills();
125068
125528
  init_agent_config();
125069
125529
  var import_yaml25 = __toESM(require_dist(), 1);
125070
- import { join as join113 } from "node:path";
125530
+ import { join as join114 } from "node:path";
125071
125531
  var MAX_SKILLS_PER_AGENT2 = 20;
125072
125532
  var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
125073
125533
  function exitCodeFor2(code) {
@@ -125142,7 +125602,7 @@ function skillInstall(opts) {
125142
125602
  return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT2})`);
125143
125603
  }
125144
125604
  const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
125145
- const skillPath = join113(poolDir, skillName);
125605
+ const skillPath = join114(poolDir, skillName);
125146
125606
  if (!existsSync112(skillPath)) {
125147
125607
  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.`);
125148
125608
  }
@@ -125314,20 +125774,20 @@ import {
125314
125774
  chmodSync as chmodSync18,
125315
125775
  closeSync as closeSync21,
125316
125776
  existsSync as existsSync113,
125317
- lstatSync as lstatSync14,
125777
+ lstatSync as lstatSync15,
125318
125778
  mkdirSync as mkdirSync65,
125319
125779
  mkdtempSync as mkdtempSync5,
125320
125780
  openSync as openSync21,
125321
125781
  readFileSync as readFileSync100,
125322
- readdirSync as readdirSync41,
125782
+ readdirSync as readdirSync42,
125323
125783
  realpathSync as realpathSync9,
125324
125784
  renameSync as renameSync31,
125325
125785
  rmSync as rmSync20,
125326
- statSync as statSync60,
125786
+ statSync as statSync61,
125327
125787
  writeFileSync as writeFileSync48
125328
125788
  } from "node:fs";
125329
125789
  import { tmpdir as tmpdir6, homedir as homedir59 } from "node:os";
125330
- import { dirname as dirname47, join as join114, relative as relative6, resolve as resolve60 } from "node:path";
125790
+ import { dirname as dirname48, join as join115, relative as relative6, resolve as resolve60 } from "node:path";
125331
125791
  import { spawnSync as spawnSync24 } from "node:child_process";
125332
125792
  var SKILL_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;
125333
125793
  var SH_SCRIPT_RE2 = /^scripts\/[A-Za-z0-9_.-]+\.sh$/;
@@ -125341,7 +125801,7 @@ function scanForClaudeP2(content) {
125341
125801
  function resolveSkillsPoolDir2(override) {
125342
125802
  const raw = override ?? "~/.switchroom/skills";
125343
125803
  if (raw.startsWith("~/")) {
125344
- return join114(homedir59(), raw.slice(2));
125804
+ return join115(homedir59(), raw.slice(2));
125345
125805
  }
125346
125806
  if (raw === "~")
125347
125807
  return homedir59();
@@ -125373,14 +125833,14 @@ function isTarballPath(p) {
125373
125833
  }
125374
125834
  function loadFromDir(dir) {
125375
125835
  const abs = realpathSync9(dir);
125376
- if (!statSync60(abs).isDirectory()) {
125836
+ if (!statSync61(abs).isDirectory()) {
125377
125837
  fail4(`--from path is not a directory: ${dir}`);
125378
125838
  }
125379
125839
  const files = {};
125380
125840
  const walk2 = (sub) => {
125381
- const entries = readdirSync41(sub, { withFileTypes: true });
125841
+ const entries = readdirSync42(sub, { withFileTypes: true });
125382
125842
  for (const ent of entries) {
125383
- const full = join114(sub, ent.name);
125843
+ const full = join115(sub, ent.name);
125384
125844
  const rel = relative6(abs, full);
125385
125845
  if (ent.isSymbolicLink()) {
125386
125846
  fail4(`refusing to read symlink inside --from dir: ${rel}`);
@@ -125415,7 +125875,7 @@ function loadFromTarball(tarPath) {
125415
125875
  fail4(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
125416
125876
  }
125417
125877
  }
125418
- const staging = mkdtempSync5(join114(tmpdir6(), "skill-apply-extract-"));
125878
+ const staging = mkdtempSync5(join115(tmpdir6(), "skill-apply-extract-"));
125419
125879
  try {
125420
125880
  const flags = isGz ? ["-xzf"] : ["-xf"];
125421
125881
  const r = spawnSync24("tar", [
@@ -125501,8 +125961,8 @@ function validatePayload(name, files) {
125501
125961
  errors2.push(`${path10} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
125502
125962
  }
125503
125963
  } else if (PY_SCRIPT_RE2.test(path10)) {
125504
- const tmp = mkdtempSync5(join114(tmpdir6(), "skill-apply-py-"));
125505
- const tmpPy = join114(tmp, "check.py");
125964
+ const tmp = mkdtempSync5(join115(tmpdir6(), "skill-apply-py-"));
125965
+ const tmpPy = join115(tmp, "check.py");
125506
125966
  try {
125507
125967
  writeFileSync48(tmpPy, content);
125508
125968
  const r = spawnSync24("python3", ["-m", "py_compile", tmpPy], {
@@ -125524,8 +125984,8 @@ function diffSummary(currentDir, files) {
125524
125984
  const currentFiles = {};
125525
125985
  if (existsSync113(currentDir)) {
125526
125986
  const walk2 = (sub) => {
125527
- for (const ent of readdirSync41(sub, { withFileTypes: true })) {
125528
- const full = join114(sub, ent.name);
125987
+ for (const ent of readdirSync42(sub, { withFileTypes: true })) {
125988
+ const full = join115(sub, ent.name);
125529
125989
  const rel = relative6(currentDir, full);
125530
125990
  if (ent.isDirectory()) {
125531
125991
  walk2(full);
@@ -125561,10 +126021,10 @@ function writePayload(poolDir, name, files) {
125561
126021
  if (!existsSync113(poolDir)) {
125562
126022
  mkdirSync65(poolDir, { recursive: true, mode: 493 });
125563
126023
  }
125564
- const target = join114(poolDir, name);
126024
+ const target = join115(poolDir, name);
125565
126025
  let targetIsSymlink = false;
125566
126026
  try {
125567
- const st = lstatSync14(target);
126027
+ const st = lstatSync15(target);
125568
126028
  if (st.isSymbolicLink()) {
125569
126029
  targetIsSymlink = true;
125570
126030
  }
@@ -125572,13 +126032,13 @@ function writePayload(poolDir, name, files) {
125572
126032
  if (targetIsSymlink) {
125573
126033
  fail4(`refusing to overwrite symlink at ${target}; investigate manually`);
125574
126034
  }
125575
- const staging = mkdtempSync5(join114(poolDir, `.skill-apply-stage-${name}-`));
126035
+ const staging = mkdtempSync5(join115(poolDir, `.skill-apply-stage-${name}-`));
125576
126036
  chmodSync18(staging, 493);
125577
126037
  let oldRename = null;
125578
126038
  try {
125579
126039
  for (const [path10, content] of Object.entries(files)) {
125580
- const full = join114(staging, path10);
125581
- mkdirSync65(dirname47(full), { recursive: true, mode: 493 });
126040
+ const full = join115(staging, path10);
126041
+ mkdirSync65(dirname48(full), { recursive: true, mode: 493 });
125582
126042
  const fd = openSync21(full, "wx");
125583
126043
  try {
125584
126044
  writeFileSync48(fd, content);
@@ -125592,7 +126052,7 @@ function writePayload(poolDir, name, files) {
125592
126052
  }
125593
126053
  let targetExists = false;
125594
126054
  try {
125595
- lstatSync14(target);
126055
+ lstatSync15(target);
125596
126056
  targetExists = true;
125597
126057
  } catch {}
125598
126058
  if (targetExists) {
@@ -125634,7 +126094,7 @@ function registerSkillCommand(program3) {
125634
126094
  if (!existsSync113(fromPath)) {
125635
126095
  fail4(`--from path does not exist: ${opts.from}`);
125636
126096
  }
125637
- const st = statSync60(fromPath);
126097
+ const st = statSync61(fromPath);
125638
126098
  if (st.isDirectory()) {
125639
126099
  files = loadFromDir(fromPath);
125640
126100
  } else if (isTarballPath(fromPath)) {
@@ -125655,7 +126115,7 @@ function registerSkillCommand(program3) {
125655
126115
  }
125656
126116
  const config = loadConfig();
125657
126117
  const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
125658
- const currentDir = join114(poolDir, name);
126118
+ const currentDir = join115(poolDir, name);
125659
126119
  console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
125660
126120
  console.log(source_default.bold("Diff vs current pool content:"));
125661
126121
  console.log(diffSummary(currentDir, files));
@@ -125693,19 +126153,19 @@ import {
125693
126153
  chmodSync as chmodSync19,
125694
126154
  closeSync as closeSync22,
125695
126155
  existsSync as existsSync114,
125696
- lstatSync as lstatSync15,
126156
+ lstatSync as lstatSync16,
125697
126157
  mkdirSync as mkdirSync66,
125698
126158
  mkdtempSync as mkdtempSync6,
125699
126159
  openSync as openSync22,
125700
126160
  readFileSync as readFileSync101,
125701
- readdirSync as readdirSync42,
126161
+ readdirSync as readdirSync43,
125702
126162
  renameSync as renameSync32,
125703
126163
  rmSync as rmSync21,
125704
- statSync as statSync61,
126164
+ statSync as statSync62,
125705
126165
  utimesSync,
125706
126166
  writeFileSync as writeFileSync49
125707
126167
  } from "node:fs";
125708
- import { dirname as dirname48, join as join115, relative as relative7, resolve as resolve61 } from "node:path";
126168
+ import { dirname as dirname49, join as join116, relative as relative7, resolve as resolve61 } from "node:path";
125709
126169
  import { homedir as homedir60, tmpdir as tmpdir7 } from "node:os";
125710
126170
  import { spawnSync as spawnSync25 } from "node:child_process";
125711
126171
  var PERSONAL_PREFIX = "personal-";
@@ -125714,10 +126174,10 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
125714
126174
  var PERSONAL_SKILLS_SUBPATH2 = "personal-skills";
125715
126175
  function resolveConfigSkillsDir(agent) {
125716
126176
  const override = process.env.SWITCHROOM_CONFIG_DIR;
125717
- const candidate = override ? resolve61(override) : join115(homedir60(), ".switchroom-config");
126177
+ const candidate = override ? resolve61(override) : join116(homedir60(), ".switchroom-config");
125718
126178
  if (!existsSync114(candidate))
125719
126179
  return null;
125720
- return join115(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH2);
126180
+ return join116(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH2);
125721
126181
  }
125722
126182
  var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
125723
126183
  function sweepMirrorPriors(configSkillsRoot) {
@@ -125725,7 +126185,7 @@ function sweepMirrorPriors(configSkillsRoot) {
125725
126185
  if (!existsSync114(configSkillsRoot))
125726
126186
  return;
125727
126187
  const now2 = Date.now();
125728
- for (const ent of readdirSync42(configSkillsRoot)) {
126188
+ for (const ent of readdirSync43(configSkillsRoot)) {
125729
126189
  const m = /^\.(?:.+)-(?:prior|trash)-(\d+)$/.exec(ent);
125730
126190
  if (!m)
125731
126191
  continue;
@@ -125735,7 +126195,7 @@ function sweepMirrorPriors(configSkillsRoot) {
125735
126195
  if (now2 - ts < MIRROR_PRIOR_TTL_MS)
125736
126196
  continue;
125737
126197
  try {
125738
- rmSync21(join115(configSkillsRoot, ent), { recursive: true, force: true });
126198
+ rmSync21(join116(configSkillsRoot, ent), { recursive: true, force: true });
125739
126199
  } catch {}
125740
126200
  }
125741
126201
  } catch {}
@@ -125744,11 +126204,11 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
125744
126204
  const configSkillsRoot = resolveConfigSkillsDir(agent);
125745
126205
  if (!configSkillsRoot)
125746
126206
  return;
125747
- const dest = join115(configSkillsRoot, name);
126207
+ const dest = join116(configSkillsRoot, name);
125748
126208
  try {
125749
126209
  if (liveSkillDir !== null) {
125750
126210
  try {
125751
- const st = lstatSync15(liveSkillDir);
126211
+ const st = lstatSync16(liveSkillDir);
125752
126212
  if (st.isSymbolicLink()) {
125753
126213
  process.stderr.write(source_default.yellow(`warning: refusing to mirror ${liveSkillDir} \u2014 source is a symlink
125754
126214
  `));
@@ -125759,19 +126219,19 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
125759
126219
  if (liveSkillDir === null) {
125760
126220
  sweepMirrorPriors(configSkillsRoot);
125761
126221
  if (existsSync114(dest)) {
125762
- const trash = join115(configSkillsRoot, `.${name}-trash-${Date.now()}`);
126222
+ const trash = join116(configSkillsRoot, `.${name}-trash-${Date.now()}`);
125763
126223
  renameSync32(dest, trash);
125764
126224
  }
125765
126225
  return;
125766
126226
  }
125767
126227
  mkdirSync66(configSkillsRoot, { recursive: true, mode: 493 });
125768
126228
  sweepMirrorPriors(configSkillsRoot);
125769
- const staging = mkdtempSync6(join115(configSkillsRoot, `.${name}-staging-`));
126229
+ const staging = mkdtempSync6(join116(configSkillsRoot, `.${name}-staging-`));
125770
126230
  const walk2 = (src, dst) => {
125771
126231
  mkdirSync66(dst, { recursive: true, mode: 493 });
125772
- for (const ent of readdirSync42(src, { withFileTypes: true })) {
125773
- const s = join115(src, ent.name);
125774
- const d = join115(dst, ent.name);
126232
+ for (const ent of readdirSync43(src, { withFileTypes: true })) {
126233
+ const s = join116(src, ent.name);
126234
+ const d = join116(dst, ent.name);
125775
126235
  if (ent.isSymbolicLink())
125776
126236
  continue;
125777
126237
  if (ent.isDirectory())
@@ -125784,7 +126244,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
125784
126244
  walk2(liveSkillDir, staging);
125785
126245
  chmodSync19(staging, 493);
125786
126246
  if (existsSync114(dest)) {
125787
- const prior = join115(configSkillsRoot, `.${name}-prior-${Date.now()}`);
126247
+ const prior = join116(configSkillsRoot, `.${name}-prior-${Date.now()}`);
125788
126248
  renameSync32(dest, prior);
125789
126249
  }
125790
126250
  renameSync32(staging, dest);
@@ -125814,20 +126274,20 @@ function resolveAgent(opts) {
125814
126274
  function resolveAgentsRoot(opts) {
125815
126275
  if (opts.root)
125816
126276
  return resolve61(opts.root);
125817
- return join115(homedir60(), ".switchroom", "agents");
126277
+ return join116(homedir60(), ".switchroom", "agents");
125818
126278
  }
125819
126279
  function personalSkillDir(agentsRoot, agent, name) {
125820
- return join115(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
126280
+ return join116(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
125821
126281
  }
125822
126282
  function trashDir(agentsRoot, agent) {
125823
- return join115(agentsRoot, agent, ".claude", TRASH_DIRNAME);
126283
+ return join116(agentsRoot, agent, ".claude", TRASH_DIRNAME);
125824
126284
  }
125825
126285
  function countPersonalSkills(agentsRoot, agent) {
125826
- const skillsDir = join115(agentsRoot, agent, ".claude", "skills");
126286
+ const skillsDir = join116(agentsRoot, agent, ".claude", "skills");
125827
126287
  if (!existsSync114(skillsDir))
125828
126288
  return 0;
125829
126289
  let n = 0;
125830
- for (const ent of readdirSync42(skillsDir, { withFileTypes: true })) {
126290
+ for (const ent of readdirSync43(skillsDir, { withFileTypes: true })) {
125831
126291
  if (ent.isDirectory() && ent.name.startsWith(PERSONAL_PREFIX))
125832
126292
  n += 1;
125833
126293
  }
@@ -125855,13 +126315,13 @@ function readStdinSync2() {
125855
126315
  }
125856
126316
  function loadFromDir2(dir) {
125857
126317
  const abs = resolve61(dir);
125858
- if (!statSync61(abs).isDirectory()) {
126318
+ if (!statSync62(abs).isDirectory()) {
125859
126319
  fail5(`--from path is not a directory: ${dir}`);
125860
126320
  }
125861
126321
  const files = {};
125862
126322
  const walk2 = (sub) => {
125863
- for (const ent of readdirSync42(sub, { withFileTypes: true })) {
125864
- const full = join115(sub, ent.name);
126323
+ for (const ent of readdirSync43(sub, { withFileTypes: true })) {
126324
+ const full = join116(sub, ent.name);
125865
126325
  if (ent.isSymbolicLink()) {
125866
126326
  fail5(`refusing to read symlink in --from dir: ${relative7(abs, full)}`);
125867
126327
  }
@@ -125914,8 +126374,8 @@ function behavioralValidate(files) {
125914
126374
  errors2.push(`${path10} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
125915
126375
  }
125916
126376
  } else if (PY_SCRIPT_RE.test(path10)) {
125917
- const tmp = mkdtempSync6(join115(tmpdir7(), "skill-personal-py-"));
125918
- const tmpPy = join115(tmp, "check.py");
126377
+ const tmp = mkdtempSync6(join116(tmpdir7(), "skill-personal-py-"));
126378
+ const tmpPy = join116(tmp, "check.py");
125919
126379
  try {
125920
126380
  writeFileSync49(tmpPy, content);
125921
126381
  const r = spawnSync25("python3", ["-m", "py_compile", tmpPy], {
@@ -125936,12 +126396,12 @@ function sweepTrash(agentsRoot, agent) {
125936
126396
  if (!existsSync114(trash))
125937
126397
  return;
125938
126398
  const now2 = Date.now();
125939
- for (const ent of readdirSync42(trash, { withFileTypes: true })) {
126399
+ for (const ent of readdirSync43(trash, { withFileTypes: true })) {
125940
126400
  if (!ent.isDirectory())
125941
126401
  continue;
125942
- const entPath = join115(trash, ent.name);
126402
+ const entPath = join116(trash, ent.name);
125943
126403
  try {
125944
- const st = statSync61(entPath);
126404
+ const st = statSync62(entPath);
125945
126405
  if (now2 - st.mtimeMs > TRASH_TTL_MS) {
125946
126406
  rmSync21(entPath, { recursive: true, force: true });
125947
126407
  }
@@ -125951,7 +126411,7 @@ function sweepTrash(agentsRoot, agent) {
125951
126411
  function writePersonalSkill(targetDir, files) {
125952
126412
  let targetIsSymlink = false;
125953
126413
  try {
125954
- const st = lstatSync15(targetDir);
126414
+ const st = lstatSync16(targetDir);
125955
126415
  if (st.isSymbolicLink()) {
125956
126416
  targetIsSymlink = true;
125957
126417
  }
@@ -125959,14 +126419,14 @@ function writePersonalSkill(targetDir, files) {
125959
126419
  if (targetIsSymlink) {
125960
126420
  fail5(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
125961
126421
  }
125962
- mkdirSync66(dirname48(targetDir), { recursive: true, mode: 493 });
125963
- const staging = mkdtempSync6(join115(dirname48(targetDir), `.skill-personal-stage-`));
126422
+ mkdirSync66(dirname49(targetDir), { recursive: true, mode: 493 });
126423
+ const staging = mkdtempSync6(join116(dirname49(targetDir), `.skill-personal-stage-`));
125964
126424
  chmodSync19(staging, 493);
125965
126425
  let oldRename = null;
125966
126426
  try {
125967
126427
  for (const [path10, content] of Object.entries(files)) {
125968
- const full = join115(staging, path10);
125969
- mkdirSync66(dirname48(full), { recursive: true, mode: 493 });
126428
+ const full = join116(staging, path10);
126429
+ mkdirSync66(dirname49(full), { recursive: true, mode: 493 });
125970
126430
  const fd = openSync22(full, "wx");
125971
126431
  try {
125972
126432
  writeFileSync49(fd, content);
@@ -125980,7 +126440,7 @@ function writePersonalSkill(targetDir, files) {
125980
126440
  }
125981
126441
  let targetExists = false;
125982
126442
  try {
125983
- lstatSync15(targetDir);
126443
+ lstatSync16(targetDir);
125984
126444
  targetExists = true;
125985
126445
  } catch {}
125986
126446
  if (targetExists) {
@@ -126015,7 +126475,7 @@ function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
126015
126475
  const target = personalSkillDir(agentsRoot, agent, name);
126016
126476
  const exists = (() => {
126017
126477
  try {
126018
- lstatSync15(target);
126478
+ lstatSync16(target);
126019
126479
  return true;
126020
126480
  } catch {
126021
126481
  return false;
@@ -126067,7 +126527,7 @@ function loadFiles(opts) {
126067
126527
  if (!existsSync114(p)) {
126068
126528
  fail5(`--from path does not exist: ${opts.from}`);
126069
126529
  }
126070
- const st = statSync61(p);
126530
+ const st = statSync62(p);
126071
126531
  if (st.isDirectory()) {
126072
126532
  return loadFromDir2(p);
126073
126533
  }
@@ -126112,10 +126572,10 @@ function editPersonalAction(name, opts) {
126112
126572
  }
126113
126573
  var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
126114
126574
  function defaultSharedRoot() {
126115
- return join115(homedir60(), ".switchroom", "skills");
126575
+ return join116(homedir60(), ".switchroom", "skills");
126116
126576
  }
126117
126577
  function defaultBundledRoot() {
126118
- return join115(homedir60(), ".switchroom", "skills", "_bundled");
126578
+ return join116(homedir60(), ".switchroom", "skills", "_bundled");
126119
126579
  }
126120
126580
  function resolveCloneSource(source, opts) {
126121
126581
  const m = CLONE_SOURCE_RE.exec(source);
@@ -126125,11 +126585,11 @@ function resolveCloneSource(source, opts) {
126125
126585
  const tier = m[1];
126126
126586
  const slug = m[2];
126127
126587
  const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
126128
- const dir = join115(root, slug);
126588
+ const dir = join116(root, slug);
126129
126589
  if (!existsSync114(dir)) {
126130
126590
  fail5(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
126131
126591
  }
126132
- const st = lstatSync15(dir);
126592
+ const st = lstatSync16(dir);
126133
126593
  if (st.isSymbolicLink()) {
126134
126594
  fail5(`clone source ${JSON.stringify(source)} is a symlink at ${dir}; ` + `point clone at the canonical pool path instead`);
126135
126595
  }
@@ -126140,8 +126600,8 @@ function readSourceFiles(dir) {
126140
126600
  const files = {};
126141
126601
  const skipped = [];
126142
126602
  const walk2 = (sub) => {
126143
- for (const ent of readdirSync42(sub, { withFileTypes: true })) {
126144
- const full = join115(sub, ent.name);
126603
+ for (const ent of readdirSync43(sub, { withFileTypes: true })) {
126604
+ const full = join116(sub, ent.name);
126145
126605
  if (ent.isSymbolicLink()) {
126146
126606
  continue;
126147
126607
  }
@@ -126156,7 +126616,7 @@ function readSourceFiles(dir) {
126156
126616
  continue;
126157
126617
  }
126158
126618
  try {
126159
- const st = lstatSync15(full);
126619
+ const st = lstatSync16(full);
126160
126620
  if (st.size > CLONE_MAX_FILE_BYTES) {
126161
126621
  fail5(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
126162
126622
  }
@@ -126238,7 +126698,7 @@ function removePersonalAction(name, opts) {
126238
126698
  }
126239
126699
  const target = personalSkillDir(agentsRoot, agent, name);
126240
126700
  try {
126241
- const st = lstatSync15(target);
126701
+ const st = lstatSync16(target);
126242
126702
  if (st.isSymbolicLink()) {
126243
126703
  fail5(`refusing to remove symlink at ${target}; investigate manually`);
126244
126704
  }
@@ -126252,7 +126712,7 @@ function removePersonalAction(name, opts) {
126252
126712
  const trashRoot2 = trashDir(agentsRoot, agent);
126253
126713
  mkdirSync66(trashRoot2, { recursive: true, mode: 493 });
126254
126714
  const ts = Date.now();
126255
- const trashTarget = join115(trashRoot2, `${name}-${ts}`);
126715
+ const trashTarget = join116(trashRoot2, `${name}-${ts}`);
126256
126716
  renameSync32(target, trashTarget);
126257
126717
  const now2 = new Date(ts);
126258
126718
  utimesSync(trashTarget, now2, now2);
@@ -126271,27 +126731,27 @@ function listPersonalAction(opts) {
126271
126731
  const agent = resolveAgent(opts);
126272
126732
  const agentsRoot = resolveAgentsRoot(opts);
126273
126733
  sweepTrash(agentsRoot, agent);
126274
- const skillsDir = join115(agentsRoot, agent, ".claude", "skills");
126734
+ const skillsDir = join116(agentsRoot, agent, ".claude", "skills");
126275
126735
  const personal = [];
126276
126736
  if (existsSync114(skillsDir)) {
126277
- for (const ent of readdirSync42(skillsDir, { withFileTypes: true })) {
126737
+ for (const ent of readdirSync43(skillsDir, { withFileTypes: true })) {
126278
126738
  if (!ent.isDirectory())
126279
126739
  continue;
126280
126740
  if (!ent.name.startsWith(PERSONAL_PREFIX))
126281
126741
  continue;
126282
126742
  const skillName = ent.name.slice(PERSONAL_PREFIX.length);
126283
- const skillPath = join115(skillsDir, ent.name);
126743
+ const skillPath = join116(skillsDir, ent.name);
126284
126744
  let fileCount = 0;
126285
126745
  let totalBytes = 0;
126286
126746
  const walk2 = (sub) => {
126287
- for (const e of readdirSync42(sub, { withFileTypes: true })) {
126747
+ for (const e of readdirSync43(sub, { withFileTypes: true })) {
126288
126748
  if (e.isFile()) {
126289
126749
  fileCount += 1;
126290
126750
  try {
126291
- totalBytes += statSync61(join115(sub, e.name)).size;
126751
+ totalBytes += statSync62(join116(sub, e.name)).size;
126292
126752
  } catch {}
126293
126753
  } else if (e.isDirectory()) {
126294
- walk2(join115(sub, e.name));
126754
+ walk2(join116(sub, e.name));
126295
126755
  }
126296
126756
  }
126297
126757
  };
@@ -126330,7 +126790,7 @@ function registerSkillPersonalCommands(program3) {
126330
126790
  // src/cli/self-improve-propose-skill.ts
126331
126791
  import { createConnection as createConnection4 } from "node:net";
126332
126792
  import { homedir as homedir61 } from "node:os";
126333
- import { join as join116 } from "node:path";
126793
+ import { join as join117 } from "node:path";
126334
126794
  import { readFileSync as readFileSync102 } from "node:fs";
126335
126795
 
126336
126796
  // src/self-improve/skill-proposals.ts
@@ -126378,7 +126838,7 @@ var STOPWORDS = new Set([
126378
126838
  // src/cli/self-improve-propose-skill.ts
126379
126839
  var IPC_CONNECT_TIMEOUT_MS = 5000;
126380
126840
  function gatewaySocketPath() {
126381
- return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join116(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join116(homedir61(), ".claude", "channels", "telegram", "gateway.sock"));
126841
+ return process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR ? join117(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join117(homedir61(), ".claude", "channels", "telegram", "gateway.sock"));
126382
126842
  }
126383
126843
  function fail6(msg, code = 1) {
126384
126844
  console.error(msg);
@@ -126458,11 +126918,11 @@ function registerSelfImproveProposeSkillCommand(program3) {
126458
126918
  // src/cli/self-improve-eval-case.ts
126459
126919
  import { createConnection as createConnection5 } from "node:net";
126460
126920
  import { homedir as homedir62 } from "node:os";
126461
- import { join as join120, resolve as resolve62, sep as sep7 } from "node:path";
126921
+ import { join as join121, resolve as resolve62, sep as sep7 } from "node:path";
126462
126922
  import { existsSync as existsSync119, readFileSync as readFileSync107, realpathSync as realpathSync10 } from "node:fs";
126463
126923
 
126464
126924
  // src/self-improve/apply-guard.ts
126465
- import { existsSync as existsSync117, lstatSync as lstatSync16, readFileSync as readFileSync105, statSync as statSync63 } from "node:fs";
126925
+ import { existsSync as existsSync117, lstatSync as lstatSync17, readFileSync as readFileSync105, statSync as statSync64 } from "node:fs";
126466
126926
 
126467
126927
  // src/self-improve/config.ts
126468
126928
  function intEnv(name, def) {
@@ -126483,11 +126943,11 @@ function resolveSelfImproveConfig() {
126483
126943
  }
126484
126944
 
126485
126945
  // src/self-improve/eval-gate.ts
126486
- import { existsSync as existsSync115, readFileSync as readFileSync103, readdirSync as readdirSync43, statSync as statSync62 } from "node:fs";
126487
- import { join as join117 } from "node:path";
126946
+ import { existsSync as existsSync115, readFileSync as readFileSync103, readdirSync as readdirSync44, statSync as statSync63 } from "node:fs";
126947
+ import { join as join118 } from "node:path";
126488
126948
  import { spawnSync as spawnSync26 } from "node:child_process";
126489
126949
  function evalsJsonPath(skillDir) {
126490
- return join117(skillDir, "evals", "evals.json");
126950
+ return join118(skillDir, "evals", "evals.json");
126491
126951
  }
126492
126952
  function skillHasEvals(skillDir) {
126493
126953
  const p = evalsJsonPath(skillDir);
@@ -126501,7 +126961,7 @@ function skillHasEvals(skillDir) {
126501
126961
  }
126502
126962
  }
126503
126963
  function aggregateBenchmark(benchmarkDir, repoRoot, pythonBin = process.env.SWITCHROOM_PYTHON ?? "python3") {
126504
- const script = join117(repoRoot, "skills", "skill-creator", "scripts", "aggregate_benchmark.py");
126964
+ const script = join118(repoRoot, "skills", "skill-creator", "scripts", "aggregate_benchmark.py");
126505
126965
  if (!existsSync115(script)) {
126506
126966
  return { ok: false, error: `aggregate_benchmark.py not found at ${script}` };
126507
126967
  }
@@ -126515,25 +126975,25 @@ function aggregateBenchmark(benchmarkDir, repoRoot, pythonBin = process.env.SWIT
126515
126975
  error: `aggregate_benchmark exited ${r.status}: ${(r.stderr ?? "").trim()}`
126516
126976
  };
126517
126977
  }
126518
- return { ok: true, benchmarkJson: join117(benchmarkDir, "benchmark.json") };
126978
+ return { ok: true, benchmarkJson: join118(benchmarkDir, "benchmark.json") };
126519
126979
  }
126520
126980
  function isBenchmarkDir(dir) {
126521
126981
  try {
126522
- if (!statSync62(dir).isDirectory())
126982
+ if (!statSync63(dir).isDirectory())
126523
126983
  return false;
126524
126984
  } catch {
126525
126985
  return false;
126526
126986
  }
126527
126987
  const hasEvalDirs = (d) => {
126528
126988
  try {
126529
- return readdirSync43(d, { withFileTypes: true }).some((e) => e.isDirectory() && e.name.startsWith("eval-"));
126989
+ return readdirSync44(d, { withFileTypes: true }).some((e) => e.isDirectory() && e.name.startsWith("eval-"));
126530
126990
  } catch {
126531
126991
  return false;
126532
126992
  }
126533
126993
  };
126534
126994
  if (hasEvalDirs(dir))
126535
126995
  return true;
126536
- const runs = join117(dir, "runs");
126996
+ const runs = join118(dir, "runs");
126537
126997
  return existsSync115(runs) && hasEvalDirs(runs);
126538
126998
  }
126539
126999
  function resolveBenchmarkDir(baseDir, explicit) {
@@ -126544,13 +127004,13 @@ function resolveBenchmarkDir(baseDir, explicit) {
126544
127004
  return baseDir;
126545
127005
  let entries;
126546
127006
  try {
126547
- entries = readdirSync43(baseDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join117(baseDir, e.name)).filter(isBenchmarkDir);
127007
+ entries = readdirSync44(baseDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join118(baseDir, e.name)).filter(isBenchmarkDir);
126548
127008
  } catch {
126549
127009
  return null;
126550
127010
  }
126551
127011
  if (entries.length === 0)
126552
127012
  return null;
126553
- entries.sort((a, b) => statSync62(b).mtimeMs - statSync62(a).mtimeMs);
127013
+ entries.sort((a, b) => statSync63(b).mtimeMs - statSync63(a).mtimeMs);
126554
127014
  return entries[0];
126555
127015
  }
126556
127016
  var CANDIDATE_CONFIGS = ["with_skill", "candidate", "new_skill", "after"];
@@ -126617,12 +127077,12 @@ import {
126617
127077
  mkdirSync as mkdirSync67,
126618
127078
  openSync as openSync23,
126619
127079
  readFileSync as readFileSync104,
126620
- readdirSync as readdirSync44,
127080
+ readdirSync as readdirSync45,
126621
127081
  renameSync as renameSync33,
126622
127082
  writeFileSync as writeFileSync50,
126623
127083
  writeSync as writeSync12
126624
127084
  } from "node:fs";
126625
- import { dirname as dirname49, join as join118 } from "node:path";
127085
+ import { dirname as dirname50, join as join119 } from "node:path";
126626
127086
  import { createHash as createHash21 } from "node:crypto";
126627
127087
 
126628
127088
  // src/self-improve/pii-scan.ts
@@ -126773,7 +127233,7 @@ function readEvalsDoc(skillDir) {
126773
127233
  }
126774
127234
  }
126775
127235
  function writeAtomic(path10, content) {
126776
- mkdirSync67(dirname49(path10), { recursive: true });
127236
+ mkdirSync67(dirname50(path10), { recursive: true });
126777
127237
  const tmp = `${path10}.tmp-${process.pid}-${Date.now()}`;
126778
127238
  writeFileSync50(tmp, content, { encoding: "utf-8", mode: 420 });
126779
127239
  renameSync33(tmp, path10);
@@ -126812,7 +127272,7 @@ function appendEvalCase(skillDir, slug, input) {
126812
127272
  }
126813
127273
  var HELD_OUT_FILE = "self-improve-held-out-evals.jsonl";
126814
127274
  function heldOutPath(stateDir) {
126815
- return join118(stateDir, HELD_OUT_FILE);
127275
+ return join119(stateDir, HELD_OUT_FILE);
126816
127276
  }
126817
127277
  function heldOutHasFingerprint(stateDir, slug, fp) {
126818
127278
  const path10 = heldOutPath(stateDir);
@@ -126861,10 +127321,10 @@ function appendHeldOutCase(stateDir, slug, ec, opts = {}) {
126861
127321
  var EVAL_MANIFEST_FILE = "self-improve-eval-manifest.json";
126862
127322
  var EVAL_BASELINE_SUBDIR = "self-improve-eval-baselines";
126863
127323
  function manifestPath(stateDir) {
126864
- return join118(stateDir, EVAL_MANIFEST_FILE);
127324
+ return join119(stateDir, EVAL_MANIFEST_FILE);
126865
127325
  }
126866
127326
  function baselineEvalsPath(stateDir, slug) {
126867
- return join118(stateDir, EVAL_BASELINE_SUBDIR, slug, "evals.json");
127327
+ return join119(stateDir, EVAL_BASELINE_SUBDIR, slug, "evals.json");
126868
127328
  }
126869
127329
  function evalsSha256(skillDir) {
126870
127330
  const p = evalsJsonPath(skillDir);
@@ -126900,7 +127360,7 @@ function recordEvalBaseline(stateDir, slug, skillDir, opts = {}) {
126900
127360
  m[slug] = { sha256: sha, updated_at: new Date(now2()).toISOString(), skill_dir: skillDir };
126901
127361
  writeManifest(stateDir, m);
126902
127362
  const snap = baselineEvalsPath(stateDir, slug);
126903
- mkdirSync67(dirname49(snap), { recursive: true });
127363
+ mkdirSync67(dirname50(snap), { recursive: true });
126904
127364
  copyFileSync15(evalsJsonPath(skillDir), snap);
126905
127365
  }
126906
127366
 
@@ -126908,7 +127368,7 @@ function recordEvalBaseline(stateDir, slug, skillDir, opts = {}) {
126908
127368
  var BENCHMARK_SUBDIR = "self-improve-benchmarks";
126909
127369
  function ownsSkill(skillDir) {
126910
127370
  try {
126911
- const st = lstatSync16(skillDir);
127371
+ const st = lstatSync17(skillDir);
126912
127372
  if (st.isSymbolicLink())
126913
127373
  return false;
126914
127374
  return st.isDirectory();
@@ -126926,10 +127386,10 @@ import {
126926
127386
  readFileSync as readFileSync106,
126927
127387
  writeSync as writeSync13
126928
127388
  } from "node:fs";
126929
- import { join as join119 } from "node:path";
127389
+ import { join as join120 } from "node:path";
126930
127390
  var EVAL_CASE_PROPOSALS_FILE = "eval-case-proposals.jsonl";
126931
127391
  function proposalsPath(stateDir) {
126932
- return join119(stateDir, EVAL_CASE_PROPOSALS_FILE);
127392
+ return join120(stateDir, EVAL_CASE_PROPOSALS_FILE);
126933
127393
  }
126934
127394
  function isRecord2(v) {
126935
127395
  return !!v && typeof v === "object" && typeof v.id === "string" && typeof v.skill_slug === "string" && !!v.case && typeof v.case === "object";
@@ -126964,16 +127424,16 @@ function getEvalCaseProposal(stateDir, id) {
126964
127424
  // src/cli/self-improve-eval-case.ts
126965
127425
  var IPC_CONNECT_TIMEOUT_MS2 = 5000;
126966
127426
  function stateDir() {
126967
- return process.env.TELEGRAM_STATE_DIR ?? join120(homedir62(), ".claude", "channels", "telegram");
127427
+ return process.env.TELEGRAM_STATE_DIR ?? join121(homedir62(), ".claude", "channels", "telegram");
126968
127428
  }
126969
127429
  function gatewaySocketPath2() {
126970
- return process.env.SWITCHROOM_GATEWAY_SOCKET ?? join120(stateDir(), "gateway.sock");
127430
+ return process.env.SWITCHROOM_GATEWAY_SOCKET ?? join121(stateDir(), "gateway.sock");
126971
127431
  }
126972
127432
  function skillsRoot() {
126973
- return join120(homedir62(), ".claude", "skills");
127433
+ return join121(homedir62(), ".claude", "skills");
126974
127434
  }
126975
127435
  function skillDirFor(slug) {
126976
- return join120(skillsRoot(), slug);
127436
+ return join121(skillsRoot(), slug);
126977
127437
  }
126978
127438
  function authorizeApplyTarget(persistedDir, slug) {
126979
127439
  const dir = skillDirFor(slug);
@@ -127171,20 +127631,20 @@ function registerSelfImproveEvalCaseCommands(program3) {
127171
127631
 
127172
127632
  // src/cli/self-improve-bench.ts
127173
127633
  import { homedir as homedir63 } from "node:os";
127174
- import { dirname as dirname50, join as join121 } from "node:path";
127634
+ import { dirname as dirname51, join as join122 } from "node:path";
127175
127635
  init_shipped_assets();
127176
127636
  function fail8(msg, code = 1) {
127177
127637
  console.error(msg);
127178
127638
  process.exit(code);
127179
127639
  }
127180
127640
  function stateDir2() {
127181
- return process.env.TELEGRAM_STATE_DIR ?? join121(homedir63(), ".claude", "channels", "telegram");
127641
+ return process.env.TELEGRAM_STATE_DIR ?? join122(homedir63(), ".claude", "channels", "telegram");
127182
127642
  }
127183
127643
  function skillDirFor2(slug) {
127184
- return join121(homedir63(), ".claude", "skills", slug);
127644
+ return join122(homedir63(), ".claude", "skills", slug);
127185
127645
  }
127186
127646
  function benchmarkBaseDir(slug) {
127187
- return join121(stateDir2(), BENCHMARK_SUBDIR, slug);
127647
+ return join122(stateDir2(), BENCHMARK_SUBDIR, slug);
127188
127648
  }
127189
127649
  function resolveAggregatorRepoRoot() {
127190
127650
  const res = resolveShippedAsset(SKILLS_ASSET, {
@@ -127196,7 +127656,7 @@ function resolveAggregatorRepoRoot() {
127196
127656
  `);
127197
127657
  return null;
127198
127658
  }
127199
- return dirname50(res.path);
127659
+ return dirname51(res.path);
127200
127660
  }
127201
127661
  function registerSelfImproveBenchCommand(program3) {
127202
127662
  const parent = program3.commands.find((c) => c.name() === "self-improve") ?? program3.command("self-improve").description("Agent self-improvement ops");
@@ -127246,9 +127706,9 @@ function registerSelfImproveBenchCommand(program3) {
127246
127706
  init_esm();
127247
127707
  init_helpers();
127248
127708
  var import_yaml26 = __toESM(require_dist(), 1);
127249
- import { existsSync as existsSync120, readdirSync as readdirSync45, readFileSync as readFileSync108, statSync as statSync64 } from "node:fs";
127709
+ import { existsSync as existsSync120, readdirSync as readdirSync46, readFileSync as readFileSync108, statSync as statSync65 } from "node:fs";
127250
127710
  import { homedir as homedir64 } from "node:os";
127251
- import { join as join122, resolve as resolve63 } from "node:path";
127711
+ import { join as join123, resolve as resolve63 } from "node:path";
127252
127712
  var PERSONAL_PREFIX2 = "personal-";
127253
127713
  var BUNDLED_SUBDIR = "_bundled";
127254
127714
  var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
@@ -127262,7 +127722,7 @@ function defaultBundledRoot2() {
127262
127722
  return resolve63(homedir64(), ".switchroom/skills/_bundled");
127263
127723
  }
127264
127724
  function readSkillFrontmatter(skillDir) {
127265
- const mdPath = join122(skillDir, "SKILL.md");
127725
+ const mdPath = join123(skillDir, "SKILL.md");
127266
127726
  if (!existsSync120(mdPath))
127267
127727
  return null;
127268
127728
  let content;
@@ -127295,9 +127755,9 @@ function readSkillFrontmatter(skillDir) {
127295
127755
  return { fm: parsed };
127296
127756
  }
127297
127757
  function statSkillMd(skillDir) {
127298
- const mdPath = join122(skillDir, "SKILL.md");
127758
+ const mdPath = join123(skillDir, "SKILL.md");
127299
127759
  try {
127300
- const st = statSync64(mdPath);
127760
+ const st = statSync65(mdPath);
127301
127761
  return { size: st.size, mtime: st.mtime.toISOString() };
127302
127762
  } catch {
127303
127763
  return null;
@@ -127306,22 +127766,22 @@ function statSkillMd(skillDir) {
127306
127766
  function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
127307
127767
  if (!AGENT_NAME_RE3.test(agent))
127308
127768
  return [];
127309
- const skillsDir = join122(agentsRoot, agent, ".claude/skills");
127769
+ const skillsDir = join123(agentsRoot, agent, ".claude/skills");
127310
127770
  if (!existsSync120(skillsDir))
127311
127771
  return [];
127312
127772
  const out = [];
127313
127773
  let entries;
127314
127774
  try {
127315
- entries = readdirSync45(skillsDir);
127775
+ entries = readdirSync46(skillsDir);
127316
127776
  } catch {
127317
127777
  return [];
127318
127778
  }
127319
127779
  for (const ent of entries) {
127320
127780
  if (!ent.startsWith(PERSONAL_PREFIX2))
127321
127781
  continue;
127322
- const dirPath = join122(skillsDir, ent);
127782
+ const dirPath = join123(skillsDir, ent);
127323
127783
  try {
127324
- if (!statSync64(dirPath).isDirectory())
127784
+ if (!statSync65(dirPath).isDirectory())
127325
127785
  continue;
127326
127786
  } catch {
127327
127787
  continue;
@@ -127350,7 +127810,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
127350
127810
  const out = [];
127351
127811
  let entries;
127352
127812
  try {
127353
- entries = readdirSync45(sharedRoot);
127813
+ entries = readdirSync46(sharedRoot);
127354
127814
  } catch {
127355
127815
  return [];
127356
127816
  }
@@ -127359,9 +127819,9 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
127359
127819
  continue;
127360
127820
  if (ent.startsWith("."))
127361
127821
  continue;
127362
- const dirPath = join122(sharedRoot, ent);
127822
+ const dirPath = join123(sharedRoot, ent);
127363
127823
  try {
127364
- if (!statSync64(dirPath).isDirectory())
127824
+ if (!statSync65(dirPath).isDirectory())
127365
127825
  continue;
127366
127826
  } catch {
127367
127827
  continue;
@@ -127388,16 +127848,16 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
127388
127848
  const out = [];
127389
127849
  let entries;
127390
127850
  try {
127391
- entries = readdirSync45(bundledRoot);
127851
+ entries = readdirSync46(bundledRoot);
127392
127852
  } catch {
127393
127853
  return [];
127394
127854
  }
127395
127855
  for (const ent of entries) {
127396
127856
  if (ent.startsWith("."))
127397
127857
  continue;
127398
- const dirPath = join122(bundledRoot, ent);
127858
+ const dirPath = join123(bundledRoot, ent);
127399
127859
  try {
127400
- if (!statSync64(dirPath).isDirectory())
127860
+ if (!statSync65(dirPath).isDirectory())
127401
127861
  continue;
127402
127862
  } catch {
127403
127863
  continue;
@@ -127544,15 +128004,15 @@ init_operator_uid();
127544
128004
  import {
127545
128005
  existsSync as existsSync122,
127546
128006
  mkdirSync as mkdirSync69,
127547
- readdirSync as readdirSync46,
128007
+ readdirSync as readdirSync47,
127548
128008
  writeFileSync as writeFileSync51,
127549
- statSync as statSync65,
127550
- lstatSync as lstatSync17,
128009
+ statSync as statSync66,
128010
+ lstatSync as lstatSync18,
127551
128011
  realpathSync as realpathSync11,
127552
128012
  copyFileSync as copyFileSync16
127553
128013
  } from "node:fs";
127554
128014
  import { homedir as homedir65 } from "node:os";
127555
- import { join as join123 } from "node:path";
128015
+ import { join as join124 } from "node:path";
127556
128016
  import { spawnSync as spawnSync29 } from "node:child_process";
127557
128017
 
127558
128018
  // src/cli/singleton-stale-cleanup.ts
@@ -127908,10 +128368,10 @@ function resolveHostdHostHome(env2 = process.env, home2 = homedir65()) {
127908
128368
  return resolved;
127909
128369
  }
127910
128370
  function resolveHostdSkillsTarget(hostHome) {
127911
- const skillsPath = join123(hostHome, ".switchroom", "skills");
128371
+ const skillsPath = join124(hostHome, ".switchroom", "skills");
127912
128372
  let st;
127913
128373
  try {
127914
- st = lstatSync17(skillsPath);
128374
+ st = lstatSync18(skillsPath);
127915
128375
  } catch {
127916
128376
  return;
127917
128377
  }
@@ -127932,10 +128392,10 @@ function resolveHostdSkillsTarget(hostHome) {
127932
128392
  return target;
127933
128393
  }
127934
128394
  function hostdDir() {
127935
- return join123(homedir65(), ".switchroom", "hostd");
128395
+ return join124(homedir65(), ".switchroom", "hostd");
127936
128396
  }
127937
128397
  function hostdComposePath() {
127938
- return join123(hostdDir(), "docker-compose.yml");
128398
+ return join124(hostdDir(), "docker-compose.yml");
127939
128399
  }
127940
128400
  function backupExistingCompose() {
127941
128401
  const p = hostdComposePath();
@@ -128062,12 +128522,12 @@ function doStatus() {
128062
128522
  if (existsSync122(dir)) {
128063
128523
  const entries = [];
128064
128524
  try {
128065
- for (const name of readdirSync46(dir)) {
128525
+ for (const name of readdirSync47(dir)) {
128066
128526
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
128067
128527
  continue;
128068
- const sockPath = join123(dir, name, "sock");
128528
+ const sockPath = join124(dir, name, "sock");
128069
128529
  if (existsSync122(sockPath)) {
128070
- const st = statSync65(sockPath);
128530
+ const st = statSync66(sockPath);
128071
128531
  if ((st.mode & 61440) === 49152) {
128072
128532
  entries.push(`${name} \u2192 ${sockPath}`);
128073
128533
  }
@@ -128160,7 +128620,7 @@ init_operator_uid();
128160
128620
  init_compose();
128161
128621
  import { chownSync as chownSync14, existsSync as existsSync123, mkdirSync as mkdirSync70, writeFileSync as writeFileSync52, copyFileSync as copyFileSync17 } from "node:fs";
128162
128622
  import { homedir as homedir66 } from "node:os";
128163
- import { join as join124 } from "node:path";
128623
+ import { join as join125 } from "node:path";
128164
128624
  import { spawnSync as spawnSync30 } from "node:child_process";
128165
128625
  function resolveWebImageTag(explicitTag, release) {
128166
128626
  if (explicitTag)
@@ -128262,10 +128722,10 @@ services:
128262
128722
  `;
128263
128723
  }
128264
128724
  function webdDir() {
128265
- return join124(homedir66(), ".switchroom", "web");
128725
+ return join125(homedir66(), ".switchroom", "web");
128266
128726
  }
128267
128727
  function webdComposePath() {
128268
- return join124(webdDir(), "docker-compose.yml");
128728
+ return join125(webdDir(), "docker-compose.yml");
128269
128729
  }
128270
128730
  function backupExistingCompose2() {
128271
128731
  const p = webdComposePath();
@@ -128431,9 +128891,9 @@ function registerWebdCommand(program3) {
128431
128891
  // src/cli/host-repair.ts
128432
128892
  init_source();
128433
128893
  import { homedir as homedir67 } from "node:os";
128434
- import { join as join125 } from "node:path";
128894
+ import { join as join126 } from "node:path";
128435
128895
  var ARTIFACT_ALLOWLIST = {
128436
- dockerComposePluginDir: (home2) => join125(home2, ".docker", "cli-plugins", "docker-compose"),
128896
+ dockerComposePluginDir: (home2) => join126(home2, ".docker", "cli-plugins", "docker-compose"),
128437
128897
  stateSentinel: "/state"
128438
128898
  };
128439
128899
  function isStateBogusAutoDir(probe2) {
@@ -128587,18 +129047,18 @@ function applyMountRepairs(items, deps) {
128587
129047
  function registerHostCommand(program3) {
128588
129048
  const host = program3.command("host").description("Host-level maintenance operations for switchroom");
128589
129049
  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) => {
128590
- const { rmdirSync: rmdirSync2, rmSync: rmSync22, lstatSync: lstatSync18, readdirSync: readdirSync47 } = await import("node:fs");
129050
+ const { rmdirSync: rmdirSync2, rmSync: rmSync22, lstatSync: lstatSync19, readdirSync: readdirSync48 } = await import("node:fs");
128591
129051
  const probe2 = {
128592
129052
  lstat(path10) {
128593
129053
  try {
128594
- return lstatSync18(path10);
129054
+ return lstatSync19(path10);
128595
129055
  } catch {
128596
129056
  return null;
128597
129057
  }
128598
129058
  },
128599
129059
  readdir(path10) {
128600
129060
  try {
128601
- return readdirSync47(path10);
129061
+ return readdirSync48(path10);
128602
129062
  } catch {
128603
129063
  return null;
128604
129064
  }
@@ -128887,9 +129347,9 @@ function printDeepDiveBrief(targets) {
128887
129347
 
128888
129348
  // src/cli/hindsight-watch.ts
128889
129349
  init_source();
128890
- import { existsSync as existsSync128, readdirSync as readdirSync51 } from "node:fs";
129350
+ import { existsSync as existsSync128, readdirSync as readdirSync52 } from "node:fs";
128891
129351
  import { homedir as homedir70 } from "node:os";
128892
- import { join as join128, resolve as resolve66 } from "node:path";
129352
+ import { join as join129, resolve as resolve66 } from "node:path";
128893
129353
 
128894
129354
  // src/host-control/config-degraded.ts
128895
129355
  import { connect as connect3 } from "node:net";
@@ -128958,7 +129418,7 @@ init_install_cron();
128958
129418
 
128959
129419
  // src/hindsight-watch/probe.ts
128960
129420
  import { spawnSync as spawnSync32 } from "node:child_process";
128961
- import { readFileSync as readFileSync113, readdirSync as readdirSync50, statSync as statSync67 } from "node:fs";
129421
+ import { readFileSync as readFileSync113, readdirSync as readdirSync51, statSync as statSync68 } from "node:fs";
128962
129422
  import { homedir as homedir69 } from "node:os";
128963
129423
  import { resolve as resolve65 } from "node:path";
128964
129424
 
@@ -129086,13 +129546,13 @@ function readLlmSignals(series) {
129086
129546
  }
129087
129547
 
129088
129548
  // src/hindsight-watch/recall-log.ts
129089
- import { closeSync as closeSync25, existsSync as existsSync127, openSync as openSync25, readdirSync as readdirSync49, readSync as readSync6, statSync as statSync66 } from "node:fs";
129090
- import { join as join127 } from "node:path";
129549
+ import { closeSync as closeSync25, existsSync as existsSync127, openSync as openSync25, readdirSync as readdirSync50, readSync as readSync6, statSync as statSync67 } from "node:fs";
129550
+ import { join as join128 } from "node:path";
129091
129551
  var RECALL_WINDOW_ROWS = 200;
129092
129552
  var RECALL_MAX_TAIL_BYTES = 1024 * 1024;
129093
129553
  var RECALL_MAX_ROW_AGE_MS = 24 * 60 * 60 * 1000;
129094
129554
  function recallLogPath2(agentDir) {
129095
- return join127(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
129555
+ return join128(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
129096
129556
  }
129097
129557
  function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
129098
129558
  if (!existsSync127(path10))
@@ -129101,7 +129561,7 @@ function readRecallLogTail2(path10, windowRows = RECALL_WINDOW_ROWS) {
129101
129561
  let truncatedHead = false;
129102
129562
  let fd;
129103
129563
  try {
129104
- const size = statSync66(path10).size;
129564
+ const size = statSync67(path10).size;
129105
129565
  const start = Math.max(0, size - RECALL_MAX_TAIL_BYTES);
129106
129566
  truncatedHead = start > 0;
129107
129567
  const length = size - start;
@@ -129236,11 +129696,11 @@ function summarizeRecallRows2(rows) {
129236
129696
  return out;
129237
129697
  }
129238
129698
  function probeRecallLogs(agentsDir, now2, windowRows = RECALL_WINDOW_ROWS, maxAgeMs = RECALL_MAX_ROW_AGE_MS) {
129239
- const names = readdirSync49(agentsDir);
129699
+ const names = readdirSync50(agentsDir);
129240
129700
  const all = [];
129241
129701
  let agents = 0;
129242
129702
  for (const name of names) {
129243
- const rows = withinWindow(readRecallLogTail2(recallLogPath2(join127(agentsDir, name)), windowRows), now2, maxAgeMs);
129703
+ const rows = withinWindow(readRecallLogTail2(recallLogPath2(join128(agentsDir, name)), windowRows), now2, maxAgeMs);
129244
129704
  if (rows.length > 0)
129245
129705
  agents++;
129246
129706
  all.push(...rows);
@@ -129333,7 +129793,7 @@ function readDropCount(hindsightDir) {
129333
129793
  const path10 = resolve65(hindsightDir, "pending-drops.json");
129334
129794
  let parsed;
129335
129795
  try {
129336
- if (statSync67(path10).size > DROPS_LEDGER_MAX_BYTES)
129796
+ if (statSync68(path10).size > DROPS_LEDGER_MAX_BYTES)
129337
129797
  return 0;
129338
129798
  parsed = JSON.parse(readFileSync113(path10, "utf8"));
129339
129799
  } catch {
@@ -129350,7 +129810,7 @@ function resolveAgentsDir2(explicit) {
129350
129810
  function probeSpool(agentsDir = resolveAgentsDir2()) {
129351
129811
  let names;
129352
129812
  try {
129353
- names = readdirSync50(agentsDir);
129813
+ names = readdirSync51(agentsDir);
129354
129814
  } catch (e) {
129355
129815
  throw new ProbeError(`reading ${agentsDir}: ${e.message}`, "spool");
129356
129816
  }
@@ -129364,7 +129824,7 @@ function probeSpool(agentsDir = resolveAgentsDir2()) {
129364
129824
  const hindsightDir = resolve65(agentsDir, name, "home", ".hindsight");
129365
129825
  let entries = null;
129366
129826
  try {
129367
- entries = readdirSync50(resolve65(hindsightDir, "pending-retains"));
129827
+ entries = readdirSync51(resolve65(hindsightDir, "pending-retains"));
129368
129828
  } catch {}
129369
129829
  if (entries !== null) {
129370
129830
  agents++;
@@ -129376,19 +129836,19 @@ function probeSpool(agentsDir = resolveAgentsDir2()) {
129376
129836
  }
129377
129837
  }
129378
129838
  try {
129379
- for (const f of readdirSync50(resolve65(hindsightDir, "pending-evicted"))) {
129839
+ for (const f of readdirSync51(resolve65(hindsightDir, "pending-evicted"))) {
129380
129840
  if (f.endsWith(".json"))
129381
129841
  evicted++;
129382
129842
  }
129383
129843
  } catch {}
129384
129844
  try {
129385
- for (const f of readdirSync50(resolve65(hindsightDir, "pending-dead"))) {
129845
+ for (const f of readdirSync51(resolve65(hindsightDir, "pending-dead"))) {
129386
129846
  if (f.endsWith(".dead"))
129387
129847
  dead++;
129388
129848
  }
129389
129849
  } catch {}
129390
129850
  try {
129391
- for (const f of readdirSync50(resolve65(hindsightDir, "pending-duplicate"))) {
129851
+ for (const f of readdirSync51(resolve65(hindsightDir, "pending-duplicate"))) {
129392
129852
  if (f.endsWith(".json"))
129393
129853
  duplicates++;
129394
129854
  }
@@ -130298,7 +130758,7 @@ function registerHindsightWatchCommand(program3) {
130298
130758
  process.exitCode = runInstallCron(opts.cronUser);
130299
130759
  return;
130300
130760
  }
130301
- const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ?? join128(process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir70(), ".switchroom", "agents");
130761
+ const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ?? join129(process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir70(), ".switchroom", "agents");
130302
130762
  const statePath = opts.state ?? defaultStatePath2();
130303
130763
  const log = (m) => {
130304
130764
  process.stderr.write(`${m}
@@ -130412,7 +130872,7 @@ function orderRelayCandidates(candidates, preferred = PREFERRED_RELAY_AGENT) {
130412
130872
  }
130413
130873
  function collectRelayCandidates(agentsDir) {
130414
130874
  const candidates = [];
130415
- for (const name of readdirSync51(agentsDir).sort()) {
130875
+ for (const name of readdirSync52(agentsDir).sort()) {
130416
130876
  const sock = resolve66(agentsDir, name, "telegram", "gateway.sock");
130417
130877
  if (existsSync128(sock))
130418
130878
  candidates.push({ agent: name, sock });
@@ -130437,7 +130897,7 @@ async function notifyOperator(agentsDir, text, log) {
130437
130897
  // src/cli/hindsight-bench.ts
130438
130898
  init_source();
130439
130899
  import { mkdirSync as mkdirSync72, readFileSync as readFileSync114, writeFileSync as writeFileSync54 } from "node:fs";
130440
- import { dirname as dirname53, resolve as resolve67 } from "node:path";
130900
+ import { dirname as dirname54, resolve as resolve67 } from "node:path";
130441
130901
 
130442
130902
  // src/hindsight-bench/anonymise.ts
130443
130903
  function pseudonym(n, width) {
@@ -131565,7 +132025,7 @@ function readResult(path10) {
131565
132025
  }
131566
132026
  function writeOut(path10, body) {
131567
132027
  const abs = resolve67(path10);
131568
- mkdirSync72(dirname53(abs), { recursive: true });
132028
+ mkdirSync72(dirname54(abs), { recursive: true });
131569
132029
  writeFileSync54(abs, body);
131570
132030
  process.stderr.write(`${source_default.green("\u2713")} wrote ${abs}
131571
132031
  `);
@@ -131770,13 +132230,13 @@ async function runMeasureMode(opts) {
131770
132230
 
131771
132231
  // src/cli/openrouter-watch.ts
131772
132232
  init_source();
131773
- import { existsSync as existsSync130, readdirSync as readdirSync52 } from "node:fs";
132233
+ import { existsSync as existsSync130, readdirSync as readdirSync53 } from "node:fs";
131774
132234
  import { homedir as homedir72 } from "node:os";
131775
- import { join as join129, resolve as resolve69 } from "node:path";
132235
+ import { join as join130, resolve as resolve69 } from "node:path";
131776
132236
 
131777
132237
  // src/openrouter/install-cron.ts
131778
132238
  import { existsSync as existsSync129, mkdirSync as mkdirSync73, readFileSync as readFileSync115, renameSync as renameSync34, writeFileSync as writeFileSync55 } from "node:fs";
131779
- import { dirname as dirname54 } from "node:path";
132239
+ import { dirname as dirname55 } from "node:path";
131780
132240
  var CRON_PATH3 = "/etc/cron.d/openrouter-watch";
131781
132241
  var CRON_SCHEDULE2 = "17 * * * *";
131782
132242
  var CRON_LOG_PATH3 = "/var/log/openrouter-watch.log";
@@ -131800,7 +132260,7 @@ function installCron3(opts) {
131800
132260
  }
131801
132261
  } catch {}
131802
132262
  }
131803
- mkdirSync73(dirname54(path10), { recursive: true });
132263
+ mkdirSync73(dirname55(path10), { recursive: true });
131804
132264
  const tmp = `${path10}.${process.pid}.tmp`;
131805
132265
  writeFileSync55(tmp, content, { mode: 420 });
131806
132266
  renameSync34(tmp, path10);
@@ -131809,7 +132269,7 @@ function installCron3(opts) {
131809
132269
  // src/openrouter/state.ts
131810
132270
  import { mkdirSync as mkdirSync74, readFileSync as readFileSync116, renameSync as renameSync35, writeFileSync as writeFileSync56 } from "node:fs";
131811
132271
  import { homedir as homedir71 } from "node:os";
131812
- import { dirname as dirname55, resolve as resolve68 } from "node:path";
132272
+ import { dirname as dirname56, resolve as resolve68 } from "node:path";
131813
132273
  function defaultStatePath3(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir71()) {
131814
132274
  return resolve68(home2, ".switchroom", "openrouter-watch", "state.json");
131815
132275
  }
@@ -131836,7 +132296,7 @@ function loadState2(path10) {
131836
132296
  return out;
131837
132297
  }
131838
132298
  function saveState2(path10, state) {
131839
- mkdirSync74(dirname55(path10), { recursive: true, mode: 448 });
132299
+ mkdirSync74(dirname56(path10), { recursive: true, mode: 448 });
131840
132300
  const tmp = `${path10}.${process.pid}.tmp`;
131841
132301
  const payload = { v: 1, ...state };
131842
132302
  writeFileSync56(tmp, JSON.stringify(payload, null, 2) + `
@@ -131927,7 +132387,7 @@ function registerOpenRouterWatchCommand(program3) {
131927
132387
  process.exitCode = runInstallCron2(opts.cronUser);
131928
132388
  return;
131929
132389
  }
131930
- const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ?? join129(process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir72(), ".switchroom", "agents");
132390
+ const agentsDir = opts.agentsDir ?? process.env.SWITCHROOM_AGENTS_DIR ?? join130(process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir72(), ".switchroom", "agents");
131931
132391
  const statePath = opts.state ?? defaultStatePath3();
131932
132392
  const log = (m) => {
131933
132393
  process.stderr.write(`${m}
@@ -132024,7 +132484,7 @@ function runInstallCron2(cronUser) {
132024
132484
  async function notifyOperator2(agentsDir, text, log) {
132025
132485
  const candidates = [];
132026
132486
  try {
132027
- for (const name of readdirSync52(agentsDir).sort()) {
132487
+ for (const name of readdirSync53(agentsDir).sort()) {
132028
132488
  const sock = resolve69(agentsDir, name, "telegram", "gateway.sock");
132029
132489
  if (existsSync130(sock))
132030
132490
  candidates.push({ agent: name, sock });
@@ -132047,7 +132507,7 @@ init_sync();
132047
132507
  init_install_cron2();
132048
132508
  init_flock();
132049
132509
  import { homedir as homedir73 } from "node:os";
132050
- import { join as join130 } from "node:path";
132510
+ import { join as join131 } from "node:path";
132051
132511
  function switchroomHome() {
132052
132512
  return process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir73();
132053
132513
  }
@@ -132059,9 +132519,9 @@ function resolveSyncOptions(configPath, opts) {
132059
132519
  cfg = loaded.config_repo;
132060
132520
  } catch {}
132061
132521
  const home2 = switchroomHome();
132062
- const repoPath = opts.path ? resolvePath(opts.path) : cfg.path ? resolvePath(cfg.path) : join130(home2, ".switchroom-config");
132063
- const livePath = process.env.SWITCHROOM_CONFIG ?? join130(home2, ".switchroom", "switchroom.yaml");
132064
- const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? join130(home2, ".switchroom", "agents");
132522
+ const repoPath = opts.path ? resolvePath(opts.path) : cfg.path ? resolvePath(cfg.path) : join131(home2, ".switchroom-config");
132523
+ const livePath = process.env.SWITCHROOM_CONFIG ?? join131(home2, ".switchroom", "switchroom.yaml");
132524
+ const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? join131(home2, ".switchroom", "agents");
132065
132525
  const push = opts.push === false ? false : cfg.push ?? true;
132066
132526
  const remote = opts.remote ?? cfg.remote ?? "origin";
132067
132527
  const requirePrivate = opts.requirePrivate ?? cfg.require_private ?? true;
@@ -132245,6 +132705,7 @@ registerDoctorCommand(program3);
132245
132705
  registerUpdateCommand(program3);
132246
132706
  registerRolloutCommand(program3);
132247
132707
  registerRollbackCommand(program3);
132708
+ registerHostCliUpgradeCommand(program3);
132248
132709
  registerRestartCommand(program3);
132249
132710
  registerVersionCommand(program3);
132250
132711
  registerVersionsCommand(program3);