code-auditor-mcp 3.1.1 → 3.4.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 (357) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +665 -1
  3. package/README.md +85 -11
  4. package/dist/__test_tmp__/spec18_test.d.ts +2 -0
  5. package/dist/__test_tmp__/spec18_test.d.ts.map +1 -0
  6. package/dist/__test_tmp__/spec18_test.js +5 -0
  7. package/dist/__test_tmp__/spec18_test.js.map +1 -0
  8. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts +6 -0
  9. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts.map +1 -0
  10. package/dist/__tests__/fixtures/spec-20/scripts/tool.js +12 -0
  11. package/dist/__tests__/fixtures/spec-20/scripts/tool.js.map +1 -0
  12. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts +7 -0
  13. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts.map +1 -0
  14. package/dist/__tests__/fixtures/spec-20/src/demo-module.js +13 -0
  15. package/dist/__tests__/fixtures/spec-20/src/demo-module.js.map +1 -0
  16. package/dist/analyzers/analyzerUtils.js +2 -2
  17. package/dist/analyzers/analyzerUtils.js.map +1 -1
  18. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +1 -1
  19. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
  20. package/dist/analyzers/cross-language/APIContractAnalyzer.js +2 -2
  21. package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
  22. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts +3 -0
  23. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts.map +1 -1
  24. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js +3 -3
  25. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js.map +1 -1
  26. package/dist/analyzers/cross-language/SchemaValidator.d.ts +1 -1
  27. package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
  28. package/dist/analyzers/cross-language/SchemaValidator.js +2 -2
  29. package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
  30. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +83 -0
  31. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -0
  32. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +611 -0
  33. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -0
  34. package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
  35. package/dist/analyzers/documentationAnalyzer.js +8 -4
  36. package/dist/analyzers/documentationAnalyzer.js.map +1 -1
  37. package/dist/analyzers/orm/adapterRegistry.d.ts +41 -0
  38. package/dist/analyzers/orm/adapterRegistry.d.ts.map +1 -0
  39. package/dist/analyzers/orm/adapterRegistry.js +73 -0
  40. package/dist/analyzers/orm/adapterRegistry.js.map +1 -0
  41. package/dist/analyzers/orm/drizzleAdapter.d.ts +42 -0
  42. package/dist/analyzers/orm/drizzleAdapter.d.ts.map +1 -0
  43. package/dist/analyzers/orm/drizzleAdapter.js +166 -0
  44. package/dist/analyzers/orm/drizzleAdapter.js.map +1 -0
  45. package/dist/analyzers/orm/index.d.ts +14 -0
  46. package/dist/analyzers/orm/index.d.ts.map +1 -0
  47. package/dist/analyzers/orm/index.js +24 -0
  48. package/dist/analyzers/orm/index.js.map +1 -0
  49. package/dist/analyzers/orm/prismaAdapter.d.ts +45 -0
  50. package/dist/analyzers/orm/prismaAdapter.d.ts.map +1 -0
  51. package/dist/analyzers/orm/prismaAdapter.js +170 -0
  52. package/dist/analyzers/orm/prismaAdapter.js.map +1 -0
  53. package/dist/analyzers/orm/types.d.ts +63 -0
  54. package/dist/analyzers/orm/types.d.ts.map +1 -0
  55. package/dist/analyzers/orm/types.js +10 -0
  56. package/dist/analyzers/orm/types.js.map +1 -0
  57. package/dist/analyzers/provenance.d.ts +133 -0
  58. package/dist/analyzers/provenance.d.ts.map +1 -0
  59. package/dist/analyzers/provenance.js +1041 -0
  60. package/dist/analyzers/provenance.js.map +1 -0
  61. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  62. package/dist/analyzers/reactAnalyzer.js +117 -1
  63. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  64. package/dist/analyzers/ruleRegistry.d.ts +38 -0
  65. package/dist/analyzers/ruleRegistry.d.ts.map +1 -0
  66. package/dist/analyzers/ruleRegistry.js +123 -0
  67. package/dist/analyzers/ruleRegistry.js.map +1 -0
  68. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +58 -0
  69. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -0
  70. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +445 -0
  71. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -0
  72. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +52 -0
  73. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
  74. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +108 -26
  75. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
  76. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +64 -0
  77. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  78. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +259 -53
  79. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  80. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
  81. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +5 -15
  82. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
  83. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  84. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +10 -10
  85. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  86. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +25 -0
  87. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
  88. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +250 -59
  89. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
  90. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +103 -0
  91. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -0
  92. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +750 -0
  93. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -0
  94. package/dist/auditRunner.d.ts.map +1 -1
  95. package/dist/auditRunner.js +483 -3
  96. package/dist/auditRunner.js.map +1 -1
  97. package/dist/baseline.d.ts +98 -0
  98. package/dist/baseline.d.ts.map +1 -0
  99. package/dist/baseline.js +184 -0
  100. package/dist/baseline.js.map +1 -0
  101. package/dist/churn/churnExtractor.d.ts +32 -0
  102. package/dist/churn/churnExtractor.d.ts.map +1 -0
  103. package/dist/churn/churnExtractor.js +261 -0
  104. package/dist/churn/churnExtractor.js.map +1 -0
  105. package/dist/cli.js +1215 -11
  106. package/dist/cli.js.map +1 -1
  107. package/dist/codeIndexDB-enhanced.js +1 -1
  108. package/dist/codeIndexDB-enhanced.js.map +1 -1
  109. package/dist/codeIndexDB.d.ts +101 -0
  110. package/dist/codeIndexDB.d.ts.map +1 -1
  111. package/dist/codeIndexDB.js +661 -5
  112. package/dist/codeIndexDB.js.map +1 -1
  113. package/dist/config/configLoader.d.ts.map +1 -1
  114. package/dist/config/configLoader.js +95 -1
  115. package/dist/config/configLoader.js.map +1 -1
  116. package/dist/config/defaults.d.ts +72 -1
  117. package/dist/config/defaults.d.ts.map +1 -1
  118. package/dist/config/defaults.js +146 -15
  119. package/dist/config/defaults.js.map +1 -1
  120. package/dist/config/pathProfiles.d.ts +38 -0
  121. package/dist/config/pathProfiles.d.ts.map +1 -0
  122. package/dist/config/pathProfiles.js +47 -0
  123. package/dist/config/pathProfiles.js.map +1 -0
  124. package/dist/conventions/conventionMiner.d.ts +63 -0
  125. package/dist/conventions/conventionMiner.d.ts.map +1 -0
  126. package/dist/conventions/conventionMiner.js +696 -0
  127. package/dist/conventions/conventionMiner.js.map +1 -0
  128. package/dist/coverage/coverageService.d.ts +49 -0
  129. package/dist/coverage/coverageService.d.ts.map +1 -0
  130. package/dist/coverage/coverageService.js +117 -0
  131. package/dist/coverage/coverageService.js.map +1 -0
  132. package/dist/coverage/istanbulParser.d.ts +84 -0
  133. package/dist/coverage/istanbulParser.d.ts.map +1 -0
  134. package/dist/coverage/istanbulParser.js +120 -0
  135. package/dist/coverage/istanbulParser.js.map +1 -0
  136. package/dist/coverage/lcovParser.d.ts +40 -0
  137. package/dist/coverage/lcovParser.d.ts.map +1 -0
  138. package/dist/coverage/lcovParser.js +168 -0
  139. package/dist/coverage/lcovParser.js.map +1 -0
  140. package/dist/fingerprint.d.ts +17 -0
  141. package/dist/fingerprint.d.ts.map +1 -1
  142. package/dist/fingerprint.js +42 -0
  143. package/dist/fingerprint.js.map +1 -1
  144. package/dist/grammars/tree-sitter-css.wasm +0 -0
  145. package/dist/graph/blastRadius.d.ts +27 -0
  146. package/dist/graph/blastRadius.d.ts.map +1 -0
  147. package/dist/graph/blastRadius.js +150 -0
  148. package/dist/graph/blastRadius.js.map +1 -0
  149. package/dist/graph/callGraph.d.ts +78 -0
  150. package/dist/graph/callGraph.d.ts.map +1 -0
  151. package/dist/graph/callGraph.js +484 -0
  152. package/dist/graph/callGraph.js.map +1 -0
  153. package/dist/graph/importGraph.d.ts +91 -0
  154. package/dist/graph/importGraph.d.ts.map +1 -0
  155. package/dist/graph/importGraph.js +602 -0
  156. package/dist/graph/importGraph.js.map +1 -0
  157. package/dist/graph/outputFormatter.d.ts +55 -0
  158. package/dist/graph/outputFormatter.d.ts.map +1 -0
  159. package/dist/graph/outputFormatter.js +283 -0
  160. package/dist/graph/outputFormatter.js.map +1 -0
  161. package/dist/hooks/codex.d.ts +36 -1
  162. package/dist/hooks/codex.d.ts.map +1 -1
  163. package/dist/hooks/codex.js +88 -54
  164. package/dist/hooks/codex.js.map +1 -1
  165. package/dist/hotspots/hotspotScorer.d.ts +20 -0
  166. package/dist/hotspots/hotspotScorer.d.ts.map +1 -0
  167. package/dist/hotspots/hotspotScorer.js +202 -0
  168. package/dist/hotspots/hotspotScorer.js.map +1 -0
  169. package/dist/invariants/invariant-rules.schema.json +42 -1
  170. package/dist/invariants/ruleEngine.d.ts +4 -1
  171. package/dist/invariants/ruleEngine.d.ts.map +1 -1
  172. package/dist/invariants/ruleEngine.js +141 -5
  173. package/dist/invariants/ruleEngine.js.map +1 -1
  174. package/dist/invariants/ruleValidator.d.ts.map +1 -1
  175. package/dist/invariants/ruleValidator.js +62 -1
  176. package/dist/invariants/ruleValidator.js.map +1 -1
  177. package/dist/invariants/types.d.ts +23 -2
  178. package/dist/invariants/types.d.ts.map +1 -1
  179. package/dist/languages/UniversalAnalyzer.d.ts +1 -1
  180. package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
  181. package/dist/languages/UniversalAnalyzer.js +61 -4
  182. package/dist/languages/UniversalAnalyzer.js.map +1 -1
  183. package/dist/languages/index.d.ts +1 -0
  184. package/dist/languages/index.d.ts.map +1 -1
  185. package/dist/languages/index.js +3 -0
  186. package/dist/languages/index.js.map +1 -1
  187. package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts +59 -0
  188. package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts.map +1 -0
  189. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +236 -0
  190. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -0
  191. package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
  192. package/dist/languages/tree-sitter/parser.js +2 -0
  193. package/dist/languages/tree-sitter/parser.js.map +1 -1
  194. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  195. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +61 -10
  196. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  197. package/dist/ledger.d.ts +128 -0
  198. package/dist/ledger.d.ts.map +1 -0
  199. package/dist/ledger.js +345 -0
  200. package/dist/ledger.js.map +1 -0
  201. package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
  202. package/dist/mcp-tools/projectTasks.js +9 -14
  203. package/dist/mcp-tools/projectTasks.js.map +1 -1
  204. package/dist/mcp.js +0 -0
  205. package/dist/mcpAuditJobs.d.ts.map +1 -1
  206. package/dist/mcpAuditJobs.js +3 -0
  207. package/dist/mcpAuditJobs.js.map +1 -1
  208. package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
  209. package/dist/reporting/csvReportGenerator.js +7 -1
  210. package/dist/reporting/csvReportGenerator.js.map +1 -1
  211. package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
  212. package/dist/reporting/htmlReportGenerator.js +5 -1
  213. package/dist/reporting/htmlReportGenerator.js.map +1 -1
  214. package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
  215. package/dist/reporting/jsonReportGenerator.js +10 -2
  216. package/dist/reporting/jsonReportGenerator.js.map +1 -1
  217. package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
  218. package/dist/reporting/sarifReportGenerator.js +17 -17
  219. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  220. package/dist/scripts/runBench.d.ts +94 -0
  221. package/dist/scripts/runBench.d.ts.map +1 -0
  222. package/dist/scripts/runBench.js +1559 -0
  223. package/dist/scripts/runBench.js.map +1 -0
  224. package/dist/search/QueryParser.d.ts.map +1 -1
  225. package/dist/search/QueryParser.js +51 -4
  226. package/dist/search/QueryParser.js.map +1 -1
  227. package/dist/services/CodeMapGenerator.d.ts +33 -0
  228. package/dist/services/CodeMapGenerator.d.ts.map +1 -1
  229. package/dist/services/CodeMapGenerator.js +404 -0
  230. package/dist/services/CodeMapGenerator.js.map +1 -1
  231. package/dist/styles/normalizer.d.ts +37 -0
  232. package/dist/styles/normalizer.d.ts.map +1 -0
  233. package/dist/styles/normalizer.js +301 -0
  234. package/dist/styles/normalizer.js.map +1 -0
  235. package/dist/styles/styleExtractor.d.ts +38 -0
  236. package/dist/styles/styleExtractor.d.ts.map +1 -0
  237. package/dist/styles/styleExtractor.js +630 -0
  238. package/dist/styles/styleExtractor.js.map +1 -0
  239. package/dist/styles/styleIndexer.d.ts +35 -0
  240. package/dist/styles/styleIndexer.d.ts.map +1 -0
  241. package/dist/styles/styleIndexer.js +266 -0
  242. package/dist/styles/styleIndexer.js.map +1 -0
  243. package/dist/styles/tailwindConfigLoader.d.ts +38 -0
  244. package/dist/styles/tailwindConfigLoader.d.ts.map +1 -0
  245. package/dist/styles/tailwindConfigLoader.js +374 -0
  246. package/dist/styles/tailwindConfigLoader.js.map +1 -0
  247. package/dist/styles/tailwindExpander.d.ts +38 -0
  248. package/dist/styles/tailwindExpander.d.ts.map +1 -0
  249. package/dist/styles/tailwindExpander.js +637 -0
  250. package/dist/styles/tailwindExpander.js.map +1 -0
  251. package/dist/styles/types.d.ts +92 -0
  252. package/dist/styles/types.d.ts.map +1 -0
  253. package/dist/styles/types.js +10 -0
  254. package/dist/styles/types.js.map +1 -0
  255. package/dist/symbols.d.ts +21 -0
  256. package/dist/symbols.d.ts.map +1 -0
  257. package/dist/symbols.js +31 -0
  258. package/dist/symbols.js.map +1 -0
  259. package/dist/types.d.ts +326 -3
  260. package/dist/types.d.ts.map +1 -1
  261. package/dist/types.js.map +1 -1
  262. package/package.json +9 -5
  263. package/plugin/.claude-plugin/plugin.json +1 -1
  264. package/plugin/scripts/hook-audit.sh +5 -1
  265. package/plugin/skills/code-auditor/SKILL.md +51 -0
  266. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.d.ts +0 -21
  267. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.d.ts.map +0 -1
  268. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.js +0 -21
  269. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.js.map +0 -1
  270. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.d.ts +0 -13
  271. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.d.ts.map +0 -1
  272. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.js +0 -12
  273. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.js.map +0 -1
  274. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.d.ts +0 -11
  275. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.d.ts.map +0 -1
  276. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.js +0 -22
  277. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.js.map +0 -1
  278. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.d.ts +0 -9
  279. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.d.ts.map +0 -1
  280. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.js +0 -13
  281. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.js.map +0 -1
  282. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.d.ts +0 -13
  283. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.d.ts.map +0 -1
  284. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.js +0 -18
  285. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.js.map +0 -1
  286. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.d.ts +0 -10
  287. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.d.ts.map +0 -1
  288. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.js +0 -16
  289. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.js.map +0 -1
  290. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.d.ts +0 -14
  291. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.d.ts.map +0 -1
  292. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.js +0 -18
  293. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.js.map +0 -1
  294. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.d.ts +0 -13
  295. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.d.ts.map +0 -1
  296. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.js +0 -22
  297. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.js.map +0 -1
  298. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.d.ts +0 -10
  299. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.d.ts.map +0 -1
  300. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.js +0 -20
  301. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.js.map +0 -1
  302. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.d.ts +0 -18
  303. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.d.ts.map +0 -1
  304. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.js +0 -28
  305. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.js.map +0 -1
  306. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.d.ts +0 -11
  307. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.d.ts.map +0 -1
  308. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.js +0 -31
  309. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.js.map +0 -1
  310. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.d.ts +0 -11
  311. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.d.ts.map +0 -1
  312. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.js +0 -18
  313. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.js.map +0 -1
  314. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.d.ts +0 -13
  315. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.d.ts.map +0 -1
  316. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.js +0 -61
  317. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.js.map +0 -1
  318. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.d.ts +0 -10
  319. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.d.ts.map +0 -1
  320. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.js +0 -15
  321. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.js.map +0 -1
  322. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.d.ts +0 -11
  323. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.d.ts.map +0 -1
  324. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.js +0 -49
  325. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.js.map +0 -1
  326. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.d.ts +0 -10
  327. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.d.ts.map +0 -1
  328. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.js +0 -19
  329. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.js.map +0 -1
  330. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.d.ts +0 -37
  331. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.d.ts.map +0 -1
  332. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.js +0 -73
  333. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.js.map +0 -1
  334. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.d.ts +0 -12
  335. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.d.ts.map +0 -1
  336. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.js +0 -82
  337. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.js.map +0 -1
  338. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.d.ts +0 -10
  339. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.d.ts.map +0 -1
  340. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.js +0 -15
  341. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.js.map +0 -1
  342. package/dist/analyzers/dataAccessAnalyzer.d.ts +0 -46
  343. package/dist/analyzers/dataAccessAnalyzer.d.ts.map +0 -1
  344. package/dist/analyzers/dataAccessAnalyzer.js +0 -474
  345. package/dist/analyzers/dataAccessAnalyzer.js.map +0 -1
  346. package/dist/analyzers/dryAnalyzer.d.ts +0 -31
  347. package/dist/analyzers/dryAnalyzer.d.ts.map +0 -1
  348. package/dist/analyzers/dryAnalyzer.js +0 -734
  349. package/dist/analyzers/dryAnalyzer.js.map +0 -1
  350. package/dist/analyzers/schemaAnalyzer.d.ts +0 -23
  351. package/dist/analyzers/schemaAnalyzer.d.ts.map +0 -1
  352. package/dist/analyzers/schemaAnalyzer.js +0 -564
  353. package/dist/analyzers/schemaAnalyzer.js.map +0 -1
  354. package/dist/analyzers/solidAnalyzer.d.ts +0 -33
  355. package/dist/analyzers/solidAnalyzer.d.ts.map +0 -1
  356. package/dist/analyzers/solidAnalyzer.js +0 -847
  357. package/dist/analyzers/solidAnalyzer.js.map +0 -1
