code-auditor-mcp 5.0.2 → 5.2.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 (336) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +184 -3
  3. package/README.md +24 -2
  4. package/configs/hhra-compat.json +12 -118
  5. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
  6. package/dist/analyzers/cross-language/DependencyGraphBuilder.js +11 -1
  7. package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
  8. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
  9. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +57 -52
  10. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
  11. package/dist/analyzers/provenance.d.ts +9 -4
  12. package/dist/analyzers/provenance.d.ts.map +1 -1
  13. package/dist/analyzers/provenance.js +8 -25
  14. package/dist/analyzers/provenance.js.map +1 -1
  15. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  16. package/dist/analyzers/reactAnalyzer.js +0 -5
  17. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  18. package/dist/analyzers/ruleRegistry.js +4 -4
  19. package/dist/analyzers/ruleRegistry.js.map +1 -1
  20. package/dist/analyzers/ruleTiming.d.ts +1 -1
  21. package/dist/analyzers/ruleTiming.js +2 -2
  22. package/dist/analyzers/ruleTiming.js.map +1 -1
  23. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
  24. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +22 -26
  25. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
  26. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +32 -15
  27. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  28. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +189 -115
  29. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  30. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +0 -2
  31. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  32. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +0 -5
  33. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  34. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +2 -10
  35. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
  36. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +7 -49
  37. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
  38. package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
  39. package/dist/analyzers/universal/schema/config.js +0 -2
  40. package/dist/analyzers/universal/schema/config.js.map +1 -1
  41. package/dist/analyzers/universal/schema/types.d.ts +0 -2
  42. package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
  43. package/dist/analyzers/vendorSurfaces.d.ts +126 -0
  44. package/dist/analyzers/vendorSurfaces.d.ts.map +1 -0
  45. package/dist/analyzers/vendorSurfaces.js +580 -0
  46. package/dist/analyzers/vendorSurfaces.js.map +1 -0
  47. package/dist/auditRunner.d.ts.map +1 -1
  48. package/dist/auditRunner.js +136 -142
  49. package/dist/auditRunner.js.map +1 -1
  50. package/dist/churn/churnExtractor.d.ts +0 -1
  51. package/dist/churn/churnExtractor.d.ts.map +1 -1
  52. package/dist/churn/churnExtractor.js +2 -4
  53. package/dist/churn/churnExtractor.js.map +1 -1
  54. package/dist/cli.d.ts +1 -1
  55. package/dist/cli.d.ts.map +1 -1
  56. package/dist/cli.js +195 -26
  57. package/dist/cli.js.map +1 -1
  58. package/dist/codeIndex/migrations.d.ts.map +1 -1
  59. package/dist/codeIndex/migrations.js +4 -3
  60. package/dist/codeIndex/migrations.js.map +1 -1
  61. package/dist/codeIndexDB.d.ts +11 -2
  62. package/dist/codeIndexDB.d.ts.map +1 -1
  63. package/dist/codeIndexDB.js +27 -2
  64. package/dist/codeIndexDB.js.map +1 -1
  65. package/dist/config/configLoader.d.ts +60 -7
  66. package/dist/config/configLoader.d.ts.map +1 -1
  67. package/dist/config/configLoader.js +252 -60
  68. package/dist/config/configLoader.js.map +1 -1
  69. package/dist/config/defaults.d.ts +77 -18
  70. package/dist/config/defaults.d.ts.map +1 -1
  71. package/dist/config/defaults.js +105 -43
  72. package/dist/config/defaults.js.map +1 -1
  73. package/dist/constants.d.ts +2 -1
  74. package/dist/constants.d.ts.map +1 -1
  75. package/dist/constants.js +4 -0
  76. package/dist/constants.js.map +1 -1
  77. package/dist/daemon/main.d.ts +0 -1
  78. package/dist/daemon/main.d.ts.map +1 -1
  79. package/dist/daemon/main.js +11 -4
  80. package/dist/daemon/main.js.map +1 -1
  81. package/dist/dataPaths.d.ts +17 -11
  82. package/dist/dataPaths.d.ts.map +1 -1
  83. package/dist/dataPaths.js +51 -16
  84. package/dist/dataPaths.js.map +1 -1
  85. package/dist/hookLog.d.ts +54 -0
  86. package/dist/hookLog.d.ts.map +1 -0
  87. package/dist/hookLog.js +105 -0
  88. package/dist/hookLog.js.map +1 -0
  89. package/dist/hookLogPath.generated.d.ts +3 -0
  90. package/dist/hookLogPath.generated.d.ts.map +1 -0
  91. package/dist/hookLogPath.generated.js +8 -0
  92. package/dist/hookLogPath.generated.js.map +1 -0
  93. package/dist/index.d.ts +0 -5
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +0 -7
  96. package/dist/index.js.map +1 -1
  97. package/dist/installConfig.d.ts +15 -2
  98. package/dist/installConfig.d.ts.map +1 -1
  99. package/dist/installConfig.js +17 -2
  100. package/dist/installConfig.js.map +1 -1
  101. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  102. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +18 -3
  103. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  104. package/dist/mcp-tools/workflowGuide.js +8 -8
  105. package/dist/mcp-tools/workflowGuide.js.map +1 -1
  106. package/dist/mcp-tools-shared.d.ts.map +1 -1
  107. package/dist/mcp-tools-shared.js +5 -2
  108. package/dist/mcp-tools-shared.js.map +1 -1
  109. package/dist/mcp-ui-simple.d.ts +5 -0
  110. package/dist/mcp-ui-simple.d.ts.map +1 -1
  111. package/dist/mcp-ui-simple.js +9 -2
  112. package/dist/mcp-ui-simple.js.map +1 -1
  113. package/dist/mcp.d.ts +0 -1
  114. package/dist/mcp.d.ts.map +1 -1
  115. package/dist/mcp.js +36 -33
  116. package/dist/mcp.js.map +1 -1
  117. package/dist/mcpAuditJobs.d.ts +10 -0
  118. package/dist/mcpAuditJobs.d.ts.map +1 -1
  119. package/dist/mcpAuditJobs.js +10 -1
  120. package/dist/mcpAuditJobs.js.map +1 -1
  121. package/dist/mcpAutoIndex.d.ts.map +1 -1
  122. package/dist/mcpAutoIndex.js +2 -1
  123. package/dist/mcpAutoIndex.js.map +1 -1
  124. package/dist/phase/colorMath.d.ts +18 -5
  125. package/dist/phase/colorMath.d.ts.map +1 -1
  126. package/dist/phase/colorMath.js +88 -5
  127. package/dist/phase/colorMath.js.map +1 -1
  128. package/dist/phase/dataAccessCallsCandidates.js +1 -1
  129. package/dist/phase/dataAccessCallsCandidates.js.map +1 -1
  130. package/dist/phase/goFunctionAnalysis.d.ts +2 -2
  131. package/dist/phase/goFunctionAnalysis.js +2 -2
  132. package/dist/phase/goFunctions.d.ts +2 -2
  133. package/dist/phase/goFunctions.js +2 -2
  134. package/dist/phase/goImports.d.ts +2 -2
  135. package/dist/phase/goImports.js +2 -2
  136. package/dist/phase/goSwitches.d.ts +2 -2
  137. package/dist/phase/goSwitches.js +2 -2
  138. package/dist/phase/loopQueryCandidates.js +1 -1
  139. package/dist/phase/loopQueryCandidates.js.map +1 -1
  140. package/dist/phase/phaseModel.d.ts +37 -8
  141. package/dist/phase/phaseModel.d.ts.map +1 -1
  142. package/dist/phase/phaseModel.js +33 -8
  143. package/dist/phase/phaseModel.js.map +1 -1
  144. package/dist/phase/producers.d.ts.map +1 -1
  145. package/dist/phase/producers.js +24 -17
  146. package/dist/phase/producers.js.map +1 -1
  147. package/dist/phase/reachability.d.ts +44 -1
  148. package/dist/phase/reachability.d.ts.map +1 -1
  149. package/dist/phase/reachability.js +79 -0
  150. package/dist/phase/reachability.js.map +1 -1
  151. package/dist/phase/rules/crossDomain.d.ts.map +1 -1
  152. package/dist/phase/rules/crossDomain.js +16 -6
  153. package/dist/phase/rules/crossDomain.js.map +1 -1
  154. package/dist/phase/rules/dry.d.ts.map +1 -1
  155. package/dist/phase/rules/dry.js +0 -1
  156. package/dist/phase/rules/dry.js.map +1 -1
  157. package/dist/phase/rules/goRules.d.ts +2 -2
  158. package/dist/phase/rules/goRules.js +2 -2
  159. package/dist/phase/rules/secrets.d.ts +22 -8
  160. package/dist/phase/rules/secrets.d.ts.map +1 -1
  161. package/dist/phase/rules/secrets.js +48 -67
  162. package/dist/phase/rules/secrets.js.map +1 -1
  163. package/dist/phase/rules/styles.d.ts +23 -9
  164. package/dist/phase/rules/styles.d.ts.map +1 -1
  165. package/dist/phase/rules/styles.js +47 -15
  166. package/dist/phase/rules/styles.js.map +1 -1
  167. package/dist/phase/rules/unreferencedModule.d.ts.map +1 -1
  168. package/dist/phase/rules/unreferencedModule.js +16 -5
  169. package/dist/phase/rules/unreferencedModule.js.map +1 -1
  170. package/dist/phase/runner.d.ts +16 -7
  171. package/dist/phase/runner.d.ts.map +1 -1
  172. package/dist/phase/runner.js +32 -20
  173. package/dist/phase/runner.js.map +1 -1
  174. package/dist/phase/stylesCss.d.ts.map +1 -1
  175. package/dist/phase/stylesCss.js +4 -0
  176. package/dist/phase/stylesCss.js.map +1 -1
  177. package/dist/phase/stylesMarkup.d.ts.map +1 -1
  178. package/dist/phase/stylesMarkup.js +4 -0
  179. package/dist/phase/stylesMarkup.js.map +1 -1
  180. package/dist/phase/stylesSource.d.ts.map +1 -1
  181. package/dist/phase/stylesSource.js +6 -0
  182. package/dist/phase/stylesSource.js.map +1 -1
  183. package/dist/phase/testProgram.d.ts +57 -0
  184. package/dist/phase/testProgram.d.ts.map +1 -0
  185. package/dist/phase/testProgram.js +244 -0
  186. package/dist/phase/testProgram.js.map +1 -0
  187. package/dist/phase/typeDeclarations.d.ts +2 -2
  188. package/dist/phase/typeDeclarations.js +2 -2
  189. package/dist/phase/types.d.ts +46 -0
  190. package/dist/phase/types.d.ts.map +1 -1
  191. package/dist/pipelineAdapters.d.ts +1 -171
  192. package/dist/pipelineAdapters.d.ts.map +1 -1
  193. package/dist/pipelineAdapters.js +47 -1281
  194. package/dist/pipelineAdapters.js.map +1 -1
  195. package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
  196. package/dist/reporting/csvReportGenerator.js +0 -6
  197. package/dist/reporting/csvReportGenerator.js.map +1 -1
  198. package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
  199. package/dist/reporting/htmlReportGenerator.js +0 -4
  200. package/dist/reporting/htmlReportGenerator.js.map +1 -1
  201. package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
  202. package/dist/reporting/jsonReportGenerator.js +0 -1
  203. package/dist/reporting/jsonReportGenerator.js.map +1 -1
  204. package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
  205. package/dist/reporting/sarifReportGenerator.js +0 -7
  206. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  207. package/dist/sqlite/driver.d.ts +17 -1
  208. package/dist/sqlite/driver.d.ts.map +1 -1
  209. package/dist/sqlite/driver.js +28 -5
  210. package/dist/sqlite/driver.js.map +1 -1
  211. package/dist/styles/cssInJsHarvest.d.ts +36 -0
  212. package/dist/styles/cssInJsHarvest.d.ts.map +1 -0
  213. package/dist/styles/cssInJsHarvest.js +78 -0
  214. package/dist/styles/cssInJsHarvest.js.map +1 -0
  215. package/dist/styles/definedClasses.d.ts +95 -0
  216. package/dist/styles/definedClasses.d.ts.map +1 -0
  217. package/dist/styles/definedClasses.js +129 -0
  218. package/dist/styles/definedClasses.js.map +1 -0
  219. package/dist/styles/markupStyleHarvest.d.ts +33 -0
  220. package/dist/styles/markupStyleHarvest.d.ts.map +1 -0
  221. package/dist/styles/markupStyleHarvest.js +73 -0
  222. package/dist/styles/markupStyleHarvest.js.map +1 -0
  223. package/dist/styles/styleExtractor.d.ts +37 -0
  224. package/dist/styles/styleExtractor.d.ts.map +1 -1
  225. package/dist/styles/styleExtractor.js +89 -33
  226. package/dist/styles/styleExtractor.js.map +1 -1
  227. package/dist/styles/styleIndexer.d.ts.map +1 -1
  228. package/dist/styles/styleIndexer.js +11 -10
  229. package/dist/styles/styleIndexer.js.map +1 -1
  230. package/dist/styles/tailwindExpander.d.ts.map +1 -1
  231. package/dist/styles/tailwindExpander.js +31 -10
  232. package/dist/styles/tailwindExpander.js.map +1 -1
  233. package/dist/telemetry.d.ts +67 -1
  234. package/dist/telemetry.d.ts.map +1 -1
  235. package/dist/telemetry.js +100 -20
  236. package/dist/telemetry.js.map +1 -1
  237. package/dist/types/crossLanguage.d.ts +1 -0
  238. package/dist/types/crossLanguage.d.ts.map +1 -1
  239. package/dist/types.d.ts +23 -56
  240. package/dist/types.d.ts.map +1 -1
  241. package/dist/types.js +4 -4
  242. package/dist/types.js.map +1 -1
  243. package/dist/version.generated.d.ts +1 -1
  244. package/dist/version.generated.js +1 -1
  245. package/examples/.auditrc.example.json +7 -22
  246. package/examples/.auditrc.json +12 -155
  247. package/examples/.code-auditor.json +8 -51
  248. package/examples/nextjs.auditrc.json +7 -34
  249. package/examples/node-api.auditrc.json +6 -46
  250. package/examples/react.auditrc.json +8 -25
  251. package/examples/tolerant-solid.auditrc.json +12 -44
  252. package/package.json +6 -5
  253. package/plugin/.claude-plugin/manifest.json +10 -9
  254. package/plugin/.claude-plugin/plugin.json +1 -1
  255. package/plugin/hooks/hooks.json +4 -4
  256. package/plugin/scripts/hook-audit.sh +9 -0
  257. package/plugin/scripts/hook-common.sh +8 -2
  258. package/plugin/scripts/hook-drift-check.sh +10 -1
  259. package/plugin/scripts/hook-log-path.sh +7 -0
  260. package/plugin/scripts/hook-self-audit.sh +9 -0
  261. package/plugin/scripts/hook-warm.sh +17 -11
  262. package/plugin/skills/code-auditor/SKILL.md +26 -3
  263. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts +0 -6
  264. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts.map +0 -1
  265. package/dist/__tests__/fixtures/spec-20/scripts/tool.js +0 -12
  266. package/dist/__tests__/fixtures/spec-20/scripts/tool.js.map +0 -1
  267. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts +0 -7
  268. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts.map +0 -1
  269. package/dist/__tests__/fixtures/spec-20/src/demo-module.js +0 -13
  270. package/dist/__tests__/fixtures/spec-20/src/demo-module.js.map +0 -1
  271. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts +0 -14
  272. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts.map +0 -1
  273. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js +0 -16
  274. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js.map +0 -1
  275. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts +0 -15
  276. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts.map +0 -1
  277. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js +0 -17
  278. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js.map +0 -1
  279. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +0 -14
  280. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +0 -1
  281. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +0 -19
  282. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +0 -1
  283. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +0 -18
  284. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +0 -1
  285. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +0 -19
  286. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +0 -1
  287. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts +0 -19
  288. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts.map +0 -1
  289. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js +0 -19
  290. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js.map +0 -1
  291. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +0 -17
  292. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +0 -1
  293. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js +0 -19
  294. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +0 -1
  295. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +0 -25
  296. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +0 -1
  297. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js +0 -21
  298. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +0 -1
  299. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts +0 -15
  300. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts.map +0 -1
  301. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js +0 -19
  302. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js.map +0 -1
  303. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts +0 -14
  304. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts.map +0 -1
  305. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js +0 -14
  306. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js.map +0 -1
  307. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +0 -15
  308. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +0 -1
  309. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js +0 -16
  310. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +0 -1
  311. package/dist/languages/go/analyzer-darwin-amd64 +0 -0
  312. package/dist/languages/go/analyzer-darwin-arm64 +0 -0
  313. package/dist/languages/go/analyzer-linux-amd64 +0 -0
  314. package/dist/languages/go/analyzer-linux-arm64 +0 -0
  315. package/dist/languages/go/analyzer-src/analyzer.go +0 -643
  316. package/dist/languages/go/analyzer-src/dataaccess.go +0 -388
  317. package/dist/languages/go/analyzer-src/go.mod +0 -3
  318. package/dist/languages/go/analyzer-src/indexer.go +0 -268
  319. package/dist/languages/go/analyzer-src/parser.go +0 -461
  320. package/dist/languages/go/analyzer-src/solid.go +0 -311
  321. package/dist/languages/go/analyzer-src/testconventions.go +0 -25
  322. package/dist/languages/go/analyzer-src/types.go +0 -156
  323. package/dist/languages/go/analyzer-windows-amd64.exe +0 -0
  324. package/dist/languages/go/go.mod +0 -7
  325. package/dist/languages/go/main.go +0 -203
  326. package/dist/native/ast-grep-napi.darwin-arm64.node +0 -0
  327. package/dist/native/ast-grep-napi.darwin-x64.node +0 -0
  328. package/dist/native/ast-grep-napi.linux-arm64-gnu.node +0 -0
  329. package/dist/native/ast-grep-napi.linux-arm64-musl.node +0 -0
  330. package/dist/native/ast-grep-napi.linux-x64-gnu.node +0 -0
  331. package/dist/native/ast-grep-napi.linux-x64-musl.node +0 -0
  332. package/dist/native/ast-grep-napi.win32-x64-msvc.node +0 -0
  333. package/dist/native-bootstrap.d.ts +0 -15
  334. package/dist/native-bootstrap.d.ts.map +0 -1
  335. package/dist/native-bootstrap.js +0 -75
  336. package/dist/native-bootstrap.js.map +0 -1
