@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
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ var applyConfigDefaults = (config) => {
10
10
  agent: {
11
11
  ...config.corpus.agent,
12
12
  index: agentIndex,
13
- include: config.corpus.agent.include ?? ["**/*.md"],
13
+ include: config.corpus.agent.include ?? ["**/*.{md,mdx}"],
14
14
  exclude: config.corpus.agent.exclude ?? [...DEFAULT_AGENT_EXCLUDE]
15
15
  }
16
16
  },
@@ -103,9 +103,11 @@ var OwnershipEntrySchema = z.object({
103
103
  humanDoc: z.string().min(1).max(512).optional()
104
104
  }).strict();
105
105
  var RoutingConfigSchema = z.object({
106
- plugin: z.enum(["pnpm-monorepo", "npm-workspaces", "yarn-workspaces", "custom"]).optional(),
106
+ plugin: z.enum(["pnpm-monorepo", "npm-workspaces", "yarn-workspaces", "pattern-files", "custom"]).optional(),
107
107
  options: z.object({
108
108
  packages: z.array(z.string().min(1).max(256)).max(128).optional(),
109
+ /** Infer ownership from corpus paths/frontmatter (default true). */
110
+ ownershipFromCorpus: z.boolean().optional(),
109
111
  ownership: z.record(z.string().min(1).max(256), OwnershipEntrySchema).optional(),
110
112
  intents: z.array(
111
113
  z.object({
@@ -125,7 +127,8 @@ var RoutingConfigSchema = z.object({
125
127
  }).strict().optional()
126
128
  }).strict();
127
129
  var GatesConfigSchema = z.object({
128
- preset: z.enum(["minimal", "standard", "strict"]).optional(),
130
+ /** minimal: freshness · standard: + human links · strict: + okf-type · playbook: freshness + okf soft style */
131
+ preset: z.enum(["minimal", "standard", "strict", "playbook"]).optional(),
129
132
  include: z.array(
130
133
  z.enum([
131
134
  "index-freshness",
@@ -420,7 +423,9 @@ var KnowledgeEntrySchema = z3.object({
420
423
  type: z3.string().min(1).max(128),
421
424
  title: z3.string().min(1).max(256),
422
425
  path: z3.string().min(1).max(512),
423
- description: z3.string().max(1024).optional(),
426
+ description: z3.string().max(2048).optional(),
427
+ /** Flattened body excerpt for full-text search (not for display). */
428
+ body: z3.string().max(8e3).optional(),
424
429
  links: z3.array(z3.string().min(1).max(512)).max(64).optional(),
425
430
  tags: z3.array(z3.string().min(1).max(64)).max(32).optional()
426
431
  }).strict();
@@ -625,7 +630,8 @@ var DocBridgeIndexV1JsonSchema = {
625
630
  type: { type: "string", minLength: 1, maxLength: 128 },
626
631
  title: { type: "string", minLength: 1, maxLength: 256 },
627
632
  path: { type: "string", minLength: 1, maxLength: 512 },
628
- description: { type: "string", maxLength: 1024 },
633
+ description: { type: "string", maxLength: 2048 },
634
+ body: { type: "string", maxLength: 8e3 },
629
635
  links: stringArray(64),
630
636
  tags: stringArray(32)
631
637
  }
@@ -695,16 +701,80 @@ ${zodIssues(result.error).map((i) => ` - ${i.path}: ${i.message}`).join("\n")}`
695
701
  };
696
702
 
697
703
  // src/index-builder/build-index.ts
698
- import { mkdirSync, readFileSync as readFileSync7, writeFileSync } from "fs";
699
- import { dirname as dirname3, join as join9 } from "path";
704
+ import { mkdirSync, readFileSync as readFileSync8, writeFileSync } from "fs";
705
+ import { dirname as dirname3, join as join10 } from "path";
700
706
 
701
707
  // src/lib/paths.ts
702
708
  import { resolve as resolve2 } from "path";
703
709
  var toPosix = (value) => value.split("\\").join("/");
704
710
 
711
+ // src/lib/package-manager.ts
712
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
713
+ import { join as join2 } from "path";
714
+ var detectPackageManager = (root) => {
715
+ if (existsSync2(join2(root, "pnpm-lock.yaml")) || existsSync2(join2(root, "pnpm-workspace.yaml"))) {
716
+ return "pnpm";
717
+ }
718
+ if (existsSync2(join2(root, "yarn.lock"))) return "yarn";
719
+ if (existsSync2(join2(root, "bun.lockb")) || existsSync2(join2(root, "bun.lock"))) return "bun";
720
+ if (existsSync2(join2(root, "package-lock.json"))) return "npm";
721
+ try {
722
+ const pkg = JSON.parse(readFileSync2(join2(root, "package.json"), "utf8"));
723
+ const pm = pkg.packageManager?.split("@")[0];
724
+ if (pm === "pnpm" || pm === "npm" || pm === "yarn" || pm === "bun") return pm;
725
+ } catch {
726
+ }
727
+ return "npm";
728
+ };
729
+ var defaultChecksForTarget = (root, opts) => {
730
+ const pm = detectPackageManager(root);
731
+ if (/\.mdx?$/.test(opts.packagePath) || opts.packagePath.includes("/pillars/")) {
732
+ try {
733
+ const pkg = JSON.parse(readFileSync2(join2(root, "package.json"), "utf8"));
734
+ const scripts = pkg.scripts ?? {};
735
+ if (scripts["check:okf-type"]) return ["pnpm run check:okf-type"];
736
+ if (scripts["docs:bridge:gate"]) return ["pnpm run docs:bridge:gate"];
737
+ if (scripts.test) return [pm === "pnpm" ? "pnpm test" : "npm test"];
738
+ } catch {
739
+ }
740
+ }
741
+ const isWorkspace = existsSync2(join2(root, "pnpm-workspace.yaml")) || existsSync2(join2(root, "lerna.json")) || Boolean(
742
+ (() => {
743
+ try {
744
+ const pkg = JSON.parse(readFileSync2(join2(root, "package.json"), "utf8"));
745
+ return Boolean(pkg.workspaces);
746
+ } catch {
747
+ return false;
748
+ }
749
+ })()
750
+ );
751
+ const filter = opts.packageName ?? (opts.packagePath.startsWith("packages/") || opts.packagePath.startsWith("apps/") ? opts.packageId : void 0);
752
+ if (pm === "pnpm") {
753
+ if (isWorkspace && filter) {
754
+ const base = [`pnpm --filter ${filter} test`];
755
+ if (opts.strict) base.push(`pnpm --filter ${filter} lint`);
756
+ return base;
757
+ }
758
+ return opts.strict ? ["pnpm test", "pnpm run lint"] : ["pnpm test"];
759
+ }
760
+ if (pm === "yarn") {
761
+ if (isWorkspace && filter) {
762
+ return opts.strict ? [`yarn workspace ${filter} test`, `yarn workspace ${filter} lint`] : [`yarn workspace ${filter} test`];
763
+ }
764
+ return opts.strict ? ["yarn test", "yarn lint"] : ["yarn test"];
765
+ }
766
+ if (pm === "bun") {
767
+ return opts.strict ? ["bun test", "bun run lint"] : ["bun test"];
768
+ }
769
+ if (isWorkspace && filter) {
770
+ return opts.strict ? [`npm test -w ${filter}`, `npm run lint -w ${filter}`] : [`npm test -w ${filter}`];
771
+ }
772
+ return opts.strict ? ["npm test", "npm run lint"] : ["npm test"];
773
+ };
774
+
705
775
  // src/index-builder/scan-corpus.ts
706
- import { readFileSync as readFileSync2 } from "fs";
707
- import { join as join3 } from "path";
776
+ import { readFileSync as readFileSync3 } from "fs";
777
+ import { join as join4 } from "path";
708
778
 
709
779
  // src/lib/markdown.ts
710
780
  var parseFrontmatter = (markdown) => {
@@ -754,7 +824,7 @@ var firstHeading = (markdown) => {
754
824
  }
755
825
  return void 0;
756
826
  };
757
- var firstParagraph = (markdown) => {
827
+ var firstParagraph = (markdown, maxLen = 400) => {
758
828
  const { body } = parseFrontmatter(markdown);
759
829
  const lines = body.split("\n");
760
830
  const buf = [];
@@ -766,11 +836,27 @@ var firstParagraph = (markdown) => {
766
836
  }
767
837
  if (t.startsWith("#")) continue;
768
838
  if (t.startsWith("---")) continue;
839
+ if (t.startsWith("```")) break;
840
+ if (t.startsWith("|") || t.startsWith("- [") || t.startsWith("* [")) {
841
+ if (buf.length) break;
842
+ continue;
843
+ }
769
844
  buf.push(t);
770
- if (buf.join(" ").length > 40) break;
771
- }
772
- const text = buf.join(" ").trim();
773
- return text || void 0;
845
+ if (buf.join(" ").length >= maxLen) break;
846
+ }
847
+ let text = buf.join(" ").replace(/\s+/g, " ").trim();
848
+ if (!text) return void 0;
849
+ if (text.length <= maxLen) return text;
850
+ const sliced = text.slice(0, maxLen);
851
+ const sentenceEnd = Math.max(sliced.lastIndexOf(". "), sliced.lastIndexOf("! "), sliced.lastIndexOf("? "));
852
+ if (sentenceEnd > maxLen * 0.4) return sliced.slice(0, sentenceEnd + 1).trim();
853
+ const wordEnd = sliced.lastIndexOf(" ");
854
+ return (wordEnd > 0 ? sliced.slice(0, wordEnd) : sliced).trim();
855
+ };
856
+ var extractSearchBody = (markdown, maxLen = 6e3) => {
857
+ const { body } = parseFrontmatter(markdown);
858
+ const text = body.replace(/```[\s\S]*?```/g, " ").replace(/!\[[^\]]*\]\([^)]+\)/g, " ").replace(/\[[^\]]*\]\([^)]+\)/g, " ").replace(/^#+\s+/gm, "").replace(/[|>*_`#]/g, " ").replace(/\s+/g, " ").trim();
859
+ return text.length > maxLen ? text.slice(0, maxLen) : text;
774
860
  };
775
861
  var slugFromPath = (relPath) => {
776
862
  const base = relPath.replace(/\.mdx?$/, "");
@@ -780,7 +866,7 @@ var slugFromPath = (relPath) => {
780
866
 
781
867
  // src/lib/walk.ts
782
868
  import { readdirSync, statSync } from "fs";
783
- import { join as join2 } from "path";
869
+ import { join as join3 } from "path";
784
870
  var DEFAULT_SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "coverage", ".doc-bridge"]);
785
871
  var walkFiles = (root, opts) => {
786
872
  const extensions = opts?.extensions ?? [".md"];
@@ -794,7 +880,7 @@ var walkFiles = (root, opts) => {
794
880
  return;
795
881
  }
796
882
  for (const name of entries) {
797
- const abs = join2(dir, name);
883
+ const abs = join3(dir, name);
798
884
  let st;
799
885
  try {
800
886
  st = statSync(abs);
@@ -818,17 +904,19 @@ var walkFiles = (root, opts) => {
818
904
 
819
905
  // src/index-builder/scan-corpus.ts
820
906
  var scanAgentCorpus = (root, config) => {
821
- const agentRoot = join3(root, config.corpus.agent.root);
907
+ const agentRoot = join4(root, config.corpus.agent.root);
822
908
  const files = walkFiles(agentRoot, { extensions: [".md", ".mdx"] });
823
909
  const corpusRelRoot = toPosix(config.corpus.agent.root);
824
910
  return files.map((abs) => {
825
911
  const relToCorpus = toPosix(abs.replace(`${toPosix(agentRoot)}/`, ""));
826
912
  const relPath = `${corpusRelRoot}/${relToCorpus}`;
827
- const raw = readFileSync2(abs, "utf8");
913
+ const raw = readFileSync3(abs, "utf8");
828
914
  const { data: frontmatter } = parseFrontmatter(raw);
829
915
  const id = frontmatterString(frontmatter, "id") ?? frontmatterString(frontmatter, "package") ?? slugFromPath(relToCorpus);
830
916
  const title = firstHeading(raw) ?? id;
831
- const description = firstParagraph(raw);
917
+ const purpose = frontmatterString(frontmatter, "purpose");
918
+ const description = purpose ?? firstParagraph(raw, 400);
919
+ const body = extractSearchBody(raw);
832
920
  return {
833
921
  id,
834
922
  type: "agent-doc",
@@ -837,21 +925,35 @@ var scanAgentCorpus = (root, config) => {
837
925
  absPath: abs,
838
926
  relPath,
839
927
  frontmatter,
840
- ...description ? { description } : {}
928
+ ...description ? { description } : {},
929
+ ...body ? { body } : {}
841
930
  };
842
931
  });
843
932
  };
844
933
  var guessAgentDocForPackage = (corpus, packageId) => {
845
- const exact = corpus.find(
846
- (doc) => frontmatterString(doc.frontmatter, "package") === packageId || doc.relPath.endsWith(`/packages/${packageId}.md`) || doc.relPath.endsWith(`/${packageId}.md`) || doc.id === packageId
847
- );
848
- return exact?.path;
934
+ const candidates = [
935
+ (doc) => frontmatterString(doc.frontmatter, "package") === packageId,
936
+ (doc) => doc.id === packageId,
937
+ (doc) => doc.relPath.endsWith(`/packages/${packageId}.md`),
938
+ (doc) => doc.relPath.endsWith(`/packages/${packageId}.mdx`),
939
+ (doc) => doc.relPath.endsWith(`/packages/${packageId}/index.md`),
940
+ (doc) => doc.relPath.endsWith(`/packages/${packageId}/index.mdx`),
941
+ (doc) => doc.relPath.endsWith(`/${packageId}.md`),
942
+ (doc) => doc.relPath.endsWith(`/${packageId}.mdx`),
943
+ (doc) => doc.relPath.includes(`/packages/${packageId}/`)
944
+ ];
945
+ for (const match of candidates) {
946
+ const hit = corpus.find(match);
947
+ if (hit) return hit.path;
948
+ }
949
+ return void 0;
849
950
  };
850
951
  var ownershipFromFrontmatter = (corpus) => {
851
952
  const out = [];
852
953
  for (const doc of corpus) {
853
- const id = frontmatterString(doc.frontmatter, "package") ?? (frontmatterString(doc.frontmatter, "type") === "package" ? frontmatterString(doc.frontmatter, "id") ?? doc.id : void 0);
854
- const path = frontmatterString(doc.frontmatter, "editRoot") ?? frontmatterString(doc.frontmatter, "path");
954
+ const type = frontmatterString(doc.frontmatter, "type");
955
+ const id = frontmatterString(doc.frontmatter, "package") ?? (type === "package" || type === "module" || type === "pattern" ? frontmatterString(doc.frontmatter, "id") ?? doc.id : void 0);
956
+ const path = frontmatterString(doc.frontmatter, "editRoot") ?? frontmatterString(doc.frontmatter, "path") ?? (type === "package" || type === "module" ? `packages/${id}` : void 0);
855
957
  if (!id || !path) continue;
856
958
  const purpose = frontmatterString(doc.frontmatter, "purpose") ?? doc.description;
857
959
  const checks = frontmatterStringList(doc.frontmatter, "checks");
@@ -867,13 +969,55 @@ var ownershipFromFrontmatter = (corpus) => {
867
969
  }
868
970
  return out;
869
971
  };
972
+ var ownershipFromCorpus = (corpus) => {
973
+ const out = [];
974
+ const seen = /* @__PURE__ */ new Set();
975
+ for (const doc of corpus) {
976
+ if (frontmatterString(doc.frontmatter, "package") && frontmatterString(doc.frontmatter, "editRoot")) {
977
+ continue;
978
+ }
979
+ let id;
980
+ let path;
981
+ let purpose = frontmatterString(doc.frontmatter, "purpose") ?? doc.description;
982
+ const packagesFile = /(?:^|\/)packages\/([^/]+)\.(?:md|mdx)$/.exec(doc.relPath);
983
+ const packagesIndex = /(?:^|\/)packages\/([^/]+)\/index\.(?:md|mdx)$/.exec(doc.relPath);
984
+ const registryReadme = /(?:^|\/)registry\/([^/]+)\/README\.(?:md|mdx)$/i.exec(doc.relPath);
985
+ const patternFile = /(?:^|\/)pillars\/[^/]+\/([^/]+(?:-pattern)?)\.(?:md|mdx)$/.exec(doc.relPath);
986
+ const topLevelPkg = /(?:^|\/)for-agents\/([^/]+)\.(?:md|mdx)$/.exec(doc.relPath);
987
+ if (packagesFile?.[1] && packagesFile[1] !== "index") {
988
+ id = packagesFile[1];
989
+ path = `packages/${id}`;
990
+ } else if (packagesIndex?.[1]) {
991
+ id = packagesIndex[1];
992
+ path = `packages/${id}`;
993
+ } else if (registryReadme?.[1]) {
994
+ id = registryReadme[1];
995
+ path = `registry/${id}`;
996
+ } else if (patternFile?.[1] && patternFile[1] !== "index" && patternFile[1] !== "universal") {
997
+ id = patternFile[1];
998
+ path = doc.path;
999
+ purpose = purpose ?? `Playbook pattern: ${id}`;
1000
+ } else if (topLevelPkg?.[1] && topLevelPkg[1] !== "index" && topLevelPkg[1] !== "INDEX") {
1001
+ id = topLevelPkg[1];
1002
+ path = `packages/${id}`;
1003
+ }
1004
+ if (!id || !path || seen.has(id)) continue;
1005
+ seen.add(id);
1006
+ const humanDoc = frontmatterString(doc.frontmatter, "humanDoc");
1007
+ const checks = frontmatterStringList(doc.frontmatter, "checks");
1008
+ out.push({
1009
+ id,
1010
+ path,
1011
+ agentDoc: doc.path,
1012
+ ...purpose ? { purpose } : {},
1013
+ ...checks ? { checks } : {},
1014
+ ...humanDoc ? { humanDoc } : {}
1015
+ });
1016
+ }
1017
+ return out;
1018
+ };
870
1019
 
871
1020
  // src/index-builder/build-handoffs.ts
872
- var defaultChecks = (config) => {
873
- const preset = config.gates?.preset ?? "minimal";
874
- if (preset === "strict" || preset === "standard") return ["npm test", "npm run lint"];
875
- return ["npm test"];
876
- };
877
1021
  var collectPackages = (config, discovered, corpus) => {
878
1022
  const byId = /* @__PURE__ */ new Map();
879
1023
  for (const [id, entry] of Object.entries(config.routing?.options?.ownership ?? {})) {
@@ -891,7 +1035,11 @@ var collectPackages = (config, discovered, corpus) => {
891
1035
  ...pkg.name ? { name: pkg.name } : existing.name ? { name: existing.name } : {}
892
1036
  });
893
1037
  }
894
- for (const seed of ownershipFromFrontmatter(corpus)) {
1038
+ const seeds = [
1039
+ ...ownershipFromFrontmatter(corpus),
1040
+ ...config.routing?.options?.ownershipFromCorpus === false ? [] : ownershipFromCorpus(corpus)
1041
+ ];
1042
+ for (const seed of seeds) {
895
1043
  const existing = byId.get(seed.id);
896
1044
  if (!existing) {
897
1045
  byId.set(seed.id, { id: seed.id, path: seed.path });
@@ -903,22 +1051,59 @@ var collectPackages = (config, discovered, corpus) => {
903
1051
  }
904
1052
  return [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
905
1053
  };
906
- var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}) => {
1054
+ var resolveHumanDoc = (packageId, override, fmHuman, humanDocs = {}) => {
1055
+ if (override) return override;
1056
+ if (fmHuman) return fmHuman;
1057
+ if (humanDocs[packageId]) return humanDocs[packageId];
1058
+ const aliases = [
1059
+ packageId,
1060
+ packageId.replace(/^@[^/]+\//, ""),
1061
+ packageId.replace(/^os-/, ""),
1062
+ packageId.replace(/-pattern$/, ""),
1063
+ `packages/${packageId}`,
1064
+ `reference/packages/${packageId}`,
1065
+ `packages/${packageId}/index`
1066
+ ];
1067
+ for (const alias of aliases) {
1068
+ if (humanDocs[alias]) return humanDocs[alias];
1069
+ }
1070
+ for (const [key, url] of Object.entries(humanDocs)) {
1071
+ if (key === packageId || key.endsWith(`/${packageId}`) || key.endsWith(`/${packageId}/index`) || key.endsWith(`-${packageId}`)) {
1072
+ return url;
1073
+ }
1074
+ }
1075
+ return void 0;
1076
+ };
1077
+ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root = process.cwd()) => {
907
1078
  const ownership = {};
908
1079
  const handoffs = {};
909
- const checks = defaultChecks(config);
910
- const fmSeeds = new Map(ownershipFromFrontmatter(corpus).map((seed) => [seed.id, seed]));
1080
+ const strict = (config.gates?.preset ?? "minimal") !== "minimal";
1081
+ const fmSeeds = new Map(
1082
+ [...ownershipFromFrontmatter(corpus), ...ownershipFromCorpus(corpus)].map((seed) => [
1083
+ seed.id,
1084
+ seed
1085
+ ])
1086
+ );
911
1087
  for (const pkg of packages) {
912
1088
  const override = config.routing?.options?.ownership?.[pkg.id];
913
1089
  const fm = fmSeeds.get(pkg.id);
914
1090
  const agentDoc = override?.agentDoc ?? fm?.agentDoc ?? guessAgentDocForPackage(corpus, pkg.id) ?? config.corpus.agent.index;
915
1091
  const startHere = agentDoc ?? "";
916
1092
  const purpose = override?.purpose ?? fm?.purpose;
917
- const humanDoc = override?.humanDoc ?? fm?.humanDoc ?? humanDocs[pkg.id];
1093
+ const path = override?.path ?? fm?.path ?? pkg.path;
1094
+ const checks = [
1095
+ ...override?.checks ?? fm?.checks ?? defaultChecksForTarget(root, {
1096
+ packageId: pkg.id,
1097
+ packagePath: path,
1098
+ ...pkg.name ? { packageName: pkg.name } : {},
1099
+ strict
1100
+ })
1101
+ ];
1102
+ const humanDoc = resolveHumanDoc(pkg.id, override?.humanDoc, fm?.humanDoc, humanDocs);
918
1103
  const record = {
919
1104
  id: pkg.id,
920
- path: override?.path ?? fm?.path ?? pkg.path,
921
- checks: [...override?.checks ?? fm?.checks ?? checks],
1105
+ path,
1106
+ checks,
922
1107
  ...override?.group ? { group: override.group } : {},
923
1108
  ...override?.layer ? { layer: override.layer } : {},
924
1109
  ...purpose ? { purpose } : {},
@@ -972,7 +1157,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}) => {
972
1157
  };
973
1158
 
974
1159
  // src/version.ts
975
- var PACKAGE_VERSION = "0.1.0-alpha.1";
1160
+ var PACKAGE_VERSION = "0.1.0-alpha.3";
976
1161
 
977
1162
  // src/index-builder/capabilities.ts
978
1163
  var renderCapabilitiesJson = (config, index, paths) => {
@@ -1041,13 +1226,13 @@ ${lines.join("\n")}
1041
1226
  };
1042
1227
 
1043
1228
  // src/index-builder/human-adapters/docusaurus.ts
1044
- import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
1045
- import { join as join5 } from "path";
1229
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
1230
+ import { join as join6 } from "path";
1046
1231
  import vm2 from "vm";
1047
1232
 
1048
1233
  // src/index-builder/human-adapters/core.ts
1049
- import { readFileSync as readFileSync3 } from "fs";
1050
- import { join as join4 } from "path";
1234
+ import { readFileSync as readFileSync4 } from "fs";
1235
+ import { join as join5 } from "path";
1051
1236
  var optionString = (options, keys) => {
1052
1237
  for (const key of keys) {
1053
1238
  const value = options?.[key];
@@ -1078,10 +1263,10 @@ var humanUrl = (slug, urlPrefix) => {
1078
1263
  };
1079
1264
  var scanMarkdownDocs = (root, humanRoot, options) => {
1080
1265
  const out = [];
1081
- const absRoot = join4(root, humanRoot);
1266
+ const absRoot = join5(root, humanRoot);
1082
1267
  for (const abs of walkFiles(absRoot, { extensions: [".md", ".mdx"] })) {
1083
1268
  const relToHumanRoot = toPosix(abs.replace(`${toPosix(absRoot)}/`, ""));
1084
- const raw = readFileSync3(abs, "utf8");
1269
+ const raw = readFileSync4(abs, "utf8");
1085
1270
  if (options?.includeRelPath && !options.includeRelPath(relToHumanRoot, raw)) continue;
1086
1271
  out.push({
1087
1272
  id: options?.idForDoc?.(relToHumanRoot, raw) ?? docId(relToHumanRoot, raw),
@@ -1120,8 +1305,8 @@ var docusaurusRecordId = (relPath, raw) => {
1120
1305
  return frontmatter.package ?? frontmatter.module ?? docusaurusSidebarId(relPath, raw);
1121
1306
  };
1122
1307
  var sidebarsValue = (file) => {
1123
- if (!existsSync2(file)) return void 0;
1124
- 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 =");
1308
+ if (!existsSync3(file)) return void 0;
1309
+ 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 =");
1125
1310
  const sandbox = { module: { exports: {} }, exports: {} };
1126
1311
  vm2.runInNewContext(raw, sandbox, { timeout: 250 });
1127
1312
  return sandbox.module.exports;
@@ -1151,7 +1336,7 @@ var visitSidebar = (value, filter) => {
1151
1336
  };
1152
1337
  var readSidebars = (root, sidebarsFile) => {
1153
1338
  if (!sidebarsFile) return { enabled: false, ids: /* @__PURE__ */ new Set(), autogenDirs: [] };
1154
- const value = sidebarsValue(join5(root, sidebarsFile));
1339
+ const value = sidebarsValue(join6(root, sidebarsFile));
1155
1340
  const filter = { ids: /* @__PURE__ */ new Set(), autogenDirs: [] };
1156
1341
  visitSidebar(value, filter);
1157
1342
  return { enabled: true, ...filter };
@@ -1180,13 +1365,13 @@ var docusaurusAdapter = {
1180
1365
  };
1181
1366
 
1182
1367
  // src/index-builder/human-adapters/fumadocs.ts
1183
- import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
1184
- import { join as join6 } from "path";
1368
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
1369
+ import { join as join7 } from "path";
1185
1370
  var readMetaPages = (dir) => {
1186
- const file = join6(dir, "meta.json");
1187
- if (!existsSync3(file)) return void 0;
1371
+ const file = join7(dir, "meta.json");
1372
+ if (!existsSync4(file)) return void 0;
1188
1373
  try {
1189
- const meta = JSON.parse(readFileSync5(file, "utf8"));
1374
+ const meta = JSON.parse(readFileSync6(file, "utf8"));
1190
1375
  return Array.isArray(meta.pages) ? meta.pages.filter((page) => typeof page === "string") : void 0;
1191
1376
  } catch {
1192
1377
  return void 0;
@@ -1201,7 +1386,7 @@ var isListedByMeta = (contentRoot, relPath) => {
1201
1386
  if (isDotFile(relPath)) return false;
1202
1387
  const parts = relPath.split("/");
1203
1388
  for (let i = 0; i < parts.length; i += 1) {
1204
- const dir = join6(contentRoot, ...parts.slice(0, i));
1389
+ const dir = join7(contentRoot, ...parts.slice(0, i));
1205
1390
  const pages = readMetaPages(dir);
1206
1391
  if (!pages?.length || pages.includes("...")) continue;
1207
1392
  const key = pageKey(parts[i] ?? "");
@@ -1214,9 +1399,16 @@ var fumadocsAdapter = {
1214
1399
  scan: ({ root, config }) => {
1215
1400
  const contentDir = optionString(config.options, ["contentDir", "root"]);
1216
1401
  if (!contentDir) return [];
1217
- const contentRoot = join6(root, contentDir);
1402
+ const contentRoot = join7(root, contentDir);
1403
+ const excludePrefixes = Array.isArray(config.options?.excludePrefixes) ? config.options.excludePrefixes.filter((v) => typeof v === "string") : typeof config.options?.excludePrefix === "string" ? [config.options.excludePrefix] : [];
1404
+ if (!excludePrefixes.includes("for-agents")) excludePrefixes.push("for-agents");
1218
1405
  return scanMarkdownDocs(root, contentDir, {
1219
- includeRelPath: (relPath) => isListedByMeta(contentRoot, relPath),
1406
+ includeRelPath: (relPath) => {
1407
+ if (excludePrefixes.some((prefix) => relPath === prefix || relPath.startsWith(`${prefix}/`))) {
1408
+ return false;
1409
+ }
1410
+ return isListedByMeta(contentRoot, relPath);
1411
+ },
1220
1412
  urlPrefix: config.options?.urlPrefix,
1221
1413
  stripGroups: true
1222
1414
  });
@@ -1227,7 +1419,7 @@ var fumadocsAdapter = {
1227
1419
  var plainMarkdownAdapter = {
1228
1420
  plugin: "plain-markdown",
1229
1421
  scan: ({ root, config }) => {
1230
- const humanRoot = optionString(config.options, ["root"]) ?? "docs";
1422
+ const humanRoot = optionString(config.options, ["contentDir", "root", "docsDir"]) ?? "docs";
1231
1423
  return scanMarkdownDocs(root, humanRoot, { urlPrefix: config.options?.urlPrefix });
1232
1424
  }
1233
1425
  };
@@ -1246,10 +1438,14 @@ var humanConfigs = (config) => {
1246
1438
  var scanHumanDocRecords = (root, config) => {
1247
1439
  const out = [];
1248
1440
  const seen = /* @__PURE__ */ new Set();
1441
+ const agentRoot = config.corpus.agent.root.replace(/\/$/, "");
1249
1442
  for (const human of humanConfigs(config)) {
1250
1443
  const adapter = ADAPTERS.find((candidate) => candidate.plugin === human.plugin);
1251
1444
  if (!adapter) continue;
1252
1445
  for (const record of adapter.scan({ root, config: human })) {
1446
+ if (record.path === agentRoot || record.path.startsWith(`${agentRoot}/`) || record.path.includes("/for-agents/") || record.path.endsWith("/for-agents")) {
1447
+ continue;
1448
+ }
1253
1449
  if (seen.has(record.id)) continue;
1254
1450
  seen.add(record.id);
1255
1451
  out.push(record);
@@ -1260,26 +1456,26 @@ var scanHumanDocRecords = (root, config) => {
1260
1456
  var scanHumanDocs = (root, config) => Object.fromEntries(scanHumanDocRecords(root, config).map((doc) => [doc.id, doc.url]));
1261
1457
 
1262
1458
  // src/index-builder/plugins/pnpm-monorepo.ts
1263
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
1264
- import { basename as basename2, join as join8 } from "path";
1459
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
1460
+ import { basename as basename2, join as join9 } from "path";
1265
1461
 
1266
1462
  // src/lib/glob-expand.ts
1267
- import { existsSync as existsSync4, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
1268
- import { join as join7 } from "path";
1463
+ import { existsSync as existsSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
1464
+ import { join as join8 } from "path";
1269
1465
  var expandWorkspaceGlobs = (root, patterns) => {
1270
1466
  const dirs = /* @__PURE__ */ new Set();
1271
1467
  for (const pattern of patterns) {
1272
1468
  const normalized = toPosix(pattern).replace(/\/$/, "");
1273
1469
  if (!normalized.includes("*")) {
1274
- const abs = join7(root, normalized);
1275
- if (existsSync4(abs)) dirs.add(toPosix(abs));
1470
+ const abs = join8(root, normalized);
1471
+ if (existsSync5(abs)) dirs.add(toPosix(abs));
1276
1472
  continue;
1277
1473
  }
1278
1474
  const star = normalized.indexOf("*");
1279
- const base = join7(root, normalized.slice(0, star).replace(/\/$/, ""));
1280
- if (!existsSync4(base)) continue;
1475
+ const base = join8(root, normalized.slice(0, star).replace(/\/$/, ""));
1476
+ if (!existsSync5(base)) continue;
1281
1477
  for (const name of readdirSync2(base)) {
1282
- const abs = join7(base, name);
1478
+ const abs = join8(base, name);
1283
1479
  try {
1284
1480
  if (statSync2(abs).isDirectory()) dirs.add(toPosix(abs));
1285
1481
  } catch {
@@ -1311,10 +1507,10 @@ var parsePnpmWorkspace = (yaml) => {
1311
1507
  return patterns;
1312
1508
  };
1313
1509
  var readPackageJson = (dir) => {
1314
- const file = join8(dir, "package.json");
1315
- if (!existsSync5(file)) return null;
1510
+ const file = join9(dir, "package.json");
1511
+ if (!existsSync6(file)) return null;
1316
1512
  try {
1317
- return JSON.parse(readFileSync6(file, "utf8"));
1513
+ return JSON.parse(readFileSync7(file, "utf8"));
1318
1514
  } catch {
1319
1515
  return null;
1320
1516
  }
@@ -1323,9 +1519,9 @@ var discoverPnpmPackages = (root, config) => {
1323
1519
  const explicit = config.routing?.options?.packages;
1324
1520
  let patterns = explicit;
1325
1521
  if (!patterns?.length) {
1326
- const workspaceFile = join8(root, "pnpm-workspace.yaml");
1327
- if (existsSync5(workspaceFile)) {
1328
- patterns = parsePnpmWorkspace(readFileSync6(workspaceFile, "utf8"));
1522
+ const workspaceFile = join9(root, "pnpm-workspace.yaml");
1523
+ if (existsSync6(workspaceFile)) {
1524
+ patterns = parsePnpmWorkspace(readFileSync7(workspaceFile, "utf8"));
1329
1525
  }
1330
1526
  }
1331
1527
  if (!patterns?.length) return [];
@@ -1346,7 +1542,7 @@ var discoverPnpmPackages = (root, config) => {
1346
1542
  var projectName = (root, config) => {
1347
1543
  if (config.project?.name) return config.project.name;
1348
1544
  try {
1349
- const pkg = JSON.parse(readFileSync7(join9(root, "package.json"), "utf8"));
1545
+ const pkg = JSON.parse(readFileSync8(join10(root, "package.json"), "utf8"));
1350
1546
  if (pkg.name) return pkg.name;
1351
1547
  } catch {
1352
1548
  }
@@ -1354,7 +1550,7 @@ var projectName = (root, config) => {
1354
1550
  };
1355
1551
  var existingGeneratedAt = (indexPath, contentHash) => {
1356
1552
  try {
1357
- const index = JSON.parse(readFileSync7(indexPath, "utf8"));
1553
+ const index = JSON.parse(readFileSync8(indexPath, "utf8"));
1358
1554
  return index.contentHash === contentHash && typeof index.generatedAt === "string" ? index.generatedAt : void 0;
1359
1555
  } catch {
1360
1556
  return void 0;
@@ -1365,14 +1561,14 @@ var buildDocBridgeIndex = (opts) => {
1365
1561
  const config = opts.config;
1366
1562
  const write = opts.write ?? true;
1367
1563
  const outFile = config.index?.outFile ?? ".doc-bridge/index.json";
1368
- const indexPath = join9(root, outFile);
1564
+ const indexPath = join10(root, outFile);
1369
1565
  const corpus = scanAgentCorpus(root, config);
1370
1566
  const knowledge = corpus.map(({ absPath: _a, relPath: _r, frontmatter: _f, ...entry }) => entry);
1371
1567
  const shouldDiscover = config.routing?.plugin === "pnpm-monorepo" || Boolean(config.routing?.options?.packages?.length) || config.routing?.plugin === "npm-workspaces" || config.routing?.plugin === "yarn-workspaces";
1372
1568
  const discovered = shouldDiscover ? discoverPnpmPackages(root, config) : [];
1373
1569
  const packages = collectPackages(config, discovered, corpus);
1374
1570
  const humanDocs = scanHumanDocs(root, config);
1375
- const { lookup, handoffs } = buildLookup(config, packages, corpus, outFile, humanDocs);
1571
+ const { lookup, handoffs } = buildLookup(config, packages, corpus, outFile, humanDocs, root);
1376
1572
  const hashPayload = {
1377
1573
  schemaVersion: 1,
1378
1574
  knowledge,
@@ -1400,7 +1596,7 @@ var buildDocBridgeIndex = (opts) => {
1400
1596
  if (config.index?.llmsTxt?.enabled !== false) {
1401
1597
  const llmsOut = config.index?.llmsTxt?.outFile ?? "llms.txt";
1402
1598
  llmsTxtRelPath = toPosix(llmsOut);
1403
- llmsTxtPath = join9(root, llmsOut);
1599
+ llmsTxtPath = join10(root, llmsOut);
1404
1600
  if (write) {
1405
1601
  writeFileSync(
1406
1602
  llmsTxtPath,
@@ -1412,7 +1608,7 @@ var buildDocBridgeIndex = (opts) => {
1412
1608
  let capabilitiesPath;
1413
1609
  if (config.index?.capabilities?.enabled !== false) {
1414
1610
  const capabilitiesOut = config.index?.capabilities?.outFile ?? ".doc-bridge/capabilities.json";
1415
- capabilitiesPath = join9(root, capabilitiesOut);
1611
+ capabilitiesPath = join10(root, capabilitiesOut);
1416
1612
  if (write) {
1417
1613
  mkdirSync(dirname3(capabilitiesPath), { recursive: true });
1418
1614
  writeFileSync(
@@ -1434,11 +1630,11 @@ var buildDocBridgeIndex = (opts) => {
1434
1630
  };
1435
1631
 
1436
1632
  // src/gates/run-gates.ts
1437
- import { readFileSync as readFileSync9 } from "fs";
1633
+ import { readFileSync as readFileSync10 } from "fs";
1438
1634
 
1439
1635
  // src/query/load-index.ts
1440
- import { existsSync as existsSync6, readFileSync as readFileSync8 } from "fs";
1441
- import { join as join10, resolve as resolve3 } from "path";
1636
+ import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
1637
+ import { join as join11, resolve as resolve3 } from "path";
1442
1638
  var IndexNotFoundError = class extends Error {
1443
1639
  constructor(path) {
1444
1640
  super(`Missing index at ${path}. Run: ak-docs index`);
@@ -1447,11 +1643,11 @@ var IndexNotFoundError = class extends Error {
1447
1643
  }
1448
1644
  path;
1449
1645
  };
1450
- var indexFilePath = (root, config) => join10(root, config.index?.outFile ?? ".doc-bridge/index.json");
1646
+ var indexFilePath = (root, config) => join11(root, config.index?.outFile ?? ".doc-bridge/index.json");
1451
1647
  var loadDocBridgeIndex = (root, config) => {
1452
1648
  const path = indexFilePath(root, config);
1453
- if (!existsSync6(path)) throw new IndexNotFoundError(path);
1454
- const raw = JSON.parse(readFileSync8(path, "utf8"));
1649
+ if (!existsSync7(path)) throw new IndexNotFoundError(path);
1650
+ const raw = JSON.parse(readFileSync9(path, "utf8"));
1455
1651
  return parseDocBridgeIndex(raw);
1456
1652
  };
1457
1653
  var resolveRoot = (cwd) => resolve3(cwd ?? process.cwd());
@@ -1516,7 +1712,7 @@ var runOkfTypeGate = (root, config) => {
1516
1712
  const required = config.corpus.agent.okf?.requireType ?? config.gates?.preset === "strict";
1517
1713
  if (!required) return { id: "okf-type", ok: true, message: "OKF type frontmatter not required" };
1518
1714
  const allowed = config.corpus.agent.okf?.allowedTypes;
1519
- const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({ path: doc.path, type: frontmatterType(readFileSync9(doc.absPath, "utf8")) })).filter((doc) => !doc.type || allowed && !allowed.includes(doc.type));
1715
+ 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));
1520
1716
  if (bad.length) {
1521
1717
  return {
1522
1718
  id: "okf-type",
@@ -1537,13 +1733,22 @@ var DOCS_STYLE_RULES = {
1537
1733
  "examples",
1538
1734
  "no-stale-wording"
1539
1735
  ],
1540
- "playbook-okf": ["title", "purpose", "owner-source", "no-stale-wording"]
1736
+ /** Full playbook OKF style (strict). */
1737
+ "playbook-okf": ["title", "purpose", "owner-source", "no-stale-wording"],
1738
+ /** Soft profile for large existing OKF corpora (title + no stale placeholders). */
1739
+ "playbook-okf-soft": ["title", "no-stale-wording"],
1740
+ "title-only": ["title"]
1541
1741
  };
1542
1742
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1543
1743
  var docsStyleOptions = (config) => {
1544
1744
  const raw = config.gates?.options?.["docs-style"];
1545
- if (!isRecord(raw)) return { profile: "playbook-okf", required: DOCS_STYLE_RULES["playbook-okf"] };
1546
- const profile = raw.profile === "google-dev-docs" || raw.profile === "playbook-okf" || raw.profile === "custom" ? raw.profile : "playbook-okf";
1745
+ if (!isRecord(raw)) {
1746
+ if (config.gates?.preset === "playbook") {
1747
+ return { profile: "playbook-okf-soft", required: DOCS_STYLE_RULES["playbook-okf-soft"] };
1748
+ }
1749
+ return { profile: "playbook-okf", required: DOCS_STYLE_RULES["playbook-okf"] };
1750
+ }
1751
+ 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";
1547
1752
  const customRequired = Array.isArray(raw.required) ? raw.required.filter(
1548
1753
  (rule) => [
1549
1754
  "title",
@@ -1581,7 +1786,7 @@ var runDocsStyleGate = (root, config) => {
1581
1786
  }
1582
1787
  const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({
1583
1788
  path: doc.path,
1584
- missing: missingStyleRules(readFileSync9(doc.absPath, "utf8"), required)
1789
+ missing: missingStyleRules(readFileSync10(doc.absPath, "utf8"), required)
1585
1790
  })).filter((doc) => doc.missing.length > 0);
1586
1791
  if (bad.length) {
1587
1792
  return {
@@ -1605,7 +1810,10 @@ var runGates = (root, config, ids) => {
1605
1810
  var resolveGateIds = (config) => {
1606
1811
  const preset = config.gates?.preset ?? "minimal";
1607
1812
  const ids = new Set(
1608
- preset === "minimal" ? ["index-freshness"] : preset === "strict" ? ["index-freshness", "human-guide-links", "okf-type"] : ["index-freshness", "human-guide-links"]
1813
+ preset === "minimal" ? ["index-freshness"] : preset === "strict" ? ["index-freshness", "human-guide-links", "okf-type", "docs-style"] : preset === "playbook" ? (
1814
+ // okf-type only — docs-style soft is opt-in via include (large corpora vary)
1815
+ ["index-freshness", "okf-type"]
1816
+ ) : ["index-freshness", "human-guide-links"]
1609
1817
  );
1610
1818
  for (const id of config.gates?.include ?? []) {
1611
1819
  if (SUPPORTED_GATES.includes(id)) ids.add(id);
@@ -1617,24 +1825,63 @@ var resolveGateIds = (config) => {
1617
1825
  };
1618
1826
 
1619
1827
  // src/mcp/server.ts
1620
- import { readFileSync as readFileSync11, realpathSync } from "fs";
1828
+ import { readFileSync as readFileSync12, realpathSync } from "fs";
1621
1829
  import { relative, resolve as resolve4 } from "path";
1622
1830
  import { z as z5, ZodError } from "zod";
1623
1831
 
1624
1832
  // src/query/search.ts
1625
- var tokenize = (value) => value.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 2);
1833
+ var tokenize = (value) => value.toLowerCase().split(/[^a-z0-9@/_-]+/).filter((t) => t.length >= 2);
1834
+ var PACKAGE_INTENT = /\b(package|module|pkg|edit|change|where|owns?|ownership|handoff|start)\b/i;
1835
+ var scoreHay = (tokens, hay, weight = 1) => {
1836
+ let score = 0;
1837
+ for (const token of tokens) {
1838
+ if (!hay.includes(token)) continue;
1839
+ score += token.length * weight;
1840
+ if (new RegExp(`(?:^|[^a-z0-9])${token}(?:[^a-z0-9]|$)`).test(hay)) {
1841
+ score += token.length;
1842
+ }
1843
+ }
1844
+ return score;
1845
+ };
1846
+ var identityBoost = (id, path, tokens, term) => {
1847
+ const idLower = id.toLowerCase();
1848
+ const termLower = term.toLowerCase().trim();
1849
+ const base = path.split("/").pop()?.replace(/\.mdx?$/i, "").toLowerCase() ?? "";
1850
+ let boost = 0;
1851
+ if (idLower === termLower || base === termLower) boost += 200;
1852
+ if (tokens.length === 1 && (idLower === tokens[0] || base === tokens[0])) boost += 200;
1853
+ for (const token of tokens) {
1854
+ if (idLower === token) boost += 120;
1855
+ else if (idLower.startsWith(`${token}-`) || idLower.endsWith(`-${token}`)) boost += 40;
1856
+ else if (idLower.includes(token) && idLower.length <= token.length + 4) boost += 30;
1857
+ if (base === token) boost += 100;
1858
+ }
1859
+ if (tokens.includes(idLower)) boost += Math.max(0, 40 - idLower.length);
1860
+ return boost;
1861
+ };
1862
+ var preferOwnership = (term) => PACKAGE_INTENT.test(term) || /^(where|how).*(edit|change|package|module)/i.test(term);
1626
1863
  var searchIndex = (index, term, limit = 20) => {
1627
1864
  const tokens = tokenize(term);
1628
1865
  if (!tokens.length) return [];
1629
- const matches = [];
1630
- for (const entry of index.knowledge) {
1631
- const hay = `${entry.id} ${entry.title} ${entry.description ?? ""} ${entry.path}`.toLowerCase();
1632
- let score = 0;
1633
- for (const token of tokens) {
1634
- if (hay.includes(token)) score += token.length;
1866
+ const wantOwnership = preferOwnership(term);
1867
+ const byPath = /* @__PURE__ */ new Map();
1868
+ const consider = (match) => {
1869
+ const key = match.path;
1870
+ const existing = byPath.get(key);
1871
+ if (!existing) {
1872
+ byPath.set(key, match);
1873
+ return;
1635
1874
  }
1875
+ 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;
1876
+ if (prefer) byPath.set(key, match);
1877
+ };
1878
+ for (const entry of index.knowledge) {
1879
+ const body = entry.body ?? "";
1880
+ const hay = `${entry.id} ${entry.title} ${entry.description ?? ""} ${entry.path} ${body}`.toLowerCase();
1881
+ let score = scoreHay(tokens, hay, 1);
1882
+ score += identityBoost(entry.id, entry.path, tokens, term);
1636
1883
  if (score > 0) {
1637
- matches.push({
1884
+ consider({
1638
1885
  type: "knowledge",
1639
1886
  id: entry.id,
1640
1887
  path: entry.path,
@@ -1644,22 +1891,31 @@ var searchIndex = (index, term, limit = 20) => {
1644
1891
  }
1645
1892
  }
1646
1893
  for (const [id, owner] of Object.entries(index.lookup?.ownership ?? {})) {
1647
- const hay = `${id} ${owner.path} ${owner.purpose ?? ""} ${owner.group ?? ""}`.toLowerCase();
1648
- let score = 0;
1649
- for (const token of tokens) {
1650
- if (hay.includes(token)) score += token.length * 2;
1651
- }
1894
+ const path = owner.agentDoc ?? owner.path;
1895
+ const hay = `${id} ${owner.path} ${owner.purpose ?? ""} ${owner.group ?? ""} ${owner.agentDoc ?? ""} ${owner.humanDoc ?? ""}`.toLowerCase();
1896
+ let score = scoreHay(tokens, hay, 2);
1897
+ score += identityBoost(id, path, tokens, term);
1898
+ if (wantOwnership) score += 25;
1899
+ score += 15;
1652
1900
  if (score > 0) {
1653
- matches.push({
1901
+ consider({
1654
1902
  type: "ownership",
1655
1903
  id,
1656
- path: owner.agentDoc ?? owner.path,
1904
+ path,
1657
1905
  ...owner.purpose ? { summary: owner.purpose } : {},
1658
1906
  score
1659
1907
  });
1660
1908
  }
1661
1909
  }
1662
- return matches.sort((a, b) => b.score - a.score).slice(0, limit);
1910
+ return [...byPath.values()].sort((a, b) => {
1911
+ if (b.score !== a.score) return b.score - a.score;
1912
+ const aExact = tokens.includes(a.id.toLowerCase()) ? 1 : 0;
1913
+ const bExact = tokens.includes(b.id.toLowerCase()) ? 1 : 0;
1914
+ if (bExact !== aExact) return bExact - aExact;
1915
+ if (a.type === "ownership" && b.type !== "ownership") return -1;
1916
+ if (b.type === "ownership" && a.type !== "ownership") return 1;
1917
+ return a.id.localeCompare(b.id);
1918
+ }).slice(0, limit);
1663
1919
  };
1664
1920
 
1665
1921
  // src/retriever/doc-bridge-retriever.ts
@@ -1689,15 +1945,15 @@ var createDocBridgeRetriever = (index, options = {}) => ({
1689
1945
  });
1690
1946
 
1691
1947
  // src/memory/ingest.ts
1692
- import { existsSync as existsSync7, readFileSync as readFileSync10 } from "fs";
1693
- import { join as join11 } from "path";
1948
+ import { existsSync as existsSync8, readFileSync as readFileSync11 } from "fs";
1949
+ import { join as join12 } from "path";
1694
1950
  var memoryFact = (raw, id) => firstParagraph(raw) ?? firstHeading(raw) ?? id;
1695
1951
  var relativePath = (root, abs) => toPosix(abs).replace(`${toPosix(root)}/`, "");
1696
1952
  var ingestMarkdownDir = (root, dir, source, confidence) => {
1697
- if (!existsSync7(dir)) return [];
1953
+ if (!existsSync8(dir)) return [];
1698
1954
  return walkFiles(dir, { extensions: [".md", ".mdc"] }).map((abs) => {
1699
1955
  const rel = relativePath(root, abs);
1700
- const raw = readFileSync10(abs, "utf8");
1956
+ const raw = readFileSync11(abs, "utf8");
1701
1957
  const id = slugFromPath(rel);
1702
1958
  return {
1703
1959
  schemaVersion: 1,
@@ -1712,10 +1968,10 @@ var ingestMarkdownDir = (root, dir, source, confidence) => {
1712
1968
  });
1713
1969
  };
1714
1970
  var ingestCursorRules = (root) => {
1715
- const dir = join11(root, ".cursor", "rules");
1971
+ const dir = join12(root, ".cursor", "rules");
1716
1972
  return ingestMarkdownDir(root, dir, "cursor", 0.6);
1717
1973
  };
1718
- var ingestAgentMemory = (root) => ingestMarkdownDir(root, join11(root, ".agent-memory"), "agent-memory", 0.7);
1974
+ var ingestAgentMemory = (root) => ingestMarkdownDir(root, join12(root, ".agent-memory"), "agent-memory", 0.7);
1719
1975
  var ingestMemoryCandidates = (root) => [
1720
1976
  ...ingestAgentMemory(root),
1721
1977
  ...ingestCursorRules(root)
@@ -2020,7 +2276,7 @@ var handleMcpRequest = (ctx, request) => {
2020
2276
  return {
2021
2277
  protocolVersion: "2024-11-05",
2022
2278
  capabilities: { tools: {} },
2023
- serverInfo: { name: "ak-docs", version: "0.1.0-alpha.1" }
2279
+ serverInfo: { name: "ak-docs", version: PACKAGE_VERSION }
2024
2280
  };
2025
2281
  }
2026
2282
  if (request.method === "tools/list") return { tools: MCP_TOOLS };
@@ -2045,7 +2301,7 @@ var handleMcpRequest = (ctx, request) => {
2045
2301
  }
2046
2302
  if (name === "doc.get") {
2047
2303
  const relPath = findDocPath(index(), parseToolArgs("doc.get", DocGetArgsSchema, args));
2048
- return textResult(readFileSync11(resolveDocPath(ctx.root, relPath), "utf8"));
2304
+ return textResult(readFileSync12(resolveDocPath(ctx.root, relPath), "utf8"));
2049
2305
  }
2050
2306
  if (name === "gate.status") return textResult(runGates(ctx.root, ctx.config));
2051
2307
  if (name === "retriever.query") {
@@ -2123,7 +2379,7 @@ var startMcpStdioServer = (ctx) => {
2123
2379
  };
2124
2380
 
2125
2381
  // src/federation/llms.ts
2126
- import { existsSync as existsSync8, readFileSync as readFileSync12 } from "fs";
2382
+ import { existsSync as existsSync9, readFileSync as readFileSync13 } from "fs";
2127
2383
  import { resolve as resolve5 } from "path";
2128
2384
  var tokenize2 = (value) => value.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length >= 2);
2129
2385
  var scoreText = (query, text) => {
@@ -2137,10 +2393,14 @@ var defaultFetchText = async (url) => {
2137
2393
  return res.text();
2138
2394
  };
2139
2395
  var sourceText = async (root, source, fetchText) => {
2140
- if (/^https?:\/\//.test(source)) return fetchText(source);
2141
- const path = resolve5(root, source);
2142
- if (!existsSync8(path)) throw new Error(`Federation source not found: ${source}`);
2143
- return readFileSync12(path, "utf8");
2396
+ try {
2397
+ if (/^https?:\/\//.test(source)) return await fetchText(source);
2398
+ const path = resolve5(root, source);
2399
+ if (!existsSync9(path)) return null;
2400
+ return readFileSync13(path, "utf8");
2401
+ } catch {
2402
+ return null;
2403
+ }
2144
2404
  };
2145
2405
  var sameOrigin = (base, target) => {
2146
2406
  if (!/^https?:\/\//.test(base) || !/^https?:\/\//.test(target)) return true;
@@ -2179,22 +2439,28 @@ var chunksFromMarkdown = (property, raw, sourceUrl) => {
2179
2439
  var loadFederatedChunks = async (root, config, options = {}) => {
2180
2440
  const fetchText = options.fetchText ?? defaultFetchText;
2181
2441
  const chunks = [];
2442
+ const warnings = [];
2182
2443
  for (const source of config.federation?.sources ?? []) {
2183
2444
  if (source.includeInRetriever === false || !source.llmsTxt) continue;
2184
2445
  const llms = await sourceText(root, source.llmsTxt, fetchText);
2446
+ if (!llms) {
2447
+ warnings.push(`federation source skipped (unavailable): ${source.id} \u2192 ${source.llmsTxt}`);
2448
+ continue;
2449
+ }
2185
2450
  chunks.push(...chunksFromMarkdown(source.id, llms, source.llmsTxt));
2186
2451
  const links = parseLlmsTxtLinks(llms);
2187
2452
  for (const link of links) {
2188
2453
  const url = !/^https?:\/\//.test(link.url) && source.rawBaseUrl ? `${source.rawBaseUrl.replace(/\/$/, "")}/${link.url.replace(/^\//, "")}` : link.url;
2189
2454
  if (!/\.(md|txt)(?:$|\?)/.test(url)) continue;
2190
2455
  if (!sameOrigin(source.llmsTxt, url)) continue;
2191
- try {
2192
- const raw = await sourceText(root, url, fetchText);
2193
- chunks.push(...chunksFromMarkdown(source.id, raw, url));
2194
- } catch {
2195
- }
2456
+ const raw = await sourceText(root, url, fetchText);
2457
+ if (raw) chunks.push(...chunksFromMarkdown(source.id, raw, url));
2196
2458
  }
2197
2459
  }
2460
+ if (warnings.length && process.stderr.isTTY) {
2461
+ for (const w of warnings) process.stderr.write(`${w}
2462
+ `);
2463
+ }
2198
2464
  return chunks;
2199
2465
  };
2200
2466
  var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
@@ -2216,11 +2482,11 @@ var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
2216
2482
  };
2217
2483
 
2218
2484
  // src/intelligence/rag.ts
2219
- import { readFileSync as readFileSync13 } from "fs";
2220
- import { join as join13 } from "path";
2485
+ import { readFileSync as readFileSync14 } from "fs";
2486
+ import { join as join14 } from "path";
2221
2487
 
2222
2488
  // src/intelligence/adapter.ts
2223
- import { join as join12 } from "path";
2489
+ import { join as join13 } from "path";
2224
2490
 
2225
2491
  // src/intelligence/peers.ts
2226
2492
  var PeerMissingError = class extends Error {
@@ -2335,7 +2601,7 @@ var resolveIntelligenceRuntime = async (config) => {
2335
2601
  }
2336
2602
  return { adapter, embed, provider, ...model ? { model } : {} };
2337
2603
  };
2338
- var defaultVectorStorePath = (root) => join12(root, ".doc-bridge", "vectors");
2604
+ var defaultVectorStorePath = (root) => join13(root, ".doc-bridge", "vectors");
2339
2605
 
2340
2606
  // src/intelligence/rag.ts
2341
2607
  var loadDocuments = (root, index, sources) => {
@@ -2343,10 +2609,10 @@ var loadDocuments = (root, index, sources) => {
2343
2609
  const docs = [];
2344
2610
  if (includeAgent) {
2345
2611
  for (const entry of index.knowledge) {
2346
- const abs = join13(root, entry.path);
2612
+ const abs = join14(root, entry.path);
2347
2613
  let content = "";
2348
2614
  try {
2349
- content = readFileSync13(abs, "utf8");
2615
+ content = readFileSync14(abs, "utf8");
2350
2616
  } catch {
2351
2617
  content = [entry.title, entry.description].filter(Boolean).join("\n\n");
2352
2618
  }
@@ -2364,7 +2630,7 @@ var createDocBridgeRag = async (root, config, index) => {
2364
2630
  const { embed } = await resolveIntelligenceRuntime(config);
2365
2631
  const ragMod = await importPeer("@agentskit/rag");
2366
2632
  const memoryMod = await importPeer("@agentskit/memory");
2367
- const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join13(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
2633
+ const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join14(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
2368
2634
  const store = memoryMod.fileVectorMemory({ path: storePath });
2369
2635
  const rag = ragMod.createRAG({
2370
2636
  embed,