code-auditor-mcp 4.1.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1086) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +374 -0
  3. package/README.md +4 -4
  4. package/configs/hhra-compat.json +0 -8
  5. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +1 -1
  6. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +1 -1
  7. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +7 -1
  8. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +1 -1
  9. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +2 -8
  10. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +1 -1
  11. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +4 -4
  12. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +1 -1
  13. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +1 -1
  14. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +1 -1
  15. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +1 -1
  16. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +2 -2
  17. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +1 -1
  18. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +1 -1
  19. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +1 -1
  20. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +1 -1
  21. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +1 -1
  22. package/dist/analyzers/applicability.d.ts +42 -0
  23. package/dist/analyzers/applicability.d.ts.map +1 -1
  24. package/dist/analyzers/applicability.js +74 -0
  25. package/dist/analyzers/applicability.js.map +1 -1
  26. package/dist/analyzers/cross-language/APIContractAnalyzer.js +18 -24
  27. package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
  28. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +30 -23
  29. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
  30. package/dist/analyzers/cross-language/DependencyGraphBuilder.js +113 -76
  31. package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
  32. package/dist/analyzers/cross-language/SchemaValidator.d.ts +2 -0
  33. package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
  34. package/dist/analyzers/cross-language/SchemaValidator.js +8 -3
  35. package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
  36. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
  37. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +210 -148
  38. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
  39. package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
  40. package/dist/analyzers/documentationAnalyzer.js +23 -28
  41. package/dist/analyzers/documentationAnalyzer.js.map +1 -1
  42. package/dist/analyzers/handleIdentification.d.ts +356 -0
  43. package/dist/analyzers/handleIdentification.d.ts.map +1 -0
  44. package/dist/analyzers/handleIdentification.js +424 -0
  45. package/dist/analyzers/handleIdentification.js.map +1 -0
  46. package/dist/analyzers/manifestStaleness.d.ts +64 -0
  47. package/dist/analyzers/manifestStaleness.d.ts.map +1 -0
  48. package/dist/analyzers/manifestStaleness.js +115 -0
  49. package/dist/analyzers/manifestStaleness.js.map +1 -0
  50. package/dist/analyzers/orgFilterTiers.d.ts +73 -1
  51. package/dist/analyzers/orgFilterTiers.d.ts.map +1 -1
  52. package/dist/analyzers/orgFilterTiers.js +166 -1
  53. package/dist/analyzers/orgFilterTiers.js.map +1 -1
  54. package/dist/analyzers/provenance.d.ts +251 -57
  55. package/dist/analyzers/provenance.d.ts.map +1 -1
  56. package/dist/analyzers/provenance.js +1109 -429
  57. package/dist/analyzers/provenance.js.map +1 -1
  58. package/dist/analyzers/reactAnalyzer.d.ts +2 -0
  59. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  60. package/dist/analyzers/reactAnalyzer.js +2 -0
  61. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  62. package/dist/analyzers/receiverResolution.d.ts +183 -0
  63. package/dist/analyzers/receiverResolution.d.ts.map +1 -0
  64. package/dist/analyzers/receiverResolution.js +540 -0
  65. package/dist/analyzers/receiverResolution.js.map +1 -0
  66. package/dist/analyzers/receiverRoot.d.ts +328 -0
  67. package/dist/analyzers/receiverRoot.d.ts.map +1 -0
  68. package/dist/analyzers/receiverRoot.js +990 -0
  69. package/dist/analyzers/receiverRoot.js.map +1 -0
  70. package/dist/analyzers/ruleRegistry.d.ts +12 -68
  71. package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
  72. package/dist/analyzers/ruleRegistry.js +32 -342
  73. package/dist/analyzers/ruleRegistry.js.map +1 -1
  74. package/dist/analyzers/ruleTiming.d.ts +10 -0
  75. package/dist/analyzers/ruleTiming.d.ts.map +1 -1
  76. package/dist/analyzers/ruleTiming.js +20 -0
  77. package/dist/analyzers/ruleTiming.js.map +1 -1
  78. package/dist/analyzers/sqlLiteral.d.ts +76 -0
  79. package/dist/analyzers/sqlLiteral.d.ts.map +1 -0
  80. package/dist/analyzers/sqlLiteral.js +419 -0
  81. package/dist/analyzers/sqlLiteral.js.map +1 -0
  82. package/dist/analyzers/tsEcosystem.d.ts +115 -0
  83. package/dist/analyzers/tsEcosystem.d.ts.map +1 -0
  84. package/dist/analyzers/tsEcosystem.js +231 -0
  85. package/dist/analyzers/tsEcosystem.js.map +1 -0
  86. package/dist/analyzers/tsExpressionDescriptor.d.ts +156 -0
  87. package/dist/analyzers/tsExpressionDescriptor.d.ts.map +1 -0
  88. package/dist/analyzers/tsExpressionDescriptor.js +374 -0
  89. package/dist/analyzers/tsExpressionDescriptor.js.map +1 -0
  90. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +6 -0
  91. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
  92. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +107 -44
  93. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
  94. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +1 -12
  95. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
  96. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +24 -101
  97. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
  98. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +287 -49
  99. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  100. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +2130 -512
  101. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  102. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
  103. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +2 -18
  104. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
  105. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +0 -33
  106. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  107. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +20 -143
  108. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  109. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +4 -3
  110. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
  111. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +36 -13
  112. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
  113. package/dist/analyzers/universal/UniversalSecretsAnalyzer.d.ts.map +1 -1
  114. package/dist/analyzers/universal/UniversalSecretsAnalyzer.js +6 -0
  115. package/dist/analyzers/universal/UniversalSecretsAnalyzer.js.map +1 -1
  116. package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts +15 -0
  117. package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts.map +1 -1
  118. package/dist/analyzers/universal/UniversalSecurityAnalyzer.js +25 -0
  119. package/dist/analyzers/universal/UniversalSecurityAnalyzer.js.map +1 -1
  120. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +21 -32
  121. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
  122. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +166 -196
  123. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
  124. package/dist/analyzers/universal/functionConcerns.d.ts +3 -0
  125. package/dist/analyzers/universal/functionConcerns.d.ts.map +1 -1
  126. package/dist/analyzers/universal/functionConcerns.js +4 -1
  127. package/dist/analyzers/universal/functionConcerns.js.map +1 -1
  128. package/dist/analyzers/universal/schema/codeAnalysis.d.ts +310 -69
  129. package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
  130. package/dist/analyzers/universal/schema/codeAnalysis.js +709 -383
  131. package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
  132. package/dist/analyzers/universal/schema/config.d.ts +3 -14
  133. package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
  134. package/dist/analyzers/universal/schema/config.js +3 -16
  135. package/dist/analyzers/universal/schema/config.js.map +1 -1
  136. package/dist/analyzers/universal/schema/discovery.d.ts +22 -1
  137. package/dist/analyzers/universal/schema/discovery.d.ts.map +1 -1
  138. package/dist/analyzers/universal/schema/discovery.js +36 -20
  139. package/dist/analyzers/universal/schema/discovery.js.map +1 -1
  140. package/dist/analyzers/universal/schema/jsonSchema.d.ts.map +1 -1
  141. package/dist/analyzers/universal/schema/jsonSchema.js +13 -3
  142. package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -1
  143. package/dist/analyzers/universal/schema/migrations.d.ts +187 -27
  144. package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -1
  145. package/dist/analyzers/universal/schema/migrations.js +443 -186
  146. package/dist/analyzers/universal/schema/migrations.js.map +1 -1
  147. package/dist/analyzers/universal/schema/types.d.ts +17 -3
  148. package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
  149. package/dist/analyzers/universal/schema/violations.js +1 -1
  150. package/dist/analyzers/universal/schema/violations.js.map +1 -1
  151. package/dist/analyzers/universal/styleScale.d.ts +52 -0
  152. package/dist/analyzers/universal/styleScale.d.ts.map +1 -0
  153. package/dist/analyzers/universal/styleScale.js +95 -0
  154. package/dist/analyzers/universal/styleScale.js.map +1 -0
  155. package/dist/applyDataDirEnv.js +1 -1
  156. package/dist/applyDataDirEnv.js.map +1 -1
  157. package/dist/auditRouter.d.ts +26 -30
  158. package/dist/auditRouter.d.ts.map +1 -1
  159. package/dist/auditRouter.js +373 -143
  160. package/dist/auditRouter.js.map +1 -1
  161. package/dist/auditRunner.d.ts +64 -1
  162. package/dist/auditRunner.d.ts.map +1 -1
  163. package/dist/auditRunner.js +1537 -1051
  164. package/dist/auditRunner.js.map +1 -1
  165. package/dist/baseline.d.ts +30 -3
  166. package/dist/baseline.d.ts.map +1 -1
  167. package/dist/baseline.js +70 -9
  168. package/dist/baseline.js.map +1 -1
  169. package/dist/cli.js +997 -812
  170. package/dist/cli.js.map +1 -1
  171. package/dist/codeIndex/analyzerConfig.d.ts +60 -0
  172. package/dist/codeIndex/analyzerConfig.d.ts.map +1 -0
  173. package/dist/codeIndex/analyzerConfig.js +98 -0
  174. package/dist/codeIndex/analyzerConfig.js.map +1 -0
  175. package/dist/codeIndex/auditResults.d.ts +39 -0
  176. package/dist/codeIndex/auditResults.d.ts.map +1 -0
  177. package/dist/codeIndex/auditResults.js +99 -0
  178. package/dist/codeIndex/auditResults.js.map +1 -0
  179. package/dist/codeIndex/codeMap.d.ts +59 -0
  180. package/dist/codeIndex/codeMap.d.ts.map +1 -0
  181. package/dist/codeIndex/codeMap.js +72 -0
  182. package/dist/codeIndex/codeMap.js.map +1 -0
  183. package/dist/codeIndex/conventions.d.ts +33 -0
  184. package/dist/codeIndex/conventions.d.ts.map +1 -0
  185. package/dist/codeIndex/conventions.js +84 -0
  186. package/dist/codeIndex/conventions.js.map +1 -0
  187. package/dist/codeIndex/coverage.d.ts +78 -0
  188. package/dist/codeIndex/coverage.d.ts.map +1 -0
  189. package/dist/codeIndex/coverage.js +164 -0
  190. package/dist/codeIndex/coverage.js.map +1 -0
  191. package/dist/codeIndex/dependencyGraph.d.ts +97 -0
  192. package/dist/codeIndex/dependencyGraph.d.ts.map +1 -0
  193. package/dist/codeIndex/dependencyGraph.js +241 -0
  194. package/dist/codeIndex/dependencyGraph.js.map +1 -0
  195. package/dist/codeIndex/functionIndex.d.ts +111 -0
  196. package/dist/codeIndex/functionIndex.d.ts.map +1 -0
  197. package/dist/codeIndex/functionIndex.js +306 -0
  198. package/dist/codeIndex/functionIndex.js.map +1 -0
  199. package/dist/codeIndex/inferredReceivers.d.ts +32 -0
  200. package/dist/codeIndex/inferredReceivers.d.ts.map +1 -0
  201. package/dist/codeIndex/inferredReceivers.js +41 -0
  202. package/dist/codeIndex/inferredReceivers.js.map +1 -0
  203. package/dist/codeIndex/meta.d.ts +30 -0
  204. package/dist/codeIndex/meta.d.ts.map +1 -0
  205. package/dist/codeIndex/meta.js +37 -0
  206. package/dist/codeIndex/meta.js.map +1 -0
  207. package/dist/codeIndex/migrations.d.ts +96 -0
  208. package/dist/codeIndex/migrations.d.ts.map +1 -0
  209. package/dist/codeIndex/migrations.js +1302 -0
  210. package/dist/codeIndex/migrations.js.map +1 -0
  211. package/dist/codeIndex/projectTasks.d.ts +54 -0
  212. package/dist/codeIndex/projectTasks.d.ts.map +1 -0
  213. package/dist/codeIndex/projectTasks.js +81 -0
  214. package/dist/codeIndex/projectTasks.js.map +1 -0
  215. package/dist/codeIndex/provenance.d.ts +47 -0
  216. package/dist/codeIndex/provenance.d.ts.map +1 -0
  217. package/dist/codeIndex/provenance.js +48 -0
  218. package/dist/codeIndex/provenance.js.map +1 -0
  219. package/dist/codeIndex/rawSql.d.ts +54 -0
  220. package/dist/codeIndex/rawSql.d.ts.map +1 -0
  221. package/dist/codeIndex/rawSql.js +71 -0
  222. package/dist/codeIndex/rawSql.js.map +1 -0
  223. package/dist/codeIndex/schema.d.ts +99 -0
  224. package/dist/codeIndex/schema.d.ts.map +1 -0
  225. package/dist/codeIndex/schema.js +174 -0
  226. package/dist/codeIndex/schema.js.map +1 -0
  227. package/dist/codeIndex/search.d.ts +40 -0
  228. package/dist/codeIndex/search.d.ts.map +1 -0
  229. package/dist/codeIndex/search.js +316 -0
  230. package/dist/codeIndex/search.js.map +1 -0
  231. package/dist/codeIndex/shared.d.ts +27 -0
  232. package/dist/codeIndex/shared.d.ts.map +1 -0
  233. package/dist/codeIndex/shared.js +46 -0
  234. package/dist/codeIndex/shared.js.map +1 -0
  235. package/dist/codeIndex/sqliteCollection.d.ts +81 -0
  236. package/dist/codeIndex/sqliteCollection.d.ts.map +1 -0
  237. package/dist/codeIndex/sqliteCollection.js +250 -0
  238. package/dist/codeIndex/sqliteCollection.js.map +1 -0
  239. package/dist/codeIndex/whitelist.d.ts +59 -0
  240. package/dist/codeIndex/whitelist.d.ts.map +1 -0
  241. package/dist/codeIndex/whitelist.js +188 -0
  242. package/dist/codeIndex/whitelist.js.map +1 -0
  243. package/dist/codeIndexDB.d.ts +130 -277
  244. package/dist/codeIndexDB.d.ts.map +1 -1
  245. package/dist/codeIndexDB.js +364 -2748
  246. package/dist/codeIndexDB.js.map +1 -1
  247. package/dist/codeIndexService.d.ts +87 -12
  248. package/dist/codeIndexService.d.ts.map +1 -1
  249. package/dist/codeIndexService.js +110 -34
  250. package/dist/codeIndexService.js.map +1 -1
  251. package/dist/componentScanner.d.ts +35 -0
  252. package/dist/componentScanner.d.ts.map +1 -1
  253. package/dist/componentScanner.js +114 -80
  254. package/dist/componentScanner.js.map +1 -1
  255. package/dist/config/configGeneratePath.d.ts +4 -0
  256. package/dist/config/configGeneratePath.d.ts.map +1 -1
  257. package/dist/config/configGeneratePath.js +4 -0
  258. package/dist/config/configGeneratePath.js.map +1 -1
  259. package/dist/config/configLoader.d.ts +16 -1
  260. package/dist/config/configLoader.d.ts.map +1 -1
  261. package/dist/config/configLoader.js +26 -23
  262. package/dist/config/configLoader.js.map +1 -1
  263. package/dist/config/defaults.d.ts +11 -7
  264. package/dist/config/defaults.d.ts.map +1 -1
  265. package/dist/config/defaults.js +21 -20
  266. package/dist/config/defaults.js.map +1 -1
  267. package/dist/config/effectiveConfig.d.ts +17 -2
  268. package/dist/config/effectiveConfig.d.ts.map +1 -1
  269. package/dist/config/effectiveConfig.js +32 -25
  270. package/dist/config/effectiveConfig.js.map +1 -1
  271. package/dist/config/lintConfigReader.d.ts +6 -0
  272. package/dist/config/lintConfigReader.d.ts.map +1 -1
  273. package/dist/config/lintConfigReader.js +6 -0
  274. package/dist/config/lintConfigReader.js.map +1 -1
  275. package/dist/config/staticObjectExtract.d.ts +4 -0
  276. package/dist/config/staticObjectExtract.d.ts.map +1 -1
  277. package/dist/config/staticObjectExtract.js +7 -3
  278. package/dist/config/staticObjectExtract.js.map +1 -1
  279. package/dist/config/thresholdRationales.d.ts +2 -0
  280. package/dist/config/thresholdRationales.d.ts.map +1 -1
  281. package/dist/config/thresholdRationales.js +5 -2
  282. package/dist/config/thresholdRationales.js.map +1 -1
  283. package/dist/constants.d.ts +1 -1
  284. package/dist/constants.d.ts.map +1 -1
  285. package/dist/constants.js +10 -5
  286. package/dist/constants.js.map +1 -1
  287. package/dist/conventions/conventionMiner.d.ts +105 -2
  288. package/dist/conventions/conventionMiner.d.ts.map +1 -1
  289. package/dist/conventions/conventionMiner.js +334 -259
  290. package/dist/conventions/conventionMiner.js.map +1 -1
  291. package/dist/coverage/coverageService.js +4 -4
  292. package/dist/coverage/coverageService.js.map +1 -1
  293. package/dist/daemon/args.d.ts +3 -0
  294. package/dist/daemon/args.d.ts.map +1 -1
  295. package/dist/daemon/args.js +3 -0
  296. package/dist/daemon/args.js.map +1 -1
  297. package/dist/daemon/core.d.ts +33 -27
  298. package/dist/daemon/core.d.ts.map +1 -1
  299. package/dist/daemon/core.js +198 -162
  300. package/dist/daemon/core.js.map +1 -1
  301. package/dist/daemon/lspServer.d.ts +8 -0
  302. package/dist/daemon/lspServer.d.ts.map +1 -1
  303. package/dist/daemon/lspServer.js +8 -0
  304. package/dist/daemon/lspServer.js.map +1 -1
  305. package/dist/daemon/main.d.ts +2 -0
  306. package/dist/daemon/main.d.ts.map +1 -1
  307. package/dist/daemon/main.js +2 -0
  308. package/dist/daemon/main.js.map +1 -1
  309. package/dist/daemon/resolve.d.ts +22 -7
  310. package/dist/daemon/resolve.d.ts.map +1 -1
  311. package/dist/daemon/resolve.js +29 -8
  312. package/dist/daemon/resolve.js.map +1 -1
  313. package/dist/daemon/socketClient.d.ts +10 -1
  314. package/dist/daemon/socketClient.d.ts.map +1 -1
  315. package/dist/daemon/socketClient.js +10 -1
  316. package/dist/daemon/socketClient.js.map +1 -1
  317. package/dist/daemon/socketServer.d.ts +13 -0
  318. package/dist/daemon/socketServer.d.ts.map +1 -1
  319. package/dist/daemon/socketServer.js +13 -0
  320. package/dist/daemon/socketServer.js.map +1 -1
  321. package/dist/dataPaths.d.ts +27 -8
  322. package/dist/dataPaths.d.ts.map +1 -1
  323. package/dist/dataPaths.js +28 -44
  324. package/dist/dataPaths.js.map +1 -1
  325. package/dist/dismissals.d.ts +22 -1
  326. package/dist/dismissals.d.ts.map +1 -1
  327. package/dist/dismissals.js +22 -1
  328. package/dist/dismissals.js.map +1 -1
  329. package/dist/enforcement/gate.d.ts +1 -0
  330. package/dist/enforcement/gate.d.ts.map +1 -1
  331. package/dist/enforcement/gate.js +1 -0
  332. package/dist/enforcement/gate.js.map +1 -1
  333. package/dist/fingerprint.d.ts +6 -0
  334. package/dist/fingerprint.d.ts.map +1 -1
  335. package/dist/fingerprint.js +46 -1
  336. package/dist/fingerprint.js.map +1 -1
  337. package/dist/functionScanner.d.ts +30 -1
  338. package/dist/functionScanner.d.ts.map +1 -1
  339. package/dist/functionScanner.js +258 -256
  340. package/dist/functionScanner.js.map +1 -1
  341. package/dist/generators/AWSQConfigGenerator.d.ts +23 -0
  342. package/dist/generators/AWSQConfigGenerator.d.ts.map +1 -1
  343. package/dist/generators/AWSQConfigGenerator.js +23 -0
  344. package/dist/generators/AWSQConfigGenerator.js.map +1 -1
  345. package/dist/generators/BaseConfigGenerator.d.ts +7 -0
  346. package/dist/generators/BaseConfigGenerator.d.ts.map +1 -1
  347. package/dist/generators/BaseConfigGenerator.js +7 -0
  348. package/dist/generators/BaseConfigGenerator.js.map +1 -1
  349. package/dist/generators/ClaudeConfigGenerator.d.ts +23 -0
  350. package/dist/generators/ClaudeConfigGenerator.d.ts.map +1 -1
  351. package/dist/generators/ClaudeConfigGenerator.js +23 -0
  352. package/dist/generators/ClaudeConfigGenerator.js.map +1 -1
  353. package/dist/generators/ClineConfigGenerator.d.ts +23 -0
  354. package/dist/generators/ClineConfigGenerator.d.ts.map +1 -1
  355. package/dist/generators/ClineConfigGenerator.js +23 -0
  356. package/dist/generators/ClineConfigGenerator.js.map +1 -1
  357. package/dist/generators/CodeiumConfigGenerator.d.ts +23 -0
  358. package/dist/generators/CodeiumConfigGenerator.d.ts.map +1 -1
  359. package/dist/generators/CodeiumConfigGenerator.js +23 -0
  360. package/dist/generators/CodeiumConfigGenerator.js.map +1 -1
  361. package/dist/generators/CodexConfigGenerator.d.ts +23 -0
  362. package/dist/generators/CodexConfigGenerator.d.ts.map +1 -1
  363. package/dist/generators/CodexConfigGenerator.js +23 -0
  364. package/dist/generators/CodexConfigGenerator.js.map +1 -1
  365. package/dist/generators/ConfigGeneratorFactory.d.ts +9 -0
  366. package/dist/generators/ConfigGeneratorFactory.d.ts.map +1 -1
  367. package/dist/generators/ConfigGeneratorFactory.js +9 -0
  368. package/dist/generators/ConfigGeneratorFactory.js.map +1 -1
  369. package/dist/generators/ContinueConfigGenerator.d.ts +23 -0
  370. package/dist/generators/ContinueConfigGenerator.d.ts.map +1 -1
  371. package/dist/generators/ContinueConfigGenerator.js +23 -0
  372. package/dist/generators/ContinueConfigGenerator.js.map +1 -1
  373. package/dist/generators/CopilotConfigGenerator.d.ts +23 -0
  374. package/dist/generators/CopilotConfigGenerator.d.ts.map +1 -1
  375. package/dist/generators/CopilotConfigGenerator.js +23 -0
  376. package/dist/generators/CopilotConfigGenerator.js.map +1 -1
  377. package/dist/generators/CursorConfigGenerator.d.ts +23 -0
  378. package/dist/generators/CursorConfigGenerator.d.ts.map +1 -1
  379. package/dist/generators/CursorConfigGenerator.js +23 -0
  380. package/dist/generators/CursorConfigGenerator.js.map +1 -1
  381. package/dist/generators/GeminiConfigGenerator.d.ts +23 -0
  382. package/dist/generators/GeminiConfigGenerator.d.ts.map +1 -1
  383. package/dist/generators/GeminiConfigGenerator.js +23 -0
  384. package/dist/generators/GeminiConfigGenerator.js.map +1 -1
  385. package/dist/generators/VSCodeConfigGenerator.d.ts +23 -0
  386. package/dist/generators/VSCodeConfigGenerator.d.ts.map +1 -1
  387. package/dist/generators/VSCodeConfigGenerator.js +23 -0
  388. package/dist/generators/VSCodeConfigGenerator.js.map +1 -1
  389. package/dist/generators/ZCodeConfigGenerator.d.ts +23 -0
  390. package/dist/generators/ZCodeConfigGenerator.d.ts.map +1 -1
  391. package/dist/generators/ZCodeConfigGenerator.js +23 -0
  392. package/dist/generators/ZCodeConfigGenerator.js.map +1 -1
  393. package/dist/graph/blastRadius.d.ts.map +1 -1
  394. package/dist/graph/blastRadius.js +12 -7
  395. package/dist/graph/blastRadius.js.map +1 -1
  396. package/dist/graph/callGraph.d.ts +31 -0
  397. package/dist/graph/callGraph.d.ts.map +1 -1
  398. package/dist/graph/callGraph.js +62 -32
  399. package/dist/graph/callGraph.js.map +1 -1
  400. package/dist/graph/importClassification.d.ts +28 -0
  401. package/dist/graph/importClassification.d.ts.map +1 -1
  402. package/dist/graph/importClassification.js +57 -0
  403. package/dist/graph/importClassification.js.map +1 -1
  404. package/dist/graph/importGraph.d.ts +38 -0
  405. package/dist/graph/importGraph.d.ts.map +1 -1
  406. package/dist/graph/importGraph.js +38 -0
  407. package/dist/graph/importGraph.js.map +1 -1
  408. package/dist/graph/outputFormatter.d.ts +12 -4
  409. package/dist/graph/outputFormatter.d.ts.map +1 -1
  410. package/dist/graph/outputFormatter.js +12 -62
  411. package/dist/graph/outputFormatter.js.map +1 -1
  412. package/dist/hooks/codex.d.ts +8 -1
  413. package/dist/hooks/codex.d.ts.map +1 -1
  414. package/dist/hooks/codex.js +7 -0
  415. package/dist/hooks/codex.js.map +1 -1
  416. package/dist/hooks/core.d.ts +6 -0
  417. package/dist/hooks/core.d.ts.map +1 -1
  418. package/dist/hooks/core.js +6 -1
  419. package/dist/hooks/core.js.map +1 -1
  420. package/dist/hooks/cursor.d.ts +11 -1
  421. package/dist/hooks/cursor.d.ts.map +1 -1
  422. package/dist/hooks/cursor.js +10 -0
  423. package/dist/hooks/cursor.js.map +1 -1
  424. package/dist/index.d.ts +4 -0
  425. package/dist/index.d.ts.map +1 -1
  426. package/dist/index.js +4 -0
  427. package/dist/index.js.map +1 -1
  428. package/dist/installConfig.d.ts +23 -5
  429. package/dist/installConfig.d.ts.map +1 -1
  430. package/dist/installConfig.js +23 -5
  431. package/dist/installConfig.js.map +1 -1
  432. package/dist/installer.d.ts +6 -0
  433. package/dist/installer.d.ts.map +1 -1
  434. package/dist/installer.js +6 -0
  435. package/dist/installer.js.map +1 -1
  436. package/dist/invariants/ruleEngine.d.ts +3 -0
  437. package/dist/invariants/ruleEngine.d.ts.map +1 -1
  438. package/dist/invariants/ruleEngine.js +156 -119
  439. package/dist/invariants/ruleEngine.js.map +1 -1
  440. package/dist/invariants/ruleValidator.d.ts +3 -0
  441. package/dist/invariants/ruleValidator.d.ts.map +1 -1
  442. package/dist/invariants/ruleValidator.js +185 -178
  443. package/dist/invariants/ruleValidator.js.map +1 -1
  444. package/dist/languages/LanguageOrchestrator.d.ts +4 -1
  445. package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
  446. package/dist/languages/LanguageOrchestrator.js +8 -19
  447. package/dist/languages/LanguageOrchestrator.js.map +1 -1
  448. package/dist/languages/RuntimeManager.d.ts +8 -0
  449. package/dist/languages/RuntimeManager.d.ts.map +1 -1
  450. package/dist/languages/RuntimeManager.js +55 -5
  451. package/dist/languages/RuntimeManager.js.map +1 -1
  452. package/dist/languages/UniversalAnalyzer.js +1 -1
  453. package/dist/languages/UniversalAnalyzer.js.map +1 -1
  454. package/dist/languages/adapterBridge.d.ts +4 -0
  455. package/dist/languages/adapterBridge.d.ts.map +1 -1
  456. package/dist/languages/adapterBridge.js +37 -3
  457. package/dist/languages/adapterBridge.js.map +1 -1
  458. package/dist/languages/go/GoAdapter.d.ts.map +1 -1
  459. package/dist/languages/go/GoAdapter.js +11 -14
  460. package/dist/languages/go/GoAdapter.js.map +1 -1
  461. package/dist/languages/go/analyzer-darwin-amd64 +0 -0
  462. package/dist/languages/go/analyzer-darwin-arm64 +0 -0
  463. package/dist/languages/go/analyzer-linux-amd64 +0 -0
  464. package/dist/languages/go/analyzer-linux-arm64 +0 -0
  465. package/dist/languages/go/analyzer-src/analyzer.go +2 -0
  466. package/dist/languages/go/analyzer-src/dataaccess.go +388 -0
  467. package/dist/languages/go/analyzer-src/types.go +8 -0
  468. package/dist/languages/go/analyzer-windows-amd64.exe +0 -0
  469. package/dist/languages/go/database-packages.json +8 -0
  470. package/dist/languages/go/goResolution.d.ts +229 -0
  471. package/dist/languages/go/goResolution.d.ts.map +1 -0
  472. package/dist/languages/go/goResolution.js +799 -0
  473. package/dist/languages/go/goResolution.js.map +1 -0
  474. package/dist/languages/go/stdlib-packages.json +353 -0
  475. package/dist/languages/go/tenantInputs.d.ts +37 -0
  476. package/dist/languages/go/tenantInputs.d.ts.map +1 -0
  477. package/dist/languages/go/tenantInputs.js +121 -0
  478. package/dist/languages/go/tenantInputs.js.map +1 -0
  479. package/dist/languages/index.d.ts +1 -0
  480. package/dist/languages/index.d.ts.map +1 -1
  481. package/dist/languages/index.js +5 -0
  482. package/dist/languages/index.js.map +1 -1
  483. package/dist/languages/json/JsonAdapter.d.ts +177 -0
  484. package/dist/languages/json/JsonAdapter.d.ts.map +1 -0
  485. package/dist/languages/json/JsonAdapter.js +531 -0
  486. package/dist/languages/json/JsonAdapter.js.map +1 -0
  487. package/dist/languages/locationIndex.d.ts +41 -0
  488. package/dist/languages/locationIndex.d.ts.map +1 -0
  489. package/dist/languages/locationIndex.js +42 -0
  490. package/dist/languages/locationIndex.js.map +1 -0
  491. package/dist/languages/sql/dialectDetection.d.ts +57 -0
  492. package/dist/languages/sql/dialectDetection.d.ts.map +1 -0
  493. package/dist/languages/sql/dialectDetection.js +135 -0
  494. package/dist/languages/sql/dialectDetection.js.map +1 -0
  495. package/dist/languages/sql/sqlAst.d.ts +494 -0
  496. package/dist/languages/sql/sqlAst.d.ts.map +1 -0
  497. package/dist/languages/sql/sqlAst.js +1817 -0
  498. package/dist/languages/sql/sqlAst.js.map +1 -0
  499. package/dist/languages/testConventions.d.ts +12 -1
  500. package/dist/languages/testConventions.d.ts.map +1 -1
  501. package/dist/languages/testConventions.js +13 -1
  502. package/dist/languages/testConventions.js.map +1 -1
  503. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +1 -1
  504. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -1
  505. package/dist/languages/tree-sitter/converter.js +2 -2
  506. package/dist/languages/tree-sitter/converter.js.map +1 -1
  507. package/dist/languages/tree-sitter/parser.d.ts +3 -4
  508. package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
  509. package/dist/languages/tree-sitter/parser.js +10 -4
  510. package/dist/languages/tree-sitter/parser.js.map +1 -1
  511. package/dist/languages/tree-sitter/rawNode.d.ts +3 -0
  512. package/dist/languages/tree-sitter/rawNode.d.ts.map +1 -1
  513. package/dist/languages/tree-sitter/rawNode.js +3 -0
  514. package/dist/languages/tree-sitter/rawNode.js.map +1 -1
  515. package/dist/languages/types.d.ts +7 -0
  516. package/dist/languages/types.d.ts.map +1 -1
  517. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +36 -0
  518. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  519. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +168 -45
  520. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  521. package/dist/languages/typescript/database-packages.json +19 -0
  522. package/dist/ledger.d.ts +118 -2
  523. package/dist/ledger.d.ts.map +1 -1
  524. package/dist/ledger.js +167 -45
  525. package/dist/ledger.js.map +1 -1
  526. package/dist/legacyIndexNotice.d.ts +32 -0
  527. package/dist/legacyIndexNotice.d.ts.map +1 -0
  528. package/dist/legacyIndexNotice.js +89 -0
  529. package/dist/legacyIndexNotice.js.map +1 -0
  530. package/dist/mcp-tools/discoveryQueries.d.ts +55 -0
  531. package/dist/mcp-tools/discoveryQueries.d.ts.map +1 -0
  532. package/dist/mcp-tools/discoveryQueries.js +157 -0
  533. package/dist/mcp-tools/discoveryQueries.js.map +1 -0
  534. package/dist/mcp-tools/projectTasks.d.ts +9 -123
  535. package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
  536. package/dist/mcp-tools/projectTasks.js +180 -170
  537. package/dist/mcp-tools/projectTasks.js.map +1 -1
  538. package/dist/mcp-tools/whitelistTools.d.ts +20 -0
  539. package/dist/mcp-tools/whitelistTools.d.ts.map +1 -1
  540. package/dist/mcp-tools/whitelistTools.js +20 -0
  541. package/dist/mcp-tools/whitelistTools.js.map +1 -1
  542. package/dist/mcp-tools/workflowGuide.d.ts +9 -0
  543. package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
  544. package/dist/mcp-tools/workflowGuide.js +13 -5
  545. package/dist/mcp-tools/workflowGuide.js.map +1 -1
  546. package/dist/mcp-tools-shared.d.ts +78 -0
  547. package/dist/mcp-tools-shared.d.ts.map +1 -1
  548. package/dist/mcp-tools-shared.js +203 -340
  549. package/dist/mcp-tools-shared.js.map +1 -1
  550. package/dist/mcp-ui-simple.d.ts +2 -0
  551. package/dist/mcp-ui-simple.d.ts.map +1 -1
  552. package/dist/mcp-ui-simple.js +12 -5
  553. package/dist/mcp-ui-simple.js.map +1 -1
  554. package/dist/mcp.d.ts +6 -0
  555. package/dist/mcp.d.ts.map +1 -1
  556. package/dist/mcp.js +832 -851
  557. package/dist/mcp.js.map +1 -1
  558. package/dist/mcpAuditJobs.d.ts +29 -33
  559. package/dist/mcpAuditJobs.d.ts.map +1 -1
  560. package/dist/mcpAuditJobs.js +102 -791
  561. package/dist/mcpAuditJobs.js.map +1 -1
  562. package/dist/mcpAutoIndex.d.ts +5 -0
  563. package/dist/mcpAutoIndex.d.ts.map +1 -1
  564. package/dist/mcpAutoIndex.js +7 -2
  565. package/dist/mcpAutoIndex.js.map +1 -1
  566. package/dist/mcpDiagnostics.d.ts +7 -0
  567. package/dist/mcpDiagnostics.d.ts.map +1 -1
  568. package/dist/mcpDiagnostics.js +34 -9
  569. package/dist/mcpDiagnostics.js.map +1 -1
  570. package/dist/mcpToolErrors.d.ts +16 -0
  571. package/dist/mcpToolErrors.d.ts.map +1 -1
  572. package/dist/mcpToolErrors.js +16 -0
  573. package/dist/mcpToolErrors.js.map +1 -1
  574. package/dist/nextFile.d.ts +6 -0
  575. package/dist/nextFile.d.ts.map +1 -1
  576. package/dist/nextFile.js +6 -0
  577. package/dist/nextFile.js.map +1 -1
  578. package/dist/nextFileIncremental.d.ts +44 -4
  579. package/dist/nextFileIncremental.d.ts.map +1 -1
  580. package/dist/nextFileIncremental.js +47 -7
  581. package/dist/nextFileIncremental.js.map +1 -1
  582. package/dist/phase/batchFunctions.d.ts +38 -0
  583. package/dist/phase/batchFunctions.d.ts.map +1 -0
  584. package/dist/phase/batchFunctions.js +64 -0
  585. package/dist/phase/batchFunctions.js.map +1 -0
  586. package/dist/phase/checks.d.ts +39 -0
  587. package/dist/phase/checks.d.ts.map +1 -0
  588. package/dist/phase/checks.js +48 -0
  589. package/dist/phase/checks.js.map +1 -0
  590. package/dist/phase/codeBlocks.d.ts +25 -0
  591. package/dist/phase/codeBlocks.d.ts.map +1 -0
  592. package/dist/phase/codeBlocks.js +268 -0
  593. package/dist/phase/codeBlocks.js.map +1 -0
  594. package/dist/phase/colorMath.d.ts +41 -0
  595. package/dist/phase/colorMath.d.ts.map +1 -0
  596. package/dist/phase/colorMath.js +109 -0
  597. package/dist/phase/colorMath.js.map +1 -0
  598. package/dist/phase/config.d.ts +29 -0
  599. package/dist/phase/config.d.ts.map +1 -0
  600. package/dist/phase/config.js +54 -0
  601. package/dist/phase/config.js.map +1 -0
  602. package/dist/phase/consumed.d.ts +47 -0
  603. package/dist/phase/consumed.d.ts.map +1 -0
  604. package/dist/phase/consumed.js +22 -0
  605. package/dist/phase/consumed.js.map +1 -0
  606. package/dist/phase/conventionMining.d.ts +59 -0
  607. package/dist/phase/conventionMining.d.ts.map +1 -0
  608. package/dist/phase/conventionMining.js +152 -0
  609. package/dist/phase/conventionMining.js.map +1 -0
  610. package/dist/phase/coverage.d.ts +65 -0
  611. package/dist/phase/coverage.d.ts.map +1 -0
  612. package/dist/phase/coverage.js +121 -0
  613. package/dist/phase/coverage.js.map +1 -0
  614. package/dist/phase/dag.d.ts +49 -0
  615. package/dist/phase/dag.d.ts.map +1 -0
  616. package/dist/phase/dag.js +120 -0
  617. package/dist/phase/dag.js.map +1 -0
  618. package/dist/phase/dataAccessCalls.d.ts +33 -0
  619. package/dist/phase/dataAccessCalls.d.ts.map +1 -0
  620. package/dist/phase/dataAccessCalls.js +42 -0
  621. package/dist/phase/dataAccessCalls.js.map +1 -0
  622. package/dist/phase/dataAccessCallsCandidates.d.ts +25 -0
  623. package/dist/phase/dataAccessCallsCandidates.d.ts.map +1 -0
  624. package/dist/phase/dataAccessCallsCandidates.js +27 -0
  625. package/dist/phase/dataAccessCallsCandidates.js.map +1 -0
  626. package/dist/phase/dynamicSql.d.ts +18 -0
  627. package/dist/phase/dynamicSql.d.ts.map +1 -0
  628. package/dist/phase/dynamicSql.js +20 -0
  629. package/dist/phase/dynamicSql.js.map +1 -0
  630. package/dist/phase/exportForm.d.ts +23 -0
  631. package/dist/phase/exportForm.d.ts.map +1 -0
  632. package/dist/phase/exportForm.js +29 -0
  633. package/dist/phase/exportForm.js.map +1 -0
  634. package/dist/phase/exportSymbols.d.ts +21 -0
  635. package/dist/phase/exportSymbols.d.ts.map +1 -0
  636. package/dist/phase/exportSymbols.js +28 -0
  637. package/dist/phase/exportSymbols.js.map +1 -0
  638. package/dist/phase/fileHeader.d.ts +15 -0
  639. package/dist/phase/fileHeader.d.ts.map +1 -0
  640. package/dist/phase/fileHeader.js +33 -0
  641. package/dist/phase/fileHeader.js.map +1 -0
  642. package/dist/phase/fileImports.d.ts +39 -0
  643. package/dist/phase/fileImports.d.ts.map +1 -0
  644. package/dist/phase/fileImports.js +138 -0
  645. package/dist/phase/fileImports.js.map +1 -0
  646. package/dist/phase/fileSymbols.d.ts +25 -0
  647. package/dist/phase/fileSymbols.d.ts.map +1 -0
  648. package/dist/phase/fileSymbols.js +302 -0
  649. package/dist/phase/fileSymbols.js.map +1 -0
  650. package/dist/phase/functionBodies.d.ts +28 -0
  651. package/dist/phase/functionBodies.d.ts.map +1 -0
  652. package/dist/phase/functionBodies.js +67 -0
  653. package/dist/phase/functionBodies.js.map +1 -0
  654. package/dist/phase/functionIndex.d.ts +28 -0
  655. package/dist/phase/functionIndex.d.ts.map +1 -0
  656. package/dist/phase/functionIndex.js +152 -0
  657. package/dist/phase/functionIndex.js.map +1 -0
  658. package/dist/phase/goFunctionAnalysis.d.ts +47 -0
  659. package/dist/phase/goFunctionAnalysis.d.ts.map +1 -0
  660. package/dist/phase/goFunctionAnalysis.js +291 -0
  661. package/dist/phase/goFunctionAnalysis.js.map +1 -0
  662. package/dist/phase/goFunctions.d.ts +38 -0
  663. package/dist/phase/goFunctions.d.ts.map +1 -0
  664. package/dist/phase/goFunctions.js +154 -0
  665. package/dist/phase/goFunctions.js.map +1 -0
  666. package/dist/phase/goImports.d.ts +29 -0
  667. package/dist/phase/goImports.d.ts.map +1 -0
  668. package/dist/phase/goImports.js +68 -0
  669. package/dist/phase/goImports.js.map +1 -0
  670. package/dist/phase/goPackageBindings.d.ts +22 -0
  671. package/dist/phase/goPackageBindings.d.ts.map +1 -0
  672. package/dist/phase/goPackageBindings.js +30 -0
  673. package/dist/phase/goPackageBindings.js.map +1 -0
  674. package/dist/phase/goSwitches.d.ts +28 -0
  675. package/dist/phase/goSwitches.d.ts.map +1 -0
  676. package/dist/phase/goSwitches.js +52 -0
  677. package/dist/phase/goSwitches.js.map +1 -0
  678. package/dist/phase/importForm.d.ts +20 -0
  679. package/dist/phase/importForm.d.ts.map +1 -0
  680. package/dist/phase/importForm.js +27 -0
  681. package/dist/phase/importForm.js.map +1 -0
  682. package/dist/phase/importSpecifiers.d.ts +20 -0
  683. package/dist/phase/importSpecifiers.d.ts.map +1 -0
  684. package/dist/phase/importSpecifiers.js +30 -0
  685. package/dist/phase/importSpecifiers.js.map +1 -0
  686. package/dist/phase/imports.d.ts +18 -0
  687. package/dist/phase/imports.d.ts.map +1 -0
  688. package/dist/phase/imports.js +25 -0
  689. package/dist/phase/imports.js.map +1 -0
  690. package/dist/phase/jsonDocument.d.ts +26 -0
  691. package/dist/phase/jsonDocument.d.ts.map +1 -0
  692. package/dist/phase/jsonDocument.js +36 -0
  693. package/dist/phase/jsonDocument.js.map +1 -0
  694. package/dist/phase/localBinding.d.ts +33 -0
  695. package/dist/phase/localBinding.d.ts.map +1 -0
  696. package/dist/phase/localBinding.js +208 -0
  697. package/dist/phase/localBinding.js.map +1 -0
  698. package/dist/phase/loopQueries.d.ts +31 -0
  699. package/dist/phase/loopQueries.d.ts.map +1 -0
  700. package/dist/phase/loopQueries.js +44 -0
  701. package/dist/phase/loopQueries.js.map +1 -0
  702. package/dist/phase/loopQueryCandidates.d.ts +26 -0
  703. package/dist/phase/loopQueryCandidates.d.ts.map +1 -0
  704. package/dist/phase/loopQueryCandidates.js +28 -0
  705. package/dist/phase/loopQueryCandidates.js.map +1 -0
  706. package/dist/phase/oracles.d.ts +216 -0
  707. package/dist/phase/oracles.d.ts.map +1 -0
  708. package/dist/phase/oracles.js +484 -0
  709. package/dist/phase/oracles.js.map +1 -0
  710. package/dist/phase/phaseModel.d.ts +190 -0
  711. package/dist/phase/phaseModel.d.ts.map +1 -0
  712. package/dist/phase/phaseModel.js +468 -0
  713. package/dist/phase/phaseModel.js.map +1 -0
  714. package/dist/phase/producers.d.ts +450 -0
  715. package/dist/phase/producers.d.ts.map +1 -0
  716. package/dist/phase/producers.js +1027 -0
  717. package/dist/phase/producers.js.map +1 -0
  718. package/dist/phase/querySiteCandidates.d.ts +27 -0
  719. package/dist/phase/querySiteCandidates.d.ts.map +1 -0
  720. package/dist/phase/querySiteCandidates.js +66 -0
  721. package/dist/phase/querySiteCandidates.js.map +1 -0
  722. package/dist/phase/querySites.d.ts +30 -0
  723. package/dist/phase/querySites.d.ts.map +1 -0
  724. package/dist/phase/querySites.js +93 -0
  725. package/dist/phase/querySites.js.map +1 -0
  726. package/dist/phase/reachability.d.ts +61 -0
  727. package/dist/phase/reachability.d.ts.map +1 -0
  728. package/dist/phase/reachability.js +104 -0
  729. package/dist/phase/reachability.js.map +1 -0
  730. package/dist/phase/reactComponents.d.ts +33 -0
  731. package/dist/phase/reactComponents.d.ts.map +1 -0
  732. package/dist/phase/reactComponents.js +47 -0
  733. package/dist/phase/reactComponents.js.map +1 -0
  734. package/dist/phase/receiverActivity.d.ts +35 -0
  735. package/dist/phase/receiverActivity.d.ts.map +1 -0
  736. package/dist/phase/receiverActivity.js +66 -0
  737. package/dist/phase/receiverActivity.js.map +1 -0
  738. package/dist/phase/receiverConsumers.d.ts +200 -0
  739. package/dist/phase/receiverConsumers.d.ts.map +1 -0
  740. package/dist/phase/receiverConsumers.js +1025 -0
  741. package/dist/phase/receiverConsumers.js.map +1 -0
  742. package/dist/phase/receiverProvenance.d.ts +97 -0
  743. package/dist/phase/receiverProvenance.d.ts.map +1 -0
  744. package/dist/phase/receiverProvenance.js +337 -0
  745. package/dist/phase/receiverProvenance.js.map +1 -0
  746. package/dist/phase/routing.d.ts +47 -0
  747. package/dist/phase/routing.d.ts.map +1 -0
  748. package/dist/phase/routing.js +62 -0
  749. package/dist/phase/routing.js.map +1 -0
  750. package/dist/phase/rules/conventions.d.ts +61 -0
  751. package/dist/phase/rules/conventions.d.ts.map +1 -0
  752. package/dist/phase/rules/conventions.js +403 -0
  753. package/dist/phase/rules/conventions.js.map +1 -0
  754. package/dist/phase/rules/crossDomain.d.ts +102 -0
  755. package/dist/phase/rules/crossDomain.d.ts.map +1 -0
  756. package/dist/phase/rules/crossDomain.js +918 -0
  757. package/dist/phase/rules/crossDomain.js.map +1 -0
  758. package/dist/phase/rules/dataAccess.d.ts +62 -0
  759. package/dist/phase/rules/dataAccess.d.ts.map +1 -0
  760. package/dist/phase/rules/dataAccess.js +480 -0
  761. package/dist/phase/rules/dataAccess.js.map +1 -0
  762. package/dist/phase/rules/dependencyGraph.d.ts +44 -0
  763. package/dist/phase/rules/dependencyGraph.d.ts.map +1 -0
  764. package/dist/phase/rules/dependencyGraph.js +260 -0
  765. package/dist/phase/rules/dependencyGraph.js.map +1 -0
  766. package/dist/phase/rules/documentation.d.ts +45 -0
  767. package/dist/phase/rules/documentation.d.ts.map +1 -0
  768. package/dist/phase/rules/documentation.js +340 -0
  769. package/dist/phase/rules/documentation.js.map +1 -0
  770. package/dist/phase/rules/dry.d.ts +109 -0
  771. package/dist/phase/rules/dry.d.ts.map +1 -0
  772. package/dist/phase/rules/dry.js +803 -0
  773. package/dist/phase/rules/dry.js.map +1 -0
  774. package/dist/phase/rules/fileDocumentation.d.ts +36 -0
  775. package/dist/phase/rules/fileDocumentation.d.ts.map +1 -0
  776. package/dist/phase/rules/fileDocumentation.js +134 -0
  777. package/dist/phase/rules/fileDocumentation.js.map +1 -0
  778. package/dist/phase/rules/functionBodies.d.ts +33 -0
  779. package/dist/phase/rules/functionBodies.d.ts.map +1 -0
  780. package/dist/phase/rules/functionBodies.js +71 -0
  781. package/dist/phase/rules/functionBodies.js.map +1 -0
  782. package/dist/phase/rules/goRules.d.ts +49 -0
  783. package/dist/phase/rules/goRules.d.ts.map +1 -0
  784. package/dist/phase/rules/goRules.js +209 -0
  785. package/dist/phase/rules/goRules.js.map +1 -0
  786. package/dist/phase/rules/querySites.d.ts +29 -0
  787. package/dist/phase/rules/querySites.d.ts.map +1 -0
  788. package/dist/phase/rules/querySites.js +94 -0
  789. package/dist/phase/rules/querySites.js.map +1 -0
  790. package/dist/phase/rules/react.d.ts +38 -0
  791. package/dist/phase/rules/react.d.ts.map +1 -0
  792. package/dist/phase/rules/react.js +202 -0
  793. package/dist/phase/rules/react.js.map +1 -0
  794. package/dist/phase/rules/registry.d.ts +170 -0
  795. package/dist/phase/rules/registry.d.ts.map +1 -0
  796. package/dist/phase/rules/registry.js +103 -0
  797. package/dist/phase/rules/registry.js.map +1 -0
  798. package/dist/phase/rules/schema.d.ts +79 -0
  799. package/dist/phase/rules/schema.d.ts.map +1 -0
  800. package/dist/phase/rules/schema.js +273 -0
  801. package/dist/phase/rules/schema.js.map +1 -0
  802. package/dist/phase/rules/schemaJson.d.ts +32 -0
  803. package/dist/phase/rules/schemaJson.d.ts.map +1 -0
  804. package/dist/phase/rules/schemaJson.js +81 -0
  805. package/dist/phase/rules/schemaJson.js.map +1 -0
  806. package/dist/phase/rules/schemaValidator.d.ts +38 -0
  807. package/dist/phase/rules/schemaValidator.d.ts.map +1 -0
  808. package/dist/phase/rules/schemaValidator.js +107 -0
  809. package/dist/phase/rules/schemaValidator.js.map +1 -0
  810. package/dist/phase/rules/secrets.d.ts +34 -0
  811. package/dist/phase/rules/secrets.d.ts.map +1 -0
  812. package/dist/phase/rules/secrets.js +208 -0
  813. package/dist/phase/rules/secrets.js.map +1 -0
  814. package/dist/phase/rules/security.d.ts +30 -0
  815. package/dist/phase/rules/security.d.ts.map +1 -0
  816. package/dist/phase/rules/security.js +85 -0
  817. package/dist/phase/rules/security.js.map +1 -0
  818. package/dist/phase/rules/securityDefects.d.ts +37 -0
  819. package/dist/phase/rules/securityDefects.d.ts.map +1 -0
  820. package/dist/phase/rules/securityDefects.js +167 -0
  821. package/dist/phase/rules/securityDefects.js.map +1 -0
  822. package/dist/phase/rules/solid.d.ts +61 -0
  823. package/dist/phase/rules/solid.d.ts.map +1 -0
  824. package/dist/phase/rules/solid.js +562 -0
  825. package/dist/phase/rules/solid.js.map +1 -0
  826. package/dist/phase/rules/styles.d.ts +77 -0
  827. package/dist/phase/rules/styles.d.ts.map +1 -0
  828. package/dist/phase/rules/styles.js +868 -0
  829. package/dist/phase/rules/styles.js.map +1 -0
  830. package/dist/phase/rules/unreferencedModule.d.ts +26 -0
  831. package/dist/phase/rules/unreferencedModule.d.ts.map +1 -0
  832. package/dist/phase/rules/unreferencedModule.js +61 -0
  833. package/dist/phase/rules/unreferencedModule.js.map +1 -0
  834. package/dist/phase/runner.d.ts +549 -0
  835. package/dist/phase/runner.d.ts.map +1 -0
  836. package/dist/phase/runner.js +1098 -0
  837. package/dist/phase/runner.js.map +1 -0
  838. package/dist/phase/schemaCode.d.ts +34 -0
  839. package/dist/phase/schemaCode.d.ts.map +1 -0
  840. package/dist/phase/schemaCode.js +53 -0
  841. package/dist/phase/schemaCode.js.map +1 -0
  842. package/dist/phase/schemaObjects.d.ts +37 -0
  843. package/dist/phase/schemaObjects.d.ts.map +1 -0
  844. package/dist/phase/schemaObjects.js +139 -0
  845. package/dist/phase/schemaObjects.js.map +1 -0
  846. package/dist/phase/schemaUsage.d.ts +30 -0
  847. package/dist/phase/schemaUsage.d.ts.map +1 -0
  848. package/dist/phase/schemaUsage.js +99 -0
  849. package/dist/phase/schemaUsage.js.map +1 -0
  850. package/dist/phase/schemaUsageCandidates.d.ts +32 -0
  851. package/dist/phase/schemaUsageCandidates.d.ts.map +1 -0
  852. package/dist/phase/schemaUsageCandidates.js +165 -0
  853. package/dist/phase/schemaUsageCandidates.js.map +1 -0
  854. package/dist/phase/schemaValidations.d.ts +34 -0
  855. package/dist/phase/schemaValidations.d.ts.map +1 -0
  856. package/dist/phase/schemaValidations.js +53 -0
  857. package/dist/phase/schemaValidations.js.map +1 -0
  858. package/dist/phase/secretCandidates.d.ts +28 -0
  859. package/dist/phase/secretCandidates.d.ts.map +1 -0
  860. package/dist/phase/secretCandidates.js +132 -0
  861. package/dist/phase/secretCandidates.js.map +1 -0
  862. package/dist/phase/securityCandidates.d.ts +29 -0
  863. package/dist/phase/securityCandidates.d.ts.map +1 -0
  864. package/dist/phase/securityCandidates.js +469 -0
  865. package/dist/phase/securityCandidates.js.map +1 -0
  866. package/dist/phase/seeded-defects.d.ts +18 -0
  867. package/dist/phase/seeded-defects.d.ts.map +1 -0
  868. package/dist/phase/seeded-defects.js +61 -0
  869. package/dist/phase/seeded-defects.js.map +1 -0
  870. package/dist/phase/stringLiterals.d.ts +24 -0
  871. package/dist/phase/stringLiterals.d.ts.map +1 -0
  872. package/dist/phase/stringLiterals.js +35 -0
  873. package/dist/phase/stringLiterals.js.map +1 -0
  874. package/dist/phase/stylesCss.d.ts +25 -0
  875. package/dist/phase/stylesCss.d.ts.map +1 -0
  876. package/dist/phase/stylesCss.js +34 -0
  877. package/dist/phase/stylesCss.js.map +1 -0
  878. package/dist/phase/stylesMarkup.d.ts +31 -0
  879. package/dist/phase/stylesMarkup.d.ts.map +1 -0
  880. package/dist/phase/stylesMarkup.js +48 -0
  881. package/dist/phase/stylesMarkup.js.map +1 -0
  882. package/dist/phase/stylesSource.d.ts +36 -0
  883. package/dist/phase/stylesSource.d.ts.map +1 -0
  884. package/dist/phase/stylesSource.js +50 -0
  885. package/dist/phase/stylesSource.js.map +1 -0
  886. package/dist/phase/typeDeclarations.d.ts +33 -0
  887. package/dist/phase/typeDeclarations.d.ts.map +1 -0
  888. package/dist/phase/typeDeclarations.js +76 -0
  889. package/dist/phase/typeDeclarations.js.map +1 -0
  890. package/dist/phase/types.d.ts +2191 -0
  891. package/dist/phase/types.d.ts.map +1 -0
  892. package/dist/phase/types.js +27 -0
  893. package/dist/phase/types.js.map +1 -0
  894. package/dist/phase/withinFileProvenance.d.ts +50 -0
  895. package/dist/phase/withinFileProvenance.d.ts.map +1 -0
  896. package/dist/phase/withinFileProvenance.js +112 -0
  897. package/dist/phase/withinFileProvenance.js.map +1 -0
  898. package/dist/pipeline.d.ts +37 -30
  899. package/dist/pipeline.d.ts.map +1 -1
  900. package/dist/pipeline.js +131 -326
  901. package/dist/pipeline.js.map +1 -1
  902. package/dist/pipelineAdapters.d.ts +104 -21
  903. package/dist/pipelineAdapters.d.ts.map +1 -1
  904. package/dist/pipelineAdapters.js +1145 -988
  905. package/dist/pipelineAdapters.js.map +1 -1
  906. package/dist/pipelineTypes.d.ts +21 -0
  907. package/dist/pipelineTypes.d.ts.map +1 -1
  908. package/dist/pipelineTypes.js +21 -10
  909. package/dist/pipelineTypes.js.map +1 -1
  910. package/dist/presets/presets.d.ts +9 -2
  911. package/dist/presets/presets.d.ts.map +1 -1
  912. package/dist/presets/presets.js +15 -26
  913. package/dist/presets/presets.js.map +1 -1
  914. package/dist/reporting/csvReportGenerator.d.ts +14 -3
  915. package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
  916. package/dist/reporting/csvReportGenerator.js +14 -3
  917. package/dist/reporting/csvReportGenerator.js.map +1 -1
  918. package/dist/reporting/htmlReportGenerator.d.ts +5 -1
  919. package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
  920. package/dist/reporting/htmlReportGenerator.js +5 -1
  921. package/dist/reporting/htmlReportGenerator.js.map +1 -1
  922. package/dist/reporting/jsonReportGenerator.d.ts +9 -2
  923. package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
  924. package/dist/reporting/jsonReportGenerator.js +19 -5
  925. package/dist/reporting/jsonReportGenerator.js.map +1 -1
  926. package/dist/reporting/reportGenerator.d.ts +9 -2
  927. package/dist/reporting/reportGenerator.d.ts.map +1 -1
  928. package/dist/reporting/reportGenerator.js +9 -2
  929. package/dist/reporting/reportGenerator.js.map +1 -1
  930. package/dist/reporting/sarifReportGenerator.d.ts +14 -0
  931. package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
  932. package/dist/reporting/sarifReportGenerator.js +14 -0
  933. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  934. package/dist/reporting/sizeDistribution.d.ts +19 -2
  935. package/dist/reporting/sizeDistribution.d.ts.map +1 -1
  936. package/dist/reporting/sizeDistribution.js +23 -5
  937. package/dist/reporting/sizeDistribution.js.map +1 -1
  938. package/dist/ruleAliases.d.ts +6 -8
  939. package/dist/ruleAliases.d.ts.map +1 -1
  940. package/dist/ruleAliases.js +29 -4
  941. package/dist/ruleAliases.js.map +1 -1
  942. package/dist/search/QueryParser.d.ts +1 -0
  943. package/dist/search/QueryParser.d.ts.map +1 -1
  944. package/dist/search/QueryParser.js +32 -91
  945. package/dist/search/QueryParser.js.map +1 -1
  946. package/dist/services/CodeMapGenerator.d.ts +19 -20
  947. package/dist/services/CodeMapGenerator.d.ts.map +1 -1
  948. package/dist/services/CodeMapGenerator.js +214 -194
  949. package/dist/services/CodeMapGenerator.js.map +1 -1
  950. package/dist/services/ProjectTaskRepository.d.ts +65 -3
  951. package/dist/services/ProjectTaskRepository.d.ts.map +1 -1
  952. package/dist/services/ProjectTaskRepository.js +94 -25
  953. package/dist/services/ProjectTaskRepository.js.map +1 -1
  954. package/dist/services/auditJobService.d.ts +17 -1
  955. package/dist/services/auditJobService.d.ts.map +1 -1
  956. package/dist/services/auditJobService.js +16 -0
  957. package/dist/services/auditJobService.js.map +1 -1
  958. package/dist/services/fileAccounting.d.ts +43 -5
  959. package/dist/services/fileAccounting.d.ts.map +1 -1
  960. package/dist/services/fileAccounting.js +48 -9
  961. package/dist/services/fileAccounting.js.map +1 -1
  962. package/dist/services/projectTaskHelpers.d.ts +45 -0
  963. package/dist/services/projectTaskHelpers.d.ts.map +1 -1
  964. package/dist/services/projectTaskHelpers.js +45 -0
  965. package/dist/services/projectTaskHelpers.js.map +1 -1
  966. package/dist/services/whitelistService.d.ts +40 -5
  967. package/dist/services/whitelistService.d.ts.map +1 -1
  968. package/dist/services/whitelistService.js +87 -46
  969. package/dist/services/whitelistService.js.map +1 -1
  970. package/dist/sqlite/betterSqlite3.d.ts +35 -1
  971. package/dist/sqlite/betterSqlite3.d.ts.map +1 -1
  972. package/dist/sqlite/betterSqlite3.js +35 -1
  973. package/dist/sqlite/betterSqlite3.js.map +1 -1
  974. package/dist/sqlite/driver.d.ts +23 -0
  975. package/dist/sqlite/driver.d.ts.map +1 -1
  976. package/dist/sqlite/driver.js +23 -0
  977. package/dist/sqlite/driver.js.map +1 -1
  978. package/dist/sqlite/nodeSqlite.d.ts +35 -1
  979. package/dist/sqlite/nodeSqlite.d.ts.map +1 -1
  980. package/dist/sqlite/nodeSqlite.js +35 -1
  981. package/dist/sqlite/nodeSqlite.js.map +1 -1
  982. package/dist/structuralSignature.d.ts +9 -0
  983. package/dist/structuralSignature.d.ts.map +1 -1
  984. package/dist/structuralSignature.js +9 -0
  985. package/dist/structuralSignature.js.map +1 -1
  986. package/dist/styles/cssAstExtractor.d.ts +15 -0
  987. package/dist/styles/cssAstExtractor.d.ts.map +1 -1
  988. package/dist/styles/cssAstExtractor.js +15 -0
  989. package/dist/styles/cssAstExtractor.js.map +1 -1
  990. package/dist/styles/normalizer.d.ts +6 -0
  991. package/dist/styles/normalizer.d.ts.map +1 -1
  992. package/dist/styles/normalizer.js +6 -0
  993. package/dist/styles/normalizer.js.map +1 -1
  994. package/dist/styles/styleExtractor.d.ts +29 -2
  995. package/dist/styles/styleExtractor.d.ts.map +1 -1
  996. package/dist/styles/styleExtractor.js +67 -48
  997. package/dist/styles/styleExtractor.js.map +1 -1
  998. package/dist/styles/styleIndexer.d.ts +8 -0
  999. package/dist/styles/styleIndexer.d.ts.map +1 -1
  1000. package/dist/styles/styleIndexer.js +135 -138
  1001. package/dist/styles/styleIndexer.js.map +1 -1
  1002. package/dist/styles/tailwindConfigLoader.d.ts +12 -0
  1003. package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
  1004. package/dist/styles/tailwindConfigLoader.js +12 -0
  1005. package/dist/styles/tailwindConfigLoader.js.map +1 -1
  1006. package/dist/styles/tailwindExpander.d.ts +3 -0
  1007. package/dist/styles/tailwindExpander.d.ts.map +1 -1
  1008. package/dist/styles/tailwindExpander.js +8 -4
  1009. package/dist/styles/tailwindExpander.js.map +1 -1
  1010. package/dist/styles/tailwindProbe.d.ts +9 -0
  1011. package/dist/styles/tailwindProbe.d.ts.map +1 -1
  1012. package/dist/styles/tailwindProbe.js +181 -166
  1013. package/dist/styles/tailwindProbe.js.map +1 -1
  1014. package/dist/styles/tailwindUtilityExpander.d.ts +13 -0
  1015. package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
  1016. package/dist/styles/tailwindUtilityExpander.js +13 -0
  1017. package/dist/styles/tailwindUtilityExpander.js.map +1 -1
  1018. package/dist/symbols.d.ts +13 -11
  1019. package/dist/symbols.d.ts.map +1 -1
  1020. package/dist/symbols.js +14 -21
  1021. package/dist/symbols.js.map +1 -1
  1022. package/dist/telemetry.d.ts +31 -3
  1023. package/dist/telemetry.d.ts.map +1 -1
  1024. package/dist/telemetry.js +31 -3
  1025. package/dist/telemetry.js.map +1 -1
  1026. package/dist/tool-registry.d.ts +37 -1
  1027. package/dist/tool-registry.d.ts.map +1 -1
  1028. package/dist/tool-registry.js +37 -1
  1029. package/dist/tool-registry.js.map +1 -1
  1030. package/dist/types.d.ts +106 -92
  1031. package/dist/types.d.ts.map +1 -1
  1032. package/dist/types.js +7 -16
  1033. package/dist/types.js.map +1 -1
  1034. package/dist/ui/InteractivePrompts.d.ts +12 -0
  1035. package/dist/ui/InteractivePrompts.d.ts.map +1 -1
  1036. package/dist/ui/InteractivePrompts.js +12 -0
  1037. package/dist/ui/InteractivePrompts.js.map +1 -1
  1038. package/dist/utils/astParser.d.ts +15 -0
  1039. package/dist/utils/astParser.d.ts.map +1 -1
  1040. package/dist/utils/astParser.js +15 -0
  1041. package/dist/utils/astParser.js.map +1 -1
  1042. package/dist/utils/astUtils.d.ts +60 -0
  1043. package/dist/utils/astUtils.d.ts.map +1 -1
  1044. package/dist/utils/astUtils.js +350 -362
  1045. package/dist/utils/astUtils.js.map +1 -1
  1046. package/dist/utils/contentHash.d.ts +22 -0
  1047. package/dist/utils/contentHash.d.ts.map +1 -0
  1048. package/dist/utils/contentHash.js +26 -0
  1049. package/dist/utils/contentHash.js.map +1 -0
  1050. package/dist/utils/dependencyExtractor.d.ts +28 -0
  1051. package/dist/utils/dependencyExtractor.d.ts.map +1 -1
  1052. package/dist/utils/dependencyExtractor.js +28 -1
  1053. package/dist/utils/dependencyExtractor.js.map +1 -1
  1054. package/dist/utils/errorMessage.d.ts +10 -0
  1055. package/dist/utils/errorMessage.d.ts.map +1 -0
  1056. package/dist/utils/errorMessage.js +12 -0
  1057. package/dist/utils/errorMessage.js.map +1 -0
  1058. package/dist/utils/fileDiscovery.d.ts +61 -2
  1059. package/dist/utils/fileDiscovery.d.ts.map +1 -1
  1060. package/dist/utils/fileDiscovery.js +131 -23
  1061. package/dist/utils/fileDiscovery.js.map +1 -1
  1062. package/dist/utils/reactDetection.d.ts +34 -5
  1063. package/dist/utils/reactDetection.d.ts.map +1 -1
  1064. package/dist/utils/reactDetection.js +73 -52
  1065. package/dist/utils/reactDetection.js.map +1 -1
  1066. package/dist/version.generated.d.ts +1 -1
  1067. package/dist/version.generated.js +1 -1
  1068. package/dist/workers/auditWorker.js +7 -3
  1069. package/dist/workers/auditWorker.js.map +1 -1
  1070. package/examples/.auditrc.example.json +0 -8
  1071. package/examples/.auditrc.json +0 -11
  1072. package/examples/.code-auditor.json +0 -2
  1073. package/examples/nextjs.auditrc.json +0 -8
  1074. package/examples/node-api.auditrc.json +0 -7
  1075. package/examples/react.auditrc.json +0 -6
  1076. package/examples/tolerant-solid.auditrc.json +0 -5
  1077. package/package.json +14 -8
  1078. package/plugin/.claude-plugin/manifest.json +16 -0
  1079. package/plugin/.claude-plugin/plugin.json +1 -1
  1080. package/plugin/hooks/hooks.json +6 -2
  1081. package/plugin/scripts/hook-audit.sh +41 -14
  1082. package/plugin/scripts/hook-common.sh +50 -5
  1083. package/plugin/scripts/hook-drift-check.sh +72 -0
  1084. package/plugin/scripts/hook-self-audit.sh +17 -12
  1085. package/plugin/skills/code-auditor/SKILL.md +1 -1
  1086. package/server.json +2 -2
