@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.
- package/CHANGELOG.md +28 -0
- package/README.md +1 -1
- package/dist/cli/program.js +416 -148
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +6 -3
- package/dist/config/index.js.map +1 -1
- package/dist/{index-CD_zmKXf.d.ts → index-CPUJbTbg.d.ts} +19 -10
- package/dist/index.d.ts +25 -6
- package/dist/index.js +398 -132
- package/dist/index.js.map +1 -1
- package/docs/DOGFOOD-ROUND2.md +142 -0
- package/docs/DOGFOOD.md +92 -0
- package/package.json +32 -14
- package/scripts/prepare.mjs +44 -0
- package/src/cli/program.ts +947 -0
- package/src/config/defaults.ts +63 -0
- package/src/config/define-config.ts +6 -0
- package/src/config/index.ts +15 -0
- package/src/config/load-config.ts +138 -0
- package/src/config/schema.ts +294 -0
- package/src/federation/llms.ts +154 -0
- package/src/gates/run-gates.ts +274 -0
- package/src/index-builder/build-handoffs.ts +232 -0
- package/src/index-builder/build-index.ts +137 -0
- package/src/index-builder/capabilities.ts +37 -0
- package/src/index-builder/content-hash.ts +19 -0
- package/src/index-builder/human-adapters/core.ts +100 -0
- package/src/index-builder/human-adapters/docusaurus.ts +113 -0
- package/src/index-builder/human-adapters/fumadocs.ts +70 -0
- package/src/index-builder/human-adapters/index.ts +52 -0
- package/src/index-builder/human-adapters/plain-markdown.ts +10 -0
- package/src/index-builder/llms-txt.ts +19 -0
- package/src/index-builder/plugins/human-markdown.ts +7 -0
- package/src/index-builder/plugins/pnpm-monorepo.ts +72 -0
- package/src/index-builder/scan-corpus.ts +185 -0
- package/src/index.ts +120 -0
- package/src/intelligence/adapter.ts +90 -0
- package/src/intelligence/chat.ts +148 -0
- package/src/intelligence/peers.ts +59 -0
- package/src/intelligence/rag.ts +98 -0
- package/src/lib/glob-expand.ts +33 -0
- package/src/lib/markdown.ts +117 -0
- package/src/lib/package-manager.ts +104 -0
- package/src/lib/paths.ts +6 -0
- package/src/lib/walk.ts +45 -0
- package/src/mcp/server.ts +276 -0
- package/src/memory/ingest.ts +51 -0
- package/src/memory/pipeline.ts +119 -0
- package/src/query/load-index.ts +25 -0
- package/src/query/query.ts +142 -0
- package/src/query/search.ts +126 -0
- package/src/retriever/doc-bridge-retriever.ts +62 -0
- package/src/schemas/agent-handoff.ts +82 -0
- package/src/schemas/doc-bridge-index.ts +108 -0
- package/src/schemas/json-schemas.ts +157 -0
- package/src/schemas/memory-candidate.ts +37 -0
- package/src/shims/agentskit-peers.d.ts +80 -0
- package/src/validate.ts +67 -0
- package/src/version.ts +1 -0
- package/tsconfig.json +21 -0
- 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
|
-
|
|
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(
|
|
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:
|
|
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
|
|
699
|
-
import { dirname as dirname3, join as
|
|
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
|
|
707
|
-
import { join as
|
|
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
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
846
|
-
(doc) => frontmatterString(doc.frontmatter, "package") === packageId
|
|
847
|
-
|
|
848
|
-
|
|
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
|
|
854
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
|
910
|
-
const fmSeeds = new Map(
|
|
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
|
|
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
|
|
921
|
-
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.
|
|
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
|
|
1045
|
-
import { join as
|
|
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
|
|
1050
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
1124
|
-
const raw =
|
|
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(
|
|
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
|
|
1184
|
-
import { join as
|
|
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 =
|
|
1187
|
-
if (!
|
|
1371
|
+
const file = join7(dir, "meta.json");
|
|
1372
|
+
if (!existsSync4(file)) return void 0;
|
|
1188
1373
|
try {
|
|
1189
|
-
const meta = JSON.parse(
|
|
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 =
|
|
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 =
|
|
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) =>
|
|
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
|
|
1264
|
-
import { basename as basename2, join as
|
|
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
|
|
1268
|
-
import { join as
|
|
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 =
|
|
1275
|
-
if (
|
|
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 =
|
|
1280
|
-
if (!
|
|
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 =
|
|
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 =
|
|
1315
|
-
if (!
|
|
1510
|
+
const file = join9(dir, "package.json");
|
|
1511
|
+
if (!existsSync6(file)) return null;
|
|
1316
1512
|
try {
|
|
1317
|
-
return JSON.parse(
|
|
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 =
|
|
1327
|
-
if (
|
|
1328
|
-
patterns = parsePnpmWorkspace(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
1633
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
1438
1634
|
|
|
1439
1635
|
// src/query/load-index.ts
|
|
1440
|
-
import { existsSync as
|
|
1441
|
-
import { join as
|
|
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) =>
|
|
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 (!
|
|
1454
|
-
const raw = JSON.parse(
|
|
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(
|
|
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
|
-
|
|
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))
|
|
1546
|
-
|
|
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(
|
|
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"] :
|
|
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
|
|
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
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
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
|
-
|
|
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
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
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
|
-
|
|
1901
|
+
consider({
|
|
1654
1902
|
type: "ownership",
|
|
1655
1903
|
id,
|
|
1656
|
-
path
|
|
1904
|
+
path,
|
|
1657
1905
|
...owner.purpose ? { summary: owner.purpose } : {},
|
|
1658
1906
|
score
|
|
1659
1907
|
});
|
|
1660
1908
|
}
|
|
1661
1909
|
}
|
|
1662
|
-
return
|
|
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
|
|
1693
|
-
import { join as
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
1971
|
+
const dir = join12(root, ".cursor", "rules");
|
|
1716
1972
|
return ingestMarkdownDir(root, dir, "cursor", 0.6);
|
|
1717
1973
|
};
|
|
1718
|
-
var ingestAgentMemory = (root) => ingestMarkdownDir(root,
|
|
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:
|
|
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(
|
|
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
|
|
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
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
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
|
-
|
|
2192
|
-
|
|
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
|
|
2220
|
-
import { join as
|
|
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
|
|
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) =>
|
|
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 =
|
|
2612
|
+
const abs = join14(root, entry.path);
|
|
2347
2613
|
let content = "";
|
|
2348
2614
|
try {
|
|
2349
|
-
content =
|
|
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" ?
|
|
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,
|