@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.
Files changed (267) hide show
  1. package/CHANGELOG.md +105 -1
  2. package/README.md +64 -31
  3. package/dist/agents/inspect.js +58 -51
  4. package/dist/cli/commands/brain-mcp.d.ts +9 -0
  5. package/dist/cli/commands/brain-mcp.js +38 -0
  6. package/dist/cli/commands/explain.js +1 -1
  7. package/dist/cli/commands/fix.js +22 -6
  8. package/dist/cli/commands/scan.d.ts +5 -0
  9. package/dist/cli/commands/scan.js +25 -6
  10. package/dist/cli/commands/verify.d.ts +6 -0
  11. package/dist/cli/commands/verify.js +25 -11
  12. package/dist/cli/program.js +61 -7
  13. package/dist/constants.d.ts +1 -1
  14. package/dist/constants.js +1 -1
  15. package/dist/core/fix/apply.d.ts +5 -1
  16. package/dist/core/fix/apply.js +34 -14
  17. package/dist/core/fix/plan.d.ts +1 -1
  18. package/dist/core/fix/plan.js +110 -3
  19. package/dist/core/fix/render.d.ts +2 -0
  20. package/dist/core/fix/render.js +57 -9
  21. package/dist/core/fix/run.js +5 -1
  22. package/dist/core/fix/types.d.ts +1 -1
  23. package/dist/core/fix/types.js +5 -1
  24. package/dist/core/fix/writers/claude-settings.d.ts +16 -0
  25. package/dist/core/fix/writers/claude-settings.js +100 -0
  26. package/dist/core/fix/writers/codex-config.d.ts +22 -0
  27. package/dist/core/fix/writers/codex-config.js +183 -0
  28. package/dist/core/fix/writers/cursorignore.js +2 -8
  29. package/dist/core/path-resolution/index.d.ts +5 -0
  30. package/dist/core/path-resolution/index.js +5 -0
  31. package/dist/core/path-resolution/prepare.d.ts +33 -0
  32. package/dist/core/path-resolution/prepare.js +98 -0
  33. package/dist/core/policy/evaluate.d.ts +44 -0
  34. package/dist/core/policy/evaluate.js +120 -0
  35. package/dist/core/rules/claude-deny.d.ts +8 -0
  36. package/dist/core/rules/claude-deny.js +39 -0
  37. package/dist/core/rules/codex-deny.d.ts +5 -0
  38. package/dist/core/rules/codex-deny.js +42 -0
  39. package/dist/core/rules/context/generated-directory.js +43 -9
  40. package/dist/core/rules/context/large-log-file.js +27 -10
  41. package/dist/core/rules/ignore.js +2 -5
  42. package/dist/core/rules/instructions/missing-path-reference.d.ts +5 -1
  43. package/dist/core/rules/instructions/missing-path-reference.js +24 -6
  44. package/dist/core/rules/path-kind.d.ts +15 -2
  45. package/dist/core/rules/path-kind.js +113 -10
  46. package/dist/core/rules/security/env-file-exposure.js +9 -24
  47. package/dist/core/rules/text-cache.js +46 -40
  48. package/dist/core/understanding/architecture/index.d.ts +4 -0
  49. package/dist/core/understanding/architecture/index.js +3 -0
  50. package/dist/core/understanding/architecture/infer.d.ts +6 -0
  51. package/dist/core/understanding/architecture/infer.js +39 -0
  52. package/dist/core/understanding/architecture/models.d.ts +23 -0
  53. package/dist/core/understanding/architecture/models.js +127 -0
  54. package/dist/core/understanding/architecture/patterns/distribution.d.ts +2 -0
  55. package/dist/core/understanding/architecture/patterns/distribution.js +115 -0
  56. package/dist/core/understanding/architecture/patterns/index.d.ts +2 -0
  57. package/dist/core/understanding/architecture/patterns/index.js +8 -0
  58. package/dist/core/understanding/architecture/patterns/layered.d.ts +2 -0
  59. package/dist/core/understanding/architecture/patterns/layered.js +279 -0
  60. package/dist/core/understanding/architecture/patterns/structural.d.ts +2 -0
  61. package/dist/core/understanding/architecture/patterns/structural.js +197 -0
  62. package/dist/core/understanding/architecture/rules.d.ts +4 -0
  63. package/dist/core/understanding/architecture/rules.js +6 -0
  64. package/dist/core/understanding/architecture/types.d.ts +52 -0
  65. package/dist/core/understanding/architecture/types.js +1 -0
  66. package/dist/core/understanding/brain/build.d.ts +13 -0
  67. package/dist/core/understanding/brain/build.js +158 -0
  68. package/dist/core/understanding/brain/claims/index.d.ts +3 -0
  69. package/dist/core/understanding/brain/claims/index.js +2 -0
  70. package/dist/core/understanding/brain/claims/lifecycle.d.ts +16 -0
  71. package/dist/core/understanding/brain/claims/lifecycle.js +78 -0
  72. package/dist/core/understanding/brain/claims/types.d.ts +32 -0
  73. package/dist/core/understanding/brain/claims/types.js +25 -0
  74. package/dist/core/understanding/brain/components/index.d.ts +17 -0
  75. package/dist/core/understanding/brain/components/index.js +97 -0
  76. package/dist/core/understanding/brain/components/types.d.ts +16 -0
  77. package/dist/core/understanding/brain/components/types.js +8 -0
  78. package/dist/core/understanding/brain/confidence.d.ts +19 -0
  79. package/dist/core/understanding/brain/confidence.js +39 -0
  80. package/dist/core/understanding/brain/contract.d.ts +17 -0
  81. package/dist/core/understanding/brain/contract.js +100 -0
  82. package/dist/core/understanding/brain/contradictions/index.d.ts +12 -0
  83. package/dist/core/understanding/brain/contradictions/index.js +57 -0
  84. package/dist/core/understanding/brain/contradictions/types.d.ts +12 -0
  85. package/dist/core/understanding/brain/contradictions/types.js +8 -0
  86. package/dist/core/understanding/brain/delta.d.ts +25 -0
  87. package/dist/core/understanding/brain/delta.js +116 -0
  88. package/dist/core/understanding/brain/evidence/index.d.ts +3 -0
  89. package/dist/core/understanding/brain/evidence/index.js +2 -0
  90. package/dist/core/understanding/brain/evidence/redact.d.ts +4 -0
  91. package/dist/core/understanding/brain/evidence/redact.js +36 -0
  92. package/dist/core/understanding/brain/evidence/types.d.ts +34 -0
  93. package/dist/core/understanding/brain/evidence/types.js +35 -0
  94. package/dist/core/understanding/brain/explain.d.ts +17 -0
  95. package/dist/core/understanding/brain/explain.js +33 -0
  96. package/dist/core/understanding/brain/index.d.ts +28 -0
  97. package/dist/core/understanding/brain/index.js +16 -0
  98. package/dist/core/understanding/brain/migrate.d.ts +21 -0
  99. package/dist/core/understanding/brain/migrate.js +47 -0
  100. package/dist/core/understanding/brain/query.d.ts +60 -0
  101. package/dist/core/understanding/brain/query.js +92 -0
  102. package/dist/core/understanding/brain/security.d.ts +3 -0
  103. package/dist/core/understanding/brain/security.js +43 -0
  104. package/dist/core/understanding/brain/storage/index.d.ts +2 -0
  105. package/dist/core/understanding/brain/storage/index.js +1 -0
  106. package/dist/core/understanding/brain/storage/store.d.ts +53 -0
  107. package/dist/core/understanding/brain/storage/store.js +305 -0
  108. package/dist/core/understanding/brain/trace.d.ts +24 -0
  109. package/dist/core/understanding/brain/trace.js +207 -0
  110. package/dist/core/understanding/brain/types.d.ts +32 -0
  111. package/dist/core/understanding/brain/types.js +8 -0
  112. package/dist/core/understanding/brain/version.d.ts +13 -0
  113. package/dist/core/understanding/brain/version.js +7 -0
  114. package/dist/core/understanding/delta/compare.d.ts +26 -0
  115. package/dist/core/understanding/delta/compare.js +74 -0
  116. package/dist/core/understanding/delta/index.d.ts +2 -0
  117. package/dist/core/understanding/delta/index.js +1 -0
  118. package/dist/core/understanding/dependencies/discover.d.ts +7 -0
  119. package/dist/core/understanding/dependencies/discover.js +550 -0
  120. package/dist/core/understanding/dependencies/extract.d.ts +10 -0
  121. package/dist/core/understanding/dependencies/extract.js +189 -0
  122. package/dist/core/understanding/dependencies/index.d.ts +4 -0
  123. package/dist/core/understanding/dependencies/index.js +3 -0
  124. package/dist/core/understanding/dependencies/models.d.ts +12 -0
  125. package/dist/core/understanding/dependencies/models.js +125 -0
  126. package/dist/core/understanding/dependencies/types.d.ts +27 -0
  127. package/dist/core/understanding/dependencies/types.js +1 -0
  128. package/dist/core/understanding/domain/discover.d.ts +14 -0
  129. package/dist/core/understanding/domain/discover.js +90 -0
  130. package/dist/core/understanding/domain/index.d.ts +2 -0
  131. package/dist/core/understanding/domain/index.js +1 -0
  132. package/dist/core/understanding/entrypoints/discover.d.ts +6 -0
  133. package/dist/core/understanding/entrypoints/discover.js +62 -0
  134. package/dist/core/understanding/entrypoints/extract.d.ts +19 -0
  135. package/dist/core/understanding/entrypoints/extract.js +43 -0
  136. package/dist/core/understanding/entrypoints/index.d.ts +4 -0
  137. package/dist/core/understanding/entrypoints/index.js +3 -0
  138. package/dist/core/understanding/entrypoints/models.d.ts +26 -0
  139. package/dist/core/understanding/entrypoints/models.js +216 -0
  140. package/dist/core/understanding/entrypoints/types.d.ts +20 -0
  141. package/dist/core/understanding/entrypoints/types.js +1 -0
  142. package/dist/core/understanding/index.d.ts +32 -0
  143. package/dist/core/understanding/index.js +18 -0
  144. package/dist/core/understanding/mind/build.d.ts +16 -0
  145. package/dist/core/understanding/mind/build.js +134 -0
  146. package/dist/core/understanding/mind/index.d.ts +6 -0
  147. package/dist/core/understanding/mind/index.js +3 -0
  148. package/dist/core/understanding/mind/query.d.ts +33 -0
  149. package/dist/core/understanding/mind/query.js +39 -0
  150. package/dist/core/understanding/mind/types.d.ts +24 -0
  151. package/dist/core/understanding/mind/types.js +7 -0
  152. package/dist/core/understanding/model/builder.d.ts +9 -0
  153. package/dist/core/understanding/model/builder.js +209 -0
  154. package/dist/core/understanding/model/ids.d.ts +7 -0
  155. package/dist/core/understanding/model/ids.js +48 -0
  156. package/dist/core/understanding/model/index.d.ts +7 -0
  157. package/dist/core/understanding/model/index.js +6 -0
  158. package/dist/core/understanding/model/schema.d.ts +12 -0
  159. package/dist/core/understanding/model/schema.js +33 -0
  160. package/dist/core/understanding/model/serializer.d.ts +9 -0
  161. package/dist/core/understanding/model/serializer.js +46 -0
  162. package/dist/core/understanding/model/types.d.ts +110 -0
  163. package/dist/core/understanding/model/types.js +1 -0
  164. package/dist/core/understanding/model/validator.d.ts +5 -0
  165. package/dist/core/understanding/model/validator.js +126 -0
  166. package/dist/core/understanding/model/version.d.ts +9 -0
  167. package/dist/core/understanding/model/version.js +7 -0
  168. package/dist/core/understanding/ownership/discover.d.ts +12 -0
  169. package/dist/core/understanding/ownership/discover.js +242 -0
  170. package/dist/core/understanding/ownership/index.d.ts +2 -0
  171. package/dist/core/understanding/ownership/index.js +1 -0
  172. package/dist/core/understanding/ownership/types.d.ts +23 -0
  173. package/dist/core/understanding/ownership/types.js +1 -0
  174. package/dist/core/understanding/query/engine.d.ts +24 -0
  175. package/dist/core/understanding/query/engine.js +35 -0
  176. package/dist/core/understanding/query/errors.d.ts +13 -0
  177. package/dist/core/understanding/query/errors.js +26 -0
  178. package/dist/core/understanding/query/executor.d.ts +8 -0
  179. package/dist/core/understanding/query/executor.js +34 -0
  180. package/dist/core/understanding/query/index.d.ts +7 -0
  181. package/dist/core/understanding/query/index.js +5 -0
  182. package/dist/core/understanding/query/models.d.ts +102 -0
  183. package/dist/core/understanding/query/models.js +1 -0
  184. package/dist/core/understanding/query/query.d.ts +68 -0
  185. package/dist/core/understanding/query/query.js +145 -0
  186. package/dist/core/understanding/query/registry.d.ts +8 -0
  187. package/dist/core/understanding/query/registry.js +340 -0
  188. package/dist/core/understanding/query/types.d.ts +63 -0
  189. package/dist/core/understanding/query/types.js +1 -0
  190. package/dist/core/understanding/relationships/discover.d.ts +11 -0
  191. package/dist/core/understanding/relationships/discover.js +517 -0
  192. package/dist/core/understanding/relationships/extract.d.ts +14 -0
  193. package/dist/core/understanding/relationships/extract.js +165 -0
  194. package/dist/core/understanding/relationships/index.d.ts +4 -0
  195. package/dist/core/understanding/relationships/index.js +3 -0
  196. package/dist/core/understanding/relationships/models.d.ts +21 -0
  197. package/dist/core/understanding/relationships/models.js +185 -0
  198. package/dist/core/understanding/relationships/types.d.ts +42 -0
  199. package/dist/core/understanding/relationships/types.js +1 -0
  200. package/dist/core/understanding/risks/discover.d.ts +8 -0
  201. package/dist/core/understanding/risks/discover.js +155 -0
  202. package/dist/core/understanding/risks/index.d.ts +2 -0
  203. package/dist/core/understanding/risks/index.js +1 -0
  204. package/dist/core/understanding/risks/types.d.ts +26 -0
  205. package/dist/core/understanding/risks/types.js +1 -0
  206. package/dist/core/understanding/shared/domain-lexicon.d.ts +9 -0
  207. package/dist/core/understanding/shared/domain-lexicon.js +107 -0
  208. package/dist/core/understanding/shared/index.d.ts +2 -0
  209. package/dist/core/understanding/shared/index.js +2 -0
  210. package/dist/core/understanding/shared/tokens.d.ts +7 -0
  211. package/dist/core/understanding/shared/tokens.js +141 -0
  212. package/dist/core/understanding/snapshot/identity.d.ts +24 -0
  213. package/dist/core/understanding/snapshot/identity.js +79 -0
  214. package/dist/core/understanding/snapshot/index.d.ts +2 -0
  215. package/dist/core/understanding/snapshot/index.js +1 -0
  216. package/dist/core/understanding/types/index.d.ts +22 -0
  217. package/dist/core/understanding/types/index.js +1 -0
  218. package/dist/core/understanding/understand/formatter.d.ts +10 -0
  219. package/dist/core/understanding/understand/formatter.js +115 -0
  220. package/dist/core/understanding/understand/index.d.ts +4 -0
  221. package/dist/core/understanding/understand/index.js +3 -0
  222. package/dist/core/understanding/understand/service.d.ts +17 -0
  223. package/dist/core/understanding/understand/service.js +76 -0
  224. package/dist/core/understanding/understand/summary.d.ts +14 -0
  225. package/dist/core/understanding/understand/summary.js +39 -0
  226. package/dist/core/understanding/understand/types.d.ts +31 -0
  227. package/dist/core/understanding/understand/types.js +1 -0
  228. package/dist/discovery/files.js +7 -2
  229. package/dist/discovery/log-like.d.ts +2 -0
  230. package/dist/discovery/log-like.js +7 -0
  231. package/dist/index.d.ts +2 -0
  232. package/dist/index.js +1 -0
  233. package/dist/mcp/brain/compile.d.ts +9 -0
  234. package/dist/mcp/brain/compile.js +42 -0
  235. package/dist/mcp/brain/errors.d.ts +12 -0
  236. package/dist/mcp/brain/errors.js +23 -0
  237. package/dist/mcp/brain/index.d.ts +11 -0
  238. package/dist/mcp/brain/index.js +8 -0
  239. package/dist/mcp/brain/provenance.d.ts +29 -0
  240. package/dist/mcp/brain/provenance.js +59 -0
  241. package/dist/mcp/brain/schemas.d.ts +13 -0
  242. package/dist/mcp/brain/schemas.js +105 -0
  243. package/dist/mcp/brain/security/root.d.ts +6 -0
  244. package/dist/mcp/brain/security/root.js +66 -0
  245. package/dist/mcp/brain/server.d.ts +12 -0
  246. package/dist/mcp/brain/server.js +46 -0
  247. package/dist/mcp/brain/session.d.ts +33 -0
  248. package/dist/mcp/brain/session.js +117 -0
  249. package/dist/mcp/brain/tools/handlers.d.ts +13 -0
  250. package/dist/mcp/brain/tools/handlers.js +483 -0
  251. package/dist/mcp/brain/tools/registry.d.ts +9 -0
  252. package/dist/mcp/brain/tools/registry.js +191 -0
  253. package/dist/reporters/github/annotations.d.ts +8 -0
  254. package/dist/reporters/github/annotations.js +42 -0
  255. package/dist/reporters/github/emit.d.ts +23 -0
  256. package/dist/reporters/github/emit.js +31 -0
  257. package/dist/reporters/github/summary.d.ts +20 -0
  258. package/dist/reporters/github/summary.js +111 -0
  259. package/dist/reporters/terminal/report.d.ts +5 -0
  260. package/dist/reporters/terminal/report.js +68 -5
  261. package/dist/reporters/verify/terminal.d.ts +4 -0
  262. package/dist/reporters/verify/terminal.js +26 -0
  263. package/dist/utils/fs.d.ts +5 -0
  264. package/dist/utils/fs.js +34 -0
  265. package/dist/utils/path.d.ts +5 -0
  266. package/dist/utils/path.js +16 -0
  267. package/package.json +15 -1
