@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.
Files changed (43) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/CONTRIBUTING.md +6 -0
  3. package/PRIVACY.md +38 -0
  4. package/README.md +30 -3
  5. package/SECURITY.md +7 -2
  6. package/action.yml +1 -1
  7. package/dist/cli/program.js +400 -156
  8. package/dist/cli/program.js.map +1 -1
  9. package/dist/config/index.d.ts +1 -1
  10. package/dist/config/index.js +3 -1
  11. package/dist/config/index.js.map +1 -1
  12. package/dist/{index-DGI9TBLE.d.ts → index-DAeq_OIi.d.ts} +22 -22
  13. package/dist/index.d.ts +47 -12
  14. package/dist/index.js +376 -131
  15. package/dist/index.js.map +1 -1
  16. package/docs/POSITIONING.md +1 -1
  17. package/docs/examples.md +4 -0
  18. package/docs/getting-started.md +1 -1
  19. package/docs/landing/index.html +1 -1
  20. package/docs/mcp.md +16 -0
  21. package/docs/qa/vitepress-starlight-adapters.md +19 -0
  22. package/docs/spec/config-v1.md +33 -2
  23. package/examples/nextra-only.config.ts +17 -0
  24. package/examples/nx-monorepo.config.ts +11 -0
  25. package/examples/starlight-only.config.ts +17 -0
  26. package/examples/vitepress-only.config.ts +19 -0
  27. package/mcpb/.mcpbignore +8 -0
  28. package/mcpb/icon.png +0 -0
  29. package/mcpb/manifest.json +96 -0
  30. package/package.json +14 -11
  31. package/src/config/schema.ts +3 -1
  32. package/src/index-builder/build-handoffs.ts +2 -0
  33. package/src/index-builder/build-index.ts +7 -1
  34. package/src/index-builder/human-adapters/index.ts +6 -0
  35. package/src/index-builder/human-adapters/nextra.ts +43 -0
  36. package/src/index-builder/human-adapters/starlight.ts +40 -0
  37. package/src/index-builder/human-adapters/vitepress.ts +43 -0
  38. package/src/index-builder/plugins/nx.ts +161 -0
  39. package/src/index-builder/plugins/pnpm-monorepo.ts +1 -0
  40. package/src/index-builder/watch-index.ts +11 -2
  41. package/src/index.ts +1 -0
  42. package/src/mcp/server.ts +58 -26
  43. 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 readFileSync6, writeFileSync } from "fs";
