code-auditor-mcp 4.1.1 → 5.0.2

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 (1087) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +381 -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 +427 -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 +348 -0
  67. package/dist/analyzers/receiverRoot.d.ts.map +1 -0
  68. package/dist/analyzers/receiverRoot.js +1032 -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 +241 -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.map +1 -1
  95. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +23 -35
  96. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
  97. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +287 -49
  98. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  99. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +2130 -512
  100. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  101. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
  102. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +2 -18
  103. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
  104. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +0 -33
  105. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  106. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +20 -143
  107. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  108. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +4 -3
  109. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
  110. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +36 -13
  111. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
  112. package/dist/analyzers/universal/UniversalSecretsAnalyzer.d.ts.map +1 -1
  113. package/dist/analyzers/universal/UniversalSecretsAnalyzer.js +6 -0
  114. package/dist/analyzers/universal/UniversalSecretsAnalyzer.js.map +1 -1
  115. package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts +15 -0
  116. package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts.map +1 -1
  117. package/dist/analyzers/universal/UniversalSecurityAnalyzer.js +25 -0
  118. package/dist/analyzers/universal/UniversalSecurityAnalyzer.js.map +1 -1
  119. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +21 -32
  120. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
  121. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +166 -196
  122. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
  123. package/dist/analyzers/universal/functionConcerns.d.ts +3 -0
  124. package/dist/analyzers/universal/functionConcerns.d.ts.map +1 -1
  125. package/dist/analyzers/universal/functionConcerns.js +4 -1
  126. package/dist/analyzers/universal/functionConcerns.js.map +1 -1
  127. package/dist/analyzers/universal/schema/codeAnalysis.d.ts +310 -69
  128. package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
  129. package/dist/analyzers/universal/schema/codeAnalysis.js +709 -383
  130. package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
  131. package/dist/analyzers/universal/schema/config.d.ts +3 -14
  132. package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
  133. package/dist/analyzers/universal/schema/config.js +3 -16
  134. package/dist/analyzers/universal/schema/config.js.map +1 -1
  135. package/dist/analyzers/universal/schema/discovery.d.ts +22 -1
  136. package/dist/analyzers/universal/schema/discovery.d.ts.map +1 -1
  137. package/dist/analyzers/universal/schema/discovery.js +36 -20
  138. package/dist/analyzers/universal/schema/discovery.js.map +1 -1
  139. package/dist/analyzers/universal/schema/jsonSchema.d.ts.map +1 -1
  140. package/dist/analyzers/universal/schema/jsonSchema.js +13 -3
  141. package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -1
  142. package/dist/analyzers/universal/schema/migrations.d.ts +187 -27
  143. package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -1
  144. package/dist/analyzers/universal/schema/migrations.js +443 -186
  145. package/dist/analyzers/universal/schema/migrations.js.map +1 -1
  146. package/dist/analyzers/universal/schema/types.d.ts +17 -3
  147. package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
  148. package/dist/analyzers/universal/schema/violations.js +1 -1
  149. package/dist/analyzers/universal/schema/violations.js.map +1 -1
  150. package/dist/analyzers/universal/styleScale.d.ts +52 -0
  151. package/dist/analyzers/universal/styleScale.d.ts.map +1 -0
  152. package/dist/analyzers/universal/styleScale.js +95 -0
  153. package/dist/analyzers/universal/styleScale.js.map +1 -0
  154. package/dist/applyDataDirEnv.js +1 -1
  155. package/dist/applyDataDirEnv.js.map +1 -1
  156. package/dist/auditRouter.d.ts +26 -30
  157. package/dist/auditRouter.d.ts.map +1 -1
  158. package/dist/auditRouter.js +373 -143
  159. package/dist/auditRouter.js.map +1 -1
  160. package/dist/auditRunner.d.ts +66 -1
  161. package/dist/auditRunner.d.ts.map +1 -1
  162. package/dist/auditRunner.js +1544 -1034
  163. package/dist/auditRunner.js.map +1 -1
  164. package/dist/baseline.d.ts +30 -3
  165. package/dist/baseline.d.ts.map +1 -1
  166. package/dist/baseline.js +70 -9
  167. package/dist/baseline.js.map +1 -1
  168. package/dist/cli.js +983 -821
  169. package/dist/cli.js.map +1 -1
  170. package/dist/codeIndex/analyzerConfig.d.ts +60 -0
  171. package/dist/codeIndex/analyzerConfig.d.ts.map +1 -0
  172. package/dist/codeIndex/analyzerConfig.js +98 -0
  173. package/dist/codeIndex/analyzerConfig.js.map +1 -0
  174. package/dist/codeIndex/auditResults.d.ts +39 -0
  175. package/dist/codeIndex/auditResults.d.ts.map +1 -0
  176. package/dist/codeIndex/auditResults.js +99 -0
  177. package/dist/codeIndex/auditResults.js.map +1 -0
  178. package/dist/codeIndex/codeMap.d.ts +59 -0
  179. package/dist/codeIndex/codeMap.d.ts.map +1 -0
  180. package/dist/codeIndex/codeMap.js +72 -0
  181. package/dist/codeIndex/codeMap.js.map +1 -0
  182. package/dist/codeIndex/conventions.d.ts +33 -0
  183. package/dist/codeIndex/conventions.d.ts.map +1 -0
  184. package/dist/codeIndex/conventions.js +84 -0
  185. package/dist/codeIndex/conventions.js.map +1 -0
  186. package/dist/codeIndex/coverage.d.ts +78 -0
  187. package/dist/codeIndex/coverage.d.ts.map +1 -0
  188. package/dist/codeIndex/coverage.js +164 -0
  189. package/dist/codeIndex/coverage.js.map +1 -0
  190. package/dist/codeIndex/dependencyGraph.d.ts +97 -0
  191. package/dist/codeIndex/dependencyGraph.d.ts.map +1 -0
  192. package/dist/codeIndex/dependencyGraph.js +241 -0
  193. package/dist/codeIndex/dependencyGraph.js.map +1 -0
  194. package/dist/codeIndex/functionIndex.d.ts +111 -0
  195. package/dist/codeIndex/functionIndex.d.ts.map +1 -0
  196. package/dist/codeIndex/functionIndex.js +306 -0
  197. package/dist/codeIndex/functionIndex.js.map +1 -0
  198. package/dist/codeIndex/inferredReceivers.d.ts +32 -0
  199. package/dist/codeIndex/inferredReceivers.d.ts.map +1 -0
  200. package/dist/codeIndex/inferredReceivers.js +41 -0
  201. package/dist/codeIndex/inferredReceivers.js.map +1 -0
  202. package/dist/codeIndex/meta.d.ts +30 -0
  203. package/dist/codeIndex/meta.d.ts.map +1 -0
  204. package/dist/codeIndex/meta.js +37 -0
  205. package/dist/codeIndex/meta.js.map +1 -0
  206. package/dist/codeIndex/migrations.d.ts +96 -0
  207. package/dist/codeIndex/migrations.d.ts.map +1 -0
  208. package/dist/codeIndex/migrations.js +1302 -0
  209. package/dist/codeIndex/migrations.js.map +1 -0
  210. package/dist/codeIndex/projectTasks.d.ts +54 -0
  211. package/dist/codeIndex/projectTasks.d.ts.map +1 -0
  212. package/dist/codeIndex/projectTasks.js +81 -0
  213. package/dist/codeIndex/projectTasks.js.map +1 -0
  214. package/dist/codeIndex/provenance.d.ts +47 -0
  215. package/dist/codeIndex/provenance.d.ts.map +1 -0
  216. package/dist/codeIndex/provenance.js +48 -0
  217. package/dist/codeIndex/provenance.js.map +1 -0
  218. package/dist/codeIndex/rawSql.d.ts +54 -0
  219. package/dist/codeIndex/rawSql.d.ts.map +1 -0
  220. package/dist/codeIndex/rawSql.js +71 -0
  221. package/dist/codeIndex/rawSql.js.map +1 -0
  222. package/dist/codeIndex/schema.d.ts +99 -0
  223. package/dist/codeIndex/schema.d.ts.map +1 -0
  224. package/dist/codeIndex/schema.js +174 -0
  225. package/dist/codeIndex/schema.js.map +1 -0
  226. package/dist/codeIndex/search.d.ts +40 -0
  227. package/dist/codeIndex/search.d.ts.map +1 -0
  228. package/dist/codeIndex/search.js +316 -0
  229. package/dist/codeIndex/search.js.map +1 -0
  230. package/dist/codeIndex/shared.d.ts +27 -0
  231. package/dist/codeIndex/shared.d.ts.map +1 -0
  232. package/dist/codeIndex/shared.js +46 -0
  233. package/dist/codeIndex/shared.js.map +1 -0
  234. package/dist/codeIndex/sqliteCollection.d.ts +81 -0
  235. package/dist/codeIndex/sqliteCollection.d.ts.map +1 -0
  236. package/dist/codeIndex/sqliteCollection.js +250 -0
  237. package/dist/codeIndex/sqliteCollection.js.map +1 -0
  238. package/dist/codeIndex/whitelist.d.ts +59 -0
  239. package/dist/codeIndex/whitelist.d.ts.map +1 -0
  240. package/dist/codeIndex/whitelist.js +188 -0
  241. package/dist/codeIndex/whitelist.js.map +1 -0
  242. package/dist/codeIndexDB.d.ts +132 -271
  243. package/dist/codeIndexDB.d.ts.map +1 -1
  244. package/dist/codeIndexDB.js +365 -2729
  245. package/dist/codeIndexDB.js.map +1 -1
  246. package/dist/codeIndexService.d.ts +87 -12
  247. package/dist/codeIndexService.d.ts.map +1 -1
  248. package/dist/codeIndexService.js +110 -34
  249. package/dist/codeIndexService.js.map +1 -1
  250. package/dist/componentScanner.d.ts +35 -0
  251. package/dist/componentScanner.d.ts.map +1 -1
  252. package/dist/componentScanner.js +114 -80
  253. package/dist/componentScanner.js.map +1 -1
  254. package/dist/config/configGeneratePath.d.ts +4 -0
  255. package/dist/config/configGeneratePath.d.ts.map +1 -1
  256. package/dist/config/configGeneratePath.js +4 -0
  257. package/dist/config/configGeneratePath.js.map +1 -1
  258. package/dist/config/configLoader.d.ts +15 -0
  259. package/dist/config/configLoader.d.ts.map +1 -1
  260. package/dist/config/configLoader.js +15 -16
  261. package/dist/config/configLoader.js.map +1 -1
  262. package/dist/config/defaults.d.ts +11 -7
  263. package/dist/config/defaults.d.ts.map +1 -1
  264. package/dist/config/defaults.js +16 -14
  265. package/dist/config/defaults.js.map +1 -1
  266. package/dist/config/effectiveConfig.d.ts +17 -2
  267. package/dist/config/effectiveConfig.d.ts.map +1 -1
  268. package/dist/config/effectiveConfig.js +32 -25
  269. package/dist/config/effectiveConfig.js.map +1 -1
  270. package/dist/config/lintConfigReader.d.ts +6 -0
  271. package/dist/config/lintConfigReader.d.ts.map +1 -1
  272. package/dist/config/lintConfigReader.js +6 -0
  273. package/dist/config/lintConfigReader.js.map +1 -1
  274. package/dist/config/staticObjectExtract.d.ts +4 -0
  275. package/dist/config/staticObjectExtract.d.ts.map +1 -1
  276. package/dist/config/staticObjectExtract.js +7 -3
  277. package/dist/config/staticObjectExtract.js.map +1 -1
  278. package/dist/config/thresholdRationales.d.ts +2 -0
  279. package/dist/config/thresholdRationales.d.ts.map +1 -1
  280. package/dist/config/thresholdRationales.js +5 -2
  281. package/dist/config/thresholdRationales.js.map +1 -1
  282. package/dist/constants.d.ts +1 -1
  283. package/dist/conventions/conventionMiner.d.ts +105 -2
  284. package/dist/conventions/conventionMiner.d.ts.map +1 -1
  285. package/dist/conventions/conventionMiner.js +334 -259
  286. package/dist/conventions/conventionMiner.js.map +1 -1
  287. package/dist/coverage/coverageService.js +4 -4
  288. package/dist/coverage/coverageService.js.map +1 -1
  289. package/dist/daemon/args.d.ts +3 -0
  290. package/dist/daemon/args.d.ts.map +1 -1
  291. package/dist/daemon/args.js +3 -0
  292. package/dist/daemon/args.js.map +1 -1
  293. package/dist/daemon/core.d.ts +33 -27
  294. package/dist/daemon/core.d.ts.map +1 -1
  295. package/dist/daemon/core.js +198 -162
  296. package/dist/daemon/core.js.map +1 -1
  297. package/dist/daemon/lspServer.d.ts +8 -0
  298. package/dist/daemon/lspServer.d.ts.map +1 -1
  299. package/dist/daemon/lspServer.js +8 -0
  300. package/dist/daemon/lspServer.js.map +1 -1
  301. package/dist/daemon/main.d.ts +2 -0
  302. package/dist/daemon/main.d.ts.map +1 -1
  303. package/dist/daemon/main.js +22 -4
  304. package/dist/daemon/main.js.map +1 -1
  305. package/dist/daemon/resolve.d.ts +18 -3
  306. package/dist/daemon/resolve.d.ts.map +1 -1
  307. package/dist/daemon/resolve.js +25 -4
  308. package/dist/daemon/resolve.js.map +1 -1
  309. package/dist/daemon/socketClient.d.ts +10 -1
  310. package/dist/daemon/socketClient.d.ts.map +1 -1
  311. package/dist/daemon/socketClient.js +10 -1
  312. package/dist/daemon/socketClient.js.map +1 -1
  313. package/dist/daemon/socketServer.d.ts +13 -0
  314. package/dist/daemon/socketServer.d.ts.map +1 -1
  315. package/dist/daemon/socketServer.js +13 -0
  316. package/dist/daemon/socketServer.js.map +1 -1
  317. package/dist/dataPaths.d.ts +17 -8
  318. package/dist/dataPaths.d.ts.map +1 -1
  319. package/dist/dataPaths.js +16 -44
  320. package/dist/dataPaths.js.map +1 -1
  321. package/dist/dismissals.d.ts +22 -1
  322. package/dist/dismissals.d.ts.map +1 -1
  323. package/dist/dismissals.js +22 -1
  324. package/dist/dismissals.js.map +1 -1
  325. package/dist/enforcement/gate.d.ts +1 -0
  326. package/dist/enforcement/gate.d.ts.map +1 -1
  327. package/dist/enforcement/gate.js +1 -0
  328. package/dist/enforcement/gate.js.map +1 -1
  329. package/dist/fingerprint.d.ts +6 -0
  330. package/dist/fingerprint.d.ts.map +1 -1
  331. package/dist/fingerprint.js +46 -1
  332. package/dist/fingerprint.js.map +1 -1
  333. package/dist/functionScanner.d.ts +30 -1
  334. package/dist/functionScanner.d.ts.map +1 -1
  335. package/dist/functionScanner.js +258 -256
  336. package/dist/functionScanner.js.map +1 -1
  337. package/dist/generators/AWSQConfigGenerator.d.ts +23 -0
  338. package/dist/generators/AWSQConfigGenerator.d.ts.map +1 -1
  339. package/dist/generators/AWSQConfigGenerator.js +23 -0
  340. package/dist/generators/AWSQConfigGenerator.js.map +1 -1
  341. package/dist/generators/BaseConfigGenerator.d.ts +7 -0
  342. package/dist/generators/BaseConfigGenerator.d.ts.map +1 -1
  343. package/dist/generators/BaseConfigGenerator.js +7 -0
  344. package/dist/generators/BaseConfigGenerator.js.map +1 -1
  345. package/dist/generators/ClaudeConfigGenerator.d.ts +23 -0
  346. package/dist/generators/ClaudeConfigGenerator.d.ts.map +1 -1
  347. package/dist/generators/ClaudeConfigGenerator.js +23 -0
  348. package/dist/generators/ClaudeConfigGenerator.js.map +1 -1
  349. package/dist/generators/ClineConfigGenerator.d.ts +23 -0
  350. package/dist/generators/ClineConfigGenerator.d.ts.map +1 -1
  351. package/dist/generators/ClineConfigGenerator.js +23 -0
  352. package/dist/generators/ClineConfigGenerator.js.map +1 -1
  353. package/dist/generators/CodeiumConfigGenerator.d.ts +23 -0
  354. package/dist/generators/CodeiumConfigGenerator.d.ts.map +1 -1
  355. package/dist/generators/CodeiumConfigGenerator.js +23 -0
  356. package/dist/generators/CodeiumConfigGenerator.js.map +1 -1
  357. package/dist/generators/CodexConfigGenerator.d.ts +23 -0
  358. package/dist/generators/CodexConfigGenerator.d.ts.map +1 -1
  359. package/dist/generators/CodexConfigGenerator.js +23 -0
  360. package/dist/generators/CodexConfigGenerator.js.map +1 -1
  361. package/dist/generators/ConfigGeneratorFactory.d.ts +9 -0
  362. package/dist/generators/ConfigGeneratorFactory.d.ts.map +1 -1
  363. package/dist/generators/ConfigGeneratorFactory.js +9 -0
  364. package/dist/generators/ConfigGeneratorFactory.js.map +1 -1
  365. package/dist/generators/ContinueConfigGenerator.d.ts +23 -0
  366. package/dist/generators/ContinueConfigGenerator.d.ts.map +1 -1
  367. package/dist/generators/ContinueConfigGenerator.js +23 -0
  368. package/dist/generators/ContinueConfigGenerator.js.map +1 -1
  369. package/dist/generators/CopilotConfigGenerator.d.ts +23 -0
  370. package/dist/generators/CopilotConfigGenerator.d.ts.map +1 -1
  371. package/dist/generators/CopilotConfigGenerator.js +23 -0
  372. package/dist/generators/CopilotConfigGenerator.js.map +1 -1
  373. package/dist/generators/CursorConfigGenerator.d.ts +23 -0
  374. package/dist/generators/CursorConfigGenerator.d.ts.map +1 -1
  375. package/dist/generators/CursorConfigGenerator.js +23 -0
  376. package/dist/generators/CursorConfigGenerator.js.map +1 -1
  377. package/dist/generators/GeminiConfigGenerator.d.ts +23 -0
  378. package/dist/generators/GeminiConfigGenerator.d.ts.map +1 -1
  379. package/dist/generators/GeminiConfigGenerator.js +23 -0
  380. package/dist/generators/GeminiConfigGenerator.js.map +1 -1
  381. package/dist/generators/VSCodeConfigGenerator.d.ts +23 -0
  382. package/dist/generators/VSCodeConfigGenerator.d.ts.map +1 -1
  383. package/dist/generators/VSCodeConfigGenerator.js +23 -0
  384. package/dist/generators/VSCodeConfigGenerator.js.map +1 -1
  385. package/dist/generators/ZCodeConfigGenerator.d.ts +23 -0
  386. package/dist/generators/ZCodeConfigGenerator.d.ts.map +1 -1
  387. package/dist/generators/ZCodeConfigGenerator.js +23 -0
  388. package/dist/generators/ZCodeConfigGenerator.js.map +1 -1
  389. package/dist/graph/blastRadius.d.ts.map +1 -1
  390. package/dist/graph/blastRadius.js +12 -7
  391. package/dist/graph/blastRadius.js.map +1 -1
  392. package/dist/graph/callGraph.d.ts +31 -0
  393. package/dist/graph/callGraph.d.ts.map +1 -1
  394. package/dist/graph/callGraph.js +62 -32
  395. package/dist/graph/callGraph.js.map +1 -1
  396. package/dist/graph/importClassification.d.ts +28 -0
  397. package/dist/graph/importClassification.d.ts.map +1 -1
  398. package/dist/graph/importClassification.js +57 -0
  399. package/dist/graph/importClassification.js.map +1 -1
  400. package/dist/graph/importGraph.d.ts +38 -0
  401. package/dist/graph/importGraph.d.ts.map +1 -1
  402. package/dist/graph/importGraph.js +38 -0
  403. package/dist/graph/importGraph.js.map +1 -1
  404. package/dist/graph/outputFormatter.d.ts +12 -4
  405. package/dist/graph/outputFormatter.d.ts.map +1 -1
  406. package/dist/graph/outputFormatter.js +12 -62
  407. package/dist/graph/outputFormatter.js.map +1 -1
  408. package/dist/graph/resolutionEnvironment.d.ts +61 -0
  409. package/dist/graph/resolutionEnvironment.d.ts.map +1 -0
  410. package/dist/graph/resolutionEnvironment.js +57 -0
  411. package/dist/graph/resolutionEnvironment.js.map +1 -0
  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 +827 -850
  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 +491 -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 +1030 -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 +207 -0
  739. package/dist/phase/receiverConsumers.d.ts.map +1 -0
  740. package/dist/phase/receiverConsumers.js +1063 -0
  741. package/dist/phase/receiverConsumers.js.map +1 -0
  742. package/dist/phase/receiverProvenance.d.ts +120 -0
  743. package/dist/phase/receiverProvenance.d.ts.map +1 -0
  744. package/dist/phase/receiverProvenance.js +377 -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 +2199 -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 +1144 -984
  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 +114 -71
  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 +18 -9
  1078. package/plugin/.claude-plugin/manifest.json +16 -0
  1079. package/plugin/.claude-plugin/plugin.json +1 -1
  1080. package/plugin/hooks/hooks.json +7 -3
  1081. package/plugin/scripts/hook-audit.sh +46 -16
  1082. package/plugin/scripts/hook-common.sh +81 -12
  1083. package/plugin/scripts/hook-drift-check.sh +76 -0
  1084. package/plugin/scripts/hook-self-audit.sh +22 -14
  1085. package/plugin/scripts/hook-warm.sh +5 -6
  1086. package/plugin/skills/code-auditor/SKILL.md +1 -1
  1087. package/server.json +10 -4
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,12 +98,461 @@ 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
+ const heldOutByProfile = result.summary?.heldOut ?? 0;
129
+ if (coverage && coverage.length > 0) {
130
+ const covFired = coverage.filter(c => c.state === 'fired').length;
131
+ const covClean = coverage.filter(c => c.state === 'clean').length;
132
+ const covIncomplete = coverage.filter(c => c.state === 'incomplete').length;
133
+ const covNotApplicable = coverage.filter(c => c.state === 'notApplicable').length;
134
+ const covCannotFire = coverage.filter(c => c.state === 'cannot-fire').length;
135
+ // Spec 69 §10 R4 follow-up — "clean" is not a printable verdict while any
136
+ // site is unproven (cannot-fire) or held out by a path profile: a rule that
137
+ // reports "clean" may have under-reported on access it could not observe.
138
+ const cleanSuppressed = unprovenSiteCount > 0 || heldOutByProfile > 0;
139
+ const cleanPart = cleanSuppressed ? '' : `${covClean} clean · `;
140
+ console.log(chalk.gray(`── Coverage panel ── ${covFired} fired · ${cleanPart}` +
141
+ `${covIncomplete} incomplete · ${covNotApplicable} not-applicable · ` +
142
+ `${covCannotFire} cannot-fire · ${unprovenSiteCount} unproven sites · ` +
143
+ `${heldOutByProfile} held out by profile`));
144
+ }
145
+ // ── Skipped analyzers (Spec 61) ──────────────────────────────
146
+ const skippedAnalyzers = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'go-toolchain-missing' ||
147
+ d.kind === 'go-analyzer-wrong-arch' ||
148
+ d.kind === 'go-analyzer-build-failed');
149
+ if (skippedAnalyzers.length > 0) {
150
+ console.log(chalk.yellow(`── Skipped analyzers ── ${skippedAnalyzers.length}`));
151
+ for (const d of skippedAnalyzers) {
152
+ const name = d.analyzerName || 'go';
153
+ console.log(chalk.yellow(` ${name} [${d.kind}] — ${d.message}`));
154
+ }
155
+ }
156
+ // ── Ignored config keys (Spec 61 R1.4) ─────────────────────────
157
+ const ignoredConfigKeys = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'config-key-rejected');
158
+ if (ignoredConfigKeys.length > 0) {
159
+ console.log(chalk.yellow(`── Ignored config keys ── ${ignoredConfigKeys.length}`));
160
+ for (const d of ignoredConfigKeys) {
161
+ console.log(chalk.yellow(` ${d.message}`));
162
+ }
163
+ }
164
+ // ── Cannot-fire sites (Spec 69 §10) ───────────────────────────
165
+ // The cannot-fire verdict surface is the release bar: every DB-access call
166
+ // site whose receiver could not be proven a handle must be *visible with its
167
+ // reason*, never capped or folded into a silent clean. Grouped by file so a
168
+ // reader can see exactly which files the table-reference and data-access
169
+ // rules may have under-reported on.
170
+ const allDiagnostics = result.metadata?.diagnostics ?? [];
171
+ const cannotFireSites = allDiagnostics.filter((d) => d.kind === 'cannot-fire');
172
+ if (cannotFireSites.length > 0) {
173
+ const byFile = new Map();
174
+ for (const d of cannotFireSites) {
175
+ const key = d.file ?? '(unknown)';
176
+ const entry = byFile.get(key) ?? { lines: [], reasons: new Set() };
177
+ if (typeof d.line === 'number')
178
+ entry.lines.push(d.line);
179
+ const rawReason = typeof d.details?.reason === 'string' ? d.details.reason : d.message;
180
+ const reason = sanitizeReason(rawReason);
181
+ if (reason)
182
+ entry.reasons.add(reason);
183
+ byFile.set(key, entry);
184
+ }
185
+ console.log(chalk.yellow(`── Cannot-fire sites ── ${cannotFireSites.length} unproven DB-access call sites across ${byFile.size} files`));
186
+ console.log(chalk.yellow(` Table-reference and data-access rules may report clean on access they could not observe.`));
187
+ for (const [file, entry] of [...byFile.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
188
+ const reasons = [...entry.reasons];
189
+ const shown = reasons.slice(0, 2).join(' │ ');
190
+ const more = reasons.length > 2 ? ` (+${reasons.length - 2} more reasons)` : '';
191
+ const loc = entry.lines.length === 1 ? ` (line ${entry.lines[0]})` : '';
192
+ console.log(chalk.yellow(` ${file} — ${entry.lines.length} site(s)${loc}: ${shown}${more}`));
193
+ }
194
+ }
195
+ // ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
196
+ const otherCoverageGaps = allDiagnostics.filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found');
197
+ if (otherCoverageGaps.length > 0) {
198
+ const byKind = {};
199
+ for (const d of otherCoverageGaps)
200
+ byKind[d.kind] = (byKind[d.kind] ?? 0) + 1;
201
+ const totals = Object.entries(byKind)
202
+ .map(([kind, n]) => `${n} ${kind}`)
203
+ .join(' · ');
204
+ console.log(chalk.gray(`── Coverage gaps ── ${totals}`));
205
+ for (const d of otherCoverageGaps.slice(0, 20)) {
206
+ const loc = d.file ? `${d.file}${typeof d.line === 'number' ? `:${d.line}` : ''}` : '(unknown)';
207
+ console.log(chalk.gray(` ${loc} [${d.kind}] — ${d.message}`));
208
+ }
209
+ if (otherCoverageGaps.length > 20) {
210
+ console.log(chalk.gray(` … and ${otherCoverageGaps.length - 20} more`));
211
+ }
212
+ }
213
+ // ── Spec 60 R1 — test coverage (reporting, not detection) ─────
214
+ const testCoverage = result.metadata?.testCoverage;
215
+ if (testCoverage) {
216
+ const pct = testCoverage.total > 0
217
+ ? ((testCoverage.tested / testCoverage.total) * 100).toFixed(1)
218
+ : '0.0';
219
+ let line = `tested ${testCoverage.tested}/${testCoverage.total} (${pct}%)` +
220
+ ` · untested-live ${testCoverage.untestedLive}` +
221
+ ` · untested-dead ${testCoverage.untestedDead}`;
222
+ if (testCoverage.deadDrop > 0) {
223
+ line += ` (was ${testCoverage.deadPreException} pre-exception; −${testCoverage.deadDrop} entry points exempted)`;
224
+ }
225
+ console.log(chalk.gray(`── Test coverage ── ${line}`));
226
+ }
227
+ // ── Spec 60 R2 — size distributions (median / p95 / max) ──────
228
+ const sizeDistributions = result.metadata?.sizeDistributions;
229
+ if (sizeDistributions && sizeDistributions.length > 0) {
230
+ console.log(chalk.gray(`── Size distributions ──`));
231
+ for (const d of sizeDistributions) {
232
+ const fmt = (n) => Number.isInteger(n) ? String(n) : n.toFixed(1);
233
+ const tail = d.maxEntry
234
+ ? ` (${d.maxEntry.name}${d.maxEntry.fileType ? ` · ${d.maxEntry.fileType}` : ''} · ${d.maxEntry.entityType})`
235
+ : '';
236
+ const pop = d.population ? `${d.population}, n=${d.count}` : `n=${d.count}`;
237
+ console.log(chalk.gray(` ${d.measure} [${pop}]: median ${fmt(d.median)} · p95 ${fmt(d.p95)} · max ${fmt(d.max)}${tail}`));
238
+ }
239
+ }
240
+ // ── Spec 60 R3 — dead-and-duplicated clusters ─────────────────
241
+ const deadClusters = result.metadata?.deadClusters;
242
+ if (deadClusters && deadClusters.length > 0) {
243
+ console.log(chalk.gray(`── Dead-and-duplicated ── ${deadClusters.length} clusters`));
244
+ for (const c of deadClusters.slice(0, 10)) {
245
+ console.log(chalk.gray(` ${c.count} × ${c.basename}`));
246
+ }
247
+ if (deadClusters.length > 10) {
248
+ console.log(chalk.gray(` … and ${deadClusters.length - 10} more clusters`));
249
+ }
250
+ }
251
+ }
252
+ /**
253
+ * Print the findings summary: the delta view when a baseline exists, or the
254
+ * absolute summary otherwise. Recomputes the dismissed/gating suffixes so the
255
+ * summary is self-contained.
256
+ */
257
+ function printFindingSummary(result, violations, baseline, options) {
258
+ // Spec 57 — dismissed count is reported alongside the total, never
259
+ // subtracted from it ("43 findings, 3 dismissed").
260
+ const dismissedCount = result.summary.dismissed ?? 0;
261
+ const dismissedSuffix = dismissedCount > 0 ? `, ${dismissedCount} dismissed` : '';
262
+ const gateExcludedCount = violations.filter((v) => v.gateExcluded).length;
263
+ const gatingCount = (severity) => violations.filter((v) => v.severity === severity && !v.gateExcluded && !v.dismissed).length;
264
+ // Spec 69 §10 R4 follow-up — the headline states findings, unproven sites,
265
+ // and held-out readings together. Held-out readings are named "held out by
266
+ // profile" (they still count in the total but do not block the gate).
267
+ const heldOutByProfile = result.summary?.heldOut ?? gateExcludedCount;
268
+ const unprovenSites = result.summary?.unprovenSites ?? 0;
269
+ const findingsSuffix = `${dismissedSuffix}` +
270
+ `${unprovenSites > 0 ? ` · ${unprovenSites.toLocaleString()} unproven sites` : ''}` +
271
+ `${heldOutByProfile > 0 ? ` · ${heldOutByProfile.toLocaleString()} held out by profile` : ''}`;
272
+ const summaryLine = (label, total, severity) => gateExcludedCount > 0
273
+ ? `${label}: ${total} (${gatingCount(severity)} gating)`
274
+ : `${label}: ${total}`;
275
+ // ── Delta output (Spec 18 R2) ─────────────────────────────────
276
+ if (baseline && !options.full) {
277
+ const newViolations = violations.filter((v) => v.new === true);
278
+ const knownCount = baseline.knownCount ?? 0;
279
+ const fixedCount = baseline.fixedCount ?? 0;
280
+ const previousKnown = baseline.previousKnownCount ?? 0;
281
+ const currentDebt = newViolations.length + knownCount;
282
+ const debtDelta = currentDebt - previousKnown;
283
+ const trendIcon = debtDelta > 0 ? '↑' : debtDelta < 0 ? '↓' : '→';
284
+ const trendLabel = debtDelta > 0
285
+ ? `(debt increased since last baseline)`
286
+ : debtDelta < 0
287
+ ? `(debt decreased since last baseline)`
288
+ : '(unchanged)';
289
+ console.log(`\n📊 Delta: +${newViolations.length} new · −${fixedCount} fixed · ${knownCount} known ${trendIcon} ${trendLabel}`);
290
+ if (newViolations.length > 0) {
291
+ console.log(chalk.gray(`\n── New readings (${newViolations.length}) ──────────────────────────`));
292
+ for (const v of newViolations) {
293
+ const icon = v.severity === 'critical' ? '🔴' :
294
+ v.severity === 'severe' ? '🟠' : '🟡';
295
+ console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.severity}] ${v.message}`);
296
+ }
297
+ }
298
+ else {
299
+ console.log(chalk.green('\n✓ No new readings since last baseline.'));
300
+ if (knownCount > 0) {
301
+ console.log(chalk.gray(` ${knownCount} known reading(s) are still open — recorded, not resolved.`));
302
+ }
303
+ }
304
+ // Debt by analyzer
305
+ console.log(chalk.gray(`\n── Debt by Analyzer ──────────────────────────`));
306
+ const analyzerCounts = {};
307
+ for (const v of violations) {
308
+ const a = v.analyzer || 'unknown';
309
+ if (!analyzerCounts[a])
310
+ analyzerCounts[a] = { known: 0, new: 0 };
311
+ if (v.new === false)
312
+ analyzerCounts[a].known++;
313
+ else if (v.new === true)
314
+ analyzerCounts[a].new++;
315
+ }
316
+ for (const [analyzer, counts] of Object.entries(analyzerCounts).sort()) {
317
+ const newPart = counts.new > 0 ? ` (+${counts.new})` : '';
318
+ console.log(`${analyzer}: ${counts.known.toLocaleString()} known${newPart}`);
319
+ }
320
+ // Top files
321
+ console.log(chalk.gray(`\n── Top Files ─────────────────────────────────`));
322
+ const fileCounts = new Map();
323
+ for (const v of violations) {
324
+ const f = v.file || '';
325
+ fileCounts.set(f, (fileCounts.get(f) || 0) + 1);
326
+ }
327
+ const topFiles = [...fileCounts.entries()]
328
+ .sort((a, b) => b[1] - a[1])
329
+ .slice(0, 5);
330
+ for (const [file, count] of topFiles) {
331
+ console.log(`${file} — ${count} reading${count !== 1 ? 's' : ''}`);
332
+ }
333
+ console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit --full')} to see all ${currentDebt.toLocaleString()} readings.`));
334
+ }
335
+ else if (!baseline) {
336
+ // No baseline: current behavior + hint
337
+ console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
338
+ console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
339
+ console.log(summaryLine('Severe', result.summary.severe, 'severe'));
340
+ console.log(summaryLine('High', result.summary.high, 'high'));
341
+ console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
342
+ console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit baseline')} to adopt the ratchet and track changes over time.`));
343
+ }
344
+ else {
345
+ // --full with baseline: full itemized inventory (current behavior)
346
+ console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
347
+ console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
348
+ console.log(summaryLine('Severe', result.summary.severe, 'severe'));
349
+ console.log(summaryLine('High', result.summary.high, 'high'));
350
+ console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
351
+ }
352
+ }
353
+ /**
354
+ * Print unparsed-file failures and skipped-extension notice.
355
+ */
356
+ function printUnparsedAndSkipped(result) {
357
+ // Spec 32 — unparsed files are never silent.
358
+ const unparsedFiles = result.metadata?.unparsedFiles ?? [];
359
+ if (unparsedFiles.length > 0) {
360
+ console.error(chalk.red(`\n⚠️ ${unparsedFiles.length} file${unparsedFiles.length !== 1 ? 's' : ''} failed to parse:`));
361
+ for (const u of unparsedFiles.slice(0, 20)) {
362
+ console.error(` ${u.filePath} — ${u.reason}`);
363
+ }
364
+ if (unparsedFiles.length > 20) {
365
+ console.error(` … and ${unparsedFiles.length - 20} more`);
366
+ }
367
+ }
368
+ // Spec 43 R5 follow-up — extensions present on disk that discovery skipped.
369
+ const skippedExtensions = result.metadata?.skippedExtensions ?? [];
370
+ if (skippedExtensions.length > 0) {
371
+ console.log(chalk.gray(`\n── Not analyzed (skipped extensions) ────────`));
372
+ const parts = skippedExtensions.map(s => `${s.ext} (${s.count})`);
373
+ console.log(` ${parts.join(', ')}`);
374
+ }
375
+ }
376
+ /**
377
+ * Print per-analyzer pipeline activity and the detailed coverage breakdown.
378
+ */
379
+ function printPipelineStages(result, options) {
380
+ if (options.json)
381
+ return;
382
+ const analyzerFiles = [];
383
+ for (const [name, ar] of Object.entries(result.analyzerResults)) {
384
+ const vCount = ar.violations.length;
385
+ if (isVisitorStatus(ar.status)) {
386
+ analyzerFiles.push(`${name} [visitor-ran]: ${getFilesProcessed(ar.status)} files, ${vCount} violations`);
387
+ }
388
+ else if (isReducerStatus(ar.status)) {
389
+ analyzerFiles.push(`${name} [reducer-ran]: ${getFactsConsumed(ar.status)} facts, ${vCount} violations`);
390
+ }
391
+ else {
392
+ analyzerFiles.push(`${name} [notRun]: ${vCount} violations`);
393
+ }
394
+ }
395
+ if (analyzerFiles.length > 0) {
396
+ console.log(chalk.gray(`\n── Pipeline Stages ──────────────────────────`));
397
+ console.log(analyzerFiles.join('\n'));
398
+ }
399
+ // ── Coverage (Spec 27) ──────────────────────────────────────────
400
+ const coverage = result.metadata?.coverage;
401
+ if (coverage && coverage.length > 0) {
402
+ const fired = coverage.filter(c => c.state === 'fired');
403
+ const clean = coverage.filter(c => c.state === 'clean');
404
+ const incomplete = coverage.filter(c => c.state === 'incomplete');
405
+ const notApplicable = coverage.filter(c => c.state === 'notApplicable');
406
+ const cannotFire = coverage.filter(c => c.state === 'cannot-fire');
407
+ const firedCount = fired.reduce((s, c) => s + c.count, 0);
408
+ console.log(chalk.gray(`\n── Coverage ─────────────────────────────────`));
409
+ console.log(` ${fired.length} fired (${firedCount.toLocaleString()} violations), ` +
410
+ `${clean.length} clean, ` +
411
+ `${incomplete.length} incomplete, ` +
412
+ `${notApplicable.length} notApplicable, ` +
413
+ `${cannotFire.length} cannot-fire ` +
414
+ `(${coverage.length} rules registered)`);
415
+ if (cannotFire.length > 0) {
416
+ console.log(chalk.yellow(` ── Cannot Fire (broken in the tool) ──`));
417
+ for (const c of cannotFire) {
418
+ console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
419
+ }
420
+ }
421
+ if (notApplicable.length > 0) {
422
+ console.log(chalk.gray(` ── Not Applicable ──`));
423
+ for (const c of notApplicable) {
424
+ console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
425
+ }
426
+ }
427
+ if (clean.length > 0) {
428
+ console.log(chalk.gray(` ── Clean ──`));
429
+ for (const c of clean) {
430
+ console.log(` ${c.ruleId} (0)`);
431
+ }
432
+ }
433
+ if (incomplete.length > 0) {
434
+ console.log(chalk.gray(` ── Incomplete (missing facts for some files) ──`));
435
+ for (const c of incomplete) {
436
+ console.log(` ${c.ruleId} (0)`);
437
+ }
438
+ }
439
+ }
440
+ }
441
+ /**
442
+ * Generate and write the formatted report when --format is specified.
443
+ */
444
+ async function writeReport(result, options) {
445
+ if (!options.format)
446
+ return;
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 ext = options.format === 'sarif' ? 'sarif' : options.format;
460
+ if (options.output) {
461
+ const outputDir = resolve(options.output);
462
+ const reportPath = join(outputDir, `audit-report.${ext}`);
463
+ let exists = false;
464
+ try {
465
+ await fs.access(reportPath);
466
+ exists = true;
467
+ }
468
+ catch { /* ok */ }
469
+ if (exists && !options.overwrite) {
470
+ console.error(chalk.red(`Refusing to overwrite existing report ${reportPath} — pass --overwrite to replace it.`));
471
+ process.exit(1);
472
+ }
473
+ await fs.mkdir(dirname(reportPath), { recursive: true });
474
+ await fs.writeFile(reportPath, report, 'utf-8');
475
+ console.log(chalk.green(`\nReport written to ${reportPath}`));
476
+ }
477
+ else {
478
+ // No output path — default to stdout.
479
+ process.stdout.write(report + '\n');
480
+ }
481
+ }
482
+ /**
483
+ * Apply the exit-code gates: fail-on-regression, zero-files, parse-failure,
484
+ * and --fail-on severity thresholds.
485
+ */
486
+ function applyFailOnGates(result, violations, baseline, options, failOnSeverity) {
487
+ // --fail-on-regression: compare total high debt to baseline snapshot
488
+ if (baseline && options.failOnRegression) {
489
+ const currentDebt = violations.filter((v) => v.new || v.new === false).length;
490
+ const snapshotDebt = baseline.previousKnownCount ?? 0;
491
+ if (currentDebt > snapshotDebt) {
492
+ console.error(chalk.red(`Debt regression: ${currentDebt - snapshotDebt} readings added without re-baselining.`));
493
+ process.exit(2);
494
+ }
495
+ }
496
+ // Pipeline-failure gate — the audit pipeline itself threw (schema drift,
497
+ // DB write failure, …). One error, reported once by the runner; this exits
498
+ // non-zero (broken tool) so a swallowed pipeline throw can never read as a
499
+ // clean pass.
500
+ {
501
+ const diagnostics = result.metadata?.diagnostics ?? [];
502
+ const pipelineErrors = diagnostics.filter((d) => d.kind === 'pipeline-error');
503
+ if (pipelineErrors.length > 0) {
504
+ console.error(`Pipeline failure: ${pipelineErrors[0].message}`);
505
+ process.exit(1);
506
+ }
507
+ }
508
+ // Zero-files gate — any enabled analyzer matching zero source files is a
509
+ // dark-analyzer failure; fail the run so the bug can't hide.
510
+ {
511
+ const diagnostics = result.metadata?.diagnostics ?? [];
512
+ const zeroFileWarnings = diagnostics.filter((d) => d.kind === 'zero-files');
513
+ if (zeroFileWarnings.length > 0) {
514
+ const names = zeroFileWarnings.map((d) => d.analyzerName ?? d.analyzer).join(', ');
515
+ console.error(`Zero-files failure: ${zeroFileWarnings.length} analyzer(s) matched zero source files (${names})`);
516
+ process.exit(2);
517
+ }
518
+ }
519
+ // Spec 32 — a run where any file failed to parse is incomplete and must
520
+ // not exit 0.
521
+ {
522
+ const unparsedFiles = result.metadata?.unparsedFiles ?? [];
523
+ if (unparsedFiles.length > 0) {
524
+ console.error(`Parse-failure: ${unparsedFiles.length} file(s) could not be parsed; analysis is incomplete.`);
525
+ process.exit(2);
526
+ }
527
+ }
528
+ // --fail-on: evaluate new + invariant findings only (unless --include-baseline)
529
+ if (failOnSeverity) {
530
+ const evaluableViolations = (baseline && !options.includeBaseline)
531
+ ? violations.filter((v) => v.new || v.analyzer === 'invariants')
532
+ : violations;
533
+ const severityOrder = SEVERITIES;
534
+ const failIndex = severityOrder.indexOf(failOnSeverity);
535
+ const hasAtOrAbove = evaluableViolations.some((v) => {
536
+ // Spec 57 — a dismissed finding never blocks the gate.
537
+ if (v.dismissed)
538
+ return false;
539
+ // Spec 36 R4 — a finding from a path-profile-excluded file never blocks.
540
+ if (v.gateExcluded)
541
+ return false;
542
+ const vIndex = severityOrder.indexOf(v.severity);
543
+ return vIndex >= 0 && vIndex <= failIndex;
544
+ });
545
+ if (hasAtOrAbove) {
546
+ process.exit(2);
547
+ }
548
+ }
549
+ }
91
550
  // Legacy audit command (default behavior)