@@ -4,13 +4,17 @@
4
4
  */
5
5
  import { statSync, readFileSync } from 'fs';
6
6
  import path from 'path';
7
+ import { fileURLToPath } from 'url';
7
8
  import { execSync } from 'child_process';
9
+ import { randomUUID } from 'node:crypto';
8
10
  import { AuditAbortedError, AuditHandoffError, } from './types.js';
9
11
  import { discoverFiles } from './utils/fileDiscovery.js';
10
12
  import { loadConfig } from './config/configLoader.js';
11
13
  import { generateReport } from './reporting/reportGenerator.js';
12
14
  import { extractFunctionsFromFile } from './functionScanner.js';
13
15
  import { isMcpDebugEnabled, logMcpDebug, logMcpInfo } from './mcpDiagnostics.js';
16
+ import { loadBaseline, matchFindings, hashBaseline } from './baseline.js';
17
+ import { computeImpact, LATENCY_BUDGET_MS } from './graph/blastRadius.js';
14
18
  // Import universal analyzers
15
19
  import { initializeLanguages } from './languages/index.js';
16
20
  import { UniversalSOLIDAnalyzer } from './analyzers/universal/UniversalSOLIDAnalyzer.js';
@@ -18,12 +22,24 @@ import { UniversalDRYAnalyzer } from './analyzers/universal/UniversalDRYAnalyzer
18
22
  import { UniversalDataAccessAnalyzer } from './analyzers/universal/UniversalDataAccessAnalyzer.js';
