@agentskit/doc-bridge 0.1.0-alpha.1 → 0.1.0-alpha.3

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +1 -1
  3. package/dist/cli/program.js +416 -148
  4. package/dist/cli/program.js.map +1 -1
  5. package/dist/config/index.d.ts +1 -1
  6. package/dist/config/index.js +6 -3
  7. package/dist/config/index.js.map +1 -1
  8. package/dist/{index-CD_zmKXf.d.ts → index-CPUJbTbg.d.ts} +19 -10
  9. package/dist/index.d.ts +25 -6
  10. package/dist/index.js +398 -132
  11. package/dist/index.js.map +1 -1
  12. package/docs/DOGFOOD-ROUND2.md +142 -0
  13. package/docs/DOGFOOD.md +92 -0
  14. package/package.json +32 -14
  15. package/scripts/prepare.mjs +44 -0
  16. package/src/cli/program.ts +947 -0
  17. package/src/config/defaults.ts +63 -0
  18. package/src/config/define-config.ts +6 -0
  19. package/src/config/index.ts +15 -0
  20. package/src/config/load-config.ts +138 -0
  21. package/src/config/schema.ts +294 -0
  22. package/src/federation/llms.ts +154 -0
  23. package/src/gates/run-gates.ts +274 -0
  24. package/src/index-builder/build-handoffs.ts +232 -0
  25. package/src/index-builder/build-index.ts +137 -0
  26. package/src/index-builder/capabilities.ts +37 -0
  27. package/src/index-builder/content-hash.ts +19 -0
  28. package/src/index-builder/human-adapters/core.ts +100 -0
  29. package/src/index-builder/human-adapters/docusaurus.ts +113 -0
  30. package/src/index-builder/human-adapters/fumadocs.ts +70 -0
  31. package/src/index-builder/human-adapters/index.ts +52 -0
  32. package/src/index-builder/human-adapters/plain-markdown.ts +10 -0
  33. package/src/index-builder/llms-txt.ts +19 -0
  34. package/src/index-builder/plugins/human-markdown.ts +7 -0
  35. package/src/index-builder/plugins/pnpm-monorepo.ts +72 -0
  36. package/src/index-builder/scan-corpus.ts +185 -0
  37. package/src/index.ts +120 -0
  38. package/src/intelligence/adapter.ts +90 -0
  39. package/src/intelligence/chat.ts +148 -0
  40. package/src/intelligence/peers.ts +59 -0
  41. package/src/intelligence/rag.ts +98 -0
  42. package/src/lib/glob-expand.ts +33 -0
  43. package/src/lib/markdown.ts +117 -0
  44. package/src/lib/package-manager.ts +104 -0
  45. package/src/lib/paths.ts +6 -0
  46. package/src/lib/walk.ts +45 -0
  47. package/src/mcp/server.ts +276 -0
  48. package/src/memory/ingest.ts +51 -0
  49. package/src/memory/pipeline.ts +119 -0
  50. package/src/query/load-index.ts +25 -0
  51. package/src/query/query.ts +142 -0
  52. package/src/query/search.ts +126 -0
  53. package/src/retriever/doc-bridge-retriever.ts +62 -0
  54. package/src/schemas/agent-handoff.ts +82 -0
  55. package/src/schemas/doc-bridge-index.ts +108 -0
  56. package/src/schemas/json-schemas.ts +157 -0
  57. package/src/schemas/memory-candidate.ts +37 -0
  58. package/src/shims/agentskit-peers.d.ts +80 -0
  59. package/src/validate.ts +67 -0
  60. package/src/version.ts +1 -0
  61. package/tsconfig.json +21 -0
  62. package/tsup.config.ts +15 -0
@@ -1,5 +1,5 @@
1
1
  // src/cli/program.ts
2
- import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync14, writeFileSync as writeFileSync2 } from "fs";
2
+ import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync15, writeFileSync as writeFileSync2 } from "fs";
3
3
  import { dirname as dirname4, resolve as resolve6 } from "path";
4
4
  import { createInterface } from "readline/promises";
5
5
 
@@ -20,7 +20,7 @@ var applyConfigDefaults = (config) => {
20
20
  agent: {
21
21
  ...config.corpus.agent,
22
22
  index: agentIndex,
23
- include: config.corpus.agent.include ?? ["**/*.md"],
23
+ include: config.corpus.agent.include ?? ["**/*.{md,mdx}"],
24
24
  exclude: config.corpus.agent.exclude ?? [...DEFAULT_AGENT_EXCLUDE]
25
25
  }
26
26
  },
@@ -113,9 +113,11 @@ var OwnershipEntrySchema = z.object({
113
113
  humanDoc: z.string().min(1).max(512).optional()
114
114
  }).strict();
