@praneeth_54/agentdoctor 0.3.0-beta → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +105 -1
- package/README.md +64 -31
- package/dist/agents/inspect.js +58 -51
- package/dist/cli/commands/brain-mcp.d.ts +9 -0
- package/dist/cli/commands/brain-mcp.js +38 -0
- package/dist/cli/commands/explain.js +1 -1
- package/dist/cli/commands/fix.js +22 -6
- package/dist/cli/commands/scan.d.ts +5 -0
- package/dist/cli/commands/scan.js +25 -6
- package/dist/cli/commands/verify.d.ts +6 -0
- package/dist/cli/commands/verify.js +25 -11
- package/dist/cli/program.js +61 -7
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/fix/apply.d.ts +5 -1
- package/dist/core/fix/apply.js +34 -14
- package/dist/core/fix/plan.d.ts +1 -1
- package/dist/core/fix/plan.js +110 -3
- package/dist/core/fix/render.d.ts +2 -0
- package/dist/core/fix/render.js +57 -9
- package/dist/core/fix/run.js +5 -1
- package/dist/core/fix/types.d.ts +1 -1
- package/dist/core/fix/types.js +5 -1
- package/dist/core/fix/writers/claude-settings.d.ts +16 -0
- package/dist/core/fix/writers/claude-settings.js +100 -0
- package/dist/core/fix/writers/codex-config.d.ts +22 -0
- package/dist/core/fix/writers/codex-config.js +183 -0
- package/dist/core/fix/writers/cursorignore.js +2 -8
- package/dist/core/path-resolution/index.d.ts +5 -0
- package/dist/core/path-resolution/index.js +5 -0
- package/dist/core/path-resolution/prepare.d.ts +33 -0
- package/dist/core/path-resolution/prepare.js +98 -0
- package/dist/core/policy/evaluate.d.ts +44 -0
- package/dist/core/policy/evaluate.js +120 -0
- package/dist/core/rules/claude-deny.d.ts +8 -0
- package/dist/core/rules/claude-deny.js +39 -0
- package/dist/core/rules/codex-deny.d.ts +5 -0
- package/dist/core/rules/codex-deny.js +42 -0
- package/dist/core/rules/context/generated-directory.js +43 -9
- package/dist/core/rules/context/large-log-file.js +27 -10
- package/dist/core/rules/ignore.js +2 -5
- package/dist/core/rules/instructions/missing-path-reference.d.ts +5 -1
- package/dist/core/rules/instructions/missing-path-reference.js +24 -6
- package/dist/core/rules/path-kind.d.ts +15 -2
- package/dist/core/rules/path-kind.js +113 -10
- package/dist/core/rules/security/env-file-exposure.js +9 -24
- package/dist/core/rules/text-cache.js +46 -40
- package/dist/core/understanding/architecture/index.d.ts +4 -0
- package/dist/core/understanding/architecture/index.js +3 -0
- package/dist/core/understanding/architecture/infer.d.ts +6 -0
- package/dist/core/understanding/architecture/infer.js +39 -0
- package/dist/core/understanding/architecture/models.d.ts +23 -0
- package/dist/core/understanding/architecture/models.js +127 -0
- package/dist/core/understanding/architecture/patterns/distribution.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/distribution.js +115 -0
- package/dist/core/understanding/architecture/patterns/index.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/index.js +8 -0
- package/dist/core/understanding/architecture/patterns/layered.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/layered.js +279 -0
- package/dist/core/understanding/architecture/patterns/structural.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/structural.js +197 -0
- package/dist/core/understanding/architecture/rules.d.ts +4 -0
- package/dist/core/understanding/architecture/rules.js +6 -0
- package/dist/core/understanding/architecture/types.d.ts +52 -0
- package/dist/core/understanding/architecture/types.js +1 -0
- package/dist/core/understanding/brain/build.d.ts +13 -0
- package/dist/core/understanding/brain/build.js +158 -0
- package/dist/core/understanding/brain/claims/index.d.ts +3 -0
- package/dist/core/understanding/brain/claims/index.js +2 -0
- package/dist/core/understanding/brain/claims/lifecycle.d.ts +16 -0
- package/dist/core/understanding/brain/claims/lifecycle.js +78 -0
- package/dist/core/understanding/brain/claims/types.d.ts +32 -0
- package/dist/core/understanding/brain/claims/types.js +25 -0
- package/dist/core/understanding/brain/components/index.d.ts +17 -0
- package/dist/core/understanding/brain/components/index.js +97 -0
- package/dist/core/understanding/brain/components/types.d.ts +16 -0
- package/dist/core/understanding/brain/components/types.js +8 -0
- package/dist/core/understanding/brain/confidence.d.ts +19 -0
- package/dist/core/understanding/brain/confidence.js +39 -0
- package/dist/core/understanding/brain/contract.d.ts +17 -0
- package/dist/core/understanding/brain/contract.js +100 -0
- package/dist/core/understanding/brain/contradictions/index.d.ts +12 -0
- package/dist/core/understanding/brain/contradictions/index.js +57 -0
- package/dist/core/understanding/brain/contradictions/types.d.ts +12 -0
- package/dist/core/understanding/brain/contradictions/types.js +8 -0
- package/dist/core/understanding/brain/delta.d.ts +25 -0
- package/dist/core/understanding/brain/delta.js +116 -0
- package/dist/core/understanding/brain/evidence/index.d.ts +3 -0
- package/dist/core/understanding/brain/evidence/index.js +2 -0
- package/dist/core/understanding/brain/evidence/redact.d.ts +4 -0
- package/dist/core/understanding/brain/evidence/redact.js +36 -0
- package/dist/core/understanding/brain/evidence/types.d.ts +34 -0
- package/dist/core/understanding/brain/evidence/types.js +35 -0
- package/dist/core/understanding/brain/explain.d.ts +17 -0
- package/dist/core/understanding/brain/explain.js +33 -0
- package/dist/core/understanding/brain/index.d.ts +28 -0
- package/dist/core/understanding/brain/index.js +16 -0
- package/dist/core/understanding/brain/migrate.d.ts +21 -0
- package/dist/core/understanding/brain/migrate.js +47 -0
- package/dist/core/understanding/brain/query.d.ts +60 -0
- package/dist/core/understanding/brain/query.js +92 -0
- package/dist/core/understanding/brain/security.d.ts +3 -0
- package/dist/core/understanding/brain/security.js +43 -0
- package/dist/core/understanding/brain/storage/index.d.ts +2 -0
- package/dist/core/understanding/brain/storage/index.js +1 -0
- package/dist/core/understanding/brain/storage/store.d.ts +53 -0
- package/dist/core/understanding/brain/storage/store.js +305 -0
- package/dist/core/understanding/brain/trace.d.ts +24 -0
- package/dist/core/understanding/brain/trace.js +207 -0
- package/dist/core/understanding/brain/types.d.ts +32 -0
- package/dist/core/understanding/brain/types.js +8 -0
- package/dist/core/understanding/brain/version.d.ts +13 -0
- package/dist/core/understanding/brain/version.js +7 -0
- package/dist/core/understanding/delta/compare.d.ts +26 -0
- package/dist/core/understanding/delta/compare.js +74 -0
- package/dist/core/understanding/delta/index.d.ts +2 -0
- package/dist/core/understanding/delta/index.js +1 -0
- package/dist/core/understanding/dependencies/discover.d.ts +7 -0
- package/dist/core/understanding/dependencies/discover.js +550 -0
- package/dist/core/understanding/dependencies/extract.d.ts +10 -0
- package/dist/core/understanding/dependencies/extract.js +189 -0
- package/dist/core/understanding/dependencies/index.d.ts +4 -0
- package/dist/core/understanding/dependencies/index.js +3 -0
- package/dist/core/understanding/dependencies/models.d.ts +12 -0
- package/dist/core/understanding/dependencies/models.js +125 -0
- package/dist/core/understanding/dependencies/types.d.ts +27 -0
- package/dist/core/understanding/dependencies/types.js +1 -0
- package/dist/core/understanding/domain/discover.d.ts +14 -0
- package/dist/core/understanding/domain/discover.js +90 -0
- package/dist/core/understanding/domain/index.d.ts +2 -0
- package/dist/core/understanding/domain/index.js +1 -0
- package/dist/core/understanding/entrypoints/discover.d.ts +6 -0
- package/dist/core/understanding/entrypoints/discover.js +62 -0
- package/dist/core/understanding/entrypoints/extract.d.ts +19 -0
- package/dist/core/understanding/entrypoints/extract.js +43 -0
- package/dist/core/understanding/entrypoints/index.d.ts +4 -0
- package/dist/core/understanding/entrypoints/index.js +3 -0
- package/dist/core/understanding/entrypoints/models.d.ts +26 -0
- package/dist/core/understanding/entrypoints/models.js +216 -0
- package/dist/core/understanding/entrypoints/types.d.ts +20 -0
- package/dist/core/understanding/entrypoints/types.js +1 -0
- package/dist/core/understanding/index.d.ts +32 -0
- package/dist/core/understanding/index.js +18 -0
- package/dist/core/understanding/mind/build.d.ts +16 -0
- package/dist/core/understanding/mind/build.js +134 -0
- package/dist/core/understanding/mind/index.d.ts +6 -0
- package/dist/core/understanding/mind/index.js +3 -0
- package/dist/core/understanding/mind/query.d.ts +33 -0
- package/dist/core/understanding/mind/query.js +39 -0
- package/dist/core/understanding/mind/types.d.ts +24 -0
- package/dist/core/understanding/mind/types.js +7 -0
- package/dist/core/understanding/model/builder.d.ts +9 -0
- package/dist/core/understanding/model/builder.js +209 -0
- package/dist/core/understanding/model/ids.d.ts +7 -0
- package/dist/core/understanding/model/ids.js +48 -0
- package/dist/core/understanding/model/index.d.ts +7 -0
- package/dist/core/understanding/model/index.js +6 -0
- package/dist/core/understanding/model/schema.d.ts +12 -0
- package/dist/core/understanding/model/schema.js +33 -0
- package/dist/core/understanding/model/serializer.d.ts +9 -0
- package/dist/core/understanding/model/serializer.js +46 -0
- package/dist/core/understanding/model/types.d.ts +110 -0
- package/dist/core/understanding/model/types.js +1 -0
- package/dist/core/understanding/model/validator.d.ts +5 -0
- package/dist/core/understanding/model/validator.js +126 -0
- package/dist/core/understanding/model/version.d.ts +9 -0
- package/dist/core/understanding/model/version.js +7 -0
- package/dist/core/understanding/ownership/discover.d.ts +12 -0
- package/dist/core/understanding/ownership/discover.js +242 -0
- package/dist/core/understanding/ownership/index.d.ts +2 -0
- package/dist/core/understanding/ownership/index.js +1 -0
- package/dist/core/understanding/ownership/types.d.ts +23 -0
- package/dist/core/understanding/ownership/types.js +1 -0
- package/dist/core/understanding/query/engine.d.ts +24 -0
- package/dist/core/understanding/query/engine.js +35 -0
- package/dist/core/understanding/query/errors.d.ts +13 -0
- package/dist/core/understanding/query/errors.js +26 -0
- package/dist/core/understanding/query/executor.d.ts +8 -0
- package/dist/core/understanding/query/executor.js +34 -0
- package/dist/core/understanding/query/index.d.ts +7 -0
- package/dist/core/understanding/query/index.js +5 -0
- package/dist/core/understanding/query/models.d.ts +102 -0
- package/dist/core/understanding/query/models.js +1 -0
- package/dist/core/understanding/query/query.d.ts +68 -0
- package/dist/core/understanding/query/query.js +145 -0
- package/dist/core/understanding/query/registry.d.ts +8 -0
- package/dist/core/understanding/query/registry.js +340 -0
- package/dist/core/understanding/query/types.d.ts +63 -0
- package/dist/core/understanding/query/types.js +1 -0
- package/dist/core/understanding/relationships/discover.d.ts +11 -0
- package/dist/core/understanding/relationships/discover.js +517 -0
- package/dist/core/understanding/relationships/extract.d.ts +14 -0
- package/dist/core/understanding/relationships/extract.js +165 -0
- package/dist/core/understanding/relationships/index.d.ts +4 -0
- package/dist/core/understanding/relationships/index.js +3 -0
- package/dist/core/understanding/relationships/models.d.ts +21 -0
- package/dist/core/understanding/relationships/models.js +185 -0
- package/dist/core/understanding/relationships/types.d.ts +42 -0
- package/dist/core/understanding/relationships/types.js +1 -0
- package/dist/core/understanding/risks/discover.d.ts +8 -0
- package/dist/core/understanding/risks/discover.js +155 -0
- package/dist/core/understanding/risks/index.d.ts +2 -0
- package/dist/core/understanding/risks/index.js +1 -0
- package/dist/core/understanding/risks/types.d.ts +26 -0
- package/dist/core/understanding/risks/types.js +1 -0
- package/dist/core/understanding/shared/domain-lexicon.d.ts +9 -0
- package/dist/core/understanding/shared/domain-lexicon.js +107 -0
- package/dist/core/understanding/shared/index.d.ts +2 -0
- package/dist/core/understanding/shared/index.js +2 -0
- package/dist/core/understanding/shared/tokens.d.ts +7 -0
- package/dist/core/understanding/shared/tokens.js +141 -0
- package/dist/core/understanding/snapshot/identity.d.ts +24 -0
- package/dist/core/understanding/snapshot/identity.js +79 -0
- package/dist/core/understanding/snapshot/index.d.ts +2 -0
- package/dist/core/understanding/snapshot/index.js +1 -0
- package/dist/core/understanding/types/index.d.ts +22 -0
- package/dist/core/understanding/types/index.js +1 -0
- package/dist/core/understanding/understand/formatter.d.ts +10 -0
- package/dist/core/understanding/understand/formatter.js +115 -0
- package/dist/core/understanding/understand/index.d.ts +4 -0
- package/dist/core/understanding/understand/index.js +3 -0
- package/dist/core/understanding/understand/service.d.ts +17 -0
- package/dist/core/understanding/understand/service.js +76 -0
- package/dist/core/understanding/understand/summary.d.ts +14 -0
- package/dist/core/understanding/understand/summary.js +39 -0
- package/dist/core/understanding/understand/types.d.ts +31 -0
- package/dist/core/understanding/understand/types.js +1 -0
- package/dist/discovery/files.js +7 -2
- package/dist/discovery/log-like.d.ts +2 -0
- package/dist/discovery/log-like.js +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/mcp/brain/compile.d.ts +9 -0
- package/dist/mcp/brain/compile.js +42 -0
- package/dist/mcp/brain/errors.d.ts +12 -0
- package/dist/mcp/brain/errors.js +23 -0
- package/dist/mcp/brain/index.d.ts +11 -0
- package/dist/mcp/brain/index.js +8 -0
- package/dist/mcp/brain/provenance.d.ts +29 -0
- package/dist/mcp/brain/provenance.js +59 -0
- package/dist/mcp/brain/schemas.d.ts +13 -0
- package/dist/mcp/brain/schemas.js +105 -0
- package/dist/mcp/brain/security/root.d.ts +6 -0
- package/dist/mcp/brain/security/root.js +66 -0
- package/dist/mcp/brain/server.d.ts +12 -0
- package/dist/mcp/brain/server.js +46 -0
- package/dist/mcp/brain/session.d.ts +33 -0
- package/dist/mcp/brain/session.js +117 -0
- package/dist/mcp/brain/tools/handlers.d.ts +13 -0
- package/dist/mcp/brain/tools/handlers.js +483 -0
- package/dist/mcp/brain/tools/registry.d.ts +9 -0
- package/dist/mcp/brain/tools/registry.js +191 -0
- package/dist/reporters/github/annotations.d.ts +8 -0
- package/dist/reporters/github/annotations.js +42 -0
- package/dist/reporters/github/emit.d.ts +23 -0
- package/dist/reporters/github/emit.js +31 -0
- package/dist/reporters/github/summary.d.ts +20 -0
- package/dist/reporters/github/summary.js +111 -0
- package/dist/reporters/terminal/report.d.ts +5 -0
- package/dist/reporters/terminal/report.js +68 -5
- package/dist/reporters/verify/terminal.d.ts +4 -0
- package/dist/reporters/verify/terminal.js +26 -0
- package/dist/utils/fs.d.ts +5 -0
- package/dist/utils/fs.js +34 -0
- package/dist/utils/path.d.ts +5 -0
- package/dist/utils/path.js +16 -0
- package/package.json +15 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { averageConfidence, clampModelConfidence, deepFreeze, stableModelId } from "./ids.js";
|
|
2
|
+
import { COMPILER_PASSES } from "./schema.js";
|
|
3
|
+
import { PROJECT_MODEL_SCHEMA_VERSION, UNDERSTANDING_COMPILER_VERSION } from "./version.js";
|
|
4
|
+
import { validateProjectModel } from "./validator.js";
|
|
5
|
+
function assertBuilderInput(input) {
|
|
6
|
+
if (!input.domains ||
|
|
7
|
+
!input.entrypoints ||
|
|
8
|
+
!input.dependencies ||
|
|
9
|
+
!input.relationships ||
|
|
10
|
+
!input.architectures) {
|
|
11
|
+
throw new Error("ProjectModelBuilder requires domains, entrypoints, dependencies, relationships, and architectures");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function countEvidence(lists) {
|
|
15
|
+
let total = 0;
|
|
16
|
+
for (const list of lists) {
|
|
17
|
+
total += list.length;
|
|
18
|
+
}
|
|
19
|
+
return total;
|
|
20
|
+
}
|
|
21
|
+
function buildSummary(options) {
|
|
22
|
+
const { domains, entrypoints, dependencies, relationships, architectures, input } = options;
|
|
23
|
+
const confidences = [
|
|
24
|
+
...domains.map((d) => d.confidence),
|
|
25
|
+
...entrypoints.map((e) => e.confidence),
|
|
26
|
+
...dependencies.map((d) => d.confidence),
|
|
27
|
+
...relationships.map((r) => r.confidence),
|
|
28
|
+
...architectures.map((a) => a.confidence),
|
|
29
|
+
];
|
|
30
|
+
const statistics = {
|
|
31
|
+
domainCount: domains.length,
|
|
32
|
+
entrypointCount: entrypoints.length,
|
|
33
|
+
dependencyCount: dependencies.length,
|
|
34
|
+
relationshipCount: relationships.length,
|
|
35
|
+
architectureCount: architectures.length,
|
|
36
|
+
evidenceCount: countEvidence([
|
|
37
|
+
...domains.map((d) => d.evidence),
|
|
38
|
+
...entrypoints.map((e) => e.evidence),
|
|
39
|
+
...dependencies.map((d) => d.evidence),
|
|
40
|
+
...relationships.map((r) => r.evidence),
|
|
41
|
+
...architectures.map((a) => a.evidence),
|
|
42
|
+
]),
|
|
43
|
+
averageConfidence: averageConfidence(confidences),
|
|
44
|
+
passTimingMs: {
|
|
45
|
+
domains: input.domains.timingMs,
|
|
46
|
+
entrypoints: input.entrypoints.timingMs,
|
|
47
|
+
dependencies: input.dependencies.timingMs,
|
|
48
|
+
relationships: input.relationships.timingMs,
|
|
49
|
+
architectures: input.architectures.timingMs,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
topDomains: domains
|
|
54
|
+
.slice()
|
|
55
|
+
.sort((a, b) => b.confidence - a.confidence || a.name.localeCompare(b.name))
|
|
56
|
+
.slice(0, 5)
|
|
57
|
+
.map((d) => d.name),
|
|
58
|
+
topArchitectures: architectures
|
|
59
|
+
.slice()
|
|
60
|
+
.sort((a, b) => b.confidence - a.confidence || a.pattern.localeCompare(b.pattern))
|
|
61
|
+
.slice(0, 5)
|
|
62
|
+
.map((a) => a.pattern),
|
|
63
|
+
frameworks: [...new Set(entrypoints.map((e) => e.framework))].sort((a, b) => a.localeCompare(b)),
|
|
64
|
+
statistics,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Builds a canonical immutable ProjectModel from compiler pass outputs.
|
|
69
|
+
*/
|
|
70
|
+
export class ProjectModelBuilder {
|
|
71
|
+
build(input) {
|
|
72
|
+
assertBuilderInput(input);
|
|
73
|
+
const generatedAt = input.generatedAt ?? new Date().toISOString();
|
|
74
|
+
const projectName = input.projectName?.trim() || "project";
|
|
75
|
+
const domains = input.domains.domains
|
|
76
|
+
.map((domain) => ({
|
|
77
|
+
id: stableModelId("domain-discovery", "domain", domain.name),
|
|
78
|
+
name: domain.name,
|
|
79
|
+
paths: [...domain.evidence].sort((a, b) => a.localeCompare(b)),
|
|
80
|
+
evidence: [...domain.evidence].sort((a, b) => a.localeCompare(b)),
|
|
81
|
+
confidence: clampModelConfidence(domain.confidence),
|
|
82
|
+
sourcePass: "domain-discovery",
|
|
83
|
+
timestamp: generatedAt,
|
|
84
|
+
}))
|
|
85
|
+
.sort((a, b) => b.confidence - a.confidence || a.name.localeCompare(b.name));
|
|
86
|
+
const entrypoints = input.entrypoints.entrypoints
|
|
87
|
+
.map((entry) => ({
|
|
88
|
+
id: stableModelId("entrypoint-discovery", "entrypoint", `${entry.framework}:${entry.file}`),
|
|
89
|
+
framework: entry.framework,
|
|
90
|
+
file: entry.file,
|
|
91
|
+
evidence: [...entry.evidence].sort((a, b) => a.localeCompare(b)),
|
|
92
|
+
confidence: clampModelConfidence(entry.confidence),
|
|
93
|
+
sourcePass: "entrypoint-discovery",
|
|
94
|
+
timestamp: generatedAt,
|
|
95
|
+
}))
|
|
96
|
+
.sort((a, b) => b.confidence - a.confidence ||
|
|
97
|
+
a.framework.localeCompare(b.framework) ||
|
|
98
|
+
a.file.localeCompare(b.file));
|
|
99
|
+
const dependencies = input.dependencies.dependencies
|
|
100
|
+
.map((dep) => ({
|
|
101
|
+
id: stableModelId("dependency-discovery", "dependency", `${dep.from}->${dep.to}:${dep.type}`),
|
|
102
|
+
from: dep.from,
|
|
103
|
+
to: dep.to,
|
|
104
|
+
type: dep.type,
|
|
105
|
+
evidence: [...dep.evidence].sort((a, b) => a.localeCompare(b)),
|
|
106
|
+
confidence: clampModelConfidence(dep.confidence),
|
|
107
|
+
sourcePass: "dependency-discovery",
|
|
108
|
+
timestamp: generatedAt,
|
|
109
|
+
}))
|
|
110
|
+
.sort((a, b) => b.confidence - a.confidence ||
|
|
111
|
+
a.from.localeCompare(b.from) ||
|
|
112
|
+
a.to.localeCompare(b.to) ||
|
|
113
|
+
a.type.localeCompare(b.type));
|
|
114
|
+
const relationships = input.relationships.relationships
|
|
115
|
+
.map((rel) => ({
|
|
116
|
+
id: stableModelId("relationship-discovery", "relationship", `${rel.source}->${rel.target}:${rel.relationship}`),
|
|
117
|
+
source: rel.source,
|
|
118
|
+
target: rel.target,
|
|
119
|
+
relationship: rel.relationship,
|
|
120
|
+
strength: rel.strength,
|
|
121
|
+
bidirectional: Boolean(rel.bidirectional),
|
|
122
|
+
evidence: [...rel.evidence].sort((a, b) => a.localeCompare(b)),
|
|
123
|
+
confidence: clampModelConfidence(rel.confidence),
|
|
124
|
+
sourcePass: "relationship-discovery",
|
|
125
|
+
timestamp: generatedAt,
|
|
126
|
+
}))
|
|
127
|
+
.sort((a, b) => b.confidence - a.confidence ||
|
|
128
|
+
a.relationship.localeCompare(b.relationship) ||
|
|
129
|
+
a.source.localeCompare(b.source) ||
|
|
130
|
+
a.target.localeCompare(b.target));
|
|
131
|
+
const architectures = input.architectures.architectures
|
|
132
|
+
.map((arch) => ({
|
|
133
|
+
id: stableModelId("architecture-inference", "architecture", arch.pattern),
|
|
134
|
+
pattern: arch.pattern,
|
|
135
|
+
matchedRules: [...arch.matchedRules].sort((a, b) => a.localeCompare(b)),
|
|
136
|
+
conflictingEvidence: [...arch.conflictingEvidence].sort((a, b) => a.localeCompare(b)),
|
|
137
|
+
unknowns: [...arch.unknowns].sort((a, b) => a.localeCompare(b)),
|
|
138
|
+
evidence: [...arch.evidence].sort((a, b) => a.localeCompare(b)),
|
|
139
|
+
confidence: clampModelConfidence(arch.confidence),
|
|
140
|
+
sourcePass: "architecture-inference",
|
|
141
|
+
timestamp: generatedAt,
|
|
142
|
+
}))
|
|
143
|
+
.sort((a, b) => b.confidence - a.confidence || a.pattern.localeCompare(b.pattern));
|
|
144
|
+
const projectEvidence = [
|
|
145
|
+
`domains:${domains.length}`,
|
|
146
|
+
`entrypoints:${entrypoints.length}`,
|
|
147
|
+
`dependencies:${dependencies.length}`,
|
|
148
|
+
`relationships:${relationships.length}`,
|
|
149
|
+
`architectures:${architectures.length}`,
|
|
150
|
+
];
|
|
151
|
+
const project = {
|
|
152
|
+
id: stableModelId("project-model", "project", projectName),
|
|
153
|
+
name: projectName,
|
|
154
|
+
evidence: projectEvidence,
|
|
155
|
+
confidence: averageConfidence([
|
|
156
|
+
...domains.map((d) => d.confidence),
|
|
157
|
+
...entrypoints.map((e) => e.confidence),
|
|
158
|
+
...dependencies.map((d) => d.confidence),
|
|
159
|
+
...relationships.map((r) => r.confidence),
|
|
160
|
+
...architectures.map((a) => a.confidence),
|
|
161
|
+
]),
|
|
162
|
+
sourcePass: "project-model",
|
|
163
|
+
timestamp: generatedAt,
|
|
164
|
+
};
|
|
165
|
+
const metadata = {
|
|
166
|
+
id: stableModelId("project-model", "metadata", `${projectName}:${PROJECT_MODEL_SCHEMA_VERSION}`),
|
|
167
|
+
schemaVersion: PROJECT_MODEL_SCHEMA_VERSION,
|
|
168
|
+
project,
|
|
169
|
+
evidence: [`schema:${PROJECT_MODEL_SCHEMA_VERSION}`, ...projectEvidence],
|
|
170
|
+
confidence: project.confidence,
|
|
171
|
+
sourcePass: "project-model",
|
|
172
|
+
timestamp: generatedAt,
|
|
173
|
+
};
|
|
174
|
+
const compilerMetadata = {
|
|
175
|
+
compilerVersion: UNDERSTANDING_COMPILER_VERSION,
|
|
176
|
+
schemaVersion: PROJECT_MODEL_SCHEMA_VERSION,
|
|
177
|
+
generatedAt,
|
|
178
|
+
passes: COMPILER_PASSES,
|
|
179
|
+
};
|
|
180
|
+
const summary = buildSummary({
|
|
181
|
+
domains,
|
|
182
|
+
entrypoints,
|
|
183
|
+
dependencies,
|
|
184
|
+
relationships,
|
|
185
|
+
architectures,
|
|
186
|
+
input,
|
|
187
|
+
});
|
|
188
|
+
const model = deepFreeze({
|
|
189
|
+
metadata,
|
|
190
|
+
domains,
|
|
191
|
+
entrypoints,
|
|
192
|
+
dependencies,
|
|
193
|
+
relationships,
|
|
194
|
+
architectures,
|
|
195
|
+
summary,
|
|
196
|
+
compilerMetadata,
|
|
197
|
+
});
|
|
198
|
+
const validation = validateProjectModel(model);
|
|
199
|
+
if (!validation.ok) {
|
|
200
|
+
const detail = validation.issues.map((i) => `${i.path}: ${i.message}`).join("; ");
|
|
201
|
+
throw new Error(`ProjectModel validation failed: ${detail}`);
|
|
202
|
+
}
|
|
203
|
+
return model;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** Convenience function wrapping ProjectModelBuilder. */
|
|
207
|
+
export function buildProjectModel(input) {
|
|
208
|
+
return new ProjectModelBuilder().build(input);
|
|
209
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CompilerPassId } from "./types.js";
|
|
2
|
+
/** Deterministic short stable id from pass + kind + natural key. */
|
|
3
|
+
export declare function stableModelId(pass: CompilerPassId, kind: string, key: string): string;
|
|
4
|
+
export declare function clampModelConfidence(value: number): number;
|
|
5
|
+
export declare function averageConfidence(values: readonly number[]): number;
|
|
6
|
+
export declare function deepFreeze<T>(value: T): T;
|
|
7
|
+
export declare function isIsoTimestamp(value: string): boolean;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/** Deterministic short stable id from pass + kind + natural key. */
|
|
3
|
+
export function stableModelId(pass, kind, key) {
|
|
4
|
+
const digest = createHash("sha256")
|
|
5
|
+
.update(pass)
|
|
6
|
+
.update("\0")
|
|
7
|
+
.update(kind)
|
|
8
|
+
.update("\0")
|
|
9
|
+
.update(key)
|
|
10
|
+
.digest("hex")
|
|
11
|
+
.slice(0, 16);
|
|
12
|
+
return `${kind}_${digest}`;
|
|
13
|
+
}
|
|
14
|
+
export function clampModelConfidence(value) {
|
|
15
|
+
if (Number.isNaN(value) || value < 0) {
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
if (value > 1) {
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
return Math.round(value * 100) / 100;
|
|
22
|
+
}
|
|
23
|
+
export function averageConfidence(values) {
|
|
24
|
+
if (values.length === 0) {
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
const sum = values.reduce((acc, value) => acc + value, 0);
|
|
28
|
+
return clampModelConfidence(sum / values.length);
|
|
29
|
+
}
|
|
30
|
+
export function deepFreeze(value) {
|
|
31
|
+
if (value === null || typeof value !== "object") {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
if (Object.isFrozen(value)) {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
for (const child of Object.values(value)) {
|
|
38
|
+
deepFreeze(child);
|
|
39
|
+
}
|
|
40
|
+
return Object.freeze(value);
|
|
41
|
+
}
|
|
42
|
+
export function isIsoTimestamp(value) {
|
|
43
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(value)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const parsed = Date.parse(value);
|
|
47
|
+
return !Number.isNaN(parsed);
|
|
48
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ProjectModelBuilder, buildProjectModel } from "./builder.js";
|
|
2
|
+
export { serializeProjectModel, parseProjectModel } from "./serializer.js";
|
|
3
|
+
export { validateProjectModel } from "./validator.js";
|
|
4
|
+
export { PROJECT_MODEL_SCHEMA } from "./schema.js";
|
|
5
|
+
export { PROJECT_MODEL_SCHEMA_VERSION, UNDERSTANDING_COMPILER_VERSION } from "./version.js";
|
|
6
|
+
export { stableModelId, clampModelConfidence } from "./ids.js";
|
|
7
|
+
export type { CompilerMetadata, CompilerPassId, ProjectArchitecture, ProjectDependency, ProjectDomain, ProjectEntrypoint, ProjectModel, ProjectModelBuilderInput, ProjectModelValidationResult, ProjectRelationship, ProjectSummary, } from "./types.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ProjectModelBuilder, buildProjectModel } from "./builder.js";
|
|
2
|
+
export { serializeProjectModel, parseProjectModel } from "./serializer.js";
|
|
3
|
+
export { validateProjectModel } from "./validator.js";
|
|
4
|
+
export { PROJECT_MODEL_SCHEMA } from "./schema.js";
|
|
5
|
+
export { PROJECT_MODEL_SCHEMA_VERSION, UNDERSTANDING_COMPILER_VERSION } from "./version.js";
|
|
6
|
+
export { stableModelId, clampModelConfidence } from "./ids.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CompilerPassId } from "./types.js";
|
|
2
|
+
export declare const PROJECT_MODEL_REQUIRED_ROOT_KEYS: readonly ["metadata", "domains", "entrypoints", "dependencies", "relationships", "architectures", "summary", "compilerMetadata"];
|
|
3
|
+
export declare const MODEL_PROVENANCE_KEYS: readonly ["id", "evidence", "confidence", "sourcePass", "timestamp"];
|
|
4
|
+
export declare const COMPILER_PASSES: readonly CompilerPassId[];
|
|
5
|
+
export declare const PROJECT_MODEL_SCHEMA: {
|
|
6
|
+
readonly schemaVersion: "1.0.0";
|
|
7
|
+
readonly compilerVersion: "0.1.0";
|
|
8
|
+
readonly requiredRootKeys: readonly ["metadata", "domains", "entrypoints", "dependencies", "relationships", "architectures", "summary", "compilerMetadata"];
|
|
9
|
+
readonly provenanceKeys: readonly ["id", "evidence", "confidence", "sourcePass", "timestamp"];
|
|
10
|
+
readonly passes: readonly CompilerPassId[];
|
|
11
|
+
};
|
|
12
|
+
export type ProjectModelSchema = typeof PROJECT_MODEL_SCHEMA;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { PROJECT_MODEL_SCHEMA_VERSION, UNDERSTANDING_COMPILER_VERSION } from "./version.js";
|
|
2
|
+
export const PROJECT_MODEL_REQUIRED_ROOT_KEYS = [
|
|
3
|
+
"metadata",
|
|
4
|
+
"domains",
|
|
5
|
+
"entrypoints",
|
|
6
|
+
"dependencies",
|
|
7
|
+
"relationships",
|
|
8
|
+
"architectures",
|
|
9
|
+
"summary",
|
|
10
|
+
"compilerMetadata",
|
|
11
|
+
];
|
|
12
|
+
export const MODEL_PROVENANCE_KEYS = [
|
|
13
|
+
"id",
|
|
14
|
+
"evidence",
|
|
15
|
+
"confidence",
|
|
16
|
+
"sourcePass",
|
|
17
|
+
"timestamp",
|
|
18
|
+
];
|
|
19
|
+
export const COMPILER_PASSES = [
|
|
20
|
+
"domain-discovery",
|
|
21
|
+
"entrypoint-discovery",
|
|
22
|
+
"dependency-discovery",
|
|
23
|
+
"relationship-discovery",
|
|
24
|
+
"architecture-inference",
|
|
25
|
+
"project-model",
|
|
26
|
+
];
|
|
27
|
+
export const PROJECT_MODEL_SCHEMA = {
|
|
28
|
+
schemaVersion: PROJECT_MODEL_SCHEMA_VERSION,
|
|
29
|
+
compilerVersion: UNDERSTANDING_COMPILER_VERSION,
|
|
30
|
+
requiredRootKeys: PROJECT_MODEL_REQUIRED_ROOT_KEYS,
|
|
31
|
+
provenanceKeys: MODEL_PROVENANCE_KEYS,
|
|
32
|
+
passes: COMPILER_PASSES,
|
|
33
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProjectModel } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Serialize a ProjectModel to deterministic JSON (sorted keys, 2-space indent).
|
|
4
|
+
*/
|
|
5
|
+
export declare function serializeProjectModel(model: ProjectModel): string;
|
|
6
|
+
/**
|
|
7
|
+
* Parse JSON into a ProjectModel and validate schema/provenance.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseProjectModel(json: string): ProjectModel;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { validateProjectModel } from "./validator.js";
|
|
2
|
+
function sortKeysDeep(value) {
|
|
3
|
+
if (Array.isArray(value)) {
|
|
4
|
+
return value.map(sortKeysDeep);
|
|
5
|
+
}
|
|
6
|
+
if (value && typeof value === "object") {
|
|
7
|
+
const record = value;
|
|
8
|
+
const sorted = {};
|
|
9
|
+
for (const key of Object.keys(record).sort((a, b) => a.localeCompare(b))) {
|
|
10
|
+
sorted[key] = sortKeysDeep(record[key]);
|
|
11
|
+
}
|
|
12
|
+
return sorted;
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Serialize a ProjectModel to deterministic JSON (sorted keys, 2-space indent).
|
|
18
|
+
*/
|
|
19
|
+
export function serializeProjectModel(model) {
|
|
20
|
+
const validation = validateProjectModel(model);
|
|
21
|
+
if (!validation.ok) {
|
|
22
|
+
const detail = validation.issues.map((i) => `${i.path}: ${i.message}`).join("; ");
|
|
23
|
+
throw new Error(`Cannot serialize invalid ProjectModel: ${detail}`);
|
|
24
|
+
}
|
|
25
|
+
return `${JSON.stringify(sortKeysDeep(model), null, 2)}\n`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse JSON into a ProjectModel and validate schema/provenance.
|
|
29
|
+
*/
|
|
30
|
+
export function parseProjectModel(json) {
|
|
31
|
+
let parsed;
|
|
32
|
+
try {
|
|
33
|
+
parsed = JSON.parse(json);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const message = error instanceof Error ? error.message : "invalid JSON";
|
|
37
|
+
throw new Error(`ProjectModel parse failed: ${message}`);
|
|
38
|
+
}
|
|
39
|
+
const model = parsed;
|
|
40
|
+
const validation = validateProjectModel(model);
|
|
41
|
+
if (!validation.ok) {
|
|
42
|
+
const detail = validation.issues.map((i) => `${i.path}: ${i.message}`).join("; ");
|
|
43
|
+
throw new Error(`ProjectModel parse validation failed: ${detail}`);
|
|
44
|
+
}
|
|
45
|
+
return model;
|
|
46
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { ArchitectureInferenceResult, ArchitecturePattern } from "../architecture/types.js";
|
|
2
|
+
import type { DependencyDiscoveryResult, DependencyRelationType } from "../dependencies/types.js";
|
|
3
|
+
import type { EntrypointDiscoveryResult, EntrypointFramework } from "../entrypoints/types.js";
|
|
4
|
+
import type { RelationshipDiscoveryResult, RelationshipKind, RelationshipStrength } from "../relationships/types.js";
|
|
5
|
+
import type { DomainDiscoveryResult } from "../types/index.js";
|
|
6
|
+
import type { ProjectModelSchemaVersion, UnderstandingCompilerVersion } from "./version.js";
|
|
7
|
+
export type CompilerPassId = "domain-discovery" | "entrypoint-discovery" | "dependency-discovery" | "relationship-discovery" | "architecture-inference" | "project-model";
|
|
8
|
+
/**
|
|
9
|
+
* Every modeled object carries provenance required by the compiler contract.
|
|
10
|
+
*/
|
|
11
|
+
export interface ModelProvenance {
|
|
12
|
+
id: string;
|
|
13
|
+
evidence: readonly string[];
|
|
14
|
+
confidence: number;
|
|
15
|
+
sourcePass: CompilerPassId;
|
|
16
|
+
timestamp: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ProjectIdentity extends ModelProvenance {
|
|
19
|
+
name: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ProjectDomain extends ModelProvenance {
|
|
22
|
+
name: string;
|
|
23
|
+
paths: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
export interface ProjectEntrypoint extends ModelProvenance {
|
|
26
|
+
framework: EntrypointFramework;
|
|
27
|
+
file: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ProjectDependency extends ModelProvenance {
|
|
30
|
+
from: string;
|
|
31
|
+
to: string;
|
|
32
|
+
type: DependencyRelationType;
|
|
33
|
+
}
|
|
34
|
+
export interface ProjectRelationship extends ModelProvenance {
|
|
35
|
+
source: string;
|
|
36
|
+
target: string;
|
|
37
|
+
relationship: RelationshipKind;
|
|
38
|
+
strength: RelationshipStrength;
|
|
39
|
+
bidirectional: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface ProjectArchitecture extends ModelProvenance {
|
|
42
|
+
pattern: ArchitecturePattern;
|
|
43
|
+
matchedRules: readonly string[];
|
|
44
|
+
conflictingEvidence: readonly string[];
|
|
45
|
+
unknowns: readonly string[];
|
|
46
|
+
}
|
|
47
|
+
export interface ProjectStatistics {
|
|
48
|
+
domainCount: number;
|
|
49
|
+
entrypointCount: number;
|
|
50
|
+
dependencyCount: number;
|
|
51
|
+
relationshipCount: number;
|
|
52
|
+
architectureCount: number;
|
|
53
|
+
evidenceCount: number;
|
|
54
|
+
averageConfidence: number;
|
|
55
|
+
passTimingMs: {
|
|
56
|
+
domains: number;
|
|
57
|
+
entrypoints: number;
|
|
58
|
+
dependencies: number;
|
|
59
|
+
relationships: number;
|
|
60
|
+
architectures: number;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export interface ProjectSummary {
|
|
64
|
+
topDomains: readonly string[];
|
|
65
|
+
topArchitectures: readonly string[];
|
|
66
|
+
frameworks: readonly string[];
|
|
67
|
+
statistics: ProjectStatistics;
|
|
68
|
+
}
|
|
69
|
+
export interface ProjectMetadata extends ModelProvenance {
|
|
70
|
+
schemaVersion: ProjectModelSchemaVersion;
|
|
71
|
+
project: ProjectIdentity;
|
|
72
|
+
}
|
|
73
|
+
export interface CompilerMetadata {
|
|
74
|
+
compilerVersion: UnderstandingCompilerVersion;
|
|
75
|
+
schemaVersion: ProjectModelSchemaVersion;
|
|
76
|
+
generatedAt: string;
|
|
77
|
+
passes: readonly CompilerPassId[];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Canonical immutable output of the Software Understanding Compiler.
|
|
81
|
+
*/
|
|
82
|
+
export interface ProjectModel {
|
|
83
|
+
metadata: ProjectMetadata;
|
|
84
|
+
domains: readonly ProjectDomain[];
|
|
85
|
+
entrypoints: readonly ProjectEntrypoint[];
|
|
86
|
+
dependencies: readonly ProjectDependency[];
|
|
87
|
+
relationships: readonly ProjectRelationship[];
|
|
88
|
+
architectures: readonly ProjectArchitecture[];
|
|
89
|
+
summary: ProjectSummary;
|
|
90
|
+
compilerMetadata: CompilerMetadata;
|
|
91
|
+
}
|
|
92
|
+
export interface ProjectModelBuilderInput {
|
|
93
|
+
domains: DomainDiscoveryResult;
|
|
94
|
+
entrypoints: EntrypointDiscoveryResult;
|
|
95
|
+
dependencies: DependencyDiscoveryResult;
|
|
96
|
+
relationships: RelationshipDiscoveryResult;
|
|
97
|
+
architectures: ArchitectureInferenceResult;
|
|
98
|
+
/** Optional display name; defaults to "project". */
|
|
99
|
+
projectName?: string;
|
|
100
|
+
/** Fixed ISO timestamp for deterministic builds (tests). */
|
|
101
|
+
generatedAt?: string;
|
|
102
|
+
}
|
|
103
|
+
export interface ProjectModelValidationIssue {
|
|
104
|
+
path: string;
|
|
105
|
+
message: string;
|
|
106
|
+
}
|
|
107
|
+
export interface ProjectModelValidationResult {
|
|
108
|
+
ok: boolean;
|
|
109
|
+
issues: readonly ProjectModelValidationIssue[];
|
|
110
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { COMPILER_PASSES, MODEL_PROVENANCE_KEYS, PROJECT_MODEL_REQUIRED_ROOT_KEYS, } from "./schema.js";
|
|
2
|
+
import { isIsoTimestamp } from "./ids.js";
|
|
3
|
+
import { PROJECT_MODEL_SCHEMA_VERSION, UNDERSTANDING_COMPILER_VERSION } from "./version.js";
|
|
4
|
+
function issue(path, message) {
|
|
5
|
+
return { path, message };
|
|
6
|
+
}
|
|
7
|
+
function validateProvenance(path, value, issues) {
|
|
8
|
+
const before = issues.length;
|
|
9
|
+
if (!value || typeof value !== "object") {
|
|
10
|
+
issues.push(issue(path, "expected provenance object"));
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const record = value;
|
|
14
|
+
for (const key of MODEL_PROVENANCE_KEYS) {
|
|
15
|
+
if (!(key in record)) {
|
|
16
|
+
issues.push(issue(`${path}.${key}`, "missing required provenance field"));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (typeof record.id !== "string" || record.id.length === 0) {
|
|
20
|
+
issues.push(issue(`${path}.id`, "id must be a non-empty string"));
|
|
21
|
+
}
|
|
22
|
+
if (!Array.isArray(record.evidence) || record.evidence.some((e) => typeof e !== "string")) {
|
|
23
|
+
issues.push(issue(`${path}.evidence`, "evidence must be string[]"));
|
|
24
|
+
}
|
|
25
|
+
else if (record.evidence.length === 0) {
|
|
26
|
+
issues.push(issue(`${path}.evidence`, "evidence must not be empty"));
|
|
27
|
+
}
|
|
28
|
+
if (typeof record.confidence !== "number" || record.confidence < 0 || record.confidence > 1) {
|
|
29
|
+
issues.push(issue(`${path}.confidence`, "confidence must be a number in [0,1]"));
|
|
30
|
+
}
|
|
31
|
+
if (typeof record.sourcePass !== "string" ||
|
|
32
|
+
!COMPILER_PASSES.includes(record.sourcePass)) {
|
|
33
|
+
issues.push(issue(`${path}.sourcePass`, "sourcePass must be a known compiler pass"));
|
|
34
|
+
}
|
|
35
|
+
if (typeof record.timestamp !== "string" || !isIsoTimestamp(record.timestamp)) {
|
|
36
|
+
issues.push(issue(`${path}.timestamp`, "timestamp must be an ISO-8601 UTC string"));
|
|
37
|
+
}
|
|
38
|
+
return issues.length === before;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Validate a ProjectModel against the versioned schema contract.
|
|
42
|
+
*/
|
|
43
|
+
export function validateProjectModel(model) {
|
|
44
|
+
const issues = [];
|
|
45
|
+
if (!model || typeof model !== "object") {
|
|
46
|
+
return { ok: false, issues: [issue("$", "model must be an object")] };
|
|
47
|
+
}
|
|
48
|
+
for (const key of PROJECT_MODEL_REQUIRED_ROOT_KEYS) {
|
|
49
|
+
if (!(key in model)) {
|
|
50
|
+
issues.push(issue(key, "missing required root key"));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
validateProvenance("metadata", model.metadata, issues);
|
|
54
|
+
if (model.metadata) {
|
|
55
|
+
if (model.metadata.schemaVersion !== PROJECT_MODEL_SCHEMA_VERSION) {
|
|
56
|
+
issues.push(issue("metadata.schemaVersion", `expected ${PROJECT_MODEL_SCHEMA_VERSION}, got ${String(model.metadata.schemaVersion)}`));
|
|
57
|
+
}
|
|
58
|
+
validateProvenance("metadata.project", model.metadata.project, issues);
|
|
59
|
+
if (model.metadata.project && typeof model.metadata.project.name !== "string") {
|
|
60
|
+
issues.push(issue("metadata.project.name", "project name must be a string"));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const collections = [
|
|
64
|
+
{ path: "domains", items: model.domains },
|
|
65
|
+
{ path: "entrypoints", items: model.entrypoints },
|
|
66
|
+
{ path: "dependencies", items: model.dependencies },
|
|
67
|
+
{ path: "relationships", items: model.relationships },
|
|
68
|
+
{ path: "architectures", items: model.architectures },
|
|
69
|
+
];
|
|
70
|
+
for (const collection of collections) {
|
|
71
|
+
if (!Array.isArray(collection.items)) {
|
|
72
|
+
issues.push(issue(collection.path, "must be an array"));
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
collection.items.forEach((item, index) => {
|
|
76
|
+
validateProvenance(`${collection.path}[${index}]`, item, issues);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (!model.summary || typeof model.summary !== "object") {
|
|
80
|
+
issues.push(issue("summary", "summary must be an object"));
|
|
81
|
+
}
|
|
82
|
+
else if (!model.summary.statistics || typeof model.summary.statistics !== "object") {
|
|
83
|
+
issues.push(issue("summary.statistics", "statistics must be an object"));
|
|
84
|
+
}
|
|
85
|
+
if (!model.compilerMetadata || typeof model.compilerMetadata !== "object") {
|
|
86
|
+
issues.push(issue("compilerMetadata", "compilerMetadata must be an object"));
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
if (model.compilerMetadata.compilerVersion !== UNDERSTANDING_COMPILER_VERSION) {
|
|
90
|
+
issues.push(issue("compilerMetadata.compilerVersion", `expected ${UNDERSTANDING_COMPILER_VERSION}`));
|
|
91
|
+
}
|
|
92
|
+
if (model.compilerMetadata.schemaVersion !== PROJECT_MODEL_SCHEMA_VERSION) {
|
|
93
|
+
issues.push(issue("compilerMetadata.schemaVersion", `expected ${PROJECT_MODEL_SCHEMA_VERSION}`));
|
|
94
|
+
}
|
|
95
|
+
if (typeof model.compilerMetadata.generatedAt !== "string" ||
|
|
96
|
+
!isIsoTimestamp(model.compilerMetadata.generatedAt)) {
|
|
97
|
+
issues.push(issue("compilerMetadata.generatedAt", "must be ISO-8601 UTC"));
|
|
98
|
+
}
|
|
99
|
+
if (!Array.isArray(model.compilerMetadata.passes)) {
|
|
100
|
+
issues.push(issue("compilerMetadata.passes", "passes must be an array"));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Stable id uniqueness across all provenance-bearing objects
|
|
104
|
+
const ids = new Map();
|
|
105
|
+
const track = (path, id) => {
|
|
106
|
+
const existing = ids.get(id);
|
|
107
|
+
if (existing) {
|
|
108
|
+
issues.push(issue(path, `duplicate id ${id} (also at ${existing})`));
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
ids.set(id, path);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
if (model.metadata?.id) {
|
|
115
|
+
track("metadata", model.metadata.id);
|
|
116
|
+
}
|
|
117
|
+
if (model.metadata?.project?.id) {
|
|
118
|
+
track("metadata.project", model.metadata.project.id);
|
|
119
|
+
}
|
|
120
|
+
model.domains?.forEach((d, i) => track(`domains[${i}]`, d.id));
|
|
121
|
+
model.entrypoints?.forEach((e, i) => track(`entrypoints[${i}]`, e.id));
|
|
122
|
+
model.dependencies?.forEach((d, i) => track(`dependencies[${i}]`, d.id));
|
|
123
|
+
model.relationships?.forEach((r, i) => track(`relationships[${i}]`, r.id));
|
|
124
|
+
model.architectures?.forEach((a, i) => track(`architectures[${i}]`, a.id));
|
|
125
|
+
return { ok: issues.length === 0, issues };
|
|
126
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Semver of the Project Model JSON schema shape. */
|
|
2
|
+
export declare const PROJECT_MODEL_SCHEMA_VERSION: "1.0.0";
|
|
3
|
+
/**
|
|
4
|
+
* Internal understanding compiler version that produced the model.
|
|
5
|
+
* Independent of the published AgentDoctor package version.
|
|
6
|
+
*/
|
|
7
|
+
export declare const UNDERSTANDING_COMPILER_VERSION: "0.1.0";
|
|
8
|
+
export type ProjectModelSchemaVersion = typeof PROJECT_MODEL_SCHEMA_VERSION;
|
|
9
|
+
export type UnderstandingCompilerVersion = typeof UNDERSTANDING_COMPILER_VERSION;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Semver of the Project Model JSON schema shape. */
|
|
2
|
+
export const PROJECT_MODEL_SCHEMA_VERSION = "1.0.0";
|
|
3
|
+
/**
|
|
4
|
+
* Internal understanding compiler version that produced the model.
|
|
5
|
+
* Independent of the published AgentDoctor package version.
|
|
6
|
+
*/
|
|
7
|
+
export const UNDERSTANDING_COMPILER_VERSION = "0.1.0";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { OwnershipDiscoveryOptions, OwnershipDiscoveryResult, OwnershipMatch } from "./types.js";
|
|
2
|
+
export declare function scoreOwnershipConfidence(source: OwnershipMatch["source"]): number;
|
|
3
|
+
/** Match a repo-relative path against a CODEOWNERS pattern (subset of GitHub rules). */
|
|
4
|
+
export declare function matchCodeownersPattern(filePath: string, pattern: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Discover ownership from explicit repository evidence only.
|
|
7
|
+
* Sources: CODEOWNERS, MAINTAINERS, package.json author/maintainers.
|
|
8
|
+
* No git-blame guessing.
|
|
9
|
+
*/
|
|
10
|
+
export declare function discoverOwnership(options?: OwnershipDiscoveryOptions): Promise<OwnershipDiscoveryResult>;
|
|
11
|
+
/** Resolve owners for a path using last-matching CODEOWNERS rule semantics when possible. */
|
|
12
|
+
export declare function ownersForPath(filePath: string, ownerships: readonly OwnershipMatch[]): OwnershipMatch | null;
|