switchroom 0.15.10 → 0.15.12

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,11 @@ 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
+ }).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
13719
  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
13720
  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
13721
  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.")
@@ -15435,87 +15440,9 @@ var init_cron_routing = __esm(() => {
15435
15440
  OPUS_MODEL_RE = /opus/i;
15436
15441
  });
15437
15442
 
15438
- // src/scheduler/cron-cadence.ts
15439
- function csvSmallestGap(field) {
15440
- if (!field.includes(","))
15441
- return null;
15442
- const parts = field.split(",").map((s) => Number(s)).filter((n) => Number.isInteger(n) && n >= 0);
15443
- if (parts.length < 2)
15444
- return null;
15445
- const sorted = [...parts].sort((a, b) => a - b);
15446
- let smallest = Infinity;
15447
- for (let i = 1;i < sorted.length; i++) {
15448
- const gap = sorted[i] - sorted[i - 1];
15449
- if (gap > 0 && gap < smallest)
15450
- smallest = gap;
15451
- }
15452
- return Number.isFinite(smallest) ? smallest : null;
15453
- }
15454
- function estimateCronGapMin(expr) {
15455
- const fields = expr.trim().split(/\s+/);
15456
- if (fields.length < 5)
15457
- return Infinity;
15458
- const [min, hour] = fields;
15459
- if (min === "*")
15460
- return 1;
15461
- const minStep = min.match(/^\*\/(\d+)$/);
15462
- if (minStep) {
15463
- const n = Number(minStep[1]);
15464
- return n > 0 ? n : Infinity;
15465
- }
15466
- const minCsv = csvSmallestGap(min);
15467
- if (minCsv !== null)
15468
- return minCsv;
15469
- if (!/^\d+$/.test(min))
15470
- return Infinity;
15471
- if (hour === "*")
15472
- return 60;
15473
- const hourStep = hour.match(/^\*\/(\d+)$/);
15474
- if (hourStep) {
15475
- const n = Number(hourStep[1]);
15476
- return n > 0 ? n * 60 : Infinity;
15477
- }
15478
- const hourCsv = csvSmallestGap(hour);
15479
- if (hourCsv !== null)
15480
- return hourCsv * 60;
15481
- if (/^\d+$/.test(hour))
15482
- return 1440;
15483
- return Infinity;
15484
- }
15485
-
15486
15443
  // src/scheduler/tier-selector.ts