@@ -0,0 +1,279 @@
1
+ import { emptyRuleResult, findRelationships, hasRelationship, relationshipEvidenceLines, } from "../models.js";
2
+ function evaluateMvc(ctx) {
3
+ const result = emptyRuleResult();
4
+ const routeToController = findRelationships(ctx.input, {
5
+ relationship: "EXPOSES",
6
+ targetIncludes: "Controller",
7
+ });
8
+ const controllerToService = findRelationships(ctx.input, {
9
+ relationship: ["USES", "CALLS"],
10
+ sourceIncludes: "Controller",
11
+ targetIncludes: "Service",
12
+ });
13
+ if (routeToController.length > 0) {
14
+ result.matchedRules.push("Route -> Controller");
15
+ result.evidence.push(...relationshipEvidenceLines(routeToController));
16
+ result.supportScore += 1.2;
17
+ }
18
+ if (controllerToService.length > 0) {
19
+ result.matchedRules.push("Controller -> Service");
20
+ result.evidence.push(...relationshipEvidenceLines(controllerToService));
21
+ result.supportScore += 1.4;
22
+ }
23
+ const widgetBloc = findRelationships(ctx.input, {
24
+ relationship: "USES",
25
+ sourceIncludes: "Widget",
26
+ targetIncludes: "Bloc",
27
+ });
28
+ if (widgetBloc.length > 0) {
29
+ result.conflictingEvidence.push(...relationshipEvidenceLines(widgetBloc).map((e) => `BLoC-style edge conflicts with MVC: ${e}`));
30
+ result.conflictScore += 1.1;
31
+ }
32
+ if (result.matchedRules.length > 0 && !hasRelationship(ctx.input, { evidenceIncludes: "View" })) {
33
+ result.unknowns.push("No View layer evidence");
34
+ }
35
+ return result;
36
+ }
37
+ function evaluateMvvm(ctx) {
38
+ const result = emptyRuleResult(["No explicit ViewModel role in relationship graph"]);
39
+ const viewModelish = findRelationships(ctx.input, {
40
+ evidenceIncludes: ["viewmodel", "view-model", "ViewModel"],
41
+ });
42
+ const widgetToService = findRelationships(ctx.input, {
43
+ relationship: "USES",
44
+ sourceIncludes: ["Widget", "View"],
45
+ targetIncludes: ["Service", "ViewModel", "Model"],
46
+ });
47
+ if (viewModelish.length > 0) {
48
+ result.matchedRules.push("ViewModel naming in relationships");
49
+ result.evidence.push(...relationshipEvidenceLines(viewModelish));
50
+ result.supportScore += 1.5;
51
+ result.unknowns = [];
52
+ }
53
+ if (widgetToService.length > 0 && viewModelish.length > 0) {
54
+ result.matchedRules.push("View/Widget binds to model layer");
55
+ result.evidence.push(...relationshipEvidenceLines(widgetToService));
56
+ result.supportScore += 1;
57
+ }
58
+ const controllerService = findRelationships(ctx.input, {
59
+ relationship: "USES",
60
+ sourceIncludes: "Controller",
61
+ targetIncludes: "Service",
62
+ });
63
+ if (controllerService.length > 0 && viewModelish.length === 0) {
64
+ result.conflictingEvidence.push("Controller -> Service edges look like MVC, not MVVM");
65
+ result.conflictScore += 0.8;
66
+ }
67
+ return result;
68
+ }
69
+ function evaluateClean(ctx) {
70
+ const result = emptyRuleResult();
71
+ const controllerService = findRelationships(ctx.input, {
72
+ relationship: ["USES", "CALLS"],
73
+ sourceIncludes: "Controller",
74
+ targetIncludes: "Service",
75
+ });
76
+ const serviceRepo = findRelationships(ctx.input, {
77
+ relationship: "USES",
78
+ sourceIncludes: "Service",
79
+ targetIncludes: "Repository",
80
+ });
81
+ const repoDb = findRelationships(ctx.input, {
82
+ relationship: "USES",
83
+ sourceIncludes: "Repository",
84
+ targetIncludes: ["Database", "Entity"],
85
+ });
86
+ const implementsPort = findRelationships(ctx.input, {
87
+ relationship: "IMPLEMENTS",
88
+ });
89
+ if (controllerService.length && serviceRepo.length && repoDb.length) {
90
+ result.matchedRules.push("Presentation -> Application -> Domain -> Infrastructure");
91
+ result.evidence.push(...relationshipEvidenceLines(controllerService), ...relationshipEvidenceLines(serviceRepo), ...relationshipEvidenceLines(repoDb));
92
+ result.supportScore += 2.2;
93
+ }
94
+ else {
95
+ if (controllerService.length) {
96
+ result.matchedRules.push("Presentation calls application services");
97
+ result.evidence.push(...relationshipEvidenceLines(controllerService));
98
+ result.supportScore += 0.7;
99
+ }
100
+ if (serviceRepo.length) {
101
+ result.matchedRules.push("Application depends on repositories");
102
+ result.evidence.push(...relationshipEvidenceLines(serviceRepo));
103
+ result.supportScore += 0.8;
104
+ }
105
+ if (repoDb.length) {
106
+ result.matchedRules.push("Repositories touch infrastructure/persistence");
107
+ result.evidence.push(...relationshipEvidenceLines(repoDb));
108
+ result.supportScore += 0.8;
109
+ }
110
+ }
111
+ if (implementsPort.length > 0) {
112
+ result.matchedRules.push("Repositories behind interfaces");
113
+ result.evidence.push(...relationshipEvidenceLines(implementsPort));
114
+ result.supportScore += 1.3;
115
+ }
116
+ else if (result.matchedRules.length > 0) {
117
+ result.unknowns.push("No dependency inversion metadata");
118
+ }
119
+ const controllerRepo = findRelationships(ctx.input, {
120
+ relationship: "USES",
121
+ sourceIncludes: "Controller",
122
+ targetIncludes: ["Repository", "Database", "Entity"],
123
+ });
124
+ if (controllerRepo.length > 0) {
125
+ result.conflictingEvidence.push(`Infrastructure accessed directly in ${controllerRepo.length} module(s)`);
126
+ result.conflictScore += 1.2 * Math.min(controllerRepo.length, 3);
127
+ }
128
+ return result;
129
+ }
130
+ function evaluateLayered(ctx) {
131
+ const result = emptyRuleResult();
132
+ const layers = [
133
+ findRelationships(ctx.input, {
134
+ relationship: ["USES", "CALLS", "EXPOSES"],
135
+ evidenceIncludes: "Controller → Service",
136
+ }),
137
+ findRelationships(ctx.input, {
138
+ relationship: "USES",
139
+ evidenceIncludes: "Service → Repository",
140
+ }),
141
+ findRelationships(ctx.input, {
142
+ relationship: "USES",
143
+ evidenceIncludes: "Repository → Database",
144
+ }),
145
+ ];
146
+ let depth = 0;
147
+ if (layers[0]?.length) {
148
+ depth += 1;
149
+ result.matchedRules.push("Controller layer above Service layer");
150
+ result.evidence.push(...relationshipEvidenceLines(layers[0]));
151
+ }
152
+ if (layers[1]?.length) {
153
+ depth += 1;
154
+ result.matchedRules.push("Service layer above Repository layer");
155
+ result.evidence.push(...relationshipEvidenceLines(layers[1]));
156
+ }
157
+ if (layers[2]?.length) {
158
+ depth += 1;
159
+ result.matchedRules.push("Repository layer above persistence");
160
+ result.evidence.push(...relationshipEvidenceLines(layers[2]));
161
+ }
162
+ if (depth >= 2) {
163
+ result.supportScore += depth * 0.9;
164
+ }
165
+ if (depth >= 2 && findRelationships(ctx.input, { relationship: "IMPLEMENTS" }).length === 0) {
166
+ result.unknowns.push("Layering observed without explicit ports/interfaces");
167
+ }
168
+ return result;
169
+ }
170
+ function evaluateHexagonal(ctx) {
171
+ const result = emptyRuleResult();
172
+ const ports = findRelationships(ctx.input, { relationship: "IMPLEMENTS" });
173
+ const moduleProvides = findRelationships(ctx.input, {
174
+ relationship: "PROVIDES",
175
+ sourceIncludes: "Module",
176
+ });
177
+ const configures = findRelationships(ctx.input, { relationship: "CONFIGURES" });
178
+ if (ports.length > 0) {
179
+ result.matchedRules.push("Port interfaces implemented by adapters");
180
+ result.evidence.push(...relationshipEvidenceLines(ports));
181
+ result.supportScore += 1.4;
182
+ }
183
+ if (moduleProvides.length > 0) {
184
+ result.matchedRules.push("Composition root wires application ports");
185
+ result.evidence.push(...relationshipEvidenceLines(moduleProvides));
186
+ result.supportScore += 1;
187
+ }
188
+ if (configures.length > 0) {
189
+ result.matchedRules.push("Adapters configured at boundaries");
190
+ result.evidence.push(...relationshipEvidenceLines(configures));
191
+ result.supportScore += 0.8;
192
+ }
193
+ if (result.matchedRules.length > 0 && ports.length === 0) {
194
+ result.unknowns.push("No explicit port/adapter naming beyond module wiring");
195
+ }
196
+ const leak = findRelationships(ctx.input, {
197
+ relationship: "USES",
198
+ sourceIncludes: "Controller",
199
+ targetIncludes: ["Database", "Entity"],
200
+ });
201
+ if (leak.length > 0) {
202
+ result.conflictingEvidence.push("Controllers depend on infrastructure entities");
203
+ result.conflictScore += 1;
204
+ }
205
+ return result;
206
+ }
207
+ function evaluateOnion(ctx) {
208
+ const result = emptyRuleResult();
209
+ const domainNames = ctx.input.domains.domains.map((d) => d.name);
210
+ const domainProvides = domainNames.length > 0
211
+ ? findRelationships(ctx.input, {
212
+ relationship: "PROVIDES",
213
+ targetIncludes: domainNames,
214
+ })
215
+ : [];
216
+ const featureContains = findRelationships(ctx.input, { relationship: "CONTAINS" });
217
+ const repo = findRelationships(ctx.input, {
218
+ relationship: "USES",
219
+ sourceIncludes: "Service",
220
+ targetIncludes: "Repository",
221
+ });
222
+ if (ctx.input.domains.domains.length > 0 &&
223
+ (domainProvides.length > 0 || featureContains.length > 0)) {
224
+ result.matchedRules.push("Domain concepts at the center of feature slices");
225
+ result.evidence.push(...ctx.input.domains.domains.slice(0, 3).map((d) => `domain:${d.name}`), ...relationshipEvidenceLines(domainProvides));
226
+ result.supportScore += 1.2;
227
+ }
228
+ if (repo.length > 0) {
229
+ result.matchedRules.push("Outer layers depend inward toward repositories/domain");
230
+ result.evidence.push(...relationshipEvidenceLines(repo));
231
+ result.supportScore += 1;
232
+ }
233
+ if (result.matchedRules.length > 0 && domainProvides.length === 0) {
234
+ result.unknowns.push("Domain labels present but weak inward dependency proof");
235
+ }
236
+ return result;
237
+ }
238
+ function evaluateDdd(ctx) {
239
+ const result = emptyRuleResult();
240
+ if (ctx.input.domains.domains.length >= 1) {
241
+ result.matchedRules.push("Bounded domain vocabulary discovered");
242
+ result.evidence.push(...ctx.input.domains.domains
243
+ .slice(0, 4)
244
+ .map((d) => `domain ${d.name} (confidence ${d.confidence}) via ${d.evidence[0] ?? "path"}`));
245
+ result.supportScore += Math.min(ctx.input.domains.domains.length, 3) * 0.7;
246
+ }
247
+ const provides = findRelationships(ctx.input, {
248
+ relationship: "PROVIDES",
249
+ evidenceIncludes: "domain",
250
+ });
251
+ if (provides.length > 0) {
252
+ result.matchedRules.push("Features provide domain capabilities");
253
+ result.evidence.push(...relationshipEvidenceLines(provides));
254
+ result.supportScore += 1.1;
255
+ }
256
+ const aggregates = findRelationships(ctx.input, {
257
+ relationship: "USES",
258
+ sourceIncludes: "Repository",
259
+ targetIncludes: ["Entity", "Database"],
260
+ });
261
+ if (aggregates.length > 0) {
262
+ result.matchedRules.push("Repositories protect persistence/entity access");
263
+ result.evidence.push(...relationshipEvidenceLines(aggregates));
264
+ result.supportScore += 0.9;
265
+ }
266
+ if (result.matchedRules.length > 0 && provides.length === 0) {
267
+ result.unknowns.push("No explicit aggregate/root relationship metadata");
268
+ }
269
+ return result;
270
+ }
271
+ export const layeredPatterns = [
272
+ { pattern: "MVC", evaluate: evaluateMvc },
273
+ { pattern: "MVVM", evaluate: evaluateMvvm },
274
+ { pattern: "Clean Architecture", evaluate: evaluateClean },
275
+ { pattern: "Layered Architecture", evaluate: evaluateLayered },
276
+ { pattern: "Hexagonal", evaluate: evaluateHexagonal },
277
+ { pattern: "Onion", evaluate: evaluateOnion },
278
+ { pattern: "DDD", evaluate: evaluateDdd },
279
+ ];
@@ -0,0 +1,2 @@
1
+ import type { ArchitecturePatternDefinition } from "../types.js";
2
+ export declare const structuralPatterns: ArchitecturePatternDefinition[];
@@ -0,0 +1,197 @@
1
+ import { emptyRuleResult, findDependencies, findEntrypoints, findRelationships, relationshipEvidenceLines, } from "../models.js";
2
+ function evaluateRepository(ctx) {
3
+ const result = emptyRuleResult();
4
+ const serviceRepo = findRelationships(ctx.input, {
5
+ relationship: "USES",
6
+ sourceIncludes: "Service",
7
+ targetIncludes: "Repository",
8
+ });
9
+ const repoDb = findRelationships(ctx.input, {
10
+ relationship: "USES",
11
+ sourceIncludes: "Repository",
12
+ targetIncludes: ["Database", "Entity"],
13
+ });
14
+ if (serviceRepo.length > 0) {
15
+ result.matchedRules.push("Services depend on repositories");
16
+ result.evidence.push(...relationshipEvidenceLines(serviceRepo));
17
+ result.supportScore += 1.4;
18
+ }
19
+ if (repoDb.length > 0) {
20
+ result.matchedRules.push("Repositories encapsulate persistence");
21
+ result.evidence.push(...relationshipEvidenceLines(repoDb));
22
+ result.supportScore += 1.2;
23
+ }
24
+ const leak = findRelationships(ctx.input, {
25
+ relationship: "USES",
26
+ sourceIncludes: "Controller",
27
+ targetIncludes: ["Database", "Entity"],
28
+ });
29
+ if (leak.length > 0) {
30
+ result.conflictingEvidence.push("Controllers bypass repositories to persistence");
31
+ result.conflictScore += 1;
32
+ }
33
+ if (result.matchedRules.length > 0 && repoDb.length === 0) {
34
+ result.unknowns.push("Repository present without clear persistence target");
35
+ }
36
+ return result;
37
+ }
38
+ function evaluateServiceLayer(ctx) {
39
+ const result = emptyRuleResult();
40
+ const controllerService = findRelationships(ctx.input, {
41
+ relationship: ["USES", "CALLS"],
42
+ sourceIncludes: ["Controller", "API", "Route"],
43
+ targetIncludes: "Service",
44
+ });
45
+ const apiService = findRelationships(ctx.input, {
46
+ relationship: "CALLS",
47
+ sourceIncludes: "API",
48
+ targetIncludes: "Service",
49
+ });
50
+ if (controllerService.length > 0) {
51
+ result.matchedRules.push("Application entrypoints delegate to services");
52
+ result.evidence.push(...relationshipEvidenceLines(controllerService));
53
+ result.supportScore += 1.5;
54
+ }
55
+ if (apiService.length > 0) {
56
+ result.matchedRules.push("API layer calls service layer");
57
+ result.evidence.push(...relationshipEvidenceLines(apiService));
58
+ result.supportScore += 1.1;
59
+ }
60
+ if (result.matchedRules.length > 0) {
61
+ const serviceRepo = findRelationships(ctx.input, {
62
+ relationship: "USES",
63
+ sourceIncludes: "Service",
64
+ targetIncludes: "Repository",
65
+ });
66
+ if (serviceRepo.length === 0) {
67
+ result.unknowns.push("Service layer detected without downstream repository edges");
68
+ }
69
+ }
70
+ return result;
71
+ }
72
+ function evaluateFeatureFirst(ctx) {
73
+ const result = emptyRuleResult();
74
+ const contains = findRelationships(ctx.input, { relationship: "CONTAINS" });
75
+ const consumes = findRelationships(ctx.input, { relationship: "CONSUMES" });
76
+ const owns = findRelationships(ctx.input, { relationship: "OWNS" });
77
+ if (contains.length >= 2) {
78
+ result.matchedRules.push("Features contain multiple components");
79
+ result.evidence.push(...relationshipEvidenceLines(contains, 4));
80
+ result.supportScore += 1.3;
81
+ }
82
+ if (consumes.length > 0) {
83
+ result.matchedRules.push("Cross-feature consumption edges");
84
+ result.evidence.push(...relationshipEvidenceLines(consumes));
85
+ result.supportScore += 1.2;
86
+ }
87
+ if (owns.length > 0) {
88
+ result.matchedRules.push("Packages own feature slices");
89
+ result.evidence.push(...relationshipEvidenceLines(owns));
90
+ result.supportScore += 0.8;
91
+ }
92
+ if (result.matchedRules.length > 0 && consumes.length === 0) {
93
+ result.unknowns.push("Feature folders present without cross-feature dependency proof");
94
+ }
95
+ return result;
96
+ }
97
+ function evaluateBloc(ctx) {
98
+ const result = emptyRuleResult();
99
+ const widgetBloc = findRelationships(ctx.input, {
100
+ relationship: "USES",
101
+ sourceIncludes: "Widget",
102
+ targetIncludes: "Bloc",
103
+ });
104
+ const blocRepo = findRelationships(ctx.input, {
105
+ relationship: "USES",
106
+ sourceIncludes: "Bloc",
107
+ targetIncludes: "Repository",
108
+ });
109
+ const flutter = findEntrypoints(ctx.input, { framework: "Flutter" });
110
+ if (widgetBloc.length > 0) {
111
+ result.matchedRules.push("Widget -> Bloc");
112
+ result.evidence.push(...relationshipEvidenceLines(widgetBloc));
113
+ result.supportScore += 1.6;
114
+ }
115
+ if (blocRepo.length > 0) {
116
+ result.matchedRules.push("Bloc -> Repository");
117
+ result.evidence.push(...relationshipEvidenceLines(blocRepo));
118
+ result.supportScore += 1.3;
119
+ }
120
+ if (flutter.length > 0 && result.matchedRules.length > 0) {
121
+ result.matchedRules.push("Flutter entrypoint accompanies BLoC edges");
122
+ result.evidence.push(...flutter.slice(0, 2).map((e) => `entrypoint:${e.file}`));
123
+ result.supportScore += 0.7;
124
+ }
125
+ const mvc = findRelationships(ctx.input, {
126
+ relationship: "USES",
127
+ sourceIncludes: "Controller",
128
+ targetIncludes: "Service",
129
+ });
130
+ if (mvc.length > 0 && widgetBloc.length > 0) {
131
+ result.conflictingEvidence.push("Controller/Service MVC edges coexist with BLoC UI edges");
132
+ result.conflictScore += 0.4;
133
+ }
134
+ if (widgetBloc.length > 0 && blocRepo.length === 0) {
135
+ result.unknowns.push("BLoC UI wiring without repository edge evidence");
136
+ }
137
+ return result;
138
+ }
139
+ function evaluateRiverpod(ctx) {
140
+ const result = emptyRuleResult(["No Riverpod-specific provider graph metadata"]);
141
+ const providerish = [
142
+ ...findRelationships(ctx.input, { evidenceIncludes: ["riverpod", "Provider", "ref.watch"] }),
143
+ ...findDependencies(ctx.input, { evidenceIncludes: ["riverpod"] }),
144
+ ...findEntrypoints(ctx.input, { evidenceIncludes: ["riverpod", "Provider"] }),
145
+ ];
146
+ if (providerish.length === 0) {
147
+ return result;
148
+ }
149
+ result.unknowns = [];
150
+ result.matchedRules.push("Riverpod/Provider evidence in discovery outputs");
151
+ result.evidence.push(...providerish.slice(0, 4).map((item) => {
152
+ if ("relationship" in item) {
153
+ return `${item.source} ${item.relationship} ${item.target}`;
154
+ }
155
+ if ("framework" in item) {
156
+ return `entrypoint:${item.file}`;
157
+ }
158
+ return `dependency:${item.from}->${item.to}`;
159
+ }));
160
+ result.supportScore += 1.5;
161
+ const flutter = findEntrypoints(ctx.input, { framework: "Flutter" });
162
+ if (flutter.length === 0) {
163
+ result.conflictingEvidence.push("Riverpod signals without Flutter entrypoints");
164
+ result.conflictScore += 0.6;
165
+ }
166
+ return result;
167
+ }
168
+ function evaluateRedux(ctx) {
169
+ const result = emptyRuleResult(["No reducer/store relationship metadata"]);
170
+ const reduxish = [
171
+ ...findRelationships(ctx.input, {
172
+ evidenceIncludes: ["redux", "reducer", "createStore", "useSelector", "dispatch"],
173
+ }),
174
+ ...findDependencies(ctx.input, { evidenceIncludes: ["redux", "reducer", "store"] }),
175
+ ];
176
+ if (reduxish.length === 0) {
177
+ return result;
178
+ }
179
+ result.unknowns = [];
180
+ result.matchedRules.push("Redux store/reducer evidence in discovery outputs");
181
+ result.evidence.push(...reduxish.slice(0, 4).map((item) => {
182
+ if ("relationship" in item) {
183
+ return `${item.source} ${item.relationship} ${item.target}`;
184
+ }
185
+ return `dependency:${item.from}->${item.to}`;
186
+ }));
187
+ result.supportScore += 1.5;
188
+ return result;
189
+ }
190
+ export const structuralPatterns = [
191
+ { pattern: "Repository Pattern", evaluate: evaluateRepository },
192
+ { pattern: "Service Layer", evaluate: evaluateServiceLayer },
193
+ { pattern: "Feature-first", evaluate: evaluateFeatureFirst },
194
+ { pattern: "BLoC", evaluate: evaluateBloc },
195
+ { pattern: "Riverpod", evaluate: evaluateRiverpod },
196
+ { pattern: "Redux", evaluate: evaluateRedux },
197
+ ];
@@ -0,0 +1,4 @@
1
+ import type { ArchitecturePatternDefinition } from "./types.js";
2
+ /** Ordered pattern rule registry used by the inference engine. */
3
+ export declare const ARCHITECTURE_RULES: readonly ArchitecturePatternDefinition[];
4
+ export declare function listArchitecturePatterns(): string[];
@@ -0,0 +1,6 @@
1
+ import { ARCHITECTURE_PATTERNS } from "./patterns/index.js";
2
+ /** Ordered pattern rule registry used by the inference engine. */
3
+ export const ARCHITECTURE_RULES = ARCHITECTURE_PATTERNS;
4
+ export function listArchitecturePatterns() {
5
+ return ARCHITECTURE_RULES.map((rule) => rule.pattern);
6
+ }
@@ -0,0 +1,52 @@
1
+ import type { DependencyDiscoveryResult } from "../dependencies/types.js";
2
+ import type { EntrypointDiscoveryResult } from "../entrypoints/types.js";
3
+ import type { RelationshipDiscoveryResult, RelationshipKind } from "../relationships/types.js";
4
+ import type { DomainDiscoveryResult } from "../types/index.js";
5
+ export type ArchitecturePattern = "MVC" | "MVVM" | "Clean Architecture" | "Layered Architecture" | "Hexagonal" | "Onion" | "DDD" | "Repository Pattern" | "Service Layer" | "Feature-first" | "BLoC" | "Riverpod" | "Redux" | "Microservice" | "Modular Monolith" | "Plugin Architecture" | "Monorepo Workspace";
6
+ export interface ArchitectureInferenceInput {
7
+ domains: DomainDiscoveryResult;
8
+ entrypoints: EntrypointDiscoveryResult;
9
+ dependencies: DependencyDiscoveryResult;
10
+ relationships: RelationshipDiscoveryResult;
11
+ }
12
+ export interface ArchitectureMatch {
13
+ pattern: ArchitecturePattern;
14
+ confidence: number;
15
+ evidence: string[];
16
+ matchedRules: string[];
17
+ conflictingEvidence: string[];
18
+ unknowns: string[];
19
+ }
20
+ export interface ArchitectureInferenceResult {
21
+ architectures: ArchitectureMatch[];
22
+ timingMs: number;
23
+ patternsEvaluated: number;
24
+ }
25
+ export interface ArchitectureInferenceOptions {
26
+ /** Minimum confidence to include a pattern (0–1). Default 0.55. Never reaches 1.0. */
27
+ minConfidence?: number;
28
+ }
29
+ export interface PatternRuleContext {
30
+ input: ArchitectureInferenceInput;
31
+ }
32
+ export interface PatternRuleResult {
33
+ matchedRules: string[];
34
+ evidence: string[];
35
+ conflictingEvidence: string[];
36
+ unknowns: string[];
37
+ /** Relative weight of support before conflict penalties. */
38
+ supportScore: number;
39
+ /** Relative weight of conflicts. */
40
+ conflictScore: number;
41
+ }
42
+ export type PatternEvaluator = (ctx: PatternRuleContext) => PatternRuleResult;
43
+ export interface ArchitecturePatternDefinition {
44
+ pattern: ArchitecturePattern;
45
+ evaluate: PatternEvaluator;
46
+ }
47
+ export type RelationPredicate = {
48
+ relationship?: RelationshipKind | RelationshipKind[];
49
+ sourceIncludes?: string | string[];
50
+ targetIncludes?: string | string[];
51
+ evidenceIncludes?: string | string[];
52
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ import type { ProjectModel } from "../model/types.js";
2
+ import { type OwnershipDiscoveryResult } from "../ownership/index.js";
3
+ import { type RiskDiscoveryResult } from "../risks/index.js";
4
+ import { type BrainClaim } from "./claims/index.js";
5
+ import { type ProjectBrain } from "./types.js";
6
+ export interface BuildProjectBrainOptions {
7
+ cwd?: string;
8
+ ownership?: OwnershipDiscoveryResult;
9
+ risks?: RiskDiscoveryResult;
10
+ previousClaims?: readonly BrainClaim[];
11
+ generatedAt?: string;
12
+ }
13
+ export declare function buildProjectBrain(model: ProjectModel, options?: BuildProjectBrainOptions): Promise<ProjectBrain>;