@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.
- package/CHANGELOG.md +46 -0
- package/README.md +139 -57
- package/action.yml +78 -0
- package/dist/cli/program.js +1000 -70
- package/dist/cli/program.js.map +1 -1
- package/dist/index.d.ts +238 -26
- package/dist/index.js +717 -23
- package/dist/index.js.map +1 -1
- package/docs/DOGFOOD-ROUND3.md +74 -0
- package/docs/DOGFOOD-V1.md +84 -0
- package/docs/POSITIONING.md +2 -0
- package/docs/RELEASE.md +5 -3
- package/docs/chat-and-rag.md +2 -0
- package/docs/getting-started.md +14 -1
- package/docs/landing/index.html +299 -0
- package/docs/mcp.md +10 -1
- package/docs/ollama-demo.md +64 -0
- package/docs/playbook/doc-bridge-pattern.md +114 -0
- package/docs/recipes/index-pipeline.md +89 -0
- package/docs/skills/doc-bridge.md +64 -0
- package/docs/spec/cli.md +6 -0
- package/docs/spec/playbook-feedback.md +12 -4
- package/examples/demo-example/doc-bridge.config.json +23 -0
- package/examples/demo-example/docs/for-agents/INDEX.md +3 -0
- package/examples/demo-example/docs/for-agents/packages/example.md +14 -0
- package/examples/demo-example/package.json +7 -0
- package/examples/demo-example/src/.gitkeep +0 -0
- package/examples/demo-monorepo/doc-bridge.config.json +37 -0
- package/examples/demo-monorepo/docs/for-agents/INDEX.md +4 -0
- package/examples/demo-monorepo/docs/for-agents/packages/auth.md +22 -0
- package/examples/demo-monorepo/docs/for-agents/packages/billing.md +19 -0
- package/examples/demo-monorepo/docs/human/guides/auth.md +7 -0
- package/examples/demo-monorepo/package.json +7 -0
- package/examples/demo-monorepo/packages/auth/package.json +8 -0
- package/examples/demo-monorepo/packages/billing/package.json +7 -0
- package/examples/demo-monorepo/pnpm-workspace.yaml +2 -0
- package/examples/ollama-chat.config.ts +42 -0
- package/package.json +7 -4
- package/src/cli/demo.ts +143 -0
- package/src/cli/program.ts +194 -14
- package/src/doctor/badge.ts +53 -0
- package/src/doctor/run-doctor.ts +286 -0
- package/src/index-builder/build-handoffs.ts +19 -1
- package/src/index-builder/watch-index.ts +94 -0
- package/src/index.ts +24 -0
- package/src/intelligence/adapter.ts +14 -1
- package/src/mcp/install.ts +84 -0
- package/src/memory/github-pr.ts +190 -0
- package/src/playbook/doc-bridge-pattern.ts +121 -0
- package/src/query/query.ts +19 -1
- package/src/schemas/agent-handoff.ts +11 -0
- 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
|
-
|
|
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 = "
|
|
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 = (
|
|
1260
|
-
if (typeof urlPrefix !== "string" || !urlPrefix) return
|
|
1274
|
+
var humanUrl = (slug2, urlPrefix) => {
|
|
1275
|
+
if (typeof urlPrefix !== "string" || !urlPrefix) return slug2;
|
|
1261
1276
|
const prefix = urlPrefix.replace(/\/$/, "");
|
|
1262
|
-
return
|
|
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
|
|
1294
|
-
if (!
|
|
1295
|
-
return
|
|
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
|
-
|
|
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
|
+
``,
|
|
2482
|
+
``,
|
|
2483
|
+
`}?style=flat-square)`
|
|
2484
|
+
].join(" ");
|
|
2485
|
+
};
|
|
2486
|
+
var formatDoctorBadgeJson = (metrics) => JSON.stringify(
|
|
2487
|
+
{
|
|
2488
|
+
schemaVersion: 1,
|
|
2489
|
+
...metrics,
|
|
2490
|
+
markdown: formatDoctorBadgeMarkdown(metrics),
|
|
2491
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2492
|
+
},
|
|
2493
|
+
null,
|
|
2494
|
+
2
|
|
2495
|
+
);
|
|
2496
|
+
|
|
2497
|
+
// src/doctor/run-doctor.ts
|
|
2498
|
+
var gradeForScore = (score) => {
|
|
2499
|
+
if (score >= 90) return "A";
|
|
2500
|
+
if (score >= 75) return "B";
|
|
2501
|
+
if (score >= 60) return "C";
|
|
2502
|
+
if (score >= 40) return "D";
|
|
2503
|
+
return "F";
|
|
2504
|
+
};
|
|
2505
|
+
var agentDocPaths = (index) => {
|
|
2506
|
+
const paths = /* @__PURE__ */ new Set();
|
|
2507
|
+
for (const owner of Object.values(index.lookup?.ownership ?? {})) {
|
|
2508
|
+
if (owner.agentDoc) paths.add(owner.agentDoc);
|
|
2509
|
+
}
|
|
2510
|
+
for (const handoff of Object.values(index.handoffs ?? {})) {
|
|
2511
|
+
if (handoff.startHere) paths.add(handoff.startHere);
|
|
2512
|
+
}
|
|
2513
|
+
return paths;
|
|
2514
|
+
};
|
|
2515
|
+
var computeScore = (coverage) => {
|
|
2516
|
+
let score = 0;
|
|
2517
|
+
if (coverage.freshness.hasIndex) score += 15;
|
|
2518
|
+
if (coverage.freshness.ok) score += 15;
|
|
2519
|
+
const { total, withAgentDoc, withHumanDoc } = coverage.packages;
|
|
2520
|
+
if (total > 0) {
|
|
2521
|
+
score += Math.round(withAgentDoc / total * 35);
|
|
2522
|
+
score += Math.round(withHumanDoc / total * 20);
|
|
2523
|
+
} else if (coverage.agentDocs.indexed > 0) {
|
|
2524
|
+
score += 35;
|
|
2525
|
+
}
|
|
2526
|
+
if (coverage.gates.ok) score += 15;
|
|
2527
|
+
else {
|
|
2528
|
+
const passed = coverage.gates.results.filter((gate) => gate.ok).length;
|
|
2529
|
+
const totalGates = coverage.gates.results.length || 1;
|
|
2530
|
+
score += Math.round(passed / totalGates * 10);
|
|
2531
|
+
}
|
|
2532
|
+
return Math.min(100, Math.max(0, score));
|
|
2533
|
+
};
|
|
2534
|
+
var buildIssues = (coverage) => {
|
|
2535
|
+
const issues = [];
|
|
2536
|
+
if (!coverage.freshness.hasIndex) {
|
|
2537
|
+
issues.push({
|
|
2538
|
+
severity: "error",
|
|
2539
|
+
code: "index-missing",
|
|
2540
|
+
message: "No doc-bridge index found.",
|
|
2541
|
+
action: "ak-docs index"
|
|
2542
|
+
});
|
|
2543
|
+
} else if (!coverage.freshness.ok) {
|
|
2544
|
+
issues.push({
|
|
2545
|
+
severity: "error",
|
|
2546
|
+
code: "index-stale",
|
|
2547
|
+
message: coverage.freshness.message,
|
|
2548
|
+
action: "ak-docs index"
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
for (const id of coverage.packages.missingAgentDoc) {
|
|
2552
|
+
issues.push({
|
|
2553
|
+
severity: "warn",
|
|
2554
|
+
code: "missing-agent-doc",
|
|
2555
|
+
message: `Package "${id}" has no dedicated agent doc.`,
|
|
2556
|
+
action: `ak-docs init --scaffold-workspaces # or edit docs/for-agents/packages/${id}.md`
|
|
2557
|
+
});
|
|
2558
|
+
}
|
|
2559
|
+
for (const id of coverage.packages.missingHumanDoc) {
|
|
2560
|
+
issues.push({
|
|
2561
|
+
severity: "info",
|
|
2562
|
+
code: "missing-human-doc",
|
|
2563
|
+
message: `Package "${id}" has no linked human guide.`,
|
|
2564
|
+
action: "ak-docs bootstrap agent-docs"
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
for (const gate of coverage.gates.results.filter((result) => !result.ok)) {
|
|
2568
|
+
issues.push({
|
|
2569
|
+
severity: gate.id === "index-freshness" ? "error" : "warn",
|
|
2570
|
+
code: `gate-${gate.id}`,
|
|
2571
|
+
message: gate.message,
|
|
2572
|
+
action: gate.id === "index-freshness" ? "ak-docs index" : "ak-docs gate run"
|
|
2573
|
+
});
|
|
2574
|
+
}
|
|
2575
|
+
return issues;
|
|
2576
|
+
};
|
|
2577
|
+
var buildNextActions = (issues, coverage) => {
|
|
2578
|
+
const actions = /* @__PURE__ */ new Set();
|
|
2579
|
+
for (const issue of issues) {
|
|
2580
|
+
if (issue.action) actions.add(issue.action);
|
|
2581
|
+
}
|
|
2582
|
+
if (!coverage.freshness.hasIndex || !coverage.freshness.ok) {
|
|
2583
|
+
actions.add("ak-docs index");
|
|
2584
|
+
}
|
|
2585
|
+
if (coverage.packages.missingHumanDoc.length) {
|
|
2586
|
+
actions.add("ak-docs bootstrap agent-docs");
|
|
2587
|
+
}
|
|
2588
|
+
if (coverage.packages.total > 0) {
|
|
2589
|
+
const sample = coverage.packages.missingAgentDoc[0] ?? coverage.packages.missingHumanDoc[0];
|
|
2590
|
+
if (sample) actions.add(`ak-docs query package ${sample} --agent`);
|
|
2591
|
+
}
|
|
2592
|
+
if (!actions.size) {
|
|
2593
|
+
actions.add("ak-docs mcp install --cursor");
|
|
2594
|
+
actions.add("ak-docs gate run");
|
|
2595
|
+
}
|
|
2596
|
+
return [...actions].slice(0, 6);
|
|
2597
|
+
};
|
|
2598
|
+
var runDoctor = (root, config) => {
|
|
2599
|
+
let index;
|
|
2600
|
+
let hasIndex = true;
|
|
2601
|
+
let freshnessOk = false;
|
|
2602
|
+
let freshnessMessage = "Index is fresh";
|
|
2603
|
+
try {
|
|
2604
|
+
index = loadDocBridgeIndex(root, config);
|
|
2605
|
+
const next = buildDocBridgeIndex({ root, config, write: false }).index.contentHash;
|
|
2606
|
+
freshnessOk = index.contentHash === next;
|
|
2607
|
+
freshnessMessage = freshnessOk ? "Index is fresh" : "Index is stale. Run: ak-docs index";
|
|
2608
|
+
} catch (error) {
|
|
2609
|
+
if (error instanceof IndexNotFoundError) {
|
|
2610
|
+
hasIndex = false;
|
|
2611
|
+
freshnessOk = false;
|
|
2612
|
+
freshnessMessage = error.message;
|
|
2613
|
+
index = buildDocBridgeIndex({ root, config, write: false }).index;
|
|
2614
|
+
} else {
|
|
2615
|
+
throw error;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
const ownership = Object.entries(index.lookup?.ownership ?? {});
|
|
2619
|
+
const missingAgentDoc = ownership.filter(([, owner]) => !owner.agentDoc || owner.agentDoc === config.corpus.agent.index).map(([id]) => id);
|
|
2620
|
+
const missingHumanDoc = ownership.filter(([, owner]) => !owner.humanDoc).map(([id]) => id);
|
|
2621
|
+
const indexedPaths = new Set(index.knowledge.map((entry) => entry.path));
|
|
2622
|
+
const expectedAgentDocs = agentDocPaths(index);
|
|
2623
|
+
const unindexed = [...expectedAgentDocs].filter((path) => !indexedPaths.has(path));
|
|
2624
|
+
const corpusDocs = scanAgentCorpus(root, config).filter(
|
|
2625
|
+
(doc) => doc.path !== config.corpus.agent.index
|
|
2626
|
+
);
|
|
2627
|
+
const gates = runGates(root, config);
|
|
2628
|
+
const coverage = {
|
|
2629
|
+
packages: {
|
|
2630
|
+
total: ownership.length,
|
|
2631
|
+
withAgentDoc: ownership.length - missingAgentDoc.length,
|
|
2632
|
+
withHumanDoc: ownership.length - missingHumanDoc.length,
|
|
2633
|
+
missingAgentDoc,
|
|
2634
|
+
missingHumanDoc
|
|
2635
|
+
},
|
|
2636
|
+
agentDocs: {
|
|
2637
|
+
total: corpusDocs.length,
|
|
2638
|
+
indexed: corpusDocs.filter((doc) => indexedPaths.has(doc.path)).length,
|
|
2639
|
+
unindexed: corpusDocs.filter((doc) => !indexedPaths.has(doc.path)).map((doc) => doc.path)
|
|
2640
|
+
},
|
|
2641
|
+
freshness: {
|
|
2642
|
+
ok: freshnessOk,
|
|
2643
|
+
message: freshnessMessage,
|
|
2644
|
+
hasIndex
|
|
2645
|
+
},
|
|
2646
|
+
gates
|
|
2647
|
+
};
|
|
2648
|
+
const issues = buildIssues(coverage);
|
|
2649
|
+
const score = computeScore(coverage);
|
|
2650
|
+
const nextActions = buildNextActions(issues, coverage);
|
|
2651
|
+
const report = {
|
|
2652
|
+
ok: issues.every((issue) => issue.severity !== "error") && gates.ok,
|
|
2653
|
+
score,
|
|
2654
|
+
grade: gradeForScore(score),
|
|
2655
|
+
coverage,
|
|
2656
|
+
badge: { handoffPct: 0, bridgePct: 0, score, grade: gradeForScore(score), packages: 0 },
|
|
2657
|
+
issues,
|
|
2658
|
+
nextActions
|
|
2659
|
+
};
|
|
2660
|
+
return { ...report, badge: doctorBadgeMetrics(report) };
|
|
2661
|
+
};
|
|
2662
|
+
var formatDoctorText = (report) => {
|
|
2663
|
+
const { coverage } = report;
|
|
2664
|
+
const handoffPct = coverage.packages.total > 0 ? Math.round(coverage.packages.withAgentDoc / coverage.packages.total * 100) : 0;
|
|
2665
|
+
const humanPct = coverage.packages.total > 0 ? Math.round(coverage.packages.withHumanDoc / coverage.packages.total * 100) : 0;
|
|
2666
|
+
const lines = [
|
|
2667
|
+
"doc-bridge doctor",
|
|
2668
|
+
"\u2500".repeat(40),
|
|
2669
|
+
`Score: ${report.score}/100 (${report.grade})`,
|
|
2670
|
+
"",
|
|
2671
|
+
"Coverage",
|
|
2672
|
+
` Packages: ${coverage.packages.total}`,
|
|
2673
|
+
` Agent docs: ${coverage.packages.withAgentDoc}/${coverage.packages.total} (${handoffPct}% handoff-ready)`,
|
|
2674
|
+
` Human guides: ${coverage.packages.withHumanDoc}/${coverage.packages.total} (${humanPct}% bridged)`,
|
|
2675
|
+
` Corpus indexed: ${coverage.agentDocs.indexed}/${coverage.agentDocs.total} agent docs`,
|
|
2676
|
+
` Index freshness: ${coverage.freshness.ok ? "fresh" : "stale or missing"}`,
|
|
2677
|
+
` Gates: ${coverage.gates.results.filter((g) => g.ok).length}/${coverage.gates.results.length} passing`,
|
|
2678
|
+
` Badge: handoff ${report.badge.handoffPct}% \xB7 bridge ${report.badge.bridgePct}%`
|
|
2679
|
+
];
|
|
2680
|
+
if (coverage.packages.missingHumanDoc.length) {
|
|
2681
|
+
lines.push("", "Missing humanDoc (bridge gap)", ...coverage.packages.missingHumanDoc.map((id) => ` \u2022 ${id}`));
|
|
2682
|
+
}
|
|
2683
|
+
if (coverage.packages.missingAgentDoc.length) {
|
|
2684
|
+
lines.push("", "Missing agent doc", ...coverage.packages.missingAgentDoc.map((id) => ` \u2022 ${id}`));
|
|
2685
|
+
}
|
|
2686
|
+
if (report.issues.length) {
|
|
2687
|
+
lines.push("", "Issues");
|
|
2688
|
+
for (const issue of report.issues.slice(0, 8)) {
|
|
2689
|
+
const icon = issue.severity === "error" ? "\u2717" : issue.severity === "warn" ? "!" : "\xB7";
|
|
2690
|
+
lines.push(` ${icon} [${issue.code}] ${issue.message}`);
|
|
2691
|
+
}
|
|
2692
|
+
if (report.issues.length > 8) {
|
|
2693
|
+
lines.push(` \u2026 +${report.issues.length - 8} more`);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
lines.push("", "Next actions", ...report.nextActions.map((action) => ` \u2192 ${action}`));
|
|
2697
|
+
return lines;
|
|
2698
|
+
};
|
|
2699
|
+
|
|
2700
|
+
// src/index-builder/watch-index.ts
|
|
2701
|
+
import { existsSync as existsSync10, watch } from "fs";
|
|
2702
|
+
import { dirname as dirname5, resolve as resolve6 } from "path";
|
|
2703
|
+
var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
|
|
2704
|
+
var collectWatchRoots = (root, config, configPath) => {
|
|
2705
|
+
const roots = /* @__PURE__ */ new Set();
|
|
2706
|
+
roots.add(resolve6(root, config.corpus.agent.root));
|
|
2707
|
+
const humanSources = config.corpus.human ? Array.isArray(config.corpus.human) ? config.corpus.human : [config.corpus.human] : [];
|
|
2708
|
+
for (const source of humanSources) {
|
|
2709
|
+
const humanOpts = source.options ?? {};
|
|
2710
|
+
for (const key of ["contentDir", "docsDir", "root"]) {
|
|
2711
|
+
const value = humanOpts[key];
|
|
2712
|
+
if (typeof value === "string" && value.length) roots.add(resolve6(root, value));
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
if (configPath) roots.add(dirname5(resolve6(configPath)));
|
|
2716
|
+
return [...roots].filter((dir) => existsSync10(dir));
|
|
2717
|
+
};
|
|
2718
|
+
var watchDocBridgeIndex = (opts) => {
|
|
2719
|
+
const debounceMs = opts.debounceMs ?? 350;
|
|
2720
|
+
let timer;
|
|
2721
|
+
let running = false;
|
|
2722
|
+
const rebuild = () => {
|
|
2723
|
+
if (timer) clearTimeout(timer);
|
|
2724
|
+
timer = setTimeout(() => {
|
|
2725
|
+
if (running) return;
|
|
2726
|
+
running = true;
|
|
2727
|
+
try {
|
|
2728
|
+
const result = buildDocBridgeIndex({ root: opts.root, config: opts.config });
|
|
2729
|
+
const handoffCount = Object.keys(result.index.handoffs ?? {}).length;
|
|
2730
|
+
const summary = {
|
|
2731
|
+
knowledgeCount: result.index.knowledge.length,
|
|
2732
|
+
handoffCount,
|
|
2733
|
+
hash: result.index.contentHash.slice(0, 8)
|
|
2734
|
+
};
|
|
2735
|
+
opts.onRebuild?.(summary);
|
|
2736
|
+
process.stdout.write(
|
|
2737
|
+
`[ak-docs] indexed ${summary.knowledgeCount} docs, ${summary.handoffCount} handoffs (${summary.hash}\u2026)
|
|
2738
|
+
`
|
|
2739
|
+
);
|
|
2740
|
+
} catch (error) {
|
|
2741
|
+
process.stderr.write(
|
|
2742
|
+
`[ak-docs] index failed: ${error instanceof Error ? error.message : String(error)}
|
|
2743
|
+
`
|
|
2744
|
+
);
|
|
2745
|
+
} finally {
|
|
2746
|
+
running = false;
|
|
2747
|
+
}
|
|
2748
|
+
}, debounceMs);
|
|
2749
|
+
};
|
|
2750
|
+
rebuild();
|
|
2751
|
+
for (const dir of collectWatchRoots(opts.root, opts.config, opts.configPath)) {
|
|
2752
|
+
watch(dir, { recursive: true }, (_event, filename) => {
|
|
2753
|
+
if (!filename || !WATCH_PATTERN.test(filename)) return;
|
|
2754
|
+
rebuild();
|
|
2755
|
+
});
|
|
2756
|
+
}
|
|
2757
|
+
const configDir = resolve6(opts.root);
|
|
2758
|
+
if (existsSync10(configDir)) {
|
|
2759
|
+
watch(configDir, (_event, filename) => {
|
|
2760
|
+
if (!filename || !/doc-bridge\.config/.test(filename)) return;
|
|
2761
|
+
rebuild();
|
|
2762
|
+
});
|
|
2763
|
+
}
|
|
2764
|
+
process.stdout.write(
|
|
2765
|
+
`[ak-docs] watching ${collectWatchRoots(opts.root, opts.config, opts.configPath).join(", ") || opts.root} (Ctrl+C to stop)
|
|
2766
|
+
`
|
|
2767
|
+
);
|
|
2768
|
+
return new Promise((resolvePromise) => {
|
|
2769
|
+
const onSignal = () => resolvePromise(0);
|
|
2770
|
+
process.once("SIGINT", onSignal);
|
|
2771
|
+
process.once("SIGTERM", onSignal);
|
|
2772
|
+
});
|
|
2773
|
+
};
|
|
2774
|
+
|
|
2775
|
+
// src/memory/github-pr.ts
|
|
2776
|
+
import { execFileSync, spawnSync } from "child_process";
|
|
2777
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
2778
|
+
import { join as join15 } from "path";
|
|
2779
|
+
var run = (cmd, args, cwd) => {
|
|
2780
|
+
const result = spawnSync(cmd, [...args], { cwd, encoding: "utf8" });
|
|
2781
|
+
const out = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
2782
|
+
return { ok: result.status === 0, out };
|
|
2783
|
+
};
|
|
2784
|
+
var hasGh = () => run("gh", ["--version"], process.cwd()).ok;
|
|
2785
|
+
var slug = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
2786
|
+
var defaultPromotionDraftPath = (root) => join15(root, ".doc-bridge", "drafts", `memory-promotion-${slug()}.md`);
|
|
2787
|
+
var writePromotionDraft = (root, draft, path) => {
|
|
2788
|
+
const draftPath = path ?? defaultPromotionDraftPath(root);
|
|
2789
|
+
mkdirSync3(join15(root, ".doc-bridge", "drafts"), { recursive: true });
|
|
2790
|
+
writeFileSync3(draftPath, `${draft.body}
|
|
2791
|
+
`, "utf8");
|
|
2792
|
+
return draftPath;
|
|
2793
|
+
};
|
|
2794
|
+
var promoteMemoryToGithubPr = (root, draft, options = {}) => {
|
|
2795
|
+
if (!draft.ok && !options.force) {
|
|
2796
|
+
return {
|
|
2797
|
+
ok: false,
|
|
2798
|
+
dryRun: Boolean(options.dryRun),
|
|
2799
|
+
draftPath: "",
|
|
2800
|
+
branch: "",
|
|
2801
|
+
commands: [],
|
|
2802
|
+
message: "Safety scan blocked promotion. Fix findings or pass --force to draft anyway."
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
const branch = options.branch ?? `doc-bridge/memory-promotion-${slug()}`;
|
|
2806
|
+
const draftPath = writePromotionDraft(root, draft);
|
|
2807
|
+
const relDraft = draftPath.startsWith(`${root}/`) ? draftPath.slice(root.length + 1) : draftPath;
|
|
2808
|
+
const commands = [
|
|
2809
|
+
`git checkout -b ${branch}`,
|
|
2810
|
+
`git add ${relDraft}`,
|
|
2811
|
+
`git commit -m "draft: doc-bridge memory promotion"`,
|
|
2812
|
+
`git push -u origin ${branch}`,
|
|
2813
|
+
`gh pr create --draft --title "${draft.title}" --body-file ${relDraft}${options.base ? ` --base ${options.base}` : ""}`
|
|
2814
|
+
];
|
|
2815
|
+
if (options.dryRun) {
|
|
2816
|
+
return {
|
|
2817
|
+
ok: true,
|
|
2818
|
+
dryRun: true,
|
|
2819
|
+
draftPath,
|
|
2820
|
+
branch,
|
|
2821
|
+
commands,
|
|
2822
|
+
message: `Wrote draft to ${relDraft}. Run the printed git/gh commands to open a draft PR.`
|
|
2823
|
+
};
|
|
2824
|
+
}
|
|
2825
|
+
if (!existsSync11(join15(root, ".git"))) {
|
|
2826
|
+
return {
|
|
2827
|
+
ok: false,
|
|
2828
|
+
dryRun: false,
|
|
2829
|
+
draftPath,
|
|
2830
|
+
branch,
|
|
2831
|
+
commands,
|
|
2832
|
+
message: "Not a git repository. Commit the draft manually or run with --dry-run."
|
|
2833
|
+
};
|
|
2834
|
+
}
|
|
2835
|
+
if (!hasGh()) {
|
|
2836
|
+
return {
|
|
2837
|
+
ok: false,
|
|
2838
|
+
dryRun: false,
|
|
2839
|
+
draftPath,
|
|
2840
|
+
branch,
|
|
2841
|
+
commands,
|
|
2842
|
+
message: "GitHub CLI (gh) not found. Install gh or use --dry-run for local draft + commands."
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
const auth = run("gh", ["auth", "status"], root);
|
|
2846
|
+
if (!auth.ok) {
|
|
2847
|
+
return {
|
|
2848
|
+
ok: false,
|
|
2849
|
+
dryRun: false,
|
|
2850
|
+
draftPath,
|
|
2851
|
+
branch,
|
|
2852
|
+
commands,
|
|
2853
|
+
message: `gh is not authenticated. Run: gh auth login
|
|
2854
|
+
${auth.out}`
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2857
|
+
const checkout = run("git", ["checkout", "-b", branch], root);
|
|
2858
|
+
if (!checkout.ok) {
|
|
2859
|
+
return {
|
|
2860
|
+
ok: false,
|
|
2861
|
+
dryRun: false,
|
|
2862
|
+
draftPath,
|
|
2863
|
+
branch,
|
|
2864
|
+
commands,
|
|
2865
|
+
message: `git checkout failed: ${checkout.out}`
|
|
2866
|
+
};
|
|
2867
|
+
}
|
|
2868
|
+
run("git", ["add", relDraft], root);
|
|
2869
|
+
const commit = run("git", ["commit", "-m", "draft: doc-bridge memory promotion"], root);
|
|
2870
|
+
if (!commit.ok) {
|
|
2871
|
+
return {
|
|
2872
|
+
ok: false,
|
|
2873
|
+
dryRun: false,
|
|
2874
|
+
draftPath,
|
|
2875
|
+
branch,
|
|
2876
|
+
commands,
|
|
2877
|
+
message: `git commit failed: ${commit.out}`
|
|
2878
|
+
};
|
|
2879
|
+
}
|
|
2880
|
+
const push = run("git", ["push", "-u", "origin", branch], root);
|
|
2881
|
+
if (!push.ok) {
|
|
2882
|
+
return {
|
|
2883
|
+
ok: false,
|
|
2884
|
+
dryRun: false,
|
|
2885
|
+
draftPath,
|
|
2886
|
+
branch,
|
|
2887
|
+
commands,
|
|
2888
|
+
message: `git push failed: ${push.out}`
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
const prArgs = [
|
|
2892
|
+
"pr",
|
|
2893
|
+
"create",
|
|
2894
|
+
"--draft",
|
|
2895
|
+
"--title",
|
|
2896
|
+
draft.title,
|
|
2897
|
+
"--body-file",
|
|
2898
|
+
relDraft,
|
|
2899
|
+
...options.base ? ["--base", options.base] : []
|
|
2900
|
+
];
|
|
2901
|
+
let prUrl = "";
|
|
2902
|
+
try {
|
|
2903
|
+
prUrl = execFileSync("gh", prArgs, { cwd: root, encoding: "utf8" }).trim();
|
|
2904
|
+
} catch (error) {
|
|
2905
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2906
|
+
return {
|
|
2907
|
+
ok: false,
|
|
2908
|
+
dryRun: false,
|
|
2909
|
+
draftPath,
|
|
2910
|
+
branch,
|
|
2911
|
+
commands,
|
|
2912
|
+
message: `gh pr create failed: ${message}`
|
|
2913
|
+
};
|
|
2914
|
+
}
|
|
2915
|
+
return {
|
|
2916
|
+
ok: true,
|
|
2917
|
+
dryRun: false,
|
|
2918
|
+
draftPath,
|
|
2919
|
+
branch,
|
|
2920
|
+
prUrl,
|
|
2921
|
+
...prUrl ? { previewUrl: prUrl } : {},
|
|
2922
|
+
commands,
|
|
2923
|
+
message: prUrl ? `Draft PR opened: ${prUrl}` : "Draft PR created."
|
|
2924
|
+
};
|
|
2925
|
+
};
|
|
2926
|
+
|
|
2381
2927
|
// src/federation/llms.ts
|
|
2382
|
-
import { existsSync as
|
|
2383
|
-
import { resolve as
|
|
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 =
|
|
2399
|
-
if (!
|
|
2400
|
-
return
|
|
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
|
|
2486
|
-
import { join as
|
|
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
|
|
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(
|
|
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) =>
|
|
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 =
|
|
3170
|
+
const abs = join17(root, entry.path);
|
|
2613
3171
|
let content = "";
|
|
2614
3172
|
try {
|
|
2615
|
-
content =
|
|
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" ?
|
|
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
|