@@ -35,12 +35,12 @@ import { writeAuditToLedger, detectRunInput } from './ledger.js';
35
35
  import { runPipeline, makeVisitorStatus, getFilesProcessed, isVisitorStatus } from './pipeline.js';
36
36
  import { getRuleTimingSortedDesc } from './analyzers/ruleTiming.js';
37
37
  import { createFunctionIndexVisitor, createStylesCssVisitor, createStylesSourceVisitor, createStylesReducer, createInvariantsReducer, createSchemaCodeVisitor, } from './pipelineAdapters.js';
38
- import { computeSizeDistributions } from './reporting/sizeDistribution.js';
39
- import { splitRoutes, attributeRoutes, enabledMigratedRules } from './phase/routing.js';
40
38
  import { runPhaseModel } from './phase/phaseModel.js';
39
+ import { resolveVendorSurfaces } from './analyzers/vendorSurfaces.js';
41
40
  import { resolvePhaseThresholds } from './phase/config.js';
42
41
  import { deriveCoverage, presentFormatsOf } from './phase/coverage.js';
43
42
  import { MIGRATED_RULES, RULE_ANALYZER } from './phase/rules/registry.js';
43
+ import { computeReachabilityScores } from './phase/reachability.js';
44
44
  import { seedDryPairs, resolveBlockConfig } from './phase/rules/dry.js';
