@modootoday/pmcp 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,68 +1,80 @@
1
+ import {
2
+ loadEmbedder,
3
+ openEntryVectors
4
+ } from "./chunk-XEH4ZGGJ.js";
1
5
  import {
2
6
  frontmatterObject,
3
7
  readCatalog,
4
8
  specProblems
5
- } from "./chunk-VQHNG3KM.js";
9
+ } from "./chunk-VRZFM2M5.js";
10
+ import {
11
+ ConfigError
12
+ } from "./chunk-MSTKY33C.js";
13
+ import {
14
+ DEFAULT_MODEL_ID,
15
+ resolveEncoder
16
+ } from "./chunk-XOH2CW6P.js";
6
17
 
7
- // src/config.ts
8
- import { existsSync, readFileSync } from "fs";
9
- import { dirname, join, resolve } from "path";
10
- import { parse } from "smol-toml";
11
- var CONFIG_FILE = "pmcp.toml";
12
- var ConfigError = class extends Error {
13
- constructor(path, message) {
14
- super(`${path}: ${message}`);
15
- this.name = "ConfigError";
16
- }
17
- };
18
- var PATH_KEYS = ["roots", "workspaces", "packages", "marketplaces"];
19
- var CATALOG_KEYS = /* @__PURE__ */ new Set([...PATH_KEYS, "scopes"]);
20
- function findConfig(start) {
21
- let dir = resolve(start);
22
- for (; ; ) {
23
- const candidate = join(dir, CONFIG_FILE);
24
- if (existsSync(candidate)) return candidate;
25
- const parent = dirname(dir);
26
- if (parent === dir) return null;
27
- dir = parent;
28
- }
29
- }
30
- function stringList(path, key, value) {
31
- if (!Array.isArray(value) || !value.every((v) => typeof v === "string")) {
32
- throw new ConfigError(path, `[catalog] ${key} must be a list of strings`);
33
- }
34
- return value;
35
- }
36
- function readConfig(path) {
37
- const absolute = resolve(path);
38
- const dir = dirname(absolute);
39
- let raw;
18
+ // src/search.ts
19
+ import { existsSync } from "fs";
20
+ async function prepareSearch(catalog, options) {
21
+ const lexical = (reason) => ({
22
+ ranking: "lexical",
23
+ reason,
24
+ options: {}
25
+ });
26
+ if (!existsSync(options.indexPath)) return lexical("index_missing");
27
+ let store;
40
28
  try {
41
- raw = parse(readFileSync(absolute, "utf8"));
42
- } catch (error) {
43
- const reason = error instanceof Error ? error.message : "unreadable";
44
- throw new ConfigError(absolute, reason.split("\n")[0] ?? reason);
45
- }
46
- const section = raw["catalog"];
47
- if (section === void 0) return { path: absolute, dir, catalog: {}, raw };
48
- if (section === null || typeof section !== "object" || Array.isArray(section)) {
49
- throw new ConfigError(absolute, "[catalog] must be a table");
29
+ store = await openEntryVectors(options.indexPath, { readOnly: true });
30
+ } catch {
31
+ return lexical("index_unavailable");
50
32
  }
51
- const catalog = {};
52
- for (const [key, value] of Object.entries(section)) {
53
- if (!CATALOG_KEYS.has(key)) {
54
- throw new ConfigError(absolute, `unknown key [catalog] ${key}`);
55
- }
56
- const list = stringList(absolute, key, value);
57
- catalog[key] = key === "scopes" ? list : list.map((p) => resolve(dir, p));
33
+ if (!store) return lexical("index_unavailable");
34
+ try {
35
+ const entries = (options.loadCatalog ?? readCatalog)(catalog);
36
+ const selected = resolveEncoder(
37
+ options.modelId ?? options.embedder?.modelId ?? DEFAULT_MODEL_ID,
38
+ options
39
+ );
40
+ const modelId = selected.modelId;
41
+ const key = selected.indexKey;
42
+ if (options.embedder?.indexKey !== void 0 && options.embedder.indexKey !== key)
43
+ return lexical("vector_mismatch");
44
+ const descriptions = new Map(
45
+ entries.map((entry2) => [entry2.name, entry2.description])
46
+ );
47
+ if (entries.length === 0 || store.stale(key, descriptions).length > 0)
48
+ return lexical("index_stale");
49
+ const vectors = store.read(key);
50
+ const embedder = options.embedder ?? await loadEmbedder(selected.modelId, {
51
+ ...options,
52
+ localOnly: options.localOnly ?? true
53
+ });
54
+ if (!embedder || embedder.modelId !== modelId)
55
+ return lexical("encoder_unavailable");
56
+ if (embedder.indexKey !== void 0 && embedder.indexKey !== key)
57
+ return lexical("vector_mismatch");
58
+ const probe = await embedder.embed("pmcp");
59
+ if (probe.length === 0 || !entries.every(
60
+ (entry2) => vectors.get(entry2.name)?.length === probe.length
61
+ ))
62
+ return lexical("vector_mismatch");
63
+ return {
64
+ ranking: "semantic",
65
+ options: { embedder, vectors, loadCatalog: () => entries }
66
+ };
67
+ } catch {
68
+ return lexical("encoder_unavailable");
69
+ } finally {
70
+ store.close();
58
71
  }
59
- return { path: absolute, dir, catalog, raw };
60
72
  }
61
73
 
62
74
  // src/project/package-rules.ts
63
75
  import { execFileSync } from "child_process";
64
76
  import { existsSync as existsSync2, lstatSync, readdirSync, readlinkSync } from "fs";
65
- import { dirname as dirname2, join as join2, posix } from "path";
77
+ import { dirname, join, posix } from "path";
66
78
  var ProjectError = class extends Error {
67
79
  constructor(message) {
68
80
  super(message);
@@ -107,11 +119,11 @@ function linkTarget(abs) {
107
119
  }
108
120
  function legacyLinks(root) {
109
121
  const found = /* @__PURE__ */ new Map();
110
- const dir = join2(root, RULES_DIR);
122
+ const dir = join(root, RULES_DIR);
111
123
  if (!existsSync2(dir)) return found;
112
124
  for (const entry2 of readdirSync(dir).sort()) {
113
125
  if (!entry2.endsWith(".md")) continue;
114
- const target = linkTarget(join2(dir, entry2));
126
+ const target = linkTarget(join(dir, entry2));
115
127
  if (target === null || !target.startsWith("../../")) continue;
116
128
  found.set(entry2.slice(0, -3), target.slice("../../".length));
117
129
  }
@@ -131,14 +143,14 @@ function packageRuleOutputs(root, spec, owned) {
131
143
  const legacyByDir = /* @__PURE__ */ new Map();
132
144
  for (const [name, target] of legacy) {
133
145
  if (!byTarget.has(target)) byTarget.set(target, name);
134
- const dir = dirname2(target);
146
+ const dir = dirname(target);
135
147
  if (!legacyByDir.has(dir)) legacyByDir.set(dir, name);
136
148
  }
137
149
  const outputs = [];
138
150
  const taken = /* @__PURE__ */ new Map();
139
151
  for (const file of files) {
140
152
  const pkg = file.slice(0, -suffix.length);
141
- const name = byTarget.get(file) ?? legacyByDir.get(dirname2(file)) ?? deriveName(pkg, spec.strip);
153
+ const name = byTarget.get(file) ?? legacyByDir.get(dirname(file)) ?? deriveName(pkg, spec.strip);
142
154
  const other = taken.get(name);
143
155
  if (other !== void 0) {
144
156
  throw new ProjectError(
@@ -156,15 +168,15 @@ function packageRuleOutputs(root, spec, owned) {
156
168
  }
157
169
 
158
170
  // src/project/plan.ts
159
- import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
160
- import { join as join3 } from "path";
171
+ import { existsSync as existsSync3, readFileSync, readdirSync as readdirSync2 } from "fs";
172
+ import { join as join2 } from "path";
161
173
 
162
174
  // src/project/frontmatter.ts
163
- import { parse as parse2 } from "yaml";
175
+ import { parse } from "yaml";
164
176
  function splitFrontmatter(source) {
165
177
  const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/u.exec(source);
166
178
  if (!match) return { meta: {}, body: source };
167
- const meta = parse2(match[1] ?? "");
179
+ const meta = parse(match[1] ?? "");
168
180
  const object = meta !== null && typeof meta === "object" && !Array.isArray(meta) ? meta : {};
169
181
  return { meta: object, body: match[2] ?? "" };
170
182
  }
@@ -208,6 +220,20 @@ function splitKeyed(path) {
208
220
  }
209
221
 
210
222
  // src/project/mcp.ts
223
+ var runtimeByTool = {
224
+ claude: "claude-code",
225
+ codex: "codex-cli",
226
+ gemini: "gemini-cli",
227
+ grok: "grok-cli",
228
+ antigravity: "antigravity"
229
+ };
230
+ function launchForTool(server, tool) {
231
+ if (!server.mailbox) return launch(server);
232
+ return launch({
233
+ ...server,
234
+ env: { ...server.env, PMCP_MAILBOX_RUNTIME: runtimeByTool[tool] }
235
+ });
236
+ }
211
237
  var ROOT_VAR = "${PROJECT_ROOT}";
212
238
  var quote = (text) => `'${text.replaceAll("'", "'\\''")}'`;
213
239
  function shellWord(text) {
@@ -259,16 +285,18 @@ function tomlServers(servers) {
259
285
  `;
260
286
  }
261
287
  function mcpOutputs(spec) {
262
- const servers = Object.entries(spec.mcp).map(
263
- ([alias, server]) => [alias, launch(server)]
264
- );
288
+ const servers = Object.entries(spec.mcp);
265
289
  if (servers.length === 0) return [];
266
290
  const outputs = [];
267
291
  for (const [alias, server] of servers) {
268
292
  const tools = spec.mcp[alias]?.tools;
269
293
  if (spec.tools.has("claude")) {
270
294
  outputs.push(
271
- jsonOutput(".mcp.json", ["mcpServers", alias], entry(server))
295
+ jsonOutput(
296
+ ".mcp.json",
297
+ ["mcpServers", alias],
298
+ entry(launchForTool(server, "claude"))
299
+ )
272
300
  );
273
301
  }
274
302
  if (spec.tools.has("gemini")) {
@@ -277,7 +305,7 @@ function mcpOutputs(spec) {
277
305
  jsonOutput(
278
306
  ".gemini/settings.json",
279
307
  ["mcpServers", alias],
280
- entry(server, extra)
308
+ entry(launchForTool(server, "gemini"), extra)
281
309
  )
282
310
  );
283
311
  }
@@ -286,16 +314,36 @@ function mcpOutputs(spec) {
286
314
  jsonOutput(
287
315
  ".agents/mcp_config.json",
288
316
  ["mcpServers", alias],
289
- entry(server)
317
+ entry(launchForTool(server, "antigravity"))
290
318
  )
291
319
  );
292
320
  }
293
321
  }
294
322
  if (spec.tools.has("codex")) {
295
- outputs.push(blockOutput(".codex/config.toml", tomlServers(servers)));
323
+ outputs.push(
324
+ blockOutput(
325
+ ".codex/config.toml",
326
+ tomlServers(
327
+ servers.map(([alias, server]) => [
328
+ alias,
329
+ launchForTool(server, "codex")
330
+ ])
331
+ )
332
+ )
333
+ );
296
334
  }
297
335
  if (spec.tools.has("grok")) {
298
- outputs.push(blockOutput(".grok/config.toml", tomlServers(servers)));
336
+ outputs.push(
337
+ blockOutput(
338
+ ".grok/config.toml",
339
+ tomlServers(
340
+ servers.map(([alias, server]) => [
341
+ alias,
342
+ launchForTool(server, "grok")
343
+ ])
344
+ )
345
+ )
346
+ );
299
347
  }
300
348
  return outputs;
301
349
  }
@@ -312,15 +360,15 @@ function listDirs(dir) {
312
360
  }
313
361
  var tomlString = (value) => JSON.stringify(typeof value === "string" ? value : String(value ?? ""));
314
362
  function skillNames(root) {
315
- const dir = join3(root, ".agents/skills");
363
+ const dir = join2(root, ".agents/skills");
316
364
  return listDirs(dir).filter(
317
- (name) => existsSync3(join3(dir, name, "SKILL.md"))
365
+ (name) => existsSync3(join2(dir, name, "SKILL.md"))
318
366
  );
319
367
  }
320
368
  function agentNames(root) {
321
- const dir = join3(root, ".agents/agents");
369
+ const dir = join2(root, ".agents/agents");
322
370
  return listDirs(dir).filter(
323
- (name) => existsSync3(join3(dir, name, "agent.md"))
371
+ (name) => existsSync3(join2(dir, name, "agent.md"))
324
372
  );
325
373
  }
326
374
  function skillOutputs(spec) {
@@ -354,13 +402,13 @@ policy:
354
402
  }
355
403
  function ruleOutputs(spec) {
356
404
  if (!spec.tools.has("claude")) return [];
357
- const dir = join3(spec.root, ".agents/rules");
405
+ const dir = join2(spec.root, ".agents/rules");
358
406
  if (!existsSync3(dir)) return [];
359
407
  const outputs = [];
360
408
  for (const file of readdirSync2(dir).filter((f) => f.endsWith(".md")).sort()) {
361
409
  const rel = `.agents/rules/${file}`;
362
410
  const { meta, body } = splitFrontmatter(
363
- readFileSync2(join3(spec.root, rel), "utf8")
411
+ readFileSync(join2(spec.root, rel), "utf8")
364
412
  );
365
413
  const globs = [meta["globs"] ?? []].flat().filter((g) => typeof g === "string" && g !== "");
366
414
  const head = globs.length > 0 ? renderFrontmatter({ paths: globs }) : "";
@@ -379,7 +427,7 @@ function agentOutputs(spec) {
379
427
  for (const name of agentNames(spec.root)) {
380
428
  const rel = `.agents/agents/${name}/agent.md`;
381
429
  const { meta, body } = splitFrontmatter(
382
- readFileSync2(join3(spec.root, rel), "utf8")
430
+ readFileSync(join2(spec.root, rel), "utf8")
383
431
  );
384
432
  const overrides = spec.agents[name] ?? {};
385
433
  const note = `<!-- ${MARK} from ${rel} -->
@@ -453,14 +501,14 @@ import {
453
501
  existsSync as existsSync4,
454
502
  lstatSync as lstatSync2,
455
503
  mkdirSync,
456
- readFileSync as readFileSync3,
504
+ readFileSync as readFileSync2,
457
505
  readdirSync as readdirSync3,
458
506
  readlinkSync as readlinkSync2,
459
507
  rmSync,
460
508
  symlinkSync,
461
509
  writeFileSync
462
510
  } from "fs";
463
- import { dirname as dirname3, join as join4, posix as posix2 } from "path";
511
+ import { dirname as dirname2, join as join3, posix as posix2 } from "path";
464
512
  var LOCK_FILE = "pmcp.lock";
465
513
  var BEGIN = "# >>> pmcp project (edit pmcp.toml, not this block) >>>";
466
514
  var END = "# <<< pmcp project <<<";
@@ -470,9 +518,9 @@ var PREDECESSORS = [
470
518
  ];
471
519
  var sha = (text) => createHash("sha256").update(text).digest("hex");
472
520
  function readLock(root) {
473
- const path = join4(root, LOCK_FILE);
521
+ const path = join3(root, LOCK_FILE);
474
522
  if (!existsSync4(path)) return [];
475
- const lock = JSON.parse(readFileSync3(path, "utf8"));
523
+ const lock = JSON.parse(readFileSync2(path, "utf8"));
476
524
  return lock.outputs ?? [];
477
525
  }
478
526
  function linkOf(abs) {
@@ -485,7 +533,7 @@ function linkOf(abs) {
485
533
  var exists = (abs) => existsSync4(abs) || linkOf(abs) !== null;
486
534
  function readJson(abs) {
487
535
  if (!existsSync4(abs)) return {};
488
- const value = JSON.parse(readFileSync3(abs, "utf8"));
536
+ const value = JSON.parse(readFileSync2(abs, "utf8"));
489
537
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
490
538
  }
491
539
  function getKey(object, key) {
@@ -532,29 +580,29 @@ function withoutBlock(text) {
532
580
  return `${text.slice(0, start).replace(/\n+$/u, "\n")}${after}`;
533
581
  }
534
582
  function present(root, output) {
535
- const abs = join4(root, "file" in output ? output.file : output.path);
583
+ const abs = join3(root, "file" in output ? output.file : output.path);
536
584
  switch (output.kind) {
537
585
  case "link":
538
586
  return linkOf(abs) === output.target;
539
587
  case "file":
540
- return existsSync4(abs) && linkOf(abs) === null && readFileSync3(abs, "utf8") === output.content;
588
+ return existsSync4(abs) && linkOf(abs) === null && readFileSync2(abs, "utf8") === output.content;
541
589
  case "json":
542
590
  return same(getKey(readJson(abs), output.key), output.value);
543
591
  case "block":
544
- return existsSync4(abs) && readFileSync3(abs, "utf8").includes(block(output.content));
592
+ return existsSync4(abs) && readFileSync2(abs, "utf8").includes(block(output.content));
545
593
  }
546
594
  }
547
595
  function replaceable(root, output, owned) {
548
596
  if (output.kind === "json" || output.kind === "block") return true;
549
597
  if (owned.has(output.path)) return true;
550
- const abs = join4(root, output.path);
598
+ const abs = join3(root, output.path);
551
599
  if (!exists(abs)) return true;
552
600
  const target = linkOf(abs);
553
601
  if (target !== null) {
554
602
  return output.kind === "link" && target === output.target;
555
603
  }
556
604
  if (lstatSync2(abs).isDirectory()) return false;
557
- const text = readFileSync3(abs, "utf8");
605
+ const text = readFileSync2(abs, "utf8");
558
606
  if (output.kind === "file" && text === output.content) return true;
559
607
  return PREDECESSORS.some((mark) => text.includes(mark));
560
608
  }
@@ -562,14 +610,14 @@ function undeclared(spec, outputs) {
562
610
  const declared = new Set(outputs.map((o) => o.path));
563
611
  const found = [];
564
612
  for (const dir of censusDirs(spec)) {
565
- const abs = join4(spec.root, dir);
613
+ const abs = join3(spec.root, dir);
566
614
  if (!existsSync4(abs)) continue;
567
615
  for (const entry2 of readdirSync3(abs)) {
568
616
  const path = `${dir}/${entry2}`;
569
617
  if (!declared.has(path)) found.push(path);
570
618
  }
571
619
  }
572
- const rules = join4(spec.root, RULES_DIR);
620
+ const rules = join3(spec.root, RULES_DIR);
573
621
  if (spec.tools.has("grok") && existsSync4(rules)) {
574
622
  for (const entry2 of readdirSync3(rules, { withFileTypes: true })) {
575
623
  if (entry2.isDirectory()) continue;
@@ -597,9 +645,9 @@ function deadGlobs(root, outputs) {
597
645
  if (rule.kind === "file") {
598
646
  text = rule.content;
599
647
  } else if (rule.kind === "link") {
600
- const abs = join4(root, posix2.join(posix2.dirname(rule.path), rule.target));
648
+ const abs = join3(root, posix2.join(posix2.dirname(rule.path), rule.target));
601
649
  if (!existsSync4(abs)) continue;
602
- text = readFileSync3(abs, "utf8");
650
+ text = readFileSync2(abs, "utf8");
603
651
  } else {
604
652
  continue;
605
653
  }
@@ -659,8 +707,8 @@ function project(spec, options) {
659
707
  for (const output of outputs) {
660
708
  if (output.kind !== "link" && output.kind !== "file") continue;
661
709
  if (present(spec.root, output)) continue;
662
- const abs = join4(spec.root, output.path);
663
- mkdirSync(dirname3(abs), { recursive: true });
710
+ const abs = join3(spec.root, output.path);
711
+ mkdirSync(dirname2(abs), { recursive: true });
664
712
  rmSync(abs, { force: true, recursive: true });
665
713
  if (output.kind === "link") symlinkSync(output.target, abs);
666
714
  else writeFileSync(abs, output.content);
@@ -685,12 +733,12 @@ function project(spec, options) {
685
733
  group.drops.push(keyed.key);
686
734
  jsonFiles.set(keyed.file, group);
687
735
  } else {
688
- rmSync(join4(spec.root, entry2.path), { force: true, recursive: true });
736
+ rmSync(join3(spec.root, entry2.path), { force: true, recursive: true });
689
737
  }
690
738
  pruned += 1;
691
739
  }
692
740
  for (const [file, { sets, drops }] of jsonFiles) {
693
- const abs = join4(spec.root, file);
741
+ const abs = join3(spec.root, file);
694
742
  const before = readJson(abs);
695
743
  const after = structuredClone(before);
696
744
  for (const key of drops) deleteKey(after, key);
@@ -699,20 +747,20 @@ function project(spec, options) {
699
747
  }
700
748
  if (same(before, after)) continue;
701
749
  if (!existsSync4(abs) && Object.keys(after).length === 0) continue;
702
- mkdirSync(dirname3(abs), { recursive: true });
750
+ mkdirSync(dirname2(abs), { recursive: true });
703
751
  writeFileSync(abs, `${JSON.stringify(after, null, 2)}
704
752
  `);
705
753
  }
706
754
  for (const [file, content] of blockFiles) {
707
- const abs = join4(spec.root, file);
708
- const before = existsSync4(abs) ? readFileSync3(abs, "utf8") : "";
755
+ const abs = join3(spec.root, file);
756
+ const before = existsSync4(abs) ? readFileSync2(abs, "utf8") : "";
709
757
  let after = withoutBlock(before);
710
758
  if (content !== null) {
711
759
  const sep = after === "" || after.endsWith("\n\n") ? "" : after.endsWith("\n") ? "\n" : "\n\n";
712
760
  after = `${after}${sep}${block(content)}`;
713
761
  }
714
762
  if (after === before) continue;
715
- mkdirSync(dirname3(abs), { recursive: true });
763
+ mkdirSync(dirname2(abs), { recursive: true });
716
764
  writeFileSync(abs, after);
717
765
  }
718
766
  if (spec.packageRules) {
@@ -724,7 +772,7 @@ function project(spec, options) {
724
772
  )
725
773
  );
726
774
  for (const [name, target] of legacyLinks(spec.root)) {
727
- const abs = join4(spec.root, RULES_DIR, `${name}.md`);
775
+ const abs = join3(spec.root, RULES_DIR, `${name}.md`);
728
776
  const broken = !existsSync4(abs);
729
777
  if (!broken && !linkedDirs.has(posix2.dirname(target))) continue;
730
778
  rmSync(abs, { force: true });
@@ -732,7 +780,7 @@ function project(spec, options) {
732
780
  }
733
781
  }
734
782
  writeFileSync(
735
- join4(spec.root, LOCK_FILE),
783
+ join3(spec.root, LOCK_FILE),
736
784
  `${JSON.stringify({ version: 1, outputs: outputs.map(lockEntry) }, null, 2)}
737
785
  `
738
786
  );
@@ -741,9 +789,9 @@ function project(spec, options) {
741
789
 
742
790
  // src/project/doctor.ts
743
791
  import { spawnSync } from "child_process";
744
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
745
- import { join as join5 } from "path";
746
- import { parse as parse3 } from "smol-toml";
792
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
793
+ import { join as join4 } from "path";
794
+ import { parse as parse2 } from "smol-toml";
747
795
  var spawnRunner = (command, args, cwd) => {
748
796
  const result = spawnSync(command, [...args], {
749
797
  cwd,
@@ -804,10 +852,10 @@ function claude(spec, o) {
804
852
  }
805
853
  function codex(spec, o) {
806
854
  const findings = [];
807
- const config = join5(o.home, ".codex/config.toml");
855
+ const config = join4(o.home, ".codex/config.toml");
808
856
  let trusted = false;
809
857
  if (existsSync5(config)) {
810
- const projects = parse3(readFileSync4(config, "utf8")).projects ?? {};
858
+ const projects = parse2(readFileSync3(config, "utf8")).projects ?? {};
811
859
  const root = spec.root.replace(/\/$/u, "");
812
860
  trusted = projects[root]?.trust_level === "trusted" || projects[`${root}/`]?.trust_level === "trusted";
813
861
  }
@@ -934,8 +982,8 @@ function grok(spec, o) {
934
982
  }
935
983
  function antigravity(spec) {
936
984
  const foreign = agentNames(spec.root).filter((name) => {
937
- const text = readFileSync4(
938
- join5(spec.root, ".agents/agents", name, "agent.md"),
985
+ const text = readFileSync3(
986
+ join4(spec.root, ".agents/agents", name, "agent.md"),
939
987
  "utf8"
940
988
  );
941
989
  return splitFrontmatter(text).meta["tools"] !== void 0;
@@ -1074,6 +1122,9 @@ function readSpec(config) {
1074
1122
  throw new ConfigError(path, `${where} command is required`);
1075
1123
  }
1076
1124
  const env = {};
1125
+ if (entry2["mailbox"] !== void 0 && typeof entry2["mailbox"] !== "boolean") {
1126
+ throw new ConfigError(path, `${where} mailbox must be a boolean`);
1127
+ }
1077
1128
  for (const [key, v] of Object.entries(
1078
1129
  table(path, `${where} env`, entry2["env"])
1079
1130
  )) {
@@ -1083,6 +1134,7 @@ function readSpec(config) {
1083
1134
  env[key] = v;
1084
1135
  }
1085
1136
  mcp[named(path, "[mcp]", alias)] = {
1137
+ ...entry2["mailbox"] === true ? { mailbox: true } : {},
1086
1138
  command,
1087
1139
  args: entry2["args"] === void 0 ? [] : strings(path, `${where} args`, entry2["args"]),
1088
1140
  env,
@@ -1093,16 +1145,16 @@ function readSpec(config) {
1093
1145
  }
1094
1146
 
1095
1147
  // src/validate.ts
1096
- import { existsSync as existsSync6, readFileSync as readFileSync5, statSync } from "fs";
1097
- import { basename, join as join6, resolve as resolve2 } from "path";
1148
+ import { existsSync as existsSync6, readFileSync as readFileSync4, statSync } from "fs";
1149
+ import { basename, join as join5, resolve } from "path";
1098
1150
  function validateSkillDir(dir) {
1099
- const root = resolve2(dir);
1100
- const skillMd = basename(root) === "SKILL.md" ? root : join6(root, "SKILL.md");
1151
+ const root = resolve(dir);
1152
+ const skillMd = basename(root) === "SKILL.md" ? root : join5(root, "SKILL.md");
1101
1153
  if (!existsSync6(skillMd) || !statSync(skillMd).isFile()) {
1102
1154
  return [{ path: root, reason: "no SKILL.md", level: "error" }];
1103
1155
  }
1104
- const front = frontmatterObject(readFileSync5(skillMd, "utf8"));
1105
- const directory = basename(skillMd === root ? join6(root, "..") : root);
1156
+ const front = frontmatterObject(readFileSync4(skillMd, "utf8"));
1157
+ const directory = basename(skillMd === root ? join5(root, "..") : root);
1106
1158
  return specProblems(front, directory).map((reason) => ({
1107
1159
  path: skillMd,
1108
1160
  reason,
@@ -1120,10 +1172,7 @@ function validateCatalog(options) {
1120
1172
  }
1121
1173
 
1122
1174
  export {
1123
- CONFIG_FILE,
1124
- ConfigError,
1125
- findConfig,
1126
- readConfig,
1175
+ prepareSearch,
1127
1176
  splitFrontmatter,
1128
1177
  renderFrontmatter,
1129
1178
  ProjectError,
@@ -1139,4 +1188,4 @@ export {
1139
1188
  validateSkillDir,
1140
1189
  validateCatalog
1141
1190
  };
1142
- //# sourceMappingURL=chunk-QCOJ3WCM.js.map
1191
+ //# sourceMappingURL=chunk-ZUFFVIHQ.js.map