@agentskit/doc-bridge 0.1.0-alpha.3 → 1.0.1

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 (52) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +139 -57
  3. package/action.yml +78 -0
  4. package/dist/cli/program.js +1000 -70
  5. package/dist/cli/program.js.map +1 -1
  6. package/dist/index.d.ts +238 -26
  7. package/dist/index.js +717 -23
  8. package/dist/index.js.map +1 -1
  9. package/docs/DOGFOOD-ROUND3.md +74 -0
  10. package/docs/DOGFOOD-V1.md +84 -0
  11. package/docs/POSITIONING.md +2 -0
  12. package/docs/RELEASE.md +5 -3
  13. package/docs/chat-and-rag.md +2 -0
  14. package/docs/getting-started.md +14 -1
  15. package/docs/landing/index.html +299 -0
  16. package/docs/mcp.md +10 -1
  17. package/docs/ollama-demo.md +64 -0
  18. package/docs/playbook/doc-bridge-pattern.md +114 -0
  19. package/docs/recipes/index-pipeline.md +89 -0
  20. package/docs/skills/doc-bridge.md +64 -0
  21. package/docs/spec/cli.md +6 -0
  22. package/docs/spec/playbook-feedback.md +12 -4
  23. package/examples/demo-example/doc-bridge.config.json +23 -0
  24. package/examples/demo-example/docs/for-agents/INDEX.md +3 -0
  25. package/examples/demo-example/docs/for-agents/packages/example.md +14 -0
  26. package/examples/demo-example/package.json +7 -0
  27. package/examples/demo-example/src/.gitkeep +0 -0
  28. package/examples/demo-monorepo/doc-bridge.config.json +37 -0
  29. package/examples/demo-monorepo/docs/for-agents/INDEX.md +4 -0
  30. package/examples/demo-monorepo/docs/for-agents/packages/auth.md +22 -0
  31. package/examples/demo-monorepo/docs/for-agents/packages/billing.md +19 -0
  32. package/examples/demo-monorepo/docs/human/guides/auth.md +7 -0
  33. package/examples/demo-monorepo/package.json +7 -0
  34. package/examples/demo-monorepo/packages/auth/package.json +8 -0
  35. package/examples/demo-monorepo/packages/billing/package.json +7 -0
  36. package/examples/demo-monorepo/pnpm-workspace.yaml +2 -0
  37. package/examples/ollama-chat.config.ts +42 -0
  38. package/package.json +7 -4
  39. package/src/cli/demo.ts +143 -0
  40. package/src/cli/program.ts +194 -14
  41. package/src/doctor/badge.ts +53 -0
  42. package/src/doctor/run-doctor.ts +286 -0
  43. package/src/index-builder/build-handoffs.ts +19 -1
  44. package/src/index-builder/watch-index.ts +94 -0
  45. package/src/index.ts +24 -0
  46. package/src/intelligence/adapter.ts +14 -1
  47. package/src/mcp/install.ts +84 -0
  48. package/src/memory/github-pr.ts +190 -0
  49. package/src/playbook/doc-bridge-pattern.ts +121 -0
  50. package/src/query/query.ts +19 -1
  51. package/src/schemas/agent-handoff.ts +11 -0
  52. package/src/version.ts +1 -1
package/dist/index.js CHANGED
@@ -372,6 +372,11 @@ var HandoffTargetSchema = z2.object({
372
372
  group: z2.string().min(1).max(128).optional(),
373
373
  layer: z2.string().min(1).max(32).optional()
374
374
  }).strict();
375
+ var HandoffBridgeSchema = z2.object({
376
+ humanDoc: z2.enum(["linked", "missing", "external"]),
377
+ action: z2.string().min(1).max(256).optional(),
378
+ bootstrap: z2.string().min(1).max(256).optional()
379
+ }).strict();
375
380
  var AgentHandoffV1Schema = z2.object({
376
381
  type: z2.literal("agent-handoff"),
377
382
  schemaVersion: z2.literal(HANDOFF_SCHEMA_VERSION).default(HANDOFF_SCHEMA_VERSION),
@@ -382,6 +387,7 @@ var AgentHandoffV1Schema = z2.object({
382
387
  editRoots: z2.array(z2.string().min(1).max(512)).max(32),
383
388
  checks: z2.array(z2.string().min(1).max(256)).max(32),
384
389
  humanDoc: z2.string().min(1).max(512).nullable().optional(),
390
+ bridge: HandoffBridgeSchema.optional(),
385
391
  playbookPatterns: z2.array(z2.string().url()).max(16).optional(),
386
392
  notes: z2.array(z2.string().min(1).max(1024)).max(16)
387
393
  }).strict();
@@ -1111,6 +1117,11 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
1111
1117
  ...agentDoc ? { agentDoc } : {}
1112
1118
  };
1113
1119
  ownership[pkg.id] = record;
1120
+ const bridge = record.humanDoc ? /^https?:\/\//.test(record.humanDoc) ? { humanDoc: "external" } : { humanDoc: "linked" } : config.corpus.human ? {
1121
+ humanDoc: "missing",
1122
+ action: "ak-docs bootstrap agent-docs",
1123
+ bootstrap: `docs/for-agents/human/${pkg.id}.md`
1124
+ } : void 0;
1114
1125
  handoffs[pkg.id] = {
1115
1126
  type: "agent-handoff",
1116
1127
  schemaVersion: 1,
@@ -1129,7 +1140,11 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
1129
1140
  editRoots: [record.path],
1130
1141
  checks: [...record.checks],
1131
1142
  ...record.humanDoc ? { humanDoc: record.humanDoc } : {},
1132
- notes: record.purpose ? [record.purpose] : []
1143
+ ...bridge ? { bridge } : {},
1144
+ notes: [
1145
+ ...record.purpose ? [record.purpose] : [],
1146
+ ...!record.humanDoc && config.corpus.human ? [`Human guide missing for ${pkg.id}. Run: ak-docs bootstrap agent-docs`] : []
1147
+ ]
1133
1148
  };
1134
1149
  }
1135
1150
  const intents = {};
@@ -1157,7 +1172,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
1157
1172
  };
1158
1173
 
1159
1174
  // src/version.ts