92
551
  program
93
552
  .command('audit', { isDefault: true })
94
553
  .description('Run code quality audit (default command)')
95
554
  .option('-p, --path <path>', 'Path to audit', process.cwd())
96
- .option('-c, --config <config>', 'Configuration name')
555
+ .option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
97
556
  .option('-o, --output <dir>', 'Output directory for reports')
98
557
  .option('-f, --format <format>', 'Report format: html, json, csv, or sarif')
99
558
  .option('--overwrite', 'Overwrite an existing report file at --output (default: refuse)')
@@ -103,9 +562,6 @@ program
103
562
  .option('--fail-on-regression', 'Exit code 2 when total high debt exceeds the baseline snapshot')
104
563
  .option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
105
564
  .option('--detach', 'Run the audit in a detached background process and print the job ID')
106
- .option('--partition-strategy <strategy>', 'Partition strategy for detached runs: none, auto, or top-level')
107
- .option('--max-partitions <n>', 'Maximum number of partition shards (detached runs)')
108
- .option('--shard-timeout-ms <ms>', 'Per-shard timeout in milliseconds (detached runs)')
109
565
  .option('--explain-skipped', 'Print a per-reason breakdown of files dropped from analysis')
110
566
  .action(async (options) => {
111
567
  console.log(chalk.blue('🔍 Code Quality Audit Tool'));
@@ -138,302 +594,20 @@ program
138
594
  }
139
595
  }
