@agentskit/doc-bridge 1.4.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -33,6 +33,14 @@ var applyConfigDefaults = (config) => {
33
33
  preset: "minimal",
34
34
  ...config.gates
35
35
  },
36
+ rules: {
37
+ mode: "default",
38
+ ...config.rules
39
+ },
40
+ safety: {
41
+ redactSecrets: true,
42
+ ...config.safety
43
+ },
36
44
  surfaces: {
37
45
  cli: {
38
46
  bin: "ak-docs",
@@ -41,7 +49,22 @@ var applyConfigDefaults = (config) => {
41
49
  },
42
50
  mcp: {
43
51
  enabled: true,
44
- tools: ["handoff.resolve", "doc.search", "doc.get", "gate.status"],
52
+ tools: [
53
+ "handoff.resolve",
54
+ "doc.search",
55
+ "doc.get",
56
+ "gate.status",
57
+ "retriever.query",
58
+ "memory.classify",
59
+ "memory.promoteDraft",
60
+ "registry.topology",
61
+ "docbridge.snapshot",
62
+ "docbridge.report",
63
+ "docbridge.diagnostics",
64
+ "docbridge.relations",
65
+ "docbridge.run",
66
+ "docbridge.proposals"
67
+ ],
45
68
  transport: "stdio",
46
69
  ...config.surfaces?.mcp
47
70
  },
@@ -159,6 +182,39 @@ var GatesConfigSchema = z.object({
159
182
  ).max(16).optional(),
160
183
  options: z.record(z.string(), z.unknown()).optional()
161
184
  }).strict();
185
+ var RuleIdSchema = z.enum([
186
+ "documentation-quality",
187
+ "graph-undocumented-relation",
188
+ "declared-unobserved-relation",
189
+ "unresolved-reference",
190
+ "conflicting-declaration",
191
+ "not-analyzed-coverage",
192
+ "stale-documentation",
193
+ "centrality-risk",
194
+ "critical-path-risk",
195
+ "freshness",
196
+ "ownership"
197
+ ]);
198
+ var RuleSeveritySchema = z.enum(["off", "info", "warn", "error"]);
199
+ var RulesConfigSchema = z.object({
200
+ mode: z.enum(["default", "recommended", "strict"]).optional(),
201
+ severity: z.record(RuleIdSchema, RuleSeveritySchema).optional(),
202
+ ignore: z.array(RuleIdSchema).max(128).optional(),
203
+ criticalEntities: z.array(z.string().min(1).max(256)).max(128).optional(),
204
+ criticalPaths: z.array(z.string().min(1).max(512)).max(128).optional(),
205
+ warningThresholds: z.record(RuleIdSchema, z.number().int().min(1).max(1e5)).optional()
206
+ }).strict();
207
+ var WorkflowConfigSchema = z.object({
208
+ stateDir: z.string().min(1).max(512).optional()
209
+ }).strict();
210
+ var RepositorySafetyConfigSchema = z.object({
211
+ exclude: z.array(z.string().min(1).max(512)).max(128).optional(),
212
+ maxFiles: z.number().int().positive().max(1e6).optional(),
213
+ maxBytes: z.number().int().positive().max(1e10).optional(),
214
+ maxTimeMs: z.number().int().positive().max(864e5).optional(),
215
+ maxMemoryMb: z.number().int().positive().max(1048576).optional(),
216
+ redactSecrets: z.boolean().optional()
217
+ }).strict();
162
218
  var SurfacesConfigSchema = z.object({
163
219
  cli: z.object({
164
220
  bin: z.string().min(1).max(64).optional(),
@@ -176,7 +232,13 @@ var SurfacesConfigSchema = z.object({
176
232
  "retriever.query",
177
233
  "memory.classify",
178
234
  "memory.promoteDraft",
179
- "registry.topology"
235
+ "registry.topology",
236
+ "docbridge.snapshot",
237
+ "docbridge.report",
238
+ "docbridge.diagnostics",
239
+ "docbridge.relations",
240
+ "docbridge.run",
241
+ "docbridge.proposals"
180
242
  ])
181
243
  ).max(16).optional(),
182
244
  transport: z.enum(["stdio", "http"]).optional(),
@@ -219,7 +281,13 @@ var IntelligenceConfigSchema = z.object({
219
281
  }).strict().optional()
220
282
  }).strict().optional(),
221
283
  runtime: z.enum(["agentskit", "custom"]).optional(),
222
- runtimeModule: z.string().min(1).max(512).optional()
284
+ runtimeModule: z.string().min(1).max(512).optional(),
285
+ registry: z.object({
286
+ enabled: z.boolean().optional(),
287
+ agentId: z.string().min(1).max(256).optional(),
288
+ agentRoot: z.string().min(1).max(512).optional(),
289
+ runnerModule: z.string().min(1).max(512).optional()
290
+ }).strict().optional()
223
291
  }).strict();