package/dist/cli.js CHANGED
@@ -7,7 +7,6 @@ import './native-bootstrap.js';
7
7
  import { Command } from 'commander';
8
8
  import chalk from 'chalk';
9
9
  import { createAuditRunner } from './auditRunner.js';
10
- import { runAuditDispatch } from './auditRouter.js';
11
10
  import { promises as fs } from 'fs';
12
11
  import { createInterface } from 'readline';
13
12
  import { dirname, isAbsolute, join, relative, resolve } from 'path';
@@ -23,7 +22,6 @@ import { fingerprint, buildFingerprintInput } from './fingerprint.js';
23
22
  import { buildDismissalEntry, upsertDismissal } from './dismissals.js';
24
23
  import { resolveTelemetryConfig, signatureForFinding, buildTelemetryPayload, formatTelemetryPreview, sendTelemetry, languageHint } from './telemetry.js';
25
24
  import { getInstallId } from './installConfig.js';
26
- import { ALL_ANALYZERS } from './analyzers/ruleRegistry.js';
27
25
  import { computeGatingDecision } from './enforcement/gate.js';
28
26
  import { BLOCKING_SEVERITIES, SEVERITIES } from './types.js';
29
27
  import { rankFilesByPriority, orderFindingsWithinFile } from './nextFile.js';