15487
- function recommendCronTier(input, frequentGapMin = DEFAULT_FREQUENT_GAP_MIN) {
15488
- if (input.kind === "poll") {
15489
- return { tier: "poll", source: "explicit", reason: "declared kind: poll (model-free check)" };
15490
- }
15491
- if (input.context === "fresh") {
15492
- return { tier: "cheap", source: "explicit", reason: "declared context: fresh (cheap cron session)" };
15493
- }
15494
- if (input.context === "agent") {
15495
- return { tier: "main", source: "explicit", reason: "declared context: agent (full live session)" };
15496
- }
15497
- if (input.model !== undefined) {
15498
- return isKnownCheapModel(input.model) ? { tier: "cheap", source: "explicit", reason: `cheap model '${input.model}' \u2192 cheap cron session` } : { tier: "main", source: "explicit", reason: `model '${input.model}' is not a known-cheap id \u2192 full live session` };
15499
- }
15500
- if (input.smallestGapMin <= frequentGapMin) {
15501
- return {
15502
- tier: "cheap",
15503
- source: "cadence-default",
15504
- reason: `fires every ~${input.smallestGapMin}min (\u2264 ${frequentGapMin}min) \u2014 defaulting to a cheap ` + `session; set context: agent (or an Opus/custom model) if this needs the agent's full context`
15505
- };
15506
- }
15507
- return {
15508
- tier: "main",
15509
- source: "cadence-default",
15510
- reason: `fires every ~${input.smallestGapMin}min (> ${frequentGapMin}min) \u2014 defaulting to the agent's ` + `full session; set model: sonnet (or context: fresh) to run it cheaply`
15511
- };
15512
- }
15513
- function applyDefaultTier(entry, frequentGapMin = DEFAULT_FREQUENT_GAP_MIN) {
15514
- if (entry.kind === "poll" || entry.context !== undefined || entry.model !== undefined) {
15515
- return entry;
15516
- }
15517
- const rec = recommendCronTier({ smallestGapMin: estimateCronGapMin(entry.cron) }, frequentGapMin);
15518
- return rec.tier === "cheap" ? { ...entry, context: "fresh" } : entry;
15444
+ function applyDefaultTier(entry, _frequentGapMin = DEFAULT_FREQUENT_GAP_MIN) {
15445
+ return entry;
15519
15446
  }
15520
15447
  var DEFAULT_FREQUENT_GAP_MIN = 60;
15521
15448
  var init_tier_selector = __esm(() => {
@@ -29244,7 +29171,7 @@ var init_thinking_effort_risk = __esm(() => {
29244
29171
  // src/manifest.ts
29245
29172
  import {
29246
29173
  existsSync as existsSync52,
29247
- readFileSync as readFileSync47,
29174
+ readFileSync as readFileSync48,
29248
29175
  readdirSync as readdirSync19
29249
29176
  } from "node:fs";
29250
29177
  import { dirname as dirname12, join as join47 } from "node:path";
@@ -29266,7 +29193,7 @@ function loadManifest(manifestPath) {
29266
29193
  }
29267
29194
  let raw;
29268
29195
  try {
29269
- raw = readFileSync47(path4, "utf-8");
29196
+ raw = readFileSync48(path4, "utf-8");
29270
29197
  } catch (err) {
29271
29198
  throw new Error(`Failed to read manifest at ${path4}: ${err.message}`);
29272
29199
  }
@@ -29331,7 +29258,7 @@ function probePlaywrightMcpVersion() {
29331
29258
  const pkgPath = join47(npxCache, entry, "node_modules/@playwright/mcp/package.json");
29332
29259
  if (existsSync52(pkgPath)) {
29333
29260
  try {
29334
- const pkg = JSON.parse(readFileSync47(pkgPath, "utf-8"));
29261
+ const pkg = JSON.parse(readFileSync48(pkgPath, "utf-8"));
29335
29262
  if (pkg.version)
29336
29263
  return pkg.version;
29337
29264
  } catch {}
@@ -29566,7 +29493,7 @@ var init_doctor_memory = __esm(() => {
29566
29493
  });
29567
29494
 
29568
29495
  // src/cli/doctor-docker.ts
29569
- import { readFileSync as readFileSync48 } from "node:fs";
29496
+ import { readFileSync as readFileSync49 } from "node:fs";
29570
29497
  function imageTagOf(ref) {
29571
29498
  if (!ref)
29572
29499
  return "<absent>";
@@ -29581,7 +29508,7 @@ function isDockerMode(opts) {
29581
29508
  return true;
29582
29509
  if (opts?.composePath) {
29583
29510
  try {
29584
- readFileSync48(opts.composePath, "utf8");
29511
+ readFileSync49(opts.composePath, "utf8");
29585
29512
  return true;
29586
29513
  } catch {}
29587
29514
  }
@@ -29770,7 +29697,7 @@ var init_doctor_docker = __esm(() => {
29770
29697
  });
29771
29698
 
29772
29699
  // src/cli/doctor-auth-broker.ts
29773
- import { existsSync as existsSync53, readFileSync as readFileSync49 } from "node:fs";
29700
+ import { existsSync as existsSync53, readFileSync as readFileSync50 } from "node:fs";
29774
29701
  import { createHash as createHash10 } from "node:crypto";
29775
29702
  import { spawnSync as spawnSync6 } from "node:child_process";
29776
29703
  import { homedir as homedir26 } from "node:os";
@@ -29884,7 +29811,7 @@ function checkAuthBrokerDrift(deps = {}) {
29884
29811
  }
29885
29812
  let index;
29886
29813
  try {
29887
- index = JSON.parse(readFileSync49(indexPath, "utf-8"));
29814
+ index = JSON.parse(readFileSync50(indexPath, "utf-8"));
29888
29815
  } catch (err) {
29889
29816
  return {
29890
29817
  name: "auth-broker: drift",
@@ -29904,7 +29831,7 @@ function checkAuthBrokerDrift(deps = {}) {
29904
29831
  }
29905
29832
  let got;
29906
29833
  try {
29907
- got = sha256Hex(readFileSync49(credsPath, "utf-8"));
29834
+ got = sha256Hex(readFileSync50(credsPath, "utf-8"));
29908
29835
  } catch (err) {
29909
29836
  divergent.push(`${label} (read failed: ${err.message})`);
29910
29837
  continue;
@@ -29945,7 +29872,7 @@ function checkAuthBrokerThresholdViolations(deps = {}) {
29945
29872
  }
29946
29873
  let violations;
29947
29874
  try {
29948
- violations = JSON.parse(readFileSync49(path4, "utf-8"));
29875
+ violations = JSON.parse(readFileSync50(path4, "utf-8"));
29949
29876
  } catch (err) {
29950
29877
  return {
29951
29878
  name: "auth-broker: threshold violations",
@@ -31981,7 +31908,7 @@ import {
31981
31908
  existsSync as existsSync57,
31982
31909
  lstatSync as lstatSync6,
31983
31910
  mkdirSync as mkdirSync31,
31984
- readFileSync as readFileSync50,
31911
+ readFileSync as readFileSync51,
31985
31912
  readdirSync as readdirSync21,
31986
31913
  statSync as statSync25
31987
31914
  } from "node:fs";
@@ -32741,7 +32668,7 @@ function classifyReadError(err) {
32741
32668
  }
32742
32669
  function tryReadHostFile(path4) {
32743
32670
  try {
32744
- return { kind: "ok", content: readFileSync50(path4, "utf-8") };
32671
+ return { kind: "ok", content: readFileSync51(path4, "utf-8") };
32745
32672
  } catch (err) {
32746
32673
  const kind = classifyReadError(err);
32747
32674
  const error = err?.message ?? String(err);
@@ -32757,7 +32684,7 @@ function parseEnvFile(path4) {
32757
32684
  return {};
32758
32685
  let content;
32759
32686
  try {
32760
- content = readFileSync50(path4, "utf-8");
32687
+ content = readFileSync51(path4, "utf-8");
32761
32688
  } catch {
32762
32689
  return {};
32763
32690
  }
@@ -32874,7 +32801,7 @@ function checkStartShStale(agentName, startShPath) {
32874
32801
  }
32875
32802
  let content;
32876
32803
  try {
32877
- content = readFileSync50(startShPath, "utf-8");
32804
+ content = readFileSync51(startShPath, "utf-8");
32878
32805
  } catch (err) {
32879
32806
  return {
32880
32807
  name: label,
@@ -32987,7 +32914,7 @@ function isSwitchroomCheckout(dir) {
32987
32914
  const pkgPath = join59(dir, "package.json");
32988
32915
  if (!existsSync57(pkgPath))
32989
32916
  return false;
32990
- const pkg = JSON.parse(readFileSync50(pkgPath, "utf-8"));
32917
+ const pkg = JSON.parse(readFileSync51(pkgPath, "utf-8"));
32991
32918
  return pkg.name === "switchroom";
32992
32919
  } catch {
32993
32920
  return false;
@@ -33108,7 +33035,7 @@ function checkAgents(config, configPath) {
33108
33035
  });
33109
33036
  } else {
33110
33037
  try {
33111
- const mcp = JSON.parse(readFileSync50(mcpJsonPath, "utf-8"));
33038
+ const mcp = JSON.parse(readFileSync51(mcpJsonPath, "utf-8"));
33112
33039
  const hasSwitchroomTelegram = !!mcp.mcpServers?.["switchroom-telegram"];
33113
33040
  const memoryEnabled = isHindsightEnabled(config);
33114
33041
  const hasHindsight = !!mcp.mcpServers?.hindsight;
@@ -33603,11 +33530,11 @@ function runDockerSection(config) {
33603
33530
  let composeYaml;
33604
33531
  let dockerfileAgent;
33605
33532
  try {
33606
- composeYaml = readFileSync50(composePath, "utf8");
33533
+ composeYaml = readFileSync51(composePath, "utf8");
33607
33534
  } catch {}
33608
33535
  const dockerfilePath = resolve32(process.env.HOME ?? "", ".switchroom", "docker", "Dockerfile.agent");
33609
33536
  try {
33610
- dockerfileAgent = readFileSync50(dockerfilePath, "utf8");
33537
+ dockerfileAgent = readFileSync51(dockerfilePath, "utf8");
33611
33538
  } catch {}
33612
33539
  return runDockerChecks({
33613
33540
  config,
@@ -49914,7 +49841,7 @@ __export(exports_server2, {
49914
49841
  TOOLS: () => TOOLS2
49915
49842
  });
49916
49843
  import { randomBytes as randomBytes15 } from "node:crypto";
49917
- import { existsSync as existsSync83, readFileSync as readFileSync69 } from "node:fs";
49844
+ import { existsSync as existsSync83, readFileSync as readFileSync70 } from "node:fs";
49918
49845
  function selfSocketPath() {
49919
49846
  return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
49920
49847
  }
@@ -50092,7 +50019,7 @@ function getLastUpdateApplyStatus() {
50092
50019
  }
50093
50020
  let raw;
50094
50021
  try {
50095
- raw = readFileSync69(path8, "utf-8");
50022
+ raw = readFileSync70(path8, "utf-8");
50096
50023
  } catch (err2) {
50097
50024
  return errorText2(`get_status: failed to read audit log at ${path8}: ${err2.message}`);
50098
50025
  }
@@ -50325,8 +50252,8 @@ var {
50325
50252
  } = import__.default;
50326
50253
 
50327
50254
  // src/build-info.ts
50328
- var VERSION = "0.15.10";
50329
- var COMMIT_SHA = "bb9182e1";
50255
+ var VERSION = "0.15.12";
50256
+ var COMMIT_SHA = "18b7b6e6";
50330
50257
 
50331
50258
  // src/cli/agent.ts
50332
50259
  init_source();
@@ -67107,6 +67034,15 @@ import { createInterface as createInterface4 } from "node:readline";
67107
67034
 
67108
67035
  // src/cli/telegram-yaml.ts
67109
67036
  var import_yaml11 = __toESM(require_dist(), 1);
67037
+ function setLinearAgent(yamlText, agentName, opts) {
67038
+ const doc = import_yaml11.parseDocument(yamlText);
67039
+ ensureAgent(doc, agentName);
67040
+ const block = { enabled: true, token: opts.token };
67041
+ if (opts.workspaceId)
67042
+ block.workspace_id = opts.workspaceId;
67043
+ doc.setIn(["agents", agentName, "channels", "telegram", "linear_agent"], block);
67044
+ return String(doc);
67045
+ }
67110
67046
  function setTelegramFeature(yamlText, agentName, feature, value) {
67111
67047
  const doc = import_yaml11.parseDocument(yamlText);
67112
67048
  ensureAgent(doc, agentName);
@@ -67665,6 +67601,100 @@ function promptHidden2(prompt) {
67665
67601
  });
67666
67602
  }
67667
67603
 
67604
+ // src/cli/linear-agent.ts
67605
+ init_source();
67606
+ init_helpers();
67607
+ import { readFileSync as readFileSync39, writeFileSync as writeFileSync22 } from "node:fs";
67608
+ function registerLinearAgentCommand(program3) {
67609
+ 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.");
67610
+ 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) => {
67611
+ if (!/^[a-z][a-z0-9_-]{0,63}$/.test(opts.agent)) {
67612
+ fail2(`--agent must be a lowercase agent slug (got '${opts.agent}').`);
67613
+ }
67614
+ if (!opts.token || opts.token.trim().length === 0) {
67615
+ fail2("--token must be a non-empty Linear app token.");
67616
+ }
67617
+ const vaultKey = `linear/${opts.agent}/token`;
67618
+ if (!opts.dryRun) {
67619
+ await vaultPut(program3, vaultKey, opts.token);
67620
+ } else {
67621
+ console.log(source_default.gray(`[dry-run] would store the Linear token in the vault as '${vaultKey}'`));
67622
+ }
67623
+ const path4 = getConfigPath(program3);
67624
+ const before = readFileSync39(path4, "utf-8");
67625
+ let after;
67626
+ try {
67627
+ after = setLinearAgent(before, opts.agent, {
67628
+ token: `vault:${vaultKey}`,
67629
+ ...opts.workspaceId ? { workspaceId: opts.workspaceId } : {}
67630
+ });
67631
+ } catch (err) {
67632
+ fail2(err.message);
67633
+ }
67634
+ if (opts.dryRun) {
67635
+ console.log(source_default.bold(`[dry-run] would edit ${path4}`));
67636
+ console.log(makeUnifiedDiff2(before, after));
67637
+ } else {
67638
+ writeFileSync22(path4, after, "utf-8");
67639
+ console.log(source_default.green(`\u2713 Enabled linear-agent for agent '${opts.agent}'`));
67640
+ console.log(source_default.gray(` Vault key: ${vaultKey}`));
67641
+ console.log(source_default.gray(` Run 'switchroom agent restart ${opts.agent}' to pick up the change.`));
67642
+ }
67643
+ printLinearInstructions(opts, vaultKey);
67644
+ }));
67645
+ }
67646
+ function printLinearInstructions(opts, vaultKey) {
67647
+ const base = opts.webhookBase ?? "https://<your-switchroom-web-host>";
67648
+ const webhookUrl = `${base.replace(/\/$/, "")}/webhook/${opts.agent}/linear`;
67649
+ console.log("");
67650
+ console.log(source_default.bold("Next steps in Linear (browser, one-time per agent):"));
67651
+ console.log(source_default.gray(" 1. Create / open your Linear OAuth app with actor=app and scopes"));
67652
+ console.log(source_default.gray(" app:mentionable + app:assignable (https://linear.app/developers/agents)."));
67653
+ if (opts.clientId) {
67654
+ const redirect = opts.redirectUri ?? `${base.replace(/\/$/, "")}/oauth/callback`;
67655
+ 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`;
67656
+ console.log(source_default.gray(" 2. Authorize the app as an actor (open in a browser):"));
67657
+ console.log(source_default.cyan(` ${authorizeUrl}`));
67658
+ console.log(source_default.gray(" The redirect delivers the app token you pass to this verb via --token."));
67659
+ } else {
67660
+ console.log(source_default.gray(" 2. Authorize the app (actor=app) in a browser; capture the app token and"));
67661
+ console.log(source_default.gray(" re-run this verb with --token (and optionally --client-id to print the"));
67662
+ console.log(source_default.gray(" authorize URL)."));
67663
+ }
67664
+ console.log(source_default.gray(" 3. Register this webhook URL on the app (AgentSessionEvent + Issue/Comment):"));
67665
+ console.log(source_default.cyan(` ${webhookUrl}`));
67666
+ 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>').`));
67667
+ console.log("");
67668
+ console.log(source_default.gray(` Token is read at runtime from vault:${vaultKey} (actor=app).`));
67669
+ }
67670
+ function makeUnifiedDiff2(before, after) {
67671
+ const a = before.split(`
67672
+ `);
67673
+ const b = after.split(`
67674
+ `);
67675
+ const out = [];
67676
+ let i = 0, j = 0;
67677
+ while (i < a.length || j < b.length) {
67678
+ if (i < a.length && j < b.length && a[i] === b[j]) {
67679
+ out.push(` ${a[i]}`);
67680
+ i++;
67681
+ j++;
67682
+ } else if (j < b.length && (i >= a.length || a[i] !== b[j])) {
67683
+ out.push(source_default.green(`+ ${b[j]}`));
67684
+ j++;
67685
+ } else {
67686
+ out.push(source_default.red(`- ${a[i]}`));
67687
+ i++;
67688
+ }
67689
+ }
67690
+ return out.join(`
67691
+ `);
67692
+ }
67693
+ function fail2(msg) {
67694
+ console.error(source_default.red(`Error: ${msg}`));
67695
+ process.exit(1);
67696
+ }
67697
+
67668
67698
  // src/cli/memory.ts
67669
67699
  init_source();
67670
67700
  init_hindsight();
@@ -67934,7 +67964,7 @@ async function ensureHindsightConsumer(configPath, account, uid = HINDSIGHT_DEFA
67934
67964
  // src/cli/memory.ts
67935
67965
  init_loader();
67936
67966
  var import_yaml12 = __toESM(require_dist(), 1);
67937
- import { existsSync as existsSync44, readFileSync as readFileSync39, writeFileSync as writeFileSync22 } from "node:fs";
67967
+ import { existsSync as existsSync44, readFileSync as readFileSync40, writeFileSync as writeFileSync23 } from "node:fs";
67938
67968
  import { join as join39 } from "node:path";
67939
67969
  function readRecallLog(agentDir, limit) {
67940
67970
  const path4 = join39(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
@@ -67942,7 +67972,7 @@ function readRecallLog(agentDir, limit) {
67942
67972
  return [];
67943
67973
  let raw;
67944
67974
  try {
67945
- raw = readFileSync39(path4, "utf-8");
67975
+ raw = readFileSync40(path4, "utf-8");
67946
67976
  } catch {
67947
67977
  return [];
67948
67978
  }
@@ -68137,7 +68167,7 @@ Cross-agent reflection plan
68137
68167
  const configPath = getConfigPath(program3);
68138
68168
  try {
68139
68169
  if (existsSync44(configPath)) {
68140
- const raw = readFileSync39(configPath, "utf-8");
68170
+ const raw = readFileSync40(configPath, "utf-8");
68141
68171
  const doc = import_yaml12.default.parseDocument(raw);
68142
68172
  if (!doc.has("memory")) {
68143
68173
  doc.set("memory", { backend: "hindsight", shared_collection: "shared", config: { provider, url } });
@@ -68153,7 +68183,7 @@ Cross-agent reflection plan
68153
68183
  }
68154
68184
  }
68155
68185
  }
68156
- writeFileSync22(configPath, doc.toString(), "utf-8");
68186
+ writeFileSync23(configPath, doc.toString(), "utf-8");
68157
68187
  console.log(source_default.gray(` Updated ${configPath} with memory.config.url = ${url}`));
68158
68188
  console.log(source_default.gray(" Run `switchroom agent reconcile all --restart` to apply this to existing agents."));
68159
68189
  }
@@ -68246,7 +68276,7 @@ init_loader();
68246
68276
  init_client();
68247
68277
  init_lifecycle();
68248
68278
  import {
68249
- readFileSync as readFileSync45,
68279
+ readFileSync as readFileSync46,
68250
68280
  existsSync as existsSync50,
68251
68281
  realpathSync as realpathSync4,
68252
68282
  mkdirSync as mkdirSync29,
@@ -68265,7 +68295,7 @@ init_lifecycle();
68265
68295
  init_manager();
68266
68296
  init_hindsight();
68267
68297
  import { spawnSync as spawnSync5 } from "node:child_process";
68268
- import { existsSync as existsSync47, readFileSync as readFileSync42, statSync as statSync22 } from "node:fs";
68298
+ import { existsSync as existsSync47, readFileSync as readFileSync43, statSync as statSync22 } from "node:fs";
68269
68299
  import { resolve as resolve27 } from "node:path";
68270
68300
  init_audit_reader();
68271
68301
 
@@ -72773,8 +72803,8 @@ init_paths();
72773
72803
  import {
72774
72804
  existsSync as existsSync45,
72775
72805
  mkdirSync as mkdirSync25,
72776
- readFileSync as readFileSync40,
72777
- writeFileSync as writeFileSync23
72806
+ readFileSync as readFileSync41,
72807
+ writeFileSync as writeFileSync24
72778
72808
  } from "node:fs";
72779
72809
  import { dirname as dirname9 } from "node:path";
72780
72810
  import { randomUUID as randomUUID3 } from "node:crypto";
@@ -72794,7 +72824,7 @@ function getDistinctId() {
72794
72824
  const path4 = resolveStatePath("analytics-id");
72795
72825
  try {
72796
72826
  if (existsSync45(path4)) {
72797
- const existing = readFileSync40(path4, "utf-8").trim();
72827
+ const existing = readFileSync41(path4, "utf-8").trim();
72798
72828
  if (existing) {
72799
72829
  cachedDistinctId = existing;
72800
72830
  return existing;
@@ -72805,7 +72835,7 @@ function getDistinctId() {
72805
72835
  cachedDistinctId = id;
72806
72836
  try {
72807
72837
  mkdirSync25(dirname9(path4), { recursive: true });
72808
- writeFileSync23(path4, id, "utf-8");
72838
+ writeFileSync24(path4, id, "utf-8");
72809
72839
  } catch {}
72810
72840
  return id;
72811
72841
  }
@@ -72930,7 +72960,7 @@ function getAgentWorkspaceAccount(yamlText, provider, agent) {
72930
72960
  // src/web/config-edit-plan.ts
72931
72961
  init_schema();
72932
72962
  var import_yaml14 = __toESM(require_dist(), 1);
72933
- import { readFileSync as readFileSync41 } from "node:fs";
72963
+ import { readFileSync as readFileSync42 } from "node:fs";
72934
72964
 
72935
72965
  class ConfigPlanError extends Error {
72936
72966
  }
@@ -72940,7 +72970,7 @@ function composeTransforms(...transforms) {
72940
72970
  function planConfigEdit(configPath, transform) {
72941
72971
  let before;
72942
72972
  try {
72943
- before = readFileSync41(configPath, "utf-8");
72973
+ before = readFileSync42(configPath, "utf-8");
72944
72974
  } catch (err) {
72945
72975
  throw new ConfigPlanError(`could not read config: ${err.message}`);
72946
72976
  }
@@ -72971,7 +73001,7 @@ import {
72971
73001
  mkdirSync as mkdirSync26,
72972
73002
  mkdtempSync as mkdtemp,
72973
73003
  rmSync as rmrf,
72974
- writeFileSync as writeFileSync24
73004
+ writeFileSync as writeFileSync25
72975
73005
  } from "node:fs";
72976
73006
  import { spawnSync as spawnSync4 } from "node:child_process";
72977
73007
  import { tmpdir as tmpdir4 } from "node:os";
@@ -72986,8 +73016,8 @@ function generateUnifiedDiff(before, after, name = "switchroom.yaml", gitBin = "
72986
73016
  try {
72987
73017
  mkdirSync26(join41(dir, "cur"), { recursive: true });
72988
73018
  mkdirSync26(join41(dir, "new"), { recursive: true });
72989
- writeFileSync24(join41(dir, "cur", name), before);
72990
- writeFileSync24(join41(dir, "new", name), after);
73019
+ writeFileSync25(join41(dir, "cur", name), before);
73020
+ writeFileSync25(join41(dir, "new", name), after);
72991
73021
  const r = spawnSync4(gitBin, ["diff", "--no-index", "--no-color", "--", `cur/${name}`, `new/${name}`], { cwd: dir, encoding: "utf-8", timeout: 1e4 });
72992
73022
  if (r.status === 0)
72993
73023
  return "";
@@ -73624,7 +73654,7 @@ async function handleGetSystemHealth(config, home2) {
73624
73654
  const logPath = defaultAuditLogPath2(home2);
73625
73655
  if (existsSync47(logPath)) {
73626
73656
  hostd.auditLogPresent = true;
73627
- const raw = readFileSync42(logPath, "utf-8");
73657
+ const raw = readFileSync43(logPath, "utf-8");
73628
73658
  hostd.recent = readAndFilter(raw, {}, 10);
73629
73659
  }
73630
73660
  } catch (err) {
@@ -73984,7 +74014,7 @@ async function handleGetMemoryHealth(config, opts) {
73984
74014
  }
73985
74015
 
73986
74016
  // src/web/webhook-handler.ts
73987
- import { appendFileSync as appendFileSync4, existsSync as existsSync49, mkdirSync as mkdirSync28, readFileSync as readFileSync44, writeFileSync as writeFileSync25 } from "fs";
74017
+ import { appendFileSync as appendFileSync4, existsSync as existsSync49, mkdirSync as mkdirSync28, readFileSync as readFileSync45, writeFileSync as writeFileSync26 } from "fs";
73988
74018
  import { join as join45 } from "path";
73989
74019
  import { homedir as homedir24 } from "os";
73990
74020
 
@@ -74191,7 +74221,7 @@ function forwardToGateway(socketPath, req, opts = {}) {
74191
74221
  }
74192
74222
 
74193
74223
  // src/web/webhook-edge.ts
74194
- import { existsSync as existsSync48, readFileSync as readFileSync43 } from "fs";
74224
+ import { existsSync as existsSync48, readFileSync as readFileSync44 } from "fs";
74195
74225
  import { join as join44 } from "path";
74196
74226
  import { homedir as homedir23 } from "os";
74197
74227
  import { timingSafeEqual as timingSafeEqual2 } from "crypto";
@@ -74204,7 +74234,7 @@ function loadEdgeSecret(path4) {
74204
74234
  try {
74205
74235
  if (!existsSync48(p))
74206
74236
  return null;
74207
- const raw = readFileSync43(p, "utf-8").trim();
74237
+ const raw = readFileSync44(p, "utf-8").trim();
74208
74238
  return raw.length > 0 ? raw : null;
74209
74239
  } catch {
74210
74240
  return null;
@@ -74238,7 +74268,7 @@ function loadDedupFile(path4) {
74238
74268
  try {
74239
74269
  if (!existsSync49(path4))
74240
74270
  return {};
74241
- const raw = JSON.parse(readFileSync44(path4, "utf-8"));
74271
+ const raw = JSON.parse(readFileSync45(path4, "utf-8"));
74242
74272
  return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
74243
74273
  } catch {
74244
74274
  return {};
@@ -74252,7 +74282,7 @@ function saveDedupFile(path4, deliveries, now) {
74252
74282
  }
74253
74283
  const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
74254
74284
  const final = Object.fromEntries(sorted);
74255
- writeFileSync25(path4, JSON.stringify({ deliveries: final }), {
74285
+ writeFileSync26(path4, JSON.stringify({ deliveries: final }), {
74256
74286
  mode: 384
74257
74287
  });
74258
74288
  }
@@ -74521,7 +74551,7 @@ function resolveWebToken() {
74521
74551
  const home2 = process.env.HOME ?? homedir25();
74522
74552
  const tokenPath = join46(home2, ".switchroom", "web-token");
74523
74553
  if (existsSync50(tokenPath)) {
74524
- const existing = readFileSync45(tokenPath, "utf8").trim();
74554
+ const existing = readFileSync46(tokenPath, "utf8").trim();
74525
74555
  if (existing.length > 0)
74526
74556
  return existing;
74527
74557
  }
@@ -74538,7 +74568,7 @@ function resolveWebToken() {
74538
74568
  return token;
74539
74569
  } catch (err) {
74540
74570
  if (err.code === "EEXIST") {
74541
- const existing = readFileSync45(tokenPath, "utf8").trim();
74571
+ const existing = readFileSync46(tokenPath, "utf8").trim();
74542
74572
  if (existing.length > 0)
74543
74573
  return existing;
74544
74574
  }
@@ -74608,7 +74638,7 @@ function loadWebhookSecrets() {
74608
74638
  if (!existsSync50(path4))
74609
74639
  return {};
74610
74640
  try {
74611
- const parsed = JSON.parse(readFileSync45(path4, "utf-8"));
74641
+ const parsed = JSON.parse(readFileSync46(path4, "utf-8"));
74612
74642
  return parsed && typeof parsed === "object" ? parsed : {};
74613
74643
  } catch (err) {
74614
74644
  process.stderr.write(`webhook-ingest: failed to parse ${path4}: ${err.message} \u2014 webhooks will return 401 until fixed
@@ -74981,7 +75011,7 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
74981
75011
  }
74982
75012
  const ext = extname(realFullPath);
74983
75013
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
74984
- const content = readFileSync45(realFullPath);
75014
+ const content = readFileSync46(realFullPath);
74985
75015
  return new Response(content, {
74986
75016
  headers: { "Content-Type": contentType }
74987
75017
  });
@@ -75115,7 +75145,7 @@ Starting Switchroom dashboard...
75115
75145
  // src/cli/setup.ts
75116
75146
  init_source();
75117
75147
  init_loader();
75118
- import { existsSync as existsSync51, copyFileSync as copyFileSync8, readFileSync as readFileSync46, writeFileSync as writeFileSync26, mkdirSync as mkdirSync30 } from "node:fs";
75148
+ import { existsSync as existsSync51, copyFileSync as copyFileSync8, readFileSync as readFileSync47, writeFileSync as writeFileSync27, mkdirSync as mkdirSync30 } from "node:fs";
75119
75149
  import { resolve as resolve29, dirname as dirname11 } from "node:path";
75120
75150
  init_vault();
75121
75151
  init_manager();
@@ -75844,10 +75874,10 @@ async function stepAutoUnlock(config, switchroomConfigPath, nonInteractive) {
75844
75874
  try {
75845
75875
  const yamlPath = existsSync51(resolve29(process.cwd(), "switchroom.yaml")) ? resolve29(process.cwd(), "switchroom.yaml") : resolve29(process.cwd(), "switchroom.yml");
75846
75876
  if (existsSync51(yamlPath)) {
75847
- const content = readFileSync46(yamlPath, "utf-8");
75877
+ const content = readFileSync47(yamlPath, "utf-8");
75848
75878
  const result = insertVaultBrokerApprovalAuth(content, "telegram-id");
75849
75879
  if (result.kind === "rewritten") {
75850
- writeFileSync26(yamlPath, result.content, "utf-8");
75880
+ writeFileSync27(yamlPath, result.content, "utf-8");
75851
75881
  console.log(source_default.green(` ${STEP_DONE} Set vault.broker.approvalAuth: telegram-id in ${yamlPath}`));
75852
75882
  } else if (result.kind === "already-set") {
75853
75883
  console.log(source_default.gray(" approvalAuth already set \u2014 leaving it alone."));
@@ -75879,7 +75909,7 @@ async function stepDangerousMode(config, nonInteractive) {
75879
75909
  ];
75880
75910
  for (const configPath of configPaths) {
75881
75911
  if (existsSync51(configPath)) {
75882
- let content = readFileSync46(configPath, "utf-8");
75912
+ let content = readFileSync47(configPath, "utf-8");
75883
75913
  const agentNames = Object.keys(config.agents);
75884
75914
  for (const name of agentNames) {
75885
75915
  const agentPattern = new RegExp(`(^ ${name}:\\s*\\n)`, "m");
@@ -75893,7 +75923,7 @@ async function stepDangerousMode(config, nonInteractive) {
75893
75923
  }
75894
75924
  config.agents[name].dangerous_mode = true;
75895
75925
  }
75896
- writeFileSync26(configPath, content, "utf-8");
75926
+ writeFileSync27(configPath, content, "utf-8");
75897
75927
  console.log(source_default.green(` ${STEP_DONE} Enabled dangerous_mode for all agents in ${configPath}`));
75898
75928
  break;
75899
75929
  }
@@ -76008,7 +76038,7 @@ init_doctor();
76008
76038
  init_source();
76009
76039
  init_loader();
76010
76040
  init_lifecycle();
76011
- 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";
76041
+ 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";
76012
76042
  import { spawnSync as spawnSync9 } from "node:child_process";
76013
76043
  import { join as join60, dirname as dirname14, resolve as resolve33 } from "node:path";
76014
76044
  import { homedir as homedir36 } from "node:os";
@@ -76018,7 +76048,7 @@ function runningFromSwitchroomCheckout(scriptPath) {
76018
76048
  for (let i = 0;i < 12; i++) {
76019
76049
  if (existsSync58(join60(dir, ".git"))) {
76020
76050
  try {
76021
- const pkg = JSON.parse(readFileSync51(join60(dir, "package.json"), "utf-8"));
76051
+ const pkg = JSON.parse(readFileSync52(join60(dir, "package.json"), "utf-8"));
76022
76052
  if (pkg.name === "switchroom")
76023
76053
  return true;
76024
76054
  } catch {}
@@ -76286,7 +76316,7 @@ function defaultStatusProbe(composePath) {
76286
76316
  const pkgPath = join60(dir, "package.json");
76287
76317
  if (existsSync58(pkgPath)) {
76288
76318
  try {
76289
- const pkg = JSON.parse(readFileSync51(pkgPath, "utf-8"));
76319
+ const pkg = JSON.parse(readFileSync52(pkgPath, "utf-8"));
76290
76320
  if (typeof pkg.version === "string")
76291
76321
  cliVersion = pkg.version;
76292
76322
  } catch (err) {
@@ -76502,7 +76532,7 @@ init_source();
76502
76532
  init_helpers();
76503
76533
  init_lifecycle();
76504
76534
  import { execSync as execSync4 } from "node:child_process";
76505
- import { existsSync as existsSync59, readFileSync as readFileSync52 } from "node:fs";
76535
+ import { existsSync as existsSync59, readFileSync as readFileSync53 } from "node:fs";
76506
76536
  import { dirname as dirname15, join as join61 } from "node:path";
76507
76537
  function getClaudeCodeVersion() {
76508
76538
  try {
@@ -76556,7 +76586,7 @@ function locateSwitchroomInstallDir() {
76556
76586
  const pkgPath = join61(dir, "package.json");
76557
76587
  if (existsSync59(pkgPath)) {
76558
76588
  try {
76559
- const pkg = JSON.parse(readFileSync52(pkgPath, "utf-8"));
76589
+ const pkg = JSON.parse(readFileSync53(pkgPath, "utf-8"));
76560
76590
  if (pkg.name === "switchroom" && existsSync59(join61(dir, ".git"))) {
76561
76591
  return dir;
76562
76592
  }
@@ -76787,11 +76817,11 @@ import {
76787
76817
  mkdirSync as mkdirSync33,
76788
76818
  openSync as openSync11,
76789
76819
  readdirSync as readdirSync22,
76790
- readFileSync as readFileSync53,
76820
+ readFileSync as readFileSync54,
76791
76821
  renameSync as renameSync12,
76792
76822
  statSync as statSync27,
76793
76823
  unlinkSync as unlinkSync11,
76794
- writeFileSync as writeFileSync27,
76824
+ writeFileSync as writeFileSync28,
76795
76825
  writeSync as writeSync7
76796
76826
  } from "node:fs";
76797
76827
  import { join as join62 } from "node:path";
@@ -77197,7 +77227,7 @@ function readAll(stateDir) {
77197
77227
  return [];
77198
77228
  let raw;
77199
77229
  try {
77200
- raw = readFileSync53(path4, "utf-8");
77230
+ raw = readFileSync54(path4, "utf-8");
77201
77231
  } catch {
77202
77232
  return [];
77203
77233
  }
@@ -77345,7 +77375,7 @@ function writeAll(stateDir, events) {
77345
77375
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
77346
77376
  `) + `
77347
77377
  `;
77348
- writeFileSync27(tmp, body, "utf-8");
77378
+ writeFileSync28(tmp, body, "utf-8");
77349
77379
  renameSync12(tmp, path4);
77350
77380
  }
77351
77381
  var ORPHAN_TMP_TTL_MS = 60000;
@@ -77408,7 +77438,7 @@ function withLock(stateDir, fn) {
77408
77438
  function tryStealStaleLock(lockPath) {
77409
77439
  let pidStr;
77410
77440
  try {
77411
- pidStr = readFileSync53(lockPath, "utf-8").trim();
77441
+ pidStr = readFileSync54(lockPath, "utf-8").trim();
77412
77442
  } catch {
77413
77443
  return true;
77414
77444
  }
@@ -77665,9 +77695,9 @@ import { createHash as createHash11 } from "node:crypto";
77665
77695
  import {
77666
77696
  existsSync as existsSync61,
77667
77697
  mkdirSync as mkdirSync34,
77668
- readFileSync as readFileSync54,
77698
+ readFileSync as readFileSync55,
77669
77699
  rmSync as rmSync13,
77670
- writeFileSync as writeFileSync28
77700
+ writeFileSync as writeFileSync29
77671
77701
  } from "node:fs";
77672
77702
  import { dirname as dirname16, join as join63 } from "node:path";
77673
77703
  import { homedir as homedir37 } from "node:os";
@@ -77685,7 +77715,7 @@ function defaultPythonCacheRoot() {
77685
77715
  return join63(homedir37(), ".switchroom", "deps", "python");
77686
77716
  }
77687
77717
  function hashFile(path4) {
77688
- return createHash11("sha256").update(readFileSync54(path4)).digest("hex");
77718
+ return createHash11("sha256").update(readFileSync55(path4)).digest("hex");
77689
77719
  }
77690
77720
  function ensurePythonEnv(opts) {
77691
77721
  const { skillName, requirementsPath, force = false } = opts;
@@ -77701,7 +77731,7 @@ function ensurePythonEnv(opts) {
77701
77731
  const pipBin = join63(binDir, "pip");
77702
77732
  const targetHash = hashFile(requirementsPath);
77703
77733
  if (!force && existsSync61(stampPath) && existsSync61(pythonBin)) {
77704
- const existingHash = readFileSync54(stampPath, "utf8").trim();
77734
+ const existingHash = readFileSync55(stampPath, "utf8").trim();
77705
77735
  if (existingHash === targetHash) {
77706
77736
  return {
77707
77737
  skillName,
@@ -77735,7 +77765,7 @@ function ensurePythonEnv(opts) {
77735
77765
  const e = err;
77736
77766
  throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
77737
77767
  }
77738
- writeFileSync28(stampPath, targetHash + `
77768
+ writeFileSync29(stampPath, targetHash + `
77739
77769
  `);
77740
77770
  return {
77741
77771
  skillName,
@@ -77753,9 +77783,9 @@ import {
77753
77783
  copyFileSync as copyFileSync9,
77754
77784
  existsSync as existsSync62,
77755
77785
  mkdirSync as mkdirSync35,
77756
- readFileSync as readFileSync55,
77786
+ readFileSync as readFileSync56,
77757
77787
  rmSync as rmSync14,
77758
- writeFileSync as writeFileSync29
77788
+ writeFileSync as writeFileSync30
77759
77789
  } from "node:fs";
77760
77790
  import { dirname as dirname17, join as join64 } from "node:path";
77761
77791
  import { homedir as homedir38 } from "node:os";
@@ -77788,7 +77818,7 @@ function hashDepInputs(packageJsonPath) {
77788
77818
  const hasher = createHash12("sha256");
77789
77819
  hasher.update(`package.json
77790
77820
  `);
77791
- hasher.update(readFileSync55(packageJsonPath));
77821
+ hasher.update(readFileSync56(packageJsonPath));
77792
77822
  for (const lockName of ALL_LOCKFILES) {
77793
77823
  const lockPath = join64(sourceDir, lockName);
77794
77824
  if (existsSync62(lockPath)) {
@@ -77797,7 +77827,7 @@ function hashDepInputs(packageJsonPath) {
77797
77827
  hasher.update(lockName);
77798
77828
  hasher.update(`
77799
77829
  `);
77800
- hasher.update(readFileSync55(lockPath));
77830
+ hasher.update(readFileSync56(lockPath));
77801
77831
  }
77802
77832
  }
77803
77833
  return hasher.digest("hex");
@@ -77816,7 +77846,7 @@ function ensureNodeEnv(opts) {
77816
77846
  const binDir = join64(nodeModulesDir, ".bin");
77817
77847
  const targetHash = hashDepInputs(packageJsonPath);
77818
77848
  if (!force && existsSync62(stampPath) && existsSync62(nodeModulesDir)) {
77819
- const existingHash = readFileSync55(stampPath, "utf8").trim();
77849
+ const existingHash = readFileSync56(stampPath, "utf8").trim();
77820
77850
  if (existingHash === targetHash) {
77821
77851
  return {
77822
77852
  skillName,
@@ -77852,7 +77882,7 @@ function ensureNodeEnv(opts) {
77852
77882
  const e = err;
77853
77883
  throw new NodeEnvError(`Failed to install node deps for skill "${skillName}" with ${installer}: ${e.message}`, e.stderr?.toString());
77854
77884
  }
77855
- writeFileSync29(stampPath, targetHash + `
77885
+ writeFileSync30(stampPath, targetHash + `
77856
77886
  `);
77857
77887
  return {
77858
77888
  skillName,
@@ -78835,7 +78865,7 @@ function safeParseInt(value, fallback) {
78835
78865
  init_helpers();
78836
78866
  init_loader();
78837
78867
  init_merge();
78838
- import { copyFileSync as copyFileSync10, existsSync as existsSync65, readFileSync as readFileSync56, writeFileSync as writeFileSync30 } from "node:fs";
78868
+ import { copyFileSync as copyFileSync10, existsSync as existsSync65, readFileSync as readFileSync57, writeFileSync as writeFileSync31 } from "node:fs";
78839
78869
  import { join as join66, resolve as resolve39 } from "node:path";
78840
78870
  init_schema();
78841
78871
  function resolveSoulTargetOrExit(program3, agentName) {
@@ -78881,7 +78911,7 @@ function registerSoulCommand(program3) {
78881
78911
  console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
78882
78912
  process.exit(1);
78883
78913
  }
78884
- process.stdout.write(readFileSync56(t.soulPath, "utf-8"));
78914
+ process.stdout.write(readFileSync57(t.soulPath, "utf-8"));
78885
78915
  }));
78886
78916
  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) => {
78887
78917
  const t = resolveSoulTargetOrExit(program3, agentName);
@@ -78912,7 +78942,7 @@ function registerSoulCommand(program3) {
78912
78942
  }
78913
78943
  copyFileSync10(t.soulPath, backupPath);
78914
78944
  }
78915
- writeFileSync30(t.soulPath, content, "utf-8");
78945
+ writeFileSync31(t.soulPath, content, "utf-8");
78916
78946
  if (backupPath) {
78917
78947
  console.log(`soul: re-seeded ${agentName}'s SOUL.md from profile ` + `"${t.profileName}".
78918
78948
  ` + ` Previous version saved to ${backupPath}`);
@@ -78926,7 +78956,7 @@ function registerSoulCommand(program3) {
78926
78956
  // src/cli/debug.ts
78927
78957
  init_helpers();
78928
78958
  init_loader();
78929
- import { existsSync as existsSync66, readFileSync as readFileSync57, readdirSync as readdirSync23, statSync as statSync28 } from "node:fs";
78959
+ import { existsSync as existsSync66, readFileSync as readFileSync58, readdirSync as readdirSync23, statSync as statSync28 } from "node:fs";
78930
78960
  import { resolve as resolve40, join as join67 } from "node:path";
78931
78961
  import { createHash as createHash13 } from "node:crypto";
78932
78962
  init_merge();
@@ -78942,7 +78972,7 @@ function readMcpServerNames(agentDir) {
78942
78972
  if (!existsSync66(mcpPath))
78943
78973
  return [];
78944
78974
  try {
78945
- const parsed = JSON.parse(readFileSync57(mcpPath, "utf-8"));
78975
+ const parsed = JSON.parse(readFileSync58(mcpPath, "utf-8"));
78946
78976
  return Object.keys(parsed.mcpServers ?? {});
78947
78977
  } catch {
78948
78978
  return null;
@@ -78977,7 +79007,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
78977
79007
  }
78978
79008
  function extractLatestUserMessage(transcriptPath) {
78979
79009
  try {
78980
- const content = readFileSync57(transcriptPath, "utf-8");
79010
+ const content = readFileSync58(transcriptPath, "utf-8");
78981
79011
  const lines = content.trim().split(`
78982
79012
  `).filter(Boolean);
78983
79013
  for (let i = lines.length - 1;i >= 0; i--) {
@@ -79081,7 +79111,7 @@ function registerDebugCommand(program3) {
79081
79111
  }
79082
79112
  console.log(`=== Append System Prompt (per-session) ===
79083
79113
  `);
79084
- const handoffContent = existsSync66(handoffPath) ? readFileSync57(handoffPath, "utf-8") : "";
79114
+ const handoffContent = existsSync66(handoffPath) ? readFileSync58(handoffPath, "utf-8") : "";
79085
79115
  if (handoffContent.trim().length > 0) {
79086
79116
  console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
79087
79117
  console.log(handoffContent);
@@ -79092,7 +79122,7 @@ function registerDebugCommand(program3) {
79092
79122
  }
79093
79123
  console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
79094
79124
  `);
79095
- const claudeMdContent = existsSync66(claudeMdPath) ? readFileSync57(claudeMdPath, "utf-8") : "";
79125
+ const claudeMdContent = existsSync66(claudeMdPath) ? readFileSync58(claudeMdPath, "utf-8") : "";
79096
79126
  if (claudeMdContent.trim().length > 0) {
79097
79127
  console.log(`(${formatBytes(claudeMdContent.length)})`);
79098
79128
  console.log(claudeMdContent);
@@ -79103,7 +79133,7 @@ function registerDebugCommand(program3) {
79103
79133
  }
79104
79134
  console.log(`=== Persona (SOUL.md) ===
79105
79135
  `);
79106
- const soulMdContent = existsSync66(soulMdPath) ? readFileSync57(soulMdPath, "utf-8") : existsSync66(workspaceSoulMdPath) ? readFileSync57(workspaceSoulMdPath, "utf-8") : "";
79136
+ const soulMdContent = existsSync66(soulMdPath) ? readFileSync58(soulMdPath, "utf-8") : existsSync66(workspaceSoulMdPath) ? readFileSync58(workspaceSoulMdPath, "utf-8") : "";
79107
79137
  if (soulMdContent.trim().length > 0) {
79108
79138
  console.log(`(${formatBytes(soulMdContent.length)})`);
79109
79139
  console.log(soulMdContent);
@@ -79167,8 +79197,8 @@ function registerDebugCommand(program3) {
79167
79197
  const fleetDir = join67(agentsDir, "..", "fleet");
79168
79198
  const fleetInvPath = join67(fleetDir, "switchroom-invariants.md");
79169
79199
  const fleetClaudePath = join67(fleetDir, "CLAUDE.md");
79170
- const fleetInvBytes = existsSync66(fleetInvPath) ? readFileSync57(fleetInvPath, "utf-8").length : 0;
79171
- const fleetClaudeBytes = existsSync66(fleetClaudePath) ? readFileSync57(fleetClaudePath, "utf-8").length : 0;
79200
+ const fleetInvBytes = existsSync66(fleetInvPath) ? readFileSync58(fleetInvPath, "utf-8").length : 0;
79201
+ const fleetClaudeBytes = existsSync66(fleetClaudePath) ? readFileSync58(fleetClaudePath, "utf-8").length : 0;
79172
79202
  const fleetBytes = fleetInvBytes + fleetClaudeBytes;
79173
79203
  const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
79174
79204
  console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
@@ -79209,8 +79239,8 @@ import { randomBytes as randomBytes13 } from "node:crypto";
79209
79239
  // src/worktree/registry.ts
79210
79240
  import {
79211
79241
  mkdirSync as mkdirSync36,
79212
- writeFileSync as writeFileSync31,
79213
- readFileSync as readFileSync58,
79242
+ writeFileSync as writeFileSync32,
79243
+ readFileSync as readFileSync59,
79214
79244
  readdirSync as readdirSync24,
79215
79245
  unlinkSync as unlinkSync12,
79216
79246
  existsSync as existsSync67,
@@ -79231,14 +79261,14 @@ function writeRecord(record2) {
79231
79261
  ensureDir2();
79232
79262
  const target = recordPath(record2.id);
79233
79263
  const tmp = `${target}.tmp${process.pid}`;
79234
- writeFileSync31(tmp, JSON.stringify(record2, null, 2) + `
79264
+ writeFileSync32(tmp, JSON.stringify(record2, null, 2) + `
79235
79265
  `, { mode: 384 });
79236
79266
  renameSync13(tmp, target);
79237
79267
  }
79238
79268
  function readRecord(id) {
79239
79269
  const path7 = recordPath(id);
79240
79270
  try {
79241
- const raw = readFileSync58(path7, "utf8");
79271
+ const raw = readFileSync59(path7, "utf8");
79242
79272
  return JSON.parse(raw);
79243
79273
  } catch {
79244
79274
  return null;
@@ -79603,7 +79633,7 @@ import {
79603
79633
  mkdirSync as mkdirSync38,
79604
79634
  readdirSync as readdirSync25,
79605
79635
  rmSync as rmSync15,
79606
- writeFileSync as writeFileSync32
79636
+ writeFileSync as writeFileSync33
79607
79637
  } from "node:fs";
79608
79638
  import { join as join70 } from "node:path";
79609
79639
  function encodeCredentialsFilename(email) {
@@ -79805,7 +79835,7 @@ function writeSeedFile(dir, email, seed) {
79805
79835
  }
79806
79836
  const filename = encodeCredentialsFilename(email);
79807
79837
  const filePath = join70(dir, filename);
79808
- writeFileSync32(filePath, JSON.stringify(seed), { mode: 384 });
79838
+ writeFileSync33(filePath, JSON.stringify(seed), { mode: 384 });
79809
79839
  chmodSync9(filePath, 384);
79810
79840
  return filePath;
79811
79841
  }
@@ -79963,7 +79993,7 @@ function registerDriveMcpLauncherCommand(program3) {
79963
79993
  // src/cli/m365-mcp-launcher.ts
79964
79994
  init_scaffold_integration();
79965
79995
  import { spawn as spawn6 } from "node:child_process";
79966
- import { writeFileSync as writeFileSync33, mkdirSync as mkdirSync39 } from "node:fs";
79996
+ import { writeFileSync as writeFileSync34, mkdirSync as mkdirSync39 } from "node:fs";
79967
79997
  import { dirname as dirname18, join as join71 } from "node:path";
79968
79998
  var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
79969
79999
  var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
@@ -79990,7 +80020,7 @@ function writeRefreshHeartbeat(agentName, data) {
79990
80020
  const path7 = heartbeatPath(agentName);
79991
80021
  try {
79992
80022
  mkdirSync39(dirname18(path7), { recursive: true });
79993
- writeFileSync33(path7, JSON.stringify(data, null, 2), { mode: 420 });
80023
+ writeFileSync34(path7, JSON.stringify(data, null, 2), { mode: 420 });
79994
80024
  } catch {}
79995
80025
  }
79996
80026
  function heartbeatPath(agentName) {
@@ -80181,7 +80211,7 @@ function registerM365McpLauncherCommand(program3) {
80181
80211
  // src/cli/notion-mcp-launcher.ts
80182
80212
  init_scaffold_integration();
80183
80213
  import { spawn as spawn7 } from "node:child_process";
80184
- import { existsSync as existsSync71, mkdirSync as mkdirSync40, writeFileSync as writeFileSync34 } from "node:fs";
80214
+ import { existsSync as existsSync71, mkdirSync as mkdirSync40, writeFileSync as writeFileSync35 } from "node:fs";
80185
80215
  import { dirname as dirname19 } from "node:path";
80186
80216
  var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
80187
80217
  var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
@@ -80195,7 +80225,7 @@ function defaultWriteHeartbeat(path7, contents) {
80195
80225
  const dir = dirname19(path7);
80196
80226
  if (!existsSync71(dir))
80197
80227
  mkdirSync40(dir, { recursive: true });
80198
- writeFileSync34(path7, contents);
80228
+ writeFileSync35(path7, contents);
80199
80229
  } catch {}
80200
80230
  }
80201
80231
  async function runNotionMcpLauncher(opts, runtime) {
@@ -80305,7 +80335,7 @@ function registerNotionMcpLauncherCommand(program3) {
80305
80335
 
80306
80336
  // src/cli/deliver-file.ts
80307
80337
  init_client2();
80308
- import { readFileSync as readFileSync59, statSync as statSync29 } from "node:fs";
80338
+ import { readFileSync as readFileSync60, statSync as statSync29 } from "node:fs";
80309
80339
  import { basename as basename8 } from "node:path";
80310
80340
 
80311
80341
  // src/delivery/onedrive.ts
@@ -80645,7 +80675,7 @@ async function defaultResolveProvider() {
80645
80675
  async function runDeliverFile(localPath, deps = {}) {
80646
80676
  const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
80647
80677
  const sizeOf = deps.fileSize ?? ((p) => statSync29(p).size);
80648
- const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync59(p)));
80678
+ const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync60(p)));
80649
80679
  const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
80650
80680
  let size;
80651
80681
  try {
@@ -80935,7 +80965,7 @@ async function fetchToken(vaultKey) {
80935
80965
 
80936
80966
  // src/cli/apply.ts
80937
80967
  init_source();
80938
- 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";
80968
+ 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";
80939
80969
  import { mkdir as mkdir2 } from "node:fs/promises";
80940
80970
  import { spawnSync as childSpawnSync } from "node:child_process";
80941
80971
  import readline from "node:readline";
@@ -81332,7 +81362,7 @@ init_loader();
81332
81362
  init_loader();
81333
81363
 
81334
81364
  // src/cli/update-prompt-hook.ts
81335
- import { existsSync as existsSync72, readFileSync as readFileSync60, writeFileSync as writeFileSync35, chmodSync as chmodSync10, mkdirSync as mkdirSync41 } from "node:fs";
81365
+ import { existsSync as existsSync72, readFileSync as readFileSync61, writeFileSync as writeFileSync36, chmodSync as chmodSync10, mkdirSync as mkdirSync41 } from "node:fs";
81336
81366
  import { join as join72 } from "node:path";
81337
81367
  var HOOK_FILENAME = "update-card-on-prompt.sh";
81338
81368
  function updatePromptHookScript() {
@@ -81404,9 +81434,9 @@ function installUpdatePromptHook(agentDir) {
81404
81434
  const scriptPath = join72(hooksDir, HOOK_FILENAME);
81405
81435
  const desired = updatePromptHookScript();
81406
81436
  let installed = false;
81407
- const existing = existsSync72(scriptPath) ? readFileSync60(scriptPath, "utf-8") : "";
81437
+ const existing = existsSync72(scriptPath) ? readFileSync61(scriptPath, "utf-8") : "";
81408
81438
  if (existing !== desired) {
81409
- writeFileSync35(scriptPath, desired, { mode: 493 });
81439
+ writeFileSync36(scriptPath, desired, { mode: 493 });
81410
81440
  chmodSync10(scriptPath, 493);
81411
81441
  installed = true;
81412
81442
  } else {
@@ -81418,7 +81448,7 @@ function installUpdatePromptHook(agentDir) {
81418
81448
  if (!existsSync72(settingsPath)) {
81419
81449
  return { scriptPath, settingsPath, installed };
81420
81450
  }
81421
- const raw = readFileSync60(settingsPath, "utf-8");
81451
+ const raw = readFileSync61(settingsPath, "utf-8");
81422
81452
  let parsed;
81423
81453
  try {
81424
81454
  parsed = JSON.parse(raw);
@@ -81451,7 +81481,7 @@ function installUpdatePromptHook(agentDir) {
81451
81481
  });
81452
81482
  hooks.UserPromptSubmit = list2;
81453
81483
  parsed.hooks = hooks;
81454
- writeFileSync35(settingsPath, JSON.stringify(parsed, null, 2) + `
81484
+ writeFileSync36(settingsPath, JSON.stringify(parsed, null, 2) + `
81455
81485
  `, { mode: 384 });
81456
81486
  installed = true;
81457
81487
  }
@@ -81574,24 +81604,24 @@ async function ensureHostMountSources(config) {
81574
81604
  }
81575
81605
  const autoUnlockPath = join74(home2, ".switchroom", "vault-auto-unlock");
81576
81606
  if (!existsSync74(autoUnlockPath)) {
81577
- writeFileSync36(autoUnlockPath, "", { mode: 384 });
81607
+ writeFileSync37(autoUnlockPath, "", { mode: 384 });
81578
81608
  }
81579
81609
  const auditLogPath = join74(home2, ".switchroom", "vault-audit.log");
81580
81610
  if (!existsSync74(auditLogPath)) {
81581
- writeFileSync36(auditLogPath, "", { mode: 420 });
81611
+ writeFileSync37(auditLogPath, "", { mode: 420 });
81582
81612
  }
81583
81613
  const grantsDbPath = join74(home2, ".switchroom", "vault-grants.db");
81584
81614
  if (!existsSync74(grantsDbPath)) {
81585
- writeFileSync36(grantsDbPath, "", { mode: 384 });
81615
+ writeFileSync37(grantsDbPath, "", { mode: 384 });
81586
81616
  }
81587
81617
  const hostdAuditLogPath = join74(home2, ".switchroom", "host-control-audit.log");
81588
81618
  if (!existsSync74(hostdAuditLogPath)) {
81589
- writeFileSync36(hostdAuditLogPath, "", { mode: 420 });
81619
+ writeFileSync37(hostdAuditLogPath, "", { mode: 420 });
81590
81620
  }
81591
81621
  for (const name of Object.keys(config.agents)) {
81592
81622
  const tokenPath = join74(home2, ".switchroom", "agents", name, ".vault-token");
81593
81623
  if (!existsSync74(tokenPath)) {
81594
- writeFileSync36(tokenPath, "", { mode: 384 });
81624
+ writeFileSync37(tokenPath, "", { mode: 384 });
81595
81625
  }
81596
81626
  try {
81597
81627
  const uid = allocateAgentUid(name);
@@ -81602,13 +81632,13 @@ async function ensureHostMountSources(config) {
81602
81632
  await mkdir2(fleetDir, { recursive: true });
81603
81633
  const invariantsPath = join74(fleetDir, "switchroom-invariants.md");
81604
81634
  const invariantsCanonical = renderFleetInvariants();
81605
- const invariantsCurrent = existsSync74(invariantsPath) ? readFileSync61(invariantsPath, "utf-8") : null;
81635
+ const invariantsCurrent = existsSync74(invariantsPath) ? readFileSync62(invariantsPath, "utf-8") : null;
81606
81636
  if (invariantsCurrent !== invariantsCanonical) {
81607
- writeFileSync36(invariantsPath, invariantsCanonical, { mode: 420 });
81637
+ writeFileSync37(invariantsPath, invariantsCanonical, { mode: 420 });
81608
81638
  }
81609
81639
  const fleetClaudePath = join74(fleetDir, "CLAUDE.md");
81610
81640
  if (!existsSync74(fleetClaudePath)) {
81611
- writeFileSync36(fleetClaudePath, [
81641
+ writeFileSync37(fleetClaudePath, [
81612
81642
  "# Switchroom fleet defaults",
81613
81643
  "",
81614
81644
  "Operator-owned fleet brain. Every agent reads this via",
@@ -81700,7 +81730,7 @@ function writeInstallTypeCache(homeDir = homedir43()) {
81700
81730
  detected_at: new Date().toISOString(),
81701
81731
  source_paths: ctx.source_paths
81702
81732
  };
81703
- writeFileSync36(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
81733
+ writeFileSync37(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
81704
81734
  renameSync14(tmp, out);
81705
81735
  return out;
81706
81736
  }
@@ -81896,10 +81926,10 @@ Done. Scaffolded ${scaffolded}/${allAgentNames.length} agents.
81896
81926
  };
81897
81927
  }
81898
81928
  function formatScaffoldFailureResolution(failures, scaffolded, agentsTotal) {
81899
- const fail2 = failures.length;
81929
+ const fail3 = failures.length;
81900
81930
  const lines = [];
81901
81931
  lines.push("");
81902
- lines.push(`ERROR: Scaffolded ${scaffolded}/${agentsTotal} agents. ${fail2} failed.`);
81932
+ lines.push(`ERROR: Scaffolded ${scaffolded}/${agentsTotal} agents. ${fail3} failed.`);
81903
81933
  lines.push("");
81904
81934
  lines.push("Per-agent state dirs are mode 0700 owned by per-agent UIDs (the v0.7+");
81905
81935
  lines.push("docker model). The operator cannot write into them without privilege");
@@ -81932,7 +81962,7 @@ function copyExampleConfig2(name) {
81932
81962
  }
81933
81963
  const embedded = EMBEDDED_EXAMPLES[name];
81934
81964
  if (embedded !== undefined) {
81935
- writeFileSync36(dest, embedded, { encoding: "utf8" });
81965
+ writeFileSync37(dest, embedded, { encoding: "utf8" });
81936
81966
  console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
81937
81967
  return;
81938
81968
  }
@@ -82127,7 +82157,7 @@ function runRedactStdin() {
82127
82157
  }
82128
82158
 
82129
82159
  // src/cli/status-ask.ts
82130
- import { readFileSync as readFileSync62, existsSync as existsSync75, readdirSync as readdirSync27 } from "node:fs";
82160
+ import { readFileSync as readFileSync63, existsSync as existsSync75, readdirSync as readdirSync27 } from "node:fs";
82131
82161
  import { join as join75 } from "node:path";
82132
82162
  import { homedir as homedir44 } from "node:os";
82133
82163
 
@@ -82403,7 +82433,7 @@ function runReport(opts) {
82403
82433
  for (const src of sources) {
82404
82434
  let content;
82405
82435
  try {
82406
- content = readFileSync62(src.path, "utf-8");
82436
+ content = readFileSync63(src.path, "utf-8");
82407
82437
  } catch (err) {
82408
82438
  process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
82409
82439
  `);
@@ -82508,7 +82538,7 @@ import {
82508
82538
  mkdirSync as mkdirSync43,
82509
82539
  openSync as openSync13,
82510
82540
  readdirSync as readdirSync28,
82511
- readFileSync as readFileSync63,
82541
+ readFileSync as readFileSync64,
82512
82542
  renameSync as renameSync15,
82513
82543
  statSync as statSync30,
82514
82544
  unlinkSync as unlinkSync14,
@@ -82632,7 +82662,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
82632
82662
  continue;
82633
82663
  const full = join76(paths.skillsDir, name);
82634
82664
  try {
82635
- const raw = readFileSync63(full, "utf-8");
82665
+ const raw = readFileSync64(full, "utf-8");
82636
82666
  const slug = name.replace(/\.ya?ml$/i, "");
82637
82667
  out.push({ slug, path: full, raw });
82638
82668
  } catch {}
@@ -82659,7 +82689,7 @@ function listOverlayEntries(agent, opts = {}) {
82659
82689
  continue;
82660
82690
  const full = join76(paths.scheduleDir, name);
82661
82691
  try {
82662
- const raw = readFileSync63(full, "utf-8");
82692
+ const raw = readFileSync64(full, "utf-8");
82663
82693
  const slug = name.replace(/\.ya?ml$/i, "");
82664
82694
  out.push({ slug, path: full, raw });
82665
82695
  } catch {}
@@ -82807,10 +82837,10 @@ import {
82807
82837
  mkdirSync as mkdirSync44,
82808
82838
  openSync as openSync14,
82809
82839
  readdirSync as readdirSync29,
82810
- readFileSync as readFileSync64,
82840
+ readFileSync as readFileSync65,
82811
82841
  renameSync as renameSync16,
82812
82842
  unlinkSync as unlinkSync15,
82813
- writeFileSync as writeFileSync37,
82843
+ writeFileSync as writeFileSync38,
82814
82844
  writeSync as writeSync9
82815
82845
  } from "node:fs";
82816
82846
  import { join as join77 } from "node:path";
@@ -82853,7 +82883,7 @@ function stagePendingScheduleEntry(opts) {
82853
82883
  }
82854
82884
  renameSync16(yamlTmp, yamlPath);
82855
82885
  }
82856
- writeFileSync37(metaPath, JSON.stringify(meta, null, 2) + `
82886
+ writeFileSync38(metaPath, JSON.stringify(meta, null, 2) + `
82857
82887
  `, { mode: 384 });
82858
82888
  return { stageId, yamlPath, metaPath };
82859
82889
  }
@@ -82871,7 +82901,7 @@ function listPendingScheduleEntries(agent, opts = {}) {
82871
82901
  if (!existsSync77(yamlPath))
82872
82902
  continue;
82873
82903
  try {
82874
- const meta = JSON.parse(readFileSync64(metaPath, "utf-8"));
82904
+ const meta = JSON.parse(readFileSync65(metaPath, "utf-8"));
82875
82905
  if (meta?.v !== 1 || typeof meta.stage_id !== "string")
82876
82906
  continue;
82877
82907
  out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
@@ -82909,7 +82939,7 @@ function denyPendingScheduleEntry(opts) {
82909
82939
  }
82910
82940
 
82911
82941
  // src/cli/agent-config-write.ts
82912
- import { existsSync as existsSync78, readFileSync as readFileSync65 } from "node:fs";
82942
+ import { existsSync as existsSync78, readFileSync as readFileSync66 } from "node:fs";
82913
82943
  import { execFileSync as execFileSync25 } from "node:child_process";
82914
82944
 
82915
82945
  // src/scheduler/schedule-report.ts
@@ -83277,7 +83307,7 @@ function scheduleRemove(opts) {
83277
83307
  let priorContent = null;
83278
83308
  try {
83279
83309
  if (existsSync78(match.path))
83280
- priorContent = readFileSync65(match.path, "utf-8");
83310
+ priorContent = readFileSync66(match.path, "utf-8");
83281
83311
  } catch {}
83282
83312
  deleteOverlayEntry(agent, match.slug, { root: opts.root });
83283
83313
  const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
@@ -83480,7 +83510,7 @@ function registerAgentConfigWriteCommands(program3) {
83480
83510
  }
83481
83511
  let blob;
83482
83512
  if (opts.jsonl) {
83483
- blob = existsSync78(opts.jsonl) ? readFileSync65(opts.jsonl, "utf-8") : "";
83513
+ blob = existsSync78(opts.jsonl) ? readFileSync66(opts.jsonl, "utf-8") : "";
83484
83514
  } else {
83485
83515
  try {
83486
83516
  blob = execFileSync25("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
@@ -83760,13 +83790,13 @@ import {
83760
83790
  mkdirSync as mkdirSync45,
83761
83791
  mkdtempSync as mkdtempSync5,
83762
83792
  openSync as openSync15,
83763
- readFileSync as readFileSync66,
83793
+ readFileSync as readFileSync67,
83764
83794
  readdirSync as readdirSync30,
83765
83795
  realpathSync as realpathSync7,
83766
83796
  renameSync as renameSync17,
83767
83797
  rmSync as rmSync16,
83768
83798
  statSync as statSync31,
83769
- writeFileSync as writeFileSync38
83799
+ writeFileSync as writeFileSync39
83770
83800
  } from "node:fs";
83771
83801
  import { tmpdir as tmpdir5, homedir as homedir45 } from "node:os";
83772
83802
  import { dirname as dirname23, join as join79, relative as relative2, resolve as resolve46 } from "node:path";
@@ -83996,7 +84026,7 @@ function isTarballPath(p) {
83996
84026
  function loadFromDir(dir) {
83997
84027
  const abs = realpathSync7(dir);
83998
84028
  if (!statSync31(abs).isDirectory()) {
83999
- fail2(`--from path is not a directory: ${dir}`);
84029
+ fail3(`--from path is not a directory: ${dir}`);
84000
84030
  }
84001
84031
  const files = {};
84002
84032
  const walk2 = (sub) => {
@@ -84005,14 +84035,14 @@ function loadFromDir(dir) {
84005
84035
  const full = join79(sub, ent.name);
84006
84036
  const rel = relative2(abs, full);
84007
84037
  if (ent.isSymbolicLink()) {
84008
- fail2(`refusing to read symlink inside --from dir: ${rel}`);
84038
+ fail3(`refusing to read symlink inside --from dir: ${rel}`);
84009
84039
  }
84010
84040
  if (ent.isDirectory()) {
84011
84041
  walk2(full);
84012
84042
  continue;
84013
84043
  }
84014
84044
  if (ent.isFile()) {
84015
- const buf = readFileSync66(full);
84045
+ const buf = readFileSync67(full);
84016
84046
  files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
84017
84047
  }
84018
84048
  }
@@ -84028,13 +84058,13 @@ function loadFromTarball(tarPath) {
84028
84058
  stdio: ["ignore", "pipe", "pipe"]
84029
84059
  });
84030
84060
  if (list2.status !== 0) {
84031
- fail2(`tar list failed (exit ${list2.status}): ${(list2.stderr ?? "").trim()}`);
84061
+ fail3(`tar list failed (exit ${list2.status}): ${(list2.stderr ?? "").trim()}`);
84032
84062
  }
84033
84063
  const entries = (list2.stdout ?? "").split(`
84034
84064
  `).map((s) => s.trim()).filter((s) => s.length > 0 && !s.endsWith("/"));
84035
84065
  for (const entry of entries) {
84036
84066
  if (!validateRelPath(entry)) {
84037
- fail2(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
84067
+ fail3(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
84038
84068
  }
84039
84069
  }
84040
84070
  const staging = mkdtempSync5(join79(tmpdir5(), "skill-apply-extract-"));
@@ -84051,7 +84081,7 @@ function loadFromTarball(tarPath) {
84051
84081
  ], { stdio: "pipe" });
84052
84082
  if (r.status !== 0) {
84053
84083
  const stderr = r.stderr?.toString("utf-8") ?? "(no stderr)";
84054
- fail2(`tar extraction failed (exit ${r.status}): ${stderr.trim()}`);
84084
+ fail3(`tar extraction failed (exit ${r.status}): ${stderr.trim()}`);
84055
84085
  }
84056
84086
  return loadFromDir(staging);
84057
84087
  } finally {
@@ -84061,13 +84091,13 @@ function loadFromTarball(tarPath) {
84061
84091
  }
84062
84092
  }
84063
84093
  function loadSingleFile(filePath) {
84064
- const content = readFileSync66(filePath, "utf-8");
84094
+ const content = readFileSync67(filePath, "utf-8");
84065
84095
  return { "SKILL.md": content };
84066
84096
  }
84067
84097
  function loadFromStdin() {
84068
84098
  const content = readStdinSync();
84069
84099
  if (content.length === 0) {
84070
- fail2("no content on stdin; either pipe SKILL.md content in or pass --from");
84100
+ fail3("no content on stdin; either pipe SKILL.md content in or pass --from");
84071
84101
  }
84072
84102
  return { "SKILL.md": content };
84073
84103
  }
@@ -84126,7 +84156,7 @@ function validatePayload(name, files) {
84126
84156
  const tmp = mkdtempSync5(join79(tmpdir5(), "skill-apply-py-"));
84127
84157
  const tmpPy = join79(tmp, "check.py");
84128
84158
  try {
84129
- writeFileSync38(tmpPy, content);
84159
+ writeFileSync39(tmpPy, content);
84130
84160
  const r = spawnSync11("python3", ["-m", "py_compile", tmpPy], {
84131
84161
  encoding: "utf-8"
84132
84162
  });
@@ -84152,7 +84182,7 @@ function diffSummary(currentDir, files) {
84152
84182
  if (ent.isDirectory()) {
84153
84183
  walk2(full);
84154
84184
  } else if (ent.isFile()) {
84155
- currentFiles[rel.replace(/\\/g, "/")] = readFileSync66(full, "utf-8");
84185
+ currentFiles[rel.replace(/\\/g, "/")] = readFileSync67(full, "utf-8");
84156
84186
  }
84157
84187
  }
84158
84188
  };
@@ -84192,7 +84222,7 @@ function writePayload(poolDir, name, files) {
84192
84222
  }
84193
84223
  } catch {}
84194
84224
  if (targetIsSymlink) {
84195
- fail2(`refusing to overwrite symlink at ${target}; investigate manually`);
84225
+ fail3(`refusing to overwrite symlink at ${target}; investigate manually`);
84196
84226
  }
84197
84227
  const staging = mkdtempSync5(join79(poolDir, `.skill-apply-stage-${name}-`));
84198
84228
  let oldRename = null;
@@ -84202,7 +84232,7 @@ function writePayload(poolDir, name, files) {
84202
84232
  mkdirSync45(dirname23(full), { recursive: true, mode: 493 });
84203
84233
  const fd = openSync15(full, "wx");
84204
84234
  try {
84205
- writeFileSync38(fd, content);
84235
+ writeFileSync39(fd, content);
84206
84236
  } finally {
84207
84237
  closeSync15(fd);
84208
84238
  }
@@ -84240,7 +84270,7 @@ function writePayload(poolDir, name, files) {
84240
84270
  throw err2;
84241
84271
  }
84242
84272
  }
84243
- function fail2(msg) {
84273
+ function fail3(msg) {
84244
84274
  console.error(source_default.red(`error: ${msg}`));
84245
84275
  process.exit(2);
84246
84276
  }
@@ -84253,7 +84283,7 @@ function registerSkillCommand(program3) {
84253
84283
  } else {
84254
84284
  const fromPath = resolve46(opts.from);
84255
84285
  if (!existsSync80(fromPath)) {
84256
- fail2(`--from path does not exist: ${opts.from}`);
84286
+ fail3(`--from path does not exist: ${opts.from}`);
84257
84287
  }
84258
84288
  const st = statSync31(fromPath);
84259
84289
  if (st.isDirectory()) {
@@ -84263,7 +84293,7 @@ function registerSkillCommand(program3) {
84263
84293
  } else if (fromPath.endsWith(".md")) {
84264
84294
  files = loadSingleFile(fromPath);
84265
84295
  } else {
84266
- fail2(`--from must be a directory, a .tar/.tar.gz/.tgz tarball, or a ` + `.md file. Got: ${opts.from}`);
84296
+ fail3(`--from must be a directory, a .tar/.tar.gz/.tgz tarball, or a ` + `.md file. Got: ${opts.from}`);
84267
84297
  }
84268
84298
  }
84269
84299
  const v = validatePayload(name, files);
@@ -84312,13 +84342,13 @@ import {
84312
84342
  mkdirSync as mkdirSync46,
84313
84343
  mkdtempSync as mkdtempSync6,
84314
84344
  openSync as openSync16,
84315
- readFileSync as readFileSync67,
84345
+ readFileSync as readFileSync68,
84316
84346
  readdirSync as readdirSync31,
84317
84347
  renameSync as renameSync18,
84318
84348
  rmSync as rmSync17,
84319
84349
  statSync as statSync32,
84320
84350
  utimesSync,
84321
- writeFileSync as writeFileSync39
84351
+ writeFileSync as writeFileSync40
84322
84352
  } from "node:fs";
84323
84353
  import { dirname as dirname24, join as join80, relative as relative3, resolve as resolve47 } from "node:path";
84324
84354
  import { homedir as homedir46, tmpdir as tmpdir6 } from "node:os";
@@ -84394,7 +84424,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
84394
84424
  if (ent.isDirectory())
84395
84425
  walk2(s, d);
84396
84426
  else if (ent.isFile()) {
84397
- writeFileSync39(d, readFileSync67(s));
84427
+ writeFileSync40(d, readFileSync68(s));
84398
84428
  }
84399
84429
  }
84400
84430
  };
@@ -84409,7 +84439,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
84409
84439
  `));
84410
84440
  }
84411
84441
  }
84412
- function fail3(msg, exit = 2) {
84442
+ function fail4(msg, exit = 2) {
84413
84443
  console.error(source_default.red(`error: ${msg}`));
84414
84444
  process.exit(exit);
84415
84445
  }
@@ -84417,10 +84447,10 @@ function resolveAgent(opts) {
84417
84447
  const fromEnv = process.env.SWITCHROOM_AGENT_NAME;
84418
84448
  const agent = opts.agent ?? fromEnv;
84419
84449
  if (!agent) {
84420
- fail3("agent name required: pass --agent <name>, or set SWITCHROOM_AGENT_NAME " + "in the calling environment (set by switchroom for in-container agents).");
84450
+ fail4("agent name required: pass --agent <name>, or set SWITCHROOM_AGENT_NAME " + "in the calling environment (set by switchroom for in-container agents).");
84421
84451
  }
84422
84452
  if (!SKILL_SLUG_RE.test(agent)) {
84423
- fail3(`agent name has invalid shape: ${JSON.stringify(agent)}`);
84453
+ fail4(`agent name has invalid shape: ${JSON.stringify(agent)}`);
84424
84454
  }
84425
84455
  return agent;
84426
84456
  }
@@ -84458,14 +84488,14 @@ function readStdinSync2() {
84458
84488
  function loadFromDir2(dir) {
84459
84489
  const abs = resolve47(dir);
84460
84490
  if (!statSync32(abs).isDirectory()) {
84461
- fail3(`--from path is not a directory: ${dir}`);
84491
+ fail4(`--from path is not a directory: ${dir}`);
84462
84492
  }
84463
84493
  const files = {};
84464
84494
  const walk2 = (sub) => {
84465
84495
  for (const ent of readdirSync31(sub, { withFileTypes: true })) {
84466
84496
  const full = join80(sub, ent.name);
84467
84497
  if (ent.isSymbolicLink()) {
84468
- fail3(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
84498
+ fail4(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
84469
84499
  }
84470
84500
  if (ent.isDirectory()) {
84471
84501
  walk2(full);
@@ -84473,7 +84503,7 @@ function loadFromDir2(dir) {
84473
84503
  }
84474
84504
  if (ent.isFile()) {
84475
84505
  const rel = relative3(abs, full).replace(/\\/g, "/");
84476
- files[rel] = readFileSync67(full, "utf-8");
84506
+ files[rel] = readFileSync68(full, "utf-8");
84477
84507
  }
84478
84508
  }
84479
84509
  };
@@ -84483,7 +84513,7 @@ function loadFromDir2(dir) {
84483
84513
  function loadFromStdin2() {
84484
84514
  const raw = readStdinSync2();
84485
84515
  if (raw.length === 0) {
84486
- fail3("no content on stdin; pipe a SKILL.md or JSON file-map");
84516
+ fail4("no content on stdin; pipe a SKILL.md or JSON file-map");
84487
84517
  }
84488
84518
  const trimmed = raw.trimStart();
84489
84519
  if (trimmed.startsWith("{")) {
@@ -84491,15 +84521,15 @@ function loadFromStdin2() {
84491
84521
  try {
84492
84522
  parsed = JSON.parse(raw);
84493
84523
  } catch (err2) {
84494
- fail3(`stdin starts with '{' but is not valid JSON: ${err2.message}`);
84524
+ fail4(`stdin starts with '{' but is not valid JSON: ${err2.message}`);
84495
84525
  }
84496
84526
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
84497
- fail3("stdin JSON must be an object of {path: content, ...}");
84527
+ fail4("stdin JSON must be an object of {path: content, ...}");
84498
84528
  }
84499
84529
  const files = {};
84500
84530
  for (const [k, v] of Object.entries(parsed)) {
84501
84531
  if (typeof v !== "string") {
84502
- fail3(`stdin JSON: value for ${JSON.stringify(k)} must be a string`);
84532
+ fail4(`stdin JSON: value for ${JSON.stringify(k)} must be a string`);
84503
84533
  }
84504
84534
  files[k] = v;
84505
84535
  }
@@ -84519,7 +84549,7 @@ function behavioralValidate(files) {
84519
84549
  const tmp = mkdtempSync6(join80(tmpdir6(), "skill-personal-py-"));
84520
84550
  const tmpPy = join80(tmp, "check.py");
84521
84551
  try {
84522
- writeFileSync39(tmpPy, content);
84552
+ writeFileSync40(tmpPy, content);
84523
84553
  const r = spawnSync12("python3", ["-m", "py_compile", tmpPy], {
84524
84554
  encoding: "utf-8"
84525
84555
  });
@@ -84559,7 +84589,7 @@ function writePersonalSkill(targetDir, files) {
84559
84589
  }
84560
84590
  } catch {}
84561
84591
  if (targetIsSymlink) {
84562
- fail3(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
84592
+ fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
84563
84593
  }
84564
84594
  mkdirSync46(dirname24(targetDir), { recursive: true, mode: 493 });
84565
84595
  const staging = mkdtempSync6(join80(dirname24(targetDir), `.skill-personal-stage-`));
@@ -84570,7 +84600,7 @@ function writePersonalSkill(targetDir, files) {
84570
84600
  mkdirSync46(dirname24(full), { recursive: true, mode: 493 });
84571
84601
  const fd = openSync16(full, "wx");
84572
84602
  try {
84573
- writeFileSync39(fd, content);
84603
+ writeFileSync40(fd, content);
84574
84604
  } finally {
84575
84605
  closeSync16(fd);
84576
84606
  }
@@ -84611,7 +84641,7 @@ function writePersonalSkill(targetDir, files) {
84611
84641
  function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
84612
84642
  sweepTrash(agentsRoot, agent);
84613
84643
  if (!SKILL_SLUG_RE.test(name)) {
84614
- fail3(`skill name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(name)}`);
84644
+ fail4(`skill name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(name)}`);
84615
84645
  }
84616
84646
  const target = personalSkillDir(agentsRoot, agent, name);
84617
84647
  const exists = (() => {
@@ -84623,10 +84653,10 @@ function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
84623
84653
  }
84624
84654
  })();
84625
84655
  if (ensureNew && exists) {
84626
- 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);
84656
+ 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);
84627
84657
  }
84628
84658
  if (!ensureNew && !exists) {
84629
- fail3(`personal skill ${JSON.stringify(name)} does not exist for agent ${JSON.stringify(agent)}. ` + `Use \`skill init-personal\` to create it.`, 9);
84659
+ fail4(`personal skill ${JSON.stringify(name)} does not exist for agent ${JSON.stringify(agent)}. ` + `Use \`skill init-personal\` to create it.`, 9);
84630
84660
  }
84631
84661
  const v = validateSkillBundle(name, files);
84632
84662
  if (!v.ok) {
@@ -84652,16 +84682,16 @@ function loadFiles(opts) {
84652
84682
  }
84653
84683
  const p = resolve47(opts.from);
84654
84684
  if (!existsSync81(p)) {
84655
- fail3(`--from path does not exist: ${opts.from}`);
84685
+ fail4(`--from path does not exist: ${opts.from}`);
84656
84686
  }
84657
84687
  const st = statSync32(p);
84658
84688
  if (st.isDirectory()) {
84659
84689
  return loadFromDir2(p);
84660
84690
  }
84661
84691
  if (p.endsWith(".md")) {
84662
- return { "SKILL.md": readFileSync67(p, "utf-8") };
84692
+ return { "SKILL.md": readFileSync68(p, "utf-8") };
84663
84693
  }
84664
- fail3(`--from must be a directory or a .md file. Got: ${opts.from}`);
84694
+ fail4(`--from must be a directory or a .md file. Got: ${opts.from}`);
84665
84695
  }
84666
84696
  function initPersonalAction(name, opts) {
84667
84697
  const agent = resolveAgent(opts);
@@ -84707,18 +84737,18 @@ function defaultBundledRoot() {
84707
84737
  function resolveCloneSource(source, opts) {
84708
84738
  const m = CLONE_SOURCE_RE.exec(source);
84709
84739
  if (!m) {
84710
- fail3(`source must be \`shared:<name>\` or \`bundled:<name>\` (got ${JSON.stringify(source)})`);
84740
+ fail4(`source must be \`shared:<name>\` or \`bundled:<name>\` (got ${JSON.stringify(source)})`);
84711
84741
  }
84712
84742
  const tier = m[1];
84713
84743
  const slug = m[2];
84714
84744
  const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
84715
84745
  const dir = join80(root, slug);
84716
84746
  if (!existsSync81(dir)) {
84717
- fail3(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
84747
+ fail4(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
84718
84748
  }
84719
84749
  const st = lstatSync9(dir);
84720
84750
  if (st.isSymbolicLink()) {
84721
- fail3(`clone source ${JSON.stringify(source)} is a symlink at ${dir}; ` + `point clone at the canonical pool path instead`);
84751
+ fail4(`clone source ${JSON.stringify(source)} is a symlink at ${dir}; ` + `point clone at the canonical pool path instead`);
84722
84752
  }
84723
84753
  return { tier, slug, dir };
84724
84754
  }
@@ -84745,10 +84775,10 @@ function readSourceFiles(dir) {
84745
84775
  try {
84746
84776
  const st = lstatSync9(full);
84747
84777
  if (st.size > CLONE_MAX_FILE_BYTES) {
84748
- fail3(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
84778
+ fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
84749
84779
  }
84750
84780
  } catch {}
84751
- files[rel] = readFileSync67(full, "utf-8");
84781
+ files[rel] = readFileSync68(full, "utf-8");
84752
84782
  }
84753
84783
  }
84754
84784
  };
@@ -84779,11 +84809,11 @@ function clonePersonalAction(source, opts) {
84779
84809
  const src = resolveCloneSource(source, opts);
84780
84810
  const newName = opts.name ?? src.slug;
84781
84811
  if (!SKILL_SLUG_RE.test(newName)) {
84782
- fail3(`destination name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(newName)}`);
84812
+ fail4(`destination name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(newName)}`);
84783
84813
  }
84784
84814
  const { files, skipped } = readSourceFiles(src.dir);
84785
84815
  if (!files["SKILL.md"]) {
84786
- fail3(`source ${JSON.stringify(source)} has no SKILL.md at ${src.dir}`);
84816
+ fail4(`source ${JSON.stringify(source)} has no SKILL.md at ${src.dir}`);
84787
84817
  }
84788
84818
  if (newName !== src.slug) {
84789
84819
  files["SKILL.md"] = rewriteSkillMdName(files["SKILL.md"], newName);
@@ -84821,18 +84851,18 @@ function removePersonalAction(name, opts) {
84821
84851
  const agentsRoot = resolveAgentsRoot(opts);
84822
84852
  sweepTrash(agentsRoot, agent);
84823
84853
  if (!SKILL_SLUG_RE.test(name)) {
84824
- fail3(`skill name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(name)}`);
84854
+ fail4(`skill name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(name)}`);
84825
84855
  }
84826
84856
  const target = personalSkillDir(agentsRoot, agent, name);
84827
84857
  try {
84828
84858
  const st = lstatSync9(target);
84829
84859
  if (st.isSymbolicLink()) {
84830
- fail3(`refusing to remove symlink at ${target}; investigate manually`);
84860
+ fail4(`refusing to remove symlink at ${target}; investigate manually`);
84831
84861
  }
84832
84862
  } catch (err2) {
84833
84863
  const e = err2;
84834
84864
  if (e.code === "ENOENT") {
84835
- fail3(`personal skill ${JSON.stringify(name)} does not exist for agent ${JSON.stringify(agent)}`, 1);
84865
+ fail4(`personal skill ${JSON.stringify(name)} does not exist for agent ${JSON.stringify(agent)}`, 1);
84836
84866
  }
84837
84867
  throw err2;
84838
84868
  }
@@ -84917,7 +84947,7 @@ function registerSkillPersonalCommands(program3) {
84917
84947
  // src/cli/skill-search.ts
84918
84948
  init_helpers();
84919
84949
  var import_yaml23 = __toESM(require_dist(), 1);
84920
- import { existsSync as existsSync82, readdirSync as readdirSync32, readFileSync as readFileSync68, statSync as statSync33 } from "node:fs";
84950
+ import { existsSync as existsSync82, readdirSync as readdirSync32, readFileSync as readFileSync69, statSync as statSync33 } from "node:fs";
84921
84951
  import { homedir as homedir47 } from "node:os";
84922
84952
  import { join as join81, resolve as resolve48 } from "node:path";
84923
84953
  var PERSONAL_PREFIX2 = "personal-";
@@ -84938,7 +84968,7 @@ function readSkillFrontmatter(skillDir) {
84938
84968
  return null;
84939
84969
  let content;
84940
84970
  try {
84941
- content = readFileSync68(mdPath, "utf-8");
84971
+ content = readFileSync69(mdPath, "utf-8");
84942
84972
  } catch {
84943
84973
  return null;
84944
84974
  }
@@ -85210,7 +85240,7 @@ function registerHostdMcpCommand(program3) {
85210
85240
  // src/cli/hostd.ts
85211
85241
  init_source();
85212
85242
  init_helpers();
85213
- 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";
85243
+ 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";
85214
85244
  import { homedir as homedir48 } from "node:os";
85215
85245
  import { join as join82 } from "node:path";
85216
85246
  import { spawnSync as spawnSync14 } from "node:child_process";
@@ -85386,7 +85416,7 @@ async function doInstall(opts, program3) {
85386
85416
  const bak = backupExistingCompose();
85387
85417
  if (bak)
85388
85418
  console.log(source_default.dim(` Backed up existing compose to ${bak}`));
85389
- writeFileSync40(composePath, yaml, "utf8");
85419
+ writeFileSync41(composePath, yaml, "utf8");
85390
85420
  console.log(source_default.green(` \u2713 Wrote ${composePath}`));
85391
85421
  const adminAgents = Object.entries(cfg.agents ?? {}).filter(([, a]) => a?.admin === true).map(([name]) => name);
85392
85422
  console.log(source_default.dim(` agents served (one socket each): ${allAgents.length === 0 ? "(none)" : allAgents.join(", ")}`));
@@ -85493,7 +85523,7 @@ function registerHostdCommand(program3) {
85493
85523
  The log is created when hostd handles its first privileged-verb request.`));
85494
85524
  return;
85495
85525
  }
85496
- const raw = readFileSync70(logPath, "utf-8");
85526
+ const raw = readFileSync71(logPath, "utf-8");
85497
85527
  const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
85498
85528
  const filters = {
85499
85529
  agent: opts.agent,
@@ -85535,7 +85565,7 @@ The log is created when hostd handles its first privileged-verb request.`));
85535
85565
  // src/cli/webd.ts
85536
85566
  init_source();
85537
85567
  init_helpers();
85538
- import { existsSync as existsSync85, mkdirSync as mkdirSync48, writeFileSync as writeFileSync41, copyFileSync as copyFileSync13 } from "node:fs";
85568
+ import { existsSync as existsSync85, mkdirSync as mkdirSync48, writeFileSync as writeFileSync42, copyFileSync as copyFileSync13 } from "node:fs";
85539
85569
  import { homedir as homedir49 } from "node:os";
85540
85570
  import { join as join83 } from "node:path";
85541
85571
  import { spawnSync as spawnSync15 } from "node:child_process";
@@ -85671,7 +85701,7 @@ async function doInstall2(opts, program3) {
85671
85701
  const bak = backupExistingCompose2();
85672
85702
  if (bak)
85673
85703
  console.log(source_default.dim(` Backed up existing compose to ${bak}`));
85674
- writeFileSync41(composePath, yaml, "utf8");
85704
+ writeFileSync42(composePath, yaml, "utf8");
85675
85705
  console.log(source_default.green(` \u2713 Wrote ${composePath}`));
85676
85706
  console.log(source_default.dim(` running as uid ${operatorUid} (operator), network_mode: host`));
85677
85707
  console.log(source_default.dim(` Pulling ghcr.io/switchroom/switchroom-web:${imageTag}\u2026`));
@@ -85776,6 +85806,7 @@ registerTopicsCommand(program3);
85776
85806
  registerAuthCommand(program3);
85777
85807
  registerVaultCommand(program3);
85778
85808
  registerTelegramCommand(program3);
85809
+ registerLinearAgentCommand(program3);
85779
85810
  registerMemoryCommand(program3);
85780
85811
  registerWebCommand(program3);
85781
85812
  registerHandoffCommand(program3);