45
45
  import { checkUnresolvedReceiverImports, checkUnprovenQueryReceivers, checkUnresolvedQueries, checkUnparseableSql, dedupeCannotFireByReceiver } from './analyzers/universal/schema/codeAnalysis.js';
46
46
  import { readProjectManifest, computeManifestStaleness } from './analyzers/manifestStaleness.js';
@@ -200,7 +200,7 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
200
200
  phase: 'discovery',
201
201
  message: `Discovering files... (scope: ${scopeResultType})`
202
202
  });
203
- const { files, skippedExtensions } = await resolveDiscoveryScope(mergedOptions, scope, fileAccounting);
203
+ const { files, skippedExtensions, changedScopeNoBaseline } = await resolveDiscoveryScope(mergedOptions, scope, fileAccounting);
204
204
  throwIfAborted(mergedOptions.abortSignal);
205
205
  // Detect changed functions for non-all scopes
206
206
  let changedFunctions;
@@ -228,6 +228,7 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
228
228
  scope,
229
229
  isScoped,
230
230
  scopeResultType,
231
+ changedScopeNoBaseline,
231
232
  };
232
233
  }
233
234
  /**
@@ -238,6 +239,11 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
238
239
  */
239
240
  async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
240
241
  let files;
242
+ // True when a bare `changed` scope found no baseline in the index to diff
243
+ // against (an empty `functions` table). That is "the diff cannot be computed",
244
+ // not "nothing changed" — surfaced as a diagnostic so the run never reports a
245
+ // false clean. Explicit-files and all/other scopes leave this false.
246
+ let changedScopeNoBaseline = false;
241
247
  // Extensions present on disk that discovery skipped (Spec 43 R5 follow-up).
