@agentskit/doc-bridge 1.7.44 → 1.7.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/CONTRIBUTING.md +6 -4
- package/action.yml +1 -1
- package/dist/cli/program.js +378 -189
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +5 -3
- package/dist/config/index.js.map +1 -1
- package/dist/index-BUL0q7s8.d.ts +660 -0
- package/dist/index.d.ts +637 -2724
- package/dist/index.js +328 -149
- package/dist/index.js.map +1 -1
- package/docs/RELEASE.md +22 -8
- package/docs/agent-corpus/INDEX.md +2 -2
- package/docs/agent-corpus/chat.md +2 -2
- package/docs/agent-corpus/cli.md +2 -2
- package/docs/agent-corpus/conformance.md +2 -2
- package/docs/agent-corpus/doc-bridge.md +1 -1
- package/docs/agent-corpus/doctor.md +2 -2
- package/docs/agent-corpus/gates.md +2 -2
- package/docs/agent-corpus/mcp.md +2 -2
- package/docs/agent-corpus/memory.md +2 -2
- package/docs/agent-corpus/query.md +2 -2
- package/docs/knowledge-engine-runbook.md +13 -1
- package/docs/spec/benchmark-v1.md +6 -0
- package/docs/spec/config-v1.md +45 -0
- package/docs/validation-cycle-plan.md +19 -0
- package/docs/verification-harness.md +4 -0
- package/mcpb/manifest.json +1 -1
- package/package.json +68 -70
- package/scripts/check-ecosystem-upstream.mjs +3 -2
- package/scripts/report-visual-check.mjs +20 -3
- package/scripts/verification-harness.mjs +0 -1
- package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
- package/src/cli/demo.ts +2 -2
- package/src/cli/program.ts +15 -5
- package/src/config/load-config.ts +7 -1
- package/src/config/schema.ts +4 -2
- package/src/conformance/documentation-standard-v1.ts +14 -8
- package/src/discovery/documentation.ts +44 -18
- package/src/discovery/repository.ts +77 -28
- package/src/doctor/run-doctor.ts +2 -15
- package/src/federation/llms.ts +72 -20
- package/src/fixes/proposals.ts +4 -3
- package/src/index-builder/human-adapters/fumadocs.ts +1 -1
- package/src/index-builder/watch-index.ts +1 -1
- package/src/lib/bounded-text.ts +15 -10
- package/src/reconciliation/reconcile.ts +47 -5
- package/src/report/html.ts +21 -15
- package/src/rules/engine.ts +15 -2
- package/src/safety/repository.ts +1 -1
- package/src/schemas/knowledge.ts +5 -2
- package/src/validate.ts +7 -1
- package/src/version.ts +1 -1
- package/dist/index-C2PCQSrB.d.ts +0 -2251
package/dist/cli/program.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/cli/program.ts
|
|
2
|
-
import { existsSync as existsSync21, mkdirSync as
|
|
3
|
-
import { dirname as
|
|
2
|
+
import { closeSync as closeSync3, existsSync as existsSync21, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync21, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
3
|
+
import { dirname as dirname9, relative as relative10, resolve as resolve18 } from "path";
|
|
4
4
|
import { createInterface } from "readline/promises";
|
|
5
5
|
|
|
6
6
|
// src/config/load-config.ts
|
|
@@ -467,17 +467,19 @@ var RuleIdSchema = z.enum([
|
|
|
467
467
|
var RuleSeveritySchema = z.enum(["off", "info", "warn", "error"]);
|
|
468
468
|
var RulesConfigSchema = z.object({
|
|
469
469
|
mode: z.enum(["default", "recommended", "strict"]).optional(),
|
|
470
|
-
severity: z.
|
|
470
|
+
severity: z.partialRecord(RuleIdSchema, RuleSeveritySchema).optional(),
|
|
471
471
|
ignore: z.array(RuleIdSchema).max(128).optional(),
|
|
472
472
|
criticalEntities: z.array(z.string().min(1).max(256)).max(128).optional(),
|
|
473
473
|
criticalPaths: z.array(z.string().min(1).max(512)).max(128).optional(),
|
|
474
|
-
warningThresholds: z.
|
|
474
|
+
warningThresholds: z.partialRecord(RuleIdSchema, z.number().int().min(1).max(1e5)).optional()
|
|
475
475
|
}).strict();
|
|
476
476
|
var ReconciliationConfigSchema = z.object({
|
|
477
477
|
/** Semantic comparison level. Raw discovery always keeps file-level relations. */
|
|
478
478
|
scope: z.enum(["file", "module", "package"]).optional(),
|
|
479
479
|
/** Relation kinds that must have documentation declarations. Omit to require all observed kinds; [] disables this signal. */
|
|
480
480
|
requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional(),
|
|
481
|
+
/** Limit missing-declaration findings to relations whose endpoints are internal project entities. */
|
|
482
|
+
requiredRelationTargets: z.enum(["all", "internal"]).optional(),
|
|
481
483
|
includeOrphanedDocuments: z.boolean().optional()
|
|
482
484
|
}).strict();
|
|
483
485
|
var AnalysisConfigSchema = z.object({
|
|
@@ -756,7 +758,7 @@ var parseConfig = (input) => {
|
|
|
756
758
|
throw new Error(
|
|
757
759
|
`Invalid doc-bridge config:
|
|
758
760
|
${result.error.issues.map(
|
|
759
|
-
(issue) => ` - ${issue.path.join(".") || "(root)"}: ${issue.message}`
|
|
761
|
+
(issue) => ` - ${issue.path.join(".") || "(root)"}: ${issue.code === "invalid_type" && issue.message.endsWith("received undefined") ? "Required" : issue.code === "invalid_value" && "values" in issue ? "Invalid enum value" : issue.message}`
|
|
760
762
|
).join("\n")}`
|
|
761
763
|
);
|
|
762
764
|
};
|
|
@@ -780,7 +782,7 @@ var projectRootFromConfigPath = (configFilePath, projectRootField) => {
|
|
|
780
782
|
};
|
|
781
783
|
|
|
782
784
|
// src/conformance/documentation-standard-v1.ts
|
|
783
|
-
import { existsSync as existsSync9,
|
|
785
|
+
import { closeSync as closeSync2, existsSync as existsSync9, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync8, realpathSync as realpathSync6 } from "fs";
|
|
784
786
|
import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve6, sep as sep5 } from "path";
|
|
785
787
|
|
|
786
788
|
// src/conformance/ecosystem-contract.ts
|
|
@@ -960,7 +962,7 @@ var parseCanonicalEcosystemContract = (manifestInput, claimsInput) => {
|
|
|
960
962
|
|
|
961
963
|
// src/index-builder/build-index.ts
|
|
962
964
|
import { mkdirSync, readFileSync as readFileSync7, writeFileSync } from "fs";
|
|
963
|
-
import { dirname as
|
|
965
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
964
966
|
|
|
965
967
|
// src/lib/paths.ts
|
|
966
968
|
import { existsSync as existsSync2, realpathSync } from "fs";
|
|
@@ -1044,22 +1046,27 @@ var defaultChecksForTarget = (root, opts) => {
|
|
|
1044
1046
|
import { minimatch } from "minimatch";
|
|
1045
1047
|
|
|
1046
1048
|
// src/lib/bounded-text.ts
|
|
1047
|
-
import { readFileSync as readFileSync3
|
|
1049
|
+
import { closeSync, fstatSync, openSync, readFileSync as readFileSync3 } from "fs";
|
|
1048
1050
|
var MAX_DOCUMENT_BYTES = 4 * 1024 * 1024;
|
|
1049
1051
|
var MAX_CORPUS_BYTES = 64 * 1024 * 1024;
|
|
1050
1052
|
var readBoundedText = (path, budget, limits) => {
|
|
1051
1053
|
const maxFileBytes = limits?.maxFileBytes ?? MAX_DOCUMENT_BYTES;
|
|
1052
1054
|
const maxCorpusBytes = limits?.maxCorpusBytes ?? MAX_CORPUS_BYTES;
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
throw new Error(`Documentation
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1055
|
+
const fd = openSync(path, "r");
|
|
1056
|
+
try {
|
|
1057
|
+
const stat = fstatSync(fd);
|
|
1058
|
+
if (!stat.isFile()) throw new Error(`Documentation path is not a regular file: ${path}`);
|
|
1059
|
+
if (stat.size > maxFileBytes) {
|
|
1060
|
+
throw new Error(`Documentation file exceeds the ${maxFileBytes} byte limit: ${path}`);
|
|
1061
|
+
}
|
|
1062
|
+
if (budget.used + stat.size > maxCorpusBytes) {
|
|
1063
|
+
throw new Error(`Documentation corpus exceeds the ${maxCorpusBytes} byte read budget.`);
|
|
1064
|
+
}
|
|
1065
|
+
budget.used += stat.size;
|
|
1066
|
+
return readFileSync3(fd, "utf8");
|
|
1067
|
+
} finally {
|
|
1068
|
+
closeSync(fd);
|
|
1060
1069
|
}
|
|
1061
|
-
budget.used += stat.size;
|
|
1062
|
-
return readFileSync3(path, "utf8");
|
|
1063
1070
|
};
|
|
1064
1071
|
|
|
1065
1072
|
// src/lib/markdown.ts
|
|
@@ -1483,7 +1490,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1483
1490
|
};
|
|
1484
1491
|
|
|
1485
1492
|
// src/version.ts
|
|
1486
|
-
var PACKAGE_VERSION = "1.7.
|
|
1493
|
+
var PACKAGE_VERSION = "1.7.45";
|
|
1487
1494
|
|
|
1488
1495
|
// src/index-builder/capabilities.ts
|
|
1489
1496
|
var renderCapabilitiesJson = (config, index, paths) => {
|
|
@@ -1914,7 +1921,7 @@ var scanHumanDocs = (root, config) => Object.fromEntries(scanHumanDocRecords(roo
|
|
|
1914
1921
|
|
|
1915
1922
|
// src/index-builder/plugins/nx.ts
|
|
1916
1923
|
import { existsSync as existsSync6, lstatSync as lstatSync2, readFileSync as readFileSync5, realpathSync as realpathSync5 } from "fs";
|
|
1917
|
-
import { basename as basename2, dirname as
|
|
1924
|
+
import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve5, sep as sep4 } from "path";
|
|
1918
1925
|
var NX_SCAN_SKIP = /* @__PURE__ */ new Set([
|
|
1919
1926
|
"node_modules",
|
|
1920
1927
|
".git",
|
|
@@ -1978,7 +1985,7 @@ var discoverNxProjects = (root, config) => {
|
|
|
1978
1985
|
for (const manifest of manifests) {
|
|
1979
1986
|
const json = readJsonRecord(manifest);
|
|
1980
1987
|
if (!json) continue;
|
|
1981
|
-
const manifestDir =
|
|
1988
|
+
const manifestDir = dirname2(manifest);
|
|
1982
1989
|
const manifestName = basename2(manifest);
|
|
1983
1990
|
const isProjectJson = manifestName === "project.json";
|
|
1984
1991
|
if (!isProjectJson && manifestName !== "package.json") continue;
|
|
@@ -2026,7 +2033,7 @@ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
|
2026
2033
|
import { basename as basename3, join as join6 } from "path";
|
|
2027
2034
|
|
|
2028
2035
|
// src/lib/glob-expand.ts
|
|
2029
|
-
import { existsSync as existsSync7, readdirSync as readdirSync2, statSync
|
|
2036
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, statSync } from "fs";
|
|
2030
2037
|
import { join as join5 } from "path";
|
|
2031
2038
|
var expandWorkspaceGlobs = (root, patterns) => {
|
|
2032
2039
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -2043,7 +2050,7 @@ var expandWorkspaceGlobs = (root, patterns) => {
|
|
|
2043
2050
|
for (const name of readdirSync2(base)) {
|
|
2044
2051
|
const abs = join5(base, name);
|
|
2045
2052
|
try {
|
|
2046
|
-
if (
|
|
2053
|
+
if (statSync(abs).isDirectory()) dirs.add(toPosix(abs));
|
|
2047
2054
|
} catch {
|
|
2048
2055
|
}
|
|
2049
2056
|
}
|
|
@@ -2153,7 +2160,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
2153
2160
|
lookup
|
|
2154
2161
|
};
|
|
2155
2162
|
if (write) {
|
|
2156
|
-
mkdirSync(
|
|
2163
|
+
mkdirSync(dirname3(indexPath), { recursive: true });
|
|
2157
2164
|
writeFileSync(indexPath, `${JSON.stringify(index, null, 2)}
|
|
2158
2165
|
`, "utf8");
|
|
2159
2166
|
}
|
|
@@ -2176,7 +2183,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
2176
2183
|
const capabilitiesOut = config.index?.capabilities?.outFile ?? ".doc-bridge/capabilities.json";
|
|
2177
2184
|
capabilitiesPath = join7(root, capabilitiesOut);
|
|
2178
2185
|
if (write) {
|
|
2179
|
-
mkdirSync(
|
|
2186
|
+
mkdirSync(dirname3(capabilitiesPath), { recursive: true });
|
|
2180
2187
|
writeFileSync(
|
|
2181
2188
|
capabilitiesPath,
|
|
2182
2189
|
renderCapabilitiesJson(config, index, {
|
|
@@ -2222,11 +2229,10 @@ var fileEvidence = (root, path, options) => {
|
|
|
2222
2229
|
evidence: { path, detail: "Path escapes the project root." }
|
|
2223
2230
|
};
|
|
2224
2231
|
}
|
|
2225
|
-
|
|
2226
|
-
return { exists: false, content: "", evidence: { path, detail: "File does not exist." } };
|
|
2227
|
-
}
|
|
2232
|
+
let fd;
|
|
2228
2233
|
try {
|
|
2229
|
-
|
|
2234
|
+
fd = openSync2(abs, "r");
|
|
2235
|
+
const stat = fstatSync2(fd);
|
|
2230
2236
|
if (!stat.isFile()) {
|
|
2231
2237
|
return { exists: false, content: "", evidence: { path, detail: "Path is not a regular file." } };
|
|
2232
2238
|
}
|
|
@@ -2247,7 +2253,7 @@ var fileEvidence = (root, path, options) => {
|
|
|
2247
2253
|
evidence: { path, detail: `Text evidence exceeds ${MAX_TEXT_EVIDENCE_BYTES} bytes.` }
|
|
2248
2254
|
};
|
|
2249
2255
|
}
|
|
2250
|
-
const content = readFileSync8(
|
|
2256
|
+
const content = readFileSync8(fd, "utf8");
|
|
2251
2257
|
return {
|
|
2252
2258
|
exists: content.trim().length > 0,
|
|
2253
2259
|
content,
|
|
@@ -2256,8 +2262,15 @@ var fileEvidence = (root, path, options) => {
|
|
|
2256
2262
|
detail: content.trim().length > 0 ? "File exists and is non-empty." : "File is empty."
|
|
2257
2263
|
}
|
|
2258
2264
|
};
|
|
2259
|
-
} catch {
|
|
2260
|
-
|
|
2265
|
+
} catch (error) {
|
|
2266
|
+
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
2267
|
+
return {
|
|
2268
|
+
exists: false,
|
|
2269
|
+
content: "",
|
|
2270
|
+
evidence: { path, detail: code === "ENOENT" ? "File does not exist." : "File is not readable text." }
|
|
2271
|
+
};
|
|
2272
|
+
} finally {
|
|
2273
|
+
if (fd !== void 0) closeSync2(fd);
|
|
2261
2274
|
}
|
|
2262
2275
|
};
|
|
2263
2276
|
var resultWithException = (draft, options) => {
|
|
@@ -2623,7 +2636,7 @@ var EntitySchema = z3.object({
|
|
|
2623
2636
|
aliases: z3.array(boundedString(256)).max(32).optional(),
|
|
2624
2637
|
provenance: ProvenanceSchema,
|
|
2625
2638
|
evidence: z3.array(EvidenceSchema2).max(64),
|
|
2626
|
-
metadata: z3.record(z3.unknown()).optional()
|
|
2639
|
+
metadata: z3.record(z3.string(), z3.unknown()).optional()
|
|
2627
2640
|
}).strict();
|
|
2628
2641
|
var RelationSchema = z3.object({
|
|
2629
2642
|
id: boundedString(256),
|
|
@@ -2633,7 +2646,7 @@ var RelationSchema = z3.object({
|
|
|
2633
2646
|
discriminator: boundedString(256).optional(),
|
|
2634
2647
|
provenance: ProvenanceSchema,
|
|
2635
2648
|
evidence: z3.array(EvidenceSchema2).max(64),
|
|
2636
|
-
metadata: z3.record(z3.unknown()).optional()
|
|
2649
|
+
metadata: z3.record(z3.string(), z3.unknown()).optional()
|
|
2637
2650
|
}).strict();
|
|
2638
2651
|
var DiscoverySnapshotV1Schema = z3.object({
|
|
2639
2652
|
type: z3.literal("discovery-snapshot"),
|
|
@@ -2679,11 +2692,14 @@ var ReconciliationReportV1Schema = z3.object({
|
|
|
2679
2692
|
diagnosticCount: z3.number().int().nonnegative(),
|
|
2680
2693
|
scope: z3.enum(["file", "module", "package"]).optional(),
|
|
2681
2694
|
requiredRelationKinds: z3.array(boundedString(128)).max(128).optional(),
|
|
2695
|
+
requiredRelationTargets: z3.enum(["all", "internal"]).optional(),
|
|
2682
2696
|
diagnosticsByCode: z3.record(z3.string().max(128), z3.number().int().nonnegative()).optional(),
|
|
2683
2697
|
diagnosticsByStatus: z3.record(z3.string().max(128), z3.number().int().nonnegative()).optional(),
|
|
2684
2698
|
documentation: z3.object({
|
|
2685
2699
|
documentCount: z3.number().int().nonnegative(),
|
|
2686
2700
|
documentedDocumentCount: z3.number().int().nonnegative(),
|
|
2701
|
+
documentClassificationCounts: z3.record(z3.string().max(128), z3.number().int().nonnegative()),
|
|
2702
|
+
documentedDocumentClassificationCounts: z3.record(z3.string().max(128), z3.number().int().nonnegative()),
|
|
2687
2703
|
packageCount: z3.number().int().nonnegative(),
|
|
2688
2704
|
packageStatus: z3.object({
|
|
2689
2705
|
fresh: z3.number().int().nonnegative(),
|
|
@@ -2809,6 +2825,29 @@ var scalar = (value) => {
|
|
|
2809
2825
|
}
|
|
2810
2826
|
return trimmed;
|
|
2811
2827
|
};
|
|
2828
|
+
var isFieldName = (value) => {
|
|
2829
|
+
if (!/^[A-Za-z]/.test(value)) return false;
|
|
2830
|
+
for (const character of value.slice(1)) {
|
|
2831
|
+
if (!/[A-Za-z0-9_-]/.test(character)) return false;
|
|
2832
|
+
}
|
|
2833
|
+
return true;
|
|
2834
|
+
};
|
|
2835
|
+
var parseIndentedField = (raw, indentation) => {
|
|
2836
|
+
const prefix = " ".repeat(indentation);
|
|
2837
|
+
if (!raw.startsWith(prefix) || raw[indentation] === " ") return void 0;
|
|
2838
|
+
const body = raw.slice(indentation);
|
|
2839
|
+
const separator = body.indexOf(":");
|
|
2840
|
+
if (separator <= 0) return void 0;
|
|
2841
|
+
const key = body.slice(0, separator).trim();
|
|
2842
|
+
return isFieldName(key) ? { key, value: body.slice(separator + 1).trim() } : void 0;
|
|
2843
|
+
};
|
|
2844
|
+
var parseListItem = (raw, indentation) => {
|
|
2845
|
+
const prefix = `${" ".repeat(indentation)}-`;
|
|
2846
|
+
if (!raw.startsWith(prefix)) return void 0;
|
|
2847
|
+
const rest = raw.slice(prefix.length);
|
|
2848
|
+
if (rest && !/\s/.test(rest[0] ?? "")) return void 0;
|
|
2849
|
+
return rest.trim();
|
|
2850
|
+
};
|
|
2812
2851
|
var conventionalPackageReference = (path, agentRoot) => {
|
|
2813
2852
|
const prefix = `${agentRoot.replace(/\/$/, "")}/`;
|
|
2814
2853
|
if (!path.startsWith(prefix)) return void 0;
|
|
@@ -2918,11 +2957,10 @@ var parseBlock = (input, options = {}) => {
|
|
|
2918
2957
|
section = void 0;
|
|
2919
2958
|
continue;
|
|
2920
2959
|
}
|
|
2921
|
-
|
|
2960
|
+
const sectionField = parseIndentedField(raw, 2);
|
|
2961
|
+
if (sectionField) {
|
|
2922
2962
|
finishRelation();
|
|
2923
|
-
const
|
|
2924
|
-
const key = match?.[1];
|
|
2925
|
-
const value = match?.[2] ?? "";
|
|
2963
|
+
const { key, value } = sectionField;
|
|
2926
2964
|
if (key !== "covers" && key !== "relations") {
|
|
2927
2965
|
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown docbridge field: ${key ?? "(missing)"}.`, line);
|
|
2928
2966
|
section = void 0;
|
|
@@ -2939,35 +2977,36 @@ var parseBlock = (input, options = {}) => {
|
|
|
2939
2977
|
}
|
|
2940
2978
|
continue;
|
|
2941
2979
|
}
|
|
2942
|
-
|
|
2943
|
-
|
|
2980
|
+
const listValue = parseListItem(raw, 4);
|
|
2981
|
+
if (section === "covers" && listValue !== void 0) {
|
|
2982
|
+
const value = scalar(listValue);
|
|
2944
2983
|
if (!value) addDiagnostic(diagnostics, input, "DOCBRIDGE_REFERENCE_MISSING", "covers entries must not be empty.", line);
|
|
2945
2984
|
else covers.push({ value, line });
|
|
2946
2985
|
continue;
|
|
2947
2986
|
}
|
|
2948
|
-
if (section === "relations" &&
|
|
2987
|
+
if (section === "relations" && listValue !== void 0) {
|
|
2949
2988
|
finishRelation();
|
|
2950
|
-
const firstField =
|
|
2989
|
+
const firstField = parseIndentedField(` ${listValue}`, 4);
|
|
2951
2990
|
current = { startLine: line, endLine: line, fields: /* @__PURE__ */ new Set() };
|
|
2952
|
-
if (firstField?.
|
|
2953
|
-
current.fields.add(firstField
|
|
2954
|
-
current[firstField
|
|
2955
|
-
} else if (
|
|
2991
|
+
if (firstField?.key) {
|
|
2992
|
+
current.fields.add(firstField.key);
|
|
2993
|
+
current[firstField.key] = scalar(firstField.value);
|
|
2994
|
+
} else if (listValue) {
|
|
2956
2995
|
addDiagnostic(diagnostics, input, "DOCBRIDGE_RELATION_INVALID", "Relation entries must be field mappings.", line);
|
|
2957
2996
|
}
|
|
2958
2997
|
continue;
|
|
2959
2998
|
}
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
const key = field
|
|
2999
|
+
const field = parseIndentedField(raw, 6);
|
|
3000
|
+
if (section === "relations" && current && field) {
|
|
3001
|
+
const { key, value } = field;
|
|
2963
3002
|
current.endLine = line;
|
|
2964
|
-
if (!
|
|
2965
|
-
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown relation field: ${key
|
|
3003
|
+
if (!["from", "to", "kind", "detection"].includes(key)) {
|
|
3004
|
+
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown relation field: ${key}.`, line);
|
|
2966
3005
|
} else if (current.fields.has(key)) {
|
|
2967
3006
|
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_DUPLICATE", `Duplicate relation field: ${key}.`, line);
|
|
2968
3007
|
} else {
|
|
2969
3008
|
current.fields.add(key);
|
|
2970
|
-
current[key] = scalar(
|
|
3009
|
+
current[key] = scalar(value);
|
|
2971
3010
|
}
|
|
2972
3011
|
continue;
|
|
2973
3012
|
}
|
|
@@ -3058,14 +3097,14 @@ var applyDocumentationDeclarations = (snapshot, documents, options = {}) => {
|
|
|
3058
3097
|
// src/discovery/repository.ts
|
|
3059
3098
|
import { execFileSync } from "child_process";
|
|
3060
3099
|
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
3061
|
-
import { basename as basename4, dirname as
|
|
3100
|
+
import { basename as basename4, dirname as dirname4, extname, join as join8, relative as relative6, resolve as resolve8, sep as sep7 } from "path";
|
|
3062
3101
|
import * as ts from "typescript";
|
|
3063
3102
|
|
|
3064
3103
|
// src/safety/repository.ts
|
|
3065
|
-
import { lstatSync as lstatSync3, readdirSync as readdirSync3, realpathSync as realpathSync7, statSync as
|
|
3104
|
+
import { lstatSync as lstatSync3, readdirSync as readdirSync3, realpathSync as realpathSync7, statSync as statSync2 } from "fs";
|
|
3066
3105
|
import { isAbsolute as isAbsolute5, relative as relative5, resolve as resolve7, sep as sep6 } from "path";
|
|
3067
3106
|
import { minimatch as minimatch4 } from "minimatch";
|
|
3068
|
-
var DEFAULT_SAFETY_EXCLUDES = ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**", "**/.doc-bridge/**", "**/.turbo/**", "**/.env", "**/.env.*", "**/*secret*", "**/*credential*", "**/*.pem", "**/*.key"];
|
|
3107
|
+
var DEFAULT_SAFETY_EXCLUDES = ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**", "**/.doc-bridge/**", "**/.next/**", "**/out/**", "**/.turbo/**", "**/.svelte-kit/**", "**/.mcpb-build/**", "**/.mcpb-output/**", "**/.env", "**/.env.*", "**/*secret*", "**/*credential*", "**/*.pem", "**/*.key"];
|
|
3069
3108
|
var containedPath = (root, candidate) => {
|
|
3070
3109
|
const projectRoot = realpathSync7.native(resolve7(root));
|
|
3071
3110
|
const unresolved = resolve7(projectRoot, candidate);
|
|
@@ -3125,7 +3164,7 @@ var safeWalkFiles = (root, options = {}) => {
|
|
|
3125
3164
|
reason = `Repository scan exceeded the ${options.maxFiles ?? 1e4} file limit.`;
|
|
3126
3165
|
return;
|
|
3127
3166
|
}
|
|
3128
|
-
bytes +=
|
|
3167
|
+
bytes += statSync2(absolute).size;
|
|
3129
3168
|
if (options.maxBytes !== void 0 && bytes > options.maxBytes) {
|
|
3130
3169
|
reason = `Repository scan exceeded the ${options.maxBytes} byte limit.`;
|
|
3131
3170
|
return;
|
|
@@ -3182,6 +3221,7 @@ var firstLineContaining = (text, pattern) => {
|
|
|
3182
3221
|
};
|
|
3183
3222
|
var documentClassification = (path) => {
|
|
3184
3223
|
if (/(^|\/)docs\/for-agents(?:\/|$)/.test(path)) return "agent";
|
|
3224
|
+
if (/(^|\/)docs-archive(?:\/|$)/.test(path)) return "archive";
|
|
3185
3225
|
if (/(^|\/)docs(?:\/|$)/.test(path)) return "human";
|
|
3186
3226
|
if (/(^|\/)(README|CONTRIBUTING|SECURITY|CHANGELOG)(?:\.|$)/i.test(path)) return "project";
|
|
3187
3227
|
return "unclassified";
|
|
@@ -3260,7 +3300,7 @@ var readCompilerOptions = (root) => {
|
|
|
3260
3300
|
if (!configPath) return { options: {} };
|
|
3261
3301
|
const parsed = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
3262
3302
|
if (parsed.error) return { options: {}, error: ts.flattenDiagnosticMessageText(parsed.error.messageText, "\n") };
|
|
3263
|
-
const config = ts.parseJsonConfigFileContent(parsed.config, ts.sys,
|
|
3303
|
+
const config = ts.parseJsonConfigFileContent(parsed.config, ts.sys, dirname4(configPath));
|
|
3264
3304
|
if (config.errors.length) {
|
|
3265
3305
|
return {
|
|
3266
3306
|
options: config.options,
|
|
@@ -3329,20 +3369,56 @@ var exportedNames = (sourceFile) => {
|
|
|
3329
3369
|
};
|
|
3330
3370
|
var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
|
|
3331
3371
|
const references = [];
|
|
3372
|
+
const dynamicEvidence = [];
|
|
3332
3373
|
let hasDynamic = false;
|
|
3333
3374
|
let hasLiteralDynamic = false;
|
|
3334
3375
|
let hasRuntimeWiring = false;
|
|
3335
3376
|
let hasUnresolvedRuntimeWiring = false;
|
|
3336
3377
|
const importedBindings = /* @__PURE__ */ new Map();
|
|
3337
3378
|
const staticStringBindings = /* @__PURE__ */ new Map();
|
|
3379
|
+
const localBindings = /* @__PURE__ */ new Set();
|
|
3380
|
+
const resolveStaticString = (expression) => {
|
|
3381
|
+
if (ts.isStringLiteralLike(expression)) return expression.text;
|
|
3382
|
+
if (ts.isIdentifier(expression)) return staticStringBindings.get(expression.text);
|
|
3383
|
+
if (ts.isParenthesizedExpression(expression)) return resolveStaticString(expression.expression);
|
|
3384
|
+
if (ts.isBinaryExpression(expression) && expression.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
3385
|
+
const left = resolveStaticString(expression.left);
|
|
3386
|
+
const right = resolveStaticString(expression.right);
|
|
3387
|
+
return left !== void 0 && right !== void 0 ? left + right : void 0;
|
|
3388
|
+
}
|
|
3389
|
+
return void 0;
|
|
3390
|
+
};
|
|
3338
3391
|
const collectStaticStringBindings = (node) => {
|
|
3339
|
-
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.
|
|
3392
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isVariableDeclarationList(node.parent) && (node.parent.flags & ts.NodeFlags.Const) !== 0) {
|
|
3393
|
+
const value = resolveStaticString(node.initializer);
|
|
3340
3394
|
const previous = staticStringBindings.get(node.name.text);
|
|
3341
|
-
staticStringBindings.set(node.name.text, !staticStringBindings.has(node.name.text) || previous ===
|
|
3395
|
+
staticStringBindings.set(node.name.text, !staticStringBindings.has(node.name.text) || previous === value ? value : void 0);
|
|
3342
3396
|
}
|
|
3397
|
+
if ((ts.isVariableDeclaration(node) || ts.isParameter(node) || ts.isBindingElement(node)) && ts.isIdentifier(node.name)) localBindings.add(node.name.text);
|
|
3398
|
+
if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isEnumDeclaration(node)) && node.name) localBindings.add(node.name.text);
|
|
3343
3399
|
ts.forEachChild(node, collectStaticStringBindings);
|
|
3344
3400
|
};
|
|
3345
3401
|
collectStaticStringBindings(sourceFile);
|
|
3402
|
+
const addImportedBindingReference = (expression, node) => {
|
|
3403
|
+
if (ts.isIdentifier(expression)) {
|
|
3404
|
+
const specifier = importedBindings.get(expression.text);
|
|
3405
|
+
if (specifier) {
|
|
3406
|
+
addReference({ text: specifier }, "runtime-wiring", node, "runtime-wiring-static");
|
|
3407
|
+
return true;
|
|
3408
|
+
}
|
|
3409
|
+
return false;
|
|
3410
|
+
}
|
|
3411
|
+
if (ts.isPropertyAccessExpression(expression)) return addImportedBindingReference(expression.expression, node);
|
|
3412
|
+
if (ts.isCallExpression(expression)) return addImportedBindingReference(expression.expression, node);
|
|
3413
|
+
return false;
|
|
3414
|
+
};
|
|
3415
|
+
const isKnownLocal = (expression) => {
|
|
3416
|
+
if (ts.isIdentifier(expression)) return localBindings.has(expression.text) || importedBindings.has(expression.text);
|
|
3417
|
+
if (expression.kind === ts.SyntaxKind.ThisKeyword) return true;
|
|
3418
|
+
if (ts.isPropertyAccessExpression(expression)) return isKnownLocal(expression.expression);
|
|
3419
|
+
if (ts.isCallExpression(expression)) return isKnownLocal(expression.expression);
|
|
3420
|
+
return ts.isStringLiteralLike(expression);
|
|
3421
|
+
};
|
|
3346
3422
|
const addReference = (specifier, kind, node, detection) => {
|
|
3347
3423
|
references.push({ specifier: specifier.text, kind, evidence: nodeEvidence(root, path, sourceFile, node), ...detection ? { detection } : {} });
|
|
3348
3424
|
};
|
|
@@ -3362,32 +3438,35 @@ var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
|
|
|
3362
3438
|
importedBindings.set(node.name.text, node.moduleReference.expression.text);
|
|
3363
3439
|
} else if (ts.isCallExpression(node)) {
|
|
3364
3440
|
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
addReference(node.arguments[0], "imports", node, "dynamic-literal");
|
|
3368
|
-
} else if (node.arguments[0] && ts.isIdentifier(node.arguments[0]) && staticStringBindings.get(node.arguments[0].text)) {
|
|
3441
|
+
const specifier = node.arguments[0] ? resolveStaticString(node.arguments[0]) : void 0;
|
|
3442
|
+
if (specifier !== void 0) {
|
|
3369
3443
|
hasLiteralDynamic = true;
|
|
3370
|
-
|
|
3371
|
-
|
|
3444
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
3445
|
+
addReference({ text: specifier }, "imports", node, "dynamic-literal");
|
|
3446
|
+
} else {
|
|
3447
|
+
hasDynamic = true;
|
|
3448
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
3449
|
+
}
|
|
3372
3450
|
} else if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
|
|
3373
3451
|
const argument = node.arguments[0];
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3452
|
+
const specifier = argument ? resolveStaticString(argument) : void 0;
|
|
3453
|
+
if (specifier !== void 0) {
|
|
3454
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
3455
|
+
addReference({ text: specifier }, "imports", node);
|
|
3456
|
+
} else {
|
|
3457
|
+
hasDynamic = true;
|
|
3458
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
3459
|
+
}
|
|
3377
3460
|
} else if (ts.isPropertyAccessExpression(node.expression) && runtimeWiringMethods.has(node.expression.name.text)) {
|
|
3378
3461
|
hasRuntimeWiring = true;
|
|
3379
|
-
|
|
3380
|
-
let hasPotentialTargetArgument = false;
|
|
3462
|
+
let hasUnresolvedTarget = false;
|
|
3381
3463
|
for (const argument of node.arguments) {
|
|
3382
|
-
if (ts.
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
if (specifier) addReference({ text: specifier }, "runtime-wiring", node, "runtime-wiring-static");
|
|
3386
|
-
} else if (ts.isPropertyAccessExpression(argument) || ts.isCallExpression(argument)) {
|
|
3387
|
-
hasPotentialTargetArgument = true;
|
|
3388
|
-
}
|
|
3464
|
+
if (ts.isStringLiteralLike(argument)) continue;
|
|
3465
|
+
if (addImportedBindingReference(argument, node)) continue;
|
|
3466
|
+
if (!isKnownLocal(argument)) hasUnresolvedTarget = true;
|
|
3389
3467
|
}
|
|
3390
|
-
|
|
3468
|
+
const receiver = node.expression.expression;
|
|
3469
|
+
if (hasUnresolvedTarget && !isKnownLocal(receiver)) hasUnresolvedRuntimeWiring = true;
|
|
3391
3470
|
}
|
|
3392
3471
|
}
|
|
3393
3472
|
ts.forEachChild(node, visit);
|
|
@@ -3396,6 +3475,7 @@ var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
|
|
|
3396
3475
|
return {
|
|
3397
3476
|
references,
|
|
3398
3477
|
exports: exportedNames(sourceFile),
|
|
3478
|
+
dynamicEvidence,
|
|
3399
3479
|
hasDynamic,
|
|
3400
3480
|
hasLiteralDynamic,
|
|
3401
3481
|
hasRuntimeWiring,
|
|
@@ -3403,7 +3483,7 @@ var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
|
|
|
3403
3483
|
};
|
|
3404
3484
|
};
|
|
3405
3485
|
var resolveRelativeModule = (specifier, containingFile, modulePaths) => {
|
|
3406
|
-
const base = resolve8(
|
|
3486
|
+
const base = resolve8(dirname4(containingFile), specifier);
|
|
3407
3487
|
const extension = extname(base);
|
|
3408
3488
|
const extensionlessBase = extension ? base.slice(0, -extension.length) : base;
|
|
3409
3489
|
const candidates = [
|
|
@@ -3472,11 +3552,11 @@ var artifact = (root, config, files, entities, relations, coverage) => {
|
|
|
3472
3552
|
sourceRevision: revision.value,
|
|
3473
3553
|
sourceRevisionKind: revision.kind,
|
|
3474
3554
|
configurationHash: sha256NormalizedV1(config ?? {}),
|
|
3475
|
-
pipelineVersion: "1.
|
|
3476
|
-
analyzerVersions: { repository: "1.
|
|
3555
|
+
pipelineVersion: "1.1.8",
|
|
3556
|
+
analyzerVersions: { repository: "1.1.1", "js-ts": "1.3.4" },
|
|
3477
3557
|
entities: [...entities].sort((a, b) => a.id.localeCompare(b.id)),
|
|
3478
3558
|
relations: [...relations].sort((a, b) => a.id.localeCompare(b.id)),
|
|
3479
|
-
coverage: coverage.map((entry) => ({ ...entry, analyzerVersion: entry.analyzerVersion ?? ({ repository: "1.
|
|
3559
|
+
coverage: coverage.map((entry) => ({ ...entry, analyzerVersion: entry.analyzerVersion ?? ({ repository: "1.1.1", "js-ts": "1.3.4" }[entry.analyzer] ?? "1.0.0") }))
|
|
3480
3560
|
};
|
|
3481
3561
|
return DiscoverySnapshotV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
|
|
3482
3562
|
};
|
|
@@ -3570,6 +3650,7 @@ var discoverRepository = (opts = {}) => {
|
|
|
3570
3650
|
const includeTestRuntimeWiring = opts.config?.analysis?.jsTs?.includeTestRuntimeWiring ?? false;
|
|
3571
3651
|
let observedLiteralDynamic = false;
|
|
3572
3652
|
let observedUnresolvedDynamic = false;
|
|
3653
|
+
const observedDynamicEvidence = [];
|
|
3573
3654
|
let observedRuntimeWiring = false;
|
|
3574
3655
|
let observedUnresolvedRuntimeWiring = false;
|
|
3575
3656
|
for (const module of modules.values()) {
|
|
@@ -3579,6 +3660,7 @@ var discoverRepository = (opts = {}) => {
|
|
|
3579
3660
|
const references = moduleReferences(root, module.absPath, sourceFile, runtimeWiringMethods);
|
|
3580
3661
|
observedLiteralDynamic ||= references.hasLiteralDynamic;
|
|
3581
3662
|
observedUnresolvedDynamic ||= references.hasDynamic;
|
|
3663
|
+
observedDynamicEvidence.push(...references.dynamicEvidence);
|
|
3582
3664
|
observedRuntimeWiring ||= references.hasRuntimeWiring;
|
|
3583
3665
|
observedUnresolvedRuntimeWiring ||= references.hasUnresolvedRuntimeWiring;
|
|
3584
3666
|
for (const reference of references.references) {
|
|
@@ -3590,10 +3672,10 @@ var discoverRepository = (opts = {}) => {
|
|
|
3590
3672
|
}
|
|
3591
3673
|
addRelation({ id: entityId("relation", `${module.entityId}:${reference.kind}:${target.targetId}`), kind: reference.kind, from: module.entityId, to: target.targetId, provenance: "observed", evidence: [reference.evidence], ...reference.detection ? { metadata: { detection: reference.detection } } : {} });
|
|
3592
3674
|
}
|
|
3593
|
-
if (references.hasLiteralDynamic || references.hasDynamic) coverage.push({ analyzer: "js-ts", scope: `dynamic-imports:${module.path}`, status: references.hasDynamic ? "not-analyzed" : "complete", reason: references.hasDynamic ? "A non-literal dynamic import was found; the target is unresolved." : "Literal dynamic imports were resolved.", evidence: [
|
|
3675
|
+
if (references.hasLiteralDynamic || references.hasDynamic) coverage.push({ analyzer: "js-ts", scope: `dynamic-imports:${module.path}`, status: references.hasDynamic ? "not-analyzed" : "complete", reason: references.hasDynamic ? "A non-literal dynamic import was found; the target is unresolved." : "Literal dynamic imports were resolved.", evidence: [...references.dynamicEvidence.slice(0, 32)] });
|
|
3594
3676
|
if (references.hasUnresolvedRuntimeWiring) coverage.push({ analyzer: "js-ts", scope: `runtime-wiring:${module.path}`, status: "not-analyzed", reason: "A runtime registration/wiring call was found without a statically imported target.", evidence: [lineEvidence("code", root, module.absPath)] });
|
|
3595
3677
|
}
|
|
3596
|
-
if (dynamicCoverageIndex >= 0) coverage[dynamicCoverageIndex] = observedUnresolvedDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "partial", reason: "Literal dynamic imports are resolved; non-literal import expressions and require calls remain unresolved." } : observedLiteralDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "complete", reason: "All observed dynamic imports used literal targets and were resolved." } : { analyzer: "js-ts", scope: "dynamic-imports", status: "not-applicable", reason: "No dynamic loading expression was observed." };
|
|
3678
|
+
if (dynamicCoverageIndex >= 0) coverage[dynamicCoverageIndex] = observedUnresolvedDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "partial", reason: "Literal dynamic imports are resolved; non-literal import expressions and require calls remain unresolved. Evidence lists representative dynamic loading sites.", evidence: [...observedDynamicEvidence.slice(0, 32)] } : observedLiteralDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "complete", reason: "All observed dynamic imports used literal targets and were resolved.", evidence: [...observedDynamicEvidence.slice(0, 32)] } : { analyzer: "js-ts", scope: "dynamic-imports", status: "not-applicable", reason: "No dynamic loading expression was observed." };
|
|
3597
3679
|
const runtimeCoverageIndex = coverage.findIndex((entry) => entry.scope === "runtime-wiring");
|
|
3598
3680
|
if (runtimeCoverageIndex >= 0) coverage[runtimeCoverageIndex] = observedUnresolvedRuntimeWiring ? { analyzer: "js-ts", scope: "runtime-wiring", status: "partial", reason: "Some configured runtime-wiring calls remain unresolved after static binding analysis." } : observedRuntimeWiring ? { analyzer: "js-ts", scope: "runtime-wiring", status: "complete", reason: "All observed configured runtime-wiring calls resolved to static bindings." } : { analyzer: "js-ts", scope: "runtime-wiring", status: "not-applicable", reason: "No configured runtime-wiring call was observed." };
|
|
3599
3681
|
return artifact(root, opts.config, allFiles, [...entities.values()], [...relations.values()], coverage);
|
|
@@ -3727,9 +3809,20 @@ var defaultFetchText = async (url) => {
|
|
|
3727
3809
|
if (!res.ok) throw new Error(`Failed to fetch ${url}: ${res.status}`);
|
|
3728
3810
|
return res.text();
|
|
3729
3811
|
};
|
|
3812
|
+
var httpUrl = (value) => {
|
|
3813
|
+
try {
|
|
3814
|
+
const parsed = new URL(value);
|
|
3815
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return void 0;
|
|
3816
|
+
if (parsed.username || parsed.password) return void 0;
|
|
3817
|
+
return parsed.href;
|
|
3818
|
+
} catch {
|
|
3819
|
+
return void 0;
|
|
3820
|
+
}
|
|
3821
|
+
};
|
|
3730
3822
|
var sourceText = async (root, source, fetchText) => {
|
|
3731
3823
|
try {
|
|
3732
|
-
|
|
3824
|
+
const remote = httpUrl(source);
|
|
3825
|
+
if (remote) return await fetchText(remote);
|
|
3733
3826
|
const path = resolve9(root, source);
|
|
3734
3827
|
if (!existsSync11(path)) return null;
|
|
3735
3828
|
return readFileSync10(path, "utf8");
|
|
@@ -3742,22 +3835,57 @@ var sameOrigin = (base, target) => {
|
|
|
3742
3835
|
return new URL(base).origin === new URL(target).origin;
|
|
3743
3836
|
};
|
|
3744
3837
|
var parseLlmsTxtLinks = (raw) => {
|
|
3745
|
-
const links = [
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3838
|
+
const links = [];
|
|
3839
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
3840
|
+
let cursor = 0;
|
|
3841
|
+
while (cursor < line.length) {
|
|
3842
|
+
const open = line.indexOf("[", cursor);
|
|
3843
|
+
if (open < 0) break;
|
|
3844
|
+
const titleEnd = line.indexOf("]", open + 1);
|
|
3845
|
+
const urlStart = titleEnd < 0 ? -1 : line.indexOf("(", titleEnd + 1);
|
|
3846
|
+
const urlEnd = urlStart < 0 ? -1 : line.indexOf(")", urlStart + 1);
|
|
3847
|
+
if (titleEnd < 0 || urlStart !== titleEnd + 1 || urlEnd < 0) {
|
|
3848
|
+
cursor = open + 1;
|
|
3849
|
+
continue;
|
|
3850
|
+
}
|
|
3851
|
+
const title = line.slice(open + 1, titleEnd).trim();
|
|
3852
|
+
const url = line.slice(urlStart + 1, urlEnd).trim();
|
|
3853
|
+
const description = line.slice(urlEnd + 1).trim().replace(/^:\s*/, "");
|
|
3854
|
+
if (url) links.push({ title: title || url, url, ...description ? { description } : {} });
|
|
3855
|
+
cursor = urlEnd + 1;
|
|
3856
|
+
}
|
|
3857
|
+
for (const token of line.split(/\s+/)) {
|
|
3858
|
+
let end = token.length;
|
|
3859
|
+
while (end > 0 && "),.;:".includes(token[end - 1] ?? "")) end -= 1;
|
|
3860
|
+
const url = token.slice(0, end);
|
|
3861
|
+
if (!/^https?:\/\//i.test(url) || links.some((link) => link.url === url)) continue;
|
|
3862
|
+
links.push({ title: slugFromPath(url), url });
|
|
3863
|
+
}
|
|
3753
3864
|
}
|
|
3754
3865
|
return links;
|
|
3755
3866
|
};
|
|
3867
|
+
var firstMatchingLine = (section, predicate) => section.split(/\r?\n/).find((line) => predicate(line.trim()))?.trim();
|
|
3868
|
+
var sectionTitle = (section, sourceUrl) => {
|
|
3869
|
+
const titleLine = firstMatchingLine(section, (line) => line.startsWith("title:"));
|
|
3870
|
+
if (titleLine) return titleLine.slice("title:".length).trim();
|
|
3871
|
+
const urlLine = firstMatchingLine(section, (line) => /^https?:\/\//i.test(line));
|
|
3872
|
+
if (urlLine) {
|
|
3873
|
+
try {
|
|
3874
|
+
return new URL(urlLine).pathname.split("/").filter(Boolean).at(-1) ?? slugFromPath(sourceUrl);
|
|
3875
|
+
} catch {
|
|
3876
|
+
return slugFromPath(sourceUrl);
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
const heading = firstMatchingLine(section, (line) => line.startsWith("#"));
|
|
3880
|
+
if (heading) return heading.replace(/^#+\s*/, "").trim();
|
|
3881
|
+
return slugFromPath(sourceUrl);
|
|
3882
|
+
};
|
|
3756
3883
|
var chunksFromMarkdown = (property, raw, sourceUrl) => {
|
|
3757
|
-
const
|
|
3884
|
+
const frontmatterEnd = raw.startsWith("---\n") ? raw.indexOf("\n---", 4) : -1;
|
|
3885
|
+
const searchable = raw.includes("\n==== ") ? raw : frontmatterEnd >= 0 ? raw.slice(frontmatterEnd + "\n---".length).replace(/^\n/, "") : raw;
|
|
3758
3886
|
const sections = searchable.includes("\n==== ") ? searchable.split(/\n====\s+/).filter((section) => section.trim()) : searchable.split(/\n(?=##?\s+)/);
|
|
3759
3887
|
return sections.map((section, index) => {
|
|
3760
|
-
const title =
|
|
3888
|
+
const title = sectionTitle(section, sourceUrl);
|
|
3761
3889
|
const id = slugFromPath(title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")) || `${index}`;
|
|
3762
3890
|
return {
|
|
3763
3891
|
chunkKey: `${property}:federated:${id}`,
|
|
@@ -3785,8 +3913,15 @@ var loadFederatedChunks = async (root, config, options = {}) => {
|
|
|
3785
3913
|
chunks.push(...chunksFromMarkdown(source.id, llms, source.llmsTxt));
|
|
3786
3914
|
const links = parseLlmsTxtLinks(llms);
|
|
3787
3915
|
for (const link of links) {
|
|
3788
|
-
const
|
|
3789
|
-
|
|
3916
|
+
const linkUrl = httpUrl(link.url);
|
|
3917
|
+
const baseUrl = source.rawBaseUrl ? httpUrl(source.rawBaseUrl) : void 0;
|
|
3918
|
+
const url = linkUrl ?? (baseUrl ? new URL(link.url, baseUrl).href : link.url);
|
|
3919
|
+
let pathname = url;
|
|
3920
|
+
try {
|
|
3921
|
+
pathname = new URL(url).pathname;
|
|
3922
|
+
} catch {
|
|
3923
|
+
}
|
|
3924
|
+
if (!/\.(md|txt)$/i.test(pathname)) continue;
|
|
3790
3925
|
if (!sameOrigin(source.llmsTxt, url)) continue;
|
|
3791
3926
|
const raw = await sourceText(root, url, fetchText);
|
|
3792
3927
|
if (raw) chunks.push(...chunksFromMarkdown(source.id, raw, url));
|
|
@@ -3992,9 +4127,14 @@ var MemoryCandidateV1Schema = z6.object({
|
|
|
3992
4127
|
}).strict();
|
|
3993
4128
|
|
|
3994
4129
|
// src/validate.ts
|
|
4130
|
+
var zodMessage = (issue) => {
|
|
4131
|
+
if (issue.code === "invalid_type" && issue.message.endsWith("received undefined")) return "Required";
|
|
4132
|
+
if (issue.code === "invalid_value" && "values" in issue) return "Invalid enum value";
|
|
4133
|
+
return issue.message;
|
|
4134
|
+
};
|
|
3995
4135
|
var zodIssues = (error) => error.issues.map((issue) => ({
|
|
3996
4136
|
path: issue.path.join(".") || "(root)",
|
|
3997
|
-
message: issue
|
|
4137
|
+
message: zodMessage(issue)
|
|
3998
4138
|
}));
|
|
3999
4139
|
var safeParseAgentHandoff = (input) => {
|
|
4000
4140
|
const legacy = AgentHandoffLegacySchema.safeParse(input);
|
|
@@ -4336,8 +4476,20 @@ var evaluateRules = (report, options = {}) => {
|
|
|
4336
4476
|
const sortedFindings = [...findings].sort((a, b) => a.id.localeCompare(b.id));
|
|
4337
4477
|
return { mode: resolved.mode, findings: sortedFindings, exitCode: sortedFindings.some((finding) => finding.severity === "error") ? 1 : 0 };
|
|
4338
4478
|
};
|
|
4339
|
-
var parseRuleId = (value) =>
|
|
4340
|
-
|
|
4479
|
+
var parseRuleId = (value) => {
|
|
4480
|
+
try {
|
|
4481
|
+
return RuleIdSchema.parse(value);
|
|
4482
|
+
} catch {
|
|
4483
|
+
throw new Error(`Invalid enum value: ${value}`);
|
|
4484
|
+
}
|
|
4485
|
+
};
|
|
4486
|
+
var parseRuleSeverity = (value) => {
|
|
4487
|
+
try {
|
|
4488
|
+
return RuleSeveritySchema.parse(value);
|
|
4489
|
+
} catch {
|
|
4490
|
+
throw new Error(`Invalid enum value: ${value}`);
|
|
4491
|
+
}
|
|
4492
|
+
};
|
|
4341
4493
|
|
|
4342
4494
|
// src/query/query.ts
|
|
4343
4495
|
var handoffForPackage = (index, id, config) => {
|
|
@@ -5008,7 +5160,7 @@ ${auth.out}`
|
|
|
5008
5160
|
|
|
5009
5161
|
// src/index-builder/watch-index.ts
|
|
5010
5162
|
import { existsSync as existsSync15, watch } from "fs";
|
|
5011
|
-
import { dirname as
|
|
5163
|
+
import { dirname as dirname5, resolve as resolve11 } from "path";
|
|
5012
5164
|
var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
|
|
5013
5165
|
var NX_MANIFEST_PATTERN = /(^|[/\\])(project|package)\.json$/i;
|
|
5014
5166
|
var collectWatchRoots = (root, config, configPath) => {
|
|
@@ -5022,7 +5174,7 @@ var collectWatchRoots = (root, config, configPath) => {
|
|
|
5022
5174
|
if (typeof value === "string" && value.length) roots.add(resolve11(root, value));
|
|
5023
5175
|
}
|
|
5024
5176
|
}
|
|
5025
|
-
if (configPath) roots.add(
|
|
5177
|
+
if (configPath) roots.add(dirname5(resolve11(configPath)));
|
|
5026
5178
|
return [...roots].filter((dir) => existsSync15(dir));
|
|
5027
5179
|
};
|
|
5028
5180
|
var watchDocBridgeIndex = (opts) => {
|
|
@@ -5091,7 +5243,7 @@ var watchDocBridgeIndex = (opts) => {
|
|
|
5091
5243
|
|
|
5092
5244
|
// src/workflow/engine.ts
|
|
5093
5245
|
import { appendFileSync, existsSync as existsSync16, mkdirSync as mkdirSync3, readFileSync as readFileSync15, renameSync, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
5094
|
-
import { join as
|
|
5246
|
+
import { join as join14, relative as relative7, resolve as resolve12 } from "path";
|
|
5095
5247
|
var WORKFLOW_STAGES = ["collect", "normalize", "reconcile", "evaluate", "report"];
|
|
5096
5248
|
var stageState = {
|
|
5097
5249
|
collect: "discovering",
|
|
@@ -5100,7 +5252,7 @@ var stageState = {
|
|
|
5100
5252
|
evaluate: "proposed",
|
|
5101
5253
|
report: "delivered"
|
|
5102
5254
|
};
|
|
5103
|
-
var defaultStateDir = (root) =>
|
|
5255
|
+
var defaultStateDir = (root) => join14(root, ".doc-bridge", "workflow");
|
|
5104
5256
|
var atomicWrite = (path, value) => {
|
|
5105
5257
|
const temp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
5106
5258
|
writeFileSync3(temp, `${JSON.stringify(value, null, 2)}
|
|
@@ -5108,10 +5260,10 @@ var atomicWrite = (path, value) => {
|
|
|
5108
5260
|
renameSync(temp, path);
|
|
5109
5261
|
};
|
|
5110
5262
|
var writeManifest = (stateDir, run2) => {
|
|
5111
|
-
atomicWrite(
|
|
5263
|
+
atomicWrite(join14(stateDir, "manifest.json"), run2);
|
|
5112
5264
|
};
|
|
5113
5265
|
var appendTransition = (stateDir, transition2) => {
|
|
5114
|
-
appendFileSync(
|
|
5266
|
+
appendFileSync(join14(stateDir, "transitions.jsonl"), `${JSON.stringify(transition2)}
|
|
5115
5267
|
`, "utf8");
|
|
5116
5268
|
};
|
|
5117
5269
|
var transition = (run2, to, reason) => {
|
|
@@ -5141,7 +5293,7 @@ var transition = (run2, to, reason) => {
|
|
|
5141
5293
|
};
|
|
5142
5294
|
var runId = () => `${Date.now()}-${process.pid}`;
|
|
5143
5295
|
var stageInputHash = (options, stage, input) => sha256NormalizedV1({ stage, input, sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, pipelineVersion: options.pipelineVersion ?? "1.0.0", analyzerVersions: options.analyzerVersions ?? {}, toolVersion: options.toolVersion ?? "1.0.0" });
|
|
5144
|
-
var stageArtifactPath = (stateDir, stage, inputHash) =>
|
|
5296
|
+
var stageArtifactPath = (stateDir, stage, inputHash) => join14(stateDir, "artifacts", `${stage}-${inputHash}.json`);
|
|
5145
5297
|
var readArtifact = (path) => JSON.parse(readFileSync15(path, "utf8"));
|
|
5146
5298
|
var readVerifiedArtifact = (path, stage, step) => {
|
|
5147
5299
|
const artifact2 = readArtifact(path);
|
|
@@ -5161,11 +5313,11 @@ var stepOutput = (stateDir, run2, stage) => {
|
|
|
5161
5313
|
return readVerifiedArtifact(stepArtifactPath(stateDir, step), stage, step).value;
|
|
5162
5314
|
};
|
|
5163
5315
|
var acquireLock = (stateDir) => {
|
|
5164
|
-
const lock =
|
|
5316
|
+
const lock = join14(stateDir, ".lock");
|
|
5165
5317
|
try {
|
|
5166
5318
|
mkdirSync3(lock);
|
|
5167
5319
|
} catch {
|
|
5168
|
-
const ownerPath =
|
|
5320
|
+
const ownerPath = join14(lock, "owner.json");
|
|
5169
5321
|
try {
|
|
5170
5322
|
const owner = JSON.parse(readFileSync15(ownerPath, "utf8"));
|
|
5171
5323
|
if (typeof owner.pid === "number") process.kill(owner.pid, 0);
|
|
@@ -5176,11 +5328,11 @@ var acquireLock = (stateDir) => {
|
|
|
5176
5328
|
mkdirSync3(lock);
|
|
5177
5329
|
}
|
|
5178
5330
|
}
|
|
5179
|
-
writeFileSync3(
|
|
5331
|
+
writeFileSync3(join14(lock, "owner.json"), JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
|
|
5180
5332
|
return () => rmSync(lock, { recursive: true, force: true });
|
|
5181
5333
|
};
|
|
5182
5334
|
var loadManifest = (stateDir) => {
|
|
5183
|
-
const path =
|
|
5335
|
+
const path = join14(stateDir, "manifest.json");
|
|
5184
5336
|
if (!existsSync16(path)) return void 0;
|
|
5185
5337
|
return WorkflowRunV1Schema.parse(JSON.parse(readFileSync15(path, "utf8")));
|
|
5186
5338
|
};
|
|
@@ -5210,7 +5362,7 @@ var selectedStages = (stage) => stage && stage !== "all" ? [stage] : WORKFLOW_ST
|
|
|
5210
5362
|
var runWorkflow = (options) => {
|
|
5211
5363
|
const root = resolve12(options.root);
|
|
5212
5364
|
const stateDir = resolve12(root, options.stateDir ?? defaultStateDir(root));
|
|
5213
|
-
mkdirSync3(
|
|
5365
|
+
mkdirSync3(join14(stateDir, "artifacts"), { recursive: true });
|
|
5214
5366
|
const release = acquireLock(stateDir);
|
|
5215
5367
|
try {
|
|
5216
5368
|
let run2 = loadManifest(stateDir);
|
|
@@ -5276,7 +5428,7 @@ var runWorkflow = (options) => {
|
|
|
5276
5428
|
const value = handler({ root, stage, input, previousOutput });
|
|
5277
5429
|
const outputHash = sha256NormalizedV1(value);
|
|
5278
5430
|
const artifact2 = { type: "workflow-step-artifact", stage, inputHash, outputHash, value };
|
|
5279
|
-
mkdirSync3(
|
|
5431
|
+
mkdirSync3(join14(stateDir, "artifacts"), { recursive: true });
|
|
5280
5432
|
if (existsSync16(artifactPath)) {
|
|
5281
5433
|
const existingArtifact = readArtifact(artifactPath);
|
|
5282
5434
|
if (existingArtifact.outputHash !== outputHash) throw new Error(`Immutable workflow artifact collision for stage "${stage}".`);
|
|
@@ -5302,14 +5454,14 @@ var runWorkflow = (options) => {
|
|
|
5302
5454
|
run2 = complete;
|
|
5303
5455
|
}
|
|
5304
5456
|
writeManifest(stateDir, run2);
|
|
5305
|
-
if (run2.state === "delivered") atomicWrite(
|
|
5457
|
+
if (run2.state === "delivered") atomicWrite(join14(stateDir, "last-known-good.json"), { runId: run2.runId, manifestHash: run2.contentHash, report: run2.steps.find((step) => step.name === "report")?.artifactRefs?.[0] });
|
|
5306
5458
|
}
|
|
5307
5459
|
return { run: run2, stateDir, reusedStages };
|
|
5308
5460
|
} finally {
|
|
5309
5461
|
release();
|
|
5310
5462
|
}
|
|
5311
5463
|
};
|
|
5312
|
-
var loadWorkflowManifest = (stateDir) => WorkflowRunV1Schema.parse(JSON.parse(readFileSync15(
|
|
5464
|
+
var loadWorkflowManifest = (stateDir) => WorkflowRunV1Schema.parse(JSON.parse(readFileSync15(join14(resolve12(stateDir), "manifest.json"), "utf8")));
|
|
5313
5465
|
var loadWorkflowStepOutput = (stateDir, stage) => stepOutput(resolve12(stateDir), loadWorkflowManifest(stateDir), stage);
|
|
5314
5466
|
|
|
5315
5467
|
// src/doctor/badge.ts
|
|
@@ -5473,13 +5625,13 @@ var docBridgePatternPayload = () => ({
|
|
|
5473
5625
|
// src/cli/demo.ts
|
|
5474
5626
|
import { cpSync, existsSync as existsSync18, mkdtempSync, readFileSync as readFileSync17, rmSync as rmSync2 } from "fs";
|
|
5475
5627
|
import { tmpdir } from "os";
|
|
5476
|
-
import { dirname as
|
|
5628
|
+
import { dirname as dirname7, join as join16, resolve as resolve14 } from "path";
|
|
5477
5629
|
import { fileURLToPath } from "url";
|
|
5478
5630
|
|
|
5479
5631
|
// src/mcp/install.ts
|
|
5480
5632
|
import { existsSync as existsSync17, mkdirSync as mkdirSync4, readFileSync as readFileSync16, writeFileSync as writeFileSync4 } from "fs";
|
|
5481
5633
|
import { homedir } from "os";
|
|
5482
|
-
import { dirname as
|
|
5634
|
+
import { dirname as dirname6, join as join15, resolve as resolve13 } from "path";
|
|
5483
5635
|
var SERVER_NAME = "ak-docs";
|
|
5484
5636
|
var mcpServerEntry = (root) => ({
|
|
5485
5637
|
command: "npx",
|
|
@@ -5496,13 +5648,13 @@ var readJson2 = (path) => {
|
|
|
5496
5648
|
}
|
|
5497
5649
|
};
|
|
5498
5650
|
var writeJson = (path, value) => {
|
|
5499
|
-
mkdirSync4(
|
|
5651
|
+
mkdirSync4(dirname6(path), { recursive: true });
|
|
5500
5652
|
writeFileSync4(path, `${JSON.stringify(value, null, 2)}
|
|
5501
5653
|
`, "utf8");
|
|
5502
5654
|
};
|
|
5503
5655
|
var resolveTargetPath = (target, root) => {
|
|
5504
5656
|
if (target === "cursor") return resolve13(root, ".cursor", "mcp.json");
|
|
5505
|
-
return
|
|
5657
|
+
return join15(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
5506
5658
|
};
|
|
5507
5659
|
var installMcpConfig = (root, target) => {
|
|
5508
5660
|
const configPath = resolveTargetPath(target, root);
|
|
@@ -5531,12 +5683,12 @@ var installMcpConfig = (root, target) => {
|
|
|
5531
5683
|
var mcpSnippet = (root) => JSON.stringify({ mcpServers: { [SERVER_NAME]: mcpServerEntry(root) } }, null, 2);
|
|
5532
5684
|
|
|
5533
5685
|
// src/cli/demo.ts
|
|
5534
|
-
var packageRoot = resolve14(
|
|
5686
|
+
var packageRoot = resolve14(dirname7(fileURLToPath(import.meta.url)), "..", "..");
|
|
5535
5687
|
var fixturePath = (fixture) => {
|
|
5536
5688
|
if (fixture === "monorepo") {
|
|
5537
|
-
return
|
|
5689
|
+
return join16(packageRoot, "examples", "demo-monorepo");
|
|
5538
5690
|
}
|
|
5539
|
-
return
|
|
5691
|
+
return join16(packageRoot, "examples", "demo-example");
|
|
5540
5692
|
};
|
|
5541
5693
|
var formatHandoffText = (handoff) => {
|
|
5542
5694
|
const bridge = handoff.bridge?.humanDoc === "missing" ? `human guide: missing \u2192 ${handoff.bridge.action ?? "ak-docs bootstrap agent-docs"}` : handoff.humanDoc ? `human guide: ${handoff.humanDoc}` : "human guide: (none)";
|
|
@@ -5554,9 +5706,9 @@ var runDemo = (root, config, fixture = "example", options = {}) => {
|
|
|
5554
5706
|
const fixtureDir = fixturePath(fixture);
|
|
5555
5707
|
if (options.copyFixture && existsSync18(fixtureDir)) {
|
|
5556
5708
|
for (const rel of ["doc-bridge.config.json", "docs", "packages", "pnpm-workspace.yaml", "package.json"]) {
|
|
5557
|
-
const src =
|
|
5709
|
+
const src = join16(fixtureDir, rel);
|
|
5558
5710
|
if (!existsSync18(src)) continue;
|
|
5559
|
-
const dest =
|
|
5711
|
+
const dest = join16(root, rel);
|
|
5560
5712
|
cpSync(src, dest, { recursive: true });
|
|
5561
5713
|
}
|
|
5562
5714
|
}
|
|
@@ -5611,14 +5763,14 @@ var formatDemoText = (result) => {
|
|
|
5611
5763
|
return lines;
|
|
5612
5764
|
};
|
|
5613
5765
|
var withDemoWorkspace = (fixture, fn) => {
|
|
5614
|
-
const dir = mkdtempSync(
|
|
5766
|
+
const dir = mkdtempSync(join16(tmpdir(), "ak-docs-demo-"));
|
|
5615
5767
|
try {
|
|
5616
5768
|
const fixtureDir = fixturePath(fixture);
|
|
5617
5769
|
if (!existsSync18(fixtureDir)) {
|
|
5618
5770
|
throw new Error(`Demo fixture "${fixture}" not found at ${fixtureDir}`);
|
|
5619
5771
|
}
|
|
5620
5772
|
cpSync(fixtureDir, dir, { recursive: true });
|
|
5621
|
-
const config = JSON.parse(readFileSync17(
|
|
5773
|
+
const config = JSON.parse(readFileSync17(join16(dir, "doc-bridge.config.json"), "utf8"));
|
|
5622
5774
|
return fn(dir, config);
|
|
5623
5775
|
} finally {
|
|
5624
5776
|
rmSync2(dir, { recursive: true, force: true });
|
|
@@ -5633,16 +5785,6 @@ var gradeForScore = (score) => {
|
|
|
5633
5785
|
if (score >= 40) return "D";
|
|
5634
5786
|
return "F";
|
|
5635
5787
|
};
|
|
5636
|
-
var agentDocPaths = (index) => {
|
|
5637
|
-
const paths = /* @__PURE__ */ new Set();
|
|
5638
|
-
for (const owner of Object.values(index.lookup?.ownership ?? {})) {
|
|
5639
|
-
if (owner.agentDoc) paths.add(owner.agentDoc);
|
|
5640
|
-
}
|
|
5641
|
-
for (const handoff of Object.values(index.handoffs ?? {})) {
|
|
5642
|
-
if (handoff.startHere) paths.add(handoff.startHere);
|
|
5643
|
-
}
|
|
5644
|
-
return paths;
|
|
5645
|
-
};
|
|
5646
5788
|
var computeScore = (coverage) => {
|
|
5647
5789
|
let score = 0;
|
|
5648
5790
|
if (coverage.freshness.hasIndex) score += 15;
|
|
@@ -5730,7 +5872,7 @@ var runDoctor = (root, config) => {
|
|
|
5730
5872
|
let index;
|
|
5731
5873
|
let hasIndex = true;
|
|
5732
5874
|
let freshnessOk = false;
|
|
5733
|
-
let freshnessMessage
|
|
5875
|
+
let freshnessMessage;
|
|
5734
5876
|
try {
|
|
5735
5877
|
index = loadDocBridgeIndex(root, config);
|
|
5736
5878
|
const next = buildDocBridgeIndex({ root, config, write: false }).index.contentHash;
|
|
@@ -5750,8 +5892,6 @@ var runDoctor = (root, config) => {
|
|
|
5750
5892
|
const missingAgentDoc = ownership.filter(([, owner]) => !owner.agentDoc || owner.agentDoc === config.corpus.agent.index).map(([id]) => id);
|
|
5751
5893
|
const missingHumanDoc = ownership.filter(([, owner]) => !owner.humanDoc).map(([id]) => id);
|
|
5752
5894
|
const indexedPaths = new Set(index.knowledge.map((entry) => entry.path));
|
|
5753
|
-
const expectedAgentDocs = agentDocPaths(index);
|
|
5754
|
-
const unindexed = [...expectedAgentDocs].filter((path) => !indexedPaths.has(path));
|
|
5755
5895
|
const corpusDocs = scanAgentCorpus(root, config).filter(
|
|
5756
5896
|
(doc) => doc.path !== config.corpus.agent.index
|
|
5757
5897
|
);
|
|
@@ -5829,13 +5969,13 @@ var formatDoctorText = (report) => {
|
|
|
5829
5969
|
};
|
|
5830
5970
|
|
|
5831
5971
|
// src/mcp/server.ts
|
|
5832
|
-
import { mkdirSync as
|
|
5833
|
-
import { join as
|
|
5972
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync20, realpathSync as realpathSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
5973
|
+
import { join as join19, relative as relative9, resolve as resolve17 } from "path";
|
|
5834
5974
|
import { z as z8, ZodError } from "zod";
|
|
5835
5975
|
|
|
5836
5976
|
// src/fixes/proposals.ts
|
|
5837
|
-
import { existsSync as existsSync19, readdirSync as readdirSync4, readFileSync as readFileSync18, realpathSync as realpathSync8, renameSync as renameSync2,
|
|
5838
|
-
import { basename as basename5, dirname as
|
|
5977
|
+
import { existsSync as existsSync19, readdirSync as readdirSync4, readFileSync as readFileSync18, realpathSync as realpathSync8, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
|
|
5978
|
+
import { basename as basename5, dirname as dirname8, extname as extname2, join as join17, relative as relative8, resolve as resolve15, sep as sep8 } from "path";
|
|
5839
5979
|
var hash2 = (value) => sha256NormalizedV1(value);
|
|
5840
5980
|
var artifactMetadata = (root, options) => ({
|
|
5841
5981
|
schemaVersion: 1,
|
|
@@ -5874,7 +6014,7 @@ var makeProposal = (root, options, changes, preconditions, postconditions) => {
|
|
|
5874
6014
|
};
|
|
5875
6015
|
var walkMarkdown = (root, directory = root) => readdirSync4(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
5876
6016
|
if (entry.name === ".git" || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build") return [];
|
|
5877
|
-
const path =
|
|
6017
|
+
const path = join17(directory, entry.name);
|
|
5878
6018
|
if (entry.isDirectory()) return walkMarkdown(root, path);
|
|
5879
6019
|
return entry.isFile() && [".md", ".mdx"].includes(extname2(entry.name).toLowerCase()) ? [relative8(root, path).split(sep8).join("/")] : [];
|
|
5880
6020
|
});
|
|
@@ -5885,18 +6025,18 @@ var createMarkdownLinkFixProposal = (root, options) => {
|
|
|
5885
6025
|
const paths = walkMarkdown(projectRoot);
|
|
5886
6026
|
const changes = [];
|
|
5887
6027
|
for (const path of paths) {
|
|
5888
|
-
const content = readFileSync18(
|
|
6028
|
+
const content = readFileSync18(join17(projectRoot, path), "utf8");
|
|
5889
6029
|
let next = content;
|
|
5890
6030
|
for (const match of content.matchAll(localLink)) {
|
|
5891
6031
|
const target = match[3];
|
|
5892
6032
|
if (!target || match[1] === "!" || /^(?:[a-z]+:|\/|#)/i.test(target)) continue;
|
|
5893
6033
|
const targetPath = target.split("#")[0]?.split("?")[0];
|
|
5894
|
-
if (!targetPath || existsSync19(resolve15(projectRoot,
|
|
6034
|
+
if (!targetPath || existsSync19(resolve15(projectRoot, dirname8(path), targetPath))) continue;
|
|
5895
6035
|
const targetStem = basename5(targetPath, extname2(targetPath)).toLowerCase();
|
|
5896
6036
|
const labelStem = (match[2] ?? "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
5897
6037
|
const candidates = paths.filter((candidate) => basename5(candidate, extname2(candidate)).toLowerCase() === targetStem || basename5(candidate, extname2(candidate)).toLowerCase().replace(/[^a-z0-9]+/g, "") === labelStem);
|
|
5898
6038
|
if (candidates.length !== 1) continue;
|
|
5899
|
-
let replacement = relative8(
|
|
6039
|
+
let replacement = relative8(dirname8(path), candidates[0]).split(sep8).join("/");
|
|
5900
6040
|
if (target.startsWith("./") && !replacement.startsWith(".")) replacement = `./${replacement}`;
|
|
5901
6041
|
next = next.replace(match[0], match[0].replace(target, replacement));
|
|
5902
6042
|
}
|
|
@@ -5908,8 +6048,13 @@ var createArtifactNormalizationProposal = (root, artifactPath, options) => {
|
|
|
5908
6048
|
const projectRoot = realpathSync8.native(resolve15(root));
|
|
5909
6049
|
const path = artifactPath.split(sep8).join("/");
|
|
5910
6050
|
const absolute = containedPath(projectRoot, path);
|
|
5911
|
-
if (!absolute
|
|
5912
|
-
|
|
6051
|
+
if (!absolute) return void 0;
|
|
6052
|
+
let before;
|
|
6053
|
+
try {
|
|
6054
|
+
before = readFileSync18(absolute, "utf8");
|
|
6055
|
+
} catch {
|
|
6056
|
+
return void 0;
|
|
6057
|
+
}
|
|
5913
6058
|
let after;
|
|
5914
6059
|
try {
|
|
5915
6060
|
after = `${JSON.stringify(sortJson(JSON.parse(before)), null, 2)}
|
|
@@ -5974,8 +6119,8 @@ var applyFixProposal = (root, proposalInput, options = {}) => {
|
|
|
5974
6119
|
};
|
|
5975
6120
|
|
|
5976
6121
|
// src/agents/registry-adapter.ts
|
|
5977
|
-
import { existsSync as existsSync20, readFileSync as readFileSync19, writeFileSync as writeFileSync6, mkdirSync as
|
|
5978
|
-
import { join as
|
|
6122
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5 } from "fs";
|
|
6123
|
+
import { join as join18, resolve as resolve16 } from "path";
|
|
5979
6124
|
import { pathToFileURL } from "url";
|
|
5980
6125
|
import { z as z7 } from "zod";
|
|
5981
6126
|
var DEFAULT_REGISTRY_AGENT_ID = "ecosystem-doc-bridge-corpus-scanner";
|
|
@@ -5997,7 +6142,7 @@ var registryConfig = (config) => config.intelligence?.registry;
|
|
|
5997
6142
|
var loadRegistryAgentRunner = async (root, config) => {
|
|
5998
6143
|
const metadata = loadRegistryAgentMetadata(root, config);
|
|
5999
6144
|
const configured = registryConfig(config)?.runnerModule;
|
|
6000
|
-
const modulePath = configured ? containedPath(root, configured) : containedPath(root,
|
|
6145
|
+
const modulePath = configured ? containedPath(root, configured) : containedPath(root, join18(metadata.root, "doc-bridge-adapter.js"));
|
|
6001
6146
|
if (!modulePath || !existsSync20(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.`);
|
|
6002
6147
|
const loaded = await import(pathToFileURL(modulePath).href);
|
|
6003
6148
|
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;
|
|
@@ -6008,9 +6153,9 @@ var loadRegistryAgentMetadata = (root, config) => {
|
|
|
6008
6153
|
const settings = registryConfig(config);
|
|
6009
6154
|
const id = settings?.agentId ?? DEFAULT_REGISTRY_AGENT_ID;
|
|
6010
6155
|
const agentRoot = settings?.agentRoot ?? "agents";
|
|
6011
|
-
const agentPath = containedPath(root,
|
|
6012
|
-
if (!agentPath || !existsSync20(agentPath)) throw new Error(`AgentsKit Registry agent "${id}" is not installed at ${
|
|
6013
|
-
const metadataPath = [
|
|
6156
|
+
const agentPath = containedPath(root, join18(agentRoot, id));
|
|
6157
|
+
if (!agentPath || !existsSync20(agentPath)) throw new Error(`AgentsKit Registry agent "${id}" is not installed at ${join18(agentRoot, id)}. Install it with: npx agentskit add ${id}`);
|
|
6158
|
+
const metadataPath = [join18(agentPath, "agent.json"), join18(agentPath, "manifest.json")].find(existsSync20);
|
|
6014
6159
|
if (!metadataPath) throw new Error(`Registry agent "${id}" is installed but has no agent.json or manifest.json metadata.`);
|
|
6015
6160
|
const metadata = RegistryAgentMetadataSchema.parse(JSON.parse(readFileSync19(metadataPath, "utf8")));
|
|
6016
6161
|
if (metadata.id !== id) throw new Error(`Installed Registry agent metadata id "${metadata.id}" does not match configured id "${id}".`);
|
|
@@ -6060,8 +6205,8 @@ var persistRegistryAgentProposal = (stateDir, proposal) => {
|
|
|
6060
6205
|
AgentProposalV1Schema.parse(proposal);
|
|
6061
6206
|
if (proposal.contentHash !== contentHashForArtifactV1(proposal)) throw new Error("Cannot persist a Registry agent proposal with an invalid contentHash.");
|
|
6062
6207
|
const safeHash = contentHashForArtifactV1(proposal);
|
|
6063
|
-
|
|
6064
|
-
const path =
|
|
6208
|
+
mkdirSync5(join18(resolve16(stateDir), "agents"), { recursive: true });
|
|
6209
|
+
const path = join18(resolve16(stateDir), "agents", `${proposal.origin.id}-${safeHash}.json`);
|
|
6065
6210
|
writeFileSync6(path, `${JSON.stringify(proposal, null, 2)}
|
|
6066
6211
|
`, "utf8");
|
|
6067
6212
|
return path;
|
|
@@ -6258,7 +6403,7 @@ var workflowReport = (ctx, runId2) => {
|
|
|
6258
6403
|
ensureLatestRun(ctx, runId2);
|
|
6259
6404
|
return parseReconciliationReport(loadWorkflowStepOutput(workflowStateDir(ctx), "reconcile"));
|
|
6260
6405
|
};
|
|
6261
|
-
var proposalPath = (ctx) =>
|
|
6406
|
+
var proposalPath = (ctx) => join19(ctx.root, ".doc-bridge", "proposal.json");
|
|
6262
6407
|
var readSavedProposal = (ctx, input) => {
|
|
6263
6408
|
if (input !== void 0) return FixProposalV1Schema.parse(input);
|
|
6264
6409
|
try {
|
|
@@ -6268,7 +6413,7 @@ var readSavedProposal = (ctx, input) => {
|
|
|
6268
6413
|
}
|
|
6269
6414
|
};
|
|
6270
6415
|
var saveProposal = (ctx, proposal) => {
|
|
6271
|
-
|
|
6416
|
+
mkdirSync6(join19(ctx.root, ".doc-bridge"), { recursive: true });
|
|
6272
6417
|
writeFileSync7(proposalPath(ctx), `${JSON.stringify(proposal, null, 2)}
|
|
6273
6418
|
`, "utf8");
|
|
6274
6419
|
};
|
|
@@ -6485,11 +6630,13 @@ var startMcpStdioServer = (ctx) => {
|
|
|
6485
6630
|
|
|
6486
6631
|
// src/reconciliation/reconcile.ts
|
|
6487
6632
|
var ignoredDocumentationRelations = /* @__PURE__ */ new Set(["covers"]);
|
|
6633
|
+
var isInternalEntity = (id) => !id.startsWith("external:") && !id.startsWith("unresolved:");
|
|
6488
6634
|
var metadataDetection = (relation) => {
|
|
6489
6635
|
const detection = relation.metadata?.detection;
|
|
6490
6636
|
return typeof detection === "string" ? detection : void 0;
|
|
6491
6637
|
};
|
|
6492
|
-
var
|
|
6638
|
+
var normalizeDetection = (value) => value === "dynamic-literal" ? "dynamic" : value;
|
|
6639
|
+
var relationDetection = (relation) => normalizeDetection(relation.discriminator ?? metadataDetection(relation) ?? "static");
|
|
6493
6640
|
var entityResolver = (snapshots) => {
|
|
6494
6641
|
const references = /* @__PURE__ */ new Map();
|
|
6495
6642
|
const entities = snapshots.flatMap((snapshot) => snapshot.entities).sort((a, b) => a.id.localeCompare(b.id));
|
|
@@ -6516,6 +6663,19 @@ var diagnosticCounts = (values) => {
|
|
|
6516
6663
|
for (const value of values) counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
6517
6664
|
return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
|
|
6518
6665
|
};
|
|
6666
|
+
var documentClass = (entity) => {
|
|
6667
|
+
const classification = entity.metadata?.classification;
|
|
6668
|
+
return typeof classification === "string" && classification.length > 0 ? classification : "unclassified";
|
|
6669
|
+
};
|
|
6670
|
+
var countDocumentClasses = (documents, selectedIds) => {
|
|
6671
|
+
const counts = /* @__PURE__ */ new Map();
|
|
6672
|
+
for (const document of documents) {
|
|
6673
|
+
if (selectedIds && !selectedIds.has(document.id)) continue;
|
|
6674
|
+
const classification = documentClass(document);
|
|
6675
|
+
counts.set(classification, (counts.get(classification) ?? 0) + 1);
|
|
6676
|
+
}
|
|
6677
|
+
return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
|
|
6678
|
+
};
|
|
6519
6679
|
var diagnosticId = (code, value) => `reconciliation:${code}:${sha256NormalizedV1(value).slice(0, 32)}`;
|
|
6520
6680
|
var coverageAvailable = (snapshot, relation) => {
|
|
6521
6681
|
const status = snapshot.coverage.find(
|
|
@@ -6568,7 +6728,6 @@ var aggregatedRelations = (relations, scope, entities, packageByModule) => {
|
|
|
6568
6728
|
const parts = key.split("\0");
|
|
6569
6729
|
const from = parts[0] ?? first.from;
|
|
6570
6730
|
const to = parts[1] ?? first.to;
|
|
6571
|
-
const kind = parts[2] ?? first.kind;
|
|
6572
6731
|
const detection = parts[3] ?? relationDetection(first);
|
|
6573
6732
|
const mergedEvidence = mergeEvidence(...group);
|
|
6574
6733
|
return {
|
|
@@ -6650,9 +6809,18 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
6650
6809
|
group.push(relation);
|
|
6651
6810
|
declaredByBase.set(base2, group);
|
|
6652
6811
|
}
|
|
6812
|
+
const observedDetectionsByBase = /* @__PURE__ */ new Map();
|
|
6813
|
+
for (const relation of observedRelations) {
|
|
6814
|
+
const base2 = relationBase(relation, resolveEntity2);
|
|
6815
|
+
const detections = observedDetectionsByBase.get(base2) ?? /* @__PURE__ */ new Set();
|
|
6816
|
+
detections.add(relationDetection(relation));
|
|
6817
|
+
observedDetectionsByBase.set(base2, detections);
|
|
6818
|
+
}
|
|
6653
6819
|
for (const group of declaredByBase.values()) {
|
|
6654
6820
|
const detections = new Set(group.map(relationDetection));
|
|
6655
6821
|
if (detections.size < 2) continue;
|
|
6822
|
+
const observedDetections = observedDetectionsByBase.get(relationBase(group[0], resolveEntity2));
|
|
6823
|
+
if (observedDetections && [...detections].every((detection) => observedDetections.has(detection))) continue;
|
|
6656
6824
|
diagnostics.push(reportDiagnostic(
|
|
6657
6825
|
"CONFLICTING_DECLARATIONS",
|
|
6658
6826
|
"conflict",
|
|
@@ -6679,7 +6847,7 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
6679
6847
|
void 0,
|
|
6680
6848
|
[relation.id, match.id]
|
|
6681
6849
|
));
|
|
6682
|
-
} else if (coverageAvailable(observed, relation) && (requiredRelationKinds === void 0 || requiredRelationKinds.has(relation.kind))) {
|
|
6850
|
+
} else if (coverageAvailable(observed, relation) && (requiredRelationKinds === void 0 || requiredRelationKinds.has(relation.kind)) && (options.requiredRelationTargets !== "internal" || relation.from !== relation.to && isInternalEntity(relation.from) && isInternalEntity(relation.to))) {
|
|
6683
6851
|
diagnostics.push(reportDiagnostic(
|
|
6684
6852
|
"RELATION_UNDOCUMENTED",
|
|
6685
6853
|
"undocumented",
|
|
@@ -6742,9 +6910,13 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
6742
6910
|
else if (statuses.has("undocumented") || statuses.has("unresolved") || statuses.has("not-analyzed")) packageStatus.unverified += 1;
|
|
6743
6911
|
else packageStatus.fresh += 1;
|
|
6744
6912
|
}
|
|
6913
|
+
const documents = observed.entities.filter((entity) => entity.kind === "document");
|
|
6914
|
+
const documentedDocumentIds = new Set(allDeclaredRelations.filter((relation) => relation.from.startsWith("document:")).map((relation) => relation.from));
|
|
6745
6915
|
const documentation = {
|
|
6746
|
-
documentCount:
|
|
6747
|
-
documentedDocumentCount:
|
|
6916
|
+
documentCount: documents.length,
|
|
6917
|
+
documentedDocumentCount: documentedDocumentIds.size,
|
|
6918
|
+
documentClassificationCounts: countDocumentClasses(documents),
|
|
6919
|
+
documentedDocumentClassificationCounts: countDocumentClasses(documents, documentedDocumentIds),
|
|
6748
6920
|
packageCount: packageEntities.length,
|
|
6749
6921
|
packageStatus
|
|
6750
6922
|
};
|
|
@@ -6767,6 +6939,7 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
6767
6939
|
diagnosticCount: sortedDiagnostics.length,
|
|
6768
6940
|
...options.scope === void 0 ? {} : { scope: options.scope },
|
|
6769
6941
|
...options.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: [...new Set(options.requiredRelationKinds)].sort() },
|
|
6942
|
+
...options.requiredRelationTargets === void 0 ? {} : { requiredRelationTargets: options.requiredRelationTargets },
|
|
6770
6943
|
diagnosticsByCode: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.code)),
|
|
6771
6944
|
diagnosticsByStatus: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.status)),
|
|
6772
6945
|
documentation
|
|
@@ -6889,7 +7062,11 @@ var reportData = (snapshot, report, includeSnippets, privacy = "private") => {
|
|
|
6889
7062
|
pipelineVersion: snapshot.pipelineVersion,
|
|
6890
7063
|
analyzerVersions: snapshot.analyzerVersions,
|
|
6891
7064
|
diagnosticCount: report.diagnostics.length,
|
|
7065
|
+
actionableCount: report.diagnostics.filter((diagnostic2) => diagnostic2.status !== "confirmed").length,
|
|
7066
|
+
confirmedCount: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "confirmed").length,
|
|
6892
7067
|
requiredRelationKinds: report.summary.requiredRelationKinds,
|
|
7068
|
+
requiredRelationTargets: report.summary.requiredRelationTargets,
|
|
7069
|
+
documentation: report.summary.documentation,
|
|
6893
7070
|
diagnosticSummary: {
|
|
6894
7071
|
errors: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error").length,
|
|
6895
7072
|
warnings: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "warn").length,
|
|
@@ -7047,8 +7224,8 @@ styles += String.raw`
|
|
|
7047
7224
|
:root{--ink:#182b2b;--muted:#536664;--paper:#efe9df;--panel:#f8f5ee;--line:#c8c8b9;--green:#176b51;--blue:#155a68;--amber:#93530b;--red:#9b302b;--shadow:none}
|
|
7048
7225
|
*{box-sizing:border-box}body{background:var(--paper);font-family:"Avenir Next","Segoe UI",ui-sans-serif,system-ui,sans-serif;font-size:14px;line-height:1.5}.shell{max-width:1720px;padding:30px clamp(20px,4vw,64px) 56px}.masthead{align-items:flex-start;border-bottom:2px solid var(--ink);padding:4px 0 28px}.masthead>div:first-child{max-width:840px}.eyebrow{font-size:10px;letter-spacing:.16em}.masthead h1{font-family:"Iowan Old Style",Georgia,serif;font-size:clamp(3rem,7vw,7.2rem);letter-spacing:-.075em;line-height:.9;margin:16px 0 20px}.lede{font-size:17px;line-height:1.45;max-width:650px}.run-meta{padding-top:3px;min-width:210px}.read-only{border:0;border-radius:0;padding:0;background:none;color:var(--green);font-size:10px;letter-spacing:.1em;text-transform:uppercase}.run-meta strong{color:var(--ink);font-size:12px;overflow-wrap:anywhere;margin-top:16px}.summary{grid-template-columns:repeat(5,minmax(0,1fr));gap:0;margin:0;padding:18px 0 22px;border-bottom:1px solid var(--line)}.metric{border:0;border-left:1px solid var(--line);border-radius:0;background:none;box-shadow:none;padding:0 18px}.metric:first-child{border-left:0;padding-left:0}.metric b{font-family:"Iowan Old Style",Georgia,serif;font-size:38px;letter-spacing:-.05em}.metric span{font-size:11px;letter-spacing:.07em;text-transform:uppercase}.lens-bar{align-items:flex-end;margin:42px 0 6px}.tabs{gap:0}.tab,.level{border:0;border-bottom:2px solid transparent;border-radius:0;background:transparent;color:var(--muted);padding:8px 14px;min-height:38px;font-weight:700}.tab:first-child,.level:first-child{padding-left:0}.tab:hover,.level:hover{background:none;color:var(--ink);border-color:var(--line)}.tab[aria-selected=true],.level[aria-pressed=true]{background:none;color:var(--ink);border-color:var(--green)}.lens-bar>.subtle{max-width:430px;text-align:right}.filters{border-block:1px solid var(--line);padding:14px 0;margin:12px 0 24px}.filters input,.filters select,.filters button{border:1px solid var(--line);border-radius:3px;background:var(--panel);min-height:38px}.filters button{font-weight:700}.insights{grid-template-columns:repeat(4,minmax(0,1fr));gap:0;border-top:1px solid var(--line);border-bottom:1px solid var(--line);margin:18px 0 30px}.insight{border:0;border-left:1px solid var(--line);border-radius:0;background:none;box-shadow:none;padding:14px 16px 16px}.insight:first-child{border-left:0;padding-left:0}.insight h3{font-size:12px;letter-spacing:.02em}.insight p{font-size:13px}.tag{border-radius:2px;padding:2px 5px;font-size:9px}.workspace{grid-template-columns:minmax(0,1fr) 350px;gap:24px}.panel{border:1px solid var(--line);border-radius:3px;background:var(--panel);box-shadow:none;padding:24px}.panel h2{font-family:"Iowan Old Style",Georgia,serif;font-size:30px;letter-spacing:-.04em}.map-wrap{border:1px solid #aeb9ae;border-radius:2px;background-color:#e8eee7;background-image:linear-gradient(rgba(24,43,43,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(24,43,43,.07) 1px,transparent 1px);background-size:24px 24px}.map-wrap::-webkit-scrollbar{height:10px}.map-wrap::-webkit-scrollbar-thumb{background:var(--line)}#graph{height:620px}.graph-node rect{fill:#f8f5ee;stroke:#274744;stroke-width:1.4;rx:2}.graph-node:hover rect,.graph-node:focus rect{stroke:var(--blue);stroke-width:2.5}.graph-node.selected rect{fill:#dceee1;stroke:var(--green);stroke-width:3}.graph-node.issue rect{fill:#fff0d4;stroke:var(--amber)}.edge{stroke:#69857d;stroke-width:1.5;opacity:.8}.edge.alert{stroke:var(--amber);stroke-width:2.5}.edge-label{fill:var(--ink);font-size:10px;font-weight:700}.node-label{font-size:12px;font-weight:800}.node-kind{font-size:10px;fill:var(--muted)}.node-count{font-size:10px;fill:var(--green);font-weight:800}.side{top:20px}.side .panel-head{border-bottom:1px solid var(--line);padding-bottom:14px}.detail-title{font-family:"Iowan Old Style",Georgia,serif;font-size:28px}.detail-grid{gap:0}.detail-grid>div{border-left:1px solid var(--line);padding-left:10px}.detail-grid>div:nth-child(odd){border-left:0;padding-left:0}.detail-grid b{font-family:"Iowan Old Style",Georgia,serif;font-size:26px}.run{margin-top:24px}.run-summary{border-bottom:1px solid var(--line);padding-bottom:12px}.chip{border:0;border-left:1px solid var(--line);border-radius:0;padding:2px 10px}.chip:first-child{border-left:0;padding-left:0}.finding-groups{grid-template-columns:repeat(2,minmax(0,1fr));gap:0}.finding-group{border:0;border-top:1px solid var(--line);border-radius:0;background:none;padding:16px 14px 16px 0}.finding-group:nth-child(even){padding-left:14px;border-left:1px solid var(--line)}.finding-group h3{font-size:13px;letter-spacing:.02em}.finding-detail{border-top:2px solid var(--ink)}.finding{padding:16px 0}.metadata{gap:24px;margin-top:26px}.metadata div{border-top:1px solid var(--line);padding-top:9px}.map-wrap,.panel,.finding-group,.insight{transition:background-color .16s ease,border-color .16s ease}@media(max-width:900px){.masthead{display:block}.run-meta{padding-top:24px;text-align:left}.summary{grid-template-columns:repeat(3,minmax(0,1fr));row-gap:18px}.metric:nth-child(4){border-left:0;padding-left:0}.workspace{grid-template-columns:1fr}.side{position:static}.lens-bar{display:block}.lens-bar>.subtle{text-align:left;margin-top:10px}.finding-groups{grid-template-columns:1fr}.finding-group:nth-child(even){padding-left:0;border-left:0}}@media(max-width:560px){.shell{padding:20px 16px 40px}.masthead h1{font-size:clamp(3rem,16vw,5rem)}.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.metric:nth-child(odd){border-left:0;padding-left:0}.metric:nth-child(even){border-left:1px solid var(--line);padding-left:12px}.insights{grid-template-columns:1fr}.insight,.insight:first-child{border-left:0;border-top:1px solid var(--line);padding-left:0}.insight:first-child{border-top:0}.panel{padding:16px}.panel-head{display:block}.panel-head>.tabs{margin-top:12px}.filters label{width:100%}.filters input,.filters select{width:100%}.metadata{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){.map-wrap,.panel,.finding-group,.insight{transition:none}}@media(prefers-color-scheme:dark){:root{--ink:#e8eee7;--muted:#b0beb3;--paper:#131c1a;--panel:#1b2824;--line:#42534b;--green:#72d09d;--blue:#86cfe1;--amber:#f0bc6b;--red:#ff9186}.map-wrap{background-color:#17231e;background-image:linear-gradient(rgba(232,238,231,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(232,238,231,.07) 1px,transparent 1px)}.graph-node rect{fill:#1c2b25;stroke:#90aa99}.graph-node.selected rect{fill:#294936}.graph-node.issue rect{fill:#40321e}.filters input,.filters select,.filters button{background:#202f29;color:var(--ink)}}
|
|
7049
7226
|
`;
|
|
7050
|
-
styles += String.raw`.graph-node .node-label,.graph-node text.node-label{fill:var(--ink)}.graph-node .node-kind{fill:var(--muted)}.edge-label{fill:var(--ink)}@media(max-width:1100px){.workspace{grid-template-columns:1fr}.side{position:static}.map-wrap #graph{min-width:0!important;width:100%!important}.panel-head>.tabs{margin-top:12px}}@media(max-width:700px){.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.lens-bar{display:block}.lens-bar>.subtle{text-align:left;margin-top:10px}.filters label{width:100%}.filters input,.filters select{width:100%}.finding-groups{grid-template-columns:1fr}.finding-group:nth-child(even){padding-left:0;border-left:0}}`;
|
|
7051
|
-
styles += String.raw`.
|
|
7227
|
+
styles += String.raw`.graph-node .node-label,.graph-node text.node-label{fill:var(--ink)}.graph-node .node-kind{fill:var(--muted)}.edge-label{fill:var(--ink)}.panel-head>div:first-child{min-width:0;flex:1 1 0}.panel-head>.tabs:not(.report-tabs){flex-wrap:nowrap;max-width:100%;overflow-x:auto}.panel-head>.tabs:not(.report-tabs) .level{flex:0 0 auto}@media(max-width:1100px){.workspace{grid-template-columns:1fr}.side{position:static}.map-wrap #graph{min-width:0!important;width:100%!important}.panel-head>.tabs{margin-top:12px}}@media(max-width:700px){.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.lens-bar{display:block}.lens-bar>.subtle{text-align:left;margin-top:10px}.filters label{width:100%}.filters input,.filters select{width:100%}.finding-groups{grid-template-columns:1fr}.finding-group:nth-child(even){padding-left:0;border-left:0}}`;
|
|
7228
|
+
styles += String.raw`.report-tabs{display:flex;gap:0;align-items:flex-end;border-bottom:1px solid var(--line);margin:24px 0 0}.report-tabs .tab{font-size:13px}.breadcrumbs{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:0 0 14px;color:var(--muted);font-size:12px}.breadcrumbs button{border:0;background:none;color:var(--blue);padding:3px 0;font-weight:700}.breadcrumbs button:hover{text-decoration:underline}.breadcrumbs .current{color:var(--ink);font-weight:700}.scope-note{margin:0 0 12px}.report-dashboard{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(280px,1fr);gap:24px}.report-table{width:100%;border-collapse:collapse;font-size:12px}.report-table th,.report-table td{border-top:1px solid var(--line);padding:10px 8px;text-align:left;vertical-align:top}.report-table th{color:var(--muted);font-size:10px;letter-spacing:.08em;text-transform:uppercase}.report-table td:first-child{font-weight:750}.bar-list{display:grid;gap:10px}.bar-item{display:grid;grid-template-columns:minmax(120px,1fr) 2fr auto;gap:10px;align-items:center;font-size:12px}.bar-item i{height:8px;background:var(--line);display:block;overflow:hidden}.bar-item i b{display:block;height:100%;background:var(--green)}.map-wrap{touch-action:none;cursor:grab}.map-wrap.dragging{cursor:grabbing}@media(max-width:700px){.report-tabs{overflow:auto}.report-tabs .tab{white-space:nowrap}.report-dashboard{grid-template-columns:1fr}.bar-item{grid-template-columns:minmax(100px,1fr) 1fr auto}}`;
|
|
7052
7229
|
styles += String.raw`body[data-report-view="architecture"] #insights,body[data-report-view="architecture"] .filters,body[data-report-view="architecture"] .run,body[data-report-view="architecture"] .metadata{display:none}body[data-report-view="insights"] .workspace,body[data-report-view="insights"] .filters,body[data-report-view="insights"] .run,body[data-report-view="insights"] .metadata{display:none}body[data-report-view="findings"] .workspace,body[data-report-view="findings"] #insights,body[data-report-view="findings"] #report-dashboard,body[data-report-view="findings"] #coverage-title,body[data-report-view="findings"] #coverage-title~*{display:none}body[data-report-view="findings"] .run[aria-labelledby="coverage-title"]{display:none}body[data-report-view="coverage"] .workspace,body[data-report-view="coverage"] #insights,body[data-report-view="coverage"] #report-dashboard,body[data-report-view="coverage"] .filters,body[data-report-view="coverage"] .run[aria-labelledby="run-title"],body[data-report-view="coverage"] .metadata{display:none}body[data-report-view="insights"] #report-dashboard{display:block}body[data-report-view="map"] #report-dashboard{display:none}`;
|
|
7053
7230
|
styles += String.raw`body[data-report-view="architecture"] #report-dashboard{display:none!important}.edge{fill:none}`;
|
|
7054
7231
|
styles += String.raw`:root{--ink:#17251f;--muted:#586b63;--paper:#f2f5f1;--panel:#fbfcf9;--line:#c9d5cc;--green:#13734a;--blue:#155f78;--amber:#995d08;--red:#a52e2b}html,body{max-width:100%;overflow-x:hidden}body{font-size:15px;line-height:1.55}.shell{width:100%;max-width:1600px;margin:0 auto;padding:28px clamp(16px,3vw,48px) 56px}.masthead{gap:32px;padding:0 0 24px;align-items:flex-start}.masthead h1{font-size:clamp(2.8rem,5.5vw,5.4rem);line-height:.94;margin:12px 0 16px;max-width:100%;overflow-wrap:anywhere}.lede{max-width:60ch;font-size:16px}.run-meta{min-width:0;max-width:300px}.summary{margin:0;padding:18px 0 20px;grid-template-columns:repeat(5,minmax(0,1fr));gap:0}.metric{min-width:0;padding:0 16px;border-inline-start:1px solid var(--line)}.metric:first-child{padding-inline-start:0;border-inline-start:0}.metric b{font-size:clamp(1.8rem,3vw,3rem);max-width:100%;overflow-wrap:anywhere}.metric span{font-size:10px;letter-spacing:.06em;line-height:1.25}.lens-bar{margin:24px 0 4px;align-items:flex-end}.lens-bar>.subtle{max-width:52ch}.report-tabs{max-width:100%;overflow-x:auto}.tab,.level{min-height:40px;padding:8px 12px}.filters{max-width:100%;align-items:center}.filters label{min-width:0}.filters input,.filters select{max-width:100%;min-width:0}.workspace{min-width:0;grid-template-columns:minmax(0,1fr) minmax(280px,340px);gap:20px}.panel{min-width:0}.map-wrap{min-width:0;max-width:100%;width:100%;overflow:hidden;contain:layout paint}#graph{display:block;width:100%!important;min-width:0!important;max-width:100%;height:clamp(480px,60vh,680px)!important}.side{min-width:0}.detail-title,.path,.list,.finding,.finding-group,.report-table{min-width:0;overflow-wrap:anywhere}.report-dashboard{min-width:0}.report-table{table-layout:fixed}.report-table th,.report-table td{overflow-wrap:anywhere}.bar-item{min-width:0}.bar-item span{min-width:0;overflow-wrap:anywhere}.tab:focus-visible,.level:focus-visible,input:focus-visible,select:focus-visible{outline:3px solid var(--blue);outline-offset:2px}@media(max-width:1100px){.workspace{grid-template-columns:1fr}.side{position:static}.summary{grid-template-columns:repeat(3,minmax(0,1fr));row-gap:16px}.summary .metric:nth-child(4){border-inline-start:0;padding-inline-start:0}}@media(max-width:700px){.shell{padding:12px 16px 32px}.masthead{padding-bottom:14px}.masthead h1{font-size:clamp(2.2rem,11vw,3.4rem);margin:6px 0 10px}.lede{font-size:14px;line-height:1.35}.run-meta{display:none}.summary{padding:10px 0 12px;grid-template-columns:repeat(2,minmax(0,1fr));row-gap:8px}.summary .metric:nth-child(even){border-inline-start:1px solid var(--line);padding-inline-start:12px}.summary .metric:nth-child(odd){border-inline-start:0;padding-inline-start:0}.metric{padding-inline:10px}.metric b{font-size:1.65rem}.lens-bar{display:block;margin-top:14px}.lens-bar>.subtle{display:none}.filters{display:grid;grid-template-columns:1fr;gap:10px}.filters label{display:grid;grid-template-columns:1fr;gap:5px}.filters input,.filters select,.filters button{width:100%;min-height:44px}.panel{padding:16px}.panel-head{display:block}.panel-head>.tabs{margin-top:12px}.finding-groups{grid-template-columns:1fr}.report-dashboard{grid-template-columns:1fr}#graph{height:520px!important}.lens-bar + #lens-caption{margin:8px 0}.lens-bar + #lens-caption + #insights{margin-top:8px}}@media(prefers-color-scheme:dark){:root{--ink:#edf5ef;--muted:#b7c6ba;--paper:#111815;--panel:#1a2821;--line:#405248;--green:#73d19d;--blue:#8bd1e6;--amber:#f0bd70;--red:#ff9389}}`;
|
|
@@ -7058,7 +7235,8 @@ styles += String.raw`#lens-caption + p.subtle{display:none}`;
|
|
|
7058
7235
|
var script = String.raw`
|
|
7059
7236
|
const data=${"${DATA}"};
|
|
7060
7237
|
data.entities=data.entities||[];data.relations=data.relations||[];data.diagnostics=data.diagnostics||[];
|
|
7061
|
-
|
|
7238
|
+
globalThis.__DOC_BRIDGE_DATA__=data;
|
|
7239
|
+
const policySuffix=data.requiredRelationKinds===undefined?"Policy: all observed relation kinds require declarations.":data.requiredRelationKinds.length?"Policy: declarations required for "+data.requiredRelationKinds.join(", ")+(data.requiredRelationTargets==="internal"?" between internal entities":"")+".":"Policy: missing relation declarations disabled; stale, conflicting, and unresolved declarations remain checked.";
|
|
7062
7240
|
const ensurePolicyNote=()=>{const note=document.querySelector("#map-note");if(note&&!note.textContent.includes(policySuffix))note.textContent=(note.textContent+" · "+policySuffix).trim()};
|
|
7063
7241
|
new MutationObserver(ensurePolicyNote).observe(document.querySelector("#map-note"),{childList:true,characterData:true});
|
|
7064
7242
|
const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],hasLevelChunks=globalThis.__DOC_BRIDGE_HAS_LEVEL_CHUNKS__===true,loadedChunks=new Set(),loadingChunks=new Map();
|
|
@@ -7069,7 +7247,7 @@ const groupById=new Map(Object.entries(data.view?.groups||{}));
|
|
|
7069
7247
|
const packageChunkNameById=new Map([...groupById.values()].flatMap((group)=>group.members).filter((id)=>data.entities.find((entity)=>entity.id===id)?.kind==="package").sort().map((id,index)=>[id,data.view?.levelChunks?.packages?.[index]]));
|
|
7070
7248
|
const addFinding=(index,id,finding)=>{const values=index.get(id)||[];values.push(finding);index.set(id,values)};
|
|
7071
7249
|
const addRelation=(id,relation)=>{const values=relationsByEntity.get(id)||[];values.push(relation);relationsByEntity.set(id,values)};
|
|
7072
|
-
const hydrate=()=>{byId.clear();parent.clear();relationById.clear();packageMembers.clear();findingIndex.clear();relationFindingIndex.clear();relationsByEntity.clear();groupEntityIds.clear();groupFindingIndex.clear();groupFindingCounts.clear();packageCache.clear();nodeIssueCache.clear();data.entities.forEach((entity)=>{byId.set(entity.id,entity);const groupId=data.view?.entityGroup?.[entity.id];if(groupId){const ids=groupEntityIds.get(groupId)||new Set();ids.add(entity.id);groupEntityIds.set(groupId,ids)}});data.relations.forEach((relation)=>{relationById.set(relation.id,relation);addRelation(relation.from,relation);addRelation(relation.to,relation);if(relation.kind==="contains"){if(!parent.has(relation.to))parent.set(relation.to,relation.from);const members=packageMembers.get(relation.from)||new Set();members.add(relation.to);packageMembers.set(relation.from,members)}});data.diagnostics.forEach((finding)=>{(finding.entityIds||[]).forEach((id)=>addFinding(findingIndex,id,finding));(finding.relationIds||[]).forEach((id)=>addFinding(relationFindingIndex,id,finding));const scopes=data.view?.diagnosticGroup?.[finding.id]||[];scopes.forEach((scope)=>{addFinding(groupFindingIndex,scope,finding);groupFindingCounts.set(scope,(groupFindingCounts.get(scope)||0)+1)})})};
|
|
7250
|
+
const hydrate=()=>{byId.clear();parent.clear();relationById.clear();packageMembers.clear();findingIndex.clear();relationFindingIndex.clear();relationsByEntity.clear();groupEntityIds.clear();groupFindingIndex.clear();groupFindingCounts.clear();packageCache.clear();nodeIssueCache.clear();data.entities.forEach((entity)=>{byId.set(entity.id,entity);const groupId=data.view?.entityGroup?.[entity.id];if(groupId){const ids=groupEntityIds.get(groupId)||new Set();ids.add(entity.id);groupEntityIds.set(groupId,ids)}});data.relations.forEach((relation)=>{relationById.set(relation.id,relation);addRelation(relation.from,relation);addRelation(relation.to,relation);if(relation.kind==="contains"){if(!parent.has(relation.to))parent.set(relation.to,relation.from);const members=packageMembers.get(relation.from)||new Set();members.add(relation.to);packageMembers.set(relation.from,members)}});data.diagnostics.forEach((finding)=>{(finding.entityIds||[]).forEach((id)=>addFinding(findingIndex,id,finding));(finding.relationIds||[]).forEach((id)=>addFinding(relationFindingIndex,id,finding));const scopes=data.view?.diagnosticGroup?.[finding.id]||[];if(finding.status!=="confirmed")scopes.forEach((scope)=>{addFinding(groupFindingIndex,scope,finding);groupFindingCounts.set(scope,(groupFindingCounts.get(scope)||0)+1)})})};
|
|
7073
7251
|
const isLevelChunk=(name)=>name.startsWith("chunks/levels-");
|
|
7074
7252
|
const isDetailChunk=(name)=>name.startsWith("chunks/details-");
|
|
7075
7253
|
const hasChunk=(name)=>isLevelChunk(name)?activeLevelChunk===name:isDetailChunk(name)?loadedChunks.has(name):!lazyChunks.includes(name)||loadedChunks.has(name);
|
|
@@ -7086,10 +7264,10 @@ const viewLabels={architecture:"Architecture",drift:"Insights",risks:"Findings",
|
|
|
7086
7264
|
const ensureReportChrome=()=>{document.querySelectorAll(".tab[data-lens]").forEach((tab)=>{tab.textContent=viewLabels[tab.dataset.lens]||tab.dataset.lens;tab.closest(".tabs")?.classList.add("report-tabs")});let breadcrumbs=document.querySelector("#breadcrumbs");if(!breadcrumbs){breadcrumbs=document.createElement("nav");breadcrumbs.id="breadcrumbs";breadcrumbs.className="breadcrumbs";breadcrumbs.setAttribute("aria-label","Report location");document.querySelector(".workspace")?.before(breadcrumbs)}return breadcrumbs};
|
|
7087
7265
|
const label=(entity)=>entity?.name||entity?.id||"Unknown";
|
|
7088
7266
|
const short=(value,max)=>{const text=String(value??""),limit=text==="External dependencies"?16:max;return text.length>limit?text.slice(0,limit-1)+"…":text};
|
|
7089
|
-
const renderBreadcrumbs=()=>{const breadcrumbs=ensureReportChrome(),items=[{label:"Repository",selected:"",level:"overview"}];if(state.selected){const group=groupById.get(state.selected),entity=byId.get(state.selected);if(group)items.push({label:group.name,selected:group.id,level:"package"});else if(entity){const packageId=packageOf(entity.id),packageEntity=byId.get(packageId),groupId=groupFor(packageId);if(
|
|
7267
|
+
const renderBreadcrumbs=()=>{const breadcrumbs=ensureReportChrome(),items=[{label:"Repository",selected:"",level:"overview"}];if(state.selected){const group=groupById.get(state.selected),entity=byId.get(state.selected);if(group)items.push({label:group.name,selected:group.id,level:"package"});else if(entity){const packageId=packageOf(entity.id),packageEntity=byId.get(packageId),groupId=groupFor(packageId),group=groupById.get(groupId);if(group&&groupId!==packageId&&group.name!==label(packageEntity))items.push({label:group.name,selected:groupId,level:"package"});if(packageEntity&&packageEntity.id!==entity.id)items.push({label:label(packageEntity),selected:packageEntity.id,level:"module"});items.push({label:label(entity),selected:entity.id,level:state.level})}}breadcrumbs.innerHTML=items.map((item,index)=>{const separator="<span aria-hidden=\"true\">"+(index?" / ":"")+"</span>";if(index===items.length-1)return separator+"<span class=\"current\">"+esc(item.label)+"</span>";return separator+"<button type=\"button\" data-breadcrumb-selected=\""+esc(item.selected)+"\" data-breadcrumb-level=\""+esc(item.level)+"\">"+esc(item.label)+"</button>"}).join("")};
|
|
7090
7268
|
const degreeMap=(relations)=>{const degrees=new Map();relations.forEach((relation)=>{degrees.set(relation.from,(degrees.get(relation.from)||0)+1);degrees.set(relation.to,(degrees.get(relation.to)||0)+1)});return degrees};
|
|
7091
7269
|
const relationHealth=(relation)=>{if(!relation)return "";const findings=relationFindings(relation.id);return findings.some((finding)=>finding.severity==="error")?"error":findings.length?"warn":""};
|
|
7092
|
-
const nodeIssues=(nodeId)=>{if(nodeIssueCache.has(nodeId))return nodeIssueCache.get(nodeId);const ids=groupEntityIds.get(nodeId)||new Set([nodeId]),findings=new Set(groupFindingIndex.get(nodeId)||[]),relations=new Set();for(const id of ids){(findingIndex.get(id)||[]).forEach((finding)=>findings.add(finding));(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation))}relations.forEach((relation)=>(relationFindingIndex.get(relation.id)||[]).forEach((finding)=>findings.add(finding)));const result=[...findings];nodeIssueCache.set(nodeId,result);return result};
|
|
7270
|
+
const nodeIssues=(nodeId)=>{if(nodeIssueCache.has(nodeId))return nodeIssueCache.get(nodeId);const ids=groupEntityIds.get(nodeId)||new Set([nodeId]),findings=new Set(groupFindingIndex.get(nodeId)||[]),relations=new Set();for(const id of ids){(findingIndex.get(id)||[]).filter((finding)=>finding.status!=="confirmed").forEach((finding)=>findings.add(finding));(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation))}relations.forEach((relation)=>(relationFindingIndex.get(relation.id)||[]).filter((finding)=>finding.status!=="confirmed").forEach((finding)=>findings.add(finding)));const result=[...findings];nodeIssueCache.set(nodeId,result);return result};
|
|
7093
7271
|
const findingInLens=(finding)=>state.lens==="architecture"||(state.lens==="drift"&&finding.status!=="confirmed")||(state.lens==="risks"&&(finding.severity==="error"||finding.severity==="warn"))||(state.lens==="evidence"&&finding.evidence.length>0);
|
|
7094
7272
|
const relationFindings=(id)=>relationFindingIndex.get(id)||data.view?.diagnosticRelationFindings?.[id]||[];
|
|
7095
7273
|
const relationInLens=(id)=>relationFindings(id).some(findingInLens);
|
|
@@ -7099,8 +7277,8 @@ const detailChunkName=()=>{const entity=state.selected?byId.get(state.selected):
|
|
|
7099
7277
|
const nodesFor=()=>{if(state.level==="overview")return data.view?.overview?.nodes||[];const packages=packageNodes(),selectedEntity=state.selected?byId.get(state.selected):null,groupScope=state.selected&&groupById.has(state.selected)?state.selected:null,packageScope=selectedEntity?.kind==="package"?selectedEntity.id:state.selected&&!groupScope?packageOf(state.selected):null;if(state.level==="package"&&!state.selected){const packageIds=new Set(packages.map((node)=>node.id));return packages.concat(state.lens==="architecture"?[]:externalFor(packageIds))}if((state.level==="module"||state.level==="file")&&!state.selected)return[];let nodes=data.entities.filter((entity)=>{if(state.level==="package")return entity.kind==="package"&&(!groupScope||groupFor(entity.id)===groupScope)&&(!packageScope||entity.id===packageScope);if(state.level==="module")return entity.kind==="module"&&(groupScope?groupFor(entity.id)===groupScope:packageOf(entity.id)===packageScope);return Boolean(entity.path)&&(groupScope?groupFor(entity.id)===groupScope:packageOf(entity.id)===packageScope)});if(state.level==="module"&&selectedEntity?.kind==="module"){const neighborhood=new Set([selectedEntity.id]);(relationsByEntity.get(selectedEntity.id)||[]).forEach((relation)=>{if(!["imports","re-exports"].includes(relation.kind))return;if(relation.from===selectedEntity.id)neighborhood.add(relation.to);if(relation.to===selectedEntity.id)neighborhood.add(relation.from)});nodes=data.entities.filter((entity)=>entity.kind==="module"&&neighborhood.has(entity.id))}const scopedIds=new Set(nodes.map((node)=>node.id)),external=state.lens==="architecture"?[]:externalFor(scopedIds),degrees=degreeMap(data.relations);return nodes.concat(external).sort((a,b)=>(degrees.get(b.id)||0)-(degrees.get(a.id)||0)||a.id.localeCompare(b.id)).slice(0,state.level==="file"?80:60)};
|
|
7100
7278
|
const graphModelUncached=()=>{if(state.level==="overview"){const overviewNodes=data.view?.overview?.nodes||[],overviewNodeIds=new Set(overviewNodes.map((node)=>node.id)),base=(data.view?.overview?.edges||[]).filter((edge)=>overviewNodeIds.has(edge.from)&&overviewNodeIds.has(edge.to)),edges=state.lens==="architecture"?base:base.filter((edge)=>edge.relationIds.some(relationInLens)),visible=new Set(edges.flatMap((edge)=>[edge.from,edge.to]));return{nodes:state.lens==="architecture"?overviewNodes:overviewNodes.filter((node)=>visible.has(node.id)||nodeIssues(node.id).some((finding)=>findingInLens(finding))),edges:edges.map((edge)=>({...edge,health:edge.relationIds.some((id)=>relationHealth(relationById.get(id)))?"error":""}))}}const nodes=nodesFor(),ids=new Set(nodes.map((node)=>node.id)),aggregate=state.level==="package"&&(!state.selected||state.selected.startsWith("group:")),edges=new Map();data.relations.forEach((relation)=>{const essential=state.level==="package"?relation.kind==="depends-on":relation.kind==="imports"||relation.kind==="re-exports";if(relation.kind==="contains"|| (state.lens==="architecture"&&!essential)||(state.lens!=="architecture"&&!relationInLens(relation.id)))return;const from=aggregate?packageOf(relation.from):relation.from,to=aggregate?packageOf(relation.to):relation.to;if(from===to||!ids.has(from)||!ids.has(to))return;const key=from+"→"+to,current=edges.get(key)||{from,to,count:0,kinds:new Set(),relationIds:new Set(),health:""};current.count++;current.kinds.add(relation.kind);current.relationIds.add(relation.id);current.health=current.health==="error"||relationHealth(relation)==="error"?"error":current.health||relationHealth(relation);edges.set(key,current)});const visibleEdges=[...edges.values()].sort((a,b)=>b.count-a.count||a.from.localeCompare(b.from)||a.to.localeCompare(b.to));const visible=new Set(visibleEdges.flatMap((edge)=>[edge.from,edge.to]));return{nodes:state.lens==="architecture"?nodes:nodes.filter((node)=>visible.has(node.id)||nodeIssues(node.id).some((finding)=>findingInLens(finding))),edges:visibleEdges}};
|
|
7101
7279
|
const graphModel=()=>{const key=state.lens+"|"+state.level+"|"+(state.selected||"");if(graphCacheKey===key&&graphCache)return graphCache;graphCacheKey=key;return graphCache=graphModelUncached()};
|
|
7102
|
-
const renderInsights=()=>{const summary=data.diagnosticSummary||{},undocumented=summary.undocumented??data.diagnostics.filter((finding)=>finding.status==="undocumented").length,drift=summary.drift??data.diagnostics.filter((finding)=>finding.status==="stale-or-unverified"||finding.status==="conflict").length,unsupported=(data.coverage||[]).filter((entry)=>entry.status==="not-analyzed"||entry.status==="partial").length,model=graphModel(),degrees=degreeMap(model.edges),values=[...degrees.values()].sort((a,b)=>a-b),median=values.length?values[Math.floor(values.length/2)]:0,hot=[...degrees.values()].filter((value)=>value>=Math.max(4,median*2)).length,isolated=model.nodes.filter((node)=>!model.edges.some((edge)=>edge.from===node.id||edge.to===node.id)).length;document.querySelector("#insights").innerHTML=[["Documentation drift",undocumented+drift,"Relations or docs needing comparison.",""],["Unanalyzed scope",unsupported,"Coverage gaps are explicit.",""],["Connectivity hotspots",hot,"Heuristic: unusually connected nodes.","heuristic"],["Disconnected nodes",isolated,"Heuristic: no visible edge at this level.","heuristic"]].map(([title,count,copy,tag])=>"<article class=\"insight\"><h3>"+esc(title)+" <span class=\"tag "+tag+"\">"+(tag?"heuristic":"signal")+"</span></h3><p><strong>"+count+"</strong> · "+esc(copy)+"</p></article>").join("")};
|
|
7103
|
-
const renderInsightsDashboard=()=>{let dashboard=document.querySelector("#report-dashboard");if(!dashboard){dashboard=document.createElement("section");dashboard.id="report-dashboard";dashboard.className="report-dashboard";document.querySelector(".filters")?.before(dashboard)}if(!
|
|
7280
|
+
const renderInsights=()=>{const summary=data.diagnosticSummary||{},documentation=data.documentation||{},classificationCounts=documentation.documentClassificationCounts||{},documentedCounts=documentation.documentedDocumentClassificationCounts||{},agentDocs=classificationCounts.agent??0,documentedAgentDocs=documentedCounts.agent??0,undocumented=summary.undocumented??data.diagnostics.filter((finding)=>finding.status==="undocumented").length,drift=summary.drift??data.diagnostics.filter((finding)=>finding.status==="stale-or-unverified"||finding.status==="conflict").length,unsupported=(data.coverage||[]).filter((entry)=>entry.status==="not-analyzed"||entry.status==="partial").length,model=graphModel(),degrees=degreeMap(model.edges),values=[...degrees.values()].sort((a,b)=>a-b),median=values.length?values[Math.floor(values.length/2)]:0,hot=[...degrees.values()].filter((value)=>value>=Math.max(4,median*2)).length,isolated=model.nodes.filter((node)=>!model.edges.some((edge)=>edge.from===node.id||edge.to===node.id)).length;document.querySelector("#insights").innerHTML=[["Documentation drift",undocumented+drift,"Relations or docs needing comparison.",""],["Agent documentation",documentedAgentDocs+"/"+agentDocs,"Configured agent-corpus documents linked to knowledge.",""],["Unanalyzed scope",unsupported,"Coverage gaps are explicit.",""],["Connectivity hotspots",hot,"Heuristic: unusually connected nodes.","heuristic"],["Disconnected nodes",isolated,"Heuristic: no visible edge at this level.","heuristic"]].map(([title,count,copy,tag])=>"<article class=\"insight\"><h3>"+esc(title)+" <span class=\"tag "+tag+"\">"+(tag?"heuristic":"signal")+"</span></h3><p><strong>"+count+"</strong> · "+esc(copy)+"</p></article>").join("")};
|
|
7281
|
+
const renderInsightsDashboard=()=>{let dashboard=document.querySelector("#report-dashboard");if(!dashboard){dashboard=document.createElement("section");dashboard.id="report-dashboard";dashboard.className="report-dashboard";document.querySelector(".filters")?.before(dashboard)}const actionable=data.diagnostics.filter((finding)=>finding.status!=="confirmed");if(!actionable.length){dashboard.innerHTML="<p class=\"empty\">No actionable findings. Confirmed checks are available in Evidence.</p>";return}const degrees=degreeMap(data.relations),groups=[...groupById.entries()].filter(([,group])=>group.kind!=="external"),rows=groups.map(([id,group])=>{const members=groupEntityIds.get(id)||new Set(),modules=group.moduleCount||[...members].filter((entityId)=>byId.get(entityId)?.kind==="module").length;return{name:group.name,packages:group.members.length,modules,findings:groupFindingCounts.get(id)||0}}).sort((a,b)=>b.findings-a.findings||a.name.localeCompare(b.name)),topPackages=[...data.entities].filter((entity)=>entity.kind==="package").map((entity)=>({name:label(entity),degree:degrees.get(entity.id)||0})).sort((a,b)=>b.degree-a.degree||a.name.localeCompare(b.name)).slice(0,10),max=Math.max(1,...topPackages.map((row)=>row.degree));dashboard.innerHTML="<section><div class=\"eyebrow\">Attention by scope</div><h2>Where the map needs a closer look</h2><table class=\"report-table\"><thead><tr><th>Scope</th><th>Packages</th><th>Modules</th><th>Findings</th></tr></thead><tbody>"+rows.map((row)=>"<tr><td>"+esc(row.name)+"</td><td>"+row.packages+"</td><td>"+row.modules+"</td><td><strong>"+row.findings+"</strong></td></tr>").join("")+"</tbody></table></section><section><div class=\"eyebrow\">Connectivity</div><h2>Most connected packages</h2><div class=\"bar-list\">"+topPackages.map((row)=>"<div class=\"bar-item\"><span>"+esc(row.name)+"</span><i><b style=\"width:"+Math.round(row.degree/max*100)+"%\"></b></i><strong>"+row.degree+"</strong></div>").join("")+"</div></section>"};
|
|
7104
7282
|
const mapState={scale:1,dragging:false,x:0,y:0};
|
|
7105
7283
|
const applyMapTransform=()=>{const svg=document.querySelector("#graph");if(!svg)return;const dense=svg.closest(".map-wrap")?.classList.contains("dense");svg.setAttribute("preserveAspectRatio",dense?"xMinYMin meet":"xMidYMid meet");svg.style.transform="translate("+mapState.x+"px,"+mapState.y+"px) scale("+mapState.scale+")";svg.style.transformOrigin="center center"};
|
|
7106
7284
|
const routeGraphEdges=()=>{const svg=document.querySelector("#graph");if(!svg)return;svg.querySelectorAll("line.edge").forEach((line,index)=>{const x1=Number(line.getAttribute("x1")),y1=Number(line.getAttribute("y1")),x2=Number(line.getAttribute("x2")),y2=Number(line.getAttribute("y2")),midX=(x1+x2)/2+((index%5)-2)*14,path=document.createElementNS("http://www.w3.org/2000/svg","path");path.setAttribute("class",line.getAttribute("class")||"edge");path.setAttribute("fill","none");path.setAttribute("marker-end",line.getAttribute("marker-end")||"url(#arrow)");path.setAttribute("d","M "+x1+" "+y1+" H "+midX+" V "+y2+" H "+x2);const label=line.parentElement?.querySelector("text.edge-label");if(label){label.setAttribute("x",String(midX+3));label.setAttribute("y",String((y1+y2)/2))}line.replaceWith(path)})};
|
|
@@ -7109,16 +7287,16 @@ let nodeClickTimer;
|
|
|
7109
7287
|
document.addEventListener("dblclick",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);enterNode(node.dataset.node)},true);
|
|
7110
7288
|
document.addEventListener("click",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);nodeClickTimer=setTimeout(async()=>{state.selected=node.dataset.node;render();const chunk=detailChunkName();if(chunk&&!hasChunk(chunk)){await loadChunk(chunk);render()}},380)},true);
|
|
7111
7289
|
document.addEventListener("click",(event)=>{const crumb=event.target.closest?.("[data-breadcrumb-level]");if(!crumb)return;event.preventDefault();event.stopImmediatePropagation();state.level=crumb.dataset.breadcrumbLevel;state.selected=crumb.dataset.breadcrumbSelected||null;mapState.scale=1;mapState.x=0;mapState.y=0;render()},true);
|
|
7112
|
-
const diagnosticsFor=(id)=>findingIndex.get(id)||[];
|
|
7290
|
+
const diagnosticsFor=(id)=>(findingIndex.get(id)||[]).filter((finding)=>finding.status!=="confirmed");
|
|
7113
7291
|
const renderDetails=()=>{const panel=document.querySelector("#details"),entity=state.selected?byId.get(state.selected):null,group=state.selected?groupById.get(state.selected):null;if(group){const members=group.members.map((id)=>byId.get(id)).filter(Boolean),relations=new Set(),ids=groupEntityIds.get(state.selected)||new Set();ids.forEach((id)=>(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation)));const findings=nodeIssues(state.selected),evidence=members.flatMap((member)=>member.evidence||[]).slice(0,8),unit=group.kind==="external"?"dependencies":"packages";panel.innerHTML="<div class=\"eyebrow\">Selected group</div><h2 class=\"detail-title\">"+esc(group.name)+"</h2><span class=\"tag\">"+esc(group.kind)+"</span><p class=\"path\">"+esc(group.path||"Derived from stable package identity and repository structure")+"</p><div class=\"detail-grid\"><div><b>"+members.length+"</b><span>"+unit+"</span></div><div><b>"+relations.size+"</b><span>relations</span></div><div><b>"+findings.length+"</b><span>findings</span></div><div><b>"+evidence.length+"</b><span>evidence items</span></div></div><h3>Members</h3><ul class=\"list\">"+(members.length?members.slice(0,8).map((member)=>"<li>"+esc(label(member))+"</li>").join(""):"<li>No members recorded.</li>")+(members.length>8?"<li class=\"subtle\">+"+(members.length-8)+" more — choose Package level to inspect all.</li>":"")+"</ul>"+(findings.length?"<h3 style=\"margin-top:16px\">Attention</h3><ul class=\"list\">"+findings.slice(0,5).map((finding)=>"<li><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> "+esc(finding.code)+"</li>").join("")+"</ul>":"");return}if(!entity){panel.innerHTML="<p class=\"subtle\">Select a node in the map to inspect its evidence, connectivity, and findings.</p>";return}const relations=relationsByEntity.get(entity.id)||[],incoming=relations.filter((relation)=>relation.to===entity.id),outgoing=relations.filter((relation)=>relation.from===entity.id),findings=diagnosticsFor(entity.id),evidence=[...(entity.evidence||[]),...incoming.flatMap((relation)=>relation.evidence||[]),...outgoing.flatMap((relation)=>relation.evidence||[])].slice(0,8);panel.innerHTML="<div class=\"eyebrow\">Selected entity</div><h2 class=\"detail-title\">"+esc(label(entity))+"</h2><span class=\"tag\">"+esc(entity.kind)+"</span><p class=\"path\">"+esc(entity.path||entity.id)+"</p><div class=\"detail-grid\"><div><b>"+incoming.length+"</b><span>incoming</span></div><div><b>"+outgoing.length+"</b><span>outgoing</span></div><div><b>"+findings.length+"</b><span>findings</span></div><div><b>"+evidence.length+"</b><span>evidence items</span></div></div><h3>Evidence</h3><ul class=\"list\">"+(evidence.length?evidence.map((item)=>"<li>"+esc(item.path+(item.lineStart?":"+item.lineStart:"")+(item.context?" — "+item.context:""))+"</li>").join(""):"<li>No evidence recorded.</li>")+"</ul>"+(findings.length?"<h3 style=\"margin-top:16px\">Attention</h3><ul class=\"list\">"+findings.slice(0,5).map((finding)=>"<li><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> "+esc(finding.code)+"</li>").join("")+"</ul>":"")};
|
|
7114
7292
|
const renderGraph=()=>{const model=graphModel(),svg=document.querySelector("#graph"),dense=model.edges.length>64,edges=dense?[...model.edges].sort((a,b)=>(b.health?1:0)-(a.health?1:0)||b.count-a.count||a.from.localeCompare(b.from)||a.to.localeCompare(b.to)).slice(0,64):model.edges;if(!model.nodes.length){svg.innerHTML="<text x=\"500\" y=\"270\" text-anchor=\"middle\" class=\"subtle\">Select an app or package to expand this view.</text>";return}const ids=new Set(model.nodes.map((node)=>node.id)),incoming=new Map(model.nodes.map((node)=>[node.id,0])),outgoing=new Map(model.nodes.map((node)=>[node.id,[]]));edges.forEach((edge)=>{if(!ids.has(edge.from)||!ids.has(edge.to))return;incoming.set(edge.to,(incoming.get(edge.to)||0)+1);outgoing.get(edge.from).push(edge.to)});const rank=new Map(),work=[];model.nodes.filter((node)=>(incoming.get(node.id)||0)===0).sort((a,b)=>label(a).localeCompare(label(b))||a.id.localeCompare(b.id)).forEach((node)=>{rank.set(node.id,0);work.push(node)});for(let index=0;index<work.length;index++){const node=work[index];for(const next of outgoing.get(node.id)||[]){const nextRank=Math.max(rank.get(next)||0,(rank.get(node.id)||0)+1);rank.set(next,nextRank);if(!work.some((item)=>item.id===next))work.push(byId.get(next)||{id:next,name:next})}}model.nodes.forEach((node)=>{if(!rank.has(node.id))rank.set(node.id,0)});const columns=new Map();model.nodes.forEach((node)=>{const items=columns.get(rank.get(node.id))||[];items.push(node);columns.set(rank.get(node.id),items)});for(const items of columns.values())items.sort((a,b)=>label(a).localeCompare(label(b))||a.id.localeCompare(b.id));if(dense){columns.clear();model.nodes.forEach((node,index)=>{const column=Math.floor(index/8),items=columns.get(column)||[];items.push(node);columns.set(column,items)})}const maxRows=Math.max(1,...[...columns.values()].map((items)=>items.length)),cellWidth=190,cellHeight=82,pad=28,columnCount=Math.max(1,...columns.keys())+1,width=Math.max(620,columnCount*cellWidth+pad*2,Math.min(1000,svg.clientWidth*1.05)),height=Math.max(520,maxRows*cellHeight+pad*2);svg.setAttribute("viewBox","0 0 "+width+" "+height);svg.style.width=dense?Math.max(1000,width)+"px":"100%";svg.style.minWidth="0";svg.style.height="100%";document.querySelector(".map-wrap")?.classList.toggle("dense",dense);const positions=new Map();for(const [column,items] of columns)items.forEach((node,row)=>positions.set(node.id,{x:pad+column*cellWidth+78,y:pad+row*cellHeight+27}));const defs="<defs><marker id=\"arrow\" markerWidth=\"8\" markerHeight=\"8\" refX=\"7\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L0,6 L7,3 z\" fill=\"#789087\"/></marker></defs>",edgeMarkup=edges.map((edge)=>{const from=positions.get(edge.from),to=positions.get(edge.to);if(!from||!to)return "";const midX=(from.x+to.x)/2,midY=(from.y+to.y)/2;return "<g><line class=\"edge "+(edge.health?"alert":"")+"\" x1=\""+from.x+"\" y1=\""+from.y+"\" x2=\""+to.x+"\" y2=\""+to.y+"\" marker-end=\"url(#arrow)\"/><text class=\"edge-label\" x=\""+midX+"\" y=\""+midY+"\">"+esc(edge.count>1?edge.count+"×":"")+"</text></g>"}).join(""),nodes=model.nodes.map((node)=>{const pos=positions.get(node.id),issues=nodeIssues(node.id),degree=degreeMap(edges).get(node.id)||0,selected=state.selected===node.id?" selected":"",issue=issues.length?" issue":"",nodeLabel=label(node),nodeKind=node.kind==="domain"?(node.memberCount||0)+" packages":node.kind;return "<g class=\"graph-node"+selected+issue+"\" transform=\"translate("+(pos.x-78)+","+(pos.y-27)+")\"><title>"+esc(nodeLabel)+" · "+esc(nodeKind)+"</title><rect width=\"156\" height=\"54\" rx=\"8\" role=\"button\" tabindex=\"0\" aria-label=\""+esc(nodeLabel+" "+nodeKind)+"\" data-node=\""+esc(node.id)+"\"></rect><text class=\"node-label\" x=\"10\" y=\"19\">"+esc(short(nodeLabel,22))+"</text><text class=\"node-kind\" x=\"10\" y=\"34\">"+esc(short(nodeKind,22))+"</text><text class=\"node-count\" x=\"144\" y=\"19\" text-anchor=\"end\">"+(degree||"")+"</text></g>"}).join("");svg.innerHTML=defs+edgeMarkup+nodes;routeGraphEdges();applyMapTransform();document.querySelector("#map-note").textContent=(state.level==="overview"?"Bounded domain view. ":state.level+" view. ")+"Directional edges are aggregated from canonical relations; "+model.edges.length+" visible connection groups"+(dense?" (showing "+edges.length+" prioritized of "+model.edges.length+").":".");};
|
|
7115
7293
|
const findingMatches=(finding)=>{const query=state.query.toLowerCase();return(!query||[finding.id,finding.code,finding.message,...finding.entityIds,...finding.relationIds].join(" ").toLowerCase().includes(query))&&(!state.status||finding.status===state.status)&&(!state.severity||finding.severity===state.severity)};
|
|
7116
7294
|
const findingScope=(finding)=>data.view?.diagnosticGroup?.[finding.id]?.[0]||(()=>{const ids=[...(finding.entityIds||[])];(finding.relationIds||[]).forEach((id)=>{const relation=relationById.get(id);if(relation)ids.push(relation.from,relation.to)});return ids.map(groupFor).sort()[0]||"repository"})();
|
|
7117
7295
|
const findingGroups=(findings)=>{const groups=new Map();findings.forEach((finding)=>{const scope=findingScope(finding),key=[scope,finding.code,finding.status,finding.severity].join("|"),group=groups.get(key)||{key,scope,code:finding.code,status:finding.status,severity:finding.severity,findings:[]};group.findings.push(finding);groups.set(key,group)});return[...groups.values()].sort((left,right)=>right.findings.length-left.findings.length||left.code.localeCompare(right.code)||left.key.localeCompare(right.key))};
|
|
7118
7296
|
const renderFinding=(finding)=>"<article class=\"finding\" id=\""+esc("diagnostic-"+finding.id.replace(/[^A-Za-z0-9_-]+/g,"-"))+"\"><div class=\"finding-head\"><h3>"+esc(finding.code)+"</h3><span><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> <span class=\"tag\">"+esc(finding.status)+"</span></span></div><p>"+esc(finding.message)+"</p>"+(finding.evidence.length?"<ul>"+finding.evidence.slice(0,4).map((item)=>"<li>"+esc(item.path+(item.lineStart?":"+item.lineStart:"")+(item.context?" — "+item.context:""))+"</li>").join("")+"</ul>":"")+(finding.remediation?"<p><strong>Next check:</strong> "+esc(finding.remediation)+"</p>":"")+"</article>";
|
|
7119
|
-
const renderFindings=()=>{let findings=data.diagnostics.filter(findingMatches);if(state.lens==="risks")findings=findings.filter((finding)=>finding.severity==="error"||finding.severity==="warn");if(state.lens==="evidence")findings=findings.filter((finding)=>finding.evidence.length);if(state.lens==="drift")findings=findings.filter((finding)=>finding.status!=="confirmed");const groups=findingGroups(findings),active=groups.find((group)=>group.key===state.findingGroup);if(!active){state.findingGroup=null;state.findingPage=0}syncHash();const selected=active||null,pageSize=40,pageCount=selected?Math.ceil(selected.findings.length/pageSize):0,page=Math.max(0,Math.min(state.findingPage,Math.max(0,pageCount-1))),start=page*pageSize;document.querySelector("#finding-count").textContent=findings.length+"
|
|
7297
|
+
const renderFindings=()=>{let findings=data.diagnostics.filter(findingMatches);if(state.lens==="risks")findings=findings.filter((finding)=>finding.severity==="error"||finding.severity==="warn");if(state.lens==="evidence")findings=findings.filter((finding)=>finding.evidence.length);if(state.lens==="drift")findings=findings.filter((finding)=>finding.status!=="confirmed");const itemLabel=state.lens==="evidence"?"evidence checks":"findings",groups=findingGroups(findings),active=groups.find((group)=>group.key===state.findingGroup);if(!active){state.findingGroup=null;state.findingPage=0}syncHash();const selected=active||null,pageSize=40,pageCount=selected?Math.ceil(selected.findings.length/pageSize):0,page=Math.max(0,Math.min(state.findingPage,Math.max(0,pageCount-1))),start=page*pageSize;document.querySelector("#finding-count").textContent=findings.length+" "+itemLabel+" · "+groups.length+" groups";document.querySelector("#findings").innerHTML=findings.length?"<div class=\"finding-groups\">"+groups.map((group)=>"<article class=\"finding-group\"><div class=\"finding-head\"><h3>"+esc(group.code)+"</h3><span><span class=\"tag "+esc(group.severity)+"\">"+esc(group.severity)+"</span> <span class=\"tag\">"+esc(group.status)+"</span></span></div><p><strong>"+group.findings.length+"</strong> "+itemLabel+" · "+esc(groupById.get(group.scope)?.name||group.scope)+"</p><p>"+esc(group.findings[0].message)+"</p><button class=\"tab\" type=\"button\" data-finding-group=\""+esc(group.key)+"\" aria-expanded=\""+String(selected?.key===group.key)+"\">Inspect group</button></article>").join("")+"</div>"+(selected?"<div class=\"finding-detail\"><div class=\"finding-head\"><h3>"+esc(selected.code)+" · "+esc(groupById.get(selected.scope)?.name||selected.scope)+"</h3><span class=\"subtle\">"+selected.findings.length+" "+itemLabel+"</span></div>"+selected.findings.slice(start,start+pageSize).map(renderFinding).join("")+"<div class=\"finding-actions\"><button class=\"tab\" type=\"button\" data-finding-group=\""+esc(selected.key)+"\" data-finding-page=\""+(page-1)+"\" "+(page===0?"disabled":"")+">Previous</button><span class=\"subtle\">Showing "+(start+1)+"–"+Math.min(start+pageSize,selected.findings.length)+" of "+selected.findings.length+" · page "+(page+1)+"/"+pageCount+"</span><button class=\"tab\" type=\"button\" data-finding-group=\""+esc(selected.key)+"\" data-finding-page=\""+(page+1)+"\" "+(page+1>=pageCount?"disabled":"")+">Next</button></div></div>":""):"<p class=\"empty\">No "+itemLabel+" match the current lens and filters.</p>"};
|
|
7120
7298
|
const renderCoverage=()=>{document.querySelector("#coverage-list").innerHTML=(data.coverage||[]).map((entry)=>{const width=entry.status==="complete"?100:entry.status==="partial"?55:12;return"<div class=\"coverage-row\"><div><b>"+esc(entry.analyzer)+"</b><br><span class=\"subtle\">"+esc(entry.scope)+"</span></div><div class=\"bar\"><i class=\""+(entry.status==="complete"?"":entry.status==="partial"?"partial":"none")+"\" style=\"width:"+width+"%\"></i></div><div>"+esc(entry.status)+"</div></div>"}).join("")||"<p class=\"empty\">No coverage metadata.</p>"};
|
|
7121
|
-
const deferFindings=()=>{if(findingsLoaded())return;const
|
|
7299
|
+
const deferFindings=()=>{if(findingsLoaded())return;const actionable=data.actionableCount??data.diagnosticCount??0,confirmed=data.confirmedCount??0;document.querySelector("#finding-count").textContent=actionable+" actionable findings · "+confirmed+" confirmed checks available on demand";document.querySelector("#findings").innerHTML="<div class=\"empty\"><p>Findings stay out of the first paint so large repositories remain responsive. Confirmed checks are evidence, not issues.</p><button id=\"load-findings\" class=\"tab\" type=\"button\">Load findings</button></div>"};
|
|
7122
7300
|
const scopePrompt=()=>{if((state.level==="module"||state.level==="file")&&!state.selected){document.querySelector("#map-note").textContent="Select an app or package to inspect this level.";document.querySelector("#graph").innerHTML="<text x=\"500\" y=\"270\" text-anchor=\"middle\" class=\"subtle\">Select an app or package to expand this view.</text>"}};
|
|
7123
7301
|
const findingsLoaded=()=>!lazyChunks.includes("chunks/findings.js")||loadedChunks.has("chunks/findings.js");
|
|
7124
7302
|
const levelsLoaded=()=>!hasLevelChunks||state.level==="overview"||activeLevelChunk===levelChunkName();
|
|
@@ -7154,11 +7332,12 @@ var render = (input, options) => {
|
|
|
7154
7332
|
const data = reportData(snapshot, report, includeSnippets, options.privacy);
|
|
7155
7333
|
const scriptBody = script.replace("${DATA}", options.dataExpression ?? embeddedJson(data));
|
|
7156
7334
|
const largeNote = snapshot.entities.length > 500 || report.diagnostics.length > 1e3 ? "Large snapshots are rendered from compact canonical data with progressive graph levels." : "The viewer starts with a grouped topology and expands into canonical entities on demand.";
|
|
7157
|
-
const
|
|
7335
|
+
const actionableCount = report.diagnostics.filter((diagnostic2) => diagnostic2.status !== "confirmed").length;
|
|
7336
|
+
const confirmedCount = report.diagnostics.filter((diagnostic2) => diagnostic2.status === "confirmed").length;
|
|
7158
7337
|
const unsupportedCount = snapshot.coverage.filter((entry) => entry.status !== "complete").length;
|
|
7159
|
-
const policyNote = report.summary.requiredRelationKinds === void 0 ? "Missing relation declarations are checked for every observed relation kind." : report.summary.requiredRelationKinds.length ? `Missing relation declarations are checked for: ${report.summary.requiredRelationKinds.join(", ")}.` : "Missing relation declarations are disabled by configuration; stale, conflicting, and unresolved declarations remain checked.";
|
|
7338
|
+
const policyNote = report.summary.requiredRelationKinds === void 0 ? "Missing relation declarations are checked for every observed relation kind." : report.summary.requiredRelationKinds.length ? `Missing relation declarations are checked for: ${report.summary.requiredRelationKinds.join(", ")}${report.summary.requiredRelationTargets === "internal" ? " between internal entities" : ""}.` : "Missing relation declarations are disabled by configuration; stale, conflicting, and unresolved declarations remain checked.";
|
|
7160
7339
|
const statusOptions = ["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"];
|
|
7161
|
-
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(data.project.name)}</title><style>${styles}</style></head><body>${options.dataScripts ?? ""}<main class="shell"><header class="masthead"><div><div class="eyebrow">Doc Bridge / Knowledge report</div><h1>${escapeHtml(data.project.name)}</h1><p class="lede">A read-only architecture and documentation map. Start broad, then follow evidence to the exact entity, relation, or finding.</p></div><div class="run-meta"><span class="read-only">Read-only snapshot</span><strong>${escapeHtml(data.revision)}</strong><span>${escapeHtml(data.revisionKind)} \xB7 pipeline ${escapeHtml(data.pipelineVersion)}</span></div></header><section class="summary" aria-label="Snapshot summary"><div class="metric"><b>${snapshot.entities.length}</b><span>entities</span></div><div class="metric"><b>${snapshot.relations.length}</b><span>canonical relations</span></div><div class="metric ${
|
|
7340
|
+
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(data.project.name)}</title><style>${styles}</style></head><body>${options.dataScripts ?? ""}<main class="shell"><header class="masthead"><div><div class="eyebrow">Doc Bridge / Knowledge report</div><h1>${escapeHtml(data.project.name)}</h1><p class="lede">A read-only architecture and documentation map. Start broad, then follow evidence to the exact entity, relation, or finding.</p></div><div class="run-meta"><span class="read-only">Read-only snapshot</span><strong>${escapeHtml(data.revision)}</strong><span>${escapeHtml(data.revisionKind)} \xB7 pipeline ${escapeHtml(data.pipelineVersion)}</span></div></header><section class="summary" aria-label="Snapshot summary"><div class="metric"><b>${snapshot.entities.length}</b><span>entities</span></div><div class="metric"><b>${snapshot.relations.length}</b><span>canonical relations</span></div><div class="metric ${actionableCount ? "warn" : ""}"><b>${actionableCount}</b><span>actionable findings</span></div><div class="metric"><b>${confirmedCount}</b><span>confirmed checks</span></div><div class="metric ${unsupportedCount ? "bad" : ""}"><b>${unsupportedCount}</b><span>partial / unanalyzed scopes</span></div></section><div class="lens-bar"><nav class="tabs" aria-label="Report lenses"><button class="tab" data-lens="architecture" aria-selected="true">Architecture</button><button class="tab" data-lens="drift" aria-selected="false">Documentation drift</button><button class="tab" data-lens="risks" aria-selected="false">Risks & hotspots</button><button class="tab" data-lens="evidence" aria-selected="false">Evidence</button></nav><span class="subtle">${escapeHtml(largeNote)}</span></div><p id="lens-caption" class="subtle">The repository topology at the selected level.</p><p class="subtle">${escapeHtml(policyNote)}</p><section id="insights" class="insights" aria-label="Attention signals"></section><section class="filters" aria-label="Finding filters"><label>Search <input id="search" type="search" placeholder="Press / to search findings"></label><label>Status <select id="status"><option value="">Any status</option>${statusOptions.map((value) => `<option>${escapeHtml(value)}</option>`).join("")}</select></label><label>Severity <select id="severity"><option value="">Any severity</option><option>error</option><option>warn</option><option>info</option></select></label><button id="reset" type="button">Reset filters</button></section><div class="workspace"><section class="panel" aria-labelledby="architecture-title"><div class="panel-head"><div><h2 id="architecture-title">Architecture map</h2><p id="map-note" class="subtle">Grouped package/external view.</p></div><div class="tabs" aria-label="Graph level"><button class="level" data-level="overview" aria-pressed="true">Overview</button><button class="level" data-level="package" aria-pressed="false">Package</button><button class="level" data-level="module" aria-pressed="false">Module</button><button class="level" data-level="file" aria-pressed="false">File</button></div></div><div class="map-wrap"><svg id="graph" viewBox="0 0 1000 560" role="img" aria-label="Interactive architecture graph"></svg></div><p class="subtle">Node number = visible connection degree. Amber nodes/edges have actionable findings. Click or focus a node to inspect its evidence, connectivity, and findings. Grouped edges preserve canonical relation direction and count.</p></section><aside class="panel side" aria-labelledby="details-title"><div class="panel-head"><div><div class="eyebrow">Evidence trail</div><h2 id="details-title">Details</h2></div><button id="clear-selection" class="tab" type="button">Clear</button></div><div id="details"><p class="subtle">Select a node in the map to inspect its evidence, connectivity, and findings.</p></div></aside></div><section class="panel run" aria-labelledby="run-title"><div class="panel-head"><div><div class="eyebrow">Jest-like diagnostics</div><h2 id="run-title">Run report</h2></div><span id="finding-count" class="subtle">${actionableCount} actionable findings \xB7 ${confirmedCount} confirmed checks</span></div><div class="run-summary"><span class="chip"><b>${report.diagnostics.filter((item) => item.severity === "error").length}</b> errors</span><span class="chip"><b>${report.diagnostics.filter((item) => item.severity === "warn").length}</b> warnings</span><span class="chip"><b>${report.diagnostics.filter((item) => item.status === "undocumented").length}</b> undocumented</span><span class="chip"><b>${report.diagnostics.filter((item) => item.status === "stale-or-unverified" || item.status === "conflict").length}</b> drift/conflict</span><span class="chip"><b>${confirmedCount}</b> confirmed checks</span></div><div id="findings"></div></section><section class="panel run" aria-labelledby="coverage-title"><div class="panel-head"><div><div class="eyebrow">Analyzer boundaries</div><h2 id="coverage-title">Coverage & unsupported areas</h2></div><span class="subtle">Explicit limits are part of the evidence</span></div><div id="coverage-list"></div></section><section class="metadata" aria-label="Run metadata"><div><b>Snapshot</b>${escapeHtml(data.snapshotHash)}</div><div><b>Report</b>${escapeHtml(data.reportHash)}</div><div><b>Configuration</b>${escapeHtml(data.configurationHash)}</div><div><b>Analyzers</b>${escapeHtml(Object.entries(data.analyzerVersions).map(([name, version]) => `${name} ${version}`).join(", "))}</div><div><b>Source revision</b>${escapeHtml(data.revision)} (${escapeHtml(data.revisionKind)})</div><div><b>Mode</b>${options.privacy === "anonymized" ? "Anonymized read-only browser viewer; evidence paths and project identity are redacted." : "Read-only browser viewer; approvals and fixes remain outside this artifact."}</div></section></main><script>${scriptBody}</script></body></html>`;
|
|
7162
7341
|
};
|
|
7163
7342
|
var parseReportInput = (input) => {
|
|
7164
7343
|
if (!input || typeof input !== "object") throw new Error("Input must contain snapshot and report artifacts.");
|
|
@@ -7264,7 +7443,7 @@ var renderOfflineReportArtifact = (input, options = {}) => {
|
|
|
7264
7443
|
diagnosticRelationFindings: Object.fromEntries(diagnosticRelationFindings)
|
|
7265
7444
|
};
|
|
7266
7445
|
const files = {
|
|
7267
|
-
"chunks/overview.js": chunkScript({ project: data.project, revision: data.revision, revisionKind: data.revisionKind, snapshotHash: data.snapshotHash, reportHash: data.reportHash, configurationHash: data.configurationHash, pipelineVersion: data.pipelineVersion, analyzerVersions: data.analyzerVersions, coverage: data.coverage, diagnosticCount: data.diagnosticCount, diagnosticSummary: data.diagnosticSummary, requiredRelationKinds: data.requiredRelationKinds, entities: packageEntities, relations: packageRelations, view: overviewView }),
|
|
7446
|
+
"chunks/overview.js": chunkScript({ project: data.project, revision: data.revision, revisionKind: data.revisionKind, snapshotHash: data.snapshotHash, reportHash: data.reportHash, configurationHash: data.configurationHash, pipelineVersion: data.pipelineVersion, analyzerVersions: data.analyzerVersions, coverage: data.coverage, diagnosticCount: data.diagnosticCount, actionableCount: data.actionableCount, confirmedCount: data.confirmedCount, diagnosticSummary: data.diagnosticSummary, documentation: data.documentation, requiredRelationKinds: data.requiredRelationKinds, entities: packageEntities, relations: packageRelations, view: overviewView }),
|
|
7268
7447
|
...levelFiles,
|
|
7269
7448
|
...detailFiles,
|
|
7270
7449
|
"chunks/findings.js": chunkScript({ diagnostics: data.diagnostics })
|
|
@@ -7672,6 +7851,7 @@ var reconcileWorkflow = (root, config) => {
|
|
|
7672
7851
|
const report = reconcileKnowledge(snapshot, declared, {
|
|
7673
7852
|
...config.reconciliation?.scope === void 0 ? {} : { scope: config.reconciliation.scope },
|
|
7674
7853
|
...config.reconciliation?.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: config.reconciliation.requiredRelationKinds },
|
|
7854
|
+
...config.reconciliation?.requiredRelationTargets === void 0 ? {} : { requiredRelationTargets: config.reconciliation.requiredRelationTargets },
|
|
7675
7855
|
...config.reconciliation?.includeOrphanedDocuments === void 0 ? {} : { includeOrphanedDocuments: config.reconciliation.includeOrphanedDocuments }
|
|
7676
7856
|
});
|
|
7677
7857
|
return runWorkflow(workflowOptions(root, config, snapshot.sourceRevision, "reconcile", { reconcile: () => report }, { pipelineVersion: snapshot.pipelineVersion, analyzerVersions: snapshot.analyzerVersions }));
|
|
@@ -7725,7 +7905,7 @@ var writeAtomicFile = (path, content) => {
|
|
|
7725
7905
|
renameSync3(temporaryPath, path);
|
|
7726
7906
|
};
|
|
7727
7907
|
var writeReportArtifact = (htmlPath, artifact2) => {
|
|
7728
|
-
|
|
7908
|
+
mkdirSync7(dirname9(htmlPath), { recursive: true });
|
|
7729
7909
|
if (artifact2.mode === "single-file") {
|
|
7730
7910
|
writeAtomicFile(htmlPath, artifact2.indexHtml);
|
|
7731
7911
|
const artifactDir2 = htmlPath.replace(/\.html?$/i, "");
|
|
@@ -7738,14 +7918,14 @@ var writeReportArtifact = (htmlPath, artifact2) => {
|
|
|
7738
7918
|
const launcherTemp = `${htmlPath}.tmp-${process.pid}`;
|
|
7739
7919
|
rmSync3(temporaryDir, { recursive: true, force: true });
|
|
7740
7920
|
rmSync3(backupDir, { recursive: true, force: true });
|
|
7741
|
-
|
|
7921
|
+
mkdirSync7(temporaryDir, { recursive: true });
|
|
7742
7922
|
for (const [file, content] of Object.entries(artifact2.files)) {
|
|
7743
7923
|
const filePath = resolve18(temporaryDir, file);
|
|
7744
|
-
|
|
7924
|
+
mkdirSync7(dirname9(filePath), { recursive: true });
|
|
7745
7925
|
writeFileSync8(filePath, content, "utf8");
|
|
7746
7926
|
}
|
|
7747
7927
|
writeFileSync8(resolve18(temporaryDir, "manifest.json"), artifact2.manifest, "utf8");
|
|
7748
|
-
const frameSource = `${relative10(
|
|
7928
|
+
const frameSource = `${relative10(dirname9(htmlPath), artifactDir)}/index.html`;
|
|
7749
7929
|
const launcher = `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Doc Bridge report</title></head><body style="margin:0"><iframe title="Doc Bridge report" src="${frameSource}" style="border:0;width:100vw;height:100vh"></iframe></body></html>`;
|
|
7750
7930
|
writeFileSync8(launcherTemp, launcher, "utf8");
|
|
7751
7931
|
try {
|
|
@@ -7777,7 +7957,7 @@ var runWorkflowCommand = (command, flags, configPath, argv) => {
|
|
|
7777
7957
|
const report = parseReconciliationReport(loadWorkflowStepOutput(result.stateDir, "reconcile"));
|
|
7778
7958
|
const outputPath = optionValues(argv, "--output")[0] ?? ".doc-bridge/report.html";
|
|
7779
7959
|
const htmlPath = resolve18(root, outputPath);
|
|
7780
|
-
|
|
7960
|
+
mkdirSync7(dirname9(htmlPath), { recursive: true });
|
|
7781
7961
|
const thresholdValue = optionValues(argv, "--report-threshold")[0];
|
|
7782
7962
|
const thresholdBytes = thresholdValue === void 0 ? void 0 : Number(thresholdValue);
|
|
7783
7963
|
if (thresholdBytes !== void 0 && (!Number.isSafeInteger(thresholdBytes) || thresholdBytes < 1)) throw new Error("--report-threshold must be a positive integer.");
|
|
@@ -7855,7 +8035,7 @@ var runFixCommand = (argv, positional, configPath) => {
|
|
|
7855
8035
|
}
|
|
7856
8036
|
const outputPath = optionValues(argv, "--output")[0];
|
|
7857
8037
|
if (outputPath) {
|
|
7858
|
-
|
|
8038
|
+
mkdirSync7(dirname9(resolve18(root, outputPath)), { recursive: true });
|
|
7859
8039
|
writeFileSync8(resolve18(root, outputPath), `${JSON.stringify(proposal2, null, 2)}
|
|
7860
8040
|
`, "utf8");
|
|
7861
8041
|
}
|
|
@@ -7895,10 +8075,19 @@ var runSuggestCommand = async (flags, configPath) => {
|
|
|
7895
8075
|
}
|
|
7896
8076
|
};
|
|
7897
8077
|
var writeIfMissing = (path, contents) => {
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
8078
|
+
mkdirSync7(dirname9(path), { recursive: true });
|
|
8079
|
+
try {
|
|
8080
|
+
const fd = openSync3(path, "wx");
|
|
8081
|
+
try {
|
|
8082
|
+
writeFileSync8(fd, contents, "utf8");
|
|
8083
|
+
return true;
|
|
8084
|
+
} finally {
|
|
8085
|
+
closeSync3(fd);
|
|
8086
|
+
}
|
|
8087
|
+
} catch (error) {
|
|
8088
|
+
if (error.code === "EEXIST") return false;
|
|
8089
|
+
throw error;
|
|
8090
|
+
}
|
|
7902
8091
|
};
|
|
7903
8092
|
var demoOwnership = {
|
|
7904
8093
|
example: {
|
|
@@ -8397,7 +8586,7 @@ var runCli = (argv) => {
|
|
|
8397
8586
|
const report = runDoctor(root, config);
|
|
8398
8587
|
if (flags.has("--write-badge")) {
|
|
8399
8588
|
const badgePath = resolve18(root, ".doc-bridge", "coverage-badge.json");
|
|
8400
|
-
|
|
8589
|
+
mkdirSync7(dirname9(badgePath), { recursive: true });
|
|
8401
8590
|
writeFileSync8(badgePath, `${formatDoctorBadgeJson(report.badge)}
|
|
8402
8591
|
`, "utf8");
|
|
8403
8592
|
}
|