140
596
  }
141
- const result = await runAuditDispatch({
597
+ const result = await createAuditRunner({
142
598
  projectRoot: options.path,
143
599
  configName: options.config,
144
600
  outputDirectory: options.output,
145
601
  presets: presetIds
146
- });
602
+ }).run();
147
603
  const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
148
604
  const baseline = result.metadata?.baseline;
149
- // Spec 57 — dismissed count is reported alongside the total, never
150
- // subtracted from it ("43 findings, 3 dismissed").
151
- const dismissedCount = result.summary.dismissed ?? 0;
152
- const dismissedSuffix = dismissedCount > 0 ? `, ${dismissedCount} dismissed` : '';
153
- // Spec 36 R4 — a path-profile-excluded finding reports at its real
154
- // severity but never gates. When any finding is gate-excluded, surface
155
- // the gating count next to the total so a clean exit doesn't read as
156
- // "0 findings" beside a non-zero critical count (e.g. knex: 38 critical,
157
- // all test-dir, exit 0). The excluded total still reads in full — the
158
- // distinction is gating vs. reporting, never subtraction.
159
- const gateExcludedCount = violations.filter((v) => v.gateExcluded).length;
160
- const gatingCount = (severity) => violations.filter((v) => v.severity === severity && !v.gateExcluded && !v.dismissed).length;
161
- const findingsSuffix = `${dismissedSuffix}${gateExcludedCount > 0 ? ` (${gateExcludedCount.toLocaleString()} excluded from gate)` : ''}`;
162
- const summaryLine = (label, total, severity) => gateExcludedCount > 0
163
- ? `${label}: ${total} (${gatingCount(severity)} gating)`
164
- : `${label}: ${total}`;
165
- // ── Coverage panel leads the report (Spec 47 R2) ─────────────
166
- // A diagnostic report opens with what was measured before it lists any
167
- // readings, so a zero-reading report can't be mistaken for a clean tree.
168
- const coverage = result.metadata?.coverage;
169
- if (coverage && coverage.length > 0) {
170
- const covFired = coverage.filter(c => c.state === 'fired').length;
171
- const covClean = coverage.filter(c => c.state === 'clean').length;
172
- const covUnassessed = coverage.filter(c => c.state === 'unassessed').length;
173
- const covNotApplicable = coverage.filter(c => c.state === 'notApplicable').length;
174
- const covCannotFire = coverage.filter(c => c.state === 'cannot-fire').length;
175
- console.log(chalk.gray(`── Coverage panel ── ${covFired} fired · ${covClean} clean · ` +
176
- `${covUnassessed} unassessed · ${covNotApplicable} not-applicable · ` +
177
- `${covCannotFire} cannot-fire`));
178
- }
179
- // ── Skipped analyzers (Spec 61) ──────────────────────────────
180
- // A language present on disk but never analyzed is a stated gap, not a
181
- // silent zero. The Go path surfaces a named runtime failure (missing
182
- // toolchain, wrong-arch prebuilt binary, failed rebuild) as a
183
- // metadata.diagnostics entry; before this section those kinds were
184
- // invisible outside --json, so a polyglot repo with .go files and no Go
185
- // toolchain printed zero Go findings and exited clean — the silent-zero
186
- // failure mode. These lead above coverage gaps: a skipped analyzer is a
187
- // whole language unmeasured, more severe than a per-file visibility gap.
188
- const skippedAnalyzers = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'go-toolchain-missing' ||
189
- d.kind === 'go-analyzer-wrong-arch' ||
190
- d.kind === 'go-analyzer-build-failed');
191
- if (skippedAnalyzers.length > 0) {
192
- console.log(chalk.yellow(`── Skipped analyzers ── ${skippedAnalyzers.length}`));
193
- for (const d of skippedAnalyzers) {
194
- const name = d.analyzerName || 'go';
195
- console.log(chalk.yellow(` ${name} [${d.kind}] — ${d.message}`));
196
- }
197
- }
198
- // ── Ignored config keys (Spec 61 R1.4) ─────────────────────────
199
- // Keys dropped by sanitizeProjectFileConfig: undeclared keys (like
200
- // `scope`, which previously reached execSync via mergeConfig's raw-key
201
- // iteration) and path keys resolving outside the project root. Surfaced
202
- // so a silently-ignored config never reads as "loaded fine".
203
- const ignoredConfigKeys = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'config-key-rejected');
204
- if (ignoredConfigKeys.length > 0) {
205
- console.log(chalk.yellow(`── Ignored config keys ── ${ignoredConfigKeys.length}`));
206
- for (const d of ignoredConfigKeys) {
207
- console.log(chalk.yellow(` ${d.message}`));
208
- }
209
- }
210
- // ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
211
- // Coverage diagnostics are the analyzer saying "my visibility ends here",
212
- // not "the code is wrong". They lead with counts, then per-occurrence
213
- // file:line — visible and counted, never blocking (they never reach the
214
- // gate). Rendered alongside the coverage panel that leads the report.
215
- 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');
216
- if (coverageDiagnostics.length > 0) {
217
- const byKind = {};
218
- for (const d of coverageDiagnostics)
219
- byKind[d.kind] = (byKind[d.kind] ?? 0) + 1;
220
- const totals = Object.entries(byKind)
221
- .map(([kind, n]) => `${n} ${kind}`)
222
- .join(' · ');
223
- console.log(chalk.gray(`── Coverage gaps ── ${totals}`));
224
- for (const d of coverageDiagnostics.slice(0, 20)) {
225
- const loc = d.file ? `${d.file}${typeof d.line === 'number' ? `:${d.line}` : ''}` : '(unknown)';
226
- console.log(chalk.gray(` ${loc} [${d.kind}] — ${d.message}`));
227
- }
228
- if (coverageDiagnostics.length > 20) {
229
- console.log(chalk.gray(` … and ${coverageDiagnostics.length - 20} more`));
230
- }
231
- }
232
- // ── Spec 60 R1 — test coverage (reporting, not detection) ─────
233
- // Classifies every non-test module as tested / untested-live /
234
- // untested-dead off the file-level import edges. The dead count is
235
- // post-entry-point-exception, with the drop attributed.
236
- const testCoverage = result.metadata?.testCoverage;
237
- if (testCoverage) {
238
- const pct = testCoverage.total > 0
239
- ? ((testCoverage.tested / testCoverage.total) * 100).toFixed(1)
240
- : '0.0';
241
- let line = `tested ${testCoverage.tested}/${testCoverage.total} (${pct}%)` +
242
- ` · untested-live ${testCoverage.untestedLive}` +
243
- ` · untested-dead ${testCoverage.untestedDead}`;
244
- if (testCoverage.deadDrop > 0) {
245
- line += ` (was ${testCoverage.deadPreException} pre-exception; −${testCoverage.deadDrop} entry points exempted)`;
246
- }
247
- console.log(chalk.gray(`── Test coverage ── ${line}`));
248
- }
249
- // ── Spec 60 R2 — size distributions (median / p95 / max) ──────
250
- const sizeDistributions = result.metadata?.sizeDistributions;
251
- if (sizeDistributions && sizeDistributions.length > 0) {
252
- console.log(chalk.gray(`── Size distributions ──`));
253
- for (const d of sizeDistributions) {
254
- const fmt = (n) => Number.isInteger(n) ? String(n) : n.toFixed(1);
255
- const tail = d.maxEntry
256
- ? ` (${d.maxEntry.name}${d.maxEntry.fileType ? ` · ${d.maxEntry.fileType}` : ''} · ${d.maxEntry.entityType})`
257
- : '';
258
- const pop = d.population ? `${d.population}, n=${d.count}` : `n=${d.count}`;
259
- console.log(chalk.gray(` ${d.measure} [${pop}]: median ${fmt(d.median)} · p95 ${fmt(d.p95)} · max ${fmt(d.max)}${tail}`));
260
- }
261
- }
262
- // ── Spec 60 R3 — dead-and-duplicated clusters ─────────────────
263
- const deadClusters = result.metadata?.deadClusters;
264
- if (deadClusters && deadClusters.length > 0) {
265
- console.log(chalk.gray(`── Dead-and-duplicated ── ${deadClusters.length} clusters`));
266
- for (const c of deadClusters.slice(0, 10)) {
267
- console.log(chalk.gray(` ${c.count} × ${c.basename}`));
268
- }
269
- if (deadClusters.length > 10) {
270
- console.log(chalk.gray(` … and ${deadClusters.length - 10} more clusters`));
271
- }
272
- }
273
- // ── Delta output (Spec 18 R2) ─────────────────────────────────
274
- if (baseline && !options.full) {
275
- const newViolations = violations.filter((v) => v.new === true);
276
- const knownCount = baseline.knownCount ?? 0;
277
- const fixedCount = baseline.fixedCount ?? 0;
278
- const previousKnown = baseline.previousKnownCount ?? 0;
279
- const currentDebt = newViolations.length + knownCount;
280
- const debtDelta = currentDebt - previousKnown;
281
- const trendIcon = debtDelta > 0 ? '↑' : debtDelta < 0 ? '↓' : '→';
282
- const trendLabel = debtDelta > 0
283
- ? `(debt increased since last baseline)`
284
- : debtDelta < 0
285
- ? `(debt decreased since last baseline)`
286
- : '(unchanged)';
287
- console.log(`\n📊 Delta: +${newViolations.length} new · −${fixedCount} fixed · ${knownCount} known ${trendIcon} ${trendLabel}`);
288
- if (newViolations.length > 0) {
289
- console.log(chalk.gray(`\n── New readings (${newViolations.length}) ──────────────────────────`));
290
- for (const v of newViolations) {
291
- const icon = v.severity === 'critical' ? '🔴' :
292
- v.severity === 'severe' ? '🟠' : '🟡';
293
- console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.message}`);
294
- }
295
- }
296
- else {
297
- console.log(chalk.green('\n✓ No new readings since last baseline.'));
298
- if (knownCount > 0) {
299
- console.log(chalk.gray(` ${knownCount} known reading(s) are still open — recorded, not resolved.`));
300
- }
301
- }
302
- // Debt by analyzer
303
- console.log(chalk.gray(`\n── Debt by Analyzer ──────────────────────────`));
304
- const analyzerCounts = {};
305
- for (const v of violations) {
306
- const a = v.analyzer || 'unknown';
307
- if (!analyzerCounts[a])
308
- analyzerCounts[a] = { known: 0, new: 0 };
309
- if (v.new === false)
310
- analyzerCounts[a].known++;
311
- else if (v.new === true)
312
- analyzerCounts[a].new++;
313
- }
314
- for (const [analyzer, counts] of Object.entries(analyzerCounts).sort()) {
315
- const newPart = counts.new > 0 ? ` (+${counts.new})` : '';
316
- console.log(`${analyzer}: ${counts.known.toLocaleString()} known${newPart}`);
317
- }
318
- // Top files
319
- console.log(chalk.gray(`\n── Top Files ─────────────────────────────────`));
320
- const fileCounts = new Map();
321
- for (const v of violations) {
322
- const f = v.file || '';
323
- fileCounts.set(f, (fileCounts.get(f) || 0) + 1);
324
- }
325
- const topFiles = [...fileCounts.entries()]
326
- .sort((a, b) => b[1] - a[1])
327
- .slice(0, 5);
328
- for (const [file, count] of topFiles) {
329
- console.log(`${file} — ${count} reading${count !== 1 ? 's' : ''}`);
330
- }
331
- console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit --full')} to see all ${currentDebt.toLocaleString()} readings.`));
332
- }
333
- else if (!baseline) {
334
- // No baseline: current behavior + hint
335
- console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
336
- console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
337
- console.log(summaryLine('Severe', result.summary.severe, 'severe'));
338
- console.log(summaryLine('High', result.summary.high, 'high'));
339
- console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
340
- console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit baseline')} to adopt the ratchet and track changes over time.`));
341
- }
342
- else {
343
- // --full with baseline: full itemized inventory (current behavior)
344
- console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
345
- console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
346
- console.log(summaryLine('Severe', result.summary.severe, 'severe'));
347
- console.log(summaryLine('High', result.summary.high, 'high'));
348
- console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
349
- }
605
+ printAuditDiagnostics(result);
606
+ printFindingSummary(result, violations, baseline, options);
350
607
  // Spec 44 R4 — file accounting: analyzed/dropped totals + optional breakdown.
