@agentskit/doc-bridge 1.2.6 → 1.4.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 (47) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +32 -6
  3. package/action.yml +1 -1
  4. package/dist/cli/program.js +344 -130
  5. package/dist/cli/program.js.map +1 -1
  6. package/dist/config/index.d.ts +1 -1
  7. package/dist/config/index.js +3 -1
  8. package/dist/config/index.js.map +1 -1
  9. package/dist/{index-DGI9TBLE.d.ts → index-DAeq_OIi.d.ts} +22 -22
  10. package/dist/index.d.ts +7 -4
  11. package/dist/index.js +320 -105
  12. package/dist/index.js.map +1 -1
  13. package/docs/MARKETPLACE.md +1 -1
  14. package/docs/POSITIONING.md +1 -1
  15. package/docs/examples.md +4 -0
  16. package/docs/getting-started.md +1 -1
  17. package/docs/guides/choose-context-layer.md +129 -0
  18. package/docs/guides/gate-ci.md +1 -1
  19. package/docs/guides/meta.json +1 -0
  20. package/docs/landing/index.html +2 -2
  21. package/docs/playbook/doc-bridge-pattern.md +1 -1
  22. package/docs/qa/vitepress-starlight-adapters.md +19 -0
  23. package/docs/recipes/index-pipeline.md +1 -1
  24. package/docs/spec/config-v1.md +33 -2
  25. package/examples/nextra-only.config.ts +17 -0
  26. package/examples/nx-monorepo.config.ts +11 -0
  27. package/examples/starlight-only.config.ts +17 -0
  28. package/examples/vitepress-only.config.ts +19 -0
  29. package/mcpb/manifest.json +1 -1
  30. package/package.json +15 -4
  31. package/skills/doc-bridge-handoff/SKILL.md +38 -0
  32. package/skills/doc-bridge-handoff/fixtures/synthetic-repo/doc-bridge.config.json +23 -0
  33. package/skills/doc-bridge-handoff/fixtures/synthetic-repo/docs/for-agents/packages/payments.md +5 -0
  34. package/skills/doc-bridge-handoff/fixtures/synthetic-repo/packages/payments/package.json +4 -0
  35. package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +70 -0
  36. package/src/config/schema.ts +3 -1
  37. package/src/index-builder/build-handoffs.ts +2 -0
  38. package/src/index-builder/build-index.ts +7 -1
  39. package/src/index-builder/human-adapters/index.ts +6 -0
  40. package/src/index-builder/human-adapters/nextra.ts +43 -0
  41. package/src/index-builder/human-adapters/starlight.ts +40 -0
  42. package/src/index-builder/human-adapters/vitepress.ts +43 -0
  43. package/src/index-builder/plugins/nx.ts +161 -0
  44. package/src/index-builder/plugins/pnpm-monorepo.ts +1 -0
  45. package/src/index-builder/watch-index.ts +11 -2
  46. package/src/index.ts +1 -0
  47. 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.6";
1540
+ var PACKAGE_VERSION = "1.4.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,
@@ -3247,12 +3453,12 @@ var findDocPath = (index, args) => {
3247
3453
  return doc.path;
3248
3454
  };
3249
3455
  var resolveDocPath = (root, relPath) => {
3250
- const rootAbs = realpathSync6.native(root);
3251
- const unresolved = resolve7(rootAbs, relPath);
3252
- const unresolvedRel = relative4(rootAbs, unresolved);
3456
+ const rootAbs = realpathSync7.native(root);
3457
+ const unresolved = resolve8(rootAbs, relPath);
3458
+ const unresolvedRel = relative5(rootAbs, unresolved);
3253
3459
  if (unresolvedRel.startsWith("..")) throw new Error("doc.get path escapes project root");
3254
- const abs = realpathSync6.native(unresolved);
3255
- const rel = relative4(rootAbs, abs);
3460
+ const abs = realpathSync7.native(unresolved);
3461
+ const rel = relative5(rootAbs, abs);
3256
3462
  if (rel.startsWith("..")) throw new Error("doc.get path escapes project root");
3257
3463
  return abs;
3258
3464
  };
@@ -3286,7 +3492,7 @@ var handleMcpRequest = (ctx, request) => {
3286
3492
  }
