@praneeth_54/agentdoctor 1.0.0 → 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.
Files changed (208) hide show
  1. package/CHANGELOG.md +34 -5
  2. package/README.md +15 -12
  3. package/dist/cli/commands/brain-mcp.d.ts +9 -0
  4. package/dist/cli/commands/brain-mcp.js +38 -0
  5. package/dist/cli/program.js +13 -0
  6. package/dist/constants.d.ts +1 -1
  7. package/dist/constants.js +1 -1
  8. package/dist/core/understanding/architecture/index.d.ts +4 -0
  9. package/dist/core/understanding/architecture/index.js +3 -0
  10. package/dist/core/understanding/architecture/infer.d.ts +6 -0
  11. package/dist/core/understanding/architecture/infer.js +39 -0
  12. package/dist/core/understanding/architecture/models.d.ts +23 -0
  13. package/dist/core/understanding/architecture/models.js +127 -0
  14. package/dist/core/understanding/architecture/patterns/distribution.d.ts +2 -0
  15. package/dist/core/understanding/architecture/patterns/distribution.js +115 -0
  16. package/dist/core/understanding/architecture/patterns/index.d.ts +2 -0
  17. package/dist/core/understanding/architecture/patterns/index.js +8 -0
  18. package/dist/core/understanding/architecture/patterns/layered.d.ts +2 -0
  19. package/dist/core/understanding/architecture/patterns/layered.js +279 -0
  20. package/dist/core/understanding/architecture/patterns/structural.d.ts +2 -0
  21. package/dist/core/understanding/architecture/patterns/structural.js +197 -0
  22. package/dist/core/understanding/architecture/rules.d.ts +4 -0
  23. package/dist/core/understanding/architecture/rules.js +6 -0
  24. package/dist/core/understanding/architecture/types.d.ts +52 -0
  25. package/dist/core/understanding/architecture/types.js +1 -0
  26. package/dist/core/understanding/brain/build.d.ts +13 -0
  27. package/dist/core/understanding/brain/build.js +158 -0
  28. package/dist/core/understanding/brain/claims/index.d.ts +3 -0
  29. package/dist/core/understanding/brain/claims/index.js +2 -0
  30. package/dist/core/understanding/brain/claims/lifecycle.d.ts +16 -0
  31. package/dist/core/understanding/brain/claims/lifecycle.js +78 -0
  32. package/dist/core/understanding/brain/claims/types.d.ts +32 -0
  33. package/dist/core/understanding/brain/claims/types.js +25 -0
  34. package/dist/core/understanding/brain/components/index.d.ts +17 -0
  35. package/dist/core/understanding/brain/components/index.js +97 -0
  36. package/dist/core/understanding/brain/components/types.d.ts +16 -0
  37. package/dist/core/understanding/brain/components/types.js +8 -0
  38. package/dist/core/understanding/brain/confidence.d.ts +19 -0
  39. package/dist/core/understanding/brain/confidence.js +39 -0
  40. package/dist/core/understanding/brain/contract.d.ts +17 -0
  41. package/dist/core/understanding/brain/contract.js +100 -0
  42. package/dist/core/understanding/brain/contradictions/index.d.ts +12 -0
  43. package/dist/core/understanding/brain/contradictions/index.js +57 -0
  44. package/dist/core/understanding/brain/contradictions/types.d.ts +12 -0
  45. package/dist/core/understanding/brain/contradictions/types.js +8 -0
  46. package/dist/core/understanding/brain/delta.d.ts +25 -0
  47. package/dist/core/understanding/brain/delta.js +116 -0
  48. package/dist/core/understanding/brain/evidence/index.d.ts +3 -0
  49. package/dist/core/understanding/brain/evidence/index.js +2 -0
  50. package/dist/core/understanding/brain/evidence/redact.d.ts +4 -0
  51. package/dist/core/understanding/brain/evidence/redact.js +36 -0
  52. package/dist/core/understanding/brain/evidence/types.d.ts +34 -0
  53. package/dist/core/understanding/brain/evidence/types.js +35 -0
  54. package/dist/core/understanding/brain/explain.d.ts +17 -0
  55. package/dist/core/understanding/brain/explain.js +33 -0
  56. package/dist/core/understanding/brain/index.d.ts +28 -0
  57. package/dist/core/understanding/brain/index.js +16 -0
  58. package/dist/core/understanding/brain/migrate.d.ts +21 -0
  59. package/dist/core/understanding/brain/migrate.js +47 -0
  60. package/dist/core/understanding/brain/query.d.ts +60 -0
  61. package/dist/core/understanding/brain/query.js +92 -0
  62. package/dist/core/understanding/brain/security.d.ts +3 -0
  63. package/dist/core/understanding/brain/security.js +43 -0
  64. package/dist/core/understanding/brain/storage/index.d.ts +2 -0
  65. package/dist/core/understanding/brain/storage/index.js +1 -0
  66. package/dist/core/understanding/brain/storage/store.d.ts +53 -0
  67. package/dist/core/understanding/brain/storage/store.js +305 -0
  68. package/dist/core/understanding/brain/trace.d.ts +24 -0
  69. package/dist/core/understanding/brain/trace.js +207 -0
  70. package/dist/core/understanding/brain/types.d.ts +32 -0
  71. package/dist/core/understanding/brain/types.js +8 -0
  72. package/dist/core/understanding/brain/version.d.ts +13 -0
  73. package/dist/core/understanding/brain/version.js +7 -0
  74. package/dist/core/understanding/delta/compare.d.ts +26 -0
  75. package/dist/core/understanding/delta/compare.js +74 -0
  76. package/dist/core/understanding/delta/index.d.ts +2 -0
  77. package/dist/core/understanding/delta/index.js +1 -0
  78. package/dist/core/understanding/dependencies/discover.d.ts +7 -0
  79. package/dist/core/understanding/dependencies/discover.js +550 -0
  80. package/dist/core/understanding/dependencies/extract.d.ts +10 -0
  81. package/dist/core/understanding/dependencies/extract.js +189 -0
  82. package/dist/core/understanding/dependencies/index.d.ts +4 -0
  83. package/dist/core/understanding/dependencies/index.js +3 -0
  84. package/dist/core/understanding/dependencies/models.d.ts +12 -0
  85. package/dist/core/understanding/dependencies/models.js +125 -0
  86. package/dist/core/understanding/dependencies/types.d.ts +27 -0
  87. package/dist/core/understanding/dependencies/types.js +1 -0
  88. package/dist/core/understanding/domain/discover.d.ts +14 -0
  89. package/dist/core/understanding/domain/discover.js +90 -0
  90. package/dist/core/understanding/domain/index.d.ts +2 -0
  91. package/dist/core/understanding/domain/index.js +1 -0
  92. package/dist/core/understanding/entrypoints/discover.d.ts +6 -0
  93. package/dist/core/understanding/entrypoints/discover.js +62 -0
  94. package/dist/core/understanding/entrypoints/extract.d.ts +19 -0
  95. package/dist/core/understanding/entrypoints/extract.js +43 -0
  96. package/dist/core/understanding/entrypoints/index.d.ts +4 -0
  97. package/dist/core/understanding/entrypoints/index.js +3 -0
  98. package/dist/core/understanding/entrypoints/models.d.ts +26 -0
  99. package/dist/core/understanding/entrypoints/models.js +216 -0
  100. package/dist/core/understanding/entrypoints/types.d.ts +20 -0
  101. package/dist/core/understanding/entrypoints/types.js +1 -0
  102. package/dist/core/understanding/index.d.ts +32 -0
  103. package/dist/core/understanding/index.js +18 -0
  104. package/dist/core/understanding/mind/build.d.ts +16 -0
  105. package/dist/core/understanding/mind/build.js +134 -0
  106. package/dist/core/understanding/mind/index.d.ts +6 -0
  107. package/dist/core/understanding/mind/index.js +3 -0
  108. package/dist/core/understanding/mind/query.d.ts +33 -0
  109. package/dist/core/understanding/mind/query.js +39 -0
  110. package/dist/core/understanding/mind/types.d.ts +24 -0
  111. package/dist/core/understanding/mind/types.js +7 -0
  112. package/dist/core/understanding/model/builder.d.ts +9 -0
  113. package/dist/core/understanding/model/builder.js +209 -0
  114. package/dist/core/understanding/model/ids.d.ts +7 -0
  115. package/dist/core/understanding/model/ids.js +48 -0
  116. package/dist/core/understanding/model/index.d.ts +7 -0
  117. package/dist/core/understanding/model/index.js +6 -0
  118. package/dist/core/understanding/model/schema.d.ts +12 -0
  119. package/dist/core/understanding/model/schema.js +33 -0
  120. package/dist/core/understanding/model/serializer.d.ts +9 -0
  121. package/dist/core/understanding/model/serializer.js +46 -0
  122. package/dist/core/understanding/model/types.d.ts +110 -0
  123. package/dist/core/understanding/model/types.js +1 -0
  124. package/dist/core/understanding/model/validator.d.ts +5 -0
  125. package/dist/core/understanding/model/validator.js +126 -0
  126. package/dist/core/understanding/model/version.d.ts +9 -0
  127. package/dist/core/understanding/model/version.js +7 -0
  128. package/dist/core/understanding/ownership/discover.d.ts +12 -0
  129. package/dist/core/understanding/ownership/discover.js +242 -0
  130. package/dist/core/understanding/ownership/index.d.ts +2 -0
  131. package/dist/core/understanding/ownership/index.js +1 -0
  132. package/dist/core/understanding/ownership/types.d.ts +23 -0
  133. package/dist/core/understanding/ownership/types.js +1 -0
  134. package/dist/core/understanding/query/engine.d.ts +24 -0
  135. package/dist/core/understanding/query/engine.js +35 -0
  136. package/dist/core/understanding/query/errors.d.ts +13 -0
  137. package/dist/core/understanding/query/errors.js +26 -0
  138. package/dist/core/understanding/query/executor.d.ts +8 -0
  139. package/dist/core/understanding/query/executor.js +34 -0
  140. package/dist/core/understanding/query/index.d.ts +7 -0
  141. package/dist/core/understanding/query/index.js +5 -0
  142. package/dist/core/understanding/query/models.d.ts +102 -0
  143. package/dist/core/understanding/query/models.js +1 -0
  144. package/dist/core/understanding/query/query.d.ts +68 -0
  145. package/dist/core/understanding/query/query.js +145 -0
  146. package/dist/core/understanding/query/registry.d.ts +8 -0
  147. package/dist/core/understanding/query/registry.js +340 -0
  148. package/dist/core/understanding/query/types.d.ts +63 -0
  149. package/dist/core/understanding/query/types.js +1 -0
  150. package/dist/core/understanding/relationships/discover.d.ts +11 -0
  151. package/dist/core/understanding/relationships/discover.js +517 -0
  152. package/dist/core/understanding/relationships/extract.d.ts +14 -0
  153. package/dist/core/understanding/relationships/extract.js +165 -0
  154. package/dist/core/understanding/relationships/index.d.ts +4 -0
  155. package/dist/core/understanding/relationships/index.js +3 -0
  156. package/dist/core/understanding/relationships/models.d.ts +21 -0
  157. package/dist/core/understanding/relationships/models.js +185 -0
  158. package/dist/core/understanding/relationships/types.d.ts +42 -0
  159. package/dist/core/understanding/relationships/types.js +1 -0
  160. package/dist/core/understanding/risks/discover.d.ts +8 -0
  161. package/dist/core/understanding/risks/discover.js +155 -0
  162. package/dist/core/understanding/risks/index.d.ts +2 -0
  163. package/dist/core/understanding/risks/index.js +1 -0
  164. package/dist/core/understanding/risks/types.d.ts +26 -0
  165. package/dist/core/understanding/risks/types.js +1 -0
  166. package/dist/core/understanding/shared/domain-lexicon.d.ts +9 -0
  167. package/dist/core/understanding/shared/domain-lexicon.js +107 -0
  168. package/dist/core/understanding/shared/index.d.ts +2 -0
  169. package/dist/core/understanding/shared/index.js +2 -0
  170. package/dist/core/understanding/shared/tokens.d.ts +7 -0
  171. package/dist/core/understanding/shared/tokens.js +141 -0
  172. package/dist/core/understanding/snapshot/identity.d.ts +24 -0
  173. package/dist/core/understanding/snapshot/identity.js +79 -0
  174. package/dist/core/understanding/snapshot/index.d.ts +2 -0
  175. package/dist/core/understanding/snapshot/index.js +1 -0
  176. package/dist/core/understanding/types/index.d.ts +22 -0
  177. package/dist/core/understanding/types/index.js +1 -0
  178. package/dist/core/understanding/understand/formatter.d.ts +10 -0
  179. package/dist/core/understanding/understand/formatter.js +115 -0
  180. package/dist/core/understanding/understand/index.d.ts +4 -0
  181. package/dist/core/understanding/understand/index.js +3 -0
  182. package/dist/core/understanding/understand/service.d.ts +17 -0
  183. package/dist/core/understanding/understand/service.js +76 -0
  184. package/dist/core/understanding/understand/summary.d.ts +14 -0
  185. package/dist/core/understanding/understand/summary.js +39 -0
  186. package/dist/core/understanding/understand/types.d.ts +31 -0
  187. package/dist/core/understanding/understand/types.js +1 -0
  188. package/dist/mcp/brain/compile.d.ts +9 -0
  189. package/dist/mcp/brain/compile.js +42 -0
  190. package/dist/mcp/brain/errors.d.ts +12 -0
  191. package/dist/mcp/brain/errors.js +23 -0
  192. package/dist/mcp/brain/index.d.ts +11 -0
  193. package/dist/mcp/brain/index.js +8 -0
  194. package/dist/mcp/brain/provenance.d.ts +29 -0
  195. package/dist/mcp/brain/provenance.js +59 -0
  196. package/dist/mcp/brain/schemas.d.ts +13 -0
  197. package/dist/mcp/brain/schemas.js +105 -0
  198. package/dist/mcp/brain/security/root.d.ts +6 -0
  199. package/dist/mcp/brain/security/root.js +66 -0
  200. package/dist/mcp/brain/server.d.ts +12 -0
  201. package/dist/mcp/brain/server.js +46 -0
  202. package/dist/mcp/brain/session.d.ts +33 -0
  203. package/dist/mcp/brain/session.js +117 -0
  204. package/dist/mcp/brain/tools/handlers.d.ts +13 -0
  205. package/dist/mcp/brain/tools/handlers.js +483 -0
  206. package/dist/mcp/brain/tools/registry.d.ts +9 -0
  207. package/dist/mcp/brain/tools/registry.js +191 -0
  208. package/package.json +5 -1
@@ -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;