115
115
  var RoutingConfigSchema = z.object({
116
- plugin: z.enum(["pnpm-monorepo", "npm-workspaces", "yarn-workspaces", "custom"]).optional(),
116
+ plugin: z.enum(["pnpm-monorepo", "npm-workspaces", "yarn-workspaces", "pattern-files", "custom"]).optional(),
117
117
  options: z.object({
118
118
  packages: z.array(z.string().min(1).max(256)).max(128).optional(),
119
+ /** Infer ownership from corpus paths/frontmatter (default true). */
120
+ ownershipFromCorpus: z.boolean().optional(),
119
121
  ownership: z.record(z.string().min(1).max(256), OwnershipEntrySchema).optional(),
120
122
  intents: z.array(
121
123
  z.object({
@@ -135,7 +137,8 @@ var RoutingConfigSchema = z.object({
135
137
  }).strict().optional()
136
138
  }).strict();
137
139
  var GatesConfigSchema = z.object({
138
- preset: z.enum(["minimal", "standard", "strict"]).optional(),
140
+ /** minimal: freshness · standard: + human links · strict: + okf-type · playbook: freshness + okf soft style */
141
+ preset: z.enum(["minimal", "standard", "strict", "playbook"]).optional(),
139
142
  include: z.array(
140
143
  z.enum([
141
144
  "index-freshness",
@@ -343,16 +346,80 @@ var projectRootFromConfigPath = (configFilePath, projectRootField) => {
343
346
  };
344
347
 
345
348
  // src/index-builder/build-index.ts
346
- import { mkdirSync, readFileSync as readFileSync7, writeFileSync } from "fs";
347
- import { dirname as dirname3, join as join9 } from "path";
349
+ import { mkdirSync, readFileSync as readFileSync8, writeFileSync } from "fs";
350
+ import { dirname as dirname3, join as join10 } from "path";
348
351
 
349
352
  // src/lib/paths.ts
350
353
  import { resolve as resolve2 } from "path";
351
354
  var toPosix = (value) => value.split("\\").join("/");
352
355
 
356
+ // src/lib/package-manager.ts
357
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
358
+ import { join as join2 } from "path";
359
+ var detectPackageManager = (root) => {
360
+ if (existsSync2(join2(root, "pnpm-lock.yaml")) || existsSync2(join2(root, "pnpm-workspace.yaml"))) {
361
+ return "pnpm";
362
+ }
363
+ if (existsSync2(join2(root, "yarn.lock"))) return "yarn";
364
+ if (existsSync2(join2(root, "bun.lockb")) || existsSync2(join2(root, "bun.lock"))) return "bun";
365
+ if (existsSync2(join2(root, "package-lock.json"))) return "npm";
366
+ try {
367
+ const pkg = JSON.parse(readFileSync2(join2(root, "package.json"), "utf8"));
368
+ const pm = pkg.packageManager?.split("@")[0];
369
+ if (pm === "pnpm" || pm === "npm" || pm === "yarn" || pm === "bun") return pm;
370
+ } catch {
371
+ }
372
+ return "npm";
373
+ };
374
+ var defaultChecksForTarget = (root, opts) => {
375
+ const pm = detectPackageManager(root);
376
+ if (/\.mdx?$/.test(opts.packagePath) || opts.packagePath.includes("/pillars/")) {
377
+ try {
378
+ const pkg = JSON.parse(readFileSync2(join2(root, "package.json"), "utf8"));
379
+ const scripts = pkg.scripts ?? {};
380
+ if (scripts["check:okf-type"]) return ["pnpm run check:okf-type"];
381
+ if (scripts["docs:bridge:gate"]) return ["pnpm run docs:bridge:gate"];
382
+ if (scripts.test) return [pm === "pnpm" ? "pnpm test" : "npm test"];
383
+ } catch {
384
+ }
385
+ }
386
+ const isWorkspace = existsSync2(join2(root, "pnpm-workspace.yaml")) || existsSync2(join2(root, "lerna.json")) || Boolean(
387
+ (() => {
388
+ try {
389
+ const pkg = JSON.parse(readFileSync2(join2(root, "package.json"), "utf8"));
390
+ return Boolean(pkg.workspaces);
391
+ } catch {
392
+ return false;
393
+ }
394
+ })()
395
+ );
396
+ const filter = opts.packageName ?? (opts.packagePath.startsWith("packages/") || opts.packagePath.startsWith("apps/") ? opts.packageId : void 0);
397
+ if (pm === "pnpm") {
398
+ if (isWorkspace && filter) {
399
+ const base = [`pnpm --filter ${filter} test`];
400
+ if (opts.strict) base.push(`pnpm --filter ${filter} lint`);
401
+ return base;
402
+ }
403
+ return opts.strict ? ["pnpm test", "pnpm run lint"] : ["pnpm test"];
404
+ }
405
+ if (pm === "yarn") {
406
+ if (isWorkspace && filter) {
407
+ return opts.strict ? [`yarn workspace ${filter} test`, `yarn workspace ${filter} lint`] : [`yarn workspace ${filter} test`];
408
+ }
409
+ return opts.strict ? ["yarn test", "yarn lint"] : ["yarn test"];
410
+ }
411
+ if (pm === "bun") {
412
+ return opts.strict ? ["bun test", "bun run lint"] : ["bun test"];
413
+ }
414
+ if (isWorkspace && filter) {
415
+ return opts.strict ? [`npm test -w ${filter}`, `npm run lint -w ${filter}`] : [`npm test -w ${filter}`];
416
+ }
417
+ return opts.strict ? ["npm test", "npm run lint"] : ["npm test"];
418
+ };
419
+
353
420
  // src/index-builder/scan-corpus.ts
354
- import { readFileSync as readFileSync2 } from "fs";
355
- import { join as join3 } from "path";
421
+ import { readFileSync as readFileSync3 } from "fs";
422
+ import { join as join4 } from "path";
356
423
 
357
424
  // src/lib/markdown.ts
358
425
  var parseFrontmatter = (markdown) => {
@@ -402,7 +469,7 @@ var firstHeading = (markdown) => {
402
469
  }
403
470
  return void 0;
404
471
  };
405
- var firstParagraph = (markdown) => {
472
+ var firstParagraph = (markdown, maxLen = 400) => {
406
473
  const { body } = parseFrontmatter(markdown);
407
474
  const lines = body.split("\n");
408
475
  const buf = [];
@@ -414,11 +481,27 @@ var firstParagraph = (markdown) => {
414
481
  }
415
482
  if (t.startsWith("#")) continue;
416
483
  if (t.startsWith("---")) continue;
484
+ if (t.startsWith("```")) break;
485
+ if (t.startsWith("|") || t.startsWith("- [") || t.startsWith("* [")) {
486
+ if (buf.length) break;
487
+ continue;
488
+ }
417
489
  buf.push(t);
418
- if (buf.join(" ").length > 40) break;
419
- }
420
- const text = buf.join(" ").trim();
421
- return text || void 0;
490
+ if (buf.join(" ").length >= maxLen) break;
491
+ }
492
+ let text = buf.join(" ").replace(/\s+/g, " ").trim();
493
+ if (!text) return void 0;
494
+ if (text.length <= maxLen) return text;
495
+ const sliced = text.slice(0, maxLen);
496
+ const sentenceEnd = Math.max(sliced.lastIndexOf(". "), sliced.lastIndexOf("! "), sliced.lastIndexOf("? "));
497
+ if (sentenceEnd > maxLen * 0.4) return sliced.slice(0, sentenceEnd + 1).trim();
498
+ const wordEnd = sliced.lastIndexOf(" ");
499
+ return (wordEnd > 0 ? sliced.slice(0, wordEnd) : sliced).trim();
500
+ };
501
+ var extractSearchBody = (markdown, maxLen = 6e3) => {
502
+ const { body } = parseFrontmatter(markdown);
503
+ const text = body.replace(/```[\s\S]*?```/g, " ").replace(/!\[[^\]]*\]\([^)]+\)/g, " ").replace(/\[[^\]]*\]\([^)]+\)/g, " ").replace(/^#+\s+/gm, "").replace(/[|>*_`#]/g, " ").replace(/\s+/g, " ").trim();
504
+ return text.length > maxLen ? text.slice(0, maxLen) : text;
422
505
  };
423
506
  var slugFromPath = (relPath) => {
424
507
  const base = relPath.replace(/\.mdx?$/, "");
@@ -428,7 +511,7 @@ var slugFromPath = (relPath) => {
428
511
 
429
512
  // src/lib/walk.ts
430
513
  import { readdirSync, statSync } from "fs";
431
- import { join as join2 } from "path";
514
+ import { join as join3 } from "path";
432
515
  var DEFAULT_SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "coverage", ".doc-bridge"]);
433
516
  var walkFiles = (root, opts) => {
434
517
  const extensions = opts?.extensions ?? [".md"];
@@ -442,7 +525,7 @@ var walkFiles = (root, opts) => {
442
525
  return;
443
526
  }
444
527
  for (const name of entries) {
445
- const abs = join2(dir, name);
528
+ const abs = join3(dir, name);
446
529
  let st;
447
530
  try {
448
531
  st = statSync(abs);
@@ -466,17 +549,19 @@ var walkFiles = (root, opts) => {
466
549
 
467
550
  // src/index-builder/scan-corpus.ts
468
551
  var scanAgentCorpus = (root, config) => {
469
- const agentRoot = join3(root, config.corpus.agent.root);
552
+ const agentRoot = join4(root, config.corpus.agent.root);
470
553
  const files = walkFiles(agentRoot, { extensions: [".md", ".mdx"] });
471
554
  const corpusRelRoot = toPosix(config.corpus.agent.root);
472
555
  return files.map((abs) => {
473
556
  const relToCorpus = toPosix(abs.replace(`${toPosix(agentRoot)}/`, ""));
474
557
  const relPath = `${corpusRelRoot}/${relToCorpus}`;
475
- const raw = readFileSync2(abs, "utf8");
558
+ const raw = readFileSync3(abs, "utf8");
476
559
  const { data: frontmatter } = parseFrontmatter(raw);
477
560
  const id = frontmatterString(frontmatter, "id") ?? frontmatterString(frontmatter, "package") ?? slugFromPath(relToCorpus);
478
561
  const title = firstHeading(raw) ?? id;
479
- const description = firstParagraph(raw);
562
+ const purpose = frontmatterString(frontmatter, "purpose");
563
+ const description = purpose ?? firstParagraph(raw, 400);
564
+ const body = extractSearchBody(raw);
480
565
  return {
481
566
  id,
482
567
  type: "agent-doc",
@@ -485,21 +570,35 @@ var scanAgentCorpus = (root, config) => {
485
570
  absPath: abs,
486
571
  relPath,
487
572
  frontmatter,
488
- ...description ? { description } : {}
573
+ ...description ? { description } : {},
574
+ ...body ? { body } : {}
489
575
  };
490
576
  });
491
577
  };
492
578
  var guessAgentDocForPackage = (corpus, packageId) => {
493
- const exact = corpus.find(
494
- (doc) => frontmatterString(doc.frontmatter, "package") === packageId || doc.relPath.endsWith(`/packages/${packageId}.md`) || doc.relPath.endsWith(`/${packageId}.md`) || doc.id === packageId
495
- );
496
- return exact?.path;
579
+ const candidates = [
580
+ (doc) => frontmatterString(doc.frontmatter, "package") === packageId,
581
+ (doc) => doc.id === packageId,
582
+ (doc) => doc.relPath.endsWith(`/packages/${packageId}.md`),
583
+ (doc) => doc.relPath.endsWith(`/packages/${packageId}.mdx`),
584
+ (doc) => doc.relPath.endsWith(`/packages/${packageId}/index.md`),
585
+ (doc) => doc.relPath.endsWith(`/packages/${packageId}/index.mdx`),
586
+ (doc) => doc.relPath.endsWith(`/${packageId}.md`),
587
+ (doc) => doc.relPath.endsWith(`/${packageId}.mdx`),
588
+ (doc) => doc.relPath.includes(`/packages/${packageId}/`)
589
+ ];
590
+ for (const match of candidates) {
591
+ const hit = corpus.find(match);
592
+ if (hit) return hit.path;
593
+ }
594
+ return void 0;
497
595
  };
498
596
  var ownershipFromFrontmatter = (corpus) => {
499
597
  const out = [];
500
598
  for (const doc of corpus) {
501
- const id = frontmatterString(doc.frontmatter, "package") ?? (frontmatterString(doc.frontmatter, "type") === "package" ? frontmatterString(doc.frontmatter, "id") ?? doc.id : void 0);
502
- const path = frontmatterString(doc.frontmatter, "editRoot") ?? frontmatterString(doc.frontmatter, "path");
599
+ const type = frontmatterString(doc.frontmatter, "type");
600
+ const id = frontmatterString(doc.frontmatter, "package") ?? (type === "package" || type === "module" || type === "pattern" ? frontmatterString(doc.frontmatter, "id") ?? doc.id : void 0);
601
+ const path = frontmatterString(doc.frontmatter, "editRoot") ?? frontmatterString(doc.frontmatter, "path") ?? (type === "package" || type === "module" ? `packages/${id}` : void 0);
503
602
  if (!id || !path) continue;
504
603
  const purpose = frontmatterString(doc.frontmatter, "purpose") ?? doc.description;
505
604
  const checks = frontmatterStringList(doc.frontmatter, "checks");
@@ -515,13 +614,55 @@ var ownershipFromFrontmatter = (corpus) => {
515
614
  }
516
615
  return out;
517
616
  };
617
+ var ownershipFromCorpus = (corpus) => {
618
+ const out = [];
619
+ const seen = /* @__PURE__ */ new Set();
620
+ for (const doc of corpus) {
621
+ if (frontmatterString(doc.frontmatter, "package") && frontmatterString(doc.frontmatter, "editRoot")) {
622
+ continue;
623
+ }
624
+ let id;
625
+ let path;
626
+ let purpose = frontmatterString(doc.frontmatter, "purpose") ?? doc.description;
627
+ const packagesFile = /(?:^|\/)packages\/([^/]+)\.(?:md|mdx)$/.exec(doc.relPath);
628
+ const packagesIndex = /(?:^|\/)packages\/([^/]+)\/index\.(?:md|mdx)$/.exec(doc.relPath);
629
+ const registryReadme = /(?:^|\/)registry\/([^/]+)\/README\.(?:md|mdx)$/i.exec(doc.relPath);
630
+ const patternFile = /(?:^|\/)pillars\/[^/]+\/([^/]+(?:-pattern)?)\.(?:md|mdx)$/.exec(doc.relPath);
631
+ const topLevelPkg = /(?:^|\/)for-agents\/([^/]+)\.(?:md|mdx)$/.exec(doc.relPath);
632
+ if (packagesFile?.[1] && packagesFile[1] !== "index") {
633
+ id = packagesFile[1];
634
+ path = `packages/${id}`;
635
+ } else if (packagesIndex?.[1]) {
636
+ id = packagesIndex[1];
637
+ path = `packages/${id}`;
638
+ } else if (registryReadme?.[1]) {
639
+ id = registryReadme[1];
640
+ path = `registry/${id}`;
641
+ } else if (patternFile?.[1] && patternFile[1] !== "index" && patternFile[1] !== "universal") {
642
+ id = patternFile[1];
643
+ path = doc.path;
644
+ purpose = purpose ?? `Playbook pattern: ${id}`;
645
+ } else if (topLevelPkg?.[1] && topLevelPkg[1] !== "index" && topLevelPkg[1] !== "INDEX") {
646
+ id = topLevelPkg[1];
647
+ path = `packages/${id}`;
648
+ }
649
+ if (!id || !path || seen.has(id)) continue;
650
+ seen.add(id);
651
+ const humanDoc = frontmatterString(doc.frontmatter, "humanDoc");
652
+ const checks = frontmatterStringList(doc.frontmatter, "checks");
653
+ out.push({
654
+ id,
655
+ path,
656
+ agentDoc: doc.path,
657
+ ...purpose ? { purpose } : {},
658
+ ...checks ? { checks } : {},
659
+ ...humanDoc ? { humanDoc } : {}
660
+ });
661
+ }
662
+ return out;
663
+ };
518
664
 
519
665
  // src/index-builder/build-handoffs.ts
520
- var defaultChecks = (config) => {
521
- const preset = config.gates?.preset ?? "minimal";
522
- if (preset === "strict" || preset === "standard") return ["npm test", "npm run lint"];
523
- return ["npm test"];
524
- };
525
666
  var collectPackages = (config, discovered, corpus) => {
526
667
  const byId = /* @__PURE__ */ new Map();
527
668
  for (const [id, entry] of Object.entries(config.routing?.options?.ownership ?? {})) {
@@ -539,7 +680,11 @@ var collectPackages = (config, discovered, corpus) => {
539
680
  ...pkg.name ? { name: pkg.name } : existing.name ? { name: existing.name } : {}
540
681
  });
541
682
  }
542
- for (const seed of ownershipFromFrontmatter(corpus)) {
683
+ const seeds = [
684
+ ...ownershipFromFrontmatter(corpus),
685
+ ...config.routing?.options?.ownershipFromCorpus === false ? [] : ownershipFromCorpus(corpus)
686
+ ];
687
+ for (const seed of seeds) {
543
688
  const existing = byId.get(seed.id);
544
689
  if (!existing) {
545
690
  byId.set(seed.id, { id: seed.id, path: seed.path });
@@ -551,22 +696,59 @@ var collectPackages = (config, discovered, corpus) => {
551
696
  }
552
697
  return [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
553
698
  };
554
- var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}) => {
699
+ var resolveHumanDoc = (packageId, override, fmHuman, humanDocs = {}) => {
700
+ if (override) return override;
701
+ if (fmHuman) return fmHuman;
702
+ if (humanDocs[packageId]) return humanDocs[packageId];
703
+ const aliases = [
704
+ packageId,
705
+ packageId.replace(/^@[^/]+\//, ""),
706
+ packageId.replace(/^os-/, ""),
707
+ packageId.replace(/-pattern$/, ""),
708
+ `packages/${packageId}`,
709
+ `reference/packages/${packageId}`,
710
+ `packages/${packageId}/index`
711
+ ];
712
+ for (const alias of aliases) {
713
+ if (humanDocs[alias]) return humanDocs[alias];
714
+ }
715
+ for (const [key, url] of Object.entries(humanDocs)) {
716
+ if (key === packageId || key.endsWith(`/${packageId}`) || key.endsWith(`/${packageId}/index`) || key.endsWith(`-${packageId}`)) {
717
+ return url;
718
+ }
719
+ }
720
+ return void 0;
721
+ };
722
+ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root = process.cwd()) => {
555
723
  const ownership = {};
556
724
  const handoffs = {};
557
- const checks = defaultChecks(config);
558
- const fmSeeds = new Map(ownershipFromFrontmatter(corpus).map((seed) => [seed.id, seed]));
725
+ const strict = (config.gates?.preset ?? "minimal") !== "minimal";
726
+ const fmSeeds = new Map(
727
+ [...ownershipFromFrontmatter(corpus), ...ownershipFromCorpus(corpus)].map((seed) => [
728
+ seed.id,
729
+ seed
730
+ ])
731
+ );
559
732
  for (const pkg of packages) {
560
733
  const override = config.routing?.options?.ownership?.[pkg.id];
561
734
  const fm = fmSeeds.get(pkg.id);
562
735
  const agentDoc = override?.agentDoc ?? fm?.agentDoc ?? guessAgentDocForPackage(corpus, pkg.id) ?? config.corpus.agent.index;
563
736
  const startHere = agentDoc ?? "";
564
737
  const purpose = override?.purpose ?? fm?.purpose;
565
- const humanDoc = override?.humanDoc ?? fm?.humanDoc ?? humanDocs[pkg.id];
738
+ const path = override?.path ?? fm?.path ?? pkg.path;
739
+ const checks = [
740
+ ...override?.checks ?? fm?.checks ?? defaultChecksForTarget(root, {
741
+ packageId: pkg.id,
742
+ packagePath: path,
743
+ ...pkg.name ? { packageName: pkg.name } : {},
744
+ strict
745
+ })
746
+ ];
747
+ const humanDoc = resolveHumanDoc(pkg.id, override?.humanDoc, fm?.humanDoc, humanDocs);
566
748
  const record = {
567
749
  id: pkg.id,
568
- path: override?.path ?? fm?.path ?? pkg.path,
569
- checks: [...override?.checks ?? fm?.checks ?? checks],
750
+ path,
751
+ checks,
570
752
  ...override?.group ? { group: override.group } : {},
571
753
  ...override?.layer ? { layer: override.layer } : {},
572
754
  ...purpose ? { purpose } : {},
@@ -620,7 +802,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}) => {
620
802
  };
621
803
 
622
804
  // src/version.ts
623
- var PACKAGE_VERSION = "0.1.0-alpha.1";
805
+ var PACKAGE_VERSION = "0.1.0-alpha.3";
624
806
 
625
807
  // src/index-builder/capabilities.ts
626
808
  var renderCapabilitiesJson = (config, index, paths) => {
@@ -689,13 +871,13 @@ ${lines.join("\n")}
689
871
  };
690
872
 
691
873
  // src/index-builder/human-adapters/docusaurus.ts
692
- import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
693
- import { join as join5 } from "path";
874
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
875
+ import { join as join6 } from "path";
694
876
  import vm2 from "vm";
695
877
 
696
878
  // src/index-builder/human-adapters/core.ts
697
- import { readFileSync as readFileSync3 } from "fs";
698
- import { join as join4 } from "path";
879
+ import { readFileSync as readFileSync4 } from "fs";
880
+ import { join as join5 } from "path";
699
881
  var optionString = (options, keys) => {
700
882
  for (const key of keys) {
701
883
  const value = options?.[key];
@@ -726,10 +908,10 @@ var humanUrl = (slug, urlPrefix) => {
726
908
  };
727
909
  var scanMarkdownDocs = (root, humanRoot, options) => {
728
910
  const out = [];
729
- const absRoot = join4(root, humanRoot);
911
+ const absRoot = join5(root, humanRoot);
730
912
  for (const abs of walkFiles(absRoot, { extensions: [".md", ".mdx"] })) {
731
913
  const relToHumanRoot = toPosix(abs.replace(`${toPosix(absRoot)}/`, ""));
732
- const raw = readFileSync3(abs, "utf8");
914
+ const raw = readFileSync4(abs, "utf8");
733
915
  if (options?.includeRelPath && !options.includeRelPath(relToHumanRoot, raw)) continue;
734
916
  out.push({
735
917
  id: options?.idForDoc?.(relToHumanRoot, raw) ?? docId(relToHumanRoot, raw),
@@ -768,8 +950,8 @@ var docusaurusRecordId = (relPath, raw) => {
768
950
  return frontmatter.package ?? frontmatter.module ?? docusaurusSidebarId(relPath, raw);
769
951
  };
770
952
  var sidebarsValue = (file) => {
771
- if (!existsSync2(file)) return void 0;
772
- const raw = readFileSync4(file, "utf8").replace(/import\s+type\s+[\s\S]*?;?\n/g, "").replace(/:\s*[A-Za-z0-9_.$<>{}\[\],\s]+(?=\s*=)/g, "").replace(/\s+satisfies\s+[A-Za-z0-9_.$<>{}\[\],\s]+(?=\s*(?:;|\n|$))/g, "").replace(/export\s+default/, "module.exports =");
953
+ if (!existsSync3(file)) return void 0;
954
+ const raw = readFileSync5(file, "utf8").replace(/import\s+type\s+[\s\S]*?;?\n/g, "").replace(/:\s*[A-Za-z0-9_.$<>{}\[\],\s]+(?=\s*=)/g, "").replace(/\s+satisfies\s+[A-Za-z0-9_.$<>{}\[\],\s]+(?=\s*(?:;|\n|$))/g, "").replace(/export\s+default/, "module.exports =");
773
955
  const sandbox = { module: { exports: {} }, exports: {} };
774
956
  vm2.runInNewContext(raw, sandbox, { timeout: 250 });
775
957
  return sandbox.module.exports;
@@ -799,7 +981,7 @@ var visitSidebar = (value, filter) => {
799
981
  };
800
982
  var readSidebars = (root, sidebarsFile) => {
801
983
  if (!sidebarsFile) return { enabled: false, ids: /* @__PURE__ */ new Set(), autogenDirs: [] };
802
- const value = sidebarsValue(join5(root, sidebarsFile));
984
+ const value = sidebarsValue(join6(root, sidebarsFile));
803
985
  const filter = { ids: /* @__PURE__ */ new Set(), autogenDirs: [] };
804
986
  visitSidebar(value, filter);
805
987
  return { enabled: true, ...filter };
@@ -828,13 +1010,13 @@ var docusaurusAdapter = {
828
1010
  };
829
1011
 
830
1012
  // src/index-builder/human-adapters/fumadocs.ts
831
- import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
832
- import { join as join6 } from "path";
1013
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
1014
+ import { join as join7 } from "path";
833
1015
  var readMetaPages = (dir) => {
834
- const file = join6(dir, "meta.json");
835
- if (!existsSync3(file)) return void 0;
1016
+ const file = join7(dir, "meta.json");
1017
+ if (!existsSync4(file)) return void 0;
836
1018
  try {
837
- const meta = JSON.parse(readFileSync5(file, "utf8"));
1019
+ const meta = JSON.parse(readFileSync6(file, "utf8"));
838
1020
  return Array.isArray(meta.pages) ? meta.pages.filter((page) => typeof page === "string") : void 0;
839
1021
  } catch {
840
1022
  return void 0;
@@ -849,7 +1031,7 @@ var isListedByMeta = (contentRoot, relPath) => {
849
1031
  if (isDotFile(relPath)) return false;
850
1032
  const parts = relPath.split("/");
851
1033
  for (let i = 0; i < parts.length; i += 1) {
852
- const dir = join6(contentRoot, ...parts.slice(0, i));
1034
+ const dir = join7(contentRoot, ...parts.slice(0, i));
853
1035
  const pages = readMetaPages(dir);
854
1036
  if (!pages?.length || pages.includes("...")) continue;
855
1037
  const key = pageKey(parts[i] ?? "");
@@ -862,9 +1044,16 @@ var fumadocsAdapter = {
862
1044
  scan: ({ root, config }) => {
863
1045
  const contentDir = optionString(config.options, ["contentDir", "root"]);
864
1046
  if (!contentDir) return [];
865
- const contentRoot = join6(root, contentDir);
1047
+ const contentRoot = join7(root, contentDir);
1048
+ const excludePrefixes = Array.isArray(config.options?.excludePrefixes) ? config.options.excludePrefixes.filter((v) => typeof v === "string") : typeof config.options?.excludePrefix === "string" ? [config.options.excludePrefix] : [];
1049
+ if (!excludePrefixes.includes("for-agents")) excludePrefixes.push("for-agents");
866
1050
  return scanMarkdownDocs(root, contentDir, {
867
- includeRelPath: (relPath) => isListedByMeta(contentRoot, relPath),
1051
+ includeRelPath: (relPath) => {
1052
+ if (excludePrefixes.some((prefix) => relPath === prefix || relPath.startsWith(`${prefix}/`))) {
1053
+ return false;
1054
+ }
1055
+ return isListedByMeta(contentRoot, relPath);
1056
+ },
868
1057
  urlPrefix: config.options?.urlPrefix,
869
1058
  stripGroups: true
870
1059
  });
@@ -875,7 +1064,7 @@ var fumadocsAdapter = {
875
1064
  var plainMarkdownAdapter = {
876
1065
  plugin: "plain-markdown",
877
1066
  scan: ({ root, config }) => {
878
- const humanRoot = optionString(config.options, ["root"]) ?? "docs";
1067
+ const humanRoot = optionString(config.options, ["contentDir", "root", "docsDir"]) ?? "docs";
879
1068
  return scanMarkdownDocs(root, humanRoot, { urlPrefix: config.options?.urlPrefix });
880
1069
  }
881
1070
  };
@@ -894,10 +1083,14 @@ var humanConfigs = (config) => {
894
1083
  var scanHumanDocRecords = (root, config) => {
895
1084
  const out = [];
896
1085
  const seen = /* @__PURE__ */ new Set();
1086
+ const agentRoot = config.corpus.agent.root.replace(/\/$/, "");
897
1087
  for (const human of humanConfigs(config)) {
898
1088
  const adapter = ADAPTERS.find((candidate) => candidate.plugin === human.plugin);
899
1089
  if (!adapter) continue;
900
1090
  for (const record of adapter.scan({ root, config: human })) {
1091
+ if (record.path === agentRoot || record.path.startsWith(`${agentRoot}/`) || record.path.includes("/for-agents/") || record.path.endsWith("/for-agents")) {
1092
+ continue;
1093
+ }
901
1094
  if (seen.has(record.id)) continue;
902
1095
  seen.add(record.id);
903
1096
  out.push(record);
@@ -908,26 +1101,26 @@ var scanHumanDocRecords = (root, config) => {
908
1101
  var scanHumanDocs = (root, config) => Object.fromEntries(scanHumanDocRecords(root, config).map((doc) => [doc.id, doc.url]));
909
1102
 
910
1103
  // src/index-builder/plugins/pnpm-monorepo.ts
911
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
912
- import { basename as basename2, join as join8 } from "path";
1104
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
1105
+ import { basename as basename2, join as join9 } from "path";
913
1106
 
914
1107
  // src/lib/glob-expand.ts
915
- import { existsSync as existsSync4, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
916
- import { join as join7 } from "path";
1108
+ import { existsSync as existsSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
1109
+ import { join as join8 } from "path";
917
1110
  var expandWorkspaceGlobs = (root, patterns) => {
918
1111
  const dirs = /* @__PURE__ */ new Set();
919
1112
  for (const pattern of patterns) {
920
1113
  const normalized = toPosix(pattern).replace(/\/$/, "");
921
1114
  if (!normalized.includes("*")) {
922
- const abs = join7(root, normalized);
923
- if (existsSync4(abs)) dirs.add(toPosix(abs));
1115
+ const abs = join8(root, normalized);
1116
+ if (existsSync5(abs)) dirs.add(toPosix(abs));
924
1117
  continue;
925
1118
  }
926
1119
  const star = normalized.indexOf("*");
927
- const base = join7(root, normalized.slice(0, star).replace(/\/$/, ""));
928
- if (!existsSync4(base)) continue;
1120
+ const base = join8(root, normalized.slice(0, star).replace(/\/$/, ""));
1121
+ if (!existsSync5(base)) continue;
929
1122
  for (const name of readdirSync2(base)) {
930
- const abs = join7(base, name);
1123
+ const abs = join8(base, name);
931
1124
  try {
932
1125
  if (statSync2(abs).isDirectory()) dirs.add(toPosix(abs));
933
1126
  } catch {
@@ -959,10 +1152,10 @@ var parsePnpmWorkspace = (yaml) => {
959
1152
  return patterns;
960
1153
  };
961
1154
  var readPackageJson = (dir) => {
962
- const file = join8(dir, "package.json");
963
- if (!existsSync5(file)) return null;
1155
+ const file = join9(dir, "package.json");
1156
+ if (!existsSync6(file)) return null;
964
1157
  try {
965
- return JSON.parse(readFileSync6(file, "utf8"));
1158
+ return JSON.parse(readFileSync7(file, "utf8"));
966
1159
  } catch {
967
1160
  return null;
968
1161
  }
@@ -971,9 +1164,9 @@ var discoverPnpmPackages = (root, config) => {
971
1164
  const explicit = config.routing?.options?.packages;
972
1165
  let patterns = explicit;
973
1166
  if (!patterns?.length) {
974
- const workspaceFile = join8(root, "pnpm-workspace.yaml");
975
- if (existsSync5(workspaceFile)) {
976
- patterns = parsePnpmWorkspace(readFileSync6(workspaceFile, "utf8"));
1167
+ const workspaceFile = join9(root, "pnpm-workspace.yaml");
1168
+ if (existsSync6(workspaceFile)) {
1169
+ patterns = parsePnpmWorkspace(readFileSync7(workspaceFile, "utf8"));
977
1170
  }
978
1171
  }
979
1172
  if (!patterns?.length) return [];
@@ -994,7 +1187,7 @@ var discoverPnpmPackages = (root, config) => {
994
1187
  var projectName = (root, config) => {
995
1188
  if (config.project?.name) return config.project.name;
996
1189
  try {
997
- const pkg = JSON.parse(readFileSync7(join9(root, "package.json"), "utf8"));
1190
+ const pkg = JSON.parse(readFileSync8(join10(root, "package.json"), "utf8"));
998
1191
  if (pkg.name) return pkg.name;
999
1192
  } catch {
1000
1193
  }
@@ -1002,7 +1195,7 @@ var projectName = (root, config) => {
1002
1195
  };
1003
1196
  var existingGeneratedAt = (indexPath, contentHash) => {
1004
1197
  try {
1005
- const index = JSON.parse(readFileSync7(indexPath, "utf8"));
1198
+ const index = JSON.parse(readFileSync8(indexPath, "utf8"));
1006
1199
  return index.contentHash === contentHash && typeof index.generatedAt === "string" ? index.generatedAt : void 0;
1007
1200
  } catch {
1008
1201
  return void 0;
@@ -1013,14 +1206,14 @@ var buildDocBridgeIndex = (opts) => {
1013
1206
  const config = opts.config;
1014
1207
  const write = opts.write ?? true;
1015
1208
  const outFile = config.index?.outFile ?? ".doc-bridge/index.json";
1016
- const indexPath = join9(root, outFile);
1209
+ const indexPath = join10(root, outFile);
1017
1210
  const corpus = scanAgentCorpus(root, config);
1018
1211
  const knowledge = corpus.map(({ absPath: _a, relPath: _r, frontmatter: _f, ...entry }) => entry);
1019
1212
  const shouldDiscover = config.routing?.plugin === "pnpm-monorepo" || Boolean(config.routing?.options?.packages?.length) || config.routing?.plugin === "npm-workspaces" || config.routing?.plugin === "yarn-workspaces";
1020
1213
  const discovered = shouldDiscover ? discoverPnpmPackages(root, config) : [];
1021
1214
  const packages = collectPackages(config, discovered, corpus);
1022
1215
  const humanDocs = scanHumanDocs(root, config);
1023
- const { lookup, handoffs } = buildLookup(config, packages, corpus, outFile, humanDocs);
1216
+ const { lookup, handoffs } = buildLookup(config, packages, corpus, outFile, humanDocs, root);
1024
1217
  const hashPayload = {
1025
1218
  schemaVersion: 1,
1026
1219
  knowledge,
@@ -1048,7 +1241,7 @@ var buildDocBridgeIndex = (opts) => {
1048
1241
  if (config.index?.llmsTxt?.enabled !== false) {
1049
1242
  const llmsOut = config.index?.llmsTxt?.outFile ?? "llms.txt";
1050
1243
  llmsTxtRelPath = toPosix(llmsOut);
1051
- llmsTxtPath = join9(root, llmsOut);
1244
+ llmsTxtPath = join10(root, llmsOut);
1052
1245
  if (write) {
1053
1246
  writeFileSync(
1054
1247
  llmsTxtPath,
@@ -1060,7 +1253,7 @@ var buildDocBridgeIndex = (opts) => {
1060
1253
  let capabilitiesPath;
1061
1254
  if (config.index?.capabilities?.enabled !== false) {
1062
1255
  const capabilitiesOut = config.index?.capabilities?.outFile ?? ".doc-bridge/capabilities.json";
1063
- capabilitiesPath = join9(root, capabilitiesOut);
1256
+ capabilitiesPath = join10(root, capabilitiesOut);
1064
1257
  if (write) {
1065
1258
  mkdirSync(dirname3(capabilitiesPath), { recursive: true });
1066
1259
  writeFileSync(
@@ -1082,23 +1275,62 @@ var buildDocBridgeIndex = (opts) => {
1082
1275
  };
1083
1276
 
1084
1277
  // src/federation/llms.ts
1085
- import { existsSync as existsSync6, readFileSync as readFileSync8 } from "fs";
1278
+ import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
1086
1279
  import { resolve as resolve3 } from "path";
1087
1280
 
1088
1281
  // src/query/search.ts
1089
- var tokenize = (value) => value.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 2);
1282
+ var tokenize = (value) => value.toLowerCase().split(/[^a-z0-9@/_-]+/).filter((t) => t.length >= 2);
1283
+ var PACKAGE_INTENT = /\b(package|module|pkg|edit|change|where|owns?|ownership|handoff|start)\b/i;
1284
+ var scoreHay = (tokens, hay, weight = 1) => {
1285
+ let score = 0;
1286
+ for (const token of tokens) {
1287
+ if (!hay.includes(token)) continue;
1288
+ score += token.length * weight;
1289
+ if (new RegExp(`(?:^|[^a-z0-9])${token}(?:[^a-z0-9]|$)`).test(hay)) {
1290
+ score += token.length;
1291
+ }
1292
+ }
1293
+ return score;
1294
+ };
1295
+ var identityBoost = (id, path, tokens, term) => {
1296
+ const idLower = id.toLowerCase();
1297
+ const termLower = term.toLowerCase().trim();
1298
+ const base = path.split("/").pop()?.replace(/\.mdx?$/i, "").toLowerCase() ?? "";
1299
+ let boost = 0;
1300
+ if (idLower === termLower || base === termLower) boost += 200;
1301
+ if (tokens.length === 1 && (idLower === tokens[0] || base === tokens[0])) boost += 200;
1302
+ for (const token of tokens) {
1303
+ if (idLower === token) boost += 120;
1304
+ else if (idLower.startsWith(`${token}-`) || idLower.endsWith(`-${token}`)) boost += 40;
1305
+ else if (idLower.includes(token) && idLower.length <= token.length + 4) boost += 30;
1306
+ if (base === token) boost += 100;
1307
+ }
1308
+ if (tokens.includes(idLower)) boost += Math.max(0, 40 - idLower.length);
1309
+ return boost;
1310
+ };
1311
+ var preferOwnership = (term) => PACKAGE_INTENT.test(term) || /^(where|how).*(edit|change|package|module)/i.test(term);
1090
1312
  var searchIndex = (index, term, limit = 20) => {
1091
1313
  const tokens = tokenize(term);
1092
1314
  if (!tokens.length) return [];
1093
- const matches = [];
1094
- for (const entry of index.knowledge) {
1095
- const hay = `${entry.id} ${entry.title} ${entry.description ?? ""} ${entry.path}`.toLowerCase();
1096
- let score = 0;
1097
- for (const token of tokens) {
1098
- if (hay.includes(token)) score += token.length;
1315
+ const wantOwnership = preferOwnership(term);
1316
+ const byPath = /* @__PURE__ */ new Map();
1317
+ const consider = (match) => {
1318
+ const key = match.path;
1319
+ const existing = byPath.get(key);
1320
+ if (!existing) {
1321
+ byPath.set(key, match);
1322
+ return;
1099
1323
  }
1324
+ const prefer = match.score > existing.score || match.score === existing.score && match.type === "ownership" && existing.type !== "ownership" || wantOwnership && match.type === "ownership" && existing.type !== "ownership" && match.score >= existing.score - 20;
1325
+ if (prefer) byPath.set(key, match);
1326
+ };
1327
+ for (const entry of index.knowledge) {
1328
+ const body = entry.body ?? "";
1329
+ const hay = `${entry.id} ${entry.title} ${entry.description ?? ""} ${entry.path} ${body}`.toLowerCase();
1330
+ let score = scoreHay(tokens, hay, 1);
1331
+ score += identityBoost(entry.id, entry.path, tokens, term);
1100
1332
  if (score > 0) {
1101
- matches.push({
1333
+ consider({
1102
1334
  type: "knowledge",
1103
1335
  id: entry.id,
1104
1336
  path: entry.path,
@@ -1108,22 +1340,31 @@ var searchIndex = (index, term, limit = 20) => {
1108
1340
  }
1109
1341
  }
1110
1342
  for (const [id, owner] of Object.entries(index.lookup?.ownership ?? {})) {
1111
- const hay = `${id} ${owner.path} ${owner.purpose ?? ""} ${owner.group ?? ""}`.toLowerCase();
1112
- let score = 0;
1113
- for (const token of tokens) {
1114
- if (hay.includes(token)) score += token.length * 2;
1115
- }
1343
+ const path = owner.agentDoc ?? owner.path;
1344
+ const hay = `${id} ${owner.path} ${owner.purpose ?? ""} ${owner.group ?? ""} ${owner.agentDoc ?? ""} ${owner.humanDoc ?? ""}`.toLowerCase();
1345
+ let score = scoreHay(tokens, hay, 2);
1346
+ score += identityBoost(id, path, tokens, term);
1347
+ if (wantOwnership) score += 25;
1348
+ score += 15;
1116
1349
  if (score > 0) {
1117
- matches.push({
1350
+ consider({
1118
1351
  type: "ownership",
1119
1352
  id,
1120
- path: owner.agentDoc ?? owner.path,
1353
+ path,
1121
1354
  ...owner.purpose ? { summary: owner.purpose } : {},
1122
1355
  score
1123
1356
  });
1124
1357
  }
1125
1358
  }
1126
- return matches.sort((a, b) => b.score - a.score).slice(0, limit);
1359
+ return [...byPath.values()].sort((a, b) => {
1360
+ if (b.score !== a.score) return b.score - a.score;
1361
+ const aExact = tokens.includes(a.id.toLowerCase()) ? 1 : 0;
1362
+ const bExact = tokens.includes(b.id.toLowerCase()) ? 1 : 0;
1363
+ if (bExact !== aExact) return bExact - aExact;
1364
+ if (a.type === "ownership" && b.type !== "ownership") return -1;
1365
+ if (b.type === "ownership" && a.type !== "ownership") return 1;
1366
+ return a.id.localeCompare(b.id);
1367
+ }).slice(0, limit);
1127
1368
  };
1128
1369
 
1129
1370
  // src/retriever/doc-bridge-retriever.ts
@@ -1162,10 +1403,14 @@ var defaultFetchText = async (url) => {
1162
1403
  return res.text();
1163
1404
  };
1164
1405
  var sourceText = async (root, source, fetchText) => {
1165
- if (/^https?:\/\//.test(source)) return fetchText(source);
1166
- const path = resolve3(root, source);
1167
- if (!existsSync6(path)) throw new Error(`Federation source not found: ${source}`);
1168
- return readFileSync8(path, "utf8");
1406
+ try {
1407
+ if (/^https?:\/\//.test(source)) return await fetchText(source);
1408
+ const path = resolve3(root, source);
1409
+ if (!existsSync7(path)) return null;
1410
+ return readFileSync9(path, "utf8");
1411
+ } catch {
1412
+ return null;
1413
+ }
1169
1414
  };
1170
1415
  var sameOrigin = (base, target) => {
1171
1416
  if (!/^https?:\/\//.test(base) || !/^https?:\/\//.test(target)) return true;
@@ -1204,22 +1449,28 @@ var chunksFromMarkdown = (property, raw, sourceUrl) => {
1204
1449
  var loadFederatedChunks = async (root, config, options = {}) => {
1205
1450
  const fetchText = options.fetchText ?? defaultFetchText;
1206
1451
  const chunks = [];
1452
+ const warnings = [];
1207
1453
  for (const source of config.federation?.sources ?? []) {
1208
1454
  if (source.includeInRetriever === false || !source.llmsTxt) continue;
1209
1455
  const llms = await sourceText(root, source.llmsTxt, fetchText);
1456
+ if (!llms) {
1457
+ warnings.push(`federation source skipped (unavailable): ${source.id} \u2192 ${source.llmsTxt}`);
1458
+ continue;
1459
+ }
1210
1460
  chunks.push(...chunksFromMarkdown(source.id, llms, source.llmsTxt));
1211
1461
  const links = parseLlmsTxtLinks(llms);
1212
1462
  for (const link of links) {
1213
1463
  const url = !/^https?:\/\//.test(link.url) && source.rawBaseUrl ? `${source.rawBaseUrl.replace(/\/$/, "")}/${link.url.replace(/^\//, "")}` : link.url;
1214
1464
  if (!/\.(md|txt)(?:$|\?)/.test(url)) continue;
1215
1465
  if (!sameOrigin(source.llmsTxt, url)) continue;
1216
- try {
1217
- const raw = await sourceText(root, url, fetchText);
1218
- chunks.push(...chunksFromMarkdown(source.id, raw, url));
1219
- } catch {
1220
- }
1466
+ const raw = await sourceText(root, url, fetchText);
1467
+ if (raw) chunks.push(...chunksFromMarkdown(source.id, raw, url));
1221
1468
  }
1222
1469
  }
1470
+ if (warnings.length && process.stderr.isTTY) {
1471
+ for (const w of warnings) process.stderr.write(`${w}
1472
+ `);
1473
+ }
1223
1474
  return chunks;
1224
1475
  };
1225
1476
  var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
@@ -1241,11 +1492,11 @@ var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
1241
1492
  };
1242
1493
 
1243
1494
  // src/gates/run-gates.ts
1244
- import { readFileSync as readFileSync10 } from "fs";
1495
+ import { readFileSync as readFileSync11 } from "fs";
1245
1496
 
1246
1497
  // src/query/load-index.ts
1247
- import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
1248
- import { join as join10, resolve as resolve4 } from "path";
1498
+ import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
1499
+ import { join as join11, resolve as resolve4 } from "path";
1249
1500
 
1250
1501
  // src/schemas/agent-handoff.ts
1251
1502
  import { z as z2 } from "zod";
@@ -1319,7 +1570,9 @@ var KnowledgeEntrySchema = z3.object({
1319
1570
  type: z3.string().min(1).max(128),
1320
1571
  title: z3.string().min(1).max(256),
1321
1572
  path: z3.string().min(1).max(512),
1322
- description: z3.string().max(1024).optional(),
1573
+ description: z3.string().max(2048).optional(),
1574
+ /** Flattened body excerpt for full-text search (not for display). */
1575
+ body: z3.string().max(8e3).optional(),
1323
1576
  links: z3.array(z3.string().min(1).max(512)).max(64).optional(),
1324
1577
  tags: z3.array(z3.string().min(1).max(64)).max(32).optional()
1325
1578
  }).strict();
@@ -1450,11 +1703,11 @@ var IndexNotFoundError = class extends Error {
1450
1703
  }
1451
1704
  path;
1452
1705
  };
1453
- var indexFilePath = (root, config) => join10(root, config.index?.outFile ?? ".doc-bridge/index.json");
1706
+ var indexFilePath = (root, config) => join11(root, config.index?.outFile ?? ".doc-bridge/index.json");
1454
1707
  var loadDocBridgeIndex = (root, config) => {
1455
1708
  const path = indexFilePath(root, config);
1456
- if (!existsSync7(path)) throw new IndexNotFoundError(path);
1457
- const raw = JSON.parse(readFileSync9(path, "utf8"));
1709
+ if (!existsSync8(path)) throw new IndexNotFoundError(path);
1710
+ const raw = JSON.parse(readFileSync10(path, "utf8"));
1458
1711
  return parseDocBridgeIndex(raw);
1459
1712
  };
1460
1713
 
@@ -1518,7 +1771,7 @@ var runOkfTypeGate = (root, config) => {
1518
1771
  const required = config.corpus.agent.okf?.requireType ?? config.gates?.preset === "strict";
1519
1772
  if (!required) return { id: "okf-type", ok: true, message: "OKF type frontmatter not required" };
1520
1773
  const allowed = config.corpus.agent.okf?.allowedTypes;
1521
- const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({ path: doc.path, type: frontmatterType(readFileSync10(doc.absPath, "utf8")) })).filter((doc) => !doc.type || allowed && !allowed.includes(doc.type));
1774
+ const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({ path: doc.path, type: frontmatterType(readFileSync11(doc.absPath, "utf8")) })).filter((doc) => !doc.type || allowed && !allowed.includes(doc.type));
1522
1775
  if (bad.length) {
1523
1776
  return {
1524
1777
  id: "okf-type",
@@ -1539,13 +1792,22 @@ var DOCS_STYLE_RULES = {
1539
1792
  "examples",
1540
1793
  "no-stale-wording"
1541
1794
  ],
1542
- "playbook-okf": ["title", "purpose", "owner-source", "no-stale-wording"]
1795
+ /** Full playbook OKF style (strict). */
1796
+ "playbook-okf": ["title", "purpose", "owner-source", "no-stale-wording"],
1797
+ /** Soft profile for large existing OKF corpora (title + no stale placeholders). */
1798
+ "playbook-okf-soft": ["title", "no-stale-wording"],
1799
+ "title-only": ["title"]
1543
1800
  };
1544
1801
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1545
1802
  var docsStyleOptions = (config) => {
1546
1803
  const raw = config.gates?.options?.["docs-style"];
1547
- if (!isRecord(raw)) return { profile: "playbook-okf", required: DOCS_STYLE_RULES["playbook-okf"] };
1548
- const profile = raw.profile === "google-dev-docs" || raw.profile === "playbook-okf" || raw.profile === "custom" ? raw.profile : "playbook-okf";
1804
+ if (!isRecord(raw)) {
1805
+ if (config.gates?.preset === "playbook") {
1806
+ return { profile: "playbook-okf-soft", required: DOCS_STYLE_RULES["playbook-okf-soft"] };
1807
+ }
1808
+ return { profile: "playbook-okf", required: DOCS_STYLE_RULES["playbook-okf"] };
1809
+ }
1810
+ const profile = raw.profile === "google-dev-docs" || raw.profile === "playbook-okf" || raw.profile === "playbook-okf-soft" || raw.profile === "title-only" || raw.profile === "custom" ? raw.profile : "playbook-okf";
1549
1811
  const customRequired = Array.isArray(raw.required) ? raw.required.filter(
1550
1812
  (rule) => [
1551
1813
  "title",
@@ -1583,7 +1845,7 @@ var runDocsStyleGate = (root, config) => {
1583
1845
  }
1584
1846
  const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({
1585
1847
  path: doc.path,
1586
- missing: missingStyleRules(readFileSync10(doc.absPath, "utf8"), required)
1848
+ missing: missingStyleRules(readFileSync11(doc.absPath, "utf8"), required)
1587
1849
  })).filter((doc) => doc.missing.length > 0);
1588
1850
  if (bad.length) {
1589
1851
  return {
@@ -1607,7 +1869,10 @@ var runGates = (root, config, ids) => {
1607
1869
  var resolveGateIds = (config) => {
1608
1870
  const preset = config.gates?.preset ?? "minimal";
1609
1871
  const ids = new Set(
1610
- preset === "minimal" ? ["index-freshness"] : preset === "strict" ? ["index-freshness", "human-guide-links", "okf-type"] : ["index-freshness", "human-guide-links"]
1872
+ preset === "minimal" ? ["index-freshness"] : preset === "strict" ? ["index-freshness", "human-guide-links", "okf-type", "docs-style"] : preset === "playbook" ? (
1873
+ // okf-type only — docs-style soft is opt-in via include (large corpora vary)
1874
+ ["index-freshness", "okf-type"]
1875
+ ) : ["index-freshness", "human-guide-links"]
1611
1876
  );
1612
1877
  for (const id of config.gates?.include ?? []) {
1613
1878
  if (SUPPORTED_GATES.includes(id)) ids.add(id);
@@ -1720,7 +1985,7 @@ var runQuery = (index, config, req) => {
1720
1985
  };
1721
1986
 
1722
1987
  // src/intelligence/adapter.ts
1723
- import { join as join11 } from "path";
1988
+ import { join as join12 } from "path";
1724
1989
 
1725
1990
  // src/intelligence/peers.ts
1726
1991
  var PeerMissingError = class extends Error {
@@ -1835,20 +2100,20 @@ var resolveIntelligenceRuntime = async (config) => {
1835
2100
  }
1836
2101
  return { adapter, embed, provider, ...model ? { model } : {} };
1837
2102
  };
1838
- var defaultVectorStorePath = (root) => join11(root, ".doc-bridge", "vectors");
2103
+ var defaultVectorStorePath = (root) => join12(root, ".doc-bridge", "vectors");
1839
2104
 
1840
2105
  // src/intelligence/rag.ts
1841
- import { readFileSync as readFileSync11 } from "fs";
1842
- import { join as join12 } from "path";
2106
+ import { readFileSync as readFileSync12 } from "fs";
2107
+ import { join as join13 } from "path";
1843
2108
  var loadDocuments = (root, index, sources) => {
1844
2109
  const includeAgent = sources.includes("agent") || sources.length === 0;
1845
2110
  const docs = [];
1846
2111
  if (includeAgent) {
1847
2112
  for (const entry of index.knowledge) {
1848
- const abs = join12(root, entry.path);
2113
+ const abs = join13(root, entry.path);
1849
2114
  let content = "";
1850
2115
  try {
1851
- content = readFileSync11(abs, "utf8");
2116
+ content = readFileSync12(abs, "utf8");
1852
2117
  } catch {
1853
2118
  content = [entry.title, entry.description].filter(Boolean).join("\n\n");
1854
2119
  }
@@ -1866,7 +2131,7 @@ var createDocBridgeRag = async (root, config, index) => {
1866
2131
  const { embed } = await resolveIntelligenceRuntime(config);
1867
2132
  const ragMod = await importPeer("@agentskit/rag");
1868
2133
  const memoryMod = await importPeer("@agentskit/memory");
1869
- const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join12(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
2134
+ const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join13(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
1870
2135
  const store = memoryMod.fileVectorMemory({ path: storePath });
1871
2136
  const rag = ragMod.createRAG({
1872
2137
  embed,
@@ -2001,15 +2266,15 @@ var startInkChat = async (root, config, index) => {
2001
2266
  };
2002
2267
 
2003
2268
  // src/memory/ingest.ts
2004
- import { existsSync as existsSync8, readFileSync as readFileSync12 } from "fs";
2005
- import { join as join13 } from "path";
2269
+ import { existsSync as existsSync9, readFileSync as readFileSync13 } from "fs";
2270
+ import { join as join14 } from "path";
2006
2271
  var memoryFact = (raw, id) => firstParagraph(raw) ?? firstHeading(raw) ?? id;
2007
2272
  var relativePath = (root, abs) => toPosix(abs).replace(`${toPosix(root)}/`, "");
2008
2273
  var ingestMarkdownDir = (root, dir, source, confidence) => {
2009
- if (!existsSync8(dir)) return [];
2274
+ if (!existsSync9(dir)) return [];
2010
2275
  return walkFiles(dir, { extensions: [".md", ".mdc"] }).map((abs) => {
2011
2276
  const rel = relativePath(root, abs);
2012
- const raw = readFileSync12(abs, "utf8");
2277
+ const raw = readFileSync13(abs, "utf8");
2013
2278
  const id = slugFromPath(rel);
2014
2279
  return {
2015
2280
  schemaVersion: 1,
@@ -2024,10 +2289,10 @@ var ingestMarkdownDir = (root, dir, source, confidence) => {
2024
2289
  });
2025
2290
  };
2026
2291
  var ingestCursorRules = (root) => {
2027
- const dir = join13(root, ".cursor", "rules");
2292
+ const dir = join14(root, ".cursor", "rules");
2028
2293
  return ingestMarkdownDir(root, dir, "cursor", 0.6);
2029
2294
  };
2030
- var ingestAgentMemory = (root) => ingestMarkdownDir(root, join13(root, ".agent-memory"), "agent-memory", 0.7);
2295
+ var ingestAgentMemory = (root) => ingestMarkdownDir(root, join14(root, ".agent-memory"), "agent-memory", 0.7);
2031
2296
  var ingestMemoryCandidates = (root) => [
2032
2297
  ...ingestAgentMemory(root),
2033
2298
  ...ingestCursorRules(root)
@@ -2113,7 +2378,7 @@ var draftMemoryPromotion = (classifications) => {
2113
2378
  };
2114
2379
 
2115
2380
  // src/mcp/server.ts
2116
- import { readFileSync as readFileSync13, realpathSync } from "fs";
2381
+ import { readFileSync as readFileSync14, realpathSync } from "fs";
2117
2382
  import { relative, resolve as resolve5 } from "path";
2118
2383
  import { z as z5, ZodError } from "zod";
2119
2384
  var MCP_TOOLS = [
@@ -2234,7 +2499,7 @@ var handleMcpRequest = (ctx, request) => {
2234
2499
  return {
2235
2500
  protocolVersion: "2024-11-05",
2236
2501
  capabilities: { tools: {} },
2237
- serverInfo: { name: "ak-docs", version: "0.1.0-alpha.1" }
2502
+ serverInfo: { name: "ak-docs", version: PACKAGE_VERSION }
2238
2503
  };
2239
2504
  }
2240
2505
  if (request.method === "tools/list") return { tools: MCP_TOOLS };
@@ -2259,7 +2524,7 @@ var handleMcpRequest = (ctx, request) => {
2259
2524
  }
2260
2525
  if (name === "doc.get") {
2261
2526
  const relPath = findDocPath(index(), parseToolArgs("doc.get", DocGetArgsSchema, args));
2262
- return textResult(readFileSync13(resolveDocPath(ctx.root, relPath), "utf8"));
2527
+ return textResult(readFileSync14(resolveDocPath(ctx.root, relPath), "utf8"));
2263
2528
  }
2264
2529
  if (name === "gate.status") return textResult(runGates(ctx.root, ctx.config));
2265
2530
  if (name === "retriever.query") {
@@ -2436,35 +2701,38 @@ var writeTextQuery = (payload) => {
2436
2701
  writeLines([
2437
2702
  `Search: ${String(data.term ?? "")}`,
2438
2703
  `Matches: ${String(data.count ?? matches.length)}`,
2439
- ...matches.map(
2440
- (match) => [match.type, match.id, match.path, match.summary].filter(Boolean).join(" ")
2441
- )
2704
+ ...matches.length ? matches.map(
2705
+ (match) => formatSearchMatch(match)
2706
+ ) : [" (none)"]
2442
2707
  ]);
2443
2708
  return;
2444
2709
  }
2445
2710
  writeLines([textValue(result.data)]);
2446
2711
  };
2712
+ var formatSearchMatch = (match) => {
2713
+ const summary = match.summary ? match.summary.replace(/\s+/g, " ").slice(0, 100) : "";
2714
+ const score = typeof match.score === "number" ? ` score=${match.score}` : "";
2715
+ return ` [${match.type}] ${match.id}${score}
2716
+ ${match.path}${summary ? `
2717
+ ${summary}` : ""}`;
2718
+ };
2447
2719
  var writeTextSearch = (term, matches) => {
2448
2720
  writeLines([
2449
2721
  `Search: ${term}`,
2450
2722
  `Matches: ${matches.length}`,
2451
- ...matches.map(
2452
- (match) => [match.type, match.id, match.path, match.summary].filter(Boolean).join(" ")
2453
- )
2723
+ ...matches.length ? matches.map(formatSearchMatch) : [" (none)"]
2454
2724
  ]);
2455
2725
  };
2456
2726
  var writeAsk = (question, matches, index) => {
2457
- const best = matches[0];
2458
- const owner = matches.find((match) => match.type === "ownership" || index.lookup?.ownership?.[match.id]);
2459
- const bestQuery = owner ? `ak-docs query ownership ${owner.id} --agent` : "ak-docs list knowledge --text";
2727
+ const owner = matches.find((match) => match.type === "ownership") ?? matches.find((match) => Boolean(index.lookup?.ownership?.[match.id]));
2728
+ const best = owner ?? matches[0];
2729
+ const bestQuery = best && (best.type === "ownership" || index.lookup?.ownership?.[best.id]) ? `ak-docs query ownership ${best.id} --agent` : "ak-docs list knowledge --text";
2460
2730
  writeLines([
2461
2731
  `Question: ${question}`,
2462
2732
  best ? `Best match: ${best.type} ${best.id} (${best.path})` : "Best match: none",
2463
2733
  "",
2464
2734
  "Matches:",
2465
- ...matches.length ? matches.slice(0, 5).map(
2466
- (match) => [match.type, match.id, match.path, match.summary].filter(Boolean).join(" ")
2467
- ) : ["No local matches. Try: ak-docs search <term>"],
2735
+ ...matches.length ? matches.slice(0, 5).map(formatSearchMatch) : [" No local matches. Try: ak-docs search <term>"],
2468
2736
  "",
2469
2737
  "Next commands:",
2470
2738
  ...best ? [
@@ -2482,7 +2750,7 @@ var readIndexedDoc = (root, config, idOrPath) => {
2482
2750
  if (abs !== rootAbs && !abs.startsWith(`${rootAbs}/`)) {
2483
2751
  throw new Error(`Indexed doc escapes project root: ${entry.path}`);
2484
2752
  }
2485
- return readFileSync14(abs, "utf8");
2753
+ return readFileSync15(abs, "utf8");
2486
2754
  };
2487
2755
  var runAskRepl = async (root, config) => {
2488
2756
  const index = loadDocBridgeIndex(root, config);
@@ -2558,7 +2826,7 @@ var diagnosticNextCommands = (config, result) => {
2558
2826
  ];
2559
2827
  };
2560
2828
  var writeIfMissing = (path, contents) => {
2561
- if (existsSync9(path)) return false;
2829
+ if (existsSync10(path)) return false;
2562
2830
  mkdirSync2(dirname4(path), { recursive: true });
2563
2831
  writeFileSync2(path, contents, "utf8");
2564
2832
  return true;
@@ -2673,7 +2941,7 @@ var bootstrapAgentDocs = (root, config) => {
2673
2941
  const created = [];
2674
2942
  const skipped = [];
2675
2943
  for (const doc of scanHumanDocRecords(root, config)) {
2676
- const raw = readFileSync14(doc.path, "utf8");
2944
+ const raw = readFileSync15(doc.path, "utf8");
2677
2945
  const body = raw.replace(/^---\n[\s\S]*?\n---\n?/, "");
2678
2946
  const title = firstHeading(body) ?? doc.id;
2679
2947
  const description = firstParagraph(body);
@@ -2742,7 +3010,7 @@ var runCli = (argv) => {
2742
3010
  }
2743
3011
  try {
2744
3012
  const abs = resolve6(file);
2745
- const raw = readFileSync14(abs, "utf8");
3013
+ const raw = readFileSync15(abs, "utf8");
2746
3014
  const handoff = parseAgentHandoff(JSON.parse(raw));
2747
3015
  writeJson({ ok: true, handoff });
2748
3016
  return 0;