3287
3493
  if (name === "doc.get") {
3288
3494
  const relPath = findDocPath(index(), parseToolArgs("doc.get", DocGetArgsSchema, args));
3289
- return textResult(readFileSync11(resolveDocPath(ctx.root, relPath), "utf8"));
3495
+ return textResult(readFileSync12(resolveDocPath(ctx.root, relPath), "utf8"));
3290
3496
  }
3291
3497
  if (name === "gate.status") return textResult(runGates(ctx.root, ctx.config));
3292
3498
  if (name === "retriever.query") {
@@ -3378,9 +3584,9 @@ var startMcpStdioServer = (ctx) => {
3378
3584
  };
3379
3585
 
3380
3586
  // src/mcp/install.ts
3381
- 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";
3382
3588
  import { homedir } from "os";
3383
- 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";
3384
3590
  var SERVER_NAME = "ak-docs";
3385
3591
  var mcpServerEntry = (root) => ({
3386
3592
  command: "npx",
@@ -3388,26 +3594,26 @@ var mcpServerEntry = (root) => ({
3388
3594
  cwd: root
3389
3595
  });
3390
3596
  var readJson = (path) => {
3391
- if (!existsSync11(path)) return {};
3597
+ if (!existsSync12(path)) return {};
3392
3598
  try {
3393
- const parsed = JSON.parse(readFileSync12(path, "utf8"));
3599
+ const parsed = JSON.parse(readFileSync13(path, "utf8"));
3394
3600
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
3395
3601
  } catch {
3396
3602
  return {};
3397
3603
  }
3398
3604
  };
3399
3605
  var writeJson = (path, value) => {
3400
- mkdirSync2(dirname4(path), { recursive: true });
3606
+ mkdirSync2(dirname5(path), { recursive: true });
3401
3607
  writeFileSync2(path, `${JSON.stringify(value, null, 2)}
3402
3608
  `, "utf8");
3403
3609
  };
3404
3610
  var resolveTargetPath = (target, root) => {
3405
- if (target === "cursor") return resolve8(root, ".cursor", "mcp.json");
3611
+ if (target === "cursor") return resolve9(root, ".cursor", "mcp.json");
3406
3612
  return join10(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
3407
3613
  };
3408
3614
  var installMcpConfig = (root, target) => {
3409
3615
  const configPath = resolveTargetPath(target, root);
3410
- const created = !existsSync11(configPath);
3616
+ const created = !existsSync12(configPath);
3411
3617
  const existing = readJson(configPath);
3412
3618
  const servers = existing.mcpServers && typeof existing.mcpServers === "object" && !Array.isArray(existing.mcpServers) ? { ...existing.mcpServers } : {};
3413
3619
  servers[SERVER_NAME] = mcpServerEntry(root);
@@ -3673,22 +3879,23 @@ var formatDoctorText = (report) => {
3673
3879
  };
3674
3880
 
3675
3881
  // src/index-builder/watch-index.ts
3676
- import { existsSync as existsSync12, watch } from "fs";
3677
- 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";
3678
3884
  var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
3885
+ var NX_MANIFEST_PATTERN = /(^|[/\\])(project|package)\.json$/i;
3679
3886
  var collectWatchRoots = (root, config, configPath) => {
3680
3887
  const roots = /* @__PURE__ */ new Set();
3681
- roots.add(resolve9(root, config.corpus.agent.root));
3888
+ roots.add(resolve10(root, config.corpus.agent.root));
3682
3889
  const humanSources = config.corpus.human ? Array.isArray(config.corpus.human) ? config.corpus.human : [config.corpus.human] : [];
3683
3890
  for (const source of humanSources) {
3684
3891
  const humanOpts = source.options ?? {};
3685
- for (const key of ["contentDir", "docsDir", "root"]) {
3892
+ for (const key of ["contentDir", "docsDir", "root", "srcDir"]) {
3686
3893
  const value = humanOpts[key];
3687
- if (typeof value === "string" && value.length) roots.add(resolve9(root, value));
3894
+ if (typeof value === "string" && value.length) roots.add(resolve10(root, value));
3688
3895
  }
3689
3896
  }
3690
- if (configPath) roots.add(dirname5(resolve9(configPath)));
3691
- return [...roots].filter((dir) => existsSync12(dir));
3897
+ if (configPath) roots.add(dirname6(resolve10(configPath)));
3898
+ return [...roots].filter((dir) => existsSync13(dir));
3692
3899
  };
3693
3900
  var watchDocBridgeIndex = (opts) => {
3694
3901
  const debounceMs = opts.debounceMs ?? 350;
@@ -3729,8 +3936,15 @@ var watchDocBridgeIndex = (opts) => {
3729
3936
  rebuild();
3730
3937
  });
3731
3938
  }
3732
- const configDir = resolve9(opts.root);
3733
- 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)) {
3734
3948
  watch(configDir, (_event, filename) => {
3735
3949
  if (!filename || !/doc-bridge\.config/.test(filename)) return;
3736
3950
  rebuild();
@@ -3749,7 +3963,7 @@ var watchDocBridgeIndex = (opts) => {
3749
3963
 
3750
3964
  // src/memory/github-pr.ts
3751
3965
  import { execFileSync, spawnSync } from "child_process";
3752
- 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";
3753
3967
  import { join as join12 } from "path";
3754
3968
  var run = (cmd, args, cwd) => {
3755
3969
  const result = spawnSync(cmd, [...args], { cwd, encoding: "utf8" });
@@ -3797,7 +4011,7 @@ var promoteMemoryToGithubPr = (root, draft, options = {}) => {
3797
4011
  message: `Wrote draft to ${relDraft}. Run the printed git/gh commands to open a draft PR.`
3798
4012
  };
3799
4013
  }
3800
- if (!existsSync13(join12(root, ".git"))) {
4014
+ if (!existsSync14(join12(root, ".git"))) {
3801
4015
  return {
3802
4016
  ok: false,
3803
4017
  dryRun: false,
@@ -3900,8 +4114,8 @@ ${auth.out}`
3900
4114
  };
3901
4115
 
3902
4116
  // src/federation/llms.ts
3903
- import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
3904
- import { resolve as resolve10 } from "path";
4117
+ import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
4118
+ import { resolve as resolve11 } from "path";
3905
4119
  var tokenize2 = (value) => value.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length >= 2);
3906
4120
  var scoreText = (query, text) => {
3907
4121
  const hay = text.toLowerCase();
@@ -3916,9 +4130,9 @@ var defaultFetchText = async (url) => {
3916
4130
  var sourceText = async (root, source, fetchText) => {
3917
4131
  try {
3918
4132
  if (/^https?:\/\//.test(source)) return await fetchText(source);
3919
- const path = resolve10(root, source);
3920
- if (!existsSync14(path)) return null;
3921
- return readFileSync13(path, "utf8");
4133
+ const path = resolve11(root, source);
4134
+ if (!existsSync15(path)) return null;
4135
+ return readFileSync14(path, "utf8");
3922
4136
  } catch {
3923
4137
  return null;
3924
4138
  }
@@ -4003,7 +4217,7 @@ var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
4003
4217
  };
4004
4218
 
4005
4219
  // src/intelligence/rag.ts
4006
- import { readFileSync as readFileSync14 } from "fs";
4220
+ import { readFileSync as readFileSync15 } from "fs";
4007
4221
  import { join as join14 } from "path";
4008
4222
 
4009
4223
  // src/intelligence/adapter.ts
@@ -4145,7 +4359,7 @@ var loadDocuments = (root, index, sources) => {
4145
4359
  const abs = join14(root, entry.path);
4146
4360
  let content = "";
4147
4361
  try {
4148
- content = readFileSync14(abs, "utf8");
4362
+ content = readFileSync15(abs, "utf8");
4149
4363
  } catch {
4150
4364
  content = [entry.title, entry.description].filter(Boolean).join("\n\n");
4151
4365
  }
@@ -4455,6 +4669,7 @@ export {
4455
4669
  createDocBridgeRetriever,
4456
4670
  defaultPromotionDraftPath,
4457
4671
  defineConfig,
4672
+ discoverNxProjects,
4458
4673
  docBridgePatternMarkdown,
4459
4674
  docBridgePatternPayload,
4460
4675
  doctorBadgeMetrics,