351
608
  printFileAccounting(result, !!options.explainSkipped);
352
- // Spec 32 — unparsed files are never silent. A run where any file failed
353
- // to parse (WASM abort, read error, …) must surface the count + reasons.
354
- const unparsedFiles = result.metadata?.unparsedFiles ?? [];
355
- if (unparsedFiles.length > 0) {
356
- console.error(chalk.red(`\n⚠️ ${unparsedFiles.length} file${unparsedFiles.length !== 1 ? 's' : ''} failed to parse:`));
357
- for (const u of unparsedFiles.slice(0, 20)) {
358
- console.error(` ${u.filePath} — ${u.reason}`);
359
- }
360
- if (unparsedFiles.length > 20) {
361
- console.error(` … and ${unparsedFiles.length - 20} more`);
362
- }
363
- }
364
- // Spec 43 R5 follow-up — extensions present on disk that discovery skipped.
365
- // Informational: surfaces "what isn't being analyzed here" so an unlisted
366
- // extension (e.g. `.mdx`) never vanishes silently at discovery. Not an
367
- // error — a skipped extension is not necessarily a defect (`.md`/`.svg`
368
- // are legitimately not source), unlike `unparsedFiles` above.
369
- const skippedExtensions = result.metadata?.skippedExtensions ?? [];
370
- if (skippedExtensions.length > 0) {
371
- console.log(chalk.gray(`\n── Not analyzed (skipped extensions) ────────`));
372
- const parts = skippedExtensions.map(s => `${s.ext} (${s.count})`);
373
- console.log(` ${parts.join(', ')}`);
374
- }
375
- // Per-analyzer activity (read from result data, not serialized summary)
376
- // — surfaces zero-scan failures that would otherwise be invisible.
377
- if (!options.json) {
378
- const analyzerFiles = [];
379
- for (const [name, ar] of Object.entries(result.analyzerResults)) {
380
- const vCount = ar.violations.length;
381
- if (isVisitorStatus(ar.status)) {
382
- analyzerFiles.push(`${name} [visitor-ran]: ${getFilesProcessed(ar.status)} files, ${vCount} violations`);
383
- }
384
- else if (isReducerStatus(ar.status)) {
385
- analyzerFiles.push(`${name} [reducer-ran]: ${getFactsConsumed(ar.status)} facts, ${vCount} violations`);
386
- }
387
- else {
388
- analyzerFiles.push(`${name} [notRun]: ${vCount} violations`);
389
- }
390
- }
391
- if (analyzerFiles.length > 0) {
392
- console.log(chalk.gray(`\n── Pipeline Stages ──────────────────────────`));
393
- console.log(analyzerFiles.join('\n'));
394
- }
395
- // ── Coverage (Spec 27) ──────────────────────────────────────────
396
- const coverage = result.metadata?.coverage;
397
- if (coverage && coverage.length > 0) {
398
- const fired = coverage.filter(c => c.state === 'fired');
399
- const clean = coverage.filter(c => c.state === 'clean');
400
- const unassessed = coverage.filter(c => c.state === 'unassessed');
401
- const notApplicable = coverage.filter(c => c.state === 'notApplicable');
402
- const cannotFire = coverage.filter(c => c.state === 'cannot-fire');
403
- const firedCount = fired.reduce((s, c) => s + c.count, 0);
404
- console.log(chalk.gray(`\n── Coverage ─────────────────────────────────`));
405
- console.log(` ${fired.length} fired (${firedCount.toLocaleString()} violations), ` +
406
- `${clean.length} clean, ` +
407
- `${unassessed.length} unassessed, ` +
408
- `${notApplicable.length} notApplicable, ` +
409
- `${cannotFire.length} cannot-fire ` +
410
- `(${coverage.length} rules registered)`);
411
- if (cannotFire.length > 0) {
412
- console.log(chalk.yellow(` ── Cannot Fire (broken in the tool) ──`));
413
- for (const c of cannotFire) {
414
- console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
415
- }
416
- }
417
- if (notApplicable.length > 0) {
418
- console.log(chalk.gray(` ── Not Applicable ──`));
419
- for (const c of notApplicable) {
420
- console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
421
- }
422
- }
423
- if (clean.length > 0) {
424
- console.log(chalk.gray(` ── Clean ──`));
425
- for (const c of clean) {
426
- console.log(` ${c.ruleId} (0)`);
427
- }
428
- }
429
- if (unassessed.length > 0) {
430
- console.log(chalk.gray(` ── Unassessed (zero readings, applicability unknown) ──`));
431
- for (const c of unassessed) {
432
- console.log(` ${c.ruleId} (0)`);
433
- }
434
- }
435
- }
436
- }
609
+ printUnparsedAndSkipped(result);
610
+ printPipelineStages(result, options);
437
611
  // Spec-20 R4: built-in profile visibility — silent behavior changes
438
612
  // are never acceptable. Notify when scripts-and-tests excludes findings
439
613
  // from the blocking gate.
@@ -443,103 +617,8 @@ program
443
617
  console.log(chalk.blue(`\nℹ️ ${builtinCapped.toLocaleString()} readings excluded from the blocking gate by built-in profile "scripts-and-tests" (scripts/tests/fixtures).`));
444
618
  console.log(chalk.gray(` Set ${chalk.cyan('"builtin": false')} in .codeauditor.json to disable.`));
445
619
  }
446
- // Generate formatted report if --format is specified
447
- if (options.format) {
448
- const validFormats = ['html', 'json', 'csv', 'sarif'];
449
- if (!validFormats.includes(options.format)) {
450
- console.error(chalk.red(`Unknown format: "${options.format}". Must be one of: ${validFormats.join(', ')}`));
451
- process.exit(1);
452
- }
453
- const { generateReport } = await import('./reporting/reportGenerator.js');
454
- const { readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
455
- const rootDir = resolve(options.path || process.cwd());
456
- const config = options.format === 'sarif'
457
- ? { rootDir, ...readVersionControlProvenance(rootDir) }
458
- : { rootDir };
459
- const report = generateReport(result, options.format, config);
460
- const ext = options.format === 'sarif' ? 'sarif' : options.format;
461
- if (options.output) {
462
- // Explicit output path — write a file. Refuse to clobber an existing
463
- // report unless --overwrite is passed. A report write is destructive;
464
- // a silent overwrite of `audit-report.json` in a consuming repo is the
465
- // defect this guard removes.
466
- const outputDir = resolve(options.output);
467
- const reportPath = join(outputDir, `audit-report.${ext}`);
468
- let exists = false;
469
- try {
470
- await fs.access(reportPath);
471
- exists = true;
472
- }
473
- catch { /* ok */ }
474
- if (exists && !options.overwrite) {
475
- console.error(chalk.red(`Refusing to overwrite existing report ${reportPath} — pass --overwrite to replace it.`));
476
- process.exit(1);
477
- }
478
- await fs.mkdir(dirname(reportPath), { recursive: true });
479
- await fs.writeFile(reportPath, report, 'utf-8');
480
- console.log(chalk.green(`\nReport written to ${reportPath}`));
481
- }
482
- else {
483
- // No output path — default to stdout. Never write a report into the
484
- // audited project without an explicit --output on the command line.
485
- process.stdout.write(report + '\n');
486
- }
487
- }
488
- // ── Fail-on logic (Spec 18 R3) ───────────────────────────────
489
- // --fail-on-regression: compare total high debt to baseline snapshot
490
- if (baseline && options.failOnRegression) {
491
- const currentDebt = violations.filter((v) => v.new || v.new === false).length;
492
- const snapshotDebt = baseline.previousKnownCount ?? 0;
493
- if (currentDebt > snapshotDebt) {
494
- console.error(chalk.red(`Debt regression: ${currentDebt - snapshotDebt} readings added without re-baselining.`));
495
- process.exit(2);
496
- }
497
- }
498
- // Zero-files gate — any enabled analyzer matching zero source files is a
499
- // dark-analyzer failure; fail the run so the bug can't hide.
500
- {
501
- const diagnostics = result.metadata?.diagnostics ?? [];
502
- const zeroFileWarnings = diagnostics.filter((d) => d.kind === 'zero-files');
503
- if (zeroFileWarnings.length > 0) {
504
- const names = zeroFileWarnings.map((d) => d.analyzerName ?? d.analyzer).join(', ');
505
- console.error(`Zero-files failure: ${zeroFileWarnings.length} analyzer(s) matched zero source files (${names})`);
506
- process.exit(2);
507
- }
508
- }
509
- // Spec 32 — a run where any file failed to parse is incomplete and must
510
- // not exit 0. This mirrors the zero-files gate: a silent dark-analyzer
511
- // failure at file granularity is a hard failure.
512
- {
513
- const unparsedFiles = result.metadata?.unparsedFiles ?? [];
514
- if (unparsedFiles.length > 0) {
515
- console.error(`Parse-failure: ${unparsedFiles.length} file(s) could not be parsed; analysis is incomplete.`);
516
- process.exit(2);
517
- }
518
- }
519
- // --fail-on: evaluate new + invariant findings only (unless --include-baseline)
520
- if (failOnSeverity) {
521
- const evaluableViolations = (baseline && !options.includeBaseline)
522
- ? violations.filter((v) => v.new || v.analyzer === 'invariants')
523
- : violations;
524
- const severityOrder = SEVERITIES;
525
- const failIndex = severityOrder.indexOf(failOnSeverity);
526
- const hasAtOrAbove = evaluableViolations.some((v) => {
527
- // Spec 57 — a dismissed finding never blocks the gate.
528
- if (v.dismissed)
529
- return false;
530
- // Spec 36 R4 — a finding from a path-profile-excluded file (e.g. a
531
- // test/script under "scripts-and-tests") never blocks the gate,
532
- // whatever its severity. The file is doing its job; a hardcoded
533
- // connection string in a stress-test script is not a defect to block on.
534
- if (v.gateExcluded)
535
- return false;
536
- const vIndex = severityOrder.indexOf(v.severity);
537
- return vIndex >= 0 && vIndex <= failIndex;
538
- });
539
- if (hasAtOrAbove) {
540
- process.exit(2);
541
- }
542
- }
620
+ await writeReport(result, options);
621
+ applyFailOnGates(result, violations, baseline, options, failOnSeverity);
543
622
  }