242
248
  // Populated only for the `all` scope — scoped runs are explicitly scoped by
243
249
  // the user, so "what wasn't analyzed" there is the scope itself, not the
@@ -269,12 +275,29 @@ async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
269
275
  error: err instanceof Error ? err.message : String(err)
270
276
  });
271
277
  }
272
- const modifiedFiles = mergedOptions.explicitFiles !== undefined
273
- ? mergedOptions.explicitFiles
274
- : (db ? await db.detectModifiedFiles(path.resolve(mergedOptions.projectRoot || process.cwd())) : []);
278
+ let modifiedFiles;
279
+ if (mergedOptions.explicitFiles !== undefined) {
280
+ modifiedFiles = mergedOptions.explicitFiles;
281
+ }
282
+ else if (db) {
283
+ // `undefined` here means *no baseline* (an empty `functions` table) — a
284
+ // different answer from `[]` ("baseline present, nothing modified"). The
285
+ // former is reported as an uncomputable diff, never as a clean run. The
286
+ // `db`-unavailable case (init threw, e.g. a future schema version) keeps
287
+ // its existing fail-open zero-file path and the later engine-error
288
+ // diagnostic.
289
+ const detected = await db.detectModifiedFiles(path.resolve(mergedOptions.projectRoot || process.cwd()));
290
+ if (detected === undefined)
291
+ changedScopeNoBaseline = true;
292
+ modifiedFiles = detected ?? [];
293
+ }
294
+ else {
295
+ modifiedFiles = [];
296
+ }
275
297
  files = [...new Set(modifiedFiles.map((f) => path.resolve(f)))].sort();
276
298
  logMcpInfo('discovery', 'changed scope resolved', {
277
- fileCount: files.length
299
+ fileCount: files.length,
300
+ noBaseline: changedScopeNoBaseline
278
301
  });
279
302
  }
280
303
  else if (Array.isArray(scope)) {
@@ -290,7 +313,7 @@ async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
290
313
  files = discovered.files;
291
314
  skippedExtensions = discovered.skippedExtensions;
292
315
  }
293
- return { files, skippedExtensions };
316
+ return { files, skippedExtensions, changedScopeNoBaseline };
294
317
  }
295
318
  /**
296
319
  * Detect changed functions for a scoped run via the code index (Spec 60.1).
@@ -527,8 +550,6 @@ function buildPipelineAdapters(analyzers) {
527
550
  const pipelineVisitors = [];
528
551
  const pipelineReducers = [];
529
552
  const pipelineDerivedReducers = [];
530
- let reactBundle;
531
- let solidBundle;
532
553
  // Always-on infrastructure: function-index visitor populates the
533
554
  // `functions` table so conventions + cross-domain reducers have data
534
555
  // even on a cold run with no prior index sync.
@@ -567,8 +588,6 @@ function buildPipelineAdapters(analyzers) {
567
588
  pipelineVisitors,
568
589
  pipelineReducers,
569
590
  pipelineDerivedReducers,
570
- reactBundle,
571
- solidBundle,
572
591
  };
573
592
  }
574
593
  /**
@@ -707,19 +726,18 @@ function buildPipelineConfig(inputs) {
707
726
  };
708
727
  }
709
728
  /**
710
- * Run the both-paths split (Spec 68 §11.1): strip migrated rules' legacy
711
- * emission and re-emit phase findings into the correct analyzer buckets.
712
- * Mutates `analyzerResults` and `pipelineAnalyzerConfig` (dry divergence).
729
+ * Run the phase model — the single analysis path (Spec 68 §15) — and re-emit its
730
+ * findings into the correct analyzer buckets. The legacy analyzer path is
731
+ * deleted; there is no route split to choose between. Mutates `analyzerResults`
732
+ * and `pipelineAnalyzerConfig` (dry divergence).
713
733
  */