1032
- import { dirname as dirname3, join as join7 } from "path";
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, packageId) => {
1303
+ var guessAgentDocForPackage = (corpus, packageId2) => {
1302
1304
  const candidates = [
1303
- (doc) => frontmatterString(doc.frontmatter, "package") === packageId,
1304
- (doc) => doc.id === packageId,
1305
- (doc) => doc.relPath.endsWith(`/packages/${packageId}.md`),
1306
- (doc) => doc.relPath.endsWith(`/packages/${packageId}.mdx`),
1307
- (doc) => doc.relPath.endsWith(`/packages/${packageId}/index.md`),
1308
- (doc) => doc.relPath.endsWith(`/packages/${packageId}/index.mdx`),
1309
- (doc) => doc.relPath.endsWith(`/${packageId}.md`),
1310
- (doc) => doc.relPath.endsWith(`/${packageId}.mdx`),
1311
- (doc) => doc.relPath.includes(`/packages/${packageId}/`)
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 = (packageId, override, fmHuman, humanDocs = {}) => {
1425
+ var resolveHumanDoc = (packageId2, override, fmHuman, humanDocs = {}) => {
1423
1426
  if (override) return override;
1424
1427
  if (fmHuman) return fmHuman;
1425
- if (humanDocs[packageId]) return humanDocs[packageId];
1428
+ if (humanDocs[packageId2]) return humanDocs[packageId2];
1426
1429
  const aliases = [
1427
- packageId,
1428
- packageId.replace(/^@[^/]+\//, ""),
1429
- packageId.replace(/^os-/, ""),
1430
- packageId.replace(/-pattern$/, ""),
1431
- `packages/${packageId}`,
1432
- `reference/packages/${packageId}`,
1433
- `packages/${packageId}/index`
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 === packageId || key.endsWith(`/${packageId}`) || key.endsWith(`/${packageId}/index`) || key.endsWith(`-${packageId}`)) {
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.2.4";
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 existsSync7, readFileSync as readFileSync5 } from "fs";
1844
- import { basename as basename2, join as join6 } from "path";
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 existsSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
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 (existsSync6(abs)) dirs.add(toPosix(abs));
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 (!existsSync6(base)) continue;
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 (!existsSync7(file)) return null;
2101
+ if (!existsSync8(file)) return null;
1896
2102
  try {
1897
- return JSON.parse(readFileSync5(file, "utf8"));
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 (existsSync7(workspaceFile)) {
1908
- patterns = parsePnpmWorkspace(readFileSync5(workspaceFile, "utf8"));
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 = basename2(abs);
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(readFileSync6(join7(root, "package.json"), "utf8"));
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(readFileSync6(indexPath, "utf8"));
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(dirname3(indexPath), { recursive: true });
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(dirname3(capabilitiesPath), { recursive: true });
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 readFileSync9 } from "fs";
2249
+ import { readFileSync as readFileSync10 } from "fs";
2044
2250
 
2045
2251
  // src/conformance/documentation-standard-v1.ts
2046
- import { existsSync as existsSync8, readFileSync as readFileSync7, realpathSync as realpathSync5, statSync as statSync3 } from "fs";
2047
- import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve5, sep as sep4 } from "path";
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 = realpathSync5.native(resolve5(root));
2227
- const unresolved = resolve5(rootAbs, path);
2228
- const unresolvedRel = relative3(rootAbs, unresolved);
2229
- if (isAbsolute3(unresolvedRel) || unresolvedRel === ".." || unresolvedRel.startsWith(`..${sep4}`)) return void 0;
2230
- if (!existsSync8(unresolved)) return unresolved;
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 = realpathSync5.native(unresolved);
2233
- const rel = relative3(rootAbs, abs);
2234
- return !isAbsolute3(rel) && rel !== ".." && !rel.startsWith(`..${sep4}`) ? abs : void 0;
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 (!existsSync8(abs)) {
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(relative3(resolve5(root), abs)) || ".", detail: "File exists and is non-empty." }
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 = readFileSync7(abs, "utf8");
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(relative3(resolve5(root), abs)) || ".",
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(relative3(resolve5(root), doc.path)),
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) ?? resolve5(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) ?? resolve5(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 existsSync9, readFileSync as readFileSync8 } from "fs";
2580
- import { join as join8, resolve as resolve6 } from "path";
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 (!existsSync9(path)) throw new IndexNotFoundError(path);
2593
- const raw = JSON.parse(readFileSync8(path, "utf8"));
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) => resolve6(cwd ?? process.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(readFileSync9(doc.absPath, "utf8")) })).filter((doc) => !doc.type || allowed && !allowed.includes(doc.type));
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 isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
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 (!isRecord(raw)) {
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(readFileSync9(doc.absPath, "utf8"), required)
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 readFileSync11, realpathSync as realpathSync6 } from "fs";
2789
- import { relative as relative4, resolve as resolve7 } from "path";
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 existsSync10, readFileSync as readFileSync10 } from "fs";
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 (!existsSync10(dir)) return [];
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 = readFileSync10(abs, "utf8");
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
- description: "Resolve a package or ownership id to an AgentHandoff.",
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
- description: "Search the deterministic doc-bridge index.",
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
- description: "Read an indexed agent documentation file by id or path.",
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
- description: "Run the index-freshness gate.",
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
- description: "Return local doc-bridge retriever chunks for a query.",
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
- description: "Classify local memory candidates into agent/human/playbook/discard routes.",
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
- description: "Build a safe draft promotion body for local memory candidates.",
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
- description: "Return the doc-curator registry topology.",
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 = realpathSync6.native(root);
3235
- const unresolved = resolve7(rootAbs, relPath);
3236
- const unresolvedRel = relative4(rootAbs, unresolved);
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 = realpathSync6.native(unresolved);
3239
- const rel = relative4(rootAbs, abs);
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(readFileSync11(resolveDocPath(ctx.root, relPath), "utf8"));
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(`Content-Length: ${Buffer.byteLength(body)}\r
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
- const headerEnd = buffer.indexOf("\r\n\r\n");
3331
- if (headerEnd === -1) return;
3332
- const header = buffer.subarray(0, headerEnd).toString("utf8");
3333
- const match = /content-length:\s*(\d+)/i.exec(header);
3334
- if (!match?.[1]) {
3335
- buffer = buffer.subarray(headerEnd + 4);
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 length = Number(match[1]);
3339
- const bodyStart = headerEnd + 4;
3340
- const bodyEnd = bodyStart + length;
3341
- if (buffer.length < bodyEnd) return;
3342
- const raw = buffer.subarray(bodyStart, bodyEnd).toString("utf8");
3343
- buffer = buffer.subarray(bodyEnd);
3344
- respond(ctx, JSON.parse(raw));
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 existsSync11, mkdirSync as mkdirSync2, readFileSync as readFileSync12, writeFileSync as writeFileSync2 } from "fs";
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 dirname4, join as join10, resolve as resolve8 } from "path";
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 (!existsSync11(path)) return {};
3597
+ if (!existsSync12(path)) return {};
3362
3598
  try {
3363
- const parsed = JSON.parse(readFileSync12(path, "utf8"));
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(dirname4(path), { recursive: true });
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 resolve8(root, ".cursor", "mcp.json");
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 = !existsSync11(configPath);
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 existsSync12, watch } from "fs";
3647
- import { dirname as dirname5, resolve as resolve9 } from "path";
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(resolve9(root, config.corpus.agent.root));
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(resolve9(root, value));
3894
+ if (typeof value === "string" && value.length) roots.add(resolve10(root, value));
3658
3895
  }
3659
3896
  }
3660
- if (configPath) roots.add(dirname5(resolve9(configPath)));
3661
- return [...roots].filter((dir) => existsSync12(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 configDir = resolve9(opts.root);
3703
- if (existsSync12(configDir)) {
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 existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
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 (!existsSync13(join12(root, ".git"))) {
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 existsSync14, readFileSync as readFileSync13 } from "fs";
3874
- import { resolve as resolve10 } from "path";
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 = resolve10(root, source);
3890
- if (!existsSync14(path)) return null;
3891
- return readFileSync13(path, "utf8");
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 readFileSync14 } from "fs";
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 = readFileSync14(abs, "utf8");
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,