switchroom 0.15.11 → 0.15.13

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.
@@ -13711,6 +13711,12 @@ var init_schema = __esm(() => {
13711
13711
  }).optional().describe("Per-source rate limit for the webhook ingest path (#714). " + "Off by default \u2014 when this key is absent the handler skips " + "rate-limit checks entirely. Opt in by setting `rpm` to an " + "integer requests-per-minute (token bucket per (agent, source); " + "burst equal to rpm). When enabled, exceeding the limit returns " + "429 with Retry-After header; first throttle event per " + "(agent, source) per 60s window is written to " + "<agent>/telegram/issues.jsonl. " + "Cascades from defaults.channels.telegram.webhook_rate_limit."),
13712
13712
  webhook_via_gateway: exports_external.boolean().optional().describe("Route verified webhook events to the agent's in-container gateway " + "over a peercred-gated UDS (<agent>/telegram/webhook.sock) instead " + "of having the host-side web receiver write the agent dir directly. " + "Required under the Docker runtime: the receiver runs as the host " + "operator UID and cannot write the per-agent-UID-owned agent dir " + "(EACCES 500) nor connect the gateway socket. When true the gateway " + "(running as the agent UID) becomes the sole writer of " + "webhook-events.jsonl + dedup/cooldown state and also fires " + "webhook_dispatch. Off by default for back-compat with host-runtime " + "installs. See docs/rfcs/webhook-via-gateway-socket.md."),
13713
13713
  webhook_require_edge: exports_external.boolean().optional().describe("Cloudflare-only edge lock: require the X-Switchroom-Edge header " + "(injected by a Cloudflare Transform Rule on hooks.switchroom.ai) to " + "match the operator's edge secret at ~/.switchroom/webhook-edge-secret " + "before any HMAC verification; reject 403 otherwise. Proves the " + "request entered through our Cloudflare edge \u2014 the per-agent HMAC " + "alone can't (it proves body provenance, not network path). Stacks " + "on the GitHub-IP WAF + per-agent HMAC. Fail-closed: when required " + "but the secret file is missing/empty every request is rejected. Off " + "by default. See docs/rfcs/webhook-cloudflare-edge-lock.md."),
13714
+ linear_agent: exports_external.object({
13715
+ enabled: exports_external.boolean(),
13716
+ token: exports_external.string().describe("vault:<key> reference to the Linear OAuth app token (actor=app). " + "Resolved at runtime via the vault broker (canonically " + "vault:linear/<agent>/token). Never an inline literal."),
13717
+ workspace_id: exports_external.string().optional().describe("Optional Linear workspace (organization) id this agent is " + "installed into. Informational \u2014 used for setup hints and " + "multi-workspace disambiguation; the token already scopes the " + "app to its workspace."),
13718
+ default_team_id: exports_external.string().optional().describe("Optional Linear team id new captured issues file into when the " + "agent doesn't pass an explicit team_id. Unnecessary for a " + "single-team workspace (auto-resolved); set it only when the " + "workspace has multiple teams. Manage via " + "`switchroom linear-agent set-team <agent> <team>`.")
13719
+ }).optional().describe("Linear first-class agent integration (#2298). When enabled, the " + "agent appears in a Linear workspace as an app actor (own name/" + "avatar, @-mentionable, delegate-assignable). Linear AgentSessionEvent " + "webhooks (mention / delegation) wake the agent instantly via the " + "same gateway inject path as webhook_dispatch, tagged " + 'meta.source="linear" with the agent_session_id, and the agent ' + "responds with structured AgentActivity (thought/message/complete/" + "error) via the linear_agent_activity MCP tool. Builds the " + "session-lifecycle layer on top of the plain webhook_sources:[linear] " + "+ webhook_dispatch support (#2272). The OAuth app token is stored in " + "the vault and referenced here as vault:linear/<agent>/token; run " + "`switchroom linear-agent setup <agent>` to provision it. Off by " + "default \u2014 opt in per agent. Cascades from " + "defaults.channels.telegram.linear_agent."),
13714
13720
  chat_id: exports_external.string().regex(/^-\d+$/, 'supergroup chat_id must be a negative integer as a string (e.g. "-1001234567890")').optional().describe("Per-agent supergroup ID \u2014 overrides fleet `telegram.forum_chat_id`. " + "When set, requires `default_topic_id`. Negative integer as string. " + "Forbidden when `dm_only: true`. See docs/rfcs/supergroup-mode.md."),
13715
13721
  default_topic_id: exports_external.number().int().positive().optional().describe("Forum topic ID this agent's automated outbounds default to when " + "no more-specific alias resolves. Defaults to General (topic 1) when " + "`chat_id` is set and this is omitted \u2014 set it only to pin a different " + "fallback topic. " + "Telegram's General topic is `id=1` at MTProto but sends omit the " + "field \u2014 the outbound wrapper strips `message_thread_id === 1` " + "on send. Forbidden when `dm_only: true`."),
13716
13722
  topic_aliases: exports_external.record(exports_external.string(), exports_external.number().int().positive()).optional().describe("Operator-friendly names for forum topic IDs (e.g. " + "`{ general: 1, planning: 17, cron: 23, admin: 31, alerts: 41 }`). " + "Referenced from per-cron `topic:` fields and the outbound router " + "for autonomous events (boot \u2192 alerts, hostd \u2192 admin, etc.). " + "Cascades per-key through defaults \u2192 profile \u2192 agent.")
@@ -14635,6 +14641,16 @@ function mergeAgentConfig(defaultsIn, agentIn) {
14635
14641
  }
14636
14642
  merged.reaction_dispatch = combined;
14637
14643
  }