714
- async function applyPhaseModelSplit(inputs) {
734
+ async function runPhaseModelStage(inputs) {
715
735
  const { mergedOptions, root, files, isScoped, pipelineAnalyzerConfig, analyzerResults, pipelineIndexHandle } = inputs;
716
736
  let phaseFindings = [];
717
737
  let phaseIncompleteFacts = new Map();
718
- let routeAttribution;
738
+ let phaseReachability;
719
739
  let receiverDiagnostics = [];
720
- const { migrated } = splitRoutes();
721
- routeAttribution = Object.fromEntries(attributeRoutes());
722
- if (migrated.size > 0) {
740
+ if (MIGRATED_RULES.length > 0) {
723
741
  // The `dry/diverging-clone` rule reads its knobs from `dry.divergence`.
724
742
  const topLevelDivergence = mergedOptions.divergence;
725
743
  if (topLevelDivergence && !pipelineAnalyzerConfig['dry']?.divergence) {
@@ -758,12 +776,17 @@ async function applyPhaseModelSplit(inputs) {
758
776
  tsconfigAliases: infraConfig.tsconfigAliases,
759
777
  packageEntryPoints: infraConfig.packageEntryPoints,
760
778
  indexHandle: pipelineIndexHandle,
761
- enabledRules: enabledMigratedRules(),
779
+ enabledRules: new Set(MIGRATED_RULES.map((r) => r.id)),
762
780
  externalTables,
763
781
  workerCount: resolveWorkerCount(),
764
782
  sqlDialect: infraConfig.sqlDialect,
765
783
  sqlDialectReason: infraConfig.sqlDialectReason,
766
784
  declaredTypePackages: infraConfig.declaredTypePackages,
785
+ // Item B (reissued) — resolve each vendor's declaration from the tree under
786
+ // analysis (its own node_modules / workspace path) once per run. The
787
+ // loop-query / data-access producers read these resolved surfaces; nothing
788
+ // about a vendor's API is committed and nothing is fetched at analysis time.
789
+ vendorSurfaces: resolveVendorSurfaces(root),
767
790
  // Spec 70 2b — the diverging-clone write: seed `dry_pair_history` from the
768
791
  // phase `code-block` fact (replacing `createDryVisitor`/`persistDryPairs`).
769
792
  persistDryPairHistory: (facts) => {
@@ -774,6 +797,11 @@ async function applyPhaseModelSplit(inputs) {
774
797
  });
775
798
  phaseFindings = phaseResult.findings;
776
799
  phaseIncompleteFacts = phaseResult.incompleteFacts;
800
+ const reachabilityFact = phaseResult.facts.get('reachability');
801
+ const fileImports = phaseResult.facts.get('file-imports');
802
+ phaseReachability = reachabilityFact && fileImports
803
+ ? computeReachabilityScores(fileImports, reachabilityFact)
804
+ : undefined;
777
805
  // Spec 69 §10 + Part 2b — re-home the pre-pass's diagnostics (deleted from
778
806
  // `pipeline.ts`) on the phase result. Cannot-fire: re-derive per file from
779
807
  // `phaseResult.unprovenQueryReceivers` + `phaseResult.unresolvedImports`, keyed
@@ -781,12 +809,34 @@ async function applyPhaseModelSplit(inputs) {
781
809
  // receivers/imports never reached it). Manifest-stale: the ecosystem-list
782
810
  // self-check, unchanged from the deleted `buildPipelineResult` loop.
783
811
  receiverDiagnostics = await buildReceiverDiagnostics(phaseResult.unprovenQueryReceivers, phaseResult.unresolvedImports, phaseResult.unresolvedQuerySites, phaseResult.unparseableSql, root);
784
- // Strip the migrated rules' legacy emission from every analyzer result.
785
- let strippedCount = 0;
812
+ // Spec 68 §8 — `unreferenced-module`'s unproven modules: a module with no
813
+ // resolved importer while the graph carries a computed-specifier dynamic
814
+ // import. The rule withholds the dead-code finding for these; this surfaces
815
+ // the disposition (with its reason) as a coverage diagnostic instead.
816
+ for (const m of phaseResult.unprovenModules) {
817
+ receiverDiagnostics.push({
818
+ analyzerName: 'dependency-graph',
819
+ kind: 'unresolved-dynamic-import',
820
+ message: m.reason,
821
+ file: m.file,
822
+ line: m.line,
823
+ });
824
+ }
825
+ // §15 — a rule that could not run (fail-open, missing dependency) reported
826
+ // itself on the phase result's diagnostic channel. Merge those alongside the
827
+ // receiver-diagnostics the pre-pass re-derived, so the styles
828
+ // `undefined-class-disabled` diagnostic (re-homed from the deleted
829
+ // `initTailwindProbe`) reaches the same off-ladder surface.
830
+ receiverDiagnostics.push(...phaseResult.diagnostics);
831
+ // Strip the migrated rules' legacy emission from every analyzer result —
832
+ // the phase model is their single source of truth now (§15), and the legacy
833
+ // factories that keep running for their infra side-effect (styles reducer,
834
+ // schema-code visitor) still emit their migrated findings alongside. This is
835
+ // a direct projection of MIGRATED_RULES, not a route split: there is no
836
+ // "phase vs legacy" choice anymore, only discarding a stale duplicate.
837
+ const migratedRules = new Set(MIGRATED_RULES.map((r) => r.id));
786
838
  for (const result of Object.values(analyzerResults)) {
787
- const before = result.violations.length;
788
- result.violations = result.violations.filter((v) => !migrated.has(v.rule));
789
- strippedCount += before - result.violations.length;
839
+ result.violations = result.violations.filter((v) => !migratedRules.has(v.rule));
790
840
  }
791
841
  // Re-emit phase findings as violations, bucketed by the rule's analyzer.
792
842
  const byAnalyzer = new Map();
@@ -836,13 +886,8 @@ async function applyPhaseModelSplit(inputs) {
836
886
  analyzerResults[analyzer] = makeEmptyAnalyzerResult(analyzer, phaseFiles.length);
837
887
  }
838
888
  }
839
- logMcpInfo('analysis', 'phase model (both paths)', {
840
- migrated: migrated.size,
841
- stripped: strippedCount,
842
- reemitted: phaseFindings.length,
843
- });
844
889
  }
845
- return { phaseFindings, phaseIncompleteFacts, routeAttribution, receiverDiagnostics };
890
+ return { phaseFindings, phaseIncompleteFacts, phaseReachability, receiverDiagnostics };
846
891
  }
847
892
  /**
848
893
  * Spec 69 §10 + Part 2b — re-derive the pre-pass's coverage diagnostics from the
@@ -962,29 +1007,6 @@ export async function persistDryPairs(dryPairs, root) {
962
1007
  // dry_pair_history persistence is advisory — non-fatal
963
1008
  }
964
1009
  }
965
- /**
966
- * Run the React analyzer's cross-component finalization (require-error-boundary
967
- * / raw-element checks), appending extra violations into the `react` result.
968
- */
969
- async function finalizeReact(reactBundle, analyzerResults, pipelineAnalyzerConfig) {
970
- if (reactBundle) {
971
- try {
972
- const reactResult = analyzerResults['react'];
973
- if (reactResult) {
974
- // Pass the react namespace (not the whole namespaced config) so
975
- // finalize reads `requireErrorBoundaries`/`rawElementCheck` at the
976
- // same level the visitor's `context.config` does.
977
- const extraViolations = await reactBundle.finalizeCrossComponent(pipelineAnalyzerConfig['react'] ?? {});
978
- reactResult.violations.push(...extraViolations);
979
- }
980
- }
981
- catch (err) {
982
- logMcpInfo('analysis', 'react finalization failed (non-fatal)', {
983
- error: err instanceof Error ? err.message : String(err)
984
- });
985
- }
986
- }
987
- }
988
1010
  /**
989
1011
  * Build the pipeline, run it, and fold its outputs — plus the both-paths phase
990
1012
  * split and derived coverage — into a single result bundle for `run`.
@@ -995,7 +1017,7 @@ async function runPipelineStage(inputs) {
995
1017
  let indexFactsWritten = 0;
996
1018
  let writeIndexFactsMs = 0;
997
1019
  const { auditIndex, pipelineIndexHandle, indexSkipDiagnostic } = await initializeAuditIndex(root);
998
- const { pipelineVisitors, pipelineReducers, pipelineDerivedReducers, reactBundle, solidBundle, } = buildPipelineAdapters(analyzers);
1020
+ const { pipelineVisitors, pipelineReducers, pipelineDerivedReducers, } = buildPipelineAdapters(analyzers);
999
1021
  // ── Safeguard warnings ────────────────────────────────────────────────
1000
1022
  if (auditIndex && analyzers.includes('cross-domain')) {
1001
1023
  const suCount = auditIndex.rawSql.count('schema_usage');
@@ -1011,6 +1033,7 @@ async function runPipelineStage(inputs) {
1011
1033
  // Outputs hoisted so derived coverage (after the split) can read them.
1012
1034
  let phaseFindings = [];
1013
1035
  let phaseIncompleteFacts = new Map();
1036
+ let phaseReachability;
1014
1037
  let pipelineCoverage;
1015
1038
  let pipelineTableCatalog;
1016
1039
  let pipelineStageTiming;
@@ -1023,7 +1046,6 @@ async function runPipelineStage(inputs) {
1023
1046
  let pipelineTestCoverage;
1024
1047
  let pipelineDeadClusters;
1025
1048
  let pipelineSizeDistributions;
1026
- let routeAttribution;
1027
1049
  let receiverDiagnostics = [];
1028
1050
  if (hasPipelineAnalyzers) {
1029
1051
  const pipelineAnalyzerConfig = buildPipelineAnalyzerConfig(analyzers, mergedOptions, root, files, corpusFiles, provenanceTiming);
@@ -1052,13 +1074,12 @@ async function runPipelineStage(inputs) {
1052
1074
  status: ar.status?.status,
1053
1075
  });
1054
1076
  }
1055
- await finalizeReact(reactBundle, analyzerResults, pipelineAnalyzerConfig);
1056
- const split = await applyPhaseModelSplit({
1077
+ const split = await runPhaseModelStage({
1057
1078
  mergedOptions, root, files, isScoped, pipelineAnalyzerConfig, analyzerResults, pipelineIndexHandle,
1058
1079
  });
1059
1080
  phaseFindings = split.phaseFindings;
1060
1081
  phaseIncompleteFacts = split.phaseIncompleteFacts;
1061
- routeAttribution = split.routeAttribution;
1082
+ phaseReachability = split.phaseReachability;
1062
1083
  receiverDiagnostics = split.receiverDiagnostics;
1063
1084
  // Spec 68 §8 — derived coverage, computed AFTER the split so phaseFindings
1064
1085
  // holds the migrated rules' re-emitted findings.
@@ -1066,7 +1087,7 @@ async function runPipelineStage(inputs) {
1066
1087
  rules: MIGRATED_RULES,
1067
1088
  findings: phaseFindings,
1068
1089
  presentFormats: presentFormatsOf(files),
1069
- enabledRules: enabledMigratedRules(),
1090
+ enabledRules: new Set(MIGRATED_RULES.map((r) => r.id)),
1070
1091
  incompleteFacts: phaseIncompleteFacts,
1071
1092
  groupOf: (ruleId) => RULE_ANALYZER.get(ruleId) ?? ruleId,
1072
1093
  });
@@ -1088,11 +1109,6 @@ async function runPipelineStage(inputs) {
1088
1109
  ];
1089
1110
  pipelineTestCoverage = pipelineResult.metadata?.testCoverage;
1090
1111
  pipelineDeadClusters = pipelineResult.metadata?.deadClusters;
1091
- // Spec 60 R2 — aggregate the SOLID analyzer's raw size readings.
1092
- if (solidBundle) {
1093
- const samples = await solidBundle.getSizeSamples();
1094
- pipelineSizeDistributions = computeSizeDistributions(samples);
1095
- }
1096
1112
  }
1097
1113
  catch (error) {
1098
1114
  if (error instanceof AuditAbortedError) {
@@ -1138,6 +1154,7 @@ async function runPipelineStage(inputs) {
1138
1154
  analyzerResults,
1139
1155
  phaseFindings,
1140
1156
  phaseIncompleteFacts,
1157
+ phaseReachability,
1141
1158
  pipelineCoverage,
1142
1159
  pipelineTableCatalog,
1143
1160
  pipelineStageTiming,
@@ -1150,7 +1167,6 @@ async function runPipelineStage(inputs) {
1150
1167
  pipelineTestCoverage,
1151
1168
  pipelineDeadClusters,
1152
1169
  pipelineSizeDistributions,
1153
- routeAttribution,
1154
1170
  indexFactsWritten,
1155
1171
  writeIndexFactsMs,
1156
1172
  };
@@ -1254,75 +1270,40 @@ function stampAnalyzerFields(ordered) {
1254
1270
  }
1255
1271
  }
1256
1272
  /**
1257
- * Spec 13 R2 — attach hotspot + reachability scores to every violation and
1258
- * reorder within severity tiers (live code first, then higher hotspot, original
1259
- * order as tiebreaker). Advisory — failure is non-fatal (no churn/hotspot data
1260
- * is common on a first run).
1273
+ * Spec 13 R2 — attach a reachability score to every violation and reorder within
1274
+ * severity tiers (live code first: entry point → imported → dead), original
1275
+ * order as tiebreaker. Derived from the phase `reachability` fact (reverse
1276
+ * import adjacency + package entry points); no DB read. Advisory — a missing
1277
+ * fact (a run where the phase model produced no reachability) leaves every
1278
+ * finding at the neutral 0.5 and preserves original order.
1261
1279
  */
1262
- async function applyHotspotScoring(ordered, root) {
1263
- try {
1264
- const indexDb = CodeIndexDB.getInstance(undefined, root);
1265
- await indexDb.initialize();
1266
- const hotspotRows = indexDb.rawDb
1267
- .prepare('SELECT target, type, score FROM hotspot_scores')
1268
- .all();
1269
- const hotspotByTarget = new Map();
1270
- const hotspotByFile = new Map();
1271
- for (const row of hotspotRows) {
1272
- hotspotByTarget.set(row.target, row.score);
1273
- if (row.type === 'file') {
1274
- hotspotByFile.set(row.target, row.score);
1275
- }
1276
- }
1277
- const reachabilityByFile = new Map();
1278
- const reachRows = indexDb.rawDb
1279
- .prepare("SELECT node_key, weight FROM graph_cache WHERE graph_type = 'reachability'")
1280
- .all();
1281
- for (const row of reachRows) {
1282
- reachabilityByFile.set(row.node_key, row.weight);
1283
- }
1284
- for (const analyzerName of Object.keys(ordered)) {
1285
- const result = ordered[analyzerName];
1286
- const violations = result.violations;
1287
- for (const v of violations) {
1288
- const funcName = v.symbol;
1289
- const file = v.file;
1290
- // Try function-level hotspot first: "filePath::symbol"
1291
- const funcTarget = funcName ? `${file}::${funcName}` : undefined;
1292
- v.hotspot = (funcTarget ? hotspotByTarget.get(funcTarget) : undefined)
1293
- ?? hotspotByFile.get(file)
1294
- ?? 0;
1295
- // Files with no cross-language entities (no reachability data) get a
1296
- // neutral 0.5 so we never demote a finding we know nothing about.
1297
- v.reachability = reachabilityByFile.get(file) ?? 0.5;
1298
- }
1299
- const severityOrder = {
1300
- critical: 0,
1301
- severe: 1,
1302
- high: 2,
1303
- info: 3,
1304
- };
1305
- const indexed = violations.map((v, i) => ({ v, i }));
1306
- indexed.sort((a, b) => {
1307
- const sevA = severityOrder[a.v.severity] ?? 99;
1308
- const sevB = severityOrder[b.v.severity] ?? 99;
1309
- if (sevA !== sevB)
1310
- return sevA - sevB;
1311
- const reachA = a.v.reachability ?? 0.5;
1312
- const reachB = b.v.reachability ?? 0.5;
1313
- if (reachA !== reachB)
1314
- return reachB - reachA;
1315
- const hsA = a.v.hotspot ?? 0;
1316
- const hsB = b.v.hotspot ?? 0;
1317
- if (hsA !== hsB)
1318
- return hsB - hsA;
1319
- return a.i - b.i;
1320
- });
1321
- result.violations = indexed.map((x) => x.v);
1280
+ function applyReachabilityRanking(ordered, reachabilityByFile) {
1281
+ const severityOrder = {
1282
+ critical: 0,
1283
+ severe: 1,
1284
+ high: 2,
1285
+ info: 3,
1286
+ };
1287
+ for (const result of Object.values(ordered)) {
1288
+ const violations = result.violations;
1289
+ for (const v of violations) {
1290
+ // A file absent from the corpus graph gets a neutral 0.5 so we never
1291
+ // demote a finding we know nothing about.
1292
+ v.reachability = reachabilityByFile?.get(v.file) ?? 0.5;
1322
1293
  }
1323
- }
1324
- catch {
1325
- // Hotspot/reachability reordering is advisory — failure is non-fatal
1294
+ const indexed = violations.map((v, i) => ({ v, i }));
1295
+ indexed.sort((a, b) => {
1296
+ const sevA = severityOrder[a.v.severity] ?? 99;
1297
+ const sevB = severityOrder[b.v.severity] ?? 99;
1298
+ if (sevA !== sevB)
1299
+ return sevA - sevB;
1300
+ const reachA = a.v.reachability ?? 0.5;
1301
+ const reachB = b.v.reachability ?? 0.5;
1302
+ if (reachA !== reachB)
1303
+ return reachB - reachA;
1304
+ return a.i - b.i;
1305
+ });
1306
+ result.violations = indexed.map((x) => x.v);
1326
1307
  }
1327
1308
  }
1328
1309
  /**
@@ -1369,7 +1350,7 @@ function classifyBaselineFindings(ordered, mergedOptions, files, isScoped) {
1369
1350
  * Spec 60 size/test/distribution reports), and hook-contract-clean violations.
1370
1351
  */
1371
1352
  function buildAuditResult(inputs) {
1372
- const { ordered, files, analyzers, mergedOptions, isScoped, scopeResultType, indexFactsWritten, writeIndexFactsMs, startTime, startCpu, provenanceTiming, blastRadius, zeroFilesDiagnostics, pipelineDiagnostics, rejected, lintConfigDiagnostics, configPath, baselineMetadata, pipelineCoverage, routeAttribution, pipelineTableCatalog, pipelineStageTiming, thresholdChanges, thresholdSources, pipelineSkippedFiles, pipelineUnparsedFiles, skippedExtensions, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, collectedFunctions, fileToFunctionsMap, } = inputs;
1353
+ const { ordered, files, analyzers, mergedOptions, isScoped, scopeResultType, indexFactsWritten, writeIndexFactsMs, startTime, startCpu, provenanceTiming, blastRadius, changedScopeNoBaseline, zeroFilesDiagnostics, pipelineDiagnostics, rejected, lintConfigDiagnostics, configPath, baselineMetadata, pipelineCoverage, pipelineTableCatalog, pipelineStageTiming, thresholdChanges, thresholdSources, pipelineSkippedFiles, pipelineUnparsedFiles, skippedExtensions, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, collectedFunctions, fileToFunctionsMap, } = inputs;
1373
1354
  // Spec 69 §10 R4 — count query-shaped call sites whose DB receiver is
1374
1355
  // unproven, keyed by the analyzer that owns the receiver-resolution
1375
1356
  // instrument (`schema`). This is the site-level `cannot-fire` surface that
@@ -1380,6 +1361,19 @@ function buildAuditResult(inputs) {
1380
1361
  continue;
1381
1362
  unprovenSitesByAnalyzer.set(d.analyzerName, (unprovenSitesByAnalyzer.get(d.analyzerName) ?? 0) + 1);
1382
1363
  }
1364
+ // A bare `changed` scope that found no baseline to diff against (an empty
1365
+ // `functions` table) is reported as an uncomputable diff, never as a clean
1366
+ // run. The diagnostic names both possible causes: the index was never
1367
+ // populated, or it was discarded (e.g. after corruption).
1368
+ const noBaselineDiagnostics = changedScopeNoBaseline
1369
+ ? [{
1370
+ analyzerName: 'code-index',
1371
+ kind: 'no-baseline',
1372
+ message: 'Cannot determine what changed: the code index holds no baseline to diff against ' +
1373
+ '(no previously indexed files). No index has been built yet, or a prior index was ' +
1374
+ 'discarded. Run a full audit to build the baseline, then re-run `changed`.',
1375
+ }]
1376
+ : [];
1383
1377
  const summary = generateSummary(ordered, files.length, unprovenSitesByAnalyzer);
1384
1378
  return {
1385
1379
  timestamp: new Date(),
@@ -1397,8 +1391,9 @@ function buildAuditResult(inputs) {
1397
1391
  scope: scopeResultType,
1398
1392
  provenanceResolutionMs: provenanceTiming.totalMs,
1399
1393
  ...(blastRadius && { blastRadius }),
1400
- ...((zeroFilesDiagnostics.length > 0 || (pipelineDiagnostics?.length ?? 0) > 0 || rejected.length > 0 || lintConfigDiagnostics.length > 0) && {
1394
+ ...((noBaselineDiagnostics.length > 0 || zeroFilesDiagnostics.length > 0 || (pipelineDiagnostics?.length ?? 0) > 0 || rejected.length > 0 || lintConfigDiagnostics.length > 0) && {
1401
1395
  diagnostics: [
1396
+ ...noBaselineDiagnostics,
1402
1397
  ...zeroFilesDiagnostics,
1403
1398
  ...(pipelineDiagnostics ?? []),
1404
1399
  ...lintConfigDiagnostics,
@@ -1413,7 +1408,6 @@ function buildAuditResult(inputs) {
1413
1408
  }),
1414
1409
  ...(baselineMetadata && { baseline: baselineMetadata }),
1415
1410
  ...(pipelineCoverage && { coverage: pipelineCoverage }),
1416
- ...(routeAttribution && { routeAttribution }),
1417
1411
  ...(pipelineTableCatalog && { tableCatalog: pipelineTableCatalog }),
1418
1412
  ...(pipelineStageTiming && { stageTiming: pipelineStageTiming }),
1419
1413
  ...(thresholdChanges.length > 0 && { thresholdChanges }),
@@ -1492,7 +1486,7 @@ export function createAuditRunner(options = {}) {
1492
1486
  // discovery (full `all` scope) and the pipeline (stage 1/2) so every touched
1493
1487
  // file lands in exactly one terminal state and the balance can be asserted.
1494
1488
  const fileAccounting = new FileAccounting();
1495
- const { files, changedFunctions, skippedExtensions, blastRadius, corpusFiles, scope, isScoped, scopeResultType, } = await discoverAuditFiles(mergedOptions, root, fileAccounting);
1489
+ const { files, changedFunctions, skippedExtensions, blastRadius, corpusFiles, scope, isScoped, scopeResultType, changedScopeNoBaseline, } = await discoverAuditFiles(mergedOptions, root, fileAccounting);
1496
1490
  const { collectedFunctions, fileToFunctionsMap } = await collectIndexedFunctions(mergedOptions, files);
1497
1491
  // Run analyzers
1498
1492
  // Spec 68 §15 — no selection model: the full pipeline always runs. `indexOnly`
@@ -1510,7 +1504,7 @@ export function createAuditRunner(options = {}) {
1510
1504
  mergedOptions, root, files, corpusFiles, isScoped, fileAccounting,
1511
1505
  styleConsumedFiles, styleContributingFiles, analyzers, provenanceTiming,
1512
1506
  });
1513
- const { analyzerResults, phaseFindings, phaseIncompleteFacts, pipelineCoverage, pipelineTableCatalog, pipelineStageTiming, pipelineSkippedFiles, pipelineUnparsedFiles, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineDiagnostics, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, routeAttribution, indexFactsWritten, writeIndexFactsMs, } = pipeline;
1507
+ const { analyzerResults, phaseFindings, phaseIncompleteFacts, phaseReachability, pipelineCoverage, pipelineTableCatalog, pipelineStageTiming, pipelineSkippedFiles, pipelineUnparsedFiles, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineDiagnostics, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, indexFactsWritten, writeIndexFactsMs, } = pipeline;
1514
1508
  // ── Zero-files diagnostic ─────────────────────────────────────────────
1515
1509
  // Extracted to runZeroFilesDiagnostics() for testability. Runs against the
1516
1510
  // raw analyzerResults before truthiness filtering so the "no result" pass
@@ -1526,8 +1520,8 @@ export function createAuditRunner(options = {}) {
1526
1520
  // Normalize the analyzer field on every violation to its result key (the
1527
1521
  // single source of truth — see stampAnalyzerFields).
1528
1522
  stampAnalyzerFields(orderedAnalyzerResults);
1529
- // Spec 13 R2 — hotspot scoring & finding reordering (advisory).
1530
- await applyHotspotScoring(orderedAnalyzerResults, root);
1523
+ // Spec 13 R2 — reachability scoring & finding reordering (advisory).
1524
+ applyReachabilityRanking(orderedAnalyzerResults, phaseReachability);
1531
1525
  // Hook-contract guard: no violation may carry an empty file path, line 0, or
1532
1526
  // missing line. A sentinel violation with file:'' broke the Claude Code hook's
1533
1527
  // JSON consumer (Spec 15 regression); line:0 from CrossDomainAnalyzer broke it
@@ -1552,6 +1546,7 @@ export function createAuditRunner(options = {}) {
1552
1546
  startCpu,
1553
1547
  provenanceTiming,
1554
1548
  blastRadius,
1549
+ changedScopeNoBaseline,
1555
1550
  zeroFilesDiagnostics,
1556
1551
  pipelineDiagnostics,
1557
1552
  rejected,
@@ -1559,7 +1554,6 @@ export function createAuditRunner(options = {}) {
1559
1554
  configPath,
1560
1555
  baselineMetadata,
1561
1556
  pipelineCoverage,
1562
- routeAttribution,
1563
1557
  pipelineTableCatalog,
1564
1558
  pipelineStageTiming,
1565
1559
  thresholdChanges,