switchroom 0.13.64 → 0.14.0

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.
@@ -29685,8 +29685,201 @@ var init_doctor_drive = __esm(() => {
29685
29685
  init_doctor_secret_access();
29686
29686
  });
29687
29687
 
29688
+ // src/cli/doctor-webkite.ts
29689
+ import {
29690
+ existsSync as realExistsSync2,
29691
+ readFileSync as realReadFileSync2,
29692
+ readdirSync as realReaddirSync,
29693
+ openSync as realOpenSync,
29694
+ readSync as realReadSync,
29695
+ closeSync as realCloseSync
29696
+ } from "node:fs";
29697
+ import { join as join45 } from "node:path";
29698
+ import { homedir as homedir25 } from "node:os";
29699
+ function defaultReadHead(p, n) {
29700
+ let fd;
29701
+ try {
29702
+ fd = realOpenSync(p, "r");
29703
+ const buf = Buffer.alloc(n);
29704
+ const read = realReadSync(fd, buf, 0, n, 0);
29705
+ return buf.subarray(0, read);
29706
+ } catch {
29707
+ return null;
29708
+ } finally {
29709
+ if (fd !== undefined) {
29710
+ try {
29711
+ realCloseSync(fd);
29712
+ } catch {}
29713
+ }
29714
+ }
29715
+ }
29716
+ function resolveDeps2(config, deps) {
29717
+ let agentsDir = deps.agentsDir;
29718
+ if (agentsDir === undefined) {
29719
+ try {
29720
+ agentsDir = resolveAgentsDir(config);
29721
+ } catch {
29722
+ agentsDir = undefined;
29723
+ }
29724
+ }
29725
+ return {
29726
+ homeDir: deps.homeDir ?? homedir25(),
29727
+ agentsDir,
29728
+ existsSync: deps.existsSync ?? ((p) => realExistsSync2(p)),
29729
+ readFileSync: deps.readFileSync ?? ((p) => realReadFileSync2(p, "utf-8")),
29730
+ readdirSync: deps.readdirSync ?? ((p) => realReaddirSync(p)),
29731
+ readHead: deps.readHead ?? defaultReadHead
29732
+ };
29733
+ }
29734
+ function classifyArch(d, path4) {
29735
+ const head = d.readHead(path4, 20);
29736
+ if (head === null || head.length < 20)
29737
+ return "unreadable";
29738
+ if (!(head[0] === 127 && head[1] === 69 && head[2] === 76 && head[3] === 70)) {
29739
+ return "not-elf";
29740
+ }
29741
+ const eMachine = head[18] | head[19] << 8;
29742
+ return eMachine === 62 ? "elf-x64" : "elf-other-arch";
29743
+ }
29744
+ function webkiteEnabled(config, agent) {
29745
+ const mcp = config.agents?.[agent]?.mcp_servers;
29746
+ return (mcp ?? {})["webkite"] !== false;
29747
+ }
29748
+ function runWebkiteChecks(config, deps = {}) {
29749
+ const agents = Object.keys(config.agents ?? {});
29750
+ const enabledAgents = agents.filter((a) => webkiteEnabled(config, a));
29751
+ if (enabledAgents.length === 0)
29752
+ return [];
29753
+ const d = resolveDeps2(config, deps);
29754
+ const results = [];
29755
+ const binPath = join45(d.homeDir, ".switchroom", "bin", "webkite");
29756
+ if (!d.existsSync(binPath)) {
29757
+ results.push({
29758
+ name: "webkite: binary",
29759
+ status: "warn",
29760
+ detail: `not staged at ${binPath} \u2014 agents run in degraded "no-webkite" mode (native WebFetch kept by the fail-safe gate)`,
29761
+ fix: "Stage the private-beta binary: cp <webkite> ~/.switchroom/bin/webkite && chmod +x; then `switchroom update`."
29762
+ });
29763
+ } else {
29764
+ const arch = classifyArch(d, binPath);
29765
+ if (arch === "elf-x64") {
29766
+ results.push({ name: "webkite: binary", status: "ok", detail: `staged, linux x86-64 ELF (${binPath})` });
29767
+ } else if (arch === "unreadable") {
29768
+ results.push({ name: "webkite: binary", status: "skip", detail: `present but unreadable by the operator \u2014 re-run doctor without sudo` });
29769
+ } else if (arch === "not-elf") {
29770
+ results.push({
29771
+ name: "webkite: binary",
29772
+ status: "fail",
29773
+ detail: `${binPath} is not a Linux ELF binary (likely a macOS/other-OS build) \u2014 it cannot run in the agent container`,
29774
+ fix: "Replace with a linux x86-64 build (static musl preferred). See the webkite release artifacts."
29775
+ });
29776
+ } else {
29777
+ results.push({
29778
+ name: "webkite: binary",
29779
+ status: "fail",
29780
+ detail: `${binPath} is an ELF binary for the wrong CPU arch (not x86-64) \u2014 it cannot run in the agent container`,
29781
+ fix: "Replace with a linux x86-64 (amd64) build."
29782
+ });
29783
+ }
29784
+ }
29785
+ const cloakDir = join45(d.homeDir, ".cloakbrowser");
29786
+ let chromeFound = false;
29787
+ if (d.existsSync(cloakDir)) {
29788
+ try {
29789
+ for (const entry of d.readdirSync(cloakDir)) {
29790
+ if (entry.startsWith("chromium-") && d.existsSync(join45(cloakDir, entry, "chrome"))) {
29791
+ chromeFound = true;
29792
+ break;
29793
+ }
29794
+ }
29795
+ } catch {}
29796
+ }
29797
+ if (chromeFound) {
29798
+ results.push({ name: "webkite: cloakbrowser", status: "ok", detail: "stealth Chromium present (local render available)" });
29799
+ } else {
29800
+ results.push({
29801
+ name: "webkite: cloakbrowser",
29802
+ status: "warn",
29803
+ detail: "no local stealth Chromium at ~/.cloakbrowser/chromium-*/chrome \u2014 webkite can only cloud-render (Cloudflare/Firecrawl) and will fail bot-gated sites with no local fallback",
29804
+ fix: "Install once on the host: XDG_DATA_HOME=~/.switchroom/webkite-share webkite setup --yes cloakbrowser"
29805
+ });
29806
+ }
29807
+ if (d.agentsDir === undefined) {
29808
+ results.push({
29809
+ name: "webkite: scaffold",
29810
+ status: "warn",
29811
+ detail: "agents_dir unresolved (no switchroom.agents_dir) \u2014 cannot verify per-agent webkite wiring"
29812
+ });
29813
+ return results;
29814
+ }
29815
+ for (const agent of enabledAgents) {
29816
+ const agentDir = join45(d.agentsDir, agent);
29817
+ const settingsPath = join45(agentDir, ".claude", "settings.json");
29818
+ const mcpPath = join45(agentDir, ".mcp.json");
29819
+ if (!d.existsSync(settingsPath) && !d.existsSync(mcpPath)) {
29820
+ continue;
29821
+ }
29822
+ let settings = null;
29823
+ let mcp = null;
29824
+ let unreadable = false;
29825
+ for (const [path4, set] of [
29826
+ [settingsPath, (v) => {
29827
+ settings = v;
29828
+ }],
29829
+ [mcpPath, (v) => {
29830
+ mcp = v;
29831
+ }]
29832
+ ]) {
29833
+ if (!d.existsSync(path4))
29834
+ continue;
29835
+ try {
29836
+ set(JSON.parse(d.readFileSync(path4)));
29837
+ } catch (err) {
29838
+ const code = err?.code;
29839
+ if (code === "EACCES" || code === "EPERM") {
29840
+ unreadable = true;
29841
+ }
29842
+ }
29843
+ }
29844
+ if (unreadable) {
29845
+ results.push({
29846
+ name: `webkite: ${agent} scaffold`,
29847
+ status: "skip",
29848
+ detail: "scaffold files are agent-UID-owned (0600) \u2014 re-run doctor without sudo to read them"
29849
+ });
29850
+ continue;
29851
+ }
29852
+ const mcpHasWebkite = !!(mcp && mcp.mcpServers?.["webkite"]);
29853
+ const allow = settings?.permissions?.allow ?? [];
29854
+ const deny = settings?.permissions?.deny ?? [];
29855
+ const allowOk = allow.includes("mcp__webkite__*");
29856
+ const denyOk = deny.includes("WebFetch");
29857
+ const problems = [];
29858
+ if (!mcpHasWebkite)
29859
+ problems.push(".mcp.json missing the webkite entry");
29860
+ if (!allowOk)
29861
+ problems.push("permissions.allow missing mcp__webkite__* (first fetch will prompt)");
29862
+ if (!denyOk)
29863
+ problems.push("permissions.deny missing WebFetch (native fetch not disabled)");
29864
+ if (problems.length === 0) {
29865
+ results.push({ name: `webkite: ${agent} scaffold`, status: "ok", detail: "wired + pre-approved + WebFetch denied" });
29866
+ } else {
29867
+ results.push({
29868
+ name: `webkite: ${agent} scaffold`,
29869
+ status: "warn",
29870
+ detail: problems.join("; "),
29871
+ fix: "Run `switchroom apply` (or `switchroom agent restart <agent>`) to re-scaffold the webkite wiring."
29872
+ });
29873
+ }
29874
+ }
29875
+ return results;
29876
+ }
29877
+ var init_doctor_webkite = __esm(() => {
29878
+ init_loader();
29879
+ });
29880
+
29688
29881
  // src/cli/doctor-scaffold-wiring.ts
29689
- import { join as join45, resolve as resolve30 } from "node:path";
29882
+ import { join as join46, resolve as resolve30 } from "node:path";
29690
29883
  function readJson2(d, path4) {
29691
29884
  if (!d.existsSync(path4))
29692
29885
  return { kind: "absent" };
@@ -29729,8 +29922,8 @@ function checkIntegrationScaffoldWiring(args) {
29729
29922
  });
29730
29923
  continue;
29731
29924
  }
29732
- const mcpPath = join45(agentDir, ".mcp.json");
29733
- const claudeJsonPath = join45(agentDir, ".claude", ".claude.json");
29925
+ const mcpPath = join46(agentDir, ".mcp.json");
29926
+ const claudeJsonPath = join46(agentDir, ".claude", ".claude.json");
29734
29927
  const mcpRead = readJson2(deps, mcpPath);
29735
29928
  const trustRead = readJson2(deps, claudeJsonPath);
