@agentskit/doc-bridge 1.2.4 → 1.3.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 +21 -0
- package/CONTRIBUTING.md +6 -0
- package/PRIVACY.md +38 -0
- package/README.md +30 -3
- package/SECURITY.md +7 -2
- package/action.yml +1 -1
- package/dist/cli/program.js +400 -156
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +3 -1
- package/dist/config/index.js.map +1 -1
- package/dist/{index-DGI9TBLE.d.ts → index-DAeq_OIi.d.ts} +22 -22
- package/dist/index.d.ts +47 -12
- package/dist/index.js +376 -131
- package/dist/index.js.map +1 -1
- package/docs/POSITIONING.md +1 -1
- package/docs/examples.md +4 -0
- package/docs/getting-started.md +1 -1
- package/docs/landing/index.html +1 -1
- package/docs/mcp.md +16 -0
- package/docs/qa/vitepress-starlight-adapters.md +19 -0
- package/docs/spec/config-v1.md +33 -2
- package/examples/nextra-only.config.ts +17 -0
- package/examples/nx-monorepo.config.ts +11 -0
- package/examples/starlight-only.config.ts +17 -0
- package/examples/vitepress-only.config.ts +19 -0
- package/mcpb/.mcpbignore +8 -0
- package/mcpb/icon.png +0 -0
- package/mcpb/manifest.json +96 -0
- package/package.json +14 -11
- package/src/config/schema.ts +3 -1
- package/src/index-builder/build-handoffs.ts +2 -0
- package/src/index-builder/build-index.ts +7 -1
- package/src/index-builder/human-adapters/index.ts +6 -0
- package/src/index-builder/human-adapters/nextra.ts +43 -0
- package/src/index-builder/human-adapters/starlight.ts +40 -0
- package/src/index-builder/human-adapters/vitepress.ts +43 -0
- package/src/index-builder/plugins/nx.ts +161 -0
- package/src/index-builder/plugins/pnpm-monorepo.ts +1 -0
- package/src/index-builder/watch-index.ts +11 -2
- package/src/index.ts +1 -0
- package/src/mcp/server.ts +58 -26
- package/src/version.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -64,6 +64,8 @@ var HumanCorpusPluginIdSchema = z.enum([
|
|
|
64
64
|
"docusaurus",
|
|
65
65
|
"mkdocs",
|
|
66
66
|
"vitepress",
|
|
67
|
+
"starlight",
|
|
68
|
+
"nextra",
|
|
67
69
|
"custom"
|
|
68
70
|
]);
|
|
69
71
|
var AgentCorpusConfigSchema = z.object({
|
|
@@ -103,7 +105,7 @@ var OwnershipEntrySchema = z.object({
|
|
|
103
105
|
humanDoc: z.string().min(1).max(512).optional()
|
|
104
106
|
}).strict();
|
|
105
107
|
var RoutingConfigSchema = z.object({
|
|
106
|
-
plugin: z.enum(["pnpm-monorepo", "npm-workspaces", "yarn-workspaces", "pattern-files", "custom"]).optional(),
|
|
108
|
+
plugin: z.enum(["pnpm-monorepo", "npm-workspaces", "yarn-workspaces", "nx", "pattern-files", "custom"]).optional(),
|
|
107
109
|
options: z.object({
|
|
108
110
|
packages: z.array(z.string().min(1).max(256)).max(128).optional(),
|
|
109
111
|
/** Infer ownership from corpus paths/frontmatter (default true). */
|
|
@@ -1028,8 +1030,8 @@ ${zodIssues(result.error).map((i) => ` - ${i.path}: ${i.message}`).join("\n")}`
|
|
|
1028
1030
|
};
|
|
1029
1031
|
|
|
1030
1032
|
// src/index-builder/build-index.ts
|
|
1031
|
-
import { mkdirSync, readFileSync as
|
|
1032
|
-
import { dirname as
|
|
1033
|
+
import { mkdirSync, readFileSync as readFileSync7, writeFileSync } from "fs";
|
|
1034
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
1033
1035
|
|
|
1034
1036
|
// src/lib/paths.ts
|
|
1035
1037
|
import { existsSync as existsSync2, realpathSync } from "fs";
|
|
@@ -1298,17 +1300,17 @@ var scanAgentCorpus = (root, config) => {
|
|
|
1298
1300
|
};
|
|
1299
1301
|
});
|
|
1300
1302
|
};
|
|
1301
|
-
var guessAgentDocForPackage = (corpus,
|
|
1303
|
+
var guessAgentDocForPackage = (corpus, packageId2) => {
|
|
1302
1304
|
const candidates = [
|
|
1303
|
-
(doc) => frontmatterString(doc.frontmatter, "package") ===
|
|
1304
|
-
(doc) => doc.id ===
|
|
1305
|
-
(doc) => doc.relPath.endsWith(`/packages/${
|
|
1306
|
-
(doc) => doc.relPath.endsWith(`/packages/${
|
|
1307
|
-
(doc) => doc.relPath.endsWith(`/packages/${
|
|
1308
|
-
(doc) => doc.relPath.endsWith(`/packages/${
|
|
1309
|
-
(doc) => doc.relPath.endsWith(`/${
|
|
1310
|
-
(doc) => doc.relPath.endsWith(`/${
|
|
1311
|
-
(doc) => doc.relPath.includes(`/packages/${
|
|
1305
|
+
(doc) => frontmatterString(doc.frontmatter, "package") === packageId2,
|
|
1306
|
+
(doc) => doc.id === packageId2,
|
|
1307
|
+
(doc) => doc.relPath.endsWith(`/packages/${packageId2}.md`),
|
|
1308
|
+
(doc) => doc.relPath.endsWith(`/packages/${packageId2}.mdx`),
|
|
1309
|
+
(doc) => doc.relPath.endsWith(`/packages/${packageId2}/index.md`),
|
|
1310
|
+
(doc) => doc.relPath.endsWith(`/packages/${packageId2}/index.mdx`),
|
|
1311
|
+
(doc) => doc.relPath.endsWith(`/${packageId2}.md`),
|
|
1312
|
+
(doc) => doc.relPath.endsWith(`/${packageId2}.mdx`),
|
|
1313
|
+
(doc) => doc.relPath.includes(`/packages/${packageId2}/`)
|
|
1312
1314
|
];
|
|
1313
1315
|
for (const match of candidates) {
|
|
1314
1316
|
const hit = corpus.find(match);
|
|
@@ -1400,7 +1402,8 @@ var collectPackages = (config, discovered, corpus) => {
|
|
|
1400
1402
|
byId.set(pkg.id, {
|
|
1401
1403
|
id: pkg.id,
|
|
1402
1404
|
path: existing.path || pkg.path,
|
|
1403
|
-
...pkg.name ? { name: pkg.name } : existing.name ? { name: existing.name } : {}
|
|
1405
|
+
...pkg.name ? { name: pkg.name } : existing.name ? { name: existing.name } : {},
|
|
1406
|
+
...pkg.checks ? { checks: pkg.checks } : existing.checks ? { checks: existing.checks } : {}
|
|
1404
1407
|
});
|
|
1405
1408
|
}
|
|
1406
1409
|
const seeds = [
|
|
@@ -1419,24 +1422,24 @@ var collectPackages = (config, discovered, corpus) => {
|
|
|
1419
1422
|
}
|
|
1420
1423
|
return [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
1421
1424
|
};
|
|
1422
|
-
var resolveHumanDoc = (
|
|
1425
|
+
var resolveHumanDoc = (packageId2, override, fmHuman, humanDocs = {}) => {
|
|
1423
1426
|
if (override) return override;
|
|
1424
1427
|
if (fmHuman) return fmHuman;
|
|
1425
|
-
if (humanDocs[
|
|
1428
|
+
if (humanDocs[packageId2]) return humanDocs[packageId2];
|
|
1426
1429
|
const aliases = [
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
`packages/${
|
|
1432
|
-
`reference/packages/${
|
|
1433
|
-
`packages/${
|
|
1430
|
+
packageId2,
|
|
1431
|
+
packageId2.replace(/^@[^/]+\//, ""),
|
|
1432
|
+
packageId2.replace(/^os-/, ""),
|
|
1433
|
+
packageId2.replace(/-pattern$/, ""),
|
|
1434
|
+
`packages/${packageId2}`,
|
|
1435
|
+
`reference/packages/${packageId2}`,
|
|
1436
|
+
`packages/${packageId2}/index`
|
|
1434
1437
|
];
|
|
1435
1438
|
for (const alias of aliases) {
|
|
1436
1439
|
if (humanDocs[alias]) return humanDocs[alias];
|
|
1437
1440
|
}
|
|
1438
1441
|
for (const [key, url] of Object.entries(humanDocs)) {
|
|
1439
|
-
if (key ===
|
|
1442
|
+
if (key === packageId2 || key.endsWith(`/${packageId2}`) || key.endsWith(`/${packageId2}/index`) || key.endsWith(`-${packageId2}`)) {
|
|
1440
1443
|
return url;
|
|
1441
1444
|
}
|
|
1442
1445
|
}
|
|
@@ -1460,7 +1463,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1460
1463
|
const purpose = override?.purpose ?? fm?.purpose;
|
|
1461
1464
|
const path = override?.path ?? fm?.path ?? pkg.path;
|
|
1462
1465
|
const checks = [
|
|
1463
|
-
...override?.checks ?? fm?.checks ?? defaultChecksForTarget(root, {
|
|
1466
|
+
...override?.checks ?? fm?.checks ?? pkg.checks ?? defaultChecksForTarget(root, {
|
|
1464
1467
|
packageId: pkg.id,
|
|
1465
1468
|
packagePath: path,
|
|
1466
1469
|
...pkg.name ? { packageName: pkg.name } : {},
|
|
@@ -1534,7 +1537,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1534
1537
|
};
|
|
1535
1538
|
|
|
1536
1539
|
// src/version.ts
|
|
1537
|
-
var PACKAGE_VERSION = "1.
|
|
1540
|
+
var PACKAGE_VERSION = "1.3.0";
|
|
1538
1541
|
|
|
1539
1542
|
// src/index-builder/capabilities.ts
|
|
1540
1543
|
var renderCapabilitiesJson = (config, index, paths) => {
|
|
@@ -1791,6 +1794,38 @@ var fumadocsAdapter = {
|
|
|
1791
1794
|
}
|
|
1792
1795
|
};
|
|
1793
1796
|
|
|
1797
|
+
// src/index-builder/human-adapters/nextra.ts
|
|
1798
|
+
var nextraRecordId = (relPath, raw) => {
|
|
1799
|
+
const frontmatter = parseFrontmatter2(raw);
|
|
1800
|
+
return frontmatter.package ?? frontmatter.module ?? frontmatter.id ?? (routeSlug(relPath) || "index");
|
|
1801
|
+
};
|
|
1802
|
+
var isIndexPage = (path) => /(^|[/\\])index\.mdx?$/i.test(path);
|
|
1803
|
+
var resolveRouteCollisions = (records) => {
|
|
1804
|
+
const byUrl = /* @__PURE__ */ new Map();
|
|
1805
|
+
for (const record of records) {
|
|
1806
|
+
const normalized = { ...record, url: record.url || "/" };
|
|
1807
|
+
const indexPage = isIndexPage(record.path);
|
|
1808
|
+
const existing = byUrl.get(normalized.url);
|
|
1809
|
+
if (!existing || indexPage || !existing.indexPage) {
|
|
1810
|
+
byUrl.set(normalized.url, { record: normalized, indexPage });
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
return [...byUrl.values()].map(({ record }) => record);
|
|
1814
|
+
};
|
|
1815
|
+
var nextraAdapter = {
|
|
1816
|
+
plugin: "nextra",
|
|
1817
|
+
scan: ({ root, config }) => {
|
|
1818
|
+
const contentDir = optionString(config.options, ["contentDir", "docsDir", "root"]);
|
|
1819
|
+
if (!contentDir) return [];
|
|
1820
|
+
return resolveRouteCollisions(
|
|
1821
|
+
scanMarkdownDocs(root, contentDir, {
|
|
1822
|
+
idForDoc: nextraRecordId,
|
|
1823
|
+
urlPrefix: optionString(config.options, ["urlPrefix", "contentDirBasePath"]) ?? "/"
|
|
1824
|
+
})
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
};
|
|
1828
|
+
|
|
1794
1829
|
// src/index-builder/human-adapters/plain-markdown.ts
|
|
1795
1830
|
var plainMarkdownAdapter = {
|
|
1796
1831
|
plugin: "plain-markdown",
|
|
@@ -1800,11 +1835,73 @@ var plainMarkdownAdapter = {
|
|
|
1800
1835
|
}
|
|
1801
1836
|
};
|
|
1802
1837
|
|
|
1838
|
+
// src/index-builder/human-adapters/starlight.ts
|
|
1839
|
+
import { slug as githubSlug } from "github-slugger";
|
|
1840
|
+
var isStarlightPage = (relPath, raw) => {
|
|
1841
|
+
if (relPath.split("/").some((part) => part.startsWith(".") || part.startsWith("_"))) return false;
|
|
1842
|
+
return parseFrontmatter2(raw).draft !== "true";
|
|
1843
|
+
};
|
|
1844
|
+
var starlightFileSlug = (relPath) => routeSlug(relPath).split("/").map((part) => githubSlug(part)).filter(Boolean).join("/");
|
|
1845
|
+
var starlightSlug = (relPath, raw) => {
|
|
1846
|
+
const slug2 = parseFrontmatter2(raw).slug;
|
|
1847
|
+
return slug2 ? slug2.replace(/^\/+|\/+$/g, "") : starlightFileSlug(relPath);
|
|
1848
|
+
};
|
|
1849
|
+
var starlightAdapter = {
|
|
1850
|
+
plugin: "starlight",
|
|
1851
|
+
scan: ({ root, config }) => {
|
|
1852
|
+
const contentDir = optionString(config.options, ["contentDir", "docsDir", "root"]);
|
|
1853
|
+
if (!contentDir) return [];
|
|
1854
|
+
return scanMarkdownDocs(root, contentDir, {
|
|
1855
|
+
includeRelPath: isStarlightPage,
|
|
1856
|
+
slugForDoc: starlightSlug,
|
|
1857
|
+
urlPrefix: config.options?.urlPrefix
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
};
|
|
1861
|
+
|
|
1862
|
+
// src/index-builder/human-adapters/vitepress.ts
|
|
1863
|
+
import { minimatch } from "minimatch";
|
|
1864
|
+
var isVitePressPage = (relPath) => !relPath.split("/").some((part) => part === ".vitepress" || part.startsWith("."));
|
|
1865
|
+
var srcExcludePatterns = (value) => {
|
|
1866
|
+
if (value === void 0) return [];
|
|
1867
|
+
if (!Array.isArray(value) || value.length > 64) {
|
|
1868
|
+
throw new Error("VitePress srcExclude must be an array of at most 64 glob patterns.");
|
|
1869
|
+
}
|
|
1870
|
+
return value.map((pattern) => {
|
|
1871
|
+
if (typeof pattern !== "string" || !pattern || pattern.length > 256 || pattern.includes("\0")) {
|
|
1872
|
+
throw new Error("Each VitePress srcExclude pattern must be a non-empty string up to 256 characters.");
|
|
1873
|
+
}
|
|
1874
|
+
return pattern;
|
|
1875
|
+
});
|
|
1876
|
+
};
|
|
1877
|
+
var isExcluded = (relPath, patterns) => patterns.some((pattern) => minimatch(relPath, pattern, { dot: true }));
|
|
1878
|
+
var vitepressSlug = (relPath, cleanUrls) => {
|
|
1879
|
+
const slug2 = routeSlug(relPath);
|
|
1880
|
+
if (cleanUrls || /(?:^|\/)index\.mdx?$/.test(relPath)) return slug2;
|
|
1881
|
+
return `${slug2}.html`;
|
|
1882
|
+
};
|
|
1883
|
+
var vitepressAdapter = {
|
|
1884
|
+
plugin: "vitepress",
|
|
1885
|
+
scan: ({ root, config }) => {
|
|
1886
|
+
const docsDir = optionString(config.options, ["docsDir", "root", "srcDir"]);
|
|
1887
|
+
if (!docsDir) return [];
|
|
1888
|
+
const srcExclude = srcExcludePatterns(config.options?.srcExclude);
|
|
1889
|
+
return scanMarkdownDocs(root, docsDir, {
|
|
1890
|
+
includeRelPath: (relPath) => isVitePressPage(relPath) && !isExcluded(relPath, srcExclude),
|
|
1891
|
+
slugForDoc: (relPath) => vitepressSlug(relPath, config.options?.cleanUrls === true),
|
|
1892
|
+
urlPrefix: config.options?.urlPrefix
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
};
|
|
1896
|
+
|
|
1803
1897
|
// src/index-builder/human-adapters/index.ts
|
|
1804
1898
|
var ADAPTERS = [
|
|
1805
1899
|
plainMarkdownAdapter,
|
|
1806
1900
|
fumadocsAdapter,
|
|
1807
|
-
docusaurusAdapter
|
|
1901
|
+
docusaurusAdapter,
|
|
1902
|
+
vitepressAdapter,
|
|
1903
|
+
starlightAdapter,
|
|
1904
|
+
nextraAdapter
|
|
1808
1905
|
];
|
|
1809
1906
|
var humanConfigs = (config) => {
|
|
1810
1907
|
const human = config.corpus.human;
|
|
@@ -1839,12 +1936,121 @@ var scanHumanDocRecords = (root, config) => {
|
|
|
1839
1936
|
};
|
|
1840
1937
|
var scanHumanDocs = (root, config) => Object.fromEntries(scanHumanDocRecords(root, config).map((doc) => [doc.id, doc.url]));
|
|
1841
1938
|
|
|
1939
|
+
// src/index-builder/plugins/nx.ts
|
|
1940
|
+
import { existsSync as existsSync6, lstatSync as lstatSync2, readFileSync as readFileSync5, realpathSync as realpathSync5 } from "fs";
|
|
1941
|
+
import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve5, sep as sep4 } from "path";
|
|
1942
|
+
var NX_SCAN_SKIP = /* @__PURE__ */ new Set([
|
|
1943
|
+
"node_modules",
|
|
1944
|
+
".git",
|
|
1945
|
+
".nx",
|
|
1946
|
+
"dist",
|
|
1947
|
+
"coverage",
|
|
1948
|
+
".doc-bridge"
|
|
1949
|
+
]);
|
|
1950
|
+
var NX_PROJECT_NAME = /^(?:@[A-Za-z0-9._-]+\/)?[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
1951
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1952
|
+
var readJsonRecord = (path) => {
|
|
1953
|
+
try {
|
|
1954
|
+
const parsed = JSON.parse(readFileSync5(path, "utf8"));
|
|
1955
|
+
return isRecord(parsed) ? parsed : void 0;
|
|
1956
|
+
} catch {
|
|
1957
|
+
return void 0;
|
|
1958
|
+
}
|
|
1959
|
+
};
|
|
1960
|
+
var safeProjectPath = (root, manifestDir, declaredRoot) => {
|
|
1961
|
+
const candidate = typeof declaredRoot === "string" ? resolve5(root, declaredRoot) : manifestDir;
|
|
1962
|
+
const rel = toPosix(relative3(root, candidate)) || ".";
|
|
1963
|
+
if (isAbsolute3(rel) || rel === ".." || rel.startsWith("../")) return void 0;
|
|
1964
|
+
try {
|
|
1965
|
+
if (!lstatSync2(candidate).isDirectory()) return void 0;
|
|
1966
|
+
const canonicalRoot = realpathSync5.native(root);
|
|
1967
|
+
const canonicalCandidate = realpathSync5.native(candidate);
|
|
1968
|
+
const canonicalRel = relative3(canonicalRoot, canonicalCandidate);
|
|
1969
|
+
if (isAbsolute3(canonicalRel) || canonicalRel === ".." || canonicalRel.startsWith(`..${sep4}`)) {
|
|
1970
|
+
return void 0;
|
|
1971
|
+
}
|
|
1972
|
+
} catch {
|
|
1973
|
+
return void 0;
|
|
1974
|
+
}
|
|
1975
|
+
return rel;
|
|
1976
|
+
};
|
|
1977
|
+
var packageId = (name) => name.startsWith("@") ? name.split("/").at(-1) ?? name : name;
|
|
1978
|
+
var commandPrefix = (root) => {
|
|
1979
|
+
const manager = detectPackageManager(root);
|
|
1980
|
+
if (manager === "pnpm") return "pnpm exec nx run";
|
|
1981
|
+
if (manager === "yarn") return "yarn nx run";
|
|
1982
|
+
if (manager === "bun") return "bunx nx run";
|
|
1983
|
+
return "npx nx run";
|
|
1984
|
+
};
|
|
1985
|
+
var inferredChecks = (root, config, projectName2, targets) => {
|
|
1986
|
+
const prefix = commandPrefix(root);
|
|
1987
|
+
const strict = (config.gates?.preset ?? "minimal") !== "minimal";
|
|
1988
|
+
const checks = [
|
|
1989
|
+
...targets.has("test") ? [`${prefix} ${projectName2}:test`] : [],
|
|
1990
|
+
...strict && targets.has("lint") ? [`${prefix} ${projectName2}:lint`] : []
|
|
1991
|
+
];
|
|
1992
|
+
return checks.length ? checks : void 0;
|
|
1993
|
+
};
|
|
1994
|
+
var discoverNxProjects = (root, config) => {
|
|
1995
|
+
if (!existsSync6(resolve5(root, "nx.json"))) return [];
|
|
1996
|
+
const manifests = walkFiles(root, {
|
|
1997
|
+
extensions: ["project.json", "package.json"],
|
|
1998
|
+
skipDirs: NX_SCAN_SKIP,
|
|
1999
|
+
maxFiles: 1e4
|
|
2000
|
+
});
|
|
2001
|
+
const projects = /* @__PURE__ */ new Map();
|
|
2002
|
+
for (const manifest of manifests) {
|
|
2003
|
+
const json = readJsonRecord(manifest);
|
|
2004
|
+
if (!json) continue;
|
|
2005
|
+
const manifestDir = dirname3(manifest);
|
|
2006
|
+
const manifestName = basename2(manifest);
|
|
2007
|
+
const isProjectJson = manifestName === "project.json";
|
|
2008
|
+
if (!isProjectJson && manifestName !== "package.json") continue;
|
|
2009
|
+
const nx = json.nx;
|
|
2010
|
+
if (!isProjectJson && !isRecord(nx)) continue;
|
|
2011
|
+
const path = safeProjectPath(root, manifestDir, isProjectJson ? json.root : void 0);
|
|
2012
|
+
if (!path) continue;
|
|
2013
|
+
const projectPackage = isProjectJson ? readJsonRecord(resolve5(root, path, "package.json")) : void 0;
|
|
2014
|
+
const projectName2 = typeof json.name === "string" ? json.name : typeof projectPackage?.name === "string" ? projectPackage.name : basename2(path === "." ? root : path);
|
|
2015
|
+
if (!NX_PROJECT_NAME.test(projectName2)) continue;
|
|
2016
|
+
const targets = /* @__PURE__ */ new Set();
|
|
2017
|
+
const targetRecord = isProjectJson ? json.targets : isRecord(nx) ? nx.targets : void 0;
|
|
2018
|
+
if (isRecord(targetRecord)) {
|
|
2019
|
+
for (const target of Object.keys(targetRecord)) targets.add(target);
|
|
2020
|
+
}
|
|
2021
|
+
if (!isProjectJson && isRecord(json.scripts)) {
|
|
2022
|
+
for (const script of Object.keys(json.scripts)) targets.add(script);
|
|
2023
|
+
}
|
|
2024
|
+
const id = packageId(projectName2);
|
|
2025
|
+
const rank = isProjectJson ? 2 : 1;
|
|
2026
|
+
const existing = projects.get(id);
|
|
2027
|
+
if (existing && existing.path !== path) {
|
|
2028
|
+
throw new Error(
|
|
2029
|
+
`Nx project identity collision for "${id}": "${existing.name ?? id}" at "${existing.path}" and "${projectName2}" at "${path}". Use unique Nx project names.`
|
|
2030
|
+
);
|
|
2031
|
+
}
|
|
2032
|
+
const mergedTargets = [.../* @__PURE__ */ new Set([...existing?.targets ?? [], ...targets])];
|
|
2033
|
+
const projectJsonWins = rank >= (existing?.rank ?? 0);
|
|
2034
|
+
const resolvedName = projectJsonWins ? projectName2 : existing?.name ?? projectName2;
|
|
2035
|
+
const checks = inferredChecks(root, config, resolvedName, new Set(mergedTargets));
|
|
2036
|
+
projects.set(id, {
|
|
2037
|
+
id,
|
|
2038
|
+
path,
|
|
2039
|
+
name: resolvedName,
|
|
2040
|
+
...checks ? { checks } : {},
|
|
2041
|
+
rank: projectJsonWins ? rank : existing?.rank ?? rank,
|
|
2042
|
+
targets: mergedTargets
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
return [...projects.values()].sort((a, b) => a.id.localeCompare(b.id)).map(({ rank: _rank, targets: _targets, ...project }) => project);
|
|
2046
|
+
};
|
|
2047
|
+
|
|
1842
2048
|
// src/index-builder/plugins/pnpm-monorepo.ts
|
|
1843
|
-
import { existsSync as
|
|
1844
|
-
import { basename as
|
|
2049
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
2050
|
+
import { basename as basename3, join as join6 } from "path";
|
|
1845
2051
|
|
|
1846
2052
|
// src/lib/glob-expand.ts
|
|
1847
|
-
import { existsSync as
|
|
2053
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
1848
2054
|
import { join as join5 } from "path";
|
|
1849
2055
|
var expandWorkspaceGlobs = (root, patterns) => {
|
|
1850
2056
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -1852,12 +2058,12 @@ var expandWorkspaceGlobs = (root, patterns) => {
|
|
|
1852
2058
|
const normalized = toPosix(pattern).replace(/\/$/, "");
|
|
1853
2059
|
if (!normalized.includes("*")) {
|
|
1854
2060
|
const abs = join5(root, normalized);
|
|
1855
|
-
if (
|
|
2061
|
+
if (existsSync7(abs)) dirs.add(toPosix(abs));
|
|
1856
2062
|
continue;
|
|
1857
2063
|
}
|
|
1858
2064
|
const star = normalized.indexOf("*");
|
|
1859
2065
|
const base = join5(root, normalized.slice(0, star).replace(/\/$/, ""));
|
|
1860
|
-
if (!
|
|
2066
|
+
if (!existsSync7(base)) continue;
|
|
1861
2067
|
for (const name of readdirSync2(base)) {
|
|
1862
2068
|
const abs = join5(base, name);
|
|
1863
2069
|
try {
|
|
@@ -1892,9 +2098,9 @@ var parsePnpmWorkspace = (yaml) => {
|
|
|
1892
2098
|
};
|
|
1893
2099
|
var readPackageJson = (dir) => {
|
|
1894
2100
|
const file = join6(dir, "package.json");
|
|
1895
|
-
if (!
|
|
2101
|
+
if (!existsSync8(file)) return null;
|
|
1896
2102
|
try {
|
|
1897
|
-
return JSON.parse(
|
|
2103
|
+
return JSON.parse(readFileSync6(file, "utf8"));
|
|
1898
2104
|
} catch {
|
|
1899
2105
|
return null;
|
|
1900
2106
|
}
|
|
@@ -1904,8 +2110,8 @@ var discoverPnpmPackages = (root, config) => {
|
|
|
1904
2110
|
let patterns = explicit;
|
|
1905
2111
|
if (!patterns?.length) {
|
|
1906
2112
|
const workspaceFile = join6(root, "pnpm-workspace.yaml");
|
|
1907
|
-
if (
|
|
1908
|
-
patterns = parsePnpmWorkspace(
|
|
2113
|
+
if (existsSync8(workspaceFile)) {
|
|
2114
|
+
patterns = parsePnpmWorkspace(readFileSync6(workspaceFile, "utf8"));
|
|
1909
2115
|
}
|
|
1910
2116
|
}
|
|
1911
2117
|
if (!patterns?.length) return [];
|
|
@@ -1915,7 +2121,7 @@ var discoverPnpmPackages = (root, config) => {
|
|
|
1915
2121
|
const pkg = readPackageJson(abs);
|
|
1916
2122
|
if (!pkg) continue;
|
|
1917
2123
|
const rel = toPosix(abs.replace(`${toPosix(root)}/`, ""));
|
|
1918
|
-
const folderId =
|
|
2124
|
+
const folderId = basename3(abs);
|
|
1919
2125
|
const id = pkg.name?.startsWith("@") ? pkg.name.split("/").pop() ?? folderId : pkg.name ?? folderId;
|
|
1920
2126
|
out.push({ id, path: rel, ...pkg.name ? { name: pkg.name } : {} });
|
|
1921
2127
|
}
|
|
@@ -1926,7 +2132,7 @@ var discoverPnpmPackages = (root, config) => {
|
|
|
1926
2132
|
var projectName = (root, config) => {
|
|
1927
2133
|
if (config.project?.name) return config.project.name;
|
|
1928
2134
|
try {
|
|
1929
|
-
const pkg = JSON.parse(
|
|
2135
|
+
const pkg = JSON.parse(readFileSync7(join7(root, "package.json"), "utf8"));
|
|
1930
2136
|
if (pkg.name) return pkg.name;
|
|
1931
2137
|
} catch {
|
|
1932
2138
|
}
|
|
@@ -1934,7 +2140,7 @@ var projectName = (root, config) => {
|
|
|
1934
2140
|
};
|
|
1935
2141
|
var existingGeneratedAt = (indexPath, contentHash) => {
|
|
1936
2142
|
try {
|
|
1937
|
-
const index = JSON.parse(
|
|
2143
|
+
const index = JSON.parse(readFileSync7(indexPath, "utf8"));
|
|
1938
2144
|
return index.contentHash === contentHash && typeof index.generatedAt === "string" ? index.generatedAt : void 0;
|
|
1939
2145
|
} catch {
|
|
1940
2146
|
return void 0;
|
|
@@ -1949,7 +2155,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
1949
2155
|
const corpus = scanAgentCorpus(root, config);
|
|
1950
2156
|
const knowledge = corpus.map(({ absPath: _a, relPath: _r, frontmatter: _f, ...entry }) => entry);
|
|
1951
2157
|
const shouldDiscover = config.routing?.plugin === "pnpm-monorepo" || Boolean(config.routing?.options?.packages?.length) || config.routing?.plugin === "npm-workspaces" || config.routing?.plugin === "yarn-workspaces";
|
|
1952
|
-
const discovered = shouldDiscover ? discoverPnpmPackages(root, config) : [];
|
|
2158
|
+
const discovered = config.routing?.plugin === "nx" ? discoverNxProjects(root, config) : shouldDiscover ? discoverPnpmPackages(root, config) : [];
|
|
1953
2159
|
const packages = collectPackages(config, discovered, corpus);
|
|
1954
2160
|
const humanDocs = scanHumanDocs(root, config);
|
|
1955
2161
|
const { lookup, handoffs } = buildLookup(config, packages, corpus, outFile, humanDocs, root);
|
|
@@ -1971,7 +2177,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
1971
2177
|
lookup
|
|
1972
2178
|
};
|
|
1973
2179
|
if (write) {
|
|
1974
|
-
mkdirSync(
|
|
2180
|
+
mkdirSync(dirname4(indexPath), { recursive: true });
|
|
1975
2181
|
writeFileSync(indexPath, `${JSON.stringify(index, null, 2)}
|
|
1976
2182
|
`, "utf8");
|
|
1977
2183
|
}
|
|
@@ -1994,7 +2200,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
1994
2200
|
const capabilitiesOut = config.index?.capabilities?.outFile ?? ".doc-bridge/capabilities.json";
|
|
1995
2201
|
capabilitiesPath = join7(root, capabilitiesOut);
|
|
1996
2202
|
if (write) {
|
|
1997
|
-
mkdirSync(
|
|
2203
|
+
mkdirSync(dirname4(capabilitiesPath), { recursive: true });
|
|
1998
2204
|
writeFileSync(
|
|
1999
2205
|
capabilitiesPath,
|
|
2000
2206
|
renderCapabilitiesJson(config, index, {
|
|
@@ -2040,11 +2246,11 @@ function formatEcosystemLlmsSection(options) {
|
|
|
2040
2246
|
}
|
|
2041
2247
|
|
|
2042
2248
|
// src/gates/run-gates.ts
|
|
2043
|
-
import { readFileSync as
|
|
2249
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
2044
2250
|
|
|
2045
2251
|
// src/conformance/documentation-standard-v1.ts
|
|
2046
|
-
import { existsSync as
|
|
2047
|
-
import { isAbsolute as
|
|
2252
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, realpathSync as realpathSync6, statSync as statSync3 } from "fs";
|
|
2253
|
+
import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve6, sep as sep5 } from "path";
|
|
2048
2254
|
|
|
2049
2255
|
// src/conformance/ecosystem-contract.ts
|
|
2050
2256
|
import { z as z5 } from "zod";
|
|
@@ -2223,15 +2429,15 @@ var DOCUMENTATION_STANDARD_V1_ID = "documentation-standard-v1";
|
|
|
2223
2429
|
var DOCUMENTATION_STANDARD_V1_STATUS = "stable";
|
|
2224
2430
|
var MAX_TEXT_EVIDENCE_BYTES = 4 * 1024 * 1024;
|
|
2225
2431
|
var safePath = (root, path) => {
|
|
2226
|
-
const rootAbs =
|
|
2227
|
-
const unresolved =
|
|
2228
|
-
const unresolvedRel =
|
|
2229
|
-
if (
|
|
2230
|
-
if (!
|
|
2432
|
+
const rootAbs = realpathSync6.native(resolve6(root));
|
|
2433
|
+
const unresolved = resolve6(rootAbs, path);
|
|
2434
|
+
const unresolvedRel = relative4(rootAbs, unresolved);
|
|
2435
|
+
if (isAbsolute4(unresolvedRel) || unresolvedRel === ".." || unresolvedRel.startsWith(`..${sep5}`)) return void 0;
|
|
2436
|
+
if (!existsSync9(unresolved)) return unresolved;
|
|
2231
2437
|
try {
|
|
2232
|
-
const abs =
|
|
2233
|
-
const rel =
|
|
2234
|
-
return !
|
|
2438
|
+
const abs = realpathSync6.native(unresolved);
|
|
2439
|
+
const rel = relative4(rootAbs, abs);
|
|
2440
|
+
return !isAbsolute4(rel) && rel !== ".." && !rel.startsWith(`..${sep5}`) ? abs : void 0;
|
|
2235
2441
|
} catch {
|
|
2236
2442
|
return void 0;
|
|
2237
2443
|
}
|
|
@@ -2245,7 +2451,7 @@ var fileEvidence = (root, path, options) => {
|
|
|
2245
2451
|
evidence: { path, detail: "Path escapes the project root." }
|
|
2246
2452
|
};
|
|
2247
2453
|
}
|
|
2248
|
-
if (!
|
|
2454
|
+
if (!existsSync9(abs)) {
|
|
2249
2455
|
return { exists: false, content: "", evidence: { path, detail: "File does not exist." } };
|
|
2250
2456
|
}
|
|
2251
2457
|
try {
|
|
@@ -2260,7 +2466,7 @@ var fileEvidence = (root, path, options) => {
|
|
|
2260
2466
|
return {
|
|
2261
2467
|
exists: true,
|
|
2262
2468
|
content: "",
|
|
2263
|
-
evidence: { path: toPosix(
|
|
2469
|
+
evidence: { path: toPosix(relative4(resolve6(root), abs)) || ".", detail: "File exists and is non-empty." }
|
|
2264
2470
|
};
|
|
2265
2471
|
}
|
|
2266
2472
|
if (stat.size > MAX_TEXT_EVIDENCE_BYTES) {
|
|
@@ -2270,12 +2476,12 @@ var fileEvidence = (root, path, options) => {
|
|
|
2270
2476
|
evidence: { path, detail: `Text evidence exceeds ${MAX_TEXT_EVIDENCE_BYTES} bytes.` }
|
|
2271
2477
|
};
|
|
2272
2478
|
}
|
|
2273
|
-
const content =
|
|
2479
|
+
const content = readFileSync8(abs, "utf8");
|
|
2274
2480
|
return {
|
|
2275
2481
|
exists: content.trim().length > 0,
|
|
2276
2482
|
content,
|
|
2277
2483
|
evidence: {
|
|
2278
|
-
path: toPosix(
|
|
2484
|
+
path: toPosix(relative4(resolve6(root), abs)) || ".",
|
|
2279
2485
|
detail: content.trim().length > 0 ? "File exists and is non-empty." : "File is empty."
|
|
2280
2486
|
}
|
|
2281
2487
|
};
|
|
@@ -2310,7 +2516,7 @@ var humanDocsRule = (root, config) => {
|
|
|
2310
2516
|
passed: docs.length > 0,
|
|
2311
2517
|
message: docs.length > 0 ? `Found ${docs.length} human document(s).` : "No human documentation was discovered.",
|
|
2312
2518
|
evidence: docs.slice(0, 10).map((doc) => ({
|
|
2313
|
-
path: toPosix(
|
|
2519
|
+
path: toPosix(relative4(resolve6(root), doc.path)),
|
|
2314
2520
|
detail: `Human route: ${doc.url}`
|
|
2315
2521
|
})),
|
|
2316
2522
|
remediation: {
|
|
@@ -2321,10 +2527,10 @@ var humanDocsRule = (root, config) => {
|
|
|
2321
2527
|
};
|
|
2322
2528
|
var llmsRule = (root, config, options) => {
|
|
2323
2529
|
const llmsPath = config.index?.llmsTxt?.outFile ?? "llms.txt";
|
|
2324
|
-
const llmsKey = safePath(root, llmsPath) ??
|
|
2530
|
+
const llmsKey = safePath(root, llmsPath) ?? resolve6(root, llmsPath);
|
|
2325
2531
|
const rawSources = /* @__PURE__ */ new Map();
|
|
2326
2532
|
for (const path of options.rawSources ?? []) {
|
|
2327
|
-
const key = safePath(root, path) ??
|
|
2533
|
+
const key = safePath(root, path) ?? resolve6(root, path);
|
|
2328
2534
|
if (key !== llmsKey && !rawSources.has(key)) rawSources.set(key, path);
|
|
2329
2535
|
}
|
|
2330
2536
|
const paths = [llmsPath, ...rawSources.values()];
|
|
@@ -2576,8 +2782,8 @@ var formatDocumentationStandardText = (report) => [
|
|
|
2576
2782
|
];
|
|
2577
2783
|
|
|
2578
2784
|
// src/query/load-index.ts
|
|
2579
|
-
import { existsSync as
|
|
2580
|
-
import { join as join8, resolve as
|
|
2785
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
2786
|
+
import { join as join8, resolve as resolve7 } from "path";
|
|
2581
2787
|
var IndexNotFoundError = class extends Error {
|
|
2582
2788
|
constructor(path) {
|
|
2583
2789
|
super(`Missing index at ${path}. Run: ak-docs index`);
|
|
@@ -2589,11 +2795,11 @@ var IndexNotFoundError = class extends Error {
|
|
|
2589
2795
|
var indexFilePath = (root, config) => join8(root, config.index?.outFile ?? ".doc-bridge/index.json");
|
|
2590
2796
|
var loadDocBridgeIndex = (root, config) => {
|
|
2591
2797
|
const path = indexFilePath(root, config);
|
|
2592
|
-
if (!
|
|
2593
|
-
const raw = JSON.parse(
|
|
2798
|
+
if (!existsSync10(path)) throw new IndexNotFoundError(path);
|
|
2799
|
+
const raw = JSON.parse(readFileSync9(path, "utf8"));
|
|
2594
2800
|
return parseDocBridgeIndex(raw);
|
|
2595
2801
|
};
|
|
2596
|
-
var resolveRoot = (cwd) =>
|
|
2802
|
+
var resolveRoot = (cwd) => resolve7(cwd ?? process.cwd());
|
|
2597
2803
|
|
|
2598
2804
|
// src/gates/run-gates.ts
|
|
2599
2805
|
var RESERVED_GATE_IDS = /* @__PURE__ */ new Set(["link-rot", "routing-currency", "bootstrap-size"]);
|
|
@@ -2666,7 +2872,7 @@ var runOkfTypeGate = (root, config) => {
|
|
|
2666
2872
|
const required = config.corpus.agent.okf?.requireType ?? config.gates?.preset === "strict";
|
|
2667
2873
|
if (!required) return { id: "okf-type", ok: true, message: "OKF type frontmatter not required" };
|
|
2668
2874
|
const allowed = config.corpus.agent.okf?.allowedTypes;
|
|
2669
|
-
const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({ path: doc.path, type: frontmatterType(
|
|
2875
|
+
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));
|
|
2670
2876
|
if (bad.length) {
|
|
2671
2877
|
return {
|
|
2672
2878
|
id: "okf-type",
|
|
@@ -2693,10 +2899,10 @@ var DOCS_STYLE_RULES = {
|
|
|
2693
2899
|
"playbook-okf-soft": ["title", "no-stale-wording"],
|
|
2694
2900
|
"title-only": ["title"]
|
|
2695
2901
|
};
|
|
2696
|
-
var
|
|
2902
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2697
2903
|
var docsStyleOptions = (config) => {
|
|
2698
2904
|
const raw = config.gates?.options?.["docs-style"];
|
|
2699
|
-
if (!
|
|
2905
|
+
if (!isRecord2(raw)) {
|
|
2700
2906
|
if (config.gates?.preset === "playbook") {
|
|
2701
2907
|
return { profile: "playbook-okf-soft", required: DOCS_STYLE_RULES["playbook-okf-soft"] };
|
|
2702
2908
|
}
|
|
@@ -2740,7 +2946,7 @@ var runDocsStyleGate = (root, config) => {
|
|
|
2740
2946
|
}
|
|
2741
2947
|
const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({
|
|
2742
2948
|
path: doc.path,
|
|
2743
|
-
missing: missingStyleRules(
|
|
2949
|
+
missing: missingStyleRules(readFileSync10(doc.absPath, "utf8"), required)
|
|
2744
2950
|
})).filter((doc) => doc.missing.length > 0);
|
|
2745
2951
|
if (bad.length) {
|
|
2746
2952
|
return {
|
|
@@ -2785,8 +2991,8 @@ var resolveGateIds = (config) => {
|
|
|
2785
2991
|
};
|
|
2786
2992
|
|
|
2787
2993
|
// src/mcp/server.ts
|
|
2788
|
-
import { readFileSync as
|
|
2789
|
-
import { relative as
|
|
2994
|
+
import { readFileSync as readFileSync12, realpathSync as realpathSync7 } from "fs";
|
|
2995
|
+
import { relative as relative5, resolve as resolve8 } from "path";
|
|
2790
2996
|
import { z as z6, ZodError } from "zod";
|
|
2791
2997
|
|
|
2792
2998
|
// src/query/search.ts
|
|
@@ -2905,15 +3111,15 @@ var createDocBridgeRetriever = (index, options = {}) => ({
|
|
|
2905
3111
|
});
|
|
2906
3112
|
|
|
2907
3113
|
// src/memory/ingest.ts
|
|
2908
|
-
import { existsSync as
|
|
3114
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
|
|
2909
3115
|
import { join as join9 } from "path";
|
|
2910
3116
|
var memoryFact = (raw, id) => firstParagraph(raw) ?? firstHeading(raw) ?? id;
|
|
2911
3117
|
var relativePath = (root, abs) => toPosix(abs).replace(`${toPosix(root)}/`, "");
|
|
2912
3118
|
var ingestMarkdownDir = (root, dir, source, confidence) => {
|
|
2913
|
-
if (!
|
|
3119
|
+
if (!existsSync11(dir)) return [];
|
|
2914
3120
|
return walkFiles(dir, { extensions: [".md", ".mdc"] }).map((abs) => {
|
|
2915
3121
|
const rel = relativePath(root, abs);
|
|
2916
|
-
const raw =
|
|
3122
|
+
const raw = readFileSync11(abs, "utf8");
|
|
2917
3123
|
const id = slugFromPath(rel);
|
|
2918
3124
|
return {
|
|
2919
3125
|
schemaVersion: 1,
|
|
@@ -3130,7 +3336,9 @@ var runQuery = (index, config, req) => {
|
|
|
3130
3336
|
var MCP_TOOLS = [
|
|
3131
3337
|
{
|
|
3132
3338
|
name: "handoff.resolve",
|
|
3133
|
-
|
|
3339
|
+
title: "Resolve repository handoff",
|
|
3340
|
+
description: "Resolve a package or ownership id to its deterministic AgentHandoff.",
|
|
3341
|
+
annotations: { readOnlyHint: true },
|
|
3134
3342
|
inputSchema: {
|
|
3135
3343
|
type: "object",
|
|
3136
3344
|
properties: { id: { type: "string" }, kind: { type: "string", enum: ["package", "ownership"] } },
|
|
@@ -3139,7 +3347,9 @@ var MCP_TOOLS = [
|
|
|
3139
3347
|
},
|
|
3140
3348
|
{
|
|
3141
3349
|
name: "doc.search",
|
|
3142
|
-
|
|
3350
|
+
title: "Search repository documentation",
|
|
3351
|
+
description: "Search the deterministic Doc Bridge index for repository documentation.",
|
|
3352
|
+
annotations: { readOnlyHint: true },
|
|
3143
3353
|
inputSchema: {
|
|
3144
3354
|
type: "object",
|
|
3145
3355
|
properties: { term: { type: "string" }, limit: { type: "number" } },
|
|
@@ -3148,7 +3358,9 @@ var MCP_TOOLS = [
|
|
|
3148
3358
|
},
|
|
3149
3359
|
{
|
|
3150
3360
|
name: "doc.get",
|
|
3151
|
-
|
|
3361
|
+
title: "Read indexed documentation",
|
|
3362
|
+
description: "Read one indexed agent documentation file by id or indexed path.",
|
|
3363
|
+
annotations: { readOnlyHint: true },
|
|
3152
3364
|
inputSchema: {
|
|
3153
3365
|
type: "object",
|
|
3154
3366
|
properties: { id: { type: "string" }, path: { type: "string" } }
|
|
@@ -3156,12 +3368,16 @@ var MCP_TOOLS = [
|
|
|
3156
3368
|
},
|
|
3157
3369
|
{
|
|
3158
3370
|
name: "gate.status",
|
|
3159
|
-
|
|
3371
|
+
title: "Check documentation gates",
|
|
3372
|
+
description: "Evaluate documentation gates without writing files.",
|
|
3373
|
+
annotations: { readOnlyHint: true },
|
|
3160
3374
|
inputSchema: { type: "object", properties: {} }
|
|
3161
3375
|
},
|
|
3162
3376
|
{
|
|
3163
3377
|
name: "retriever.query",
|
|
3164
|
-
|
|
3378
|
+
title: "Retrieve documentation context",
|
|
3379
|
+
description: "Return relevant local Doc Bridge index chunks for a query.",
|
|
3380
|
+
annotations: { readOnlyHint: true },
|
|
3165
3381
|
inputSchema: {
|
|
3166
3382
|
type: "object",
|
|
3167
3383
|
properties: { query: { type: "string" }, limit: { type: "number" } },
|
|
@@ -3170,17 +3386,23 @@ var MCP_TOOLS = [
|
|
|
3170
3386
|
},
|
|
3171
3387
|
{
|
|
3172
3388
|
name: "memory.classify",
|
|
3173
|
-
|
|
3389
|
+
title: "Classify memory candidates",
|
|
3390
|
+
description: "Classify local memory candidates into agent, human, playbook, or discard routes.",
|
|
3391
|
+
annotations: { readOnlyHint: true },
|
|
3174
3392
|
inputSchema: { type: "object", properties: {} }
|
|
3175
3393
|
},
|
|
3176
3394
|
{
|
|
3177
3395
|
name: "memory.promoteDraft",
|
|
3178
|
-
|
|
3396
|
+
title: "Draft memory promotion",
|
|
3397
|
+
description: "Build a reviewable draft promotion body from local memory candidates without publishing it.",
|
|
3398
|
+
annotations: { readOnlyHint: true },
|
|
3179
3399
|
inputSchema: { type: "object", properties: {} }
|
|
3180
3400
|
},
|
|
3181
3401
|
{
|
|
3182
3402
|
name: "registry.topology",
|
|
3183
|
-
|
|
3403
|
+
title: "Inspect registry topology",
|
|
3404
|
+
description: "Return the static Doc Bridge curator and delegate topology.",
|
|
3405
|
+
annotations: { readOnlyHint: true },
|
|
3184
3406
|
inputSchema: { type: "object", properties: {} }
|
|
3185
3407
|
}
|
|
3186
3408
|
];
|
|
@@ -3231,12 +3453,12 @@ var findDocPath = (index, args) => {
|
|
|
3231
3453
|
return doc.path;
|
|
3232
3454
|
};
|
|
3233
3455
|
var resolveDocPath = (root, relPath) => {
|
|
3234
|
-
const rootAbs =
|
|
3235
|
-
const unresolved =
|
|
3236
|
-
const unresolvedRel =
|
|
3456
|
+
const rootAbs = realpathSync7.native(root);
|
|
3457
|
+
const unresolved = resolve8(rootAbs, relPath);
|
|
3458
|
+
const unresolvedRel = relative5(rootAbs, unresolved);
|
|
3237
3459
|
if (unresolvedRel.startsWith("..")) throw new Error("doc.get path escapes project root");
|
|
3238
|
-
const abs =
|
|
3239
|
-
const rel =
|
|
3460
|
+
const abs = realpathSync7.native(unresolved);
|
|
3461
|
+
const rel = relative5(rootAbs, abs);
|
|
3240
3462
|
if (rel.startsWith("..")) throw new Error("doc.get path escapes project root");
|
|
3241
3463
|
return abs;
|
|
3242
3464
|
};
|
|
@@ -3270,7 +3492,7 @@ var handleMcpRequest = (ctx, request) => {
|
|
|
3270
3492
|
}
|
|
3271
3493
|
if (name === "doc.get") {
|
|
3272
3494
|
const relPath = findDocPath(index(), parseToolArgs("doc.get", DocGetArgsSchema, args));
|
|
3273
|
-
return textResult(
|
|
3495
|
+
return textResult(readFileSync12(resolveDocPath(ctx.root, relPath), "utf8"));
|
|
3274
3496
|
}
|
|
3275
3497
|
if (name === "gate.status") return textResult(runGates(ctx.root, ctx.config));
|
|
3276
3498
|
if (name === "retriever.query") {
|
|
@@ -3297,13 +3519,14 @@ var handleMcpRequest = (ctx, request) => {
|
|
|
3297
3519
|
if (request.method?.startsWith("notifications/")) return void 0;
|
|
3298
3520
|
throw new Error(`Unsupported MCP method "${request.method ?? ""}"`);
|
|
3299
3521
|
};
|
|
3300
|
-
var writeFrame = (payload) => {
|
|
3522
|
+
var writeFrame = (payload, framing) => {
|
|
3301
3523
|
const body = JSON.stringify(payload);
|
|
3302
|
-
process.stdout.write(
|
|
3524
|
+
process.stdout.write(framing === "json-line" ? `${body}
|
|
3525
|
+
` : `Content-Length: ${Buffer.byteLength(body)}\r
|
|
3303
3526
|
\r
|
|
3304
3527
|
${body}`);
|
|
3305
3528
|
};
|
|
3306
|
-
var respond = (ctx, request) => {
|
|
3529
|
+
var respond = (ctx, request, framing) => {
|
|
3307
3530
|
if (request.id === void 0) {
|
|
3308
3531
|
try {
|
|
3309
3532
|
handleMcpRequest(ctx, request);
|
|
@@ -3313,13 +3536,13 @@ var respond = (ctx, request) => {
|
|
|
3313
3536
|
}
|
|
3314
3537
|
try {
|
|
3315
3538
|
const result = handleMcpRequest(ctx, request);
|
|
3316
|
-
writeFrame({ jsonrpc: "2.0", id: request.id, result: result ?? {} });
|
|
3539
|
+
writeFrame({ jsonrpc: "2.0", id: request.id, result: result ?? {} }, framing);
|
|
3317
3540
|
} catch (error) {
|
|
3318
3541
|
writeFrame({
|
|
3319
3542
|
jsonrpc: "2.0",
|
|
3320
3543
|
id: request.id,
|
|
3321
3544
|
error: { code: -32e3, message: error instanceof Error ? error.message : String(error) }
|
|
3322
|
-
});
|
|
3545
|
+
}, framing);
|
|
3323
3546
|
}
|
|
3324
3547
|
};
|
|
3325
3548
|
var startMcpStdioServer = (ctx) => {
|
|
@@ -3327,30 +3550,43 @@ var startMcpStdioServer = (ctx) => {
|
|
|
3327
3550
|
process.stdin.on("data", (chunk) => {
|
|
3328
3551
|
buffer = Buffer.concat([buffer, chunk]);
|
|
3329
3552
|
while (true) {
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3553
|
+
if (/^content-length:/i.test(buffer.subarray(0, Math.min(buffer.length, 32)).toString("utf8"))) {
|
|
3554
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
3555
|
+
if (headerEnd === -1) return;
|
|
3556
|
+
const header = buffer.subarray(0, headerEnd).toString("utf8");
|
|
3557
|
+
const match = /content-length:\s*(\d+)/i.exec(header);
|
|
3558
|
+
if (!match?.[1]) {
|
|
3559
|
+
buffer = buffer.subarray(headerEnd + 4);
|
|
3560
|
+
continue;
|
|
3561
|
+
}
|
|
3562
|
+
const length = Number(match[1]);
|
|
3563
|
+
const bodyStart = headerEnd + 4;
|
|
3564
|
+
const bodyEnd = bodyStart + length;
|
|
3565
|
+
if (buffer.length < bodyEnd) return;
|
|
3566
|
+
const raw2 = buffer.subarray(bodyStart, bodyEnd).toString("utf8");
|
|
3567
|
+
buffer = buffer.subarray(bodyEnd);
|
|
3568
|
+
respond(ctx, JSON.parse(raw2), "content-length");
|
|
3336
3569
|
continue;
|
|
3337
3570
|
}
|
|
3338
|
-
const
|
|
3339
|
-
|
|
3340
|
-
const
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3571
|
+
const lineEnd = buffer.indexOf("\n");
|
|
3572
|
+
if (lineEnd === -1) return;
|
|
3573
|
+
const raw = buffer.subarray(0, lineEnd).toString("utf8").trim();
|
|
3574
|
+
buffer = buffer.subarray(lineEnd + 1);
|
|
3575
|
+
if (!raw) continue;
|
|
3576
|
+
try {
|
|
3577
|
+
respond(ctx, JSON.parse(raw), "json-line");
|
|
3578
|
+
} catch {
|
|
3579
|
+
writeFrame({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } }, "json-line");
|
|
3580
|
+
}
|
|
3345
3581
|
}
|
|
3346
3582
|
});
|
|
3347
3583
|
process.stdin.resume();
|
|
3348
3584
|
};
|
|
3349
3585
|
|
|
3350
3586
|
// src/mcp/install.ts
|
|
3351
|
-
import { existsSync as
|
|
3587
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync2, readFileSync as readFileSync13, writeFileSync as writeFileSync2 } from "fs";
|
|
3352
3588
|
import { homedir } from "os";
|
|
3353
|
-
import { dirname as
|
|
3589
|
+
import { dirname as dirname5, join as join10, resolve as resolve9 } from "path";
|
|
3354
3590
|
var SERVER_NAME = "ak-docs";
|
|
3355
3591
|
var mcpServerEntry = (root) => ({
|
|
3356
3592
|
command: "npx",
|
|
@@ -3358,26 +3594,26 @@ var mcpServerEntry = (root) => ({
|
|
|
3358
3594
|
cwd: root
|
|
3359
3595
|
});
|
|
3360
3596
|
var readJson = (path) => {
|
|
3361
|
-
if (!
|
|
3597
|
+
if (!existsSync12(path)) return {};
|
|
3362
3598
|
try {
|
|
3363
|
-
const parsed = JSON.parse(
|
|
3599
|
+
const parsed = JSON.parse(readFileSync13(path, "utf8"));
|
|
3364
3600
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
3365
3601
|
} catch {
|
|
3366
3602
|
return {};
|
|
3367
3603
|
}
|
|
3368
3604
|
};
|
|
3369
3605
|
var writeJson = (path, value) => {
|
|
3370
|
-
mkdirSync2(
|
|
3606
|
+
mkdirSync2(dirname5(path), { recursive: true });
|
|
3371
3607
|
writeFileSync2(path, `${JSON.stringify(value, null, 2)}
|
|
3372
3608
|
`, "utf8");
|
|
3373
3609
|
};
|
|
3374
3610
|
var resolveTargetPath = (target, root) => {
|
|
3375
|
-
if (target === "cursor") return
|
|
3611
|
+
if (target === "cursor") return resolve9(root, ".cursor", "mcp.json");
|
|
3376
3612
|
return join10(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
3377
3613
|
};
|
|
3378
3614
|
var installMcpConfig = (root, target) => {
|
|
3379
3615
|
const configPath = resolveTargetPath(target, root);
|
|
3380
|
-
const created = !
|
|
3616
|
+
const created = !existsSync12(configPath);
|
|
3381
3617
|
const existing = readJson(configPath);
|
|
3382
3618
|
const servers = existing.mcpServers && typeof existing.mcpServers === "object" && !Array.isArray(existing.mcpServers) ? { ...existing.mcpServers } : {};
|
|
3383
3619
|
servers[SERVER_NAME] = mcpServerEntry(root);
|
|
@@ -3643,22 +3879,23 @@ var formatDoctorText = (report) => {
|
|
|
3643
3879
|
};
|
|
3644
3880
|
|
|
3645
3881
|
// src/index-builder/watch-index.ts
|
|
3646
|
-
import { existsSync as
|
|
3647
|
-
import { dirname as
|
|
3882
|
+
import { existsSync as existsSync13, watch } from "fs";
|
|
3883
|
+
import { dirname as dirname6, resolve as resolve10 } from "path";
|
|
3648
3884
|
var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
|
|
3885
|
+
var NX_MANIFEST_PATTERN = /(^|[/\\])(project|package)\.json$/i;
|
|
3649
3886
|
var collectWatchRoots = (root, config, configPath) => {
|
|
3650
3887
|
const roots = /* @__PURE__ */ new Set();
|
|
3651
|
-
roots.add(
|
|
3888
|
+
roots.add(resolve10(root, config.corpus.agent.root));
|
|
3652
3889
|
const humanSources = config.corpus.human ? Array.isArray(config.corpus.human) ? config.corpus.human : [config.corpus.human] : [];
|
|
3653
3890
|
for (const source of humanSources) {
|
|
3654
3891
|
const humanOpts = source.options ?? {};
|
|
3655
|
-
for (const key of ["contentDir", "docsDir", "root"]) {
|
|
3892
|
+
for (const key of ["contentDir", "docsDir", "root", "srcDir"]) {
|
|
3656
3893
|
const value = humanOpts[key];
|
|
3657
|
-
if (typeof value === "string" && value.length) roots.add(
|
|
3894
|
+
if (typeof value === "string" && value.length) roots.add(resolve10(root, value));
|
|
3658
3895
|
}
|
|
3659
3896
|
}
|
|
3660
|
-
if (configPath) roots.add(
|
|
3661
|
-
return [...roots].filter((dir) =>
|
|
3897
|
+
if (configPath) roots.add(dirname6(resolve10(configPath)));
|
|
3898
|
+
return [...roots].filter((dir) => existsSync13(dir));
|
|
3662
3899
|
};
|
|
3663
3900
|
var watchDocBridgeIndex = (opts) => {
|
|
3664
3901
|
const debounceMs = opts.debounceMs ?? 350;
|
|
@@ -3699,8 +3936,15 @@ var watchDocBridgeIndex = (opts) => {
|
|
|
3699
3936
|
rebuild();
|
|
3700
3937
|
});
|
|
3701
3938
|
}
|
|
3702
|
-
const
|
|
3703
|
-
if (
|
|
3939
|
+
const nxRoot = resolve10(opts.root);
|
|
3940
|
+
if (opts.config.routing?.plugin === "nx" && existsSync13(nxRoot)) {
|
|
3941
|
+
watch(nxRoot, { recursive: true }, (_event, filename) => {
|
|
3942
|
+
if (!filename || !NX_MANIFEST_PATTERN.test(filename)) return;
|
|
3943
|
+
rebuild();
|
|
3944
|
+
});
|
|
3945
|
+
}
|
|
3946
|
+
const configDir = resolve10(opts.root);
|
|
3947
|
+
if (existsSync13(configDir)) {
|
|
3704
3948
|
watch(configDir, (_event, filename) => {
|
|
3705
3949
|
if (!filename || !/doc-bridge\.config/.test(filename)) return;
|
|
3706
3950
|
rebuild();
|
|
@@ -3719,7 +3963,7 @@ var watchDocBridgeIndex = (opts) => {
|
|
|
3719
3963
|
|
|
3720
3964
|
// src/memory/github-pr.ts
|
|
3721
3965
|
import { execFileSync, spawnSync } from "child_process";
|
|
3722
|
-
import { existsSync as
|
|
3966
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
3723
3967
|
import { join as join12 } from "path";
|
|
3724
3968
|
var run = (cmd, args, cwd) => {
|
|
3725
3969
|
const result = spawnSync(cmd, [...args], { cwd, encoding: "utf8" });
|
|
@@ -3767,7 +4011,7 @@ var promoteMemoryToGithubPr = (root, draft, options = {}) => {
|
|
|
3767
4011
|
message: `Wrote draft to ${relDraft}. Run the printed git/gh commands to open a draft PR.`
|
|
3768
4012
|
};
|
|
3769
4013
|
}
|
|
3770
|
-
if (!
|
|
4014
|
+
if (!existsSync14(join12(root, ".git"))) {
|
|
3771
4015
|
return {
|
|
3772
4016
|
ok: false,
|
|
3773
4017
|
dryRun: false,
|
|
@@ -3870,8 +4114,8 @@ ${auth.out}`
|
|
|
3870
4114
|
};
|
|
3871
4115
|
|
|
3872
4116
|
// src/federation/llms.ts
|
|
3873
|
-
import { existsSync as
|
|
3874
|
-
import { resolve as
|
|
4117
|
+
import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
|
|
4118
|
+
import { resolve as resolve11 } from "path";
|
|
3875
4119
|
var tokenize2 = (value) => value.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length >= 2);
|
|
3876
4120
|
var scoreText = (query, text) => {
|
|
3877
4121
|
const hay = text.toLowerCase();
|
|
@@ -3886,9 +4130,9 @@ var defaultFetchText = async (url) => {
|
|
|
3886
4130
|
var sourceText = async (root, source, fetchText) => {
|
|
3887
4131
|
try {
|
|
3888
4132
|
if (/^https?:\/\//.test(source)) return await fetchText(source);
|
|
3889
|
-
const path =
|
|
3890
|
-
if (!
|
|
3891
|
-
return
|
|
4133
|
+
const path = resolve11(root, source);
|
|
4134
|
+
if (!existsSync15(path)) return null;
|
|
4135
|
+
return readFileSync14(path, "utf8");
|
|
3892
4136
|
} catch {
|
|
3893
4137
|
return null;
|
|
3894
4138
|
}
|
|
@@ -3973,7 +4217,7 @@ var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
|
|
|
3973
4217
|
};
|
|
3974
4218
|
|
|
3975
4219
|
// src/intelligence/rag.ts
|
|
3976
|
-
import { readFileSync as
|
|
4220
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
3977
4221
|
import { join as join14 } from "path";
|
|
3978
4222
|
|
|
3979
4223
|
// src/intelligence/adapter.ts
|
|
@@ -4115,7 +4359,7 @@ var loadDocuments = (root, index, sources) => {
|
|
|
4115
4359
|
const abs = join14(root, entry.path);
|
|
4116
4360
|
let content = "";
|
|
4117
4361
|
try {
|
|
4118
|
-
content =
|
|
4362
|
+
content = readFileSync15(abs, "utf8");
|
|
4119
4363
|
} catch {
|
|
4120
4364
|
content = [entry.title, entry.description].filter(Boolean).join("\n\n");
|
|
4121
4365
|
}
|
|
@@ -4425,6 +4669,7 @@ export {
|
|
|
4425
4669
|
createDocBridgeRetriever,
|
|
4426
4670
|
defaultPromotionDraftPath,
|
|
4427
4671
|
defineConfig,
|
|
4672
|
+
discoverNxProjects,
|
|
4428
4673
|
docBridgePatternMarkdown,
|
|
4429
4674
|
docBridgePatternPayload,
|
|
4430
4675
|
doctorBadgeMetrics,
|