544
623
  catch (error) {
545
624
  console.error(chalk.red('Error:'), error);
@@ -556,7 +635,7 @@ program
556
635
  .option('--fail-on-zero-files', 'Exit code 2 when any enabled analyzer matches zero source files', true)
557
636
  .option('--stdin', 'Read file paths from stdin (one per line)')
558
637
  .option('--explain-skipped', 'Print a per-reason breakdown of files dropped from analysis')
559
- .option('-p, --path <projectPath>', 'Project root path', process.cwd())
638
+ .option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
560
639
  .action(async (paths, options) => {
561
640
  try {
562
641
  const fileSet = new Set();
@@ -592,11 +671,17 @@ program
592
671
  // the gate never misses a finding. SARIF needs full run metadata, so it
593
672
  // always runs in-process.
594
673
  const projectRoot = resolve(options.path || process.cwd());
674
+ const hookMode = process.env.CODE_AUDITOR_HOOK === '1' || process.env.CODE_AUDITOR_HOOK === 'true';
595
675
  let result;
596
676
  if (resolvedPaths.length > 0 && options.format !== 'sarif') {
597
- const { resolveDaemon, readDaemonDiagnostics } = await import('./daemon/resolve.js');
677
+ const { resolveDaemon, readDaemonDiagnostics, startDaemonDetached } = await import('./daemon/resolve.js');
598
678
  const daemon = await resolveDaemon(projectRoot);
599
679
  if (daemon.mode === 'not-ready') {
680
+ // Hook mode (Spec 68 §hook-contract): never block the edit on an audit the
681
+ // daemon can't serve yet — it is still indexing, and the finding arrives on
682
+ // a later edit. Say nothing, exit clean. A non-hook caller gets the status.
683
+ if (hookMode)
684
+ process.exit(0);
600
685
  reportDaemonIndexing(daemon.state, !!options.json);
601
686
  return;
602
687
  }
@@ -605,14 +690,27 @@ program
605
690
  if (diag && diag.status === 'ready' && diag.staleFiles.length === 0) {
606
691
  result = await buildChangedResultFromDiagnostics(diag.diagnostics, projectRoot);
607
692
  }
693
+ else if (hookMode) {
694
+ // A just-edited file whose re-audit hasn't landed is not trustworthy from
695
+ // cache. Report nothing; the finding arrives on the next edit.
696
+ process.exit(0);
697
+ }
698
+ }
699
+ else if (hookMode) {
700
+ // No daemon: enqueue by starting one detached, then exit. The hook reads
701
+ // results from the daemon's cache on later edits — it never computes them.
702
+ startDaemonDetached(projectRoot);
703
+ process.exit(0);
608
704
  }
609
705
  }
610
706
  if (!result) {
707
+ // Hook mode never falls back to an in-process audit (Spec 68 §hook-contract).
708
+ if (hookMode)
709
+ process.exit(0);
611
710
  await initParsers();
612
711
  const runner = createAuditRunner({
613
712
  projectRoot: options.path,
614
713
  scope,
615
- analyzerConcurrency: 4
616
714
  });
617
715
  result = await runner.run();
618
716
  }
@@ -621,7 +719,7 @@ program
621
719
  // Spec 57 — dismissed count is reported alongside the total, never
622
720
  // subtracted from it. The hook surface (`changed`) is agent-facing, so it
623
721
  // must show the same "N findings, M dismissed" figure as the full report.
624
- const dismissedCount = result.summary.dismissed ?? 0;
722
+ const dismissedCount = result.summary?.dismissed ?? 0;
625
723
  // Blocking gate decision (Spec 45 R1/R4, Spec 54 R3), computed once so the
626
724
  // agent-facing before/after count (Spec 45 A2) and the exit code agree on
627
725
  // the same number. Every registered rule participates (R1); enforcement is
@@ -629,98 +727,21 @@ program
629
727
  // {critical, severe, high} — every finding is a defect and every finding
630
728
  // blocks; there is no configurable gate.
631
729
  const { blocking, resolutionGaps } = computeGatingDecision(violations, BLOCKING_SEVERITIES);
632
- // JSON output
633
- if (options.format === 'sarif') {
634
- const { generateSARIFReport, readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
635
- const projectDir = resolve(options.path || process.cwd());
636
- const sarifOutput = generateSARIFReport(result, { rootDir: projectDir, ...readVersionControlProvenance(projectDir) });
637
- process.stdout.write(sarifOutput + '\n');
638
- }
639
- else if (options.json) {
640
- const projectDir = resolve(options.path || process.cwd());
641
- const jsonViolations = violations.map((v) => {
642
- // Compute relative path if file resolves inside the project
643
- let filePath = v.file || '';
644
- if (filePath.startsWith('/') || filePath.startsWith('\\\\')) {
645
- const rel = relative(projectDir, filePath);
646
- // Only use relative path if it doesn't escape the project
647
- if (!rel.startsWith('..') && !isAbsolute(rel)) {
648
- filePath = rel;
649
- }
650
- }
651
- // Find column: prefer explicit column, then start.column, then default to 1
652
- const col = v.column ?? v.start?.column ?? 1;
653
- return {
654
- analyzer: v.analyzer || '',
655
- rule: v.rule,
656
- severity: v.severity,
657
- message: v.message,
658
- file: filePath,
659
- line: v.line ?? v.start?.line,
660
- column: col,
661
- endLine: v.end?.line,
662
- endColumn: v.end?.column,
663
- enclosingSymbol: v.symbol || v.enclosingFunction || '',
664
- suggestion: v.suggestion || '',
665
- details: v.details || '',
666
- ...(v.new !== undefined && { new: v.new }),
667
- ...(v.dismissed !== undefined && { dismissed: v.dismissed }),
668
- fingerprint: fingerprint(buildFingerprintInput(v))
669
- };
670
- });
671
- // Coverage diagnostics ride alongside violations so an agent sees "the
672
- // analyzer couldn't resolve this" without a metadata flag — and without
673
- // it ever gating (diagnostics never reach computeGatingDecision).
674
- const jsonDiagnostics = (result.metadata?.diagnostics ?? []).map((d) => ({
675
- analyzer: d.analyzerName ?? d.analyzer ?? '',
676
- kind: d.kind,
677
- message: d.message,
678
- ...(d.file ? { file: d.file } : {}),
679
- ...(typeof d.line === 'number' ? { line: d.line } : {}),
680
- ...(d.details ? { details: d.details } : {}),
681
- }));
682
- const jsonOutput = { violations: jsonViolations, diagnostics: jsonDiagnostics };
683
- process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
684
- }
685
- else if (!options.quiet || violations.length > 0) {
686
- // Console output — Spec 45 A2: agent-facing output emits counts (per
687
- // analyzer/rule/severity, plus the before/after gate figure) alongside
688
- // findings. The hook and the `changed` command are the agent's surface.
689
- console.log(chalk.blue('🔍 Diff-Scoped Code Audit'));
690
- console.log(chalk.gray('══════════════════════════════════════════════════'));
691
- printFileAccounting(result, !!options.explainSkipped);
692
- if (violations.length > 0) {
693
- console.log('');
694
- printCountSummary(violations);
695
- console.log(chalk.bold(`gate before/after: ${violations.length} → ${blocking.length} blocking`));
696
- if (dismissedCount > 0) {
697
- console.log(chalk.dim(` ${dismissedCount} dismissed — still counted above, excluded from the gate`));
698
- }
699
- console.log(chalk.gray('── Violations ────────────────────────────────────'));
700
- for (const v of violations) {
701
- const icon = v.severity === 'critical' ? '🔴' :
702
- v.severity === 'severe' ? '🟠' : '🟡';
703
- const statusTag = v.new === false
704
- ? chalk.dim(' [known — still open]')
705
- : '';
706
- const dismissedTag = v.dismissed
707
- ? chalk.dim(' [dismissed]')
708
- : '';
709
- console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.message}${statusTag}${dismissedTag}`);
710
- }
711
- }
712
- else {
713
- console.log(chalk.green('\n✓ No readings.'));
714
- }
715
- }
716
- // Spec 38 R2/R3 — per-rule timing + gate wall-clock, opt-in by env var.
730
+ await emitChangedOutput(result, violations, blocking, dismissedCount, options);
731
+ // Spec 38 R2/R3 — per-rule timing + gate cpu-time, opt-in by env var.
717
732
  // Emitted to stderr so it never corrupts --json stdout. Slowest rule first.
733
+ // The gate metric is CPU time (auditCpuMs), not wall clock: the budget is
734
+ // about a slow *rule*, and CPU time isolates that from a slow machine.
718
735
  if (process.env.CODE_AUDIT_RULE_TIMING === '1') {
719
736
  const ruleTiming = result.metadata?.ruleTiming;
720
- const gateMs = result.metadata?.auditDuration;
737
+ const gateMs = result.metadata?.auditCpuMs;
738
+ const gateWallMs = result.metadata?.auditDuration;
721
739
  const lines = [];
722
740
  lines.push('');
723
- lines.push(`⏱ gate wall-clock: ${(gateMs ?? 0).toFixed(1)} ms (budget 300 ms)`);
741
+ // §16 criterion 16 — report both wall-clock (what the hook's user feels)
742
+ // and CPU (what the rule actually burned); the budget asserts on CPU.
743
+ lines.push(`⏱ gate wall-clock: ${(gateWallMs ?? 0).toFixed(1)} ms`);
744
+ lines.push(`⏱ gate cpu-time: ${(gateMs ?? 0).toFixed(1)} ms (budget 400 ms)`);
724
745
  lines.push('── per-rule timing (slowest first) ──');
725
746
  if (ruleTiming && ruleTiming.length > 0) {
726
747
  for (const t of ruleTiming) {
@@ -753,6 +774,17 @@ program
753
774
  process.exit(2);
754
775
  }
755
776
  }
777
+ // Pipeline-failure gate — the audit pipeline itself threw. One error,
778
+ // reported once by the runner; exit non-zero (broken tool) so a swallowed
779
+ // pipeline throw can never read as a clean pass.
780
+ {
781
+ const diagnostics = result.metadata?.diagnostics ?? [];
782
+ const pipelineErrors = diagnostics.filter((d) => d.kind === 'pipeline-error');
783
+ if (pipelineErrors.length > 0) {
784
+ console.error(`Pipeline failure: ${pipelineErrors[0].message}`);
785
+ process.exit(1);
786
+ }
787
+ }
756
788
  // Exit code based on --no-fail-on-zero-files (default-on in v3.4.8)
757
789
  // Any enabled analyzer matching zero source files is a failure — a >50%
758
790
  // threshold lets the exact bug (3/9 analyzers dark) pass silently.
@@ -771,6 +803,96 @@ program
771
803
  process.exit(1);
772
804
  }
773
805
  });
806
+ /**
807
+ * Emit the changed-audit output (SARIF, JSON, or console) for the hook surface.
808
+ * The SARIF branch dynamically imports its generator, so this is async.
809
+ */
810
+ async function emitChangedOutput(result, violations, blocking, dismissedCount, options) {
811
+ // JSON output
812
+ if (options.format === 'sarif') {
813
+ const { generateSARIFReport, readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
814
+ const projectDir = resolve(options.path || process.cwd());
815
+ const sarifOutput = generateSARIFReport(result, { rootDir: projectDir, ...readVersionControlProvenance(projectDir) });
816
+ process.stdout.write(sarifOutput + '\n');
817
+ }
818
+ else if (options.json) {
819
+ const projectDir = resolve(options.path || process.cwd());
820
+ const jsonViolations = violations.map((v) => {
821
+ // Compute relative path if file resolves inside the project
822
+ let filePath = v.file || '';
823
+ if (filePath.startsWith('/') || filePath.startsWith('\\\\')) {
824
+ const rel = relative(projectDir, filePath);
825
+ // Only use relative path if it doesn't escape the project
826
+ if (!rel.startsWith('..') && !isAbsolute(rel)) {
827
+ filePath = rel;
828
+ }
829
+ }
830
+ // Find column: prefer explicit column, then start.column, then default to 1
831
+ const col = v.column ?? v.start?.column ?? 1;
832
+ return {
833
+ analyzer: v.analyzer || '',
834
+ rule: v.rule,
835
+ severity: v.severity,
836
+ message: v.message,
837
+ file: filePath,
838
+ line: v.line ?? v.start?.line,
839
+ column: col,
840
+ endLine: v.end?.line,
841
+ endColumn: v.end?.column,
842
+ enclosingSymbol: v.symbol || v.enclosingFunction || '',
843
+ suggestion: v.suggestion || '',
844
+ details: v.details || '',
845
+ ...(v.new !== undefined && { new: v.new }),
846
+ ...(v.dismissed !== undefined && { dismissed: v.dismissed }),
847
+ fingerprint: fingerprint(buildFingerprintInput(v))
848
+ };
849
+ });
850
+ // Coverage diagnostics ride alongside violations so an agent sees "the
851
+ // analyzer couldn't resolve this" without a metadata flag — and without
852
+ // it ever gating (diagnostics never reach computeGatingDecision).
853
+ const jsonDiagnostics = (result.metadata?.diagnostics ?? []).map((d) => ({
854
+ analyzer: d.analyzerName ?? d.analyzer ?? '',
855
+ kind: d.kind,
856
+ message: d.message,
857
+ ...(d.file ? { file: d.file } : {}),
858
+ ...(typeof d.line === 'number' ? { line: d.line } : {}),
859
+ ...(d.details ? { details: d.details } : {}),
860
+ }));
861
+ const jsonOutput = { violations: jsonViolations, diagnostics: jsonDiagnostics };
862
+ process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
863
+ }
864
+ else if (!options.quiet || violations.length > 0) {
865
+ // Console output — Spec 45 A2: agent-facing output emits counts (per
866
+ // analyzer/rule/severity, plus the before/after gate figure) alongside
867
+ // findings. The hook and the `changed` command are the agent's surface.
868
+ console.log(chalk.blue('🔍 Diff-Scoped Code Audit'));
869
+ console.log(chalk.gray('══════════════════════════════════════════════════'));
870
+ printFileAccounting(result, !!options.explainSkipped);
871
+ if (violations.length > 0) {
872
+ console.log('');
873
+ printCountSummary(violations);
874
+ console.log(chalk.bold(`gate before/after: ${violations.length} → ${blocking.length} blocking`));
875
+ if (dismissedCount > 0) {
876
+ console.log(chalk.dim(` ${dismissedCount} dismissed — still counted above, excluded from the gate`));
877
+ }
878
+ console.log(chalk.gray('── Violations ────────────────────────────────────'));
879
+ for (const v of violations) {
880
+ const icon = v.severity === 'critical' ? '🔴' :
881
+ v.severity === 'severe' ? '🟠' : '🟡';
882
+ const statusTag = v.new === false
883
+ ? chalk.dim(' [known — still open]')
884
+ : '';
885
+ const dismissedTag = v.dismissed
886
+ ? chalk.dim(' [dismissed]')
887
+ : '';
888
+ console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.severity}] ${v.message}${statusTag}${dismissedTag}`);
889
+ }
890
+ }
891
+ else {
892
+ console.log(chalk.green('\n✓ No readings.'));
893
+ }
894
+ }
895
+ }
774
896
  // Spec 45 A2 — counts are emitted where useful (per analyzer, per rule, per
775
897
  // severity), on agent-facing surfaces as well as human ones. This reverts Spec
776
898
  // 36 R3's "no bare counts": an agent now sees the shape of what fired instead
@@ -810,11 +932,61 @@ function printCountSummary(violations) {
810
932
  */
811
933
  async function buildChangedResultFromDiagnostics(violations, projectRoot) {
812
934
  const analyzerResults = {};
935
+ const byAnalyzer = {};
936
+ let totalViolations = 0;
937
+ let criticalIssues = 0;
938
+ let severe = 0;
939
+ let high = 0;
940
+ let dismissed = 0;
941
+ const violationsByCategory = {};
813
942
  for (const v of violations) {
814
943
  const key = v.analyzer || 'unknown';
815
944
  (analyzerResults[key] = analyzerResults[key] || { violations: [] }).violations.push(v);
945
+ totalViolations++;
946
+ switch (v.severity) {
947
+ case 'critical':
948
+ criticalIssues++;
949
+ break;
950
+ case 'severe':
951
+ severe++;
952
+ break;
953
+ case 'high':
954
+ high++;
955
+ break;
956
+ }
957
+ const rule = v.rule || 'unknown';
958
+ violationsByCategory[rule] = (violationsByCategory[rule] ?? 0) + 1;
959
+ const bucket = byAnalyzer[key] ?? (byAnalyzer[key] = { violations: 0 });
960
+ bucket.violations++;
961
+ if (v.dismissed)
962
+ dismissed++;
816
963
  }
817
- return { analyzerResults, metadata: { configUsed: undefined, diagnostics: [] } };
964
+ const topIssues = Object.entries(violationsByCategory)
965
+ .sort(([, a], [, b]) => b - a)
966
+ .slice(0, 5)
967
+ .map(([type, count]) => ({ type, count }));
968
+ // A *real* summary derived from the diagnostics the daemon served — not a
969
+ // zero-filled placeholder (a stub satisfying the type, with a `dismissed: 0`
970
+ // that was never measured). Every field here is computed from the actual
971
+ // findings: severity/category/byAnalyzer rollups, plus `dismissed` from each
972
+ // diagnostic's own `dismissed` flag. `totalFiles` and the per-analyzer
973
+ // `filesProcessed`/`fatalErrors`/`unprovenSites` are not derivable from a flat
974
+ // per-file diagnostic list, so they are omitted; the guarded read
975
+ // (`result.summary?.dismissed ?? 0`) tolerates their absence.
976
+ return {
977
+ analyzerResults,
978
+ summary: {
979
+ totalViolations,
980
+ criticalIssues,
981
+ severe,
982
+ high,
983
+ violationsByCategory,
984
+ topIssues,
985
+ byAnalyzer,
986
+ dismissed,
987
+ },
988
+ metadata: { configUsed: undefined, diagnostics: [] },
989
+ };
818
990
  }
819
991
  // Self-audit gate (Spec 33 Item 15 + Spec 44 remediation). Runs the full
820
992
  // analyzer pipeline over the tool's own production source and asserts zero
@@ -850,7 +1022,7 @@ program
850
1022
  .description('Audit the tool\'s own analyzers/ + languages/ source and fail on blocking readings')
851
1023
  .option('--json', 'Output violations as machine-readable JSON to stdout')
852
1024
  .option('--stdin', 'Read file paths from stdin (one per line)')
853
- .option('-p, --path <projectPath>', 'Project root path', process.cwd())
1025
+ .option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
854
1026
  .option('--fail-on <severity>', 'Blocking severity floor: critical, severe, or high', 'high')
855
1027
  .action(async (paths, options) => {
856
1028
  try {
@@ -889,7 +1061,6 @@ program
889
1061
  const runner = createAuditRunner({
890
1062
  projectRoot: options.path,
891
1063
  scope,
892
- analyzerConcurrency: 4
893
1064
  });
894
1065
  const result = await runner.run();
895
1066
  const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
@@ -934,7 +1105,7 @@ program
934
1105
  for (const v of blocking) {
935
1106
  const icon = v.severity === 'critical' ? '🔴' :
936
1107
  v.severity === 'severe' ? '🟠' : '🟡';
937
- console.log(`${icon} ${v.file}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.message}`);
1108
+ console.log(`${icon} ${v.file}${lineSuffix(v.line)} [${v.severity}] ${v.message}`);
938
1109
  }
939
1110
  }
940
1111
  else {
@@ -960,8 +1131,8 @@ program
960
1131
  program
961
1132
  .command('next-file')
962
1133
  .description('Audit and return the highest-priority file with readings (file-by-file refactor loop)')
963
- .option('-p, --path <projectPath>', 'Project root path', process.cwd())
964
- .option('-c, --config <config>', 'Configuration name')
1134
+ .option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
1135
+ .option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
965
1136
  .option('--json', 'Output a single JSON object (or {done:true}) to stdout')
966
1137
  .action(async (options) => {
967
1138
  try {
@@ -1059,7 +1230,7 @@ function printNextFile(violations, summary, projectDir, json) {
1059
1230
  console.log(chalk.gray('\n── Readings ────────────────────────────────────────'));
1060
1231
  for (const v of ordered) {
1061
1232
  const icon = v.severity === 'critical' ? '🔴' : v.severity === 'severe' ? '🟠' : '🟡';
1062
- console.log(`${icon} ${chalk.bold(relativize(v.file || ''))}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.rule} — ${v.message}`);
1233
+ console.log(`${icon} ${chalk.bold(relativize(v.file || ''))}${lineSuffix(v.line)} [${v.severity}] ${v.rule} — ${v.message}`);
1063
1234
  }
1064
1235
  }
1065
1236
  }
@@ -1067,8 +1238,8 @@ function printNextFile(violations, summary, projectDir, json) {
1067
1238
  program
1068
1239
  .command('baseline')
1069
1240
  .description('Snapshot current advisory readings as the baseline (excludes invariants)')
1070
- .option('-p, --path <path>', 'Project path', process.cwd())
1071
- .option('--json', 'Output as JSON')
1241
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1242
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1072
1243
  .action(async (options) => {
1073
1244
  try {
1074
1245
  await initParsers();
@@ -1134,9 +1305,9 @@ program
1134
1305
  .command('dismiss <fingerprint>')
1135
1306
  .description('Dismiss one finding by fingerprint — a written reason is required')
1136
1307
  .requiredOption('--reason <reason>', 'Why this finding is dismissed (required — a dismissal without a reason is a config error)')
1137
- .option('-p, --path <path>', 'Project path', process.cwd())
1308
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1138
1309
  .option('--telemetry-endpoint <url>', 'Feedback service endpoint override (defaults to the opt-in endpoint recorded by the telemetry tool)')
1139
- .option('--json', 'Output as JSON')
1310
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1140
1311
  .action(async (fingerprintArg, options) => {
1141
1312
  try {
1142
1313
  const reason = (options.reason ?? '').trim();
@@ -1248,7 +1419,7 @@ indexCmd
1248
1419
  .command('sync')
1249
1420
  .description('Synchronize index from source files')
1250
1421
  .option('--path <path>', 'Path to a specific file or directory', process.cwd())
1251
- .option('--json', 'Output as JSON')
1422
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1252
1423
  .action(async (options) => {
1253
1424
  try {
1254
1425
  await initParsers();
@@ -1289,64 +1460,65 @@ indexCmd
1289
1460
  process.exit(1);
1290
1461
  }
1291
1462
  });
1292
- indexCmd
1293
- .command('cleanup')
1294
- .description('Remove index entries for deleted files')
1295
- .option('--json', 'Output as JSON')
1296
- .action(async (options) => {
1297
- try {
1298
- const db = CodeIndexDB.getInstance();
1299
- await db.initialize();
1300
- const result = await db.bulkCleanup();
1301
- if (options.json) {
1302
- process.stdout.write(JSON.stringify({ mode: 'cleanup', success: true, ...result }) + '\n');
1463
+ /** Register a simple `index` subcommand that runs one mutation on the
1464
+ * initialized index and prints the outcome (JSON or console). `cleanup` and
1465
+ * `reset` share the get-instance/initialize/json-vs-console/try-catch
1466
+ * scaffolding; only the mutation and its result prose differ, so they live
1467
+ * here once and each command is a thin `defineIndexMutationCommand(...)` call. */
1468
+ function defineIndexMutationCommand(name, description, work) {
1469
+ indexCmd
1470
+ .command(name)
1471
+ .description(description)
1472
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1473
+ .action(async (options) => {
1474
+ try {
1475
+ const db = CodeIndexDB.getInstance();
1476
+ await db.initialize();
1477
+ const { extra, greenLine, grayLine } = await work(db);
1478
+ if (options.json) {
1479
+ process.stdout.write(JSON.stringify({ mode: name, success: true, ...extra }) + '\n');
1480
+ }
1481
+ else {
1482
+ console.log(chalk.green(greenLine));
1483
+ console.log(chalk.gray(grayLine));
1484
+ }
1303
1485
  }
1304
- else {
1305
- console.log(chalk.green('✓ Cleanup complete'));
1306
- console.log(chalk.gray(`Removed ${result.removedCount} entries from ${result.removedFiles.length} deleted files`));
1486
+ catch (error) {
1487
+ console.error(chalk.red('Error:'), error);
1488
+ process.exit(1);
1307
1489
  }
1308
- }
1309
- catch (error) {
1310
- console.error(chalk.red('Error:'), error);
1311
- process.exit(1);
1312
- }
1490
+ });
1491
+ }
1492
+ defineIndexMutationCommand('cleanup', 'Remove index entries for deleted files', async (db) => {
1493
+ const result = await db.bulkCleanup();
1494
+ return {
1495
+ extra: result,
1496
+ greenLine: '✓ Cleanup complete',
1497
+ grayLine: `Removed ${result.removedCount} entries from ${result.removedFiles.length} deleted files`,
1498
+ };
1313
1499
  });
1314
- indexCmd
1315
- .command('reset')
1316
- .description('Clear all analysis data (preserves tasks, config, whitelist)')
1317
- .option('--json', 'Output as JSON')
1318
- .action(async (options) => {
1319
- try {
1320
- const db = CodeIndexDB.getInstance();
1321
- await db.initialize();
1322
- await db.clearIndex();
1323
- if (options.json) {
1324
- process.stdout.write(JSON.stringify({ mode: 'reset', success: true, message: 'All analysis data cleared' }) + '\n');
1325
- }
1326
- else {
1327
- console.log(chalk.green('✓ Index reset complete'));
1328
- console.log(chalk.gray('All analysis-derived data cleared (tasks, config, and whitelist preserved)'));
1329
- }
1330
- }
1331
- catch (error) {
1332
- console.error(chalk.red('Error:'), error);
1333
- process.exit(1);
1334
- }
1500
+ defineIndexMutationCommand('reset', 'Clear all analysis data (preserves tasks, config, whitelist)', async (db) => {
1501
+ await db.clearIndex();
1502
+ return {
1503
+ extra: { message: 'All analysis data cleared' },
1504
+ greenLine: '✓ Index reset complete',
1505
+ grayLine: 'All analysis-derived data cleared (tasks, config, and whitelist preserved)',
1506
+ };
1335
1507
  });
1336
1508
  // ── Index status command (Spec 14 R1) ──────────────────────────
1337
1509
  indexCmd
1338
1510
  .command('status')
1339
1511
  .description('Show index health and graph statistics')
1340
- .option('--json', 'Output as JSON')
1512
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1341
1513
  .action(async (options) => {
1342
1514
  try {
1343
1515
  const db = CodeIndexDB.getInstance();
1344
1516
  await db.initialize();
1345
- const stats = await db.getStats();
1517
+ const stats = await db.functionIndex.getStats();
1346
1518
  // Graph stats — non-fatal if graph cache is empty
1347
1519
  let graphStats = null;
1348
1520
  try {
1349
- graphStats = db.getGraphStats();
1521
+ graphStats = db.graph.getGraphStats();
1350
1522
  }
1351
1523
  catch {
1352
1524
  // No graph data yet — ok
@@ -1417,7 +1589,7 @@ configCmd
1417
1589
  .command('rules-list')
1418
1590
  .description('List all configured invariant rules from .codeauditor.json')
1419
1591
  .option('--config-path <path>', 'Path to .codeauditor.json')
1420
- .option('--json', 'Output as JSON')
1592
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1421
1593
  .action(async (options) => {
1422
1594
  try {
1423
1595
  const pathModule = await import('path');
@@ -1427,7 +1599,9 @@ configCmd
1427
1599
  try {
1428
1600
  const raw = readFileSync(configPath, 'utf-8');
1429
1601
  const config = JSON.parse(raw);
1430
- const rules = config?.rules ?? [];
1602
+ // `rules` is an array by schema; tolerate the `{}` shorthand some fixtures
1603
+ // and hand-edited configs use for "no rules" — `.map` on `{}` would throw.
1604
+ const rules = Array.isArray(config?.rules) ? config.rules : [];
1431
1605
  if (options.json) {
1432
1606
  process.stdout.write(JSON.stringify({
1433
1607
  rules: rules.map((r) => ({ id: r.id, kind: r.kind, severity: r.severity, message: r.message || null })),
@@ -1465,7 +1639,7 @@ configCmd
1465
1639
  .command('rules-check')
1466
1640
  .description('Validate the current .codeauditor.json rules')
1467
1641
  .option('--config-path <path>', 'Path to .codeauditor.json')
1468
- .option('--json', 'Output as JSON')
1642
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1469
1643
  .action(async (options) => {
1470
1644
  try {
1471
1645
  const pathModule = await import('path');
@@ -1513,9 +1687,9 @@ configCmd
1513
1687
  configCmd
1514
1688
  .command('profiles')
1515
1689
  .description('Show active path profiles and resolve per-file matching')
1516
- .option('-p, --path <path>', 'Project path', process.cwd())
1690
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1517
1691
  .option('--file <file>', 'Resolve profiles for a specific file')
1518
- .option('--json', 'Output as JSON')
1692
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1519
1693
  .action(async (options) => {
1520
1694
  try {
1521
1695
  const fs = await import('fs/promises');
@@ -1613,8 +1787,8 @@ configCmd
1613
1787
  configCmd
1614
1788
  .command('detection')
1615
1789
  .description('Show DB-provenanced and inferred receiver identifiers with per-entry evidence')
1616
- .option('-p, --path <path>', 'Project path', process.cwd())
1617
- .option('--json', 'Output as JSON')
1790
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1791
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1618
1792
  .option('--mode <mode>', 'Detection mode filter (hybrid, provenance, names)')
1619
1793
  .action(async (options) => {
1620
1794
  try {
@@ -1666,9 +1840,7 @@ configCmd
1666
1840
  // Build provenance context using schema analyzer config (most complete)
1667
1841
  const ctx = buildProvenanceContext(ast, adapter, sourceCode, {
1668
1842
  mode: detectionMode,
1669
- dbReceiverNames: schemaCfg.dbReceiverNames ?? daCfg.dbReceiverNames,
1670
1843
  dbBindingNames: schemaCfg.dbBindingNames ?? daCfg.dbBindingNames,
1671
- dbCallMethods: schemaCfg.dbCallMethods ?? daCfg.dbCallMethods,
1672
1844
  });
1673
1845
  // Collect DB-provenanced
1674
1846
  for (const [id, evidence] of ctx.dbProvenanced) {
@@ -1721,70 +1893,84 @@ configCmd
1721
1893
  entry.validatorProvenanced.push(v);
1722
1894
  }
1723
1895
  for (const [file, data] of byFile) {
1724
- db.storeFileProvenance(file, data);
1896
+ db.provenance.storeFileProvenance(file, data);
1725
1897
  }
1726
1898
  // Store inferred receivers
1727
- db.storeInferredReceivers(inferred);
1899
+ db.inferredReceivers.storeInferredReceivers(inferred);
1728
1900
  }
1729
1901
  catch {
1730
1902
  // Index not available — skip persistence
1731
1903
  }
1732
- if (options.json) {
1733
- process.stdout.write(JSON.stringify({
1734
- mode: detectionMode,
1735
- projectRoot,
1736
- filesScanned: files.slice(0, 500).length,
1737
- dbProvenanced: provenanced,
1738
- validatorProvenanced: validatorIdentifiers,
1739
- inferred,
1740
- }, null, 2) + '\n');
1741
- }
1742
- else {
1743
- console.log(chalk.bold(`Detection mode: ${chalk.cyan(detectionMode)}`));
1744
- console.log(chalk.gray(`Scanned ${files.slice(0, 500).length} files`));
1745
- console.log();
1746
- if (provenanced.length > 0) {
1747
- console.log(chalk.bold('DB-Provenanced Identifiers:'));
1748
- for (const p of provenanced) {
1749
- const reasonColor = p.reason === 'fallback'
1750
- ? chalk.yellow
1751
- : p.reason === 'package'
1752
- ? chalk.green
1753
- : chalk.cyan;
1754
- console.log(` ${chalk.white(p.identifier)} ${chalk.gray(`(${p.file})`)}`);
1755
- console.log(` ${reasonColor(`reason: ${p.reason}`)} — ${chalk.gray(p.source)}`);
1756
- if (p.chain) {
1757
- console.log(` ${chalk.gray(`chain: ${p.chain.join(' → ')}`)}`);
1758
- }
1759
- }
1760
- console.log();
1761
- }
1762
- if (inferred.length > 0) {
1763
- console.log(chalk.bold('Inferred Receivers:'));
1764
- for (const i of inferred) {
1765
- console.log(` ${chalk.white(i.identifier)} ${chalk.gray(`(${i.file})`)}`);
1766
- console.log(` ${chalk.magenta(i.reason)}`);
1767
- }
1768
- console.log();
1769
- }
1770
- if (validatorIdentifiers.length > 0) {
1771
- console.log(chalk.bold('Validator-Provenanced Identifiers:'));
1772
- for (const v of validatorIdentifiers) {
1773
- console.log(` ${chalk.white(v.identifier)} ${chalk.gray(`(${v.file})`)}`);
1774
- console.log(` ${chalk.green(`reason: ${v.reason}`)} — ${chalk.gray(v.source)}`);
1775
- }
1776
- console.log();
1777
- }
1778
- if (provenanced.length === 0 && inferred.length === 0 && validatorIdentifiers.length === 0) {
1779
- console.log(chalk.gray('No DB-provenanced, inferred, or validator-provenanced identifiers found.'));
1780
- }
1781
- }
1904
+ printDetectionResults({
1905
+ json: options.json,
1906
+ mode: detectionMode,
1907
+ projectRoot,
1908
+ filesScanned: files.slice(0, 500).length,
1909
+ dbProvenanced: provenanced,
1910
+ validatorProvenanced: validatorIdentifiers,
1911
+ inferred,
1912
+ });
1782
1913
  }
1783
1914
  catch (error) {
1784
1915
  console.error(chalk.red('Error:'), error);
1785
1916
  process.exit(1);
1786
1917
  }
1787
1918
  });
1919
+ // Renders the `config detection` results — pulled out of the `.action` handler so
1920
+ // the scan/persist logic stays short and the two output shapes (JSON vs human) are
1921
+ // isolated from the provenance collection.
1922
+ function printDetectionResults(params) {
1923
+ const { json, mode, projectRoot, filesScanned, dbProvenanced, validatorProvenanced, inferred } = params;
1924
+ if (json) {
1925
+ process.stdout.write(JSON.stringify({
1926
+ mode,
1927
+ projectRoot,
1928
+ filesScanned,
1929
+ dbProvenanced,
1930
+ validatorProvenanced,
1931
+ inferred,
1932
+ }, null, 2) + '\n');
1933
+ return;
1934
+ }
1935
+ console.log(chalk.bold(`Detection mode: ${chalk.cyan(mode)}`));
1936
+ console.log(chalk.gray(`Scanned ${filesScanned} files`));
1937
+ console.log();
1938
+ if (dbProvenanced.length > 0) {
1939
+ console.log(chalk.bold('DB-Provenanced Identifiers:'));
1940
+ for (const p of dbProvenanced) {
1941
+ const reasonColor = p.reason === 'fallback'
1942
+ ? chalk.yellow
1943
+ : p.reason === 'package'
1944
+ ? chalk.green
1945
+ : chalk.cyan;
1946
+ console.log(` ${chalk.white(p.identifier)} ${chalk.gray(`(${p.file})`)}`);
1947
+ console.log(` ${reasonColor(`reason: ${p.reason}`)} — ${chalk.gray(p.source)}`);
1948
+ if (p.chain) {
1949
+ console.log(` ${chalk.gray(`chain: ${p.chain.join(' → ')}`)}`);
1950
+ }
1951
+ }
1952
+ console.log();
1953
+ }
1954
+ if (inferred.length > 0) {
1955
+ console.log(chalk.bold('Inferred Receivers:'));
1956
+ for (const i of inferred) {
1957
+ console.log(` ${chalk.white(i.identifier)} ${chalk.gray(`(${i.file})`)}`);
1958
+ console.log(` ${chalk.magenta(i.reason)}`);
1959
+ }
1960
+ console.log();
1961
+ }
1962
+ if (validatorProvenanced.length > 0) {
1963
+ console.log(chalk.bold('Validator-Provenanced Identifiers:'));
1964
+ for (const v of validatorProvenanced) {
1965
+ console.log(` ${chalk.white(v.identifier)} ${chalk.gray(`(${v.file})`)}`);
1966
+ console.log(` ${chalk.green(`reason: ${v.reason}`)} — ${chalk.gray(v.source)}`);
1967
+ }
1968
+ console.log();
1969
+ }
1970
+ if (dbProvenanced.length === 0 && inferred.length === 0 && validatorProvenanced.length === 0) {
1971
+ console.log(chalk.gray('No DB-provenanced, inferred, or validator-provenanced identifiers found.'));
1972
+ }
1973
+ }
1788
1974
  // Search command
1789
1975
  program
1790
1976
  .command('search <query>')
@@ -1852,7 +2038,7 @@ tasksCmd
1852
2038
  .option('--priority <priority>', 'Filter by priority (low, medium, high)')
1853
2039
  .option('--label <label>', 'Filter by label')
1854
2040
  .option('--limit <limit>', 'Result limit', '50')
1855
- .option('--json', 'Output as JSON')
2041
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1856
2042
  .action(async (options) => {
1857
2043
  try {
1858
2044
  const args = { action: 'list' };
@@ -1887,7 +2073,7 @@ tasksCmd
1887
2073
  }
1888
2074
  }
1889
2075
  else {
1890
- console.error(chalk.red(result.error || 'Unknown error'));
2076
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
1891
2077
  process.exit(1);
1892
2078
  }
1893
2079
  }
@@ -1904,7 +2090,7 @@ tasksCmd
1904
2090
  .option('--priority <priority>', 'Priority (low, medium, high)')
1905
2091
  .option('--status <status>', 'Status (open, in_progress)')
1906
2092
  .option('--labels <labels>', 'Comma-separated labels')
1907
- .option('--json', 'Output as JSON')
2093
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1908
2094
  .action(async (options) => {
1909
2095
  try {
1910
2096
  const args = {
@@ -1929,7 +2115,7 @@ tasksCmd
1929
2115
  console.log(chalk.green(`✓ Task created: ${task.title || task.taskId || 'unknown'}`));
1930
2116
  }
1931
2117
  else {
1932
- console.error(chalk.red(result.error || 'Unknown error'));
2118
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
1933
2119
  process.exit(1);
1934
2120
  }
1935
2121
  }
@@ -1941,7 +2127,7 @@ tasksCmd
1941
2127
  tasksCmd
1942
2128
  .command('get <taskId>')
1943
2129
  .description('Get a task by ID')
1944
- .option('--json', 'Output as JSON')
2130
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1945
2131
  .action(async (taskId, options) => {
1946
2132
  try {
1947
2133
  const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
@@ -1967,7 +2153,7 @@ tasksCmd
1967
2153
  console.log(chalk.dim(`\nfingerprint: ${t.fingerprint}`));
1968
2154
  }
1969
2155
  else {
1970
- console.error(chalk.red(result.error || 'Unknown error'));
2156
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
1971
2157
  process.exit(1);
1972
2158
  }
1973
2159
  }
@@ -1984,7 +2170,7 @@ tasksCmd
1984
2170
  .option('--status <status>', 'New status (open, in_progress, completed)')
1985
2171
  .option('--priority <priority>', 'New priority (low, medium, high)')
1986
2172
  .option('--labels <labels>', 'Comma-separated labels')
1987
- .option('--json', 'Output as JSON')
2173
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1988
2174
  .action(async (taskId, options) => {
1989
2175
  try {
1990
2176
  const patch = {};
@@ -2011,7 +2197,7 @@ tasksCmd
2011
2197
  console.log(chalk.green(`✓ Task updated`));
2012
2198
  }
2013
2199
  else {
2014
- console.error(chalk.red(result.error || 'Unknown error'));
2200
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
2015
2201
  process.exit(1);
2016
2202
  }
2017
2203
  }
@@ -2020,60 +2206,45 @@ tasksCmd
2020
2206
  process.exit(1);
2021
2207
  }
2022
2208
  });
2023
- tasksCmd
2024
- .command('complete <taskId>')
2025
- .description('Mark a task as completed')
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: 'complete_task', 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 completed`));
2209
+ /** Run a single-taskId tasks subcommand and print its result. */
2210
+ function makeTaskIdAction(action, successLabel) {
2211
+ return async (taskId, options) => {
2212
+ try {
2213
+ const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
2214
+ const result = await handleProjectTasks({ action, taskId });
2215
+ if (options.json) {
2216
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
2217
+ }
2218
+ else if (result.success) {
2219
+ console.log(chalk.green(`✓ ${successLabel}`));
2220
+ }
2221
+ else {
2222
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
2223
+ process.exit(1);
2224
+ }
2036
2225
  }
2037
- else {
2038
- console.error(chalk.red(result.error || 'Unknown error'));
2226
+ catch (error) {
2227
+ console.error(chalk.red('Error:'), error);
2039
2228
  process.exit(1);
2040
2229
  }
2041
- }
2042
- catch (error) {
2043
- console.error(chalk.red('Error:'), error);
2044
- process.exit(1);
2045
- }
2046
- });
2230
+ };
2231
+ }
2232
+ tasksCmd
2233
+ .command('complete <taskId>')
2234
+ .description('Mark a task as completed')
2235
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2236
+ .action(makeTaskIdAction('complete_task', 'Task completed'));
2047
2237
  tasksCmd
2048
2238
  .command('delete <taskId>')
2049
2239
  .description('Delete a task')
2050
- .option('--json', 'Output as JSON')
2051
- .action(async (taskId, options) => {
2052
- try {
2053
- const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
2054
- const result = await handleProjectTasks({ action: 'delete', taskId });
2055
- if (options.json) {
2056
- process.stdout.write(JSON.stringify(result, null, 2) + '\n');
2057
- }
2058
- else if (result.success) {
2059
- console.log(chalk.green(`✓ Task deleted`));
2060
- }
2061
- else {
2062
- console.error(chalk.red(result.error || 'Unknown error'));
2063
- process.exit(1);
2064
- }
2065
- }
2066
- catch (error) {
2067
- console.error(chalk.red('Error:'), error);
2068
- process.exit(1);
2069
- }
2070
- });
2240
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2241
+ .action(makeTaskIdAction('delete', 'Task deleted'));
2071
2242
  tasksCmd
2072
2243
  .command('from-audit')
2073
2244
  .description('Populate tasks from audit violations')
2074
2245
  .option('--auditJobId <id>', 'Specific audit result ID')
2075
2246
  .option('--severities <severities>', 'Comma-separated severities (critical,severe,high)', 'critical,severe,high')
2076
- .option('--json', 'Output as JSON')
2247
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2077
2248
  .action(async (options) => {
2078
2249
  try {
2079
2250
  const args = { action: 'from_audit' };
@@ -2100,7 +2271,7 @@ tasksCmd
2100
2271
  console.log(chalk.yellow(result.error));
2101
2272
  }
2102
2273
  else {
2103
- console.error(chalk.red(result.error || 'Unknown error'));
2274
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
2104
2275
  process.exit(1);
2105
2276
  }
2106
2277
  }
@@ -2116,7 +2287,7 @@ program
2116
2287
  .description('Rank files and functions by hotspot score (churn × complexity)')
2117
2288
  .option('--limit <n>', 'Max entries to show')
2118
2289
  .option('--path <dir>', 'Target directory (triggers spot churn extraction if index is empty)')
2119
- .option('--json', 'Output as JSON')
2290
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2120
2291
  .action(async (options) => {
2121
2292
  try {
2122
2293
  const db = CodeIndexDB.getInstance();
@@ -2191,8 +2362,8 @@ program
2191
2362
  .command('risk')
2192
2363
  .description('Rank functions by architectural risk (PageRank × betweenness × complexity × untested)')
2193
2364
  .option('--limit <n>', 'Max entries to show', '20')
2194
- .option('--path <dir>', 'Project path', process.cwd())
2195
- .option('--json', 'Output as JSON')
2365
+ .option('--path <dir>', OPTION_PROJECT_PATH, process.cwd())
2366
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2196
2367
  .option('--format <format>', 'Output format: dot (call-graph neighborhood of top functions)')
2197
2368
  .action(async (options) => {
2198
2369
  try {
@@ -2207,10 +2378,13 @@ program
2207
2378
  if (options.format === 'dot') {
2208
2379
  // DOT output: call-graph neighborhood of top-N risk functions
2209
2380
  const { callGraphToDot } = await import('./graph/outputFormatter.js');
2210
- // Collect IDs for top risk functions
2381
+ // Collect IDs for top risk functions — one query for the whole top-N
2382
+ // set, not one query per function (loop-query).
2211
2383
  const topIds = new Set();
2212
- for (const e of top) {
2213
- const rows = rawDb.prepare('SELECT id FROM functions WHERE name = ? AND file_path = ?').all(e.functionName, e.filePath);
2384
+ if (top.length > 0) {
2385
+ const orClause = top.map(() => '(name = ? AND file_path = ?)').join(' OR ');
2386
+ const params = top.flatMap((e) => [e.functionName, e.filePath]);
2387
+ const rows = rawDb.prepare(`SELECT id FROM functions WHERE ${orClause}`).all(...params);
2214
2388
  for (const r of rows)
2215
2389
  topIds.add(r.id);
2216
2390
  }
@@ -2267,7 +2441,7 @@ ledgerCmd
2267
2441
  .command('list')
2268
2442
  .description('List recent audit runs')
2269
2443
  .option('--limit <n>', 'Max runs to show', '20')
2270
- .option('--json', 'Output as JSON')
2444
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2271
2445
  .action(async (options) => {
2272
2446
  try {
2273
2447
  const { listRuns } = await import('./ledger.js');
@@ -2316,7 +2490,7 @@ ledgerCmd
2316
2490
  ledgerCmd
2317
2491
  .command('stats')
2318
2492
  .description('Show aggregated ledger statistics')
2319
- .option('--json', 'Output as JSON')
2493
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2320
2494
  .action(async (options) => {
2321
2495
  try {
2322
2496
  const { getLedgerStats } = await import('./ledger.js');
@@ -2349,7 +2523,7 @@ ledgerCmd
2349
2523
  .command('trends')
2350
2524
  .description('Compare consecutive full-audit runs and report new vs fixed readings per rule')
2351
2525
  .option('--since <runId>', 'Only consider runs after this run ID')
2352
- .option('--json', 'Output as JSON')
2526
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2353
2527
  .action(async (options) => {
2354
2528
  try {
2355
2529
  const { getTrends } = await import('./ledger.js');
@@ -2412,9 +2586,6 @@ ledgerCmd
2412
2586
  // `audit --detach` forks workers/auditJobRunner; `status`/`result`/`jobs` read
2413
2587
  // the persisted ledger. One job model (the ledger run), one write path — these
2414
2588
  // are thin wrappers over ledger.ts + mcpAuditJobs.ts, not a second store.
2415
- /** Full analyzer set — derives from createAuditRunner's registry so `--detach`
2416
- * produces the same per-rule coverage as a synchronous `audit`. */
2417
- const DETACHED_DEFAULT_ANALYZERS = [...ALL_ANALYZERS];
2418
2589
  /** Lease TTL used by read-path reclaim, mirroring mcpAuditJobs.jobLeaseTtlMs. */
2419
2590
  const CLI_JOB_LEASE_TTL_MS = Number(process.env.CODE_AUDITOR_JOB_LEASE_TTL_MS) || 30_000;
2420
2591
  function spec41StatusBadge(status) {
@@ -2496,12 +2667,8 @@ async function runDetachedAudit(options) {
2496
2667
  const logFd = openSync(logPath, 'a');
2497
2668
  const argsJson = JSON.stringify({
2498
2669
  path: auditPath,
2499
- ...(options.partitionStrategy && { partitionStrategy: options.partitionStrategy }),
2500
- ...(options.maxPartitions && { maxPartitions: Number(options.maxPartitions) }),
2501
- ...(options.shardTimeoutMs && { shardTimeoutMs: Number(options.shardTimeoutMs) }),
2502
2670
  });
2503
2671
  const defaultsJson = JSON.stringify({
2504
- defaultAnalyzers: DETACHED_DEFAULT_ANALYZERS,
2505
2672
  defaultMinSeverity: 'high',
2506
2673
  defaultGenerateCodeMap: false,
2507
2674
  });
@@ -2521,7 +2688,7 @@ program
2521
2688
  .command('status <jobId>')
2522
2689
  .description('Show a detached audit job status, progress, and staleness')
2523
2690
  .option('-p, --path <path>', 'Project root containing the job (default: cwd)', process.cwd())
2524
- .option('--json', 'Output as JSON')
2691
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2525
2692
  .action(async (jobId, options) => {
2526
2693
  try {
2527
2694
  const { getLedgerRun, reclaimStaleRunning, computeStaleness } = await import('./ledger.js');
@@ -2584,12 +2751,12 @@ program
2584
2751
  .option('--analyzer <analyzer>', 'Filter by analyzer')
2585
2752
  .option('--file <file>', 'Filter by file path (substring match)')
2586
2753
  .option('--severity <severity>', 'Filter by severity (critical|severe|high)')
2587
- .option('--state [state]', 'Query coverage by state (fired|clean|notApplicable|cannot-fire|unassessed); omit value for all')
2754
+ .option('--state [state]', 'Query coverage by state (fired|clean|notApplicable|cannot-fire|incomplete); omit value for all')
2588
2755
  .option('--count', 'Group findings by analyzer/rule with counts')
2589
2756
  .option('--limit <n>', 'Max findings to return (0 = unbounded)', '50')
2590
2757
  .option('--offset <n>', 'Findings offset', '0')
2591
2758
  .option('--stale', 'Force full content re-hash for staleness')
2592
- .option('--json', 'Output as JSON')
2759
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2593
2760
  .action(async (jobId, options) => {
2594
2761
  try {
2595
2762
  const { getLedgerRun, queryLedgerFindings, queryLedgerCoverage, computeStaleness } = await import('./ledger.js');
@@ -2682,7 +2849,7 @@ program
2682
2849
  .option('--prune', 'Delete all but the newest runs for this project')
2683
2850
  .option('--keep <n>', 'Runs to keep when --prune is set', '10')
2684
2851
  .option('--limit <n>', 'Runs to display (staleness computed for these)', '20')
2685
- .option('--json', 'Output as JSON')
2852
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2686
2853
  .action(async (options) => {
2687
2854
  try {
2688
2855
  const { listLedgerRuns, reclaimStaleRunning, pruneLedgerRuns, computeStaleness } = await import('./ledger.js');
@@ -2738,7 +2905,7 @@ conventionsCmd
2738
2905
  .command('list')
2739
2906
  .description('List mined conventions from the code index')
2740
2907
  .option('--domain <domain>', 'Filter by domain: usage-pair, import-form, error-handling, export-shape, naming')
2741
- .option('--json', 'Output as JSON')
2908
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2742
2909
  .action(async (options) => {
2743
2910
  try {
2744
2911
  const db = CodeIndexDB.getInstance();
@@ -2802,7 +2969,7 @@ conventionsCmd
2802
2969
  .command('propose')
2803
2970
  .description('Emit ready-to-paste .codeauditor.json rules from mined conventions')
2804
2971
  .option('--domain <domain>', 'Filter by domain: naming or import-form (other domains do not map to rule kinds)')
2805
- .option('--json', 'Output as JSON')
2972
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2806
2973
  .action(async (options) => {
2807
2974
  try {
2808
2975
  const db = CodeIndexDB.getInstance();
@@ -2912,8 +3079,8 @@ program
2912
3079
  program
2913
3080
  .command('architecture')
2914
3081
  .description('Analyze import graph: community detection, directory purity, Martin instability metrics')
2915
- .option('--path <dir>', 'Project path', process.cwd())
2916
- .option('--json', 'Output as JSON')
3082
+ .option('--path <dir>', OPTION_PROJECT_PATH, process.cwd())
3083
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2917
3084
  .option('--format <format>', 'Output format: dot or mermaid (import graph colored by community)')
2918
3085
  .action(async (options) => {
2919
3086
  try {
@@ -3111,7 +3278,7 @@ coverageCmd
3111
3278
  coverageCmd
3112
3279
  .command('by-risk', { isDefault: true })
3113
3280
  .description('Show coverage rate by risk decile')
3114
- .option('--json', 'Output as JSON')
3281
+ .option('--json', OPTION_OUTPUT_AS_JSON)
3115
3282
  .action(async (options) => {
3116
3283
  try {
3117
3284
  const { generateCoverageReport } = await import('./coverage/coverageService.js');
@@ -3160,8 +3327,8 @@ coverageCmd
3160
3327
  program
3161
3328
  .command('print-config <file>')
3162
3329
  .description('Print the effective config for a file, naming the source of every value')
3163
- .option('-p, --path <path>', 'Project path', process.cwd())
3164
- .option('--json', 'Output as JSON')
3330
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
3331
+ .option('--json', OPTION_OUTPUT_AS_JSON)
3165
3332
  .option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
3166
3333
  .action(async (file, options) => {
3167
3334
  try {
@@ -3192,7 +3359,6 @@ program
3192
3359
  }
3193
3360
  const analyzerConfigs = (config.analyzerConfigs ?? {});
3194
3361
  const pathProfiles = (config.pathProfiles ?? []);
3195
- const enabledAnalyzers = (config.enabledAnalyzers ?? []);
3196
3362
  // Resolve --preset ids to ordered preset objects (Spec 38 R4). Unknown
3197
3363
  // ids are a hard error, not a silent skip.
3198
3364
  const presetIds = Array.isArray(options.preset) ? options.preset : (options.preset ? [options.preset] : []);
@@ -3212,7 +3378,6 @@ program
3212
3378
  projectRoot,
3213
3379
  analyzerConfigs,
3214
3380
  pathProfiles,
3215
- enabledAnalyzers,
3216
3381
  presets,
3217
3382
  lintConfig,
3218
3383
  });
@@ -3325,7 +3490,7 @@ program
3325
3490
  for (const v of sorted) {
3326
3491
  const icon = v.severity === 'critical' ? '🔴' :
3327
3492
  v.severity === 'severe' ? '🟠' : '🟡';
3328
- console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.rule || v.severity}] ${v.message}`);
3493
+ console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.rule || v.severity}] ${v.message}`);
3329
3494
  }
3330
3495
  }
3331
3496
  catch (error) {
@@ -3390,8 +3555,8 @@ const daemonCmd = program
3390
3555
  daemonCmd
3391
3556
  .command('start')
3392
3557
  .description('Start the daemon for this project (detached by default)')
3393
- .option('-p, --path <path>', 'Project root', process.cwd())
3394
- .option('-c, --config <config>', 'Configuration name')
3558
+ .option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
3559
+ .option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
3395
3560
  .option('--foreground', 'Run in the foreground (blocking; useful for debugging)')
3396
3561
  .action(async (options) => {
3397
3562
  try {
@@ -3422,7 +3587,7 @@ daemonCmd
3422
3587
  daemonCmd
3423
3588
  .command('stop')
3424
3589
  .description('Ask the daemon to shut down')
3425
- .option('-p, --path <path>', 'Project root', process.cwd())
3590
+ .option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
3426
3591
  .action(async (options) => {
3427
3592
  try {
3428
3593
  const { resolveDaemonSocketPath } = await import('./dataPaths.js');
@@ -3444,8 +3609,8 @@ daemonCmd
3444
3609
  daemonCmd
3445
3610
  .command('status')
3446
3611
  .description('Show daemon status')
3447
- .option('-p, --path <path>', 'Project root', process.cwd())
3448
- .option('--json', 'Output as JSON')
3612
+ .option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
3613
+ .option('--json', OPTION_OUTPUT_AS_JSON)
3449
3614
  .action(async (options) => {
3450
3615
  try {
3451
3616
  const { resolveDaemon } = await import('./daemon/resolve.js');
@@ -3638,6 +3803,136 @@ async function generateConfigurations(options) {
3638
3803
  console.log(chalk.gray(' 2. Run ') + chalk.cyan('code-audit') + chalk.gray(' to enforce your rules'));
3639
3804
  console.log(chalk.gray(' 3. Use ') + chalk.cyan('code-audit changed') + chalk.gray(' in your agent hook (gating rules block on new readings)'));
3640
3805
  }
3806
+ async function promptKindSpecificFields(kind, rule) {
3807
+ switch (kind) {
3808
+ case 'import-ban': {
3809
+ const { module } = await inquirer.prompt({
3810
+ module: {
3811
+ type: 'input',
3812
+ message: 'Banned module specifier (e.g. "lodash" or "@old-lib/*"):',
3813
+ validate: (input) => input.trim() ? true : 'Module specifier is required',
3814
+ },
3815
+ });
3816
+ rule.module = module;
3817
+ const { addExcept } = await inquirer.prompt({
3818
+ addExcept: {
3819
+ type: 'confirm',
3820
+ message: 'Add exception paths (files allowed to import it)?',
3821
+ default: false,
3822
+ },
3823
+ });
3824
+ if (addExcept) {
3825
+ const { except } = await inquirer.prompt({
3826
+ except: {
3827
+ type: 'input',
3828
+ message: 'Exception globs (comma-separated, e.g. "src/migration/**"):',
3829
+ },
3830
+ });
3831
+ const exceptList = except.split(',').map((s) => s.trim()).filter(Boolean);
3832
+ if (exceptList.length > 0)
3833
+ rule.except = exceptList;
3834
+ }
3835
+ break;
3836
+ }
3837
+ case 'call-constraint': {
3838
+ const { callee } = await inquirer.prompt({
3839
+ callee: {
3840
+ type: 'input',
3841
+ message: 'Callee (function name, optionally path-qualified as "path/glob#name"):',
3842
+ validate: (input) => input.trim() ? true : 'Callee is required',
3843
+ },
3844
+ });
3845
+ rule.callee = callee;
3846
+ const { mode } = await inquirer.prompt({
3847
+ mode: {
3848
+ type: 'list',
3849
+ message: 'Restriction mode:',
3850
+ choices: [
3851
+ { name: 'Allow only specific callers (allowFrom)', value: 'allow' },
3852
+ { name: 'Deny specific callers (denyFrom)', value: 'deny' },
3853
+ ],
3854
+ },
3855
+ });
3856
+ const { paths } = await inquirer.prompt({
3857
+ paths: {
3858
+ type: 'input',
3859
+ message: `Path globs (comma-separated) for ${mode === 'allow' ? 'allowFrom' : 'denyFrom'}:`,
3860
+ validate: (input) => input.trim() ? true : 'At least one path glob is required',
3861
+ },
3862
+ });
3863
+ const pathList = paths.split(',').map((s) => s.trim()).filter(Boolean);
3864
+ if (mode === 'allow') {
3865
+ rule.allowFrom = pathList;
3866
+ }
3867
+ else {
3868
+ rule.denyFrom = pathList;
3869
+ }
3870
+ break;
3871
+ }
3872
+ case 'module-boundary': {
3873
+ const { from, to } = await inquirer.prompt({
3874
+ from: {
3875
+ type: 'input',
3876
+ message: 'From (path glob for files that must not import):',
3877
+ validate: (input) => input.trim() ? true : '"from" path glob is required',
3878
+ },
3879
+ to: {
3880
+ type: 'input',
3881
+ message: 'To (path glob for files that must not be imported):',
3882
+ validate: (input) => input.trim() ? true : '"to" path glob is required',
3883
+ },
3884
+ });
3885
+ rule.from = from;
3886
+ rule.to = to;
3887
+ break;
3888
+ }
3889
+ case 'naming': {
3890
+ const { path, exports: exportPattern } = await inquirer.prompt({
3891
+ path: {
3892
+ type: 'input',
3893
+ message: 'Path (glob for files this rule applies to):',
3894
+ validate: (input) => input.trim() ? true : 'Path glob is required',
3895
+ },
3896
+ exports: {
3897
+ type: 'input',
3898
+ message: 'Exports regex (exported symbols must match, e.g. "^use[A-Z]"):',
3899
+ validate: (input) => input.trim() ? true : 'Exports regex is required',
3900
+ },
3901
+ });
3902
+ rule.path = path;
3903
+ rule.exports = exportPattern;
3904
+ break;
3905
+ }
3906
+ case 'ast-pattern': {
3907
+ const { pattern } = await inquirer.prompt({
3908
+ pattern: {
3909
+ type: 'input',
3910
+ message: 'AST pattern (ast-grep syntax, e.g. "new Function($$$)"):',
3911
+ validate: (input) => input.trim() ? true : 'Pattern is required',
3912
+ },
3913
+ });
3914
+ rule.pattern = pattern;
3915
+ const { addLanguage } = await inquirer.prompt({
3916
+ addLanguage: {
3917
+ type: 'confirm',
3918
+ message: 'Restrict to a specific language? (default: typescript)',
3919
+ default: false,
3920
+ },
3921
+ });
3922
+ if (addLanguage) {
3923
+ const { language } = await inquirer.prompt({
3924
+ language: {
3925
+ type: 'list',
3926
+ message: 'Language:',
3927
+ choices: ['typescript', 'javascript', 'go'],
3928
+ },
3929
+ });
3930
+ rule.language = language;
3931
+ }
3932
+ break;
3933
+ }
3934
+ }
3935
+ }
3641
3936
  /**
3642
3937
  * Interactive rule builder — walks the user through adding rules one at a time.
3643
3938
  */
@@ -3696,134 +3991,7 @@ async function buildRulesInteractively() {
3696
3991
  message: common.message,
3697
3992
  };
3698
3993
  // Kind-specific fields
3699
- switch (kind) {
3700
- case 'import-ban': {
3701
- const { module } = await inquirer.prompt({
3702
- module: {
3703
- type: 'input',
3704
- message: 'Banned module specifier (e.g. "lodash" or "@old-lib/*"):',
3705
- validate: (input) => input.trim() ? true : 'Module specifier is required',
3706
- },
3707
- });
3708
- rule.module = module;
3709
- const { addExcept } = await inquirer.prompt({
3710
- addExcept: {
3711
- type: 'confirm',
3712
- message: 'Add exception paths (files allowed to import it)?',
3713
- default: false,
3714
- },
3715
- });
3716
- if (addExcept) {
3717
- const { except } = await inquirer.prompt({
3718
- except: {
3719
- type: 'input',
3720
- message: 'Exception globs (comma-separated, e.g. "src/migration/**"):',
3721
- },
3722
- });
3723
- const exceptList = except.split(',').map((s) => s.trim()).filter(Boolean);
3724
- if (exceptList.length > 0)
3725
- rule.except = exceptList;
3726
- }
3727
- break;
3728
- }
3729
- case 'call-constraint': {
3730
- const { callee } = await inquirer.prompt({
3731
- callee: {
3732
- type: 'input',
3733
- message: 'Callee (function name, optionally path-qualified as "path/glob#name"):',
3734
- validate: (input) => input.trim() ? true : 'Callee is required',
3735
- },
3736
- });
3737
- rule.callee = callee;
3738
- const { mode } = await inquirer.prompt({
3739
- mode: {
3740
- type: 'list',
3741
- message: 'Restriction mode:',
3742
- choices: [
3743
- { name: 'Allow only specific callers (allowFrom)', value: 'allow' },
3744
- { name: 'Deny specific callers (denyFrom)', value: 'deny' },
3745
- ],
3746
- },
3747
- });
3748
- const { paths } = await inquirer.prompt({
3749
- paths: {
3750
- type: 'input',
3751
- message: `Path globs (comma-separated) for ${mode === 'allow' ? 'allowFrom' : 'denyFrom'}:`,
3752
- validate: (input) => input.trim() ? true : 'At least one path glob is required',
3753
- },
3754
- });
3755
- const pathList = paths.split(',').map((s) => s.trim()).filter(Boolean);
3756
- if (mode === 'allow') {
3757
- rule.allowFrom = pathList;
3758
- }
3759
- else {
3760
- rule.denyFrom = pathList;
3761
- }
3762
- break;
3763
- }
3764
- case 'module-boundary': {
3765
- const { from, to } = await inquirer.prompt({
3766
- from: {
3767
- type: 'input',
3768
- message: 'From (path glob for files that must not import):',
3769
- validate: (input) => input.trim() ? true : '"from" path glob is required',
3770
- },
3771
- to: {
3772
- type: 'input',
3773
- message: 'To (path glob for files that must not be imported):',
3774
- validate: (input) => input.trim() ? true : '"to" path glob is required',
3775
- },
3776
- });
3777
- rule.from = from;
3778
- rule.to = to;
3779
- break;
3780
- }
3781
- case 'naming': {
3782
- const { path, exports: exportPattern } = await inquirer.prompt({
3783
- path: {
3784
- type: 'input',
3785
- message: 'Path (glob for files this rule applies to):',
3786
- validate: (input) => input.trim() ? true : 'Path glob is required',
3787
- },
3788
- exports: {
3789
- type: 'input',
3790
- message: 'Exports regex (exported symbols must match, e.g. "^use[A-Z]"):',
3791
- validate: (input) => input.trim() ? true : 'Exports regex is required',
3792
- },
3793
- });
3794
- rule.path = path;
3795
- rule.exports = exportPattern;
3796
- break;
3797
- }
3798
- case 'ast-pattern': {
3799
- const { pattern } = await inquirer.prompt({
3800
- pattern: {
3801
- type: 'input',
3802
- message: 'AST pattern (ast-grep syntax, e.g. "new Function($$$)"):',
3803
- validate: (input) => input.trim() ? true : 'Pattern is required',
3804
- },
3805
- });
3806
- rule.pattern = pattern;
3807
- const { addLanguage } = await inquirer.prompt({
3808
- addLanguage: {
3809
- type: 'confirm',
3810
- message: 'Restrict to a specific language? (default: typescript)',
3811
- default: false,
3812
- },
3813
- });
3814
- if (addLanguage) {
3815
- const { language } = await inquirer.prompt({
3816
- language: {
3817
- type: 'list',
3818
- message: 'Language:',
3819
- choices: ['typescript', 'javascript', 'go'],
3820
- },
3821
- });
3822
- rule.language = language;
3823
- }
3824
- break;
3825
- }
3826
- }
3994
+ await promptKindSpecificFields(kind, rule);
3827
3995
  rules.push(rule);
3828
3996
  console.log(chalk.green(` ✓ Added rule "${rule.id}" [${rule.kind}]`));
3829
3997
  const { cont } = await inquirer.prompt({
@@ -3956,7 +4124,7 @@ async function runSearch(query, options) {
3956
4124
  await db.initialize();
3957
4125
  // --definition: look up a specific symbol
3958
4126
  if (options.definition) {
3959
- const func = await db.findDefinition(query);
4127
+ const func = await db.functionIndex.findDefinition(query);
3960
4128
  if (!func) {
3961
4129
  console.log(chalk.yellow(`No definition found for "${query}"`));
3962
4130
  return;
@@ -3968,9 +4136,6 @@ async function runSearch(query, options) {
3968
4136
  console.log(chalk.blue(`\nDefinition: ${func.name}`));
3969
4137
  console.log(chalk.gray('──────────────────────────────────────────'));
3970
4138
  console.log(`${chalk.bold('File:')} ${func.filePath}${func.lineNumber ? `:${func.lineNumber}` : ''}`);
3971
- if (func.signature) {
3972
- console.log(`${chalk.bold('Signature:')} ${func.signature}`);
3973
- }
3974
4139
  if (func.purpose) {
3975
4140
  console.log(`${chalk.bold('Purpose:')} ${func.purpose}`);
3976
4141
  }
@@ -4002,7 +4167,7 @@ async function runSearch(query, options) {
4002
4167
  }
4003
4168
  searchOptions.filters.language = options.language;
4004
4169
  }
4005
- const result = await db.searchFunctions(searchOptions);
4170
+ const result = await db.search.searchFunctions(searchOptions);
4006
4171
  if (options.json) {
4007
4172
  process.stdout.write(JSON.stringify({
4008
4173
  query,
@@ -4025,9 +4190,6 @@ async function runSearch(query, options) {
4025
4190
  const lang = func.language ? chalk.dim(` [${func.language}]`) : '';
4026
4191
  const lineInfo = func.lineNumber ? chalk.dim(`:${func.lineNumber}`) : '';
4027
4192
  console.log(`${chalk.bold(func.name)}${lang} — ${chalk.green(func.filePath)}${lineInfo}`);
4028
- if (func.signature) {
4029
- console.log(chalk.dim(` ${func.signature}`));
4030
- }
4031
4193
  if (func.purpose) {
4032
4194
  console.log(chalk.dim(` ${func.purpose}`));
4033
4195
  }