1160
- var PACKAGE_VERSION = "0.1.0-alpha.3";
1175
+ var PACKAGE_VERSION = "1.0.0";
1161
1176
 
1162
1177
  // src/index-builder/capabilities.ts
1163
1178
  var renderCapabilitiesJson = (config, index, paths) => {
@@ -1256,10 +1271,10 @@ var docId = (relToHumanRoot, raw) => {
1256
1271
  return meta.package ?? meta.module ?? meta.id ?? slugFromPath(relToHumanRoot);
1257
1272
  };
1258
1273
  var routeSlug = (relToHumanRoot, opts) => relToHumanRoot.split("/").filter((part) => !opts?.stripGroups || !/^\(.+\)$/.test(part)).join("/").replace(/(?:^|\/)index\.mdx?$/, "").replace(/\.mdx?$/, "");
1259
- var humanUrl = (slug, urlPrefix) => {
1260
- if (typeof urlPrefix !== "string" || !urlPrefix) return slug;
1274
+ var humanUrl = (slug2, urlPrefix) => {
1275
+ if (typeof urlPrefix !== "string" || !urlPrefix) return slug2;
1261
1276
  const prefix = urlPrefix.replace(/\/$/, "");
1262
- return slug ? `${prefix}/${slug.replace(/^\//, "")}` : prefix;
1277
+ return slug2 ? `${prefix}/${slug2.replace(/^\//, "")}` : prefix;
1263
1278
  };
1264
1279
  var scanMarkdownDocs = (root, humanRoot, options) => {
1265
1280
  const out = [];
@@ -1290,9 +1305,9 @@ var docusaurusFileSlug = (relPath) => {
1290
1305
  return routeSlug(relPath);
1291
1306
  };
1292
1307
  var docusaurusSlug = (relPath, raw) => {
1293
- const slug = parseFrontmatter2(raw).slug;
1294
- if (!slug) return docusaurusFileSlug(relPath);
1295
- return slug.replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "");
1308
+ const slug2 = parseFrontmatter2(raw).slug;
1309
+ if (!slug2) return docusaurusFileSlug(relPath);
1310
+ return slug2.replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "");
1296
1311
  };
1297
1312
  var docusaurusSidebarId = (relPath, raw) => {
1298
1313
  const id = parseFrontmatter2(raw).id;
@@ -2062,6 +2077,11 @@ var handoffForPackage = (index, id, config) => {
2062
2077
  if (fromIndex) return normalizeAgentHandoff(fromIndex);
2063
2078
  const owner = index.lookup?.ownership?.[id];
2064
2079
  if (!owner) throw new Error(`Unknown package/ownership id "${id}". Try: ak-docs list packages`);
2080
+ const bridge = owner.humanDoc ? /^https?:\/\//.test(owner.humanDoc) ? { humanDoc: "external" } : { humanDoc: "linked" } : config.corpus.human ? {
2081
+ humanDoc: "missing",
2082
+ action: "ak-docs bootstrap agent-docs",
2083
+ bootstrap: `docs/for-agents/human/${id}.md`
2084
+ } : void 0;
2065
2085
  return normalizeAgentHandoff({
2066
2086
  type: "agent-handoff",
2067
2087
  source: config.index?.outFile ?? ".doc-bridge/index.json",
@@ -2077,7 +2097,11 @@ var handoffForPackage = (index, id, config) => {
2077
2097
  editRoots: [owner.path],
2078
2098
  checks: [...owner.checks],
2079
2099
  ...owner.humanDoc ? { humanDoc: owner.humanDoc } : {},
2080
- notes: owner.purpose ? [owner.purpose] : []
2100
+ ...bridge ? { bridge } : {},
2101
+ notes: [
2102
+ ...owner.purpose ? [owner.purpose] : [],
2103
+ ...!owner.humanDoc && config.corpus.human ? [`Human guide missing for ${id}. Run: ak-docs bootstrap agent-docs`] : []
2104
+ ]
2081
2105
  });
2082
2106
  };
2083
2107
  var runQuery = (index, config, req) => {
@@ -2378,9 +2402,531 @@ var startMcpStdioServer = (ctx) => {
2378
2402
  process.stdin.resume();
2379
2403
  };
2380
2404
 
2405
+ // src/mcp/install.ts
2406
+ import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync13, writeFileSync as writeFileSync2 } from "fs";
2407
+ import { homedir } from "os";
2408
+ import { dirname as dirname4, join as join13, resolve as resolve5 } from "path";
2409
+ var SERVER_NAME = "ak-docs";
2410
+ var mcpServerEntry = (root) => ({
2411
+ command: "npx",
2412
+ args: ["ak-docs", "mcp"],
2413
+ cwd: root
2414
+ });
2415
+ var readJson = (path) => {
2416
+ if (!existsSync9(path)) return {};
2417
+ try {
2418
+ const parsed = JSON.parse(readFileSync13(path, "utf8"));
2419
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
2420
+ } catch {
2421
+ return {};
2422
+ }
2423
+ };
2424
+ var writeJson = (path, value) => {
2425
+ mkdirSync2(dirname4(path), { recursive: true });
2426
+ writeFileSync2(path, `${JSON.stringify(value, null, 2)}
2427
+ `, "utf8");
2428
+ };
2429
+ var resolveTargetPath = (target, root) => {
2430
+ if (target === "cursor") return resolve5(root, ".cursor", "mcp.json");
2431
+ return join13(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
2432
+ };
2433
+ var installMcpConfig = (root, target) => {
2434
+ const configPath = resolveTargetPath(target, root);
2435
+ const created = !existsSync9(configPath);
2436
+ const existing = readJson(configPath);
2437
+ const servers = existing.mcpServers && typeof existing.mcpServers === "object" && !Array.isArray(existing.mcpServers) ? { ...existing.mcpServers } : {};
2438
+ servers[SERVER_NAME] = mcpServerEntry(root);
2439
+ writeJson(configPath, { ...existing, mcpServers: servers });
2440
+ const nextSteps = target === "cursor" ? [
2441
+ "Restart Cursor or reload MCP servers",
2442
+ "Paste the doc-bridge skill into Cursor rules (see docs/skills/doc-bridge.md)",
2443
+ "Before editing packages/* call handoff.resolve"
2444
+ ] : [
2445
+ "Restart Claude Desktop",
2446
+ "Run ak-docs index after doc changes"
2447
+ ];
2448
+ return {
2449
+ ok: true,
2450
+ target,
2451
+ configPath,
2452
+ created,
2453
+ serverName: SERVER_NAME,
2454
+ nextSteps
2455
+ };
2456
+ };
2457
+ var mcpSnippet = (root) => JSON.stringify({ mcpServers: { [SERVER_NAME]: mcpServerEntry(root) } }, null, 2);
2458
+
2459
+ // src/doctor/badge.ts
2460
+ var doctorBadgeMetrics = (report) => {
2461
+ const { packages } = report.coverage;
2462
+ const handoffPct = packages.total > 0 ? Math.round(packages.withAgentDoc / packages.total * 100) : 0;
2463
+ const bridgePct = packages.total > 0 ? Math.round(packages.withHumanDoc / packages.total * 100) : 0;
2464
+ return {
2465
+ handoffPct,
2466
+ bridgePct,
2467
+ score: report.score,
2468
+ grade: report.grade,
2469
+ packages: packages.total
2470
+ };
2471
+ };
2472
+ var badgeColor = (pct) => {
2473
+ if (pct >= 80) return "2ea44f";
2474
+ if (pct >= 50) return "dbab09";
2475
+ return "cb2431";
2476
+ };
2477
+ var formatDoctorBadgeMarkdown = (metrics) => {
2478
+ const handoff = `handoff_coverage-${metrics.handoffPct}%25-${badgeColor(metrics.handoffPct)}`;
2479
+ const bridge = `human_bridge-${metrics.bridgePct}%25-${badgeColor(metrics.bridgePct)}`;
2480
+ return [
2481
+ `![handoff coverage](https://img.shields.io/badge/${handoff}?style=flat-square)`,
2482
+ `![human bridge](https://img.shields.io/badge/${bridge}?style=flat-square)`,
2483
+ `![doc-bridge score](https://img.shields.io/badge/doc--bridge_score-${metrics.score}%2F100-${badgeColor(metrics.score)}?style=flat-square)`
2484
+ ].join(" ");
2485
+ };
2486
+ var formatDoctorBadgeJson = (metrics) => JSON.stringify(
2487
+ {
2488
+ schemaVersion: 1,
2489
+ ...metrics,
2490
+ markdown: formatDoctorBadgeMarkdown(metrics),
2491
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2492
+ },
2493
+ null,
2494
+ 2
2495
+ );
2496
+
2497
+ // src/doctor/run-doctor.ts
2498
+ var gradeForScore = (score) => {
2499
+ if (score >= 90) return "A";
2500
+ if (score >= 75) return "B";
2501
+ if (score >= 60) return "C";
2502
+ if (score >= 40) return "D";
2503
+ return "F";
2504
+ };
2505
+ var agentDocPaths = (index) => {
2506
+ const paths = /* @__PURE__ */ new Set();
2507
+ for (const owner of Object.values(index.lookup?.ownership ?? {})) {
2508
+ if (owner.agentDoc) paths.add(owner.agentDoc);
2509
+ }
2510
+ for (const handoff of Object.values(index.handoffs ?? {})) {
2511
+ if (handoff.startHere) paths.add(handoff.startHere);
2512
+ }
2513
+ return paths;
2514
+ };
2515
+ var computeScore = (coverage) => {
2516
+ let score = 0;
2517
+ if (coverage.freshness.hasIndex) score += 15;
2518
+ if (coverage.freshness.ok) score += 15;
2519
+ const { total, withAgentDoc, withHumanDoc } = coverage.packages;
2520
+ if (total > 0) {
2521
+ score += Math.round(withAgentDoc / total * 35);
2522
+ score += Math.round(withHumanDoc / total * 20);
2523
+ } else if (coverage.agentDocs.indexed > 0) {
2524
+ score += 35;
2525
+ }
2526
+ if (coverage.gates.ok) score += 15;
2527
+ else {
2528
+ const passed = coverage.gates.results.filter((gate) => gate.ok).length;
2529
+ const totalGates = coverage.gates.results.length || 1;
2530
+ score += Math.round(passed / totalGates * 10);
2531
+ }
2532
+ return Math.min(100, Math.max(0, score));
2533
+ };
2534
+ var buildIssues = (coverage) => {
2535
+ const issues = [];
2536
+ if (!coverage.freshness.hasIndex) {
2537
+ issues.push({
2538
+ severity: "error",
2539
+ code: "index-missing",
2540
+ message: "No doc-bridge index found.",
2541
+ action: "ak-docs index"
2542
+ });
2543
+ } else if (!coverage.freshness.ok) {
2544
+ issues.push({
2545
+ severity: "error",
2546
+ code: "index-stale",
2547
+ message: coverage.freshness.message,
2548
+ action: "ak-docs index"
2549
+ });
2550
+ }
2551
+ for (const id of coverage.packages.missingAgentDoc) {
2552
+ issues.push({
2553
+ severity: "warn",
2554
+ code: "missing-agent-doc",
2555
+ message: `Package "${id}" has no dedicated agent doc.`,
2556
+ action: `ak-docs init --scaffold-workspaces # or edit docs/for-agents/packages/${id}.md`
2557
+ });
2558
+ }
2559
+ for (const id of coverage.packages.missingHumanDoc) {
2560
+ issues.push({
2561
+ severity: "info",
2562
+ code: "missing-human-doc",
2563
+ message: `Package "${id}" has no linked human guide.`,
2564
+ action: "ak-docs bootstrap agent-docs"
2565
+ });
2566
+ }
2567
+ for (const gate of coverage.gates.results.filter((result) => !result.ok)) {
2568
+ issues.push({
2569
+ severity: gate.id === "index-freshness" ? "error" : "warn",
2570
+ code: `gate-${gate.id}`,
2571
+ message: gate.message,
2572
+ action: gate.id === "index-freshness" ? "ak-docs index" : "ak-docs gate run"
2573
+ });
2574
+ }
2575
+ return issues;
2576
+ };
2577
+ var buildNextActions = (issues, coverage) => {
2578
+ const actions = /* @__PURE__ */ new Set();
2579
+ for (const issue of issues) {
2580
+ if (issue.action) actions.add(issue.action);
2581
+ }
2582
+ if (!coverage.freshness.hasIndex || !coverage.freshness.ok) {
2583
+ actions.add("ak-docs index");
2584
+ }
2585
+ if (coverage.packages.missingHumanDoc.length) {
2586
+ actions.add("ak-docs bootstrap agent-docs");
2587
+ }
2588
+ if (coverage.packages.total > 0) {
2589
+ const sample = coverage.packages.missingAgentDoc[0] ?? coverage.packages.missingHumanDoc[0];
2590
+ if (sample) actions.add(`ak-docs query package ${sample} --agent`);
2591
+ }
2592
+ if (!actions.size) {
2593
+ actions.add("ak-docs mcp install --cursor");
2594
+ actions.add("ak-docs gate run");
2595
+ }
2596
+ return [...actions].slice(0, 6);
2597
+ };
2598
+ var runDoctor = (root, config) => {
2599
+ let index;
2600
+ let hasIndex = true;
2601
+ let freshnessOk = false;
2602
+ let freshnessMessage = "Index is fresh";
2603
+ try {
2604
+ index = loadDocBridgeIndex(root, config);
2605
+ const next = buildDocBridgeIndex({ root, config, write: false }).index.contentHash;
2606
+ freshnessOk = index.contentHash === next;
2607
+ freshnessMessage = freshnessOk ? "Index is fresh" : "Index is stale. Run: ak-docs index";
2608
+ } catch (error) {
2609
+ if (error instanceof IndexNotFoundError) {
2610
+ hasIndex = false;
2611
+ freshnessOk = false;
2612
+ freshnessMessage = error.message;
2613
+ index = buildDocBridgeIndex({ root, config, write: false }).index;
2614
+ } else {
2615
+ throw error;
2616
+ }
2617
+ }
2618
+ const ownership = Object.entries(index.lookup?.ownership ?? {});
2619
+ const missingAgentDoc = ownership.filter(([, owner]) => !owner.agentDoc || owner.agentDoc === config.corpus.agent.index).map(([id]) => id);
2620
+ const missingHumanDoc = ownership.filter(([, owner]) => !owner.humanDoc).map(([id]) => id);
2621
+ const indexedPaths = new Set(index.knowledge.map((entry) => entry.path));
2622
+ const expectedAgentDocs = agentDocPaths(index);
2623
+ const unindexed = [...expectedAgentDocs].filter((path) => !indexedPaths.has(path));
2624
+ const corpusDocs = scanAgentCorpus(root, config).filter(
2625
+ (doc) => doc.path !== config.corpus.agent.index
2626
+ );
2627
+ const gates = runGates(root, config);
2628
+ const coverage = {
2629
+ packages: {
2630
+ total: ownership.length,
2631
+ withAgentDoc: ownership.length - missingAgentDoc.length,
2632
+ withHumanDoc: ownership.length - missingHumanDoc.length,
2633
+ missingAgentDoc,
2634
+ missingHumanDoc
2635
+ },
2636
+ agentDocs: {
2637
+ total: corpusDocs.length,
2638
+ indexed: corpusDocs.filter((doc) => indexedPaths.has(doc.path)).length,
2639
+ unindexed: corpusDocs.filter((doc) => !indexedPaths.has(doc.path)).map((doc) => doc.path)
2640
+ },
2641
+ freshness: {
2642
+ ok: freshnessOk,
2643
+ message: freshnessMessage,
2644
+ hasIndex
2645
+ },
2646
+ gates
2647
+ };
2648
+ const issues = buildIssues(coverage);
2649
+ const score = computeScore(coverage);
2650
+ const nextActions = buildNextActions(issues, coverage);
2651
+ const report = {
2652
+ ok: issues.every((issue) => issue.severity !== "error") && gates.ok,
2653
+ score,
2654
+ grade: gradeForScore(score),
2655
+ coverage,
2656
+ badge: { handoffPct: 0, bridgePct: 0, score, grade: gradeForScore(score), packages: 0 },
2657
+ issues,
2658
+ nextActions
2659
+ };
2660
+ return { ...report, badge: doctorBadgeMetrics(report) };
2661
+ };
2662
+ var formatDoctorText = (report) => {
2663
+ const { coverage } = report;
2664
+ const handoffPct = coverage.packages.total > 0 ? Math.round(coverage.packages.withAgentDoc / coverage.packages.total * 100) : 0;
2665
+ const humanPct = coverage.packages.total > 0 ? Math.round(coverage.packages.withHumanDoc / coverage.packages.total * 100) : 0;
2666
+ const lines = [
2667
+ "doc-bridge doctor",
2668
+ "\u2500".repeat(40),
2669
+ `Score: ${report.score}/100 (${report.grade})`,
2670
+ "",
2671
+ "Coverage",
2672
+ ` Packages: ${coverage.packages.total}`,
2673
+ ` Agent docs: ${coverage.packages.withAgentDoc}/${coverage.packages.total} (${handoffPct}% handoff-ready)`,
2674
+ ` Human guides: ${coverage.packages.withHumanDoc}/${coverage.packages.total} (${humanPct}% bridged)`,
2675
+ ` Corpus indexed: ${coverage.agentDocs.indexed}/${coverage.agentDocs.total} agent docs`,
2676
+ ` Index freshness: ${coverage.freshness.ok ? "fresh" : "stale or missing"}`,
2677
+ ` Gates: ${coverage.gates.results.filter((g) => g.ok).length}/${coverage.gates.results.length} passing`,
2678
+ ` Badge: handoff ${report.badge.handoffPct}% \xB7 bridge ${report.badge.bridgePct}%`
2679
+ ];
2680
+ if (coverage.packages.missingHumanDoc.length) {
2681
+ lines.push("", "Missing humanDoc (bridge gap)", ...coverage.packages.missingHumanDoc.map((id) => ` \u2022 ${id}`));
2682
+ }
2683
+ if (coverage.packages.missingAgentDoc.length) {
2684
+ lines.push("", "Missing agent doc", ...coverage.packages.missingAgentDoc.map((id) => ` \u2022 ${id}`));
2685
+ }
2686
+ if (report.issues.length) {
2687
+ lines.push("", "Issues");
2688
+ for (const issue of report.issues.slice(0, 8)) {
2689
+ const icon = issue.severity === "error" ? "\u2717" : issue.severity === "warn" ? "!" : "\xB7";
2690
+ lines.push(` ${icon} [${issue.code}] ${issue.message}`);
2691
+ }
2692
+ if (report.issues.length > 8) {
2693
+ lines.push(` \u2026 +${report.issues.length - 8} more`);
2694
+ }
2695
+ }
2696
+ lines.push("", "Next actions", ...report.nextActions.map((action) => ` \u2192 ${action}`));
2697
+ return lines;
2698
+ };
2699
+
2700
+ // src/index-builder/watch-index.ts
2701
+ import { existsSync as existsSync10, watch } from "fs";
2702
+ import { dirname as dirname5, resolve as resolve6 } from "path";
2703
+ var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
2704
+ var collectWatchRoots = (root, config, configPath) => {
2705
+ const roots = /* @__PURE__ */ new Set();
2706
+ roots.add(resolve6(root, config.corpus.agent.root));
2707
+ const humanSources = config.corpus.human ? Array.isArray(config.corpus.human) ? config.corpus.human : [config.corpus.human] : [];
2708
+ for (const source of humanSources) {
2709
+ const humanOpts = source.options ?? {};
2710
+ for (const key of ["contentDir", "docsDir", "root"]) {
2711
+ const value = humanOpts[key];
2712
+ if (typeof value === "string" && value.length) roots.add(resolve6(root, value));
2713
+ }
2714
+ }
2715
+ if (configPath) roots.add(dirname5(resolve6(configPath)));
2716
+ return [...roots].filter((dir) => existsSync10(dir));
2717
+ };
2718
+ var watchDocBridgeIndex = (opts) => {
2719
+ const debounceMs = opts.debounceMs ?? 350;
2720
+ let timer;
2721
+ let running = false;
2722
+ const rebuild = () => {
2723
+ if (timer) clearTimeout(timer);
2724
+ timer = setTimeout(() => {
2725
+ if (running) return;
2726
+ running = true;
2727
+ try {
2728
+ const result = buildDocBridgeIndex({ root: opts.root, config: opts.config });
2729
+ const handoffCount = Object.keys(result.index.handoffs ?? {}).length;
2730
+ const summary = {
2731
+ knowledgeCount: result.index.knowledge.length,
2732
+ handoffCount,
2733
+ hash: result.index.contentHash.slice(0, 8)
2734
+ };
2735
+ opts.onRebuild?.(summary);
2736
+ process.stdout.write(
2737
+ `[ak-docs] indexed ${summary.knowledgeCount} docs, ${summary.handoffCount} handoffs (${summary.hash}\u2026)
2738
+ `
2739
+ );
2740
+ } catch (error) {
2741
+ process.stderr.write(
2742
+ `[ak-docs] index failed: ${error instanceof Error ? error.message : String(error)}
2743
+ `
2744
+ );
2745
+ } finally {
2746
+ running = false;
2747
+ }
2748
+ }, debounceMs);
2749
+ };
2750
+ rebuild();
2751
+ for (const dir of collectWatchRoots(opts.root, opts.config, opts.configPath)) {
2752
+ watch(dir, { recursive: true }, (_event, filename) => {
2753
+ if (!filename || !WATCH_PATTERN.test(filename)) return;
2754
+ rebuild();
2755
+ });
2756
+ }
2757
+ const configDir = resolve6(opts.root);
2758
+ if (existsSync10(configDir)) {
2759
+ watch(configDir, (_event, filename) => {
2760
+ if (!filename || !/doc-bridge\.config/.test(filename)) return;
2761
+ rebuild();
2762
+ });
2763
+ }
2764
+ process.stdout.write(
2765
+ `[ak-docs] watching ${collectWatchRoots(opts.root, opts.config, opts.configPath).join(", ") || opts.root} (Ctrl+C to stop)
2766
+ `
2767
+ );
2768
+ return new Promise((resolvePromise) => {
2769
+ const onSignal = () => resolvePromise(0);
2770
+ process.once("SIGINT", onSignal);
2771
+ process.once("SIGTERM", onSignal);
2772
+ });
2773
+ };
2774
+
2775
+ // src/memory/github-pr.ts
2776
+ import { execFileSync, spawnSync } from "child_process";
2777
+ import { existsSync as existsSync11, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
2778
+ import { join as join15 } from "path";
2779
+ var run = (cmd, args, cwd) => {
2780
+ const result = spawnSync(cmd, [...args], { cwd, encoding: "utf8" });
2781
+ const out = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
2782
+ return { ok: result.status === 0, out };
2783
+ };
2784
+ var hasGh = () => run("gh", ["--version"], process.cwd()).ok;
2785
+ var slug = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
2786
+ var defaultPromotionDraftPath = (root) => join15(root, ".doc-bridge", "drafts", `memory-promotion-${slug()}.md`);
2787
+ var writePromotionDraft = (root, draft, path) => {
2788
+ const draftPath = path ?? defaultPromotionDraftPath(root);
2789
+ mkdirSync3(join15(root, ".doc-bridge", "drafts"), { recursive: true });
2790
+ writeFileSync3(draftPath, `${draft.body}
2791
+ `, "utf8");
2792
+ return draftPath;
2793
+ };
2794
+ var promoteMemoryToGithubPr = (root, draft, options = {}) => {
2795
+ if (!draft.ok && !options.force) {
2796
+ return {
2797
+ ok: false,
2798
+ dryRun: Boolean(options.dryRun),
2799
+ draftPath: "",
2800
+ branch: "",
2801
+ commands: [],
2802
+ message: "Safety scan blocked promotion. Fix findings or pass --force to draft anyway."
2803
+ };
2804
+ }
2805
+ const branch = options.branch ?? `doc-bridge/memory-promotion-${slug()}`;
2806
+ const draftPath = writePromotionDraft(root, draft);
2807
+ const relDraft = draftPath.startsWith(`${root}/`) ? draftPath.slice(root.length + 1) : draftPath;
2808
+ const commands = [
2809
+ `git checkout -b ${branch}`,
2810
+ `git add ${relDraft}`,
2811
+ `git commit -m "draft: doc-bridge memory promotion"`,
2812
+ `git push -u origin ${branch}`,
2813
+ `gh pr create --draft --title "${draft.title}" --body-file ${relDraft}${options.base ? ` --base ${options.base}` : ""}`
2814
+ ];
2815
+ if (options.dryRun) {
2816
+ return {
2817
+ ok: true,
2818
+ dryRun: true,
2819
+ draftPath,
2820
+ branch,
2821
+ commands,
2822
+ message: `Wrote draft to ${relDraft}. Run the printed git/gh commands to open a draft PR.`
2823
+ };
2824
+ }
2825
+ if (!existsSync11(join15(root, ".git"))) {
2826
+ return {
2827
+ ok: false,
2828
+ dryRun: false,
2829
+ draftPath,
2830
+ branch,
2831
+ commands,
2832
+ message: "Not a git repository. Commit the draft manually or run with --dry-run."
2833
+ };
2834
+ }
2835
+ if (!hasGh()) {
2836
+ return {
2837
+ ok: false,
2838
+ dryRun: false,
2839
+ draftPath,
2840
+ branch,
2841
+ commands,
2842
+ message: "GitHub CLI (gh) not found. Install gh or use --dry-run for local draft + commands."
2843
+ };
2844
+ }
2845
+ const auth = run("gh", ["auth", "status"], root);
2846
+ if (!auth.ok) {
2847
+ return {
2848
+ ok: false,
2849
+ dryRun: false,
2850
+ draftPath,
2851
+ branch,
2852
+ commands,
2853
+ message: `gh is not authenticated. Run: gh auth login
2854
+ ${auth.out}`
2855
+ };
2856
+ }
2857
+ const checkout = run("git", ["checkout", "-b", branch], root);
2858
+ if (!checkout.ok) {
2859
+ return {
2860
+ ok: false,
2861
+ dryRun: false,
2862
+ draftPath,
2863
+ branch,
2864
+ commands,
2865
+ message: `git checkout failed: ${checkout.out}`
2866
+ };
2867
+ }
2868
+ run("git", ["add", relDraft], root);
2869
+ const commit = run("git", ["commit", "-m", "draft: doc-bridge memory promotion"], root);
2870
+ if (!commit.ok) {
2871
+ return {
2872
+ ok: false,
2873
+ dryRun: false,
2874
+ draftPath,
2875
+ branch,
2876
+ commands,
2877
+ message: `git commit failed: ${commit.out}`
2878
+ };
2879
+ }
2880
+ const push = run("git", ["push", "-u", "origin", branch], root);
2881
+ if (!push.ok) {
2882
+ return {
2883
+ ok: false,
2884
+ dryRun: false,
2885
+ draftPath,
2886
+ branch,
2887
+ commands,
2888
+ message: `git push failed: ${push.out}`
2889
+ };
2890
+ }
2891
+ const prArgs = [
2892
+ "pr",
2893
+ "create",
2894
+ "--draft",
2895
+ "--title",
2896
+ draft.title,
2897
+ "--body-file",
2898
+ relDraft,
2899
+ ...options.base ? ["--base", options.base] : []
2900
+ ];
2901
+ let prUrl = "";
2902
+ try {
2903
+ prUrl = execFileSync("gh", prArgs, { cwd: root, encoding: "utf8" }).trim();
2904
+ } catch (error) {
2905
+ const message = error instanceof Error ? error.message : String(error);
2906
+ return {
2907
+ ok: false,
2908
+ dryRun: false,
2909
+ draftPath,
2910
+ branch,
2911
+ commands,
2912
+ message: `gh pr create failed: ${message}`
2913
+ };
2914
+ }
2915
+ return {
2916
+ ok: true,
2917
+ dryRun: false,
2918
+ draftPath,
2919
+ branch,
2920
+ prUrl,
2921
+ ...prUrl ? { previewUrl: prUrl } : {},
2922
+ commands,
2923
+ message: prUrl ? `Draft PR opened: ${prUrl}` : "Draft PR created."
2924
+ };
2925
+ };
2926
+
2381
2927
  // src/federation/llms.ts
2382
- import { existsSync as existsSync9, readFileSync as readFileSync13 } from "fs";
2383
- import { resolve as resolve5 } from "path";
2928
+ import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
2929
+ import { resolve as resolve7 } from "path";
2384
2930
  var tokenize2 = (value) => value.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length >= 2);
2385
2931
  var scoreText = (query, text) => {
2386
2932
  const hay = text.toLowerCase();
@@ -2395,9 +2941,9 @@ var defaultFetchText = async (url) => {
2395
2941
  var sourceText = async (root, source, fetchText) => {
2396
2942
  try {
2397
2943
  if (/^https?:\/\//.test(source)) return await fetchText(source);
2398
- const path = resolve5(root, source);
2399
- if (!existsSync9(path)) return null;
2400
- return readFileSync13(path, "utf8");
2944
+ const path = resolve7(root, source);
2945
+ if (!existsSync12(path)) return null;
2946
+ return readFileSync14(path, "utf8");
2401
2947
  } catch {
2402
2948
  return null;
2403
2949
  }
@@ -2482,11 +3028,11 @@ var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
2482
3028
  };
2483
3029
 
2484
3030
  // src/intelligence/rag.ts
2485
- import { readFileSync as readFileSync14 } from "fs";
2486
- import { join as join14 } from "path";
3031
+ import { readFileSync as readFileSync15 } from "fs";
3032
+ import { join as join17 } from "path";
2487
3033
 
2488
3034
  // src/intelligence/adapter.ts
2489
- import { join as join13 } from "path";
3035
+ import { join as join16 } from "path";
2490
3036
 
2491
3037
  // src/intelligence/peers.ts
2492
3038
  var PeerMissingError = class extends Error {
@@ -2540,6 +3086,18 @@ var readEnv = (name) => {
2540
3086
  const value = process.env[name];
2541
3087
  return value && value.length > 0 ? value : void 0;
2542
3088
  };
3089
+ var providerDefaultApiKeyEnv = (provider) => {
3090
+ switch (provider) {
3091
+ case "openai":
3092
+ return "OPENAI_API_KEY";
3093
+ case "anthropic":
3094
+ return "ANTHROPIC_API_KEY";
3095
+ case "openrouter":
3096
+ return "OPENROUTER_API_KEY";
3097
+ default:
3098
+ return void 0;
3099
+ }
3100
+ };
2543
3101
  var resolveIntelligenceRuntime = async (config) => {
2544
3102
  if (!config.intelligence?.enabled) {
2545
3103
  throw new Error("Intelligence is disabled. Set intelligence.enabled: true in doc-bridge config.");
@@ -2551,7 +3109,7 @@ var resolveIntelligenceRuntime = async (config) => {
2551
3109
  const adapters = await importPeer("@agentskit/adapters");
2552
3110
  const provider = adapterCfg.provider;
2553
3111
  const model = adapterCfg.model;
2554
- const apiKey = readEnv(adapterCfg.apiKeyEnv) ?? readEnv("OPENAI_API_KEY") ?? readEnv("ANTHROPIC_API_KEY");
3112
+ const apiKey = readEnv(adapterCfg.apiKeyEnv) ?? readEnv(providerDefaultApiKeyEnv(provider));
2555
3113
  const baseUrl = adapterCfg.baseUrl;
2556
3114
  let adapter;
2557
3115
  let embed;
@@ -2601,7 +3159,7 @@ var resolveIntelligenceRuntime = async (config) => {
2601
3159
  }
2602
3160
  return { adapter, embed, provider, ...model ? { model } : {} };
2603
3161
  };
2604
- var defaultVectorStorePath = (root) => join13(root, ".doc-bridge", "vectors");
3162
+ var defaultVectorStorePath = (root) => join16(root, ".doc-bridge", "vectors");
2605
3163
 
2606
3164
  // src/intelligence/rag.ts
2607
3165
  var loadDocuments = (root, index, sources) => {
@@ -2609,10 +3167,10 @@ var loadDocuments = (root, index, sources) => {
2609
3167
  const docs = [];
2610
3168
  if (includeAgent) {
2611
3169
  for (const entry of index.knowledge) {
2612
- const abs = join14(root, entry.path);
3170
+ const abs = join17(root, entry.path);
2613
3171
  let content = "";
2614
3172
  try {
2615
- content = readFileSync14(abs, "utf8");
3173
+ content = readFileSync15(abs, "utf8");
2616
3174
  } catch {
2617
3175
  content = [entry.title, entry.description].filter(Boolean).join("\n\n");
2618
3176
  }
@@ -2630,7 +3188,7 @@ var createDocBridgeRag = async (root, config, index) => {
2630
3188
  const { embed } = await resolveIntelligenceRuntime(config);
2631
3189
  const ragMod = await importPeer("@agentskit/rag");
2632
3190
  const memoryMod = await importPeer("@agentskit/memory");
2633
- const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join14(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
3191
+ const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join17(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
2634
3192
  const store = memoryMod.fileVectorMemory({ path: storePath });
2635
3193
  const rag = ragMod.createRAG({
2636
3194
  embed,
@@ -2763,17 +3321,140 @@ var startInkChat = async (root, config, index) => {
2763
3321
  throw wrapIntelligenceError(error);
2764
3322
  }
2765
3323
  };
3324
+
3325
+ // src/playbook/doc-bridge-pattern.ts
3326
+ var DOC_BRIDGE_PATTERN_ID = "doc-bridge-pattern";
3327
+ var DOC_BRIDGE_PATTERN_META = {
3328
+ id: DOC_BRIDGE_PATTERN_ID,
3329
+ title: "Doc Bridge Pattern",
3330
+ slug: "pillars/ai-collaboration/doc-bridge-pattern",
3331
+ license: "CC-BY-4.0",
3332
+ visibility: "public",
3333
+ playbookUrl: "https://playbook.agentskit.io/patterns/doc-bridge-pattern",
3334
+ npmPackage: "@agentskit/doc-bridge",
3335
+ cli: "ak-docs"
3336
+ };
3337
+ var docBridgePatternMarkdown = () => `---
3338
+ type: pattern
3339
+ id: ${DOC_BRIDGE_PATTERN_ID}
3340
+ purpose: Route coding agents to the correct package, checks, and human docs in any monorepo.
3341
+ owner: AgentsKit
3342
+ license: CC-BY-4.0
3343
+ visibility: public
3344
+ tags: [agents, documentation, monorepo, handoff, mcp]
3345
+ ---
3346
+
3347
+ # Doc Bridge Pattern
3348
+
3349
+ **AgentHandoff for your monorepo** \u2014 deterministic routing so agents edit the right roots, run the right checks, and stay linked to human documentation.
3350
+
3351
+ ## Problem
3352
+
3353
+ Coding agents guess package ownership. They edit sibling modules, run repo-wide tests, and ship changes without a bridge to human-facing guides. Wikis and RAG explain concepts but weakly answer *where to act*.
3354
+
3355
+ ## Solution (three artifacts)
3356
+
3357
+ | Artifact | Role |
3358
+ |----------|------|
3359
+ | **AgentHandoff** | JSON handoff: \`startHere\`, \`editRoots\`, \`checks\`, \`humanDoc\`, \`bridge\` |
3360
+ | **DocBridgeIndex** | Deterministic index + \`contentHash\` for CI freshness gates |
3361
+ | **Self-describe** | \`llms.txt\`, \`capabilities.json\` for discovery |
3362
+
3363
+ ## Four loops
3364
+
3365
+ | Loop | Command | Outcome |
3366
+ |------|---------|---------|
3367
+ | **Act** | \`ak-docs query package <id> --agent\` | Agent edits only \`editRoots\` |
3368
+ | **Bridge** | \`ak-docs bootstrap agent-docs\` | Link agent corpus \u2194 human site |
3369
+ | **Learn** | \`ak-docs memory promote --pr\` | HITL draft PR from agent memory |
3370
+ | **Explain** | \`ak-docs ask "<question>"\` | Local consult + handoff preview |
3371
+
3372
+ ## 60-second proof
3373
+
3374
+ \`\`\`bash
3375
+ npm i -D @agentskit/doc-bridge
3376
+ npx ak-docs demo --text
3377
+ ak-docs mcp install --cursor
3378
+ \`\`\`
3379
+
3380
+ ## AgentHandoff example
3381
+
3382
+ \`\`\`json
3383
+ {
3384
+ "type": "agent-handoff",
3385
+ "startHere": "docs/for-agents/packages/auth.md",
3386
+ "editRoots": ["packages/auth"],
3387
+ "checks": ["pnpm --filter @demo/auth test"],
3388
+ "humanDoc": "/docs/guides/auth",
3389
+ "bridge": { "humanDoc": "linked" }
3390
+ }
3391
+ \`\`\`
3392
+
3393
+ When \`humanDoc\` is missing, handoffs surface \`bridge.action: "ak-docs bootstrap agent-docs"\` \u2014 a feature, not a silent gap.
3394
+
3395
+ ## MCP contract
3396
+
3397
+ Agents call \`handoff.resolve\` before editing \`packages/*\`:
3398
+
3399
+ 1. Read \`startHere\`
3400
+ 2. Stay inside \`editRoots\`
3401
+ 3. Run every \`checks\` command
3402
+ 4. Link \`humanDoc\` or escalate missing bridge
3403
+
3404
+ ## CI gate
3405
+
3406
+ \`\`\`yaml
3407
+ - uses: AgentsKit-io/doc-bridge@v1.0.0
3408
+ \`\`\`
3409
+
3410
+ Or: \`ak-docs index && ak-docs gate run\` \u2014 stale index fails the PR.
3411
+
3412
+ ## Coverage metric
3413
+
3414
+ \`\`\`bash
3415
+ ak-docs doctor --text
3416
+ ak-docs doctor --badge
3417
+ \`\`\`
3418
+
3419
+ Teams track handoff % and human-bridge % daily.
3420
+
3421
+ ## When to use
3422
+
3423
+ - pnpm/npm monorepos with real package ownership
3424
+ - Fumadocus / Docusaurus human sites + dense agent corpus
3425
+ - Cursor / Claude / Codex agents that should not guess edit roots
3426
+
3427
+ ## When not to use
3428
+
3429
+ - Single-file repos with no ownership boundaries (AGENTS.md may suffice)
3430
+ - Hosted doc chat as primary product (doc-bridge is routing + bridge, not SaaS chat)
3431
+
3432
+ ## References
3433
+
3434
+ - npm: https://www.npmjs.com/package/@agentskit/doc-bridge
3435
+ - repo: https://github.com/AgentsKit-io/doc-bridge
3436
+ - skill: https://github.com/AgentsKit-io/doc-bridge/blob/master/docs/skills/doc-bridge.md
3437
+ - landing: https://agentskit-io.github.io/doc-bridge/
3438
+ `;
3439
+ var docBridgePatternPayload = () => ({
3440
+ ...DOC_BRIDGE_PATTERN_META,
3441
+ format: "okf-pattern-v1",
3442
+ body: docBridgePatternMarkdown()
3443
+ });
2766
3444
  export {
2767
3445
  AgentHandoffLegacySchema,
2768
3446
  AgentHandoffV1JsonSchema,
2769
3447
  AgentHandoffV1Schema,
2770
3448
  AgentSearchV1Schema,
2771
3449
  ConfigNotFoundError,
3450
+ DOC_BRIDGE_PATTERN_ID,
3451
+ DOC_BRIDGE_PATTERN_META,
2772
3452
  DocBridgeConfigV1Schema,
2773
3453
  DocBridgeIndexV1JsonSchema,
2774
3454
  DocBridgeIndexV1Schema,
2775
3455
  DocBridgeJsonSchemas,
2776
3456
  HANDOFF_SCHEMA_VERSION,
3457
+ HandoffBridgeSchema,
2777
3458
  HandoffTargetTypeSchema,
2778
3459
  INDEX_SCHEMA_VERSION,
2779
3460
  IndexNotFoundError,
@@ -2792,17 +3473,26 @@ export {
2792
3473
  collectPackages,
2793
3474
  createDocBridgeRag,
2794
3475
  createDocBridgeRetriever,
3476
+ defaultPromotionDraftPath,
2795
3477
  defineConfig,
3478
+ docBridgePatternMarkdown,
3479
+ docBridgePatternPayload,
3480
+ doctorBadgeMetrics,
2796
3481
  draftMemoryPromotion,
3482
+ formatDoctorBadgeJson,
3483
+ formatDoctorBadgeMarkdown,
3484
+ formatDoctorText,
2797
3485
  handleMcpRequest,
2798
3486
  indexFilePath,
2799
3487
  ingestAgentMemory,
2800
3488
  ingestCursorRules,
2801
3489
  ingestMemoryCandidates,
3490
+ installMcpConfig,
2802
3491
  layer1InstallHint,
2803
3492
  loadConfig,
2804
3493
  loadDocBridgeIndex,
2805
3494
  loadFederatedChunks,
3495
+ mcpSnippet,
2806
3496
  normalizeAgentHandoff,
2807
3497
  parseAgentHandoff,
2808
3498
  parseAgentSearch,
@@ -2811,12 +3501,14 @@ export {
2811
3501
  parseLlmsTxtLinks,
2812
3502
  parseMemoryCandidate,
2813
3503
  projectRootFromConfigPath,
3504
+ promoteMemoryToGithubPr,
2814
3505
  resolveGateIds,
2815
3506
  resolveProjectRoot,
2816
3507
  resolveRoot,
2817
3508
  retrieveDocBridgeChunks,
2818
3509
  retrieveHybridChunks,
2819
3510
  runChatOnce,
3511
+ runDoctor,
2820
3512
  runGate,
2821
3513
  runGates,
2822
3514
  runQuery,
@@ -2827,6 +3519,8 @@ export {
2827
3519
  searchIndex,
2828
3520
  sha256NormalizedV1,
2829
3521
  startInkChat,
2830
- startMcpStdioServer
3522
+ startMcpStdioServer,
3523
+ watchDocBridgeIndex,
3524
+ writePromotionDraft
2831
3525
  };
2832
3526
  //# sourceMappingURL=index.js.map