224
292
  var FederationConfigSchema = z.object({
225
293
  enabled: z.boolean().optional(),
@@ -309,6 +377,9 @@ var DocBridgeConfigV1Schema = z.object({
309
377
  index: IndexConfigSchema.optional(),
310
378
  routing: RoutingConfigSchema.optional(),
311
379
  gates: GatesConfigSchema.optional(),
380
+ rules: RulesConfigSchema.optional(),
381
+ workflow: WorkflowConfigSchema.optional(),
382
+ safety: RepositorySafetyConfigSchema.optional(),
312
383
  surfaces: SurfacesConfigSchema.optional(),
313
384
  intelligence: IntelligenceConfigSchema.optional(),
314
385
  federation: FederationConfigSchema.optional(),
@@ -995,6 +1066,231 @@ var DocBridgeJsonSchemas = {
995
1066
  memoryCandidateV1: MemoryCandidateV1JsonSchema
996
1067
  };
997
1068
 
1069
+ // src/schemas/knowledge.ts
1070
+ import { z as z5 } from "zod";
1071
+ var KNOWLEDGE_SCHEMA_VERSION = 1;
1072
+ var KNOWLEDGE_CONTENT_HASH_ALGO = "sha256-normalized-v1";
1073
+ var hash = z5.string().regex(/^[a-f0-9]{64}$/);
1074
+ var boundedString = (max) => z5.string().min(1).max(max);
1075
+ var ProvenanceSchema = z5.enum(["observed", "declared", "proposed"]);
1076
+ var FindingStatusSchema = z5.enum([
1077
+ "confirmed",
1078
+ "undocumented",
1079
+ "stale-or-unverified",
1080
+ "conflict",
1081
+ "unresolved",
1082
+ "not-analyzed"
1083
+ ]);
1084
+ var DiagnosticSeveritySchema = z5.enum(["off", "info", "warn", "error"]);
1085
+ var EvidenceSourceSchema = z5.enum([
1086
+ "code",
1087
+ "configuration",
1088
+ "documentation",
1089
+ "agent",
1090
+ "derived"
1091
+ ]);
1092
+ var EvidenceSchema = z5.object({
1093
+ source: EvidenceSourceSchema,
1094
+ path: boundedString(512),
1095
+ lineStart: z5.number().int().positive().optional(),
1096
+ lineEnd: z5.number().int().positive().optional(),
1097
+ contentHash: hash.optional(),
1098
+ context: z5.string().max(1024).optional()
1099
+ }).strict().superRefine((value, context) => {
1100
+ if (value.lineStart !== void 0 && value.lineEnd !== void 0 && value.lineEnd < value.lineStart) {
1101
+ context.addIssue({
1102
+ code: z5.ZodIssueCode.custom,
1103
+ path: ["lineEnd"],
1104
+ message: "Must be greater than or equal to lineStart"
1105
+ });
1106
+ }
1107
+ });
1108
+ var CoverageStatusSchema = z5.enum(["complete", "partial", "not-analyzed"]);
1109
+ var CoverageSchema = z5.object({
1110
+ analyzer: boundedString(128),
1111
+ scope: boundedString(512),
1112
+ status: CoverageStatusSchema,
1113
+ reason: z5.string().max(1024).optional(),
1114
+ evidence: z5.array(EvidenceSchema).max(32).optional()
1115
+ }).strict();
1116
+ var ProjectIdentitySchema = z5.object({
1117
+ name: boundedString(128),
1118
+ root: boundedString(512).optional()
1119
+ }).strict();
1120
+ var ArtifactMetadata = {
1121
+ schemaVersion: z5.literal(KNOWLEDGE_SCHEMA_VERSION),
1122
+ contentHash: hash,
1123
+ contentHashAlgo: z5.literal(KNOWLEDGE_CONTENT_HASH_ALGO),
1124
+ project: ProjectIdentitySchema,
1125
+ sourceRevision: boundedString(128),
1126
+ sourceRevisionKind: z5.enum(["git", "content"]),
1127
+ configurationHash: hash,
1128
+ pipelineVersion: boundedString(64),
1129
+ analyzerVersions: z5.record(boundedString(128), boundedString(64))
1130
+ };
1131
+ var EntitySchema = z5.object({
1132
+ id: boundedString(256),
1133
+ kind: boundedString(128),
1134
+ name: boundedString(256),
1135
+ path: boundedString(512).optional(),
1136
+ aliases: z5.array(boundedString(256)).max(32).optional(),
1137
+ provenance: ProvenanceSchema,
1138
+ evidence: z5.array(EvidenceSchema).max(64),
1139
+ metadata: z5.record(z5.unknown()).optional()
1140
+ }).strict();
1141
+ var RelationSchema = z5.object({
1142
+ id: boundedString(256),
1143
+ kind: boundedString(128),
1144
+ from: boundedString(256),
1145
+ to: boundedString(256),
1146
+ discriminator: boundedString(256).optional(),
1147
+ provenance: ProvenanceSchema,
1148
+ evidence: z5.array(EvidenceSchema).max(64),
1149
+ metadata: z5.record(z5.unknown()).optional()
1150
+ }).strict();
1151
+ var DiscoverySnapshotV1Schema = z5.object({
1152
+ type: z5.literal("discovery-snapshot"),
1153
+ ...ArtifactMetadata,
1154
+ entities: z5.array(EntitySchema).max(5e4),
1155
+ relations: z5.array(RelationSchema).max(1e5),
1156
+ coverage: z5.array(CoverageSchema).max(1e3)
1157
+ }).strict().superRefine((value, context) => {
1158
+ const entityIds = /* @__PURE__ */ new Set();
1159
+ for (const [index, entity] of value.entities.entries()) {
1160
+ if (entityIds.has(entity.id)) {
1161
+ context.addIssue({ code: z5.ZodIssueCode.custom, path: ["entities", index, "id"], message: `Duplicate entity id: ${entity.id}` });
1162
+ }
1163
+ entityIds.add(entity.id);
1164
+ }
1165
+ const relationIds = /* @__PURE__ */ new Set();
1166
+ for (const [index, relation] of value.relations.entries()) {
1167
+ if (relationIds.has(relation.id)) {
1168
+ context.addIssue({ code: z5.ZodIssueCode.custom, path: ["relations", index, "id"], message: `Duplicate relation id: ${relation.id}` });
1169
+ }
1170
+ relationIds.add(relation.id);
1171
+ }
1172
+ });
1173
+ var DiagnosticSchema = z5.object({
1174
+ id: boundedString(256),
1175
+ code: boundedString(128),
1176
+ status: FindingStatusSchema,
1177
+ severity: DiagnosticSeveritySchema,
1178
+ message: boundedString(2048),
1179
+ evidence: z5.array(EvidenceSchema).max(64),
1180
+ entityIds: z5.array(boundedString(256)).max(64).optional(),
1181
+ relationIds: z5.array(boundedString(256)).max(64).optional(),
1182
+ remediation: z5.string().max(2048).optional()
1183
+ }).strict();
1184
+ var ReconciliationReportV1Schema = z5.object({
1185
+ type: z5.literal("reconciliation-report"),
1186
+ ...ArtifactMetadata,
1187
+ snapshotHash: hash,
1188
+ diagnostics: z5.array(DiagnosticSchema).max(1e5),
1189
+ summary: z5.object({
1190
+ entityCount: z5.number().int().nonnegative(),
1191
+ relationCount: z5.number().int().nonnegative(),
1192
+ diagnosticCount: z5.number().int().nonnegative()
1193
+ }).strict()
1194
+ }).strict();
1195
+ var WorkflowStateSchema = z5.enum([
1196
+ "created",
1197
+ "discovering",
1198
+ "analyzed",
1199
+ "compared",
1200
+ "awaiting-agent",
1201
+ "proposed",
1202
+ "awaiting-approval",
1203
+ "validating",
1204
+ "delivered",
1205
+ "failed",
1206
+ "cancelled",
1207
+ "stale",
1208
+ "superseded"
1209
+ ]);
1210
+ var WorkflowStepSchema = z5.object({
1211
+ name: boundedString(128),
1212
+ status: z5.enum(["pending", "running", "completed", "failed", "skipped"]),
1213
+ inputHash: hash,
1214
+ outputHash: hash.optional(),
1215
+ artifactRefs: z5.array(boundedString(512)).max(32).optional()
1216
+ }).strict();
1217
+ var WorkflowTransitionSchema = z5.object({
1218
+ from: WorkflowStateSchema.nullable(),
1219
+ to: WorkflowStateSchema,
1220
+ at: z5.string().datetime(),
1221
+ reason: z5.string().max(1024).optional()
1222
+ }).strict();
1223
+ var WorkflowRunV1Schema = z5.object({
1224
+ type: z5.literal("workflow-run"),
1225
+ ...ArtifactMetadata,
1226
+ runId: boundedString(128),
1227
+ state: WorkflowStateSchema,
1228
+ steps: z5.array(WorkflowStepSchema).max(32),
1229
+ transitions: z5.array(WorkflowTransitionSchema).max(1e3),
1230
+ artifactRefs: z5.array(boundedString(512)).max(128)
1231
+ }).strict();
1232
+ var ProposalOriginSchema = z5.object({
1233
+ kind: z5.enum(["registry-agent", "manual", "deterministic"]),
1234
+ id: boundedString(256).optional(),
1235
+ version: boundedString(64).optional(),
1236
+ provider: boundedString(128).optional(),
1237
+ model: boundedString(256).optional(),
1238
+ capabilities: z5.array(boundedString(128)).max(32).optional()
1239
+ }).strict().superRefine((value, context) => {
1240
+ if (value.kind === "registry-agent" && value.id === void 0) {
1241
+ context.addIssue({ code: z5.ZodIssueCode.custom, path: ["id"], message: "Registry agent origin requires an id" });
1242
+ }
1243
+ });
1244
+ var AgentProposalV1Schema = z5.object({
1245
+ type: z5.literal("agent-proposal"),
1246
+ ...ArtifactMetadata,
1247
+ proposalId: boundedString(128),
1248
+ baseSnapshotHash: hash,
1249
+ baseReportHash: hash,
1250
+ relatedDiagnosticIds: z5.array(boundedString(256)).max(64),
1251
+ rationale: boundedString(4e3),
1252
+ confidence: z5.number().min(0).max(1),
1253
+ evidence: z5.array(EvidenceSchema).max(128),
1254
+ intendedChanges: z5.array(boundedString(4e3)).max(64),
1255
+ origin: ProposalOriginSchema,
1256
+ checks: z5.array(boundedString(512)).max(32)
1257
+ }).strict();
1258
+ var FixProposalStatusSchema = z5.enum(["proposed", "approved", "rejected", "stale", "applied", "failed"]);
1259
+ var AffectedFileSchema = z5.object({
1260
+ path: boundedString(512),
1261
+ contentHash: hash
1262
+ }).strict();
1263
+ var FixChangeSchema = z5.object({
1264
+ path: boundedString(512),
1265
+ before: z5.string().max(1e5),
1266
+ after: z5.string().max(1e5)
1267
+ }).strict();
1268
+ var FixProposalV1Schema = z5.object({
1269
+ type: z5.literal("fix-proposal"),
1270
+ ...ArtifactMetadata,
1271
+ proposalId: boundedString(128),
1272
+ baseRevision: boundedString(128),
1273
+ affectedFiles: z5.array(AffectedFileSchema).max(256),
1274
+ changes: z5.array(FixChangeSchema).max(256).optional(),
1275
+ preconditions: z5.array(boundedString(2048)).max(64),
1276
+ diff: boundedString(1e5),
1277
+ postconditions: z5.array(boundedString(2048)).max(64),
1278
+ approval: z5.object({
1279
+ proposalHash: hash,
1280
+ approvedAt: z5.string().datetime(),
1281
+ approvedBy: boundedString(256)
1282
+ }).strict().optional(),
1283
+ status: FixProposalStatusSchema
1284
+ }).strict().superRefine((value, context) => {
1285
+ if ((value.status === "approved" || value.status === "applied") && value.approval === void 0) {
1286
+ context.addIssue({
1287
+ code: z5.ZodIssueCode.custom,
1288
+ path: ["approval"],
1289
+ message: `Fix proposal status ${value.status} requires an approval record`
1290
+ });
1291
+ }
1292
+ });
1293
+
998
1294
  // src/validate.ts
999
1295
  var zodIssues = (error) => error.issues.map((issue) => ({
1000
1296
  path: issue.path.join(".") || "(root)",
@@ -1020,6 +1316,11 @@ ${result.issues.map((i) => ` - ${i.path}: ${i.message}`).join("\n")}`
1020
1316
  var parseAgentSearch = (input) => AgentSearchV1Schema.parse(input);
1021
1317
  var parseDocBridgeIndex = (input) => DocBridgeIndexV1Schema.parse(input);
1022
1318
  var parseMemoryCandidate = (input) => MemoryCandidateV1Schema.parse(input);
1319
+ var parseDiscoverySnapshot = (input) => DiscoverySnapshotV1Schema.parse(input);
1320
+ var parseReconciliationReport = (input) => ReconciliationReportV1Schema.parse(input);
1321
+ var parseWorkflowRun = (input) => WorkflowRunV1Schema.parse(input);
1322
+ var parseAgentProposal = (input) => AgentProposalV1Schema.parse(input);
1323
+ var parseFixProposal = (input) => FixProposalV1Schema.parse(input);
1023
1324
  var parseDocBridgeConfig = (input) => {
1024
1325
  const result = DocBridgeConfigV1Schema.safeParse(input);
1025
1326
  if (!result.success) {
@@ -1552,7 +1853,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
1552
1853
  };
1553
1854
 
1554
1855
  // src/version.ts
1555
- var PACKAGE_VERSION = "1.4.3";
1856
+ var PACKAGE_VERSION = "1.5.0";
1556
1857
 
1557
1858
  // src/index-builder/capabilities.ts
1558
1859
  var renderCapabilitiesJson = (config, index, paths) => {
@@ -1597,23 +1898,28 @@ var sortValue = (value) => {
1597
1898
  }
1598
1899
  return value;
1599
1900
  };
1901
+ var canonicalJsonV1 = (payload) => JSON.stringify(sortValue(payload));
1600
1902
  var sha256NormalizedV1 = (payload) => {
1601
- const normalized = JSON.stringify(sortValue(payload));
1903
+ const normalized = canonicalJsonV1(payload);
1602
1904
  return createHash("sha256").update(normalized, "utf8").digest("hex");
1603
1905
  };
1906
+ var contentHashForArtifactV1 = (artifact2) => {
1907
+ const { contentHash: _contentHash, ...payload } = artifact2;
1908
+ return sha256NormalizedV1(payload);
1909
+ };
1604
1910
 
1605
1911
  // src/index-builder/llms-txt.ts
1606
1912
  var routePath = (path, pathPrefix) => {
1607
1913
  const normalizedPath = path.replaceAll("\\", "/").replace(/\.(?:md|mdx)$/, "");
1608
1914
  const normalizedPrefix = pathPrefix?.replaceAll("\\", "/").replace(/\/$/, "");
1609
- const relativePath2 = normalizedPrefix && normalizedPath.startsWith(`${normalizedPrefix}/`) ? normalizedPath.slice(normalizedPrefix.length + 1) : normalizedPath;
1610
- return relativePath2.replace(/^\/+/, "");
1915
+ const relativePath3 = normalizedPrefix && normalizedPath.startsWith(`${normalizedPrefix}/`) ? normalizedPath.slice(normalizedPrefix.length + 1) : normalizedPath;
1916
+ return relativePath3.replace(/^\/+/, "");
1611
1917
  };
1612
1918
  var knowledgeUrl = (path, options = {}) => {
1613
- const relativePath2 = routePath(path, options.pathPrefix);
1919
+ const relativePath3 = routePath(path, options.pathPrefix);
1614
1920
  if (!options.urlPrefix) return path;
1615
1921
  const base = options.urlPrefix.endsWith("/") ? options.urlPrefix : `${options.urlPrefix}/`;
1616
- return new URL(relativePath2, base).toString();
1922
+ return new URL(relativePath3, base).toString();
1617
1923
  };
1618
1924
  var renderLlmsTxt = (config, knowledge, projectName2) => {
1619
1925
  const preamble = config.index?.llmsTxt?.preamble ?? `# ${projectName2}
@@ -2259,6 +2565,1427 @@ var buildDocBridgeIndex = (opts) => {
2259
2565
  };
2260
2566
  };
2261
2567
 
2568
+ // src/discovery/repository.ts
2569
+ import { execFileSync } from "child_process";
2570
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
2571
+ import { basename as basename4, dirname as dirname5, extname, join as join8, relative as relative5, resolve as resolve7, sep as sep6 } from "path";
2572
+ import * as ts from "typescript";
2573
+
2574
+ // src/safety/repository.ts
2575
+ import { lstatSync as lstatSync3, readdirSync as readdirSync3, realpathSync as realpathSync6, statSync as statSync3 } from "fs";
2576
+ import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve6, sep as sep5 } from "path";
2577
+ import { minimatch as minimatch4 } from "minimatch";
2578
+ var DEFAULT_SAFETY_EXCLUDES = ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**", "**/.doc-bridge/**", "**/.env", "**/.env.*", "**/*secret*", "**/*credential*", "**/*.pem", "**/*.key"];
2579
+ var containedPath = (root, candidate) => {
2580
+ const projectRoot = realpathSync6.native(resolve6(root));
2581
+ const unresolved = resolve6(projectRoot, candidate);
2582
+ const unresolvedRelative = relative4(projectRoot, unresolved);
2583
+ if (isAbsolute4(unresolvedRelative) || unresolvedRelative === ".." || unresolvedRelative.startsWith(`..${sep5}`)) return void 0;
2584
+ try {
2585
+ const canonical = realpathSync6.native(unresolved);
2586
+ const canonicalRelative = relative4(projectRoot, canonical);
2587
+ return isAbsolute4(canonicalRelative) || canonicalRelative === ".." || canonicalRelative.startsWith(`..${sep5}`) ? void 0 : canonical;
2588
+ } catch {
2589
+ return unresolved;
2590
+ }
2591
+ };
2592
+ var safeWalkFiles = (root, options = {}) => {
2593
+ const projectRoot = resolve6(root);
2594
+ const extensions = options.extensions ?? [];
2595
+ const excludes = options.exclude ?? DEFAULT_SAFETY_EXCLUDES;
2596
+ const files = [];
2597
+ let bytes = 0;
2598
+ let reason;
2599
+ const started = Date.now();
2600
+ const matchesExclude = (path) => excludes.some((pattern) => minimatch4(path, pattern, { dot: true }));
2601
+ const visit = (directory) => {
2602
+ if (reason) return;
2603
+ if (options.maxTimeMs !== void 0 && Date.now() - started >= options.maxTimeMs) {
2604
+ reason = `Repository scan exceeded the ${options.maxTimeMs} ms time limit.`;
2605
+ return;
2606
+ }
2607
+ if (options.maxMemoryMb !== void 0 && process.memoryUsage().heapUsed > options.maxMemoryMb * 1024 * 1024) {
2608
+ reason = `Repository scan exceeded the ${options.maxMemoryMb} MiB memory limit.`;
2609
+ return;
2610
+ }
2611
+ let entries;
2612
+ try {
2613
+ entries = readdirSync3(directory);
2614
+ } catch {
2615
+ return;
2616
+ }
2617
+ for (const name of entries.sort()) {
2618
+ const absolute = resolve6(directory, name);
2619
+ const relativePath3 = relative4(projectRoot, absolute).split(sep5).join("/");
2620
+ if (matchesExclude(relativePath3) || name === ".git") continue;
2621
+ let stats;
2622
+ try {
2623
+ stats = lstatSync3(absolute);
2624
+ } catch {
2625
+ continue;
2626
+ }
2627
+ if (stats.isSymbolicLink()) continue;
2628
+ if (stats.isDirectory()) {
2629
+ visit(absolute);
2630
+ if (reason) return;
2631
+ continue;
2632
+ }
2633
+ if (!stats.isFile() || extensions.length > 0 && !extensions.some((extension) => name.endsWith(extension))) continue;
2634
+ if (files.length >= (options.maxFiles ?? 1e4)) {
2635
+ reason = `Repository scan exceeded the ${options.maxFiles ?? 1e4} file limit.`;
2636
+ return;
2637
+ }
2638
+ bytes += statSync3(absolute).size;
2639
+ if (options.maxBytes !== void 0 && bytes > options.maxBytes) {
2640
+ reason = `Repository scan exceeded the ${options.maxBytes} byte limit.`;
2641
+ return;
2642
+ }
2643
+ files.push(absolute);
2644
+ }
2645
+ };
2646
+ visit(projectRoot);
2647
+ return { files: files.sort(), incomplete: reason !== void 0, ...reason ? { reason } : {} };
2648
+ };
2649
+ var SECRET_PATTERNS = [
2650
+ /\b(?:sk|pk)[_-](?:live|test)[_-][A-Za-z0-9_-]{12,}\b/g,
2651
+ /\b(?:ghp|github_pat|xox[baprs])_[A-Za-z0-9_-]{12,}\b/g,
2652
+ /\bAKIA[0-9A-Z]{16}\b/g,
2653
+ /(?:password|passwd|secret|token|api[_-]?key)\s*[:=]\s*["']?[^\s,"']+/gi
2654
+ ];
2655
+ var redactSecrets = (value) => SECRET_PATTERNS.reduce((result, pattern) => result.replace(pattern, "[REDACTED]"), value);
2656
+ var redactValue = (value) => Array.isArray(value) ? value.map(redactValue) : value && typeof value === "object" ? Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redactValue(item)])) : typeof value === "string" ? redactSecrets(value) : value;
2657
+
2658
+ // src/discovery/repository.ts
2659
+ var SOURCE_EXTENSIONS = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts"];
2660
+ var DOCUMENT_EXTENSIONS = [".md", ".mdx"];
2661
+ var DEFAULT_MAX_FILES2 = 1e4;
2662
+ var EMPTY_HASH = "0".repeat(64);
2663
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2664
+ var readJson = (path) => {
2665
+ try {
2666
+ const value = JSON.parse(readFileSync8(path, "utf8"));
2667
+ return isRecord2(value) ? { value } : { error: "JSON root is not an object" };
2668
+ } catch (error) {
2669
+ return { error: error instanceof Error ? error.message : String(error) };
2670
+ }
2671
+ };
2672
+ var relativePath = (root, path) => toPosix(relative5(root, path)) || ".";
2673
+ var MAX_ID_LENGTH = 256;
2674
+ var ID_HASH_LENGTH = 32;
2675
+ var entityId = (kind, value) => {
2676
+ const fullId = `${kind}:${value}`;
2677
+ if (fullId.length <= MAX_ID_LENGTH) return fullId;
2678
+ const suffix = `:${sha256NormalizedV1(fullId).slice(0, ID_HASH_LENGTH)}`;
2679
+ return `${fullId.slice(0, MAX_ID_LENGTH - suffix.length)}${suffix}`;
2680
+ };
2681
+ var lineEvidence = (source, root, path, lineStart, lineEnd) => ({
2682
+ source,
2683
+ path: relativePath(root, path),
2684
+ ...lineStart !== void 0 ? { lineStart } : {},
2685
+ ...lineEnd !== void 0 ? { lineEnd } : {}
2686
+ });
2687
+ var firstLineContaining = (text, pattern) => {
2688
+ const line = text.split(/\r?\n/).findIndex((value) => value.includes(pattern));
2689
+ return line >= 0 ? line + 1 : void 0;
2690
+ };
2691
+ var packageName = (manifest, fallback) => typeof manifest.name === "string" && manifest.name.length > 0 ? manifest.name : fallback || void 0;
2692
+ var workspacePatterns = (root, rootManifest) => {
2693
+ const fromPackageJson = rootManifest?.workspaces;
2694
+ if (Array.isArray(fromPackageJson)) return fromPackageJson.filter((value) => typeof value === "string");
2695
+ if (isRecord2(fromPackageJson) && Array.isArray(fromPackageJson.packages)) {
2696
+ return fromPackageJson.packages.filter((value) => typeof value === "string");
2697
+ }
2698
+ const workspacePath = join8(root, "pnpm-workspace.yaml");
2699
+ if (!existsSync9(workspacePath)) return [];
2700
+ const patterns = [];
2701
+ let inPackages = false;
2702
+ for (const line of readFileSync8(workspacePath, "utf8").split(/\r?\n/)) {
2703
+ const trimmed = line.trim();
2704
+ if (trimmed === "packages:") {
2705
+ inPackages = true;
2706
+ continue;
2707
+ }
2708
+ if (!inPackages) continue;
2709
+ if (trimmed.startsWith("- ")) {
2710
+ patterns.push(trimmed.slice(2).trim().replace(/^['"]|['"]$/g, ""));
2711
+ continue;
2712
+ }
2713
+ if (trimmed && !trimmed.startsWith("#")) inPackages = false;
2714
+ }
2715
+ return patterns;
2716
+ };
2717
+ var discoverPackages = (root, rootManifest, config) => {
2718
+ const packages = [];
2719
+ const coverage = [];
2720
+ const rootManifestPath = join8(root, "package.json");
2721
+ if (rootManifest) {
2722
+ const name = packageName(rootManifest, "");
2723
+ packages.push({
2724
+ id: entityId("package", packageName(rootManifest, "root") ?? "root"),
2725
+ ...name ? { name } : {},
2726
+ path: ".",
2727
+ absPath: root,
2728
+ manifestPath: rootManifestPath,
2729
+ manifest: rootManifest
2730
+ });
2731
+ }
2732
+ const configuredPatterns = config?.routing?.options?.packages;
2733
+ const patterns = configuredPatterns?.length ? [...configuredPatterns] : workspacePatterns(root, rootManifest);
2734
+ if (!patterns.length) {
2735
+ coverage.push({ status: "complete" });
2736
+ return { packages, coverage };
2737
+ }
2738
+ const dirs = expandWorkspaceGlobs(root, patterns);
2739
+ for (const absPath of dirs) {
2740
+ const manifestPath = join8(absPath, "package.json");
2741
+ const parsed = readJson(manifestPath);
2742
+ if (!parsed.value) {
2743
+ coverage.push({ status: "partial", reason: `${relativePath(root, manifestPath)}: ${parsed.error ?? "invalid package.json"}` });
2744
+ continue;
2745
+ }
2746
+ const path = relativePath(root, absPath);
2747
+ const name = packageName(parsed.value, path);
2748
+ const id = entityId("package", name ?? path);
2749
+ const duplicate = packages.find((pkg) => pkg.id === id);
2750
+ if (duplicate && duplicate.absPath !== absPath) {
2751
+ throw new Error(`Package identity collision for "${id}": "${duplicate.path}" and "${path}".`);
2752
+ }
2753
+ if (!duplicate) packages.push({ id, ...name ? { name } : {}, path, absPath, manifestPath, manifest: parsed.value });
2754
+ }
2755
+ coverage.push({ status: "complete" });
2756
+ return { packages: packages.sort((a, b) => a.id.localeCompare(b.id)), coverage };
2757
+ };
2758
+ var packageForModule = (packages, absPath) => [...packages].filter((pkg) => absPath === pkg.absPath || absPath.startsWith(`${pkg.absPath}${sep6}`)).sort((a, b) => b.absPath.length - a.absPath.length)[0];
2759
+ var readCompilerOptions = (root) => {
2760
+ const configPath = ts.findConfigFile(root, ts.sys.fileExists, "tsconfig.json");
2761
+ if (!configPath) return { options: {} };
2762
+ const parsed = ts.readConfigFile(configPath, ts.sys.readFile);
2763
+ if (parsed.error) return { options: {}, error: ts.flattenDiagnosticMessageText(parsed.error.messageText, "\n") };
2764
+ const config = ts.parseJsonConfigFileContent(parsed.config, ts.sys, dirname5(configPath));
2765
+ if (config.errors.length) {
2766
+ return {
2767
+ options: config.options,
2768
+ error: ts.flattenDiagnosticMessageText(config.errors[0]?.messageText ?? "Invalid tsconfig", "\n")
2769
+ };
2770
+ }
2771
+ return { options: config.options };
2772
+ };
2773
+ var scriptKind = (path) => {
2774
+ switch (extname(path)) {
2775
+ case ".js":
2776
+ return ts.ScriptKind.JS;
2777
+ case ".jsx":
2778
+ return ts.ScriptKind.JSX;
2779
+ case ".mjs":
2780
+ return ts.ScriptKind.JS;
2781
+ case ".cjs":
2782
+ return ts.ScriptKind.JS;
2783
+ case ".ts":
2784
+ return ts.ScriptKind.TS;
2785
+ case ".tsx":
2786
+ return ts.ScriptKind.TSX;
2787
+ case ".mts":
2788
+ return ts.ScriptKind.TS;
2789
+ case ".cts":
2790
+ return ts.ScriptKind.TS;
2791
+ default:
2792
+ return ts.ScriptKind.Unknown;
2793
+ }
2794
+ };
2795
+ var nodeEvidence = (root, path, sourceFile, node) => {
2796
+ const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
2797
+ const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line + 1;
2798
+ return lineEvidence("code", root, path, start, end);
2799
+ };
2800
+ var isExported = (node) => {
2801
+ const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : void 0;
2802
+ return modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) ?? false;
2803
+ };
2804
+ var exportedNames = (sourceFile) => {
2805
+ const names = /* @__PURE__ */ new Set();
2806
+ const addDeclarationName = (node) => {
2807
+ if (!isExported(node)) return;
2808
+ const name = ts.getNameOfDeclaration(node);
2809
+ if (name && ts.isIdentifier(name)) names.add(name.text);
2810
+ };
2811
+ const visit = (node) => {
2812
+ if (ts.isExportDeclaration(node)) {
2813
+ if (!node.exportClause) names.add("*");
2814
+ else if (ts.isNamedExports(node.exportClause)) {
2815
+ for (const element of node.exportClause.elements) names.add(element.name.text);
2816
+ }
2817
+ } else if (ts.isExportAssignment(node)) {
2818
+ names.add("default");
2819
+ } else if (ts.isClassDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) {
2820
+ addDeclarationName(node);
2821
+ } else if (ts.isVariableStatement(node) && isExported(node)) {
2822
+ for (const declaration of node.declarationList.declarations) {
2823
+ if (ts.isIdentifier(declaration.name)) names.add(declaration.name.text);
2824
+ }
2825
+ }
2826
+ ts.forEachChild(node, visit);
2827
+ };
2828
+ visit(sourceFile);
2829
+ return [...names].sort();
2830
+ };
2831
+ var moduleReferences = (root, path, sourceFile) => {
2832
+ const references = [];
2833
+ let hasDynamic = false;
2834
+ let hasRuntimeWiring = false;
2835
+ const addReference = (specifier, kind, node) => {
2836
+ references.push({ specifier: specifier.text, kind, evidence: nodeEvidence(root, path, sourceFile, node) });
2837
+ };
2838
+ const visit = (node) => {
2839
+ if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
2840
+ addReference(node.moduleSpecifier, "imports", node);
2841
+ } else if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
2842
+ addReference(node.moduleSpecifier, "re-exports", node);
2843
+ } else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) {
2844
+ addReference(node.moduleReference.expression, "imports", node);
2845
+ } else if (ts.isCallExpression(node)) {
2846
+ if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
2847
+ if (!node.arguments[0] || !ts.isStringLiteralLike(node.arguments[0])) hasDynamic = true;
2848
+ } else if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
2849
+ const argument = node.arguments[0];
2850
+ if (argument && ts.isStringLiteralLike(argument)) addReference(argument, "imports", node);
2851
+ else hasDynamic = true;
2852
+ } else if (ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "register") {
2853
+ hasRuntimeWiring = true;
2854
+ }
2855
+ }
2856
+ ts.forEachChild(node, visit);
2857
+ };
2858
+ visit(sourceFile);
2859
+ return {
2860
+ references,
2861
+ exports: exportedNames(sourceFile),
2862
+ hasDynamic,
2863
+ hasRuntimeWiring
2864
+ };
2865
+ };
2866
+ var resolveRelativeModule = (specifier, containingFile, modulePaths) => {
2867
+ const base = resolve7(dirname5(containingFile), specifier);
2868
+ const extension = extname(base);
2869
+ const extensionlessBase = extension ? base.slice(0, -extension.length) : base;
2870
+ const candidates = [
2871
+ base,
2872
+ ...SOURCE_EXTENSIONS.map((extension2) => `${base}${extension2}`),
2873
+ ...SOURCE_EXTENSIONS.map((extension2) => join8(base, `index${extension2}`)),
2874
+ ...SOURCE_EXTENSIONS.map((extension2) => `${extensionlessBase}${extension2}`),
2875
+ ...SOURCE_EXTENSIONS.map((extension2) => join8(extensionlessBase, `index${extension2}`))
2876
+ ];
2877
+ return candidates.map((candidate) => modulePaths.get(resolve7(candidate))).find(Boolean);
2878
+ };
2879
+ var resolveReference = (reference, containingFile, modules, packages, compilerOptions) => {
2880
+ if (reference.specifier.startsWith(".") || reference.specifier.startsWith("/")) {
2881
+ const relativeTarget = resolveRelativeModule(reference.specifier, containingFile, modules);
2882
+ return relativeTarget ? { targetId: relativeTarget.entityId } : void 0;
2883
+ }
2884
+ const packageTarget = [...packages].filter((pkg) => pkg.name && (reference.specifier === pkg.name || reference.specifier.startsWith(`${pkg.name}/`))).sort((a, b) => (b.name?.length ?? 0) - (a.name?.length ?? 0))[0];
2885
+ if (packageTarget) return { targetId: packageTarget.id };
2886
+ const resolved = ts.resolveModuleName(reference.specifier, containingFile, compilerOptions, ts.sys).resolvedModule?.resolvedFileName;
2887
+ const resolvedTarget = resolved ? modules.get(resolve7(resolved)) : void 0;
2888
+ if (resolvedTarget) return { targetId: resolvedTarget.entityId };
2889
+ return { targetId: entityId("external", reference.specifier) };
2890
+ };
2891
+ var dependencyEntries = (manifest) => {
2892
+ const sections = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
2893
+ return sections.flatMap((type) => {
2894
+ const value = manifest[type];
2895
+ if (!isRecord2(value)) return [];
2896
+ return Object.keys(value).sort().map((name) => ({ name, type }));
2897
+ });
2898
+ };
2899
+ var sourceRevision = (root, files) => {
2900
+ const contentRevision = () => ({
2901
+ value: sha256NormalizedV1(
2902
+ files.map((path) => ({
2903
+ path: relativePath(root, path),
2904
+ contentHash: sha256NormalizedV1(readFileSync8(path, "utf8"))
2905
+ }))
2906
+ ),
2907
+ kind: "content"
2908
+ });
2909
+ try {
2910
+ const status = execFileSync("git", ["status", "--porcelain", "--untracked-files=all"], {
2911
+ cwd: root,
2912
+ encoding: "utf8",
2913
+ stdio: ["ignore", "pipe", "ignore"]
2914
+ }).trim();
2915
+ if (status) return contentRevision();
2916
+ const value = execFileSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
2917
+ if (value) return { value, kind: "git" };
2918
+ } catch {
2919
+ }
2920
+ return contentRevision();
2921
+ };
2922
+ var hasPackageManagerMetadata = (root, rootManifest) => Boolean(
2923
+ rootManifest?.packageManager || existsSync9(join8(root, "pnpm-lock.yaml")) || existsSync9(join8(root, "pnpm-workspace.yaml")) || existsSync9(join8(root, "yarn.lock")) || existsSync9(join8(root, "bun.lock")) || existsSync9(join8(root, "bun.lockb")) || existsSync9(join8(root, "package-lock.json"))
2924
+ );
2925
+ var artifact = (root, config, files, entities, relations, coverage) => {
2926
+ const revision = sourceRevision(root, files);
2927
+ const base = {
2928
+ type: "discovery-snapshot",
2929
+ schemaVersion: 1,
2930
+ contentHash: EMPTY_HASH,
2931
+ contentHashAlgo: "sha256-normalized-v1",
2932
+ project: { name: entities.find((entity) => entity.kind === "package" && entity.path === ".")?.name ?? basename4(root), root: "." },
2933
+ sourceRevision: revision.value,
2934
+ sourceRevisionKind: revision.kind,
2935
+ configurationHash: sha256NormalizedV1(config ?? {}),
2936
+ pipelineVersion: "1.0.0",
2937
+ analyzerVersions: { repository: "1.0.0", "js-ts": "1.0.0" },
2938
+ entities: [...entities].sort((a, b) => a.id.localeCompare(b.id)),
2939
+ relations: [...relations].sort((a, b) => a.id.localeCompare(b.id)),
2940
+ coverage: [...coverage]
2941
+ };
2942
+ return DiscoverySnapshotV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
2943
+ };
2944
+ var discoverRepository = (opts = {}) => {
2945
+ const root = resolve7(opts.root ?? process.cwd());
2946
+ const maxFiles = opts.maxFiles ?? DEFAULT_MAX_FILES2;
2947
+ const safety = opts.config?.safety;
2948
+ const maxBytes = opts.maxBytes ?? safety?.maxBytes;
2949
+ const safeOptions = {
2950
+ exclude: [...DEFAULT_SAFETY_EXCLUDES, ...safety?.exclude ?? []],
2951
+ maxFiles,
2952
+ ...maxBytes !== void 0 ? { maxBytes } : {},
2953
+ ...safety?.maxTimeMs !== void 0 ? { maxTimeMs: safety.maxTimeMs } : {},
2954
+ ...safety?.maxMemoryMb !== void 0 ? { maxMemoryMb: safety.maxMemoryMb } : {}
2955
+ };
2956
+ const rootManifestPath = join8(root, "package.json");
2957
+ const rootManifest = readJson(rootManifestPath).value;
2958
+ const packageResult = discoverPackages(root, rootManifest, opts.config);
2959
+ const sourceWalk = safeWalkFiles(root, { extensions: SOURCE_EXTENSIONS, ...safeOptions });
2960
+ const documentWalk = safeWalkFiles(root, { extensions: DOCUMENT_EXTENSIONS, ...safeOptions });
2961
+ const configWalk = safeWalkFiles(root, { extensions: [".json", ".yaml", ".yml", ".js", ".ts"], ...safeOptions });
2962
+ const sourcePaths = sourceWalk.files;
2963
+ const documentPaths = documentWalk.files;
2964
+ const configPaths = configWalk.files.filter((path) => /(?:^|\/)(?:tsconfig|jsconfig|vite\.config|webpack\.config|rollup\.config|next\.config|jest\.config|eslint\.config|vitest\.config)/.test(relativePath(root, path)));
2965
+ const allFiles = [...new Set([rootManifestPath, ...sourcePaths, ...documentPaths, ...configPaths].filter(existsSync9))].sort();
2966
+ const entities = /* @__PURE__ */ new Map();
2967
+ const relations = /* @__PURE__ */ new Map();
2968
+ const addEntity = (entity) => {
2969
+ const existing = entities.get(entity.id);
2970
+ if (existing && (existing.kind !== entity.kind || existing.path !== entity.path)) throw new Error(`Entity identity collision for "${entity.id}".`);
2971
+ entities.set(entity.id, existing ?? entity);
2972
+ };
2973
+ const addRelation = (relation) => {
2974
+ const existing = relations.get(relation.id);
2975
+ if (!existing) {
2976
+ relations.set(relation.id, relation);
2977
+ return;
2978
+ }
2979
+ const evidence2 = new Map(
2980
+ [...existing.evidence, ...relation.evidence].map((item) => [
2981
+ `${item.path}:${item.lineStart ?? ""}:${item.lineEnd ?? ""}:${item.source}`,
2982
+ item
2983
+ ])
2984
+ );
2985
+ relations.set(relation.id, { ...existing, evidence: [...evidence2.values()] });
2986
+ };
2987
+ for (const pkg of packageResult.packages) {
2988
+ const text = readFileSync8(pkg.manifestPath, "utf8");
2989
+ addEntity({ id: pkg.id, kind: "package", name: pkg.name ?? pkg.path, path: pkg.path, provenance: "observed", evidence: [lineEvidence("configuration", root, pkg.manifestPath, firstLineContaining(text, '"name"'))] });
2990
+ }
2991
+ const modules = /* @__PURE__ */ new Map();
2992
+ for (const absPath of sourcePaths) {
2993
+ const path = relativePath(root, absPath);
2994
+ const pkg = packageForModule(packageResult.packages, absPath);
2995
+ const id = entityId("module", path);
2996
+ const text = readFileSync8(absPath, "utf8");
2997
+ const sourceFile = ts.createSourceFile(absPath, text, ts.ScriptTarget.Latest, true, scriptKind(absPath));
2998
+ const exports = exportedNames(sourceFile);
2999
+ modules.set(resolve7(absPath), { absPath, path, entityId: id, ...pkg ? { packageId: pkg.id } : {} });
3000
+ addEntity({ id, kind: "module", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("code", root, absPath, 1, sourceFile.getLineAndCharacterOfPosition(sourceFile.getEnd()).line + 1)], ...exports.length ? { metadata: { exports, test: /(?:\.test|\.spec|__tests__)/.test(path) } } : {} });
3001
+ if (pkg) addRelation({ id: entityId("relation", `${pkg.id}:contains:${id}`), kind: "contains", from: pkg.id, to: id, provenance: "observed", evidence: [lineEvidence("code", root, absPath, 1)] });
3002
+ }
3003
+ for (const absPath of documentPaths) {
3004
+ const path = relativePath(root, absPath);
3005
+ addEntity({ id: entityId("document", path), kind: "document", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("documentation", root, absPath, 1)] });
3006
+ }
3007
+ const compiler = readCompilerOptions(root);
3008
+ const coverage = [
3009
+ ...[sourceWalk, documentWalk, configWalk].flatMap((walk, index) => walk.incomplete ? [{ analyzer: "repository", scope: `limits:${["source", "documentation", "configuration"][index]}`, status: "partial", reason: walk.reason }] : []),
3010
+ { analyzer: "repository", scope: "package-manager", status: hasPackageManagerMetadata(root, rootManifest) ? "complete" : "partial", ...!hasPackageManagerMetadata(root, rootManifest) ? { reason: `No package manager metadata found; default helper would fall back to ${detectPackageManager(root)}.` } : {} },
3011
+ { analyzer: "repository", scope: "workspace-packages", status: packageResult.coverage.some((item) => item.status === "partial") ? "partial" : "complete", ...packageResult.coverage.find((item) => item.reason)?.reason ? { reason: packageResult.coverage.find((item) => item.reason)?.reason } : {} },
3012
+ { analyzer: "js-ts", scope: "static-imports-and-exports", status: compiler.error ? "partial" : "complete", ...compiler.error ? { reason: compiler.error } : {} },
3013
+ { analyzer: "js-ts", scope: "dynamic-imports", status: "not-analyzed", reason: "Dynamic import expressions and non-literal require calls are not resolved." },
3014
+ { analyzer: "js-ts", scope: "runtime-wiring", status: "not-analyzed", reason: "Reflection, dependency injection and runtime wiring are not inferred." },
3015
+ { analyzer: "js-ts", scope: "generated-code", status: "not-analyzed", reason: "Generated code is not interpreted as source architecture." }
3016
+ ];
3017
+ for (const pkg of packageResult.packages) {
3018
+ const text = readFileSync8(pkg.manifestPath, "utf8");
3019
+ for (const dependency of dependencyEntries(pkg.manifest)) {
3020
+ const target = packageResult.packages.find((candidate) => candidate.name === dependency.name)?.id ?? entityId("external", dependency.name);
3021
+ if (!entities.has(target)) addEntity({ id: target, kind: "external", name: dependency.name, provenance: "observed", evidence: [lineEvidence("configuration", root, pkg.manifestPath, firstLineContaining(text, `"${dependency.name}"`))] });
3022
+ addRelation({ id: entityId("relation", `${pkg.id}:depends-on:${target}:${dependency.type}`), kind: "depends-on", from: pkg.id, to: target, provenance: "observed", evidence: [lineEvidence("configuration", root, pkg.manifestPath, firstLineContaining(text, `"${dependency.name}"`))], metadata: { dependencyType: dependency.type } });
3023
+ }
3024
+ }
3025
+ for (const module of modules.values()) {
3026
+ const text = readFileSync8(module.absPath, "utf8");
3027
+ const sourceFile = ts.createSourceFile(module.absPath, text, ts.ScriptTarget.Latest, true, scriptKind(module.absPath));
3028
+ const references = moduleReferences(root, module.absPath, sourceFile);
3029
+ for (const reference of references.references) {
3030
+ const target = resolveReference(reference, module.absPath, modules, packageResult.packages, compiler.options);
3031
+ if (!target) continue;
3032
+ if (!entities.has(target.targetId)) {
3033
+ const externalName = target.targetId.replace(/^external:/, "");
3034
+ addEntity({ id: target.targetId, kind: "external", name: externalName, provenance: "observed", evidence: [reference.evidence] });
3035
+ }
3036
+ addRelation({ id: entityId("relation", `${module.entityId}:${reference.kind}:${target.targetId}`), kind: reference.kind, from: module.entityId, to: target.targetId, provenance: "observed", evidence: [reference.evidence] });
3037
+ }
3038
+ if (references.hasDynamic) coverage.push({ analyzer: "js-ts", scope: `dynamic-imports:${module.path}`, status: "not-analyzed", reason: "A dynamic import or non-literal require was found.", evidence: [lineEvidence("code", root, module.absPath)] });
3039
+ if (references.hasRuntimeWiring) coverage.push({ analyzer: "js-ts", scope: `runtime-wiring:${module.path}`, status: "not-analyzed", reason: "A possible runtime registration/wiring call was found.", evidence: [lineEvidence("code", root, module.absPath)] });
3040
+ }
3041
+ return artifact(root, opts.config, allFiles, [...entities.values()], [...relations.values()], coverage);
3042
+ };
3043
+
3044
+ // src/agents/registry-adapter.ts
3045
+ import { existsSync as existsSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
3046
+ import { join as join9, resolve as resolve8 } from "path";
3047
+ import { pathToFileURL } from "url";
3048
+ import { z as z6 } from "zod";
3049
+ var DEFAULT_REGISTRY_AGENT_ID = "ecosystem-doc-bridge-corpus-scanner";
3050
+ var RegistryAgentMetadataSchema = z6.object({
3051
+ id: z6.string().min(1).max(256),
3052
+ version: z6.string().min(1).max(64),
3053
+ provider: z6.string().min(1).max(128).optional(),
3054
+ model: z6.string().min(1).max(256).optional(),
3055
+ capabilities: z6.array(z6.string().min(1).max(128)).max(32).default([])
3056
+ }).strict();
3057
+ var deepFreeze = (value) => {
3058
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
3059
+ Object.freeze(value);
3060
+ for (const child of Object.values(value)) deepFreeze(child);
3061
+ }
3062
+ return value;
3063
+ };
3064
+ var registryConfig = (config) => config.intelligence?.registry;
3065
+ var loadRegistryAgentRunner = async (root, config) => {
3066
+ const metadata = loadRegistryAgentMetadata(root, config);
3067
+ const configured = registryConfig(config)?.runnerModule;
3068
+ const modulePath = configured ? containedPath(root, configured) : containedPath(root, join9(metadata.root, "doc-bridge-adapter.js"));
3069
+ if (!modulePath || !existsSync10(modulePath)) throw new Error(`Registry agent "${metadata.id}" has no local runner module. Configure intelligence.registry.runnerModule or add doc-bridge-adapter.js to the installed agent.`);
3070
+ const loaded = await import(pathToFileURL(modulePath).href);
3071
+ const runner = typeof loaded.run === "function" ? loaded.run : typeof loaded.default === "function" ? loaded.default : loaded.default && typeof loaded.default === "object" && "run" in loaded.default && typeof loaded.default.run === "function" ? loaded.default.run : void 0;
3072
+ if (!runner) throw new Error(`Registry agent runner at ${modulePath} must export a function or { run }. `);
3073
+ return runner;
3074
+ };
3075
+ var loadRegistryAgentMetadata = (root, config) => {
3076
+ const settings = registryConfig(config);
3077
+ const id = settings?.agentId ?? DEFAULT_REGISTRY_AGENT_ID;
3078
+ const agentRoot = settings?.agentRoot ?? "agents";
3079
+ const agentPath = containedPath(root, join9(agentRoot, id));
3080
+ if (!agentPath || !existsSync10(agentPath)) throw new Error(`AgentsKit Registry agent "${id}" is not installed at ${join9(agentRoot, id)}. Install it with: npx agentskit add ${id}`);
3081
+ const metadataPath = [join9(agentPath, "agent.json"), join9(agentPath, "manifest.json")].find(existsSync10);
3082
+ if (!metadataPath) throw new Error(`Registry agent "${id}" is installed but has no agent.json or manifest.json metadata.`);
3083
+ const metadata = RegistryAgentMetadataSchema.parse(JSON.parse(readFileSync9(metadataPath, "utf8")));
3084
+ if (metadata.id !== id) throw new Error(`Installed Registry agent metadata id "${metadata.id}" does not match configured id "${id}".`);
3085
+ return { ...metadata, root: agentPath };
3086
+ };
3087
+ var createRegistryAgentAdapter = (root, config, runner) => {
3088
+ if (!registryConfig(config)?.enabled) throw new Error("Registry agents are disabled. Set intelligence.registry.enabled: true to run an assisted workflow.");
3089
+ const metadata = loadRegistryAgentMetadata(resolve8(root), config);
3090
+ return {
3091
+ metadata,
3092
+ run: async (snapshot, report, evidence2 = report.diagnostics.flatMap((diagnostic2) => diagnostic2.evidence).slice(0, 64)) => {
3093
+ const context = deepFreeze({ snapshot: redactValue(snapshot), report: redactValue(report), evidence: redactValue(evidence2), capabilities: ["snapshot.read", "evidence.read", "proposal.write"], network: false, shell: false });
3094
+ const proposal = AgentProposalV1Schema.parse(await runner(context));
3095
+ if (proposal.contentHash !== contentHashForArtifactV1(proposal)) throw new Error("Registry agent proposal contentHash does not match its canonical contents.");
3096
+ if (proposal.baseSnapshotHash !== snapshot.contentHash || proposal.baseReportHash !== report.contentHash) throw new Error("Registry agent proposal is not based on the supplied snapshot/report hashes.");
3097
+ if (proposal.origin.kind !== "registry-agent" || proposal.origin.id !== metadata.id) throw new Error(`Registry agent proposal origin must be ${metadata.id}.`);
3098
+ return proposal;
3099
+ }
3100
+ };
3101
+ };
3102
+ var persistRegistryAgentProposal = (stateDir, proposal) => {
3103
+ AgentProposalV1Schema.parse(proposal);
3104
+ if (proposal.contentHash !== contentHashForArtifactV1(proposal)) throw new Error("Cannot persist a Registry agent proposal with an invalid contentHash.");
3105
+ const safeHash = contentHashForArtifactV1(proposal);
3106
+ mkdirSync2(join9(resolve8(stateDir), "agents"), { recursive: true });
3107
+ const path = join9(resolve8(stateDir), "agents", `${proposal.origin.id}-${safeHash}.json`);
3108
+ writeFileSync2(path, `${JSON.stringify(proposal, null, 2)}
3109
+ `, "utf8");
3110
+ return path;
3111
+ };
3112
+
3113
+ // src/discovery/documentation.ts
3114
+ var detectionValues = /* @__PURE__ */ new Set(["static", "dynamic", "external"]);
3115
+ var evidence = (path, lineStart, lineEnd = lineStart) => ({
3116
+ source: "documentation",
3117
+ path,
3118
+ lineStart,
3119
+ lineEnd
3120
+ });
3121
+ var diagnostic = (path, code, message, lineStart, lineEnd = lineStart) => ({ code, message, path: "docbridge", evidence: evidence(path, lineStart, lineEnd) });
3122
+ var scalar = (value) => {
3123
+ const trimmed = value.trim();
3124
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
3125
+ return trimmed.slice(1, -1);
3126
+ }
3127
+ return trimmed;
3128
+ };
3129
+ var list = (value) => {
3130
+ const trimmed = value.trim();
3131
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
3132
+ const body = trimmed.slice(1, -1).trim();
3133
+ return body ? body.split(",").map(scalar).filter(Boolean) : [];
3134
+ };
3135
+ var findFrontmatter = (content) => {
3136
+ const lines = content.replace(/^\uFEFF/, "").split(/\r?\n/);
3137
+ if (lines[0] !== "---") return void 0;
3138
+ const end = lines.findIndex((line, index) => index > 0 && line === "---");
3139
+ return end < 0 ? void 0 : { lines, end };
3140
+ };
3141
+ var addDiagnostic = (diagnostics, input, code, message, line, endLine = line) => {
3142
+ diagnostics.push(diagnostic(input.path, code, message, line, endLine));
3143
+ };
3144
+ var resolveEntity = (reference, entities, input, lineStart, unresolved) => {
3145
+ const resolved = entities.find((entity2) => entity2.id === reference || entity2.aliases?.includes(reference));
3146
+ if (resolved) return resolved;
3147
+ const id = `unresolved:${reference}`;
3148
+ const existing = unresolved.get(id);
3149
+ if (existing) return existing;
3150
+ const entity = {
3151
+ id,
3152
+ kind: "unresolved-reference",
3153
+ name: reference,
3154
+ provenance: "declared",
3155
+ evidence: [evidence(input.path, lineStart)]
3156
+ };
3157
+ unresolved.set(id, entity);
3158
+ return entity;
3159
+ };
3160
+ var relationKey = (from, to, kind) => `${from}\0${to}\0${kind}`;
3161
+ var parseBlock = (input) => {
3162
+ const frontmatter = findFrontmatter(input.content);
3163
+ if (!frontmatter) {
3164
+ if (input.content.replace(/^\uFEFF/, "").startsWith("---")) {
3165
+ return {
3166
+ covers: [],
3167
+ relations: [],
3168
+ hasDocbridge: true,
3169
+ diagnostics: [diagnostic(input.path, "DOCBRIDGE_FRONTMATTER_MALFORMED", "Frontmatter must close with a line containing only ---.", 1)]
3170
+ };
3171
+ }
3172
+ return { covers: [], relations: [], diagnostics: [], hasDocbridge: false };
3173
+ }
3174
+ const { lines, end } = frontmatter;
3175
+ const docbridgeLine = lines.findIndex((line, index) => index > 0 && index < end && /^docbridge\s*:/.test(line));
3176
+ if (docbridgeLine < 0) return { covers: [], relations: [], diagnostics: [], hasDocbridge: false };
3177
+ const diagnostics = [];
3178
+ const covers = [];
3179
+ const relations = [];
3180
+ const inline = lines[docbridgeLine]?.slice("docbridge:".length).trim() ?? "";
3181
+ if (inline && inline !== "{}") addDiagnostic(diagnostics, input, "DOCBRIDGE_BLOCK_MALFORMED", "docbridge must be a nested frontmatter object.", docbridgeLine + 1);
3182
+ let section;
3183
+ let current;
3184
+ const finishRelation = () => {
3185
+ if (current) relations.push(current);
3186
+ current = void 0;
3187
+ };
3188
+ for (let index = docbridgeLine + 1; index < end; index += 1) {
3189
+ const raw = lines[index] ?? "";
3190
+ const trimmed = raw.trim();
3191
+ const line = index + 1;
3192
+ if (!trimmed || trimmed.startsWith("#")) continue;
3193
+ if (/\t/.test(raw)) {
3194
+ addDiagnostic(diagnostics, input, "DOCBRIDGE_INDENTATION_INVALID", "docbridge indentation must use spaces.", line);
3195
+ continue;
3196
+ }
3197
+ if (!raw.startsWith(" ")) {
3198
+ finishRelation();
3199
+ section = void 0;
3200
+ continue;
3201
+ }
3202
+ if (/^ {2}[A-Za-z][A-Za-z0-9_-]*\s*:/.test(raw)) {
3203
+ finishRelation();
3204
+ const match = /^ {2}([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(raw);
3205
+ const key = match?.[1];
3206
+ const value = match?.[2] ?? "";
3207
+ if (key !== "covers" && key !== "relations") {
3208
+ addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown docbridge field: ${key ?? "(missing)"}.`, line);
3209
+ section = void 0;
3210
+ } else if (key === "covers") {
3211
+ section = "covers";
3212
+ if (value) {
3213
+ const values = list(value);
3214
+ if (!values) addDiagnostic(diagnostics, input, "DOCBRIDGE_COVERS_INVALID", "covers must be a list of entity references.", line);
3215
+ else values.forEach((item) => covers.push({ value: item, line }));
3216
+ }
3217
+ } else {
3218
+ section = "relations";
3219
+ if (value) addDiagnostic(diagnostics, input, "DOCBRIDGE_RELATIONS_INVALID", "relations must be a list of relation objects.", line);
3220
+ }
3221
+ continue;
3222
+ }
3223
+ if (section === "covers" && /^ {4}-\s*/.test(raw)) {
3224
+ const value = scalar(raw.replace(/^ {4}-\s*/, ""));
3225
+ if (!value) addDiagnostic(diagnostics, input, "DOCBRIDGE_REFERENCE_MISSING", "covers entries must not be empty.", line);
3226
+ else covers.push({ value, line });
3227
+ continue;
3228
+ }
3229
+ if (section === "relations" && /^ {4}-\s*/.test(raw)) {
3230
+ finishRelation();
3231
+ const firstField = /^ {4}-\s*([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(raw);
3232
+ current = { startLine: line, endLine: line, fields: /* @__PURE__ */ new Set() };
3233
+ if (firstField?.[1]) {
3234
+ current.fields.add(firstField[1]);
3235
+ current[firstField[1]] = scalar(firstField[2] ?? "");
3236
+ } else if (raw.replace(/^ {4}-\s*/, "").trim()) {
3237
+ addDiagnostic(diagnostics, input, "DOCBRIDGE_RELATION_INVALID", "Relation entries must be field mappings.", line);
3238
+ }
3239
+ continue;
3240
+ }
3241
+ if (section === "relations" && current && /^ {6}[A-Za-z][A-Za-z0-9_-]*\s*:/.test(raw)) {
3242
+ const field = /^ {6}([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(raw);
3243
+ const key = field?.[1];
3244
+ current.endLine = line;
3245
+ if (!key || !["from", "to", "kind", "detection"].includes(key)) {
3246
+ addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown relation field: ${key ?? "(missing)"}.`, line);
3247
+ } else if (current.fields.has(key)) {
3248
+ addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_DUPLICATE", `Duplicate relation field: ${key}.`, line);
3249
+ } else {
3250
+ current.fields.add(key);
3251
+ current[key] = scalar(field?.[2] ?? "");
3252
+ }
3253
+ continue;
3254
+ }
3255
+ addDiagnostic(diagnostics, input, "DOCBRIDGE_STRUCTURE_INVALID", `Invalid docbridge structure at line ${line}.`, line);
3256
+ }
3257
+ finishRelation();
3258
+ return { covers, relations, diagnostics, hasDocbridge: true };
3259
+ };
3260
+ var parseDocumentationDeclarations = (input, options) => {
3261
+ const parsed = parseBlock(input);
3262
+ if (!parsed.hasDocbridge) return { hasDocbridge: false, entities: [], relations: [], diagnostics: [] };
3263
+ const diagnostics = [...parsed.diagnostics];
3264
+ const unresolved = /* @__PURE__ */ new Map();
3265
+ const relations = [];
3266
+ const documentId = options.documentId ?? `document:${input.path}`;
3267
+ const relationClaims = /* @__PURE__ */ new Map();
3268
+ if (!parsed.covers.length && !parsed.relations.length) {
3269
+ addDiagnostic(diagnostics, input, "DOCBRIDGE_CONTENT_MISSING", "docbridge must declare covers or relations.", 1);
3270
+ }
3271
+ for (const [index, cover] of parsed.covers.entries()) {
3272
+ const target = resolveEntity(cover.value, options.snapshot.entities, input, cover.line, unresolved);
3273
+ relations.push({
3274
+ id: `relation:declared:${input.path}:covers:${index}`,
3275
+ kind: "covers",
3276
+ from: documentId,
3277
+ to: target.id,
3278
+ provenance: "declared",
3279
+ evidence: [evidence(input.path, cover.line)]
3280
+ });
3281
+ }
3282
+ for (const [index, declaration] of parsed.relations.entries()) {
3283
+ const basePath = `relations[${index}]`;
3284
+ const missing = ["from", "to", "kind", "detection"].filter((field) => !declaration[field]);
3285
+ if (missing.length) {
3286
+ addDiagnostic(diagnostics, input, "DOCBRIDGE_RELATION_FIELD_MISSING", `Relation is missing required field(s): ${missing.join(", ")}.`, declaration.startLine, declaration.endLine);
3287
+ continue;
3288
+ }
3289
+ const from = declaration.from;
3290
+ const to = declaration.to;
3291
+ const kind = declaration.kind;
3292
+ const detection = declaration.detection;
3293
+ if (!detectionValues.has(detection)) {
3294
+ addDiagnostic(diagnostics, input, "DOCBRIDGE_DETECTION_INVALID", `Invalid relation detection: ${detection}.`, declaration.startLine, declaration.endLine);
3295
+ continue;
3296
+ }
3297
+ const fromEntity = resolveEntity(from, options.snapshot.entities, input, declaration.startLine, unresolved);
3298
+ const toEntity = resolveEntity(to, options.snapshot.entities, input, declaration.startLine, unresolved);
3299
+ const key = relationKey(fromEntity.id, toEntity.id, kind);
3300
+ const previousDetection = relationClaims.get(key);
3301
+ if (previousDetection === detection) addDiagnostic(diagnostics, input, "DOCBRIDGE_DECLARATION_DUPLICATE", "Duplicate relation declaration.", declaration.startLine, declaration.endLine);
3302
+ if (previousDetection && previousDetection !== detection) addDiagnostic(diagnostics, input, "DOCBRIDGE_DECLARATION_CONFLICT", "Conflicting relation declarations use different detection values.", declaration.startLine, declaration.endLine);
3303
+ relationClaims.set(key, previousDetection ?? detection);
3304
+ relations.push({
3305
+ id: `relation:declared:${input.path}:${basePath}`,
3306
+ kind,
3307
+ from: fromEntity.id,
3308
+ to: toEntity.id,
3309
+ discriminator: detection,
3310
+ provenance: "declared",
3311
+ evidence: [evidence(input.path, declaration.startLine, declaration.endLine)],
3312
+ metadata: { detection }
3313
+ });
3314
+ }
3315
+ return {
3316
+ hasDocbridge: true,
3317
+ entities: [...unresolved.values()].sort((a, b) => a.id.localeCompare(b.id)),
3318
+ relations,
3319
+ diagnostics
3320
+ };
3321
+ };
3322
+ var applyDocumentationDeclarations = (snapshot, documents) => {
3323
+ const entities = new Map(snapshot.entities.map((entity) => [entity.id, entity]));
3324
+ const relations = new Map(snapshot.relations.map((relation) => [relation.id, relation]));
3325
+ const diagnostics = [];
3326
+ for (const document of documents) {
3327
+ const result = parseDocumentationDeclarations(document, { snapshot });
3328
+ diagnostics.push(...result.diagnostics);
3329
+ for (const entity of result.entities) entities.set(entity.id, entity);
3330
+ for (const relation of result.relations) relations.set(relation.id, relation);
3331
+ }
3332
+ const base = { ...snapshot, contentHash: "0".repeat(64), entities: [...entities.values()], relations: [...relations.values()] };
3333
+ return {
3334
+ snapshot: DiscoverySnapshotV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) }),
3335
+ diagnostics
3336
+ };
3337
+ };
3338
+
3339
+ // src/reconciliation/reconcile.ts
3340
+ var ignoredDocumentationRelations = /* @__PURE__ */ new Set(["covers"]);
3341
+ var metadataDetection = (relation) => {
3342
+ const detection = relation.metadata?.detection;
3343
+ return typeof detection === "string" ? detection : void 0;
3344
+ };
3345
+ var relationDetection = (relation) => relation.discriminator ?? metadataDetection(relation) ?? "static";
3346
+ var entityResolver = (snapshots) => {
3347
+ const references = /* @__PURE__ */ new Map();
3348
+ const entities = snapshots.flatMap((snapshot) => snapshot.entities).sort((a, b) => a.id.localeCompare(b.id));
3349
+ for (const entity of entities) {
3350
+ references.set(entity.id, entity.id);
3351
+ for (const alias of entity.aliases ?? []) {
3352
+ if (!references.has(alias)) references.set(alias, entity.id);
3353
+ }
3354
+ }
3355
+ return (reference) => references.get(reference) ?? reference;
3356
+ };
3357
+ var relationBase = (relation, resolveEntity2) => `${resolveEntity2(relation.from)}\0${resolveEntity2(relation.to)}\0${relation.kind}`;
3358
+ var evidenceKey = (item) => `${item.source}:${item.path}:${item.lineStart ?? ""}:${item.lineEnd ?? ""}:${item.context ?? ""}`;
3359
+ var mergeEvidence = (...relations) => {
3360
+ const merged = /* @__PURE__ */ new Map();
3361
+ for (const relation of relations) {
3362
+ for (const item of relation.evidence) merged.set(evidenceKey(item), item);
3363
+ }
3364
+ return [...merged.values()].sort((a, b) => evidenceKey(a).localeCompare(evidenceKey(b)));
3365
+ };
3366
+ var diagnosticId = (code, value) => `reconciliation:${code}:${sha256NormalizedV1(value).slice(0, 32)}`;
3367
+ var coverageAvailable = (snapshot, relation) => {
3368
+ const status = snapshot.coverage.find(
3369
+ (entry) => entry.scope === "static-imports-and-exports" && (relation.kind === "imports" || relation.kind === "re-exports")
3370
+ )?.status;
3371
+ return status === void 0 || status === "complete";
3372
+ };
3373
+ var entityById = (snapshots) => {
3374
+ const entities = /* @__PURE__ */ new Map();
3375
+ for (const snapshot of snapshots) {
3376
+ for (const entity of snapshot.entities) if (!entities.has(entity.id)) entities.set(entity.id, entity);
3377
+ }
3378
+ return entities;
3379
+ };
3380
+ var isUnresolved = (id, entities) => id.startsWith("unresolved:") || entities.get(id)?.kind === "unresolved-reference";
3381
+ var relationMatches = (observed, declared, resolveEntity2) => {
3382
+ if (relationBase(observed, resolveEntity2) !== relationBase(declared, resolveEntity2)) return false;
3383
+ return relationDetection(declared) === relationDetection(observed);
3384
+ };
3385
+ var reportDiagnostic = (code, status, severity, message, evidence2, value, entityIds, relationIds, remediation) => ({
3386
+ id: diagnosticId(code, value),
3387
+ code,
3388
+ status,
3389
+ severity,
3390
+ message,
3391
+ evidence: [...evidence2],
3392
+ ...entityIds?.length ? { entityIds: [...entityIds] } : {},
3393
+ ...relationIds?.length ? { relationIds: [...relationIds] } : {},
3394
+ ...remediation ? { remediation } : {}
3395
+ });
3396
+ var reconcileKnowledge = (observed, declared) => {
3397
+ const resolveEntity2 = entityResolver([observed, declared]);
3398
+ const entities = entityById([observed, declared]);
3399
+ const observedRelations = observed.relations.filter((relation) => relation.provenance === "observed" && !ignoredDocumentationRelations.has(relation.kind));
3400
+ const declaredRelations = declared.relations.filter((relation) => relation.provenance === "declared" && !ignoredDocumentationRelations.has(relation.kind));
3401
+ const diagnostics = [];
3402
+ for (const entity of declared.entities.filter((item) => isUnresolved(item.id, entities)).sort((a, b) => a.id.localeCompare(b.id))) {
3403
+ diagnostics.push(reportDiagnostic(
3404
+ "UNRESOLVED_ENTITY_REFERENCE",
3405
+ "unresolved",
3406
+ "error",
3407
+ `Declared reference could not be resolved: ${entity.name}.`,
3408
+ entity.evidence,
3409
+ entity.id,
3410
+ [entity.id],
3411
+ void 0,
3412
+ "Resolve the reference to an observed entity ID or configured alias."
3413
+ ));
3414
+ }
3415
+ const declaredByBase = /* @__PURE__ */ new Map();
3416
+ for (const relation of declaredRelations) {
3417
+ const base2 = relationBase(relation, resolveEntity2);
3418
+ const group = declaredByBase.get(base2) ?? [];
3419
+ group.push(relation);
3420
+ declaredByBase.set(base2, group);
3421
+ }
3422
+ for (const group of declaredByBase.values()) {
3423
+ const detections = new Set(group.map(relationDetection));
3424
+ if (detections.size < 2) continue;
3425
+ diagnostics.push(reportDiagnostic(
3426
+ "CONFLICTING_DECLARATIONS",
3427
+ "conflict",
3428
+ "error",
3429
+ "Declarations for the same semantic relation disagree on detection.",
3430
+ mergeEvidence(...group),
3431
+ [relationBase(group[0], resolveEntity2), [...detections].sort()],
3432
+ void 0,
3433
+ group.map((relation) => relation.id),
3434
+ "Keep one detection value for this relation or document the intended distinction with a different relation kind."
3435
+ ));
3436
+ }
3437
+ for (const relation of observedRelations) {
3438
+ const candidates = declaredByBase.get(relationBase(relation, resolveEntity2)) ?? [];
3439
+ const match = candidates.find((candidate) => relationMatches(relation, candidate, resolveEntity2));
3440
+ if (match) {
3441
+ diagnostics.push(reportDiagnostic(
3442
+ "RELATION_CONFIRMED",
3443
+ "confirmed",
3444
+ "info",
3445
+ "Observed relation is covered by a matching declaration.",
3446
+ mergeEvidence(relation, match),
3447
+ relation.id,
3448
+ void 0,
3449
+ [relation.id, match.id]
3450
+ ));
3451
+ } else if (coverageAvailable(observed, relation)) {
3452
+ diagnostics.push(reportDiagnostic(
3453
+ "RELATION_UNDOCUMENTED",
3454
+ "undocumented",
3455
+ "warn",
3456
+ "Observed relation has no matching documentation declaration.",
3457
+ relation.evidence,
3458
+ relation.id,
3459
+ void 0,
3460
+ [relation.id],
3461
+ "Add a matching relation declaration or configure this relation kind as intentionally undocumented."
3462
+ ));
3463
+ }
3464
+ }
3465
+ for (const relation of declaredRelations) {
3466
+ const candidates = observedRelations.filter((candidate) => relationBase(candidate, resolveEntity2) === relationBase(relation, resolveEntity2));
3467
+ const detection = relationDetection(relation);
3468
+ if (candidates.some((candidate) => relationMatches(candidate, relation, resolveEntity2))) continue;
3469
+ if (isUnresolved(resolveEntity2(relation.from), entities) || isUnresolved(resolveEntity2(relation.to), entities)) continue;
3470
+ if (detection === "dynamic" || detection === "external") {
3471
+ diagnostics.push(reportDiagnostic(
3472
+ "RELATION_NOT_ANALYZED",
3473
+ "not-analyzed",
3474
+ "info",
3475
+ `Declared ${detection} relation cannot be verified by the current static analyzer.`,
3476
+ relation.evidence,
3477
+ relation.id,
3478
+ void 0,
3479
+ [relation.id],
3480
+ "Enable a compatible analyzer or provide explicit observed evidence before treating this relation as confirmed."
3481
+ ));
3482
+ } else if (coverageAvailable(observed, relation)) {
3483
+ diagnostics.push(reportDiagnostic(
3484
+ "DECLARED_RELATION_STALE",
3485
+ "stale-or-unverified",
3486
+ "warn",
3487
+ candidates.length ? "Declared relation has incompatible observed evidence." : "Declared static relation was not observed.",
3488
+ candidates.length ? mergeEvidence(relation, ...candidates) : relation.evidence,
3489
+ relation.id,
3490
+ void 0,
3491
+ [relation.id, ...candidates.map((candidate) => candidate.id)],
3492
+ "Update the declaration or the implementation so both graphs describe the same relation."
3493
+ ));
3494
+ }
3495
+ }
3496
+ const sortedDiagnostics = [...diagnostics].sort((a, b) => a.id.localeCompare(b.id));
3497
+ const base = {
3498
+ type: "reconciliation-report",
3499
+ schemaVersion: 1,
3500
+ contentHash: "0".repeat(64),
3501
+ contentHashAlgo: observed.contentHashAlgo,
3502
+ project: observed.project,
3503
+ sourceRevision: observed.sourceRevision,
3504
+ sourceRevisionKind: observed.sourceRevisionKind,
3505
+ configurationHash: observed.configurationHash,
3506
+ pipelineVersion: observed.pipelineVersion,
3507
+ analyzerVersions: observed.analyzerVersions,
3508
+ snapshotHash: observed.contentHash,
3509
+ diagnostics: sortedDiagnostics,
3510
+ summary: {
3511
+ entityCount: observed.entities.length,
3512
+ relationCount: observedRelations.length,
3513
+ diagnosticCount: sortedDiagnostics.length
3514
+ }
3515
+ };
3516
+ return ReconciliationReportV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
3517
+ };
3518
+
3519
+ // src/report/html.ts
3520
+ var escapeHtml = (value) => String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3521
+ var anchor = (prefix, value) => `${prefix}-${value.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
3522
+ var evidenceText = (evidence2, includeSnippets) => {
3523
+ const location = `${evidence2.path}${evidence2.lineStart ? `:${evidence2.lineStart}${evidence2.lineEnd && evidence2.lineEnd !== evidence2.lineStart ? `-${evidence2.lineEnd}` : ""}` : ""}`;
3524
+ return `${location}${includeSnippets && evidence2.context ? ` \u2014 ${redactSecrets(evidence2.context)}` : ""}`;
3525
+ };
3526
+ var errorPage = (message) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Doc Bridge report error</title><style>body{font:16px system-ui;margin:3rem;color:#311}main{max-width:60rem;margin:auto;border:1px solid #d99;padding:2rem;border-radius:8px;background:#fff8f8}code{white-space:pre-wrap}</style></head><body><main><h1>Doc Bridge report unavailable</h1><p>The saved snapshot/report could not be rendered.</p><code>${escapeHtml(message)}</code><p>Run <code>ak-docs check</code> to regenerate valid artifacts.</p></main></body></html>`;
3527
+ var embeddedJson = (value) => JSON.stringify(value).replaceAll("<", "\\u003c");
3528
+ var controls = (statusOptions) => `<section id="controls"><label>Search <input id="search" type="search" placeholder="entity, relation, diagnostic"></label><label>Severity <select id="severity"><option value="">Any</option><option>error</option><option>warn</option><option>info</option></select></label><label>Provenance <select id="provenance"><option value="">Any</option><option>observed</option><option>declared</option><option>proposed</option></select></label><label>Status <select id="status"><option value="">Any</option>${statusOptions.map((value) => `<option>${value}</option>`).join("")}</select></label><label>Analyzer <input id="analyzer" type="search" placeholder="js-ts, report"></label><label>Entity <input id="entity" type="search" placeholder="entity id"></label><label>Relation <input id="relation" type="search" placeholder="relation id"></label><button id="reset">Reset filters</button></section>`;
3529
+ var renderCompact = (snapshot, report, entityAnchors, includeSnippets) => {
3530
+ const entities = snapshot.entities.map((entity) => ({ id: entity.id, name: entity.name, kind: entity.kind, anchor: entityAnchors.get(entity.id) ?? anchor("entity", entity.id) }));
3531
+ const entityIndexes = new Map(entities.map((entity, index) => [entity.id, index]));
3532
+ const relations = snapshot.relations.map((relation) => ({ id: relation.id, from: entityIndexes.get(relation.from), to: entityIndexes.get(relation.to), kind: relation.kind, provenance: relation.provenance }));
3533
+ const diagnostics = report.diagnostics.map((diagnostic2) => ({
3534
+ ...diagnostic2,
3535
+ evidence: diagnostic2.evidence.map(({ context, ...evidence2 }) => includeSnippets && context ? { ...evidence2, context: redactSecrets(context) } : evidence2)
3536
+ }));
3537
+ const data = embeddedJson({ entities, relations, diagnostics, coverage: snapshot.coverage });
3538
+ const script = `const data=${data};const esc=(v)=>String(v).replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;').replaceAll('"','&quot;').replaceAll("'",'&#39;');const entityById=new Map(data.entities.map((e)=>[e.id,e]));const entityLink=(id)=>{const e=entityById.get(id);return '<a href="#'+esc(e?.anchor??'entity-'+id)+'">'+esc(e?.name??id)+'</a>'};const evidenceText=(e)=>esc(e.path+(e.lineStart?':'+e.lineStart+(e.lineEnd&&e.lineEnd!==e.lineStart?'-'+e.lineEnd:''):'')+(e.context?' \u2014 '+e.context:''));const render=()=>{document.querySelector('#map').innerHTML=data.entities.map((e)=>'<div class="node" id="'+esc(e.anchor)+'" data-node="'+esc(e.anchor)+'" data-search="'+esc([e.id,e.name,e.kind].join(' '))+'"><a href="#'+esc(e.anchor)+'">'+esc(e.name)+'</a><br><span class="muted">'+esc(e.kind)+'</span></div>').join('');document.querySelector('#relations').innerHTML=data.relations.map((r)=>{const from=data.entities[r.from],to=data.entities[r.to];return '<tr id="relation-'+esc(r.id)+'" data-relation="'+esc(r.id)+'" data-from="'+esc(from?.anchor??'')+'" data-to="'+esc(to?.anchor??'')+'" data-kind="'+esc(r.kind)+'" data-provenance="'+esc(r.provenance)+'" data-analyzer="snapshot" data-search="'+esc([r.id,from?.id,to?.id,r.kind,r.provenance].join(' '))+'"><td>'+esc(r.kind)+'</td><td>'+entityLink(from?.id??'')+'</td><td>'+entityLink(to?.id??'')+'</td><td>'+esc(r.provenance)+'</td></tr>'}).join('');document.querySelector('#diagnostics').innerHTML=data.diagnostics.length?data.diagnostics.map((d)=>'<article id="diagnostic-'+esc(d.id)+'" class="diagnostic" data-status="'+esc(d.status)+'" data-severity="'+esc(d.severity)+'" data-analyzer="report" data-entity="'+esc((d.entityIds??[]).join(' '))+'" data-relation="'+esc((d.relationIds??[]).join(' '))+'" data-search="'+esc([d.id,d.code,d.message,...(d.entityIds??[]),...(d.relationIds??[])].join(' '))+'"><h3><a href="#diagnostic-'+esc(d.id)+'">'+esc(d.code)+'</a> <span class="badge '+esc(d.severity)+'">'+esc(d.severity)+'</span></h3><p>'+esc(d.message)+'</p><p>Status: <b>'+esc(d.status)+'</b></p><ul>'+d.evidence.map((e)=>'<li>'+evidenceText(e)+'</li>').join('')+'</ul>'+(d.entityIds?.length?'<p>Entities: '+d.entityIds.map(entityLink).join(', ')+'</p>':'')+'</article>').join(''):'<p class="muted">No diagnostics.</p>';document.querySelector('#coverage').innerHTML=data.coverage.length?data.coverage.map((c)=>'<li data-status="'+esc(c.status)+'" data-analyzer="'+esc(c.analyzer)+'" data-search="'+esc([c.analyzer,c.scope,c.status,c.reason??''].join(' '))+'"><b>'+esc(c.analyzer)+'</b> / '+esc(c.scope)+': '+esc(c.status)+(c.reason?' \u2014 '+esc(c.reason):'')+'</li>').join(''):'<li class="muted">No coverage metadata.</li>';document.querySelectorAll('[data-node]').forEach((node)=>node.onclick=()=>{const id=node.dataset.node;document.querySelectorAll('[data-from],[data-to]').forEach((edge)=>edge.classList.toggle('hidden',edge.dataset.from!==id&&edge.dataset.to!==id))});};const apply=()=>{const q=document.querySelector('#search').value.toLowerCase(),severity=document.querySelector('#severity').value,provenance=document.querySelector('#provenance').value,status=document.querySelector('#status').value,analyzer=document.querySelector('#analyzer').value.toLowerCase(),entity=document.querySelector('#entity').value.toLowerCase(),relation=document.querySelector('#relation').value.toLowerCase();document.querySelectorAll('[data-search]').forEach((el)=>el.classList.toggle('hidden',Boolean(q&&!el.dataset.search.toLowerCase().includes(q)||severity&&el.dataset.severity!==severity||provenance&&el.dataset.provenance!==provenance||status&&el.dataset.status!==status||analyzer&&!el.dataset.analyzer?.toLowerCase().includes(analyzer)||entity&&!el.dataset.entity?.toLowerCase().includes(entity)||relation&&!el.dataset.relation?.toLowerCase().includes(relation))));};render();for(const id of ['search','analyzer','entity','relation'])document.querySelector('#'+id).oninput=apply;for(const id of ['severity','provenance','status'])document.querySelector('#'+id).onchange=apply;document.querySelector('#reset').onclick=()=>{for(const id of ['search','analyzer','entity','relation'])document.querySelector('#'+id).value='';for(const id of ['severity','provenance','status'])document.querySelector('#'+id).value='';apply()};`;
3539
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(snapshot.project.name)}</title><style>body{font:14px system-ui;margin:0;color:#18202a;background:#f5f7fa}header,main{max-width:1200px;margin:auto;padding:1.25rem}header{background:#18202a;color:white;max-width:none;padding-left:calc((100% - 1200px)/2);padding-right:calc((100% - 1200px)/2)}main{background:white}section{margin:1.5rem 0;border-top:1px solid #d7dde5;padding-top:1rem}table{border-collapse:collapse;width:100%;margin-top:.75rem}td,th{border-bottom:1px solid #e5e9ef;text-align:left;padding:.45rem;vertical-align:top}input,select,button{padding:.45rem;margin:.15rem;border:1px solid #b9c3d0;border-radius:4px;background:white}.badge{border-radius:1rem;padding:.15rem .5rem;background:#dce4ee}.error{background:#ffd9d9}.warn{background:#fff0c2}.info{background:#dcecff}.diagnostic{border:1px solid #d7dde5;border-left:4px solid #9aa7b5;padding:.75rem;margin:.75rem 0}.diagnostic:target,tr:target{background:#fff8cf}.muted{color:#5d6a78}a{color:#0b5cad}#map{display:flex;gap:1rem;flex-wrap:wrap}.node{border:1px solid #9aa7b5;padding:.5rem;border-radius:4px}.hidden{display:none}</style></head><body><header><h1>Doc Bridge: ${escapeHtml(snapshot.project.name)}</h1><p>Offline architecture and documentation reconciliation report</p><p class="muted">Snapshot ${escapeHtml(snapshot.contentHash)} \xB7 Report ${escapeHtml(report.contentHash)} \xB7 Revision ${escapeHtml(snapshot.sourceRevision)}</p></header><main>${controls(["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"])}<section id="architecture"><h2>Architecture map</h2><p>Large snapshots are rendered from compact canonical data after load to keep the offline report bounded.</p><div id="map"></div><h3>Relations</h3><table><thead><tr><th>Kind</th><th>From</th><th>To</th><th>Provenance</th></tr></thead><tbody id="relations"></tbody></table></section><section id="diagnostic-lens"><h2>Diagnostic lens</h2><p>Findings: ${report.diagnostics.length}</p><div id="diagnostics"></div></section><section id="coverage"><h2>Coverage and unsupported areas</h2><ul></ul></section><section id="metadata"><h2>Run metadata</h2><dl><dt>Source revision</dt><dd>${escapeHtml(snapshot.sourceRevision)} (${escapeHtml(snapshot.sourceRevisionKind)})</dd><dt>Configuration hash</dt><dd>${escapeHtml(snapshot.configurationHash)}</dd><dt>Pipeline</dt><dd>${escapeHtml(snapshot.pipelineVersion)}</dd></dl></section></main><script>${script}</script></body></html>`;
3540
+ };
3541
+ var render = (input, options) => {
3542
+ const { snapshot, report } = input;
3543
+ const includeSnippets = options.includeSnippets === true;
3544
+ const entityAnchors = new Map(snapshot.entities.map((entity, index) => [entity.id, entity.id.length > 64 ? `entity-n${index}` : anchor("entity", entity.id)]));
3545
+ if (snapshot.entities.length > 500 || report.diagnostics.length > 1e3) return renderCompact(snapshot, report, entityAnchors, includeSnippets);
3546
+ const entityNames = new Map(snapshot.entities.map((entity) => [entity.id, entity.name]));
3547
+ const entityAnchor = (id) => entityAnchors.get(id) ?? anchor("entity", id);
3548
+ const entityLabel = (id) => entityNames.get(id) ?? id;
3549
+ const entityLink = (id) => `<a href="#${entityAnchor(id)}">${escapeHtml(entityLabel(id))}</a>`;
3550
+ const relations = snapshot.relations.map((relation) => `<tr id="${anchor("relation", relation.id)}" data-relation="${escapeHtml(relation.id)}" data-from="${entityAnchor(relation.from)}" data-to="${entityAnchor(relation.to)}" data-kind="${escapeHtml(relation.kind)}" data-provenance="${escapeHtml(relation.provenance)}" data-analyzer="snapshot" data-search="${escapeHtml(`${relation.id} ${relation.from} ${relation.to} ${relation.kind} ${relation.provenance}`)}"><td>${escapeHtml(relation.kind)}</td><td>${entityLink(relation.from)}</td><td>${entityLink(relation.to)}</td><td>${escapeHtml(relation.provenance)}</td></tr>`).join("");
3551
+ const diagnostics = report.diagnostics.map((diagnostic2) => `<article id="${anchor("diagnostic", diagnostic2.id)}" class="diagnostic" data-status="${escapeHtml(diagnostic2.status)}" data-severity="${escapeHtml(diagnostic2.severity)}" data-analyzer="report" data-entity="${escapeHtml(diagnostic2.entityIds?.join(" ") ?? "")}" data-relation="${escapeHtml(diagnostic2.relationIds?.join(" ") ?? "")}" data-search="${escapeHtml(`${diagnostic2.id} ${diagnostic2.code} ${diagnostic2.message} ${diagnostic2.entityIds?.join(" ") ?? ""} ${diagnostic2.relationIds?.join(" ") ?? ""}`)}"><h3><a href="#${anchor("diagnostic", diagnostic2.id)}">${escapeHtml(diagnostic2.code)}</a> <span class="badge ${escapeHtml(diagnostic2.severity)}">${escapeHtml(diagnostic2.severity)}</span></h3><p>${escapeHtml(diagnostic2.message)}</p><p>Status: <b>${escapeHtml(diagnostic2.status)}</b></p><ul>${diagnostic2.evidence.map((item) => `<li>${escapeHtml(evidenceText(item, includeSnippets))}</li>`).join("")}</ul>${diagnostic2.entityIds?.length ? `<p>Entities: ${diagnostic2.entityIds.map((id) => entityLink(id)).join(", ")}</p>` : ""}</article>`).join("");
3552
+ const coverage = snapshot.coverage.map((entry) => `<li data-status="${escapeHtml(entry.status)}" data-analyzer="${escapeHtml(entry.analyzer)}" data-search="${escapeHtml(`${entry.analyzer} ${entry.scope} ${entry.status} ${entry.reason ?? ""}`)}"><b>${escapeHtml(entry.analyzer)}</b> / ${escapeHtml(entry.scope)}: ${escapeHtml(entry.status)}${entry.reason ? ` \u2014 ${escapeHtml(entry.reason)}` : ""}</li>`).join("");
3553
+ const nodes = snapshot.entities.map((entity) => `<div class="node" id="${entityAnchor(entity.id)}" data-node="${entityAnchor(entity.id)}"><a href="#${entityAnchor(entity.id)}">${escapeHtml(entity.name)}</a><br><span class="muted">${escapeHtml(entity.kind)}</span></div>`).join("");
3554
+ const script = `const q=document.querySelector('#search'),severity=document.querySelector('#severity'),provenance=document.querySelector('#provenance'),status=document.querySelector('#status'),analyzer=document.querySelector('#analyzer'),entity=document.querySelector('#entity'),relation=document.querySelector('#relation');function apply(){const term=q.value.toLowerCase(),entityTerm=entity.value.toLowerCase(),relationTerm=relation.value.toLowerCase();document.querySelectorAll('[data-search]').forEach((el)=>{const match=(!term||el.dataset.search.toLowerCase().includes(term))&&(!severity.value||el.dataset.severity===severity.value)&&(!provenance.value||el.dataset.provenance===provenance.value)&&(!status.value||el.dataset.status===status.value)&&(!analyzer.value||el.dataset.analyzer?.toLowerCase().includes(analyzer.value.toLowerCase()))&&(!entityTerm||el.dataset.entity?.toLowerCase().includes(entityTerm))&&(!relationTerm||el.dataset.relation?.toLowerCase().includes(relationTerm));el.classList.toggle('hidden',!match)});}q.oninput=apply;severity.onchange=apply;provenance.onchange=apply;status.onchange=apply;analyzer.oninput=apply;entity.oninput=apply;relation.oninput=apply;document.querySelector('#reset').onclick=()=>{q.value='';severity.value='';provenance.value='';status.value='';analyzer.value='';entity.value='';relation.value='';apply()};document.querySelectorAll('[data-node]').forEach((node)=>node.onclick=()=>{const id=node.dataset.node;document.querySelectorAll('[data-from],[data-to]').forEach((edge)=>edge.classList.toggle('hidden',edge.dataset.from!==id&&edge.dataset.to!==id));});`;
3555
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(snapshot.project.name)}</title><style>body{font:14px system-ui;margin:0;color:#18202a;background:#f5f7fa}header,main{max-width:1200px;margin:auto;padding:1.25rem}header{background:#18202a;color:white;max-width:none;padding-left:calc((100% - 1200px)/2);padding-right:calc((100% - 1200px)/2)}main{background:white}section{margin:1.5rem 0;border-top:1px solid #d7dde5;padding-top:1rem}table{border-collapse:collapse;width:100%;margin-top:.75rem}td,th{border-bottom:1px solid #e5e9ef;text-align:left;padding:.45rem;vertical-align:top}input,select,button{padding:.45rem;margin:.15rem;border:1px solid #b9c3d0;border-radius:4px;background:white}.badge{border-radius:1rem;padding:.15rem .5rem;background:#dce4ee}.error{background:#ffd9d9}.warn{background:#fff0c2}.info{background:#dcecff}.diagnostic{border:1px solid #d7dde5;border-left:4px solid #9aa7b5;padding:.75rem;margin:.75rem 0}.diagnostic:target,tr:target{background:#fff8cf}.muted{color:#5d6a78}a{color:#0b5cad}#map{display:flex;gap:1rem;flex-wrap:wrap}.node{border:1px solid #9aa7b5;padding:.5rem;border-radius:4px}.hidden{display:none}</style></head><body><header><h1>Doc Bridge: ${escapeHtml(snapshot.project.name)}</h1><p>Offline architecture and documentation reconciliation report</p><p class="muted">Snapshot ${escapeHtml(snapshot.contentHash)} \xB7 Report ${escapeHtml(report.contentHash)} \xB7 Revision ${escapeHtml(snapshot.sourceRevision)}</p></header><main><section id="controls"><label>Search <input id="search" type="search" placeholder="entity, relation, diagnostic"></label><label>Severity <select id="severity"><option value="">Any</option><option>error</option><option>warn</option><option>info</option></select></label><label>Provenance <select id="provenance"><option value="">Any</option><option>observed</option><option>declared</option><option>proposed</option></select></label><label>Status <select id="status"><option value="">Any</option>${["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"].map((value) => `<option>${value}</option>`).join("")}</select></label><label>Analyzer <input id="analyzer" type="search" placeholder="js-ts, report"></label><label>Entity <input id="entity" type="search" placeholder="entity id"></label><label>Relation <input id="relation" type="search" placeholder="relation id"></label><button id="reset">Reset filters</button></section><section id="architecture"><h2>Architecture map</h2><p>Observed and declared relations are rendered from the canonical snapshot; browser code does not infer edges.</p><div id="map">${nodes}</div><h3>Relations</h3><table><thead><tr><th>Kind</th><th>From</th><th>To</th><th>Provenance</th></tr></thead><tbody id="relations">${relations}</tbody></table></section><section id="diagnostic-lens"><h2>Diagnostic lens</h2><p>Findings: ${report.diagnostics.length}</p><div id="diagnostics">${diagnostics || '<p class="muted">No diagnostics.</p>'}</div></section><section id="coverage"><h2>Coverage and unsupported areas</h2><ul>${coverage || "<li>No coverage metadata.</li>"}</ul></section><section id="metadata"><h2>Run metadata</h2><dl><dt>Source revision</dt><dd>${escapeHtml(snapshot.sourceRevision)} (${escapeHtml(snapshot.sourceRevisionKind)})</dd><dt>Configuration hash</dt><dd>${escapeHtml(snapshot.configurationHash)}</dd><dt>Pipeline</dt><dd>${escapeHtml(snapshot.pipelineVersion)}</dd></dl></section></main><script>${script}</script></body></html>`;
3556
+ };
3557
+ var renderOfflineReport = (input, options = {}) => {
3558
+ try {
3559
+ if (!input || typeof input !== "object") throw new Error("Input must contain snapshot and report artifacts.");
3560
+ const value = input;
3561
+ const snapshot = DiscoverySnapshotV1Schema.parse(value.snapshot);
3562
+ const report = ReconciliationReportV1Schema.parse(value.report);
3563
+ if (report.snapshotHash !== snapshot.contentHash) throw new Error("Report snapshotHash does not match snapshot contentHash.");
3564
+ return render({ snapshot, report }, options);
3565
+ } catch (error) {
3566
+ return errorPage(error instanceof Error ? error.message : String(error));
3567
+ }
3568
+ };
3569
+
3570
+ // src/fixes/proposals.ts
3571
+ import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync10, realpathSync as realpathSync7, renameSync, statSync as statSync4, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
3572
+ import { basename as basename5, dirname as dirname6, extname as extname2, join as join10, relative as relative6, resolve as resolve9, sep as sep7 } from "path";
3573
+ var hash2 = (value) => sha256NormalizedV1(value);
3574
+ var artifactMetadata = (root, options) => ({
3575
+ schemaVersion: 1,
3576
+ contentHash: "0".repeat(64),
3577
+ contentHashAlgo: "sha256-normalized-v1",
3578
+ project: { name: options.projectName ?? basename5(resolve9(root)), root: "." },
3579
+ sourceRevision: options.baseRevision,
3580
+ sourceRevisionKind: options.baseRevision.length === 40 ? "git" : "content",
3581
+ configurationHash: options.configurationHash,
3582
+ pipelineVersion: "1.0.0",
3583
+ analyzerVersions: { fixes: options.toolVersion ?? "1.0.0" }
3584
+ });
3585
+ var unifiedDiff = (changes) => changes.map((change) => {
3586
+ const before = change.before.split("\n").map((line) => `-${line}`).join("\n");
3587
+ const after = change.after.split("\n").map((line) => `+${line}`).join("\n");
3588
+ return `--- a/${change.path}
3589
+ +++ b/${change.path}
3590
+ @@
3591
+ ${before}
3592
+ ${after}`;
3593
+ }).join("\n");
3594
+ var makeProposal = (root, options, changes, preconditions, postconditions) => {
3595
+ const draft = {
3596
+ ...artifactMetadata(root, options),
3597
+ type: "fix-proposal",
3598
+ proposalId: `fix-${hash2(changes).slice(0, 20)}`,
3599
+ baseRevision: options.baseRevision,
3600
+ affectedFiles: changes.map(({ path, before }) => ({ path, contentHash: sha256NormalizedV1(before) })),
3601
+ changes,
3602
+ preconditions,
3603
+ diff: unifiedDiff(changes),
3604
+ postconditions,
3605
+ status: "proposed"
3606
+ };
3607
+ return FixProposalV1Schema.parse({ ...draft, contentHash: contentHashForArtifactV1(draft) });
3608
+ };
3609
+ var walkMarkdown = (root, directory = root) => readdirSync4(directory, { withFileTypes: true }).flatMap((entry) => {
3610
+ if (entry.name === ".git" || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build") return [];
3611
+ const path = join10(directory, entry.name);
3612
+ if (entry.isDirectory()) return walkMarkdown(root, path);
3613
+ return entry.isFile() && [".md", ".mdx"].includes(extname2(entry.name).toLowerCase()) ? [relative6(root, path).split(sep7).join("/")] : [];
3614
+ });
3615
+ var localLink = /(!?)\[([^\]]*)\]\(([^)\s]+)(?:\s+["'][^)]*["'])?\)/g;
3616
+ var sortJson = (value) => Array.isArray(value) ? value.map(sortJson) : value && typeof value === "object" ? Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, sortJson(item)])) : value;
3617
+ var createMarkdownLinkFixProposal = (root, options) => {
3618
+ const projectRoot = realpathSync7.native(resolve9(root));
3619
+ const paths = walkMarkdown(projectRoot);
3620
+ const changes = [];
3621
+ for (const path of paths) {
3622
+ const content = readFileSync10(join10(projectRoot, path), "utf8");
3623
+ let next = content;
3624
+ for (const match of content.matchAll(localLink)) {
3625
+ const target = match[3];
3626
+ if (!target || match[1] === "!" || /^(?:[a-z]+:|\/|#)/i.test(target)) continue;
3627
+ const targetPath = target.split("#")[0]?.split("?")[0];
3628
+ if (!targetPath || existsSync11(resolve9(projectRoot, dirname6(path), targetPath))) continue;
3629
+ const targetStem = basename5(targetPath, extname2(targetPath)).toLowerCase();
3630
+ const labelStem = (match[2] ?? "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
3631
+ const candidates = paths.filter((candidate) => basename5(candidate, extname2(candidate)).toLowerCase() === targetStem || basename5(candidate, extname2(candidate)).toLowerCase().replace(/[^a-z0-9]+/g, "") === labelStem);
3632
+ if (candidates.length !== 1) continue;
3633
+ let replacement = relative6(dirname6(path), candidates[0]).split(sep7).join("/");
3634
+ if (target.startsWith("./") && !replacement.startsWith(".")) replacement = `./${replacement}`;
3635
+ next = next.replace(match[0], match[0].replace(target, replacement));
3636
+ }
3637
+ if (next !== content) changes.push({ path, before: content, after: next });
3638
+ }
3639
+ return changes.length ? makeProposal(projectRoot, options, changes, ["Each replacement has exactly one Markdown target."], ["All corrected local Markdown links resolve."]) : void 0;
3640
+ };
3641
+ var createArtifactNormalizationProposal = (root, artifactPath, options) => {
3642
+ const projectRoot = realpathSync7.native(resolve9(root));
3643
+ const path = artifactPath.split(sep7).join("/");
3644
+ const absolute = containedPath(projectRoot, path);
3645
+ if (!absolute || !existsSync11(absolute) || !statSync4(absolute).isFile()) return void 0;
3646
+ const before = readFileSync10(absolute, "utf8");
3647
+ let after;
3648
+ try {
3649
+ after = `${JSON.stringify(sortJson(JSON.parse(before)), null, 2)}
3650
+ `;
3651
+ } catch {
3652
+ return void 0;
3653
+ }
3654
+ return after === before ? void 0 : makeProposal(projectRoot, options, [{ path: relative6(projectRoot, absolute).split(sep7).join("/"), before, after }], ["The artifact contains valid JSON."], ["The artifact is valid canonical JSON with one trailing newline."]);
3655
+ };
3656
+ var approveFixProposal = (proposalInput, approvedBy, approvedAt = (/* @__PURE__ */ new Date()).toISOString()) => {
3657
+ const proposal = FixProposalV1Schema.parse(proposalInput);
3658
+ if (proposal.status !== "proposed") throw new Error(`Only proposed fixes can be approved; received "${proposal.status}".`);
3659
+ const approved = { ...proposal, status: "approved", approval: { proposalHash: proposal.contentHash, approvedAt, approvedBy }, contentHash: "0".repeat(64) };
3660
+ return FixProposalV1Schema.parse({ ...approved, contentHash: contentHashForArtifactV1(approved) });
3661
+ };
3662
+ var bindingHash = (proposal) => {
3663
+ const { approval: _approval, contentHash: _contentHash, status: _status, ...rest } = proposal;
3664
+ return contentHashForArtifactV1({ ...rest, contentHash: "0".repeat(64), status: "proposed" });
3665
+ };
3666
+ var applyFixProposal = (root, proposalInput, options = {}) => {
3667
+ const proposal = FixProposalV1Schema.parse(proposalInput);
3668
+ if (proposal.status !== "approved" || !proposal.approval) throw new Error("Only an explicitly approved fix proposal can be applied.");
3669
+ if (proposal.approval.proposalHash !== bindingHash(proposal)) throw new Error("Approval is not bound to the exact proposal content.");
3670
+ if (options.currentRevision && options.currentRevision !== proposal.baseRevision) throw new Error("The repository revision changed since this proposal was created.");
3671
+ if (!proposal.changes?.length) throw new Error("This proposal has no executable changes.");
3672
+ const projectRoot = realpathSync7.native(resolve9(root));
3673
+ const originals = /* @__PURE__ */ new Map();
3674
+ const affected = new Map(proposal.affectedFiles.map((file) => [file.path, file]));
3675
+ if (proposal.changes.some((change) => !affected.has(change.path) || affected.get(change.path)?.contentHash !== sha256NormalizedV1(change.before)) || affected.size !== proposal.changes.length) {
3676
+ throw new Error("Proposal changes do not match its affected-file hashes.");
3677
+ }
3678
+ for (const file of proposal.affectedFiles) {
3679
+ const absolute = containedPath(projectRoot, file.path);
3680
+ if (!absolute || !existsSync11(absolute)) throw new Error(`Affected file is unavailable or escapes the repository root: ${file.path}`);
3681
+ const current = readFileSync10(absolute, "utf8");
3682
+ if (sha256NormalizedV1(current) !== file.contentHash) throw new Error(`Affected file changed since proposal creation: ${file.path}`);
3683
+ originals.set(absolute, current);
3684
+ }
3685
+ try {
3686
+ for (const change of proposal.changes) {
3687
+ const absolute = resolve9(projectRoot, change.path);
3688
+ writeFileSync3(`${absolute}.docbridge-${process.pid}.tmp`, change.after, "utf8");
3689
+ }
3690
+ for (const change of proposal.changes) {
3691
+ const absolute = resolve9(projectRoot, change.path);
3692
+ renameSync(`${absolute}.docbridge-${process.pid}.tmp`, absolute);
3693
+ }
3694
+ for (const change of proposal.changes) {
3695
+ if (readFileSync10(resolve9(projectRoot, change.path), "utf8") !== change.after) throw new Error(`Postcondition failed for ${change.path}`);
3696
+ }
3697
+ options.verify?.(proposal.changes.map((change) => change.path));
3698
+ } catch (error) {
3699
+ for (const [absolute, content] of originals) writeFileSync3(absolute, content, "utf8");
3700
+ for (const change of proposal.changes) {
3701
+ const temp = `${resolve9(projectRoot, change.path)}.docbridge-${process.pid}.tmp`;
3702
+ if (existsSync11(temp)) unlinkSync(temp);
3703
+ }
3704
+ throw error;
3705
+ }
3706
+ const applied = { ...proposal, status: "applied", contentHash: "0".repeat(64) };
3707
+ return FixProposalV1Schema.parse({ ...applied, contentHash: contentHashForArtifactV1(applied) });
3708
+ };
3709
+
3710
+ // src/workflow/engine.ts
3711
+ import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync3, readFileSync as readFileSync11, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync4 } from "fs";
3712
+ import { join as join11, relative as relative7, resolve as resolve10 } from "path";
3713
+ var WORKFLOW_STAGES = ["collect", "normalize", "reconcile", "evaluate", "report"];
3714
+ var stageState = {
3715
+ collect: "discovering",
3716
+ normalize: "analyzed",
3717
+ reconcile: "compared",
3718
+ evaluate: "proposed",
3719
+ report: "delivered"
3720
+ };
3721
+ var defaultStateDir = (root) => join11(root, ".doc-bridge", "workflow");
3722
+ var atomicWrite = (path, value) => {
3723
+ const temp = `${path}.tmp-${process.pid}-${Date.now()}`;
3724
+ writeFileSync4(temp, `${JSON.stringify(value, null, 2)}
3725
+ `, "utf8");
3726
+ renameSync2(temp, path);
3727
+ };
3728
+ var writeManifest = (stateDir, run2) => {
3729
+ atomicWrite(join11(stateDir, "manifest.json"), run2);
3730
+ };
3731
+ var appendTransition = (stateDir, transition2) => {
3732
+ appendFileSync(join11(stateDir, "transitions.jsonl"), `${JSON.stringify(transition2)}
3733
+ `, "utf8");
3734
+ };
3735
+ var transition = (run2, to, reason) => {
3736
+ const item = {
3737
+ from: run2.state,
3738
+ to,
3739
+ at: (/* @__PURE__ */ new Date()).toISOString(),
3740
+ ...reason ? { reason } : {}
3741
+ };
3742
+ return { ...run2, state: to, transitions: [...run2.transitions, item] };
3743
+ };
3744
+ var runId = () => `${Date.now()}-${process.pid}`;
3745
+ var stageInputHash = (options, stage, input) => sha256NormalizedV1({ stage, input, sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? "1.0.0" });
3746
+ var stageArtifactPath = (stateDir, stage, inputHash) => join11(stateDir, "artifacts", `${stage}-${inputHash}.json`);
3747
+ var readArtifact = (path) => JSON.parse(readFileSync11(path, "utf8"));
3748
+ var stepArtifactPath = (stateDir, step) => resolve10(stateDir, step.artifactRefs?.[0] ?? "");
3749
+ var stepOutput = (stateDir, run2, stage) => {
3750
+ const step = run2.steps.find((item) => item.name === stage);
3751
+ if (!step || step.status !== "completed" || !step.artifactRefs?.[0]) return null;
3752
+ return readArtifact(stepArtifactPath(stateDir, step)).value;
3753
+ };
3754
+ var acquireLock = (stateDir) => {
3755
+ const lock = join11(stateDir, ".lock");
3756
+ try {
3757
+ mkdirSync3(lock);
3758
+ } catch {
3759
+ const ownerPath = join11(lock, "owner.json");
3760
+ try {
3761
+ const owner = JSON.parse(readFileSync11(ownerPath, "utf8"));
3762
+ if (typeof owner.pid === "number") process.kill(owner.pid, 0);
3763
+ throw new Error(`Workflow is already running (pid ${owner.pid ?? "unknown"}).`);
3764
+ } catch (error) {
3765
+ if (error instanceof Error && error.message.startsWith("Workflow is already running")) throw error;
3766
+ rmSync(lock, { recursive: true, force: true });
3767
+ mkdirSync3(lock);
3768
+ }
3769
+ }
3770
+ writeFileSync4(join11(lock, "owner.json"), JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
3771
+ return () => rmSync(lock, { recursive: true, force: true });
3772
+ };
3773
+ var loadManifest = (stateDir) => {
3774
+ const path = join11(stateDir, "manifest.json");
3775
+ if (!existsSync12(path)) return void 0;
3776
+ return WorkflowRunV1Schema.parse(JSON.parse(readFileSync11(path, "utf8")));
3777
+ };
3778
+ var baseRun = (options, stateDir, supersedes) => {
3779
+ const inputHash = sha256NormalizedV1({ sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? "1.0.0" });
3780
+ return WorkflowRunV1Schema.parse({
3781
+ type: "workflow-run",
3782
+ schemaVersion: 1,
3783
+ contentHash: "0".repeat(64),
3784
+ contentHashAlgo: "sha256-normalized-v1",
3785
+ project: { name: resolve10(options.root).split("/").pop() ?? "project", root: "." },
3786
+ sourceRevision: options.sourceRevision,
3787
+ sourceRevisionKind: "content",
3788
+ configurationHash: options.configurationHash,
3789
+ pipelineVersion: "1.0.0",
3790
+ analyzerVersions: { workflow: options.toolVersion ?? "1.0.0" },
3791
+ runId: options.runId ?? runId(),
3792
+ state: "created",
3793
+ steps: WORKFLOW_STAGES.map((name) => ({ name, status: "pending", inputHash })),
3794
+ transitions: [{ from: null, to: "created", at: (/* @__PURE__ */ new Date()).toISOString() }],
3795
+ artifactRefs: [relative7(resolve10(options.root), stateDir), ...supersedes ? [`supersedes:${supersedes}`] : []]
3796
+ });
3797
+ };
3798
+ var withHash = (run2) => WorkflowRunV1Schema.parse({ ...run2, contentHash: contentHashForArtifactV1(run2) });
3799
+ var sameInputs = (run2, options) => run2.sourceRevision === options.sourceRevision && run2.configurationHash === options.configurationHash && run2.analyzerVersions.workflow === (options.toolVersion ?? "1.0.0");
3800
+ var selectedStages = (stage) => stage && stage !== "all" ? [stage] : WORKFLOW_STAGES;
3801
+ var runWorkflow = (options) => {
3802
+ const root = resolve10(options.root);
3803
+ const stateDir = resolve10(root, options.stateDir ?? defaultStateDir(root));
3804
+ mkdirSync3(join11(stateDir, "artifacts"), { recursive: true });
3805
+ const release = acquireLock(stateDir);
3806
+ try {
3807
+ let run2 = loadManifest(stateDir);
3808
+ let supersedes;
3809
+ if (run2 && !sameInputs(run2, options)) {
3810
+ supersedes = run2.runId;
3811
+ run2 = withHash(transition(run2, "stale", "Source revision, configuration hash, or tool version changed."));
3812
+ appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
3813
+ writeManifest(stateDir, run2);
3814
+ run2 = void 0;
3815
+ }
3816
+ if (!run2) {
3817
+ run2 = withHash(baseRun(options, stateDir, supersedes));
3818
+ appendTransition(stateDir, run2.transitions[0]);
3819
+ writeManifest(stateDir, run2);
3820
+ }
3821
+ if (!run2) throw new Error("Workflow manifest was not initialized.");
3822
+ const firstSelectedStage = selectedStages(options.stage)[0];
3823
+ const previousStageIndex = firstSelectedStage ? WORKFLOW_STAGES.indexOf(firstSelectedStage) - 1 : -1;
3824
+ let previousOutput = previousStageIndex >= 0 ? stepOutput(stateDir, run2, WORKFLOW_STAGES[previousStageIndex]) : null;
3825
+ const reusedStages = [];
3826
+ for (const stage of selectedStages(options.stage)) {
3827
+ const input = options.inputs?.[stage] ?? previousOutput;
3828
+ const inputHash = stageInputHash(options, stage, input);
3829
+ const existing = run2.steps.find((step) => step.name === stage);
3830
+ const artifactPath = existing?.artifactRefs?.[0] ? resolve10(stateDir, existing.artifactRefs[0]) : stageArtifactPath(stateDir, stage, inputHash);
3831
+ if (existing?.status === "completed" && existing.inputHash === inputHash && existing.outputHash && existsSync12(artifactPath)) {
3832
+ previousOutput = readArtifact(artifactPath).value;
3833
+ reusedStages.push(stage);
3834
+ continue;
3835
+ }
3836
+ const handler = options.handlers[stage];
3837
+ if (!handler) throw new Error(`No handler configured for workflow stage "${stage}".`);
3838
+ run2 = withHash(transition(run2, stageState[stage]));
3839
+ appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
3840
+ const runningStep = { name: stage, status: "running", inputHash };
3841
+ run2 = withHash({ ...run2, steps: run2.steps.map((step) => step.name === stage ? runningStep : step) });
3842
+ writeManifest(stateDir, run2);
3843
+ try {
3844
+ const value = handler({ root, stage, input, previousOutput });
3845
+ const outputHash = sha256NormalizedV1(value);
3846
+ const artifact2 = { type: "workflow-step-artifact", stage, inputHash, outputHash, value };
3847
+ mkdirSync3(join11(stateDir, "artifacts"), { recursive: true });
3848
+ if (existsSync12(artifactPath)) {
3849
+ const existingArtifact = readArtifact(artifactPath);
3850
+ if (existingArtifact.outputHash !== outputHash) throw new Error(`Immutable workflow artifact collision for stage "${stage}".`);
3851
+ } else {
3852
+ atomicWrite(artifactPath, artifact2);
3853
+ }
3854
+ const ref = relative7(stateDir, artifactPath);
3855
+ const completedStep = { name: stage, status: "completed", inputHash, outputHash, artifactRefs: [ref] };
3856
+ run2 = withHash({ ...run2, steps: run2.steps.map((step) => step.name === stage ? completedStep : step) });
3857
+ writeManifest(stateDir, run2);
3858
+ previousOutput = value;
3859
+ } catch (error) {
3860
+ run2 = withHash(transition(run2, "failed", error instanceof Error ? error.message : String(error)));
3861
+ appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
3862
+ writeManifest(stateDir, run2);
3863
+ throw error;
3864
+ }
3865
+ }
3866
+ if (selectedStages(options.stage).every((stage) => run2.steps.find((step) => step.name === stage)?.status === "completed")) {
3867
+ const complete = selectedStages(options.stage).includes("report") && run2.state !== "delivered" ? withHash(transition(run2, "delivered")) : run2;
3868
+ if (complete !== run2) {
3869
+ appendTransition(stateDir, complete.transitions[complete.transitions.length - 1]);
3870
+ run2 = complete;
3871
+ }
3872
+ writeManifest(stateDir, run2);
3873
+ if (run2.state === "delivered") atomicWrite(join11(stateDir, "last-known-good.json"), { runId: run2.runId, manifestHash: run2.contentHash, report: run2.steps.find((step) => step.name === "report")?.artifactRefs?.[0] });
3874
+ }
3875
+ return { run: run2, stateDir, reusedStages };
3876
+ } finally {
3877
+ release();
3878
+ }
3879
+ };
3880
+ var loadWorkflowManifest = (stateDir) => WorkflowRunV1Schema.parse(JSON.parse(readFileSync11(join11(resolve10(stateDir), "manifest.json"), "utf8")));
3881
+ var loadWorkflowStepOutput = (stateDir, stage) => stepOutput(resolve10(stateDir), loadWorkflowManifest(stateDir), stage);
3882
+
3883
+ // src/rules/engine.ts
3884
+ import { minimatch as minimatch5 } from "minimatch";
3885
+ var diagnosticRules = {
3886
+ DOCUMENTATION_QUALITY: "documentation-quality",
3887
+ RELATION_UNDOCUMENTED: "graph-undocumented-relation",
3888
+ DECLARED_RELATION_STALE: "declared-unobserved-relation",
3889
+ UNRESOLVED_ENTITY_REFERENCE: "unresolved-reference",
3890
+ CONFLICTING_DECLARATIONS: "conflicting-declaration",
3891
+ RELATION_NOT_ANALYZED: "not-analyzed-coverage",
3892
+ STALE_DOCUMENTATION: "stale-documentation",
3893
+ FRESHNESS_FAILURE: "freshness",
3894
+ OWNERSHIP_GAP: "ownership",
3895
+ CENTRALITY_RISK: "centrality-risk",
3896
+ CRITICAL_PATH_RISK: "critical-path-risk"
3897
+ };
3898
+ var defaultSeverity = (mode, ruleId) => {
3899
+ if (mode === "default") return "info";
3900
+ if (mode === "strict") return ruleId === "not-analyzed-coverage" ? "warn" : "error";
3901
+ return ruleId === "not-analyzed-coverage" ? "info" : "warn";
3902
+ };
3903
+ var resolvedOptions = (options) => {
3904
+ const config = RulesConfigSchema.parse(options.config ?? {});
3905
+ const mode = options.preset ?? config.mode ?? "default";
3906
+ const severity = { ...config.severity, ...options.severity };
3907
+ const ignore = /* @__PURE__ */ new Set([...config.ignore ?? [], ...options.ignore ?? []]);
3908
+ return {
3909
+ mode,
3910
+ severity,
3911
+ ignore,
3912
+ criticalEntities: options.criticalEntities ?? config.criticalEntities ?? [],
3913
+ criticalPaths: options.criticalPaths ?? config.criticalPaths ?? [],
3914
+ warningThresholds: { ...config.warningThresholds, ...options.warningThresholds }
3915
+ };
3916
+ };
3917
+ var severityFor = (ruleId, mode, overrides) => overrides[ruleId] ?? defaultSeverity(mode, ruleId);
3918
+ var findingFromDiagnostic = (diagnostic2, ruleId, severity) => ({
3919
+ id: `${diagnostic2.id}:${ruleId}`,
3920
+ ruleId,
3921
+ code: ruleId,
3922
+ status: diagnostic2.status,
3923
+ severity,
3924
+ message: diagnostic2.message,
3925
+ evidence: diagnostic2.evidence,
3926
+ ...diagnostic2.entityIds ? { entityIds: diagnostic2.entityIds } : {},
3927
+ ...diagnostic2.relationIds ? { relationIds: diagnostic2.relationIds } : {},
3928
+ ...diagnostic2.remediation ? { remediation: diagnostic2.remediation } : {},
3929
+ sourceDiagnosticCode: diagnostic2.code
3930
+ });
3931
+ var criticalFinding = (finding, severity, target) => ({
3932
+ ...finding,
3933
+ id: `${finding.id}:critical:${target}`,
3934
+ ruleId: "critical-path-risk",
3935
+ code: "critical-path-risk",
3936
+ severity,
3937
+ message: `Critical path or entity is affected: ${target}. ${finding.message}`
3938
+ });
3939
+ var evaluateRules = (report, options = {}) => {
3940
+ const resolved = resolvedOptions(options);
3941
+ const findings = [];
3942
+ for (const diagnostic2 of [...report.diagnostics].sort((a, b) => a.id.localeCompare(b.id))) {
3943
+ const ruleId = diagnosticRules[diagnostic2.code];
3944
+ if (!ruleId || resolved.ignore.has(ruleId)) continue;
3945
+ const severity = severityFor(ruleId, resolved.mode, resolved.severity);
3946
+ if (severity !== "off") findings.push(findingFromDiagnostic(diagnostic2, ruleId, severity));
3947
+ }
3948
+ const criticalSeverity = severityFor("critical-path-risk", resolved.mode, resolved.severity);
3949
+ const criticalEntitySet = new Set(resolved.criticalEntities);
3950
+ for (const finding of [...findings]) {
3951
+ const matchingEntity = (finding.entityIds ?? []).find((id) => criticalEntitySet.has(id));
3952
+ if (matchingEntity && !resolved.ignore.has("critical-path-risk") && criticalSeverity !== "off") {
3953
+ findings.push(criticalFinding(finding, criticalSeverity, matchingEntity));
3954
+ }
3955
+ for (const path of resolved.criticalPaths) {
3956
+ if (finding.evidence.some((item) => minimatch5(item.path, path, { dot: true })) && !resolved.ignore.has("critical-path-risk") && criticalSeverity !== "off") {
3957
+ findings.push(criticalFinding(finding, criticalSeverity, path));
3958
+ }
3959
+ }
3960
+ }
3961
+ const centralityThreshold = resolved.warningThresholds["centrality-risk"] ?? 3;
3962
+ const centralitySeverity = severityFor("centrality-risk", resolved.mode, resolved.severity);
3963
+ if (!resolved.ignore.has("centrality-risk") && centralitySeverity !== "off") {
3964
+ const counts = /* @__PURE__ */ new Map();
3965
+ for (const finding of findings.filter((item) => item.ruleId === "graph-undocumented-relation")) {
3966
+ for (const entityId2 of finding.entityIds ?? []) counts.set(entityId2, (counts.get(entityId2) ?? 0) + 1);
3967
+ }
3968
+ for (const [entityId2, count2] of [...counts.entries()].sort(([a], [b]) => a.localeCompare(b))) {
3969
+ if (count2 < centralityThreshold || !criticalEntitySet.has(entityId2)) continue;
3970
+ findings.push({
3971
+ id: `centrality-risk:${entityId2}`,
3972
+ ruleId: "centrality-risk",
3973
+ code: "centrality-risk",
3974
+ status: "unresolved",
3975
+ severity: centralitySeverity,
3976
+ message: `Critical entity has ${count2} undocumented relation finding(s); static centrality is a review signal, not a runtime availability claim.`,
3977
+ evidence: findings.filter((item) => item.ruleId === "graph-undocumented-relation" && item.entityIds?.includes(entityId2)).flatMap((item) => item.evidence),
3978
+ entityIds: [entityId2],
3979
+ remediation: "Review ownership, dependency boundaries, and runtime availability before declaring an SPOF."
3980
+ });
3981
+ }
3982
+ }
3983
+ const sortedFindings = [...findings].sort((a, b) => a.id.localeCompare(b.id));
3984
+ return { mode: resolved.mode, findings: sortedFindings, exitCode: sortedFindings.some((finding) => finding.severity === "error") ? 1 : 0 };
3985
+ };
3986
+ var parseRuleId = (value) => RuleIdSchema.parse(value);
3987
+ var parseRuleSeverity = (value) => RuleSeveritySchema.parse(value);
3988
+
2262
3989
  // src/federation/ecosystem-llms.ts
2263
3990
  function formatEcosystemLlmsBlock(options) {
2264
3991
  const heading = options.heading ?? "AgentsKit ecosystem";
@@ -2286,44 +4013,44 @@ function formatEcosystemLlmsSection(options) {
2286
4013
  }
2287
4014
 
2288
4015
  // src/gates/run-gates.ts
2289
- import { readFileSync as readFileSync10 } from "fs";
4016
+ import { readFileSync as readFileSync14 } from "fs";
2290
4017
 
2291
4018
  // src/conformance/documentation-standard-v1.ts
2292
- import { existsSync as existsSync9, readFileSync as readFileSync8, realpathSync as realpathSync6, statSync as statSync3 } from "fs";
2293
- import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve6, sep as sep5 } from "path";
4019
+ import { existsSync as existsSync13, readFileSync as readFileSync12, realpathSync as realpathSync8, statSync as statSync5 } from "fs";
4020
+ import { isAbsolute as isAbsolute5, relative as relative8, resolve as resolve11, sep as sep8 } from "path";
2294
4021
 
2295
4022
  // src/conformance/ecosystem-contract.ts
2296
- import { z as z5 } from "zod";
2297
- var NonEmptyStringSchema = z5.string().refine((value) => value.trim().length > 0, "must be non-empty");
2298
- var HttpsUrlSchema = z5.string().url().refine((value) => value.startsWith("https://"), "must use https");
2299
- var RepoSchema = z5.string().regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/);
2300
- var SlugSchema = z5.string().regex(/^[a-z][a-z0-9-]*$/);
2301
- var SurfaceSchema = z5.object({
4023
+ import { z as z7 } from "zod";
4024
+ var NonEmptyStringSchema = z7.string().refine((value) => value.trim().length > 0, "must be non-empty");
4025
+ var HttpsUrlSchema = z7.string().url().refine((value) => value.startsWith("https://"), "must use https");
4026
+ var RepoSchema = z7.string().regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/);
4027
+ var SlugSchema = z7.string().regex(/^[a-z][a-z0-9-]*$/);
4028
+ var SurfaceSchema = z7.object({
2302
4029
  home: HttpsUrlSchema.optional(),
2303
4030
  docs: HttpsUrlSchema.optional(),
2304
4031
  llms: HttpsUrlSchema.optional(),
2305
4032
  stats: HttpsUrlSchema.optional(),
2306
- documentation: z5.enum(["fumadocs", "repository"]),
2307
- chat: z5.enum(["agentschat", "custom", "none"])
4033
+ documentation: z7.enum(["fumadocs", "repository"]),
4034
+ chat: z7.enum(["agentschat", "custom", "none"])
2308
4035
  }).passthrough();
2309
- var ProductSchema = z5.object({
4036
+ var ProductSchema = z7.object({
2310
4037
  id: SlugSchema,
2311
4038
  name: NonEmptyStringSchema,
2312
4039
  shortName: NonEmptyStringSchema,
2313
4040
  kind: NonEmptyStringSchema,
2314
4041
  role: NonEmptyStringSchema,
2315
4042
  promise: NonEmptyStringSchema,
2316
- maturity: z5.enum(["planning", "alpha", "beta", "stable", "deprecated"]),
4043
+ maturity: z7.enum(["planning", "alpha", "beta", "stable", "deprecated"]),
2317
4044
  repo: RepoSchema.nullable(),
2318
- accent: z5.string().regex(/^#[0-9A-Fa-f]{6}$/),
4045
+ accent: z7.string().regex(/^#[0-9A-Fa-f]{6}$/),
2319
4046
  surfaces: SurfaceSchema,
2320
- navigation: z5.object({
2321
- showInBar: z5.boolean(),
2322
- order: z5.number().int().nonnegative().optional(),
2323
- next: z5.array(SlugSchema)
4047
+ navigation: z7.object({
4048
+ showInBar: z7.boolean(),
4049
+ order: z7.number().int().nonnegative().optional(),
4050
+ next: z7.array(SlugSchema)
2324
4051
  }).passthrough()
2325
4052
  }).passthrough();
2326
- var LegacyPropertySchema = z5.object({
4053
+ var LegacyPropertySchema = z7.object({
2327
4054
  id: SlugSchema,
2328
4055
  name: NonEmptyStringSchema,
2329
4056
  barLabel: NonEmptyStringSchema,
@@ -2332,50 +4059,50 @@ var LegacyPropertySchema = z5.object({
2332
4059
  repo: RepoSchema.nullable(),
2333
4060
  tagline: NonEmptyStringSchema,
2334
4061
  kind: NonEmptyStringSchema,
2335
- accent: z5.string().regex(/^#[0-9A-Fa-f]{6}$/),
4062
+ accent: z7.string().regex(/^#[0-9A-Fa-f]{6}$/),
2336
4063
  llms: HttpsUrlSchema.optional(),
2337
4064
  stats: HttpsUrlSchema.optional()
2338
4065
  }).passthrough();
2339
- var ManifestSchema = z5.object({
2340
- schemaVersion: z5.literal(2),
2341
- parentBrand: z5.object({ id: NonEmptyStringSchema, name: NonEmptyStringSchema }).passthrough(),
2342
- products: z5.array(ProductSchema).min(1),
4066
+ var ManifestSchema = z7.object({
4067
+ schemaVersion: z7.literal(2),
4068
+ parentBrand: z7.object({ id: NonEmptyStringSchema, name: NonEmptyStringSchema }).passthrough(),
4069
+ products: z7.array(ProductSchema).min(1),
2343
4070
  // Historical four-product shim or full seven-product projection of products[].
2344
- properties: z5.array(LegacyPropertySchema).refine((value) => value.length === 4 || value.length === 7, {
4071
+ properties: z7.array(LegacyPropertySchema).refine((value) => value.length === 4 || value.length === 7, {
2345
4072
  message: "must project either the legacy four products or the full seven-product catalog"
2346
4073
  }),
2347
- builder: z5.object({ id: NonEmptyStringSchema, name: NonEmptyStringSchema, url: HttpsUrlSchema }).passthrough().optional()
4074
+ builder: z7.object({ id: NonEmptyStringSchema, name: NonEmptyStringSchema, url: HttpsUrlSchema }).passthrough().optional()
2348
4075
  }).passthrough();
2349
- var EvidenceSchema = z5.discriminatedUnion("type", [
2350
- z5.object({
2351
- type: z5.literal("repository-derivation"),
4076
+ var EvidenceSchema2 = z7.discriminatedUnion("type", [
4077
+ z7.object({
4078
+ type: z7.literal("repository-derivation"),
2352
4079
  repo: RepoSchema,
2353
4080
  path: NonEmptyStringSchema,
2354
4081
  summary: NonEmptyStringSchema
2355
4082
  }).passthrough(),
2356
- z5.object({ type: z5.literal("endpoint"), url: HttpsUrlSchema, summary: NonEmptyStringSchema }).passthrough()
4083
+ z7.object({ type: z7.literal("endpoint"), url: HttpsUrlSchema, summary: NonEmptyStringSchema }).passthrough()
2357
4084
  ]);
2358
- var ClaimSchema = z5.object({
4085
+ var ClaimSchema = z7.object({
2359
4086
  id: NonEmptyStringSchema,
2360
- value: z5.number().finite().nonnegative(),
4087
+ value: z7.number().finite().nonnegative(),
2361
4088
  noun: NonEmptyStringSchema,
2362
- conservativeFloor: z5.number().int().nonnegative().optional(),
2363
- evidence: EvidenceSchema
4089
+ conservativeFloor: z7.number().int().nonnegative().optional(),
4090
+ evidence: EvidenceSchema2
2364
4091
  }).passthrough();
2365
- var ClaimProductSchema = z5.object({
4092
+ var ClaimProductSchema = z7.object({
2366
4093
  productId: SlugSchema,
2367
- source: z5.discriminatedUnion("type", [
2368
- z5.object({ type: z5.literal("endpoint"), url: HttpsUrlSchema }).passthrough(),
2369
- z5.object({ type: z5.literal("repository"), repo: RepoSchema }).passthrough(),
2370
- z5.object({ type: z5.literal("declaration"), summary: NonEmptyStringSchema }).passthrough()
4094
+ source: z7.discriminatedUnion("type", [
4095
+ z7.object({ type: z7.literal("endpoint"), url: HttpsUrlSchema }).passthrough(),
4096
+ z7.object({ type: z7.literal("repository"), repo: RepoSchema }).passthrough(),
4097
+ z7.object({ type: z7.literal("declaration"), summary: NonEmptyStringSchema }).passthrough()
2371
4098
  ]),
2372
- verification: z5.enum(["verified", "declared"]),
2373
- claims: z5.array(ClaimSchema)
4099
+ verification: z7.enum(["verified", "declared"]),
4100
+ claims: z7.array(ClaimSchema)
2374
4101
  }).passthrough();
2375
- var ClaimsSchema = z5.object({
2376
- schemaVersion: z5.literal(1),
2377
- manifestSchemaVersion: z5.literal(2),
2378
- products: z5.array(ClaimProductSchema)
4102
+ var ClaimsSchema = z7.object({
4103
+ schemaVersion: z7.literal(1),
4104
+ manifestSchemaVersion: z7.literal(2),
4105
+ products: z7.array(ClaimProductSchema)
2379
4106
  }).passthrough();
2380
4107
  var LEGACY_FOUR_PRODUCT_IDS = ["agentskit", "akos", "playbook", "registry"];
2381
4108
  var FULL_SEVEN_PRODUCT_IDS = [
@@ -2472,15 +4199,15 @@ var DOCUMENTATION_STANDARD_V1_ID = "documentation-standard-v1";
2472
4199
  var DOCUMENTATION_STANDARD_V1_STATUS = "stable";
2473
4200
  var MAX_TEXT_EVIDENCE_BYTES = 4 * 1024 * 1024;
2474
4201
  var safePath = (root, path) => {
2475
- const rootAbs = realpathSync6.native(resolve6(root));
2476
- const unresolved = resolve6(rootAbs, path);
2477
- const unresolvedRel = relative4(rootAbs, unresolved);
2478
- if (isAbsolute4(unresolvedRel) || unresolvedRel === ".." || unresolvedRel.startsWith(`..${sep5}`)) return void 0;
2479
- if (!existsSync9(unresolved)) return unresolved;
4202
+ const rootAbs = realpathSync8.native(resolve11(root));
4203
+ const unresolved = resolve11(rootAbs, path);
4204
+ const unresolvedRel = relative8(rootAbs, unresolved);
4205
+ if (isAbsolute5(unresolvedRel) || unresolvedRel === ".." || unresolvedRel.startsWith(`..${sep8}`)) return void 0;
4206
+ if (!existsSync13(unresolved)) return unresolved;
2480
4207
  try {
2481
- const abs = realpathSync6.native(unresolved);
2482
- const rel = relative4(rootAbs, abs);
2483
- return !isAbsolute4(rel) && rel !== ".." && !rel.startsWith(`..${sep5}`) ? abs : void 0;
4208
+ const abs = realpathSync8.native(unresolved);
4209
+ const rel = relative8(rootAbs, abs);
4210
+ return !isAbsolute5(rel) && rel !== ".." && !rel.startsWith(`..${sep8}`) ? abs : void 0;
2484
4211
  } catch {
2485
4212
  return void 0;
2486
4213
  }
@@ -2494,11 +4221,11 @@ var fileEvidence = (root, path, options) => {
2494
4221
  evidence: { path, detail: "Path escapes the project root." }
2495
4222
  };
2496
4223
  }
2497
- if (!existsSync9(abs)) {
4224
+ if (!existsSync13(abs)) {
2498
4225
  return { exists: false, content: "", evidence: { path, detail: "File does not exist." } };
2499
4226
  }
2500
4227
  try {
2501
- const stat = statSync3(abs);
4228
+ const stat = statSync5(abs);
2502
4229
  if (!stat.isFile()) {
2503
4230
  return { exists: false, content: "", evidence: { path, detail: "Path is not a regular file." } };
2504
4231
  }
@@ -2509,7 +4236,7 @@ var fileEvidence = (root, path, options) => {
2509
4236
  return {
2510
4237
  exists: true,
2511
4238
  content: "",
2512
- evidence: { path: toPosix(relative4(resolve6(root), abs)) || ".", detail: "File exists and is non-empty." }
4239
+ evidence: { path: toPosix(relative8(resolve11(root), abs)) || ".", detail: "File exists and is non-empty." }
2513
4240
  };
2514
4241
  }
2515
4242
  if (stat.size > MAX_TEXT_EVIDENCE_BYTES) {
@@ -2519,12 +4246,12 @@ var fileEvidence = (root, path, options) => {
2519
4246
  evidence: { path, detail: `Text evidence exceeds ${MAX_TEXT_EVIDENCE_BYTES} bytes.` }
2520
4247
  };
2521
4248
  }
2522
- const content = readFileSync8(abs, "utf8");
4249
+ const content = readFileSync12(abs, "utf8");
2523
4250
  return {
2524
4251
  exists: content.trim().length > 0,
2525
4252
  content,
2526
4253
  evidence: {
2527
- path: toPosix(relative4(resolve6(root), abs)) || ".",
4254
+ path: toPosix(relative8(resolve11(root), abs)) || ".",
2528
4255
  detail: content.trim().length > 0 ? "File exists and is non-empty." : "File is empty."
2529
4256
  }
2530
4257
  };
@@ -2559,7 +4286,7 @@ var humanDocsRule = (root, config) => {
2559
4286
  passed: docs.length > 0,
2560
4287
  message: docs.length > 0 ? `Found ${docs.length} human document(s).` : "No human documentation was discovered.",
2561
4288
  evidence: docs.slice(0, 10).map((doc) => ({
2562
- path: toPosix(relative4(resolve6(root), doc.path)),
4289
+ path: toPosix(relative8(resolve11(root), doc.path)),
2563
4290
  detail: `Human route: ${doc.url}`
2564
4291
  })),
2565
4292
  remediation: {
@@ -2570,33 +4297,33 @@ var humanDocsRule = (root, config) => {
2570
4297
  };
2571
4298
  var llmsRule = (root, config, options) => {
2572
4299
  const llmsPath = config.index?.llmsTxt?.outFile ?? "llms.txt";
2573
- const llmsKey = safePath(root, llmsPath) ?? resolve6(root, llmsPath);
4300
+ const llmsKey = safePath(root, llmsPath) ?? resolve11(root, llmsPath);
2574
4301
  const rawSources = /* @__PURE__ */ new Map();
2575
4302
  for (const path of options.rawSources ?? []) {
2576
- const key = safePath(root, path) ?? resolve6(root, path);
4303
+ const key = safePath(root, path) ?? resolve11(root, path);
2577
4304
  if (key !== llmsKey && !rawSources.has(key)) rawSources.set(key, path);
2578
4305
  }
2579
4306
  const paths = [llmsPath, ...rawSources.values()];
2580
- const evidence = paths.map((path) => fileEvidence(root, path));
4307
+ const evidence2 = paths.map((path) => fileEvidence(root, path));
2581
4308
  const generated = buildDocBridgeIndex({ root, config, write: false }).index;
2582
4309
  const expectedLlms = renderLlmsTxt(config, generated.knowledge, generated.project?.name ?? "project");
2583
- const llmsIsFresh = evidence[0]?.content === expectedLlms;
2584
- if (evidence[0]?.exists) {
2585
- evidence[0] = {
2586
- ...evidence[0],
4310
+ const llmsIsFresh = evidence2[0]?.content === expectedLlms;
4311
+ if (evidence2[0]?.exists) {
4312
+ evidence2[0] = {
4313
+ ...evidence2[0],
2587
4314
  evidence: {
2588
- ...evidence[0].evidence,
4315
+ ...evidence2[0].evidence,
2589
4316
  detail: llmsIsFresh ? "File matches the deterministic ak-docs output." : "File is stale or was not generated by the current ak-docs inputs."
2590
4317
  }
2591
4318
  };
2592
4319
  }
2593
- const passed = config.index?.llmsTxt?.enabled !== false && paths.length > 1 && llmsIsFresh && evidence.every((item) => item.exists);
4320
+ const passed = config.index?.llmsTxt?.enabled !== false && paths.length > 1 && llmsIsFresh && evidence2.every((item) => item.exists);
2594
4321
  return {
2595
4322
  id: "llms-and-raw-source",
2596
4323
  level: "required",
2597
4324
  passed,
2598
4325
  message: passed ? `Resolved llms.txt and ${paths.length - 1} raw source(s).` : "llms.txt must be enabled, current, and accompanied by at least one readable raw source.",
2599
- evidence: evidence.map((item) => item.evidence),
4326
+ evidence: evidence2.map((item) => item.evidence),
2600
4327
  remediation: {
2601
4328
  command: "ak-docs index",
2602
4329
  detail: "Generate llms.txt and configure conformance.documentationStandardV1.rawSources."
@@ -2615,8 +4342,8 @@ var ecosystemContract = (root, options) => {
2615
4342
  }
2616
4343
  const manifestFile = fileEvidence(root, declaration.manifest);
2617
4344
  const claimsFile = fileEvidence(root, declaration.claims);
2618
- const evidence = [manifestFile.evidence, claimsFile.evidence];
2619
- if (!manifestFile.exists || !claimsFile.exists) return { passed: false, urls: /* @__PURE__ */ new Set(), evidence };
4345
+ const evidence2 = [manifestFile.evidence, claimsFile.evidence];
4346
+ if (!manifestFile.exists || !claimsFile.exists) return { passed: false, urls: /* @__PURE__ */ new Set(), evidence: evidence2 };
2620
4347
  try {
2621
4348
  const manifest = JSON.parse(manifestFile.content);
2622
4349
  const claims = JSON.parse(claimsFile.content);
@@ -2634,15 +4361,15 @@ var ecosystemContract = (root, options) => {
2634
4361
  if (typeof value === "string" && /^https:\/\//.test(value)) urls.add(normalizedUrl(value));
2635
4362
  }
2636
4363
  }
2637
- evidence[0] = { path: declaration.manifest, detail: `Validated ${productIds.length} canonical product(s), including ${declaration.productId}.` };
2638
- evidence[1] = { path: declaration.claims, detail: `Validated claim-ledger identity for ${contract.claims.products.length} product(s).` };
2639
- return { passed: urls.size > 0, urls, evidence };
4364
+ evidence2[0] = { path: declaration.manifest, detail: `Validated ${productIds.length} canonical product(s), including ${declaration.productId}.` };
4365
+ evidence2[1] = { path: declaration.claims, detail: `Validated claim-ledger identity for ${contract.claims.products.length} product(s).` };
4366
+ return { passed: urls.size > 0, urls, evidence: evidence2 };
2640
4367
  } catch (error) {
2641
- evidence.push({
4368
+ evidence2.push({
2642
4369
  path: `${declaration.manifest}, ${declaration.claims}`,
2643
4370
  detail: error instanceof Error ? error.message : "Canonical ecosystem contract is invalid."
2644
4371
  });
2645
- return { passed: false, urls: /* @__PURE__ */ new Set(), evidence };
4372
+ return { passed: false, urls: /* @__PURE__ */ new Set(), evidence: evidence2 };
2646
4373
  }
2647
4374
  };
2648
4375
  var handoffsRule = (root, config) => {
@@ -2668,13 +4395,13 @@ var handoffsRule = (root, config) => {
2668
4395
  };
2669
4396
  var contributionRule = (root, options) => {
2670
4397
  const paths = options.contributionPaths?.length ? options.contributionPaths : ["CONTRIBUTING.md"];
2671
- const evidence = paths.map((path) => fileEvidence(root, path));
4398
+ const evidence2 = paths.map((path) => fileEvidence(root, path));
2672
4399
  return {
2673
4400
  id: "contribution",
2674
4401
  level: "required",
2675
- passed: evidence.some((item) => item.exists),
2676
- message: evidence.some((item) => item.exists) ? "Contribution guidance is available." : "Contribution guidance is missing.",
2677
- evidence: evidence.map((item) => item.evidence),
4402
+ passed: evidence2.some((item) => item.exists),
4403
+ message: evidence2.some((item) => item.exists) ? "Contribution guidance is available." : "Contribution guidance is missing.",
4404
+ evidence: evidence2.map((item) => item.evidence),
2678
4405
  remediation: {
2679
4406
  command: "edit CONTRIBUTING.md",
2680
4407
  detail: "Document setup, validation commands, and the pull-request workflow."
@@ -2683,13 +4410,13 @@ var contributionRule = (root, options) => {
2683
4410
  };
2684
4411
  var markersRule = (root, options, kind, level) => {
2685
4412
  const declarations = options[kind === "metadata" ? "metadata" : "diagrams"] ?? [];
2686
- const evidence = [];
4413
+ const evidence2 = [];
2687
4414
  let passed = declarations.length > 0;
2688
4415
  for (const declaration of declarations) {
2689
4416
  const file = fileEvidence(root, declaration.path);
2690
4417
  const missing = declaration.contains.filter((marker) => !file.content.includes(marker));
2691
4418
  if (!file.exists || missing.length > 0) passed = false;
2692
- evidence.push({
4419
+ evidence2.push({
2693
4420
  path: declaration.path,
2694
4421
  detail: !file.exists ? file.evidence.detail : missing.length ? `Missing marker(s): ${missing.join(", ")}` : `Found marker(s): ${declaration.contains.join(", ")}`
2695
4422
  });
@@ -2699,7 +4426,7 @@ var markersRule = (root, options, kind, level) => {
2699
4426
  level,
2700
4427
  passed,
2701
4428
  message: passed ? `${kind} evidence is complete.` : `${kind} evidence is incomplete.`,
2702
- evidence,
4429
+ evidence: evidence2,
2703
4430
  remediation: {
2704
4431
  command: "edit doc-bridge.config.json",
2705
4432
  detail: `Declare ${kind} evidence paths and markers that exist in the repository.`
@@ -2709,14 +4436,14 @@ var markersRule = (root, options, kind, level) => {
2709
4436
  var linksRule = (root, options) => {
2710
4437
  const links = options.links ?? [];
2711
4438
  const contract = ecosystemContract(root, options);
2712
- const evidence = [...contract.evidence];
4439
+ const evidence2 = [...contract.evidence];
2713
4440
  let passed = links.length > 0 && contract.passed;
2714
4441
  for (const link of links) {
2715
4442
  const sources = link.paths.map((path) => ({ path, file: fileEvidence(root, path) }));
2716
4443
  const matches = sources.filter(({ file }) => file.exists && file.content.includes(link.url));
2717
4444
  const canonical = contract.urls.has(normalizedUrl(link.url));
2718
4445
  if (matches.length === 0 || !canonical) passed = false;
2719
- evidence.push({
4446
+ evidence2.push({
2720
4447
  path: sources.map((source) => source.path).join(", "),
2721
4448
  detail: matches.length === 0 ? `Missing ${link.url}` : canonical ? `Found canonical ecosystem URL ${link.url}` : `Found ${link.url}, but it is absent from the canonical ecosystem manifest.`
2722
4449
  });
@@ -2726,7 +4453,7 @@ var linksRule = (root, options) => {
2726
4453
  level: "required",
2727
4454
  passed,
2728
4455
  message: passed ? `${links.length} required ecosystem link(s) resolve in source.` : "One or more required ecosystem links are missing from source.",
2729
- evidence,
4456
+ evidence: evidence2,
2730
4457
  remediation: {
2731
4458
  command: "edit README.md",
2732
4459
  detail: "Sync the canonical ecosystem snapshots and add each configured canonical URL to a declared documentation source."
@@ -2735,14 +4462,14 @@ var linksRule = (root, options) => {
2735
4462
  };
2736
4463
  var quickstartsRule = (root, options) => {
2737
4464
  const quickstarts = options.quickstarts ?? [];
2738
- const evidence = [];
4465
+ const evidence2 = [];
2739
4466
  let passed = quickstarts.length > 0;
2740
4467
  for (const quickstart of quickstarts) {
2741
4468
  const doc = fileEvidence(root, quickstart.doc);
2742
4469
  const test = fileEvidence(root, quickstart.test);
2743
4470
  const missingMarkers = quickstart.testContains.filter((marker) => !test.content.includes(marker));
2744
4471
  if (!doc.exists || !test.exists || missingMarkers.length > 0 || !quickstart.command.trim()) passed = false;
2745
- evidence.push(
4472
+ evidence2.push(
2746
4473
  { path: quickstart.doc, detail: `${quickstart.id}: ${doc.evidence.detail}` },
2747
4474
  {
2748
4475
  path: quickstart.test,
@@ -2755,7 +4482,7 @@ var quickstartsRule = (root, options) => {
2755
4482
  level: "required",
2756
4483
  passed,
2757
4484
  message: passed ? `${quickstarts.length} quickstart(s) have executable test evidence.` : "Quickstart test evidence is incomplete.",
2758
- evidence,
4485
+ evidence: evidence2,
2759
4486
  remediation: {
2760
4487
  command: "pnpm test",
2761
4488
  detail: "Map every quickstart to a documentation path, test file, identifying marker, and CI command."
@@ -2764,13 +4491,13 @@ var quickstartsRule = (root, options) => {
2764
4491
  };
2765
4492
  var visualsRule = (root, options) => {
2766
4493
  const visuals = options.visuals ?? [];
2767
- const evidence = visuals.map((path) => fileEvidence(root, path, { readContent: false }));
4494
+ const evidence2 = visuals.map((path) => fileEvidence(root, path, { readContent: false }));
2768
4495
  return {
2769
4496
  id: "visual-explanations",
2770
4497
  level: "recommended",
2771
- passed: visuals.length > 0 && evidence.every((item) => item.exists),
2772
- message: visuals.length > 0 && evidence.every((item) => item.exists) ? `${visuals.length} visual asset(s) found.` : "Visual explanation evidence is incomplete.",
2773
- evidence: evidence.map((item) => item.evidence),
4498
+ passed: visuals.length > 0 && evidence2.every((item) => item.exists),
4499
+ message: visuals.length > 0 && evidence2.every((item) => item.exists) ? `${visuals.length} visual asset(s) found.` : "Visual explanation evidence is incomplete.",
4500
+ evidence: evidence2.map((item) => item.evidence),
2774
4501
  remediation: {
2775
4502
  command: "edit doc-bridge.config.json",
2776
4503
  detail: "Declare the images or animations that explain the product workflow."
@@ -2819,14 +4546,14 @@ var formatDocumentationStandardText = (report) => [
2819
4546
  "",
2820
4547
  ...report.results.flatMap((result) => [
2821
4548
  `${result.status === "pass" ? "PASS" : result.status === "excepted" ? "EXCEPTED" : "FAIL"} [${result.level}] ${result.id}: ${result.message}`,
2822
- ...result.evidence.map((evidence) => ` evidence: ${evidence.path} \u2014 ${evidence.detail}`),
4549
+ ...result.evidence.map((evidence2) => ` evidence: ${evidence2.path} \u2014 ${evidence2.detail}`),
2823
4550
  ...result.exception ? [` exception: ${result.exception.reason} \u2014 ${result.exception.approvedBy} (${result.exception.trackingUrl})`] : result.ok ? [] : [` fix: ${result.remediation.command} \u2014 ${result.remediation.detail}`]
2824
4551
  ])
2825
4552
  ];
2826
4553
 
2827
4554
  // src/query/load-index.ts
2828
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
2829
- import { join as join8, resolve as resolve7 } from "path";
4555
+ import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
4556
+ import { join as join12, resolve as resolve12 } from "path";
2830
4557
  var IndexNotFoundError = class extends Error {
2831
4558
  constructor(path) {
2832
4559
  super(`Missing index at ${path}. Run: ak-docs index`);
@@ -2835,14 +4562,14 @@ var IndexNotFoundError = class extends Error {
2835
4562
  }
2836
4563
  path;
2837
4564
  };
2838
- var indexFilePath = (root, config) => join8(root, config.index?.outFile ?? ".doc-bridge/index.json");
4565
+ var indexFilePath = (root, config) => join12(root, config.index?.outFile ?? ".doc-bridge/index.json");
2839
4566
  var loadDocBridgeIndex = (root, config) => {
2840
4567
  const path = indexFilePath(root, config);
2841
- if (!existsSync10(path)) throw new IndexNotFoundError(path);
2842
- const raw = JSON.parse(readFileSync9(path, "utf8"));
4568
+ if (!existsSync14(path)) throw new IndexNotFoundError(path);
4569
+ const raw = JSON.parse(readFileSync13(path, "utf8"));
2843
4570
  return parseDocBridgeIndex(raw);
2844
4571
  };
2845
- var resolveRoot = (cwd) => resolve7(cwd ?? process.cwd());
4572
+ var resolveRoot = (cwd) => resolve12(cwd ?? process.cwd());
2846
4573
 
2847
4574
  // src/gates/run-gates.ts
2848
4575
  var RESERVED_GATE_IDS = /* @__PURE__ */ new Set(["link-rot", "routing-currency", "bootstrap-size"]);
@@ -2915,7 +4642,7 @@ var runOkfTypeGate = (root, config) => {
2915
4642
  const required = config.corpus.agent.okf?.requireType ?? config.gates?.preset === "strict";
2916
4643
  if (!required) return { id: "okf-type", ok: true, message: "OKF type frontmatter not required" };
2917
4644
  const allowed = config.corpus.agent.okf?.allowedTypes;
2918
- 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));
4645
+ const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({ path: doc.path, type: frontmatterType(readFileSync14(doc.absPath, "utf8")) })).filter((doc) => !doc.type || allowed && !allowed.includes(doc.type));
2919
4646
  if (bad.length) {
2920
4647
  return {
2921
4648
  id: "okf-type",
@@ -2942,10 +4669,10 @@ var DOCS_STYLE_RULES = {
2942
4669
  "playbook-okf-soft": ["title", "no-stale-wording"],
2943
4670
  "title-only": ["title"]
2944
4671
  };
2945
- var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4672
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2946
4673
  var docsStyleOptions = (config) => {
2947
4674
  const raw = config.gates?.options?.["docs-style"];
2948
- if (!isRecord2(raw)) {
4675
+ if (!isRecord3(raw)) {
2949
4676
  if (config.gates?.preset === "playbook") {
2950
4677
  return { profile: "playbook-okf-soft", required: DOCS_STYLE_RULES["playbook-okf-soft"] };
2951
4678
  }
@@ -2989,7 +4716,7 @@ var runDocsStyleGate = (root, config) => {
2989
4716
  }
2990
4717
  const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({
2991
4718
  path: doc.path,
2992
- missing: missingStyleRules(readFileSync10(doc.absPath, "utf8"), required)
4719
+ missing: missingStyleRules(readFileSync14(doc.absPath, "utf8"), required)
2993
4720
  })).filter((doc) => doc.missing.length > 0);
2994
4721
  if (bad.length) {
2995
4722
  return {
@@ -3034,9 +4761,9 @@ var resolveGateIds = (config) => {
3034
4761
  };
3035
4762
 
3036
4763
  // src/mcp/server.ts
3037
- import { readFileSync as readFileSync12, realpathSync as realpathSync7 } from "fs";
3038
- import { relative as relative5, resolve as resolve8 } from "path";
3039
- import { z as z6, ZodError } from "zod";
4764
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync16, realpathSync as realpathSync9, writeFileSync as writeFileSync5 } from "fs";
4765
+ import { join as join14, relative as relative9, resolve as resolve13 } from "path";
4766
+ import { z as z8, ZodError } from "zod";
3040
4767
 
3041
4768
  // src/query/search.ts
3042
4769
  var tokenize = (value) => value.toLowerCase().split(/[^a-z0-9@/_-]+/).filter((t) => t.length >= 2);
@@ -3154,15 +4881,15 @@ var createDocBridgeRetriever = (index, options = {}) => ({
3154
4881
  });
3155
4882
 
3156
4883
  // src/memory/ingest.ts
3157
- import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
3158
- import { join as join9 } from "path";
4884
+ import { existsSync as existsSync15, readFileSync as readFileSync15 } from "fs";
4885
+ import { join as join13 } from "path";
3159
4886
  var memoryFact = (raw, id) => firstParagraph(raw) ?? firstHeading(raw) ?? id;
3160
- var relativePath = (root, abs) => toPosix(abs).replace(`${toPosix(root)}/`, "");
4887
+ var relativePath2 = (root, abs) => toPosix(abs).replace(`${toPosix(root)}/`, "");
3161
4888
  var ingestMarkdownDir = (root, dir, source, confidence) => {
3162
- if (!existsSync11(dir)) return [];
4889
+ if (!existsSync15(dir)) return [];
3163
4890
  return walkFiles(dir, { extensions: [".md", ".mdc"] }).map((abs) => {
3164
- const rel = relativePath(root, abs);
3165
- const raw = readFileSync11(abs, "utf8");
4891
+ const rel = relativePath2(root, abs);
4892
+ const raw = readFileSync15(abs, "utf8");
3166
4893
  const id = slugFromPath(rel);
3167
4894
  return {
3168
4895
  schemaVersion: 1,
@@ -3177,10 +4904,10 @@ var ingestMarkdownDir = (root, dir, source, confidence) => {
3177
4904
  });
3178
4905
  };
3179
4906
  var ingestCursorRules = (root) => {
3180
- const dir = join9(root, ".cursor", "rules");
4907
+ const dir = join13(root, ".cursor", "rules");
3181
4908
  return ingestMarkdownDir(root, dir, "cursor", 0.6);
3182
4909
  };
3183
- var ingestAgentMemory = (root) => ingestMarkdownDir(root, join9(root, ".agent-memory"), "agent-memory", 0.7);
4910
+ var ingestAgentMemory = (root) => ingestMarkdownDir(root, join13(root, ".agent-memory"), "agent-memory", 0.7);
3184
4911
  var ingestMemoryCandidates = (root) => [
3185
4912
  ...ingestAgentMemory(root),
3186
4913
  ...ingestCursorRules(root)
@@ -3447,25 +5174,70 @@ var MCP_TOOLS = [
3447
5174
  description: "Return the static Doc Bridge curator and delegate topology.",
3448
5175
  annotations: { readOnlyHint: true },
3449
5176
  inputSchema: { type: "object", properties: {} }
5177
+ },
5178
+ {
5179
+ name: "docbridge.snapshot",
5180
+ title: "Read the latest discovery snapshot",
5181
+ description: "Read the bounded canonical repository snapshot from the latest workflow run.",
5182
+ annotations: { readOnlyHint: true },
5183
+ inputSchema: { type: "object", properties: { runId: { type: "string" } } }
5184
+ },
5185
+ {
5186
+ name: "docbridge.report",
5187
+ title: "Read the latest reconciliation report",
5188
+ description: "Read the canonical reconciliation report from the latest workflow run.",
5189
+ annotations: { readOnlyHint: true },
5190
+ inputSchema: { type: "object", properties: { runId: { type: "string" } } }
5191
+ },
5192
+ {
5193
+ name: "docbridge.diagnostics",
5194
+ title: "Read reconciliation diagnostics",
5195
+ description: "Read bounded diagnostics from the latest canonical reconciliation report.",
5196
+ annotations: { readOnlyHint: true },
5197
+ inputSchema: { type: "object", properties: { status: { type: "string" }, severity: { type: "string" } } }
5198
+ },
5199
+ {
5200
+ name: "docbridge.relations",
5201
+ title: "Read architecture relations",
5202
+ description: "Read bounded observed and declared relations from the latest canonical snapshot.",
5203
+ annotations: { readOnlyHint: true },
5204
+ inputSchema: { type: "object", properties: { kind: { type: "string" }, limit: { type: "number" } } }
5205
+ },
5206
+ {
5207
+ name: "docbridge.run",
5208
+ title: "Read workflow state",
5209
+ description: "Read the latest resumable workflow state and artifact references.",
5210
+ annotations: { readOnlyHint: true },
5211
+ inputSchema: { type: "object", properties: {} }
5212
+ },
5213
+ {
5214
+ name: "docbridge.proposals",
5215
+ title: "Read or approve proposals",
5216
+ description: "Create, inspect, approve and apply deterministic proposals through the shared human-gated workflow.",
5217
+ inputSchema: { type: "object", properties: { action: { type: "string", enum: ["list", "propose-links", "propose-normalize", "suggest", "approve", "apply"] }, proposalHash: { type: "string" }, artifactPath: { type: "string" }, approvedBy: { type: "string" }, proposal: { type: "object" } } }
3450
5218
  }
3451
5219
  ];
3452
5220
  var asRecord = (value) => value && typeof value === "object" ? value : {};
3453
- var HandoffResolveArgsSchema = z6.object({
3454
- id: z6.string().min(1),
3455
- kind: z6.enum(["package", "ownership"]).optional()
5221
+ var HandoffResolveArgsSchema = z8.object({
5222
+ id: z8.string().min(1),
5223
+ kind: z8.enum(["package", "ownership"]).optional()
3456
5224
  });
3457
- var DocSearchArgsSchema = z6.object({
3458
- term: z6.string().min(1),
3459
- limit: z6.number().int().positive().max(100).optional()
5225
+ var DocSearchArgsSchema = z8.object({
5226
+ term: z8.string().min(1),
5227
+ limit: z8.number().int().positive().max(100).optional()
3460
5228
  });
3461
- var RetrieverQueryArgsSchema = z6.object({
3462
- query: z6.string().min(1),
3463
- limit: z6.number().int().positive().max(100).optional()
5229
+ var RetrieverQueryArgsSchema = z8.object({
5230
+ query: z8.string().min(1),
5231
+ limit: z8.number().int().positive().max(100).optional()
3464
5232
  });
3465
- var DocGetArgsSchema = z6.object({
3466
- id: z6.string().min(1).optional(),
3467
- path: z6.string().min(1).optional()
5233
+ var DocGetArgsSchema = z8.object({
5234
+ id: z8.string().min(1).optional(),
5235
+ path: z8.string().min(1).optional()
3468
5236
  }).refine((args) => args.id || args.path, "doc.get requires id or path");
5237
+ var WorkflowRunArgsSchema = z8.object({ runId: z8.string().min(1).optional() });
5238
+ var DiagnosticsArgsSchema = z8.object({ status: z8.string().min(1).optional(), severity: z8.string().min(1).optional() });
5239
+ var RelationsArgsSchema = z8.object({ kind: z8.string().min(1).optional(), limit: z8.number().int().positive().max(500).optional() });
5240
+ var ProposalsArgsSchema = z8.object({ action: z8.enum(["list", "propose-links", "propose-normalize", "suggest", "approve", "apply"]).optional(), proposalHash: z8.string().min(1).optional(), artifactPath: z8.string().min(1).optional(), approvedBy: z8.string().min(1).optional(), proposal: z8.unknown().optional() });
3469
5241
  var parseToolArgs = (tool, schema, value) => {
3470
5242
  try {
3471
5243
  return schema.parse(value);
@@ -3496,15 +5268,54 @@ var findDocPath = (index, args) => {
3496
5268
  return doc.path;
3497
5269
  };
3498
5270
  var resolveDocPath = (root, relPath) => {
3499
- const rootAbs = realpathSync7.native(root);
3500
- const unresolved = resolve8(rootAbs, relPath);
3501
- const unresolvedRel = relative5(rootAbs, unresolved);
5271
+ const rootAbs = realpathSync9.native(root);
5272
+ const unresolved = resolve13(rootAbs, relPath);
5273
+ const unresolvedRel = relative9(rootAbs, unresolved);
3502
5274
  if (unresolvedRel.startsWith("..")) throw new Error("doc.get path escapes project root");
3503
- const abs = realpathSync7.native(unresolved);
3504
- const rel = relative5(rootAbs, abs);
5275
+ const abs = realpathSync9.native(unresolved);
5276
+ const rel = relative9(rootAbs, abs);
3505
5277
  if (rel.startsWith("..")) throw new Error("doc.get path escapes project root");
3506
5278
  return abs;
3507
5279
  };
5280
+ var workflowStateDir = (ctx) => resolve13(ctx.root, ctx.config.workflow?.stateDir ?? ".doc-bridge/workflow");
5281
+ var workflowRun = (ctx) => loadWorkflowManifest(workflowStateDir(ctx));
5282
+ var ensureLatestRun = (ctx, runId2) => {
5283
+ const run2 = workflowRun(ctx);
5284
+ if (runId2 && run2.runId !== runId2) throw new Error(`Unknown workflow run "${runId2}"`);
5285
+ if (run2.state === "stale" || run2.state === "failed") throw new Error(`Workflow run is ${run2.state}; resume or create a valid run before reading artifacts.`);
5286
+ return run2;
5287
+ };
5288
+ var workflowSnapshot = (ctx, runId2) => {
5289
+ ensureLatestRun(ctx, runId2);
5290
+ return parseDiscoverySnapshot(loadWorkflowStepOutput(workflowStateDir(ctx), "normalize"));
5291
+ };
5292
+ var workflowReport = (ctx, runId2) => {
5293
+ ensureLatestRun(ctx, runId2);
5294
+ return parseReconciliationReport(loadWorkflowStepOutput(workflowStateDir(ctx), "reconcile"));
5295
+ };
5296
+ var proposalPath = (ctx) => join14(ctx.root, ".doc-bridge", "proposal.json");
5297
+ var readSavedProposal = (ctx, input) => {
5298
+ if (input !== void 0) return FixProposalV1Schema.parse(input);
5299
+ try {
5300
+ return FixProposalV1Schema.parse(JSON.parse(readFileSync16(proposalPath(ctx), "utf8")));
5301
+ } catch {
5302
+ throw new Error(`No saved fix proposal at ${proposalPath(ctx)}.`);
5303
+ }
5304
+ };
5305
+ var saveProposal = (ctx, proposal) => {
5306
+ mkdirSync4(join14(ctx.root, ".doc-bridge"), { recursive: true });
5307
+ writeFileSync5(proposalPath(ctx), `${JSON.stringify(proposal, null, 2)}
5308
+ `, "utf8");
5309
+ };
5310
+ var enabledMcpTools = (ctx) => {
5311
+ const configured = ctx.config.surfaces?.mcp?.tools;
5312
+ if (!configured || configured.length === MCP_TOOLS.length && MCP_TOOLS.every((tool) => configured.includes(tool.name))) return MCP_TOOLS;
5313
+ return MCP_TOOLS.filter((tool) => configured.includes(tool.name));
5314
+ };
5315
+ var assertMcpToolEnabled = (ctx, name) => {
5316
+ if (!MCP_TOOLS.some((tool) => tool.name === name)) throw new Error(`Unknown tool "${name}"`);
5317
+ if (!enabledMcpTools(ctx).some((tool) => tool.name === name)) throw new Error(`MCP tool "${name}" is disabled by configuration.`);
5318
+ };
3508
5319
  var handleMcpRequest = (ctx, request) => {
3509
5320
  if (request.method === "initialize") {
3510
5321
  return {
@@ -3513,11 +5324,13 @@ var handleMcpRequest = (ctx, request) => {
3513
5324
  serverInfo: { name: "ak-docs", version: PACKAGE_VERSION }
3514
5325
  };
3515
5326
  }
3516
- if (request.method === "tools/list") return { tools: MCP_TOOLS };
5327
+ if (request.method === "tools/list") return { tools: enabledMcpTools(ctx) };
3517
5328
  if (request.method === "tools/call") {
3518
5329
  const params = asRecord(request.params);
3519
5330
  const name = params.name;
3520
5331
  const args = asRecord(params.arguments);
5332
+ if (typeof name !== "string") throw new Error("MCP tools/call requires a tool name.");
5333
+ assertMcpToolEnabled(ctx, name);
3521
5334
  const index = () => ctx.loadIndex?.() ?? loadDocBridgeIndex(ctx.root, ctx.config);
3522
5335
  if (name === "handoff.resolve") {
3523
5336
  const parsed = parseToolArgs("handoff.resolve", HandoffResolveArgsSchema, args);
@@ -3535,7 +5348,7 @@ var handleMcpRequest = (ctx, request) => {
3535
5348
  }
3536
5349
  if (name === "doc.get") {
3537
5350
  const relPath = findDocPath(index(), parseToolArgs("doc.get", DocGetArgsSchema, args));
3538
- return textResult(readFileSync12(resolveDocPath(ctx.root, relPath), "utf8"));
5351
+ return textResult(readFileSync16(resolveDocPath(ctx.root, relPath), "utf8"));
3539
5352
  }
3540
5353
  if (name === "gate.status") return textResult(runGates(ctx.root, ctx.config));
3541
5354
  if (name === "retriever.query") {
@@ -3557,6 +5370,81 @@ var handleMcpRequest = (ctx, request) => {
3557
5370
  mergePolicy: { autoMerge: false, requiresHuman: true }
3558
5371
  });
3559
5372
  }
5373
+ if (name === "docbridge.snapshot") {
5374
+ const parsed = parseToolArgs("docbridge.snapshot", WorkflowRunArgsSchema, args);
5375
+ return textResult(redactValue(workflowSnapshot(ctx, parsed.runId)));
5376
+ }
5377
+ if (name === "docbridge.report") {
5378
+ const parsed = parseToolArgs("docbridge.report", WorkflowRunArgsSchema, args);
5379
+ return textResult(redactValue(workflowReport(ctx, parsed.runId)));
5380
+ }
5381
+ if (name === "docbridge.diagnostics") {
5382
+ const parsed = parseToolArgs("docbridge.diagnostics", DiagnosticsArgsSchema, args);
5383
+ const diagnostics = workflowReport(ctx).diagnostics.filter(
5384
+ (diagnostic2) => (!parsed.status || diagnostic2.status === parsed.status) && (!parsed.severity || diagnostic2.severity === parsed.severity)
5385
+ );
5386
+ return textResult(redactValue({ reportHash: workflowReport(ctx).contentHash, diagnostics }));
5387
+ }
5388
+ if (name === "docbridge.relations") {
5389
+ const parsed = parseToolArgs("docbridge.relations", RelationsArgsSchema, args);
5390
+ const snapshot = workflowSnapshot(ctx);
5391
+ return textResult({ snapshotHash: snapshot.contentHash, relations: snapshot.relations.filter((relation) => !parsed.kind || relation.kind === parsed.kind).slice(0, parsed.limit ?? 100) });
5392
+ }
5393
+ if (name === "docbridge.run") {
5394
+ parseToolArgs("docbridge.run", z8.object({}), args);
5395
+ return textResult(workflowRun(ctx));
5396
+ }
5397
+ if (name === "docbridge.proposals") {
5398
+ const parsed = parseToolArgs("docbridge.proposals", ProposalsArgsSchema, args);
5399
+ const run2 = (() => {
5400
+ try {
5401
+ return workflowRun(ctx);
5402
+ } catch {
5403
+ return void 0;
5404
+ }
5405
+ })();
5406
+ if (!parsed.action || parsed.action === "list") {
5407
+ let proposal2;
5408
+ try {
5409
+ proposal2 = readSavedProposal(ctx, void 0);
5410
+ } catch {
5411
+ proposal2 = void 0;
5412
+ }
5413
+ return textResult(redactValue({ ...run2 ? { runId: run2.runId } : {}, proposals: proposal2 ? [proposal2] : [] }));
5414
+ }
5415
+ if (parsed.action === "suggest") {
5416
+ return loadRegistryAgentRunner(ctx.root, ctx.config).then(async (runner) => {
5417
+ const snapshot = workflowSnapshot(ctx);
5418
+ const report = workflowReport(ctx);
5419
+ const adapter = createRegistryAgentAdapter(ctx.root, ctx.config, runner);
5420
+ const proposal2 = await adapter.run(snapshot, report);
5421
+ const savedPath = persistRegistryAgentProposal(workflowStateDir(ctx), proposal2);
5422
+ return textResult(redactValue({ ...run2 ? { runId: run2.runId } : {}, proposal: proposal2, proposalPath: savedPath }));
5423
+ });
5424
+ }
5425
+ const discovered = discoverRepository({ root: ctx.root, config: ctx.config });
5426
+ const options = { baseRevision: discovered.sourceRevision, configurationHash: sha256NormalizedV1(ctx.config), ...ctx.config.project?.name ? { projectName: ctx.config.project.name } : {} };
5427
+ if (parsed.action === "propose-links") {
5428
+ const proposal2 = createMarkdownLinkFixProposal(ctx.root, options);
5429
+ if (proposal2) saveProposal(ctx, proposal2);
5430
+ return textResult(redactValue({ ...run2 ? { runId: run2.runId } : {}, proposal: proposal2 ?? null }));
5431
+ }
5432
+ if (parsed.action === "propose-normalize") {
5433
+ if (!parsed.artifactPath) throw new Error("docbridge.proposals propose-normalize requires artifactPath");
5434
+ const proposal2 = createArtifactNormalizationProposal(ctx.root, parsed.artifactPath, options);
5435
+ if (proposal2) saveProposal(ctx, proposal2);
5436
+ return textResult(redactValue({ ...run2 ? { runId: run2.runId } : {}, proposal: proposal2 ?? null }));
5437
+ }
5438
+ if (parsed.action === "approve") {
5439
+ const proposal2 = approveFixProposal(readSavedProposal(ctx, parsed.proposal), parsed.approvedBy ?? "human");
5440
+ if (parsed.proposalHash && proposal2.approval?.proposalHash !== parsed.proposalHash) throw new Error("proposalHash does not match the saved proposal");
5441
+ saveProposal(ctx, proposal2);
5442
+ return textResult(redactValue({ ...run2 ? { runId: run2.runId } : {}, proposal: proposal2 }));
5443
+ }
5444
+ const proposal = applyFixProposal(ctx.root, readSavedProposal(ctx, parsed.proposal), { currentRevision: discovered.sourceRevision });
5445
+ saveProposal(ctx, proposal);
5446
+ return textResult(redactValue({ ...run2 ? { runId: run2.runId } : {}, proposal }));
5447
+ }
3560
5448
  throw new Error(`Unknown tool "${String(name)}"`);
3561
5449
  }
3562
5450
  if (request.method?.startsWith("notifications/")) return void 0;
@@ -3569,16 +5457,16 @@ var writeFrame = (payload, framing) => {
3569
5457
  \r
3570
5458
  ${body}`);
3571
5459
  };
3572
- var respond = (ctx, request, framing) => {
5460
+ var respondMcpRequest = async (ctx, request, framing) => {
3573
5461
  if (request.id === void 0) {
3574
5462
  try {
3575
- handleMcpRequest(ctx, request);
5463
+ await handleMcpRequest(ctx, request);
3576
5464
  } catch {
3577
5465
  }
3578
5466
  return;
3579
5467
  }
3580
5468
  try {
3581
- const result = handleMcpRequest(ctx, request);
5469
+ const result = await handleMcpRequest(ctx, request);
3582
5470
  writeFrame({ jsonrpc: "2.0", id: request.id, result: result ?? {} }, framing);
3583
5471
  } catch (error) {
3584
5472
  writeFrame({
@@ -3590,6 +5478,10 @@ var respond = (ctx, request, framing) => {
3590
5478
  };
3591
5479
  var startMcpStdioServer = (ctx) => {
3592
5480
  let buffer = Buffer.alloc(0);
5481
+ let responseChain = Promise.resolve();
5482
+ const enqueueResponse = (request, framing) => {
5483
+ responseChain = responseChain.then(() => respondMcpRequest(ctx, request, framing));
5484
+ };
3593
5485
  process.stdin.on("data", (chunk) => {
3594
5486
  buffer = Buffer.concat([buffer, chunk]);
3595
5487
  while (true) {
@@ -3608,7 +5500,7 @@ var startMcpStdioServer = (ctx) => {
3608
5500
  if (buffer.length < bodyEnd) return;
3609
5501
  const raw2 = buffer.subarray(bodyStart, bodyEnd).toString("utf8");
3610
5502
  buffer = buffer.subarray(bodyEnd);
3611
- respond(ctx, JSON.parse(raw2), "content-length");
5503
+ enqueueResponse(JSON.parse(raw2), "content-length");
3612
5504
  continue;
3613
5505
  }
3614
5506
  const lineEnd = buffer.indexOf("\n");
@@ -3617,7 +5509,7 @@ var startMcpStdioServer = (ctx) => {
3617
5509
  buffer = buffer.subarray(lineEnd + 1);
3618
5510
  if (!raw) continue;
3619
5511
  try {
3620
- respond(ctx, JSON.parse(raw), "json-line");
5512
+ enqueueResponse(JSON.parse(raw), "json-line");
3621
5513
  } catch {
3622
5514
  writeFrame({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } }, "json-line");
3623
5515
  }
@@ -3627,37 +5519,37 @@ var startMcpStdioServer = (ctx) => {
3627
5519
  };
3628
5520
 
3629
5521
  // src/mcp/install.ts
3630
- import { existsSync as existsSync12, mkdirSync as mkdirSync2, readFileSync as readFileSync13, writeFileSync as writeFileSync2 } from "fs";
5522
+ import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "fs";
3631
5523
  import { homedir } from "os";
3632
- import { dirname as dirname5, join as join10, resolve as resolve9 } from "path";
5524
+ import { dirname as dirname7, join as join15, resolve as resolve14 } from "path";
3633
5525
  var SERVER_NAME = "ak-docs";
3634
5526
  var mcpServerEntry = (root) => ({
3635
5527
  command: "npx",
3636
5528
  args: ["ak-docs", "mcp"],
3637
5529
  cwd: root
3638
5530
  });
3639
- var readJson = (path) => {
3640
- if (!existsSync12(path)) return {};
5531
+ var readJson2 = (path) => {
5532
+ if (!existsSync16(path)) return {};
3641
5533
  try {
3642
- const parsed = JSON.parse(readFileSync13(path, "utf8"));
5534
+ const parsed = JSON.parse(readFileSync17(path, "utf8"));
3643
5535
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
3644
5536
  } catch {
3645
5537
  return {};
3646
5538
  }
3647
5539
  };
3648
5540
  var writeJson = (path, value) => {
3649
- mkdirSync2(dirname5(path), { recursive: true });
3650
- writeFileSync2(path, `${JSON.stringify(value, null, 2)}
5541
+ mkdirSync5(dirname7(path), { recursive: true });
5542
+ writeFileSync6(path, `${JSON.stringify(value, null, 2)}
3651
5543
  `, "utf8");
3652
5544
  };
3653
5545
  var resolveTargetPath = (target, root) => {
3654
- if (target === "cursor") return resolve9(root, ".cursor", "mcp.json");
3655
- return join10(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
5546
+ if (target === "cursor") return resolve14(root, ".cursor", "mcp.json");
5547
+ return join15(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
3656
5548
  };
3657
5549
  var installMcpConfig = (root, target) => {
3658
5550
  const configPath = resolveTargetPath(target, root);
3659
- const created = !existsSync12(configPath);
3660
- const existing = readJson(configPath);
5551
+ const created = !existsSync16(configPath);
5552
+ const existing = readJson2(configPath);
3661
5553
  const servers = existing.mcpServers && typeof existing.mcpServers === "object" && !Array.isArray(existing.mcpServers) ? { ...existing.mcpServers } : {};
3662
5554
  servers[SERVER_NAME] = mcpServerEntry(root);
3663
5555
  writeJson(configPath, { ...existing, mcpServers: servers });
@@ -3922,23 +5814,23 @@ var formatDoctorText = (report) => {
3922
5814
  };
3923
5815
 
3924
5816
  // src/index-builder/watch-index.ts
3925
- import { existsSync as existsSync13, watch } from "fs";
3926
- import { dirname as dirname6, resolve as resolve10 } from "path";
5817
+ import { existsSync as existsSync17, watch } from "fs";
5818
+ import { dirname as dirname8, resolve as resolve15 } from "path";
3927
5819
  var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
3928
5820
  var NX_MANIFEST_PATTERN = /(^|[/\\])(project|package)\.json$/i;
3929
5821
  var collectWatchRoots = (root, config, configPath) => {
3930
5822
  const roots = /* @__PURE__ */ new Set();
3931
- roots.add(resolve10(root, config.corpus.agent.root));
5823
+ roots.add(resolve15(root, config.corpus.agent.root));
3932
5824
  const humanSources = config.corpus.human ? Array.isArray(config.corpus.human) ? config.corpus.human : [config.corpus.human] : [];
3933
5825
  for (const source of humanSources) {
3934
5826
  const humanOpts = source.options ?? {};
3935
5827
  for (const key of ["contentDir", "docsDir", "root", "srcDir"]) {
3936
5828
  const value = humanOpts[key];
3937
- if (typeof value === "string" && value.length) roots.add(resolve10(root, value));
5829
+ if (typeof value === "string" && value.length) roots.add(resolve15(root, value));
3938
5830
  }
3939
5831
  }
3940
- if (configPath) roots.add(dirname6(resolve10(configPath)));
3941
- return [...roots].filter((dir) => existsSync13(dir));
5832
+ if (configPath) roots.add(dirname8(resolve15(configPath)));
5833
+ return [...roots].filter((dir) => existsSync17(dir));
3942
5834
  };
3943
5835
  var watchDocBridgeIndex = (opts) => {
3944
5836
  const debounceMs = opts.debounceMs ?? 350;
@@ -3979,15 +5871,15 @@ var watchDocBridgeIndex = (opts) => {
3979
5871
  rebuild();
3980
5872
  });
3981
5873
  }
3982
- const nxRoot = resolve10(opts.root);
3983
- if (opts.config.routing?.plugin === "nx" && existsSync13(nxRoot)) {
5874
+ const nxRoot = resolve15(opts.root);
5875
+ if (opts.config.routing?.plugin === "nx" && existsSync17(nxRoot)) {
3984
5876
  watch(nxRoot, { recursive: true }, (_event, filename) => {
3985
5877
  if (!filename || !NX_MANIFEST_PATTERN.test(filename)) return;
3986
5878
  rebuild();
3987
5879
  });
3988
5880
  }
3989
- const configDir = resolve10(opts.root);
3990
- if (existsSync13(configDir)) {
5881
+ const configDir = resolve15(opts.root);
5882
+ if (existsSync17(configDir)) {
3991
5883
  watch(configDir, (_event, filename) => {
3992
5884
  if (!filename || !/doc-bridge\.config/.test(filename)) return;
3993
5885
  rebuild();
@@ -4005,9 +5897,9 @@ var watchDocBridgeIndex = (opts) => {
4005
5897
  };
4006
5898
 
4007
5899
  // src/memory/github-pr.ts
4008
- import { execFileSync, spawnSync } from "child_process";
4009
- import { existsSync as existsSync14, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
4010
- import { join as join12 } from "path";
5900
+ import { execFileSync as execFileSync2, spawnSync } from "child_process";
5901
+ import { existsSync as existsSync18, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
5902
+ import { join as join17 } from "path";
4011
5903
  var run = (cmd, args, cwd) => {
4012
5904
  const result = spawnSync(cmd, [...args], { cwd, encoding: "utf8" });
4013
5905
  const out = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
@@ -4015,11 +5907,11 @@ var run = (cmd, args, cwd) => {
4015
5907
  };
4016
5908
  var hasGh = () => run("gh", ["--version"], process.cwd()).ok;
4017
5909
  var slug = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
4018
- var defaultPromotionDraftPath = (root) => join12(root, ".doc-bridge", "drafts", `memory-promotion-${slug()}.md`);
5910
+ var defaultPromotionDraftPath = (root) => join17(root, ".doc-bridge", "drafts", `memory-promotion-${slug()}.md`);
4019
5911
  var writePromotionDraft = (root, draft, path) => {
4020
5912
  const draftPath = path ?? defaultPromotionDraftPath(root);
4021
- mkdirSync3(join12(root, ".doc-bridge", "drafts"), { recursive: true });
4022
- writeFileSync3(draftPath, `${draft.body}
5913
+ mkdirSync6(join17(root, ".doc-bridge", "drafts"), { recursive: true });
5914
+ writeFileSync7(draftPath, `${draft.body}
4023
5915
  `, "utf8");
4024
5916
  return draftPath;
4025
5917
  };
@@ -4054,7 +5946,7 @@ var promoteMemoryToGithubPr = (root, draft, options = {}) => {
4054
5946
  message: `Wrote draft to ${relDraft}. Run the printed git/gh commands to open a draft PR.`
4055
5947
  };
4056
5948
  }
4057
- if (!existsSync14(join12(root, ".git"))) {
5949
+ if (!existsSync18(join17(root, ".git"))) {
4058
5950
  return {
4059
5951
  ok: false,
4060
5952
  dryRun: false,
@@ -4132,7 +6024,7 @@ ${auth.out}`
4132
6024
  ];
4133
6025
  let prUrl = "";
4134
6026
  try {
4135
- prUrl = execFileSync("gh", prArgs, { cwd: root, encoding: "utf8" }).trim();
6027
+ prUrl = execFileSync2("gh", prArgs, { cwd: root, encoding: "utf8" }).trim();
4136
6028
  } catch (error) {
4137
6029
  const message = error instanceof Error ? error.message : String(error);
4138
6030
  return {
@@ -4157,8 +6049,8 @@ ${auth.out}`
4157
6049
  };
4158
6050
 
4159
6051
  // src/federation/llms.ts
4160
- import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
4161
- import { resolve as resolve11 } from "path";
6052
+ import { existsSync as existsSync19, readFileSync as readFileSync18 } from "fs";
6053
+ import { resolve as resolve16 } from "path";
4162
6054
  var tokenize2 = (value) => value.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length >= 2);
4163
6055
  var scoreText = (query, text) => {
4164
6056
  const hay = text.toLowerCase();
@@ -4173,9 +6065,9 @@ var defaultFetchText = async (url) => {
4173
6065
  var sourceText = async (root, source, fetchText) => {
4174
6066
  try {
4175
6067
  if (/^https?:\/\//.test(source)) return await fetchText(source);
4176
- const path = resolve11(root, source);
4177
- if (!existsSync15(path)) return null;
4178
- return readFileSync14(path, "utf8");
6068
+ const path = resolve16(root, source);
6069
+ if (!existsSync19(path)) return null;
6070
+ return readFileSync18(path, "utf8");
4179
6071
  } catch {
4180
6072
  return null;
4181
6073
  }
@@ -4260,11 +6152,11 @@ var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
4260
6152
  };
4261
6153
 
4262
6154
  // src/intelligence/rag.ts
4263
- import { readFileSync as readFileSync15 } from "fs";
4264
- import { join as join14 } from "path";
6155
+ import { readFileSync as readFileSync19 } from "fs";
6156
+ import { join as join19 } from "path";
4265
6157
 
4266
6158
  // src/intelligence/adapter.ts
4267
- import { join as join13 } from "path";
6159
+ import { join as join18 } from "path";
4268
6160
 
4269
6161
  // src/intelligence/peers.ts
4270
6162
  var PeerMissingError = class extends Error {
@@ -4391,7 +6283,7 @@ var resolveIntelligenceRuntime = async (config) => {
4391
6283
  }
4392
6284
  return { adapter, embed, provider, ...model ? { model } : {} };
4393
6285
  };
4394
- var defaultVectorStorePath = (root) => join13(root, ".doc-bridge", "vectors");
6286
+ var defaultVectorStorePath = (root) => join18(root, ".doc-bridge", "vectors");
4395
6287
 
4396
6288
  // src/intelligence/rag.ts
4397
6289
  var loadDocuments = (root, index, sources) => {
@@ -4399,10 +6291,10 @@ var loadDocuments = (root, index, sources) => {
4399
6291
  const docs = [];
4400
6292
  if (includeAgent) {
4401
6293
  for (const entry of index.knowledge) {
4402
- const abs = join14(root, entry.path);
6294
+ const abs = join19(root, entry.path);
4403
6295
  let content = "";
4404
6296
  try {
4405
- content = readFileSync15(abs, "utf8");
6297
+ content = readFileSync19(abs, "utf8");
4406
6298
  } catch {
4407
6299
  content = [entry.title, entry.description].filter(Boolean).join("\n\n");
4408
6300
  }
@@ -4420,7 +6312,7 @@ var createDocBridgeRag = async (root, config, index) => {
4420
6312
  const { embed } = await resolveIntelligenceRuntime(config);
4421
6313
  const ragMod = await importPeer("@agentskit/rag");
4422
6314
  const memoryMod = await importPeer("@agentskit/memory");
4423
- const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join14(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
6315
+ const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join19(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
4424
6316
  const store = memoryMod.fileVectorMemory({ path: storePath });
4425
6317
  const rag = ragMod.createRAG({
4426
6318
  embed,
@@ -4674,15 +6566,22 @@ var docBridgePatternPayload = () => ({
4674
6566
  body: docBridgePatternMarkdown()
4675
6567
  });
4676
6568
  export {
6569
+ AffectedFileSchema,
4677
6570
  AgentHandoffLegacySchema,
4678
6571
  AgentHandoffV1JsonSchema,
4679
6572
  AgentHandoffV1Schema,
6573
+ AgentProposalV1Schema,
4680
6574
  AgentSearchV1Schema,
4681
6575
  ConfigNotFoundError,
6576
+ CoverageSchema,
6577
+ DEFAULT_REGISTRY_AGENT_ID,
6578
+ DEFAULT_SAFETY_EXCLUDES,
4682
6579
  DOCUMENTATION_STANDARD_V1_ID,
4683
6580
  DOCUMENTATION_STANDARD_V1_STATUS,
4684
6581
  DOC_BRIDGE_PATTERN_ID,
4685
6582
  DOC_BRIDGE_PATTERN_META,
6583
+ DiagnosticSeveritySchema,
6584
+ DiscoverySnapshotV1Schema,
4686
6585
  DocBridgeConfigV1Schema,
4687
6586
  DocBridgeIndexV1JsonSchema,
4688
6587
  DocBridgeIndexV1Schema,
@@ -4690,11 +6589,20 @@ export {
4690
6589
  DocumentationStandardRuleIdSchema,
4691
6590
  DocumentationStandardV1ConfigSchema,
4692
6591
  EcosystemContractEvidenceSchema,
6592
+ EntitySchema,
6593
+ EvidenceSchema,
6594
+ EvidenceSourceSchema,
6595
+ FindingStatusSchema,
6596
+ FixChangeSchema,
6597
+ FixProposalStatusSchema,
6598
+ FixProposalV1Schema,
4693
6599
  HANDOFF_SCHEMA_VERSION,
4694
6600
  HandoffBridgeSchema,
4695
6601
  HandoffTargetTypeSchema,
4696
6602
  INDEX_SCHEMA_VERSION,
4697
6603
  IndexNotFoundError,
6604
+ KNOWLEDGE_CONTENT_HASH_ALGO,
6605
+ KNOWLEDGE_SCHEMA_VERSION,
4698
6606
  KnowledgeEntrySchema,
4699
6607
  MCP_TOOLS,
4700
6608
  MEMORY_CANDIDATE_SCHEMA_VERSION,
@@ -4702,21 +6610,47 @@ export {
4702
6610
  MemoryCandidateV1Schema,
4703
6611
  PACKAGE_VERSION,
4704
6612
  PeerMissingError,
6613
+ ProjectIdentitySchema,
6614
+ ProposalOriginSchema,
6615
+ ProvenanceSchema,
6616
+ ReconciliationReportV1Schema,
6617
+ RelationSchema,
6618
+ RepositorySafetyConfigSchema,
6619
+ RuleIdSchema,
6620
+ RuleSeveritySchema,
6621
+ RulesConfigSchema,
6622
+ WORKFLOW_STAGES,
6623
+ WorkflowConfigSchema,
6624
+ WorkflowRunV1Schema,
6625
+ WorkflowStateSchema,
6626
+ WorkflowStepSchema,
6627
+ WorkflowTransitionSchema,
4705
6628
  applyConfigDefaults,
6629
+ applyDocumentationDeclarations,
6630
+ applyFixProposal,
6631
+ approveFixProposal,
4706
6632
  buildDocBridgeIndex,
4707
6633
  buildLookup,
6634
+ canonicalJsonV1,
4708
6635
  chunksFromMarkdown,
4709
6636
  classifyMemoryCandidates,
4710
6637
  collectPackages,
6638
+ containedPath,
6639
+ contentHashForArtifactV1,
6640
+ createArtifactNormalizationProposal,
4711
6641
  createDocBridgeRag,
4712
6642
  createDocBridgeRetriever,
6643
+ createMarkdownLinkFixProposal,
6644
+ createRegistryAgentAdapter,
4713
6645
  defaultPromotionDraftPath,
4714
6646
  defineConfig,
4715
6647
  discoverNxProjects,
6648
+ discoverRepository,
4716
6649
  docBridgePatternMarkdown,
4717
6650
  docBridgePatternPayload,
4718
6651
  doctorBadgeMetrics,
4719
6652
  draftMemoryPromotion,
6653
+ evaluateRules,
4720
6654
  formatDoctorBadgeJson,
4721
6655
  formatDoctorBadgeMarkdown,
4722
6656
  formatDoctorText,
@@ -4733,19 +6667,37 @@ export {
4733
6667
  loadConfig,
4734
6668
  loadDocBridgeIndex,
4735
6669
  loadFederatedChunks,
6670
+ loadRegistryAgentMetadata,
6671
+ loadRegistryAgentRunner,
6672
+ loadWorkflowManifest,
6673
+ loadWorkflowStepOutput,
4736
6674
  mcpSnippet,
4737
6675
  normalizeAgentHandoff,
4738
6676
  parseAgentHandoff,
6677
+ parseAgentProposal,
4739
6678
  parseAgentSearch,
6679
+ parseDiscoverySnapshot,
4740
6680
  parseDocBridgeConfig,
4741
6681
  parseDocBridgeIndex,
6682
+ parseDocumentationDeclarations,
6683
+ parseFixProposal,
4742
6684
  parseLlmsTxtLinks,
4743
6685
  parseMemoryCandidate,
6686
+ parseReconciliationReport,
6687
+ parseRuleId,
6688
+ parseRuleSeverity,
6689
+ parseWorkflowRun,
6690
+ persistRegistryAgentProposal,
4744
6691
  projectRootFromConfigPath,
4745
6692
  promoteMemoryToGithubPr,
6693
+ reconcileKnowledge,
6694
+ redactSecrets,
6695
+ redactValue,
6696
+ renderOfflineReport,
4746
6697
  resolveGateIds,
4747
6698
  resolveProjectRoot,
4748
6699
  resolveRoot,
6700
+ respondMcpRequest,
4749
6701
  retrieveDocBridgeChunks,
4750
6702
  retrieveHybridChunks,
4751
6703
  runChatOnce,
@@ -4754,7 +6706,9 @@ export {
4754
6706
  runGate,
4755
6707
  runGates,
4756
6708
  runQuery,
6709
+ runWorkflow,
4757
6710
  safeParseAgentHandoff,
6711
+ safeWalkFiles,
4758
6712
  scanHumanDocRecords,
4759
6713
  scanHumanDocs,
4760
6714
  scanMemorySafety,