@@ -36,6 +34,18 @@ const program = new Command();
36
34
  // `--fail-on advisory` must fail loudly and name the new tier, not emit a generic
37
35
  // "must be one of" list that leaves the user guessing what `advisory` became.
38
36
  const LEGACY_SEVERITY_NAMES = { advisory: 'high' };
37
+ // CLI option descriptions, hoisted so the 30+ subcommands' repeated
38
+ // option() / -p,--path descriptions share one source.
39
+ const OPTION_OUTPUT_AS_JSON = 'Output as JSON';
40
+ const OPTION_PROJECT_PATH = 'Project path';
41
+ const OPTION_PROJECT_ROOT_PATH = 'Project root path';
42
+ const OPTION_PROJECT_ROOT = 'Project root';
43
+ const OPTION_CONFIGURATION_NAME = 'Configuration name';
44
+ const UNKNOWN_ERROR = 'Unknown error';
45
+ // "file:line" suffix for a finding row; empty when the finding has no line.
46
+ function lineSuffix(line) {
47
+ return line ? `:${line}` : '';
48
+ }
39
49
  // Spec 44 R4 — file accounting summary line + `--explain-skipped` breakdown.
40
50
  // The full per-reason file lists live in the JSON report (`metadata.fileAccounting`);
41
51
  // the CLI preview is bounded to the first ~20 files per reason.
@@ -88,23 +98,456 @@ program
88
98
  .name('code-auditor')
89
99
  .description('TypeScript/JavaScript code quality auditor with AI tool integration')
90
100
  .version(`${PACKAGE_VERSION} (sqlite: ${describeSqliteBackend()})`);
