eminiar 0.13.0 → 0.13.1

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.
package/dist/cli.js CHANGED
@@ -4602,7 +4602,7 @@ function isRef(value) {
4602
4602
  function cloneIssues(issues) {
4603
4603
  return issues.map((iss) => iss.path ? { ...iss, path: iss.path.slice() } : { ...iss });
4604
4604
  }
4605
- function isRecursive(inst, stack, resolve14) {
4605
+ function isRecursive(inst, stack, resolve16) {
4606
4606
  const cached6 = recursive.get(inst);
4607
4607
  if (cached6 !== void 0)
4608
4608
  return cached6 ? PROVEN : NONE;
@@ -4612,7 +4612,7 @@ function isRecursive(inst, stack, resolve14) {
4612
4612
  let result = NONE;
4613
4613
  const check3 = (child) => {
4614
4614
  if (result !== PROVEN && child?._zod) {
4615
- const answer = isRecursive(child, stack, resolve14);
4615
+ const answer = isRecursive(child, stack, resolve16);
4616
4616
  if (answer > result)
4617
4617
  result = answer;
4618
4618
  }
@@ -4623,7 +4623,7 @@ function isRecursive(inst, stack, resolve14) {
4623
4623
  const desc = Object.getOwnPropertyDescriptor(sh, key);
4624
4624
  if (spread && !desc.enumerable)
4625
4625
  continue;
4626
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve14) : NONE;
4626
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve16) : NONE;
4627
4627
  if (child > answer)
4628
4628
  answer = child;
4629
4629
  }
@@ -4687,7 +4687,7 @@ function isRecursive(inst, stack, resolve14) {
4687
4687
  break;
4688
4688
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
4689
4689
  case "lazy": {
4690
- const inner = def._cachedInner ?? (resolve14 ? inst._zod.innerType : void 0);
4690
+ const inner = def._cachedInner ?? (resolve16 ? inst._zod.innerType : void 0);
4691
4691
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
4692
4692
  break;
4693
4693
  }
@@ -20695,6 +20695,16 @@ import { join } from "path";
20695
20695
  function eminiarHome() {
20696
20696
  return process.env.EMINIAR_HOME || join(homedir(), ".eminiar");
20697
20697
  }
20698
+ function claudeConfigDir(env = process.env, home = homedir()) {
20699
+ return env.CLAUDE_CONFIG_DIR?.trim() || join(home, ".claude");
20700
+ }
20701
+ function claudeJsonFile(env = process.env, home = homedir()) {
20702
+ const dir = env.CLAUDE_CONFIG_DIR?.trim();
20703
+ return dir ? join(dir, ".claude.json") : join(home, ".claude.json");
20704
+ }
20705
+ function claudePluginsDir(env = process.env, home = homedir()) {
20706
+ return env.CLAUDE_CODE_PLUGIN_CACHE_DIR?.trim() || join(claudeConfigDir(env, home), "plugins");
20707
+ }
20698
20708
  function paths() {
20699
20709
  const home = eminiarHome();
20700
20710
  return {
@@ -20842,10 +20852,13 @@ function envOverrides() {
20842
20852
  if (Object.keys(adj).length) o.adjudicator = adj;
20843
20853
  return o;
20844
20854
  }
20855
+ function projectKeeps(adj, k) {
20856
+ return PROJECT_ADJUDICATOR_KEYS.includes(k) || k === "mode" && adj.mode === "advise";
20857
+ }
20845
20858
  function droppedProjectAdjudicatorKeys(raw) {
20846
20859
  const adj = raw.adjudicator;
20847
20860
  if (!isPlainObject2(adj)) return [];
20848
- return Object.keys(adj).filter((k) => !PROJECT_ADJUDICATOR_KEYS.includes(k)).sort();
20861
+ return Object.keys(adj).filter((k) => !projectKeeps(adj, k)).sort();
20849
20862
  }
20850
20863
  function projectAdjudicatorWarning(projectRoot) {
20851
20864
  const lines = [];
@@ -20989,7 +21002,7 @@ function projectLayer(raw) {
20989
21002
  if (!isPlainObject2(adj)) return raw;
20990
21003
  if (findSecretFields(adj).length) return raw;
20991
21004
  const kept = {};
20992
- for (const k of PROJECT_ADJUDICATOR_KEYS) if (Object.hasOwn(adj, k)) kept[k] = adj[k];
21005
+ for (const k of Object.keys(adj)) if (projectKeeps(adj, k)) kept[k] = adj[k];
20993
21006
  return { ...ownJson(raw), adjudicator: kept };
20994
21007
  }
20995
21008
  function withoutUnsafeDocsManifest(raw, source) {
@@ -21101,9 +21114,9 @@ var init_config = __esm({
21101
21114
  }).prefault({}),
21102
21115
  minPromptChars: external_exports.number().int().min(0).default(12),
21103
21116
  /**
21104
- * Ordered ladder, smallest first; "fable" may be inserted by the user. Every provider request
21105
- * carries the whole ladder and a committed project file may set it, so it is bounded: each
21106
- * tier at most once (task #56).
21117
+ * Ordered ladder, smallest first; "fable" may be inserted by the user. Personal config only
21118
+ * (a committed project file cannot set it). Every provider request carries the whole ladder,
21119
+ * so it is bounded: each tier at most once (task #56).
21107
21120
  */
21108
21121
  tiers: external_exports.array(external_exports.enum(TIERS)).min(1).max(TIERS.length).refine((t) => new Set(t).size === t.length, "adjudicator.tiers must not repeat a tier").meta({ uniqueItems: true }).default(["haiku", "sonnet", "opus"]),
21109
21122
  weights: external_exports.object({
@@ -21359,13 +21372,10 @@ var init_config = __esm({
21359
21372
  UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
21360
21373
  PROJECT_ADJUDICATOR_KEYS = [
21361
21374
  "enabled",
21362
- "mode",
21363
21375
  "surfaces",
21364
21376
  "promptMode",
21365
21377
  "minPromptChars",
21366
- "tiers",
21367
21378
  "weights",
21368
- "apply",
21369
21379
  "advice",
21370
21380
  "embedding",
21371
21381
  "learning",
@@ -23463,7 +23473,7 @@ var init_adapter = __esm({
23463
23473
  SqliteAdapter = class _SqliteAdapter {
23464
23474
  constructor(path3, opts = {}) {
23465
23475
  this.path = path3;
23466
- this.db = new Database(path3, { timeout: 2e3 });
23476
+ this.db = new Database(path3, { timeout: opts.busyTimeoutMs ?? 2e3 });
23467
23477
  this.swarm = new SqliteSwarmStore(this.db);
23468
23478
  this.adjudicator = new SqliteAdjudicatorStore(this.db);
23469
23479
  this.db.pragma("journal_mode = WAL");
@@ -24681,15 +24691,20 @@ var init_adapter = __esm({
24681
24691
  var storage_exports = {};
24682
24692
  __export(storage_exports, {
24683
24693
  closeAllStorage: () => closeAllStorage,
24684
- getStorage: () => getStorage
24694
+ getStorage: () => getStorage,
24695
+ setStorageBusyTimeout: () => setStorageBusyTimeout
24685
24696
  });
24697
+ function setStorageBusyTimeout(ms4) {
24698
+ busyTimeoutMs = ms4;
24699
+ }
24686
24700
  function getStorage(ctx, opts = {}) {
24687
24701
  const key = `${ctx.dbPath}|${opts.vectors === false ? "novec" : "vec"}`;
24688
24702
  let adapter = open2.get(key);
24689
24703
  if (!adapter) {
24690
24704
  adapter = new SqliteAdapter(ctx.dbPath, {
24691
24705
  dims: ctx.config.embeddings.dims,
24692
- vectors: opts.vectors ?? true
24706
+ vectors: opts.vectors ?? true,
24707
+ ...busyTimeoutMs === void 0 ? {} : { busyTimeoutMs }
24693
24708
  });
24694
24709
  open2.set(key, adapter);
24695
24710
  }
@@ -24699,7 +24714,7 @@ function closeAllStorage() {
24699
24714
  for (const a of open2.values()) a.close();
24700
24715
  open2.clear();
24701
24716
  }
24702
- var open2;
24717
+ var open2, busyTimeoutMs;
24703
24718
  var init_storage = __esm({
24704
24719
  "src/storage/index.ts"() {
24705
24720
  "use strict";
@@ -27103,7 +27118,7 @@ function maskYamlBlocks(text3) {
27103
27118
  function maskCookiePairs(pairs) {
27104
27119
  return pairs.replace(
27105
27120
  /(^|;[ \t]*)([^=;]+)=([^;]*)/g,
27106
- (m, sep6, name, value) => !value || value === "***" || COOKIE_ATTRIBUTES.has(name.trim().toLowerCase()) ? m : `${sep6}${name}=***`
27121
+ (m, sep7, name, value) => !value || value === "***" || COOKIE_ATTRIBUTES.has(name.trim().toLowerCase()) ? m : `${sep7}${name}=***`
27107
27122
  );
27108
27123
  }
27109
27124
  function clean(text3) {
@@ -27753,15 +27768,15 @@ function retryWait(header) {
27753
27768
  return Number.isFinite(at) ? Math.max(0, at - Date.now()) : RETRY_BACKOFF_MS;
27754
27769
  }
27755
27770
  function sleep(ms4, signal) {
27756
- return new Promise((resolve14) => {
27757
- if (signal?.aborted) return resolve14(false);
27771
+ return new Promise((resolve16) => {
27772
+ if (signal?.aborted) return resolve16(false);
27758
27773
  const t = setTimeout(() => {
27759
27774
  signal?.removeEventListener("abort", onAbort);
27760
- resolve14(true);
27775
+ resolve16(true);
27761
27776
  }, ms4);
27762
27777
  const onAbort = () => {
27763
27778
  clearTimeout(t);
27764
- resolve14(false);
27779
+ resolve16(false);
27765
27780
  };
27766
27781
  signal?.addEventListener("abort", onAbort, { once: true });
27767
27782
  });
@@ -28520,8 +28535,8 @@ var init_units = __esm({
28520
28535
  PASSES = "(?:pass(?:es)?|be green|stay green|succeed|exits? 0|exit cleanly|(?:is|are) clean)";
28521
28536
  IS_GREEN = "(?:is|are) (?:all )?(?:clean|green)";
28522
28537
  LIST_SEP2 = String.raw`\s*(?:,|\+|&&?|/|\band\b|\bor\b)\s*`;
28523
- checkList = (item, verdict, sep6 = LIST_SEP2) => new RegExp(
28524
- String.raw`^${item}(?:${sep6}${item}){0,8}(?:\s+(?:must |should |all |still |both )*${verdict})?(?:\s+if [\w\s'-]{1,30})?\s*[.!]?$`
28538
+ checkList = (item, verdict, sep7 = LIST_SEP2) => new RegExp(
28539
+ String.raw`^${item}(?:${sep7}${item}){0,8}(?:\s+(?:must |should |all |still |both )*${verdict})?(?:\s+if [\w\s'-]{1,30})?\s*[.!]?$`
28525
28540
  );
28526
28541
  CHECKS_ONLY = checkList(CHECK_ITEM, PASSES);
28527
28542
  ROUTINE_CHECKS = checkList(
@@ -28997,10 +29012,10 @@ function finish(items2, opts, start, now, persist, locks) {
28997
29012
  async function callWithin(provider, reqs, ms4, capture) {
28998
29013
  const ac = new AbortController();
28999
29014
  let timer;
29000
- const timeout = new Promise((resolve14) => {
29015
+ const timeout = new Promise((resolve16) => {
29001
29016
  timer = setTimeout(() => {
29002
29017
  ac.abort();
29003
- resolve14({ ok: false, kind: "timeout" });
29018
+ resolve16({ ok: false, kind: "timeout" });
29004
29019
  }, ms4);
29005
29020
  timer.unref?.();
29006
29021
  });
@@ -30870,7 +30885,7 @@ function classify(q, exemplars, vectors, o) {
30870
30885
  };
30871
30886
  }
30872
30887
  function withDeadline(p2, ms4, signal) {
30873
- return new Promise((resolve14, reject) => {
30888
+ return new Promise((resolve16, reject) => {
30874
30889
  const fail3 = () => reject(new ProviderError("timeout", "embedding deadline exceeded"));
30875
30890
  if (signal?.aborted) return fail3();
30876
30891
  const timer = setTimeout(fail3, Math.max(0, ms4));
@@ -30883,7 +30898,7 @@ function withDeadline(p2, ms4, signal) {
30883
30898
  (v) => {
30884
30899
  clearTimeout(timer);
30885
30900
  signal?.removeEventListener("abort", onAbort);
30886
- resolve14(v);
30901
+ resolve16(v);
30887
30902
  },
30888
30903
  (err) => {
30889
30904
  clearTimeout(timer);
@@ -31034,16 +31049,20 @@ function yaml2() {
31034
31049
  yamlLib2 ??= createRequire2(import.meta.url)("yaml");
31035
31050
  return yamlLib2;
31036
31051
  }
31052
+ function roleHasShell(role) {
31053
+ return (role.tools?.includes("Bash") ?? true) && !role.disallowedTools?.includes("Bash");
31054
+ }
31037
31055
  function agentsDir(root) {
31038
31056
  return join6(root, ".claude", "agents");
31039
31057
  }
31040
31058
  function agentRel(agentType) {
31041
31059
  return `${AGENTS_REL}/${agentType}.md`;
31042
31060
  }
31043
- function workerTools(tools) {
31061
+ function workerTools(tools, shell = tools.includes("Bash")) {
31044
31062
  const out = tools.includes("ToolSearch") ? [...tools] : [...tools, "ToolSearch"];
31045
31063
  for (const prefix of EM_MCP_PREFIXES)
31046
- for (const t of WORKER_EM_TOOLS) if (!out.includes(prefix + t)) out.push(prefix + t);
31064
+ for (const t of WORKER_EM_TOOLS)
31065
+ if ((shell || !EXEC_TOOLS.has(t)) && !out.includes(prefix + t)) out.push(prefix + t);
31047
31066
  return out;
31048
31067
  }
31049
31068
  function frontmatter(swarm, role) {
@@ -31052,9 +31071,12 @@ function frontmatter(swarm, role) {
31052
31071
  description: `Worker "${role.role}" of the eminiar swarm "${swarm.name}": ${role.description} Spawn it for a task of this swarm and name the task as "task #N" in the prompt; the em-swarm skill coordinates runs.`
31053
31072
  };
31054
31073
  if (role.model) fm.model = role.model;
31055
- if (role.tools) fm.tools = workerTools(role.tools).join(", ");
31074
+ if (role.tools) fm.tools = workerTools(role.tools, roleHasShell(role)).join(", ");
31056
31075
  const spawns = role.tools?.some((t) => t === "Agent" || t === "Task");
31057
- const disallowed = [.../* @__PURE__ */ new Set([...role.disallowedTools ?? [], ...spawns ? [] : ["Agent"]])];
31076
+ const noShell = role.disallowedTools?.includes("Bash") ? execToolNames() : [];
31077
+ const disallowed = [
31078
+ .../* @__PURE__ */ new Set([...role.disallowedTools ?? [], ...spawns ? [] : ["Agent"], ...noShell])
31079
+ ];
31058
31080
  if (disallowed.length) fm.disallowedTools = disallowed.join(", ");
31059
31081
  if (role.skills.length) fm.skills = role.skills;
31060
31082
  if (role.hooks) {
@@ -31082,12 +31104,15 @@ function renderAgentFile(config2, swarm, role) {
31082
31104
  ...role.prompt ? [role.prompt, ""] : [],
31083
31105
  "## Swarm protocol",
31084
31106
  "",
31085
- '1. eminiar starts you with a swarm brief: the run goal, the open tasks, the shared notes and what the project knows (memories, history, structure). First claim the task your prompt names with `em_swarm {action:"claim", task:N}` (or the next open one with `em_swarm {action:"claim"}`); if the brief says you already hold a task, continue it.',
31086
- '2. Ask eminiar before acting: `em_recall {query}` for decisions and constraints, `em_history {action:"file", path}` before editing a file, `em_graph_impact {node}` before changing a symbol, `em_symbols` / `em_search` instead of grepping.',
31087
- '3. Share what other workers need with `em_swarm {action:"post", key, value}` and read theirs with `em_swarm {action:"read"}`.',
31088
- '4. Close every task with `em_swarm {action:"update", task, status, result}`: status `done`, `failed` or `blocked`; the result says what changed and how you verified it.',
31089
- "5. Record durable decisions with `em_remember` so later sessions know them.",
31090
- "6. Your final message goes back to the coordinator: lead with the outcome, then the files you changed and anything left open.",
31107
+ ...[
31108
+ 'eminiar starts you with a swarm brief: the run goal, the open tasks, the shared notes and what the project knows (memories, history, structure). First claim the task your prompt names with `em_swarm {action:"claim", task:N}` (or the next open one with `em_swarm {action:"claim"}`); if the brief says you already hold a task, continue it.',
31109
+ 'Ask eminiar before acting: `em_recall {query}` for decisions and constraints, `em_history {action:"file", path}` before editing a file, `em_graph_impact {node}` before changing a symbol, `em_symbols` / `em_search` instead of grepping.',
31110
+ ...!role.isolation && roleHasShell(role) ? [SHARED_TREE_RULE] : [],
31111
+ 'Share what other workers need with `em_swarm {action:"post", key, value}` and read theirs with `em_swarm {action:"read"}`.',
31112
+ 'Close every task with `em_swarm {action:"update", task, status, result}`: status `done`, `failed` or `blocked`; the result says what changed and how you verified it.',
31113
+ "Record durable decisions with `em_remember` so later sessions know them.",
31114
+ "Your final message goes back to the coordinator: lead with the outcome, then the files you changed and anything left open."
31115
+ ].map((step, i) => `${i + 1}. ${step}`),
31091
31116
  ""
31092
31117
  ].join("\n");
31093
31118
  const range = role.range ? modelRangeText(role.range) : void 0;
@@ -31284,7 +31309,7 @@ function syncAgents(root, config2, opts = {}) {
31284
31309
  }
31285
31310
  return { actions, changed, conflicts: actions.filter((a) => a.action === "conflict").length };
31286
31311
  }
31287
- var MARKER_PREFIX, HASH_RE, RANGE_RE, WORKER_EM_TOOLS, yamlLib2, EM_MCP_PREFIXES, AGENTS_REL, TOPOLOGY_RULE, hashOf;
31312
+ var MARKER_PREFIX, HASH_RE, RANGE_RE, WORKER_EM_TOOLS, yamlLib2, EXEC_TOOLS, EM_MCP_PREFIXES, AGENTS_REL, TOPOLOGY_RULE, execToolNames, hashOf, SHARED_TREE_RULE;
31288
31313
  var init_agents = __esm({
31289
31314
  "src/swarm/agents.ts"() {
31290
31315
  "use strict";
@@ -31310,6 +31335,11 @@ var init_agents = __esm({
31310
31335
  "em_docs",
31311
31336
  "em_fetch_and_index"
31312
31337
  ];
31338
+ EXEC_TOOLS = /* @__PURE__ */ new Set([
31339
+ "em_execute",
31340
+ "em_execute_file",
31341
+ "em_batch_execute"
31342
+ ]);
31313
31343
  EM_MCP_PREFIXES = ["mcp__plugin_eminiar_eminiar__", "mcp__eminiar__"];
31314
31344
  AGENTS_REL = ".claude/agents";
31315
31345
  TOPOLOGY_RULE = {
@@ -31317,10 +31347,12 @@ var init_agents = __esm({
31317
31347
  mesh: "peers share one task board. Claim any open task (your role's first), and read the blackboard for what the other workers found.",
31318
31348
  pipeline: "work moves in stages. You can claim a task only once every task of the earlier stages is done."
31319
31349
  };
31350
+ execToolNames = () => EM_MCP_PREFIXES.flatMap((prefix) => [...EXEC_TOOLS].map((t) => prefix + t));
31320
31351
  hashOf = (fm, rest2, range) => createHash8("sha1").update(range ? `range=${range}
31321
31352
  ${fm}
31322
31353
  ${rest2}` : `${fm}
31323
31354
  ${rest2}`).digest("hex").slice(0, 12);
31355
+ SHARED_TREE_RULE = "Other workers may be editing this working tree at the same time, and their changes are not committed. Never run `git stash`, `git checkout`, `git switch`, `git reset`, `git restore` or `git clean`, not even on one path: they hide or wipe another worker's work. If you commit, add only the files you changed: `git add -A` or `git commit -a` takes their unfinished work with yours. To test without your change (a mutation check), copy the files into a scratch directory outside the tree and run the tests there.";
31324
31356
  }
31325
31357
  });
31326
31358
 
@@ -31481,7 +31513,7 @@ function projectDirs(root, cwd) {
31481
31513
  }
31482
31514
  function pluginDirs(plugin, env, home) {
31483
31515
  const out = /* @__PURE__ */ new Set();
31484
- const pluginsRoot = env.CLAUDE_CODE_PLUGIN_CACHE_DIR?.trim() || join7(home, ".claude", "plugins");
31516
+ const pluginsRoot = claudePluginsDir(env, home);
31485
31517
  try {
31486
31518
  const j = JSON.parse(readFileSync6(join7(pluginsRoot, "installed_plugins.json"), "utf8"));
31487
31519
  for (const [key, val] of Object.entries(j?.plugins ?? {})) {
@@ -31494,8 +31526,8 @@ function pluginDirs(plugin, env, home) {
31494
31526
  }
31495
31527
  } catch {
31496
31528
  }
31497
- const sep6 = process.platform === "win32" ? ";" : ":";
31498
- for (const raw of (env.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(sep6)) {
31529
+ const sep7 = process.platform === "win32" ? ";" : ":";
31530
+ for (const raw of (env.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(sep7)) {
31499
31531
  const p2 = raw.trim().replace(/^~(?=$|[/\\])/, home);
31500
31532
  if (!p2 || !isAbsolute3(p2)) continue;
31501
31533
  const name = manifest(p2)?.name;
@@ -31582,7 +31614,7 @@ function resolveAgentPin(agentType, q) {
31582
31614
  const scopes = [];
31583
31615
  if (managed) scopes.push(["managed", join7(managed, ".claude", "agents")]);
31584
31616
  for (const d of projectDirs(q.root, q.cwd)) scopes.push(["project", d]);
31585
- scopes.push(["user", join7(home, ".claude", "agents")]);
31617
+ scopes.push(["user", join7(claudeConfigDir(env, home), "agents")]);
31586
31618
  let truncated = false;
31587
31619
  for (const [source, dir] of scopes) {
31588
31620
  const found = byName(dir, agentType);
@@ -31608,6 +31640,7 @@ var init_agent_pin = __esm({
31608
31640
  "src/adjudicator/agent-pin.ts"() {
31609
31641
  "use strict";
31610
31642
  init_esm_shims();
31643
+ init_paths();
31611
31644
  init_agents();
31612
31645
  init_savings2();
31613
31646
  FORK_AGENT = "fork";
@@ -35064,8 +35097,8 @@ var require_pattern = __commonJS({
35064
35097
  }
35065
35098
  exports.endsWithSlashGlobStar = endsWithSlashGlobStar;
35066
35099
  function isAffectDepthOfReadingPattern(pattern2) {
35067
- const basename8 = path3.basename(pattern2);
35068
- return endsWithSlashGlobStar(pattern2) || isStaticPattern(basename8);
35100
+ const basename10 = path3.basename(pattern2);
35101
+ return endsWithSlashGlobStar(pattern2) || isStaticPattern(basename10);
35069
35102
  }
35070
35103
  exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
35071
35104
  function expandPatternsWithBraceExpansion(patterns) {
@@ -35110,21 +35143,21 @@ var require_pattern = __commonJS({
35110
35143
  exports.removeDuplicateSlashes = removeDuplicateSlashes;
35111
35144
  function partitionAbsoluteAndRelative(patterns) {
35112
35145
  const absolute = [];
35113
- const relative17 = [];
35146
+ const relative18 = [];
35114
35147
  for (const pattern2 of patterns) {
35115
- if (isAbsolute6(pattern2)) {
35148
+ if (isAbsolute7(pattern2)) {
35116
35149
  absolute.push(pattern2);
35117
35150
  } else {
35118
- relative17.push(pattern2);
35151
+ relative18.push(pattern2);
35119
35152
  }
35120
35153
  }
35121
- return [absolute, relative17];
35154
+ return [absolute, relative18];
35122
35155
  }
35123
35156
  exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative;
35124
- function isAbsolute6(pattern2) {
35157
+ function isAbsolute7(pattern2) {
35125
35158
  return path3.isAbsolute(pattern2);
35126
35159
  }
35127
- exports.isAbsolute = isAbsolute6;
35160
+ exports.isAbsolute = isAbsolute7;
35128
35161
  }
35129
35162
  });
35130
35163
 
@@ -35549,11 +35582,11 @@ var require_out = __commonJS({
35549
35582
  async.read(path3, getSettings(optionsOrSettingsOrCallback), callback);
35550
35583
  }
35551
35584
  exports.stat = stat;
35552
- function statSync19(path3, optionsOrSettings) {
35585
+ function statSync21(path3, optionsOrSettings) {
35553
35586
  const settings = getSettings(optionsOrSettings);
35554
35587
  return sync.read(path3, settings);
35555
35588
  }
35556
- exports.statSync = statSync19;
35589
+ exports.statSync = statSync21;
35557
35590
  function getSettings(settingsOrOptions = {}) {
35558
35591
  if (settingsOrOptions instanceof settings_1.default) {
35559
35592
  return settingsOrOptions;
@@ -36234,41 +36267,41 @@ var require_queue = __commonJS({
36234
36267
  queue.drained = drained;
36235
36268
  return queue;
36236
36269
  function push(value) {
36237
- var p2 = new Promise(function(resolve14, reject) {
36270
+ var p2 = new Promise(function(resolve16, reject) {
36238
36271
  pushCb(value, function(err, result) {
36239
36272
  if (err) {
36240
36273
  reject(err);
36241
36274
  return;
36242
36275
  }
36243
- resolve14(result);
36276
+ resolve16(result);
36244
36277
  });
36245
36278
  });
36246
36279
  p2.catch(noop);
36247
36280
  return p2;
36248
36281
  }
36249
36282
  function unshift(value) {
36250
- var p2 = new Promise(function(resolve14, reject) {
36283
+ var p2 = new Promise(function(resolve16, reject) {
36251
36284
  unshiftCb(value, function(err, result) {
36252
36285
  if (err) {
36253
36286
  reject(err);
36254
36287
  return;
36255
36288
  }
36256
- resolve14(result);
36289
+ resolve16(result);
36257
36290
  });
36258
36291
  });
36259
36292
  p2.catch(noop);
36260
36293
  return p2;
36261
36294
  }
36262
36295
  function drained() {
36263
- var p2 = new Promise(function(resolve14) {
36296
+ var p2 = new Promise(function(resolve16) {
36264
36297
  process.nextTick(function() {
36265
36298
  if (queue.idle()) {
36266
- resolve14();
36299
+ resolve16();
36267
36300
  } else {
36268
36301
  var previousDrain = queue.drain;
36269
36302
  queue.drain = function() {
36270
36303
  if (typeof previousDrain === "function") previousDrain();
36271
- resolve14();
36304
+ resolve16();
36272
36305
  queue.drain = previousDrain;
36273
36306
  };
36274
36307
  }
@@ -36765,9 +36798,9 @@ var require_stream3 = __commonJS({
36765
36798
  });
36766
36799
  }
36767
36800
  _getStat(filepath) {
36768
- return new Promise((resolve14, reject) => {
36801
+ return new Promise((resolve16, reject) => {
36769
36802
  this._stat(filepath, this._fsStatSettings, (error62, stats) => {
36770
- return error62 === null ? resolve14(stats) : reject(error62);
36803
+ return error62 === null ? resolve16(stats) : reject(error62);
36771
36804
  });
36772
36805
  });
36773
36806
  }
@@ -36792,10 +36825,10 @@ var require_async5 = __commonJS({
36792
36825
  this._readerStream = new stream_1.default(this._settings);
36793
36826
  }
36794
36827
  dynamic(root, options) {
36795
- return new Promise((resolve14, reject) => {
36828
+ return new Promise((resolve16, reject) => {
36796
36829
  this._walkAsync(root, options, (error62, entries) => {
36797
36830
  if (error62 === null) {
36798
- resolve14(entries);
36831
+ resolve16(entries);
36799
36832
  } else {
36800
36833
  reject(error62);
36801
36834
  }
@@ -36805,10 +36838,10 @@ var require_async5 = __commonJS({
36805
36838
  async static(patterns, options) {
36806
36839
  const entries = [];
36807
36840
  const stream = this._readerStream.static(patterns, options);
36808
- return new Promise((resolve14, reject) => {
36841
+ return new Promise((resolve16, reject) => {
36809
36842
  stream.once("error", reject);
36810
36843
  stream.on("data", (entry) => entries.push(entry));
36811
- stream.once("end", () => resolve14(entries));
36844
+ stream.once("end", () => resolve16(entries));
36812
36845
  });
36813
36846
  }
36814
36847
  };
@@ -39737,13 +39770,12 @@ __export(ingest_exports, {
39737
39770
  });
39738
39771
  import { createHash as createHash10 } from "crypto";
39739
39772
  import { closeSync, existsSync as existsSync7, openSync, readdirSync as readdirSync4, readSync, statSync as statSync8 } from "fs";
39740
- import { homedir as homedir3 } from "os";
39741
39773
  import { basename as basename3, dirname as dirname6, join as join11, resolve as resolve6, sep as sep3 } from "path";
39742
39774
  function isValidSessionId(id) {
39743
39775
  return SESSION_ID_RE.test(id);
39744
39776
  }
39745
39777
  function claudeDir() {
39746
- return process.env.EMINIAR_CLAUDE_DIR || join11(homedir3(), ".claude");
39778
+ return process.env.EMINIAR_CLAUDE_DIR || claudeConfigDir();
39747
39779
  }
39748
39780
  function insideDir(path3, dir) {
39749
39781
  const d = resolve6(dir);
@@ -40032,6 +40064,7 @@ var init_ingest = __esm({
40032
40064
  "src/usage/ingest.ts"() {
40033
40065
  "use strict";
40034
40066
  init_esm_shims();
40067
+ init_paths();
40035
40068
  init_pricing();
40036
40069
  init_transcript();
40037
40070
  ZERO = { calls: 0, toolUses: 0, contextEvents: 0 };
@@ -40875,11 +40908,11 @@ function parseLimit(value) {
40875
40908
  return n;
40876
40909
  }
40877
40910
  function recentlyIngested(root, sessionId, now = Date.now()) {
40878
- const within2 = (key) => {
40911
+ const within3 = (key) => {
40879
40912
  const t = lastIngest.get(key);
40880
40913
  return t !== void 0 && now - t < INGEST_GUARD_MS;
40881
40914
  };
40882
- return within2(root) || sessionId !== void 0 && within2(sessionIngestKey(root, sessionId));
40915
+ return within3(root) || sessionId !== void 0 && within3(sessionIngestKey(root, sessionId));
40883
40916
  }
40884
40917
  function ingestFor(db, ctx, scope, sessionId, now) {
40885
40918
  const overrides = ctx.config.usage.pricing;
@@ -42691,12 +42724,13 @@ import {
42691
42724
  readdirSync as readdirSync5,
42692
42725
  readFileSync as readFileSync10,
42693
42726
  renameSync as renameSync5,
42727
+ statSync as statSync9,
42694
42728
  unlinkSync as unlinkSync5,
42695
42729
  writeFileSync as writeFileSync6
42696
42730
  } from "fs";
42697
42731
  import { connect } from "net";
42698
- import { homedir as homedir4 } from "os";
42699
- import { dirname as dirname7, join as join12 } from "path";
42732
+ import { homedir as homedir3 } from "os";
42733
+ import { basename as basename5, dirname as dirname7, join as join12 } from "path";
42700
42734
  import { parseArgs } from "util";
42701
42735
  function defaultIo() {
42702
42736
  return {
@@ -42725,7 +42759,7 @@ async function askLine(question) {
42725
42759
  }
42726
42760
  function readHidden(question) {
42727
42761
  const stdin = process.stdin;
42728
- return new Promise((resolve14, reject) => {
42762
+ return new Promise((resolve16, reject) => {
42729
42763
  let buf = "";
42730
42764
  const wasRaw = stdin.isRaw;
42731
42765
  stdin.setRawMode(true);
@@ -42738,7 +42772,7 @@ function readHidden(question) {
42738
42772
  stdin.pause();
42739
42773
  process.stdout.write("\n");
42740
42774
  if (err) reject(err);
42741
- else resolve14(buf);
42775
+ else resolve16(buf);
42742
42776
  };
42743
42777
  const onData = (chunk2) => {
42744
42778
  for (const ch of chunk2) {
@@ -42851,6 +42885,15 @@ function valueLooksLikeKey(value) {
42851
42885
  if (KEY_PREFIX.test(v)) return true;
42852
42886
  return v.split(/[\s\-_/.:@+=,;&?#%~]+/).some((seg) => seg.length >= 20 && /\d/.test(seg));
42853
42887
  }
42888
+ function pathLooksLikeKey(value) {
42889
+ const v = value.trim();
42890
+ let isFile3 = false;
42891
+ try {
42892
+ isFile3 = statSync9(v).isFile();
42893
+ } catch {
42894
+ }
42895
+ return valueLooksLikeKey(isFile3 ? basename5(v) : v);
42896
+ }
42854
42897
  function baseUrlProblem(raw) {
42855
42898
  let u;
42856
42899
  try {
@@ -42869,7 +42912,8 @@ function baseUrlProblem(raw) {
42869
42912
  function argvKeyProblem(values) {
42870
42913
  for (const flag of ["model", "since", "fixture", "repeat", "providers"]) {
42871
42914
  const v = values[flag];
42872
- if (typeof v === "string" && valueLooksLikeKey(v)) return KEY_NOT_FROM_ARGV;
42915
+ if (typeof v !== "string") continue;
42916
+ if (flag === "fixture" ? pathLooksLikeKey(v) : valueLooksLikeKey(v)) return KEY_NOT_FROM_ARGV;
42873
42917
  }
42874
42918
  const url2 = values["base-url"];
42875
42919
  if (typeof url2 === "string" && url2) return baseUrlProblem(url2);
@@ -43114,12 +43158,12 @@ async function pickProvider(io) {
43114
43158
  return direct && a !== "1" ? direct : { provider: "heuristic" };
43115
43159
  }
43116
43160
  function portOpen(port, ms4) {
43117
- return new Promise((resolve14) => {
43161
+ return new Promise((resolve16) => {
43118
43162
  const s = connect({ host: "127.0.0.1", port });
43119
43163
  const end = (v) => {
43120
43164
  clearTimeout(t);
43121
43165
  s.destroy();
43122
- resolve14(v);
43166
+ resolve16(v);
43123
43167
  };
43124
43168
  const t = setTimeout(() => end(false), ms4);
43125
43169
  s.once("connect", () => end(true));
@@ -44228,7 +44272,7 @@ var init_cli = __esm({
44228
44272
  isObj2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
44229
44273
  projectFile = (root) => join12(root, "eminiar.config.json");
44230
44274
  tilde = (p2) => {
44231
- const h = homedir4();
44275
+ const h = homedir3();
44232
44276
  return h && p2.startsWith(h) ? `~${p2.slice(h.length)}` : p2;
44233
44277
  };
44234
44278
  CHOICES = [
@@ -44457,7 +44501,6 @@ import {
44457
44501
  readFileSync as readFileSync13,
44458
44502
  writeFileSync as writeFileSync7
44459
44503
  } from "fs";
44460
- import { homedir as homedir5 } from "os";
44461
44504
  import { dirname as dirname9, join as join15 } from "path";
44462
44505
  function readJson2(file3) {
44463
44506
  if (!existsSync10(file3)) return {};
@@ -44540,10 +44583,10 @@ var init_claude_config = __esm({
44540
44583
  "use strict";
44541
44584
  init_esm_shims();
44542
44585
  init_paths();
44543
- claudeDir2 = () => process.env.CLAUDE_CONFIG_DIR || join15(homedir5(), ".claude");
44586
+ claudeDir2 = () => claudeConfigDir();
44544
44587
  settingsPath = () => join15(claudeDir2(), "settings.json");
44545
- claudeJsonPath = () => join15(homedir5(), ".claude.json");
44546
- pluginsDir = () => join15(claudeDir2(), "plugins");
44588
+ claudeJsonPath = () => claudeJsonFile();
44589
+ pluginsDir = () => claudePluginsDir();
44547
44590
  PLUGIN_SERVER_ID = "plugin:eminiar:eminiar";
44548
44591
  mcpNeedsAuthPath = () => join15(claudeDir2(), "mcp-needs-auth-cache.json");
44549
44592
  }
@@ -44753,10 +44796,10 @@ var init_server_alive = __esm({
44753
44796
 
44754
44797
  // src/hooks/cursor.ts
44755
44798
  import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync15, rmSync as rmSync2, writeFileSync as writeFileSync10 } from "fs";
44756
- import { homedir as homedir6 } from "os";
44799
+ import { homedir as homedir4 } from "os";
44757
44800
  import { join as join19 } from "path";
44758
44801
  function cursorDir() {
44759
- return process.env.CURSOR_CONFIG_DIR || join19(homedir6(), ".cursor");
44802
+ return process.env.CURSOR_CONFIG_DIR || join19(homedir4(), ".cursor");
44760
44803
  }
44761
44804
  var PENDING_TTL_MS2;
44762
44805
  var init_cursor = __esm({
@@ -45325,12 +45368,12 @@ import { join as join24 } from "path";
45325
45368
  function mcpHandshake(bin, ms4 = 15e3, opts = {}) {
45326
45369
  const exitMs = opts.exitMs ?? 2e3;
45327
45370
  const start = opts.spawn ?? ((b) => spawn(b, [], { stdio: ["pipe", "pipe", "pipe"] }));
45328
- return new Promise((resolve14) => {
45371
+ return new Promise((resolve16) => {
45329
45372
  let child;
45330
45373
  try {
45331
45374
  child = start(bin);
45332
45375
  } catch (e) {
45333
- resolve14(`spawn failed: ${e.message}`);
45376
+ resolve16(`spawn failed: ${e.message}`);
45334
45377
  return;
45335
45378
  }
45336
45379
  let buf = "";
@@ -45340,7 +45383,7 @@ function mcpHandshake(bin, ms4 = 15e3, opts = {}) {
45340
45383
  const timers = [];
45341
45384
  const done = () => {
45342
45385
  for (const t of timers) clearTimeout(t);
45343
- if (result !== void 0) resolve14(result);
45386
+ if (result !== void 0) resolve16(result);
45344
45387
  };
45345
45388
  const finish2 = (r) => {
45346
45389
  if (result !== void 0) return;
@@ -45476,7 +45519,7 @@ async function fullDoctor(opts = {}) {
45476
45519
  });
45477
45520
  }
45478
45521
  } else if (rec.method === "settings") {
45479
- const s = existsSync18(join24(process.env.HOME ?? "", ".claude", "settings.json")) ? readFileSync20(join24(process.env.HOME ?? "", ".claude", "settings.json"), "utf8") : "";
45522
+ const s = existsSync18(settingsPath()) ? readFileSync20(settingsPath(), "utf8") : "";
45480
45523
  checks.push({
45481
45524
  name: "hooks in settings.json",
45482
45525
  ok: s.includes("em-hook"),
@@ -45682,7 +45725,7 @@ async function install(opts) {
45682
45725
  if (usedMethod === "settings") {
45683
45726
  wrote = registerSettings(stable);
45684
45727
  log.ok(
45685
- "hooks written to ~/.claude/settings.json, MCP server to ~/.claude.json, skills to ~/.claude/skills"
45728
+ `hooks written to ${settingsPath()}, MCP server to ${claudeJsonPath()}, skills to ${join25(claudeDir2(), "skills")}`
45686
45729
  );
45687
45730
  }
45688
45731
  if (wantClaude) {
@@ -45754,19 +45797,7 @@ function writeTeamFiles(root, marketplace, extra = {}) {
45754
45797
  `${JSON.stringify(
45755
45798
  {
45756
45799
  $schema: "https://raw.githubusercontent.com/rydn/eminiar/main/eminiar.config.schema.json",
45757
- routing: { mode: "auto", deferToContextMode: true },
45758
- graph: {
45759
- exclude: [
45760
- "**/node_modules/**",
45761
- "**/dist/**",
45762
- "**/build/**",
45763
- "**/.git/**",
45764
- "**/coverage/**",
45765
- "**/*.d.ts"
45766
- ]
45767
- },
45768
- index: { docs: ["**/*.md", "docs/**"] },
45769
- permissions: { extraDeny: [] }
45800
+ routing: { mode: "auto", deferToContextMode: true }
45770
45801
  },
45771
45802
  null,
45772
45803
  2
@@ -45872,7 +45903,7 @@ async function uninstall(opts) {
45872
45903
  const p2 = paths();
45873
45904
  const plan = [
45874
45905
  rec?.method === "plugin" || !rec ? "claude plugin uninstall eminiar@eminiar + marketplace remove" : "",
45875
- rec?.method === "settings" ? "remove eminiar hook entries from ~/.claude/settings.json, mcpServers.eminiar from ~/.claude.json, em-* skills" : "",
45906
+ rec?.method === "settings" ? `remove eminiar hook entries from ${settingsPath()}, mcpServers.eminiar from ${claudeJsonPath()}, em-* skills` : "",
45876
45907
  rec?.wrote.cursor ? "remove eminiar hook entries from ~/.cursor/hooks.json, mcpServers.eminiar from ~/.cursor/mcp.json, em-* skills from ~/.cursor/skills" : "",
45877
45908
  rec?.wrote.statusline ? "clear the eminiar statusLine" : "",
45878
45909
  `delete ${p2.plugin}`,
@@ -46892,16 +46923,16 @@ var init_metrics = __esm({
46892
46923
  function resolveNode(db, ref) {
46893
46924
  const direct = db.graphGetNode(ref);
46894
46925
  if (direct) return direct;
46895
- const clean2 = ref.replace(/^\.\//, "");
46896
- const asFile = db.graphGetNode(fileId(clean2));
46926
+ const clean3 = ref.replace(/^\.\//, "");
46927
+ const asFile = db.graphGetNode(fileId(clean3));
46897
46928
  if (asFile) return asFile;
46898
- if (clean2.includes("#")) {
46899
- const [p2, n] = clean2.split("#");
46929
+ if (clean3.includes("#")) {
46930
+ const [p2, n] = clean3.split("#");
46900
46931
  const s = db.graphGetNode(symId(p2, n));
46901
46932
  if (s) return s;
46902
46933
  }
46903
- const found = db.graphFindNodes({ query: clean2, limit: 5 });
46904
- return found.find((n) => n.name === clean2) ?? found[0];
46934
+ const found = db.graphFindNodes({ query: clean3, limit: 5 });
46935
+ return found.find((n) => n.name === clean3) ?? found[0];
46905
46936
  }
46906
46937
  function expand2(db, node2) {
46907
46938
  if (node2.kind !== "file") return [node2.id];
@@ -48158,7 +48189,7 @@ var init_go = __esm({
48158
48189
  });
48159
48190
 
48160
48191
  // src/graph/languages/php.ts
48161
- import { readdirSync as readdirSync12, readFileSync as readFileSync22, statSync as statSync9 } from "fs";
48192
+ import { readdirSync as readdirSync12, readFileSync as readFileSync22, statSync as statSync10 } from "fs";
48162
48193
  import { dirname as dirname11, join as join28, normalize as normalize4, relative as relative9, resolve as resolve8 } from "path";
48163
48194
  function readComposer(dir) {
48164
48195
  const file3 = join28(dir, "composer.json");
@@ -49207,7 +49238,7 @@ var init_php = __esm({
49207
49238
  SPATIE_ATTRIBUTES = "Spatie\\RouteAttributes\\Attributes\\";
49208
49239
  isFile = (p2) => {
49209
49240
  try {
49210
- return statSync9(p2).isFile();
49241
+ return statSync10(p2).isFile();
49211
49242
  } catch {
49212
49243
  return false;
49213
49244
  }
@@ -49225,7 +49256,7 @@ var init_php = __esm({
49225
49256
  });
49226
49257
 
49227
49258
  // src/graph/languages/python.ts
49228
- import { existsSync as existsSync23, statSync as statSync10 } from "fs";
49259
+ import { existsSync as existsSync23, statSync as statSync11 } from "fs";
49229
49260
  import { dirname as dirname12, join as join29, normalize as normalize5, relative as relative10, resolve as resolve9 } from "path";
49230
49261
  function resolvePython(root, fromRel, module, level) {
49231
49262
  const parts = module ? module.split(".") : [];
@@ -49600,7 +49631,7 @@ var init_python = __esm({
49600
49631
  BOOL_OPS3 = /* @__PURE__ */ new Set(["and", "or"]);
49601
49632
  isFile2 = (p2) => {
49602
49633
  try {
49603
- return statSync10(p2).isFile();
49634
+ return statSync11(p2).isFile();
49604
49635
  } catch {
49605
49636
  return false;
49606
49637
  }
@@ -49629,7 +49660,7 @@ var init_python = __esm({
49629
49660
  });
49630
49661
 
49631
49662
  // src/graph/resolve.ts
49632
- import { existsSync as existsSync24, readdirSync as readdirSync13, readFileSync as readFileSync23, statSync as statSync11 } from "fs";
49663
+ import { existsSync as existsSync24, readdirSync as readdirSync13, readFileSync as readFileSync23, statSync as statSync12 } from "fs";
49633
49664
  import { dirname as dirname13, join as join30, normalize as normalize6, relative as relative11, resolve as resolve10 } from "path";
49634
49665
  function loadTsPaths(root) {
49635
49666
  const hit = tsPathsCache.get(root);
@@ -49660,7 +49691,7 @@ function loadTsPaths(root) {
49660
49691
  function probe(abs) {
49661
49692
  const tryFile = (p2) => {
49662
49693
  try {
49663
- return statSync11(p2).isFile() ? p2 : void 0;
49694
+ return statSync12(p2).isFile() ? p2 : void 0;
49664
49695
  } catch {
49665
49696
  return void 0;
49666
49697
  }
@@ -49701,9 +49732,9 @@ function loadWorkspaces(root) {
49701
49732
  if (Array.isArray(ws)) globs.push(...ws);
49702
49733
  else if (ws?.packages) globs.push(...ws.packages);
49703
49734
  try {
49704
- const yaml3 = join30(root, "pnpm-workspace.yaml");
49705
- if (existsSync24(yaml3)) {
49706
- for (const m of readFileSync23(yaml3, "utf8").matchAll(/^\s*-\s*['"]?([^'"\n#]+?)['"]?\s*$/gm))
49735
+ const yaml4 = join30(root, "pnpm-workspace.yaml");
49736
+ if (existsSync24(yaml4)) {
49737
+ for (const m of readFileSync23(yaml4, "utf8").matchAll(/^\s*-\s*['"]?([^'"\n#]+?)['"]?\s*$/gm))
49707
49738
  globs.push(m[1].trim());
49708
49739
  }
49709
49740
  } catch {
@@ -50860,7 +50891,7 @@ var init_link = __esm({
50860
50891
  });
50861
50892
 
50862
50893
  // src/graph/reindex.ts
50863
- import { readFileSync as readFileSync24, statSync as statSync12 } from "fs";
50894
+ import { readFileSync as readFileSync24, statSync as statSync13 } from "fs";
50864
50895
  import { join as join31 } from "path";
50865
50896
  function listCodeFiles(ctx) {
50866
50897
  return import_fast_glob3.default.sync(ctx.config.graph.include, {
@@ -50873,7 +50904,7 @@ function listCodeFiles(ctx) {
50873
50904
  }
50874
50905
  async function indexOne(ctx, db, rel, force) {
50875
50906
  const abs = join31(ctx.root, rel);
50876
- const st = statSync12(abs);
50907
+ const st = statSync13(abs);
50877
50908
  const backend = getCodeBackend(ctx);
50878
50909
  const language = backend.languageOf(rel) ?? null;
50879
50910
  if (st.size > MAX_CODE_BYTES) {
@@ -50973,7 +51004,7 @@ async function reindexCode(ctx, db, opts = {}) {
50973
51004
  summary.unsupported++;
50974
51005
  if (db.fileIndexGet(rel)?.status === "unsupported") continue;
50975
51006
  try {
50976
- const st = statSync12(join31(ctx.root, rel));
51007
+ const st = statSync13(join31(ctx.root, rel));
50977
51008
  db.fileIndexSet({
50978
51009
  path: rel,
50979
51010
  hash: "",
@@ -51001,7 +51032,7 @@ async function reindexCode(ctx, db, opts = {}) {
51001
51032
  const error62 = err.message;
51002
51033
  summary.errors.push({ path: rel, error: error62 });
51003
51034
  try {
51004
- const st = statSync12(join31(ctx.root, rel));
51035
+ const st = statSync13(join31(ctx.root, rel));
51005
51036
  db.fileIndexSet({
51006
51037
  path: rel,
51007
51038
  hash: "",
@@ -51064,7 +51095,7 @@ async function reindexCode(ctx, db, opts = {}) {
51064
51095
  }
51065
51096
  function statSafe(p2) {
51066
51097
  try {
51067
- return statSync12(p2).isFile();
51098
+ return statSync13(p2).isFile();
51068
51099
  } catch {
51069
51100
  return false;
51070
51101
  }
@@ -51105,7 +51136,7 @@ function indexFreshness(ctx, db) {
51105
51136
  for (const r of rows.slice(0, 400)) {
51106
51137
  try {
51107
51138
  const abs = join31(ctx.root, r.path);
51108
- const st = statSync12(abs);
51139
+ const st = statSync13(abs);
51109
51140
  if (r.mtime !== null && Math.abs(st.mtimeMs - r.mtime) < 1 && st.size === r.size) continue;
51110
51141
  if (hashText(readFileSync24(abs, "utf8")) !== r.hash) dirty.push(r.path);
51111
51142
  } catch {
@@ -51572,9 +51603,23 @@ function symbolsAt(db, path3, hunks) {
51572
51603
  }
51573
51604
  return out;
51574
51605
  }
51606
+ function isDiffRef(ref) {
51607
+ return ref.length > 0 && ref.length <= 256 && !ref.startsWith("-") && !new RegExp("\\p{Cc}", "u").test(ref);
51608
+ }
51575
51609
  function detectChanges(db, root, opts = {}) {
51576
- const args = ["diff", "--unified=0", "--no-color", "--relative", "--no-ext-diff"];
51577
- if (opts.ref) args.push(opts.ref);
51610
+ const args = [
51611
+ "diff",
51612
+ "--unified=0",
51613
+ "--no-color",
51614
+ "--relative",
51615
+ "--no-ext-diff",
51616
+ "--no-textconv"
51617
+ ];
51618
+ if (opts.ref) {
51619
+ if (!isDiffRef(opts.ref))
51620
+ throw new Error(`not a ref, sha or range: ${JSON.stringify(opts.ref)}`);
51621
+ args.push("--end-of-options", opts.ref);
51622
+ }
51578
51623
  const hunks = parseUnifiedDiff(git(root, args));
51579
51624
  if (!opts.ref) {
51580
51625
  for (const p2 of git(root, ["ls-files", "--others", "--exclude-standard"]).split("\n")) {
@@ -51784,8 +51829,8 @@ function entryPoints(root, db) {
51784
51829
  const out = [];
51785
51830
  const push = (kind, name, path3) => {
51786
51831
  if (typeof path3 !== "string") return;
51787
- const clean2 = path3.replace(/^\.\//, "");
51788
- if (!out.some((e) => e.kind === kind && e.name === name)) out.push({ kind, name, path: clean2 });
51832
+ const clean3 = path3.replace(/^\.\//, "");
51833
+ if (!out.some((e) => e.kind === kind && e.name === name)) out.push({ kind, name, path: clean3 });
51789
51834
  };
51790
51835
  const bin = pkg.bin;
51791
51836
  if (typeof bin === "string") push("bin", String(pkg.name ?? "bin"), bin);
@@ -53109,10 +53154,10 @@ import {
53109
53154
  realpathSync as realpathSync5,
53110
53155
  renameSync as renameSync6,
53111
53156
  rmSync as rmSync7,
53112
- statSync as statSync13,
53157
+ statSync as statSync14,
53113
53158
  writeFileSync as writeFileSync13
53114
53159
  } from "fs";
53115
- import { basename as basename5, dirname as dirname14, isAbsolute as isAbsolute5, join as join33, relative as relative12, sep as sep4 } from "path";
53160
+ import { basename as basename6, dirname as dirname14, isAbsolute as isAbsolute5, join as join33, relative as relative12, sep as sep4 } from "path";
53116
53161
  function docsTag(name) {
53117
53162
  return `${DOCS_PREFIX}${name}`;
53118
53163
  }
@@ -53187,7 +53232,7 @@ function locationProblem(file3, root, write2) {
53187
53232
  if (write2) {
53188
53233
  let dangling = false;
53189
53234
  try {
53190
- dangling = !statSync13(file3, { throwIfNoEntry: false });
53235
+ dangling = !statSync14(file3, { throwIfNoEntry: false });
53191
53236
  } catch {
53192
53237
  }
53193
53238
  return dangling ? "it is a symlink to a missing file" : "it is a symlink, and writes replace only a regular file";
@@ -53212,7 +53257,7 @@ function probeManifestAt(file3, opts = {}) {
53212
53257
  }
53213
53258
  let fd;
53214
53259
  try {
53215
- const st = statSync13(file3, { throwIfNoEntry: false });
53260
+ const st = statSync14(file3, { throwIfNoEntry: false });
53216
53261
  if (!st) {
53217
53262
  return lstatSync2(file3, { throwIfNoEntry: false }) ? { state: "refused", reason: "it is a symlink to a missing file" } : { state: "missing" };
53218
53263
  }
@@ -53285,8 +53330,8 @@ function writeManifest(ctx, m) {
53285
53330
  version: 1,
53286
53331
  sources: [...m.sources].sort((a, b) => a.name.localeCompare(b.name))
53287
53332
  };
53288
- const mode = p2.state === "ok" ? statSync13(file3, { throwIfNoEntry: false })?.mode : void 0;
53289
- const tmp = join33(dir, `.${basename5(file3)}.${process.pid}.${randomBytes3(6).toString("hex")}.tmp`);
53333
+ const mode = p2.state === "ok" ? statSync14(file3, { throwIfNoEntry: false })?.mode : void 0;
53334
+ const tmp = join33(dir, `.${basename6(file3)}.${process.pid}.${randomBytes3(6).toString("hex")}.tmp`);
53290
53335
  try {
53291
53336
  writeFileSync13(tmp, `${JSON.stringify(sorted, null, 2)}
53292
53337
  `, { flag: "wx" });
@@ -53403,8 +53448,8 @@ var init_manifest = __esm({
53403
53448
  });
53404
53449
 
53405
53450
  // src/docs/discover.ts
53406
- import { existsSync as existsSync26, readFileSync as readFileSync26, statSync as statSync14 } from "fs";
53407
- import { basename as basename6, join as join34, relative as relative13 } from "path";
53451
+ import { existsSync as existsSync26, readFileSync as readFileSync26, statSync as statSync15 } from "fs";
53452
+ import { basename as basename7, join as join34, relative as relative13 } from "path";
53408
53453
  function readText(file3) {
53409
53454
  try {
53410
53455
  return readFileSync26(file3, "utf8");
@@ -53425,7 +53470,7 @@ function sizeOf(files) {
53425
53470
  let n = 0;
53426
53471
  for (const f of files) {
53427
53472
  try {
53428
- n += statSync14(f).size;
53473
+ n += statSync15(f).size;
53429
53474
  } catch {
53430
53475
  }
53431
53476
  }
@@ -53592,7 +53637,7 @@ function apiSignals(dir, project) {
53592
53637
  for (const file3 of files) {
53593
53638
  let size = 0;
53594
53639
  try {
53595
- size = statSync14(file3).size;
53640
+ size = statSync15(file3).size;
53596
53641
  } catch {
53597
53642
  continue;
53598
53643
  }
@@ -53600,7 +53645,7 @@ function apiSignals(dir, project) {
53600
53645
  const text3 = readText(file3);
53601
53646
  if (!text3 || text3.includes("\0")) continue;
53602
53647
  const rel = relative13(dir, file3).split("\\").join("/");
53603
- const isEnv = basename6(file3).startsWith(".env");
53648
+ const isEnv = basename7(file3).startsWith(".env");
53604
53649
  const lines = text3.split("\n");
53605
53650
  const found = [];
53606
53651
  for (let i = 0; i < lines.length; i++) {
@@ -53688,7 +53733,7 @@ function fileSignals(dir, project) {
53688
53733
  }
53689
53734
  function localCandidates(root, dir, project, isRegistered) {
53690
53735
  const out = [];
53691
- const prefix = project ? `${slugify3(basename6(dir))}-` : "";
53736
+ const prefix = project ? `${slugify3(basename7(dir))}-` : "";
53692
53737
  const rel = (abs) => relative13(root, abs).split("\\").join("/");
53693
53738
  const push = (c) => {
53694
53739
  if (!c.files) return;
@@ -53712,7 +53757,7 @@ function localCandidates(root, dir, project, isRegistered) {
53712
53757
  push({
53713
53758
  name: `${prefix}readme`,
53714
53759
  path: rel(dir) || ".",
53715
- glob: guides.map((g) => basename6(g)),
53760
+ glob: guides.map((g) => basename7(g)),
53716
53761
  recursive: false,
53717
53762
  files: guides.length,
53718
53763
  bytes: sizeOf(guides),
@@ -53813,7 +53858,7 @@ function localCandidates(root, dir, project, isRegistered) {
53813
53858
  push({
53814
53859
  name: `${prefix}env-example`,
53815
53860
  path: rel(dir) || ".",
53816
- glob: env.map((f) => basename6(f)),
53861
+ glob: env.map((f) => basename7(f)),
53817
53862
  recursive: false,
53818
53863
  files: env.length,
53819
53864
  bytes: sizeOf(env),
@@ -54541,7 +54586,7 @@ var init_category = __esm({
54541
54586
  });
54542
54587
 
54543
54588
  // src/docs/graph.ts
54544
- import { existsSync as existsSync27, statSync as statSync15 } from "fs";
54589
+ import { existsSync as existsSync27, statSync as statSync16 } from "fs";
54545
54590
  import { dirname as dirname15, posix, relative as relative14, sep as sep5 } from "path";
54546
54591
  function manifestRel(ctx) {
54547
54592
  return relative14(ctx.root, manifestPath(ctx)).split(sep5).join("/");
@@ -54639,7 +54684,7 @@ function localDocPages(ctx, db) {
54639
54684
  }
54640
54685
  function stampOf(ctx, db) {
54641
54686
  const file3 = manifestPath(ctx);
54642
- const st = existsSync27(file3) ? statSync15(file3) : void 0;
54687
+ const st = existsSync27(file3) ? statSync16(file3) : void 0;
54643
54688
  const counts3 = db.graphNodeCounts();
54644
54689
  const syncs = readManifest(ctx).sources.map((s) => syncState(db, s.name)?.at ?? 0).join(",");
54645
54690
  return `${st ? `${Math.floor(st.mtimeMs)}:${st.size}` : "none"}|${counts3.file ?? 0}|${counts3.package ?? 0}|${syncs}`;
@@ -56681,14 +56726,14 @@ function buildHistory(ctx, db, opts = {}) {
56681
56726
  };
56682
56727
  });
56683
56728
  const bySha = new Map(commits.map((c) => [c.sha, c]));
56684
- const resolve14 = (prefix) => {
56729
+ const resolve16 = (prefix) => {
56685
56730
  if (bySha.has(prefix)) return bySha.get(prefix);
56686
56731
  for (const c of commits) if (c.sha.startsWith(prefix)) return c;
56687
56732
  return void 0;
56688
56733
  };
56689
56734
  for (const c of commits) {
56690
56735
  if (c.kind !== "revert" || !c.revertOf) continue;
56691
- const target2 = resolve14(c.revertOf);
56736
+ const target2 = resolve16(c.revertOf);
56692
56737
  if (target2) {
56693
56738
  target2.revertedBy = c.sha;
56694
56739
  c.origins.push(target2.sha);
@@ -57143,15 +57188,15 @@ function safeRegex(q) {
57143
57188
  }
57144
57189
  }
57145
57190
  function commitsForFile(m, path3) {
57146
- const clean2 = path3.replace(/^\.\//, "");
57147
- const from = m.files.get(clean2)?.renamedFrom;
57191
+ const clean3 = path3.replace(/^\.\//, "");
57192
+ const from = m.files.get(clean3)?.renamedFrom;
57148
57193
  return m.commits.filter(
57149
- (c) => c.files.some((f) => f.path === clean2 || from && f.path === from)
57194
+ (c) => c.files.some((f) => f.path === clean3 || from && f.path === from)
57150
57195
  );
57151
57196
  }
57152
57197
  function originsForFile(m, path3) {
57153
- const clean2 = path3.replace(/^\.\//, "");
57154
- const shas = new Set(m.links.filter((l) => l.path === clean2).map((l) => l.origin));
57198
+ const clean3 = path3.replace(/^\.\//, "");
57199
+ const shas = new Set(m.links.filter((l) => l.path === clean3).map((l) => l.origin));
57155
57200
  return m.commits.filter((c) => shas.has(c.sha));
57156
57201
  }
57157
57202
  function resolveCommit(m, ref) {
@@ -60522,7 +60567,7 @@ var init_symbols2 = __esm({
60522
60567
  });
60523
60568
 
60524
60569
  // src/ui/graph-api.ts
60525
- import { readFileSync as readFileSync28, realpathSync as realpathSync6, statSync as statSync16 } from "fs";
60570
+ import { readFileSync as readFileSync28, realpathSync as realpathSync6, statSync as statSync17 } from "fs";
60526
60571
  import { relative as relative16, resolve as resolve12 } from "path";
60527
60572
  function metaOf2(row) {
60528
60573
  if (!row?.meta) return {};
@@ -60583,22 +60628,22 @@ function treePayload(ctx) {
60583
60628
  return { root: ctx.root, files };
60584
60629
  }
60585
60630
  function filePayload(ctx, rel) {
60586
- const clean2 = rel.replace(/^\.\//, "");
60587
- const abs = resolve12(ctx.root, clean2);
60631
+ const clean3 = rel.replace(/^\.\//, "");
60632
+ const abs = resolve12(ctx.root, clean3);
60588
60633
  if (relative16(ctx.root, abs).startsWith("..")) throw new Error("path outside the project");
60589
60634
  let real = abs;
60590
60635
  try {
60591
60636
  real = realpathSync6(abs);
60592
60637
  } catch {
60593
- return { error: "not found", path: clean2 };
60638
+ return { error: "not found", path: clean3 };
60594
60639
  }
60595
60640
  if (relative16(realpathSync6(ctx.root), real).startsWith(".."))
60596
60641
  throw new Error("path outside the project");
60597
- const st = statSync16(real);
60598
- if (!st.isFile()) return { error: "not a file", path: clean2 };
60642
+ const st = statSync17(real);
60643
+ if (!st.isFile()) return { error: "not a file", path: clean3 };
60599
60644
  const db = getStorage(ctx);
60600
- const row = db.fileIndexGet(clean2);
60601
- const symbols = overview(db, clean2).map((n) => ({
60645
+ const row = db.fileIndexGet(clean3);
60646
+ const symbols = overview(db, clean3).map((n) => ({
60602
60647
  id: n.id,
60603
60648
  name: n.name,
60604
60649
  kind: n.kind,
@@ -60607,10 +60652,10 @@ function filePayload(ctx, rel) {
60607
60652
  exported: isExported2(n)
60608
60653
  }));
60609
60654
  if (st.size > MAX_CODE_BYTES)
60610
- return { path: clean2, tooLarge: true, size: st.size, status: row?.status ?? null, symbols };
60655
+ return { path: clean3, tooLarge: true, size: st.size, status: row?.status ?? null, symbols };
60611
60656
  const content = readFileSync28(real, "utf8");
60612
60657
  return {
60613
- path: clean2,
60658
+ path: clean3,
60614
60659
  size: st.size,
60615
60660
  language: row?.language ?? null,
60616
60661
  status: row?.status ?? null,
@@ -60650,25 +60695,25 @@ function symbolPayload(ctx, ref) {
60650
60695
  }
60651
60696
  function fileImpactPayload(ctx, rel) {
60652
60697
  const db = getStorage(ctx);
60653
- const clean2 = rel.replace(/^\.\//, "");
60654
- const r = impact(db, fileId(clean2), { depth: 2, includeGit: true });
60655
- if (!r) return { error: "not in the graph", path: clean2 };
60698
+ const clean3 = rel.replace(/^\.\//, "");
60699
+ const r = impact(db, fileId(clean3), { depth: 2, includeGit: true });
60700
+ if (!r) return { error: "not in the graph", path: clean3 };
60656
60701
  const direct = r.hits.filter(
60657
60702
  (h) => h.hops === 1 && h.id.startsWith("file:") && !h.reasons.every((x) => /^co-changed|^memory/.test(x))
60658
60703
  );
60659
60704
  const tests = r.hits.filter((h) => h.node?.path && TEST_PATH4.test(h.node.path));
60660
60705
  const coChanged = r.hits.filter((h) => h.reasons.some((x) => x.startsWith("co-changed")));
60661
60706
  const memories = r.hits.filter((h) => h.id.startsWith("memory:"));
60662
- const n = neighbors(db, fileId(clean2), 1, ["imports", "reexports"]);
60707
+ const n = neighbors(db, fileId(clean3), 1, ["imports", "reexports"]);
60663
60708
  const nodeMap = new Map((n?.nodes ?? []).map((x) => [x.id, x]));
60664
60709
  const edges = (n?.edges ?? []).slice(0, 80);
60665
- const keep = /* @__PURE__ */ new Set([fileId(clean2)]);
60710
+ const keep = /* @__PURE__ */ new Set([fileId(clean3)]);
60666
60711
  for (const e of edges) {
60667
60712
  keep.add(e.src);
60668
60713
  keep.add(e.dst);
60669
60714
  }
60670
60715
  return {
60671
- path: clean2,
60716
+ path: clean3,
60672
60717
  directDependents: direct.length,
60673
60718
  affected: r.hits.filter((h) => h.id.startsWith("file:")).length,
60674
60719
  tests: tests.length,
@@ -60685,7 +60730,7 @@ function fileImpactPayload(ctx, rel) {
60685
60730
  })),
60686
60731
  graph: {
60687
60732
  nodes: [...keep].map(
60688
- (id) => vis(nodeMap.get(id) ?? db.graphGetNode(id), id, id === fileId(clean2))
60733
+ (id) => vis(nodeMap.get(id) ?? db.graphGetNode(id), id, id === fileId(clean3))
60689
60734
  ),
60690
60735
  edges: edges.map(visEdge)
60691
60736
  }
@@ -61052,16 +61097,16 @@ function historyDiffPayload(ctx, sha, path3) {
61052
61097
  function historyFilePayload(ctx, path3) {
61053
61098
  const m = model(ctx);
61054
61099
  if (!m) return NO_GIT;
61055
- const clean2 = path3.replace(/^\.\//, "");
61056
- const h = m.files.get(clean2);
61057
- const commits = commitsForFile(m, clean2);
61058
- const origins = originsForFile(m, clean2).map((c) => ({
61100
+ const clean3 = path3.replace(/^\.\//, "");
61101
+ const h = m.files.get(clean3);
61102
+ const commits = commitsForFile(m, clean3);
61103
+ const origins = originsForFile(m, clean3).map((c) => ({
61059
61104
  ...commitRow(c),
61060
61105
  fixes: c.blamedBy.map((s) => m.bySha.get(s)).filter((x) => !!x).map((x) => ({ ...commitRow(x), daysLater: Math.round((x.at - c.at) / 864e5) }))
61061
61106
  }));
61062
61107
  return {
61063
61108
  git: true,
61064
- path: clean2,
61109
+ path: clean3,
61065
61110
  file: h ? fileRow(h) : null,
61066
61111
  commits: commits.map(commitRow),
61067
61112
  origins
@@ -61136,14 +61181,14 @@ function historyRiskPayload(ctx, paths2) {
61136
61181
  git: true,
61137
61182
  source: paths2?.length ? "paths" : "working tree",
61138
61183
  files: list2.map((p2) => {
61139
- const clean2 = p2.replace(/^\.\//, "");
61140
- const h = m.files.get(clean2);
61141
- const commits = commitsForFile(m, clean2);
61184
+ const clean3 = p2.replace(/^\.\//, "");
61185
+ const h = m.files.get(clean3);
61186
+ const commits = commitsForFile(m, clean3);
61142
61187
  return {
61143
- path: clean2,
61188
+ path: clean3,
61144
61189
  file: h ? fileRow(h) : null,
61145
61190
  lastFix: commits.find((c) => c.kind === "fix")?.subject ?? null,
61146
- origins: originsForFile(m, clean2).slice(0, 3).map(commitRow)
61191
+ origins: originsForFile(m, clean3).slice(0, 3).map(commitRow)
61147
61192
  };
61148
61193
  })
61149
61194
  };
@@ -61886,9 +61931,996 @@ var init_knowledge_api = __esm({
61886
61931
  }
61887
61932
  });
61888
61933
 
61934
+ // src/swarm/edit.ts
61935
+ import { createHash as createHash13 } from "crypto";
61936
+ import { readFileSync as readFileSync29 } from "fs";
61937
+ import { createRequire as createRequire6 } from "module";
61938
+ function yaml3() {
61939
+ yamlLib3 ??= createRequire6(import.meta.url)("yaml");
61940
+ return yamlLib3;
61941
+ }
61942
+ function hashText2(text3) {
61943
+ return createHash13("sha256").update(text3, "utf8").digest("hex");
61944
+ }
61945
+ function deepEqual(a, b) {
61946
+ if (a === b || typeof a === "number" && typeof b === "number" && Number.isNaN(a) && Number.isNaN(b))
61947
+ return true;
61948
+ if (Array.isArray(a))
61949
+ return Array.isArray(b) && a.length === b.length && a.every((x, i) => deepEqual(x, b[i]));
61950
+ if (isPlain(a) && isPlain(b)) {
61951
+ const ka = Object.keys(a);
61952
+ return ka.length === Object.keys(b).length && ka.every((k) => Object.hasOwn(b, k) && deepEqual(a[k], b[k]));
61953
+ }
61954
+ return false;
61955
+ }
61956
+ function clean2(value, depth = 0) {
61957
+ if (depth > 64) throw new Error("the draft is nested too deeply");
61958
+ if (Array.isArray(value)) return value.map((v) => clean2(v, depth + 1));
61959
+ if (isPlain(value)) {
61960
+ const out = {};
61961
+ for (const [k, v] of Object.entries(value)) {
61962
+ if (v === void 0) continue;
61963
+ Object.defineProperty(out, k, {
61964
+ value: clean2(v, depth + 1),
61965
+ enumerable: true,
61966
+ writable: true,
61967
+ configurable: true
61968
+ });
61969
+ }
61970
+ return out;
61971
+ }
61972
+ if (typeof value === "number" && !Number.isFinite(value)) throw new Error("not a finite number");
61973
+ if (typeof value === "function" || typeof value === "symbol" || typeof value === "bigint")
61974
+ throw new Error(`a ${typeof value} cannot be written to YAML`);
61975
+ return value;
61976
+ }
61977
+ function starterDoc() {
61978
+ const doc = yaml3().parseDocument(STARTER_YAML, { uniqueKeys: true, merge: true });
61979
+ return doc.toJS({ maxAliasCount: 200 });
61980
+ }
61981
+ function readSwarmDoc(root) {
61982
+ const { file: file3, rel, exists } = swarmFile(root);
61983
+ const base = { file: file3, rel };
61984
+ if (!exists)
61985
+ return { ...base, state: "absent", text: null, hash: null, doc: null, issues: [], locked: [] };
61986
+ let bytes;
61987
+ try {
61988
+ bytes = readFileSync29(file3);
61989
+ } catch (err) {
61990
+ return {
61991
+ ...base,
61992
+ state: "invalid",
61993
+ text: null,
61994
+ hash: null,
61995
+ doc: null,
61996
+ issues: [{ path: "", message: `cannot read ${rel}: ${err.message}` }],
61997
+ locked: []
61998
+ };
61999
+ }
62000
+ const text3 = bytes.toString("utf8");
62001
+ const hash2 = createHash13("sha256").update(bytes).digest("hex");
62002
+ const result = parseSwarmYaml(text3, file3, rel);
62003
+ const issues = result.state === "invalid" ? result.issues : [];
62004
+ const state = result.state === "ok" ? "ok" : "invalid";
62005
+ if (text3.length > SWARM_FILE_MAX_CHARS)
62006
+ return { ...base, state, text: text3, hash: hash2, doc: null, issues, locked: [] };
62007
+ const Y = yaml3();
62008
+ const parsed = Y.parseDocument(text3, { uniqueKeys: true, merge: true });
62009
+ let doc = null;
62010
+ let locked = [];
62011
+ if (!parsed.errors.length) {
62012
+ try {
62013
+ const js = parsed.toJS({ maxAliasCount: 200 });
62014
+ if (isPlain(js)) {
62015
+ doc = js;
62016
+ locked = collectLocked(Y, parsed).map((p2) => p2.join("."));
62017
+ }
62018
+ } catch {
62019
+ }
62020
+ }
62021
+ return { ...base, state, text: text3, hash: hash2, doc, issues, locked };
62022
+ }
62023
+ function checkSwarmText(text3, opts = {}) {
62024
+ const rel = opts.rel ?? SWARM_FILES[0];
62025
+ const result = parseSwarmYaml(text3, opts.file ?? rel, rel);
62026
+ const parsed = result.state === "ok" ? result.config.enforce && result.config.enabled : result.state === "invalid" ? result.enforce : void 0;
62027
+ return { result, enforceOk: parsed === void 0 || parsed === enforceIntent(text3) };
62028
+ }
62029
+ function mergedKeys(Y, doc, value) {
62030
+ const sources = Y.isSeq(value) ? value.items : [value];
62031
+ const keys = [];
62032
+ for (const src of sources) {
62033
+ const m = Y.isAlias(src) ? src.resolve(doc) : src;
62034
+ if (Y.isMap(m)) {
62035
+ for (const p2 of m.items) if (!isMergePair(p2)) keys.push(keyText(p2));
62036
+ }
62037
+ }
62038
+ return keys;
62039
+ }
62040
+ function collectLocked(Y, doc) {
62041
+ const out = [];
62042
+ const walk4 = (node2, path3) => {
62043
+ if (Y.isAlias(node2)) {
62044
+ out.push(path3);
62045
+ return;
62046
+ }
62047
+ if (!Y.isNode(node2)) return;
62048
+ if (node2.anchor) out.push(path3);
62049
+ if (Y.isMap(node2)) {
62050
+ const explicit = new Set(node2.items.filter((p2) => !isMergePair(p2)).map(keyText));
62051
+ for (const p2 of node2.items) {
62052
+ if (isMergePair(p2)) {
62053
+ out.push([...path3, "<<"]);
62054
+ for (const k2 of mergedKeys(Y, doc, p2.value)) if (!explicit.has(k2)) out.push([...path3, k2]);
62055
+ continue;
62056
+ }
62057
+ const k = keyText(p2);
62058
+ const child = [...path3, k];
62059
+ const hooks = path3.length === 4 && path3[0] === "swarms" && path3[2] === "agents";
62060
+ if (path3.length === 0 && k.startsWith("x-") || hooks && k === "hooks") {
62061
+ out.push(child);
62062
+ continue;
62063
+ }
62064
+ walk4(p2.value, child);
62065
+ }
62066
+ } else if (Y.isSeq(node2)) {
62067
+ node2.items.forEach((it, i) => {
62068
+ walk4(it, [...path3, String(i)]);
62069
+ });
62070
+ }
62071
+ };
62072
+ walk4(doc.contents, []);
62073
+ return out;
62074
+ }
62075
+ function orderOf(path3) {
62076
+ const last = path3[path3.length - 1];
62077
+ if (path3.length === 0) return TOP_ORDER;
62078
+ if (last === "context") return CONTEXT_ORDER;
62079
+ if (last === "model_range") return RANGE_ORDER;
62080
+ if (path3.length === 1 && path3[0] === "defaults") return DEFAULTS_ORDER;
62081
+ if (path3.length === 2 && path3[0] === "swarms") return SWARM_ORDER;
62082
+ if (path3.length === 4 && path3[0] === "swarms" && path3[2] === "agents") return ROLE_ORDER;
62083
+ return void 0;
62084
+ }
62085
+ function prefersFlow(items2) {
62086
+ return items2.length <= 6 && items2.every(
62087
+ (v) => typeof v === "number" || typeof v === "boolean" || typeof v === "string" && v.length <= 40 && !v.includes("\n")
62088
+ );
62089
+ }
62090
+ function renderText(baseText, draft, renames) {
62091
+ const Y = yaml3();
62092
+ const base = baseText ?? STARTER_YAML;
62093
+ const bom = base.startsWith("\uFEFF");
62094
+ const body = bom ? base.slice(1) : base;
62095
+ const crlfCount = (body.match(/\r\n/g) ?? []).length;
62096
+ const crlf = crlfCount > 0 && crlfCount * 2 >= (body.match(/\n/g) ?? []).length;
62097
+ const doc = Y.parseDocument(body, { uniqueKeys: true, merge: true });
62098
+ if (doc.errors.length)
62099
+ throw new RenderFail(
62100
+ "yaml",
62101
+ `the file is not valid YAML (${doc.errors[0]?.message.split("\n")[0] ?? "parse error"}); edit it in raw mode`
62102
+ );
62103
+ if (!Y.isMap(doc.contents))
62104
+ throw new RenderFail("yaml", "the file is not a mapping; edit it in raw mode");
62105
+ let target2;
62106
+ try {
62107
+ target2 = clean2(draft);
62108
+ } catch (err) {
62109
+ throw new RenderFail("mismatch", err.message);
62110
+ }
62111
+ if (!isPlain(target2)) throw new RenderFail("mismatch", "the draft is not a mapping");
62112
+ for (const r of renames) applyRename(Y, doc, r);
62113
+ let before;
62114
+ try {
62115
+ before = doc.toJS({ maxAliasCount: 200 });
62116
+ } catch (err) {
62117
+ throw new RenderFail("yaml", `the file cannot be read: ${err.message}`);
62118
+ }
62119
+ restoreNonFinite(before, target2);
62120
+ if (!renames.length && deepEqual(before, target2))
62121
+ return { text: base, changed: baseText === null };
62122
+ const locked = collectLocked(Y, doc);
62123
+ const ctx = { Y, doc, locked };
62124
+ let text3 = renames.length ? null : spliceScalars(ctx, body, before, target2);
62125
+ if (text3 === null) {
62126
+ applyDiff(ctx, [], before, target2);
62127
+ text3 = doc.toString({ lineWidth: 0, flowCollectionPadding: false });
62128
+ if (!text3.endsWith("\n")) text3 += "\n";
62129
+ if (crlf) text3 = text3.replace(/\r?\n/g, "\r\n");
62130
+ }
62131
+ if (bom) text3 = `\uFEFF${text3}`;
62132
+ const again = Y.parseDocument(bom ? text3.slice(1) : text3, { uniqueKeys: true, merge: true });
62133
+ let after;
62134
+ try {
62135
+ after = again.errors.length ? void 0 : again.toJS({ maxAliasCount: 200 });
62136
+ } catch {
62137
+ after = void 0;
62138
+ }
62139
+ if (!deepEqual(after, target2))
62140
+ throw new RenderFail(
62141
+ "mismatch",
62142
+ "the edit could not be written without changing something else (an alias or merge key in the file?); edit it in raw mode",
62143
+ locked.map((p2) => p2.join("."))
62144
+ );
62145
+ return { text: text3, changed: text3 !== baseText };
62146
+ }
62147
+ function renderSwarmDoc(baseText, draft, renames = [], opts = {}) {
62148
+ let rendered;
62149
+ try {
62150
+ rendered = renderText(baseText, draft, renames);
62151
+ } catch (err) {
62152
+ if (err instanceof RenderFail)
62153
+ return {
62154
+ ok: false,
62155
+ code: err.code,
62156
+ error: err.message,
62157
+ ...err.locked ? { locked: err.locked } : {}
62158
+ };
62159
+ throw err;
62160
+ }
62161
+ const { result, enforceOk } = checkSwarmText(rendered.text, opts);
62162
+ if (!enforceOk)
62163
+ return {
62164
+ ok: false,
62165
+ code: "enforce",
62166
+ error: "the enforce setting is not on a plain top-level `enforce:` line, so the shell hook would read it differently; edit it in raw mode"
62167
+ };
62168
+ return { ok: true, text: rendered.text, result, changed: rendered.changed };
62169
+ }
62170
+ function spliceScalars(ctx, body, oldV, newV) {
62171
+ const { Y, doc } = ctx;
62172
+ const edits = [];
62173
+ const walk4 = (path3, a, b) => {
62174
+ if (deepEqual(a, b)) return true;
62175
+ if (isPlain(a) && isPlain(b)) {
62176
+ const ka = Object.keys(a);
62177
+ if (ka.length !== Object.keys(b).length) return false;
62178
+ return ka.every((k) => Object.hasOwn(b, k) && walk4([...path3, k], a[k], b[k]));
62179
+ }
62180
+ if (Array.isArray(a) && Array.isArray(b)) {
62181
+ if (a.length !== b.length) return false;
62182
+ return a.every((x, i) => {
62183
+ const y = b[i];
62184
+ if (!deepEqual(x, y) && isScalarValue(x) && isScalarValue(y)) {
62185
+ if (b.some((v) => deepEqual(v, x)) || a.some((v) => deepEqual(v, y))) return false;
62186
+ }
62187
+ return walk4([...path3, String(i)], x, y);
62188
+ });
62189
+ }
62190
+ if (!isScalarValue(a) || !isScalarValue(b) || a === null || b === null) return false;
62191
+ if (lockedAt(ctx, path3)) return false;
62192
+ const node2 = nodeAt(Y, doc, path3);
62193
+ if (!Y.isScalar(node2) || !node2.range || node2.anchor) return false;
62194
+ if (node2.type === "BLOCK_LITERAL" || node2.type === "BLOCK_FOLDED") return false;
62195
+ if (typeof b === "string" && (b.includes("\n") || b.includes("\r"))) return false;
62196
+ if (typeof b === "string" && node2.type === "PLAIN" && /[,[\]{}]/.test(b)) return false;
62197
+ const [start, end] = node2.range;
62198
+ if (end <= start) return false;
62199
+ const fresh = new Y.Scalar(b);
62200
+ if (typeof b === "string" && node2.type && node2.type !== "PLAIN") fresh.type = node2.type;
62201
+ const text3 = new Y.Document(fresh).toString({ lineWidth: 0 }).replace(/\r?\n$/, "");
62202
+ if (text3.includes("\n")) return false;
62203
+ edits.push({ start, end, text: text3 });
62204
+ return true;
62205
+ };
62206
+ if (!walk4([], oldV, newV) || !edits.length) return null;
62207
+ let out = body;
62208
+ for (const e of edits.sort((x, y) => y.start - x.start))
62209
+ out = out.slice(0, e.start) + e.text + out.slice(e.end);
62210
+ return out;
62211
+ }
62212
+ function lockedFail(ctx, path3) {
62213
+ return new RenderFail(
62214
+ "locked",
62215
+ `${path3.join(".") || "the file"} is read-only here (an alias, anchor, merge key, x-* key or hooks); edit it in raw mode`,
62216
+ ctx.locked.map((p2) => p2.join("."))
62217
+ );
62218
+ }
62219
+ function findPair(Y, map2, name) {
62220
+ for (let i = map2.items.length - 1; i >= 0; i--) {
62221
+ const p2 = map2.items[i];
62222
+ if (Y.isScalar(p2.key) && String(p2.key.value) === name) return p2;
62223
+ }
62224
+ return void 0;
62225
+ }
62226
+ function nodeAt(Y, doc, path3) {
62227
+ let node2 = doc.contents;
62228
+ for (const seg of path3) {
62229
+ if (Y.isMap(node2)) {
62230
+ const pair = findPair(Y, node2, seg);
62231
+ if (!pair) return void 0;
62232
+ node2 = pair.value;
62233
+ } else if (Y.isSeq(node2)) {
62234
+ node2 = node2.items[Number(seg)];
62235
+ } else return void 0;
62236
+ }
62237
+ return node2;
62238
+ }
62239
+ function deleteAt(ctx, path3) {
62240
+ const { Y, doc } = ctx;
62241
+ const parent = nodeAt(Y, doc, path3.slice(0, -1));
62242
+ const last = path3[path3.length - 1];
62243
+ if (Y.isMap(parent)) {
62244
+ const pair = findPair(Y, parent, last);
62245
+ if (pair) parent.items.splice(parent.items.indexOf(pair), 1);
62246
+ } else if (Y.isSeq(parent)) parent.items.splice(Number(last), 1);
62247
+ }
62248
+ function setAt(ctx, path3, value) {
62249
+ const { Y, doc } = ctx;
62250
+ const parent = nodeAt(Y, doc, path3.slice(0, -1));
62251
+ const last = path3[path3.length - 1];
62252
+ if (Y.isMap(parent)) {
62253
+ const pair = findPair(Y, parent, last);
62254
+ if (pair) pair.value = value;
62255
+ else parent.items.push(new Y.Pair(new Y.Scalar(last), value));
62256
+ } else if (Y.isSeq(parent)) parent.items[Number(last)] = value;
62257
+ else throw new RenderFail("mismatch", `${path3.join(".")} is not inside a mapping or list`);
62258
+ }
62259
+ function restoreNonFinite(before, target2) {
62260
+ if (Array.isArray(before) && Array.isArray(target2)) {
62261
+ before.forEach((b, i) => {
62262
+ if (typeof b === "number" && !Number.isFinite(b) && target2[i] === null) target2[i] = b;
62263
+ else restoreNonFinite(b, target2[i]);
62264
+ });
62265
+ } else if (isPlain(before) && isPlain(target2)) {
62266
+ for (const [k, b] of Object.entries(before)) {
62267
+ if (!Object.hasOwn(target2, k)) continue;
62268
+ if (typeof b === "number" && !Number.isFinite(b) && target2[k] === null) target2[k] = b;
62269
+ else restoreNonFinite(b, target2[k]);
62270
+ }
62271
+ }
62272
+ }
62273
+ function applyRename(Y, doc, r) {
62274
+ const fail3 = (msg) => new RenderFail("rename", msg);
62275
+ const swarms = doc.get("swarms", true);
62276
+ if (!Y.isMap(swarms)) throw fail3("the file has no swarms to rename");
62277
+ const rename2 = (map2, from, to, what) => {
62278
+ if (!to) throw fail3(`the new ${what} name is empty`);
62279
+ const pair = findPair(Y, map2, from);
62280
+ if (!pair || !Y.isScalar(pair.key)) throw fail3(`there is no ${what} "${from}" to rename`);
62281
+ if (from === to) return;
62282
+ if (findPair(Y, map2, to)) throw fail3(`a ${what} named "${to}" already exists`);
62283
+ pair.key.value = to;
62284
+ };
62285
+ if (r.role === void 0) {
62286
+ rename2(swarms, r.swarm, r.to, "swarm");
62287
+ return;
62288
+ }
62289
+ const swarm = findPair(Y, swarms, r.swarm)?.value;
62290
+ const agents = Y.isMap(swarm) ? findPair(Y, swarm, "agents")?.value : void 0;
62291
+ if (!Y.isMap(swarm) || !Y.isMap(agents)) throw fail3(`there is no swarm "${r.swarm}" with agents`);
62292
+ rename2(agents, r.role, r.to, "agent");
62293
+ const stages = findPair(Y, swarm, "stages")?.value;
62294
+ if (Y.isSeq(stages)) {
62295
+ for (const it of stages.items)
62296
+ if (Y.isScalar(it) && it.value === r.role) it.value = r.to;
62297
+ }
62298
+ }
62299
+ function build(ctx, value, path3) {
62300
+ const { Y } = ctx;
62301
+ if (Array.isArray(value)) {
62302
+ const seq = new Y.YAMLSeq();
62303
+ seq.items = value.map((v, i) => build(ctx, v, [...path3, String(i)]));
62304
+ if (prefersFlow(value)) seq.flow = true;
62305
+ return seq;
62306
+ }
62307
+ if (isPlain(value)) {
62308
+ const map2 = new Y.YAMLMap();
62309
+ const order = orderOf(path3) ?? [];
62310
+ const keys = Object.keys(value);
62311
+ const rank2 = (k) => {
62312
+ const i = order.indexOf(k);
62313
+ return i < 0 ? order.length : i;
62314
+ };
62315
+ keys.sort((a, b) => rank2(a) - rank2(b));
62316
+ for (const k of keys)
62317
+ map2.items.push(new Y.Pair(new Y.Scalar(k), build(ctx, value[k], [...path3, k])));
62318
+ return map2;
62319
+ }
62320
+ const s = new Y.Scalar(value);
62321
+ if (typeof value === "string" && value.includes("\n")) s.type = "BLOCK_LITERAL";
62322
+ return s;
62323
+ }
62324
+ function inherit(from, to) {
62325
+ if (!from || !to || typeof from !== "object" || typeof to !== "object") return;
62326
+ const f = from;
62327
+ const t = to;
62328
+ if (f.comment !== void 0) t.comment = f.comment;
62329
+ if (f.commentBefore !== void 0) t.commentBefore = f.commentBefore;
62330
+ if (f.spaceBefore !== void 0) t.spaceBefore = f.spaceBefore;
62331
+ }
62332
+ function insertPair(ctx, map2, path3, pair) {
62333
+ const order = orderOf(path3);
62334
+ const rank2 = (k) => order?.indexOf(k) ?? -1;
62335
+ const at = rank2(keyText(pair));
62336
+ let index = map2.items.length;
62337
+ if (order && at >= 0) {
62338
+ const i = map2.items.findIndex((p2) => !isMergePair(p2) && rank2(keyText(p2)) > at);
62339
+ if (i >= 0) index = i;
62340
+ }
62341
+ if (path3.length === 1 && path3[0] === "swarms" && map2.items.length)
62342
+ pair.key.spaceBefore = true;
62343
+ map2.items.splice(index, 0, pair);
62344
+ void ctx;
62345
+ }
62346
+ function applyDiff(ctx, path3, oldV, newV) {
62347
+ const { Y, doc } = ctx;
62348
+ if (deepEqual(oldV, newV)) return;
62349
+ if (lockedAt(ctx, path3)) throw lockedFail(ctx, path3);
62350
+ if (isPlain(oldV) && isPlain(newV)) {
62351
+ const node3 = path3.length ? nodeAt(Y, doc, path3) : doc.contents;
62352
+ if (!Y.isMap(node3))
62353
+ throw new RenderFail("mismatch", `${path3.join(".") || "the file"} is not a plain mapping`);
62354
+ for (const k of Object.keys(oldV)) {
62355
+ if (Object.hasOwn(newV, k)) continue;
62356
+ const child = [...path3, k];
62357
+ if (lockedAt(ctx, child) || lockedBelow(ctx, child)) throw lockedFail(ctx, child);
62358
+ deleteAt(ctx, child);
62359
+ }
62360
+ for (const k of Object.keys(newV)) {
62361
+ const child = [...path3, k];
62362
+ if (Object.hasOwn(oldV, k)) {
62363
+ applyDiff(ctx, child, oldV[k], newV[k]);
62364
+ continue;
62365
+ }
62366
+ if (k === "<<" || path3.length === 0 && k.startsWith("x-")) throw lockedFail(ctx, child);
62367
+ if (!node3.items.length && node3.flow && !["context", "model_range"].includes(k))
62368
+ node3.flow = false;
62369
+ const pair = new Y.Pair(new Y.Scalar(k), build(ctx, newV[k], child));
62370
+ insertPair(ctx, node3, path3, pair);
62371
+ }
62372
+ return;
62373
+ }
62374
+ if (lockedBelow(ctx, path3)) throw lockedFail(ctx, path3);
62375
+ const old = nodeAt(Y, doc, path3);
62376
+ if (Array.isArray(newV)) {
62377
+ const oldSeq = Y.isSeq(old) ? old : void 0;
62378
+ const pool = oldSeq ? [...oldSeq.items] : [];
62379
+ const seq = new Y.YAMLSeq();
62380
+ seq.items = newV.map((v, i) => {
62381
+ if (isScalarValue(v)) {
62382
+ const j = pool.findIndex((it) => Y.isScalar(it) && it.value === v);
62383
+ if (j >= 0) return pool.splice(j, 1)[0];
62384
+ }
62385
+ return build(ctx, v, [...path3, String(i)]);
62386
+ });
62387
+ seq.flow = oldSeq ? oldSeq.flow === true : prefersFlow(newV);
62388
+ inherit(oldSeq, seq);
62389
+ setAt(ctx, path3, seq);
62390
+ return;
62391
+ }
62392
+ if (isScalarValue(newV) && Y.isScalar(old)) {
62393
+ const node3 = old;
62394
+ node3.value = newV;
62395
+ if (typeof newV !== "string") delete node3.type;
62396
+ else if (newV.includes("\n") && node3.type !== "BLOCK_LITERAL" && node3.type !== "BLOCK_FOLDED")
62397
+ node3.type = "BLOCK_LITERAL";
62398
+ return;
62399
+ }
62400
+ const node2 = build(ctx, newV, path3);
62401
+ inherit(old, node2);
62402
+ setAt(ctx, path3, node2);
62403
+ }
62404
+ function docWarnings(doc, activeRuns = [], prev) {
62405
+ const out = [];
62406
+ const swarms = isPlain(doc.swarms) ? doc.swarms : {};
62407
+ for (const [name, s] of Object.entries(swarms)) {
62408
+ if (!isPlain(s)) continue;
62409
+ const agents = isPlain(s.agents) ? s.agents : {};
62410
+ const roles = Object.entries(agents).filter(([, r]) => isPlain(r));
62411
+ const max = typeof s.max_agents === "number" ? s.max_agents : DEFAULT_MAX_AGENTS;
62412
+ const total = roles.reduce((n, [, r]) => n + (typeof r.count === "number" ? r.count : 1), 0);
62413
+ if (total > max)
62414
+ out.push({
62415
+ path: `swarms.${name}.max_agents`,
62416
+ kind: "capacity",
62417
+ message: `${total} workers are configured (the counts add up) but max_agents is ${max}, so at most ${max} run at once`
62418
+ });
62419
+ const stages = Array.isArray(s.stages) ? s.stages : [];
62420
+ if (s.topology === "pipeline" && stages.length) {
62421
+ const missing = roles.map(([r]) => r).filter((r) => !stages.includes(r));
62422
+ if (missing.length)
62423
+ out.push({
62424
+ path: `swarms.${name}.stages`,
62425
+ kind: "stages",
62426
+ message: `${missing.join(", ")} ${missing.length === 1 ? "is" : "are"} not listed in stages`
62427
+ });
62428
+ }
62429
+ for (const [role, r] of roles) {
62430
+ const prompt = typeof r.prompt === "string" ? r.prompt.trim() : "";
62431
+ if (!prompt)
62432
+ out.push({
62433
+ path: `swarms.${name}.agents.${role}.prompt`,
62434
+ kind: "prompt",
62435
+ message: "has no prompt; add one to tell the worker how to work"
62436
+ });
62437
+ else if (prompt.length > PROMPT_MAX * 0.8)
62438
+ out.push({
62439
+ path: `swarms.${name}.agents.${role}.prompt`,
62440
+ kind: "prompt",
62441
+ message: `is ${prompt.length} characters, close to the ${PROMPT_MAX.toLocaleString("en-US")} limit`
62442
+ });
62443
+ }
62444
+ }
62445
+ const prevSwarms = prev && isPlain(prev.swarms) ? prev.swarms : {};
62446
+ for (const run3 of activeRuns) {
62447
+ const now = swarms[run3.swarm];
62448
+ if (!isPlain(now)) {
62449
+ out.push({
62450
+ path: `swarms.${run3.swarm}`,
62451
+ kind: "active-run",
62452
+ message: `run ${run3.id} is using the swarm "${run3.swarm}", which this draft removes or renames`
62453
+ });
62454
+ continue;
62455
+ }
62456
+ const before = prevSwarms[run3.swarm];
62457
+ const was = isPlain(before) && isPlain(before.agents) ? Object.keys(before.agents) : [];
62458
+ const has = isPlain(now.agents) ? now.agents : {};
62459
+ for (const role of was)
62460
+ if (!Object.hasOwn(has, role))
62461
+ out.push({
62462
+ path: `swarms.${run3.swarm}.agents.${role}`,
62463
+ kind: "active-run",
62464
+ message: `run ${run3.id} uses the role "${role}", which this draft removes or renames`
62465
+ });
62466
+ }
62467
+ const enforcedBefore = !!prev && prev.enforce === true && prev.enabled !== false;
62468
+ if (doc.enforce === true && doc.enabled !== false && !enforcedBefore)
62469
+ out.push({
62470
+ path: "enforce",
62471
+ kind: "enforce",
62472
+ message: "with enforce on, only this file's swarm workers may run as subagents, and only during a run of their swarm (allowed_agents lists the exceptions)"
62473
+ });
62474
+ if (doc.enabled === false && prev !== null && !(prev && prev.enabled === false))
62475
+ out.push({
62476
+ path: "enabled",
62477
+ kind: "disable",
62478
+ message: "disabling swarms removes the generated agent files on the next sync"
62479
+ });
62480
+ return out;
62481
+ }
62482
+ function lineDiff(prev, next) {
62483
+ const a = prev.split(/\r?\n/);
62484
+ const b = next.split(/\r?\n/);
62485
+ let head2 = 0;
62486
+ while (head2 < a.length && head2 < b.length && a[head2] === b[head2]) head2++;
62487
+ let ea = a.length;
62488
+ let eb = b.length;
62489
+ while (ea > head2 && eb > head2 && a[ea - 1] === b[eb - 1]) {
62490
+ ea--;
62491
+ eb--;
62492
+ }
62493
+ const n = ea - head2;
62494
+ const m = eb - head2;
62495
+ const range = (from, to) => Array.from({ length: to - from }, (_, i2) => from + i2 + 1);
62496
+ if (!n && !m) return { changed: [], removed: 0 };
62497
+ if (!n || !m || a.length > DIFF_MAX_LINES || b.length > DIFF_MAX_LINES || n * m > DIFF_MAX_CELLS)
62498
+ return { changed: range(head2, eb), removed: n };
62499
+ const w = m + 1;
62500
+ const lcs = new Uint16Array((n + 1) * w);
62501
+ for (let i2 = n - 1; i2 >= 0; i2--)
62502
+ for (let j2 = m - 1; j2 >= 0; j2--)
62503
+ lcs[i2 * w + j2] = a[head2 + i2] === b[head2 + j2] ? lcs[(i2 + 1) * w + j2 + 1] + 1 : Math.max(lcs[(i2 + 1) * w + j2], lcs[i2 * w + j2 + 1]);
62504
+ const kept = /* @__PURE__ */ new Set();
62505
+ let i = 0;
62506
+ let j = 0;
62507
+ while (i < n && j < m) {
62508
+ if (a[head2 + i] === b[head2 + j]) {
62509
+ kept.add(j);
62510
+ i++;
62511
+ j++;
62512
+ } else if (lcs[(i + 1) * w + j] >= lcs[i * w + j + 1]) i++;
62513
+ else j++;
62514
+ }
62515
+ const changed = [];
62516
+ for (let k = 0; k < m; k++) if (!kept.has(k)) changed.push(head2 + k + 1);
62517
+ return { changed, removed: n - kept.size };
62518
+ }
62519
+ var yamlLib3, SWARM_FILE_MAX_CHARS, DEFAULT_MAX_AGENTS, PROMPT_MAX, isPlain, keyText, isMergePair, RenderFail, TOP_ORDER, DEFAULTS_ORDER, SWARM_ORDER, ROLE_ORDER, CONTEXT_ORDER, RANGE_ORDER, isScalarValue, startsWithPath, lockedAt, lockedBelow, DIFF_MAX_LINES, DIFF_MAX_CELLS;
62520
+ var init_edit = __esm({
62521
+ "src/swarm/edit.ts"() {
62522
+ "use strict";
62523
+ init_esm_shims();
62524
+ init_config2();
62525
+ SWARM_FILE_MAX_CHARS = 256e3;
62526
+ DEFAULT_MAX_AGENTS = 4;
62527
+ PROMPT_MAX = 2e4;
62528
+ isPlain = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
62529
+ keyText = (pair) => {
62530
+ const k = pair.key;
62531
+ return k && typeof k === "object" && "value" in k ? String(k.value) : String(pair.key);
62532
+ };
62533
+ isMergePair = (pair) => {
62534
+ const v = pair.key?.value;
62535
+ return v === "<<" || typeof v === "symbol" && v.description === "<<";
62536
+ };
62537
+ RenderFail = class extends Error {
62538
+ constructor(code2, message, locked) {
62539
+ super(message);
62540
+ this.code = code2;
62541
+ this.locked = locked;
62542
+ }
62543
+ code;
62544
+ locked;
62545
+ };
62546
+ TOP_ORDER = ["version", "enabled", "sync", "enforce", "allowed_agents", "defaults", "swarms"];
62547
+ DEFAULTS_ORDER = ["model", "model_range", "skills", "context"];
62548
+ SWARM_ORDER = [
62549
+ "description",
62550
+ "topology",
62551
+ "max_agents",
62552
+ "stages",
62553
+ "instructions",
62554
+ "context",
62555
+ "agents"
62556
+ ];
62557
+ ROLE_ORDER = [
62558
+ "description",
62559
+ "model",
62560
+ "model_range",
62561
+ "effort",
62562
+ "color",
62563
+ "count",
62564
+ "max_turns",
62565
+ "isolation",
62566
+ "tools",
62567
+ "disallowed_tools",
62568
+ "skills",
62569
+ "scope",
62570
+ "memory_tags",
62571
+ "context",
62572
+ "hooks",
62573
+ "prompt"
62574
+ ];
62575
+ CONTEXT_ORDER = ["memories", "docs", "history", "structure", "insights", "max_chars"];
62576
+ RANGE_ORDER = ["floor", "ceiling"];
62577
+ isScalarValue = (v) => v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean";
62578
+ startsWithPath = (p2, prefix) => prefix.every((k, i) => p2[i] === k);
62579
+ lockedAt = (ctx, path3) => ctx.locked.some((l) => l.length <= path3.length && startsWithPath(path3, l));
62580
+ lockedBelow = (ctx, path3) => ctx.locked.some((l) => l.length > path3.length && startsWithPath(l, path3));
62581
+ DIFF_MAX_LINES = 3e3;
62582
+ DIFF_MAX_CELLS = 4e6;
62583
+ }
62584
+ });
62585
+
62586
+ // src/swarm/save.ts
62587
+ import { createHash as createHash14, randomBytes as randomBytes4 } from "crypto";
62588
+ import {
62589
+ closeSync as closeSync3,
62590
+ fsyncSync,
62591
+ linkSync as linkSync2,
62592
+ lstatSync as lstatSync3,
62593
+ mkdirSync as mkdirSync16,
62594
+ openSync as openSync3,
62595
+ readFileSync as readFileSync30,
62596
+ realpathSync as realpathSync7,
62597
+ renameSync as renameSync7,
62598
+ rmSync as rmSync8,
62599
+ statSync as statSync18,
62600
+ writeSync
62601
+ } from "fs";
62602
+ import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute6, relative as relative17, resolve as resolve13, sep as sep6 } from "path";
62603
+ function within2(child, parent) {
62604
+ const rel = relative17(parent, child);
62605
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep6}`) && !isAbsolute6(rel);
62606
+ }
62607
+ function locationProblem2(file3, root) {
62608
+ if (file3 === root || !within2(file3, root)) return "it is outside the project";
62609
+ let realRoot;
62610
+ try {
62611
+ realRoot = realpathSync7.native(root);
62612
+ } catch (err) {
62613
+ return `the project root could not be resolved (${err.code ?? err.message})`;
62614
+ }
62615
+ try {
62616
+ let dir = dirname16(file3);
62617
+ while (!lstatSync3(dir, { throwIfNoEntry: false }) && within2(dirname16(dir), root) && dir !== root)
62618
+ dir = dirname16(dir);
62619
+ if (!within2(realpathSync7.native(dir), realRoot))
62620
+ return "its folder resolves outside the project (a symlink)";
62621
+ const lst = lstatSync3(file3, { throwIfNoEntry: false });
62622
+ if (lst?.isSymbolicLink()) return "it is a symlink, and writes replace only a regular file";
62623
+ if (lst && !lst.isFile()) return "it is not a regular file";
62624
+ return void 0;
62625
+ } catch (err) {
62626
+ return `its location could not be resolved (${err.code ?? err.message})`;
62627
+ }
62628
+ }
62629
+ function writeAll(fd, buf) {
62630
+ let off = 0;
62631
+ while (off < buf.length) {
62632
+ const n = writeSync(fd, buf, off, buf.length - off);
62633
+ if (n <= 0) throw new Error("short write: the disk did not take the whole swarm file");
62634
+ off += n;
62635
+ }
62636
+ }
62637
+ function readCurrent(file3) {
62638
+ try {
62639
+ const bytes = readFileSync30(file3);
62640
+ return {
62641
+ text: bytes.toString("utf8"),
62642
+ hash: createHash14("sha256").update(bytes).digest("hex")
62643
+ };
62644
+ } catch (err) {
62645
+ if (err.code === "ENOENT") return null;
62646
+ throw err;
62647
+ }
62648
+ }
62649
+ function saveSwarmFile(root, text3, baseHash) {
62650
+ const projectRoot = resolve13(root);
62651
+ if (text3.length > SWARM_FILE_MAX_CHARS)
62652
+ throw new SwarmSaveInvalid([
62653
+ { path: "", message: `the file is larger than ${SWARM_FILE_MAX_CHARS / 1e3} KB` }
62654
+ ]);
62655
+ const { result, enforceOk } = checkSwarmText(text3);
62656
+ if (result.state === "invalid") throw new SwarmSaveInvalid(result.issues);
62657
+ if (!enforceOk)
62658
+ throw new SwarmSaveInvalid([
62659
+ { path: "enforce", message: "enforce must be on a plain top-level `enforce:` line" }
62660
+ ]);
62661
+ const { file: file3, rel } = swarmFile(projectRoot);
62662
+ const problem = locationProblem2(file3, projectRoot);
62663
+ if (problem) throw new SwarmSaveRefused(problem);
62664
+ const current = readCurrent(file3);
62665
+ if (baseHash === null) {
62666
+ if (current) throw new SwarmSaveConflict("exists", current);
62667
+ } else if (!current) {
62668
+ throw new SwarmSaveConflict("missing", null);
62669
+ } else if (current.hash !== baseHash) {
62670
+ throw new SwarmSaveConflict("changed", current);
62671
+ }
62672
+ const hash2 = hashText2(text3);
62673
+ if (current && current.hash === hash2) return { rel, hash: hash2, created: false };
62674
+ if (!current) mkdirSync16(dirname16(file3), { recursive: true });
62675
+ const again = locationProblem2(file3, projectRoot);
62676
+ if (again) throw new SwarmSaveRefused(again);
62677
+ const mode = current ? statSync18(file3).mode & 511 : 420;
62678
+ const tmp = resolve13(
62679
+ dirname16(file3),
62680
+ `.${basename8(file3)}.${process.pid}.${randomBytes4(4).toString("hex")}.tmp`
62681
+ );
62682
+ try {
62683
+ const fd = openSync3(tmp, "wx", mode);
62684
+ try {
62685
+ writeAll(fd, Buffer.from(text3, "utf8"));
62686
+ fsyncSync(fd);
62687
+ } finally {
62688
+ closeSync3(fd);
62689
+ }
62690
+ const now = readCurrent(file3);
62691
+ if (current === null) {
62692
+ if (now) throw new SwarmSaveConflict("exists", now);
62693
+ try {
62694
+ linkSync2(tmp, file3);
62695
+ } catch (err) {
62696
+ const code2 = err.code;
62697
+ if (code2 === "EEXIST") throw new SwarmSaveConflict("exists", readCurrent(file3));
62698
+ renameSync7(tmp, file3);
62699
+ }
62700
+ } else {
62701
+ if (!now) throw new SwarmSaveConflict("missing", null);
62702
+ if (now.hash !== baseHash) throw new SwarmSaveConflict("changed", now);
62703
+ renameSync7(tmp, file3);
62704
+ }
62705
+ } finally {
62706
+ rmSync8(tmp, { force: true });
62707
+ }
62708
+ return { rel, hash: hash2, created: current === null };
62709
+ }
62710
+ var SwarmSaveRefused, SwarmSaveConflict, SwarmSaveInvalid;
62711
+ var init_save = __esm({
62712
+ "src/swarm/save.ts"() {
62713
+ "use strict";
62714
+ init_esm_shims();
62715
+ init_config2();
62716
+ init_edit();
62717
+ SwarmSaveRefused = class extends Error {
62718
+ constructor(reason) {
62719
+ super(`refusing to write the swarm file: ${reason}`);
62720
+ this.reason = reason;
62721
+ this.name = "SwarmSaveRefused";
62722
+ }
62723
+ reason;
62724
+ };
62725
+ SwarmSaveConflict = class extends Error {
62726
+ constructor(reason, current) {
62727
+ super(
62728
+ reason === "exists" ? "a swarm file already exists" : reason === "missing" ? "the swarm file was removed since it was loaded" : "the swarm file changed since it was loaded"
62729
+ );
62730
+ this.reason = reason;
62731
+ this.current = current;
62732
+ this.name = "SwarmSaveConflict";
62733
+ }
62734
+ reason;
62735
+ current;
62736
+ };
62737
+ SwarmSaveInvalid = class extends Error {
62738
+ constructor(issues) {
62739
+ super(`the swarm file is not valid: ${issues[0]?.message ?? "see the issues"}`);
62740
+ this.issues = issues;
62741
+ this.name = "SwarmSaveInvalid";
62742
+ }
62743
+ issues;
62744
+ };
62745
+ }
62746
+ });
62747
+
62748
+ // src/swarm/templates.ts
62749
+ var READ_ONLY, SWARM_TEMPLATES;
62750
+ var init_templates = __esm({
62751
+ "src/swarm/templates.ts"() {
62752
+ "use strict";
62753
+ init_esm_shims();
62754
+ READ_ONLY = ["Edit", "Write", "NotebookEdit"];
62755
+ SWARM_TEMPLATES = [
62756
+ {
62757
+ id: "feature",
62758
+ name: "Feature team",
62759
+ description: "Plan, build, test and review a change to the codebase.",
62760
+ useWhen: "You have a goal that touches several files and wants a design, code, tests and a review.",
62761
+ swarm: {
62762
+ description: "Plan, build, test and review a change to the codebase.",
62763
+ topology: "hierarchical",
62764
+ max_agents: 4,
62765
+ instructions: "Break the goal into tasks one worker can finish in one go, each naming the files it touches.\nLet the architect design first, fan the coding tasks out, then test, then review.\n",
62766
+ agents: {
62767
+ architect: {
62768
+ description: "Designs the change from the code graph, the history and past decisions.",
62769
+ model: "opus",
62770
+ disallowed_tools: [...READ_ONLY],
62771
+ memory_tags: ["decision", "constraint"],
62772
+ prompt: 'Study the affected code with em_architecture, em_symbols and em_graph_impact before proposing anything.\nCheck em_recall for decisions and constraints that apply.\nPost the design under the blackboard key "design": files to change, the approach, the risks.\n'
62773
+ },
62774
+ coder: {
62775
+ description: "Implements one task at a time with the smallest diff that works.",
62776
+ count: 2,
62777
+ prompt: 'Read the "design" note first: em_swarm {action:"read", key:"design"}.\nBefore editing a file run em_history {action:"file", path} and respect its fix record.\nKeep the change minimal and consistent with the surrounding code, and run the relevant tests.\n'
62778
+ },
62779
+ tester: {
62780
+ description: "Writes and runs tests that pin the new behaviour and its edge cases.",
62781
+ prompt: "Cover the behaviour the task describes, including edge cases and failure paths.\nRun the tests and report exact failures. Never weaken a test to make it pass.\n"
62782
+ },
62783
+ reviewer: {
62784
+ description: "Adversarial reviewer that tries to break the change before it ships.",
62785
+ model: "opus",
62786
+ disallowed_tools: [...READ_ONLY],
62787
+ memory_tags: ["constraint", "preference"],
62788
+ prompt: 'Review the diff for correctness bugs, missed edge cases, broken conventions and missing tests.\nEvery finding needs a concrete failing input or scenario.\nDo not fix anything yourself: add each finding as a task for the coder role with\nem_swarm {action:"plan", tasks:[{title, detail, role:"coder"}]}.\n'
62789
+ }
62790
+ }
62791
+ }
62792
+ },
62793
+ {
62794
+ id: "review",
62795
+ name: "Review panel",
62796
+ description: "Review the working tree from several angles at once.",
62797
+ useWhen: "A change is ready and you want independent reviewers, each with one concern.",
62798
+ swarm: {
62799
+ description: "Review the working tree from several angles at once.",
62800
+ topology: "mesh",
62801
+ max_agents: 3,
62802
+ agents: {
62803
+ correctness: {
62804
+ description: "Hunts for logic errors and unhandled edge cases.",
62805
+ disallowed_tools: [...READ_ONLY],
62806
+ prompt: 'Find bugs, each with a concrete failing input. Post each under the key "finding-<n>".'
62807
+ },
62808
+ security: {
62809
+ description: "Looks for injection, path traversal, leaked secrets and unsafe defaults.",
62810
+ disallowed_tools: [...READ_ONLY],
62811
+ prompt: "Report only exploitable issues, each with the input that triggers it."
62812
+ },
62813
+ tests: {
62814
+ description: "Checks that every changed behaviour has a test that would fail without it.",
62815
+ disallowed_tools: [...READ_ONLY],
62816
+ prompt: "List untested behaviour together with the test you would add."
62817
+ }
62818
+ }
62819
+ }
62820
+ },
62821
+ {
62822
+ id: "research",
62823
+ name: "Research group",
62824
+ description: "Investigate a question from several sources and write up what was found.",
62825
+ useWhen: "You need an answer from the code, the docs and the history before deciding anything.",
62826
+ swarm: {
62827
+ description: "Investigate a question from several sources and write up what was found.",
62828
+ topology: "mesh",
62829
+ max_agents: 3,
62830
+ instructions: "Split the question into angles, one per researcher, then let the synthesizer combine the findings.\nEvery claim needs its source: a file and line, a document, or a commit.\n",
62831
+ agents: {
62832
+ researcher: {
62833
+ description: "Investigates one angle of the question and posts what it found.",
62834
+ count: 2,
62835
+ disallowed_tools: [...READ_ONLY],
62836
+ prompt: 'Answer your angle from the code graph, the indexed docs and the history (em_symbols, em_docs, em_history).\nPost the findings under the key "finding-<angle>", each with its source.'
62837
+ },
62838
+ synthesizer: {
62839
+ description: "Combines the findings into one answer and names what is still unknown.",
62840
+ disallowed_tools: [...READ_ONLY],
62841
+ prompt: 'Read every "finding-*" note. Post the answer under the key "answer": the conclusion, the evidence, the open questions.\nWhere findings disagree, say so rather than picking one.'
62842
+ }
62843
+ }
62844
+ }
62845
+ },
62846
+ {
62847
+ id: "bugfix",
62848
+ name: "Bug squad",
62849
+ description: "Find the cause of a bug, fix it and pin it with a test.",
62850
+ useWhen: "You have a failing behaviour and want the cause found before anything is changed.",
62851
+ swarm: {
62852
+ description: "Find the cause of a bug, fix it and pin it with a test.",
62853
+ topology: "hierarchical",
62854
+ max_agents: 3,
62855
+ instructions: "Start with the debugger: nothing is fixed until the cause is named.\nThen one coder makes the smallest fix, and the tester proves it with a test that failed before.\n",
62856
+ agents: {
62857
+ debugger: {
62858
+ description: "Reproduces the bug and names its root cause.",
62859
+ model: "opus",
62860
+ disallowed_tools: [...READ_ONLY],
62861
+ memory_tags: ["decision", "constraint"],
62862
+ prompt: 'Reproduce the failure first. Use em_history to see what changed around the code and em_graph_impact to see who depends on it.\nPost the root cause under the blackboard key "cause": the file and line, why it fails, and the smallest fix.'
62863
+ },
62864
+ coder: {
62865
+ description: "Applies the smallest fix for the named cause.",
62866
+ prompt: 'Read the "cause" note first: em_swarm {action:"read", key:"cause"}.\nFix that cause and nothing else, and run the tests around it.'
62867
+ },
62868
+ tester: {
62869
+ description: "Writes a regression test that fails without the fix and passes with it.",
62870
+ prompt: "Write the test from the bug report, confirm it fails on the unfixed code, then confirm it passes.\nReport exact failures. Never weaken a test to make it pass."
62871
+ }
62872
+ }
62873
+ }
62874
+ },
62875
+ {
62876
+ id: "docs",
62877
+ name: "Docs pipeline",
62878
+ description: "Write a document, edit it, then check it against the code.",
62879
+ useWhen: "A change needs documentation and each pass should build on the last.",
62880
+ swarm: {
62881
+ description: "Write a document, edit it, then check it against the code.",
62882
+ topology: "pipeline",
62883
+ max_agents: 3,
62884
+ stages: ["writer", "editor", "checker"],
62885
+ agents: {
62886
+ writer: {
62887
+ description: "Drafts the document from the code and the design notes.",
62888
+ prompt: 'Draft the document the task asks for. Read the code it describes before writing about it.\nPost the draft under the blackboard key "draft".'
62889
+ },
62890
+ editor: {
62891
+ description: "Tightens the draft for clarity, order and tone.",
62892
+ prompt: 'Read the "draft" note and improve it: cut filler, fix the order, keep every fact.\nPost the result under the key "edited".'
62893
+ },
62894
+ checker: {
62895
+ description: "Checks every claim in the document against the code.",
62896
+ disallowed_tools: [...READ_ONLY],
62897
+ prompt: 'Read the "edited" note. For each claim, open the code it describes and confirm it.\nPost the claims that are wrong or unverifiable under the key "corrections".'
62898
+ }
62899
+ }
62900
+ }
62901
+ },
62902
+ {
62903
+ id: "blank",
62904
+ name: "Blank",
62905
+ description: "One worker to start from.",
62906
+ useWhen: "None of the above fits and you want to build the team yourself.",
62907
+ swarm: {
62908
+ description: "Describe what this swarm is for.",
62909
+ topology: "hierarchical",
62910
+ max_agents: 2,
62911
+ agents: {
62912
+ worker: {
62913
+ description: "Describe what this worker does.",
62914
+ prompt: "Describe how this worker should do its job."
62915
+ }
62916
+ }
62917
+ }
62918
+ }
62919
+ ];
62920
+ }
62921
+ });
62922
+
61889
62923
  // src/ui/swarm-api.ts
61890
- import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
61891
- import { dirname as dirname16 } from "path";
61892
62924
  function runCounts(db, run3, now) {
61893
62925
  const tasks = {
61894
62926
  pending: 0,
@@ -62162,18 +63194,18 @@ function runFromId(ctx, id) {
62162
63194
  return { run: run3 };
62163
63195
  }
62164
63196
  function initSwarm(ctx) {
62165
- const where = swarmFile(ctx.root);
62166
- if (where.exists) return fail2(409, `${where.rel} already exists; edit it instead`);
62167
- mkdirSync16(dirname16(where.file), { recursive: true });
63197
+ let rel;
62168
63198
  try {
62169
- writeFileSync14(where.file, STARTER_YAML, { flag: "wx" });
63199
+ rel = saveSwarmFile(ctx.root, STARTER_YAML, null).rel;
62170
63200
  } catch (err) {
62171
- if (err.code === "EEXIST")
62172
- return fail2(409, `${where.rel} already exists; edit it instead`);
63201
+ if (err instanceof SwarmSaveRefused) return fail2(403, err.reason);
63202
+ if (err instanceof SwarmSaveConflict)
63203
+ return fail2(409, `${swarmFile(ctx.root).rel} already exists; edit it instead`);
63204
+ if (err instanceof SwarmSaveInvalid) return fail2(400, err.message, { issues: err.issues });
62173
63205
  throw err;
62174
63206
  }
62175
63207
  const config2 = activeSwarmConfig(ctx.root);
62176
- return ok2({ created: where.rel, sync: syncAgents(ctx.root, config2) });
63208
+ return ok2({ created: rel, sync: syncAgents(ctx.root, config2) });
62177
63209
  }
62178
63210
  function syncSwarm(ctx) {
62179
63211
  const loaded = loadSwarmConfig(ctx.root);
@@ -62224,6 +63256,170 @@ function releaseSwarmWorker(ctx, body) {
62224
63256
  throw err;
62225
63257
  }
62226
63258
  }
63259
+ function configMeta() {
63260
+ return {
63261
+ models: ["inherit", "haiku", "sonnet", "opus", "fable", "opusplan"],
63262
+ tiers: [...TIERS],
63263
+ colors: [...AGENT_COLORS],
63264
+ topologies: [...TOPOLOGIES],
63265
+ efforts: ["low", "medium", "high", "xhigh", "max"],
63266
+ defaultContext: DEFAULT_CONTEXT,
63267
+ limits: CONFIG_LIMITS
63268
+ };
63269
+ }
63270
+ function activeRunsOf(ctx) {
63271
+ try {
63272
+ return getStorage(ctx).swarm.runs({ status: "active", limit: RUNS_LIMIT_MAX }).map((r) => ({ id: r.id, swarm: r.swarm, goal: r.goal }));
63273
+ } catch {
63274
+ return [];
63275
+ }
63276
+ }
63277
+ function swarmConfigDoc(ctx) {
63278
+ const read2 = readSwarmDoc(ctx.root);
63279
+ const activeRuns = activeRunsOf(ctx);
63280
+ return {
63281
+ state: read2.state,
63282
+ file: read2.rel,
63283
+ text: read2.text,
63284
+ hash: read2.hash,
63285
+ doc: read2.doc,
63286
+ issues: read2.issues,
63287
+ warnings: read2.doc ? docWarnings(read2.doc, activeRuns, read2.doc) : [],
63288
+ locked: read2.locked,
63289
+ starter: read2.state === "absent" ? starterDoc() : void 0,
63290
+ activeRuns,
63291
+ meta: configMeta()
63292
+ };
63293
+ }
63294
+ function swarmTemplates() {
63295
+ return {
63296
+ templates: SWARM_TEMPLATES.map((t) => ({
63297
+ id: t.id,
63298
+ name: t.name,
63299
+ description: t.description,
63300
+ useWhen: t.useWhen,
63301
+ topology: t.swarm.topology ?? "hierarchical",
63302
+ roles: Object.entries(t.swarm.agents).map(([role, r]) => ({
63303
+ role,
63304
+ model: r.model ?? "inherit",
63305
+ count: r.count ?? 1
63306
+ })),
63307
+ swarm: t.swarm
63308
+ }))
63309
+ };
63310
+ }
63311
+ async function prepareConfig(ctx, body) {
63312
+ const parsedBody = ConfigBody.safeParse(body);
63313
+ if (!parsedBody.success)
63314
+ return {
63315
+ reply: fail2(400, parsedBody.error.issues[0]?.message ?? "invalid body", {
63316
+ issues: parsedBody.error.issues.map((i) => ({
63317
+ path: i.path.join("."),
63318
+ message: i.message
63319
+ }))
63320
+ })
63321
+ };
63322
+ const b = parsedBody.data;
63323
+ const read2 = readSwarmDoc(ctx.root);
63324
+ if (read2.state === "invalid" && read2.text === null && read2.issues.length > 0)
63325
+ return { reply: fail2(500, read2.issues[0]?.message ?? `cannot read ${read2.rel}`) };
63326
+ if (read2.hash !== b.baseHash)
63327
+ return {
63328
+ reply: fail2(
63329
+ 409,
63330
+ read2.hash === null ? "the swarm file was removed since it was loaded" : b.baseHash === null ? "a swarm file already exists" : "the swarm file changed since it was loaded",
63331
+ { current: read2.hash === null ? null : { text: read2.text, hash: read2.hash } }
63332
+ )
63333
+ };
63334
+ const meta4 = { file: read2.file, rel: read2.rel };
63335
+ let text3;
63336
+ let issues = [];
63337
+ let draft = null;
63338
+ if (b.doc !== void 0) {
63339
+ draft = b.doc;
63340
+ const r = renderSwarmDoc(read2.text, draft, b.renames ?? [], meta4);
63341
+ if (!r.ok) return { reply: fail2(422, r.error, { locked: r.locked ?? read2.locked }) };
63342
+ text3 = r.text;
63343
+ if (r.result.state === "invalid") issues = r.result.issues;
63344
+ } else {
63345
+ text3 = b.text;
63346
+ const { result, enforceOk } = checkSwarmText(text3, meta4);
63347
+ if (result.state === "invalid") issues = result.issues;
63348
+ if (!enforceOk)
63349
+ issues = [
63350
+ ...issues,
63351
+ {
63352
+ path: "enforce",
63353
+ message: "the `enforce:` line must be a plain top-level `enforce: true|false`"
63354
+ }
63355
+ ];
63356
+ try {
63357
+ const Y = await import("yaml");
63358
+ draft = Y.parse(text3, { merge: true, maxAliasCount: 200 });
63359
+ } catch {
63360
+ draft = null;
63361
+ }
63362
+ }
63363
+ const diff2 = lineDiff(read2.text ?? "", text3);
63364
+ const warnings = draft && typeof draft === "object" ? docWarnings(draft, activeRunsOf(ctx), read2.doc) : [];
63365
+ return {
63366
+ parsed: b,
63367
+ p: {
63368
+ text: text3,
63369
+ // A no-op save keeps the on-disk hash (the text round-trip differs for invalid UTF-8).
63370
+ hash: read2.text === text3 && read2.hash ? read2.hash : hashText2(text3),
63371
+ issues,
63372
+ warnings,
63373
+ changed: diff2.changed,
63374
+ removed: diff2.removed,
63375
+ locked: read2.locked,
63376
+ baseText: read2.text,
63377
+ noop: read2.text === text3
63378
+ }
63379
+ };
63380
+ }
63381
+ async function validateConfig(ctx, body) {
63382
+ const r = await prepareConfig(ctx, body);
63383
+ if ("reply" in r) return r.reply;
63384
+ const { p: p2 } = r;
63385
+ return ok2({
63386
+ ok: p2.issues.length === 0,
63387
+ text: p2.text,
63388
+ hash: p2.hash,
63389
+ issues: p2.issues,
63390
+ warnings: p2.warnings,
63391
+ changed: p2.changed,
63392
+ removed: p2.removed,
63393
+ locked: p2.locked
63394
+ });
63395
+ }
63396
+ async function saveConfig(ctx, body) {
63397
+ const r = await prepareConfig(ctx, body);
63398
+ if ("reply" in r) return r.reply;
63399
+ const { p: p2, parsed } = r;
63400
+ if (p2.issues.length) return fail2(400, "the swarm file is not valid", { issues: p2.issues });
63401
+ let created = false;
63402
+ if (!p2.noop) {
63403
+ try {
63404
+ created = saveSwarmFile(ctx.root, p2.text, parsed.baseHash).created;
63405
+ } catch (err) {
63406
+ if (err instanceof SwarmSaveRefused) return fail2(403, err.reason);
63407
+ if (err instanceof SwarmSaveConflict) return fail2(409, err.message, { current: err.current });
63408
+ if (err instanceof SwarmSaveInvalid) return fail2(400, err.message, { issues: err.issues });
63409
+ throw err;
63410
+ }
63411
+ }
63412
+ const sync = parsed.sync ? syncAgents(ctx.root, activeSwarmConfig(ctx.root)) : null;
63413
+ return ok2({
63414
+ saved: !p2.noop,
63415
+ created,
63416
+ changed: p2.changed,
63417
+ hash: p2.hash,
63418
+ text: p2.text,
63419
+ warnings: p2.warnings,
63420
+ sync
63421
+ });
63422
+ }
62227
63423
  async function swarmRoute(ctx, req) {
62228
63424
  const { path: path3, method, query } = req;
62229
63425
  const isGet = method === "GET" || method === "HEAD";
@@ -62276,6 +63472,24 @@ async function swarmRoute(ctx, req) {
62276
63472
  now: Date.now()
62277
63473
  });
62278
63474
  }
63475
+ if (path3 === "/api/swarm/config") {
63476
+ if (!isGet) return fail2(405, "GET only");
63477
+ return ok2(swarmConfigDoc(ctx));
63478
+ }
63479
+ if (path3 === "/api/swarm/templates") {
63480
+ if (!isGet) return fail2(405, "GET only");
63481
+ return ok2(swarmTemplates());
63482
+ }
63483
+ if (path3 === "/api/swarm/config/validate" || path3 === "/api/swarm/config/save") {
63484
+ if (method !== "POST") return fail2(405, "POST only");
63485
+ let cfgBody;
63486
+ try {
63487
+ cfgBody = await readBody();
63488
+ } catch {
63489
+ return fail2(400, "invalid JSON body");
63490
+ }
63491
+ return path3.endsWith("/validate") ? validateConfig(ctx, cfgBody) : saveConfig(ctx, cfgBody);
63492
+ }
62279
63493
  if (path3 === "/api/swarm/init") {
62280
63494
  if (method !== "POST") return fail2(405, "POST only");
62281
63495
  return initSwarm(ctx);
@@ -62306,15 +63520,21 @@ async function swarmRoute(ctx, req) {
62306
63520
  }
62307
63521
  return fail2(404, "not found");
62308
63522
  }
62309
- var RUN_ID_RE, SEGMENT_GAP_MS, EVENTS_PAGE, READ_PAGE, TIMELINE_EVENTS_MAX, SNAPSHOT_EVENTS, TASK_TEXT_MAX, TASKS_SINCE_OVERLAP_MS, RUNS_LIMIT, RUNS_LIMIT_MAX, FINISH_STATUSES, ok2, fail2, ioOf, clipText;
63523
+ var RUN_ID_RE, SEGMENT_GAP_MS, EVENTS_PAGE, READ_PAGE, TIMELINE_EVENTS_MAX, SNAPSHOT_EVENTS, TASK_TEXT_MAX, TASKS_SINCE_OVERLAP_MS, RUNS_LIMIT, RUNS_LIMIT_MAX, FINISH_STATUSES, ok2, fail2, ioOf, clipText, CfgName, ConfigBody, CONFIG_LIMITS;
62310
63524
  var init_swarm_api = __esm({
62311
63525
  "src/ui/swarm-api.ts"() {
62312
63526
  "use strict";
62313
63527
  init_esm_shims();
63528
+ init_zod();
63529
+ init_types();
62314
63530
  init_storage();
62315
63531
  init_agents();
62316
63532
  init_config2();
63533
+ init_edit();
62317
63534
  init_runtime();
63535
+ init_save();
63536
+ init_templates();
63537
+ init_types2();
62318
63538
  RUN_ID_RE = /^\d{8}-[0-9a-f]{6}$/;
62319
63539
  SEGMENT_GAP_MS = 6e4;
62320
63540
  EVENTS_PAGE = 200;
@@ -62339,19 +63559,44 @@ var init_swarm_api = __esm({
62339
63559
  return e.kind === "tool" || e.kind === "tool_error" ? "other" : "swarm";
62340
63560
  };
62341
63561
  clipText = (s) => s !== null && s.length > TASK_TEXT_MAX ? s.slice(0, TASK_TEXT_MAX) : s;
63562
+ CfgName = external_exports.string().min(1).max(64);
63563
+ ConfigBody = external_exports.strictObject({
63564
+ doc: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
63565
+ text: external_exports.string().max(SWARM_FILE_MAX_CHARS).optional(),
63566
+ renames: external_exports.array(external_exports.strictObject({ swarm: CfgName, role: CfgName.optional(), to: CfgName })).max(50).optional(),
63567
+ baseHash: external_exports.string().regex(/^[0-9a-f]{64}$/).nullable(),
63568
+ sync: external_exports.boolean().optional()
63569
+ }).refine((b) => b.doc === void 0 !== (b.text === void 0), {
63570
+ message: "send exactly one of doc or text"
63571
+ });
63572
+ CONFIG_LIMITS = {
63573
+ name: 32,
63574
+ agentName: 64,
63575
+ description: 500,
63576
+ prompt: 2e4,
63577
+ maxAgents: 16,
63578
+ count: 8,
63579
+ maxTurns: 500,
63580
+ memories: 10,
63581
+ docs: 10,
63582
+ maxChars: [500, 9e3],
63583
+ fileChars: SWARM_FILE_MAX_CHARS
63584
+ };
62342
63585
  }
62343
63586
  });
62344
63587
 
62345
63588
  // src/ui/server.ts
62346
63589
  var server_exports = {};
62347
63590
  __export(server_exports, {
63591
+ requestRoot: () => requestRoot,
63592
+ resetProjectListCache: () => resetProjectListCache,
62348
63593
  serve: () => serve,
62349
63594
  uiDir: () => uiDir
62350
63595
  });
62351
63596
  import { spawn as spawn2 } from "child_process";
62352
- import { existsSync as existsSync31, readFileSync as readFileSync29, statSync as statSync17 } from "fs";
63597
+ import { existsSync as existsSync31, readFileSync as readFileSync31, statSync as statSync19 } from "fs";
62353
63598
  import { createServer } from "http";
62354
- import { extname as extname4, join as join38, normalize as normalize8 } from "path";
63599
+ import { extname as extname4, join as join38, normalize as normalize8, resolve as resolve14 } from "path";
62355
63600
  import { URL as URL2 } from "url";
62356
63601
  function json2(res, status, body) {
62357
63602
  res.writeHead(status, {
@@ -62375,12 +63620,13 @@ function strList(v) {
62375
63620
  function uiDir() {
62376
63621
  return process.env.EMINIAR_UI_DIR || join38(pluginRoot(), "dist", "ui");
62377
63622
  }
62378
- function originAllowed(req, port) {
63623
+ function originAllowed(req, port, api) {
62379
63624
  const host = req.headers.host ?? "";
62380
63625
  const ok3 = (h) => h === `127.0.0.1:${port}` || h === `localhost:${port}` || h === `[::1]:${port}`;
62381
63626
  if (!ok3(host)) return false;
62382
63627
  const method = req.method ?? "GET";
62383
- if (method === "GET" || method === "HEAD") return true;
63628
+ const read2 = method === "GET" || method === "HEAD";
63629
+ if (read2 && !api) return true;
62384
63630
  const origin = req.headers.origin;
62385
63631
  if (origin) {
62386
63632
  try {
@@ -62391,6 +63637,7 @@ function originAllowed(req, port) {
62391
63637
  }
62392
63638
  const site = req.headers["sec-fetch-site"];
62393
63639
  if (site && site !== "same-origin" && site !== "none") return false;
63640
+ if (read2) return true;
62394
63641
  const type = String(req.headers["content-type"] ?? "");
62395
63642
  return type.startsWith("application/json");
62396
63643
  }
@@ -62399,34 +63646,60 @@ function serveStatic(res, p2) {
62399
63646
  const rel = p2 === "/" ? "index.html" : p2.replace(/^\/+/, "");
62400
63647
  const file3 = normalize8(join38(dir, rel));
62401
63648
  if (!file3.startsWith(normalize8(dir))) return false;
62402
- if (!existsSync31(file3) || !statSync17(file3).isFile()) return false;
63649
+ if (!existsSync31(file3) || !statSync19(file3).isFile()) return false;
62403
63650
  const type = MIME[extname4(file3).toLowerCase()] ?? "application/octet-stream";
62404
63651
  if (rel === "index.html") {
62405
63652
  const stamp = (name) => {
62406
63653
  try {
62407
- return `/${name}?v=${Math.floor(statSync17(join38(dir, name)).mtimeMs)}`;
63654
+ return `/${name}?v=${Math.floor(statSync19(join38(dir, name)).mtimeMs)}`;
62408
63655
  } catch {
62409
63656
  return `/${name}`;
62410
63657
  }
62411
63658
  };
62412
- const html = readFileSync29(file3, "utf8").replace('"/app.global.js"', `"${stamp("app.global.js")}"`).replace('"/styles.css"', `"${stamp("styles.css")}"`);
63659
+ const html = readFileSync31(file3, "utf8").replace('"/app.global.js"', `"${stamp("app.global.js")}"`).replace('"/styles.css"', `"${stamp("styles.css")}"`);
62413
63660
  res.writeHead(200, { "content-type": type, "cache-control": "no-store" });
62414
63661
  res.end(html);
62415
63662
  return true;
62416
63663
  }
62417
63664
  res.writeHead(200, { "content-type": type, "cache-control": "no-cache" });
62418
- res.end(readFileSync29(file3));
63665
+ res.end(readFileSync31(file3));
62419
63666
  return true;
62420
63667
  }
63668
+ function requestRoot(asked, defaultRoot) {
63669
+ if (!asked || defaultRoot && resolve14(asked) === resolve14(defaultRoot)) return defaultRoot;
63670
+ const want = resolve14(asked);
63671
+ try {
63672
+ for (const p2 of listedProjects()) if (p2.root && resolve14(p2.root) === want) return p2.root;
63673
+ } catch {
63674
+ }
63675
+ return defaultRoot;
63676
+ }
63677
+ function listedProjects() {
63678
+ let mtime = -1;
63679
+ try {
63680
+ mtime = statSync19(paths().projects).mtimeMs;
63681
+ } catch {
63682
+ }
63683
+ const now = Date.now();
63684
+ if (projectList && projectList.mtime === mtime && now - projectList.at < PROJECT_LIST_TTL_MS)
63685
+ return projectList.list;
63686
+ const list2 = listProjects();
63687
+ projectList = { at: now, mtime, list: list2 };
63688
+ return list2;
63689
+ }
63690
+ function resetProjectListCache() {
63691
+ projectList = void 0;
63692
+ }
62421
63693
  async function serve(opts) {
62422
63694
  const defaultRoot = opts.root;
62423
63695
  const server = createServer(async (req, res) => {
62424
63696
  try {
62425
- if (!originAllowed(req, opts.port)) return json2(res, 403, { error: "forbidden origin" });
62426
63697
  const url3 = new URL2(req.url ?? "/", "http://127.0.0.1");
62427
63698
  const p2 = url3.pathname;
63699
+ if (!originAllowed(req, opts.port, p2.startsWith("/api/")))
63700
+ return json2(res, 403, { error: "forbidden origin" });
62428
63701
  const q = url3.searchParams;
62429
- const root = q.get("root") ?? defaultRoot ?? void 0;
63702
+ const root = requestRoot(q.get("root"), defaultRoot);
62430
63703
  const ctx = () => ctxFor(root);
62431
63704
  const num6 = (k, d) => Number(q.get(k) ?? d) || d;
62432
63705
  const str3 = (k) => q.get(k)?.trim() || void 0;
@@ -62805,7 +64078,10 @@ async function serve(opts) {
62805
64078
  categories: strList(q.get("category"))
62806
64079
  })
62807
64080
  );
62808
- if (p2 === "/api/doctor") return json2(res, 200, await doctorPayload(q.get("fix") === "1"));
64081
+ if (p2 === "/api/doctor") {
64082
+ const fix = method === "POST" && (await readJson4(req)).fix === true;
64083
+ return json2(res, 200, await doctorPayload(fix));
64084
+ }
62809
64085
  if (p2 === "/api/actions") {
62810
64086
  if (method !== "POST") return json2(res, 405, { error: "POST only" });
62811
64087
  const body = await readJson4(req);
@@ -62821,9 +64097,9 @@ async function serve(opts) {
62821
64097
  json2(res, 500, { error: err.message });
62822
64098
  }
62823
64099
  });
62824
- await new Promise((resolve14, reject) => {
64100
+ await new Promise((resolve16, reject) => {
62825
64101
  server.once("error", reject);
62826
- server.listen(opts.port, "127.0.0.1", resolve14);
64102
+ server.listen(opts.port, "127.0.0.1", resolve16);
62827
64103
  });
62828
64104
  const url2 = `http://127.0.0.1:${opts.port}/`;
62829
64105
  console.log(`eminiar dashboard at ${url2} (Ctrl-C to stop)`);
@@ -62833,11 +64109,12 @@ async function serve(opts) {
62833
64109
  }
62834
64110
  await new Promise(() => void 0);
62835
64111
  }
62836
- var MIME, FALLBACK_PAGE;
64112
+ var MIME, FALLBACK_PAGE, PROJECT_LIST_TTL_MS, projectList;
62837
64113
  var init_server = __esm({
62838
64114
  "src/ui/server.ts"() {
62839
64115
  "use strict";
62840
64116
  init_esm_shims();
64117
+ init_paths();
62841
64118
  init_version();
62842
64119
  init_adjudicator_api();
62843
64120
  init_api2();
@@ -62860,6 +64137,7 @@ var init_server = __esm({
62860
64137
  <style>body{font:14px system-ui;margin:40px;color:#1b1f24;background:#f5f6f8}code{background:#e5e7eb;padding:2px 5px;border-radius:4px}</style></head>
62861
64138
  <body><h1>eminiar dashboard</h1><p>The frontend bundle is missing. Run <code>npm run build</code> in the eminiar checkout (or reinstall the package) and reload.</p>
62862
64139
  <p>The JSON API is available under <code>/api/*</code>.</p></body></html>`;
64140
+ PROJECT_LIST_TTL_MS = 5e3;
62863
64141
  }
62864
64142
  });
62865
64143
 
@@ -62874,7 +64152,7 @@ var init_log2 = __esm({
62874
64152
  });
62875
64153
 
62876
64154
  // src/session/brief.ts
62877
- import { existsSync as existsSync32, readFileSync as readFileSync30 } from "fs";
64155
+ import { existsSync as existsSync32, readFileSync as readFileSync32 } from "fs";
62878
64156
  import { join as join39 } from "path";
62879
64157
  var init_brief2 = __esm({
62880
64158
  "src/session/brief.ts"() {
@@ -62905,8 +64183,8 @@ var init_brief3 = __esm({
62905
64183
  });
62906
64184
 
62907
64185
  // src/swarm/identity.ts
62908
- import { createHmac, randomBytes as randomBytes4, timingSafeEqual } from "crypto";
62909
- import { mkdirSync as mkdirSync17, readFileSync as readFileSync31, writeFileSync as writeFileSync15 } from "fs";
64186
+ import { createHmac, randomBytes as randomBytes5, timingSafeEqual } from "crypto";
64187
+ import { mkdirSync as mkdirSync17, readFileSync as readFileSync33, writeFileSync as writeFileSync14 } from "fs";
62910
64188
  import { join as join40 } from "path";
62911
64189
  var init_identity = __esm({
62912
64190
  "src/swarm/identity.ts"() {
@@ -62918,22 +64196,22 @@ var init_identity = __esm({
62918
64196
 
62919
64197
  // src/swarm/hooks.ts
62920
64198
  import { execFileSync } from "child_process";
62921
- import { readFileSync as readFileSync32, realpathSync as realpathSync7, statSync as statSync18 } from "fs";
62922
- import { basename as basename7, dirname as dirname17, join as join41, resolve as resolve13 } from "path";
64199
+ import { readFileSync as readFileSync34, realpathSync as realpathSync8, statSync as statSync20 } from "fs";
64200
+ import { basename as basename9, dirname as dirname17, join as join41, resolve as resolve15 } from "path";
62923
64201
  function coordinationRoot(root) {
62924
64202
  try {
62925
64203
  const dotGit = join41(root, ".git");
62926
- if (!statSync18(dotGit).isFile()) return root;
62927
- const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync32(dotGit, "utf8"));
64204
+ if (!statSync20(dotGit).isFile()) return root;
64205
+ const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync34(dotGit, "utf8"));
62928
64206
  if (!m) return root;
62929
- const gitdir = resolve13(root, m[1]);
64207
+ const gitdir = resolve15(root, m[1]);
62930
64208
  let common;
62931
64209
  try {
62932
- common = resolve13(gitdir, readFileSync32(join41(gitdir, "commondir"), "utf8").trim());
64210
+ common = resolve15(gitdir, readFileSync34(join41(gitdir, "commondir"), "utf8").trim());
62933
64211
  } catch {
62934
64212
  return root;
62935
64213
  }
62936
- if (basename7(common) === ".git") return realpathSync7(dirname17(common));
64214
+ if (basename9(common) === ".git") return realpathSync8(dirname17(common));
62937
64215
  const out = execFileSync("git", ["worktree", "list", "--porcelain"], {
62938
64216
  cwd: root,
62939
64217
  encoding: "utf8",
@@ -62942,8 +64220,8 @@ function coordinationRoot(root) {
62942
64220
  });
62943
64221
  const first = /^worktree (.+)$/m.exec(out)?.[1];
62944
64222
  if (!first || /^bare$/m.test(out.split("\n\n")[0] ?? "")) return root;
62945
- const main = realpathSync7(first);
62946
- return main !== realpathSync7(common) && statSync18(join41(main, ".git"), { throwIfNoEntry: false }) ? main : root;
64223
+ const main = realpathSync8(first);
64224
+ return main !== realpathSync8(common) && statSync20(join41(main, ".git"), { throwIfNoEntry: false }) ? main : root;
62947
64225
  } catch {
62948
64226
  return root;
62949
64227
  }
@@ -62970,7 +64248,7 @@ __export(cli_exports2, {
62970
64248
  SWARM_USAGE: () => SWARM_USAGE,
62971
64249
  swarmCommand: () => swarmCommand
62972
64250
  });
62973
- import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
64251
+ import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync15 } from "fs";
62974
64252
  import { dirname as dirname18 } from "path";
62975
64253
  import { parseArgs as parseArgs2 } from "util";
62976
64254
  async function storage2(ctx) {
@@ -62998,12 +64276,12 @@ function readerWatch(stream) {
62998
64276
  stream?.on("error", onError);
62999
64277
  return {
63000
64278
  gone: () => gone || !!stream && (stream.destroyed || !stream.writable),
63001
- wait: (ms4) => new Promise((resolve14) => {
64279
+ wait: (ms4) => new Promise((resolve16) => {
63002
64280
  const timer = setTimeout(done, ms4);
63003
64281
  function done() {
63004
64282
  clearTimeout(timer);
63005
64283
  wake = void 0;
63006
- resolve14();
64284
+ resolve16();
63007
64285
  }
63008
64286
  wake = done;
63009
64287
  })
@@ -63047,7 +64325,7 @@ async function swarmCommand(args, callerCtx, io = defaultIO, opts = {}) {
63047
64325
  return 1;
63048
64326
  }
63049
64327
  mkdirSync18(dirname18(where.file), { recursive: true });
63050
- writeFileSync16(where.file, STARTER_YAML);
64328
+ writeFileSync15(where.file, STARTER_YAML);
63051
64329
  const loaded = loadSwarmConfig(root);
63052
64330
  if (loaded.state !== "ok") {
63053
64331
  io.err(`${where.rel} was written but does not validate`);
@@ -63351,7 +64629,7 @@ __export(statusline_exports, {
63351
64629
  renderStatusline: () => renderStatusline,
63352
64630
  usageSegments: () => usageSegments
63353
64631
  });
63354
- import { existsSync as existsSync33, readFileSync as readFileSync33, writeFileSync as writeFileSync17 } from "fs";
64632
+ import { existsSync as existsSync33, readFileSync as readFileSync35, writeFileSync as writeFileSync16 } from "fs";
63355
64633
  function epochMs(v) {
63356
64634
  if (typeof v === "number" && Number.isFinite(v)) return v < 1e12 ? v * 1e3 : v;
63357
64635
  if (typeof v === "string") {
@@ -63414,7 +64692,7 @@ function renderStatusline(raw) {
63414
64692
  const cache5 = paths().statuslineCache;
63415
64693
  try {
63416
64694
  if (existsSync33(cache5)) {
63417
- const c = JSON.parse(readFileSync33(cache5, "utf8"));
64695
+ const c = JSON.parse(readFileSync35(cache5, "utf8"));
63418
64696
  if (Date.now() - c.at < CACHE_TTL_MS && c.key === key) return c.line;
63419
64697
  }
63420
64698
  } catch {
@@ -63445,7 +64723,7 @@ function renderStatusline(raw) {
63445
64723
  line = `eminiar \xB7 unavailable (${err.message.slice(0, 40)})`;
63446
64724
  }
63447
64725
  try {
63448
- writeFileSync17(cache5, JSON.stringify({ at: Date.now(), line, key }));
64726
+ writeFileSync16(cache5, JSON.stringify({ at: Date.now(), line, key }));
63449
64727
  } catch {
63450
64728
  }
63451
64729
  return line;
@@ -64034,10 +65312,10 @@ var commands = {
64034
65312
  }
64035
65313
  };
64036
65314
  function flushed(stream) {
64037
- return new Promise((resolve14) => {
64038
- if (stream.destroyed || !stream.writable) return resolve14();
64039
- stream.on("error", () => resolve14());
64040
- stream.write("", () => resolve14());
65315
+ return new Promise((resolve16) => {
65316
+ if (stream.destroyed || !stream.writable) return resolve16();
65317
+ stream.on("error", () => resolve16());
65318
+ stream.write("", () => resolve16());
64041
65319
  });
64042
65320
  }
64043
65321
  async function exitFlushed(code2) {