19
23
  import { UniversalDocumentationAnalyzer } from './analyzers/universal/UniversalDocumentationAnalyzer.js';
20
24
  import { UniversalSchemaAnalyzer } from './analyzers/universal/UniversalSchemaAnalyzer.js';
25
+ import { UniversalStylesAnalyzer } from './analyzers/universal/UniversalStylesAnalyzer.js';
26
+ import { UniversalConventionsAnalyzer } from './analyzers/universal/UniversalConventionsAnalyzer.js';
27
+ import { CrossDomainAnalyzer } from './analyzers/crossDomain/CrossDomainAnalyzer.js';
28
+ import { initializeOrmAdapters } from './analyzers/orm/index.js';
29
+ import { syncStyleIndex } from './styles/styleIndexer.js';
21
30
  import { reactAnalyzer } from './analyzers/reactAnalyzer.js';
22
31
  import { invariantsAnalyzer } from './analyzers/invariantsAnalyzer.js';
23
32
  import { hasRules } from './invariants/ruleEngine.js';
24
33
  import { CodeIndexDB } from './codeIndexDB.js';
34
+ import { writeAuditToLedger, detectRunInput } from './ledger.js';
35
+ // Package version — read once at module load
36
+ const __auditRunnerDirname = path.dirname(fileURLToPath(import.meta.url));
37
+ const _pkg = JSON.parse(readFileSync(path.join(__auditRunnerDirname, '..', 'package.json'), 'utf-8'));
38
+ const TOOL_VERSION = String(_pkg.version || '0.0.0');
25
39
  // Initialize the canonical language system once