14644
+ const linearEnabled = merged.channels?.telegram?.linear_agent?.enabled === true;
14645
+ if (linearEnabled) {
14646
+ const rd = merged.reaction_dispatch;
14647
+ if (!rd || rd.emojis === undefined) {
14648
+ merged.reaction_dispatch = {
14649
+ enabled: rd?.enabled ?? true,
14650
+ emojis: ["\uD83D\uDC68\u200d\uD83D\uDCBB", "\uD83D\uDCCC"]
14651
+ };
14652
+ }
14653
+ }
14638
14654
  if (defaults.resources || merged.resources) {
14639
14655
  const d = defaults.resources ?? {};
14640
14656
  const a = merged.resources ?? {};
@@ -29166,7 +29182,7 @@ var init_thinking_effort_risk = __esm(() => {
29166
29182
  // src/manifest.ts
29167
29183
  import {
29168
29184
  existsSync as existsSync52,
29169
- readFileSync as readFileSync47,
29185
+ readFileSync as readFileSync48,
29170
29186
  readdirSync as readdirSync19
29171
29187
  } from "node:fs";
29172
29188
  import { dirname as dirname12, join as join47 } from "node:path";
@@ -29188,7 +29204,7 @@ function loadManifest(manifestPath) {
29188
29204
  }
29189
29205
  let raw;
29190
29206
  try {
29191
- raw = readFileSync47(path4, "utf-8");
29207
+ raw = readFileSync48(path4, "utf-8");
29192
29208
  } catch (err) {
29193
29209
  throw new Error(`Failed to read manifest at ${path4}: ${err.message}`);
29194
29210
  }
@@ -29253,7 +29269,7 @@ function probePlaywrightMcpVersion() {
29253
29269
  const pkgPath = join47(npxCache, entry, "node_modules/@playwright/mcp/package.json");
29254
29270
  if (existsSync52(pkgPath)) {
29255
29271
  try {
29256
- const pkg = JSON.parse(readFileSync47(pkgPath, "utf-8"));
29272
+ const pkg = JSON.parse(readFileSync48(pkgPath, "utf-8"));
29257
29273
  if (pkg.version)
29258
29274
  return pkg.version;
29259
29275
  } catch {}
@@ -29488,7 +29504,7 @@ var init_doctor_memory = __esm(() => {
29488
29504
  });
29489
29505
 
29490
29506
  // src/cli/doctor-docker.ts
29491
- import { readFileSync as readFileSync48 } from "node:fs";
29507
+ import { readFileSync as readFileSync49 } from "node:fs";
29492
29508
  function imageTagOf(ref) {
29493
29509
  if (!ref)
29494
29510
  return "<absent>";
@@ -29503,7 +29519,7 @@ function isDockerMode(opts) {
29503
29519
  return true;
29504
29520
  if (opts?.composePath) {
29505
29521
  try {
29506
- readFileSync48(opts.composePath, "utf8");
29522
+ readFileSync49(opts.composePath, "utf8");
29507
29523
  return true;
29508
29524
  } catch {}
29509
29525
  }
@@ -29692,7 +29708,7 @@ var init_doctor_docker = __esm(() => {
29692
29708
  });
29693
29709
 
29694
29710
  // src/cli/doctor-auth-broker.ts
29695
- import { existsSync as existsSync53, readFileSync as readFileSync49 } from "node:fs";
29711
+ import { existsSync as existsSync53, readFileSync as readFileSync50 } from "node:fs";
29696
29712
  import { createHash as createHash10 } from "node:crypto";
29697
29713
  import { spawnSync as spawnSync6 } from "node:child_process";
29698
29714
  import { homedir as homedir26 } from "node:os";
@@ -29806,7 +29822,7 @@ function checkAuthBrokerDrift(deps = {}) {
29806
29822
  }
29807
29823
  let index;
29808
29824
  try {
29809
- index = JSON.parse(readFileSync49(indexPath, "utf-8"));
29825
+ index = JSON.parse(readFileSync50(indexPath, "utf-8"));
29810
29826
  } catch (err) {
29811
29827
  return {
29812
29828
  name: "auth-broker: drift",
@@ -29826,7 +29842,7 @@ function checkAuthBrokerDrift(deps = {}) {
29826
29842
  }
29827
29843
  let got;
29828
29844
  try {
29829
- got = sha256Hex(readFileSync49(credsPath, "utf-8"));
29845
+ got = sha256Hex(readFileSync50(credsPath, "utf-8"));
29830
29846
  } catch (err) {
29831
29847
  divergent.push(`${label} (read failed: ${err.message})`);
29832
29848
  continue;
@@ -29867,7 +29883,7 @@ function checkAuthBrokerThresholdViolations(deps = {}) {
29867
29883
  }
29868
29884
  let violations;
29869
29885
  try {
29870
- violations = JSON.parse(readFileSync49(path4, "utf-8"));
29886
+ violations = JSON.parse(readFileSync50(path4, "utf-8"));
29871
29887
  } catch (err) {
29872
29888
  return {
29873
29889
  name: "auth-broker: threshold violations",
@@ -31903,7 +31919,7 @@ import {
31903
31919
  existsSync as existsSync57,
31904
31920
  lstatSync as lstatSync6,
31905
31921
  mkdirSync as mkdirSync31,
31906
- readFileSync as readFileSync50,
31922
+ readFileSync as readFileSync51,
31907
31923
  readdirSync as readdirSync21,
31908
31924
  statSync as statSync25
31909
31925
  } from "node:fs";
@@ -32663,7 +32679,7 @@ function classifyReadError(err) {
32663
32679
  }
32664
32680
  function tryReadHostFile(path4) {
32665
32681
  try {
32666
- return { kind: "ok", content: readFileSync50(path4, "utf-8") };
32682
+ return { kind: "ok", content: readFileSync51(path4, "utf-8") };
32667
32683
  } catch (err) {
32668
32684
  const kind = classifyReadError(err);
32669
32685
  const error = err?.message ?? String(err);
@@ -32679,7 +32695,7 @@ function parseEnvFile(path4) {
32679
32695
  return {};
32680
32696
  let content;
32681
32697
  try {
32682
- content = readFileSync50(path4, "utf-8");
32698
+ content = readFileSync51(path4, "utf-8");
32683
32699
  } catch {
32684
32700
  return {};
32685
32701
  }
@@ -32796,7 +32812,7 @@ function checkStartShStale(agentName, startShPath) {
32796
32812
  }
32797
32813
  let content;
32798
32814
  try {
32799
- content = readFileSync50(startShPath, "utf-8");
32815
+ content = readFileSync51(startShPath, "utf-8");
32800
32816
  } catch (err) {
32801
32817
  return {
32802
32818
  name: label,
@@ -32909,7 +32925,7 @@ function isSwitchroomCheckout(dir) {
32909
32925
  const pkgPath = join59(dir, "package.json");
32910
32926
  if (!existsSync57(pkgPath))
32911
32927
  return false;
32912
- const pkg = JSON.parse(readFileSync50(pkgPath, "utf-8"));
32928
+ const pkg = JSON.parse(readFileSync51(pkgPath, "utf-8"));
32913
32929
  return pkg.name === "switchroom";
32914
32930
  } catch {
32915
32931
  return false;
@@ -33030,7 +33046,7 @@ function checkAgents(config, configPath) {
33030
33046
  });
33031
33047
  } else {
33032
33048
  try {
33033
- const mcp = JSON.parse(readFileSync50(mcpJsonPath, "utf-8"));
33049
+ const mcp = JSON.parse(readFileSync51(mcpJsonPath, "utf-8"));
33034
33050
  const hasSwitchroomTelegram = !!mcp.mcpServers?.["switchroom-telegram"];
33035
33051
  const memoryEnabled = isHindsightEnabled(config);
33036
33052
  const hasHindsight = !!mcp.mcpServers?.hindsight;
@@ -33525,11 +33541,11 @@ function runDockerSection(config) {
33525
33541
  let composeYaml;
33526
33542
  let dockerfileAgent;
33527
33543
  try {
33528
- composeYaml = readFileSync50(composePath, "utf8");
33544
+ composeYaml = readFileSync51(composePath, "utf8");
33529
33545
  } catch {}
33530
33546
  const dockerfilePath = resolve32(process.env.HOME ?? "", ".switchroom", "docker", "Dockerfile.agent");
33531
33547
  try {
33532
- dockerfileAgent = readFileSync50(dockerfilePath, "utf8");
33548
+ dockerfileAgent = readFileSync51(dockerfilePath, "utf8");
33533
33549
  } catch {}
33534
33550
  return runDockerChecks({
33535
33551
  config,
@@ -49836,7 +49852,7 @@ __export(exports_server2, {
49836
49852
  TOOLS: () => TOOLS2
49837
49853
  });
49838
49854
  import { randomBytes as randomBytes15 } from "node:crypto";
49839
- import { existsSync as existsSync83, readFileSync as readFileSync69 } from "node:fs";
49855
+ import { existsSync as existsSync83, readFileSync as readFileSync70 } from "node:fs";
49840
49856
  function selfSocketPath() {
49841
49857
  return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
49842
49858
  }
@@ -50014,7 +50030,7 @@ function getLastUpdateApplyStatus() {
50014
50030
  }
50015
50031
  let raw;
50016
50032
  try {
50017
- raw = readFileSync69(path8, "utf-8");
50033
+ raw = readFileSync70(path8, "utf-8");
50018
50034
  } catch (err2) {
50019
50035
  return errorText2(`get_status: failed to read audit log at ${path8}: ${err2.message}`);
50020
50036
  }
@@ -50247,8 +50263,8 @@ var {
50247
50263
  } = import__.default;
50248
50264
 
50249
50265
  // src/build-info.ts
50250
- var VERSION = "0.15.11";
50251
- var COMMIT_SHA = "43331954";
50266
+ var VERSION = "0.15.13";
50267
+ var COMMIT_SHA = "36ba2682";
50252
50268
 
50253
50269
  // src/cli/agent.ts
50254
50270
  init_source();
@@ -51714,6 +51730,11 @@ function webkiteDenyForAgent(agentConfig) {
51714
51730
  }
51715
51731
  var SWITCHROOM_DEFAULT_MAIN_MODEL = "claude-sonnet-4-6";
51716
51732
  var SWITCHROOM_DEFAULT_THINKING_EFFORT = "low";
51733
+ function resolveMainModel(model) {
51734
+ if (model === undefined || model === "default")
51735
+ return SWITCHROOM_DEFAULT_MAIN_MODEL;
51736
+ return model;
51737
+ }
51717
51738
  function dedupe2(items) {
51718
51739
  const seen = new Set;
51719
51740
  const out = [];
@@ -51983,6 +52004,10 @@ function channelsToEnv(agent) {
51983
52004
  if (tg.clear_status_on_completion !== undefined) {
51984
52005
  out.SWITCHROOM_TG_CLEAR_STATUS_ON_COMPLETION = tg.clear_status_on_completion ? "1" : "0";
51985
52006
  }
52007
+ const linearDefaultTeam = tg?.linear_agent?.default_team_id;
52008
+ if (linearDefaultTeam) {
52009
+ out.SWITCHROOM_LINEAR_DEFAULT_TEAM_ID = linearDefaultTeam;
52010
+ }
51986
52011
  return out;
51987
52012
  }
51988
52013
  function buildRepoEnvVars(_agentName, agentDir, agent) {
@@ -52322,6 +52347,7 @@ function buildWorkspaceContext(args) {
52322
52347
  useSwitchroomPlugin: usesSwitchroomTelegramPlugin(agentConfig),
52323
52348
  useHotReloadStable: agentConfig.channels?.telegram?.hotReloadStable === true,
52324
52349
  telegramEnabledFlag: agentConfig.channels?.telegram?.enabled === false ? "false" : "true",
52350
+ linearAgentEnabled: agentConfig.channels?.telegram?.linear_agent?.enabled === true,
52325
52351
  securityPluginDir: DOCKER_SECURITY_PLUGIN_PATH,
52326
52352
  hindsightEnabled: hindsightAutoRecallEnabled,
52327
52353
  hindsightBankIdQ: shellSingleQuote(hindsightBankId),
@@ -52333,7 +52359,7 @@ function buildWorkspaceContext(args) {
52333
52359
  hindsightTopicFilterMode,
52334
52360
  switchroomConfigPathQ: switchroomConfigPath ? shellSingleQuote(resolve11(switchroomConfigPath)) : undefined,
52335
52361
  hostHomeQ: process.env.HOME ? shellSingleQuote(process.env.HOME) : undefined,
52336
- modelQ: shellSingleQuote(agentConfig.model ?? SWITCHROOM_DEFAULT_MAIN_MODEL),
52362
+ modelQ: shellSingleQuote(resolveMainModel(agentConfig.model)),
52337
52363
  ...buildCronSessionContext(agentConfig),
52338
52364
  thinkingEffort: agentConfig.thinking_effort ?? SWITCHROOM_DEFAULT_THINKING_EFFORT,
52339
52365
  permissionMode: agentConfig.permission_mode,
@@ -52633,7 +52659,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
52633
52659
  useSwitchroomPlugin: usesSwitchroomTelegramPlugin(agentConfig),
52634
52660
  configPath: switchroomConfigPath
52635
52661
  });
52636
- settings.model = agentConfig.model ?? SWITCHROOM_DEFAULT_MAIN_MODEL;
52662
+ settings.model = resolveMainModel(agentConfig.model);
52637
52663
  const mergedSettings = agentConfig.settings_raw ? deepMergeJson(settings, agentConfig.settings_raw) : settings;
52638
52664
  if (agentConfig.settings_raw && Object.keys(agentConfig.settings_raw).length > 0) {
52639
52665
  mergedSettings._switchroomManagedRawKeys = Object.keys(agentConfig.settings_raw);
@@ -53424,7 +53450,7 @@ function reconcileAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchr
53424
53450
  hindsightTopicAliasesJsonQ: hindsightTopicAliasesJson ? shellSingleQuote(hindsightTopicAliasesJson) : undefined,
53425
53451
  hindsightTopicFilterMode,
53426
53452
  hostHomeQ: process.env.HOME ? shellSingleQuote(process.env.HOME) : undefined,
53427
- modelQ: shellSingleQuote(agentConfig.model ?? SWITCHROOM_DEFAULT_MAIN_MODEL),
53453
+ modelQ: shellSingleQuote(resolveMainModel(agentConfig.model)),
53428
53454
  ...buildCronSessionContext(agentConfig),
53429
53455
  thinkingEffort: agentConfig.thinking_effort ?? SWITCHROOM_DEFAULT_THINKING_EFFORT,
53430
53456
  permissionMode: agentConfig.permission_mode,
@@ -53509,7 +53535,8 @@ function reconcileAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchr
53509
53535
  schedule: agentConfig.schedule,
53510
53536
  useSwitchroomPlugin: usesSwitchroomTelegramPlugin(agentConfig),
53511
53537
  admin: agentConfig.admin === true || agentConfig.root === true,
53512
- root: agentConfig.root === true
53538
+ root: agentConfig.root === true,
53539
+ linearAgentEnabled: agentConfig.channels?.telegram?.linear_agent?.enabled === true
53513
53540
  };
53514
53541
  let rendered = renderTemplate(claudeMdSrc, claudeContext);
53515
53542
  const vaultProtocol = renderVaultProtocolFragment(claudeContext);
@@ -53675,7 +53702,7 @@ ${body}
53675
53702
  }
53676
53703
  }
53677
53704
  }
53678
- settings.model = agentConfig.model ?? SWITCHROOM_DEFAULT_MAIN_MODEL;
53705
+ settings.model = resolveMainModel(agentConfig.model);
53679
53706
  const mergedSettings = agentConfig.settings_raw ? deepMergeJson(settings, agentConfig.settings_raw) : settings;
53680
53707
  if (agentConfig.settings_raw && Object.keys(agentConfig.settings_raw).length > 0) {
53681
53708
  mergedSettings[META_KEY] = Object.keys(agentConfig.settings_raw);
@@ -67029,6 +67056,31 @@ import { createInterface as createInterface4 } from "node:readline";
67029
67056
 
67030
67057
  // src/cli/telegram-yaml.ts
67031
67058
  var import_yaml11 = __toESM(require_dist(), 1);
67059
+ function setLinearAgent(yamlText, agentName, opts) {
67060
+ const doc = import_yaml11.parseDocument(yamlText);
67061
+ ensureAgent(doc, agentName);
67062
+ const block = { enabled: true, token: opts.token };
67063
+ if (opts.workspaceId)
67064
+ block.workspace_id = opts.workspaceId;
67065
+ doc.setIn(["agents", agentName, "channels", "telegram", "linear_agent"], block);
67066
+ doc.setIn(["agents", agentName, "channels", "telegram", "webhook_via_gateway"], true);
67067
+ return String(doc);
67068
+ }
67069
+ function setLinearDefaultTeam(yamlText, agentName, teamId) {
67070
+ const doc = import_yaml11.parseDocument(yamlText);
67071
+ ensureAgent(doc, agentName);
67072
+ if (!doc.hasIn(["agents", agentName, "channels", "telegram", "linear_agent"])) {
67073
+ throw new Error(`agent '${agentName}' has no linear_agent block. Run 'switchroom linear-agent setup --agent ${agentName} --token <token>' first.`);
67074
+ }
67075
+ const path4 = ["agents", agentName, "channels", "telegram", "linear_agent", "default_team_id"];
67076
+ if (teamId === null) {
67077
+ if (doc.hasIn(path4))
67078
+ doc.deleteIn(path4);
67079
+ } else {
67080
+ doc.setIn(path4, teamId);
67081
+ }
67082
+ return String(doc);
67083
+ }
67032
67084
  function setTelegramFeature(yamlText, agentName, feature, value) {
67033
67085
  const doc = import_yaml11.parseDocument(yamlText);
67034
67086
  ensureAgent(doc, agentName);
@@ -67587,6 +67639,123 @@ function promptHidden2(prompt) {
67587
67639
  });
67588
67640
  }
67589
67641
 
67642
+ // src/cli/linear-agent.ts
67643
+ init_source();
67644
+ init_helpers();
67645
+ import { readFileSync as readFileSync39, writeFileSync as writeFileSync22 } from "node:fs";
67646
+ function registerLinearAgentCommand(program3) {
67647
+ const linear = program3.command("linear-agent").description("Install an agent into a Linear workspace as a first-class app actor (#2298) \u2014 @-mentionable, delegate-assignable, agent sessions wake it instantly.");
67648
+ linear.command("setup").description("Provision <agent> as a Linear agent. Vault-stores the Linear OAuth app token (actor=app) under 'linear/<agent>/token' and enables the linear_agent block in switchroom.yaml. The OAuth browser authorize step is printed as instructions (it can't run headless); pass the already-obtained --token.").requiredOption("--agent <name>", "Agent name (must exist in switchroom.yaml)").requiredOption("--token <token>", "The Linear OAuth app token (actor=app), obtained out-of-band via the browser authorize step. Stored in the vault, never in switchroom.yaml.").option("--client-id <id>", "Linear OAuth app client id (for the printed authorize-URL hint).").option("--client-secret <secret>", "Linear OAuth app client secret (informational \u2014 not stored by this verb).").option("--redirect-uri <uri>", "OAuth redirect URI registered on the Linear app (for the authorize-URL hint).").option("--workspace-id <id>", "Optional Linear workspace (organization) id to record in config.").option("--webhook-base <url>", "Base URL of the switchroom web server (e.g. https://hooks.switchroom.ai). Used to print the webhook URL to register in Linear. Defaults to a placeholder.").option("--dry-run", "Print the YAML diff + instructions without writing or vaulting anything").action(withConfigError(async (opts) => {
67649
+ if (!/^[a-z][a-z0-9_-]{0,63}$/.test(opts.agent)) {
67650
+ fail2(`--agent must be a lowercase agent slug (got '${opts.agent}').`);
67651
+ }
67652
+ if (!opts.token || opts.token.trim().length === 0) {
67653
+ fail2("--token must be a non-empty Linear app token.");
67654
+ }
67655
+ const vaultKey = `linear/${opts.agent}/token`;
67656
+ if (!opts.dryRun) {
67657
+ await vaultPut(program3, vaultKey, opts.token);
67658
+ } else {
67659
+ console.log(source_default.gray(`[dry-run] would store the Linear token in the vault as '${vaultKey}'`));
67660
+ }
67661
+ const path4 = getConfigPath(program3);
67662
+ const before = readFileSync39(path4, "utf-8");
67663
+ let after;
67664
+ try {
67665
+ after = setLinearAgent(before, opts.agent, {
67666
+ token: `vault:${vaultKey}`,
67667
+ ...opts.workspaceId ? { workspaceId: opts.workspaceId } : {}
67668
+ });
67669
+ } catch (err) {
67670
+ fail2(err.message);
67671
+ }
67672
+ if (opts.dryRun) {
67673
+ console.log(source_default.bold(`[dry-run] would edit ${path4}`));
67674
+ console.log(makeUnifiedDiff2(before, after));
67675
+ } else {
67676
+ writeFileSync22(path4, after, "utf-8");
67677
+ console.log(source_default.green(`\u2713 Enabled linear-agent for agent '${opts.agent}'`));
67678
+ console.log(source_default.gray(` Vault key: ${vaultKey}`));
67679
+ console.log(source_default.gray(` Run 'switchroom agent restart ${opts.agent}' to pick up the change.`));
67680
+ }
67681
+ printLinearInstructions(opts, vaultKey);
67682
+ }));
67683
+ linear.command("set-team").description("Set (or clear) the default Linear team captured issues file into for <agent>. Only needed when the workspace has multiple teams \u2014 a single-team workspace auto-resolves. Pass --clear to remove the default.").requiredOption("--agent <name>", "Agent name (must have a linear_agent block)").option("--team <id>", "Linear team id new captured issues default to.").option("--clear", "Remove the configured default team (revert to auto-resolve).").action(withConfigError(async (opts) => {
67684
+ if (!/^[a-z][a-z0-9_-]{0,63}$/.test(opts.agent)) {
67685
+ fail2(`--agent must be a lowercase agent slug (got '${opts.agent}').`);
67686
+ }
67687
+ if (!opts.clear && (!opts.team || opts.team.trim().length === 0)) {
67688
+ fail2("pass either --team <id> or --clear.");
67689
+ }
67690
+ const path4 = getConfigPath(program3);
67691
+ const before = readFileSync39(path4, "utf-8");
67692
+ let after;
67693
+ try {
67694
+ after = setLinearDefaultTeam(before, opts.agent, opts.clear ? null : opts.team.trim());
67695
+ } catch (err) {
67696
+ fail2(err.message);
67697
+ }
67698
+ writeFileSync22(path4, after, "utf-8");
67699
+ if (opts.clear) {
67700
+ console.log(source_default.green(`\u2713 Cleared default Linear team for '${opts.agent}' (auto-resolve).`));
67701
+ } else {
67702
+ console.log(source_default.green(`\u2713 Default Linear team for '${opts.agent}' set to ${opts.team.trim()}.`));
67703
+ }
67704
+ console.log(source_default.gray(` Run 'switchroom agent restart ${opts.agent}' to pick up the change.`));
67705
+ }));
67706
+ }
67707
+ function printLinearInstructions(opts, vaultKey) {
67708
+ const base = opts.webhookBase ?? "https://<your-switchroom-web-host>";
67709
+ const webhookUrl = `${base.replace(/\/$/, "")}/webhook/${opts.agent}/linear`;
67710
+ console.log("");
67711
+ console.log(source_default.bold("Next steps in Linear (browser, one-time per agent):"));
67712
+ console.log(source_default.gray(" 1. Create / open your Linear OAuth app with actor=app and scopes"));
67713
+ console.log(source_default.gray(" app:mentionable + app:assignable (https://linear.app/developers/agents)."));
67714
+ if (opts.clientId) {
67715
+ const redirect = opts.redirectUri ?? `${base.replace(/\/$/, "")}/oauth/callback`;
67716
+ const authorizeUrl = `https://linear.app/oauth/authorize?` + `client_id=${encodeURIComponent(opts.clientId)}` + `&redirect_uri=${encodeURIComponent(redirect)}` + `&response_type=code` + `&scope=${encodeURIComponent("read,write,app:assignable,app:mentionable")}` + `&actor=app`;
67717
+ console.log(source_default.gray(" 2. Authorize the app as an actor (open in a browser):"));
67718
+ console.log(source_default.cyan(` ${authorizeUrl}`));
67719
+ console.log(source_default.gray(" The redirect delivers the app token you pass to this verb via --token."));
67720
+ } else {
67721
+ console.log(source_default.gray(" 2. Authorize the app (actor=app) in a browser; capture the app token and"));
67722
+ console.log(source_default.gray(" re-run this verb with --token (and optionally --client-id to print the"));
67723
+ console.log(source_default.gray(" authorize URL)."));
67724
+ }
67725
+ console.log(source_default.gray(" 3. Register this webhook URL on the app (AgentSessionEvent + Issue/Comment):"));
67726
+ console.log(source_default.cyan(` ${webhookUrl}`));
67727
+ console.log(source_default.gray(" Use Linear's signing secret as the webhook secret \u2014 store it in the vault " + `under webhook/${opts.agent}/linear (e.g. 'switchroom telegram enable webhook ` + `--agent ${opts.agent} --source linear --secret <signing-secret>').`));
67728
+ console.log("");
67729
+ console.log(source_default.gray(` Token is read at runtime from vault:${vaultKey} (actor=app).`));
67730
+ }
67731
+ function makeUnifiedDiff2(before, after) {
67732
+ const a = before.split(`
67733
+ `);
67734
+ const b = after.split(`
67735
+ `);
67736
+ const out = [];
67737
+ let i = 0, j = 0;
67738
+ while (i < a.length || j < b.length) {
67739
+ if (i < a.length && j < b.length && a[i] === b[j]) {
67740
+ out.push(` ${a[i]}`);
67741
+ i++;
67742
+ j++;
67743
+ } else if (j < b.length && (i >= a.length || a[i] !== b[j])) {
67744
+ out.push(source_default.green(`+ ${b[j]}`));
67745
+ j++;
67746
+ } else {
67747
+ out.push(source_default.red(`- ${a[i]}`));
67748
+ i++;
67749
+ }
67750
+ }
67751
+ return out.join(`
67752
+ `);
67753
+ }
67754
+ function fail2(msg) {
67755
+ console.error(source_default.red(`Error: ${msg}`));
67756
+ process.exit(1);
67757
+ }
67758
+
67590
67759
  // src/cli/memory.ts
67591
67760
  init_source();
67592
67761
  init_hindsight();
@@ -67856,7 +68025,7 @@ async function ensureHindsightConsumer(configPath, account, uid = HINDSIGHT_DEFA
67856
68025
  // src/cli/memory.ts
67857
68026
  init_loader();
67858
68027
  var import_yaml12 = __toESM(require_dist(), 1);
67859
- import { existsSync as existsSync44, readFileSync as readFileSync39, writeFileSync as writeFileSync22 } from "node:fs";
68028
+ import { existsSync as existsSync44, readFileSync as readFileSync40, writeFileSync as writeFileSync23 } from "node:fs";
67860
68029
  import { join as join39 } from "node:path";
67861
68030
  function readRecallLog(agentDir, limit) {
67862
68031
  const path4 = join39(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
@@ -67864,7 +68033,7 @@ function readRecallLog(agentDir, limit) {
67864
68033
  return [];
67865
68034
  let raw;
67866
68035
  try {
67867
- raw = readFileSync39(path4, "utf-8");
68036
+ raw = readFileSync40(path4, "utf-8");
67868
68037
  } catch {
67869
68038
  return [];
67870
68039
  }
@@ -68059,7 +68228,7 @@ Cross-agent reflection plan
68059
68228
  const configPath = getConfigPath(program3);
68060
68229
  try {
68061
68230
  if (existsSync44(configPath)) {
68062
- const raw = readFileSync39(configPath, "utf-8");
68231
+ const raw = readFileSync40(configPath, "utf-8");
68063
68232
  const doc = import_yaml12.default.parseDocument(raw);
68064
68233
  if (!doc.has("memory")) {
68065
68234
  doc.set("memory", { backend: "hindsight", shared_collection: "shared", config: { provider, url } });
@@ -68075,7 +68244,7 @@ Cross-agent reflection plan
68075
68244
  }
68076
68245
  }
68077
68246
  }
68078
- writeFileSync22(configPath, doc.toString(), "utf-8");
68247
+ writeFileSync23(configPath, doc.toString(), "utf-8");
68079
68248
  console.log(source_default.gray(` Updated ${configPath} with memory.config.url = ${url}`));
68080
68249
  console.log(source_default.gray(" Run `switchroom agent reconcile all --restart` to apply this to existing agents."));
68081
68250
  }
@@ -68168,7 +68337,7 @@ init_loader();
68168
68337
  init_client();
68169
68338
  init_lifecycle();
68170
68339
  import {
68171
- readFileSync as readFileSync45,
68340
+ readFileSync as readFileSync46,
68172
68341
  existsSync as existsSync50,
68173
68342
  realpathSync as realpathSync4,
68174
68343
  mkdirSync as mkdirSync29,
@@ -68187,7 +68356,7 @@ init_lifecycle();
68187
68356
  init_manager();
68188
68357
  init_hindsight();
68189
68358
  import { spawnSync as spawnSync5 } from "node:child_process";
68190
- import { existsSync as existsSync47, readFileSync as readFileSync42, statSync as statSync22 } from "node:fs";
68359
+ import { existsSync as existsSync47, readFileSync as readFileSync43, statSync as statSync22 } from "node:fs";
68191
68360
  import { resolve as resolve27 } from "node:path";
68192
68361
  init_audit_reader();
68193
68362
 
@@ -72695,8 +72864,8 @@ init_paths();
72695
72864
  import {
72696
72865
  existsSync as existsSync45,
72697
72866
  mkdirSync as mkdirSync25,
72698
- readFileSync as readFileSync40,
72699
- writeFileSync as writeFileSync23
72867
+ readFileSync as readFileSync41,
72868
+ writeFileSync as writeFileSync24
72700
72869
  } from "node:fs";
72701
72870
  import { dirname as dirname9 } from "node:path";
72702
72871
  import { randomUUID as randomUUID3 } from "node:crypto";
@@ -72716,7 +72885,7 @@ function getDistinctId() {
72716
72885
  const path4 = resolveStatePath("analytics-id");
72717
72886
  try {
72718
72887
  if (existsSync45(path4)) {
72719
- const existing = readFileSync40(path4, "utf-8").trim();
72888
+ const existing = readFileSync41(path4, "utf-8").trim();
72720
72889
  if (existing) {
72721
72890
  cachedDistinctId = existing;
72722
72891
  return existing;
@@ -72727,7 +72896,7 @@ function getDistinctId() {
72727
72896
  cachedDistinctId = id;
72728
72897
  try {
72729
72898
  mkdirSync25(dirname9(path4), { recursive: true });
72730
- writeFileSync23(path4, id, "utf-8");
72899
+ writeFileSync24(path4, id, "utf-8");
72731
72900
  } catch {}
72732
72901
  return id;
72733
72902
  }
@@ -72852,7 +73021,7 @@ function getAgentWorkspaceAccount(yamlText, provider, agent) {
72852
73021
  // src/web/config-edit-plan.ts
72853
73022
  init_schema();
72854
73023
  var import_yaml14 = __toESM(require_dist(), 1);
72855
- import { readFileSync as readFileSync41 } from "node:fs";
73024
+ import { readFileSync as readFileSync42 } from "node:fs";
72856
73025
 
72857
73026
  class ConfigPlanError extends Error {
72858
73027
  }
@@ -72862,7 +73031,7 @@ function composeTransforms(...transforms) {
72862
73031
  function planConfigEdit(configPath, transform) {
72863
73032
  let before;
72864
73033
  try {
72865
- before = readFileSync41(configPath, "utf-8");
73034
+ before = readFileSync42(configPath, "utf-8");
72866
73035
  } catch (err) {
72867
73036
  throw new ConfigPlanError(`could not read config: ${err.message}`);
72868
73037
  }
@@ -72893,7 +73062,7 @@ import {
72893
73062
  mkdirSync as mkdirSync26,
72894
73063
  mkdtempSync as mkdtemp,
72895
73064
  rmSync as rmrf,
72896
- writeFileSync as writeFileSync24
73065
+ writeFileSync as writeFileSync25
72897
73066
  } from "node:fs";
72898
73067
  import { spawnSync as spawnSync4 } from "node:child_process";
72899
73068
  import { tmpdir as tmpdir4 } from "node:os";
@@ -72908,8 +73077,8 @@ function generateUnifiedDiff(before, after, name = "switchroom.yaml", gitBin = "
72908
73077
  try {
72909
73078
  mkdirSync26(join41(dir, "cur"), { recursive: true });
72910
73079
  mkdirSync26(join41(dir, "new"), { recursive: true });
72911
- writeFileSync24(join41(dir, "cur", name), before);
72912
- writeFileSync24(join41(dir, "new", name), after);
73080
+ writeFileSync25(join41(dir, "cur", name), before);
73081
+ writeFileSync25(join41(dir, "new", name), after);
72913
73082
  const r = spawnSync4(gitBin, ["diff", "--no-index", "--no-color", "--", `cur/${name}`, `new/${name}`], { cwd: dir, encoding: "utf-8", timeout: 1e4 });
72914
73083
  if (r.status === 0)
72915
73084
  return "";
@@ -73546,7 +73715,7 @@ async function handleGetSystemHealth(config, home2) {
73546
73715
  const logPath = defaultAuditLogPath2(home2);
73547
73716
  if (existsSync47(logPath)) {
73548
73717
  hostd.auditLogPresent = true;
73549
- const raw = readFileSync42(logPath, "utf-8");
73718
+ const raw = readFileSync43(logPath, "utf-8");
73550
73719
  hostd.recent = readAndFilter(raw, {}, 10);
73551
73720
  }
73552
73721
  } catch (err) {
@@ -73906,7 +74075,7 @@ async function handleGetMemoryHealth(config, opts) {
73906
74075
  }
73907
74076
 
73908
74077
  // src/web/webhook-handler.ts
73909
- import { appendFileSync as appendFileSync4, existsSync as existsSync49, mkdirSync as mkdirSync28, readFileSync as readFileSync44, writeFileSync as writeFileSync25 } from "fs";
74078
+ import { appendFileSync as appendFileSync4, existsSync as existsSync49, mkdirSync as mkdirSync28, readFileSync as readFileSync45, writeFileSync as writeFileSync26 } from "fs";
73910
74079
  import { join as join45 } from "path";
73911
74080
  import { homedir as homedir24 } from "os";
73912
74081
 
@@ -74113,7 +74282,7 @@ function forwardToGateway(socketPath, req, opts = {}) {
74113
74282
  }
74114
74283
 
74115
74284
  // src/web/webhook-edge.ts
74116
- import { existsSync as existsSync48, readFileSync as readFileSync43 } from "fs";
74285
+ import { existsSync as existsSync48, readFileSync as readFileSync44 } from "fs";
74117
74286
  import { join as join44 } from "path";
74118
74287
  import { homedir as homedir23 } from "os";
74119
74288
  import { timingSafeEqual as timingSafeEqual2 } from "crypto";
@@ -74126,7 +74295,7 @@ function loadEdgeSecret(path4) {
74126
74295
  try {
74127
74296
  if (!existsSync48(p))
74128
74297
  return null;
74129
- const raw = readFileSync43(p, "utf-8").trim();
74298
+ const raw = readFileSync44(p, "utf-8").trim();
74130
74299
  return raw.length > 0 ? raw : null;
74131
74300
  } catch {
74132
74301
  return null;
@@ -74160,7 +74329,7 @@ function loadDedupFile(path4) {
74160
74329
  try {
74161
74330
  if (!existsSync49(path4))
74162
74331
  return {};
74163
- const raw = JSON.parse(readFileSync44(path4, "utf-8"));
74332
+ const raw = JSON.parse(readFileSync45(path4, "utf-8"));
74164
74333
  return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
74165
74334
  } catch {
74166
74335
  return {};
@@ -74174,7 +74343,7 @@ function saveDedupFile(path4, deliveries, now) {
74174
74343
  }
74175
74344
  const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
74176
74345
  const final = Object.fromEntries(sorted);
74177
- writeFileSync25(path4, JSON.stringify({ deliveries: final }), {
74346
+ writeFileSync26(path4, JSON.stringify({ deliveries: final }), {
74178
74347
  mode: 384
74179
74348
  });
74180
74349
  }
@@ -74443,7 +74612,7 @@ function resolveWebToken() {
74443
74612
  const home2 = process.env.HOME ?? homedir25();
74444
74613
  const tokenPath = join46(home2, ".switchroom", "web-token");
74445
74614
  if (existsSync50(tokenPath)) {
74446
- const existing = readFileSync45(tokenPath, "utf8").trim();
74615
+ const existing = readFileSync46(tokenPath, "utf8").trim();
74447
74616
  if (existing.length > 0)
74448
74617
  return existing;
74449
74618
  }
@@ -74460,7 +74629,7 @@ function resolveWebToken() {
74460
74629
  return token;
74461
74630
  } catch (err) {
74462
74631
  if (err.code === "EEXIST") {
74463
- const existing = readFileSync45(tokenPath, "utf8").trim();
74632
+ const existing = readFileSync46(tokenPath, "utf8").trim();
74464
74633
  if (existing.length > 0)
74465
74634
  return existing;
74466
74635
  }
@@ -74530,7 +74699,7 @@ function loadWebhookSecrets() {
74530
74699
  if (!existsSync50(path4))
74531
74700
  return {};
74532
74701
  try {
74533
- const parsed = JSON.parse(readFileSync45(path4, "utf-8"));
74702
+ const parsed = JSON.parse(readFileSync46(path4, "utf-8"));
74534
74703
  return parsed && typeof parsed === "object" ? parsed : {};
74535
74704
  } catch (err) {
74536
74705
  process.stderr.write(`webhook-ingest: failed to parse ${path4}: ${err.message} \u2014 webhooks will return 401 until fixed
@@ -74903,7 +75072,7 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
74903
75072
  }
74904
75073
  const ext = extname(realFullPath);
74905
75074
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
74906
- const content = readFileSync45(realFullPath);
75075
+ const content = readFileSync46(realFullPath);
74907
75076
  return new Response(content, {
74908
75077
  headers: { "Content-Type": contentType }
74909
75078
  });
@@ -75037,7 +75206,7 @@ Starting Switchroom dashboard...
75037
75206
  // src/cli/setup.ts
75038
75207
  init_source();
75039
75208
  init_loader();
75040
- import { existsSync as existsSync51, copyFileSync as copyFileSync8, readFileSync as readFileSync46, writeFileSync as writeFileSync26, mkdirSync as mkdirSync30 } from "node:fs";
75209
+ import { existsSync as existsSync51, copyFileSync as copyFileSync8, readFileSync as readFileSync47, writeFileSync as writeFileSync27, mkdirSync as mkdirSync30 } from "node:fs";
75041
75210
  import { resolve as resolve29, dirname as dirname11 } from "node:path";
75042
75211
  init_vault();
75043
75212
  init_manager();
@@ -75766,10 +75935,10 @@ async function stepAutoUnlock(config, switchroomConfigPath, nonInteractive) {
75766
75935
  try {
75767
75936
  const yamlPath = existsSync51(resolve29(process.cwd(), "switchroom.yaml")) ? resolve29(process.cwd(), "switchroom.yaml") : resolve29(process.cwd(), "switchroom.yml");
75768
75937
  if (existsSync51(yamlPath)) {
75769
- const content = readFileSync46(yamlPath, "utf-8");
75938
+ const content = readFileSync47(yamlPath, "utf-8");
75770
75939
  const result = insertVaultBrokerApprovalAuth(content, "telegram-id");
75771
75940
  if (result.kind === "rewritten") {
75772
- writeFileSync26(yamlPath, result.content, "utf-8");
75941
+ writeFileSync27(yamlPath, result.content, "utf-8");
75773
75942
  console.log(source_default.green(` ${STEP_DONE} Set vault.broker.approvalAuth: telegram-id in ${yamlPath}`));
75774
75943
  } else if (result.kind === "already-set") {
75775
75944
  console.log(source_default.gray(" approvalAuth already set \u2014 leaving it alone."));
@@ -75801,7 +75970,7 @@ async function stepDangerousMode(config, nonInteractive) {
75801
75970
  ];
75802
75971
  for (const configPath of configPaths) {
75803
75972
  if (existsSync51(configPath)) {
75804
- let content = readFileSync46(configPath, "utf-8");
75973
+ let content = readFileSync47(configPath, "utf-8");
75805
75974
  const agentNames = Object.keys(config.agents);
75806
75975
  for (const name of agentNames) {
75807
75976
  const agentPattern = new RegExp(`(^ ${name}:\\s*\\n)`, "m");
@@ -75815,7 +75984,7 @@ async function stepDangerousMode(config, nonInteractive) {
75815
75984
  }
75816
75985
  config.agents[name].dangerous_mode = true;
75817
75986
  }
75818
- writeFileSync26(configPath, content, "utf-8");
75987
+ writeFileSync27(configPath, content, "utf-8");
75819
75988
  console.log(source_default.green(` ${STEP_DONE} Enabled dangerous_mode for all agents in ${configPath}`));
75820
75989
  break;
75821
75990
  }
@@ -75930,7 +76099,7 @@ init_doctor();
75930
76099
  init_source();
75931
76100
  init_loader();
75932
76101
  init_lifecycle();
75933
- import { cpSync as cpSync2, existsSync as existsSync58, mkdirSync as mkdirSync32, readFileSync as readFileSync51, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync26 } from "node:fs";
76102
+ import { cpSync as cpSync2, existsSync as existsSync58, mkdirSync as mkdirSync32, readFileSync as readFileSync52, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync26 } from "node:fs";
75934
76103
  import { spawnSync as spawnSync9 } from "node:child_process";
75935
76104
  import { join as join60, dirname as dirname14, resolve as resolve33 } from "node:path";
75936
76105
  import { homedir as homedir36 } from "node:os";
@@ -75940,7 +76109,7 @@ function runningFromSwitchroomCheckout(scriptPath) {
75940
76109
  for (let i = 0;i < 12; i++) {
75941
76110
  if (existsSync58(join60(dir, ".git"))) {
75942
76111
  try {
75943
- const pkg = JSON.parse(readFileSync51(join60(dir, "package.json"), "utf-8"));
76112
+ const pkg = JSON.parse(readFileSync52(join60(dir, "package.json"), "utf-8"));
75944
76113
  if (pkg.name === "switchroom")
75945
76114
  return true;
75946
76115
  } catch {}
@@ -76208,7 +76377,7 @@ function defaultStatusProbe(composePath) {
76208
76377
  const pkgPath = join60(dir, "package.json");
76209
76378
  if (existsSync58(pkgPath)) {
76210
76379
  try {
76211
- const pkg = JSON.parse(readFileSync51(pkgPath, "utf-8"));
76380
+ const pkg = JSON.parse(readFileSync52(pkgPath, "utf-8"));
76212
76381
  if (typeof pkg.version === "string")
76213
76382
  cliVersion = pkg.version;
76214
76383
  } catch (err) {
@@ -76424,7 +76593,7 @@ init_source();
76424
76593
  init_helpers();
76425
76594
  init_lifecycle();
76426
76595
  import { execSync as execSync4 } from "node:child_process";
76427
- import { existsSync as existsSync59, readFileSync as readFileSync52 } from "node:fs";
76596
+ import { existsSync as existsSync59, readFileSync as readFileSync53 } from "node:fs";
76428
76597
  import { dirname as dirname15, join as join61 } from "node:path";
76429
76598
  function getClaudeCodeVersion() {
76430
76599
  try {
@@ -76478,7 +76647,7 @@ function locateSwitchroomInstallDir() {
76478
76647
  const pkgPath = join61(dir, "package.json");
76479
76648
  if (existsSync59(pkgPath)) {
76480
76649
  try {
76481
- const pkg = JSON.parse(readFileSync52(pkgPath, "utf-8"));
76650
+ const pkg = JSON.parse(readFileSync53(pkgPath, "utf-8"));
76482
76651
  if (pkg.name === "switchroom" && existsSync59(join61(dir, ".git"))) {
76483
76652
  return dir;
76484
76653
  }
@@ -76709,11 +76878,11 @@ import {
76709
76878
  mkdirSync as mkdirSync33,
76710
76879
  openSync as openSync11,
76711
76880
  readdirSync as readdirSync22,
76712
- readFileSync as readFileSync53,
76881
+ readFileSync as readFileSync54,
76713
76882
  renameSync as renameSync12,
76714
76883
  statSync as statSync27,
76715
76884
  unlinkSync as unlinkSync11,
76716
- writeFileSync as writeFileSync27,
76885
+ writeFileSync as writeFileSync28,
76717
76886
  writeSync as writeSync7
76718
76887
  } from "node:fs";
76719
76888
  import { join as join62 } from "node:path";
@@ -77119,7 +77288,7 @@ function readAll(stateDir) {
77119
77288
  return [];
77120
77289
  let raw;
77121
77290
  try {
77122
- raw = readFileSync53(path4, "utf-8");
77291
+ raw = readFileSync54(path4, "utf-8");
77123
77292
  } catch {
77124
77293
  return [];
77125
77294
  }
@@ -77267,7 +77436,7 @@ function writeAll(stateDir, events) {
77267
77436
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
77268
77437
  `) + `
77269
77438
  `;
77270
- writeFileSync27(tmp, body, "utf-8");
77439
+ writeFileSync28(tmp, body, "utf-8");
77271
77440
  renameSync12(tmp, path4);
77272
77441
  }
77273
77442
  var ORPHAN_TMP_TTL_MS = 60000;
@@ -77330,7 +77499,7 @@ function withLock(stateDir, fn) {
77330
77499
  function tryStealStaleLock(lockPath) {
77331
77500
  let pidStr;
77332
77501
  try {
77333
- pidStr = readFileSync53(lockPath, "utf-8").trim();
77502
+ pidStr = readFileSync54(lockPath, "utf-8").trim();
77334
77503
  } catch {
77335
77504
  return true;
77336
77505
  }
@@ -77587,9 +77756,9 @@ import { createHash as createHash11 } from "node:crypto";
77587
77756
  import {
77588
77757
  existsSync as existsSync61,
77589
77758
  mkdirSync as mkdirSync34,
77590
- readFileSync as readFileSync54,
77759
+ readFileSync as readFileSync55,
77591
77760
  rmSync as rmSync13,
77592
- writeFileSync as writeFileSync28
77761
+ writeFileSync as writeFileSync29
77593
77762
  } from "node:fs";
77594
77763
  import { dirname as dirname16, join as join63 } from "node:path";
77595
77764
  import { homedir as homedir37 } from "node:os";
@@ -77607,7 +77776,7 @@ function defaultPythonCacheRoot() {
77607
77776
  return join63(homedir37(), ".switchroom", "deps", "python");
77608
77777
  }
77609
77778
  function hashFile(path4) {
77610
- return createHash11("sha256").update(readFileSync54(path4)).digest("hex");
77779
+ return createHash11("sha256").update(readFileSync55(path4)).digest("hex");
77611
77780
  }
77612
77781
  function ensurePythonEnv(opts) {
77613
77782
  const { skillName, requirementsPath, force = false } = opts;
@@ -77623,7 +77792,7 @@ function ensurePythonEnv(opts) {
77623
77792
  const pipBin = join63(binDir, "pip");
77624
77793
  const targetHash = hashFile(requirementsPath);
77625
77794
  if (!force && existsSync61(stampPath) && existsSync61(pythonBin)) {
77626
- const existingHash = readFileSync54(stampPath, "utf8").trim();
77795
+ const existingHash = readFileSync55(stampPath, "utf8").trim();
77627
77796
  if (existingHash === targetHash) {
77628
77797
  return {
77629
77798
  skillName,
@@ -77657,7 +77826,7 @@ function ensurePythonEnv(opts) {
77657
77826
  const e = err;
77658
77827
  throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
77659
77828
  }
77660
- writeFileSync28(stampPath, targetHash + `
77829
+ writeFileSync29(stampPath, targetHash + `
77661
77830
  `);
77662
77831
  return {
77663
77832
  skillName,
@@ -77675,9 +77844,9 @@ import {
77675
77844
  copyFileSync as copyFileSync9,
77676
77845
  existsSync as existsSync62,
77677
77846
  mkdirSync as mkdirSync35,
77678
- readFileSync as readFileSync55,
77847
+ readFileSync as readFileSync56,
77679
77848
  rmSync as rmSync14,
77680
- writeFileSync as writeFileSync29
77849
+ writeFileSync as writeFileSync30
77681
77850
  } from "node:fs";
77682
77851
  import { dirname as dirname17, join as join64 } from "node:path";
77683
77852
  import { homedir as homedir38 } from "node:os";
@@ -77710,7 +77879,7 @@ function hashDepInputs(packageJsonPath) {
77710
77879
  const hasher = createHash12("sha256");
77711
77880
  hasher.update(`package.json
77712
77881
  `);
77713
- hasher.update(readFileSync55(packageJsonPath));
77882
+ hasher.update(readFileSync56(packageJsonPath));
77714
77883
  for (const lockName of ALL_LOCKFILES) {
77715
77884
  const lockPath = join64(sourceDir, lockName);
77716
77885
  if (existsSync62(lockPath)) {
@@ -77719,7 +77888,7 @@ function hashDepInputs(packageJsonPath) {
77719
77888
  hasher.update(lockName);
77720
77889
  hasher.update(`
77721
77890
  `);
77722
- hasher.update(readFileSync55(lockPath));
77891
+ hasher.update(readFileSync56(lockPath));
77723
77892
  }
77724
77893
  }
77725
77894
  return hasher.digest("hex");
@@ -77738,7 +77907,7 @@ function ensureNodeEnv(opts) {
77738
77907
  const binDir = join64(nodeModulesDir, ".bin");
77739
77908
  const targetHash = hashDepInputs(packageJsonPath);
77740
77909
  if (!force && existsSync62(stampPath) && existsSync62(nodeModulesDir)) {
77741
- const existingHash = readFileSync55(stampPath, "utf8").trim();
77910
+ const existingHash = readFileSync56(stampPath, "utf8").trim();
77742
77911
  if (existingHash === targetHash) {
77743
77912
  return {
77744
77913
  skillName,
@@ -77774,7 +77943,7 @@ function ensureNodeEnv(opts) {
77774
77943
  const e = err;
77775
77944
  throw new NodeEnvError(`Failed to install node deps for skill "${skillName}" with ${installer}: ${e.message}`, e.stderr?.toString());
77776
77945
  }
77777
- writeFileSync29(stampPath, targetHash + `
77946
+ writeFileSync30(stampPath, targetHash + `
77778
77947
  `);
77779
77948
  return {
77780
77949
  skillName,
@@ -78757,7 +78926,7 @@ function safeParseInt(value, fallback) {
78757
78926
  init_helpers();
78758
78927
  init_loader();
78759
78928
  init_merge();
78760
- import { copyFileSync as copyFileSync10, existsSync as existsSync65, readFileSync as readFileSync56, writeFileSync as writeFileSync30 } from "node:fs";
78929
+ import { copyFileSync as copyFileSync10, existsSync as existsSync65, readFileSync as readFileSync57, writeFileSync as writeFileSync31 } from "node:fs";
78761
78930
  import { join as join66, resolve as resolve39 } from "node:path";
78762
78931
  init_schema();
78763
78932
  function resolveSoulTargetOrExit(program3, agentName) {
@@ -78803,7 +78972,7 @@ function registerSoulCommand(program3) {
78803
78972
  console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
78804
78973
  process.exit(1);
78805
78974
  }
78806
- process.stdout.write(readFileSync56(t.soulPath, "utf-8"));
78975
+ process.stdout.write(readFileSync57(t.soulPath, "utf-8"));
78807
78976
  }));
78808
78977
  cmd.command("reset <agent>").description("Re-seed SOUL.md from the agent's current profile " + "(backs the existing file up to SOUL.md.bak first)").option("-y, --yes", "Skip the confirmation prompt").action(withConfigError(async (agentName, opts) => {
78809
78978
  const t = resolveSoulTargetOrExit(program3, agentName);
@@ -78834,7 +79003,7 @@ function registerSoulCommand(program3) {
78834
79003
  }
78835
79004
  copyFileSync10(t.soulPath, backupPath);
78836
79005
  }
78837
- writeFileSync30(t.soulPath, content, "utf-8");
79006
+ writeFileSync31(t.soulPath, content, "utf-8");
78838
79007
  if (backupPath) {
78839
79008
  console.log(`soul: re-seeded ${agentName}'s SOUL.md from profile ` + `"${t.profileName}".
78840
79009
  ` + ` Previous version saved to ${backupPath}`);
@@ -78848,7 +79017,7 @@ function registerSoulCommand(program3) {
78848
79017
  // src/cli/debug.ts
78849
79018
  init_helpers();
78850
79019
  init_loader();
78851
- import { existsSync as existsSync66, readFileSync as readFileSync57, readdirSync as readdirSync23, statSync as statSync28 } from "node:fs";
79020
+ import { existsSync as existsSync66, readFileSync as readFileSync58, readdirSync as readdirSync23, statSync as statSync28 } from "node:fs";
78852
79021
  import { resolve as resolve40, join as join67 } from "node:path";
78853
79022
  import { createHash as createHash13 } from "node:crypto";
78854
79023
  init_merge();
@@ -78864,7 +79033,7 @@ function readMcpServerNames(agentDir) {
78864
79033
  if (!existsSync66(mcpPath))
78865
79034
  return [];
78866
79035
  try {
78867
- const parsed = JSON.parse(readFileSync57(mcpPath, "utf-8"));
79036
+ const parsed = JSON.parse(readFileSync58(mcpPath, "utf-8"));
78868
79037
  return Object.keys(parsed.mcpServers ?? {});
78869
79038
  } catch {
78870
79039
  return null;
@@ -78899,7 +79068,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
78899
79068
  }
78900
79069
  function extractLatestUserMessage(transcriptPath) {
78901
79070
  try {
78902
- const content = readFileSync57(transcriptPath, "utf-8");
79071
+ const content = readFileSync58(transcriptPath, "utf-8");
78903
79072
  const lines = content.trim().split(`
78904
79073
  `).filter(Boolean);
78905
79074
  for (let i = lines.length - 1;i >= 0; i--) {
@@ -79003,7 +79172,7 @@ function registerDebugCommand(program3) {
79003
79172
  }
79004
79173
  console.log(`=== Append System Prompt (per-session) ===
79005
79174
  `);
79006
- const handoffContent = existsSync66(handoffPath) ? readFileSync57(handoffPath, "utf-8") : "";
79175
+ const handoffContent = existsSync66(handoffPath) ? readFileSync58(handoffPath, "utf-8") : "";
79007
79176
  if (handoffContent.trim().length > 0) {
79008
79177
  console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
79009
79178
  console.log(handoffContent);
@@ -79014,7 +79183,7 @@ function registerDebugCommand(program3) {
79014
79183
  }
79015
79184
  console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
79016
79185
  `);
79017
- const claudeMdContent = existsSync66(claudeMdPath) ? readFileSync57(claudeMdPath, "utf-8") : "";
79186
+ const claudeMdContent = existsSync66(claudeMdPath) ? readFileSync58(claudeMdPath, "utf-8") : "";
79018
79187
  if (claudeMdContent.trim().length > 0) {
79019
79188
  console.log(`(${formatBytes(claudeMdContent.length)})`);
79020
79189
  console.log(claudeMdContent);
@@ -79025,7 +79194,7 @@ function registerDebugCommand(program3) {
79025
79194
  }
79026
79195
  console.log(`=== Persona (SOUL.md) ===
79027
79196
  `);
79028
- const soulMdContent = existsSync66(soulMdPath) ? readFileSync57(soulMdPath, "utf-8") : existsSync66(workspaceSoulMdPath) ? readFileSync57(workspaceSoulMdPath, "utf-8") : "";
79197
+ const soulMdContent = existsSync66(soulMdPath) ? readFileSync58(soulMdPath, "utf-8") : existsSync66(workspaceSoulMdPath) ? readFileSync58(workspaceSoulMdPath, "utf-8") : "";
79029
79198
  if (soulMdContent.trim().length > 0) {
79030
79199
  console.log(`(${formatBytes(soulMdContent.length)})`);
79031
79200
  console.log(soulMdContent);
@@ -79089,8 +79258,8 @@ function registerDebugCommand(program3) {
79089
79258
  const fleetDir = join67(agentsDir, "..", "fleet");
79090
79259
  const fleetInvPath = join67(fleetDir, "switchroom-invariants.md");
79091
79260
  const fleetClaudePath = join67(fleetDir, "CLAUDE.md");
79092
- const fleetInvBytes = existsSync66(fleetInvPath) ? readFileSync57(fleetInvPath, "utf-8").length : 0;
79093
- const fleetClaudeBytes = existsSync66(fleetClaudePath) ? readFileSync57(fleetClaudePath, "utf-8").length : 0;
79261
+ const fleetInvBytes = existsSync66(fleetInvPath) ? readFileSync58(fleetInvPath, "utf-8").length : 0;
79262
+ const fleetClaudeBytes = existsSync66(fleetClaudePath) ? readFileSync58(fleetClaudePath, "utf-8").length : 0;
79094
79263
  const fleetBytes = fleetInvBytes + fleetClaudeBytes;
79095
79264
  const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
79096
79265
  console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
@@ -79131,8 +79300,8 @@ import { randomBytes as randomBytes13 } from "node:crypto";
79131
79300
  // src/worktree/registry.ts
79132
79301
  import {
79133
79302
  mkdirSync as mkdirSync36,
79134
- writeFileSync as writeFileSync31,
79135
- readFileSync as readFileSync58,
79303
+ writeFileSync as writeFileSync32,
79304
+ readFileSync as readFileSync59,
79136
79305
  readdirSync as readdirSync24,
79137
79306
  unlinkSync as unlinkSync12,
79138
79307
  existsSync as existsSync67,
@@ -79153,14 +79322,14 @@ function writeRecord(record2) {
79153
79322
  ensureDir2();
79154
79323
  const target = recordPath(record2.id);
79155
79324
  const tmp = `${target}.tmp${process.pid}`;
79156
- writeFileSync31(tmp, JSON.stringify(record2, null, 2) + `
79325
+ writeFileSync32(tmp, JSON.stringify(record2, null, 2) + `
79157
79326
  `, { mode: 384 });
79158
79327
  renameSync13(tmp, target);
79159
79328
  }
79160
79329
  function readRecord(id) {
79161
79330
  const path7 = recordPath(id);
79162
79331
  try {
79163
- const raw = readFileSync58(path7, "utf8");
79332
+ const raw = readFileSync59(path7, "utf8");
79164
79333
  return JSON.parse(raw);
79165
79334
  } catch {
79166
79335
  return null;
@@ -79525,7 +79694,7 @@ import {
79525
79694
  mkdirSync as mkdirSync38,
79526
79695
  readdirSync as readdirSync25,
79527
79696
  rmSync as rmSync15,
79528
- writeFileSync as writeFileSync32
79697
+ writeFileSync as writeFileSync33
79529
79698
  } from "node:fs";
79530
79699
  import { join as join70 } from "node:path";
79531
79700
  function encodeCredentialsFilename(email) {
@@ -79727,7 +79896,7 @@ function writeSeedFile(dir, email, seed) {
79727
79896
  }
79728
79897
  const filename = encodeCredentialsFilename(email);
79729
79898
  const filePath = join70(dir, filename);
79730
- writeFileSync32(filePath, JSON.stringify(seed), { mode: 384 });
79899
+ writeFileSync33(filePath, JSON.stringify(seed), { mode: 384 });
79731
79900
  chmodSync9(filePath, 384);
79732
79901
  return filePath;
79733
79902
  }
@@ -79885,7 +80054,7 @@ function registerDriveMcpLauncherCommand(program3) {
79885
80054
  // src/cli/m365-mcp-launcher.ts
79886
80055
  init_scaffold_integration();
79887
80056
  import { spawn as spawn6 } from "node:child_process";
79888
- import { writeFileSync as writeFileSync33, mkdirSync as mkdirSync39 } from "node:fs";
80057
+ import { writeFileSync as writeFileSync34, mkdirSync as mkdirSync39 } from "node:fs";
79889
80058
  import { dirname as dirname18, join as join71 } from "node:path";
79890
80059
  var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
79891
80060
  var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
@@ -79912,7 +80081,7 @@ function writeRefreshHeartbeat(agentName, data) {
79912
80081
  const path7 = heartbeatPath(agentName);
79913
80082
  try {
79914
80083
  mkdirSync39(dirname18(path7), { recursive: true });
79915
- writeFileSync33(path7, JSON.stringify(data, null, 2), { mode: 420 });
80084
+ writeFileSync34(path7, JSON.stringify(data, null, 2), { mode: 420 });
79916
80085
  } catch {}
79917
80086
  }
79918
80087
  function heartbeatPath(agentName) {
@@ -80103,7 +80272,7 @@ function registerM365McpLauncherCommand(program3) {
80103
80272
  // src/cli/notion-mcp-launcher.ts
80104
80273
  init_scaffold_integration();
80105
80274
  import { spawn as spawn7 } from "node:child_process";
80106
- import { existsSync as existsSync71, mkdirSync as mkdirSync40, writeFileSync as writeFileSync34 } from "node:fs";
80275
+ import { existsSync as existsSync71, mkdirSync as mkdirSync40, writeFileSync as writeFileSync35 } from "node:fs";
80107
80276
  import { dirname as dirname19 } from "node:path";
80108
80277
  var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
80109
80278
  var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
@@ -80117,7 +80286,7 @@ function defaultWriteHeartbeat(path7, contents) {
80117
80286
  const dir = dirname19(path7);
80118
80287
  if (!existsSync71(dir))
80119
80288
  mkdirSync40(dir, { recursive: true });
80120
- writeFileSync34(path7, contents);
80289
+ writeFileSync35(path7, contents);
80121
80290
  } catch {}
80122
80291
  }
80123
80292
  async function runNotionMcpLauncher(opts, runtime) {
@@ -80227,7 +80396,7 @@ function registerNotionMcpLauncherCommand(program3) {
80227
80396
 
80228
80397
  // src/cli/deliver-file.ts
80229
80398
  init_client2();
80230
- import { readFileSync as readFileSync59, statSync as statSync29 } from "node:fs";
80399
+ import { readFileSync as readFileSync60, statSync as statSync29 } from "node:fs";
80231
80400
  import { basename as basename8 } from "node:path";
80232
80401
 
80233
80402
  // src/delivery/onedrive.ts
@@ -80567,7 +80736,7 @@ async function defaultResolveProvider() {
80567
80736
  async function runDeliverFile(localPath, deps = {}) {
80568
80737
  const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
80569
80738
  const sizeOf = deps.fileSize ?? ((p) => statSync29(p).size);
80570
- const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync59(p)));
80739
+ const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync60(p)));
80571
80740
  const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
80572
80741
  let size;
80573
80742
  try {
@@ -80857,7 +81026,7 @@ async function fetchToken(vaultKey) {
80857
81026
 
80858
81027
  // src/cli/apply.ts
80859
81028
  init_source();
80860
- import { accessSync as accessSync3, chownSync as chownSync5, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync74, mkdirSync as mkdirSync42, readFileSync as readFileSync61, readdirSync as readdirSync26, renameSync as renameSync14, writeFileSync as writeFileSync36 } from "node:fs";
81029
+ import { accessSync as accessSync3, chownSync as chownSync5, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync74, mkdirSync as mkdirSync42, readFileSync as readFileSync62, readdirSync as readdirSync26, renameSync as renameSync14, writeFileSync as writeFileSync37 } from "node:fs";
80861
81030
  import { mkdir as mkdir2 } from "node:fs/promises";
80862
81031
  import { spawnSync as childSpawnSync } from "node:child_process";
80863
81032
  import readline from "node:readline";
@@ -81254,7 +81423,7 @@ init_loader();
81254
81423
  init_loader();
81255
81424
 
81256
81425
  // src/cli/update-prompt-hook.ts
81257
- import { existsSync as existsSync72, readFileSync as readFileSync60, writeFileSync as writeFileSync35, chmodSync as chmodSync10, mkdirSync as mkdirSync41 } from "node:fs";
81426
+ import { existsSync as existsSync72, readFileSync as readFileSync61, writeFileSync as writeFileSync36, chmodSync as chmodSync10, mkdirSync as mkdirSync41 } from "node:fs";
81258
81427
  import { join as join72 } from "node:path";
81259
81428
  var HOOK_FILENAME = "update-card-on-prompt.sh";
81260
81429
  function updatePromptHookScript() {
@@ -81326,9 +81495,9 @@ function installUpdatePromptHook(agentDir) {
81326
81495
  const scriptPath = join72(hooksDir, HOOK_FILENAME);
81327
81496
  const desired = updatePromptHookScript();
81328
81497
  let installed = false;
81329
- const existing = existsSync72(scriptPath) ? readFileSync60(scriptPath, "utf-8") : "";
81498
+ const existing = existsSync72(scriptPath) ? readFileSync61(scriptPath, "utf-8") : "";
81330
81499
  if (existing !== desired) {
81331
- writeFileSync35(scriptPath, desired, { mode: 493 });
81500
+ writeFileSync36(scriptPath, desired, { mode: 493 });
81332
81501
  chmodSync10(scriptPath, 493);
81333
81502
  installed = true;
81334
81503
  } else {
@@ -81340,7 +81509,7 @@ function installUpdatePromptHook(agentDir) {
81340
81509
  if (!existsSync72(settingsPath)) {
81341
81510
  return { scriptPath, settingsPath, installed };
81342
81511
  }
81343
- const raw = readFileSync60(settingsPath, "utf-8");
81512
+ const raw = readFileSync61(settingsPath, "utf-8");
81344
81513
  let parsed;
81345
81514
  try {
81346
81515
  parsed = JSON.parse(raw);
@@ -81373,7 +81542,7 @@ function installUpdatePromptHook(agentDir) {
81373
81542
  });
81374
81543
  hooks.UserPromptSubmit = list2;
81375
81544
  parsed.hooks = hooks;
81376
- writeFileSync35(settingsPath, JSON.stringify(parsed, null, 2) + `
81545
+ writeFileSync36(settingsPath, JSON.stringify(parsed, null, 2) + `
81377
81546
  `, { mode: 384 });
81378
81547
  installed = true;
81379
81548
  }
@@ -81496,24 +81665,24 @@ async function ensureHostMountSources(config) {
81496
81665
  }
81497
81666
  const autoUnlockPath = join74(home2, ".switchroom", "vault-auto-unlock");
81498
81667
  if (!existsSync74(autoUnlockPath)) {
81499
- writeFileSync36(autoUnlockPath, "", { mode: 384 });
81668
+ writeFileSync37(autoUnlockPath, "", { mode: 384 });
81500
81669
  }
81501
81670
  const auditLogPath = join74(home2, ".switchroom", "vault-audit.log");
81502
81671
  if (!existsSync74(auditLogPath)) {
81503
- writeFileSync36(auditLogPath, "", { mode: 420 });
81672
+ writeFileSync37(auditLogPath, "", { mode: 420 });
81504
81673
  }
81505
81674
  const grantsDbPath = join74(home2, ".switchroom", "vault-grants.db");
81506
81675
  if (!existsSync74(grantsDbPath)) {
81507
- writeFileSync36(grantsDbPath, "", { mode: 384 });
81676
+ writeFileSync37(grantsDbPath, "", { mode: 384 });
81508
81677
  }
81509
81678
  const hostdAuditLogPath = join74(home2, ".switchroom", "host-control-audit.log");
81510
81679
  if (!existsSync74(hostdAuditLogPath)) {
81511
- writeFileSync36(hostdAuditLogPath, "", { mode: 420 });
81680
+ writeFileSync37(hostdAuditLogPath, "", { mode: 420 });
81512
81681
  }
81513
81682
  for (const name of Object.keys(config.agents)) {
81514
81683
  const tokenPath = join74(home2, ".switchroom", "agents", name, ".vault-token");
81515
81684
  if (!existsSync74(tokenPath)) {
81516
- writeFileSync36(tokenPath, "", { mode: 384 });
81685
+ writeFileSync37(tokenPath, "", { mode: 384 });
81517
81686
  }
81518
81687
  try {
81519
81688
  const uid = allocateAgentUid(name);
@@ -81524,13 +81693,13 @@ async function ensureHostMountSources(config) {
81524
81693
  await mkdir2(fleetDir, { recursive: true });
81525
81694
  const invariantsPath = join74(fleetDir, "switchroom-invariants.md");
81526
81695
  const invariantsCanonical = renderFleetInvariants();
81527
- const invariantsCurrent = existsSync74(invariantsPath) ? readFileSync61(invariantsPath, "utf-8") : null;
81696
+ const invariantsCurrent = existsSync74(invariantsPath) ? readFileSync62(invariantsPath, "utf-8") : null;
81528
81697
  if (invariantsCurrent !== invariantsCanonical) {
81529
- writeFileSync36(invariantsPath, invariantsCanonical, { mode: 420 });
81698
+ writeFileSync37(invariantsPath, invariantsCanonical, { mode: 420 });
81530
81699
  }
81531
81700
  const fleetClaudePath = join74(fleetDir, "CLAUDE.md");
81532
81701
  if (!existsSync74(fleetClaudePath)) {
81533
- writeFileSync36(fleetClaudePath, [
81702
+ writeFileSync37(fleetClaudePath, [
81534
81703
  "# Switchroom fleet defaults",
81535
81704
  "",
81536
81705
  "Operator-owned fleet brain. Every agent reads this via",
@@ -81622,7 +81791,7 @@ function writeInstallTypeCache(homeDir = homedir43()) {
81622
81791
  detected_at: new Date().toISOString(),
81623
81792
  source_paths: ctx.source_paths
81624
81793
  };
81625
- writeFileSync36(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
81794
+ writeFileSync37(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
81626
81795
  renameSync14(tmp, out);
81627
81796
  return out;
81628
81797
  }
@@ -81818,10 +81987,10 @@ Done. Scaffolded ${scaffolded}/${allAgentNames.length} agents.
81818
81987
  };
81819
81988
  }
81820
81989
  function formatScaffoldFailureResolution(failures, scaffolded, agentsTotal) {
81821
- const fail2 = failures.length;
81990
+ const fail3 = failures.length;
81822
81991
  const lines = [];
81823
81992
  lines.push("");
81824
- lines.push(`ERROR: Scaffolded ${scaffolded}/${agentsTotal} agents. ${fail2} failed.`);
81993
+ lines.push(`ERROR: Scaffolded ${scaffolded}/${agentsTotal} agents. ${fail3} failed.`);
81825
81994
  lines.push("");
81826
81995
  lines.push("Per-agent state dirs are mode 0700 owned by per-agent UIDs (the v0.7+");
81827
81996
  lines.push("docker model). The operator cannot write into them without privilege");
@@ -81854,7 +82023,7 @@ function copyExampleConfig2(name) {
81854
82023
  }
81855
82024
  const embedded = EMBEDDED_EXAMPLES[name];
81856
82025
  if (embedded !== undefined) {
81857
- writeFileSync36(dest, embedded, { encoding: "utf8" });
82026
+ writeFileSync37(dest, embedded, { encoding: "utf8" });
81858
82027
  console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
81859
82028
  return;
81860
82029
  }
@@ -82049,7 +82218,7 @@ function runRedactStdin() {
82049
82218
  }
82050
82219
 
82051
82220
  // src/cli/status-ask.ts
82052
- import { readFileSync as readFileSync62, existsSync as existsSync75, readdirSync as readdirSync27 } from "node:fs";
82221
+ import { readFileSync as readFileSync63, existsSync as existsSync75, readdirSync as readdirSync27 } from "node:fs";
82053
82222
  import { join as join75 } from "node:path";
82054
82223
  import { homedir as homedir44 } from "node:os";
82055
82224
 
@@ -82325,7 +82494,7 @@ function runReport(opts) {
82325
82494
  for (const src of sources) {
82326
82495
  let content;
82327
82496
  try {
82328
- content = readFileSync62(src.path, "utf-8");
82497
+ content = readFileSync63(src.path, "utf-8");
82329
82498
  } catch (err) {
82330
82499
  process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
82331
82500
  `);
@@ -82430,7 +82599,7 @@ import {
82430
82599
  mkdirSync as mkdirSync43,
82431
82600
  openSync as openSync13,
82432
82601
  readdirSync as readdirSync28,
82433
- readFileSync as readFileSync63,
82602
+ readFileSync as readFileSync64,
82434
82603
  renameSync as renameSync15,
82435
82604
  statSync as statSync30,
82436
82605
  unlinkSync as unlinkSync14,
@@ -82554,7 +82723,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
82554
82723
  continue;
82555
82724
  const full = join76(paths.skillsDir, name);
82556
82725
  try {
82557
- const raw = readFileSync63(full, "utf-8");
82726
+ const raw = readFileSync64(full, "utf-8");
82558
82727
  const slug = name.replace(/\.ya?ml$/i, "");
82559
82728
  out.push({ slug, path: full, raw });
82560
82729
  } catch {}
@@ -82581,7 +82750,7 @@ function listOverlayEntries(agent, opts = {}) {
82581
82750
  continue;
82582
82751
  const full = join76(paths.scheduleDir, name);
82583
82752
  try {
82584
- const raw = readFileSync63(full, "utf-8");
82753
+ const raw = readFileSync64(full, "utf-8");
82585
82754
  const slug = name.replace(/\.ya?ml$/i, "");
82586
82755
  out.push({ slug, path: full, raw });
82587
82756
  } catch {}
@@ -82729,10 +82898,10 @@ import {
82729
82898
  mkdirSync as mkdirSync44,
82730
82899
  openSync as openSync14,
82731
82900
  readdirSync as readdirSync29,
82732
- readFileSync as readFileSync64,
82901
+ readFileSync as readFileSync65,
82733
82902
  renameSync as renameSync16,
82734
82903
  unlinkSync as unlinkSync15,
82735
- writeFileSync as writeFileSync37,
82904
+ writeFileSync as writeFileSync38,
82736
82905
  writeSync as writeSync9
82737
82906
  } from "node:fs";
82738
82907
  import { join as join77 } from "node:path";
@@ -82775,7 +82944,7 @@ function stagePendingScheduleEntry(opts) {
82775
82944
  }
82776
82945
  renameSync16(yamlTmp, yamlPath);
82777
82946
  }
82778
- writeFileSync37(metaPath, JSON.stringify(meta, null, 2) + `
82947
+ writeFileSync38(metaPath, JSON.stringify(meta, null, 2) + `
82779
82948
  `, { mode: 384 });
82780
82949
  return { stageId, yamlPath, metaPath };
82781
82950
  }
@@ -82793,7 +82962,7 @@ function listPendingScheduleEntries(agent, opts = {}) {
82793
82962
  if (!existsSync77(yamlPath))
82794
82963
  continue;
82795
82964
  try {
82796
- const meta = JSON.parse(readFileSync64(metaPath, "utf-8"));
82965
+ const meta = JSON.parse(readFileSync65(metaPath, "utf-8"));
82797
82966
  if (meta?.v !== 1 || typeof meta.stage_id !== "string")
82798
82967
  continue;
82799
82968
  out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
@@ -82831,7 +83000,7 @@ function denyPendingScheduleEntry(opts) {
82831
83000
  }
82832
83001
 
82833
83002
  // src/cli/agent-config-write.ts
82834
- import { existsSync as existsSync78, readFileSync as readFileSync65 } from "node:fs";
83003
+ import { existsSync as existsSync78, readFileSync as readFileSync66 } from "node:fs";
82835
83004
  import { execFileSync as execFileSync25 } from "node:child_process";
82836
83005
 
82837
83006
  // src/scheduler/schedule-report.ts
@@ -83199,7 +83368,7 @@ function scheduleRemove(opts) {
83199
83368
  let priorContent = null;
83200
83369
  try {
83201
83370
  if (existsSync78(match.path))
83202
- priorContent = readFileSync65(match.path, "utf-8");
83371
+ priorContent = readFileSync66(match.path, "utf-8");
83203
83372
  } catch {}
83204
83373
  deleteOverlayEntry(agent, match.slug, { root: opts.root });
83205
83374
  const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
@@ -83402,7 +83571,7 @@ function registerAgentConfigWriteCommands(program3) {
83402
83571
  }
83403
83572
  let blob;
83404
83573
  if (opts.jsonl) {
83405
- blob = existsSync78(opts.jsonl) ? readFileSync65(opts.jsonl, "utf-8") : "";
83574
+ blob = existsSync78(opts.jsonl) ? readFileSync66(opts.jsonl, "utf-8") : "";
83406
83575
  } else {
83407
83576
  try {
83408
83577
  blob = execFileSync25("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
@@ -83682,13 +83851,13 @@ import {
83682
83851
  mkdirSync as mkdirSync45,
83683
83852
  mkdtempSync as mkdtempSync5,
83684
83853
  openSync as openSync15,
83685
- readFileSync as readFileSync66,
83854
+ readFileSync as readFileSync67,
83686
83855
  readdirSync as readdirSync30,
83687
83856
  realpathSync as realpathSync7,
83688
83857
  renameSync as renameSync17,
83689
83858
  rmSync as rmSync16,
83690
83859
  statSync as statSync31,
83691
- writeFileSync as writeFileSync38
83860
+ writeFileSync as writeFileSync39
83692
83861
  } from "node:fs";
83693
83862
  import { tmpdir as tmpdir5, homedir as homedir45 } from "node:os";
83694
83863
  import { dirname as dirname23, join as join79, relative as relative2, resolve as resolve46 } from "node:path";
@@ -83918,7 +84087,7 @@ function isTarballPath(p) {
83918
84087
  function loadFromDir(dir) {
83919
84088
  const abs = realpathSync7(dir);
83920
84089
  if (!statSync31(abs).isDirectory()) {
83921
- fail2(`--from path is not a directory: ${dir}`);
84090
+ fail3(`--from path is not a directory: ${dir}`);
83922
84091
  }
83923
84092
  const files = {};
83924
84093
  const walk2 = (sub) => {
@@ -83927,14 +84096,14 @@ function loadFromDir(dir) {
83927
84096
  const full = join79(sub, ent.name);
83928
84097
  const rel = relative2(abs, full);
83929
84098
  if (ent.isSymbolicLink()) {
83930
- fail2(`refusing to read symlink inside --from dir: ${rel}`);
84099
+ fail3(`refusing to read symlink inside --from dir: ${rel}`);
83931
84100
  }
83932
84101
  if (ent.isDirectory()) {
83933
84102
  walk2(full);
83934
84103
  continue;
83935
84104
  }
83936
84105
  if (ent.isFile()) {
83937
- const buf = readFileSync66(full);
84106
+ const buf = readFileSync67(full);
83938
84107
  files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
83939
84108
  }
83940
84109
  }
@@ -83950,13 +84119,13 @@ function loadFromTarball(tarPath) {
83950
84119
  stdio: ["ignore", "pipe", "pipe"]
83951
84120
  });
83952
84121
  if (list2.status !== 0) {
83953
- fail2(`tar list failed (exit ${list2.status}): ${(list2.stderr ?? "").trim()}`);
84122
+ fail3(`tar list failed (exit ${list2.status}): ${(list2.stderr ?? "").trim()}`);
83954
84123
  }
83955
84124
  const entries = (list2.stdout ?? "").split(`
83956
84125
  `).map((s) => s.trim()).filter((s) => s.length > 0 && !s.endsWith("/"));
83957
84126
  for (const entry of entries) {
83958
84127
  if (!validateRelPath(entry)) {
83959
- fail2(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
84128
+ fail3(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
83960
84129
  }
83961
84130
  }
83962
84131
  const staging = mkdtempSync5(join79(tmpdir5(), "skill-apply-extract-"));
@@ -83973,7 +84142,7 @@ function loadFromTarball(tarPath) {
83973
84142
  ], { stdio: "pipe" });
83974
84143
  if (r.status !== 0) {
83975
84144
  const stderr = r.stderr?.toString("utf-8") ?? "(no stderr)";
83976
- fail2(`tar extraction failed (exit ${r.status}): ${stderr.trim()}`);
84145
+ fail3(`tar extraction failed (exit ${r.status}): ${stderr.trim()}`);
83977
84146
  }
83978
84147
  return loadFromDir(staging);
83979
84148
  } finally {
@@ -83983,13 +84152,13 @@ function loadFromTarball(tarPath) {
83983
84152
  }
83984
84153
  }
83985
84154
  function loadSingleFile(filePath) {
83986
- const content = readFileSync66(filePath, "utf-8");
84155
+ const content = readFileSync67(filePath, "utf-8");
83987
84156
  return { "SKILL.md": content };
83988
84157
  }
83989
84158
  function loadFromStdin() {
83990
84159
  const content = readStdinSync();
83991
84160
  if (content.length === 0) {
83992
- fail2("no content on stdin; either pipe SKILL.md content in or pass --from");
84161
+ fail3("no content on stdin; either pipe SKILL.md content in or pass --from");
83993
84162
  }
83994
84163
  return { "SKILL.md": content };
83995
84164
  }
@@ -84048,7 +84217,7 @@ function validatePayload(name, files) {
84048
84217
  const tmp = mkdtempSync5(join79(tmpdir5(), "skill-apply-py-"));
84049
84218
  const tmpPy = join79(tmp, "check.py");
84050
84219
  try {
84051
- writeFileSync38(tmpPy, content);
84220
+ writeFileSync39(tmpPy, content);
84052
84221
  const r = spawnSync11("python3", ["-m", "py_compile", tmpPy], {
84053
84222
  encoding: "utf-8"
84054
84223
  });
@@ -84074,7 +84243,7 @@ function diffSummary(currentDir, files) {
84074
84243
  if (ent.isDirectory()) {
84075
84244
  walk2(full);
84076
84245
  } else if (ent.isFile()) {
84077
- currentFiles[rel.replace(/\\/g, "/")] = readFileSync66(full, "utf-8");
84246
+ currentFiles[rel.replace(/\\/g, "/")] = readFileSync67(full, "utf-8");
84078
84247
  }
84079
84248
  }
84080
84249
  };
@@ -84114,7 +84283,7 @@ function writePayload(poolDir, name, files) {
84114
84283
  }
84115
84284
  } catch {}
84116
84285
  if (targetIsSymlink) {
84117
- fail2(`refusing to overwrite symlink at ${target}; investigate manually`);
84286
+ fail3(`refusing to overwrite symlink at ${target}; investigate manually`);
84118
84287
  }
84119
84288
  const staging = mkdtempSync5(join79(poolDir, `.skill-apply-stage-${name}-`));
84120
84289
  let oldRename = null;
@@ -84124,7 +84293,7 @@ function writePayload(poolDir, name, files) {
84124
84293
  mkdirSync45(dirname23(full), { recursive: true, mode: 493 });
84125
84294
  const fd = openSync15(full, "wx");
84126
84295
  try {
84127
- writeFileSync38(fd, content);
84296
+ writeFileSync39(fd, content);
84128
84297
  } finally {
84129
84298
  closeSync15(fd);
84130
84299
  }
@@ -84162,7 +84331,7 @@ function writePayload(poolDir, name, files) {
84162
84331
  throw err2;
84163
84332
  }
84164
84333
  }
84165
- function fail2(msg) {
84334
+ function fail3(msg) {
84166
84335
  console.error(source_default.red(`error: ${msg}`));
84167
84336
  process.exit(2);
84168
84337
  }
@@ -84175,7 +84344,7 @@ function registerSkillCommand(program3) {
84175
84344
  } else {
84176
84345
  const fromPath = resolve46(opts.from);
84177
84346
  if (!existsSync80(fromPath)) {
84178
- fail2(`--from path does not exist: ${opts.from}`);
84347
+ fail3(`--from path does not exist: ${opts.from}`);
84179
84348
  }
84180
84349
  const st = statSync31(fromPath);
84181
84350
  if (st.isDirectory()) {
@@ -84185,7 +84354,7 @@ function registerSkillCommand(program3) {
84185
84354
  } else if (fromPath.endsWith(".md")) {
84186
84355
  files = loadSingleFile(fromPath);
84187
84356
  } else {
84188
- fail2(`--from must be a directory, a .tar/.tar.gz/.tgz tarball, or a ` + `.md file. Got: ${opts.from}`);
84357
+ fail3(`--from must be a directory, a .tar/.tar.gz/.tgz tarball, or a ` + `.md file. Got: ${opts.from}`);
84189
84358
  }
84190
84359
  }
84191
84360
  const v = validatePayload(name, files);
@@ -84234,13 +84403,13 @@ import {
84234
84403
  mkdirSync as mkdirSync46,
84235
84404
  mkdtempSync as mkdtempSync6,
84236
84405
  openSync as openSync16,
84237
- readFileSync as readFileSync67,
84406
+ readFileSync as readFileSync68,
84238
84407
  readdirSync as readdirSync31,
84239
84408
  renameSync as renameSync18,
84240
84409
  rmSync as rmSync17,
84241
84410
  statSync as statSync32,
84242
84411
  utimesSync,
84243
- writeFileSync as writeFileSync39
84412
+ writeFileSync as writeFileSync40
84244
84413
  } from "node:fs";
84245
84414
  import { dirname as dirname24, join as join80, relative as relative3, resolve as resolve47 } from "node:path";
84246
84415
  import { homedir as homedir46, tmpdir as tmpdir6 } from "node:os";
@@ -84316,7 +84485,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
84316
84485
  if (ent.isDirectory())
84317
84486
  walk2(s, d);
84318
84487
  else if (ent.isFile()) {
84319
- writeFileSync39(d, readFileSync67(s));
84488
+ writeFileSync40(d, readFileSync68(s));
84320
84489
  }
84321
84490
  }
84322
84491
  };
@@ -84331,7 +84500,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
84331
84500
  `));
84332
84501
  }
84333
84502
  }
84334
- function fail3(msg, exit = 2) {
84503
+ function fail4(msg, exit = 2) {
84335
84504
  console.error(source_default.red(`error: ${msg}`));
84336
84505
  process.exit(exit);
84337
84506
  }
@@ -84339,10 +84508,10 @@ function resolveAgent(opts) {
84339
84508
  const fromEnv = process.env.SWITCHROOM_AGENT_NAME;
84340
84509
  const agent = opts.agent ?? fromEnv;
84341
84510
  if (!agent) {
84342
- fail3("agent name required: pass --agent <name>, or set SWITCHROOM_AGENT_NAME " + "in the calling environment (set by switchroom for in-container agents).");
84511
+ fail4("agent name required: pass --agent <name>, or set SWITCHROOM_AGENT_NAME " + "in the calling environment (set by switchroom for in-container agents).");
84343
84512
  }
84344
84513
  if (!SKILL_SLUG_RE.test(agent)) {
84345
- fail3(`agent name has invalid shape: ${JSON.stringify(agent)}`);
84514
+ fail4(`agent name has invalid shape: ${JSON.stringify(agent)}`);
84346
84515
  }
84347
84516
  return agent;
84348
84517
  }
@@ -84380,14 +84549,14 @@ function readStdinSync2() {
84380
84549
  function loadFromDir2(dir) {
84381
84550
  const abs = resolve47(dir);
84382
84551
  if (!statSync32(abs).isDirectory()) {
84383
- fail3(`--from path is not a directory: ${dir}`);
84552
+ fail4(`--from path is not a directory: ${dir}`);
84384
84553
  }
84385
84554
  const files = {};
84386
84555
  const walk2 = (sub) => {
84387
84556
  for (const ent of readdirSync31(sub, { withFileTypes: true })) {
84388
84557
  const full = join80(sub, ent.name);
84389
84558
  if (ent.isSymbolicLink()) {
84390
- fail3(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
84559
+ fail4(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
84391
84560
  }
84392
84561
  if (ent.isDirectory()) {
84393
84562
  walk2(full);
@@ -84395,7 +84564,7 @@ function loadFromDir2(dir) {
84395
84564
  }
84396
84565
  if (ent.isFile()) {
84397
84566
  const rel = relative3(abs, full).replace(/\\/g, "/");
84398
- files[rel] = readFileSync67(full, "utf-8");
84567
+ files[rel] = readFileSync68(full, "utf-8");
84399
84568
  }
84400
84569
  }
84401
84570
  };
@@ -84405,7 +84574,7 @@ function loadFromDir2(dir) {
84405
84574
  function loadFromStdin2() {
84406
84575
  const raw = readStdinSync2();
84407
84576
  if (raw.length === 0) {
84408
- fail3("no content on stdin; pipe a SKILL.md or JSON file-map");
84577
+ fail4("no content on stdin; pipe a SKILL.md or JSON file-map");
84409
84578
  }
84410
84579
  const trimmed = raw.trimStart();
84411
84580
  if (trimmed.startsWith("{")) {
@@ -84413,15 +84582,15 @@ function loadFromStdin2() {
84413
84582
  try {
84414
84583
  parsed = JSON.parse(raw);
84415
84584
  } catch (err2) {
84416
- fail3(`stdin starts with '{' but is not valid JSON: ${err2.message}`);
84585
+ fail4(`stdin starts with '{' but is not valid JSON: ${err2.message}`);
84417
84586
  }
84418
84587
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
84419
- fail3("stdin JSON must be an object of {path: content, ...}");
84588
+ fail4("stdin JSON must be an object of {path: content, ...}");
84420
84589
  }
84421
84590
  const files = {};
84422
84591
  for (const [k, v] of Object.entries(parsed)) {
84423
84592
  if (typeof v !== "string") {
84424
- fail3(`stdin JSON: value for ${JSON.stringify(k)} must be a string`);
84593
+ fail4(`stdin JSON: value for ${JSON.stringify(k)} must be a string`);
84425
84594
  }
84426
84595
  files[k] = v;
84427
84596
  }
@@ -84441,7 +84610,7 @@ function behavioralValidate(files) {
84441
84610
  const tmp = mkdtempSync6(join80(tmpdir6(), "skill-personal-py-"));
84442
84611
  const tmpPy = join80(tmp, "check.py");
84443
84612
  try {
84444
- writeFileSync39(tmpPy, content);
84613
+ writeFileSync40(tmpPy, content);
84445
84614
  const r = spawnSync12("python3", ["-m", "py_compile", tmpPy], {
84446
84615
  encoding: "utf-8"
84447
84616
  });
@@ -84481,7 +84650,7 @@ function writePersonalSkill(targetDir, files) {
84481
84650
  }
84482
84651
  } catch {}
84483
84652
  if (targetIsSymlink) {
84484
- fail3(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
84653
+ fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
84485
84654
  }
84486
84655
  mkdirSync46(dirname24(targetDir), { recursive: true, mode: 493 });
84487
84656
  const staging = mkdtempSync6(join80(dirname24(targetDir), `.skill-personal-stage-`));
@@ -84492,7 +84661,7 @@ function writePersonalSkill(targetDir, files) {
84492
84661
  mkdirSync46(dirname24(full), { recursive: true, mode: 493 });
84493
84662
  const fd = openSync16(full, "wx");
84494
84663
  try {
84495
- writeFileSync39(fd, content);
84664
+ writeFileSync40(fd, content);
84496
84665
  } finally {
84497
84666
  closeSync16(fd);
84498
84667
  }
@@ -84533,7 +84702,7 @@ function writePersonalSkill(targetDir, files) {
84533
84702
  function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
84534
84703
  sweepTrash(agentsRoot, agent);
84535
84704
  if (!SKILL_SLUG_RE.test(name)) {
84536
- fail3(`skill name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(name)}`);
84705
+ fail4(`skill name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(name)}`);
84537
84706
  }
84538
84707
  const target = personalSkillDir(agentsRoot, agent, name);
84539
84708
  const exists = (() => {
@@ -84545,10 +84714,10 @@ function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
84545
84714
  }
84546
84715
  })();
84547
84716
  if (ensureNew && exists) {
84548
- fail3(`personal skill ${JSON.stringify(name)} already exists for agent ${JSON.stringify(agent)}. ` + `Use \`skill edit-personal\` to overwrite, or \`skill remove-personal\` first.`, 9);
84717
+ fail4(`personal skill ${JSON.stringify(name)} already exists for agent ${JSON.stringify(agent)}. ` + `Use \`skill edit-personal\` to overwrite, or \`skill remove-personal\` first.`, 9);
84549
84718
  }
84550
84719
  if (!ensureNew && !exists) {
84551
- fail3(`personal skill ${JSON.stringify(name)} does not exist for agent ${JSON.stringify(agent)}. ` + `Use \`skill init-personal\` to create it.`, 9);
84720
+ fail4(`personal skill ${JSON.stringify(name)} does not exist for agent ${JSON.stringify(agent)}. ` + `Use \`skill init-personal\` to create it.`, 9);
84552
84721
  }
84553
84722
  const v = validateSkillBundle(name, files);
84554
84723
  if (!v.ok) {
@@ -84574,16 +84743,16 @@ function loadFiles(opts) {
84574
84743
  }
84575
84744
  const p = resolve47(opts.from);
84576
84745
  if (!existsSync81(p)) {
84577
- fail3(`--from path does not exist: ${opts.from}`);
84746
+ fail4(`--from path does not exist: ${opts.from}`);
84578
84747
  }
84579
84748
  const st = statSync32(p);
84580
84749
  if (st.isDirectory()) {
84581
84750
  return loadFromDir2(p);
84582
84751
  }
84583
84752
  if (p.endsWith(".md")) {
84584
- return { "SKILL.md": readFileSync67(p, "utf-8") };
84753
+ return { "SKILL.md": readFileSync68(p, "utf-8") };
84585
84754
  }
84586
- fail3(`--from must be a directory or a .md file. Got: ${opts.from}`);
84755
+ fail4(`--from must be a directory or a .md file. Got: ${opts.from}`);
84587
84756
  }
84588
84757
  function initPersonalAction(name, opts) {
84589
84758
  const agent = resolveAgent(opts);
@@ -84629,18 +84798,18 @@ function defaultBundledRoot() {
84629
84798
  function resolveCloneSource(source, opts) {
84630
84799
  const m = CLONE_SOURCE_RE.exec(source);
84631
84800
  if (!m) {
84632
- fail3(`source must be \`shared:<name>\` or \`bundled:<name>\` (got ${JSON.stringify(source)})`);
84801
+ fail4(`source must be \`shared:<name>\` or \`bundled:<name>\` (got ${JSON.stringify(source)})`);
84633
84802
  }
84634
84803
  const tier = m[1];
84635
84804
  const slug = m[2];
84636
84805
  const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
84637
84806
  const dir = join80(root, slug);
84638
84807
  if (!existsSync81(dir)) {
84639
- fail3(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
84808
+ fail4(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
84640
84809
  }
84641
84810
  const st = lstatSync9(dir);
84642
84811
  if (st.isSymbolicLink()) {
84643
- fail3(`clone source ${JSON.stringify(source)} is a symlink at ${dir}; ` + `point clone at the canonical pool path instead`);
84812
+ fail4(`clone source ${JSON.stringify(source)} is a symlink at ${dir}; ` + `point clone at the canonical pool path instead`);
84644
84813
  }
84645
84814
  return { tier, slug, dir };
84646
84815
  }
@@ -84667,10 +84836,10 @@ function readSourceFiles(dir) {
84667
84836
  try {
84668
84837
  const st = lstatSync9(full);
84669
84838
  if (st.size > CLONE_MAX_FILE_BYTES) {
84670
- fail3(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
84839
+ fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
84671
84840
  }
84672
84841
  } catch {}
84673
- files[rel] = readFileSync67(full, "utf-8");
84842
+ files[rel] = readFileSync68(full, "utf-8");
84674
84843
  }
84675
84844
  }
84676
84845
  };
@@ -84701,11 +84870,11 @@ function clonePersonalAction(source, opts) {
84701
84870
  const src = resolveCloneSource(source, opts);
84702
84871
  const newName = opts.name ?? src.slug;
84703
84872
  if (!SKILL_SLUG_RE.test(newName)) {
84704
- fail3(`destination name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(newName)}`);
84873
+ fail4(`destination name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(newName)}`);
84705
84874
  }
84706
84875
  const { files, skipped } = readSourceFiles(src.dir);
84707
84876
  if (!files["SKILL.md"]) {
84708
- fail3(`source ${JSON.stringify(source)} has no SKILL.md at ${src.dir}`);
84877
+ fail4(`source ${JSON.stringify(source)} has no SKILL.md at ${src.dir}`);
84709
84878
  }
84710
84879
  if (newName !== src.slug) {
84711
84880
  files["SKILL.md"] = rewriteSkillMdName(files["SKILL.md"], newName);
@@ -84743,18 +84912,18 @@ function removePersonalAction(name, opts) {
84743
84912
  const agentsRoot = resolveAgentsRoot(opts);
84744
84913
  sweepTrash(agentsRoot, agent);
84745
84914
  if (!SKILL_SLUG_RE.test(name)) {
84746
- fail3(`skill name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(name)}`);
84915
+ fail4(`skill name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(name)}`);
84747
84916
  }
84748
84917
  const target = personalSkillDir(agentsRoot, agent, name);
84749
84918
  try {
84750
84919
  const st = lstatSync9(target);
84751
84920
  if (st.isSymbolicLink()) {
84752
- fail3(`refusing to remove symlink at ${target}; investigate manually`);
84921
+ fail4(`refusing to remove symlink at ${target}; investigate manually`);
84753
84922
  }
84754
84923
  } catch (err2) {
84755
84924
  const e = err2;
84756
84925
  if (e.code === "ENOENT") {
84757
- fail3(`personal skill ${JSON.stringify(name)} does not exist for agent ${JSON.stringify(agent)}`, 1);
84926
+ fail4(`personal skill ${JSON.stringify(name)} does not exist for agent ${JSON.stringify(agent)}`, 1);
84758
84927
  }
84759
84928
  throw err2;
84760
84929
  }
@@ -84839,7 +85008,7 @@ function registerSkillPersonalCommands(program3) {
84839
85008
  // src/cli/skill-search.ts
84840
85009
  init_helpers();
84841
85010
  var import_yaml23 = __toESM(require_dist(), 1);
84842
- import { existsSync as existsSync82, readdirSync as readdirSync32, readFileSync as readFileSync68, statSync as statSync33 } from "node:fs";
85011
+ import { existsSync as existsSync82, readdirSync as readdirSync32, readFileSync as readFileSync69, statSync as statSync33 } from "node:fs";
84843
85012
  import { homedir as homedir47 } from "node:os";
84844
85013
  import { join as join81, resolve as resolve48 } from "node:path";
84845
85014
  var PERSONAL_PREFIX2 = "personal-";
@@ -84860,7 +85029,7 @@ function readSkillFrontmatter(skillDir) {
84860
85029
  return null;
84861
85030
  let content;
84862
85031
  try {
84863
- content = readFileSync68(mdPath, "utf-8");
85032
+ content = readFileSync69(mdPath, "utf-8");
84864
85033
  } catch {
84865
85034
  return null;
84866
85035
  }
@@ -85132,7 +85301,7 @@ function registerHostdMcpCommand(program3) {
85132
85301
  // src/cli/hostd.ts
85133
85302
  init_source();
85134
85303
  init_helpers();
85135
- import { existsSync as existsSync84, mkdirSync as mkdirSync47, readdirSync as readdirSync33, readFileSync as readFileSync70, writeFileSync as writeFileSync40, statSync as statSync34, copyFileSync as copyFileSync12 } from "node:fs";
85304
+ import { existsSync as existsSync84, mkdirSync as mkdirSync47, readdirSync as readdirSync33, readFileSync as readFileSync71, writeFileSync as writeFileSync41, statSync as statSync34, copyFileSync as copyFileSync12 } from "node:fs";
85136
85305
  import { homedir as homedir48 } from "node:os";
85137
85306
  import { join as join82 } from "node:path";
85138
85307
  import { spawnSync as spawnSync14 } from "node:child_process";
@@ -85308,7 +85477,7 @@ async function doInstall(opts, program3) {
85308
85477
  const bak = backupExistingCompose();
85309
85478
  if (bak)
85310
85479
  console.log(source_default.dim(` Backed up existing compose to ${bak}`));
85311
- writeFileSync40(composePath, yaml, "utf8");
85480
+ writeFileSync41(composePath, yaml, "utf8");
85312
85481
  console.log(source_default.green(` \u2713 Wrote ${composePath}`));
85313
85482
  const adminAgents = Object.entries(cfg.agents ?? {}).filter(([, a]) => a?.admin === true).map(([name]) => name);
85314
85483
  console.log(source_default.dim(` agents served (one socket each): ${allAgents.length === 0 ? "(none)" : allAgents.join(", ")}`));
@@ -85415,7 +85584,7 @@ function registerHostdCommand(program3) {
85415
85584
  The log is created when hostd handles its first privileged-verb request.`));
85416
85585
  return;
85417
85586
  }
85418
- const raw = readFileSync70(logPath, "utf-8");
85587
+ const raw = readFileSync71(logPath, "utf-8");
85419
85588
  const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
85420
85589
  const filters = {
85421
85590
  agent: opts.agent,
@@ -85457,7 +85626,7 @@ The log is created when hostd handles its first privileged-verb request.`));
85457
85626
  // src/cli/webd.ts
85458
85627
  init_source();
85459
85628
  init_helpers();
85460
- import { existsSync as existsSync85, mkdirSync as mkdirSync48, writeFileSync as writeFileSync41, copyFileSync as copyFileSync13 } from "node:fs";
85629
+ import { existsSync as existsSync85, mkdirSync as mkdirSync48, writeFileSync as writeFileSync42, copyFileSync as copyFileSync13 } from "node:fs";
85461
85630
  import { homedir as homedir49 } from "node:os";
85462
85631
  import { join as join83 } from "node:path";
85463
85632
  import { spawnSync as spawnSync15 } from "node:child_process";
@@ -85593,7 +85762,7 @@ async function doInstall2(opts, program3) {
85593
85762
  const bak = backupExistingCompose2();
85594
85763
  if (bak)
85595
85764
  console.log(source_default.dim(` Backed up existing compose to ${bak}`));
85596
- writeFileSync41(composePath, yaml, "utf8");
85765
+ writeFileSync42(composePath, yaml, "utf8");
85597
85766
  console.log(source_default.green(` \u2713 Wrote ${composePath}`));
85598
85767
  console.log(source_default.dim(` running as uid ${operatorUid} (operator), network_mode: host`));
85599
85768
  console.log(source_default.dim(` Pulling ghcr.io/switchroom/switchroom-web:${imageTag}\u2026`));
@@ -85698,6 +85867,7 @@ registerTopicsCommand(program3);
85698
85867
  registerAuthCommand(program3);
85699
85868
  registerVaultCommand(program3);
85700
85869
  registerTelegramCommand(program3);
85870
+ registerLinearAgentCommand(program3);
85701
85871
  registerMemoryCommand(program3);
85702
85872
  registerWebCommand(program3);
85703
85873
  registerHandoffCommand(program3);