29736
29929
  if (mcpRead.kind === "unreadable" || trustRead.kind === "unreadable") {
@@ -29791,17 +29984,17 @@ var init_doctor_scaffold_wiring = () => {};
29791
29984
 
29792
29985
  // src/cli/doctor-microsoft.ts
29793
29986
  import {
29794
- existsSync as realExistsSync2,
29795
- readFileSync as realReadFileSync2
29987
+ existsSync as realExistsSync3,
29988
+ readFileSync as realReadFileSync3
29796
29989
  } from "node:fs";
29797
- import { join as join46 } from "node:path";
29798
- import { homedir as homedir25 } from "node:os";
29799
- function resolveDeps2(deps) {
29800
- const home2 = deps.homeDir?.() ?? homedir25();
29990
+ import { join as join47 } from "node:path";
29991
+ import { homedir as homedir26 } from "node:os";
29992
+ function resolveDeps3(deps) {
29993
+ const home2 = deps.homeDir?.() ?? homedir26();
29801
29994
  return {
29802
- existsSync: deps.existsSync ?? realExistsSync2,
29803
- readFileSync: deps.readFileSync ?? realReadFileSync2,
29804
- agentsDir: join46(home2, ".switchroom", "agents"),
29995
+ existsSync: deps.existsSync ?? realExistsSync3,
29996
+ readFileSync: deps.readFileSync ?? realReadFileSync3,
29997
+ agentsDir: join47(home2, ".switchroom", "agents"),
29805
29998
  now: deps.now ?? Date.now
29806
29999
  };
29807
30000
  }
@@ -29895,7 +30088,7 @@ function checkOAuthClient2(config, anyAgentEnabled) {
29895
30088
  ];
29896
30089
  }
29897
30090
  function readHeartbeat(d, agentName) {
29898
- const path4 = join46(d.agentsDir, agentName, "m365-launcher.heartbeat.json");
30091
+ const path4 = join47(d.agentsDir, agentName, "m365-launcher.heartbeat.json");
29899
30092
  if (!d.existsSync(path4)) {
29900
30093
  return { error: "heartbeat file missing \u2014 launcher has not yet started" };
29901
30094
  }
@@ -29964,7 +30157,7 @@ function runMicrosoftChecks(config, deps = {}) {
29964
30157
  if (!accountsConfigured && !anyAgentAccount && !config.microsoft_workspace) {
29965
30158
  return [];
29966
30159
  }
29967
- const d = resolveDeps2(deps);
30160
+ const d = resolveDeps3(deps);
29968
30161
  const results = [];
29969
30162
  results.push(...checkConfigMatrix2(config));
29970
30163
  const msAgents = computeMicrosoftEnabledAgents(config);
@@ -29988,19 +30181,19 @@ var init_doctor_microsoft = __esm(() => {
29988
30181
 
29989
30182
  // src/cli/doctor-notion.ts
29990
30183
  import {
29991
- existsSync as realExistsSync3,
29992
- readFileSync as realReadFileSync3,
30184
+ existsSync as realExistsSync4,
30185
+ readFileSync as realReadFileSync4,
29993
30186
  statSync as realStatSync
29994
30187
  } from "node:fs";
29995
- import { join as join47 } from "node:path";
29996
- import { homedir as homedir26 } from "node:os";
29997
- function resolveDeps3(deps) {
29998
- const home2 = deps.homeDir?.() ?? homedir26();
30188
+ import { join as join48 } from "node:path";
30189
+ import { homedir as homedir27 } from "node:os";
30190
+ function resolveDeps4(deps) {
30191
+ const home2 = deps.homeDir?.() ?? homedir27();
29999
30192
  return {
30000
- existsSync: deps.existsSync ?? realExistsSync3,
30001
- readFileSync: deps.readFileSync ?? realReadFileSync3,
30193
+ existsSync: deps.existsSync ?? realExistsSync4,
30194
+ readFileSync: deps.readFileSync ?? realReadFileSync4,
30002
30195
  statSync: deps.statSync ?? realStatSync,
30003
- agentsDir: join47(home2, ".switchroom", "agents"),
30196
+ agentsDir: join48(home2, ".switchroom", "agents"),
30004
30197
  now: deps.now ?? Date.now,
30005
30198
  vaultAclReader: deps.vaultAclReader ?? (async () => ({ kind: "unreachable", msg: "no default reader wired" }))
30006
30199
  };
@@ -30125,7 +30318,7 @@ function checkLauncherHeartbeat2(notionAgents, d) {
30125
30318
  return [];
30126
30319
  const results = [];
30127
30320
  for (const name of notionAgents) {
30128
- const heartbeatPath = join47(d.agentsDir, name, "notion-launcher.heartbeat.json");
30321
+ const heartbeatPath = join48(d.agentsDir, name, "notion-launcher.heartbeat.json");
30129
30322
  if (!d.existsSync(heartbeatPath)) {
30130
30323
  results.push({
30131
30324
  name: `notion:launcher-heartbeat:${name}`,
@@ -30169,7 +30362,7 @@ async function runNotionChecks(config, deps = {}) {
30169
30362
  if (notionAgents.length === 0 && !config.notion_workspace) {
30170
30363
  return [];
30171
30364
  }
30172
- const d = resolveDeps3(deps);
30365
+ const d = resolveDeps4(deps);
30173
30366
  const results = [];
30174
30367
  results.push(...checkConfigMatrix3(config));
30175
30368
  if (notionAgents.length > 0 && config.notion_workspace) {
@@ -30196,16 +30389,16 @@ var init_doctor_notion = __esm(() => {
30196
30389
 
30197
30390
  // src/cli/doctor-credentials-migration.ts
30198
30391
  import {
30199
- existsSync as realExistsSync4,
30200
- readdirSync as realReaddirSync,
30392
+ existsSync as realExistsSync5,
30393
+ readdirSync as realReaddirSync2,
30201
30394
  statSync as realStatSync2
30202
30395
  } from "node:fs";
30203
- import { homedir as homedir27 } from "node:os";
30204
- import { join as join48 } from "node:path";
30396
+ import { homedir as homedir28 } from "node:os";
30397
+ import { join as join49 } from "node:path";
30205
30398
  function runCredentialsMigrationChecks(config, deps = {}) {
30206
- const credDir = deps.credentialsDir ?? join48(homedir27(), ".switchroom", "credentials");
30207
- const existsSync49 = deps.existsSync ?? ((p) => realExistsSync4(p));
30208
- const readdirSync19 = deps.readdirSync ?? ((p) => realReaddirSync(p));
30399
+ const credDir = deps.credentialsDir ?? join49(homedir28(), ".switchroom", "credentials");
30400
+ const existsSync49 = deps.existsSync ?? ((p) => realExistsSync5(p));
30401
+ const readdirSync19 = deps.readdirSync ?? ((p) => realReaddirSync2(p));
30209
30402
  const isDirectory = deps.isDirectory ?? ((p) => {
30210
30403
  try {
30211
30404
  return realStatSync2(p).isDirectory();
@@ -30231,7 +30424,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
30231
30424
  const flat = [];
30232
30425
  const perAgentDirs = [];
30233
30426
  for (const e of entries) {
30234
- const full = join48(credDir, e);
30427
+ const full = join49(credDir, e);
30235
30428
  if (isDirectory(full) && agentNames.has(e)) {
30236
30429
  perAgentDirs.push(e);
30237
30430
  } else {
@@ -30354,19 +30547,19 @@ var init_doctor_inlined_secrets = __esm(() => {
30354
30547
 
30355
30548
  // src/cli/doctor-audit-integrity.ts
30356
30549
  import { readFileSync as fsReadFileSync2 } from "node:fs";
30357
- import { homedir as homedir28 } from "node:os";
30358
- import { join as join49 } from "node:path";
30550
+ import { homedir as homedir29 } from "node:os";
30551
+ import { join as join50 } from "node:path";
30359
30552
  function rootWrittenLogs(home2) {
30360
30553
  return [
30361
- { label: "vault-broker", path: join49(home2, ".switchroom", "vault-audit.log") },
30554
+ { label: "vault-broker", path: join50(home2, ".switchroom", "vault-audit.log") },
30362
30555
  {
30363
30556
  label: "hostd",
30364
- path: join49(home2, ".switchroom", "host-control-audit.log")
30557
+ path: join50(home2, ".switchroom", "host-control-audit.log")
30365
30558
  }
30366
30559
  ];
30367
30560
  }
30368
30561
  function runAuditIntegrityChecks(deps = {}) {
30369
- const home2 = deps.homeDir ?? homedir28();
30562
+ const home2 = deps.homeDir ?? homedir29();
30370
30563
  const read = deps.readFileSync ?? ((p) => fsReadFileSync2(p, "utf8"));
30371
30564
  const results = [];
30372
30565
  for (const { label, path: path4 } of rootWrittenLogs(home2)) {
@@ -30504,14 +30697,14 @@ var init_client4 = __esm(() => {
30504
30697
 
30505
30698
  // src/cli/doctor-agent-smoke.ts
30506
30699
  import { existsSync as existsSync49 } from "node:fs";
30507
- import { homedir as homedir29 } from "node:os";
30508
- import { join as join50 } from "node:path";
30700
+ import { homedir as homedir30 } from "node:os";
30701
+ import { join as join51 } from "node:path";
30509
30702
  import { randomUUID as randomUUID4 } from "node:crypto";
30510
30703
  async function runAgentSmokeChecks(config, deps = {}) {
30511
30704
  if (deps.fast)
30512
30705
  return [];
30513
- const home2 = deps.homeDir ?? homedir29();
30514
- const sock = deps.operatorSockPath ?? join50(home2, ".switchroom", "hostd", "operator", "sock");
30706
+ const home2 = deps.homeDir ?? homedir30();
30707
+ const sock = deps.operatorSockPath ?? join51(home2, ".switchroom", "hostd", "operator", "sock");
30515
30708
  if (!deps.hostdRequestImpl && !existsSync49(sock)) {
30516
30709
  return [
30517
30710
  {
@@ -30591,8 +30784,8 @@ var init_doctor_agent_smoke = __esm(() => {
30591
30784
  // src/cli/doctor-vault-broker-durability.ts
30592
30785
  import { execFileSync as execFileSync14 } from "node:child_process";
30593
30786
  import { existsSync as existsSync50, statSync as statSync22 } from "node:fs";
30594
- import { homedir as homedir30 } from "node:os";
30595
- import { join as join51 } from "node:path";
30787
+ import { homedir as homedir31 } from "node:os";
30788
+ import { join as join52 } from "node:path";
30596
30789
  function probeBindMountInode(hostPath, brokerContainerPath, opts) {
30597
30790
  const statHost = opts?.statHost ?? defaultStatHost;
30598
30791
  const statBroker = opts?.statBroker ?? defaultStatBroker;
@@ -30732,19 +30925,19 @@ function defaultBrokerStatusProbe() {
30732
30925
  }
30733
30926
  }
30734
30927
  function runVaultBrokerDurabilityChecks(_config, opts) {
30735
- const home2 = homedir30();
30928
+ const home2 = homedir31();
30736
30929
  const probe2 = opts?.inodeProbe ?? probeBindMountInode;
30737
30930
  return [
30738
30931
  probeBrokerUnlocked(opts?.statusProbe),
30739
30932
  probeAutoUnlockBlob(home2),
30740
30933
  probeMachineIdMount(),
30741
- formatBindMountResult("vault-broker: vault.enc bind mount", join51(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join51(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
30742
- formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join51(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join51(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
30743
- formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join51(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join51(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log"))
30934
+ formatBindMountResult("vault-broker: vault.enc bind mount", join52(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join52(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
30935
+ formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join52(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join52(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
30936
+ formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join52(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join52(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log"))
30744
30937
  ];
30745
30938
  }
30746
30939
  function probeAutoUnlockBlob(home2) {
30747
- const blobPath = join51(home2, ".switchroom", "vault-auto-unlock");
30940
+ const blobPath = join52(home2, ".switchroom", "vault-auto-unlock");
30748
30941
  if (!existsSync50(blobPath)) {
30749
30942
  return {
30750
30943
  name: "vault-broker: auto-unlock blob",
@@ -30855,16 +31048,16 @@ import {
30855
31048
  readdirSync as readdirSync19,
30856
31049
  statSync as statSync23
30857
31050
  } from "node:fs";
30858
- import { dirname as dirname12, join as join52, resolve as resolve31 } from "node:path";
31051
+ import { dirname as dirname12, join as join53, resolve as resolve31 } from "node:path";
30859
31052
  import { createPublicKey, createPrivateKey } from "node:crypto";
30860
31053
  function findInNvm(bin) {
30861
- const nvmRoot = join52(process.env.HOME ?? "", ".nvm", "versions", "node");
31054
+ const nvmRoot = join53(process.env.HOME ?? "", ".nvm", "versions", "node");
30862
31055
  if (!existsSync51(nvmRoot))
30863
31056
  return null;
30864
31057
  try {
30865
31058
  const versions = readdirSync19(nvmRoot).sort().reverse();
30866
31059
  for (const v of versions) {
30867
- const candidate = join52(nvmRoot, v, "bin", bin);
31060
+ const candidate = join53(nvmRoot, v, "bin", bin);
30868
31061
  try {
30869
31062
  const s = statSync23(candidate);
30870
31063
  if (s.isFile() || s.isSymbolicLink()) {
@@ -31029,7 +31222,7 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
31029
31222
  if (envBrowsersPath && envBrowsersPath.length > 0) {
31030
31223
  cacheLocations.push(envBrowsersPath);
31031
31224
  }
31032
- cacheLocations.push(join52(homeDir, ".cache", "ms-playwright"));
31225
+ cacheLocations.push(join53(homeDir, ".cache", "ms-playwright"));
31033
31226
  for (const cacheDir of cacheLocations) {
31034
31227
  if (!existsSync51(cacheDir))
31035
31228
  continue;
@@ -31037,10 +31230,10 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
31037
31230
  const entries = readdirSync19(cacheDir).filter((e) => e.startsWith("chromium"));
31038
31231
  for (const entry of entries) {
31039
31232
  const candidates2 = [
31040
- join52(cacheDir, entry, "chrome-linux64", "chrome"),
31041
- join52(cacheDir, entry, "chrome-linux", "chrome"),
31042
- join52(cacheDir, entry, "chrome-linux64", "headless_shell"),
31043
- join52(cacheDir, entry, "chrome-linux", "headless_shell")
31233
+ join53(cacheDir, entry, "chrome-linux64", "chrome"),
31234
+ join53(cacheDir, entry, "chrome-linux", "chrome"),
31235
+ join53(cacheDir, entry, "chrome-linux64", "headless_shell"),
31236
+ join53(cacheDir, entry, "chrome-linux", "headless_shell")
31044
31237
  ];
31045
31238
  for (const path4 of candidates2) {
31046
31239
  if (existsSync51(path4))
@@ -31150,7 +31343,7 @@ function checkUserDeclaredMcps(name, agentConfig, config, renderedMcpServers) {
31150
31343
  function checkLegacyState() {
31151
31344
  const results = [];
31152
31345
  const h = process.env.HOME ?? "/root";
31153
- const clerkDir = join52(h, LEGACY_STATE_DIR);
31346
+ const clerkDir = join53(h, LEGACY_STATE_DIR);
31154
31347
  const clerkPresent = existsSync51(clerkDir);
31155
31348
  results.push({
31156
31349
  name: "legacy ~/.clerk state",
@@ -31160,7 +31353,7 @@ function checkLegacyState() {
31160
31353
  fix: "Legacy state detected. Run `mv ~/.clerk ~/.switchroom` and rename " + "any top-level `clerk:` key in switchroom.yaml to `switchroom:`. " + "This back-compat shim is REMOVED in v0.13.0 \u2014 no automatic " + "migration exists."
31161
31354
  } : {}
31162
31355
  });
31163
- const legacySock = join52(h, ".switchroom", "vault-broker.sock");
31356
+ const legacySock = join53(h, ".switchroom", "vault-broker.sock");
31164
31357
  let sockStat = null;
31165
31358
  try {
31166
31359
  sockStat = lstatSync5(legacySock);
@@ -31456,7 +31649,7 @@ async function checkHindsight(config) {
31456
31649
  }
31457
31650
  function checkPendingRetainsQueue(dir) {
31458
31651
  const home2 = process.env.HOME ?? "";
31459
- const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join52(home2, ".hindsight", "pending-retains");
31652
+ const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join53(home2, ".hindsight", "pending-retains");
31460
31653
  if (!existsSync51(pendingDir)) {
31461
31654
  return {
31462
31655
  name: "pending-retains queue",
@@ -31587,7 +31780,7 @@ async function checkTelegram(config) {
31587
31780
  const plugin = agentConfig.channels?.telegram?.plugin ?? "switchroom";
31588
31781
  if (plugin !== "switchroom")
31589
31782
  continue;
31590
- const envPath = join52(agentsDir, name, "telegram", ".env");
31783
+ const envPath = join53(agentsDir, name, "telegram", ".env");
31591
31784
  const read = tryReadHostFile(envPath);
31592
31785
  if (read.kind === "eacces") {
31593
31786
  results.push({
@@ -31670,7 +31863,7 @@ function checkStartShStale(agentName, startShPath) {
31670
31863
  }
31671
31864
  function checkLeakedHomeSwitchroom(agentName, agentDir) {
31672
31865
  const label = `${agentName}: $HOME/.switchroom symlink (#910)`;
31673
- const path4 = join52(agentDir, "home", ".switchroom");
31866
+ const path4 = join53(agentDir, "home", ".switchroom");
31674
31867
  let stats;
31675
31868
  try {
31676
31869
  stats = lstatSync5(path4);
@@ -31707,7 +31900,7 @@ function checkLeakedHomeSwitchroom(agentName, agentDir) {
31707
31900
  }
31708
31901
  function checkRepoHygiene(repoRoot) {
31709
31902
  const results = [];
31710
- const exportDir = join52(repoRoot, "clerk-export");
31903
+ const exportDir = join53(repoRoot, "clerk-export");
31711
31904
  if (existsSync51(exportDir)) {
31712
31905
  results.push({
31713
31906
  name: "repo hygiene: clerk-export/ on disk (#1072)",
@@ -31716,7 +31909,7 @@ function checkRepoHygiene(repoRoot) {
31716
31909
  fix: `Run scripts/migrate-clerk-export-to-vault.sh to move the bundle ` + `into the vault, then delete the on-disk copy.`
31717
31910
  });
31718
31911
  }
31719
- const knownTarball = join52(repoRoot, "clerk-export-with-secrets.tar.gz");
31912
+ const knownTarball = join53(repoRoot, "clerk-export-with-secrets.tar.gz");
31720
31913
  if (existsSync51(knownTarball)) {
31721
31914
  results.push({
31722
31915
  name: "repo hygiene: clerk-export-with-secrets.tar.gz on disk (#1072)",
@@ -31734,7 +31927,7 @@ function checkRepoHygiene(repoRoot) {
31734
31927
  results.push({
31735
31928
  name: `repo hygiene: ${name} on disk (#1072)`,
31736
31929
  status: "warn",
31737
- detail: `${join52(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
31930
+ detail: `${join53(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
31738
31931
  fix: `Inspect, migrate any secrets into the vault, then delete the ` + `archive.`
31739
31932
  });
31740
31933
  }
@@ -31757,9 +31950,9 @@ function checkRepoHygiene(repoRoot) {
31757
31950
  }
31758
31951
  function isSwitchroomCheckout(dir) {
31759
31952
  try {
31760
- if (!existsSync51(join52(dir, ".git")))
31953
+ if (!existsSync51(join53(dir, ".git")))
31761
31954
  return false;
31762
- const pkgPath = join52(dir, "package.json");
31955
+ const pkgPath = join53(dir, "package.json");
31763
31956
  if (!existsSync51(pkgPath))
31764
31957
  return false;
31765
31958
  const pkg = JSON.parse(readFileSync46(pkgPath, "utf-8"));
@@ -31796,7 +31989,7 @@ function checkAgents(config, configPath) {
31796
31989
  fix: `Rotate the bot token (e.g. via \`switchroom vault\`), then run ` + `\`switchroom agent unquarantine ${name}\` and \`switchroom agent restart ${name}\``
31797
31990
  });
31798
31991
  }
31799
- results.push(checkStartShStale(name, join52(agentDir, "start.sh")));
31992
+ results.push(checkStartShStale(name, join53(agentDir, "start.sh")));
31800
31993
  results.push(checkLeakedHomeSwitchroom(name, agentDir));
31801
31994
  const status = statuses[name];
31802
31995
  const active = status?.active ?? "unknown";
@@ -31873,7 +32066,7 @@ function checkAgents(config, configPath) {
31873
32066
  }
31874
32067
  }
31875
32068
  if (agentConfig.channels?.telegram?.plugin === "switchroom") {
31876
- const mcpJsonPath = join52(agentDir, ".mcp.json");
32069
+ const mcpJsonPath = join53(agentDir, ".mcp.json");
31877
32070
  if (!existsSync51(mcpJsonPath)) {
31878
32071
  results.push({
31879
32072
  name: `${name}: .mcp.json`,
@@ -32175,7 +32368,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
32175
32368
  };
32176
32369
  }
32177
32370
  const credDir = dirname12(envPath);
32178
- const authScript = join52(credDir, "claude-auth.py");
32371
+ const authScript = join53(credDir, "claude-auth.py");
32179
32372
  if (!existsSync51(authScript)) {
32180
32373
  return {
32181
32374
  name: "mff: auth flow",
@@ -32504,7 +32697,8 @@ function registerDoctorCommand(program3) {
32504
32697
  }
32505
32698
  })
32506
32699
  },
32507
- { title: "MFF Skill", results: await checkMff(passphrase, vaultPath, config) }
32700
+ { title: "MFF Skill", results: await checkMff(passphrase, vaultPath, config) },
32701
+ { title: "Webkite", results: runWebkiteChecks(config) }
32508
32702
  ];
32509
32703
  const cwd = process.cwd();
32510
32704
  if (isSwitchroomCheckout(cwd)) {
@@ -32563,6 +32757,7 @@ var init_doctor = __esm(() => {
32563
32757
  init_doctor_auth_broker();
32564
32758
  init_doctor_hostd();
32565
32759
  init_doctor_drive();
32760
+ init_doctor_webkite();
32566
32761
  init_doctor_microsoft();
32567
32762
  init_doctor_notion();
32568
32763
  init_doctor_credentials_migration();
@@ -49083,8 +49278,8 @@ var {
49083
49278
  } = import__.default;
49084
49279
 
49085
49280
  // src/build-info.ts
49086
- var VERSION = "0.13.64";
49087
- var COMMIT_SHA = "52afe8b0";
49281
+ var VERSION = "0.14.0";
49282
+ var COMMIT_SHA = "d7cd6faa";
49088
49283
 
49089
49284
  // src/cli/agent.ts
49090
49285
  init_source();
@@ -49940,6 +50135,10 @@ var AGENT_CONFIG_MCP_TOOLS = [
49940
50135
  var HOSTD_MCP_TOOLS = [
49941
50136
  "mcp__hostd__update_check"
49942
50137
  ];
50138
+ var WEBKITE_MCP_TOOLS = [
50139
+ "mcp__webkite",
50140
+ "mcp__webkite__*"
50141
+ ];
49943
50142
  var LEGACY_SWITCHROOM_MCP_TOKENS = ["mcp__switchroom", "mcp__switchroom__*"];
49944
50143
  var LEGACY_HOSTD_BLANKET_TOKENS = ["mcp__hostd", "mcp__hostd__*"];
49945
50144
  var DEFAULT_READ_ONLY_PREAPPROVED_TOOLS = [
@@ -50755,7 +50954,8 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
50755
50954
  ...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
50756
50955
  ...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
50757
50956
  ...AGENT_CONFIG_MCP_TOOLS,
50758
- ...HOSTD_MCP_TOOLS
50957
+ ...HOSTD_MCP_TOOLS,
50958
+ ...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
50759
50959
  ]);
50760
50960
  const hindsightAutoRecallEnabled = hindsightEnabled && agentConfig.memory?.auto_recall !== false;
50761
50961
  const hindsightBankId = agentConfig.memory?.collection ?? name;
@@ -50816,7 +51016,8 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
50816
51016
  }
50817
51017
  settings.permissions = settings.permissions ?? {};
50818
51018
  const allow = (Array.isArray(settings.permissions.allow) ? settings.permissions.allow : []).filter((p) => !LEGACY_HOSTD_BLANKET_TOKENS.includes(p));
50819
- for (const t of [...AGENT_CONFIG_MCP_TOOLS, ...HOSTD_MCP_TOOLS]) {
51019
+ const webkiteAllowTools = agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS;
51020
+ for (const t of [...AGENT_CONFIG_MCP_TOOLS, ...HOSTD_MCP_TOOLS, ...webkiteAllowTools]) {
50820
51021
  if (!allow.includes(t))
50821
51022
  allow.push(t);
50822
51023
  }
@@ -51539,7 +51740,8 @@ function reconcileAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchr
51539
51740
  ...usesSwitchroomTelegramPlugin(agentConfig) ? SWITCHROOM_TELEGRAM_MCP_TOOLS : [],
51540
51741
  ...hindsightEnabled ? HINDSIGHT_MCP_TOOLS : [],
51541
51742
  ...AGENT_CONFIG_MCP_TOOLS,
51542
- ...HOSTD_MCP_TOOLS
51743
+ ...HOSTD_MCP_TOOLS,
51744
+ ...agentConfig.mcp_servers?.["webkite"] === false ? [] : WEBKITE_MCP_TOOLS
51543
51745
  ]);
51544
51746
  const desiredDeny = dedupe2([
51545
51747
  ...tools.deny ?? [],
@@ -72402,15 +72604,15 @@ init_loader();
72402
72604
  init_lifecycle();
72403
72605
  import { cpSync as cpSync2, existsSync as existsSync52, mkdirSync as mkdirSync28, readFileSync as readFileSync47, realpathSync as realpathSync5, rmSync as rmSync12, statSync as statSync24 } from "node:fs";
72404
72606
  import { spawnSync as spawnSync8 } from "node:child_process";
72405
- import { join as join53, dirname as dirname13, resolve as resolve32 } from "node:path";
72406
- import { homedir as homedir31 } from "node:os";
72407
- var DEFAULT_COMPOSE_PATH = join53(homedir31(), ".switchroom", "compose", "docker-compose.yml");
72607
+ import { join as join54, dirname as dirname13, resolve as resolve32 } from "node:path";
72608
+ import { homedir as homedir32 } from "node:os";
72609
+ var DEFAULT_COMPOSE_PATH = join54(homedir32(), ".switchroom", "compose", "docker-compose.yml");
72408
72610
  function runningFromSwitchroomCheckout(scriptPath) {
72409
72611
  let dir = dirname13(scriptPath);
72410
72612
  for (let i = 0;i < 12; i++) {
72411
- if (existsSync52(join53(dir, ".git"))) {
72613
+ if (existsSync52(join54(dir, ".git"))) {
72412
72614
  try {
72413
- const pkg = JSON.parse(readFileSync47(join53(dir, "package.json"), "utf-8"));
72615
+ const pkg = JSON.parse(readFileSync47(join54(dir, "package.json"), "utf-8"));
72414
72616
  if (pkg.name === "switchroom")
72415
72617
  return true;
72416
72618
  } catch {}
@@ -72541,7 +72743,7 @@ function planUpdate(opts) {
72541
72743
  return;
72542
72744
  }
72543
72745
  const source = resolve32(import.meta.dirname, "../../skills");
72544
- const dest = join53(homedir31(), ".switchroom", "skills", "_bundled");
72746
+ const dest = join54(homedir32(), ".switchroom", "skills", "_bundled");
72545
72747
  if (!existsSync52(source)) {
72546
72748
  process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
72547
72749
  `);
@@ -72655,7 +72857,7 @@ function defaultStatusProbe(composePath) {
72655
72857
  } catch {}
72656
72858
  let dir = dirname13(scriptPath);
72657
72859
  for (let i = 0;i < 8; i++) {
72658
- const pkgPath = join53(dir, "package.json");
72860
+ const pkgPath = join54(dir, "package.json");
72659
72861
  if (existsSync52(pkgPath)) {
72660
72862
  try {
72661
72863
  const pkg = JSON.parse(readFileSync47(pkgPath, "utf-8"));
@@ -72875,7 +73077,7 @@ init_helpers();
72875
73077
  init_lifecycle();
72876
73078
  import { execSync as execSync4 } from "node:child_process";
72877
73079
  import { existsSync as existsSync53, readFileSync as readFileSync48 } from "node:fs";
72878
- import { dirname as dirname14, join as join54 } from "node:path";
73080
+ import { dirname as dirname14, join as join55 } from "node:path";
72879
73081
  function getClaudeCodeVersion() {
72880
73082
  try {
72881
73083
  const out = execSync4("claude --version 2>/dev/null", {
@@ -72925,11 +73127,11 @@ function formatUptime3(timestamp) {
72925
73127
  function locateSwitchroomInstallDir() {
72926
73128
  let dir = import.meta.dirname;
72927
73129
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
72928
- const pkgPath = join54(dir, "package.json");
73130
+ const pkgPath = join55(dir, "package.json");
72929
73131
  if (existsSync53(pkgPath)) {
72930
73132
  try {
72931
73133
  const pkg = JSON.parse(readFileSync48(pkgPath, "utf-8"));
72932
- if (pkg.name === "switchroom" && existsSync53(join54(dir, ".git"))) {
73134
+ if (pkg.name === "switchroom" && existsSync53(join55(dir, ".git"))) {
72933
73135
  return dir;
72934
73136
  }
72935
73137
  } catch {}
@@ -73166,7 +73368,7 @@ import {
73166
73368
  writeFileSync as writeFileSync26,
73167
73369
  writeSync as writeSync7
73168
73370
  } from "node:fs";
73169
- import { join as join55 } from "node:path";
73371
+ import { join as join56 } from "node:path";
73170
73372
  import { randomBytes as randomBytes12 } from "node:crypto";
73171
73373
  import { execSync as execSync5 } from "node:child_process";
73172
73374
 
@@ -73486,7 +73688,7 @@ function redactedMarker(ruleId) {
73486
73688
  var ISSUES_FILE = "issues.jsonl";
73487
73689
  var ISSUES_LOCK = "issues.lock";
73488
73690
  function readAll(stateDir) {
73489
- const path4 = join55(stateDir, ISSUES_FILE);
73691
+ const path4 = join56(stateDir, ISSUES_FILE);
73490
73692
  if (!existsSync54(path4))
73491
73693
  return [];
73492
73694
  let raw;
@@ -73564,7 +73766,7 @@ function record(stateDir, input, nowFn = Date.now) {
73564
73766
  });
73565
73767
  }
73566
73768
  function resolve35(stateDir, fingerprint, nowFn = Date.now) {
73567
- if (!existsSync54(join55(stateDir, ISSUES_FILE)))
73769
+ if (!existsSync54(join56(stateDir, ISSUES_FILE)))
73568
73770
  return 0;
73569
73771
  return withLock(stateDir, () => {
73570
73772
  const all = readAll(stateDir);
@@ -73582,7 +73784,7 @@ function resolve35(stateDir, fingerprint, nowFn = Date.now) {
73582
73784
  });
73583
73785
  }
73584
73786
  function resolveAllBySource(stateDir, source, nowFn = Date.now) {
73585
- if (!existsSync54(join55(stateDir, ISSUES_FILE)))
73787
+ if (!existsSync54(join56(stateDir, ISSUES_FILE)))
73586
73788
  return 0;
73587
73789
  return withLock(stateDir, () => {
73588
73790
  const all = readAll(stateDir);
@@ -73600,7 +73802,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
73600
73802
  });
73601
73803
  }
73602
73804
  function prune(stateDir, opts = {}) {
73603
- if (!existsSync54(join55(stateDir, ISSUES_FILE)))
73805
+ if (!existsSync54(join56(stateDir, ISSUES_FILE)))
73604
73806
  return 0;
73605
73807
  return withLock(stateDir, () => {
73606
73808
  const all = readAll(stateDir);
@@ -73633,7 +73835,7 @@ function ensureDir(stateDir) {
73633
73835
  mkdirSync29(stateDir, { recursive: true });
73634
73836
  }
73635
73837
  function writeAll(stateDir, events) {
73636
- const path4 = join55(stateDir, ISSUES_FILE);
73838
+ const path4 = join56(stateDir, ISSUES_FILE);
73637
73839
  sweepOrphanTmpFiles(stateDir);
73638
73840
  const tmp = `${path4}.tmp-${process.pid}-${randomBytes12(4).toString("hex")}`;
73639
73841
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
@@ -73655,7 +73857,7 @@ function sweepOrphanTmpFiles(stateDir) {
73655
73857
  for (const entry of entries) {
73656
73858
  if (!entry.startsWith(TMP_PREFIX))
73657
73859
  continue;
73658
- const tmpPath = join55(stateDir, entry);
73860
+ const tmpPath = join56(stateDir, entry);
73659
73861
  try {
73660
73862
  const stat = statSync25(tmpPath);
73661
73863
  if (stat.mtimeMs < cutoff) {
@@ -73667,7 +73869,7 @@ function sweepOrphanTmpFiles(stateDir) {
73667
73869
  var LOCK_RETRY_MS = 25;
73668
73870
  var LOCK_TIMEOUT_MS = 1e4;
73669
73871
  function withLock(stateDir, fn) {
73670
- const lockPath = join55(stateDir, ISSUES_LOCK);
73872
+ const lockPath = join56(stateDir, ISSUES_LOCK);
73671
73873
  const startedAt = Date.now();
73672
73874
  let fd = null;
73673
73875
  while (fd === null) {
@@ -73951,8 +74153,8 @@ function relTime(deltaMs) {
73951
74153
  // src/cli/deps.ts
73952
74154
  init_source();
73953
74155
  import { existsSync as existsSync57 } from "node:fs";
73954
- import { homedir as homedir34 } from "node:os";
73955
- import { join as join58, resolve as resolve36 } from "node:path";
74156
+ import { homedir as homedir35 } from "node:os";
74157
+ import { join as join59, resolve as resolve36 } from "node:path";
73956
74158
 
73957
74159
  // src/deps/python.ts
73958
74160
  import { createHash as createHash11 } from "node:crypto";
@@ -73963,8 +74165,8 @@ import {
73963
74165
  rmSync as rmSync13,
73964
74166
  writeFileSync as writeFileSync27
73965
74167
  } from "node:fs";
73966
- import { dirname as dirname15, join as join56 } from "node:path";
73967
- import { homedir as homedir32 } from "node:os";
74168
+ import { dirname as dirname15, join as join57 } from "node:path";
74169
+ import { homedir as homedir33 } from "node:os";
73968
74170
  import { execFileSync as execFileSync15 } from "node:child_process";
73969
74171
 
73970
74172
  class PythonEnvError extends Error {
@@ -73976,7 +74178,7 @@ class PythonEnvError extends Error {
73976
74178
  }
73977
74179
  }
73978
74180
  function defaultPythonCacheRoot() {
73979
- return join56(homedir32(), ".switchroom", "deps", "python");
74181
+ return join57(homedir33(), ".switchroom", "deps", "python");
73980
74182
  }
73981
74183
  function hashFile(path4) {
73982
74184
  return createHash11("sha256").update(readFileSync50(path4)).digest("hex");
@@ -73988,11 +74190,11 @@ function ensurePythonEnv(opts) {
73988
74190
  if (!existsSync55(requirementsPath)) {
73989
74191
  throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
73990
74192
  }
73991
- const venvDir = join56(cacheRoot, skillName);
73992
- const stampPath = join56(venvDir, ".requirements.sha256");
73993
- const binDir = join56(venvDir, "bin");
73994
- const pythonBin = join56(binDir, "python");
73995
- const pipBin = join56(binDir, "pip");
74193
+ const venvDir = join57(cacheRoot, skillName);
74194
+ const stampPath = join57(venvDir, ".requirements.sha256");
74195
+ const binDir = join57(venvDir, "bin");
74196
+ const pythonBin = join57(binDir, "python");
74197
+ const pipBin = join57(binDir, "pip");
73996
74198
  const targetHash = hashFile(requirementsPath);
73997
74199
  if (!force && existsSync55(stampPath) && existsSync55(pythonBin)) {
73998
74200
  const existingHash = readFileSync50(stampPath, "utf8").trim();
@@ -74051,8 +74253,8 @@ import {
74051
74253
  rmSync as rmSync14,
74052
74254
  writeFileSync as writeFileSync28
74053
74255
  } from "node:fs";
74054
- import { dirname as dirname16, join as join57 } from "node:path";
74055
- import { homedir as homedir33 } from "node:os";
74256
+ import { dirname as dirname16, join as join58 } from "node:path";
74257
+ import { homedir as homedir34 } from "node:os";
74056
74258
  import { execFileSync as execFileSync16 } from "node:child_process";
74057
74259
 
74058
74260
  class NodeEnvError extends Error {
@@ -74075,7 +74277,7 @@ var LOCKFILES_FOR = {
74075
74277
  npm: ["package-lock.json"]
74076
74278
  };
74077
74279
  function defaultNodeCacheRoot() {
74078
- return join57(homedir33(), ".switchroom", "deps", "node");
74280
+ return join58(homedir34(), ".switchroom", "deps", "node");
74079
74281
  }
74080
74282
  function hashDepInputs(packageJsonPath) {
74081
74283
  const sourceDir = dirname16(packageJsonPath);
@@ -74084,7 +74286,7 @@ function hashDepInputs(packageJsonPath) {
74084
74286
  `);
74085
74287
  hasher.update(readFileSync51(packageJsonPath));
74086
74288
  for (const lockName of ALL_LOCKFILES) {
74087
- const lockPath = join57(sourceDir, lockName);
74289
+ const lockPath = join58(sourceDir, lockName);
74088
74290
  if (existsSync56(lockPath)) {
74089
74291
  hasher.update(`
74090
74292
  `);
@@ -74104,10 +74306,10 @@ function ensureNodeEnv(opts) {
74104
74306
  throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
74105
74307
  }
74106
74308
  const sourceDir = dirname16(packageJsonPath);
74107
- const envDir = join57(cacheRoot, skillName);
74108
- const stampPath = join57(envDir, ".package.sha256");
74109
- const nodeModulesDir = join57(envDir, "node_modules");
74110
- const binDir = join57(nodeModulesDir, ".bin");
74309
+ const envDir = join58(cacheRoot, skillName);
74310
+ const stampPath = join58(envDir, ".package.sha256");
74311
+ const nodeModulesDir = join58(envDir, "node_modules");
74312
+ const binDir = join58(nodeModulesDir, ".bin");
74111
74313
  const targetHash = hashDepInputs(packageJsonPath);
74112
74314
  if (!force && existsSync56(stampPath) && existsSync56(nodeModulesDir)) {
74113
74315
  const existingHash = readFileSync51(stampPath, "utf8").trim();
@@ -74125,12 +74327,12 @@ function ensureNodeEnv(opts) {
74125
74327
  rmSync14(envDir, { recursive: true, force: true });
74126
74328
  }
74127
74329
  mkdirSync31(envDir, { recursive: true });
74128
- copyFileSync9(packageJsonPath, join57(envDir, "package.json"));
74330
+ copyFileSync9(packageJsonPath, join58(envDir, "package.json"));
74129
74331
  let copiedLockfile = false;
74130
74332
  for (const lockName of LOCKFILES_FOR[installer]) {
74131
- const lockPath = join57(sourceDir, lockName);
74333
+ const lockPath = join58(sourceDir, lockName);
74132
74334
  if (existsSync56(lockPath)) {
74133
- copyFileSync9(lockPath, join57(envDir, lockName));
74335
+ copyFileSync9(lockPath, join58(envDir, lockName));
74134
74336
  copiedLockfile = true;
74135
74337
  }
74136
74338
  }
@@ -74159,7 +74361,7 @@ function ensureNodeEnv(opts) {
74159
74361
 
74160
74362
  // src/cli/deps.ts
74161
74363
  function builtinSkillsRoot() {
74162
- return resolve36(homedir34(), ".switchroom/skills/_bundled");
74364
+ return resolve36(homedir35(), ".switchroom/skills/_bundled");
74163
74365
  }
74164
74366
  function registerDepsCommand(program3) {
74165
74367
  const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
@@ -74169,13 +74371,13 @@ function registerDepsCommand(program3) {
74169
74371
  console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
74170
74372
  process.exit(1);
74171
74373
  }
74172
- const skillDir = join58(skillsRoot, skill);
74374
+ const skillDir = join59(skillsRoot, skill);
74173
74375
  if (!existsSync57(skillDir)) {
74174
74376
  console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
74175
74377
  process.exit(1);
74176
74378
  }
74177
- const requirementsPath = join58(skillDir, "requirements.txt");
74178
- const packageJsonPath = join58(skillDir, "package.json");
74379
+ const requirementsPath = join59(skillDir, "requirements.txt");
74380
+ const packageJsonPath = join59(skillDir, "package.json");
74179
74381
  const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync57(requirementsPath));
74180
74382
  const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync57(packageJsonPath));
74181
74383
  let did = 0;
@@ -75130,7 +75332,7 @@ init_helpers();
75130
75332
  init_loader();
75131
75333
  init_merge();
75132
75334
  import { copyFileSync as copyFileSync10, existsSync as existsSync59, readFileSync as readFileSync52, writeFileSync as writeFileSync29 } from "node:fs";
75133
- import { join as join59, resolve as resolve38 } from "node:path";
75335
+ import { join as join60, resolve as resolve38 } from "node:path";
75134
75336
  init_schema();
75135
75337
  function resolveSoulTargetOrExit(program3, agentName) {
75136
75338
  const config = getConfig(program3);
@@ -75154,7 +75356,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
75154
75356
  profileName,
75155
75357
  profilePath,
75156
75358
  workspaceDir,
75157
- soulPath: join59(workspaceDir, "SOUL.md"),
75359
+ soulPath: join60(workspaceDir, "SOUL.md"),
75158
75360
  soul: merged.soul
75159
75361
  };
75160
75362
  }
@@ -75221,7 +75423,7 @@ function registerSoulCommand(program3) {
75221
75423
  init_helpers();
75222
75424
  init_loader();
75223
75425
  import { existsSync as existsSync60, readFileSync as readFileSync53, readdirSync as readdirSync21, statSync as statSync26 } from "node:fs";
75224
- import { resolve as resolve39, join as join60 } from "node:path";
75426
+ import { resolve as resolve39, join as join61 } from "node:path";
75225
75427
  import { createHash as createHash13 } from "node:crypto";
75226
75428
  init_merge();
75227
75429
  init_hindsight();
@@ -75235,7 +75437,7 @@ function sha256(content) {
75235
75437
  return createHash13("sha256").update(content).digest("hex").slice(0, 16);
75236
75438
  }
75237
75439
  function findLatestTranscriptJsonl(claudeConfigDir) {
75238
- const projectsDir = join60(claudeConfigDir, "projects");
75440
+ const projectsDir = join61(claudeConfigDir, "projects");
75239
75441
  if (!existsSync60(projectsDir))
75240
75442
  return;
75241
75443
  try {
@@ -75244,8 +75446,8 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
75244
75446
  for (const entry of entries) {
75245
75447
  if (!entry.isDirectory())
75246
75448
  continue;
75247
- const projectPath = join60(projectsDir, entry.name);
75248
- const transcriptPath = join60(projectPath, "transcript.jsonl");
75449
+ const projectPath = join61(projectsDir, entry.name);
75450
+ const transcriptPath = join61(projectPath, "transcript.jsonl");
75249
75451
  if (!existsSync60(transcriptPath))
75250
75452
  continue;
75251
75453
  const stat3 = statSync26(transcriptPath);
@@ -75314,11 +75516,11 @@ function registerDebugCommand(program3) {
75314
75516
  process.exit(1);
75315
75517
  }
75316
75518
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
75317
- const claudeConfigDir = join60(agentDir, ".claude");
75318
- const claudeMdPath = join60(agentDir, "CLAUDE.md");
75319
- const soulMdPath = join60(agentDir, "SOUL.md");
75320
- const workspaceSoulMdPath = join60(workspaceDir, "SOUL.md");
75321
- const handoffPath = join60(agentDir, ".handoff.md");
75519
+ const claudeConfigDir = join61(agentDir, ".claude");
75520
+ const claudeMdPath = join61(agentDir, "CLAUDE.md");
75521
+ const soulMdPath = join61(agentDir, "SOUL.md");
75522
+ const workspaceSoulMdPath = join61(workspaceDir, "SOUL.md");
75523
+ const handoffPath = join61(agentDir, ".handoff.md");
75322
75524
  const lastN = parseInt(opts.last, 10);
75323
75525
  if (isNaN(lastN) || lastN < 1) {
75324
75526
  console.error("--last must be a positive integer");
@@ -75468,8 +75670,8 @@ init_source();
75468
75670
  // src/worktree/claim.ts
75469
75671
  import { execFileSync as execFileSync17 } from "node:child_process";
75470
75672
  import { closeSync as closeSync12, mkdirSync as mkdirSync33, openSync as openSync12, existsSync as existsSync62, unlinkSync as unlinkSync13 } from "node:fs";
75471
- import { join as join62, resolve as resolve41 } from "node:path";
75472
- import { homedir as homedir36 } from "node:os";
75673
+ import { join as join63, resolve as resolve41 } from "node:path";
75674
+ import { homedir as homedir37 } from "node:os";
75473
75675
  import { randomBytes as randomBytes13 } from "node:crypto";
75474
75676
 
75475
75677
  // src/worktree/registry.ts
@@ -75482,13 +75684,13 @@ import {
75482
75684
  existsSync as existsSync61,
75483
75685
  renameSync as renameSync12
75484
75686
  } from "node:fs";
75485
- import { join as join61, resolve as resolve40 } from "node:path";
75486
- import { homedir as homedir35 } from "node:os";
75687
+ import { join as join62, resolve as resolve40 } from "node:path";
75688
+ import { homedir as homedir36 } from "node:os";
75487
75689
  function registryDir() {
75488
- return resolve40(process.env.SWITCHROOM_WORKTREE_DIR ?? join61(homedir35(), ".switchroom", "worktrees"));
75690
+ return resolve40(process.env.SWITCHROOM_WORKTREE_DIR ?? join62(homedir36(), ".switchroom", "worktrees"));
75489
75691
  }
75490
75692
  function recordPath(id) {
75491
- return join61(registryDir(), `${id}.json`);
75693
+ return join62(registryDir(), `${id}.json`);
75492
75694
  }
75493
75695
  function ensureDir2() {
75494
75696
  mkdirSync32(registryDir(), { recursive: true });
@@ -75539,7 +75741,7 @@ function acquireRepoLock(repoPath) {
75539
75741
  const lockDir = registryDir();
75540
75742
  mkdirSync33(lockDir, { recursive: true });
75541
75743
  const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
75542
- const lockPath = join62(lockDir, `.lock-${lockName}`);
75744
+ const lockPath = join63(lockDir, `.lock-${lockName}`);
75543
75745
  const deadline = Date.now() + 5000;
75544
75746
  let fd = null;
75545
75747
  while (fd === null) {
@@ -75566,7 +75768,7 @@ function acquireRepoLock(repoPath) {
75566
75768
  }
75567
75769
  var DEFAULT_CONCURRENCY = 5;
75568
75770
  function worktreesBaseDir() {
75569
- return resolve41(process.env.SWITCHROOM_WORKTREE_BASE ?? join62(homedir36(), ".switchroom", "worktree-checkouts"));
75771
+ return resolve41(process.env.SWITCHROOM_WORKTREE_BASE ?? join63(homedir37(), ".switchroom", "worktree-checkouts"));
75570
75772
  }
75571
75773
  function shortId() {
75572
75774
  return randomBytes13(4).toString("hex");
@@ -75588,7 +75790,7 @@ function resolveRepoPath(repo, codeRepos) {
75588
75790
  }
75589
75791
  function expandHome(p) {
75590
75792
  if (p.startsWith("~/"))
75591
- return join62(homedir36(), p.slice(2));
75793
+ return join63(homedir37(), p.slice(2));
75592
75794
  return p;
75593
75795
  }
75594
75796
  async function claimWorktree(input, codeRepos) {
@@ -75616,7 +75818,7 @@ async function claimWorktree(input, codeRepos) {
75616
75818
  branch = `task/${taskSuffix}-${id}`;
75617
75819
  const baseDir = worktreesBaseDir();
75618
75820
  mkdirSync33(baseDir, { recursive: true });
75619
- worktreePath = join62(baseDir, `${id}-${taskSuffix}`);
75821
+ worktreePath = join63(baseDir, `${id}-${taskSuffix}`);
75620
75822
  const now = new Date().toISOString();
75621
75823
  const record2 = {
75622
75824
  id,
@@ -75871,7 +76073,7 @@ import {
75871
76073
  rmSync as rmSync15,
75872
76074
  writeFileSync as writeFileSync31
75873
76075
  } from "node:fs";
75874
- import { join as join63 } from "node:path";
76076
+ import { join as join64 } from "node:path";
75875
76077
  function encodeCredentialsFilename(email) {
75876
76078
  const SAFE = new Set([
75877
76079
  ..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
@@ -76061,16 +76263,16 @@ function resolveCredentialsDir(env2) {
76061
76263
  if (explicit && explicit.length > 0)
76062
76264
  return explicit;
76063
76265
  const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
76064
- return join63(stateBase, "google-workspace-mcp", "credentials");
76266
+ return join64(stateBase, "google-workspace-mcp", "credentials");
76065
76267
  }
76066
76268
  function writeSeedFile(dir, email, seed) {
76067
76269
  mkdirSync34(dir, { recursive: true, mode: 448 });
76068
76270
  chmodSync9(dir, 448);
76069
76271
  for (const name of readdirSync23(dir)) {
76070
- rmSync15(join63(dir, name), { force: true, recursive: true });
76272
+ rmSync15(join64(dir, name), { force: true, recursive: true });
76071
76273
  }
76072
76274
  const filename = encodeCredentialsFilename(email);
76073
- const filePath = join63(dir, filename);
76275
+ const filePath = join64(dir, filename);
76074
76276
  writeFileSync31(filePath, JSON.stringify(seed), { mode: 384 });
76075
76277
  chmodSync9(filePath, 384);
76076
76278
  return filePath;
@@ -76230,7 +76432,7 @@ function registerDriveMcpLauncherCommand(program3) {
76230
76432
  init_scaffold_integration();
76231
76433
  import { spawn as spawn6 } from "node:child_process";
76232
76434
  import { writeFileSync as writeFileSync32, mkdirSync as mkdirSync35 } from "node:fs";
76233
- import { dirname as dirname17, join as join64 } from "node:path";
76435
+ import { dirname as dirname17, join as join65 } from "node:path";
76234
76436
  var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
76235
76437
  var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
76236
76438
  var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
@@ -76262,7 +76464,7 @@ function writeRefreshHeartbeat(agentName, data) {
76262
76464
  function heartbeatPath(agentName) {
76263
76465
  const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
76264
76466
  if (override) {
76265
- return join64(override, `m365-launcher-${agentName}.heartbeat.json`);
76467
+ return join65(override, `m365-launcher-${agentName}.heartbeat.json`);
76266
76468
  }
76267
76469
  return "/state/agent/m365-launcher.heartbeat.json";
76268
76470
  }
@@ -77181,8 +77383,8 @@ agents:
77181
77383
 
77182
77384
  // src/cli/apply.ts
77183
77385
  init_resolver();
77184
- import { dirname as dirname21, join as join68, resolve as resolve43 } from "node:path";
77185
- import { homedir as homedir38 } from "node:os";
77386
+ import { dirname as dirname21, join as join69, resolve as resolve43 } from "node:path";
77387
+ import { homedir as homedir39 } from "node:os";
77186
77388
  import { execFileSync as execFileSync20 } from "node:child_process";
77187
77389
  init_vault();
77188
77390
  init_loader();
@@ -77190,7 +77392,7 @@ init_loader();
77190
77392
 
77191
77393
  // src/cli/update-prompt-hook.ts
77192
77394
  import { existsSync as existsSync66, readFileSync as readFileSync55, writeFileSync as writeFileSync34, chmodSync as chmodSync10, mkdirSync as mkdirSync37 } from "node:fs";
77193
- import { join as join65 } from "node:path";
77395
+ import { join as join66 } from "node:path";
77194
77396
  var HOOK_FILENAME = "update-card-on-prompt.sh";
77195
77397
  function updatePromptHookScript() {
77196
77398
  return `#!/bin/bash
@@ -77256,9 +77458,9 @@ exit 0
77256
77458
  `;
77257
77459
  }
77258
77460
  function installUpdatePromptHook(agentDir) {
77259
- const hooksDir = join65(agentDir, ".claude", "hooks");
77461
+ const hooksDir = join66(agentDir, ".claude", "hooks");
77260
77462
  mkdirSync37(hooksDir, { recursive: true });
77261
- const scriptPath = join65(hooksDir, HOOK_FILENAME);
77463
+ const scriptPath = join66(hooksDir, HOOK_FILENAME);
77262
77464
  const desired = updatePromptHookScript();
77263
77465
  let installed = false;
77264
77466
  const existing = existsSync66(scriptPath) ? readFileSync55(scriptPath, "utf-8") : "";
@@ -77271,7 +77473,7 @@ function installUpdatePromptHook(agentDir) {
77271
77473
  chmodSync10(scriptPath, 493);
77272
77474
  } catch {}
77273
77475
  }
77274
- const settingsPath = join65(agentDir, ".claude", "settings.json");
77476
+ const settingsPath = join66(agentDir, ".claude", "settings.json");
77275
77477
  if (!existsSync66(settingsPath)) {
77276
77478
  return { scriptPath, settingsPath, installed };
77277
77479
  }
@@ -77397,7 +77599,7 @@ import {
77397
77599
  realpathSync as realpathSync6,
77398
77600
  statSync as statSync27
77399
77601
  } from "node:fs";
77400
- import { join as join67 } from "node:path";
77602
+ import { join as join68 } from "node:path";
77401
77603
  function resolveOperatorUid() {
77402
77604
  const sudoUid = process.env.SUDO_UID;
77403
77605
  if (sudoUid !== undefined) {
@@ -77413,14 +77615,14 @@ function resolveOperatorUid() {
77413
77615
  return;
77414
77616
  }
77415
77617
  function operatorOwnedPaths(home2) {
77416
- const root = join67(home2, ".switchroom");
77618
+ const root = join68(home2, ".switchroom");
77417
77619
  return [
77418
- join67(root, "vault"),
77419
- join67(root, "vault-auto-unlock"),
77420
- join67(root, "vault-audit.log"),
77421
- join67(root, "host-control-audit.log"),
77422
- join67(root, "accounts"),
77423
- join67(root, "compose")
77620
+ join68(root, "vault"),
77621
+ join68(root, "vault-auto-unlock"),
77622
+ join68(root, "vault-audit.log"),
77623
+ join68(root, "host-control-audit.log"),
77624
+ join68(root, "accounts"),
77625
+ join68(root, "compose")
77424
77626
  ];
77425
77627
  }
77426
77628
  function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
@@ -77469,7 +77671,7 @@ function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
77469
77671
  } catch {}
77470
77672
  if (isDir(target)) {
77471
77673
  for (const entry of readdir2(target)) {
77472
- visit(join67(target, entry));
77674
+ visit(join68(target, entry));
77473
77675
  }
77474
77676
  }
77475
77677
  };
@@ -77483,14 +77685,14 @@ var EMBEDDED_EXAMPLES = {
77483
77685
  switchroom: switchroom_default,
77484
77686
  minimal: minimal_default
77485
77687
  };
77486
- var DEFAULT_COMPOSE_PATH2 = join68(homedir38(), ".switchroom", "compose", "docker-compose.yml");
77688
+ var DEFAULT_COMPOSE_PATH2 = join69(homedir39(), ".switchroom", "compose", "docker-compose.yml");
77487
77689
  var COMPOSE_PROJECT2 = "switchroom";
77488
77690
  function resolveVaultBindMountDir(homeDir, ctx) {
77489
77691
  const isCustomPath = ctx.migrationKind === "custom-path-skipped";
77490
77692
  if (isCustomPath && ctx.customVaultPath) {
77491
77693
  return dirname21(ctx.customVaultPath);
77492
77694
  }
77493
- return join68(homeDir, ".switchroom", "vault");
77695
+ return join69(homeDir, ".switchroom", "vault");
77494
77696
  }
77495
77697
  function inspectVaultBindMountDir(vaultDir) {
77496
77698
  if (!existsSync69(vaultDir))
@@ -77519,44 +77721,44 @@ function hasVaultRefs(value) {
77519
77721
  return false;
77520
77722
  }
77521
77723
  async function ensureHostMountSources(config) {
77522
- const home2 = homedir38();
77724
+ const home2 = homedir39();
77523
77725
  const dirs = [
77524
- join68(home2, ".switchroom", "approvals"),
77525
- join68(home2, ".switchroom", "scheduler"),
77526
- join68(home2, ".switchroom", "logs"),
77527
- join68(home2, ".switchroom", "compose"),
77528
- join68(home2, ".switchroom", "broker-operator")
77726
+ join69(home2, ".switchroom", "approvals"),
77727
+ join69(home2, ".switchroom", "scheduler"),
77728
+ join69(home2, ".switchroom", "logs"),
77729
+ join69(home2, ".switchroom", "compose"),
77730
+ join69(home2, ".switchroom", "broker-operator")
77529
77731
  ];
77530
77732
  for (const name of Object.keys(config.agents)) {
77531
- dirs.push(join68(home2, ".switchroom", "agents", name));
77532
- dirs.push(join68(home2, ".switchroom", "logs", name));
77533
- dirs.push(join68(home2, ".claude", "projects", name));
77534
- dirs.push(join68(home2, ".switchroom", "audit", name));
77535
- if (existsSync69(join68(home2, ".switchroom-config"))) {
77536
- dirs.push(join68(home2, ".switchroom-config", "agents", name, "personal-skills"));
77733
+ dirs.push(join69(home2, ".switchroom", "agents", name));
77734
+ dirs.push(join69(home2, ".switchroom", "logs", name));
77735
+ dirs.push(join69(home2, ".claude", "projects", name));
77736
+ dirs.push(join69(home2, ".switchroom", "audit", name));
77737
+ if (existsSync69(join69(home2, ".switchroom-config"))) {
77738
+ dirs.push(join69(home2, ".switchroom-config", "agents", name, "personal-skills"));
77537
77739
  }
77538
77740
  }
77539
77741
  for (const dir of dirs) {
77540
77742
  await mkdir(dir, { recursive: true });
77541
77743
  }
77542
- const autoUnlockPath = join68(home2, ".switchroom", "vault-auto-unlock");
77744
+ const autoUnlockPath = join69(home2, ".switchroom", "vault-auto-unlock");
77543
77745
  if (!existsSync69(autoUnlockPath)) {
77544
77746
  writeFileSync35(autoUnlockPath, "", { mode: 384 });
77545
77747
  }
77546
- const auditLogPath = join68(home2, ".switchroom", "vault-audit.log");
77748
+ const auditLogPath = join69(home2, ".switchroom", "vault-audit.log");
77547
77749
  if (!existsSync69(auditLogPath)) {
77548
77750
  writeFileSync35(auditLogPath, "", { mode: 420 });
77549
77751
  }
77550
- const grantsDbPath = join68(home2, ".switchroom", "vault-grants.db");
77752
+ const grantsDbPath = join69(home2, ".switchroom", "vault-grants.db");
77551
77753
  if (!existsSync69(grantsDbPath)) {
77552
77754
  writeFileSync35(grantsDbPath, "", { mode: 384 });
77553
77755
  }
77554
- const hostdAuditLogPath = join68(home2, ".switchroom", "host-control-audit.log");
77756
+ const hostdAuditLogPath = join69(home2, ".switchroom", "host-control-audit.log");
77555
77757
  if (!existsSync69(hostdAuditLogPath)) {
77556
77758
  writeFileSync35(hostdAuditLogPath, "", { mode: 420 });
77557
77759
  }
77558
77760
  for (const name of Object.keys(config.agents)) {
77559
- const tokenPath = join68(home2, ".switchroom", "agents", name, ".vault-token");
77761
+ const tokenPath = join69(home2, ".switchroom", "agents", name, ".vault-token");
77560
77762
  if (!existsSync69(tokenPath)) {
77561
77763
  writeFileSync35(tokenPath, "", { mode: 384 });
77562
77764
  }
@@ -77565,15 +77767,15 @@ async function ensureHostMountSources(config) {
77565
77767
  chownSync4(tokenPath, uid, uid);
77566
77768
  } catch {}
77567
77769
  }
77568
- const fleetDir = join68(home2, ".switchroom", "fleet");
77770
+ const fleetDir = join69(home2, ".switchroom", "fleet");
77569
77771
  await mkdir(fleetDir, { recursive: true });
77570
- const invariantsPath = join68(fleetDir, "switchroom-invariants.md");
77772
+ const invariantsPath = join69(fleetDir, "switchroom-invariants.md");
77571
77773
  const invariantsCanonical = renderFleetInvariants();
77572
77774
  const invariantsCurrent = existsSync69(invariantsPath) ? readFileSync56(invariantsPath, "utf-8") : null;
77573
77775
  if (invariantsCurrent !== invariantsCanonical) {
77574
77776
  writeFileSync35(invariantsPath, invariantsCanonical, { mode: 420 });
77575
77777
  }
77576
- const fleetClaudePath = join68(fleetDir, "CLAUDE.md");
77778
+ const fleetClaudePath = join69(fleetDir, "CLAUDE.md");
77577
77779
  if (!existsSync69(fleetClaudePath)) {
77578
77780
  writeFileSync35(fleetClaudePath, [
77579
77781
  "# Switchroom fleet defaults",
@@ -77656,10 +77858,10 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
77656
77858
  `));
77657
77859
  }
77658
77860
  }
77659
- function writeInstallTypeCache(homeDir = homedir38()) {
77861
+ function writeInstallTypeCache(homeDir = homedir39()) {
77660
77862
  const ctx = detectInstallType();
77661
- const dir = join68(homeDir, ".switchroom");
77662
- const out = join68(dir, "install-type.json");
77863
+ const dir = join69(homeDir, ".switchroom");
77864
+ const out = join69(dir, "install-type.json");
77663
77865
  const tmp = `${out}.tmp`;
77664
77866
  mkdirSync38(dir, { recursive: true });
77665
77867
  const payload = {
@@ -77708,14 +77910,14 @@ Applying switchroom config...
77708
77910
  writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
77709
77911
  `));
77710
77912
  try {
77711
- installUpdatePromptHook(join68(agentsDir, name));
77913
+ installUpdatePromptHook(join69(agentsDir, name));
77712
77914
  } catch (hookErr) {
77713
77915
  writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
77714
77916
  `));
77715
77917
  }
77716
77918
  try {
77717
77919
  const uid = allocateAgentUid(name);
77718
- alignAgentUid(name, join68(agentsDir, name), uid, {
77920
+ alignAgentUid(name, join69(agentsDir, name), uid, {
77719
77921
  confirm: !options.nonInteractive,
77720
77922
  writeOut
77721
77923
  });
@@ -77752,7 +77954,7 @@ Applying switchroom config...
77752
77954
  for (const name of agentNames) {
77753
77955
  try {
77754
77956
  const uid = allocateAgentUid(name);
77755
- alignAgentUid(name, join68(agentsDir, name), uid, {
77957
+ alignAgentUid(name, join69(agentsDir, name), uid, {
77756
77958
  confirm: !options.nonInteractive,
77757
77959
  writeOut
77758
77960
  });
@@ -77766,7 +77968,7 @@ Applying switchroom config...
77766
77968
  }
77767
77969
  const vaultPathConfigured = config.vault?.path;
77768
77970
  const customVaultPath = vaultPathConfigured ? resolvePath(vaultPathConfigured) : undefined;
77769
- const migrationResult = migrateVaultLayout(homedir38(), {
77971
+ const migrationResult = migrateVaultLayout(homedir39(), {
77770
77972
  customVaultPath
77771
77973
  });
77772
77974
  switch (migrationResult.kind) {
@@ -77792,7 +77994,7 @@ Applying switchroom config...
77792
77994
  writeErr(formatDivergentRecoveryMessage(migrationResult.details));
77793
77995
  process.exit(4);
77794
77996
  }
77795
- const postMigrationInspect = inspectVaultLayout(homedir38());
77997
+ const postMigrationInspect = inspectVaultLayout(homedir39());
77796
77998
  const acceptable = [
77797
77999
  "no-vault",
77798
78000
  "already-migrated",
@@ -77807,7 +78009,7 @@ Applying switchroom config...
77807
78009
  `));
77808
78010
  process.exit(5);
77809
78011
  }
77810
- const vaultDir = resolveVaultBindMountDir(homedir38(), {
78012
+ const vaultDir = resolveVaultBindMountDir(homedir39(), {
77811
78013
  migrationKind: migrationResult.kind,
77812
78014
  customVaultPath
77813
78015
  });
@@ -77837,7 +78039,7 @@ Applying switchroom config...
77837
78039
  imageTag: composeImageTag,
77838
78040
  buildMode: options.buildLocal ? "local" : "pull",
77839
78041
  buildContext: options.buildContext,
77840
- homeDir: homedir38(),
78042
+ homeDir: homedir39(),
77841
78043
  switchroomConfigPath,
77842
78044
  operatorUid
77843
78045
  });
@@ -77857,7 +78059,7 @@ Wrote `) + composePath + source_default.gray(` (${composeBytes} bytes)
77857
78059
  writeOut(source_default.gray(` (If pull returns 401, login to ghcr.io first: see docs/operators/install.md#ghcr-auth)
77858
78060
  `));
77859
78061
  if (process.geteuid?.() === 0 && operatorUid !== undefined) {
77860
- const restored = restoreOperatorOwnership(homedir38(), operatorUid);
78062
+ const restored = restoreOperatorOwnership(homedir39(), operatorUid);
77861
78063
  if (restored.length > 0) {
77862
78064
  writeOut(source_default.gray(` Restored operator ownership of ${restored.length} ~/.switchroom path(s)
77863
78065
  `));
@@ -77927,7 +78129,7 @@ function findUnwritableAgentDirs(config, opts) {
77927
78129
  const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
77928
78130
  const unwritable = [];
77929
78131
  for (const name of targets) {
77930
- const startSh = join68(agentsDir, name, "start.sh");
78132
+ const startSh = join69(agentsDir, name, "start.sh");
77931
78133
  if (!existsSync69(startSh))
77932
78134
  continue;
77933
78135
  try {
@@ -78107,8 +78309,8 @@ function runRedactStdin() {
78107
78309
 
78108
78310
  // src/cli/status-ask.ts
78109
78311
  import { readFileSync as readFileSync57, existsSync as existsSync70, readdirSync as readdirSync26 } from "node:fs";
78110
- import { join as join69 } from "node:path";
78111
- import { homedir as homedir39 } from "node:os";
78312
+ import { join as join70 } from "node:path";
78313
+ import { homedir as homedir40 } from "node:os";
78112
78314
 
78113
78315
  // src/status-ask/report.ts
78114
78316
  function parseJsonl(content) {
@@ -78443,7 +78645,7 @@ function resolveSources(explicitPath) {
78443
78645
  const config = loadConfig();
78444
78646
  agentsDir = resolveAgentsDir(config);
78445
78647
  } catch {
78446
- agentsDir = join69(homedir39(), ".switchroom", "agents");
78648
+ agentsDir = join70(homedir40(), ".switchroom", "agents");
78447
78649
  }
78448
78650
  if (!existsSync70(agentsDir))
78449
78651
  return [];
@@ -78455,7 +78657,7 @@ function resolveSources(explicitPath) {
78455
78657
  return [];
78456
78658
  }
78457
78659
  for (const name of entries) {
78458
- const path8 = join69(agentsDir, name, "runtime-metrics.jsonl");
78660
+ const path8 = join70(agentsDir, name, "runtime-metrics.jsonl");
78459
78661
  if (existsSync70(path8)) {
78460
78662
  sources.push({ path: path8, agent: name });
78461
78663
  }
@@ -78477,17 +78679,17 @@ function inferAgentFromPath(p) {
78477
78679
 
78478
78680
  // src/cli/agent-config.ts
78479
78681
  init_helpers();
78480
- import { join as join70 } from "node:path";
78481
- import { homedir as homedir40 } from "node:os";
78682
+ import { join as join71 } from "node:path";
78683
+ import { homedir as homedir41 } from "node:os";
78482
78684
  import {
78483
78685
  existsSync as existsSync71,
78484
78686
  mkdirSync as mkdirSync39,
78485
78687
  appendFileSync as appendFileSync4,
78486
78688
  readFileSync as readFileSync58
78487
78689
  } from "node:fs";
78488
- var AUDIT_ROOT = join70(homedir40(), ".switchroom", "audit");
78690
+ var AUDIT_ROOT = join71(homedir41(), ".switchroom", "audit");
78489
78691
  function auditPathFor(agent) {
78490
- return join70(AUDIT_ROOT, agent, "agent-config.jsonl");
78692
+ return join71(AUDIT_ROOT, agent, "agent-config.jsonl");
78491
78693
  }
78492
78694
  function appendAudit(agent, cmd, args, exit, opts = {}) {
78493
78695
  const row = {
@@ -78745,21 +78947,21 @@ import {
78745
78947
  unlinkSync as unlinkSync14,
78746
78948
  writeSync as writeSync8
78747
78949
  } from "node:fs";
78748
- import { join as join71, resolve as resolve44 } from "node:path";
78950
+ import { join as join72, resolve as resolve44 } from "node:path";
78749
78951
  var STAGING_SUBDIR = ".staging";
78750
78952
  function overlayPathsFor(agent, opts = {}) {
78751
78953
  const base = opts.root ? resolve44(opts.root, agent) : resolve44(resolveDualPath(`~/.switchroom/agents/${agent}`));
78752
- const scheduleDir = join71(base, "schedule.d");
78753
- const scheduleStagingDir = join71(scheduleDir, STAGING_SUBDIR);
78754
- const skillsDir = join71(base, "skills.d");
78755
- const skillsStagingDir = join71(skillsDir, STAGING_SUBDIR);
78954
+ const scheduleDir = join72(base, "schedule.d");
78955
+ const scheduleStagingDir = join72(scheduleDir, STAGING_SUBDIR);
78956
+ const skillsDir = join72(base, "skills.d");
78957
+ const skillsStagingDir = join72(skillsDir, STAGING_SUBDIR);
78756
78958
  return {
78757
78959
  agentRoot: base,
78758
78960
  scheduleDir,
78759
78961
  scheduleStagingDir,
78760
78962
  skillsDir,
78761
78963
  skillsStagingDir,
78762
- lockPath: join71(base, ".lock"),
78964
+ lockPath: join72(base, ".lock"),
78763
78965
  stagingDir: scheduleStagingDir
78764
78966
  };
78765
78967
  }
@@ -78813,8 +79015,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
78813
79015
  const paths = overlayPathsFor(agent, opts);
78814
79016
  return withAgentLock(paths, () => {
78815
79017
  ensureDirs(paths);
78816
- const stagingPath = join71(paths.scheduleStagingDir, `${slug}.yaml`);
78817
- const finalPath = join71(paths.scheduleDir, `${slug}.yaml`);
79018
+ const stagingPath = join72(paths.scheduleStagingDir, `${slug}.yaml`);
79019
+ const finalPath = join72(paths.scheduleDir, `${slug}.yaml`);
78818
79020
  const fd = openSync13(stagingPath, "w", 384);
78819
79021
  try {
78820
79022
  writeSync8(fd, yamlText);
@@ -78830,8 +79032,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
78830
79032
  const paths = overlayPathsFor(agent, opts);
78831
79033
  return withAgentLock(paths, () => {
78832
79034
  ensureSkillsDirs(paths);
78833
- const stagingPath = join71(paths.skillsStagingDir, `${slug}.yaml`);
78834
- const finalPath = join71(paths.skillsDir, `${slug}.yaml`);
79035
+ const stagingPath = join72(paths.skillsStagingDir, `${slug}.yaml`);
79036
+ const finalPath = join72(paths.skillsDir, `${slug}.yaml`);
78835
79037
  const fd = openSync13(stagingPath, "w", 384);
78836
79038
  try {
78837
79039
  writeSync8(fd, yamlText);
@@ -78846,7 +79048,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
78846
79048
  function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
78847
79049
  const paths = overlayPathsFor(agent, opts);
78848
79050
  return withAgentLock(paths, () => {
78849
- const finalPath = join71(paths.skillsDir, `${slug}.yaml`);
79051
+ const finalPath = join72(paths.skillsDir, `${slug}.yaml`);
78850
79052
  if (!existsSync72(finalPath))
78851
79053
  return false;
78852
79054
  unlinkSync14(finalPath);
@@ -78861,7 +79063,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
78861
79063
  for (const name of readdirSync27(paths.skillsDir)) {
78862
79064
  if (!/\.ya?ml$/i.test(name))
78863
79065
  continue;
78864
- const full = join71(paths.skillsDir, name);
79066
+ const full = join72(paths.skillsDir, name);
78865
79067
  try {
78866
79068
  const raw = readFileSync59(full, "utf-8");
78867
79069
  const slug = name.replace(/\.ya?ml$/i, "");
@@ -78873,7 +79075,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
78873
79075
  function deleteOverlayEntry(agent, slug, opts = {}) {
78874
79076
  const paths = overlayPathsFor(agent, opts);
78875
79077
  return withAgentLock(paths, () => {
78876
- const finalPath = join71(paths.scheduleDir, `${slug}.yaml`);
79078
+ const finalPath = join72(paths.scheduleDir, `${slug}.yaml`);
78877
79079
  if (!existsSync72(finalPath))
78878
79080
  return false;
78879
79081
  unlinkSync14(finalPath);
@@ -78888,7 +79090,7 @@ function listOverlayEntries(agent, opts = {}) {
78888
79090
  for (const name of readdirSync27(paths.scheduleDir)) {
78889
79091
  if (!/\.ya?ml$/i.test(name))
78890
79092
  continue;
78891
- const full = join71(paths.scheduleDir, name);
79093
+ const full = join72(paths.scheduleDir, name);
78892
79094
  try {
78893
79095
  const raw = readFileSync59(full, "utf-8");
78894
79096
  const slug = name.replace(/\.ya?ml$/i, "");
@@ -79044,12 +79246,12 @@ import {
79044
79246
  writeFileSync as writeFileSync36,
79045
79247
  writeSync as writeSync9
79046
79248
  } from "node:fs";
79047
- import { join as join72 } from "node:path";
79249
+ import { join as join73 } from "node:path";
79048
79250
  import { randomBytes as randomBytes14 } from "node:crypto";
79049
79251
  var STAGE_ID_PREFIX = "cap_";
79050
79252
  function pendingDir(agent, opts = {}) {
79051
79253
  const paths = overlayPathsFor(agent, opts);
79052
- return join72(paths.scheduleDir, ".pending");
79254
+ return join73(paths.scheduleDir, ".pending");
79053
79255
  }
79054
79256
  function ensurePendingDir(agent, opts = {}) {
79055
79257
  const dir = pendingDir(agent, opts);
@@ -79062,8 +79264,8 @@ function newStageId() {
79062
79264
  function stagePendingScheduleEntry(opts) {
79063
79265
  const dir = ensurePendingDir(opts.agent, { root: opts.root });
79064
79266
  const stageId = opts.stageId ?? newStageId();
79065
- const yamlPath = join72(dir, `${stageId}.yaml`);
79066
- const metaPath = join72(dir, `${stageId}.meta.json`);
79267
+ const yamlPath = join73(dir, `${stageId}.yaml`);
79268
+ const metaPath = join73(dir, `${stageId}.meta.json`);
79067
79269
  const meta = {
79068
79270
  v: 1,
79069
79271
  stage_id: stageId,
@@ -79097,8 +79299,8 @@ function listPendingScheduleEntries(agent, opts = {}) {
79097
79299
  if (!name.endsWith(".meta.json"))
79098
79300
  continue;
79099
79301
  const stageId = name.slice(0, -".meta.json".length);
79100
- const metaPath = join72(dir, name);
79101
- const yamlPath = join72(dir, `${stageId}.yaml`);
79302
+ const metaPath = join73(dir, name);
79303
+ const yamlPath = join73(dir, `${stageId}.yaml`);
79102
79304
  if (!existsSync73(yamlPath))
79103
79305
  continue;
79104
79306
  try {
@@ -79117,7 +79319,7 @@ function commitPendingScheduleEntry(opts) {
79117
79319
  return { committed: false, reason: "not_found" };
79118
79320
  const slug = match.meta.entry.name ?? match.stageId;
79119
79321
  const paths = overlayPathsFor(opts.agent, { root: opts.root });
79120
- const finalPath = join72(paths.scheduleDir, `${slug}.yaml`);
79322
+ const finalPath = join73(paths.scheduleDir, `${slug}.yaml`);
79121
79323
  if (existsSync73(finalPath)) {
79122
79324
  return { committed: false, reason: "slug_collision" };
79123
79325
  }
@@ -79623,7 +79825,7 @@ var import_yaml16 = __toESM(require_dist(), 1);
79623
79825
  import { existsSync as existsSync75 } from "node:fs";
79624
79826
  init_reconcile_default_skills();
79625
79827
  var import_yaml17 = __toESM(require_dist(), 1);
79626
- import { join as join73 } from "node:path";
79828
+ import { join as join74 } from "node:path";
79627
79829
  var MAX_SKILLS_PER_AGENT = 20;
79628
79830
  var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
79629
79831
  function exitCodeFor2(code) {
@@ -79698,7 +79900,7 @@ function skillInstall(opts) {
79698
79900
  return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
79699
79901
  }
79700
79902
  const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
79701
- const skillPath = join73(poolDir, skillName);
79903
+ const skillPath = join74(poolDir, skillName);
79702
79904
  if (!existsSync75(skillPath)) {
79703
79905
  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.`);
79704
79906
  }
@@ -79876,8 +80078,8 @@ import {
79876
80078
  statSync as statSync29,
79877
80079
  writeFileSync as writeFileSync37
79878
80080
  } from "node:fs";
79879
- import { tmpdir as tmpdir4, homedir as homedir41 } from "node:os";
79880
- import { dirname as dirname22, join as join74, relative as relative2, resolve as resolve45 } from "node:path";
80081
+ import { tmpdir as tmpdir4, homedir as homedir42 } from "node:os";
80082
+ import { dirname as dirname22, join as join75, relative as relative2, resolve as resolve45 } from "node:path";
79881
80083
  import { spawnSync as spawnSync10 } from "node:child_process";
79882
80084
 
79883
80085
  // src/cli/skill-common.ts
@@ -80071,10 +80273,10 @@ function scanForClaudeP2(content) {
80071
80273
  function resolveSkillsPoolDir2(override) {
80072
80274
  const raw = override ?? "~/.switchroom/skills";
80073
80275
  if (raw.startsWith("~/")) {
80074
- return join74(homedir41(), raw.slice(2));
80276
+ return join75(homedir42(), raw.slice(2));
80075
80277
  }
80076
80278
  if (raw === "~")
80077
- return homedir41();
80279
+ return homedir42();
80078
80280
  return resolve45(raw);
80079
80281
  }
80080
80282
  function readStdinSync() {
@@ -80110,7 +80312,7 @@ function loadFromDir(dir) {
80110
80312
  const walk2 = (sub) => {
80111
80313
  const entries = readdirSync29(sub, { withFileTypes: true });
80112
80314
  for (const ent of entries) {
80113
- const full = join74(sub, ent.name);
80315
+ const full = join75(sub, ent.name);
80114
80316
  const rel = relative2(abs, full);
80115
80317
  if (ent.isSymbolicLink()) {
80116
80318
  fail2(`refusing to read symlink inside --from dir: ${rel}`);
@@ -80145,7 +80347,7 @@ function loadFromTarball(tarPath) {
80145
80347
  fail2(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
80146
80348
  }
80147
80349
  }
80148
- const staging = mkdtempSync5(join74(tmpdir4(), "skill-apply-extract-"));
80350
+ const staging = mkdtempSync5(join75(tmpdir4(), "skill-apply-extract-"));
80149
80351
  try {
80150
80352
  const flags = isGz ? ["-xzf"] : ["-xf"];
80151
80353
  const r = spawnSync10("tar", [
@@ -80231,8 +80433,8 @@ function validatePayload(name, files) {
80231
80433
  errors2.push(`${path8} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
80232
80434
  }
80233
80435
  } else if (PY_SCRIPT_RE2.test(path8)) {
80234
- const tmp = mkdtempSync5(join74(tmpdir4(), "skill-apply-py-"));
80235
- const tmpPy = join74(tmp, "check.py");
80436
+ const tmp = mkdtempSync5(join75(tmpdir4(), "skill-apply-py-"));
80437
+ const tmpPy = join75(tmp, "check.py");
80236
80438
  try {
80237
80439
  writeFileSync37(tmpPy, content);
80238
80440
  const r = spawnSync10("python3", ["-m", "py_compile", tmpPy], {
@@ -80255,7 +80457,7 @@ function diffSummary(currentDir, files) {
80255
80457
  if (existsSync76(currentDir)) {
80256
80458
  const walk2 = (sub) => {
80257
80459
  for (const ent of readdirSync29(sub, { withFileTypes: true })) {
80258
- const full = join74(sub, ent.name);
80460
+ const full = join75(sub, ent.name);
80259
80461
  const rel = relative2(currentDir, full);
80260
80462
  if (ent.isDirectory()) {
80261
80463
  walk2(full);
@@ -80291,7 +80493,7 @@ function writePayload(poolDir, name, files) {
80291
80493
  if (!existsSync76(poolDir)) {
80292
80494
  mkdirSync42(poolDir, { recursive: true, mode: 493 });
80293
80495
  }
80294
- const target = join74(poolDir, name);
80496
+ const target = join75(poolDir, name);
80295
80497
  let targetIsSymlink = false;
80296
80498
  try {
80297
80499
  const st = lstatSync8(target);
@@ -80302,11 +80504,11 @@ function writePayload(poolDir, name, files) {
80302
80504
  if (targetIsSymlink) {
80303
80505
  fail2(`refusing to overwrite symlink at ${target}; investigate manually`);
80304
80506
  }
80305
- const staging = mkdtempSync5(join74(poolDir, `.skill-apply-stage-${name}-`));
80507
+ const staging = mkdtempSync5(join75(poolDir, `.skill-apply-stage-${name}-`));
80306
80508
  let oldRename = null;
80307
80509
  try {
80308
80510
  for (const [path8, content] of Object.entries(files)) {
80309
- const full = join74(staging, path8);
80511
+ const full = join75(staging, path8);
80310
80512
  mkdirSync42(dirname22(full), { recursive: true, mode: 493 });
80311
80513
  const fd = openSync15(full, "wx");
80312
80514
  try {
@@ -80384,7 +80586,7 @@ function registerSkillCommand(program3) {
80384
80586
  }
80385
80587
  const config = loadConfig();
80386
80588
  const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
80387
- const currentDir = join74(poolDir, name);
80589
+ const currentDir = join75(poolDir, name);
80388
80590
  console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
80389
80591
  console.log(source_default.bold("Diff vs current pool content:"));
80390
80592
  console.log(diffSummary(currentDir, files));
@@ -80428,8 +80630,8 @@ import {
80428
80630
  utimesSync,
80429
80631
  writeFileSync as writeFileSync38
80430
80632
  } from "node:fs";
80431
- import { dirname as dirname23, join as join75, relative as relative3, resolve as resolve46 } from "node:path";
80432
- import { homedir as homedir42, tmpdir as tmpdir5 } from "node:os";
80633
+ import { dirname as dirname23, join as join76, relative as relative3, resolve as resolve46 } from "node:path";
80634
+ import { homedir as homedir43, tmpdir as tmpdir5 } from "node:os";
80433
80635
  import { spawnSync as spawnSync11 } from "node:child_process";
80434
80636
  init_helpers();
80435
80637
  init_source();
@@ -80439,10 +80641,10 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
80439
80641
  var PERSONAL_SKILLS_SUBPATH = "personal-skills";
80440
80642
  function resolveConfigSkillsDir(agent) {
80441
80643
  const override = process.env.SWITCHROOM_CONFIG_DIR;
80442
- const candidate = override ? resolve46(override) : join75(homedir42(), ".switchroom-config");
80644
+ const candidate = override ? resolve46(override) : join76(homedir43(), ".switchroom-config");
80443
80645
  if (!existsSync77(candidate))
80444
80646
  return null;
80445
- return join75(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
80647
+ return join76(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
80446
80648
  }
80447
80649
  var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
80448
80650
  function sweepMirrorPriors(configSkillsRoot) {
@@ -80460,7 +80662,7 @@ function sweepMirrorPriors(configSkillsRoot) {
80460
80662
  if (now - ts < MIRROR_PRIOR_TTL_MS)
80461
80663
  continue;
80462
80664
  try {
80463
- rmSync17(join75(configSkillsRoot, ent), { recursive: true, force: true });
80665
+ rmSync17(join76(configSkillsRoot, ent), { recursive: true, force: true });
80464
80666
  } catch {}
80465
80667
  }
80466
80668
  } catch {}
@@ -80469,7 +80671,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
80469
80671
  const configSkillsRoot = resolveConfigSkillsDir(agent);
80470
80672
  if (!configSkillsRoot)
80471
80673
  return;
80472
- const dest = join75(configSkillsRoot, name);
80674
+ const dest = join76(configSkillsRoot, name);
80473
80675
  try {
80474
80676
  if (liveSkillDir !== null) {
80475
80677
  try {
@@ -80484,19 +80686,19 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
80484
80686
  if (liveSkillDir === null) {
80485
80687
  sweepMirrorPriors(configSkillsRoot);
80486
80688
  if (existsSync77(dest)) {
80487
- const trash = join75(configSkillsRoot, `.${name}-trash-${Date.now()}`);
80689
+ const trash = join76(configSkillsRoot, `.${name}-trash-${Date.now()}`);
80488
80690
  renameSync17(dest, trash);
80489
80691
  }
80490
80692
  return;
80491
80693
  }
80492
80694
  mkdirSync43(configSkillsRoot, { recursive: true, mode: 493 });
80493
80695
  sweepMirrorPriors(configSkillsRoot);
80494
- const staging = mkdtempSync6(join75(configSkillsRoot, `.${name}-staging-`));
80696
+ const staging = mkdtempSync6(join76(configSkillsRoot, `.${name}-staging-`));
80495
80697
  const walk2 = (src, dst) => {
80496
80698
  mkdirSync43(dst, { recursive: true, mode: 493 });
80497
80699
  for (const ent of readdirSync30(src, { withFileTypes: true })) {
80498
- const s = join75(src, ent.name);
80499
- const d = join75(dst, ent.name);
80700
+ const s = join76(src, ent.name);
80701
+ const d = join76(dst, ent.name);
80500
80702
  if (ent.isSymbolicLink())
80501
80703
  continue;
80502
80704
  if (ent.isDirectory())
@@ -80508,7 +80710,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
80508
80710
  };
80509
80711
  walk2(liveSkillDir, staging);
80510
80712
  if (existsSync77(dest)) {
80511
- const prior = join75(configSkillsRoot, `.${name}-prior-${Date.now()}`);
80713
+ const prior = join76(configSkillsRoot, `.${name}-prior-${Date.now()}`);
80512
80714
  renameSync17(dest, prior);
80513
80715
  }
80514
80716
  renameSync17(staging, dest);
@@ -80535,13 +80737,13 @@ function resolveAgent(opts) {
80535
80737
  function resolveAgentsRoot(opts) {
80536
80738
  if (opts.root)
80537
80739
  return resolve46(opts.root);
80538
- return join75(homedir42(), ".switchroom", "agents");
80740
+ return join76(homedir43(), ".switchroom", "agents");
80539
80741
  }
80540
80742
  function personalSkillDir(agentsRoot, agent, name) {
80541
- return join75(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
80743
+ return join76(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
80542
80744
  }
80543
80745
  function trashDir(agentsRoot, agent) {
80544
- return join75(agentsRoot, agent, ".claude", TRASH_DIRNAME);
80746
+ return join76(agentsRoot, agent, ".claude", TRASH_DIRNAME);
80545
80747
  }
80546
80748
  function readStdinSync2() {
80547
80749
  const chunks = [];
@@ -80571,7 +80773,7 @@ function loadFromDir2(dir) {
80571
80773
  const files = {};
80572
80774
  const walk2 = (sub) => {
80573
80775
  for (const ent of readdirSync30(sub, { withFileTypes: true })) {
80574
- const full = join75(sub, ent.name);
80776
+ const full = join76(sub, ent.name);
80575
80777
  if (ent.isSymbolicLink()) {
80576
80778
  fail3(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
80577
80779
  }
@@ -80624,8 +80826,8 @@ function behavioralValidate(files) {
80624
80826
  errors2.push(`${path8} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
80625
80827
  }
80626
80828
  } else if (PY_SCRIPT_RE.test(path8)) {
80627
- const tmp = mkdtempSync6(join75(tmpdir5(), "skill-personal-py-"));
80628
- const tmpPy = join75(tmp, "check.py");
80829
+ const tmp = mkdtempSync6(join76(tmpdir5(), "skill-personal-py-"));
80830
+ const tmpPy = join76(tmp, "check.py");
80629
80831
  try {
80630
80832
  writeFileSync38(tmpPy, content);
80631
80833
  const r = spawnSync11("python3", ["-m", "py_compile", tmpPy], {
@@ -80649,7 +80851,7 @@ function sweepTrash(agentsRoot, agent) {
80649
80851
  for (const ent of readdirSync30(trash, { withFileTypes: true })) {
80650
80852
  if (!ent.isDirectory())
80651
80853
  continue;
80652
- const entPath = join75(trash, ent.name);
80854
+ const entPath = join76(trash, ent.name);
80653
80855
  try {
80654
80856
  const st = statSync30(entPath);
80655
80857
  if (now - st.mtimeMs > TRASH_TTL_MS) {
@@ -80670,11 +80872,11 @@ function writePersonalSkill(targetDir, files) {
80670
80872
  fail3(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
80671
80873
  }
80672
80874
  mkdirSync43(dirname23(targetDir), { recursive: true, mode: 493 });
80673
- const staging = mkdtempSync6(join75(dirname23(targetDir), `.skill-personal-stage-`));
80875
+ const staging = mkdtempSync6(join76(dirname23(targetDir), `.skill-personal-stage-`));
80674
80876
  let oldRename = null;
80675
80877
  try {
80676
80878
  for (const [path8, content] of Object.entries(files)) {
80677
- const full = join75(staging, path8);
80879
+ const full = join76(staging, path8);
80678
80880
  mkdirSync43(dirname23(full), { recursive: true, mode: 493 });
80679
80881
  const fd = openSync16(full, "wx");
80680
80882
  try {
@@ -80807,10 +81009,10 @@ function editPersonalAction(name, opts) {
80807
81009
  }
80808
81010
  var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
80809
81011
  function defaultSharedRoot() {
80810
- return join75(homedir42(), ".switchroom", "skills");
81012
+ return join76(homedir43(), ".switchroom", "skills");
80811
81013
  }
80812
81014
  function defaultBundledRoot() {
80813
- return join75(homedir42(), ".switchroom", "skills", "_bundled");
81015
+ return join76(homedir43(), ".switchroom", "skills", "_bundled");
80814
81016
  }
80815
81017
  function resolveCloneSource(source, opts) {
80816
81018
  const m = CLONE_SOURCE_RE.exec(source);
@@ -80820,7 +81022,7 @@ function resolveCloneSource(source, opts) {
80820
81022
  const tier = m[1];
80821
81023
  const slug = m[2];
80822
81024
  const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
80823
- const dir = join75(root, slug);
81025
+ const dir = join76(root, slug);
80824
81026
  if (!existsSync77(dir)) {
80825
81027
  fail3(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
80826
81028
  }
@@ -80836,7 +81038,7 @@ function readSourceFiles(dir) {
80836
81038
  const skipped = [];
80837
81039
  const walk2 = (sub) => {
80838
81040
  for (const ent of readdirSync30(sub, { withFileTypes: true })) {
80839
- const full = join75(sub, ent.name);
81041
+ const full = join76(sub, ent.name);
80840
81042
  if (ent.isSymbolicLink()) {
80841
81043
  continue;
80842
81044
  }
@@ -80947,7 +81149,7 @@ function removePersonalAction(name, opts) {
80947
81149
  const trashRoot = trashDir(agentsRoot, agent);
80948
81150
  mkdirSync43(trashRoot, { recursive: true, mode: 493 });
80949
81151
  const ts = Date.now();
80950
- const trashTarget = join75(trashRoot, `${name}-${ts}`);
81152
+ const trashTarget = join76(trashRoot, `${name}-${ts}`);
80951
81153
  renameSync17(target, trashTarget);
80952
81154
  const now = new Date(ts);
80953
81155
  utimesSync(trashTarget, now, now);
@@ -80966,7 +81168,7 @@ function listPersonalAction(opts) {
80966
81168
  const agent = resolveAgent(opts);
80967
81169
  const agentsRoot = resolveAgentsRoot(opts);
80968
81170
  sweepTrash(agentsRoot, agent);
80969
- const skillsDir = join75(agentsRoot, agent, ".claude", "skills");
81171
+ const skillsDir = join76(agentsRoot, agent, ".claude", "skills");
80970
81172
  const personal = [];
80971
81173
  if (existsSync77(skillsDir)) {
80972
81174
  for (const ent of readdirSync30(skillsDir, { withFileTypes: true })) {
@@ -80975,7 +81177,7 @@ function listPersonalAction(opts) {
80975
81177
  if (!ent.name.startsWith(PERSONAL_PREFIX))
80976
81178
  continue;
80977
81179
  const skillName = ent.name.slice(PERSONAL_PREFIX.length);
80978
- const skillPath = join75(skillsDir, ent.name);
81180
+ const skillPath = join76(skillsDir, ent.name);
80979
81181
  let fileCount = 0;
80980
81182
  let totalBytes = 0;
80981
81183
  const walk2 = (sub) => {
@@ -80983,10 +81185,10 @@ function listPersonalAction(opts) {
80983
81185
  if (e.isFile()) {
80984
81186
  fileCount += 1;
80985
81187
  try {
80986
- totalBytes += statSync30(join75(sub, e.name)).size;
81188
+ totalBytes += statSync30(join76(sub, e.name)).size;
80987
81189
  } catch {}
80988
81190
  } else if (e.isDirectory()) {
80989
- walk2(join75(sub, e.name));
81191
+ walk2(join76(sub, e.name));
80990
81192
  }
80991
81193
  }
80992
81194
  };
@@ -81026,22 +81228,22 @@ function registerSkillPersonalCommands(program3) {
81026
81228
  init_helpers();
81027
81229
  var import_yaml19 = __toESM(require_dist(), 1);
81028
81230
  import { existsSync as existsSync78, readdirSync as readdirSync31, readFileSync as readFileSync64, statSync as statSync31 } from "node:fs";
81029
- import { homedir as homedir43 } from "node:os";
81030
- import { join as join76, resolve as resolve47 } from "node:path";
81231
+ import { homedir as homedir44 } from "node:os";
81232
+ import { join as join77, resolve as resolve47 } from "node:path";
81031
81233
  var PERSONAL_PREFIX2 = "personal-";
81032
81234
  var BUNDLED_SUBDIR = "_bundled";
81033
81235
  var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
81034
81236
  function defaultAgentsRoot() {
81035
- return resolve47(homedir43(), ".switchroom/agents");
81237
+ return resolve47(homedir44(), ".switchroom/agents");
81036
81238
  }
81037
81239
  function defaultSharedRoot2() {
81038
- return resolve47(homedir43(), ".switchroom/skills");
81240
+ return resolve47(homedir44(), ".switchroom/skills");
81039
81241
  }
81040
81242
  function defaultBundledRoot2() {
81041
- return resolve47(homedir43(), ".switchroom/skills/_bundled");
81243
+ return resolve47(homedir44(), ".switchroom/skills/_bundled");
81042
81244
  }
81043
81245
  function readSkillFrontmatter(skillDir) {
81044
- const mdPath = join76(skillDir, "SKILL.md");
81246
+ const mdPath = join77(skillDir, "SKILL.md");
81045
81247
  if (!existsSync78(mdPath))
81046
81248
  return null;
81047
81249
  let content;
@@ -81074,7 +81276,7 @@ function readSkillFrontmatter(skillDir) {
81074
81276
  return { fm: parsed };
81075
81277
  }
81076
81278
  function statSkillMd(skillDir) {
81077
- const mdPath = join76(skillDir, "SKILL.md");
81279
+ const mdPath = join77(skillDir, "SKILL.md");
81078
81280
  try {
81079
81281
  const st = statSync31(mdPath);
81080
81282
  return { size: st.size, mtime: st.mtime.toISOString() };
@@ -81085,7 +81287,7 @@ function statSkillMd(skillDir) {
81085
81287
  function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
81086
81288
  if (!AGENT_NAME_RE3.test(agent))
81087
81289
  return [];
81088
- const skillsDir = join76(agentsRoot, agent, ".claude/skills");
81290
+ const skillsDir = join77(agentsRoot, agent, ".claude/skills");
81089
81291
  if (!existsSync78(skillsDir))
81090
81292
  return [];
81091
81293
  const out = [];
@@ -81098,7 +81300,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
81098
81300
  for (const ent of entries) {
81099
81301
  if (!ent.startsWith(PERSONAL_PREFIX2))
81100
81302
  continue;
81101
- const dirPath = join76(skillsDir, ent);
81303
+ const dirPath = join77(skillsDir, ent);
81102
81304
  try {
81103
81305
  if (!statSync31(dirPath).isDirectory())
81104
81306
  continue;
@@ -81138,7 +81340,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
81138
81340
  continue;
81139
81341
  if (ent.startsWith("."))
81140
81342
  continue;
81141
- const dirPath = join76(sharedRoot, ent);
81343
+ const dirPath = join77(sharedRoot, ent);
81142
81344
  try {
81143
81345
  if (!statSync31(dirPath).isDirectory())
81144
81346
  continue;
@@ -81174,7 +81376,7 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
81174
81376
  for (const ent of entries) {
81175
81377
  if (ent.startsWith("."))
81176
81378
  continue;
81177
- const dirPath = join76(bundledRoot, ent);
81379
+ const dirPath = join77(bundledRoot, ent);
81178
81380
  try {
81179
81381
  if (!statSync31(dirPath).isDirectory())
81180
81382
  continue;
@@ -81319,8 +81521,8 @@ function registerHostdMcpCommand(program3) {
81319
81521
  init_source();
81320
81522
  init_helpers();
81321
81523
  import { existsSync as existsSync80, mkdirSync as mkdirSync44, readdirSync as readdirSync32, readFileSync as readFileSync66, writeFileSync as writeFileSync39, statSync as statSync32, copyFileSync as copyFileSync12 } from "node:fs";
81322
- import { homedir as homedir44 } from "node:os";
81323
- import { join as join77 } from "node:path";
81524
+ import { homedir as homedir45 } from "node:os";
81525
+ import { join as join78 } from "node:path";
81324
81526
  import { spawnSync as spawnSync13 } from "node:child_process";
81325
81527
  init_audit_reader();
81326
81528
  var DEFAULT_IMAGE_TAG = "latest";
@@ -81411,10 +81613,10 @@ networks:
81411
81613
  `;
81412
81614
  }
81413
81615
  function hostdDir() {
81414
- return join77(homedir44(), ".switchroom", "hostd");
81616
+ return join78(homedir45(), ".switchroom", "hostd");
81415
81617
  }
81416
81618
  function hostdComposePath() {
81417
- return join77(hostdDir(), "docker-compose.yml");
81619
+ return join78(hostdDir(), "docker-compose.yml");
81418
81620
  }
81419
81621
  function backupExistingCompose() {
81420
81622
  const p = hostdComposePath();
@@ -81453,7 +81655,7 @@ async function doInstall(opts, program3) {
81453
81655
  const composePath = hostdComposePath();
81454
81656
  mkdirSync44(dir, { recursive: true });
81455
81657
  const yaml = renderHostdComposeFile({
81456
- hostHome: homedir44(),
81658
+ hostHome: homedir45(),
81457
81659
  imageTag: opts.tag ?? DEFAULT_IMAGE_TAG,
81458
81660
  operatorUid: resolveOperatorUid()
81459
81661
  });
@@ -81522,7 +81724,7 @@ function doStatus() {
81522
81724
  for (const name of readdirSync32(dir)) {
81523
81725
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
81524
81726
  continue;
81525
- const sockPath = join77(dir, name, "sock");
81727
+ const sockPath = join78(dir, name, "sock");
81526
81728
  if (existsSync80(sockPath)) {
81527
81729
  const st = statSync32(sockPath);
81528
81730
  if ((st.mode & 61440) === 49152) {