101
+ /**
102
+ * Collapse a receiver-resolution reason to a single line for the cannot-fire
103
+ * panel. Reasons embed the receiver text (which can span the SQL of a chained
104
+ * `db.prepare("...").bind(...)`), so newlines must be folded and the result
105
+ * truncated to stay legible in the per-file rollup.
106
+ */
107
+ function sanitizeReason(reason) {
108
+ if (!reason)
109
+ return '';
110
+ const collapsed = reason.replace(/\s+/g, ' ').trim();
111
+ return collapsed.length > 220 ? `${collapsed.slice(0, 217)}…` : collapsed;
112
+ }
113
+ /**
114
+ * Print the pre-delta diagnostic panels: coverage, skipped analyzers, ignored
115
+ * config keys, coverage gaps, test coverage, size distributions, and dead
116
+ * clusters. Pure reporting over `result.metadata`.
117
+ */
118
+ function printAuditDiagnostics(result) {
119
+ // ── Coverage panel leads the report (Spec 47 R2) ─────────────
120
+ const coverage = result.metadata?.coverage;
121
+ // Spec 69 §10 R4 — the site-level cannot-fire count (unproven DB-access
122
+ // receiver sites) sits beside the rule-level states, so one line reports
123
+ // both the per-rule verdict and the per-call-site gap the affected rules
124
+ // may have under-reported on.
125
+ const unprovenSiteCount = (result.metadata?.diagnostics ?? [])
126
+ .filter((d) => d.kind === 'cannot-fire' && d.analyzerName === 'schema')
127
+ .length;
128
+ if (coverage && coverage.length > 0) {
129
+ const covFired = coverage.filter(c => c.state === 'fired').length;
130
+ const covClean = coverage.filter(c => c.state === 'clean').length;
131
+ const covIncomplete = coverage.filter(c => c.state === 'incomplete').length;
132
+ const covNotApplicable = coverage.filter(c => c.state === 'notApplicable').length;
133
+ const covCannotFire = coverage.filter(c => c.state === 'cannot-fire').length;
134
+ console.log(chalk.gray(`── Coverage panel ── ${covFired} fired · ${covClean} clean · ` +
135
+ `${covIncomplete} incomplete · ${covNotApplicable} not-applicable · ` +
136
+ `${covCannotFire} cannot-fire · ${unprovenSiteCount} unproven sites`));
137
+ }
138
+ // ── Skipped analyzers (Spec 61) ──────────────────────────────
139
+ const skippedAnalyzers = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'go-toolchain-missing' ||
140
+ d.kind === 'go-analyzer-wrong-arch' ||
141
+ d.kind === 'go-analyzer-build-failed');
142
+ if (skippedAnalyzers.length > 0) {
143
+ console.log(chalk.yellow(`── Skipped analyzers ── ${skippedAnalyzers.length}`));
144
+ for (const d of skippedAnalyzers) {
145
+ const name = d.analyzerName || 'go';
146
+ console.log(chalk.yellow(` ${name} [${d.kind}] — ${d.message}`));
147
+ }
148
+ }
149
+ // ── Ignored config keys (Spec 61 R1.4) ─────────────────────────
150
+ const ignoredConfigKeys = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'config-key-rejected');
151
+ if (ignoredConfigKeys.length > 0) {
152
+ console.log(chalk.yellow(`── Ignored config keys ── ${ignoredConfigKeys.length}`));
153
+ for (const d of ignoredConfigKeys) {
154
+ console.log(chalk.yellow(` ${d.message}`));
155
+ }
156
+ }
157
+ // ── Cannot-fire sites (Spec 69 §10) ───────────────────────────
158
+ // The cannot-fire verdict surface is the release bar: every DB-access call
159
+ // site whose receiver could not be proven a handle must be *visible with its
160
+ // reason*, never capped or folded into a silent clean. Grouped by file so a
161
+ // reader can see exactly which files the table-reference and data-access
162
+ // rules may have under-reported on.
163
+ const allDiagnostics = result.metadata?.diagnostics ?? [];
164
+ const cannotFireSites = allDiagnostics.filter((d) => d.kind === 'cannot-fire');
165
+ if (cannotFireSites.length > 0) {
166
+ const byFile = new Map();
167
+ for (const d of cannotFireSites) {
168
+ const key = d.file ?? '(unknown)';
169
+ const entry = byFile.get(key) ?? { lines: [], reasons: new Set() };
170
+ if (typeof d.line === 'number')
171
+ entry.lines.push(d.line);
172
+ const rawReason = typeof d.details?.reason === 'string' ? d.details.reason : d.message;
173
+ const reason = sanitizeReason(rawReason);
174
+ if (reason)
175
+ entry.reasons.add(reason);
176
+ byFile.set(key, entry);
177
+ }
178
+ console.log(chalk.yellow(`── Cannot-fire sites ── ${cannotFireSites.length} unproven DB-access call sites across ${byFile.size} files`));
179
+ console.log(chalk.yellow(` Table-reference and data-access rules may report clean on access they could not observe.`));
180
+ for (const [file, entry] of [...byFile.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
181
+ const reasons = [...entry.reasons];
182
+ const shown = reasons.slice(0, 2).join(' │ ');
183
+ const more = reasons.length > 2 ? ` (+${reasons.length - 2} more reasons)` : '';
184
+ const loc = entry.lines.length === 1 ? ` (line ${entry.lines[0]})` : '';
185
+ console.log(chalk.yellow(` ${file} — ${entry.lines.length} site(s)${loc}: ${shown}${more}`));
186
+ }
187
+ }
188
+ // ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
189
+ const otherCoverageGaps = allDiagnostics.filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found');
190
+ if (otherCoverageGaps.length > 0) {
191
+ const byKind = {};
192
+ for (const d of otherCoverageGaps)
193
+ byKind[d.kind] = (byKind[d.kind] ?? 0) + 1;
194
+ const totals = Object.entries(byKind)
195
+ .map(([kind, n]) => `${n} ${kind}`)
196
+ .join(' · ');
197
+ console.log(chalk.gray(`── Coverage gaps ── ${totals}`));
198
+ for (const d of otherCoverageGaps.slice(0, 20)) {
199
+ const loc = d.file ? `${d.file}${typeof d.line === 'number' ? `:${d.line}` : ''}` : '(unknown)';
200
+ console.log(chalk.gray(` ${loc} [${d.kind}] — ${d.message}`));
201
+ }
202
+ if (otherCoverageGaps.length > 20) {
203
+ console.log(chalk.gray(` … and ${otherCoverageGaps.length - 20} more`));
204
+ }
205
+ }
206
+ // ── Spec 60 R1 — test coverage (reporting, not detection) ─────
207
+ const testCoverage = result.metadata?.testCoverage;
208
+ if (testCoverage) {
209
+ const pct = testCoverage.total > 0
210
+ ? ((testCoverage.tested / testCoverage.total) * 100).toFixed(1)
211
+ : '0.0';
212
+ let line = `tested ${testCoverage.tested}/${testCoverage.total} (${pct}%)` +
213
+ ` · untested-live ${testCoverage.untestedLive}` +
214
+ ` · untested-dead ${testCoverage.untestedDead}`;
215
+ if (testCoverage.deadDrop > 0) {
216
+ line += ` (was ${testCoverage.deadPreException} pre-exception; −${testCoverage.deadDrop} entry points exempted)`;
217
+ }
218
+ console.log(chalk.gray(`── Test coverage ── ${line}`));
219
+ }
220
+ // ── Spec 60 R2 — size distributions (median / p95 / max) ──────
221
+ const sizeDistributions = result.metadata?.sizeDistributions;
222
+ if (sizeDistributions && sizeDistributions.length > 0) {
223
+ console.log(chalk.gray(`── Size distributions ──`));
224
+ for (const d of sizeDistributions) {
225
+ const fmt = (n) => Number.isInteger(n) ? String(n) : n.toFixed(1);
226
+ const tail = d.maxEntry
227
+ ? ` (${d.maxEntry.name}${d.maxEntry.fileType ? ` · ${d.maxEntry.fileType}` : ''} · ${d.maxEntry.entityType})`
228
+ : '';
229
+ const pop = d.population ? `${d.population}, n=${d.count}` : `n=${d.count}`;
230
+ console.log(chalk.gray(` ${d.measure} [${pop}]: median ${fmt(d.median)} · p95 ${fmt(d.p95)} · max ${fmt(d.max)}${tail}`));
231
+ }
232
+ }
233
+ // ── Spec 60 R3 — dead-and-duplicated clusters ─────────────────
234
+ const deadClusters = result.metadata?.deadClusters;
235
+ if (deadClusters && deadClusters.length > 0) {
236
+ console.log(chalk.gray(`── Dead-and-duplicated ── ${deadClusters.length} clusters`));
237
+ for (const c of deadClusters.slice(0, 10)) {
238
+ console.log(chalk.gray(` ${c.count} × ${c.basename}`));
239
+ }
240
+ if (deadClusters.length > 10) {
241
+ console.log(chalk.gray(` … and ${deadClusters.length - 10} more clusters`));
242
+ }
243
+ }
244
+ }
245
+ /**
246
+ * Print the findings summary: the delta view when a baseline exists, or the
247
+ * absolute summary otherwise. Recomputes the dismissed/gating suffixes so the
248
+ * summary is self-contained.
249
+ */
250
+ function printFindingSummary(result, violations, baseline, options) {
251
+ // Spec 57 — dismissed count is reported alongside the total, never
252
+ // subtracted from it ("43 findings, 3 dismissed").
253
+ const dismissedCount = result.summary.dismissed ?? 0;
254
+ const dismissedSuffix = dismissedCount > 0 ? `, ${dismissedCount} dismissed` : '';
255
+ const gateExcludedCount = violations.filter((v) => v.gateExcluded).length;
256
+ const gatingCount = (severity) => violations.filter((v) => v.severity === severity && !v.gateExcluded && !v.dismissed).length;
257
+ const findingsSuffix = `${dismissedSuffix}${gateExcludedCount > 0 ? ` (${gateExcludedCount.toLocaleString()} excluded from gate)` : ''}`;
258
+ const summaryLine = (label, total, severity) => gateExcludedCount > 0
259
+ ? `${label}: ${total} (${gatingCount(severity)} gating)`
260
+ : `${label}: ${total}`;
261
+ // ── Delta output (Spec 18 R2) ─────────────────────────────────
262
+ if (baseline && !options.full) {
263
+ const newViolations = violations.filter((v) => v.new === true);
264
+ const knownCount = baseline.knownCount ?? 0;
265
+ const fixedCount = baseline.fixedCount ?? 0;
266
+ const previousKnown = baseline.previousKnownCount ?? 0;
267
+ const currentDebt = newViolations.length + knownCount;
268
+ const debtDelta = currentDebt - previousKnown;
269
+ const trendIcon = debtDelta > 0 ? '↑' : debtDelta < 0 ? '↓' : '→';
270
+ const trendLabel = debtDelta > 0
271
+ ? `(debt increased since last baseline)`
272
+ : debtDelta < 0
273
+ ? `(debt decreased since last baseline)`
274
+ : '(unchanged)';
275
+ console.log(`\n📊 Delta: +${newViolations.length} new · −${fixedCount} fixed · ${knownCount} known ${trendIcon} ${trendLabel}`);
276
+ if (newViolations.length > 0) {
277
+ console.log(chalk.gray(`\n── New readings (${newViolations.length}) ──────────────────────────`));
278
+ for (const v of newViolations) {
279
+ const icon = v.severity === 'critical' ? '🔴' :
280
+ v.severity === 'severe' ? '🟠' : '🟡';
281
+ console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.severity}] ${v.message}`);
282
+ }
283
+ }
284
+ else {
285
+ console.log(chalk.green('\n✓ No new readings since last baseline.'));
286
+ if (knownCount > 0) {
287
+ console.log(chalk.gray(` ${knownCount} known reading(s) are still open — recorded, not resolved.`));
288
+ }
289
+ }
290
+ // Debt by analyzer
291
+ console.log(chalk.gray(`\n── Debt by Analyzer ──────────────────────────`));
292
+ const analyzerCounts = {};
293
+ for (const v of violations) {
294
+ const a = v.analyzer || 'unknown';
295
+ if (!analyzerCounts[a])
296
+ analyzerCounts[a] = { known: 0, new: 0 };
297
+ if (v.new === false)
298
+ analyzerCounts[a].known++;
299
+ else if (v.new === true)
300
+ analyzerCounts[a].new++;
301
+ }
302
+ for (const [analyzer, counts] of Object.entries(analyzerCounts).sort()) {
303
+ const newPart = counts.new > 0 ? ` (+${counts.new})` : '';
304
+ console.log(`${analyzer}: ${counts.known.toLocaleString()} known${newPart}`);
305
+ }
306
+ // Top files
307
+ console.log(chalk.gray(`\n── Top Files ─────────────────────────────────`));
308
+ const fileCounts = new Map();
309
+ for (const v of violations) {
310
+ const f = v.file || '';
311
+ fileCounts.set(f, (fileCounts.get(f) || 0) + 1);
312
+ }
313
+ const topFiles = [...fileCounts.entries()]
314
+ .sort((a, b) => b[1] - a[1])
315
+ .slice(0, 5);
316
+ for (const [file, count] of topFiles) {
317
+ console.log(`${file} — ${count} reading${count !== 1 ? 's' : ''}`);
318
+ }
319
+ console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit --full')} to see all ${currentDebt.toLocaleString()} readings.`));
320
+ }
321
+ else if (!baseline) {
322
+ // No baseline: current behavior + hint
323
+ console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
324
+ console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
325
+ console.log(summaryLine('Severe', result.summary.severe, 'severe'));
326
+ console.log(summaryLine('High', result.summary.high, 'high'));
327
+ console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
328
+ console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit baseline')} to adopt the ratchet and track changes over time.`));
329
+ }
330
+ else {
331
+ // --full with baseline: full itemized inventory (current behavior)
332
+ console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
333
+ console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
334
+ console.log(summaryLine('Severe', result.summary.severe, 'severe'));
335
+ console.log(summaryLine('High', result.summary.high, 'high'));
336
+ console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
337
+ }
338
+ }
339
+ /**
340
+ * Print unparsed-file failures and skipped-extension notice.
341
+ */
342
+ function printUnparsedAndSkipped(result) {
343
+ // Spec 32 — unparsed files are never silent.
344
+ const unparsedFiles = result.metadata?.unparsedFiles ?? [];
345
+ if (unparsedFiles.length > 0) {
346
+ console.error(chalk.red(`\n⚠️ ${unparsedFiles.length} file${unparsedFiles.length !== 1 ? 's' : ''} failed to parse:`));
347
+ for (const u of unparsedFiles.slice(0, 20)) {
348
+ console.error(` ${u.filePath} — ${u.reason}`);
349
+ }
350
+ if (unparsedFiles.length > 20) {
351
+ console.error(` … and ${unparsedFiles.length - 20} more`);
352
+ }
353
+ }
354
+ // Spec 43 R5 follow-up — extensions present on disk that discovery skipped.
355
+ const skippedExtensions = result.metadata?.skippedExtensions ?? [];
356
+ if (skippedExtensions.length > 0) {
357
+ console.log(chalk.gray(`\n── Not analyzed (skipped extensions) ────────`));
358
+ const parts = skippedExtensions.map(s => `${s.ext} (${s.count})`);
359
+ console.log(` ${parts.join(', ')}`);
360
+ }
361
+ }
362
+ /**
363
+ * Print per-analyzer pipeline activity and the detailed coverage breakdown.
364
+ */
365
+ function printPipelineStages(result, options) {
366
+ if (options.json)
367
+ return;
368
+ const analyzerFiles = [];
369
+ for (const [name, ar] of Object.entries(result.analyzerResults)) {
370
+ const vCount = ar.violations.length;
371
+ if (isVisitorStatus(ar.status)) {
372
+ analyzerFiles.push(`${name} [visitor-ran]: ${getFilesProcessed(ar.status)} files, ${vCount} violations`);
373
+ }
374
+ else if (isReducerStatus(ar.status)) {
375
+ analyzerFiles.push(`${name} [reducer-ran]: ${getFactsConsumed(ar.status)} facts, ${vCount} violations`);
376
+ }
377
+ else {
378
+ analyzerFiles.push(`${name} [notRun]: ${vCount} violations`);
379
+ }
380
+ }
381
+ if (analyzerFiles.length > 0) {
382
+ console.log(chalk.gray(`\n── Pipeline Stages ──────────────────────────`));
383
+ console.log(analyzerFiles.join('\n'));
384
+ }
385
+ // ── Coverage (Spec 27) ──────────────────────────────────────────
386
+ const coverage = result.metadata?.coverage;
387
+ if (coverage && coverage.length > 0) {
388
+ const fired = coverage.filter(c => c.state === 'fired');
389
+ const clean = coverage.filter(c => c.state === 'clean');
390
+ const incomplete = coverage.filter(c => c.state === 'incomplete');
391
+ const notApplicable = coverage.filter(c => c.state === 'notApplicable');
392
+ const cannotFire = coverage.filter(c => c.state === 'cannot-fire');
393
+ const firedCount = fired.reduce((s, c) => s + c.count, 0);
394
+ console.log(chalk.gray(`\n── Coverage ─────────────────────────────────`));
395
+ console.log(` ${fired.length} fired (${firedCount.toLocaleString()} violations), ` +
396
+ `${clean.length} clean, ` +
397
+ `${incomplete.length} incomplete, ` +
398
+ `${notApplicable.length} notApplicable, ` +
399
+ `${cannotFire.length} cannot-fire ` +
400
+ `(${coverage.length} rules registered)`);
401
+ if (cannotFire.length > 0) {
402
+ console.log(chalk.yellow(` ── Cannot Fire (broken in the tool) ──`));
403
+ for (const c of cannotFire) {
404
+ console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
405
+ }
406
+ }
407
+ if (notApplicable.length > 0) {
408
+ console.log(chalk.gray(` ── Not Applicable ──`));
409
+ for (const c of notApplicable) {
410
+ console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
411
+ }
412
+ }
413
+ if (clean.length > 0) {
414
+ console.log(chalk.gray(` ── Clean ──`));
415
+ for (const c of clean) {
416
+ console.log(` ${c.ruleId} (0)`);
417
+ }
418
+ }
419
+ if (incomplete.length > 0) {
420
+ console.log(chalk.gray(` ── Incomplete (missing facts for some files) ──`));
421
+ for (const c of incomplete) {
422
+ console.log(` ${c.ruleId} (0)`);
423
+ }
424
+ }
425
+ }
426
+ }
427
+ /**
428
+ * Generate and write the formatted report when --format is specified.
429
+ */
430
+ async function writeReport(result, options) {
431
+ if (!options.format)
432
+ return;
433
+ const validFormats = ['html', 'json', 'csv', 'sarif'];
434
+ if (!validFormats.includes(options.format)) {
435
+ console.error(chalk.red(`Unknown format: "${options.format}". Must be one of: ${validFormats.join(', ')}`));
436
+ process.exit(1);
437
+ }
438
+ const { generateReport } = await import('./reporting/reportGenerator.js');
439
+ const { readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
440
+ const rootDir = resolve(options.path || process.cwd());
441
+ const config = options.format === 'sarif'
442
+ ? { rootDir, ...readVersionControlProvenance(rootDir) }
443
+ : { rootDir };
444
+ const report = generateReport(result, options.format, config);
445
+ const ext = options.format === 'sarif' ? 'sarif' : options.format;
446
+ if (options.output) {
447
+ const outputDir = resolve(options.output);
448
+ const reportPath = join(outputDir, `audit-report.${ext}`);
449
+ let exists = false;
450
+ try {
451
+ await fs.access(reportPath);
452
+ exists = true;
453
+ }
454
+ catch { /* ok */ }
455
+ if (exists && !options.overwrite) {
456
+ console.error(chalk.red(`Refusing to overwrite existing report ${reportPath} — pass --overwrite to replace it.`));
457
+ process.exit(1);
458
+ }
459
+ await fs.mkdir(dirname(reportPath), { recursive: true });
460
+ await fs.writeFile(reportPath, report, 'utf-8');
461
+ console.log(chalk.green(`\nReport written to ${reportPath}`));
462
+ }
463
+ else {
464
+ // No output path — default to stdout.
465
+ process.stdout.write(report + '\n');
466
+ }
467
+ }
468
+ /**
469
+ * Apply the exit-code gates: fail-on-regression, zero-files, parse-failure,
470
+ * and --fail-on severity thresholds.
471
+ */
472
+ function applyFailOnGates(result, violations, baseline, options, failOnSeverity) {
473
+ // --fail-on-regression: compare total high debt to baseline snapshot
474
+ if (baseline && options.failOnRegression) {
475
+ const currentDebt = violations.filter((v) => v.new || v.new === false).length;
476
+ const snapshotDebt = baseline.previousKnownCount ?? 0;
477
+ if (currentDebt > snapshotDebt) {
478
+ console.error(chalk.red(`Debt regression: ${currentDebt - snapshotDebt} readings added without re-baselining.`));
479
+ process.exit(2);
480
+ }
481
+ }
482
+ // Pipeline-failure gate — the audit pipeline itself threw (schema drift,
483
+ // DB write failure, …). One error, reported once by the runner; this exits
484
+ // non-zero (broken tool) so a swallowed pipeline throw can never read as a
485
+ // clean pass.
486
+ {
487
+ const diagnostics = result.metadata?.diagnostics ?? [];
488
+ const pipelineErrors = diagnostics.filter((d) => d.kind === 'pipeline-error');
489
+ if (pipelineErrors.length > 0) {
490
+ console.error(`Pipeline failure: ${pipelineErrors[0].message}`);
491
+ process.exit(1);
492
+ }
493
+ }
494
+ // Zero-files gate — any enabled analyzer matching zero source files is a
495
+ // dark-analyzer failure; fail the run so the bug can't hide.
496
+ {
497
+ const diagnostics = result.metadata?.diagnostics ?? [];
498
+ const zeroFileWarnings = diagnostics.filter((d) => d.kind === 'zero-files');
499
+ if (zeroFileWarnings.length > 0) {
500
+ const names = zeroFileWarnings.map((d) => d.analyzerName ?? d.analyzer).join(', ');
501
+ console.error(`Zero-files failure: ${zeroFileWarnings.length} analyzer(s) matched zero source files (${names})`);
502
+ process.exit(2);
503
+ }
504
+ }
505
+ // Spec 32 — a run where any file failed to parse is incomplete and must
506
+ // not exit 0.
507
+ {
508
+ const unparsedFiles = result.metadata?.unparsedFiles ?? [];
509
+ if (unparsedFiles.length > 0) {
510
+ console.error(`Parse-failure: ${unparsedFiles.length} file(s) could not be parsed; analysis is incomplete.`);
511
+ process.exit(2);
512
+ }
513
+ }
514
+ // --fail-on: evaluate new + invariant findings only (unless --include-baseline)
515
+ if (failOnSeverity) {
516
+ const evaluableViolations = (baseline && !options.includeBaseline)
517
+ ? violations.filter((v) => v.new || v.analyzer === 'invariants')
518
+ : violations;
519
+ const severityOrder = SEVERITIES;
520
+ const failIndex = severityOrder.indexOf(failOnSeverity);
521
+ const hasAtOrAbove = evaluableViolations.some((v) => {
522
+ // Spec 57 — a dismissed finding never blocks the gate.
523
+ if (v.dismissed)
524
+ return false;
525
+ // Spec 36 R4 — a finding from a path-profile-excluded file never blocks.
526
+ if (v.gateExcluded)
527
+ return false;
528
+ const vIndex = severityOrder.indexOf(v.severity);
529
+ return vIndex >= 0 && vIndex <= failIndex;
530
+ });
531
+ if (hasAtOrAbove) {
532
+ process.exit(2);
533
+ }
534
+ }
535
+ }
91
536
  // Legacy audit command (default behavior)
92
537
  program
93
538
  .command('audit', { isDefault: true })
94
539
  .description('Run code quality audit (default command)')
95
540
  .option('-p, --path <path>', 'Path to audit', process.cwd())
96
- .option('-c, --config <config>', 'Configuration name')
541
+ .option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
97
542
  .option('-o, --output <dir>', 'Output directory for reports')
98
543
  .option('-f, --format <format>', 'Report format: html, json, csv, or sarif')
544
+ .option('--overwrite', 'Overwrite an existing report file at --output (default: refuse)')
99
545
  .option('--fail-on <severity>', 'Exit code 2 when violations at or above this severity exist')
100
546
  .option('--full', 'Show full violation inventory (overrides default delta view when baseline exists)')
101
547
  .option('--include-baseline', 'Evaluate baseline-known violations in --fail-on checks')
102
548
  .option('--fail-on-regression', 'Exit code 2 when total high debt exceeds the baseline snapshot')
103
549
  .option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
104
550
  .option('--detach', 'Run the audit in a detached background process and print the job ID')
105
- .option('--partition-strategy <strategy>', 'Partition strategy for detached runs: none, auto, or top-level')
106
- .option('--max-partitions <n>', 'Maximum number of partition shards (detached runs)')
107
- .option('--shard-timeout-ms <ms>', 'Per-shard timeout in milliseconds (detached runs)')
108
551
  .option('--explain-skipped', 'Print a per-reason breakdown of files dropped from analysis')
109
552
  .action(async (options) => {
110
553
  console.log(chalk.blue('🔍 Code Quality Audit Tool'));
@@ -137,302 +580,20 @@ program
137
580
  }
138
581
  }
139
582
  }
