@praneeth_54/agentdoctor 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -5
- package/README.md +462 -251
- package/dist/cli/commands/brain-mcp.d.ts +9 -0
- package/dist/cli/commands/brain-mcp.js +38 -0
- package/dist/cli/program.js +13 -0
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- 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/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/package.json +11 -4
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { QueryValidationError } from "./errors.js";
|
|
2
|
+
function requireNonEmptyString(value, field) {
|
|
3
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
4
|
+
throw new QueryValidationError(`${field} must be a non-empty string`);
|
|
5
|
+
}
|
|
6
|
+
return value.trim();
|
|
7
|
+
}
|
|
8
|
+
function optionalString(value, field) {
|
|
9
|
+
if (value === undefined) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
return requireNonEmptyString(value, field);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Validate and normalize a raw query object into a typed Query.
|
|
16
|
+
*/
|
|
17
|
+
export function validateQuery(input) {
|
|
18
|
+
if (!input || typeof input !== "object") {
|
|
19
|
+
throw new QueryValidationError("query must be an object");
|
|
20
|
+
}
|
|
21
|
+
const record = input;
|
|
22
|
+
if (typeof record.type !== "string") {
|
|
23
|
+
throw new QueryValidationError("query.type is required");
|
|
24
|
+
}
|
|
25
|
+
switch (record.type) {
|
|
26
|
+
case "RepositorySummary":
|
|
27
|
+
return { type: "RepositorySummary" };
|
|
28
|
+
case "ListDomains":
|
|
29
|
+
return { type: "ListDomains" };
|
|
30
|
+
case "ListEntrypoints":
|
|
31
|
+
return { type: "ListEntrypoints" };
|
|
32
|
+
case "ListArchitectures":
|
|
33
|
+
return { type: "ListArchitectures" };
|
|
34
|
+
case "ListRelationships":
|
|
35
|
+
return { type: "ListRelationships" };
|
|
36
|
+
case "ListDependencies":
|
|
37
|
+
return { type: "ListDependencies" };
|
|
38
|
+
case "Statistics":
|
|
39
|
+
return { type: "Statistics" };
|
|
40
|
+
case "FindDomain":
|
|
41
|
+
return { type: "FindDomain", name: requireNonEmptyString(record.name, "name") };
|
|
42
|
+
case "FindEntrypoint": {
|
|
43
|
+
const file = optionalString(record.file, "file");
|
|
44
|
+
const framework = optionalString(record.framework, "framework");
|
|
45
|
+
const id = optionalString(record.id, "id");
|
|
46
|
+
if (!file && !framework && !id) {
|
|
47
|
+
throw new QueryValidationError("FindEntrypoint requires file, framework, or id");
|
|
48
|
+
}
|
|
49
|
+
const query = { type: "FindEntrypoint" };
|
|
50
|
+
if (file !== undefined) {
|
|
51
|
+
query.file = file;
|
|
52
|
+
}
|
|
53
|
+
if (framework !== undefined) {
|
|
54
|
+
query.framework = framework;
|
|
55
|
+
}
|
|
56
|
+
if (id !== undefined) {
|
|
57
|
+
query.id = id;
|
|
58
|
+
}
|
|
59
|
+
return query;
|
|
60
|
+
}
|
|
61
|
+
case "FindArchitecture":
|
|
62
|
+
return {
|
|
63
|
+
type: "FindArchitecture",
|
|
64
|
+
pattern: requireNonEmptyString(record.pattern, "pattern"),
|
|
65
|
+
};
|
|
66
|
+
case "FindComponent":
|
|
67
|
+
return { type: "FindComponent", name: requireNonEmptyString(record.name, "name") };
|
|
68
|
+
case "FindRelationship": {
|
|
69
|
+
const source = optionalString(record.source, "source");
|
|
70
|
+
const target = optionalString(record.target, "target");
|
|
71
|
+
const relationship = optionalString(record.relationship, "relationship");
|
|
72
|
+
const id = optionalString(record.id, "id");
|
|
73
|
+
if (!source && !target && !relationship && !id) {
|
|
74
|
+
throw new QueryValidationError("FindRelationship requires source, target, relationship, or id");
|
|
75
|
+
}
|
|
76
|
+
const query = { type: "FindRelationship" };
|
|
77
|
+
if (source !== undefined) {
|
|
78
|
+
query.source = source;
|
|
79
|
+
}
|
|
80
|
+
if (target !== undefined) {
|
|
81
|
+
query.target = target;
|
|
82
|
+
}
|
|
83
|
+
if (relationship !== undefined) {
|
|
84
|
+
query.relationship = relationship;
|
|
85
|
+
}
|
|
86
|
+
if (id !== undefined) {
|
|
87
|
+
query.id = id;
|
|
88
|
+
}
|
|
89
|
+
return query;
|
|
90
|
+
}
|
|
91
|
+
case "FindDependency": {
|
|
92
|
+
const from = optionalString(record.from, "from");
|
|
93
|
+
const to = optionalString(record.to, "to");
|
|
94
|
+
const dependencyType = optionalString(record.dependencyType, "dependencyType");
|
|
95
|
+
const id = optionalString(record.id, "id");
|
|
96
|
+
if (!from && !to && !dependencyType && !id) {
|
|
97
|
+
throw new QueryValidationError("FindDependency requires from, to, dependencyType, or id");
|
|
98
|
+
}
|
|
99
|
+
const query = { type: "FindDependency" };
|
|
100
|
+
if (from !== undefined) {
|
|
101
|
+
query.from = from;
|
|
102
|
+
}
|
|
103
|
+
if (to !== undefined) {
|
|
104
|
+
query.to = to;
|
|
105
|
+
}
|
|
106
|
+
if (dependencyType !== undefined) {
|
|
107
|
+
query.dependencyType = dependencyType;
|
|
108
|
+
}
|
|
109
|
+
if (id !== undefined) {
|
|
110
|
+
query.id = id;
|
|
111
|
+
}
|
|
112
|
+
return query;
|
|
113
|
+
}
|
|
114
|
+
case "FindEvidence":
|
|
115
|
+
return { type: "FindEvidence", needle: requireNonEmptyString(record.needle, "needle") };
|
|
116
|
+
default:
|
|
117
|
+
throw new QueryValidationError(`unsupported query type: ${String(record.type)}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function pickDefined(base, fields) {
|
|
121
|
+
const out = { ...base };
|
|
122
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
123
|
+
if (value !== undefined) {
|
|
124
|
+
out[key] = value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
/** Typed query constructors (internal ergonomics). */
|
|
130
|
+
export const Queries = {
|
|
131
|
+
repositorySummary: () => ({ type: "RepositorySummary" }),
|
|
132
|
+
listDomains: () => ({ type: "ListDomains" }),
|
|
133
|
+
listEntrypoints: () => ({ type: "ListEntrypoints" }),
|
|
134
|
+
listArchitectures: () => ({ type: "ListArchitectures" }),
|
|
135
|
+
listRelationships: () => ({ type: "ListRelationships" }),
|
|
136
|
+
listDependencies: () => ({ type: "ListDependencies" }),
|
|
137
|
+
findDomain: (name) => ({ type: "FindDomain", name }),
|
|
138
|
+
findEntrypoint: (options) => pickDefined({ type: "FindEntrypoint" }, options),
|
|
139
|
+
findArchitecture: (pattern) => ({ type: "FindArchitecture", pattern }),
|
|
140
|
+
findComponent: (name) => ({ type: "FindComponent", name }),
|
|
141
|
+
findRelationship: (options) => pickDefined({ type: "FindRelationship" }, options),
|
|
142
|
+
findDependency: (options) => pickDefined({ type: "FindDependency" }, options),
|
|
143
|
+
findEvidence: (needle) => ({ type: "FindEvidence", needle }),
|
|
144
|
+
statistics: () => ({ type: "Statistics" }),
|
|
145
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { QueryHandlerContext, QueryHandlerOutput, QueryResultMap } from "./models.js";
|
|
2
|
+
import type { Query, QueryType } from "./types.js";
|
|
3
|
+
type Handler<T extends QueryType> = (query: Extract<Query, {
|
|
4
|
+
type: T;
|
|
5
|
+
}>, ctx: QueryHandlerContext) => QueryHandlerOutput<QueryResultMap[T]>;
|
|
6
|
+
export declare function getQueryHandler<T extends QueryType>(type: T): Handler<T>;
|
|
7
|
+
export declare function listSupportedQueryTypes(): QueryType[];
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { QueryNotFoundError } from "./errors.js";
|
|
2
|
+
function average(values) {
|
|
3
|
+
if (values.length === 0) {
|
|
4
|
+
return 0;
|
|
5
|
+
}
|
|
6
|
+
const sum = values.reduce((acc, value) => acc + value, 0);
|
|
7
|
+
return Math.round((sum / values.length) * 100) / 100;
|
|
8
|
+
}
|
|
9
|
+
function includesIgnoreCase(haystack, needle) {
|
|
10
|
+
return haystack.toLowerCase().includes(needle.toLowerCase());
|
|
11
|
+
}
|
|
12
|
+
function equalsIgnoreCase(left, right) {
|
|
13
|
+
return left.toLowerCase() === right.toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
function domainRelatedPaths(domain) {
|
|
16
|
+
return [...domain.paths, ...domain.evidence];
|
|
17
|
+
}
|
|
18
|
+
function entrypointsForDomain(model, domain) {
|
|
19
|
+
const paths = domainRelatedPaths(domain);
|
|
20
|
+
return model.entrypoints.filter((entry) => paths.some((path) => includesIgnoreCase(path, entry.file) || includesIgnoreCase(entry.file, path)));
|
|
21
|
+
}
|
|
22
|
+
function relationshipsForDomain(model, domain) {
|
|
23
|
+
return model.relationships.filter((rel) => equalsIgnoreCase(rel.source, domain.name) ||
|
|
24
|
+
equalsIgnoreCase(rel.target, domain.name) ||
|
|
25
|
+
rel.evidence.some((e) => includesIgnoreCase(e, domain.name)) ||
|
|
26
|
+
domainRelatedPaths(domain).some((path) => includesIgnoreCase(rel.source, path) ||
|
|
27
|
+
includesIgnoreCase(rel.target, path) ||
|
|
28
|
+
rel.evidence.some((e) => includesIgnoreCase(e, path))));
|
|
29
|
+
}
|
|
30
|
+
function dependenciesForDomain(model, domain) {
|
|
31
|
+
return model.dependencies.filter((dep) => equalsIgnoreCase(dep.from, domain.name) ||
|
|
32
|
+
equalsIgnoreCase(dep.to, domain.name) ||
|
|
33
|
+
dep.evidence.some((e) => includesIgnoreCase(e, domain.name)));
|
|
34
|
+
}
|
|
35
|
+
function architecturesForDomain(model, domain) {
|
|
36
|
+
const related = model.architectures.filter((arch) => arch.evidence.some((e) => includesIgnoreCase(e, domain.name)) ||
|
|
37
|
+
arch.matchedRules.some((r) => includesIgnoreCase(r, domain.name)));
|
|
38
|
+
if (related.length > 0) {
|
|
39
|
+
return related;
|
|
40
|
+
}
|
|
41
|
+
// Fall back to top architectures when domain exists but has no direct arch mentions.
|
|
42
|
+
return model.architectures.slice(0, 3);
|
|
43
|
+
}
|
|
44
|
+
const handlers = {
|
|
45
|
+
RepositorySummary: (_query, ctx) => {
|
|
46
|
+
const { model } = ctx;
|
|
47
|
+
const result = {
|
|
48
|
+
projectName: model.metadata.project.name,
|
|
49
|
+
schemaVersion: model.metadata.schemaVersion,
|
|
50
|
+
compilerVersion: model.compilerMetadata.compilerVersion,
|
|
51
|
+
generatedAt: model.compilerMetadata.generatedAt,
|
|
52
|
+
topDomains: model.summary.topDomains,
|
|
53
|
+
topArchitectures: model.summary.topArchitectures,
|
|
54
|
+
frameworks: model.summary.frameworks,
|
|
55
|
+
statistics: model.summary.statistics,
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
result,
|
|
59
|
+
evidence: [...model.metadata.evidence],
|
|
60
|
+
confidence: model.metadata.confidence,
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
ListDomains: (_query, ctx) => {
|
|
64
|
+
const domains = ctx.model.domains;
|
|
65
|
+
const result = { domains, count: domains.length };
|
|
66
|
+
return {
|
|
67
|
+
result,
|
|
68
|
+
evidence: domains.flatMap((d) => d.evidence).slice(0, 20),
|
|
69
|
+
confidence: average(domains.map((d) => d.confidence)),
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
ListEntrypoints: (_query, ctx) => {
|
|
73
|
+
const entrypoints = ctx.model.entrypoints;
|
|
74
|
+
const result = { entrypoints, count: entrypoints.length };
|
|
75
|
+
return {
|
|
76
|
+
result,
|
|
77
|
+
evidence: entrypoints.flatMap((e) => e.evidence).slice(0, 20),
|
|
78
|
+
confidence: average(entrypoints.map((e) => e.confidence)),
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
ListArchitectures: (_query, ctx) => {
|
|
82
|
+
const architectures = ctx.model.architectures;
|
|
83
|
+
const result = { architectures, count: architectures.length };
|
|
84
|
+
return {
|
|
85
|
+
result,
|
|
86
|
+
evidence: architectures.flatMap((a) => a.evidence).slice(0, 20),
|
|
87
|
+
confidence: average(architectures.map((a) => a.confidence)),
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
ListRelationships: (_query, ctx) => {
|
|
91
|
+
const relationships = ctx.model.relationships;
|
|
92
|
+
const result = { relationships, count: relationships.length };
|
|
93
|
+
return {
|
|
94
|
+
result,
|
|
95
|
+
evidence: relationships.flatMap((r) => r.evidence).slice(0, 20),
|
|
96
|
+
confidence: average(relationships.map((r) => r.confidence)),
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
ListDependencies: (_query, ctx) => {
|
|
100
|
+
const dependencies = ctx.model.dependencies;
|
|
101
|
+
const result = { dependencies, count: dependencies.length };
|
|
102
|
+
return {
|
|
103
|
+
result,
|
|
104
|
+
evidence: dependencies.flatMap((d) => d.evidence).slice(0, 20),
|
|
105
|
+
confidence: average(dependencies.map((d) => d.confidence)),
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
FindDomain: (query, ctx) => {
|
|
109
|
+
const domain = ctx.model.domains.find((d) => equalsIgnoreCase(d.name, query.name));
|
|
110
|
+
if (!domain) {
|
|
111
|
+
throw new QueryNotFoundError(`domain not found: ${query.name}`);
|
|
112
|
+
}
|
|
113
|
+
const entrypoints = entrypointsForDomain(ctx.model, domain);
|
|
114
|
+
const relationships = relationshipsForDomain(ctx.model, domain);
|
|
115
|
+
const dependencies = dependenciesForDomain(ctx.model, domain);
|
|
116
|
+
const architectures = architecturesForDomain(ctx.model, domain);
|
|
117
|
+
const result = {
|
|
118
|
+
domain,
|
|
119
|
+
entrypoints,
|
|
120
|
+
relationships,
|
|
121
|
+
dependencies,
|
|
122
|
+
architectures,
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
result,
|
|
126
|
+
evidence: [
|
|
127
|
+
...domain.evidence,
|
|
128
|
+
...entrypoints.flatMap((e) => e.evidence).slice(0, 5),
|
|
129
|
+
...relationships.flatMap((r) => r.evidence).slice(0, 5),
|
|
130
|
+
],
|
|
131
|
+
confidence: domain.confidence,
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
FindEntrypoint: (query, ctx) => {
|
|
135
|
+
const entrypoint = ctx.model.entrypoints.find((entry) => {
|
|
136
|
+
if (query.id && entry.id === query.id) {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
if (query.file && equalsIgnoreCase(entry.file, query.file)) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
if (query.framework &&
|
|
143
|
+
equalsIgnoreCase(entry.framework, query.framework) &&
|
|
144
|
+
!query.file &&
|
|
145
|
+
!query.id) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
if (query.framework &&
|
|
149
|
+
query.file &&
|
|
150
|
+
equalsIgnoreCase(entry.framework, query.framework) &&
|
|
151
|
+
equalsIgnoreCase(entry.file, query.file)) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
});
|
|
156
|
+
if (!entrypoint) {
|
|
157
|
+
throw new QueryNotFoundError("entrypoint not found");
|
|
158
|
+
}
|
|
159
|
+
const result = { entrypoint };
|
|
160
|
+
return {
|
|
161
|
+
result,
|
|
162
|
+
evidence: [...entrypoint.evidence],
|
|
163
|
+
confidence: entrypoint.confidence,
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
FindArchitecture: (query, ctx) => {
|
|
167
|
+
const architecture = ctx.model.architectures.find((arch) => equalsIgnoreCase(arch.pattern, query.pattern));
|
|
168
|
+
if (!architecture) {
|
|
169
|
+
throw new QueryNotFoundError(`architecture not found: ${query.pattern}`);
|
|
170
|
+
}
|
|
171
|
+
const result = { architecture };
|
|
172
|
+
return {
|
|
173
|
+
result,
|
|
174
|
+
evidence: [...architecture.evidence, ...architecture.matchedRules],
|
|
175
|
+
confidence: architecture.confidence,
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
FindComponent: (query, ctx) => {
|
|
179
|
+
const name = query.name;
|
|
180
|
+
const asRelationshipSource = ctx.model.relationships.filter((r) => equalsIgnoreCase(r.source, name));
|
|
181
|
+
const asRelationshipTarget = ctx.model.relationships.filter((r) => equalsIgnoreCase(r.target, name));
|
|
182
|
+
const asDependencyEndpoint = ctx.model.dependencies.filter((d) => equalsIgnoreCase(d.from, name) || equalsIgnoreCase(d.to, name));
|
|
183
|
+
const matchingEntrypoints = ctx.model.entrypoints.filter((e) => includesIgnoreCase(e.file, name) || e.evidence.some((ev) => includesIgnoreCase(ev, name)));
|
|
184
|
+
const matchingDomains = ctx.model.domains.filter((d) => equalsIgnoreCase(d.name, name) || d.evidence.some((ev) => includesIgnoreCase(ev, name)));
|
|
185
|
+
if (asRelationshipSource.length === 0 &&
|
|
186
|
+
asRelationshipTarget.length === 0 &&
|
|
187
|
+
asDependencyEndpoint.length === 0 &&
|
|
188
|
+
matchingEntrypoints.length === 0 &&
|
|
189
|
+
matchingDomains.length === 0) {
|
|
190
|
+
throw new QueryNotFoundError(`component not found: ${name}`);
|
|
191
|
+
}
|
|
192
|
+
const result = {
|
|
193
|
+
name,
|
|
194
|
+
asRelationshipSource,
|
|
195
|
+
asRelationshipTarget,
|
|
196
|
+
asDependencyEndpoint,
|
|
197
|
+
matchingEntrypoints,
|
|
198
|
+
matchingDomains,
|
|
199
|
+
};
|
|
200
|
+
const confidences = [
|
|
201
|
+
...asRelationshipSource.map((r) => r.confidence),
|
|
202
|
+
...asRelationshipTarget.map((r) => r.confidence),
|
|
203
|
+
...asDependencyEndpoint.map((d) => d.confidence),
|
|
204
|
+
...matchingEntrypoints.map((e) => e.confidence),
|
|
205
|
+
...matchingDomains.map((d) => d.confidence),
|
|
206
|
+
];
|
|
207
|
+
return {
|
|
208
|
+
result,
|
|
209
|
+
evidence: [
|
|
210
|
+
...asRelationshipSource.flatMap((r) => r.evidence),
|
|
211
|
+
...asRelationshipTarget.flatMap((r) => r.evidence),
|
|
212
|
+
...asDependencyEndpoint.flatMap((d) => d.evidence),
|
|
213
|
+
].slice(0, 30),
|
|
214
|
+
confidence: average(confidences),
|
|
215
|
+
};
|
|
216
|
+
},
|
|
217
|
+
FindRelationship: (query, ctx) => {
|
|
218
|
+
const matches = ctx.model.relationships.filter((rel) => {
|
|
219
|
+
if (query.id) {
|
|
220
|
+
return rel.id === query.id;
|
|
221
|
+
}
|
|
222
|
+
if (query.source && !equalsIgnoreCase(rel.source, query.source)) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
if (query.target && !equalsIgnoreCase(rel.target, query.target)) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
if (query.relationship && !equalsIgnoreCase(rel.relationship, query.relationship)) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
return Boolean(query.source || query.target || query.relationship);
|
|
232
|
+
});
|
|
233
|
+
const relationship = matches[0];
|
|
234
|
+
if (!relationship) {
|
|
235
|
+
throw new QueryNotFoundError("relationship not found");
|
|
236
|
+
}
|
|
237
|
+
const result = { relationship };
|
|
238
|
+
return {
|
|
239
|
+
result,
|
|
240
|
+
evidence: [...relationship.evidence],
|
|
241
|
+
confidence: relationship.confidence,
|
|
242
|
+
};
|
|
243
|
+
},
|
|
244
|
+
FindDependency: (query, ctx) => {
|
|
245
|
+
const matches = ctx.model.dependencies.filter((dep) => {
|
|
246
|
+
if (query.id) {
|
|
247
|
+
return dep.id === query.id;
|
|
248
|
+
}
|
|
249
|
+
if (query.from && !equalsIgnoreCase(dep.from, query.from)) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
if (query.to && !equalsIgnoreCase(dep.to, query.to)) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
if (query.dependencyType && !equalsIgnoreCase(dep.type, query.dependencyType)) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
return Boolean(query.from || query.to || query.dependencyType);
|
|
259
|
+
});
|
|
260
|
+
const dependency = matches[0];
|
|
261
|
+
if (!dependency) {
|
|
262
|
+
throw new QueryNotFoundError("dependency not found");
|
|
263
|
+
}
|
|
264
|
+
const result = { dependency };
|
|
265
|
+
return {
|
|
266
|
+
result,
|
|
267
|
+
evidence: [...dependency.evidence],
|
|
268
|
+
confidence: dependency.confidence,
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
FindEvidence: (query, ctx) => {
|
|
272
|
+
const needle = query.needle;
|
|
273
|
+
const hits = [];
|
|
274
|
+
const push = (collection, id, label, evidence) => {
|
|
275
|
+
const matched = evidence.filter((item) => includesIgnoreCase(item, needle));
|
|
276
|
+
if (matched.length > 0) {
|
|
277
|
+
hits.push({ collection, id, label, evidence: matched });
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
push("metadata", ctx.model.metadata.id, ctx.model.metadata.project.name, ctx.model.metadata.evidence);
|
|
281
|
+
for (const domain of ctx.model.domains) {
|
|
282
|
+
push("domains", domain.id, domain.name, domain.evidence);
|
|
283
|
+
}
|
|
284
|
+
for (const entry of ctx.model.entrypoints) {
|
|
285
|
+
push("entrypoints", entry.id, entry.file, entry.evidence);
|
|
286
|
+
}
|
|
287
|
+
for (const dep of ctx.model.dependencies) {
|
|
288
|
+
push("dependencies", dep.id, `${dep.from}->${dep.to}`, dep.evidence);
|
|
289
|
+
}
|
|
290
|
+
for (const rel of ctx.model.relationships) {
|
|
291
|
+
push("relationships", rel.id, `${rel.source}->${rel.target}`, rel.evidence);
|
|
292
|
+
}
|
|
293
|
+
for (const arch of ctx.model.architectures) {
|
|
294
|
+
push("architectures", arch.id, arch.pattern, [
|
|
295
|
+
...arch.evidence,
|
|
296
|
+
...arch.matchedRules,
|
|
297
|
+
...arch.conflictingEvidence,
|
|
298
|
+
...arch.unknowns,
|
|
299
|
+
]);
|
|
300
|
+
}
|
|
301
|
+
hits.sort((a, b) => a.collection.localeCompare(b.collection) || a.label.localeCompare(b.label));
|
|
302
|
+
if (hits.length === 0) {
|
|
303
|
+
throw new QueryNotFoundError(`evidence not found for needle: ${needle}`);
|
|
304
|
+
}
|
|
305
|
+
const result = { needle, hits, count: hits.length };
|
|
306
|
+
return {
|
|
307
|
+
result,
|
|
308
|
+
evidence: hits.flatMap((hit) => hit.evidence).slice(0, 40),
|
|
309
|
+
confidence: Math.min(0.99, 0.5 + Math.min(hits.length, 10) * 0.04),
|
|
310
|
+
};
|
|
311
|
+
},
|
|
312
|
+
Statistics: (_query, ctx) => {
|
|
313
|
+
const result = {
|
|
314
|
+
summary: ctx.model.summary,
|
|
315
|
+
model: {
|
|
316
|
+
projectName: ctx.model.metadata.project.name,
|
|
317
|
+
schemaVersion: ctx.model.metadata.schemaVersion,
|
|
318
|
+
compilerVersion: ctx.model.compilerMetadata.compilerVersion,
|
|
319
|
+
generatedAt: ctx.model.compilerMetadata.generatedAt,
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
return {
|
|
323
|
+
result,
|
|
324
|
+
evidence: [
|
|
325
|
+
`domains:${ctx.model.summary.statistics.domainCount}`,
|
|
326
|
+
`entrypoints:${ctx.model.summary.statistics.entrypointCount}`,
|
|
327
|
+
`dependencies:${ctx.model.summary.statistics.dependencyCount}`,
|
|
328
|
+
`relationships:${ctx.model.summary.statistics.relationshipCount}`,
|
|
329
|
+
`architectures:${ctx.model.summary.statistics.architectureCount}`,
|
|
330
|
+
],
|
|
331
|
+
confidence: ctx.model.summary.statistics.averageConfidence,
|
|
332
|
+
};
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
export function getQueryHandler(type) {
|
|
336
|
+
return handlers[type];
|
|
337
|
+
}
|
|
338
|
+
export function listSupportedQueryTypes() {
|
|
339
|
+
return Object.keys(handlers).sort((a, b) => a.localeCompare(b));
|
|
340
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export type QueryType = "RepositorySummary" | "ListDomains" | "ListEntrypoints" | "ListArchitectures" | "ListRelationships" | "ListDependencies" | "FindDomain" | "FindEntrypoint" | "FindArchitecture" | "FindComponent" | "FindRelationship" | "FindDependency" | "FindEvidence" | "Statistics";
|
|
2
|
+
export type Query = {
|
|
3
|
+
type: "RepositorySummary";
|
|
4
|
+
} | {
|
|
5
|
+
type: "ListDomains";
|
|
6
|
+
} | {
|
|
7
|
+
type: "ListEntrypoints";
|
|
8
|
+
} | {
|
|
9
|
+
type: "ListArchitectures";
|
|
10
|
+
} | {
|
|
11
|
+
type: "ListRelationships";
|
|
12
|
+
} | {
|
|
13
|
+
type: "ListDependencies";
|
|
14
|
+
} | {
|
|
15
|
+
type: "FindDomain";
|
|
16
|
+
name: string;
|
|
17
|
+
} | {
|
|
18
|
+
type: "FindEntrypoint";
|
|
19
|
+
file?: string;
|
|
20
|
+
framework?: string;
|
|
21
|
+
id?: string;
|
|
22
|
+
} | {
|
|
23
|
+
type: "FindArchitecture";
|
|
24
|
+
pattern: string;
|
|
25
|
+
} | {
|
|
26
|
+
type: "FindComponent";
|
|
27
|
+
name: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: "FindRelationship";
|
|
30
|
+
source?: string;
|
|
31
|
+
target?: string;
|
|
32
|
+
relationship?: string;
|
|
33
|
+
id?: string;
|
|
34
|
+
} | {
|
|
35
|
+
type: "FindDependency";
|
|
36
|
+
from?: string;
|
|
37
|
+
to?: string;
|
|
38
|
+
dependencyType?: string;
|
|
39
|
+
id?: string;
|
|
40
|
+
} | {
|
|
41
|
+
type: "FindEvidence";
|
|
42
|
+
needle: string;
|
|
43
|
+
} | {
|
|
44
|
+
type: "Statistics";
|
|
45
|
+
};
|
|
46
|
+
export interface QueryMetadata {
|
|
47
|
+
queryType: QueryType;
|
|
48
|
+
projectName: string;
|
|
49
|
+
modelId: string;
|
|
50
|
+
schemaVersion: string;
|
|
51
|
+
compilerVersion: string;
|
|
52
|
+
modelGeneratedAt: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Canonical envelope returned by every query execution.
|
|
56
|
+
*/
|
|
57
|
+
export interface QueryResponse<T> {
|
|
58
|
+
result: T;
|
|
59
|
+
evidence: readonly string[];
|
|
60
|
+
confidence: number;
|
|
61
|
+
metadata: QueryMetadata;
|
|
62
|
+
executionTimeMs: number;
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RelationshipDiscoveryOptions, RelationshipDiscoveryResult, RelationshipStrength } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Discover semantic component relationships using deterministic heuristics.
|
|
4
|
+
* Reuses dependency, entrypoint, and domain discovery. Isolated from scan/CLI.
|
|
5
|
+
*/
|
|
6
|
+
export declare function discoverRelationships(options?: RelationshipDiscoveryOptions): Promise<RelationshipDiscoveryResult>;
|
|
7
|
+
export declare function scoreRelationshipConfidence(options: {
|
|
8
|
+
base: number;
|
|
9
|
+
strength: RelationshipStrength;
|
|
10
|
+
evidenceCount: number;
|
|
11
|
+
}): number;
|