@agentskit/doc-bridge 0.1.0-alpha.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/README.md +139 -57
- package/action.yml +78 -0
- package/dist/cli/program.js +1122 -114
- package/dist/cli/program.js.map +1 -1
- package/dist/index.d.ts +258 -28
- package/dist/index.js +824 -54
- package/dist/index.js.map +1 -1
- package/docs/DOGFOOD-ROUND2.md +142 -0
- package/docs/DOGFOOD-ROUND3.md +74 -0
- package/docs/POSITIONING.md +2 -0
- package/docs/RELEASE.md +5 -3
- package/docs/chat-and-rag.md +2 -0
- package/docs/getting-started.md +14 -1
- package/docs/landing/index.html +299 -0
- package/docs/mcp.md +10 -1
- package/docs/ollama-demo.md +64 -0
- package/docs/playbook/doc-bridge-pattern.md +114 -0
- package/docs/recipes/index-pipeline.md +89 -0
- package/docs/skills/doc-bridge.md +64 -0
- package/docs/spec/cli.md +6 -0
- package/docs/spec/playbook-feedback.md +12 -4
- package/examples/demo-example/doc-bridge.config.json +23 -0
- package/examples/demo-example/docs/for-agents/INDEX.md +3 -0
- package/examples/demo-example/docs/for-agents/packages/example.md +14 -0
- package/examples/demo-example/package.json +7 -0
- package/examples/demo-example/src/.gitkeep +0 -0
- package/examples/demo-monorepo/doc-bridge.config.json +37 -0
- package/examples/demo-monorepo/docs/for-agents/INDEX.md +4 -0
- package/examples/demo-monorepo/docs/for-agents/packages/auth.md +22 -0
- package/examples/demo-monorepo/docs/for-agents/packages/billing.md +19 -0
- package/examples/demo-monorepo/docs/human/guides/auth.md +7 -0
- package/examples/demo-monorepo/package.json +7 -0
- package/examples/demo-monorepo/packages/auth/package.json +8 -0
- package/examples/demo-monorepo/packages/billing/package.json +7 -0
- package/examples/demo-monorepo/pnpm-workspace.yaml +2 -0
- package/examples/ollama-chat.config.ts +42 -0
- package/package.json +12 -9
- package/src/cli/demo.ts +143 -0
- package/src/cli/program.ts +220 -26
- package/src/doctor/badge.ts +53 -0
- package/src/doctor/run-doctor.ts +286 -0
- package/src/federation/llms.ts +20 -11
- package/src/index-builder/build-handoffs.ts +35 -2
- package/src/index-builder/scan-corpus.ts +5 -1
- package/src/index-builder/watch-index.ts +94 -0
- package/src/index.ts +24 -0
- package/src/lib/markdown.ts +31 -4
- package/src/mcp/install.ts +84 -0
- package/src/memory/github-pr.ts +190 -0
- package/src/playbook/doc-bridge-pattern.ts +121 -0
- package/src/query/query.ts +19 -1
- package/src/query/search.ts +85 -17
- package/src/schemas/agent-handoff.ts +11 -0
- package/src/schemas/doc-bridge-index.ts +3 -1
- package/src/schemas/json-schemas.ts +2 -1
- package/src/version.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -372,6 +372,11 @@ var HandoffTargetSchema = z2.object({
|
|
|
372
372
|
group: z2.string().min(1).max(128).optional(),
|
|
373
373
|
layer: z2.string().min(1).max(32).optional()
|
|
374
374
|
}).strict();
|
|
375
|
+
var HandoffBridgeSchema = z2.object({
|
|
376
|
+
humanDoc: z2.enum(["linked", "missing", "external"]),
|
|
377
|
+
action: z2.string().min(1).max(256).optional(),
|
|
378
|
+
bootstrap: z2.string().min(1).max(256).optional()
|
|
379
|
+
}).strict();
|
|
375
380
|
var AgentHandoffV1Schema = z2.object({
|
|
376
381
|
type: z2.literal("agent-handoff"),
|
|
377
382
|
schemaVersion: z2.literal(HANDOFF_SCHEMA_VERSION).default(HANDOFF_SCHEMA_VERSION),
|
|
@@ -382,6 +387,7 @@ var AgentHandoffV1Schema = z2.object({
|
|
|
382
387
|
editRoots: z2.array(z2.string().min(1).max(512)).max(32),
|
|
383
388
|
checks: z2.array(z2.string().min(1).max(256)).max(32),
|
|
384
389
|
humanDoc: z2.string().min(1).max(512).nullable().optional(),
|
|
390
|
+
bridge: HandoffBridgeSchema.optional(),
|
|
385
391
|
playbookPatterns: z2.array(z2.string().url()).max(16).optional(),
|
|
386
392
|
notes: z2.array(z2.string().min(1).max(1024)).max(16)
|
|
387
393
|
}).strict();
|
|
@@ -423,7 +429,9 @@ var KnowledgeEntrySchema = z3.object({
|
|
|
423
429
|
type: z3.string().min(1).max(128),
|
|
424
430
|
title: z3.string().min(1).max(256),
|
|
425
431
|
path: z3.string().min(1).max(512),
|
|
426
|
-
description: z3.string().max(
|
|
432
|
+
description: z3.string().max(2048).optional(),
|
|
433
|
+
/** Flattened body excerpt for full-text search (not for display). */
|
|
434
|
+
body: z3.string().max(8e3).optional(),
|
|
427
435
|
links: z3.array(z3.string().min(1).max(512)).max(64).optional(),
|
|
428
436
|
tags: z3.array(z3.string().min(1).max(64)).max(32).optional()
|
|
429
437
|
}).strict();
|
|
@@ -628,7 +636,8 @@ var DocBridgeIndexV1JsonSchema = {
|
|
|
628
636
|
type: { type: "string", minLength: 1, maxLength: 128 },
|
|
629
637
|
title: { type: "string", minLength: 1, maxLength: 256 },
|
|
630
638
|
path: { type: "string", minLength: 1, maxLength: 512 },
|
|
631
|
-
description: { type: "string", maxLength:
|
|
639
|
+
description: { type: "string", maxLength: 2048 },
|
|
640
|
+
body: { type: "string", maxLength: 8e3 },
|
|
632
641
|
links: stringArray(64),
|
|
633
642
|
tags: stringArray(32)
|
|
634
643
|
}
|
|
@@ -821,7 +830,7 @@ var firstHeading = (markdown) => {
|
|
|
821
830
|
}
|
|
822
831
|
return void 0;
|
|
823
832
|
};
|
|
824
|
-
var firstParagraph = (markdown) => {
|
|
833
|
+
var firstParagraph = (markdown, maxLen = 400) => {
|
|
825
834
|
const { body } = parseFrontmatter(markdown);
|
|
826
835
|
const lines = body.split("\n");
|
|
827
836
|
const buf = [];
|
|
@@ -833,11 +842,27 @@ var firstParagraph = (markdown) => {
|
|
|
833
842
|
}
|
|
834
843
|
if (t.startsWith("#")) continue;
|
|
835
844
|
if (t.startsWith("---")) continue;
|
|
845
|
+
if (t.startsWith("```")) break;
|
|
846
|
+
if (t.startsWith("|") || t.startsWith("- [") || t.startsWith("* [")) {
|
|
847
|
+
if (buf.length) break;
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
836
850
|
buf.push(t);
|
|
837
|
-
if (buf.join(" ").length
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
|
|
851
|
+
if (buf.join(" ").length >= maxLen) break;
|
|
852
|
+
}
|
|
853
|
+
let text = buf.join(" ").replace(/\s+/g, " ").trim();
|
|
854
|
+
if (!text) return void 0;
|
|
855
|
+
if (text.length <= maxLen) return text;
|
|
856
|
+
const sliced = text.slice(0, maxLen);
|
|
857
|
+
const sentenceEnd = Math.max(sliced.lastIndexOf(". "), sliced.lastIndexOf("! "), sliced.lastIndexOf("? "));
|
|
858
|
+
if (sentenceEnd > maxLen * 0.4) return sliced.slice(0, sentenceEnd + 1).trim();
|
|
859
|
+
const wordEnd = sliced.lastIndexOf(" ");
|
|
860
|
+
return (wordEnd > 0 ? sliced.slice(0, wordEnd) : sliced).trim();
|
|
861
|
+
};
|
|
862
|
+
var extractSearchBody = (markdown, maxLen = 6e3) => {
|
|
863
|
+
const { body } = parseFrontmatter(markdown);
|
|
864
|
+
const text = body.replace(/```[\s\S]*?```/g, " ").replace(/!\[[^\]]*\]\([^)]+\)/g, " ").replace(/\[[^\]]*\]\([^)]+\)/g, " ").replace(/^#+\s+/gm, "").replace(/[|>*_`#]/g, " ").replace(/\s+/g, " ").trim();
|
|
865
|
+
return text.length > maxLen ? text.slice(0, maxLen) : text;
|
|
841
866
|
};
|
|
842
867
|
var slugFromPath = (relPath) => {
|
|
843
868
|
const base = relPath.replace(/\.mdx?$/, "");
|
|
@@ -895,7 +920,9 @@ var scanAgentCorpus = (root, config) => {
|
|
|
895
920
|
const { data: frontmatter } = parseFrontmatter(raw);
|
|
896
921
|
const id = frontmatterString(frontmatter, "id") ?? frontmatterString(frontmatter, "package") ?? slugFromPath(relToCorpus);
|
|
897
922
|
const title = firstHeading(raw) ?? id;
|
|
898
|
-
const
|
|
923
|
+
const purpose = frontmatterString(frontmatter, "purpose");
|
|
924
|
+
const description = purpose ?? firstParagraph(raw, 400);
|
|
925
|
+
const body = extractSearchBody(raw);
|
|
899
926
|
return {
|
|
900
927
|
id,
|
|
901
928
|
type: "agent-doc",
|
|
@@ -904,7 +931,8 @@ var scanAgentCorpus = (root, config) => {
|
|
|
904
931
|
absPath: abs,
|
|
905
932
|
relPath,
|
|
906
933
|
frontmatter,
|
|
907
|
-
...description ? { description } : {}
|
|
934
|
+
...description ? { description } : {},
|
|
935
|
+
...body ? { body } : {}
|
|
908
936
|
};
|
|
909
937
|
});
|
|
910
938
|
};
|
|
@@ -1037,11 +1065,19 @@ var resolveHumanDoc = (packageId, override, fmHuman, humanDocs = {}) => {
|
|
|
1037
1065
|
packageId,
|
|
1038
1066
|
packageId.replace(/^@[^/]+\//, ""),
|
|
1039
1067
|
packageId.replace(/^os-/, ""),
|
|
1040
|
-
packageId.replace(/-pattern$/, "")
|
|
1068
|
+
packageId.replace(/-pattern$/, ""),
|
|
1069
|
+
`packages/${packageId}`,
|
|
1070
|
+
`reference/packages/${packageId}`,
|
|
1071
|
+
`packages/${packageId}/index`
|
|
1041
1072
|
];
|
|
1042
1073
|
for (const alias of aliases) {
|
|
1043
1074
|
if (humanDocs[alias]) return humanDocs[alias];
|
|
1044
1075
|
}
|
|
1076
|
+
for (const [key, url] of Object.entries(humanDocs)) {
|
|
1077
|
+
if (key === packageId || key.endsWith(`/${packageId}`) || key.endsWith(`/${packageId}/index`) || key.endsWith(`-${packageId}`)) {
|
|
1078
|
+
return url;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1045
1081
|
return void 0;
|
|
1046
1082
|
};
|
|
1047
1083
|
var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root = process.cwd()) => {
|
|
@@ -1081,6 +1117,11 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1081
1117
|
...agentDoc ? { agentDoc } : {}
|
|
1082
1118
|
};
|
|
1083
1119
|
ownership[pkg.id] = record;
|
|
1120
|
+
const bridge = record.humanDoc ? /^https?:\/\//.test(record.humanDoc) ? { humanDoc: "external" } : { humanDoc: "linked" } : config.corpus.human ? {
|
|
1121
|
+
humanDoc: "missing",
|
|
1122
|
+
action: "ak-docs bootstrap agent-docs",
|
|
1123
|
+
bootstrap: `docs/for-agents/human/${pkg.id}.md`
|
|
1124
|
+
} : void 0;
|
|
1084
1125
|
handoffs[pkg.id] = {
|
|
1085
1126
|
type: "agent-handoff",
|
|
1086
1127
|
schemaVersion: 1,
|
|
@@ -1099,7 +1140,11 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1099
1140
|
editRoots: [record.path],
|
|
1100
1141
|
checks: [...record.checks],
|
|
1101
1142
|
...record.humanDoc ? { humanDoc: record.humanDoc } : {},
|
|
1102
|
-
|
|
1143
|
+
...bridge ? { bridge } : {},
|
|
1144
|
+
notes: [
|
|
1145
|
+
...record.purpose ? [record.purpose] : [],
|
|
1146
|
+
...!record.humanDoc && config.corpus.human ? [`Human guide missing for ${pkg.id}. Run: ak-docs bootstrap agent-docs`] : []
|
|
1147
|
+
]
|
|
1103
1148
|
};
|
|
1104
1149
|
}
|
|
1105
1150
|
const intents = {};
|
|
@@ -1127,7 +1172,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1127
1172
|
};
|
|
1128
1173
|
|
|
1129
1174
|
// src/version.ts
|
|
1130
|
-
var PACKAGE_VERSION = "
|
|
1175
|
+
var PACKAGE_VERSION = "1.0.0";
|
|
1131
1176
|
|
|
1132
1177
|
// src/index-builder/capabilities.ts
|
|
1133
1178
|
var renderCapabilitiesJson = (config, index, paths) => {
|
|
@@ -1226,10 +1271,10 @@ var docId = (relToHumanRoot, raw) => {
|
|
|
1226
1271
|
return meta.package ?? meta.module ?? meta.id ?? slugFromPath(relToHumanRoot);
|
|
1227
1272
|
};
|
|
1228
1273
|
var routeSlug = (relToHumanRoot, opts) => relToHumanRoot.split("/").filter((part) => !opts?.stripGroups || !/^\(.+\)$/.test(part)).join("/").replace(/(?:^|\/)index\.mdx?$/, "").replace(/\.mdx?$/, "");
|
|
1229
|
-
var humanUrl = (
|
|
1230
|
-
if (typeof urlPrefix !== "string" || !urlPrefix) return
|
|
1274
|
+
var humanUrl = (slug2, urlPrefix) => {
|
|
1275
|
+
if (typeof urlPrefix !== "string" || !urlPrefix) return slug2;
|
|
1231
1276
|
const prefix = urlPrefix.replace(/\/$/, "");
|
|
1232
|
-
return
|
|
1277
|
+
return slug2 ? `${prefix}/${slug2.replace(/^\//, "")}` : prefix;
|
|
1233
1278
|
};
|
|
1234
1279
|
var scanMarkdownDocs = (root, humanRoot, options) => {
|
|
1235
1280
|
const out = [];
|
|
@@ -1260,9 +1305,9 @@ var docusaurusFileSlug = (relPath) => {
|
|
|
1260
1305
|
return routeSlug(relPath);
|
|
1261
1306
|
};
|
|
1262
1307
|
var docusaurusSlug = (relPath, raw) => {
|
|
1263
|
-
const
|
|
1264
|
-
if (!
|
|
1265
|
-
return
|
|
1308
|
+
const slug2 = parseFrontmatter2(raw).slug;
|
|
1309
|
+
if (!slug2) return docusaurusFileSlug(relPath);
|
|
1310
|
+
return slug2.replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "");
|
|
1266
1311
|
};
|
|
1267
1312
|
var docusaurusSidebarId = (relPath, raw) => {
|
|
1268
1313
|
const id = parseFrontmatter2(raw).id;
|
|
@@ -1800,19 +1845,58 @@ import { relative, resolve as resolve4 } from "path";
|
|
|
1800
1845
|
import { z as z5, ZodError } from "zod";
|
|
1801
1846
|
|
|
1802
1847
|
// src/query/search.ts
|
|
1803
|
-
var tokenize = (value) => value.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 2);
|
|
1848
|
+
var tokenize = (value) => value.toLowerCase().split(/[^a-z0-9@/_-]+/).filter((t) => t.length >= 2);
|
|
1849
|
+
var PACKAGE_INTENT = /\b(package|module|pkg|edit|change|where|owns?|ownership|handoff|start)\b/i;
|
|
1850
|
+
var scoreHay = (tokens, hay, weight = 1) => {
|
|
1851
|
+
let score = 0;
|
|
1852
|
+
for (const token of tokens) {
|
|
1853
|
+
if (!hay.includes(token)) continue;
|
|
1854
|
+
score += token.length * weight;
|
|
1855
|
+
if (new RegExp(`(?:^|[^a-z0-9])${token}(?:[^a-z0-9]|$)`).test(hay)) {
|
|
1856
|
+
score += token.length;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
return score;
|
|
1860
|
+
};
|
|
1861
|
+
var identityBoost = (id, path, tokens, term) => {
|
|
1862
|
+
const idLower = id.toLowerCase();
|
|
1863
|
+
const termLower = term.toLowerCase().trim();
|
|
1864
|
+
const base = path.split("/").pop()?.replace(/\.mdx?$/i, "").toLowerCase() ?? "";
|
|
1865
|
+
let boost = 0;
|
|
1866
|
+
if (idLower === termLower || base === termLower) boost += 200;
|
|
1867
|
+
if (tokens.length === 1 && (idLower === tokens[0] || base === tokens[0])) boost += 200;
|
|
1868
|
+
for (const token of tokens) {
|
|
1869
|
+
if (idLower === token) boost += 120;
|
|
1870
|
+
else if (idLower.startsWith(`${token}-`) || idLower.endsWith(`-${token}`)) boost += 40;
|
|
1871
|
+
else if (idLower.includes(token) && idLower.length <= token.length + 4) boost += 30;
|
|
1872
|
+
if (base === token) boost += 100;
|
|
1873
|
+
}
|
|
1874
|
+
if (tokens.includes(idLower)) boost += Math.max(0, 40 - idLower.length);
|
|
1875
|
+
return boost;
|
|
1876
|
+
};
|
|
1877
|
+
var preferOwnership = (term) => PACKAGE_INTENT.test(term) || /^(where|how).*(edit|change|package|module)/i.test(term);
|
|
1804
1878
|
var searchIndex = (index, term, limit = 20) => {
|
|
1805
1879
|
const tokens = tokenize(term);
|
|
1806
1880
|
if (!tokens.length) return [];
|
|
1807
|
-
const
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1881
|
+
const wantOwnership = preferOwnership(term);
|
|
1882
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
1883
|
+
const consider = (match) => {
|
|
1884
|
+
const key = match.path;
|
|
1885
|
+
const existing = byPath.get(key);
|
|
1886
|
+
if (!existing) {
|
|
1887
|
+
byPath.set(key, match);
|
|
1888
|
+
return;
|
|
1813
1889
|
}
|
|
1890
|
+
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;
|
|
1891
|
+
if (prefer) byPath.set(key, match);
|
|
1892
|
+
};
|
|
1893
|
+
for (const entry of index.knowledge) {
|
|
1894
|
+
const body = entry.body ?? "";
|
|
1895
|
+
const hay = `${entry.id} ${entry.title} ${entry.description ?? ""} ${entry.path} ${body}`.toLowerCase();
|
|
1896
|
+
let score = scoreHay(tokens, hay, 1);
|
|
1897
|
+
score += identityBoost(entry.id, entry.path, tokens, term);
|
|
1814
1898
|
if (score > 0) {
|
|
1815
|
-
|
|
1899
|
+
consider({
|
|
1816
1900
|
type: "knowledge",
|
|
1817
1901
|
id: entry.id,
|
|
1818
1902
|
path: entry.path,
|
|
@@ -1822,22 +1906,31 @@ var searchIndex = (index, term, limit = 20) => {
|
|
|
1822
1906
|
}
|
|
1823
1907
|
}
|
|
1824
1908
|
for (const [id, owner] of Object.entries(index.lookup?.ownership ?? {})) {
|
|
1825
|
-
const
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1909
|
+
const path = owner.agentDoc ?? owner.path;
|
|
1910
|
+
const hay = `${id} ${owner.path} ${owner.purpose ?? ""} ${owner.group ?? ""} ${owner.agentDoc ?? ""} ${owner.humanDoc ?? ""}`.toLowerCase();
|
|
1911
|
+
let score = scoreHay(tokens, hay, 2);
|
|
1912
|
+
score += identityBoost(id, path, tokens, term);
|
|
1913
|
+
if (wantOwnership) score += 25;
|
|
1914
|
+
score += 15;
|
|
1830
1915
|
if (score > 0) {
|
|
1831
|
-
|
|
1916
|
+
consider({
|
|
1832
1917
|
type: "ownership",
|
|
1833
1918
|
id,
|
|
1834
|
-
path
|
|
1919
|
+
path,
|
|
1835
1920
|
...owner.purpose ? { summary: owner.purpose } : {},
|
|
1836
1921
|
score
|
|
1837
1922
|
});
|
|
1838
1923
|
}
|
|
1839
1924
|
}
|
|
1840
|
-
return
|
|
1925
|
+
return [...byPath.values()].sort((a, b) => {
|
|
1926
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
1927
|
+
const aExact = tokens.includes(a.id.toLowerCase()) ? 1 : 0;
|
|
1928
|
+
const bExact = tokens.includes(b.id.toLowerCase()) ? 1 : 0;
|
|
1929
|
+
if (bExact !== aExact) return bExact - aExact;
|
|
1930
|
+
if (a.type === "ownership" && b.type !== "ownership") return -1;
|
|
1931
|
+
if (b.type === "ownership" && a.type !== "ownership") return 1;
|
|
1932
|
+
return a.id.localeCompare(b.id);
|
|
1933
|
+
}).slice(0, limit);
|
|
1841
1934
|
};
|
|
1842
1935
|
|
|
1843
1936
|
// src/retriever/doc-bridge-retriever.ts
|
|
@@ -1984,6 +2077,11 @@ var handoffForPackage = (index, id, config) => {
|
|
|
1984
2077
|
if (fromIndex) return normalizeAgentHandoff(fromIndex);
|
|
1985
2078
|
const owner = index.lookup?.ownership?.[id];
|
|
1986
2079
|
if (!owner) throw new Error(`Unknown package/ownership id "${id}". Try: ak-docs list packages`);
|
|
2080
|
+
const bridge = owner.humanDoc ? /^https?:\/\//.test(owner.humanDoc) ? { humanDoc: "external" } : { humanDoc: "linked" } : config.corpus.human ? {
|
|
2081
|
+
humanDoc: "missing",
|
|
2082
|
+
action: "ak-docs bootstrap agent-docs",
|
|
2083
|
+
bootstrap: `docs/for-agents/human/${id}.md`
|
|
2084
|
+
} : void 0;
|
|
1987
2085
|
return normalizeAgentHandoff({
|
|
1988
2086
|
type: "agent-handoff",
|
|
1989
2087
|
source: config.index?.outFile ?? ".doc-bridge/index.json",
|
|
@@ -1999,7 +2097,11 @@ var handoffForPackage = (index, id, config) => {
|
|
|
1999
2097
|
editRoots: [owner.path],
|
|
2000
2098
|
checks: [...owner.checks],
|
|
2001
2099
|
...owner.humanDoc ? { humanDoc: owner.humanDoc } : {},
|
|
2002
|
-
|
|
2100
|
+
...bridge ? { bridge } : {},
|
|
2101
|
+
notes: [
|
|
2102
|
+
...owner.purpose ? [owner.purpose] : [],
|
|
2103
|
+
...!owner.humanDoc && config.corpus.human ? [`Human guide missing for ${id}. Run: ak-docs bootstrap agent-docs`] : []
|
|
2104
|
+
]
|
|
2003
2105
|
});
|
|
2004
2106
|
};
|
|
2005
2107
|
var runQuery = (index, config, req) => {
|
|
@@ -2300,9 +2402,531 @@ var startMcpStdioServer = (ctx) => {
|
|
|
2300
2402
|
process.stdin.resume();
|
|
2301
2403
|
};
|
|
2302
2404
|
|
|
2405
|
+
// src/mcp/install.ts
|
|
2406
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync13, writeFileSync as writeFileSync2 } from "fs";
|
|
2407
|
+
import { homedir } from "os";
|
|
2408
|
+
import { dirname as dirname4, join as join13, resolve as resolve5 } from "path";
|
|
2409
|
+
var SERVER_NAME = "ak-docs";
|
|
2410
|
+
var mcpServerEntry = (root) => ({
|
|
2411
|
+
command: "npx",
|
|
2412
|
+
args: ["ak-docs", "mcp"],
|
|
2413
|
+
cwd: root
|
|
2414
|
+
});
|
|
2415
|
+
var readJson = (path) => {
|
|
2416
|
+
if (!existsSync9(path)) return {};
|
|
2417
|
+
try {
|
|
2418
|
+
const parsed = JSON.parse(readFileSync13(path, "utf8"));
|
|
2419
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
2420
|
+
} catch {
|
|
2421
|
+
return {};
|
|
2422
|
+
}
|
|
2423
|
+
};
|
|
2424
|
+
var writeJson = (path, value) => {
|
|
2425
|
+
mkdirSync2(dirname4(path), { recursive: true });
|
|
2426
|
+
writeFileSync2(path, `${JSON.stringify(value, null, 2)}
|
|
2427
|
+
`, "utf8");
|
|
2428
|
+
};
|
|
2429
|
+
var resolveTargetPath = (target, root) => {
|
|
2430
|
+
if (target === "cursor") return resolve5(root, ".cursor", "mcp.json");
|
|
2431
|
+
return join13(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
2432
|
+
};
|
|
2433
|
+
var installMcpConfig = (root, target) => {
|
|
2434
|
+
const configPath = resolveTargetPath(target, root);
|
|
2435
|
+
const created = !existsSync9(configPath);
|
|
2436
|
+
const existing = readJson(configPath);
|
|
2437
|
+
const servers = existing.mcpServers && typeof existing.mcpServers === "object" && !Array.isArray(existing.mcpServers) ? { ...existing.mcpServers } : {};
|
|
2438
|
+
servers[SERVER_NAME] = mcpServerEntry(root);
|
|
2439
|
+
writeJson(configPath, { ...existing, mcpServers: servers });
|
|
2440
|
+
const nextSteps = target === "cursor" ? [
|
|
2441
|
+
"Restart Cursor or reload MCP servers",
|
|
2442
|
+
"Paste the doc-bridge skill into Cursor rules (see docs/skills/doc-bridge.md)",
|
|
2443
|
+
"Before editing packages/* call handoff.resolve"
|
|
2444
|
+
] : [
|
|
2445
|
+
"Restart Claude Desktop",
|
|
2446
|
+
"Run ak-docs index after doc changes"
|
|
2447
|
+
];
|
|
2448
|
+
return {
|
|
2449
|
+
ok: true,
|
|
2450
|
+
target,
|
|
2451
|
+
configPath,
|
|
2452
|
+
created,
|
|
2453
|
+
serverName: SERVER_NAME,
|
|
2454
|
+
nextSteps
|
|
2455
|
+
};
|
|
2456
|
+
};
|
|
2457
|
+
var mcpSnippet = (root) => JSON.stringify({ mcpServers: { [SERVER_NAME]: mcpServerEntry(root) } }, null, 2);
|
|
2458
|
+
|
|
2459
|
+
// src/doctor/badge.ts
|
|
2460
|
+
var doctorBadgeMetrics = (report) => {
|
|
2461
|
+
const { packages } = report.coverage;
|
|
2462
|
+
const handoffPct = packages.total > 0 ? Math.round(packages.withAgentDoc / packages.total * 100) : 0;
|
|
2463
|
+
const bridgePct = packages.total > 0 ? Math.round(packages.withHumanDoc / packages.total * 100) : 0;
|
|
2464
|
+
return {
|
|
2465
|
+
handoffPct,
|
|
2466
|
+
bridgePct,
|
|
2467
|
+
score: report.score,
|
|
2468
|
+
grade: report.grade,
|
|
2469
|
+
packages: packages.total
|
|
2470
|
+
};
|
|
2471
|
+
};
|
|
2472
|
+
var badgeColor = (pct) => {
|
|
2473
|
+
if (pct >= 80) return "2ea44f";
|
|
2474
|
+
if (pct >= 50) return "dbab09";
|
|
2475
|
+
return "cb2431";
|
|
2476
|
+
};
|
|
2477
|
+
var formatDoctorBadgeMarkdown = (metrics) => {
|
|
2478
|
+
const handoff = `handoff_coverage-${metrics.handoffPct}%25-${badgeColor(metrics.handoffPct)}`;
|
|
2479
|
+
const bridge = `human_bridge-${metrics.bridgePct}%25-${badgeColor(metrics.bridgePct)}`;
|
|
2480
|
+
return [
|
|
2481
|
+
``,
|
|
2482
|
+
``,
|
|
2483
|
+
`}?style=flat-square)`
|
|
2484
|
+
].join(" ");
|
|
2485
|
+
};
|
|
2486
|
+
var formatDoctorBadgeJson = (metrics) => JSON.stringify(
|
|
2487
|
+
{
|
|
2488
|
+
schemaVersion: 1,
|
|
2489
|
+
...metrics,
|
|
2490
|
+
markdown: formatDoctorBadgeMarkdown(metrics),
|
|
2491
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2492
|
+
},
|
|
2493
|
+
null,
|
|
2494
|
+
2
|
|
2495
|
+
);
|
|
2496
|
+
|
|
2497
|
+
// src/doctor/run-doctor.ts
|
|
2498
|
+
var gradeForScore = (score) => {
|
|
2499
|
+
if (score >= 90) return "A";
|
|
2500
|
+
if (score >= 75) return "B";
|
|
2501
|
+
if (score >= 60) return "C";
|
|
2502
|
+
if (score >= 40) return "D";
|
|
2503
|
+
return "F";
|
|
2504
|
+
};
|
|
2505
|
+
var agentDocPaths = (index) => {
|
|
2506
|
+
const paths = /* @__PURE__ */ new Set();
|
|
2507
|
+
for (const owner of Object.values(index.lookup?.ownership ?? {})) {
|
|
2508
|
+
if (owner.agentDoc) paths.add(owner.agentDoc);
|
|
2509
|
+
}
|
|
2510
|
+
for (const handoff of Object.values(index.handoffs ?? {})) {
|
|
2511
|
+
if (handoff.startHere) paths.add(handoff.startHere);
|
|
2512
|
+
}
|
|
2513
|
+
return paths;
|
|
2514
|
+
};
|
|
2515
|
+
var computeScore = (coverage) => {
|
|
2516
|
+
let score = 0;
|
|
2517
|
+
if (coverage.freshness.hasIndex) score += 15;
|
|
2518
|
+
if (coverage.freshness.ok) score += 15;
|
|
2519
|
+
const { total, withAgentDoc, withHumanDoc } = coverage.packages;
|
|
2520
|
+
if (total > 0) {
|
|
2521
|
+
score += Math.round(withAgentDoc / total * 35);
|
|
2522
|
+
score += Math.round(withHumanDoc / total * 20);
|
|
2523
|
+
} else if (coverage.agentDocs.indexed > 0) {
|
|
2524
|
+
score += 35;
|
|
2525
|
+
}
|
|
2526
|
+
if (coverage.gates.ok) score += 15;
|
|
2527
|
+
else {
|
|
2528
|
+
const passed = coverage.gates.results.filter((gate) => gate.ok).length;
|
|
2529
|
+
const totalGates = coverage.gates.results.length || 1;
|
|
2530
|
+
score += Math.round(passed / totalGates * 10);
|
|
2531
|
+
}
|
|
2532
|
+
return Math.min(100, Math.max(0, score));
|
|
2533
|
+
};
|
|
2534
|
+
var buildIssues = (coverage) => {
|
|
2535
|
+
const issues = [];
|
|
2536
|
+
if (!coverage.freshness.hasIndex) {
|
|
2537
|
+
issues.push({
|
|
2538
|
+
severity: "error",
|
|
2539
|
+
code: "index-missing",
|
|
2540
|
+
message: "No doc-bridge index found.",
|
|
2541
|
+
action: "ak-docs index"
|
|
2542
|
+
});
|
|
2543
|
+
} else if (!coverage.freshness.ok) {
|
|
2544
|
+
issues.push({
|
|
2545
|
+
severity: "error",
|
|
2546
|
+
code: "index-stale",
|
|
2547
|
+
message: coverage.freshness.message,
|
|
2548
|
+
action: "ak-docs index"
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
for (const id of coverage.packages.missingAgentDoc) {
|
|
2552
|
+
issues.push({
|
|
2553
|
+
severity: "warn",
|
|
2554
|
+
code: "missing-agent-doc",
|
|
2555
|
+
message: `Package "${id}" has no dedicated agent doc.`,
|
|
2556
|
+
action: `ak-docs init --scaffold-workspaces # or edit docs/for-agents/packages/${id}.md`
|
|
2557
|
+
});
|
|
2558
|
+
}
|
|
2559
|
+
for (const id of coverage.packages.missingHumanDoc) {
|
|
2560
|
+
issues.push({
|
|
2561
|
+
severity: "info",
|
|
2562
|
+
code: "missing-human-doc",
|
|
2563
|
+
message: `Package "${id}" has no linked human guide.`,
|
|
2564
|
+
action: "ak-docs bootstrap agent-docs"
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
for (const gate of coverage.gates.results.filter((result) => !result.ok)) {
|
|
2568
|
+
issues.push({
|
|
2569
|
+
severity: gate.id === "index-freshness" ? "error" : "warn",
|
|
2570
|
+
code: `gate-${gate.id}`,
|
|
2571
|
+
message: gate.message,
|
|
2572
|
+
action: gate.id === "index-freshness" ? "ak-docs index" : "ak-docs gate run"
|
|
2573
|
+
});
|
|
2574
|
+
}
|
|
2575
|
+
return issues;
|
|
2576
|
+
};
|
|
2577
|
+
var buildNextActions = (issues, coverage) => {
|
|
2578
|
+
const actions = /* @__PURE__ */ new Set();
|
|
2579
|
+
for (const issue of issues) {
|
|
2580
|
+
if (issue.action) actions.add(issue.action);
|
|
2581
|
+
}
|
|
2582
|
+
if (!coverage.freshness.hasIndex || !coverage.freshness.ok) {
|
|
2583
|
+
actions.add("ak-docs index");
|
|
2584
|
+
}
|
|
2585
|
+
if (coverage.packages.missingHumanDoc.length) {
|
|
2586
|
+
actions.add("ak-docs bootstrap agent-docs");
|
|
2587
|
+
}
|
|
2588
|
+
if (coverage.packages.total > 0) {
|
|
2589
|
+
const sample = coverage.packages.missingAgentDoc[0] ?? coverage.packages.missingHumanDoc[0];
|
|
2590
|
+
if (sample) actions.add(`ak-docs query package ${sample} --agent`);
|
|
2591
|
+
}
|
|
2592
|
+
if (!actions.size) {
|
|
2593
|
+
actions.add("ak-docs mcp install --cursor");
|
|
2594
|
+
actions.add("ak-docs gate run");
|
|
2595
|
+
}
|
|
2596
|
+
return [...actions].slice(0, 6);
|
|
2597
|
+
};
|
|
2598
|
+
var runDoctor = (root, config) => {
|
|
2599
|
+
let index;
|
|
2600
|
+
let hasIndex = true;
|
|
2601
|
+
let freshnessOk = false;
|
|
2602
|
+
let freshnessMessage = "Index is fresh";
|
|
2603
|
+
try {
|
|
2604
|
+
index = loadDocBridgeIndex(root, config);
|
|
2605
|
+
const next = buildDocBridgeIndex({ root, config, write: false }).index.contentHash;
|
|
2606
|
+
freshnessOk = index.contentHash === next;
|
|
2607
|
+
freshnessMessage = freshnessOk ? "Index is fresh" : "Index is stale. Run: ak-docs index";
|
|
2608
|
+
} catch (error) {
|
|
2609
|
+
if (error instanceof IndexNotFoundError) {
|
|
2610
|
+
hasIndex = false;
|
|
2611
|
+
freshnessOk = false;
|
|
2612
|
+
freshnessMessage = error.message;
|
|
2613
|
+
index = buildDocBridgeIndex({ root, config, write: false }).index;
|
|
2614
|
+
} else {
|
|
2615
|
+
throw error;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
const ownership = Object.entries(index.lookup?.ownership ?? {});
|
|
2619
|
+
const missingAgentDoc = ownership.filter(([, owner]) => !owner.agentDoc || owner.agentDoc === config.corpus.agent.index).map(([id]) => id);
|
|
2620
|
+
const missingHumanDoc = ownership.filter(([, owner]) => !owner.humanDoc).map(([id]) => id);
|
|
2621
|
+
const indexedPaths = new Set(index.knowledge.map((entry) => entry.path));
|
|
2622
|
+
const expectedAgentDocs = agentDocPaths(index);
|
|
2623
|
+
const unindexed = [...expectedAgentDocs].filter((path) => !indexedPaths.has(path));
|
|
2624
|
+
const corpusDocs = scanAgentCorpus(root, config).filter(
|
|
2625
|
+
(doc) => doc.path !== config.corpus.agent.index
|
|
2626
|
+
);
|
|
2627
|
+
const gates = runGates(root, config);
|
|
2628
|
+
const coverage = {
|
|
2629
|
+
packages: {
|
|
2630
|
+
total: ownership.length,
|
|
2631
|
+
withAgentDoc: ownership.length - missingAgentDoc.length,
|
|
2632
|
+
withHumanDoc: ownership.length - missingHumanDoc.length,
|
|
2633
|
+
missingAgentDoc,
|
|
2634
|
+
missingHumanDoc
|
|
2635
|
+
},
|
|
2636
|
+
agentDocs: {
|
|
2637
|
+
total: corpusDocs.length,
|
|
2638
|
+
indexed: corpusDocs.filter((doc) => indexedPaths.has(doc.path)).length,
|
|
2639
|
+
unindexed: corpusDocs.filter((doc) => !indexedPaths.has(doc.path)).map((doc) => doc.path)
|
|
2640
|
+
},
|
|
2641
|
+
freshness: {
|
|
2642
|
+
ok: freshnessOk,
|
|
2643
|
+
message: freshnessMessage,
|
|
2644
|
+
hasIndex
|
|
2645
|
+
},
|
|
2646
|
+
gates
|
|
2647
|
+
};
|
|
2648
|
+
const issues = buildIssues(coverage);
|
|
2649
|
+
const score = computeScore(coverage);
|
|
2650
|
+
const nextActions = buildNextActions(issues, coverage);
|
|
2651
|
+
const report = {
|
|
2652
|
+
ok: issues.every((issue) => issue.severity !== "error") && gates.ok,
|
|
2653
|
+
score,
|
|
2654
|
+
grade: gradeForScore(score),
|
|
2655
|
+
coverage,
|
|
2656
|
+
badge: { handoffPct: 0, bridgePct: 0, score, grade: gradeForScore(score), packages: 0 },
|
|
2657
|
+
issues,
|
|
2658
|
+
nextActions
|
|
2659
|
+
};
|
|
2660
|
+
return { ...report, badge: doctorBadgeMetrics(report) };
|
|
2661
|
+
};
|
|
2662
|
+
var formatDoctorText = (report) => {
|
|
2663
|
+
const { coverage } = report;
|
|
2664
|
+
const handoffPct = coverage.packages.total > 0 ? Math.round(coverage.packages.withAgentDoc / coverage.packages.total * 100) : 0;
|
|
2665
|
+
const humanPct = coverage.packages.total > 0 ? Math.round(coverage.packages.withHumanDoc / coverage.packages.total * 100) : 0;
|
|
2666
|
+
const lines = [
|
|
2667
|
+
"doc-bridge doctor",
|
|
2668
|
+
"\u2500".repeat(40),
|
|
2669
|
+
`Score: ${report.score}/100 (${report.grade})`,
|
|
2670
|
+
"",
|
|
2671
|
+
"Coverage",
|
|
2672
|
+
` Packages: ${coverage.packages.total}`,
|
|
2673
|
+
` Agent docs: ${coverage.packages.withAgentDoc}/${coverage.packages.total} (${handoffPct}% handoff-ready)`,
|
|
2674
|
+
` Human guides: ${coverage.packages.withHumanDoc}/${coverage.packages.total} (${humanPct}% bridged)`,
|
|
2675
|
+
` Corpus indexed: ${coverage.agentDocs.indexed}/${coverage.agentDocs.total} agent docs`,
|
|
2676
|
+
` Index freshness: ${coverage.freshness.ok ? "fresh" : "stale or missing"}`,
|
|
2677
|
+
` Gates: ${coverage.gates.results.filter((g) => g.ok).length}/${coverage.gates.results.length} passing`,
|
|
2678
|
+
` Badge: handoff ${report.badge.handoffPct}% \xB7 bridge ${report.badge.bridgePct}%`
|
|
2679
|
+
];
|
|
2680
|
+
if (coverage.packages.missingHumanDoc.length) {
|
|
2681
|
+
lines.push("", "Missing humanDoc (bridge gap)", ...coverage.packages.missingHumanDoc.map((id) => ` \u2022 ${id}`));
|
|
2682
|
+
}
|
|
2683
|
+
if (coverage.packages.missingAgentDoc.length) {
|
|
2684
|
+
lines.push("", "Missing agent doc", ...coverage.packages.missingAgentDoc.map((id) => ` \u2022 ${id}`));
|
|
2685
|
+
}
|
|
2686
|
+
if (report.issues.length) {
|
|
2687
|
+
lines.push("", "Issues");
|
|
2688
|
+
for (const issue of report.issues.slice(0, 8)) {
|
|
2689
|
+
const icon = issue.severity === "error" ? "\u2717" : issue.severity === "warn" ? "!" : "\xB7";
|
|
2690
|
+
lines.push(` ${icon} [${issue.code}] ${issue.message}`);
|
|
2691
|
+
}
|
|
2692
|
+
if (report.issues.length > 8) {
|
|
2693
|
+
lines.push(` \u2026 +${report.issues.length - 8} more`);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
lines.push("", "Next actions", ...report.nextActions.map((action) => ` \u2192 ${action}`));
|
|
2697
|
+
return lines;
|
|
2698
|
+
};
|
|
2699
|
+
|
|
2700
|
+
// src/index-builder/watch-index.ts
|
|
2701
|
+
import { existsSync as existsSync10, watch } from "fs";
|
|
2702
|
+
import { dirname as dirname5, resolve as resolve6 } from "path";
|
|
2703
|
+
var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
|
|
2704
|
+
var collectWatchRoots = (root, config, configPath) => {
|
|
2705
|
+
const roots = /* @__PURE__ */ new Set();
|
|
2706
|
+
roots.add(resolve6(root, config.corpus.agent.root));
|
|
2707
|
+
const humanSources = config.corpus.human ? Array.isArray(config.corpus.human) ? config.corpus.human : [config.corpus.human] : [];
|
|
2708
|
+
for (const source of humanSources) {
|
|
2709
|
+
const humanOpts = source.options ?? {};
|
|
2710
|
+
for (const key of ["contentDir", "docsDir", "root"]) {
|
|
2711
|
+
const value = humanOpts[key];
|
|
2712
|
+
if (typeof value === "string" && value.length) roots.add(resolve6(root, value));
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
if (configPath) roots.add(dirname5(resolve6(configPath)));
|
|
2716
|
+
return [...roots].filter((dir) => existsSync10(dir));
|
|
2717
|
+
};
|
|
2718
|
+
var watchDocBridgeIndex = (opts) => {
|
|
2719
|
+
const debounceMs = opts.debounceMs ?? 350;
|
|
2720
|
+
let timer;
|
|
2721
|
+
let running = false;
|
|
2722
|
+
const rebuild = () => {
|
|
2723
|
+
if (timer) clearTimeout(timer);
|
|
2724
|
+
timer = setTimeout(() => {
|
|
2725
|
+
if (running) return;
|
|
2726
|
+
running = true;
|
|
2727
|
+
try {
|
|
2728
|
+
const result = buildDocBridgeIndex({ root: opts.root, config: opts.config });
|
|
2729
|
+
const handoffCount = Object.keys(result.index.handoffs ?? {}).length;
|
|
2730
|
+
const summary = {
|
|
2731
|
+
knowledgeCount: result.index.knowledge.length,
|
|
2732
|
+
handoffCount,
|
|
2733
|
+
hash: result.index.contentHash.slice(0, 8)
|
|
2734
|
+
};
|
|
2735
|
+
opts.onRebuild?.(summary);
|
|
2736
|
+
process.stdout.write(
|
|
2737
|
+
`[ak-docs] indexed ${summary.knowledgeCount} docs, ${summary.handoffCount} handoffs (${summary.hash}\u2026)
|
|
2738
|
+
`
|
|
2739
|
+
);
|
|
2740
|
+
} catch (error) {
|
|
2741
|
+
process.stderr.write(
|
|
2742
|
+
`[ak-docs] index failed: ${error instanceof Error ? error.message : String(error)}
|
|
2743
|
+
`
|
|
2744
|
+
);
|
|
2745
|
+
} finally {
|
|
2746
|
+
running = false;
|
|
2747
|
+
}
|
|
2748
|
+
}, debounceMs);
|
|
2749
|
+
};
|
|
2750
|
+
rebuild();
|
|
2751
|
+
for (const dir of collectWatchRoots(opts.root, opts.config, opts.configPath)) {
|
|
2752
|
+
watch(dir, { recursive: true }, (_event, filename) => {
|
|
2753
|
+
if (!filename || !WATCH_PATTERN.test(filename)) return;
|
|
2754
|
+
rebuild();
|
|
2755
|
+
});
|
|
2756
|
+
}
|
|
2757
|
+
const configDir = resolve6(opts.root);
|
|
2758
|
+
if (existsSync10(configDir)) {
|
|
2759
|
+
watch(configDir, (_event, filename) => {
|
|
2760
|
+
if (!filename || !/doc-bridge\.config/.test(filename)) return;
|
|
2761
|
+
rebuild();
|
|
2762
|
+
});
|
|
2763
|
+
}
|
|
2764
|
+
process.stdout.write(
|
|
2765
|
+
`[ak-docs] watching ${collectWatchRoots(opts.root, opts.config, opts.configPath).join(", ") || opts.root} (Ctrl+C to stop)
|
|
2766
|
+
`
|
|
2767
|
+
);
|
|
2768
|
+
return new Promise((resolvePromise) => {
|
|
2769
|
+
const onSignal = () => resolvePromise(0);
|
|
2770
|
+
process.once("SIGINT", onSignal);
|
|
2771
|
+
process.once("SIGTERM", onSignal);
|
|
2772
|
+
});
|
|
2773
|
+
};
|
|
2774
|
+
|
|
2775
|
+
// src/memory/github-pr.ts
|
|
2776
|
+
import { execFileSync, spawnSync } from "child_process";
|
|
2777
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
2778
|
+
import { join as join15 } from "path";
|
|
2779
|
+
var run = (cmd, args, cwd) => {
|
|
2780
|
+
const result = spawnSync(cmd, [...args], { cwd, encoding: "utf8" });
|
|
2781
|
+
const out = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
2782
|
+
return { ok: result.status === 0, out };
|
|
2783
|
+
};
|
|
2784
|
+
var hasGh = () => run("gh", ["--version"], process.cwd()).ok;
|
|
2785
|
+
var slug = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
2786
|
+
var defaultPromotionDraftPath = (root) => join15(root, ".doc-bridge", "drafts", `memory-promotion-${slug()}.md`);
|
|
2787
|
+
var writePromotionDraft = (root, draft, path) => {
|
|
2788
|
+
const draftPath = path ?? defaultPromotionDraftPath(root);
|
|
2789
|
+
mkdirSync3(join15(root, ".doc-bridge", "drafts"), { recursive: true });
|
|
2790
|
+
writeFileSync3(draftPath, `${draft.body}
|
|
2791
|
+
`, "utf8");
|
|
2792
|
+
return draftPath;
|
|
2793
|
+
};
|
|
2794
|
+
var promoteMemoryToGithubPr = (root, draft, options = {}) => {
|
|
2795
|
+
if (!draft.ok && !options.force) {
|
|
2796
|
+
return {
|
|
2797
|
+
ok: false,
|
|
2798
|
+
dryRun: Boolean(options.dryRun),
|
|
2799
|
+
draftPath: "",
|
|
2800
|
+
branch: "",
|
|
2801
|
+
commands: [],
|
|
2802
|
+
message: "Safety scan blocked promotion. Fix findings or pass --force to draft anyway."
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
const branch = options.branch ?? `doc-bridge/memory-promotion-${slug()}`;
|
|
2806
|
+
const draftPath = writePromotionDraft(root, draft);
|
|
2807
|
+
const relDraft = draftPath.startsWith(`${root}/`) ? draftPath.slice(root.length + 1) : draftPath;
|
|
2808
|
+
const commands = [
|
|
2809
|
+
`git checkout -b ${branch}`,
|
|
2810
|
+
`git add ${relDraft}`,
|
|
2811
|
+
`git commit -m "draft: doc-bridge memory promotion"`,
|
|
2812
|
+
`git push -u origin ${branch}`,
|
|
2813
|
+
`gh pr create --draft --title "${draft.title}" --body-file ${relDraft}${options.base ? ` --base ${options.base}` : ""}`
|
|
2814
|
+
];
|
|
2815
|
+
if (options.dryRun) {
|
|
2816
|
+
return {
|
|
2817
|
+
ok: true,
|
|
2818
|
+
dryRun: true,
|
|
2819
|
+
draftPath,
|
|
2820
|
+
branch,
|
|
2821
|
+
commands,
|
|
2822
|
+
message: `Wrote draft to ${relDraft}. Run the printed git/gh commands to open a draft PR.`
|
|
2823
|
+
};
|
|
2824
|
+
}
|
|
2825
|
+
if (!existsSync11(join15(root, ".git"))) {
|
|
2826
|
+
return {
|
|
2827
|
+
ok: false,
|
|
2828
|
+
dryRun: false,
|
|
2829
|
+
draftPath,
|
|
2830
|
+
branch,
|
|
2831
|
+
commands,
|
|
2832
|
+
message: "Not a git repository. Commit the draft manually or run with --dry-run."
|
|
2833
|
+
};
|
|
2834
|
+
}
|
|
2835
|
+
if (!hasGh()) {
|
|
2836
|
+
return {
|
|
2837
|
+
ok: false,
|
|
2838
|
+
dryRun: false,
|
|
2839
|
+
draftPath,
|
|
2840
|
+
branch,
|
|
2841
|
+
commands,
|
|
2842
|
+
message: "GitHub CLI (gh) not found. Install gh or use --dry-run for local draft + commands."
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
const auth = run("gh", ["auth", "status"], root);
|
|
2846
|
+
if (!auth.ok) {
|
|
2847
|
+
return {
|
|
2848
|
+
ok: false,
|
|
2849
|
+
dryRun: false,
|
|
2850
|
+
draftPath,
|
|
2851
|
+
branch,
|
|
2852
|
+
commands,
|
|
2853
|
+
message: `gh is not authenticated. Run: gh auth login
|
|
2854
|
+
${auth.out}`
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2857
|
+
const checkout = run("git", ["checkout", "-b", branch], root);
|
|
2858
|
+
if (!checkout.ok) {
|
|
2859
|
+
return {
|
|
2860
|
+
ok: false,
|
|
2861
|
+
dryRun: false,
|
|
2862
|
+
draftPath,
|
|
2863
|
+
branch,
|
|
2864
|
+
commands,
|
|
2865
|
+
message: `git checkout failed: ${checkout.out}`
|
|
2866
|
+
};
|
|
2867
|
+
}
|
|
2868
|
+
run("git", ["add", relDraft], root);
|
|
2869
|
+
const commit = run("git", ["commit", "-m", "draft: doc-bridge memory promotion"], root);
|
|
2870
|
+
if (!commit.ok) {
|
|
2871
|
+
return {
|
|
2872
|
+
ok: false,
|
|
2873
|
+
dryRun: false,
|
|
2874
|
+
draftPath,
|
|
2875
|
+
branch,
|
|
2876
|
+
commands,
|
|
2877
|
+
message: `git commit failed: ${commit.out}`
|
|
2878
|
+
};
|
|
2879
|
+
}
|
|
2880
|
+
const push = run("git", ["push", "-u", "origin", branch], root);
|
|
2881
|
+
if (!push.ok) {
|
|
2882
|
+
return {
|
|
2883
|
+
ok: false,
|
|
2884
|
+
dryRun: false,
|
|
2885
|
+
draftPath,
|
|
2886
|
+
branch,
|
|
2887
|
+
commands,
|
|
2888
|
+
message: `git push failed: ${push.out}`
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
const prArgs = [
|
|
2892
|
+
"pr",
|
|
2893
|
+
"create",
|
|
2894
|
+
"--draft",
|
|
2895
|
+
"--title",
|
|
2896
|
+
draft.title,
|
|
2897
|
+
"--body-file",
|
|
2898
|
+
relDraft,
|
|
2899
|
+
...options.base ? ["--base", options.base] : []
|
|
2900
|
+
];
|
|
2901
|
+
let prUrl = "";
|
|
2902
|
+
try {
|
|
2903
|
+
prUrl = execFileSync("gh", prArgs, { cwd: root, encoding: "utf8" }).trim();
|
|
2904
|
+
} catch (error) {
|
|
2905
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2906
|
+
return {
|
|
2907
|
+
ok: false,
|
|
2908
|
+
dryRun: false,
|
|
2909
|
+
draftPath,
|
|
2910
|
+
branch,
|
|
2911
|
+
commands,
|
|
2912
|
+
message: `gh pr create failed: ${message}`
|
|
2913
|
+
};
|
|
2914
|
+
}
|
|
2915
|
+
return {
|
|
2916
|
+
ok: true,
|
|
2917
|
+
dryRun: false,
|
|
2918
|
+
draftPath,
|
|
2919
|
+
branch,
|
|
2920
|
+
prUrl,
|
|
2921
|
+
...prUrl ? { previewUrl: prUrl } : {},
|
|
2922
|
+
commands,
|
|
2923
|
+
message: prUrl ? `Draft PR opened: ${prUrl}` : "Draft PR created."
|
|
2924
|
+
};
|
|
2925
|
+
};
|
|
2926
|
+
|
|
2303
2927
|
// src/federation/llms.ts
|
|
2304
|
-
import { existsSync as
|
|
2305
|
-
import { resolve as
|
|
2928
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
|
|
2929
|
+
import { resolve as resolve7 } from "path";
|
|
2306
2930
|
var tokenize2 = (value) => value.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length >= 2);
|
|
2307
2931
|
var scoreText = (query, text) => {
|
|
2308
2932
|
const hay = text.toLowerCase();
|
|
@@ -2315,10 +2939,14 @@ var defaultFetchText = async (url) => {
|
|
|
2315
2939
|
return res.text();
|
|
2316
2940
|
};
|
|
2317
2941
|
var sourceText = async (root, source, fetchText) => {
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2942
|
+
try {
|
|
2943
|
+
if (/^https?:\/\//.test(source)) return await fetchText(source);
|
|
2944
|
+
const path = resolve7(root, source);
|
|
2945
|
+
if (!existsSync12(path)) return null;
|
|
2946
|
+
return readFileSync14(path, "utf8");
|
|
2947
|
+
} catch {
|
|
2948
|
+
return null;
|
|
2949
|
+
}
|
|
2322
2950
|
};
|
|
2323
2951
|
var sameOrigin = (base, target) => {
|
|
2324
2952
|
if (!/^https?:\/\//.test(base) || !/^https?:\/\//.test(target)) return true;
|
|
@@ -2357,22 +2985,28 @@ var chunksFromMarkdown = (property, raw, sourceUrl) => {
|
|
|
2357
2985
|
var loadFederatedChunks = async (root, config, options = {}) => {
|
|
2358
2986
|
const fetchText = options.fetchText ?? defaultFetchText;
|
|
2359
2987
|
const chunks = [];
|
|
2988
|
+
const warnings = [];
|
|
2360
2989
|
for (const source of config.federation?.sources ?? []) {
|
|
2361
2990
|
if (source.includeInRetriever === false || !source.llmsTxt) continue;
|
|
2362
2991
|
const llms = await sourceText(root, source.llmsTxt, fetchText);
|
|
2992
|
+
if (!llms) {
|
|
2993
|
+
warnings.push(`federation source skipped (unavailable): ${source.id} \u2192 ${source.llmsTxt}`);
|
|
2994
|
+
continue;
|
|
2995
|
+
}
|
|
2363
2996
|
chunks.push(...chunksFromMarkdown(source.id, llms, source.llmsTxt));
|
|
2364
2997
|
const links = parseLlmsTxtLinks(llms);
|
|
2365
2998
|
for (const link of links) {
|
|
2366
2999
|
const url = !/^https?:\/\//.test(link.url) && source.rawBaseUrl ? `${source.rawBaseUrl.replace(/\/$/, "")}/${link.url.replace(/^\//, "")}` : link.url;
|
|
2367
3000
|
if (!/\.(md|txt)(?:$|\?)/.test(url)) continue;
|
|
2368
3001
|
if (!sameOrigin(source.llmsTxt, url)) continue;
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
chunks.push(...chunksFromMarkdown(source.id, raw, url));
|
|
2372
|
-
} catch {
|
|
2373
|
-
}
|
|
3002
|
+
const raw = await sourceText(root, url, fetchText);
|
|
3003
|
+
if (raw) chunks.push(...chunksFromMarkdown(source.id, raw, url));
|
|
2374
3004
|
}
|
|
2375
3005
|
}
|
|
3006
|
+
if (warnings.length && process.stderr.isTTY) {
|
|
3007
|
+
for (const w of warnings) process.stderr.write(`${w}
|
|
3008
|
+
`);
|
|
3009
|
+
}
|
|
2376
3010
|
return chunks;
|
|
2377
3011
|
};
|
|
2378
3012
|
var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
|
|
@@ -2394,11 +3028,11 @@ var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
|
|
|
2394
3028
|
};
|
|
2395
3029
|
|
|
2396
3030
|
// src/intelligence/rag.ts
|
|
2397
|
-
import { readFileSync as
|
|
2398
|
-
import { join as
|
|
3031
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
3032
|
+
import { join as join17 } from "path";
|
|
2399
3033
|
|
|
2400
3034
|
// src/intelligence/adapter.ts
|
|
2401
|
-
import { join as
|
|
3035
|
+
import { join as join16 } from "path";
|
|
2402
3036
|
|
|
2403
3037
|
// src/intelligence/peers.ts
|
|
2404
3038
|
var PeerMissingError = class extends Error {
|
|
@@ -2513,7 +3147,7 @@ var resolveIntelligenceRuntime = async (config) => {
|
|
|
2513
3147
|
}
|
|
2514
3148
|
return { adapter, embed, provider, ...model ? { model } : {} };
|
|
2515
3149
|
};
|
|
2516
|
-
var defaultVectorStorePath = (root) =>
|
|
3150
|
+
var defaultVectorStorePath = (root) => join16(root, ".doc-bridge", "vectors");
|
|
2517
3151
|
|
|
2518
3152
|
// src/intelligence/rag.ts
|
|
2519
3153
|
var loadDocuments = (root, index, sources) => {
|
|
@@ -2521,10 +3155,10 @@ var loadDocuments = (root, index, sources) => {
|
|
|
2521
3155
|
const docs = [];
|
|
2522
3156
|
if (includeAgent) {
|
|
2523
3157
|
for (const entry of index.knowledge) {
|
|
2524
|
-
const abs =
|
|
3158
|
+
const abs = join17(root, entry.path);
|
|
2525
3159
|
let content = "";
|
|
2526
3160
|
try {
|
|
2527
|
-
content =
|
|
3161
|
+
content = readFileSync15(abs, "utf8");
|
|
2528
3162
|
} catch {
|
|
2529
3163
|
content = [entry.title, entry.description].filter(Boolean).join("\n\n");
|
|
2530
3164
|
}
|
|
@@ -2542,7 +3176,7 @@ var createDocBridgeRag = async (root, config, index) => {
|
|
|
2542
3176
|
const { embed } = await resolveIntelligenceRuntime(config);
|
|
2543
3177
|
const ragMod = await importPeer("@agentskit/rag");
|
|
2544
3178
|
const memoryMod = await importPeer("@agentskit/memory");
|
|
2545
|
-
const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ?
|
|
3179
|
+
const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join17(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
|
|
2546
3180
|
const store = memoryMod.fileVectorMemory({ path: storePath });
|
|
2547
3181
|
const rag = ragMod.createRAG({
|
|
2548
3182
|
embed,
|
|
@@ -2675,17 +3309,140 @@ var startInkChat = async (root, config, index) => {
|
|
|
2675
3309
|
throw wrapIntelligenceError(error);
|
|
2676
3310
|
}
|
|
2677
3311
|
};
|
|
3312
|
+
|
|
3313
|
+
// src/playbook/doc-bridge-pattern.ts
|
|
3314
|
+
var DOC_BRIDGE_PATTERN_ID = "doc-bridge-pattern";
|
|
3315
|
+
var DOC_BRIDGE_PATTERN_META = {
|
|
3316
|
+
id: DOC_BRIDGE_PATTERN_ID,
|
|
3317
|
+
title: "Doc Bridge Pattern",
|
|
3318
|
+
slug: "pillars/ai-collaboration/doc-bridge-pattern",
|
|
3319
|
+
license: "CC-BY-4.0",
|
|
3320
|
+
visibility: "public",
|
|
3321
|
+
playbookUrl: "https://playbook.agentskit.io/patterns/doc-bridge-pattern",
|
|
3322
|
+
npmPackage: "@agentskit/doc-bridge",
|
|
3323
|
+
cli: "ak-docs"
|
|
3324
|
+
};
|
|
3325
|
+
var docBridgePatternMarkdown = () => `---
|
|
3326
|
+
type: pattern
|
|
3327
|
+
id: ${DOC_BRIDGE_PATTERN_ID}
|
|
3328
|
+
purpose: Route coding agents to the correct package, checks, and human docs in any monorepo.
|
|
3329
|
+
owner: AgentsKit
|
|
3330
|
+
license: CC-BY-4.0
|
|
3331
|
+
visibility: public
|
|
3332
|
+
tags: [agents, documentation, monorepo, handoff, mcp]
|
|
3333
|
+
---
|
|
3334
|
+
|
|
3335
|
+
# Doc Bridge Pattern
|
|
3336
|
+
|
|
3337
|
+
**AgentHandoff for your monorepo** \u2014 deterministic routing so agents edit the right roots, run the right checks, and stay linked to human documentation.
|
|
3338
|
+
|
|
3339
|
+
## Problem
|
|
3340
|
+
|
|
3341
|
+
Coding agents guess package ownership. They edit sibling modules, run repo-wide tests, and ship changes without a bridge to human-facing guides. Wikis and RAG explain concepts but weakly answer *where to act*.
|
|
3342
|
+
|
|
3343
|
+
## Solution (three artifacts)
|
|
3344
|
+
|
|
3345
|
+
| Artifact | Role |
|
|
3346
|
+
|----------|------|
|
|
3347
|
+
| **AgentHandoff** | JSON handoff: \`startHere\`, \`editRoots\`, \`checks\`, \`humanDoc\`, \`bridge\` |
|
|
3348
|
+
| **DocBridgeIndex** | Deterministic index + \`contentHash\` for CI freshness gates |
|
|
3349
|
+
| **Self-describe** | \`llms.txt\`, \`capabilities.json\` for discovery |
|
|
3350
|
+
|
|
3351
|
+
## Four loops
|
|
3352
|
+
|
|
3353
|
+
| Loop | Command | Outcome |
|
|
3354
|
+
|------|---------|---------|
|
|
3355
|
+
| **Act** | \`ak-docs query package <id> --agent\` | Agent edits only \`editRoots\` |
|
|
3356
|
+
| **Bridge** | \`ak-docs bootstrap agent-docs\` | Link agent corpus \u2194 human site |
|
|
3357
|
+
| **Learn** | \`ak-docs memory promote --pr\` | HITL draft PR from agent memory |
|
|
3358
|
+
| **Explain** | \`ak-docs ask "<question>"\` | Local consult + handoff preview |
|
|
3359
|
+
|
|
3360
|
+
## 60-second proof
|
|
3361
|
+
|
|
3362
|
+
\`\`\`bash
|
|
3363
|
+
npm i -D @agentskit/doc-bridge
|
|
3364
|
+
npx ak-docs demo --text
|
|
3365
|
+
ak-docs mcp install --cursor
|
|
3366
|
+
\`\`\`
|
|
3367
|
+
|
|
3368
|
+
## AgentHandoff example
|
|
3369
|
+
|
|
3370
|
+
\`\`\`json
|
|
3371
|
+
{
|
|
3372
|
+
"type": "agent-handoff",
|
|
3373
|
+
"startHere": "docs/for-agents/packages/auth.md",
|
|
3374
|
+
"editRoots": ["packages/auth"],
|
|
3375
|
+
"checks": ["pnpm --filter @demo/auth test"],
|
|
3376
|
+
"humanDoc": "/docs/guides/auth",
|
|
3377
|
+
"bridge": { "humanDoc": "linked" }
|
|
3378
|
+
}
|
|
3379
|
+
\`\`\`
|
|
3380
|
+
|
|
3381
|
+
When \`humanDoc\` is missing, handoffs surface \`bridge.action: "ak-docs bootstrap agent-docs"\` \u2014 a feature, not a silent gap.
|
|
3382
|
+
|
|
3383
|
+
## MCP contract
|
|
3384
|
+
|
|
3385
|
+
Agents call \`handoff.resolve\` before editing \`packages/*\`:
|
|
3386
|
+
|
|
3387
|
+
1. Read \`startHere\`
|
|
3388
|
+
2. Stay inside \`editRoots\`
|
|
3389
|
+
3. Run every \`checks\` command
|
|
3390
|
+
4. Link \`humanDoc\` or escalate missing bridge
|
|
3391
|
+
|
|
3392
|
+
## CI gate
|
|
3393
|
+
|
|
3394
|
+
\`\`\`yaml
|
|
3395
|
+
- uses: AgentsKit-io/doc-bridge@v1.0.0
|
|
3396
|
+
\`\`\`
|
|
3397
|
+
|
|
3398
|
+
Or: \`ak-docs index && ak-docs gate run\` \u2014 stale index fails the PR.
|
|
3399
|
+
|
|
3400
|
+
## Coverage metric
|
|
3401
|
+
|
|
3402
|
+
\`\`\`bash
|
|
3403
|
+
ak-docs doctor --text
|
|
3404
|
+
ak-docs doctor --badge
|
|
3405
|
+
\`\`\`
|
|
3406
|
+
|
|
3407
|
+
Teams track handoff % and human-bridge % daily.
|
|
3408
|
+
|
|
3409
|
+
## When to use
|
|
3410
|
+
|
|
3411
|
+
- pnpm/npm monorepos with real package ownership
|
|
3412
|
+
- Fumadocus / Docusaurus human sites + dense agent corpus
|
|
3413
|
+
- Cursor / Claude / Codex agents that should not guess edit roots
|
|
3414
|
+
|
|
3415
|
+
## When not to use
|
|
3416
|
+
|
|
3417
|
+
- Single-file repos with no ownership boundaries (AGENTS.md may suffice)
|
|
3418
|
+
- Hosted doc chat as primary product (doc-bridge is routing + bridge, not SaaS chat)
|
|
3419
|
+
|
|
3420
|
+
## References
|
|
3421
|
+
|
|
3422
|
+
- npm: https://www.npmjs.com/package/@agentskit/doc-bridge
|
|
3423
|
+
- repo: https://github.com/AgentsKit-io/doc-bridge
|
|
3424
|
+
- skill: https://github.com/AgentsKit-io/doc-bridge/blob/master/docs/skills/doc-bridge.md
|
|
3425
|
+
- landing: https://agentskit-io.github.io/doc-bridge/
|
|
3426
|
+
`;
|
|
3427
|
+
var docBridgePatternPayload = () => ({
|
|
3428
|
+
...DOC_BRIDGE_PATTERN_META,
|
|
3429
|
+
format: "okf-pattern-v1",
|
|
3430
|
+
body: docBridgePatternMarkdown()
|
|
3431
|
+
});
|
|
2678
3432
|
export {
|
|
2679
3433
|
AgentHandoffLegacySchema,
|
|
2680
3434
|
AgentHandoffV1JsonSchema,
|
|
2681
3435
|
AgentHandoffV1Schema,
|
|
2682
3436
|
AgentSearchV1Schema,
|
|
2683
3437
|
ConfigNotFoundError,
|
|
3438
|
+
DOC_BRIDGE_PATTERN_ID,
|
|
3439
|
+
DOC_BRIDGE_PATTERN_META,
|
|
2684
3440
|
DocBridgeConfigV1Schema,
|
|
2685
3441
|
DocBridgeIndexV1JsonSchema,
|
|
2686
3442
|
DocBridgeIndexV1Schema,
|
|
2687
3443
|
DocBridgeJsonSchemas,
|
|
2688
3444
|
HANDOFF_SCHEMA_VERSION,
|
|
3445
|
+
HandoffBridgeSchema,
|
|
2689
3446
|
HandoffTargetTypeSchema,
|
|
2690
3447
|
INDEX_SCHEMA_VERSION,
|
|
2691
3448
|
IndexNotFoundError,
|
|
@@ -2704,17 +3461,26 @@ export {
|
|
|
2704
3461
|
collectPackages,
|
|
2705
3462
|
createDocBridgeRag,
|
|
2706
3463
|
createDocBridgeRetriever,
|
|
3464
|
+
defaultPromotionDraftPath,
|
|
2707
3465
|
defineConfig,
|
|
3466
|
+
docBridgePatternMarkdown,
|
|
3467
|
+
docBridgePatternPayload,
|
|
3468
|
+
doctorBadgeMetrics,
|
|
2708
3469
|
draftMemoryPromotion,
|
|
3470
|
+
formatDoctorBadgeJson,
|
|
3471
|
+
formatDoctorBadgeMarkdown,
|
|
3472
|
+
formatDoctorText,
|
|
2709
3473
|
handleMcpRequest,
|
|
2710
3474
|
indexFilePath,
|
|
2711
3475
|
ingestAgentMemory,
|
|
2712
3476
|
ingestCursorRules,
|
|
2713
3477
|
ingestMemoryCandidates,
|
|
3478
|
+
installMcpConfig,
|
|
2714
3479
|
layer1InstallHint,
|
|
2715
3480
|
loadConfig,
|
|
2716
3481
|
loadDocBridgeIndex,
|
|
2717
3482
|
loadFederatedChunks,
|
|
3483
|
+
mcpSnippet,
|
|
2718
3484
|
normalizeAgentHandoff,
|
|
2719
3485
|
parseAgentHandoff,
|
|
2720
3486
|
parseAgentSearch,
|
|
@@ -2723,12 +3489,14 @@ export {
|
|
|
2723
3489
|
parseLlmsTxtLinks,
|
|
2724
3490
|
parseMemoryCandidate,
|
|
2725
3491
|
projectRootFromConfigPath,
|
|
3492
|
+
promoteMemoryToGithubPr,
|
|
2726
3493
|
resolveGateIds,
|
|
2727
3494
|
resolveProjectRoot,
|
|
2728
3495
|
resolveRoot,
|
|
2729
3496
|
retrieveDocBridgeChunks,
|
|
2730
3497
|
retrieveHybridChunks,
|
|
2731
3498
|
runChatOnce,
|
|
3499
|
+
runDoctor,
|
|
2732
3500
|
runGate,
|
|
2733
3501
|
runGates,
|
|
2734
3502
|
runQuery,
|
|
@@ -2739,6 +3507,8 @@ export {
|
|
|
2739
3507
|
searchIndex,
|
|
2740
3508
|
sha256NormalizedV1,
|
|
2741
3509
|
startInkChat,
|
|
2742
|
-
startMcpStdioServer
|
|
3510
|
+
startMcpStdioServer,
|
|
3511
|
+
watchDocBridgeIndex,
|
|
3512
|
+
writePromotionDraft
|
|
2743
3513
|
};
|
|
2744
3514
|
//# sourceMappingURL=index.js.map
|