140
- const result = await runAuditDispatch({
583
+ const result = await createAuditRunner({
141
584
  projectRoot: options.path,
142
585
  configName: options.config,
143
586
  outputDirectory: options.output,
144
587
  presets: presetIds
145
- });
588
+ }).run();
146
589
  const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
147
590
  const baseline = result.metadata?.baseline;
148
- // Spec 57 — dismissed count is reported alongside the total, never
149
- // subtracted from it ("43 findings, 3 dismissed").
150
- const dismissedCount = result.summary.dismissed ?? 0;
151
- const dismissedSuffix = dismissedCount > 0 ? `, ${dismissedCount} dismissed` : '';
152
- // Spec 36 R4 — a path-profile-excluded finding reports at its real
153
- // severity but never gates. When any finding is gate-excluded, surface
154
- // the gating count next to the total so a clean exit doesn't read as
155
- // "0 findings" beside a non-zero critical count (e.g. knex: 38 critical,
156
- // all test-dir, exit 0). The excluded total still reads in full — the
157
- // distinction is gating vs. reporting, never subtraction.
158
- const gateExcludedCount = violations.filter((v) => v.gateExcluded).length;
159
- const gatingCount = (severity) => violations.filter((v) => v.severity === severity && !v.gateExcluded && !v.dismissed).length;
160
- const findingsSuffix = `${dismissedSuffix}${gateExcludedCount > 0 ? ` (${gateExcludedCount.toLocaleString()} excluded from gate)` : ''}`;
161
- const summaryLine = (label, total, severity) => gateExcludedCount > 0
162
- ? `${label}: ${total} (${gatingCount(severity)} gating)`
163
- : `${label}: ${total}`;
164
- // ── Coverage panel leads the report (Spec 47 R2) ─────────────
165
- // A diagnostic report opens with what was measured before it lists any
166
- // readings, so a zero-reading report can't be mistaken for a clean tree.
167
- const coverage = result.metadata?.coverage;
168
- if (coverage && coverage.length > 0) {
169
- const covFired = coverage.filter(c => c.state === 'fired').length;
170
- const covClean = coverage.filter(c => c.state === 'clean').length;
171
- const covUnassessed = coverage.filter(c => c.state === 'unassessed').length;
172
- const covNotApplicable = coverage.filter(c => c.state === 'notApplicable').length;
173
- const covCannotFire = coverage.filter(c => c.state === 'cannot-fire').length;
174
- console.log(chalk.gray(`── Coverage panel ── ${covFired} fired · ${covClean} clean · ` +
175
- `${covUnassessed} unassessed · ${covNotApplicable} not-applicable · ` +
176
- `${covCannotFire} cannot-fire`));
177
- }
178
- // ── Skipped analyzers (Spec 61) ──────────────────────────────
179
- // A language present on disk but never analyzed is a stated gap, not a
180
- // silent zero. The Go path surfaces a named runtime failure (missing
181
- // toolchain, wrong-arch prebuilt binary, failed rebuild) as a
182
- // metadata.diagnostics entry; before this section those kinds were
183
- // invisible outside --json, so a polyglot repo with .go files and no Go
184
- // toolchain printed zero Go findings and exited clean — the silent-zero
185
- // failure mode. These lead above coverage gaps: a skipped analyzer is a
186
- // whole language unmeasured, more severe than a per-file visibility gap.
187
- const skippedAnalyzers = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'go-toolchain-missing' ||
188
- d.kind === 'go-analyzer-wrong-arch' ||
189
- d.kind === 'go-analyzer-build-failed');
190
- if (skippedAnalyzers.length > 0) {
191
- console.log(chalk.yellow(`── Skipped analyzers ── ${skippedAnalyzers.length}`));
192
- for (const d of skippedAnalyzers) {
193
- const name = d.analyzerName || 'go';
194
- console.log(chalk.yellow(` ${name} [${d.kind}] — ${d.message}`));
195
- }
196
- }
197
- // ── Ignored config keys (Spec 61 R1.4) ─────────────────────────
198
- // Keys dropped by sanitizeProjectFileConfig: undeclared keys (like
199
- // `scope`, which previously reached execSync via mergeConfig's raw-key
200
- // iteration) and path keys resolving outside the project root. Surfaced
201
- // so a silently-ignored config never reads as "loaded fine".
202
- const ignoredConfigKeys = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'config-key-rejected');
203
- if (ignoredConfigKeys.length > 0) {
204
- console.log(chalk.yellow(`── Ignored config keys ── ${ignoredConfigKeys.length}`));
205
- for (const d of ignoredConfigKeys) {
206
- console.log(chalk.yellow(` ${d.message}`));
207
- }
208
- }
209
- // ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
210
- // Coverage diagnostics are the analyzer saying "my visibility ends here",
211
- // not "the code is wrong". They lead with counts, then per-occurrence
212
- // file:line — visible and counted, never blocking (they never reach the
213
- // gate). Rendered alongside the coverage panel that leads the report.
214
- const coverageDiagnostics = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found' || d.kind === 'cannot-fire');
215
- if (coverageDiagnostics.length > 0) {
216
- const byKind = {};
217
- for (const d of coverageDiagnostics)
218
- byKind[d.kind] = (byKind[d.kind] ?? 0) + 1;
219
- const totals = Object.entries(byKind)
220
- .map(([kind, n]) => `${n} ${kind}`)
221
- .join(' · ');
222
- console.log(chalk.gray(`── Coverage gaps ── ${totals}`));
223
- for (const d of coverageDiagnostics.slice(0, 20)) {
224
- const loc = d.file ? `${d.file}${typeof d.line === 'number' ? `:${d.line}` : ''}` : '(unknown)';
225
- console.log(chalk.gray(` ${loc} [${d.kind}] — ${d.message}`));
226
- }
227
- if (coverageDiagnostics.length > 20) {
228
- console.log(chalk.gray(` … and ${coverageDiagnostics.length - 20} more`));
229
- }
230
- }
231
- // ── Spec 60 R1 — test coverage (reporting, not detection) ─────
232
- // Classifies every non-test module as tested / untested-live /
233
- // untested-dead off the file-level import edges. The dead count is
234
- // post-entry-point-exception, with the drop attributed.
235
- const testCoverage = result.metadata?.testCoverage;
236
- if (testCoverage) {
237
- const pct = testCoverage.total > 0
238
- ? ((testCoverage.tested / testCoverage.total) * 100).toFixed(1)
239
- : '0.0';
240
- let line = `tested ${testCoverage.tested}/${testCoverage.total} (${pct}%)` +
241
- ` · untested-live ${testCoverage.untestedLive}` +
242
- ` · untested-dead ${testCoverage.untestedDead}`;
243
- if (testCoverage.deadDrop > 0) {
244
- line += ` (was ${testCoverage.deadPreException} pre-exception; −${testCoverage.deadDrop} entry points exempted)`;
245
- }
246
- console.log(chalk.gray(`── Test coverage ── ${line}`));
247
- }
248
- // ── Spec 60 R2 — size distributions (median / p95 / max) ──────
249
- const sizeDistributions = result.metadata?.sizeDistributions;
250
- if (sizeDistributions && sizeDistributions.length > 0) {
251
- console.log(chalk.gray(`── Size distributions ──`));
252
- for (const d of sizeDistributions) {
253
- const fmt = (n) => Number.isInteger(n) ? String(n) : n.toFixed(1);
254
- const tail = d.maxEntry
255
- ? ` (${d.maxEntry.name}${d.maxEntry.fileType ? ` · ${d.maxEntry.fileType}` : ''} · ${d.maxEntry.entityType})`
256
- : '';
257
- const pop = d.population ? `${d.population}, n=${d.count}` : `n=${d.count}`;
258
- console.log(chalk.gray(` ${d.measure} [${pop}]: median ${fmt(d.median)} · p95 ${fmt(d.p95)} · max ${fmt(d.max)}${tail}`));
259
- }
260
- }
261
- // ── Spec 60 R3 — dead-and-duplicated clusters ─────────────────
262
- const deadClusters = result.metadata?.deadClusters;
263
- if (deadClusters && deadClusters.length > 0) {
264
- console.log(chalk.gray(`── Dead-and-duplicated ── ${deadClusters.length} clusters`));
265
- for (const c of deadClusters.slice(0, 10)) {
266
- console.log(chalk.gray(` ${c.count} × ${c.basename}`));
267
- }
268
- if (deadClusters.length > 10) {
269
- console.log(chalk.gray(` … and ${deadClusters.length - 10} more clusters`));
270
- }
271
- }
272
- // ── Delta output (Spec 18 R2) ─────────────────────────────────
273
- if (baseline && !options.full) {
274
- const newViolations = violations.filter((v) => v.new === true);
275
- const knownCount = baseline.knownCount ?? 0;
276
- const fixedCount = baseline.fixedCount ?? 0;
277
- const previousKnown = baseline.previousKnownCount ?? 0;
278
- const currentDebt = newViolations.length + knownCount;
279
- const debtDelta = currentDebt - previousKnown;
280
- const trendIcon = debtDelta > 0 ? '↑' : debtDelta < 0 ? '↓' : '→';
281
- const trendLabel = debtDelta > 0
282
- ? `(debt increased since last baseline)`
283
- : debtDelta < 0
284
- ? `(debt decreased since last baseline)`
285
- : '(unchanged)';
286
- console.log(`\n📊 Delta: +${newViolations.length} new · −${fixedCount} fixed · ${knownCount} known ${trendIcon} ${trendLabel}`);
287
- if (newViolations.length > 0) {
288
- console.log(chalk.gray(`\n── New readings (${newViolations.length}) ──────────────────────────`));
289
- for (const v of newViolations) {
290
- const icon = v.severity === 'critical' ? '🔴' :
291
- v.severity === 'severe' ? '🟠' : '🟡';
292
- console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.message}`);
293
- }
294
- }
295
- else {
296
- console.log(chalk.green('\n✓ No new readings since last baseline.'));
297
- if (knownCount > 0) {
298
- console.log(chalk.gray(` ${knownCount} known reading(s) are still open — recorded, not resolved.`));
299
- }
300
- }
301
- // Debt by analyzer
302
- console.log(chalk.gray(`\n── Debt by Analyzer ──────────────────────────`));
303
- const analyzerCounts = {};
304
- for (const v of violations) {
305
- const a = v.analyzer || 'unknown';
306
- if (!analyzerCounts[a])
307
- analyzerCounts[a] = { known: 0, new: 0 };
308
- if (v.new === false)
309
- analyzerCounts[a].known++;
310
- else if (v.new === true)
311
- analyzerCounts[a].new++;
312
- }
313
- for (const [analyzer, counts] of Object.entries(analyzerCounts).sort()) {
314
- const newPart = counts.new > 0 ? ` (+${counts.new})` : '';
315
- console.log(`${analyzer}: ${counts.known.toLocaleString()} known${newPart}`);
316
- }
317
- // Top files
318
- console.log(chalk.gray(`\n── Top Files ─────────────────────────────────`));
319
- const fileCounts = new Map();
320
- for (const v of violations) {
321
- const f = v.file || '';
322
- fileCounts.set(f, (fileCounts.get(f) || 0) + 1);
323
- }
324
- const topFiles = [...fileCounts.entries()]
325
- .sort((a, b) => b[1] - a[1])
326
- .slice(0, 5);
327
- for (const [file, count] of topFiles) {
328
- console.log(`${file} — ${count} reading${count !== 1 ? 's' : ''}`);
329
- }
330
- console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit --full')} to see all ${currentDebt.toLocaleString()} readings.`));
331
- }
332
- else if (!baseline) {
333
- // No baseline: current behavior + hint
334
- console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
335
- console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
336
- console.log(summaryLine('Severe', result.summary.severe, 'severe'));
337
- console.log(summaryLine('High', result.summary.high, 'high'));
338
- console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
339
- console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit baseline')} to adopt the ratchet and track changes over time.`));
340
- }
341
- else {
342
- // --full with baseline: full itemized inventory (current behavior)
343
- console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
344
- console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
345
- console.log(summaryLine('Severe', result.summary.severe, 'severe'));
346
- console.log(summaryLine('High', result.summary.high, 'high'));
347
- console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
348
- }
591
+ printAuditDiagnostics(result);
592
+ printFindingSummary(result, violations, baseline, options);
349
593
  // Spec 44 R4 — file accounting: analyzed/dropped totals + optional breakdown.
350
594
  printFileAccounting(result, !!options.explainSkipped);
351
- // Spec 32 — unparsed files are never silent. A run where any file failed
352
- // to parse (WASM abort, read error, …) must surface the count + reasons.
353
- const unparsedFiles = result.metadata?.unparsedFiles ?? [];
354
- if (unparsedFiles.length > 0) {
355
- console.error(chalk.red(`\n⚠️ ${unparsedFiles.length} file${unparsedFiles.length !== 1 ? 's' : ''} failed to parse:`));
356
- for (const u of unparsedFiles.slice(0, 20)) {
357
- console.error(` ${u.filePath} — ${u.reason}`);
358
- }
359
- if (unparsedFiles.length > 20) {
360
- console.error(` … and ${unparsedFiles.length - 20} more`);
361
- }
362
- }
363
- // Spec 43 R5 follow-up — extensions present on disk that discovery skipped.
364
- // Informational: surfaces "what isn't being analyzed here" so an unlisted
365
- // extension (e.g. `.mdx`) never vanishes silently at discovery. Not an
366
- // error — a skipped extension is not necessarily a defect (`.md`/`.svg`
367
- // are legitimately not source), unlike `unparsedFiles` above.
368
- const skippedExtensions = result.metadata?.skippedExtensions ?? [];
369
- if (skippedExtensions.length > 0) {
370
- console.log(chalk.gray(`\n── Not analyzed (skipped extensions) ────────`));
371
- const parts = skippedExtensions.map(s => `${s.ext} (${s.count})`);
372
- console.log(` ${parts.join(', ')}`);
373
- }
374
- // Per-analyzer activity (read from result data, not serialized summary)
375
- // — surfaces zero-scan failures that would otherwise be invisible.
376
- if (!options.json) {
377
- const analyzerFiles = [];
378
- for (const [name, ar] of Object.entries(result.analyzerResults)) {
379
- const vCount = ar.violations.length;
380
- if (isVisitorStatus(ar.status)) {
381
- analyzerFiles.push(`${name} [visitor-ran]: ${getFilesProcessed(ar.status)} files, ${vCount} violations`);
382
- }
383
- else if (isReducerStatus(ar.status)) {
384
- analyzerFiles.push(`${name} [reducer-ran]: ${getFactsConsumed(ar.status)} facts, ${vCount} violations`);
385
- }
386
- else {
387
- analyzerFiles.push(`${name} [notRun]: ${vCount} violations`);
388
- }
389
- }
390
- if (analyzerFiles.length > 0) {
391
- console.log(chalk.gray(`\n── Pipeline Stages ──────────────────────────`));
392
- console.log(analyzerFiles.join('\n'));
393
- }
394
- // ── Coverage (Spec 27) ──────────────────────────────────────────
395
- const coverage = result.metadata?.coverage;
396
- if (coverage && coverage.length > 0) {
397
- const fired = coverage.filter(c => c.state === 'fired');
398
- const clean = coverage.filter(c => c.state === 'clean');
399
- const unassessed = coverage.filter(c => c.state === 'unassessed');
400
- const notApplicable = coverage.filter(c => c.state === 'notApplicable');
401
- const cannotFire = coverage.filter(c => c.state === 'cannot-fire');
402
- const firedCount = fired.reduce((s, c) => s + c.count, 0);
403
- console.log(chalk.gray(`\n── Coverage ─────────────────────────────────`));
404
- console.log(` ${fired.length} fired (${firedCount.toLocaleString()} violations), ` +
405
- `${clean.length} clean, ` +
406
- `${unassessed.length} unassessed, ` +
407
- `${notApplicable.length} notApplicable, ` +
408
- `${cannotFire.length} cannot-fire ` +
409
- `(${coverage.length} rules registered)`);
410
- if (cannotFire.length > 0) {
411
- console.log(chalk.yellow(` ── Cannot Fire (broken in the tool) ──`));
412
- for (const c of cannotFire) {
413
- console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
414
- }
415
- }
416
- if (notApplicable.length > 0) {
417
- console.log(chalk.gray(` ── Not Applicable ──`));
418
- for (const c of notApplicable) {
419
- console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
420
- }
421
- }
422
- if (clean.length > 0) {
423
- console.log(chalk.gray(` ── Clean ──`));
424
- for (const c of clean) {
425
- console.log(` ${c.ruleId} (0)`);
426
- }
427
- }
428
- if (unassessed.length > 0) {
429
- console.log(chalk.gray(` ── Unassessed (zero readings, applicability unknown) ──`));
430
- for (const c of unassessed) {
431
- console.log(` ${c.ruleId} (0)`);
432
- }
433
- }
434
- }
435
- }
595
+ printUnparsedAndSkipped(result);
596
+ printPipelineStages(result, options);
436
597
  // Spec-20 R4: built-in profile visibility — silent behavior changes
437
598
  // are never acceptable. Notify when scripts-and-tests excludes findings
438
599
  // from the blocking gate.
@@ -442,82 +603,8 @@ program
442
603
  console.log(chalk.blue(`\nℹ️ ${builtinCapped.toLocaleString()} readings excluded from the blocking gate by built-in profile "scripts-and-tests" (scripts/tests/fixtures).`));
443
604
  console.log(chalk.gray(` Set ${chalk.cyan('"builtin": false')} in .codeauditor.json to disable.`));
444
605
  }
445
- // Generate formatted report if --format is specified
446
- if (options.format) {
447
- const validFormats = ['html', 'json', 'csv', 'sarif'];
448
- if (!validFormats.includes(options.format)) {
449
- console.error(chalk.red(`Unknown format: "${options.format}". Must be one of: ${validFormats.join(', ')}`));
450
- process.exit(1);
451
- }
452
- const { generateReport } = await import('./reporting/reportGenerator.js');
453
- const { readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
454
- const rootDir = resolve(options.path || process.cwd());
455
- const config = options.format === 'sarif'
456
- ? { rootDir, ...readVersionControlProvenance(rootDir) }
457
- : { rootDir };
458
- const report = generateReport(result, options.format, config);
459
- const outputDir = options.output || process.cwd();
460
- const ext = options.format === 'sarif' ? 'sarif' : options.format;
461
- const reportPath = join(outputDir, `audit-report.${ext}`);
462
- await fs.mkdir(dirname(reportPath), { recursive: true });
463
- await fs.writeFile(reportPath, report, 'utf-8');
464
- console.log(chalk.green(`\nReport written to ${reportPath}`));
465
- }
466
- // ── Fail-on logic (Spec 18 R3) ───────────────────────────────
467
- // --fail-on-regression: compare total high debt to baseline snapshot
468
- if (baseline && options.failOnRegression) {
469
- const currentDebt = violations.filter((v) => v.new || v.new === false).length;
470
- const snapshotDebt = baseline.previousKnownCount ?? 0;
471
- if (currentDebt > snapshotDebt) {
472
- console.error(chalk.red(`Debt regression: ${currentDebt - snapshotDebt} readings added without re-baselining.`));
473
- process.exit(2);
474
- }
475
- }
476
- // Zero-files gate — any enabled analyzer matching zero source files is a
477
- // dark-analyzer failure; fail the run so the bug can't hide.
478
- {
479
- const diagnostics = result.metadata?.diagnostics ?? [];
480
- const zeroFileWarnings = diagnostics.filter((d) => d.kind === 'zero-files');
481
- if (zeroFileWarnings.length > 0) {
482
- const names = zeroFileWarnings.map((d) => d.analyzerName ?? d.analyzer).join(', ');
483
- console.error(`Zero-files failure: ${zeroFileWarnings.length} analyzer(s) matched zero source files (${names})`);
484
- process.exit(2);
485
- }
486
- }
487
- // Spec 32 — a run where any file failed to parse is incomplete and must
488
- // not exit 0. This mirrors the zero-files gate: a silent dark-analyzer
489
- // failure at file granularity is a hard failure.
490
- {
491
- const unparsedFiles = result.metadata?.unparsedFiles ?? [];
492
- if (unparsedFiles.length > 0) {
493
- console.error(`Parse-failure: ${unparsedFiles.length} file(s) could not be parsed; analysis is incomplete.`);
494
- process.exit(2);
495
- }
496
- }
497
- // --fail-on: evaluate new + invariant findings only (unless --include-baseline)
498
- if (failOnSeverity) {
499
- const evaluableViolations = (baseline && !options.includeBaseline)
500
- ? violations.filter((v) => v.new || v.analyzer === 'invariants')
501
- : violations;
502
- const severityOrder = SEVERITIES;
503
- const failIndex = severityOrder.indexOf(failOnSeverity);
504
- const hasAtOrAbove = evaluableViolations.some((v) => {
505
- // Spec 57 — a dismissed finding never blocks the gate.
506
- if (v.dismissed)
507
- return false;
508
- // Spec 36 R4 — a finding from a path-profile-excluded file (e.g. a
509
- // test/script under "scripts-and-tests") never blocks the gate,
510
- // whatever its severity. The file is doing its job; a hardcoded
511
- // connection string in a stress-test script is not a defect to block on.
512
- if (v.gateExcluded)
513
- return false;
514
- const vIndex = severityOrder.indexOf(v.severity);
515
- return vIndex >= 0 && vIndex <= failIndex;
516
- });
517
- if (hasAtOrAbove) {
518
- process.exit(2);
519
- }
520
- }
606
+ await writeReport(result, options);
607
+ applyFailOnGates(result, violations, baseline, options, failOnSeverity);
521
608
  }