26
40
  initializeLanguages();
41
+ // Initialize ORM adapters for cross-domain schema extraction (Spec 15 R2)
42
+ initializeOrmAdapters();
27
43
  /**
28
44
  * Shared progress callback adapter: translates universal analyzer's (number) into
29
45
  * the object-based callback that auditRunner passes downstream.
@@ -75,6 +91,13 @@ const DEFAULT_ANALYZERS = {
75
91
  universalConfig.checkInterfaceSegregation = config.checkInterfaceSegregation;
76
92
  if (config.checkLiskovSubstitution !== undefined)
77
93
  universalConfig.checkLiskovSubstitution = config.checkLiskovSubstitution;
94
+ // Forward base-class properties (Spec-20 path profiles + severity overrides)
95
+ if (config.severityOverrides !== undefined)
96
+ universalConfig.severityOverrides = config.severityOverrides;
97
+ if (config.pathProfiles !== undefined)
98
+ universalConfig.pathProfiles = config.pathProfiles;
99
+ if (config.projectRoot !== undefined)
100
+ universalConfig.projectRoot = config.projectRoot;
78
101
  const result = await analyzer.analyze(files, universalConfig, {
79
102
  progressCallback: createProgressAdapter('solid', progressCallback),
80
103
  ...options,
@@ -127,6 +150,16 @@ const DEFAULT_ANALYZERS = {
127
150
  // R4.3: directAccess — "flag" (default) or "allow"
128
151
  if (config.directAccess !== undefined)
129
152
  universalConfig.directAccess = config.directAccess;
153
+ // Forward base-class properties (Spec-20 path profiles + severity overrides)
154
+ if (config.severityOverrides !== undefined)
155
+ universalConfig.severityOverrides = config.severityOverrides;
156
+ if (config.pathProfiles !== undefined)
157
+ universalConfig.pathProfiles = config.pathProfiles;
158
+ if (config.projectRoot !== undefined)
159
+ universalConfig.projectRoot = config.projectRoot;
160
+ // Spec 21: Forward shared provenance timing accumulator
161
+ if (config._provenanceTiming !== undefined)
162
+ universalConfig._provenanceTiming = config._provenanceTiming;
130
163
  return analyzer.analyze(files, universalConfig, {
131
164
  progressCallback: createProgressAdapter('data-access', progressCallback),
132
165
  ...options,
@@ -157,6 +190,13 @@ const DEFAULT_ANALYZERS = {
157
190
  fileHeaders: config.fileHeaders ?? config.requireFileDocs ?? false, // R1.5
158
191
  headerSkipGlobs: config.headerSkipGlobs, // R1.5 (undefined → analyzer default)
159
192
  };
193
+ // Forward base-class properties (Spec-20 path profiles + severity overrides)
194
+ if (config.severityOverrides !== undefined)
195
+ universalConfig.severityOverrides = config.severityOverrides;
196
+ if (config.pathProfiles !== undefined)
197
+ universalConfig.pathProfiles = config.pathProfiles;
198
+ if (config.projectRoot !== undefined)
199
+ universalConfig.projectRoot = config.projectRoot;
160
200
  return analyzer.analyze(files, universalConfig, {
161
201
  progressCallback: createProgressAdapter('documentation', progressCallback),
162
202
  ...options,
@@ -190,7 +230,6 @@ const DEFAULT_ANALYZERS = {
190
230
  // If database isn't available, continue without schemas
191
231
  }
192
232
  const universalConfig = {
193
- enableTableUsageTracking: config.enableTableUsageTracking ?? true,
194
233
  checkMissingReferences: config.checkMissingReferences ?? true,
195
234
  checkNamingConventions: config.checkNamingConventions ?? true,
196
235
  detectUnusedTables: config.detectUnusedTables ?? false,
@@ -205,6 +244,16 @@ const DEFAULT_ANALYZERS = {
205
244
  dbBindingNames: config.dbBindingNames ?? ['env.DB'], // R2.2
206
245
  fileGateGlobs: config.fileGateGlobs, // R2.2 (let analyzer use defaults)
207
246
  };
247
+ // Forward base-class properties (Spec-20 path profiles + severity overrides)
248
+ if (config.severityOverrides !== undefined)
249
+ universalConfig.severityOverrides = config.severityOverrides;
250
+ if (config.pathProfiles !== undefined)
251
+ universalConfig.pathProfiles = config.pathProfiles;
252
+ if (config.projectRoot !== undefined)
253
+ universalConfig.projectRoot = config.projectRoot;
254
+ // Spec 21: Forward shared provenance timing accumulator
255
+ if (config._provenanceTiming !== undefined)
256
+ universalConfig._provenanceTiming = config._provenanceTiming;
208
257
  const result = await analyzer.analyze(files, universalConfig);
209
258
  return {
210
259
  ...result,
@@ -217,6 +266,98 @@ const DEFAULT_ANALYZERS = {
217
266
  };
218
267
  },
219
268
  },
269
+ 'styles': {
270
+ name: 'styles',
271
+ description: 'Detects style fragmentation, value drift, token bypass, dead classes, off-scale values, mechanism fragmentation, and z-index sprawl',
272
+ category: 'style',
273
+ analyze: async (files, config, options, progressCallback) => {
274
+ const analyzer = new UniversalStylesAnalyzer();
275
+ const universalConfig = {};
276
+ if (config.minCorpus !== undefined)
277
+ universalConfig.minCorpus = config.minCorpus;
278
+ if (config.colorDeltaE !== undefined)
279
+ universalConfig.colorDeltaE = config.colorDeltaE;
280
+ if (config.outlierMaxShare !== undefined)
281
+ universalConfig.outlierMaxShare = config.outlierMaxShare;
282
+ if (config.modeMinCount !== undefined)
283
+ universalConfig.modeMinCount = config.modeMinCount;
284
+ if (config.scaleProperties !== undefined)
285
+ universalConfig.scaleProperties = config.scaleProperties;
286
+ if (config.zIndexMaxDistinct !== undefined)
287
+ universalConfig.zIndexMaxDistinct = config.zIndexMaxDistinct;
288
+ if (config.mechanismFragmentationMinMechanisms !== undefined)
289
+ universalConfig.mechanismFragmentationMinMechanisms = config.mechanismFragmentationMinMechanisms;
290
+ if (config.declarationSetMinDeclarations !== undefined)
291
+ universalConfig.declarationSetMinDeclarations = config.declarationSetMinDeclarations;
292
+ if (config.declarationSetSimilarityThreshold !== undefined)
293
+ universalConfig.declarationSetSimilarityThreshold = config.declarationSetSimilarityThreshold;
294
+ // Forward base-class properties (Spec-20 path profiles + severity overrides)
295
+ if (config.severityOverrides !== undefined)
296
+ universalConfig.severityOverrides = config.severityOverrides;
297
+ if (config.pathProfiles !== undefined)
298
+ universalConfig.pathProfiles = config.pathProfiles;
299
+ if (config.projectRoot !== undefined)
300
+ universalConfig.projectRoot = config.projectRoot;
301
+ return analyzer.analyze(files, universalConfig, {
302
+ progressCallback: createProgressAdapter('styles', progressCallback),
303
+ ...options,
304
+ });
305
+ },
306
+ },
307
+ 'conventions': {
308
+ name: 'conventions',
309
+ description: 'Mines codebase conventions and flags deviations at suggestion severity',
310
+ category: 'style',
311
+ analyze: async (files, config, options, progressCallback) => {
312
+ const analyzer = new UniversalConventionsAnalyzer();
313
+ const universalConfig = {};
314
+ if (config.minCorpus !== undefined)
315
+ universalConfig.minCorpus = config.minCorpus;
316
+ if (config.pairConfidence !== undefined)
317
+ universalConfig.pairConfidence = config.pairConfidence;
318
+ if (config.modeShare !== undefined)
319
+ universalConfig.modeShare = config.modeShare;
320
+ if (config.maxConventionsPerDomain !== undefined)
321
+ universalConfig.maxConventionsPerDomain = config.maxConventionsPerDomain;
322
+ // Forward base-class properties (Spec-20 path profiles + severity overrides)
323
+ if (config.severityOverrides !== undefined)
324
+ universalConfig.severityOverrides = config.severityOverrides;
325
+ if (config.pathProfiles !== undefined)
326
+ universalConfig.pathProfiles = config.pathProfiles;
327
+ if (config.projectRoot !== undefined)
328
+ universalConfig.projectRoot = config.projectRoot;
329
+ return analyzer.analyze(files, universalConfig, {
330
+ progressCallback: createProgressAdapter('conventions', progressCallback),
331
+ ...options,
332
+ });
333
+ },
334
+ },
335
+ 'cross-domain': {
336
+ name: 'cross-domain',
337
+ description: 'Detects cross-domain issues (schema lifecycle, validation bypass, coverage gaps)',
338
+ category: 'architecture',
339
+ analyze: async (files, config, options, progressCallback) => {
340
+ const analyzer = new CrossDomainAnalyzer();
341
+ const universalConfig = {};
342
+ if (config.schemaLifecycle !== undefined)
343
+ universalConfig.schemaLifecycle = config.schemaLifecycle;
344
+ if (config.validatorBypass !== undefined)
345
+ universalConfig.validatorBypass = config.validatorBypass;
346
+ if (config.coverage !== undefined)
347
+ universalConfig.coverage = config.coverage;
348
+ // Forward base-class properties (Spec-20 path profiles + severity overrides)
349
+ if (config.severityOverrides !== undefined)
350
+ universalConfig.severityOverrides = config.severityOverrides;
351
+ if (config.pathProfiles !== undefined)
352
+ universalConfig.pathProfiles = config.pathProfiles;
353
+ if (config.projectRoot !== undefined)
354
+ universalConfig.projectRoot = config.projectRoot;
355
+ return analyzer.analyze(files, universalConfig, {
356
+ progressCallback: createProgressAdapter('cross-domain', progressCallback),
357
+ ...options,
358
+ });
359
+ },
360
+ },
220
361
  };
221
362
  /**
222
363
  * Create an audit runner with the given options
@@ -305,6 +446,44 @@ export function createAuditRunner(options = {}) {
305
446
  });
306
447
  }
307
448
  }
449
+ // ── Blast-radius impact (Spec 14 R6) ──────────────────────────────
450
+ // Compute transitive-caller impact for changed functions.
451
+ // If latency exceeds the 100ms budget, skip and emit a warning;
452
+ // the feature is disabled-by-default when over budget.
453
+ let blastRadius;
454
+ if (isScoped && changedFunctions && changedFunctions.length > 0) {
455
+ try {
456
+ const db = CodeIndexDB.getInstance();
457
+ const rawDb = db.rawDb;
458
+ const functionIds = [];
459
+ for (const fn of changedFunctions) {
460
+ const row = rawDb.prepare('SELECT id FROM functions WHERE name = ? AND file_path = ?').get(fn.name, fn.filePath);
461
+ if (row)
462
+ functionIds.push(row.id);
463
+ }
464
+ if (functionIds.length > 0) {
465
+ const impact = computeImpact(rawDb, functionIds);
466
+ if (impact.latencyMs > LATENCY_BUDGET_MS) {
467
+ logMcpInfo('blast-radius', `latency ${impact.latencyMs}ms exceeds ${LATENCY_BUDGET_MS}ms budget — blast radius disabled for this run`, {});
468
+ }
469
+ else {
470
+ blastRadius = impact;
471
+ logMcpInfo('blast-radius', 'computed', {
472
+ editedFunctionCount: impact.editedFunctionCount,
473
+ transitiveCallers: impact.transitiveCallers,
474
+ reachableExports: impact.reachableExports,
475
+ latencyMs: impact.latencyMs,
476
+ });
477
+ }
478
+ }
479
+ }
480
+ catch (err) {
481
+ // Blast radius is advisory — non-fatal
482
+ logMcpInfo('blast-radius', 'computation failed (continuing)', {
483
+ error: err instanceof Error ? err.message : String(err)
484
+ });
485
+ }
486
+ }
308
487
  const handoffRemaining = [];
309
488
  const maxPerRun = mergedOptions.maxFilesPerRun;
310
489
  if (typeof maxPerRun === 'number' && maxPerRun > 0 && files.length > maxPerRun) {
@@ -391,6 +570,31 @@ export function createAuditRunner(options = {}) {
391
570
  // Run analyzers
392
571
  const analyzerResults = {};
393
572
  const enabledAnalyzers = getEnabledAnalyzers(mergedOptions, analyzerRegistry);
573
+ // ── Style index sync (Spec 10) ────────────────────────────────────
574
+ // Sync style declarations, tokens, and class usage before the
575
+ // styles analyzer runs, mirroring the function index sync pattern.
576
+ if (enabledAnalyzers.includes('styles')) {
577
+ try {
578
+ const styleDb = CodeIndexDB.getInstance();
579
+ const styleSyncResult = await syncStyleIndex(styleDb.rawDb, files, root, { scoped: isScoped });
580
+ logMcpInfo('style-index', 'style index sync complete', {
581
+ changed: styleSyncResult.changed,
582
+ skipped: styleSyncResult.skipped,
583
+ removed: styleSyncResult.removed,
584
+ errors: styleSyncResult.errors,
585
+ });
586
+ }
587
+ catch (err) {
588
+ // Non-fatal: styles analyzer will run with whatever is in the index
589
+ logMcpInfo('style-index', 'style index sync failed (continuing)', {
590
+ error: err instanceof Error ? err.message : String(err)
591
+ });
592
+ }
593
+ }
594
+ // Spec 21: Shared timing accumulator for provenance resolution.
595
+ // Injected into data-access and schema analyzers so they can report
596
+ // per-file buildProvenanceContext() wall time (hook-latency measurement).
597
+ const provenanceTiming = { totalMs: 0 };
394
598
  logMcpInfo('analysis', 'enabled analyzers', {
395
599
  names: enabledAnalyzers,
396
600
  fileCount: files.length,
@@ -421,11 +625,28 @@ export function createAuditRunner(options = {}) {
421
625
  analyzer: analyzerName,
422
626
  message: `Running ${analyzerName} analyzer...`
423
627
  });
424
- // Build analyzer config; inject full index for DRY on scoped runs
425
- const analyzerConfig = { ...(mergedOptions.analyzerConfigs?.[analyzerName] || {}) };
628
+ // Build analyzer config; inject full index for DRY on scoped runs.
629
+ // Skip pathProfiles for invariants (Spec-20 R3): no backdoor
630
+ // around declared laws — invariants severity is absolute.
631
+ const isInvariants = analyzerName === 'invariants';
632
+ const analyzerConfig = {
633
+ ...(mergedOptions.analyzerConfigs?.[analyzerName] || {}),
634
+ };
635
+ if (!isInvariants) {
636
+ analyzerConfig.pathProfiles = mergedOptions.pathProfiles;
637
+ analyzerConfig.projectRoot = root;
638
+ analyzerConfig.severityOverrides = mergedOptions.severityOverrides;
639
+ }
426
640
  if (analyzerName === 'dry' && isScoped && fullFunctionIndex) {
427
641
  analyzerConfig.fullFunctionIndex = fullFunctionIndex;
428
642
  }
643
+ // Spec 21: Pass shared provenance timing accumulator to analyzers
644
+ // that call buildProvenanceContext() (data-access and schema).
645
+ // The analyzers accumulate per-file timing into this object;
646
+ // we read it back after all analyzers complete.
647
+ if (analyzerName === 'data-access' || analyzerName === 'schema') {
648
+ analyzerConfig._provenanceTiming = provenanceTiming;
649
+ }
429
650
  logMcpInfo('analysis', `running ${analyzerName}`, { fileCount: files.length });
430
651
  try {
431
652
  const result = await analyzer.analyze(files, analyzerConfig, mergedOptions, (progress) => {
@@ -474,6 +695,208 @@ export function createAuditRunner(options = {}) {
474
695
  orderedAnalyzerResults[analyzerName] = analyzerResults[analyzerName];
475
696
  }
476
697
  }
698
+ // ── Spec 13 R5 Phase 1: Persist seeded DRY pairs ──────────────────
699
+ // Store pairs from DRY analysis in dry_pair_history for divergence tracking.
700
+ // Pair identity is fingerprint-based (file + nodeType + line),
701
+ // NOT content-hash-based — so a diverging clone stays the same pair.
702
+ let dryPersistRunId = null;
703
+ try {
704
+ const dryResult = orderedAnalyzerResults['dry'];
705
+ const dryPairs = dryResult?.dryPairs;
706
+ if (dryPairs && dryPairs.length > 0) {
707
+ const indexDb = CodeIndexDB.getInstance();
708
+ await indexDb.initialize();
709
+ dryPersistRunId = randomUUID();
710
+ const insertStmt = indexDb.rawDb.prepare(`
711
+ INSERT OR IGNORE INTO dry_pair_history
712
+ (pair_fingerprint, file1, symbol1, line1, content_hash1,
713
+ file2, symbol2, line2, content_hash2, similarity, timestamp, run_id)
714
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), ?)
715
+ `);
716
+ const tx = indexDb.rawDb.transaction(() => {
717
+ for (const pair of dryPairs) {
718
+ insertStmt.run(pair.pairFingerprint, pair.file1, pair.symbol1, pair.line1, pair.contentHash1, pair.file2, pair.symbol2, pair.line2, pair.contentHash2, pair.similarity, dryPersistRunId);
719
+ }
720
+ });
721
+ tx();
722
+ }
723
+ }
724
+ catch {
725
+ // dry_pair_history persistence is advisory — non-fatal
726
+ }
727
+ // ── Baseline classification (Spec 18 R1) ────────────────────────────
728
+ const projectRoot = path.resolve(mergedOptions.projectRoot || process.cwd());
729
+ const baseline = loadBaseline(projectRoot);
730
+ let baselineMetadata;
731
+ if (baseline) {
732
+ const allViolations = Object.values(orderedAnalyzerResults).flatMap((r) => r.violations);
733
+ // For scoped runs, limit "fixed" to in-scope files
734
+ const scopedFiles = isScoped ? files : undefined;
735
+ const classified = matchFindings(allViolations, baseline, scopedFiles);
736
+ // Tag violations with their baseline status
737
+ for (const v of classified.new) {
738
+ v.new = true;
739
+ }
740
+ for (const v of classified.known) {
741
+ v.new = false;
742
+ }
743
+ baselineMetadata = {
744
+ present: true,
745
+ hash: hashBaseline(baseline),
746
+ newCount: classified.new.length,
747
+ fixedCount: classified.fixed.length,
748
+ knownCount: classified.known.length,
749
+ previousKnownCount: baseline.metadata.totalFindings,
750
+ };
751
+ logMcpInfo('baseline', 'baseline classification complete', {
752
+ new: classified.new.length,
753
+ fixed: classified.fixed.length,
754
+ known: classified.known.length,
755
+ });
756
+ }
757
+ // ── Spec 13 R2 — Hotspot scoring & finding reordering ──────────────
758
+ // Attach hotspot scores to violations and reorder within severity tiers.
759
+ // Falls back gracefully when no churn/hotspot data exists.
760
+ try {
761
+ const indexDb = CodeIndexDB.getInstance();
762
+ await indexDb.initialize();
763
+ // Build hotspot lookup: target -> score
764
+ const hotspotRows = indexDb.rawDb
765
+ .prepare('SELECT target, type, score FROM hotspot_scores')
766
+ .all();
767
+ if (hotspotRows.length > 0) {
768
+ const hotspotByTarget = new Map();
769
+ const hotspotByFile = new Map();
770
+ for (const row of hotspotRows) {
771
+ hotspotByTarget.set(row.target, row.score);
772
+ if (row.type === 'file') {
773
+ hotspotByFile.set(row.target, row.score);
774
+ }
775
+ }
776
+ // Attach hotspot to each violation and reorder within each analyzer
777
+ for (const analyzerName of Object.keys(orderedAnalyzerResults)) {
778
+ const result = orderedAnalyzerResults[analyzerName];
779
+ const violations = result.violations;
780
+ // Attach hotspot scores
781
+ for (const v of violations) {
782
+ const funcName = v.functionName;
783
+ const file = v.file;
784
+ // Try function-level hotspot first: "filePath::functionName"
785
+ const funcTarget = funcName ? `${file}::${funcName}` : undefined;
786
+ v.hotspot = (funcTarget ? hotspotByTarget.get(funcTarget) : undefined)
787
+ ?? hotspotByFile.get(file)
788
+ ?? 0;
789
+ }
790
+ // Reorder within severity tiers: sort by hotspot descending,
791
+ // preserving original order as tiebreaker.
792
+ const severityOrder = {
793
+ critical: 0,
794
+ warning: 1,
795
+ suggestion: 2,
796
+ info: 3,
797
+ };
798
+ const indexed = violations.map((v, i) => ({ v, i }));
799
+ indexed.sort((a, b) => {
800
+ const sevA = severityOrder[a.v.severity] ?? 99;
801
+ const sevB = severityOrder[b.v.severity] ?? 99;
802
+ if (sevA !== sevB)
803
+ return sevA - sevB;
804
+ // Within same severity: higher hotspot first
805
+ const hsA = a.v.hotspot ?? 0;
806
+ const hsB = b.v.hotspot ?? 0;
807
+ if (hsA !== hsB)
808
+ return hsB - hsA;
809
+ // Tiebreaker: original order
810
+ return a.i - b.i;
811
+ });
812
+ result.violations = indexed.map(x => x.v);
813
+ }
814
+ }
815
+ }
816
+ catch {
817
+ // Hotspot reordering is advisory — failure is non-fatal
818
+ }
819
+ // ── Spec 13 R5 Phase 2: Divergence tracking pass ──────────────────
820
+ // For every pair in dry_pair_history, compare the last 2 similarity
821
+ // measurements. If similarity has declined by ≥ divergenceThreshold
822
+ // for divergenceRuns consecutive runs, emit dry/diverging-clone.
823
+ //
824
+ // New rows are inserted by Phase 1 for pairs the DRY analyzer re-detects
825
+ // this run. Pairs that weren't re-detected don't get a new measurement
826
+ // this pass — full source re-read is deferred to the 20% case.
827
+ try {
828
+ const divergenceCfg = mergedOptions.analyzerConfigs?.dry?.divergence
829
+ ?? mergedOptions.divergence
830
+ ?? { divergenceThreshold: 0.05, divergenceRuns: 2, minPairSimilarity: 0.5 };
831
+ const threshold = divergenceCfg.divergenceThreshold ?? 0.05;
832
+ const requiredDeclines = divergenceCfg.divergenceRuns ?? 2;
833
+ if (threshold > 0) {
834
+ const indexDb = CodeIndexDB.getInstance();
835
+ await indexDb.initialize();
836
+ // Get all distinct pair fingerprints
837
+ const fingerprints = indexDb.rawDb
838
+ .prepare('SELECT DISTINCT pair_fingerprint FROM dry_pair_history')
839
+ .all();
840
+ if (fingerprints.length > 0) {
841
+ const getRows = indexDb.rawDb.prepare(`
842
+ SELECT similarity, timestamp
843
+ FROM dry_pair_history
844
+ WHERE pair_fingerprint = ?
845
+ ORDER BY timestamp ASC
846
+ `);
847
+ const divergingViolations = [];
848
+ for (const { pair_fingerprint: fp } of fingerprints) {
849
+ const rows = getRows.all(fp);
850
+ if (rows.length < requiredDeclines + 1)
851
+ continue;
852
+ // Check last `requiredDeclines` consecutive pairs for decline
853
+ let consecutiveDeclines = 0;
854
+ for (let i = rows.length - requiredDeclines; i < rows.length; i++) {
855
+ if (rows[i].similarity < rows[i - 1].similarity - threshold) {
856
+ consecutiveDeclines++;
857
+ }
858
+ }
859
+ if (consecutiveDeclines >= requiredDeclines) {
860
+ // Get file/line info from the last row
861
+ const lastRow = indexDb.rawDb.prepare(`
862
+ SELECT file1, file2, line1, line2 FROM dry_pair_history
863
+ WHERE pair_fingerprint = ?
864
+ ORDER BY timestamp DESC LIMIT 1
865
+ `).get(fp);
866
+ if (lastRow) {
867
+ const currentSim = rows[rows.length - 1].similarity;
868
+ const prevSim = rows[rows.length - 2].similarity;
869
+ const drop = Math.round((prevSim - currentSim) * 1000) / 1000;
870
+ divergingViolations.push({
871
+ file: lastRow.file1,
872
+ line: lastRow.line1,
873
+ severity: 'suggestion',
874
+ message: `Clone pair has diverged: similarity dropped ${drop} (from ${prevSim.toFixed(3)} to ${currentSim.toFixed(3)}) across ${requiredDeclines} consecutive runs (pair: ${fp.slice(0, 12)}…). Review ${lastRow.file1}:${lastRow.line1} and ${lastRow.file2}:${lastRow.line2} for diverged logic.`,
875
+ analyzer: 'dry',
876
+ rule: 'dry/diverging-clone',
877
+ });
878
+ }
879
+ }
880
+ }
881
+ if (divergingViolations.length > 0) {
882
+ // Append to the dry analyzer result, or create one if none exists
883
+ if (orderedAnalyzerResults['dry']) {
884
+ orderedAnalyzerResults['dry'].violations.push(...divergingViolations);
885
+ }
886
+ else {
887
+ orderedAnalyzerResults['dry'] = {
888
+ violations: divergingViolations,
889
+ filesProcessed: 0,
890
+ executionTime: 0,
891
+ };
892
+ }
893
+ }
894
+ }
895
+ }
896
+ }
897
+ catch {
898
+ // Divergence tracking is advisory — non-fatal
899
+ }
477
900
  // Generate summary
478
901
  const summary = generateSummary(orderedAnalyzerResults, files.length);
479
902
  // Create result
@@ -488,6 +911,9 @@ export function createAuditRunner(options = {}) {
488
911
  analyzersRun: enabledAnalyzers,
489
912
  configUsed: mergedOptions,
490
913
  scope: scopeResultType,
914
+ provenanceResolutionMs: provenanceTiming.totalMs,
915
+ ...(blastRadius && { blastRadius }),
916
+ ...(baselineMetadata && { baseline: baselineMetadata }),
491
917
  ...(collectedFunctions.length > 0 && {
492
918
  collectedFunctions,
493
919
  fileToFunctionsMap: Object.fromEntries(fileToFunctionsMap)
@@ -502,6 +928,25 @@ export function createAuditRunner(options = {}) {
502
928
  if (handoffRemaining.length > 0) {
503
929
  throw new AuditHandoffError(`${handoffRemaining.length} file(s) deferred to the next worker chunk`, result, handoffRemaining);
504
930
  }
931
+ // Spec 11 R1 — write to findings ledger (non-fatal: ledger is advisory)
932
+ const ledgerRunId = (async () => {
933
+ try {
934
+ const indexDb = CodeIndexDB.getInstance();
935
+ await indexDb.initialize();
936
+ const violations = Object.values(orderedAnalyzerResults).flatMap(ar => ar.violations);
937
+ // Hook-contract guard: no violation may carry an empty file path or line 0.
938
+ // A sentinel violation with file:'' broke the Claude Code hook's JSON consumer
939
+ // (Spec 15 regression). This guard is permanent — every analyzer code path
940
+ // must produce properly anchored violations.
941
+ validateHookContract(violations);
942
+ const scopeStr = Array.isArray(scope) ? `files:${scope.length}` : (scope ?? 'all');
943
+ return writeAuditToLedger(indexDb.rawDb, detectRunInput(process.argv.slice(2).join(' '), options.surface ?? 'cli', scopeStr, root, TOOL_VERSION), violations, Date.now() - startTime, 0);
944
+ }
945
+ catch (_err) {
946
+ // ledger write is non-fatal — audit result is still valid
947
+ return null;
948
+ }
949
+ })();
505
950
  return result;
506
951
  }
507
952
  /**
@@ -692,6 +1137,41 @@ function generateSummary(analyzerResults, filesAnalyzed) {
692
1137
  topIssues
693
1138
  };
694
1139
  }
1140
+ /**
1141
+ * Hook-contract regression guard.
1142
+ *
1143
+ * Every violation must carry a non-empty `file` path. A sentinel violation with
1144
+ * `file: ''` (Spec 15 regression, commit 3419ed0) broke the Claude Code hook's
1145
+ * JSON consumer, which expects every violation to anchor to a real file.
1146
+ *
1147
+ * When `line` is present, it must be > 0 — a zero line means the analyzer
1148
+ * failed to locate the finding and shipped an unactionable anchor.
1149
+ *
1150
+ * Contract violations are logged as warnings and dropped from the pipeline
1151
+ * (they are never written to the ledger or surfaced to the hook).
1152
+ *
1153
+ * This guard is permanent — no analyzer code path may produce violations with
1154
+ * empty file paths or line 0.
1155
+ */
1156
+ function validateHookContract(violations) {
1157
+ for (let i = violations.length - 1; i >= 0; i--) {
1158
+ const v = violations[i];
1159
+ const problems = [];
1160
+ if (!v.file || v.file.trim() === '') {
1161
+ problems.push('empty file path');
1162
+ }
1163
+ if (v.line !== undefined && v.line === 0) {
1164
+ problems.push('line=0 (unactionable anchor)');
1165
+ }
1166
+ if (problems.length > 0) {
1167
+ console.warn(`[hook-contract] Dropping violation from analyzer '${v.analyzer ?? 'unknown'}': ` +
1168
+ `${problems.join(', ')}. Rule: ${v.rule ?? 'unknown'}. ` +
1169
+ `Message: "${v.message.slice(0, 120)}". ` +
1170
+ `This is a bug in the analyzer — violations must anchor to real files with valid lines.`);
1171
+ violations.splice(i, 1);
1172
+ }
1173
+ }
1174
+ }
695
1175
  /**
696
1176
  * Report progress
697
1177
  */