522
609
  catch (error) {
523
610
  console.error(chalk.red('Error:'), error);
@@ -534,7 +621,7 @@ program
534
621
  .option('--fail-on-zero-files', 'Exit code 2 when any enabled analyzer matches zero source files', true)
535
622
  .option('--stdin', 'Read file paths from stdin (one per line)')
536
623
  .option('--explain-skipped', 'Print a per-reason breakdown of files dropped from analysis')
537
- .option('-p, --path <projectPath>', 'Project root path', process.cwd())
624
+ .option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
538
625
  .action(async (paths, options) => {
539
626
  try {
540
627
  const fileSet = new Set();
@@ -570,11 +657,17 @@ program
570
657
  // the gate never misses a finding. SARIF needs full run metadata, so it
571
658
  // always runs in-process.
572
659
  const projectRoot = resolve(options.path || process.cwd());
660
+ const hookMode = process.env.CODE_AUDITOR_HOOK === '1' || process.env.CODE_AUDITOR_HOOK === 'true';
573
661
  let result;
574
662
  if (resolvedPaths.length > 0 && options.format !== 'sarif') {
575
- const { resolveDaemon, readDaemonDiagnostics } = await import('./daemon/resolve.js');
663
+ const { resolveDaemon, readDaemonDiagnostics, startDaemonDetached } = await import('./daemon/resolve.js');
576
664
  const daemon = await resolveDaemon(projectRoot);
577
665
  if (daemon.mode === 'not-ready') {
666
+ // Hook mode (Spec 68 §hook-contract): never block the edit on an audit the
667
+ // daemon can't serve yet — it is still indexing, and the finding arrives on
668
+ // a later edit. Say nothing, exit clean. A non-hook caller gets the status.
669
+ if (hookMode)
670
+ process.exit(0);
578
671
  reportDaemonIndexing(daemon.state, !!options.json);
579
672
  return;
580
673
  }
@@ -583,14 +676,27 @@ program
583
676
  if (diag && diag.status === 'ready' && diag.staleFiles.length === 0) {
584
677
  result = await buildChangedResultFromDiagnostics(diag.diagnostics, projectRoot);
585
678
  }
679
+ else if (hookMode) {
680
+ // A just-edited file whose re-audit hasn't landed is not trustworthy from
681
+ // cache. Report nothing; the finding arrives on the next edit.
682
+ process.exit(0);
683
+ }
684
+ }
685
+ else if (hookMode) {
686
+ // No daemon: enqueue by starting one detached, then exit. The hook reads
687
+ // results from the daemon's cache on later edits — it never computes them.
688
+ startDaemonDetached(projectRoot);
689
+ process.exit(0);
586
690
  }
587
691
  }
588
692
  if (!result) {
693
+ // Hook mode never falls back to an in-process audit (Spec 68 §hook-contract).
694
+ if (hookMode)
695
+ process.exit(0);
589
696
  await initParsers();
590
697
  const runner = createAuditRunner({
591
698
  projectRoot: options.path,
592
699
  scope,
593
- analyzerConcurrency: 4
594
700
  });
595
701
  result = await runner.run();
596
702
  }
@@ -599,7 +705,7 @@ program
599
705
  // Spec 57 — dismissed count is reported alongside the total, never
600
706
  // subtracted from it. The hook surface (`changed`) is agent-facing, so it
601
707
  // must show the same "N findings, M dismissed" figure as the full report.
602
- const dismissedCount = result.summary.dismissed ?? 0;
708
+ const dismissedCount = result.summary?.dismissed ?? 0;
603
709
  // Blocking gate decision (Spec 45 R1/R4, Spec 54 R3), computed once so the
604
710
  // agent-facing before/after count (Spec 45 A2) and the exit code agree on
605
711
  // the same number. Every registered rule participates (R1); enforcement is
@@ -607,98 +713,21 @@ program
607
713
  // {critical, severe, high} — every finding is a defect and every finding
608
714
  // blocks; there is no configurable gate.
609
715
  const { blocking, resolutionGaps } = computeGatingDecision(violations, BLOCKING_SEVERITIES);
610
- // JSON output
611
- if (options.format === 'sarif') {
612
- const { generateSARIFReport, readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
613
- const projectDir = resolve(options.path || process.cwd());
614
- const sarifOutput = generateSARIFReport(result, { rootDir: projectDir, ...readVersionControlProvenance(projectDir) });
615
- process.stdout.write(sarifOutput + '\n');
616
- }
617
- else if (options.json) {
618
- const projectDir = resolve(options.path || process.cwd());
619
- const jsonViolations = violations.map((v) => {
620
- // Compute relative path if file resolves inside the project
621
- let filePath = v.file || '';
622
- if (filePath.startsWith('/') || filePath.startsWith('\\\\')) {
623
- const rel = relative(projectDir, filePath);
624
- // Only use relative path if it doesn't escape the project
625
- if (!rel.startsWith('..') && !isAbsolute(rel)) {
626
- filePath = rel;
627
- }
628
- }
629
- // Find column: prefer explicit column, then start.column, then default to 1
630
- const col = v.column ?? v.start?.column ?? 1;
631
- return {
632
- analyzer: v.analyzer || '',
633
- rule: v.rule,
634
- severity: v.severity,
635
- message: v.message,
636
- file: filePath,
637
- line: v.line ?? v.start?.line,
638
- column: col,
639
- endLine: v.end?.line,
640
- endColumn: v.end?.column,
641
- enclosingSymbol: v.symbol || v.enclosingFunction || '',
642
- suggestion: v.suggestion || '',
643
- details: v.details || '',
644
- ...(v.new !== undefined && { new: v.new }),
645
- ...(v.dismissed !== undefined && { dismissed: v.dismissed }),
646
- fingerprint: fingerprint(buildFingerprintInput(v))
647
- };
648
- });
649
- // Coverage diagnostics ride alongside violations so an agent sees "the
650
- // analyzer couldn't resolve this" without a metadata flag — and without
651
- // it ever gating (diagnostics never reach computeGatingDecision).
652
- const jsonDiagnostics = (result.metadata?.diagnostics ?? []).map((d) => ({
653
- analyzer: d.analyzerName ?? d.analyzer ?? '',
654
- kind: d.kind,
655
- message: d.message,
656
- ...(d.file ? { file: d.file } : {}),
657
- ...(typeof d.line === 'number' ? { line: d.line } : {}),
658
- ...(d.details ? { details: d.details } : {}),
659
- }));
660
- const jsonOutput = { violations: jsonViolations, diagnostics: jsonDiagnostics };
661
- process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
662
- }
663
- else if (!options.quiet || violations.length > 0) {
664
- // Console output — Spec 45 A2: agent-facing output emits counts (per
665
- // analyzer/rule/severity, plus the before/after gate figure) alongside
666
- // findings. The hook and the `changed` command are the agent's surface.
667
- console.log(chalk.blue('🔍 Diff-Scoped Code Audit'));
668
- console.log(chalk.gray('══════════════════════════════════════════════════'));
669
- printFileAccounting(result, !!options.explainSkipped);
670
- if (violations.length > 0) {
671
- console.log('');
672
- printCountSummary(violations);
673
- console.log(chalk.bold(`gate before/after: ${violations.length} → ${blocking.length} blocking`));
674
- if (dismissedCount > 0) {
675
- console.log(chalk.dim(` ${dismissedCount} dismissed — still counted above, excluded from the gate`));
676
- }
677
- console.log(chalk.gray('── Violations ────────────────────────────────────'));
678
- for (const v of violations) {
679
- const icon = v.severity === 'critical' ? '🔴' :
680
- v.severity === 'severe' ? '🟠' : '🟡';
681
- const statusTag = v.new === false
682
- ? chalk.dim(' [known — still open]')
683
- : '';
684
- const dismissedTag = v.dismissed
685
- ? chalk.dim(' [dismissed]')
686
- : '';
687
- console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.message}${statusTag}${dismissedTag}`);
688
- }
689
- }
690
- else {
691
- console.log(chalk.green('\n✓ No readings.'));
692
- }
693
- }
694
- // Spec 38 R2/R3 — per-rule timing + gate wall-clock, opt-in by env var.
716
+ await emitChangedOutput(result, violations, blocking, dismissedCount, options);
717
+ // Spec 38 R2/R3 — per-rule timing + gate cpu-time, opt-in by env var.
695
718
  // Emitted to stderr so it never corrupts --json stdout. Slowest rule first.
719
+ // The gate metric is CPU time (auditCpuMs), not wall clock: the budget is
720
+ // about a slow *rule*, and CPU time isolates that from a slow machine.
696
721
  if (process.env.CODE_AUDIT_RULE_TIMING === '1') {
697
722
  const ruleTiming = result.metadata?.ruleTiming;
698
- const gateMs = result.metadata?.auditDuration;
723
+ const gateMs = result.metadata?.auditCpuMs;
724
+ const gateWallMs = result.metadata?.auditDuration;
699
725
  const lines = [];
700
726
  lines.push('');
701
- lines.push(`⏱ gate wall-clock: ${(gateMs ?? 0).toFixed(1)} ms (budget 300 ms)`);
727
+ // §16 criterion 16 — report both wall-clock (what the hook's user feels)
728
+ // and CPU (what the rule actually burned); the budget asserts on CPU.
729
+ lines.push(`⏱ gate wall-clock: ${(gateWallMs ?? 0).toFixed(1)} ms`);
730
+ lines.push(`⏱ gate cpu-time: ${(gateMs ?? 0).toFixed(1)} ms (budget 400 ms)`);
702
731
  lines.push('── per-rule timing (slowest first) ──');
703
732
  if (ruleTiming && ruleTiming.length > 0) {
704
733
  for (const t of ruleTiming) {
@@ -731,6 +760,17 @@ program
731
760
  process.exit(2);
732
761
  }
733
762
  }
763
+ // Pipeline-failure gate — the audit pipeline itself threw. One error,
764
+ // reported once by the runner; exit non-zero (broken tool) so a swallowed
765
+ // pipeline throw can never read as a clean pass.
766
+ {
767
+ const diagnostics = result.metadata?.diagnostics ?? [];
768
+ const pipelineErrors = diagnostics.filter((d) => d.kind === 'pipeline-error');
769
+ if (pipelineErrors.length > 0) {
770
+ console.error(`Pipeline failure: ${pipelineErrors[0].message}`);
771
+ process.exit(1);
772
+ }
773
+ }
734
774
  // Exit code based on --no-fail-on-zero-files (default-on in v3.4.8)
735
775
  // Any enabled analyzer matching zero source files is a failure — a >50%
736
776
  // threshold lets the exact bug (3/9 analyzers dark) pass silently.
@@ -749,6 +789,96 @@ program
749
789
  process.exit(1);
750
790
  }
751
791
  });
792
+ /**
793
+ * Emit the changed-audit output (SARIF, JSON, or console) for the hook surface.
794
+ * The SARIF branch dynamically imports its generator, so this is async.
795
+ */
796
+ async function emitChangedOutput(result, violations, blocking, dismissedCount, options) {
797
+ // JSON output
798
+ if (options.format === 'sarif') {
799
+ const { generateSARIFReport, readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
800
+ const projectDir = resolve(options.path || process.cwd());
801
+ const sarifOutput = generateSARIFReport(result, { rootDir: projectDir, ...readVersionControlProvenance(projectDir) });
802
+ process.stdout.write(sarifOutput + '\n');
803
+ }
804
+ else if (options.json) {
805
+ const projectDir = resolve(options.path || process.cwd());
806
+ const jsonViolations = violations.map((v) => {
807
+ // Compute relative path if file resolves inside the project
808
+ let filePath = v.file || '';
809
+ if (filePath.startsWith('/') || filePath.startsWith('\\\\')) {
810
+ const rel = relative(projectDir, filePath);
811
+ // Only use relative path if it doesn't escape the project
812
+ if (!rel.startsWith('..') && !isAbsolute(rel)) {
813
+ filePath = rel;
814
+ }
815
+ }
816
+ // Find column: prefer explicit column, then start.column, then default to 1
817
+ const col = v.column ?? v.start?.column ?? 1;
818
+ return {
819
+ analyzer: v.analyzer || '',
820
+ rule: v.rule,
821
+ severity: v.severity,
822
+ message: v.message,
823
+ file: filePath,
824
+ line: v.line ?? v.start?.line,
825
+ column: col,
826
+ endLine: v.end?.line,
827
+ endColumn: v.end?.column,
828
+ enclosingSymbol: v.symbol || v.enclosingFunction || '',
829
+ suggestion: v.suggestion || '',
830
+ details: v.details || '',
831
+ ...(v.new !== undefined && { new: v.new }),
832
+ ...(v.dismissed !== undefined && { dismissed: v.dismissed }),
833
+ fingerprint: fingerprint(buildFingerprintInput(v))
834
+ };
835
+ });
836
+ // Coverage diagnostics ride alongside violations so an agent sees "the
837
+ // analyzer couldn't resolve this" without a metadata flag — and without
838
+ // it ever gating (diagnostics never reach computeGatingDecision).
839
+ const jsonDiagnostics = (result.metadata?.diagnostics ?? []).map((d) => ({
840
+ analyzer: d.analyzerName ?? d.analyzer ?? '',
841
+ kind: d.kind,
842
+ message: d.message,
843
+ ...(d.file ? { file: d.file } : {}),
844
+ ...(typeof d.line === 'number' ? { line: d.line } : {}),
845
+ ...(d.details ? { details: d.details } : {}),
846
+ }));
847
+ const jsonOutput = { violations: jsonViolations, diagnostics: jsonDiagnostics };
848
+ process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
849
+ }
850
+ else if (!options.quiet || violations.length > 0) {
851
+ // Console output — Spec 45 A2: agent-facing output emits counts (per
852
+ // analyzer/rule/severity, plus the before/after gate figure) alongside
853
+ // findings. The hook and the `changed` command are the agent's surface.
854
+ console.log(chalk.blue('🔍 Diff-Scoped Code Audit'));
855
+ console.log(chalk.gray('══════════════════════════════════════════════════'));
856
+ printFileAccounting(result, !!options.explainSkipped);
857
+ if (violations.length > 0) {
858
+ console.log('');
859
+ printCountSummary(violations);
860
+ console.log(chalk.bold(`gate before/after: ${violations.length} → ${blocking.length} blocking`));
861
+ if (dismissedCount > 0) {
862
+ console.log(chalk.dim(` ${dismissedCount} dismissed — still counted above, excluded from the gate`));
863
+ }
864
+ console.log(chalk.gray('── Violations ────────────────────────────────────'));
865
+ for (const v of violations) {
866
+ const icon = v.severity === 'critical' ? '🔴' :
867
+ v.severity === 'severe' ? '🟠' : '🟡';
868
+ const statusTag = v.new === false
869
+ ? chalk.dim(' [known — still open]')
870
+ : '';
871
+ const dismissedTag = v.dismissed
872
+ ? chalk.dim(' [dismissed]')
873
+ : '';
874
+ console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.severity}] ${v.message}${statusTag}${dismissedTag}`);
875
+ }
876
+ }
877
+ else {
878
+ console.log(chalk.green('\n✓ No readings.'));
879
+ }
880
+ }
881
+ }
752
882
  // Spec 45 A2 — counts are emitted where useful (per analyzer, per rule, per
753
883
  // severity), on agent-facing surfaces as well as human ones. This reverts Spec
754
884
  // 36 R3's "no bare counts": an agent now sees the shape of what fired instead
@@ -788,11 +918,61 @@ function printCountSummary(violations) {
788
918
  */
789
919
  async function buildChangedResultFromDiagnostics(violations, projectRoot) {
790
920
  const analyzerResults = {};
921
+ const byAnalyzer = {};
922
+ let totalViolations = 0;
923
+ let criticalIssues = 0;
924
+ let severe = 0;
925
+ let high = 0;
926
+ let dismissed = 0;
927
+ const violationsByCategory = {};
791
928
  for (const v of violations) {
792
929
  const key = v.analyzer || 'unknown';
793
930
  (analyzerResults[key] = analyzerResults[key] || { violations: [] }).violations.push(v);
931
+ totalViolations++;
932
+ switch (v.severity) {
933
+ case 'critical':
934
+ criticalIssues++;
935
+ break;
936
+ case 'severe':
937
+ severe++;
938
+ break;
939
+ case 'high':
940
+ high++;
941
+ break;
942
+ }
943
+ const rule = v.rule || 'unknown';
944
+ violationsByCategory[rule] = (violationsByCategory[rule] ?? 0) + 1;
945
+ const bucket = byAnalyzer[key] ?? (byAnalyzer[key] = { violations: 0 });
946
+ bucket.violations++;
947
+ if (v.dismissed)
948
+ dismissed++;
794
949
  }
795
- return { analyzerResults, metadata: { configUsed: undefined, diagnostics: [] } };
950
+ const topIssues = Object.entries(violationsByCategory)
951
+ .sort(([, a], [, b]) => b - a)
952
+ .slice(0, 5)
953
+ .map(([type, count]) => ({ type, count }));
954
+ // A *real* summary derived from the diagnostics the daemon served — not a
955
+ // zero-filled placeholder (a stub satisfying the type, with a `dismissed: 0`
956
+ // that was never measured). Every field here is computed from the actual
957
+ // findings: severity/category/byAnalyzer rollups, plus `dismissed` from each
958
+ // diagnostic's own `dismissed` flag. `totalFiles` and the per-analyzer
959
+ // `filesProcessed`/`fatalErrors`/`unprovenSites` are not derivable from a flat
960
+ // per-file diagnostic list, so they are omitted; the guarded read
961
+ // (`result.summary?.dismissed ?? 0`) tolerates their absence.
962
+ return {
963
+ analyzerResults,
964
+ summary: {
965
+ totalViolations,
966
+ criticalIssues,
967
+ severe,
968
+ high,
969
+ violationsByCategory,
970
+ topIssues,
971
+ byAnalyzer,
972
+ dismissed,
973
+ },
974
+ metadata: { configUsed: undefined, diagnostics: [] },
975
+ };
796
976
  }
797
977
  // Self-audit gate (Spec 33 Item 15 + Spec 44 remediation). Runs the full
798
978
  // analyzer pipeline over the tool's own production source and asserts zero
@@ -828,7 +1008,7 @@ program
828
1008
  .description('Audit the tool\'s own analyzers/ + languages/ source and fail on blocking readings')
829
1009
  .option('--json', 'Output violations as machine-readable JSON to stdout')
830
1010
  .option('--stdin', 'Read file paths from stdin (one per line)')
831
- .option('-p, --path <projectPath>', 'Project root path', process.cwd())
1011
+ .option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
832
1012
  .option('--fail-on <severity>', 'Blocking severity floor: critical, severe, or high', 'high')
833
1013
  .action(async (paths, options) => {
834
1014
  try {
@@ -867,7 +1047,6 @@ program
867
1047
  const runner = createAuditRunner({
868
1048
  projectRoot: options.path,
869
1049
  scope,
870
- analyzerConcurrency: 4
871
1050
  });
872
1051
  const result = await runner.run();
873
1052
  const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
@@ -912,7 +1091,7 @@ program
912
1091
  for (const v of blocking) {
913
1092
  const icon = v.severity === 'critical' ? '🔴' :
914
1093
  v.severity === 'severe' ? '🟠' : '🟡';
915
- console.log(`${icon} ${v.file}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.message}`);
1094
+ console.log(`${icon} ${v.file}${lineSuffix(v.line)} [${v.severity}] ${v.message}`);
916
1095
  }
917
1096
  }
918
1097
  else {
@@ -938,8 +1117,8 @@ program
938
1117
  program
939
1118
  .command('next-file')
940
1119
  .description('Audit and return the highest-priority file with readings (file-by-file refactor loop)')
941
- .option('-p, --path <projectPath>', 'Project root path', process.cwd())
942
- .option('-c, --config <config>', 'Configuration name')
1120
+ .option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
1121
+ .option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
943
1122
  .option('--json', 'Output a single JSON object (or {done:true}) to stdout')
944
1123
  .action(async (options) => {
945
1124
  try {
@@ -1037,7 +1216,7 @@ function printNextFile(violations, summary, projectDir, json) {
1037
1216
  console.log(chalk.gray('\n── Readings ────────────────────────────────────────'));
1038
1217
  for (const v of ordered) {
1039
1218
  const icon = v.severity === 'critical' ? '🔴' : v.severity === 'severe' ? '🟠' : '🟡';
1040
- console.log(`${icon} ${chalk.bold(relativize(v.file || ''))}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.rule} — ${v.message}`);
1219
+ console.log(`${icon} ${chalk.bold(relativize(v.file || ''))}${lineSuffix(v.line)} [${v.severity}] ${v.rule} — ${v.message}`);
1041
1220
  }
1042
1221
  }
1043
1222
  }
@@ -1045,8 +1224,8 @@ function printNextFile(violations, summary, projectDir, json) {
1045
1224
  program
1046
1225
  .command('baseline')
1047
1226
  .description('Snapshot current advisory readings as the baseline (excludes invariants)')
1048
- .option('-p, --path <path>', 'Project path', process.cwd())
1049
- .option('--json', 'Output as JSON')
1227
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1228
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1050
1229
  .action(async (options) => {
1051
1230
  try {
1052
1231
  await initParsers();
@@ -1112,9 +1291,9 @@ program
1112
1291
  .command('dismiss <fingerprint>')
1113
1292
  .description('Dismiss one finding by fingerprint — a written reason is required')
1114
1293
  .requiredOption('--reason <reason>', 'Why this finding is dismissed (required — a dismissal without a reason is a config error)')
1115
- .option('-p, --path <path>', 'Project path', process.cwd())
1294
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1116
1295
  .option('--telemetry-endpoint <url>', 'Feedback service endpoint override (defaults to the opt-in endpoint recorded by the telemetry tool)')
1117
- .option('--json', 'Output as JSON')
1296
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1118
1297
  .action(async (fingerprintArg, options) => {
1119
1298
  try {
1120
1299
  const reason = (options.reason ?? '').trim();
@@ -1198,8 +1377,9 @@ program
1198
1377
  .description('Generate a .codeauditor.json scaffold with invariant rules')
1199
1378
  .option('-o, --output <dir>', 'Output directory', '.')
1200
1379
  .option('-i, --interactive', 'Interactive rule builder')
1201
- .option('-f, --force', 'Force overwrite existing file without confirmation')
1202
- .option('-y, --yes', 'Skip confirmation prompts (same as --force)')
1380
+ .option('--overwrite', 'Overwrite an existing .codeauditor.json without confirmation')
1381
+ .option('-f, --force', 'Alias for --overwrite')
1382
+ .option('-y, --yes', 'Alias for --overwrite')
1203
1383
  .action(async (options) => {
1204
1384
  console.log(chalk.blue('🛠️ Code Auditor Config Generator'));
1205
1385
  console.log(chalk.gray('════════════════════════════════════════════════════'));
@@ -1225,7 +1405,7 @@ indexCmd
1225
1405
  .command('sync')
1226
1406
  .description('Synchronize index from source files')
1227
1407
  .option('--path <path>', 'Path to a specific file or directory', process.cwd())
1228
- .option('--json', 'Output as JSON')
1408
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1229
1409
  .action(async (options) => {
1230
1410
  try {
1231
1411
  await initParsers();
@@ -1266,64 +1446,65 @@ indexCmd
1266
1446
  process.exit(1);
1267
1447
  }
1268
1448
  });
1269
- indexCmd
1270
- .command('cleanup')
1271
- .description('Remove index entries for deleted files')
1272
- .option('--json', 'Output as JSON')
1273
- .action(async (options) => {
1274
- try {
1275
- const db = CodeIndexDB.getInstance();
1276
- await db.initialize();
1277
- const result = await db.bulkCleanup();
1278
- if (options.json) {
1279
- process.stdout.write(JSON.stringify({ mode: 'cleanup', success: true, ...result }) + '\n');
1449
+ /** Register a simple `index` subcommand that runs one mutation on the
1450
+ * initialized index and prints the outcome (JSON or console). `cleanup` and
1451
+ * `reset` share the get-instance/initialize/json-vs-console/try-catch
1452
+ * scaffolding; only the mutation and its result prose differ, so they live
1453
+ * here once and each command is a thin `defineIndexMutationCommand(...)` call. */
1454
+ function defineIndexMutationCommand(name, description, work) {
1455
+ indexCmd
1456
+ .command(name)
1457
+ .description(description)
1458
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1459
+ .action(async (options) => {
1460
+ try {
1461
+ const db = CodeIndexDB.getInstance();
1462
+ await db.initialize();
1463
+ const { extra, greenLine, grayLine } = await work(db);
1464
+ if (options.json) {
1465
+ process.stdout.write(JSON.stringify({ mode: name, success: true, ...extra }) + '\n');
1466
+ }
1467
+ else {
1468
+ console.log(chalk.green(greenLine));
1469
+ console.log(chalk.gray(grayLine));
1470
+ }
1280
1471
  }
1281
- else {
1282
- console.log(chalk.green('✓ Cleanup complete'));
1283
- console.log(chalk.gray(`Removed ${result.removedCount} entries from ${result.removedFiles.length} deleted files`));
1472
+ catch (error) {
1473
+ console.error(chalk.red('Error:'), error);
1474
+ process.exit(1);
1284
1475
  }
1285
- }
1286
- catch (error) {
1287
- console.error(chalk.red('Error:'), error);
1288
- process.exit(1);
1289
- }
1476
+ });
1477
+ }
1478
+ defineIndexMutationCommand('cleanup', 'Remove index entries for deleted files', async (db) => {
1479
+ const result = await db.bulkCleanup();
1480
+ return {
1481
+ extra: result,
1482
+ greenLine: '✓ Cleanup complete',
1483
+ grayLine: `Removed ${result.removedCount} entries from ${result.removedFiles.length} deleted files`,
1484
+ };
1290
1485
  });
1291
- indexCmd
1292
- .command('reset')
1293
- .description('Clear all analysis data (preserves tasks, config, whitelist)')
1294
- .option('--json', 'Output as JSON')
1295
- .action(async (options) => {
1296
- try {
1297
- const db = CodeIndexDB.getInstance();
1298
- await db.initialize();
1299
- await db.clearIndex();
1300
- if (options.json) {
1301
- process.stdout.write(JSON.stringify({ mode: 'reset', success: true, message: 'All analysis data cleared' }) + '\n');
1302
- }
1303
- else {
1304
- console.log(chalk.green('✓ Index reset complete'));
1305
- console.log(chalk.gray('All analysis-derived data cleared (tasks, config, and whitelist preserved)'));
1306
- }
1307
- }
1308
- catch (error) {
1309
- console.error(chalk.red('Error:'), error);
1310
- process.exit(1);
1311
- }
1486
+ defineIndexMutationCommand('reset', 'Clear all analysis data (preserves tasks, config, whitelist)', async (db) => {
1487
+ await db.clearIndex();
1488
+ return {
1489
+ extra: { message: 'All analysis data cleared' },
1490
+ greenLine: '✓ Index reset complete',
1491
+ grayLine: 'All analysis-derived data cleared (tasks, config, and whitelist preserved)',
1492
+ };
1312
1493
  });
1313
1494
  // ── Index status command (Spec 14 R1) ──────────────────────────
1314
1495
  indexCmd
1315
1496
  .command('status')
1316
1497
  .description('Show index health and graph statistics')
1317
- .option('--json', 'Output as JSON')
1498
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1318
1499
  .action(async (options) => {
1319
1500
  try {
1320
1501
  const db = CodeIndexDB.getInstance();
1321
1502
  await db.initialize();
1322
- const stats = await db.getStats();
1503
+ const stats = await db.functionIndex.getStats();
1323
1504
  // Graph stats — non-fatal if graph cache is empty
1324
1505
  let graphStats = null;
1325
1506
  try {
1326
- graphStats = db.getGraphStats();
1507
+ graphStats = db.graph.getGraphStats();
1327
1508
  }
1328
1509
  catch {
1329
1510
  // No graph data yet — ok
@@ -1394,7 +1575,7 @@ configCmd
1394
1575
  .command('rules-list')
1395
1576
  .description('List all configured invariant rules from .codeauditor.json')
1396
1577
  .option('--config-path <path>', 'Path to .codeauditor.json')
1397
- .option('--json', 'Output as JSON')
1578
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1398
1579
  .action(async (options) => {
1399
1580
  try {
1400
1581
  const pathModule = await import('path');
@@ -1404,7 +1585,9 @@ configCmd
1404
1585
  try {
1405
1586
  const raw = readFileSync(configPath, 'utf-8');
1406
1587
  const config = JSON.parse(raw);
1407
- const rules = config?.rules ?? [];
1588
+ // `rules` is an array by schema; tolerate the `{}` shorthand some fixtures
1589
+ // and hand-edited configs use for "no rules" — `.map` on `{}` would throw.
1590
+ const rules = Array.isArray(config?.rules) ? config.rules : [];
1408
1591
  if (options.json) {
1409
1592
  process.stdout.write(JSON.stringify({
1410
1593
  rules: rules.map((r) => ({ id: r.id, kind: r.kind, severity: r.severity, message: r.message || null })),
@@ -1442,7 +1625,7 @@ configCmd
1442
1625
  .command('rules-check')
1443
1626
  .description('Validate the current .codeauditor.json rules')
1444
1627
  .option('--config-path <path>', 'Path to .codeauditor.json')
1445
- .option('--json', 'Output as JSON')
1628
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1446
1629
  .action(async (options) => {
1447
1630
  try {
1448
1631
  const pathModule = await import('path');
@@ -1490,9 +1673,9 @@ configCmd
1490
1673
  configCmd
1491
1674
  .command('profiles')
1492
1675
  .description('Show active path profiles and resolve per-file matching')
1493
- .option('-p, --path <path>', 'Project path', process.cwd())
1676
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1494
1677
  .option('--file <file>', 'Resolve profiles for a specific file')
1495
- .option('--json', 'Output as JSON')
1678
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1496
1679
  .action(async (options) => {
1497
1680
  try {
1498
1681
  const fs = await import('fs/promises');
@@ -1590,8 +1773,8 @@ configCmd
1590
1773
  configCmd
1591
1774
  .command('detection')
1592
1775
  .description('Show DB-provenanced and inferred receiver identifiers with per-entry evidence')
1593
- .option('-p, --path <path>', 'Project path', process.cwd())
1594
- .option('--json', 'Output as JSON')
1776
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1777
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1595
1778
  .option('--mode <mode>', 'Detection mode filter (hybrid, provenance, names)')
1596
1779
  .action(async (options) => {
1597
1780
  try {
@@ -1643,9 +1826,7 @@ configCmd
1643
1826
  // Build provenance context using schema analyzer config (most complete)
1644
1827
  const ctx = buildProvenanceContext(ast, adapter, sourceCode, {
1645
1828
  mode: detectionMode,
1646
- dbReceiverNames: schemaCfg.dbReceiverNames ?? daCfg.dbReceiverNames,
1647
1829
  dbBindingNames: schemaCfg.dbBindingNames ?? daCfg.dbBindingNames,
1648
- dbCallMethods: schemaCfg.dbCallMethods ?? daCfg.dbCallMethods,
1649
1830
  });
1650
1831
  // Collect DB-provenanced
1651
1832
  for (const [id, evidence] of ctx.dbProvenanced) {
@@ -1698,70 +1879,84 @@ configCmd
1698
1879
  entry.validatorProvenanced.push(v);
1699
1880
  }
1700
1881
  for (const [file, data] of byFile) {
1701
- db.storeFileProvenance(file, data);
1882
+ db.provenance.storeFileProvenance(file, data);
1702
1883
  }
1703
1884
  // Store inferred receivers
1704
- db.storeInferredReceivers(inferred);
1885
+ db.inferredReceivers.storeInferredReceivers(inferred);
1705
1886
  }
1706
1887
  catch {
1707
1888
  // Index not available — skip persistence
1708
1889
  }
1709
- if (options.json) {
1710
- process.stdout.write(JSON.stringify({
1711
- mode: detectionMode,
1712
- projectRoot,
1713
- filesScanned: files.slice(0, 500).length,
1714
- dbProvenanced: provenanced,
1715
- validatorProvenanced: validatorIdentifiers,
1716
- inferred,
1717
- }, null, 2) + '\n');
1718
- }
1719
- else {
1720
- console.log(chalk.bold(`Detection mode: ${chalk.cyan(detectionMode)}`));
1721
- console.log(chalk.gray(`Scanned ${files.slice(0, 500).length} files`));
1722
- console.log();
1723
- if (provenanced.length > 0) {
1724
- console.log(chalk.bold('DB-Provenanced Identifiers:'));
1725
- for (const p of provenanced) {
1726
- const reasonColor = p.reason === 'fallback'
1727
- ? chalk.yellow
1728
- : p.reason === 'package'
1729
- ? chalk.green
1730
- : chalk.cyan;
1731
- console.log(` ${chalk.white(p.identifier)} ${chalk.gray(`(${p.file})`)}`);
1732
- console.log(` ${reasonColor(`reason: ${p.reason}`)} — ${chalk.gray(p.source)}`);
1733
- if (p.chain) {
1734
- console.log(` ${chalk.gray(`chain: ${p.chain.join(' → ')}`)}`);
1735
- }
1736
- }
1737
- console.log();
1738
- }
1739
- if (inferred.length > 0) {
1740
- console.log(chalk.bold('Inferred Receivers:'));
1741
- for (const i of inferred) {
1742
- console.log(` ${chalk.white(i.identifier)} ${chalk.gray(`(${i.file})`)}`);
1743
- console.log(` ${chalk.magenta(i.reason)}`);
1744
- }
1745
- console.log();
1746
- }
1747
- if (validatorIdentifiers.length > 0) {
1748
- console.log(chalk.bold('Validator-Provenanced Identifiers:'));
1749
- for (const v of validatorIdentifiers) {
1750
- console.log(` ${chalk.white(v.identifier)} ${chalk.gray(`(${v.file})`)}`);
1751
- console.log(` ${chalk.green(`reason: ${v.reason}`)} — ${chalk.gray(v.source)}`);
1752
- }
1753
- console.log();
1754
- }
1755
- if (provenanced.length === 0 && inferred.length === 0 && validatorIdentifiers.length === 0) {
1756
- console.log(chalk.gray('No DB-provenanced, inferred, or validator-provenanced identifiers found.'));
1757
- }
1758
- }
1890
+ printDetectionResults({
1891
+ json: options.json,
1892
+ mode: detectionMode,
1893
+ projectRoot,
1894
+ filesScanned: files.slice(0, 500).length,
1895
+ dbProvenanced: provenanced,
1896
+ validatorProvenanced: validatorIdentifiers,
1897
+ inferred,
1898
+ });
1759
1899
  }
1760
1900
  catch (error) {
1761
1901
  console.error(chalk.red('Error:'), error);
1762
1902
  process.exit(1);
1763
1903
  }
1764
1904
  });
1905
+ // Renders the `config detection` results — pulled out of the `.action` handler so
1906
+ // the scan/persist logic stays short and the two output shapes (JSON vs human) are
1907
+ // isolated from the provenance collection.
1908
+ function printDetectionResults(params) {
1909
+ const { json, mode, projectRoot, filesScanned, dbProvenanced, validatorProvenanced, inferred } = params;
1910
+ if (json) {
1911
+ process.stdout.write(JSON.stringify({
1912
+ mode,
1913
+ projectRoot,
1914
+ filesScanned,
1915
+ dbProvenanced,
1916
+ validatorProvenanced,
1917
+ inferred,
1918
+ }, null, 2) + '\n');
1919
+ return;
1920
+ }
1921
+ console.log(chalk.bold(`Detection mode: ${chalk.cyan(mode)}`));
1922
+ console.log(chalk.gray(`Scanned ${filesScanned} files`));
1923
+ console.log();
1924
+ if (dbProvenanced.length > 0) {
1925
+ console.log(chalk.bold('DB-Provenanced Identifiers:'));
1926
+ for (const p of dbProvenanced) {
1927
+ const reasonColor = p.reason === 'fallback'
1928
+ ? chalk.yellow
1929
+ : p.reason === 'package'
1930
+ ? chalk.green
1931
+ : chalk.cyan;
1932
+ console.log(` ${chalk.white(p.identifier)} ${chalk.gray(`(${p.file})`)}`);
1933
+ console.log(` ${reasonColor(`reason: ${p.reason}`)} — ${chalk.gray(p.source)}`);
1934
+ if (p.chain) {
1935
+ console.log(` ${chalk.gray(`chain: ${p.chain.join(' → ')}`)}`);
1936
+ }
1937
+ }
1938
+ console.log();
1939
+ }
1940
+ if (inferred.length > 0) {
1941
+ console.log(chalk.bold('Inferred Receivers:'));
1942
+ for (const i of inferred) {
1943
+ console.log(` ${chalk.white(i.identifier)} ${chalk.gray(`(${i.file})`)}`);
1944
+ console.log(` ${chalk.magenta(i.reason)}`);
1945
+ }
1946
+ console.log();
1947
+ }
1948
+ if (validatorProvenanced.length > 0) {
1949
+ console.log(chalk.bold('Validator-Provenanced Identifiers:'));
1950
+ for (const v of validatorProvenanced) {
1951
+ console.log(` ${chalk.white(v.identifier)} ${chalk.gray(`(${v.file})`)}`);
1952
+ console.log(` ${chalk.green(`reason: ${v.reason}`)} — ${chalk.gray(v.source)}`);
1953
+ }
1954
+ console.log();
1955
+ }
1956
+ if (dbProvenanced.length === 0 && inferred.length === 0 && validatorProvenanced.length === 0) {
1957
+ console.log(chalk.gray('No DB-provenanced, inferred, or validator-provenanced identifiers found.'));
1958
+ }
1959
+ }
1765
1960
  // Search command
1766
1961
  program
1767
1962
  .command('search <query>')
@@ -1795,6 +1990,7 @@ program
1795
1990
  .option('--no-unused-imports', 'Hide unused import warnings')
1796
1991
  .option('--min-complexity <threshold>', 'Minimum complexity threshold for warnings', '7')
1797
1992
  .option('-o, --output <file>', 'Save output to file instead of displaying')
1993
+ .option('--overwrite', 'Overwrite an existing output file (default: refuse)')
1798
1994
  .action(async (options) => {
1799
1995
  console.log(chalk.blue('🗺️ Codebase Map Generator'));
1800
1996
  console.log(chalk.gray('════════════════════════════════════════════════════'));
@@ -1828,7 +2024,7 @@ tasksCmd
1828
2024
  .option('--priority <priority>', 'Filter by priority (low, medium, high)')
1829
2025
  .option('--label <label>', 'Filter by label')
1830
2026
  .option('--limit <limit>', 'Result limit', '50')
1831
- .option('--json', 'Output as JSON')
2027
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1832
2028
  .action(async (options) => {
1833
2029
  try {
1834
2030
  const args = { action: 'list' };
@@ -1863,7 +2059,7 @@ tasksCmd
1863
2059
  }
1864
2060
  }
1865
2061
  else {
1866
- console.error(chalk.red(result.error || 'Unknown error'));
2062
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
1867
2063
  process.exit(1);
1868
2064
  }
1869
2065
  }
@@ -1880,7 +2076,7 @@ tasksCmd
1880
2076
  .option('--priority <priority>', 'Priority (low, medium, high)')
1881
2077
  .option('--status <status>', 'Status (open, in_progress)')
1882
2078
  .option('--labels <labels>', 'Comma-separated labels')
1883
- .option('--json', 'Output as JSON')
2079
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1884
2080
  .action(async (options) => {
1885
2081
  try {
1886
2082
  const args = {
@@ -1905,7 +2101,7 @@ tasksCmd
1905
2101
  console.log(chalk.green(`✓ Task created: ${task.title || task.taskId || 'unknown'}`));
1906
2102
  }
1907
2103
  else {
1908
- console.error(chalk.red(result.error || 'Unknown error'));
2104
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
1909
2105
  process.exit(1);
1910
2106
  }
1911
2107
  }
@@ -1917,7 +2113,7 @@ tasksCmd
1917
2113
  tasksCmd
1918
2114
  .command('get <taskId>')
1919
2115
  .description('Get a task by ID')
1920
- .option('--json', 'Output as JSON')
2116
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1921
2117
  .action(async (taskId, options) => {
1922
2118
  try {
1923
2119
  const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
@@ -1943,7 +2139,7 @@ tasksCmd
1943
2139
  console.log(chalk.dim(`\nfingerprint: ${t.fingerprint}`));
1944
2140
  }
1945
2141
  else {
1946
- console.error(chalk.red(result.error || 'Unknown error'));
2142
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
1947
2143
  process.exit(1);
1948
2144
  }
1949
2145
  }
@@ -1960,7 +2156,7 @@ tasksCmd
1960
2156
  .option('--status <status>', 'New status (open, in_progress, completed)')
1961
2157
  .option('--priority <priority>', 'New priority (low, medium, high)')
1962
2158
  .option('--labels <labels>', 'Comma-separated labels')
1963
- .option('--json', 'Output as JSON')
2159
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1964
2160
  .action(async (taskId, options) => {
1965
2161
  try {
1966
2162
  const patch = {};
@@ -1987,7 +2183,7 @@ tasksCmd
1987
2183
  console.log(chalk.green(`✓ Task updated`));
1988
2184
  }
1989
2185
  else {
1990
- console.error(chalk.red(result.error || 'Unknown error'));
2186
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
1991
2187
  process.exit(1);
1992
2188
  }
1993
2189
  }
@@ -1996,60 +2192,45 @@ tasksCmd
1996
2192
  process.exit(1);
1997
2193
  }
1998
2194
  });
1999
- tasksCmd
2000
- .command('complete <taskId>')
2001
- .description('Mark a task as completed')
2002
- .option('--json', 'Output as JSON')
2003
- .action(async (taskId, options) => {
2004
- try {
2005
- const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
2006
- const result = await handleProjectTasks({ action: 'complete_task', taskId });
2007
- if (options.json) {
2008
- process.stdout.write(JSON.stringify(result, null, 2) + '\n');
2009
- }
2010
- else if (result.success) {
2011
- console.log(chalk.green(`✓ Task completed`));
2195
+ /** Run a single-taskId tasks subcommand and print its result. */
2196
+ function makeTaskIdAction(action, successLabel) {
2197
+ return async (taskId, options) => {
2198
+ try {
2199
+ const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
2200
+ const result = await handleProjectTasks({ action, taskId });
2201
+ if (options.json) {
2202
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
2203
+ }
2204
+ else if (result.success) {
2205
+ console.log(chalk.green(`✓ ${successLabel}`));
2206
+ }
2207
+ else {
2208
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
2209
+ process.exit(1);
2210
+ }
2012
2211
  }
2013
- else {
2014
- console.error(chalk.red(result.error || 'Unknown error'));
2212
+ catch (error) {
2213
+ console.error(chalk.red('Error:'), error);
2015
2214
  process.exit(1);
2016
2215
  }
2017
- }
2018
- catch (error) {
2019
- console.error(chalk.red('Error:'), error);
2020
- process.exit(1);
2021
- }
2022
- });
2216
+ };
2217
+ }
2218
+ tasksCmd
2219
+ .command('complete <taskId>')
2220
+ .description('Mark a task as completed')
2221
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2222
+ .action(makeTaskIdAction('complete_task', 'Task completed'));
2023
2223
  tasksCmd
2024
2224
  .command('delete <taskId>')
2025
2225
  .description('Delete a task')
2026
- .option('--json', 'Output as JSON')
2027
- .action(async (taskId, options) => {
2028
- try {
2029
- const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
2030
- const result = await handleProjectTasks({ action: 'delete', taskId });
2031
- if (options.json) {
2032
- process.stdout.write(JSON.stringify(result, null, 2) + '\n');
2033
- }
2034
- else if (result.success) {
2035
- console.log(chalk.green(`✓ Task deleted`));
2036
- }
2037
- else {
2038
- console.error(chalk.red(result.error || 'Unknown error'));
2039
- process.exit(1);
2040
- }
2041
- }
2042
- catch (error) {
2043
- console.error(chalk.red('Error:'), error);
2044
- process.exit(1);
2045
- }
2046
- });
2226
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2227
+ .action(makeTaskIdAction('delete', 'Task deleted'));
2047
2228
  tasksCmd
2048
2229
  .command('from-audit')
2049
2230
  .description('Populate tasks from audit violations')
2050
2231
  .option('--auditJobId <id>', 'Specific audit result ID')
2051
2232
  .option('--severities <severities>', 'Comma-separated severities (critical,severe,high)', 'critical,severe,high')
2052
- .option('--json', 'Output as JSON')
2233
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2053
2234
  .action(async (options) => {
2054
2235
  try {
2055
2236
  const args = { action: 'from_audit' };
@@ -2076,7 +2257,7 @@ tasksCmd
2076
2257
  console.log(chalk.yellow(result.error));
2077
2258
  }
2078
2259
  else {
2079
- console.error(chalk.red(result.error || 'Unknown error'));
2260
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
2080
2261
  process.exit(1);
2081
2262
  }
2082
2263
  }
@@ -2092,7 +2273,7 @@ program
2092
2273
  .description('Rank files and functions by hotspot score (churn × complexity)')
2093
2274
  .option('--limit <n>', 'Max entries to show')
2094
2275
  .option('--path <dir>', 'Target directory (triggers spot churn extraction if index is empty)')
2095
- .option('--json', 'Output as JSON')
2276
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2096
2277
  .action(async (options) => {
2097
2278
  try {
2098
2279
  const db = CodeIndexDB.getInstance();
@@ -2167,8 +2348,8 @@ program
2167
2348
  .command('risk')
2168
2349
  .description('Rank functions by architectural risk (PageRank × betweenness × complexity × untested)')
2169
2350
  .option('--limit <n>', 'Max entries to show', '20')
2170
- .option('--path <dir>', 'Project path', process.cwd())
2171
- .option('--json', 'Output as JSON')
2351
+ .option('--path <dir>', OPTION_PROJECT_PATH, process.cwd())
2352
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2172
2353
  .option('--format <format>', 'Output format: dot (call-graph neighborhood of top functions)')
2173
2354
  .action(async (options) => {
2174
2355
  try {
@@ -2183,10 +2364,13 @@ program
2183
2364
  if (options.format === 'dot') {
2184
2365
  // DOT output: call-graph neighborhood of top-N risk functions
2185
2366
  const { callGraphToDot } = await import('./graph/outputFormatter.js');
2186
- // Collect IDs for top risk functions
2367
+ // Collect IDs for top risk functions — one query for the whole top-N
2368
+ // set, not one query per function (loop-query).
2187
2369
  const topIds = new Set();
2188
- for (const e of top) {
2189
- const rows = rawDb.prepare('SELECT id FROM functions WHERE name = ? AND file_path = ?').all(e.functionName, e.filePath);
2370
+ if (top.length > 0) {
2371
+ const orClause = top.map(() => '(name = ? AND file_path = ?)').join(' OR ');
2372
+ const params = top.flatMap((e) => [e.functionName, e.filePath]);
2373
+ const rows = rawDb.prepare(`SELECT id FROM functions WHERE ${orClause}`).all(...params);
2190
2374
  for (const r of rows)
2191
2375
  topIds.add(r.id);
2192
2376
  }
@@ -2243,7 +2427,7 @@ ledgerCmd
2243
2427
  .command('list')
2244
2428
  .description('List recent audit runs')
2245
2429
  .option('--limit <n>', 'Max runs to show', '20')
2246
- .option('--json', 'Output as JSON')
2430
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2247
2431
  .action(async (options) => {
2248
2432
  try {
2249
2433
  const { listRuns } = await import('./ledger.js');
@@ -2292,7 +2476,7 @@ ledgerCmd
2292
2476
  ledgerCmd
2293
2477
  .command('stats')
2294
2478
  .description('Show aggregated ledger statistics')
2295
- .option('--json', 'Output as JSON')
2479
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2296
2480
  .action(async (options) => {
2297
2481
  try {
2298
2482
  const { getLedgerStats } = await import('./ledger.js');
@@ -2325,7 +2509,7 @@ ledgerCmd
2325
2509
  .command('trends')
2326
2510
  .description('Compare consecutive full-audit runs and report new vs fixed readings per rule')
2327
2511
  .option('--since <runId>', 'Only consider runs after this run ID')
2328
- .option('--json', 'Output as JSON')
2512
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2329
2513
  .action(async (options) => {
2330
2514
  try {
2331
2515
  const { getTrends } = await import('./ledger.js');
@@ -2388,9 +2572,6 @@ ledgerCmd
2388
2572
  // `audit --detach` forks workers/auditJobRunner; `status`/`result`/`jobs` read
2389
2573
  // the persisted ledger. One job model (the ledger run), one write path — these
2390
2574
  // are thin wrappers over ledger.ts + mcpAuditJobs.ts, not a second store.
2391
- /** Full analyzer set — derives from createAuditRunner's registry so `--detach`
2392
- * produces the same per-rule coverage as a synchronous `audit`. */
2393
- const DETACHED_DEFAULT_ANALYZERS = [...ALL_ANALYZERS];
2394
2575
  /** Lease TTL used by read-path reclaim, mirroring mcpAuditJobs.jobLeaseTtlMs. */
2395
2576
  const CLI_JOB_LEASE_TTL_MS = Number(process.env.CODE_AUDITOR_JOB_LEASE_TTL_MS) || 30_000;
2396
2577
  function spec41StatusBadge(status) {
@@ -2472,12 +2653,8 @@ async function runDetachedAudit(options) {
2472
2653
  const logFd = openSync(logPath, 'a');
2473
2654
  const argsJson = JSON.stringify({
2474
2655
  path: auditPath,
2475
- ...(options.partitionStrategy && { partitionStrategy: options.partitionStrategy }),
2476
- ...(options.maxPartitions && { maxPartitions: Number(options.maxPartitions) }),
2477
- ...(options.shardTimeoutMs && { shardTimeoutMs: Number(options.shardTimeoutMs) }),
2478
2656
  });
2479
2657
  const defaultsJson = JSON.stringify({
2480
- defaultAnalyzers: DETACHED_DEFAULT_ANALYZERS,
2481
2658
  defaultMinSeverity: 'high',
2482
2659
  defaultGenerateCodeMap: false,
2483
2660
  });
@@ -2497,7 +2674,7 @@ program
2497
2674
  .command('status <jobId>')
2498
2675
  .description('Show a detached audit job status, progress, and staleness')
2499
2676
  .option('-p, --path <path>', 'Project root containing the job (default: cwd)', process.cwd())
2500
- .option('--json', 'Output as JSON')
2677
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2501
2678
  .action(async (jobId, options) => {
2502
2679
  try {
2503
2680
  const { getLedgerRun, reclaimStaleRunning, computeStaleness } = await import('./ledger.js');
@@ -2560,12 +2737,12 @@ program
2560
2737
  .option('--analyzer <analyzer>', 'Filter by analyzer')
2561
2738
  .option('--file <file>', 'Filter by file path (substring match)')
2562
2739
  .option('--severity <severity>', 'Filter by severity (critical|severe|high)')
2563
- .option('--state [state]', 'Query coverage by state (fired|clean|notApplicable|cannot-fire|unassessed); omit value for all')
2740
+ .option('--state [state]', 'Query coverage by state (fired|clean|notApplicable|cannot-fire|incomplete); omit value for all')
2564
2741
  .option('--count', 'Group findings by analyzer/rule with counts')
2565
2742
  .option('--limit <n>', 'Max findings to return (0 = unbounded)', '50')
2566
2743
  .option('--offset <n>', 'Findings offset', '0')
2567
2744
  .option('--stale', 'Force full content re-hash for staleness')
2568
- .option('--json', 'Output as JSON')
2745
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2569
2746
  .action(async (jobId, options) => {
2570
2747
  try {
2571
2748
  const { getLedgerRun, queryLedgerFindings, queryLedgerCoverage, computeStaleness } = await import('./ledger.js');
@@ -2658,7 +2835,7 @@ program
2658
2835
  .option('--prune', 'Delete all but the newest runs for this project')
2659
2836
  .option('--keep <n>', 'Runs to keep when --prune is set', '10')
2660
2837
  .option('--limit <n>', 'Runs to display (staleness computed for these)', '20')
2661
- .option('--json', 'Output as JSON')
2838
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2662
2839
  .action(async (options) => {
2663
2840
  try {
2664
2841
  const { listLedgerRuns, reclaimStaleRunning, pruneLedgerRuns, computeStaleness } = await import('./ledger.js');
@@ -2714,7 +2891,7 @@ conventionsCmd
2714
2891
  .command('list')
2715
2892
  .description('List mined conventions from the code index')
2716
2893
  .option('--domain <domain>', 'Filter by domain: usage-pair, import-form, error-handling, export-shape, naming')
2717
- .option('--json', 'Output as JSON')
2894
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2718
2895
  .action(async (options) => {
2719
2896
  try {
2720
2897
  const db = CodeIndexDB.getInstance();
@@ -2778,7 +2955,7 @@ conventionsCmd
2778
2955
  .command('propose')
2779
2956
  .description('Emit ready-to-paste .codeauditor.json rules from mined conventions')
2780
2957
  .option('--domain <domain>', 'Filter by domain: naming or import-form (other domains do not map to rule kinds)')
2781
- .option('--json', 'Output as JSON')
2958
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2782
2959
  .action(async (options) => {
2783
2960
  try {
2784
2961
  const db = CodeIndexDB.getInstance();
@@ -2888,8 +3065,8 @@ program
2888
3065
  program
2889
3066
  .command('architecture')
2890
3067
  .description('Analyze import graph: community detection, directory purity, Martin instability metrics')
2891
- .option('--path <dir>', 'Project path', process.cwd())
2892
- .option('--json', 'Output as JSON')
3068
+ .option('--path <dir>', OPTION_PROJECT_PATH, process.cwd())
3069
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2893
3070
  .option('--format <format>', 'Output format: dot or mermaid (import graph colored by community)')
2894
3071
  .action(async (options) => {
2895
3072
  try {
@@ -3087,7 +3264,7 @@ coverageCmd
3087
3264
  coverageCmd
3088
3265
  .command('by-risk', { isDefault: true })
3089
3266
  .description('Show coverage rate by risk decile')
3090
- .option('--json', 'Output as JSON')
3267
+ .option('--json', OPTION_OUTPUT_AS_JSON)
3091
3268
  .action(async (options) => {
3092
3269
  try {
3093
3270
  const { generateCoverageReport } = await import('./coverage/coverageService.js');
@@ -3136,8 +3313,8 @@ coverageCmd
3136
3313
  program
3137
3314
  .command('print-config <file>')
3138
3315
  .description('Print the effective config for a file, naming the source of every value')
3139
- .option('-p, --path <path>', 'Project path', process.cwd())
3140
- .option('--json', 'Output as JSON')
3316
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
3317
+ .option('--json', OPTION_OUTPUT_AS_JSON)
3141
3318
  .option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
3142
3319
  .action(async (file, options) => {
3143
3320
  try {
@@ -3155,9 +3332,6 @@ program
3155
3332
  // normalizes the real config, so path keys compare cleanly instead of
3156
3333
  // reading as "user-set" because one side is absolute and the other is not.
3157
3334
  const pristine = { ...getDefaultConfig(), codeIndex: { ...DEFAULT_CODE_INDEX_CONFIG } };
3158
- if (pristine.outputDirectory) {
3159
- pristine.outputDirectory = pathModule.resolve(projectRoot, pristine.outputDirectory);
3160
- }
3161
3335
  pristine.includePaths = (pristine.includePaths ?? []).map((p) => pathModule.isAbsolute(p) ? p : pathModule.resolve(projectRoot, p));
3162
3336
  pristine.excludePaths = (pristine.excludePaths ?? []).map((p) => pathModule.isAbsolute(p) ? p : pathModule.resolve(projectRoot, p));
3163
3337
  // Load the project config (defaults when no config file exists).
@@ -3171,7 +3345,6 @@ program
3171
3345
  }
3172
3346
  const analyzerConfigs = (config.analyzerConfigs ?? {});
3173
3347
  const pathProfiles = (config.pathProfiles ?? []);
3174
- const enabledAnalyzers = (config.enabledAnalyzers ?? []);
3175
3348
  // Resolve --preset ids to ordered preset objects (Spec 38 R4). Unknown
3176
3349
  // ids are a hard error, not a silent skip.
3177
3350
  const presetIds = Array.isArray(options.preset) ? options.preset : (options.preset ? [options.preset] : []);
@@ -3191,7 +3364,6 @@ program
3191
3364
  projectRoot,
3192
3365
  analyzerConfigs,
3193
3366
  pathProfiles,
3194
- enabledAnalyzers,
3195
3367
  presets,
3196
3368
  lintConfig,
3197
3369
  });
@@ -3304,7 +3476,7 @@ program
3304
3476
  for (const v of sorted) {
3305
3477
  const icon = v.severity === 'critical' ? '🔴' :
3306
3478
  v.severity === 'severe' ? '🟠' : '🟡';
3307
- console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.rule || v.severity}] ${v.message}`);
3479
+ console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.rule || v.severity}] ${v.message}`);
3308
3480
  }
3309
3481
  }
3310
3482
  catch (error) {
@@ -3369,8 +3541,8 @@ const daemonCmd = program
3369
3541
  daemonCmd
3370
3542
  .command('start')
3371
3543
  .description('Start the daemon for this project (detached by default)')
3372
- .option('-p, --path <path>', 'Project root', process.cwd())
3373
- .option('-c, --config <config>', 'Configuration name')
3544
+ .option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
3545
+ .option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
3374
3546
  .option('--foreground', 'Run in the foreground (blocking; useful for debugging)')
3375
3547
  .action(async (options) => {
3376
3548
  try {
@@ -3401,7 +3573,7 @@ daemonCmd
3401
3573
  daemonCmd
3402
3574
  .command('stop')
3403
3575
  .description('Ask the daemon to shut down')
3404
- .option('-p, --path <path>', 'Project root', process.cwd())
3576
+ .option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
3405
3577
  .action(async (options) => {
3406
3578
  try {
3407
3579
  const { resolveDaemonSocketPath } = await import('./dataPaths.js');
@@ -3423,8 +3595,8 @@ daemonCmd
3423
3595
  daemonCmd
3424
3596
  .command('status')
3425
3597
  .description('Show daemon status')
3426
- .option('-p, --path <path>', 'Project root', process.cwd())
3427
- .option('--json', 'Output as JSON')
3598
+ .option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
3599
+ .option('--json', OPTION_OUTPUT_AS_JSON)
3428
3600
  .action(async (options) => {
3429
3601
  try {
3430
3602
  const { resolveDaemon } = await import('./daemon/resolve.js');
@@ -3534,6 +3706,9 @@ const SCAFFOLD_CONFIG = {
3534
3706
  async function generateConfigurations(options) {
3535
3707
  const outputDir = resolve(options.output || '.');
3536
3708
  const outputPath = join(outputDir, '.codeauditor.json');
3709
+ // One consent spelling across the CLI: `--overwrite`. `--force`/`--yes` are
3710
+ // retained as aliases for back-compat.
3711
+ const overwrite = Boolean(options.overwrite || options.force || options.yes);
3537
3712
  let config;
3538
3713
  if (options.interactive) {
3539
3714
  // --- Interactive rule builder ---
@@ -3553,7 +3728,7 @@ async function generateConfigurations(options) {
3553
3728
  config = await buildRulesInteractively();
3554
3729
  }
3555
3730
  // Confirm output directory
3556
- if (!options.force && !options.yes) {
3731
+ if (!overwrite) {
3557
3732
  const { dir } = await inquirer.prompt({
3558
3733
  dir: {
3559
3734
  type: 'input',
@@ -3598,12 +3773,12 @@ async function generateConfigurations(options) {
3598
3773
  exists = true;
3599
3774
  }
3600
3775
  catch { /* ok */ }
3601
- if (exists && !options.force && !options.yes) {
3776
+ if (exists && !overwrite) {
3602
3777
  console.log(chalk.yellow(`.codeauditor.json already exists at ${outputPath}`));
3603
- console.log(chalk.gray('Use --force or --yes to overwrite, or --interactive to build a custom config.'));
3778
+ console.log(chalk.gray('Use --overwrite to replace it, or --interactive to build a custom config.'));
3604
3779
  return;
3605
3780
  }
3606
- if (exists && (options.force || options.yes)) {
3781
+ if (exists && overwrite) {
3607
3782
  console.log(chalk.yellow('Overwriting existing .codeauditor.json...'));
3608
3783
  }
3609
3784
  config = SCAFFOLD_CONFIG;
@@ -3614,6 +3789,136 @@ async function generateConfigurations(options) {
3614
3789
  console.log(chalk.gray(' 2. Run ') + chalk.cyan('code-audit') + chalk.gray(' to enforce your rules'));
3615
3790
  console.log(chalk.gray(' 3. Use ') + chalk.cyan('code-audit changed') + chalk.gray(' in your agent hook (gating rules block on new readings)'));
3616
3791
  }
3792
+ async function promptKindSpecificFields(kind, rule) {
3793
+ switch (kind) {
3794
+ case 'import-ban': {
3795
+ const { module } = await inquirer.prompt({
3796
+ module: {
3797
+ type: 'input',
3798
+ message: 'Banned module specifier (e.g. "lodash" or "@old-lib/*"):',
3799
+ validate: (input) => input.trim() ? true : 'Module specifier is required',
3800
+ },
3801
+ });
3802
+ rule.module = module;
3803
+ const { addExcept } = await inquirer.prompt({
3804
+ addExcept: {
3805
+ type: 'confirm',
3806
+ message: 'Add exception paths (files allowed to import it)?',
3807
+ default: false,
3808
+ },
3809
+ });
3810
+ if (addExcept) {
3811
+ const { except } = await inquirer.prompt({
3812
+ except: {
3813
+ type: 'input',
3814
+ message: 'Exception globs (comma-separated, e.g. "src/migration/**"):',
3815
+ },
3816
+ });
3817
+ const exceptList = except.split(',').map((s) => s.trim()).filter(Boolean);
3818
+ if (exceptList.length > 0)
3819
+ rule.except = exceptList;
3820
+ }
3821
+ break;
3822
+ }
3823
+ case 'call-constraint': {
3824
+ const { callee } = await inquirer.prompt({
3825
+ callee: {
3826
+ type: 'input',
3827
+ message: 'Callee (function name, optionally path-qualified as "path/glob#name"):',
3828
+ validate: (input) => input.trim() ? true : 'Callee is required',
3829
+ },
3830
+ });
3831
+ rule.callee = callee;
3832
+ const { mode } = await inquirer.prompt({
3833
+ mode: {
3834
+ type: 'list',
3835
+ message: 'Restriction mode:',
3836
+ choices: [
3837
+ { name: 'Allow only specific callers (allowFrom)', value: 'allow' },
3838
+ { name: 'Deny specific callers (denyFrom)', value: 'deny' },
3839
+ ],
3840
+ },
3841
+ });
3842
+ const { paths } = await inquirer.prompt({
3843
+ paths: {
3844
+ type: 'input',
3845
+ message: `Path globs (comma-separated) for ${mode === 'allow' ? 'allowFrom' : 'denyFrom'}:`,
3846
+ validate: (input) => input.trim() ? true : 'At least one path glob is required',
3847
+ },
3848
+ });
3849
+ const pathList = paths.split(',').map((s) => s.trim()).filter(Boolean);
3850
+ if (mode === 'allow') {
3851
+ rule.allowFrom = pathList;
3852
+ }
3853
+ else {
3854
+ rule.denyFrom = pathList;
3855
+ }
3856
+ break;
3857
+ }
3858
+ case 'module-boundary': {
3859
+ const { from, to } = await inquirer.prompt({
3860
+ from: {
3861
+ type: 'input',
3862
+ message: 'From (path glob for files that must not import):',
3863
+ validate: (input) => input.trim() ? true : '"from" path glob is required',
3864
+ },
3865
+ to: {
3866
+ type: 'input',
3867
+ message: 'To (path glob for files that must not be imported):',
3868
+ validate: (input) => input.trim() ? true : '"to" path glob is required',
3869
+ },
3870
+ });
3871
+ rule.from = from;
3872
+ rule.to = to;
3873
+ break;
3874
+ }
3875
+ case 'naming': {
3876
+ const { path, exports: exportPattern } = await inquirer.prompt({
3877
+ path: {
3878
+ type: 'input',
3879
+ message: 'Path (glob for files this rule applies to):',
3880
+ validate: (input) => input.trim() ? true : 'Path glob is required',
3881
+ },
3882
+ exports: {
3883
+ type: 'input',
3884
+ message: 'Exports regex (exported symbols must match, e.g. "^use[A-Z]"):',
3885
+ validate: (input) => input.trim() ? true : 'Exports regex is required',
3886
+ },
3887
+ });
3888
+ rule.path = path;
3889
+ rule.exports = exportPattern;
3890
+ break;
3891
+ }
3892
+ case 'ast-pattern': {
3893
+ const { pattern } = await inquirer.prompt({
3894
+ pattern: {
3895
+ type: 'input',
3896
+ message: 'AST pattern (ast-grep syntax, e.g. "new Function($$$)"):',
3897
+ validate: (input) => input.trim() ? true : 'Pattern is required',
3898
+ },
3899
+ });
3900
+ rule.pattern = pattern;
3901
+ const { addLanguage } = await inquirer.prompt({
3902
+ addLanguage: {
3903
+ type: 'confirm',
3904
+ message: 'Restrict to a specific language? (default: typescript)',
3905
+ default: false,
3906
+ },
3907
+ });
3908
+ if (addLanguage) {
3909
+ const { language } = await inquirer.prompt({
3910
+ language: {
3911
+ type: 'list',
3912
+ message: 'Language:',
3913
+ choices: ['typescript', 'javascript', 'go'],
3914
+ },
3915
+ });
3916
+ rule.language = language;
3917
+ }
3918
+ break;
3919
+ }
3920
+ }
3921
+ }
3617
3922
  /**
3618
3923
  * Interactive rule builder — walks the user through adding rules one at a time.
3619
3924
  */
@@ -3672,134 +3977,7 @@ async function buildRulesInteractively() {
3672
3977
  message: common.message,
3673
3978
  };
3674
3979
  // Kind-specific fields
3675
- switch (kind) {
3676
- case 'import-ban': {
3677
- const { module } = await inquirer.prompt({
3678
- module: {
3679
- type: 'input',
3680
- message: 'Banned module specifier (e.g. "lodash" or "@old-lib/*"):',
3681
- validate: (input) => input.trim() ? true : 'Module specifier is required',
3682
- },
3683
- });
3684
- rule.module = module;
3685
- const { addExcept } = await inquirer.prompt({
3686
- addExcept: {
3687
- type: 'confirm',
3688
- message: 'Add exception paths (files allowed to import it)?',
3689
- default: false,
3690
- },
3691
- });
3692
- if (addExcept) {
3693
- const { except } = await inquirer.prompt({
3694
- except: {
3695
- type: 'input',
3696
- message: 'Exception globs (comma-separated, e.g. "src/migration/**"):',
3697
- },
3698
- });
3699
- const exceptList = except.split(',').map((s) => s.trim()).filter(Boolean);
3700
- if (exceptList.length > 0)
3701
- rule.except = exceptList;
3702
- }
3703
- break;
3704
- }
3705
- case 'call-constraint': {
3706
- const { callee } = await inquirer.prompt({
3707
- callee: {
3708
- type: 'input',
3709
- message: 'Callee (function name, optionally path-qualified as "path/glob#name"):',
3710
- validate: (input) => input.trim() ? true : 'Callee is required',
3711
- },
3712
- });
3713
- rule.callee = callee;
3714
- const { mode } = await inquirer.prompt({
3715
- mode: {
3716
- type: 'list',
3717
- message: 'Restriction mode:',
3718
- choices: [
3719
- { name: 'Allow only specific callers (allowFrom)', value: 'allow' },
3720
- { name: 'Deny specific callers (denyFrom)', value: 'deny' },
3721
- ],
3722
- },
3723
- });
3724
- const { paths } = await inquirer.prompt({
3725
- paths: {
3726
- type: 'input',
3727
- message: `Path globs (comma-separated) for ${mode === 'allow' ? 'allowFrom' : 'denyFrom'}:`,
3728
- validate: (input) => input.trim() ? true : 'At least one path glob is required',
3729
- },
3730
- });
3731
- const pathList = paths.split(',').map((s) => s.trim()).filter(Boolean);
3732
- if (mode === 'allow') {
3733
- rule.allowFrom = pathList;
3734
- }
3735
- else {
3736
- rule.denyFrom = pathList;
3737
- }
3738
- break;
3739
- }
3740
- case 'module-boundary': {
3741
- const { from, to } = await inquirer.prompt({
3742
- from: {
3743
- type: 'input',
3744
- message: 'From (path glob for files that must not import):',
3745
- validate: (input) => input.trim() ? true : '"from" path glob is required',
3746
- },
3747
- to: {
3748
- type: 'input',
3749
- message: 'To (path glob for files that must not be imported):',
3750
- validate: (input) => input.trim() ? true : '"to" path glob is required',
3751
- },
3752
- });
3753
- rule.from = from;
3754
- rule.to = to;
3755
- break;
3756
- }
3757
- case 'naming': {
3758
- const { path, exports: exportPattern } = await inquirer.prompt({
3759
- path: {
3760
- type: 'input',
3761
- message: 'Path (glob for files this rule applies to):',
3762
- validate: (input) => input.trim() ? true : 'Path glob is required',
3763
- },
3764
- exports: {
3765
- type: 'input',
3766
- message: 'Exports regex (exported symbols must match, e.g. "^use[A-Z]"):',
3767
- validate: (input) => input.trim() ? true : 'Exports regex is required',
3768
- },
3769
- });
3770
- rule.path = path;
3771
- rule.exports = exportPattern;
3772
- break;
3773
- }
3774
- case 'ast-pattern': {
3775
- const { pattern } = await inquirer.prompt({
3776
- pattern: {
3777
- type: 'input',
3778
- message: 'AST pattern (ast-grep syntax, e.g. "new Function($$$)"):',
3779
- validate: (input) => input.trim() ? true : 'Pattern is required',
3780
- },
3781
- });
3782
- rule.pattern = pattern;
3783
- const { addLanguage } = await inquirer.prompt({
3784
- addLanguage: {
3785
- type: 'confirm',
3786
- message: 'Restrict to a specific language? (default: typescript)',
3787
- default: false,
3788
- },
3789
- });
3790
- if (addLanguage) {
3791
- const { language } = await inquirer.prompt({
3792
- language: {
3793
- type: 'list',
3794
- message: 'Language:',
3795
- choices: ['typescript', 'javascript', 'go'],
3796
- },
3797
- });
3798
- rule.language = language;
3799
- }
3800
- break;
3801
- }
3802
- }
3980
+ await promptKindSpecificFields(kind, rule);
3803
3981
  rules.push(rule);
3804
3982
  console.log(chalk.green(` ✓ Added rule "${rule.id}" [${rule.kind}]`));
3805
3983
  const { cont } = await inquirer.prompt({
@@ -3877,9 +4055,22 @@ async function generateCodeMap(options) {
3877
4055
  const textOutput = mapGenerator.formatAsText(completeCodeMap, mapOptions);
3878
4056
  // Output results
3879
4057
  if (options.output) {
3880
- // Save to file
3881
- await fs.writeFile(options.output, textOutput);
3882
- console.log(chalk.green(`✓ Code map saved to: ${options.output}`));
4058
+ // Save to file. Refuse to clobber an existing file unless --overwrite is
4059
+ // passed — same guard as the report writer, so `map -o` cannot silently
4060
+ // destroy a file the developer did not consent to replacing.
4061
+ const outPath = resolve(options.output);
4062
+ let exists = false;
4063
+ try {
4064
+ await fs.access(outPath);
4065
+ exists = true;
4066
+ }
4067
+ catch { /* ok */ }
4068
+ if (exists && !options.overwrite) {
4069
+ console.error(chalk.red(`Refusing to overwrite existing file ${outPath} — pass --overwrite to replace it.`));
4070
+ process.exit(1);
4071
+ }
4072
+ await fs.writeFile(outPath, textOutput);
4073
+ console.log(chalk.green(`✓ Code map saved to: ${outPath}`));
3883
4074
  // Show summary
3884
4075
  console.log('');
3885
4076
  console.log(chalk.blue('Summary:'));
@@ -3919,7 +4110,7 @@ async function runSearch(query, options) {
3919
4110
  await db.initialize();
3920
4111
  // --definition: look up a specific symbol
3921
4112
  if (options.definition) {
3922
- const func = await db.findDefinition(query);
4113
+ const func = await db.functionIndex.findDefinition(query);
3923
4114
  if (!func) {
3924
4115
  console.log(chalk.yellow(`No definition found for "${query}"`));
3925
4116
  return;
@@ -3931,9 +4122,6 @@ async function runSearch(query, options) {
3931
4122
  console.log(chalk.blue(`\nDefinition: ${func.name}`));
3932
4123
  console.log(chalk.gray('──────────────────────────────────────────'));
3933
4124
  console.log(`${chalk.bold('File:')} ${func.filePath}${func.lineNumber ? `:${func.lineNumber}` : ''}`);
3934
- if (func.signature) {
3935
- console.log(`${chalk.bold('Signature:')} ${func.signature}`);
3936
- }
3937
4125
  if (func.purpose) {
3938
4126
  console.log(`${chalk.bold('Purpose:')} ${func.purpose}`);
3939
4127
  }
@@ -3965,7 +4153,7 @@ async function runSearch(query, options) {
3965
4153
  }
3966
4154
  searchOptions.filters.language = options.language;
3967
4155
  }
3968
- const result = await db.searchFunctions(searchOptions);
4156
+ const result = await db.search.searchFunctions(searchOptions);
3969
4157
  if (options.json) {
3970
4158
  process.stdout.write(JSON.stringify({
3971
4159
  query,
@@ -3988,9 +4176,6 @@ async function runSearch(query, options) {
3988
4176
  const lang = func.language ? chalk.dim(` [${func.language}]`) : '';
3989
4177
  const lineInfo = func.lineNumber ? chalk.dim(`:${func.lineNumber}`) : '';
3990
4178
  console.log(`${chalk.bold(func.name)}${lang} — ${chalk.green(func.filePath)}${lineInfo}`);
3991
- if (func.signature) {
3992
- console.log(chalk.dim(` ${func.signature}`));
3993
- }
3994
4179
  if (func.purpose) {
3995
4180
  console.log(chalk.dim(` ${func.purpose}`));
3996
4181
  }