code-auditor-mcp 4.1.1 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1082) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +338 -0
  3. package/README.md +4 -4
  4. package/configs/hhra-compat.json +0 -8
  5. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +1 -1
  6. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +1 -1
  7. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +7 -1
  8. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +1 -1
  9. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +2 -8
  10. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +1 -1
  11. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +4 -4
  12. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +1 -1
  13. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +1 -1
  14. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +1 -1
  15. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +1 -1
  16. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +2 -2
  17. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +1 -1
  18. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +1 -1
  19. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +1 -1
  20. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +1 -1
  21. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +1 -1
  22. package/dist/analyzers/applicability.d.ts +42 -0
  23. package/dist/analyzers/applicability.d.ts.map +1 -1
  24. package/dist/analyzers/applicability.js +74 -0
  25. package/dist/analyzers/applicability.js.map +1 -1
  26. package/dist/analyzers/cross-language/APIContractAnalyzer.js +18 -24
  27. package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
  28. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +30 -23
  29. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
  30. package/dist/analyzers/cross-language/DependencyGraphBuilder.js +113 -76
  31. package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
  32. package/dist/analyzers/cross-language/SchemaValidator.d.ts +2 -0
  33. package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
  34. package/dist/analyzers/cross-language/SchemaValidator.js +8 -3
  35. package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
  36. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
  37. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +210 -148
  38. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
  39. package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
  40. package/dist/analyzers/documentationAnalyzer.js +23 -28
  41. package/dist/analyzers/documentationAnalyzer.js.map +1 -1
  42. package/dist/analyzers/handleIdentification.d.ts +356 -0
  43. package/dist/analyzers/handleIdentification.d.ts.map +1 -0
  44. package/dist/analyzers/handleIdentification.js +424 -0
  45. package/dist/analyzers/handleIdentification.js.map +1 -0
  46. package/dist/analyzers/manifestStaleness.d.ts +64 -0
  47. package/dist/analyzers/manifestStaleness.d.ts.map +1 -0
  48. package/dist/analyzers/manifestStaleness.js +115 -0
  49. package/dist/analyzers/manifestStaleness.js.map +1 -0
  50. package/dist/analyzers/orgFilterTiers.d.ts +73 -1
  51. package/dist/analyzers/orgFilterTiers.d.ts.map +1 -1
  52. package/dist/analyzers/orgFilterTiers.js +166 -1
  53. package/dist/analyzers/orgFilterTiers.js.map +1 -1
  54. package/dist/analyzers/provenance.d.ts +251 -57
  55. package/dist/analyzers/provenance.d.ts.map +1 -1
  56. package/dist/analyzers/provenance.js +1109 -429
  57. package/dist/analyzers/provenance.js.map +1 -1
  58. package/dist/analyzers/reactAnalyzer.d.ts +2 -0
  59. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  60. package/dist/analyzers/reactAnalyzer.js +2 -0
  61. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  62. package/dist/analyzers/receiverResolution.d.ts +183 -0
  63. package/dist/analyzers/receiverResolution.d.ts.map +1 -0
  64. package/dist/analyzers/receiverResolution.js +540 -0
  65. package/dist/analyzers/receiverResolution.js.map +1 -0
  66. package/dist/analyzers/receiverRoot.d.ts +328 -0
  67. package/dist/analyzers/receiverRoot.d.ts.map +1 -0
  68. package/dist/analyzers/receiverRoot.js +990 -0
  69. package/dist/analyzers/receiverRoot.js.map +1 -0
  70. package/dist/analyzers/ruleRegistry.d.ts +12 -68
  71. package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
  72. package/dist/analyzers/ruleRegistry.js +32 -342
  73. package/dist/analyzers/ruleRegistry.js.map +1 -1
  74. package/dist/analyzers/ruleTiming.d.ts +10 -0
  75. package/dist/analyzers/ruleTiming.d.ts.map +1 -1
  76. package/dist/analyzers/ruleTiming.js +20 -0
  77. package/dist/analyzers/ruleTiming.js.map +1 -1
  78. package/dist/analyzers/sqlLiteral.d.ts +76 -0
  79. package/dist/analyzers/sqlLiteral.d.ts.map +1 -0
  80. package/dist/analyzers/sqlLiteral.js +419 -0
  81. package/dist/analyzers/sqlLiteral.js.map +1 -0
  82. package/dist/analyzers/tsEcosystem.d.ts +115 -0
  83. package/dist/analyzers/tsEcosystem.d.ts.map +1 -0
  84. package/dist/analyzers/tsEcosystem.js +231 -0
  85. package/dist/analyzers/tsEcosystem.js.map +1 -0
  86. package/dist/analyzers/tsExpressionDescriptor.d.ts +156 -0
  87. package/dist/analyzers/tsExpressionDescriptor.d.ts.map +1 -0
  88. package/dist/analyzers/tsExpressionDescriptor.js +374 -0
  89. package/dist/analyzers/tsExpressionDescriptor.js.map +1 -0
  90. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +6 -0
  91. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
  92. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +107 -44
  93. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
  94. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.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 +64 -1
  161. package/dist/auditRunner.d.ts.map +1 -1
  162. package/dist/auditRunner.js +1538 -1033
  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 +969 -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 +2 -0
  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/hooks/codex.d.ts +8 -1
  409. package/dist/hooks/codex.d.ts.map +1 -1
  410. package/dist/hooks/codex.js +7 -0
  411. package/dist/hooks/codex.js.map +1 -1
  412. package/dist/hooks/core.d.ts +6 -0
  413. package/dist/hooks/core.d.ts.map +1 -1
  414. package/dist/hooks/core.js +6 -1
  415. package/dist/hooks/core.js.map +1 -1
  416. package/dist/hooks/cursor.d.ts +11 -1
  417. package/dist/hooks/cursor.d.ts.map +1 -1
  418. package/dist/hooks/cursor.js +10 -0
  419. package/dist/hooks/cursor.js.map +1 -1
  420. package/dist/index.d.ts +4 -0
  421. package/dist/index.d.ts.map +1 -1
  422. package/dist/index.js +4 -0
  423. package/dist/index.js.map +1 -1
  424. package/dist/installConfig.d.ts +23 -5
  425. package/dist/installConfig.d.ts.map +1 -1
  426. package/dist/installConfig.js +23 -5
  427. package/dist/installConfig.js.map +1 -1
  428. package/dist/installer.d.ts +6 -0
  429. package/dist/installer.d.ts.map +1 -1
  430. package/dist/installer.js +6 -0
  431. package/dist/installer.js.map +1 -1
  432. package/dist/invariants/ruleEngine.d.ts +3 -0
  433. package/dist/invariants/ruleEngine.d.ts.map +1 -1
  434. package/dist/invariants/ruleEngine.js +156 -119
  435. package/dist/invariants/ruleEngine.js.map +1 -1
  436. package/dist/invariants/ruleValidator.d.ts +3 -0
  437. package/dist/invariants/ruleValidator.d.ts.map +1 -1
  438. package/dist/invariants/ruleValidator.js +185 -178
  439. package/dist/invariants/ruleValidator.js.map +1 -1
  440. package/dist/languages/LanguageOrchestrator.d.ts +4 -1
  441. package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
  442. package/dist/languages/LanguageOrchestrator.js +8 -19
  443. package/dist/languages/LanguageOrchestrator.js.map +1 -1
  444. package/dist/languages/RuntimeManager.d.ts +8 -0
  445. package/dist/languages/RuntimeManager.d.ts.map +1 -1
  446. package/dist/languages/RuntimeManager.js +55 -5
  447. package/dist/languages/RuntimeManager.js.map +1 -1
  448. package/dist/languages/UniversalAnalyzer.js +1 -1
  449. package/dist/languages/UniversalAnalyzer.js.map +1 -1
  450. package/dist/languages/adapterBridge.d.ts +4 -0
  451. package/dist/languages/adapterBridge.d.ts.map +1 -1
  452. package/dist/languages/adapterBridge.js +37 -3
  453. package/dist/languages/adapterBridge.js.map +1 -1
  454. package/dist/languages/go/GoAdapter.d.ts.map +1 -1
  455. package/dist/languages/go/GoAdapter.js +11 -14
  456. package/dist/languages/go/GoAdapter.js.map +1 -1
  457. package/dist/languages/go/analyzer-darwin-amd64 +0 -0
  458. package/dist/languages/go/analyzer-darwin-arm64 +0 -0
  459. package/dist/languages/go/analyzer-linux-amd64 +0 -0
  460. package/dist/languages/go/analyzer-linux-arm64 +0 -0
  461. package/dist/languages/go/analyzer-src/analyzer.go +2 -0
  462. package/dist/languages/go/analyzer-src/dataaccess.go +388 -0
  463. package/dist/languages/go/analyzer-src/types.go +8 -0
  464. package/dist/languages/go/analyzer-windows-amd64.exe +0 -0
  465. package/dist/languages/go/database-packages.json +8 -0
  466. package/dist/languages/go/goResolution.d.ts +229 -0
  467. package/dist/languages/go/goResolution.d.ts.map +1 -0
  468. package/dist/languages/go/goResolution.js +799 -0
  469. package/dist/languages/go/goResolution.js.map +1 -0
  470. package/dist/languages/go/stdlib-packages.json +353 -0
  471. package/dist/languages/go/tenantInputs.d.ts +37 -0
  472. package/dist/languages/go/tenantInputs.d.ts.map +1 -0
  473. package/dist/languages/go/tenantInputs.js +121 -0
  474. package/dist/languages/go/tenantInputs.js.map +1 -0
  475. package/dist/languages/index.d.ts +1 -0
  476. package/dist/languages/index.d.ts.map +1 -1
  477. package/dist/languages/index.js +5 -0
  478. package/dist/languages/index.js.map +1 -1
  479. package/dist/languages/json/JsonAdapter.d.ts +177 -0
  480. package/dist/languages/json/JsonAdapter.d.ts.map +1 -0
  481. package/dist/languages/json/JsonAdapter.js +531 -0
  482. package/dist/languages/json/JsonAdapter.js.map +1 -0
  483. package/dist/languages/locationIndex.d.ts +41 -0
  484. package/dist/languages/locationIndex.d.ts.map +1 -0
  485. package/dist/languages/locationIndex.js +42 -0
  486. package/dist/languages/locationIndex.js.map +1 -0
  487. package/dist/languages/sql/dialectDetection.d.ts +57 -0
  488. package/dist/languages/sql/dialectDetection.d.ts.map +1 -0
  489. package/dist/languages/sql/dialectDetection.js +135 -0
  490. package/dist/languages/sql/dialectDetection.js.map +1 -0
  491. package/dist/languages/sql/sqlAst.d.ts +494 -0
  492. package/dist/languages/sql/sqlAst.d.ts.map +1 -0
  493. package/dist/languages/sql/sqlAst.js +1817 -0
  494. package/dist/languages/sql/sqlAst.js.map +1 -0
  495. package/dist/languages/testConventions.d.ts +12 -1
  496. package/dist/languages/testConventions.d.ts.map +1 -1
  497. package/dist/languages/testConventions.js +13 -1
  498. package/dist/languages/testConventions.js.map +1 -1
  499. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +1 -1
  500. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -1
  501. package/dist/languages/tree-sitter/converter.js +2 -2
  502. package/dist/languages/tree-sitter/converter.js.map +1 -1
  503. package/dist/languages/tree-sitter/parser.d.ts +3 -4
  504. package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
  505. package/dist/languages/tree-sitter/parser.js +10 -4
  506. package/dist/languages/tree-sitter/parser.js.map +1 -1
  507. package/dist/languages/tree-sitter/rawNode.d.ts +3 -0
  508. package/dist/languages/tree-sitter/rawNode.d.ts.map +1 -1
  509. package/dist/languages/tree-sitter/rawNode.js +3 -0
  510. package/dist/languages/tree-sitter/rawNode.js.map +1 -1
  511. package/dist/languages/types.d.ts +7 -0
  512. package/dist/languages/types.d.ts.map +1 -1
  513. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +36 -0
  514. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  515. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +168 -45
  516. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  517. package/dist/languages/typescript/database-packages.json +19 -0
  518. package/dist/ledger.d.ts +118 -2
  519. package/dist/ledger.d.ts.map +1 -1
  520. package/dist/ledger.js +167 -45
  521. package/dist/ledger.js.map +1 -1
  522. package/dist/legacyIndexNotice.d.ts +32 -0
  523. package/dist/legacyIndexNotice.d.ts.map +1 -0
  524. package/dist/legacyIndexNotice.js +89 -0
  525. package/dist/legacyIndexNotice.js.map +1 -0
  526. package/dist/mcp-tools/discoveryQueries.d.ts +55 -0
  527. package/dist/mcp-tools/discoveryQueries.d.ts.map +1 -0
  528. package/dist/mcp-tools/discoveryQueries.js +157 -0
  529. package/dist/mcp-tools/discoveryQueries.js.map +1 -0
  530. package/dist/mcp-tools/projectTasks.d.ts +9 -123
  531. package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
  532. package/dist/mcp-tools/projectTasks.js +180 -170
  533. package/dist/mcp-tools/projectTasks.js.map +1 -1
  534. package/dist/mcp-tools/whitelistTools.d.ts +20 -0
  535. package/dist/mcp-tools/whitelistTools.d.ts.map +1 -1
  536. package/dist/mcp-tools/whitelistTools.js +20 -0
  537. package/dist/mcp-tools/whitelistTools.js.map +1 -1
  538. package/dist/mcp-tools/workflowGuide.d.ts +9 -0
  539. package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
  540. package/dist/mcp-tools/workflowGuide.js +13 -5
  541. package/dist/mcp-tools/workflowGuide.js.map +1 -1
  542. package/dist/mcp-tools-shared.d.ts +78 -0
  543. package/dist/mcp-tools-shared.d.ts.map +1 -1
  544. package/dist/mcp-tools-shared.js +203 -340
  545. package/dist/mcp-tools-shared.js.map +1 -1
  546. package/dist/mcp-ui-simple.d.ts +2 -0
  547. package/dist/mcp-ui-simple.d.ts.map +1 -1
  548. package/dist/mcp-ui-simple.js +12 -5
  549. package/dist/mcp-ui-simple.js.map +1 -1
  550. package/dist/mcp.d.ts +6 -0
  551. package/dist/mcp.d.ts.map +1 -1
  552. package/dist/mcp.js +827 -850
  553. package/dist/mcp.js.map +1 -1
  554. package/dist/mcpAuditJobs.d.ts +29 -33
  555. package/dist/mcpAuditJobs.d.ts.map +1 -1
  556. package/dist/mcpAuditJobs.js +102 -791
  557. package/dist/mcpAuditJobs.js.map +1 -1
  558. package/dist/mcpAutoIndex.d.ts +5 -0
  559. package/dist/mcpAutoIndex.d.ts.map +1 -1
  560. package/dist/mcpAutoIndex.js +7 -2
  561. package/dist/mcpAutoIndex.js.map +1 -1
  562. package/dist/mcpDiagnostics.d.ts +7 -0
  563. package/dist/mcpDiagnostics.d.ts.map +1 -1
  564. package/dist/mcpDiagnostics.js +34 -9
  565. package/dist/mcpDiagnostics.js.map +1 -1
  566. package/dist/mcpToolErrors.d.ts +16 -0
  567. package/dist/mcpToolErrors.d.ts.map +1 -1
  568. package/dist/mcpToolErrors.js +16 -0
  569. package/dist/mcpToolErrors.js.map +1 -1
  570. package/dist/nextFile.d.ts +6 -0
  571. package/dist/nextFile.d.ts.map +1 -1
  572. package/dist/nextFile.js +6 -0
  573. package/dist/nextFile.js.map +1 -1
  574. package/dist/nextFileIncremental.d.ts +44 -4
  575. package/dist/nextFileIncremental.d.ts.map +1 -1
  576. package/dist/nextFileIncremental.js +47 -7
  577. package/dist/nextFileIncremental.js.map +1 -1
  578. package/dist/phase/batchFunctions.d.ts +38 -0
  579. package/dist/phase/batchFunctions.d.ts.map +1 -0
  580. package/dist/phase/batchFunctions.js +64 -0
  581. package/dist/phase/batchFunctions.js.map +1 -0
  582. package/dist/phase/checks.d.ts +39 -0
  583. package/dist/phase/checks.d.ts.map +1 -0
  584. package/dist/phase/checks.js +48 -0
  585. package/dist/phase/checks.js.map +1 -0
  586. package/dist/phase/codeBlocks.d.ts +25 -0
  587. package/dist/phase/codeBlocks.d.ts.map +1 -0
  588. package/dist/phase/codeBlocks.js +268 -0
  589. package/dist/phase/codeBlocks.js.map +1 -0
  590. package/dist/phase/colorMath.d.ts +41 -0
  591. package/dist/phase/colorMath.d.ts.map +1 -0
  592. package/dist/phase/colorMath.js +109 -0
  593. package/dist/phase/colorMath.js.map +1 -0
  594. package/dist/phase/config.d.ts +29 -0
  595. package/dist/phase/config.d.ts.map +1 -0
  596. package/dist/phase/config.js +54 -0
  597. package/dist/phase/config.js.map +1 -0
  598. package/dist/phase/consumed.d.ts +47 -0
  599. package/dist/phase/consumed.d.ts.map +1 -0
  600. package/dist/phase/consumed.js +22 -0
  601. package/dist/phase/consumed.js.map +1 -0
  602. package/dist/phase/conventionMining.d.ts +59 -0
  603. package/dist/phase/conventionMining.d.ts.map +1 -0
  604. package/dist/phase/conventionMining.js +152 -0
  605. package/dist/phase/conventionMining.js.map +1 -0
  606. package/dist/phase/coverage.d.ts +65 -0
  607. package/dist/phase/coverage.d.ts.map +1 -0
  608. package/dist/phase/coverage.js +121 -0
  609. package/dist/phase/coverage.js.map +1 -0
  610. package/dist/phase/dag.d.ts +49 -0
  611. package/dist/phase/dag.d.ts.map +1 -0
  612. package/dist/phase/dag.js +120 -0
  613. package/dist/phase/dag.js.map +1 -0
  614. package/dist/phase/dataAccessCalls.d.ts +33 -0
  615. package/dist/phase/dataAccessCalls.d.ts.map +1 -0
  616. package/dist/phase/dataAccessCalls.js +42 -0
  617. package/dist/phase/dataAccessCalls.js.map +1 -0
  618. package/dist/phase/dataAccessCallsCandidates.d.ts +25 -0
  619. package/dist/phase/dataAccessCallsCandidates.d.ts.map +1 -0
  620. package/dist/phase/dataAccessCallsCandidates.js +27 -0
  621. package/dist/phase/dataAccessCallsCandidates.js.map +1 -0
  622. package/dist/phase/dynamicSql.d.ts +18 -0
  623. package/dist/phase/dynamicSql.d.ts.map +1 -0
  624. package/dist/phase/dynamicSql.js +20 -0
  625. package/dist/phase/dynamicSql.js.map +1 -0
  626. package/dist/phase/exportForm.d.ts +23 -0
  627. package/dist/phase/exportForm.d.ts.map +1 -0
  628. package/dist/phase/exportForm.js +29 -0
  629. package/dist/phase/exportForm.js.map +1 -0
  630. package/dist/phase/exportSymbols.d.ts +21 -0
  631. package/dist/phase/exportSymbols.d.ts.map +1 -0
  632. package/dist/phase/exportSymbols.js +28 -0
  633. package/dist/phase/exportSymbols.js.map +1 -0
  634. package/dist/phase/fileHeader.d.ts +15 -0
  635. package/dist/phase/fileHeader.d.ts.map +1 -0
  636. package/dist/phase/fileHeader.js +33 -0
  637. package/dist/phase/fileHeader.js.map +1 -0
  638. package/dist/phase/fileImports.d.ts +39 -0
  639. package/dist/phase/fileImports.d.ts.map +1 -0
  640. package/dist/phase/fileImports.js +138 -0
  641. package/dist/phase/fileImports.js.map +1 -0
  642. package/dist/phase/fileSymbols.d.ts +25 -0
  643. package/dist/phase/fileSymbols.d.ts.map +1 -0
  644. package/dist/phase/fileSymbols.js +302 -0
  645. package/dist/phase/fileSymbols.js.map +1 -0
  646. package/dist/phase/functionBodies.d.ts +28 -0
  647. package/dist/phase/functionBodies.d.ts.map +1 -0
  648. package/dist/phase/functionBodies.js +67 -0
  649. package/dist/phase/functionBodies.js.map +1 -0
  650. package/dist/phase/functionIndex.d.ts +28 -0
  651. package/dist/phase/functionIndex.d.ts.map +1 -0
  652. package/dist/phase/functionIndex.js +152 -0
  653. package/dist/phase/functionIndex.js.map +1 -0
  654. package/dist/phase/goFunctionAnalysis.d.ts +47 -0
  655. package/dist/phase/goFunctionAnalysis.d.ts.map +1 -0
  656. package/dist/phase/goFunctionAnalysis.js +291 -0
  657. package/dist/phase/goFunctionAnalysis.js.map +1 -0
  658. package/dist/phase/goFunctions.d.ts +38 -0
  659. package/dist/phase/goFunctions.d.ts.map +1 -0
  660. package/dist/phase/goFunctions.js +154 -0
  661. package/dist/phase/goFunctions.js.map +1 -0
  662. package/dist/phase/goImports.d.ts +29 -0
  663. package/dist/phase/goImports.d.ts.map +1 -0
  664. package/dist/phase/goImports.js +68 -0
  665. package/dist/phase/goImports.js.map +1 -0
  666. package/dist/phase/goPackageBindings.d.ts +22 -0
  667. package/dist/phase/goPackageBindings.d.ts.map +1 -0
  668. package/dist/phase/goPackageBindings.js +30 -0
  669. package/dist/phase/goPackageBindings.js.map +1 -0
  670. package/dist/phase/goSwitches.d.ts +28 -0
  671. package/dist/phase/goSwitches.d.ts.map +1 -0
  672. package/dist/phase/goSwitches.js +52 -0
  673. package/dist/phase/goSwitches.js.map +1 -0
  674. package/dist/phase/importForm.d.ts +20 -0
  675. package/dist/phase/importForm.d.ts.map +1 -0
  676. package/dist/phase/importForm.js +27 -0
  677. package/dist/phase/importForm.js.map +1 -0
  678. package/dist/phase/importSpecifiers.d.ts +20 -0
  679. package/dist/phase/importSpecifiers.d.ts.map +1 -0
  680. package/dist/phase/importSpecifiers.js +30 -0
  681. package/dist/phase/importSpecifiers.js.map +1 -0
  682. package/dist/phase/imports.d.ts +18 -0
  683. package/dist/phase/imports.d.ts.map +1 -0
  684. package/dist/phase/imports.js +25 -0
  685. package/dist/phase/imports.js.map +1 -0
  686. package/dist/phase/jsonDocument.d.ts +26 -0
  687. package/dist/phase/jsonDocument.d.ts.map +1 -0
  688. package/dist/phase/jsonDocument.js +36 -0
  689. package/dist/phase/jsonDocument.js.map +1 -0
  690. package/dist/phase/localBinding.d.ts +33 -0
  691. package/dist/phase/localBinding.d.ts.map +1 -0
  692. package/dist/phase/localBinding.js +208 -0
  693. package/dist/phase/localBinding.js.map +1 -0
  694. package/dist/phase/loopQueries.d.ts +31 -0
  695. package/dist/phase/loopQueries.d.ts.map +1 -0
  696. package/dist/phase/loopQueries.js +44 -0
  697. package/dist/phase/loopQueries.js.map +1 -0
  698. package/dist/phase/loopQueryCandidates.d.ts +26 -0
  699. package/dist/phase/loopQueryCandidates.d.ts.map +1 -0
  700. package/dist/phase/loopQueryCandidates.js +28 -0
  701. package/dist/phase/loopQueryCandidates.js.map +1 -0
  702. package/dist/phase/oracles.d.ts +216 -0
  703. package/dist/phase/oracles.d.ts.map +1 -0
  704. package/dist/phase/oracles.js +484 -0
  705. package/dist/phase/oracles.js.map +1 -0
  706. package/dist/phase/phaseModel.d.ts +190 -0
  707. package/dist/phase/phaseModel.d.ts.map +1 -0
  708. package/dist/phase/phaseModel.js +468 -0
  709. package/dist/phase/phaseModel.js.map +1 -0
  710. package/dist/phase/producers.d.ts +450 -0
  711. package/dist/phase/producers.d.ts.map +1 -0
  712. package/dist/phase/producers.js +1027 -0
  713. package/dist/phase/producers.js.map +1 -0
  714. package/dist/phase/querySiteCandidates.d.ts +27 -0
  715. package/dist/phase/querySiteCandidates.d.ts.map +1 -0
  716. package/dist/phase/querySiteCandidates.js +66 -0
  717. package/dist/phase/querySiteCandidates.js.map +1 -0
  718. package/dist/phase/querySites.d.ts +30 -0
  719. package/dist/phase/querySites.d.ts.map +1 -0
  720. package/dist/phase/querySites.js +93 -0
  721. package/dist/phase/querySites.js.map +1 -0
  722. package/dist/phase/reachability.d.ts +61 -0
  723. package/dist/phase/reachability.d.ts.map +1 -0
  724. package/dist/phase/reachability.js +104 -0
  725. package/dist/phase/reachability.js.map +1 -0
  726. package/dist/phase/reactComponents.d.ts +33 -0
  727. package/dist/phase/reactComponents.d.ts.map +1 -0
  728. package/dist/phase/reactComponents.js +47 -0
  729. package/dist/phase/reactComponents.js.map +1 -0
  730. package/dist/phase/receiverActivity.d.ts +35 -0
  731. package/dist/phase/receiverActivity.d.ts.map +1 -0
  732. package/dist/phase/receiverActivity.js +66 -0
  733. package/dist/phase/receiverActivity.js.map +1 -0
  734. package/dist/phase/receiverConsumers.d.ts +200 -0
  735. package/dist/phase/receiverConsumers.d.ts.map +1 -0
  736. package/dist/phase/receiverConsumers.js +1025 -0
  737. package/dist/phase/receiverConsumers.js.map +1 -0
  738. package/dist/phase/receiverProvenance.d.ts +97 -0
  739. package/dist/phase/receiverProvenance.d.ts.map +1 -0
  740. package/dist/phase/receiverProvenance.js +337 -0
  741. package/dist/phase/receiverProvenance.js.map +1 -0
  742. package/dist/phase/routing.d.ts +47 -0
  743. package/dist/phase/routing.d.ts.map +1 -0
  744. package/dist/phase/routing.js +62 -0
  745. package/dist/phase/routing.js.map +1 -0
  746. package/dist/phase/rules/conventions.d.ts +61 -0
  747. package/dist/phase/rules/conventions.d.ts.map +1 -0
  748. package/dist/phase/rules/conventions.js +403 -0
  749. package/dist/phase/rules/conventions.js.map +1 -0
  750. package/dist/phase/rules/crossDomain.d.ts +102 -0
  751. package/dist/phase/rules/crossDomain.d.ts.map +1 -0
  752. package/dist/phase/rules/crossDomain.js +918 -0
  753. package/dist/phase/rules/crossDomain.js.map +1 -0
  754. package/dist/phase/rules/dataAccess.d.ts +62 -0
  755. package/dist/phase/rules/dataAccess.d.ts.map +1 -0
  756. package/dist/phase/rules/dataAccess.js +480 -0
  757. package/dist/phase/rules/dataAccess.js.map +1 -0
  758. package/dist/phase/rules/dependencyGraph.d.ts +44 -0
  759. package/dist/phase/rules/dependencyGraph.d.ts.map +1 -0
  760. package/dist/phase/rules/dependencyGraph.js +260 -0
  761. package/dist/phase/rules/dependencyGraph.js.map +1 -0
  762. package/dist/phase/rules/documentation.d.ts +45 -0
  763. package/dist/phase/rules/documentation.d.ts.map +1 -0
  764. package/dist/phase/rules/documentation.js +340 -0
  765. package/dist/phase/rules/documentation.js.map +1 -0
  766. package/dist/phase/rules/dry.d.ts +109 -0
  767. package/dist/phase/rules/dry.d.ts.map +1 -0
  768. package/dist/phase/rules/dry.js +803 -0
  769. package/dist/phase/rules/dry.js.map +1 -0
  770. package/dist/phase/rules/fileDocumentation.d.ts +36 -0
  771. package/dist/phase/rules/fileDocumentation.d.ts.map +1 -0
  772. package/dist/phase/rules/fileDocumentation.js +134 -0
  773. package/dist/phase/rules/fileDocumentation.js.map +1 -0
  774. package/dist/phase/rules/functionBodies.d.ts +33 -0
  775. package/dist/phase/rules/functionBodies.d.ts.map +1 -0
  776. package/dist/phase/rules/functionBodies.js +71 -0
  777. package/dist/phase/rules/functionBodies.js.map +1 -0
  778. package/dist/phase/rules/goRules.d.ts +49 -0
  779. package/dist/phase/rules/goRules.d.ts.map +1 -0
  780. package/dist/phase/rules/goRules.js +209 -0
  781. package/dist/phase/rules/goRules.js.map +1 -0
  782. package/dist/phase/rules/querySites.d.ts +29 -0
  783. package/dist/phase/rules/querySites.d.ts.map +1 -0
  784. package/dist/phase/rules/querySites.js +94 -0
  785. package/dist/phase/rules/querySites.js.map +1 -0
  786. package/dist/phase/rules/react.d.ts +38 -0
  787. package/dist/phase/rules/react.d.ts.map +1 -0
  788. package/dist/phase/rules/react.js +202 -0
  789. package/dist/phase/rules/react.js.map +1 -0
  790. package/dist/phase/rules/registry.d.ts +170 -0
  791. package/dist/phase/rules/registry.d.ts.map +1 -0
  792. package/dist/phase/rules/registry.js +103 -0
  793. package/dist/phase/rules/registry.js.map +1 -0
  794. package/dist/phase/rules/schema.d.ts +79 -0
  795. package/dist/phase/rules/schema.d.ts.map +1 -0
  796. package/dist/phase/rules/schema.js +273 -0
  797. package/dist/phase/rules/schema.js.map +1 -0
  798. package/dist/phase/rules/schemaJson.d.ts +32 -0
  799. package/dist/phase/rules/schemaJson.d.ts.map +1 -0
  800. package/dist/phase/rules/schemaJson.js +81 -0
  801. package/dist/phase/rules/schemaJson.js.map +1 -0
  802. package/dist/phase/rules/schemaValidator.d.ts +38 -0
  803. package/dist/phase/rules/schemaValidator.d.ts.map +1 -0
  804. package/dist/phase/rules/schemaValidator.js +107 -0
  805. package/dist/phase/rules/schemaValidator.js.map +1 -0
  806. package/dist/phase/rules/secrets.d.ts +34 -0
  807. package/dist/phase/rules/secrets.d.ts.map +1 -0
  808. package/dist/phase/rules/secrets.js +208 -0
  809. package/dist/phase/rules/secrets.js.map +1 -0
  810. package/dist/phase/rules/security.d.ts +30 -0
  811. package/dist/phase/rules/security.d.ts.map +1 -0
  812. package/dist/phase/rules/security.js +85 -0
  813. package/dist/phase/rules/security.js.map +1 -0
  814. package/dist/phase/rules/securityDefects.d.ts +37 -0
  815. package/dist/phase/rules/securityDefects.d.ts.map +1 -0
  816. package/dist/phase/rules/securityDefects.js +167 -0
  817. package/dist/phase/rules/securityDefects.js.map +1 -0
  818. package/dist/phase/rules/solid.d.ts +61 -0
  819. package/dist/phase/rules/solid.d.ts.map +1 -0
  820. package/dist/phase/rules/solid.js +562 -0
  821. package/dist/phase/rules/solid.js.map +1 -0
  822. package/dist/phase/rules/styles.d.ts +77 -0
  823. package/dist/phase/rules/styles.d.ts.map +1 -0
  824. package/dist/phase/rules/styles.js +868 -0
  825. package/dist/phase/rules/styles.js.map +1 -0
  826. package/dist/phase/rules/unreferencedModule.d.ts +26 -0
  827. package/dist/phase/rules/unreferencedModule.d.ts.map +1 -0
  828. package/dist/phase/rules/unreferencedModule.js +61 -0
  829. package/dist/phase/rules/unreferencedModule.js.map +1 -0
  830. package/dist/phase/runner.d.ts +549 -0
  831. package/dist/phase/runner.d.ts.map +1 -0
  832. package/dist/phase/runner.js +1098 -0
  833. package/dist/phase/runner.js.map +1 -0
  834. package/dist/phase/schemaCode.d.ts +34 -0
  835. package/dist/phase/schemaCode.d.ts.map +1 -0
  836. package/dist/phase/schemaCode.js +53 -0
  837. package/dist/phase/schemaCode.js.map +1 -0
  838. package/dist/phase/schemaObjects.d.ts +37 -0
  839. package/dist/phase/schemaObjects.d.ts.map +1 -0
  840. package/dist/phase/schemaObjects.js +139 -0
  841. package/dist/phase/schemaObjects.js.map +1 -0
  842. package/dist/phase/schemaUsage.d.ts +30 -0
  843. package/dist/phase/schemaUsage.d.ts.map +1 -0
  844. package/dist/phase/schemaUsage.js +99 -0
  845. package/dist/phase/schemaUsage.js.map +1 -0
  846. package/dist/phase/schemaUsageCandidates.d.ts +32 -0
  847. package/dist/phase/schemaUsageCandidates.d.ts.map +1 -0
  848. package/dist/phase/schemaUsageCandidates.js +165 -0
  849. package/dist/phase/schemaUsageCandidates.js.map +1 -0
  850. package/dist/phase/schemaValidations.d.ts +34 -0
  851. package/dist/phase/schemaValidations.d.ts.map +1 -0
  852. package/dist/phase/schemaValidations.js +53 -0
  853. package/dist/phase/schemaValidations.js.map +1 -0
  854. package/dist/phase/secretCandidates.d.ts +28 -0
  855. package/dist/phase/secretCandidates.d.ts.map +1 -0
  856. package/dist/phase/secretCandidates.js +132 -0
  857. package/dist/phase/secretCandidates.js.map +1 -0
  858. package/dist/phase/securityCandidates.d.ts +29 -0
  859. package/dist/phase/securityCandidates.d.ts.map +1 -0
  860. package/dist/phase/securityCandidates.js +469 -0
  861. package/dist/phase/securityCandidates.js.map +1 -0
  862. package/dist/phase/seeded-defects.d.ts +18 -0
  863. package/dist/phase/seeded-defects.d.ts.map +1 -0
  864. package/dist/phase/seeded-defects.js +61 -0
  865. package/dist/phase/seeded-defects.js.map +1 -0
  866. package/dist/phase/stringLiterals.d.ts +24 -0
  867. package/dist/phase/stringLiterals.d.ts.map +1 -0
  868. package/dist/phase/stringLiterals.js +35 -0
  869. package/dist/phase/stringLiterals.js.map +1 -0
  870. package/dist/phase/stylesCss.d.ts +25 -0
  871. package/dist/phase/stylesCss.d.ts.map +1 -0
  872. package/dist/phase/stylesCss.js +34 -0
  873. package/dist/phase/stylesCss.js.map +1 -0
  874. package/dist/phase/stylesMarkup.d.ts +31 -0
  875. package/dist/phase/stylesMarkup.d.ts.map +1 -0
  876. package/dist/phase/stylesMarkup.js +48 -0
  877. package/dist/phase/stylesMarkup.js.map +1 -0
  878. package/dist/phase/stylesSource.d.ts +36 -0
  879. package/dist/phase/stylesSource.d.ts.map +1 -0
  880. package/dist/phase/stylesSource.js +50 -0
  881. package/dist/phase/stylesSource.js.map +1 -0
  882. package/dist/phase/typeDeclarations.d.ts +33 -0
  883. package/dist/phase/typeDeclarations.d.ts.map +1 -0
  884. package/dist/phase/typeDeclarations.js +76 -0
  885. package/dist/phase/typeDeclarations.js.map +1 -0
  886. package/dist/phase/types.d.ts +2191 -0
  887. package/dist/phase/types.d.ts.map +1 -0
  888. package/dist/phase/types.js +27 -0
  889. package/dist/phase/types.js.map +1 -0
  890. package/dist/phase/withinFileProvenance.d.ts +50 -0
  891. package/dist/phase/withinFileProvenance.d.ts.map +1 -0
  892. package/dist/phase/withinFileProvenance.js +112 -0
  893. package/dist/phase/withinFileProvenance.js.map +1 -0
  894. package/dist/pipeline.d.ts +37 -30
  895. package/dist/pipeline.d.ts.map +1 -1
  896. package/dist/pipeline.js +131 -326
  897. package/dist/pipeline.js.map +1 -1
  898. package/dist/pipelineAdapters.d.ts +104 -21
  899. package/dist/pipelineAdapters.d.ts.map +1 -1
  900. package/dist/pipelineAdapters.js +1144 -984
  901. package/dist/pipelineAdapters.js.map +1 -1
  902. package/dist/pipelineTypes.d.ts +21 -0
  903. package/dist/pipelineTypes.d.ts.map +1 -1
  904. package/dist/pipelineTypes.js +21 -10
  905. package/dist/pipelineTypes.js.map +1 -1
  906. package/dist/presets/presets.d.ts +9 -2
  907. package/dist/presets/presets.d.ts.map +1 -1
  908. package/dist/presets/presets.js +15 -26
  909. package/dist/presets/presets.js.map +1 -1
  910. package/dist/reporting/csvReportGenerator.d.ts +14 -3
  911. package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
  912. package/dist/reporting/csvReportGenerator.js +14 -3
  913. package/dist/reporting/csvReportGenerator.js.map +1 -1
  914. package/dist/reporting/htmlReportGenerator.d.ts +5 -1
  915. package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
  916. package/dist/reporting/htmlReportGenerator.js +5 -1
  917. package/dist/reporting/htmlReportGenerator.js.map +1 -1
  918. package/dist/reporting/jsonReportGenerator.d.ts +9 -2
  919. package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
  920. package/dist/reporting/jsonReportGenerator.js +19 -5
  921. package/dist/reporting/jsonReportGenerator.js.map +1 -1
  922. package/dist/reporting/reportGenerator.d.ts +9 -2
  923. package/dist/reporting/reportGenerator.d.ts.map +1 -1
  924. package/dist/reporting/reportGenerator.js +9 -2
  925. package/dist/reporting/reportGenerator.js.map +1 -1
  926. package/dist/reporting/sarifReportGenerator.d.ts +14 -0
  927. package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
  928. package/dist/reporting/sarifReportGenerator.js +14 -0
  929. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  930. package/dist/reporting/sizeDistribution.d.ts +19 -2
  931. package/dist/reporting/sizeDistribution.d.ts.map +1 -1
  932. package/dist/reporting/sizeDistribution.js +23 -5
  933. package/dist/reporting/sizeDistribution.js.map +1 -1
  934. package/dist/ruleAliases.d.ts +6 -8
  935. package/dist/ruleAliases.d.ts.map +1 -1
  936. package/dist/ruleAliases.js +29 -4
  937. package/dist/ruleAliases.js.map +1 -1
  938. package/dist/search/QueryParser.d.ts +1 -0
  939. package/dist/search/QueryParser.d.ts.map +1 -1
  940. package/dist/search/QueryParser.js +32 -91
  941. package/dist/search/QueryParser.js.map +1 -1
  942. package/dist/services/CodeMapGenerator.d.ts +19 -20
  943. package/dist/services/CodeMapGenerator.d.ts.map +1 -1
  944. package/dist/services/CodeMapGenerator.js +214 -194
  945. package/dist/services/CodeMapGenerator.js.map +1 -1
  946. package/dist/services/ProjectTaskRepository.d.ts +65 -3
  947. package/dist/services/ProjectTaskRepository.d.ts.map +1 -1
  948. package/dist/services/ProjectTaskRepository.js +94 -25
  949. package/dist/services/ProjectTaskRepository.js.map +1 -1
  950. package/dist/services/auditJobService.d.ts +17 -1
  951. package/dist/services/auditJobService.d.ts.map +1 -1
  952. package/dist/services/auditJobService.js +16 -0
  953. package/dist/services/auditJobService.js.map +1 -1
  954. package/dist/services/fileAccounting.d.ts +43 -5
  955. package/dist/services/fileAccounting.d.ts.map +1 -1
  956. package/dist/services/fileAccounting.js +48 -9
  957. package/dist/services/fileAccounting.js.map +1 -1
  958. package/dist/services/projectTaskHelpers.d.ts +45 -0
  959. package/dist/services/projectTaskHelpers.d.ts.map +1 -1
  960. package/dist/services/projectTaskHelpers.js +45 -0
  961. package/dist/services/projectTaskHelpers.js.map +1 -1
  962. package/dist/services/whitelistService.d.ts +40 -5
  963. package/dist/services/whitelistService.d.ts.map +1 -1
  964. package/dist/services/whitelistService.js +87 -46
  965. package/dist/services/whitelistService.js.map +1 -1
  966. package/dist/sqlite/betterSqlite3.d.ts +35 -1
  967. package/dist/sqlite/betterSqlite3.d.ts.map +1 -1
  968. package/dist/sqlite/betterSqlite3.js +35 -1
  969. package/dist/sqlite/betterSqlite3.js.map +1 -1
  970. package/dist/sqlite/driver.d.ts +23 -0
  971. package/dist/sqlite/driver.d.ts.map +1 -1
  972. package/dist/sqlite/driver.js +23 -0
  973. package/dist/sqlite/driver.js.map +1 -1
  974. package/dist/sqlite/nodeSqlite.d.ts +35 -1
  975. package/dist/sqlite/nodeSqlite.d.ts.map +1 -1
  976. package/dist/sqlite/nodeSqlite.js +35 -1
  977. package/dist/sqlite/nodeSqlite.js.map +1 -1
  978. package/dist/structuralSignature.d.ts +9 -0
  979. package/dist/structuralSignature.d.ts.map +1 -1
  980. package/dist/structuralSignature.js +9 -0
  981. package/dist/structuralSignature.js.map +1 -1
  982. package/dist/styles/cssAstExtractor.d.ts +15 -0
  983. package/dist/styles/cssAstExtractor.d.ts.map +1 -1
  984. package/dist/styles/cssAstExtractor.js +15 -0
  985. package/dist/styles/cssAstExtractor.js.map +1 -1
  986. package/dist/styles/normalizer.d.ts +6 -0
  987. package/dist/styles/normalizer.d.ts.map +1 -1
  988. package/dist/styles/normalizer.js +6 -0
  989. package/dist/styles/normalizer.js.map +1 -1
  990. package/dist/styles/styleExtractor.d.ts +29 -2
  991. package/dist/styles/styleExtractor.d.ts.map +1 -1
  992. package/dist/styles/styleExtractor.js +67 -48
  993. package/dist/styles/styleExtractor.js.map +1 -1
  994. package/dist/styles/styleIndexer.d.ts +8 -0
  995. package/dist/styles/styleIndexer.d.ts.map +1 -1
  996. package/dist/styles/styleIndexer.js +135 -138
  997. package/dist/styles/styleIndexer.js.map +1 -1
  998. package/dist/styles/tailwindConfigLoader.d.ts +12 -0
  999. package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
  1000. package/dist/styles/tailwindConfigLoader.js +12 -0
  1001. package/dist/styles/tailwindConfigLoader.js.map +1 -1
  1002. package/dist/styles/tailwindExpander.d.ts +3 -0
  1003. package/dist/styles/tailwindExpander.d.ts.map +1 -1
  1004. package/dist/styles/tailwindExpander.js +8 -4
  1005. package/dist/styles/tailwindExpander.js.map +1 -1
  1006. package/dist/styles/tailwindProbe.d.ts +9 -0
  1007. package/dist/styles/tailwindProbe.d.ts.map +1 -1
  1008. package/dist/styles/tailwindProbe.js +181 -166
  1009. package/dist/styles/tailwindProbe.js.map +1 -1
  1010. package/dist/styles/tailwindUtilityExpander.d.ts +13 -0
  1011. package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
  1012. package/dist/styles/tailwindUtilityExpander.js +13 -0
  1013. package/dist/styles/tailwindUtilityExpander.js.map +1 -1
  1014. package/dist/symbols.d.ts +13 -11
  1015. package/dist/symbols.d.ts.map +1 -1
  1016. package/dist/symbols.js +14 -21
  1017. package/dist/symbols.js.map +1 -1
  1018. package/dist/telemetry.d.ts +31 -3
  1019. package/dist/telemetry.d.ts.map +1 -1
  1020. package/dist/telemetry.js +31 -3
  1021. package/dist/telemetry.js.map +1 -1
  1022. package/dist/tool-registry.d.ts +37 -1
  1023. package/dist/tool-registry.d.ts.map +1 -1
  1024. package/dist/tool-registry.js +37 -1
  1025. package/dist/tool-registry.js.map +1 -1
  1026. package/dist/types.d.ts +101 -71
  1027. package/dist/types.d.ts.map +1 -1
  1028. package/dist/types.js +7 -16
  1029. package/dist/types.js.map +1 -1
  1030. package/dist/ui/InteractivePrompts.d.ts +12 -0
  1031. package/dist/ui/InteractivePrompts.d.ts.map +1 -1
  1032. package/dist/ui/InteractivePrompts.js +12 -0
  1033. package/dist/ui/InteractivePrompts.js.map +1 -1
  1034. package/dist/utils/astParser.d.ts +15 -0
  1035. package/dist/utils/astParser.d.ts.map +1 -1
  1036. package/dist/utils/astParser.js +15 -0
  1037. package/dist/utils/astParser.js.map +1 -1
  1038. package/dist/utils/astUtils.d.ts +60 -0
  1039. package/dist/utils/astUtils.d.ts.map +1 -1
  1040. package/dist/utils/astUtils.js +350 -362
  1041. package/dist/utils/astUtils.js.map +1 -1
  1042. package/dist/utils/contentHash.d.ts +22 -0
  1043. package/dist/utils/contentHash.d.ts.map +1 -0
  1044. package/dist/utils/contentHash.js +26 -0
  1045. package/dist/utils/contentHash.js.map +1 -0
  1046. package/dist/utils/dependencyExtractor.d.ts +28 -0
  1047. package/dist/utils/dependencyExtractor.d.ts.map +1 -1
  1048. package/dist/utils/dependencyExtractor.js +28 -1
  1049. package/dist/utils/dependencyExtractor.js.map +1 -1
  1050. package/dist/utils/errorMessage.d.ts +10 -0
  1051. package/dist/utils/errorMessage.d.ts.map +1 -0
  1052. package/dist/utils/errorMessage.js +12 -0
  1053. package/dist/utils/errorMessage.js.map +1 -0
  1054. package/dist/utils/fileDiscovery.d.ts +61 -2
  1055. package/dist/utils/fileDiscovery.d.ts.map +1 -1
  1056. package/dist/utils/fileDiscovery.js +131 -23
  1057. package/dist/utils/fileDiscovery.js.map +1 -1
  1058. package/dist/utils/reactDetection.d.ts +34 -5
  1059. package/dist/utils/reactDetection.d.ts.map +1 -1
  1060. package/dist/utils/reactDetection.js +73 -52
  1061. package/dist/utils/reactDetection.js.map +1 -1
  1062. package/dist/version.generated.d.ts +1 -1
  1063. package/dist/version.generated.js +1 -1
  1064. package/dist/workers/auditWorker.js +7 -3
  1065. package/dist/workers/auditWorker.js.map +1 -1
  1066. package/examples/.auditrc.example.json +0 -8
  1067. package/examples/.auditrc.json +0 -11
  1068. package/examples/.code-auditor.json +0 -2
  1069. package/examples/nextjs.auditrc.json +0 -8
  1070. package/examples/node-api.auditrc.json +0 -7
  1071. package/examples/react.auditrc.json +0 -6
  1072. package/examples/tolerant-solid.auditrc.json +0 -5
  1073. package/package.json +11 -5
  1074. package/plugin/.claude-plugin/manifest.json +16 -0
  1075. package/plugin/.claude-plugin/plugin.json +1 -1
  1076. package/plugin/hooks/hooks.json +6 -2
  1077. package/plugin/scripts/hook-audit.sh +41 -14
  1078. package/plugin/scripts/hook-common.sh +50 -5
  1079. package/plugin/scripts/hook-drift-check.sh +72 -0
  1080. package/plugin/scripts/hook-self-audit.sh +17 -12
  1081. package/plugin/skills/code-auditor/SKILL.md +1 -1
  1082. package/server.json +2 -2
package/dist/cli.js CHANGED
@@ -7,7 +7,6 @@ import './native-bootstrap.js';
7
7
  import { Command } from 'commander';
8
8
  import chalk from 'chalk';
9
9
  import { createAuditRunner } from './auditRunner.js';
10
- import { runAuditDispatch } from './auditRouter.js';
11
10
  import { promises as fs } from 'fs';
12
11
  import { createInterface } from 'readline';
13
12
  import { dirname, isAbsolute, join, relative, resolve } from 'path';
@@ -23,7 +22,6 @@ import { fingerprint, buildFingerprintInput } from './fingerprint.js';
23
22
  import { buildDismissalEntry, upsertDismissal } from './dismissals.js';
24
23
  import { resolveTelemetryConfig, signatureForFinding, buildTelemetryPayload, formatTelemetryPreview, sendTelemetry, languageHint } from './telemetry.js';
25
24
  import { getInstallId } from './installConfig.js';
26
- import { ALL_ANALYZERS } from './analyzers/ruleRegistry.js';
27
25
  import { computeGatingDecision } from './enforcement/gate.js';
28
26
  import { BLOCKING_SEVERITIES, SEVERITIES } from './types.js';
29
27
  import { rankFilesByPriority, orderFindingsWithinFile } from './nextFile.js';
@@ -36,6 +34,18 @@ const program = new Command();
36
34
  // `--fail-on advisory` must fail loudly and name the new tier, not emit a generic
37
35
  // "must be one of" list that leaves the user guessing what `advisory` became.
38
36
  const LEGACY_SEVERITY_NAMES = { advisory: 'high' };
37
+ // CLI option descriptions, hoisted so the 30+ subcommands' repeated
38
+ // option() / -p,--path descriptions share one source.
39
+ const OPTION_OUTPUT_AS_JSON = 'Output as JSON';
40
+ const OPTION_PROJECT_PATH = 'Project path';
41
+ const OPTION_PROJECT_ROOT_PATH = 'Project root path';
42
+ const OPTION_PROJECT_ROOT = 'Project root';
43
+ const OPTION_CONFIGURATION_NAME = 'Configuration name';
44
+ const UNKNOWN_ERROR = 'Unknown error';
45
+ // "file:line" suffix for a finding row; empty when the finding has no line.
46
+ function lineSuffix(line) {
47
+ return line ? `:${line}` : '';
48
+ }
39
49
  // Spec 44 R4 — file accounting summary line + `--explain-skipped` breakdown.
40
50
  // The full per-reason file lists live in the JSON report (`metadata.fileAccounting`);
41
51
  // the CLI preview is bounded to the first ~20 files per reason.
@@ -88,12 +98,447 @@ program
88
98
  .name('code-auditor')
89
99
  .description('TypeScript/JavaScript code quality auditor with AI tool integration')
90
100
  .version(`${PACKAGE_VERSION} (sqlite: ${describeSqliteBackend()})`);
101
+ /**
102
+ * Collapse a receiver-resolution reason to a single line for the cannot-fire
103
+ * panel. Reasons embed the receiver text (which can span the SQL of a chained
104
+ * `db.prepare("...").bind(...)`), so newlines must be folded and the result
105
+ * truncated to stay legible in the per-file rollup.
106
+ */
107
+ function sanitizeReason(reason) {
108
+ if (!reason)
109
+ return '';
110
+ const collapsed = reason.replace(/\s+/g, ' ').trim();
111
+ return collapsed.length > 220 ? `${collapsed.slice(0, 217)}…` : collapsed;
112
+ }
113
+ /**
114
+ * Print the pre-delta diagnostic panels: coverage, skipped analyzers, ignored
115
+ * config keys, coverage gaps, test coverage, size distributions, and dead
116
+ * clusters. Pure reporting over `result.metadata`.
117
+ */
118
+ function printAuditDiagnostics(result) {
119
+ // ── Coverage panel leads the report (Spec 47 R2) ─────────────
120
+ const coverage = result.metadata?.coverage;
121
+ // Spec 69 §10 R4 — the site-level cannot-fire count (unproven DB-access
122
+ // receiver sites) sits beside the rule-level states, so one line reports
123
+ // both the per-rule verdict and the per-call-site gap the affected rules
124
+ // may have under-reported on.
125
+ const unprovenSiteCount = (result.metadata?.diagnostics ?? [])
126
+ .filter((d) => d.kind === 'cannot-fire' && d.analyzerName === 'schema')
127
+ .length;
128
+ if (coverage && coverage.length > 0) {
129
+ const covFired = coverage.filter(c => c.state === 'fired').length;
130
+ const covClean = coverage.filter(c => c.state === 'clean').length;
131
+ const covIncomplete = coverage.filter(c => c.state === 'incomplete').length;
132
+ const covNotApplicable = coverage.filter(c => c.state === 'notApplicable').length;
133
+ const covCannotFire = coverage.filter(c => c.state === 'cannot-fire').length;
134
+ console.log(chalk.gray(`── Coverage panel ── ${covFired} fired · ${covClean} clean · ` +
135
+ `${covIncomplete} incomplete · ${covNotApplicable} not-applicable · ` +
136
+ `${covCannotFire} cannot-fire · ${unprovenSiteCount} unproven sites`));
137
+ }
138
+ // ── Skipped analyzers (Spec 61) ──────────────────────────────
139
+ const skippedAnalyzers = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'go-toolchain-missing' ||
140
+ d.kind === 'go-analyzer-wrong-arch' ||
141
+ d.kind === 'go-analyzer-build-failed');
142
+ if (skippedAnalyzers.length > 0) {
143
+ console.log(chalk.yellow(`── Skipped analyzers ── ${skippedAnalyzers.length}`));
144
+ for (const d of skippedAnalyzers) {
145
+ const name = d.analyzerName || 'go';
146
+ console.log(chalk.yellow(` ${name} [${d.kind}] — ${d.message}`));
147
+ }
148
+ }
149
+ // ── Ignored config keys (Spec 61 R1.4) ─────────────────────────
150
+ const ignoredConfigKeys = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'config-key-rejected');
151
+ if (ignoredConfigKeys.length > 0) {
152
+ console.log(chalk.yellow(`── Ignored config keys ── ${ignoredConfigKeys.length}`));
153
+ for (const d of ignoredConfigKeys) {
154
+ console.log(chalk.yellow(` ${d.message}`));
155
+ }
156
+ }
157
+ // ── Cannot-fire sites (Spec 69 §10) ───────────────────────────
158
+ // The cannot-fire verdict surface is the release bar: every DB-access call
159
+ // site whose receiver could not be proven a handle must be *visible with its
160
+ // reason*, never capped or folded into a silent clean. Grouped by file so a
161
+ // reader can see exactly which files the table-reference and data-access
162
+ // rules may have under-reported on.
163
+ const allDiagnostics = result.metadata?.diagnostics ?? [];
164
+ const cannotFireSites = allDiagnostics.filter((d) => d.kind === 'cannot-fire');
165
+ if (cannotFireSites.length > 0) {
166
+ const byFile = new Map();
167
+ for (const d of cannotFireSites) {
168
+ const key = d.file ?? '(unknown)';
169
+ const entry = byFile.get(key) ?? { lines: [], reasons: new Set() };
170
+ if (typeof d.line === 'number')
171
+ entry.lines.push(d.line);
172
+ const rawReason = typeof d.details?.reason === 'string' ? d.details.reason : d.message;
173
+ const reason = sanitizeReason(rawReason);
174
+ if (reason)
175
+ entry.reasons.add(reason);
176
+ byFile.set(key, entry);
177
+ }
178
+ console.log(chalk.yellow(`── Cannot-fire sites ── ${cannotFireSites.length} unproven DB-access call sites across ${byFile.size} files`));
179
+ console.log(chalk.yellow(` Table-reference and data-access rules may report clean on access they could not observe.`));
180
+ for (const [file, entry] of [...byFile.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
181
+ const reasons = [...entry.reasons];
182
+ const shown = reasons.slice(0, 2).join(' │ ');
183
+ const more = reasons.length > 2 ? ` (+${reasons.length - 2} more reasons)` : '';
184
+ const loc = entry.lines.length === 1 ? ` (line ${entry.lines[0]})` : '';
185
+ console.log(chalk.yellow(` ${file} — ${entry.lines.length} site(s)${loc}: ${shown}${more}`));
186
+ }
187
+ }
188
+ // ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
189
+ const otherCoverageGaps = allDiagnostics.filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found');
190
+ if (otherCoverageGaps.length > 0) {
191
+ const byKind = {};
192
+ for (const d of otherCoverageGaps)
193
+ byKind[d.kind] = (byKind[d.kind] ?? 0) + 1;
194
+ const totals = Object.entries(byKind)
195
+ .map(([kind, n]) => `${n} ${kind}`)
196
+ .join(' · ');
197
+ console.log(chalk.gray(`── Coverage gaps ── ${totals}`));
198
+ for (const d of otherCoverageGaps.slice(0, 20)) {
199
+ const loc = d.file ? `${d.file}${typeof d.line === 'number' ? `:${d.line}` : ''}` : '(unknown)';
200
+ console.log(chalk.gray(` ${loc} [${d.kind}] — ${d.message}`));
201
+ }
202
+ if (otherCoverageGaps.length > 20) {
203
+ console.log(chalk.gray(` … and ${otherCoverageGaps.length - 20} more`));
204
+ }
205
+ }
206
+ // ── Spec 60 R1 — test coverage (reporting, not detection) ─────
207
+ const testCoverage = result.metadata?.testCoverage;
208
+ if (testCoverage) {
209
+ const pct = testCoverage.total > 0
210
+ ? ((testCoverage.tested / testCoverage.total) * 100).toFixed(1)
211
+ : '0.0';
212
+ let line = `tested ${testCoverage.tested}/${testCoverage.total} (${pct}%)` +
213
+ ` · untested-live ${testCoverage.untestedLive}` +
214
+ ` · untested-dead ${testCoverage.untestedDead}`;
215
+ if (testCoverage.deadDrop > 0) {
216
+ line += ` (was ${testCoverage.deadPreException} pre-exception; −${testCoverage.deadDrop} entry points exempted)`;
217
+ }
218
+ console.log(chalk.gray(`── Test coverage ── ${line}`));
219
+ }
220
+ // ── Spec 60 R2 — size distributions (median / p95 / max) ──────
221
+ const sizeDistributions = result.metadata?.sizeDistributions;
222
+ if (sizeDistributions && sizeDistributions.length > 0) {
223
+ console.log(chalk.gray(`── Size distributions ──`));
224
+ for (const d of sizeDistributions) {
225
+ const fmt = (n) => Number.isInteger(n) ? String(n) : n.toFixed(1);
226
+ const tail = d.maxEntry
227
+ ? ` (${d.maxEntry.name}${d.maxEntry.fileType ? ` · ${d.maxEntry.fileType}` : ''} · ${d.maxEntry.entityType})`
228
+ : '';
229
+ const pop = d.population ? `${d.population}, n=${d.count}` : `n=${d.count}`;
230
+ console.log(chalk.gray(` ${d.measure} [${pop}]: median ${fmt(d.median)} · p95 ${fmt(d.p95)} · max ${fmt(d.max)}${tail}`));
231
+ }
232
+ }
233
+ // ── Spec 60 R3 — dead-and-duplicated clusters ─────────────────
234
+ const deadClusters = result.metadata?.deadClusters;
235
+ if (deadClusters && deadClusters.length > 0) {
236
+ console.log(chalk.gray(`── Dead-and-duplicated ── ${deadClusters.length} clusters`));
237
+ for (const c of deadClusters.slice(0, 10)) {
238
+ console.log(chalk.gray(` ${c.count} × ${c.basename}`));
239
+ }
240
+ if (deadClusters.length > 10) {
241
+ console.log(chalk.gray(` … and ${deadClusters.length - 10} more clusters`));
242
+ }
243
+ }
244
+ }
245
+ /**
246
+ * Print the findings summary: the delta view when a baseline exists, or the
247
+ * absolute summary otherwise. Recomputes the dismissed/gating suffixes so the
248
+ * summary is self-contained.
249
+ */
250
+ function printFindingSummary(result, violations, baseline, options) {
251
+ // Spec 57 — dismissed count is reported alongside the total, never
252
+ // subtracted from it ("43 findings, 3 dismissed").
253
+ const dismissedCount = result.summary.dismissed ?? 0;
254
+ const dismissedSuffix = dismissedCount > 0 ? `, ${dismissedCount} dismissed` : '';
255
+ const gateExcludedCount = violations.filter((v) => v.gateExcluded).length;
256
+ const gatingCount = (severity) => violations.filter((v) => v.severity === severity && !v.gateExcluded && !v.dismissed).length;
257
+ const findingsSuffix = `${dismissedSuffix}${gateExcludedCount > 0 ? ` (${gateExcludedCount.toLocaleString()} excluded from gate)` : ''}`;
258
+ const summaryLine = (label, total, severity) => gateExcludedCount > 0
259
+ ? `${label}: ${total} (${gatingCount(severity)} gating)`
260
+ : `${label}: ${total}`;
261
+ // ── Delta output (Spec 18 R2) ─────────────────────────────────
262
+ if (baseline && !options.full) {
263
+ const newViolations = violations.filter((v) => v.new === true);
264
+ const knownCount = baseline.knownCount ?? 0;
265
+ const fixedCount = baseline.fixedCount ?? 0;
266
+ const previousKnown = baseline.previousKnownCount ?? 0;
267
+ const currentDebt = newViolations.length + knownCount;
268
+ const debtDelta = currentDebt - previousKnown;
269
+ const trendIcon = debtDelta > 0 ? '↑' : debtDelta < 0 ? '↓' : '→';
270
+ const trendLabel = debtDelta > 0
271
+ ? `(debt increased since last baseline)`
272
+ : debtDelta < 0
273
+ ? `(debt decreased since last baseline)`
274
+ : '(unchanged)';
275
+ console.log(`\n📊 Delta: +${newViolations.length} new · −${fixedCount} fixed · ${knownCount} known ${trendIcon} ${trendLabel}`);
276
+ if (newViolations.length > 0) {
277
+ console.log(chalk.gray(`\n── New readings (${newViolations.length}) ──────────────────────────`));
278
+ for (const v of newViolations) {
279
+ const icon = v.severity === 'critical' ? '🔴' :
280
+ v.severity === 'severe' ? '🟠' : '🟡';
281
+ console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.severity}] ${v.message}`);
282
+ }
283
+ }
284
+ else {
285
+ console.log(chalk.green('\n✓ No new readings since last baseline.'));
286
+ if (knownCount > 0) {
287
+ console.log(chalk.gray(` ${knownCount} known reading(s) are still open — recorded, not resolved.`));
288
+ }
289
+ }
290
+ // Debt by analyzer
291
+ console.log(chalk.gray(`\n── Debt by Analyzer ──────────────────────────`));
292
+ const analyzerCounts = {};
293
+ for (const v of violations) {
294
+ const a = v.analyzer || 'unknown';
295
+ if (!analyzerCounts[a])
296
+ analyzerCounts[a] = { known: 0, new: 0 };
297
+ if (v.new === false)
298
+ analyzerCounts[a].known++;
299
+ else if (v.new === true)
300
+ analyzerCounts[a].new++;
301
+ }
302
+ for (const [analyzer, counts] of Object.entries(analyzerCounts).sort()) {
303
+ const newPart = counts.new > 0 ? ` (+${counts.new})` : '';
304
+ console.log(`${analyzer}: ${counts.known.toLocaleString()} known${newPart}`);
305
+ }
306
+ // Top files
307
+ console.log(chalk.gray(`\n── Top Files ─────────────────────────────────`));
308
+ const fileCounts = new Map();
309
+ for (const v of violations) {
310
+ const f = v.file || '';
311
+ fileCounts.set(f, (fileCounts.get(f) || 0) + 1);
312
+ }
313
+ const topFiles = [...fileCounts.entries()]
314
+ .sort((a, b) => b[1] - a[1])
315
+ .slice(0, 5);
316
+ for (const [file, count] of topFiles) {
317
+ console.log(`${file} — ${count} reading${count !== 1 ? 's' : ''}`);
318
+ }
319
+ console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit --full')} to see all ${currentDebt.toLocaleString()} readings.`));
320
+ }
321
+ else if (!baseline) {
322
+ // No baseline: current behavior + hint
323
+ console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
324
+ console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
325
+ console.log(summaryLine('Severe', result.summary.severe, 'severe'));
326
+ console.log(summaryLine('High', result.summary.high, 'high'));
327
+ console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
328
+ console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit baseline')} to adopt the ratchet and track changes over time.`));
329
+ }
330
+ else {
331
+ // --full with baseline: full itemized inventory (current behavior)
332
+ console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
333
+ console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
334
+ console.log(summaryLine('Severe', result.summary.severe, 'severe'));
335
+ console.log(summaryLine('High', result.summary.high, 'high'));
336
+ console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
337
+ }
338
+ }
339
+ /**
340
+ * Print unparsed-file failures and skipped-extension notice.
341
+ */
342
+ function printUnparsedAndSkipped(result) {
343
+ // Spec 32 — unparsed files are never silent.
344
+ const unparsedFiles = result.metadata?.unparsedFiles ?? [];
345
+ if (unparsedFiles.length > 0) {
346
+ console.error(chalk.red(`\n⚠️ ${unparsedFiles.length} file${unparsedFiles.length !== 1 ? 's' : ''} failed to parse:`));
347
+ for (const u of unparsedFiles.slice(0, 20)) {
348
+ console.error(` ${u.filePath} — ${u.reason}`);
349
+ }
350
+ if (unparsedFiles.length > 20) {
351
+ console.error(` … and ${unparsedFiles.length - 20} more`);
352
+ }
353
+ }
354
+ // Spec 43 R5 follow-up — extensions present on disk that discovery skipped.
355
+ const skippedExtensions = result.metadata?.skippedExtensions ?? [];
356
+ if (skippedExtensions.length > 0) {
357
+ console.log(chalk.gray(`\n── Not analyzed (skipped extensions) ────────`));
358
+ const parts = skippedExtensions.map(s => `${s.ext} (${s.count})`);
359
+ console.log(` ${parts.join(', ')}`);
360
+ }
361
+ }
362
+ /**
363
+ * Print per-analyzer pipeline activity and the detailed coverage breakdown.
364
+ */
365
+ function printPipelineStages(result, options) {
366
+ if (options.json)
367
+ return;
368
+ const analyzerFiles = [];
369
+ for (const [name, ar] of Object.entries(result.analyzerResults)) {
370
+ const vCount = ar.violations.length;
371
+ if (isVisitorStatus(ar.status)) {
372
+ analyzerFiles.push(`${name} [visitor-ran]: ${getFilesProcessed(ar.status)} files, ${vCount} violations`);
373
+ }
374
+ else if (isReducerStatus(ar.status)) {
375
+ analyzerFiles.push(`${name} [reducer-ran]: ${getFactsConsumed(ar.status)} facts, ${vCount} violations`);
376
+ }
377
+ else {
378
+ analyzerFiles.push(`${name} [notRun]: ${vCount} violations`);
379
+ }
380
+ }
381
+ if (analyzerFiles.length > 0) {
382
+ console.log(chalk.gray(`\n── Pipeline Stages ──────────────────────────`));
383
+ console.log(analyzerFiles.join('\n'));
384
+ }
385
+ // ── Coverage (Spec 27) ──────────────────────────────────────────
386
+ const coverage = result.metadata?.coverage;
387
+ if (coverage && coverage.length > 0) {
388
+ const fired = coverage.filter(c => c.state === 'fired');
389
+ const clean = coverage.filter(c => c.state === 'clean');
390
+ const incomplete = coverage.filter(c => c.state === 'incomplete');
391
+ const notApplicable = coverage.filter(c => c.state === 'notApplicable');
392
+ const cannotFire = coverage.filter(c => c.state === 'cannot-fire');
393
+ const firedCount = fired.reduce((s, c) => s + c.count, 0);
394
+ console.log(chalk.gray(`\n── Coverage ─────────────────────────────────`));
395
+ console.log(` ${fired.length} fired (${firedCount.toLocaleString()} violations), ` +
396
+ `${clean.length} clean, ` +
397
+ `${incomplete.length} incomplete, ` +
398
+ `${notApplicable.length} notApplicable, ` +
399
+ `${cannotFire.length} cannot-fire ` +
400
+ `(${coverage.length} rules registered)`);
401
+ if (cannotFire.length > 0) {
402
+ console.log(chalk.yellow(` ── Cannot Fire (broken in the tool) ──`));
403
+ for (const c of cannotFire) {
404
+ console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
405
+ }
406
+ }
407
+ if (notApplicable.length > 0) {
408
+ console.log(chalk.gray(` ── Not Applicable ──`));
409
+ for (const c of notApplicable) {
410
+ console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
411
+ }
412
+ }
413
+ if (clean.length > 0) {
414
+ console.log(chalk.gray(` ── Clean ──`));
415
+ for (const c of clean) {
416
+ console.log(` ${c.ruleId} (0)`);
417
+ }
418
+ }
419
+ if (incomplete.length > 0) {
420
+ console.log(chalk.gray(` ── Incomplete (missing facts for some files) ──`));
421
+ for (const c of incomplete) {
422
+ console.log(` ${c.ruleId} (0)`);
423
+ }
424
+ }
425
+ }
426
+ }
427
+ /**
428
+ * Generate and write the formatted report when --format is specified.
429
+ */
430
+ async function writeReport(result, options) {
431
+ if (!options.format)
432
+ return;
433
+ const validFormats = ['html', 'json', 'csv', 'sarif'];
434
+ if (!validFormats.includes(options.format)) {
435
+ console.error(chalk.red(`Unknown format: "${options.format}". Must be one of: ${validFormats.join(', ')}`));
436
+ process.exit(1);
437
+ }
438
+ const { generateReport } = await import('./reporting/reportGenerator.js');
439
+ const { readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
440
+ const rootDir = resolve(options.path || process.cwd());
441
+ const config = options.format === 'sarif'
442
+ ? { rootDir, ...readVersionControlProvenance(rootDir) }
443
+ : { rootDir };
444
+ const report = generateReport(result, options.format, config);
445
+ const ext = options.format === 'sarif' ? 'sarif' : options.format;
446
+ if (options.output) {
447
+ const outputDir = resolve(options.output);
448
+ const reportPath = join(outputDir, `audit-report.${ext}`);
449
+ let exists = false;
450
+ try {
451
+ await fs.access(reportPath);
452
+ exists = true;
453
+ }
454
+ catch { /* ok */ }
455
+ if (exists && !options.overwrite) {
456
+ console.error(chalk.red(`Refusing to overwrite existing report ${reportPath} — pass --overwrite to replace it.`));
457
+ process.exit(1);
458
+ }
459
+ await fs.mkdir(dirname(reportPath), { recursive: true });
460
+ await fs.writeFile(reportPath, report, 'utf-8');
461
+ console.log(chalk.green(`\nReport written to ${reportPath}`));
462
+ }
463
+ else {
464
+ // No output path — default to stdout.
465
+ process.stdout.write(report + '\n');
466
+ }
467
+ }
468
+ /**
469
+ * Apply the exit-code gates: fail-on-regression, zero-files, parse-failure,
470
+ * and --fail-on severity thresholds.
471
+ */
472
+ function applyFailOnGates(result, violations, baseline, options, failOnSeverity) {
473
+ // --fail-on-regression: compare total high debt to baseline snapshot
474
+ if (baseline && options.failOnRegression) {
475
+ const currentDebt = violations.filter((v) => v.new || v.new === false).length;
476
+ const snapshotDebt = baseline.previousKnownCount ?? 0;
477
+ if (currentDebt > snapshotDebt) {
478
+ console.error(chalk.red(`Debt regression: ${currentDebt - snapshotDebt} readings added without re-baselining.`));
479
+ process.exit(2);
480
+ }
481
+ }
482
+ // Pipeline-failure gate — the audit pipeline itself threw (schema drift,
483
+ // DB write failure, …). One error, reported once by the runner; this exits
484
+ // non-zero (broken tool) so a swallowed pipeline throw can never read as a
485
+ // clean pass.
486
+ {
487
+ const diagnostics = result.metadata?.diagnostics ?? [];
488
+ const pipelineErrors = diagnostics.filter((d) => d.kind === 'pipeline-error');
489
+ if (pipelineErrors.length > 0) {
490
+ console.error(`Pipeline failure: ${pipelineErrors[0].message}`);
491
+ process.exit(1);
492
+ }
493
+ }
494
+ // Zero-files gate — any enabled analyzer matching zero source files is a
495
+ // dark-analyzer failure; fail the run so the bug can't hide.
496
+ {
497
+ const diagnostics = result.metadata?.diagnostics ?? [];
498
+ const zeroFileWarnings = diagnostics.filter((d) => d.kind === 'zero-files');
499
+ if (zeroFileWarnings.length > 0) {
500
+ const names = zeroFileWarnings.map((d) => d.analyzerName ?? d.analyzer).join(', ');
501
+ console.error(`Zero-files failure: ${zeroFileWarnings.length} analyzer(s) matched zero source files (${names})`);
502
+ process.exit(2);
503
+ }
504
+ }
505
+ // Spec 32 — a run where any file failed to parse is incomplete and must
506
+ // not exit 0.
507
+ {
508
+ const unparsedFiles = result.metadata?.unparsedFiles ?? [];
509
+ if (unparsedFiles.length > 0) {
510
+ console.error(`Parse-failure: ${unparsedFiles.length} file(s) could not be parsed; analysis is incomplete.`);
511
+ process.exit(2);
512
+ }
513
+ }
514
+ // --fail-on: evaluate new + invariant findings only (unless --include-baseline)
515
+ if (failOnSeverity) {
516
+ const evaluableViolations = (baseline && !options.includeBaseline)
517
+ ? violations.filter((v) => v.new || v.analyzer === 'invariants')
518
+ : violations;
519
+ const severityOrder = SEVERITIES;
520
+ const failIndex = severityOrder.indexOf(failOnSeverity);
521
+ const hasAtOrAbove = evaluableViolations.some((v) => {
522
+ // Spec 57 — a dismissed finding never blocks the gate.
523
+ if (v.dismissed)
524
+ return false;
525
+ // Spec 36 R4 — a finding from a path-profile-excluded file never blocks.
526
+ if (v.gateExcluded)
527
+ return false;
528
+ const vIndex = severityOrder.indexOf(v.severity);
529
+ return vIndex >= 0 && vIndex <= failIndex;
530
+ });
531
+ if (hasAtOrAbove) {
532
+ process.exit(2);
533
+ }
534
+ }
535
+ }
91
536
  // Legacy audit command (default behavior)
92
537
  program
93
538
  .command('audit', { isDefault: true })
94
539
  .description('Run code quality audit (default command)')
95
540
  .option('-p, --path <path>', 'Path to audit', process.cwd())
96
- .option('-c, --config <config>', 'Configuration name')
541
+ .option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
97
542
  .option('-o, --output <dir>', 'Output directory for reports')
98
543
  .option('-f, --format <format>', 'Report format: html, json, csv, or sarif')
99
544
  .option('--overwrite', 'Overwrite an existing report file at --output (default: refuse)')
@@ -103,9 +548,6 @@ program
103
548
  .option('--fail-on-regression', 'Exit code 2 when total high debt exceeds the baseline snapshot')
104
549
  .option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
105
550
  .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
551
  .option('--explain-skipped', 'Print a per-reason breakdown of files dropped from analysis')
110
552
  .action(async (options) => {
111
553
  console.log(chalk.blue('🔍 Code Quality Audit Tool'));
@@ -138,302 +580,20 @@ program
138
580
  }
139
581
  }
140
582
  }
141
- const result = await runAuditDispatch({
583
+ const result = await createAuditRunner({
142
584
  projectRoot: options.path,
143
585
  configName: options.config,
144
586
  outputDirectory: options.output,
145
587
  presets: presetIds
146
- });
588
+ }).run();
147
589
  const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
148
590
  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
- }
591
+ printAuditDiagnostics(result);
592
+ printFindingSummary(result, violations, baseline, options);
350
593
  // Spec 44 R4 — file accounting: analyzed/dropped totals + optional breakdown.
351
594
  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
- }
595
+ printUnparsedAndSkipped(result);
596
+ printPipelineStages(result, options);
437
597
  // Spec-20 R4: built-in profile visibility — silent behavior changes
438
598
  // are never acceptable. Notify when scripts-and-tests excludes findings
439
599
  // from the blocking gate.
@@ -443,103 +603,8 @@ program
443
603
  console.log(chalk.blue(`\nℹ️ ${builtinCapped.toLocaleString()} readings excluded from the blocking gate by built-in profile "scripts-and-tests" (scripts/tests/fixtures).`));
444
604
  console.log(chalk.gray(` Set ${chalk.cyan('"builtin": false')} in .codeauditor.json to disable.`));
445
605
  }
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
- }
606
+ await writeReport(result, options);
607
+ applyFailOnGates(result, violations, baseline, options, failOnSeverity);
543
608
  }
544
609
  catch (error) {
545
610
  console.error(chalk.red('Error:'), error);
@@ -556,7 +621,7 @@ program
556
621
  .option('--fail-on-zero-files', 'Exit code 2 when any enabled analyzer matches zero source files', true)
557
622
  .option('--stdin', 'Read file paths from stdin (one per line)')
558
623
  .option('--explain-skipped', 'Print a per-reason breakdown of files dropped from analysis')
559
- .option('-p, --path <projectPath>', 'Project root path', process.cwd())
624
+ .option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
560
625
  .action(async (paths, options) => {
561
626
  try {
562
627
  const fileSet = new Set();
@@ -592,11 +657,17 @@ program
592
657
  // the gate never misses a finding. SARIF needs full run metadata, so it
593
658
  // always runs in-process.
594
659
  const projectRoot = resolve(options.path || process.cwd());
660
+ const hookMode = process.env.CODE_AUDITOR_HOOK === '1' || process.env.CODE_AUDITOR_HOOK === 'true';
595
661
  let result;
596
662
  if (resolvedPaths.length > 0 && options.format !== 'sarif') {
597
- const { resolveDaemon, readDaemonDiagnostics } = await import('./daemon/resolve.js');
663
+ const { resolveDaemon, readDaemonDiagnostics, startDaemonDetached } = await import('./daemon/resolve.js');
598
664
  const daemon = await resolveDaemon(projectRoot);
599
665
  if (daemon.mode === 'not-ready') {
666
+ // Hook mode (Spec 68 §hook-contract): never block the edit on an audit the
667
+ // daemon can't serve yet — it is still indexing, and the finding arrives on
668
+ // a later edit. Say nothing, exit clean. A non-hook caller gets the status.
669
+ if (hookMode)
670
+ process.exit(0);
600
671
  reportDaemonIndexing(daemon.state, !!options.json);
601
672
  return;
602
673
  }
@@ -605,14 +676,27 @@ program
605
676
  if (diag && diag.status === 'ready' && diag.staleFiles.length === 0) {
606
677
  result = await buildChangedResultFromDiagnostics(diag.diagnostics, projectRoot);
607
678
  }
679
+ else if (hookMode) {
680
+ // A just-edited file whose re-audit hasn't landed is not trustworthy from
681
+ // cache. Report nothing; the finding arrives on the next edit.
682
+ process.exit(0);
683
+ }
684
+ }
685
+ else if (hookMode) {
686
+ // No daemon: enqueue by starting one detached, then exit. The hook reads
687
+ // results from the daemon's cache on later edits — it never computes them.
688
+ startDaemonDetached(projectRoot);
689
+ process.exit(0);
608
690
  }
609
691
  }
610
692
  if (!result) {
693
+ // Hook mode never falls back to an in-process audit (Spec 68 §hook-contract).
694
+ if (hookMode)
695
+ process.exit(0);
611
696
  await initParsers();
612
697
  const runner = createAuditRunner({
613
698
  projectRoot: options.path,
614
699
  scope,
615
- analyzerConcurrency: 4
616
700
  });
617
701
  result = await runner.run();
618
702
  }
@@ -621,7 +705,7 @@ program
621
705
  // Spec 57 — dismissed count is reported alongside the total, never
622
706
  // subtracted from it. The hook surface (`changed`) is agent-facing, so it
623
707
  // must show the same "N findings, M dismissed" figure as the full report.
624
- const dismissedCount = result.summary.dismissed ?? 0;
708
+ const dismissedCount = result.summary?.dismissed ?? 0;
625
709
  // Blocking gate decision (Spec 45 R1/R4, Spec 54 R3), computed once so the
626
710
  // agent-facing before/after count (Spec 45 A2) and the exit code agree on
627
711
  // the same number. Every registered rule participates (R1); enforcement is
@@ -629,98 +713,21 @@ program
629
713
  // {critical, severe, high} — every finding is a defect and every finding
630
714
  // blocks; there is no configurable gate.
631
715
  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.
716
+ await emitChangedOutput(result, violations, blocking, dismissedCount, options);
717
+ // Spec 38 R2/R3 — per-rule timing + gate cpu-time, opt-in by env var.
717
718
  // Emitted to stderr so it never corrupts --json stdout. Slowest rule first.
719
+ // The gate metric is CPU time (auditCpuMs), not wall clock: the budget is
720
+ // about a slow *rule*, and CPU time isolates that from a slow machine.
718
721
  if (process.env.CODE_AUDIT_RULE_TIMING === '1') {
719
722
  const ruleTiming = result.metadata?.ruleTiming;
720
- const gateMs = result.metadata?.auditDuration;
723
+ const gateMs = result.metadata?.auditCpuMs;
724
+ const gateWallMs = result.metadata?.auditDuration;
721
725
  const lines = [];
722
726
  lines.push('');
723
- lines.push(`⏱ gate wall-clock: ${(gateMs ?? 0).toFixed(1)} ms (budget 300 ms)`);
727
+ // §16 criterion 16 — report both wall-clock (what the hook's user feels)
728
+ // and CPU (what the rule actually burned); the budget asserts on CPU.
729
+ lines.push(`⏱ gate wall-clock: ${(gateWallMs ?? 0).toFixed(1)} ms`);
730
+ lines.push(`⏱ gate cpu-time: ${(gateMs ?? 0).toFixed(1)} ms (budget 400 ms)`);
724
731
  lines.push('── per-rule timing (slowest first) ──');
725
732
  if (ruleTiming && ruleTiming.length > 0) {
726
733
  for (const t of ruleTiming) {
@@ -753,6 +760,17 @@ program
753
760
  process.exit(2);
754
761
  }
755
762
  }
763
+ // Pipeline-failure gate — the audit pipeline itself threw. One error,
764
+ // reported once by the runner; exit non-zero (broken tool) so a swallowed
765
+ // pipeline throw can never read as a clean pass.
766
+ {
767
+ const diagnostics = result.metadata?.diagnostics ?? [];
768
+ const pipelineErrors = diagnostics.filter((d) => d.kind === 'pipeline-error');
769
+ if (pipelineErrors.length > 0) {
770
+ console.error(`Pipeline failure: ${pipelineErrors[0].message}`);
771
+ process.exit(1);
772
+ }
773
+ }
756
774
  // Exit code based on --no-fail-on-zero-files (default-on in v3.4.8)
757
775
  // Any enabled analyzer matching zero source files is a failure — a >50%
758
776
  // threshold lets the exact bug (3/9 analyzers dark) pass silently.
@@ -771,6 +789,96 @@ program
771
789
  process.exit(1);
772
790
  }
773
791
  });
792
+ /**
793
+ * Emit the changed-audit output (SARIF, JSON, or console) for the hook surface.
794
+ * The SARIF branch dynamically imports its generator, so this is async.
795
+ */
796
+ async function emitChangedOutput(result, violations, blocking, dismissedCount, options) {
797
+ // JSON output
798
+ if (options.format === 'sarif') {
799
+ const { generateSARIFReport, readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
800
+ const projectDir = resolve(options.path || process.cwd());
801
+ const sarifOutput = generateSARIFReport(result, { rootDir: projectDir, ...readVersionControlProvenance(projectDir) });
802
+ process.stdout.write(sarifOutput + '\n');
803
+ }
804
+ else if (options.json) {
805
+ const projectDir = resolve(options.path || process.cwd());
806
+ const jsonViolations = violations.map((v) => {
807
+ // Compute relative path if file resolves inside the project
808
+ let filePath = v.file || '';
809
+ if (filePath.startsWith('/') || filePath.startsWith('\\\\')) {
810
+ const rel = relative(projectDir, filePath);
811
+ // Only use relative path if it doesn't escape the project
812
+ if (!rel.startsWith('..') && !isAbsolute(rel)) {
813
+ filePath = rel;
814
+ }
815
+ }
816
+ // Find column: prefer explicit column, then start.column, then default to 1
817
+ const col = v.column ?? v.start?.column ?? 1;
818
+ return {
819
+ analyzer: v.analyzer || '',
820
+ rule: v.rule,
821
+ severity: v.severity,
822
+ message: v.message,
823
+ file: filePath,
824
+ line: v.line ?? v.start?.line,
825
+ column: col,
826
+ endLine: v.end?.line,
827
+ endColumn: v.end?.column,
828
+ enclosingSymbol: v.symbol || v.enclosingFunction || '',
829
+ suggestion: v.suggestion || '',
830
+ details: v.details || '',
831
+ ...(v.new !== undefined && { new: v.new }),
832
+ ...(v.dismissed !== undefined && { dismissed: v.dismissed }),
833
+ fingerprint: fingerprint(buildFingerprintInput(v))
834
+ };
835
+ });
836
+ // Coverage diagnostics ride alongside violations so an agent sees "the
837
+ // analyzer couldn't resolve this" without a metadata flag — and without
838
+ // it ever gating (diagnostics never reach computeGatingDecision).
839
+ const jsonDiagnostics = (result.metadata?.diagnostics ?? []).map((d) => ({
840
+ analyzer: d.analyzerName ?? d.analyzer ?? '',
841
+ kind: d.kind,
842
+ message: d.message,
843
+ ...(d.file ? { file: d.file } : {}),
844
+ ...(typeof d.line === 'number' ? { line: d.line } : {}),
845
+ ...(d.details ? { details: d.details } : {}),
846
+ }));
847
+ const jsonOutput = { violations: jsonViolations, diagnostics: jsonDiagnostics };
848
+ process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
849
+ }
850
+ else if (!options.quiet || violations.length > 0) {
851
+ // Console output — Spec 45 A2: agent-facing output emits counts (per
852
+ // analyzer/rule/severity, plus the before/after gate figure) alongside
853
+ // findings. The hook and the `changed` command are the agent's surface.
854
+ console.log(chalk.blue('🔍 Diff-Scoped Code Audit'));
855
+ console.log(chalk.gray('══════════════════════════════════════════════════'));
856
+ printFileAccounting(result, !!options.explainSkipped);
857
+ if (violations.length > 0) {
858
+ console.log('');
859
+ printCountSummary(violations);
860
+ console.log(chalk.bold(`gate before/after: ${violations.length} → ${blocking.length} blocking`));
861
+ if (dismissedCount > 0) {
862
+ console.log(chalk.dim(` ${dismissedCount} dismissed — still counted above, excluded from the gate`));
863
+ }
864
+ console.log(chalk.gray('── Violations ────────────────────────────────────'));
865
+ for (const v of violations) {
866
+ const icon = v.severity === 'critical' ? '🔴' :
867
+ v.severity === 'severe' ? '🟠' : '🟡';
868
+ const statusTag = v.new === false
869
+ ? chalk.dim(' [known — still open]')
870
+ : '';
871
+ const dismissedTag = v.dismissed
872
+ ? chalk.dim(' [dismissed]')
873
+ : '';
874
+ console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.severity}] ${v.message}${statusTag}${dismissedTag}`);
875
+ }
876
+ }
877
+ else {
878
+ console.log(chalk.green('\n✓ No readings.'));
879
+ }
880
+ }
881
+ }
774
882
  // Spec 45 A2 — counts are emitted where useful (per analyzer, per rule, per
775
883
  // severity), on agent-facing surfaces as well as human ones. This reverts Spec
776
884
  // 36 R3's "no bare counts": an agent now sees the shape of what fired instead
@@ -810,11 +918,61 @@ function printCountSummary(violations) {
810
918
  */
811
919
  async function buildChangedResultFromDiagnostics(violations, projectRoot) {
812
920
  const analyzerResults = {};
921
+ const byAnalyzer = {};
922
+ let totalViolations = 0;
923
+ let criticalIssues = 0;
924
+ let severe = 0;
925
+ let high = 0;
926
+ let dismissed = 0;
927
+ const violationsByCategory = {};
813
928
  for (const v of violations) {
814
929
  const key = v.analyzer || 'unknown';
815
930
  (analyzerResults[key] = analyzerResults[key] || { violations: [] }).violations.push(v);
931
+ totalViolations++;
932
+ switch (v.severity) {
933
+ case 'critical':
934
+ criticalIssues++;
935
+ break;
936
+ case 'severe':
937
+ severe++;
938
+ break;
939
+ case 'high':
940
+ high++;
941
+ break;
942
+ }
943
+ const rule = v.rule || 'unknown';
944
+ violationsByCategory[rule] = (violationsByCategory[rule] ?? 0) + 1;
945
+ const bucket = byAnalyzer[key] ?? (byAnalyzer[key] = { violations: 0 });
946
+ bucket.violations++;
947
+ if (v.dismissed)
948
+ dismissed++;
816
949
  }
817
- return { analyzerResults, metadata: { configUsed: undefined, diagnostics: [] } };
950
+ const topIssues = Object.entries(violationsByCategory)
951
+ .sort(([, a], [, b]) => b - a)
952
+ .slice(0, 5)
953
+ .map(([type, count]) => ({ type, count }));
954
+ // A *real* summary derived from the diagnostics the daemon served — not a
955
+ // zero-filled placeholder (a stub satisfying the type, with a `dismissed: 0`
956
+ // that was never measured). Every field here is computed from the actual
957
+ // findings: severity/category/byAnalyzer rollups, plus `dismissed` from each
958
+ // diagnostic's own `dismissed` flag. `totalFiles` and the per-analyzer
959
+ // `filesProcessed`/`fatalErrors`/`unprovenSites` are not derivable from a flat
960
+ // per-file diagnostic list, so they are omitted; the guarded read
961
+ // (`result.summary?.dismissed ?? 0`) tolerates their absence.
962
+ return {
963
+ analyzerResults,
964
+ summary: {
965
+ totalViolations,
966
+ criticalIssues,
967
+ severe,
968
+ high,
969
+ violationsByCategory,
970
+ topIssues,
971
+ byAnalyzer,
972
+ dismissed,
973
+ },
974
+ metadata: { configUsed: undefined, diagnostics: [] },
975
+ };
818
976
  }
819
977
  // Self-audit gate (Spec 33 Item 15 + Spec 44 remediation). Runs the full
820
978
  // analyzer pipeline over the tool's own production source and asserts zero
@@ -850,7 +1008,7 @@ program
850
1008
  .description('Audit the tool\'s own analyzers/ + languages/ source and fail on blocking readings')
851
1009
  .option('--json', 'Output violations as machine-readable JSON to stdout')
852
1010
  .option('--stdin', 'Read file paths from stdin (one per line)')
853
- .option('-p, --path <projectPath>', 'Project root path', process.cwd())
1011
+ .option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
854
1012
  .option('--fail-on <severity>', 'Blocking severity floor: critical, severe, or high', 'high')
855
1013
  .action(async (paths, options) => {
856
1014
  try {
@@ -889,7 +1047,6 @@ program
889
1047
  const runner = createAuditRunner({
890
1048
  projectRoot: options.path,
891
1049
  scope,
892
- analyzerConcurrency: 4
893
1050
  });
894
1051
  const result = await runner.run();
895
1052
  const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
@@ -934,7 +1091,7 @@ program
934
1091
  for (const v of blocking) {
935
1092
  const icon = v.severity === 'critical' ? '🔴' :
936
1093
  v.severity === 'severe' ? '🟠' : '🟡';
937
- console.log(`${icon} ${v.file}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.message}`);
1094
+ console.log(`${icon} ${v.file}${lineSuffix(v.line)} [${v.severity}] ${v.message}`);
938
1095
  }
939
1096
  }
940
1097
  else {
@@ -960,8 +1117,8 @@ program
960
1117
  program
961
1118
  .command('next-file')
962
1119
  .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')
1120
+ .option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
1121
+ .option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
965
1122
  .option('--json', 'Output a single JSON object (or {done:true}) to stdout')
966
1123
  .action(async (options) => {
967
1124
  try {
@@ -1059,7 +1216,7 @@ function printNextFile(violations, summary, projectDir, json) {
1059
1216
  console.log(chalk.gray('\n── Readings ────────────────────────────────────────'));
1060
1217
  for (const v of ordered) {
1061
1218
  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}`);
1219
+ console.log(`${icon} ${chalk.bold(relativize(v.file || ''))}${lineSuffix(v.line)} [${v.severity}] ${v.rule} — ${v.message}`);
1063
1220
  }
1064
1221
  }
1065
1222
  }
@@ -1067,8 +1224,8 @@ function printNextFile(violations, summary, projectDir, json) {
1067
1224
  program
1068
1225
  .command('baseline')
1069
1226
  .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')
1227
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1228
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1072
1229
  .action(async (options) => {
1073
1230
  try {
1074
1231
  await initParsers();
@@ -1134,9 +1291,9 @@ program
1134
1291
  .command('dismiss <fingerprint>')
1135
1292
  .description('Dismiss one finding by fingerprint — a written reason is required')
1136
1293
  .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())
1294
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1138
1295
  .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')
1296
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1140
1297
  .action(async (fingerprintArg, options) => {
1141
1298
  try {
1142
1299
  const reason = (options.reason ?? '').trim();
@@ -1248,7 +1405,7 @@ indexCmd
1248
1405
  .command('sync')
1249
1406
  .description('Synchronize index from source files')
1250
1407
  .option('--path <path>', 'Path to a specific file or directory', process.cwd())
1251
- .option('--json', 'Output as JSON')
1408
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1252
1409
  .action(async (options) => {
1253
1410
  try {
1254
1411
  await initParsers();
@@ -1289,64 +1446,65 @@ indexCmd
1289
1446
  process.exit(1);
1290
1447
  }
1291
1448
  });
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');
1449
+ /** Register a simple `index` subcommand that runs one mutation on the
1450
+ * initialized index and prints the outcome (JSON or console). `cleanup` and
1451
+ * `reset` share the get-instance/initialize/json-vs-console/try-catch
1452
+ * scaffolding; only the mutation and its result prose differ, so they live
1453
+ * here once and each command is a thin `defineIndexMutationCommand(...)` call. */
1454
+ function defineIndexMutationCommand(name, description, work) {
1455
+ indexCmd
1456
+ .command(name)
1457
+ .description(description)
1458
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1459
+ .action(async (options) => {
1460
+ try {
1461
+ const db = CodeIndexDB.getInstance();
1462
+ await db.initialize();
1463
+ const { extra, greenLine, grayLine } = await work(db);
1464
+ if (options.json) {
1465
+ process.stdout.write(JSON.stringify({ mode: name, success: true, ...extra }) + '\n');
1466
+ }
1467
+ else {
1468
+ console.log(chalk.green(greenLine));
1469
+ console.log(chalk.gray(grayLine));
1470
+ }
1303
1471
  }
1304
- else {
1305
- console.log(chalk.green('✓ Cleanup complete'));
1306
- console.log(chalk.gray(`Removed ${result.removedCount} entries from ${result.removedFiles.length} deleted files`));
1472
+ catch (error) {
1473
+ console.error(chalk.red('Error:'), error);
1474
+ process.exit(1);
1307
1475
  }
1308
- }
1309
- catch (error) {
1310
- console.error(chalk.red('Error:'), error);
1311
- process.exit(1);
1312
- }
1476
+ });
1477
+ }
1478
+ defineIndexMutationCommand('cleanup', 'Remove index entries for deleted files', async (db) => {
1479
+ const result = await db.bulkCleanup();
1480
+ return {
1481
+ extra: result,
1482
+ greenLine: '✓ Cleanup complete',
1483
+ grayLine: `Removed ${result.removedCount} entries from ${result.removedFiles.length} deleted files`,
1484
+ };
1313
1485
  });
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
- }
1486
+ defineIndexMutationCommand('reset', 'Clear all analysis data (preserves tasks, config, whitelist)', async (db) => {
1487
+ await db.clearIndex();
1488
+ return {
1489
+ extra: { message: 'All analysis data cleared' },
1490
+ greenLine: '✓ Index reset complete',
1491
+ grayLine: 'All analysis-derived data cleared (tasks, config, and whitelist preserved)',
1492
+ };
1335
1493
  });
1336
1494
  // ── Index status command (Spec 14 R1) ──────────────────────────
1337
1495
  indexCmd
1338
1496
  .command('status')
1339
1497
  .description('Show index health and graph statistics')
1340
- .option('--json', 'Output as JSON')
1498
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1341
1499
  .action(async (options) => {
1342
1500
  try {
1343
1501
  const db = CodeIndexDB.getInstance();
1344
1502
  await db.initialize();
1345
- const stats = await db.getStats();
1503
+ const stats = await db.functionIndex.getStats();
1346
1504
  // Graph stats — non-fatal if graph cache is empty
1347
1505
  let graphStats = null;
1348
1506
  try {
1349
- graphStats = db.getGraphStats();
1507
+ graphStats = db.graph.getGraphStats();
1350
1508
  }
1351
1509
  catch {
1352
1510
  // No graph data yet — ok
@@ -1417,7 +1575,7 @@ configCmd
1417
1575
  .command('rules-list')
1418
1576
  .description('List all configured invariant rules from .codeauditor.json')
1419
1577
  .option('--config-path <path>', 'Path to .codeauditor.json')
1420
- .option('--json', 'Output as JSON')
1578
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1421
1579
  .action(async (options) => {
1422
1580
  try {
1423
1581
  const pathModule = await import('path');
@@ -1427,7 +1585,9 @@ configCmd
1427
1585
  try {
1428
1586
  const raw = readFileSync(configPath, 'utf-8');
1429
1587
  const config = JSON.parse(raw);
1430
- const rules = config?.rules ?? [];
1588
+ // `rules` is an array by schema; tolerate the `{}` shorthand some fixtures
1589
+ // and hand-edited configs use for "no rules" — `.map` on `{}` would throw.
1590
+ const rules = Array.isArray(config?.rules) ? config.rules : [];
1431
1591
  if (options.json) {
1432
1592
  process.stdout.write(JSON.stringify({
1433
1593
  rules: rules.map((r) => ({ id: r.id, kind: r.kind, severity: r.severity, message: r.message || null })),
@@ -1465,7 +1625,7 @@ configCmd
1465
1625
  .command('rules-check')
1466
1626
  .description('Validate the current .codeauditor.json rules')
1467
1627
  .option('--config-path <path>', 'Path to .codeauditor.json')
1468
- .option('--json', 'Output as JSON')
1628
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1469
1629
  .action(async (options) => {
1470
1630
  try {
1471
1631
  const pathModule = await import('path');
@@ -1513,9 +1673,9 @@ configCmd
1513
1673
  configCmd
1514
1674
  .command('profiles')
1515
1675
  .description('Show active path profiles and resolve per-file matching')
1516
- .option('-p, --path <path>', 'Project path', process.cwd())
1676
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1517
1677
  .option('--file <file>', 'Resolve profiles for a specific file')
1518
- .option('--json', 'Output as JSON')
1678
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1519
1679
  .action(async (options) => {
1520
1680
  try {
1521
1681
  const fs = await import('fs/promises');
@@ -1613,8 +1773,8 @@ configCmd
1613
1773
  configCmd
1614
1774
  .command('detection')
1615
1775
  .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')
1776
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1777
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1618
1778
  .option('--mode <mode>', 'Detection mode filter (hybrid, provenance, names)')
1619
1779
  .action(async (options) => {
1620
1780
  try {
@@ -1666,9 +1826,7 @@ configCmd
1666
1826
  // Build provenance context using schema analyzer config (most complete)
1667
1827
  const ctx = buildProvenanceContext(ast, adapter, sourceCode, {
1668
1828
  mode: detectionMode,
1669
- dbReceiverNames: schemaCfg.dbReceiverNames ?? daCfg.dbReceiverNames,
1670
1829
  dbBindingNames: schemaCfg.dbBindingNames ?? daCfg.dbBindingNames,
1671
- dbCallMethods: schemaCfg.dbCallMethods ?? daCfg.dbCallMethods,
1672
1830
  });
1673
1831
  // Collect DB-provenanced
1674
1832
  for (const [id, evidence] of ctx.dbProvenanced) {
@@ -1721,70 +1879,84 @@ configCmd
1721
1879
  entry.validatorProvenanced.push(v);
1722
1880
  }
1723
1881
  for (const [file, data] of byFile) {
1724
- db.storeFileProvenance(file, data);
1882
+ db.provenance.storeFileProvenance(file, data);
1725
1883
  }
1726
1884
  // Store inferred receivers
1727
- db.storeInferredReceivers(inferred);
1885
+ db.inferredReceivers.storeInferredReceivers(inferred);
1728
1886
  }
1729
1887
  catch {
1730
1888
  // Index not available — skip persistence
1731
1889
  }
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
- }
1890
+ printDetectionResults({
1891
+ json: options.json,
1892
+ mode: detectionMode,
1893
+ projectRoot,
1894
+ filesScanned: files.slice(0, 500).length,
1895
+ dbProvenanced: provenanced,
1896
+ validatorProvenanced: validatorIdentifiers,
1897
+ inferred,
1898
+ });
1782
1899
  }
1783
1900
  catch (error) {
1784
1901
  console.error(chalk.red('Error:'), error);
1785
1902
  process.exit(1);
1786
1903
  }
1787
1904
  });
1905
+ // Renders the `config detection` results — pulled out of the `.action` handler so
1906
+ // the scan/persist logic stays short and the two output shapes (JSON vs human) are
1907
+ // isolated from the provenance collection.
1908
+ function printDetectionResults(params) {
1909
+ const { json, mode, projectRoot, filesScanned, dbProvenanced, validatorProvenanced, inferred } = params;
1910
+ if (json) {
1911
+ process.stdout.write(JSON.stringify({
1912
+ mode,
1913
+ projectRoot,
1914
+ filesScanned,
1915
+ dbProvenanced,
1916
+ validatorProvenanced,
1917
+ inferred,
1918
+ }, null, 2) + '\n');
1919
+ return;
1920
+ }
1921
+ console.log(chalk.bold(`Detection mode: ${chalk.cyan(mode)}`));
1922
+ console.log(chalk.gray(`Scanned ${filesScanned} files`));
1923
+ console.log();
1924
+ if (dbProvenanced.length > 0) {
1925
+ console.log(chalk.bold('DB-Provenanced Identifiers:'));
1926
+ for (const p of dbProvenanced) {
1927
+ const reasonColor = p.reason === 'fallback'
1928
+ ? chalk.yellow
1929
+ : p.reason === 'package'
1930
+ ? chalk.green
1931
+ : chalk.cyan;
1932
+ console.log(` ${chalk.white(p.identifier)} ${chalk.gray(`(${p.file})`)}`);
1933
+ console.log(` ${reasonColor(`reason: ${p.reason}`)} — ${chalk.gray(p.source)}`);
1934
+ if (p.chain) {
1935
+ console.log(` ${chalk.gray(`chain: ${p.chain.join(' → ')}`)}`);
1936
+ }
1937
+ }
1938
+ console.log();
1939
+ }
1940
+ if (inferred.length > 0) {
1941
+ console.log(chalk.bold('Inferred Receivers:'));
1942
+ for (const i of inferred) {
1943
+ console.log(` ${chalk.white(i.identifier)} ${chalk.gray(`(${i.file})`)}`);
1944
+ console.log(` ${chalk.magenta(i.reason)}`);
1945
+ }
1946
+ console.log();
1947
+ }
1948
+ if (validatorProvenanced.length > 0) {
1949
+ console.log(chalk.bold('Validator-Provenanced Identifiers:'));
1950
+ for (const v of validatorProvenanced) {
1951
+ console.log(` ${chalk.white(v.identifier)} ${chalk.gray(`(${v.file})`)}`);
1952
+ console.log(` ${chalk.green(`reason: ${v.reason}`)} — ${chalk.gray(v.source)}`);
1953
+ }
1954
+ console.log();
1955
+ }
1956
+ if (dbProvenanced.length === 0 && inferred.length === 0 && validatorProvenanced.length === 0) {
1957
+ console.log(chalk.gray('No DB-provenanced, inferred, or validator-provenanced identifiers found.'));
1958
+ }
1959
+ }
1788
1960
  // Search command
1789
1961
  program
1790
1962
  .command('search <query>')
@@ -1852,7 +2024,7 @@ tasksCmd
1852
2024
  .option('--priority <priority>', 'Filter by priority (low, medium, high)')
1853
2025
  .option('--label <label>', 'Filter by label')
1854
2026
  .option('--limit <limit>', 'Result limit', '50')
1855
- .option('--json', 'Output as JSON')
2027
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1856
2028
  .action(async (options) => {
1857
2029
  try {
1858
2030
  const args = { action: 'list' };
@@ -1887,7 +2059,7 @@ tasksCmd
1887
2059
  }
1888
2060
  }
1889
2061
  else {
1890
- console.error(chalk.red(result.error || 'Unknown error'));
2062
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
1891
2063
  process.exit(1);
1892
2064
  }
1893
2065
  }
@@ -1904,7 +2076,7 @@ tasksCmd
1904
2076
  .option('--priority <priority>', 'Priority (low, medium, high)')
1905
2077
  .option('--status <status>', 'Status (open, in_progress)')
1906
2078
  .option('--labels <labels>', 'Comma-separated labels')
1907
- .option('--json', 'Output as JSON')
2079
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1908
2080
  .action(async (options) => {
1909
2081
  try {
1910
2082
  const args = {
@@ -1929,7 +2101,7 @@ tasksCmd
1929
2101
  console.log(chalk.green(`✓ Task created: ${task.title || task.taskId || 'unknown'}`));
1930
2102
  }
1931
2103
  else {
1932
- console.error(chalk.red(result.error || 'Unknown error'));
2104
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
1933
2105
  process.exit(1);
1934
2106
  }
1935
2107
  }
@@ -1941,7 +2113,7 @@ tasksCmd
1941
2113
  tasksCmd
1942
2114
  .command('get <taskId>')
1943
2115
  .description('Get a task by ID')
1944
- .option('--json', 'Output as JSON')
2116
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1945
2117
  .action(async (taskId, options) => {
1946
2118
  try {
1947
2119
  const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
@@ -1967,7 +2139,7 @@ tasksCmd
1967
2139
  console.log(chalk.dim(`\nfingerprint: ${t.fingerprint}`));
1968
2140
  }
1969
2141
  else {
1970
- console.error(chalk.red(result.error || 'Unknown error'));
2142
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
1971
2143
  process.exit(1);
1972
2144
  }
1973
2145
  }
@@ -1984,7 +2156,7 @@ tasksCmd
1984
2156
  .option('--status <status>', 'New status (open, in_progress, completed)')
1985
2157
  .option('--priority <priority>', 'New priority (low, medium, high)')
1986
2158
  .option('--labels <labels>', 'Comma-separated labels')
1987
- .option('--json', 'Output as JSON')
2159
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1988
2160
  .action(async (taskId, options) => {
1989
2161
  try {
1990
2162
  const patch = {};
@@ -2011,7 +2183,7 @@ tasksCmd
2011
2183
  console.log(chalk.green(`✓ Task updated`));
2012
2184
  }
2013
2185
  else {
2014
- console.error(chalk.red(result.error || 'Unknown error'));
2186
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
2015
2187
  process.exit(1);
2016
2188
  }
2017
2189
  }
@@ -2020,60 +2192,45 @@ tasksCmd
2020
2192
  process.exit(1);
2021
2193
  }
2022
2194
  });
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`));
2195
+ /** Run a single-taskId tasks subcommand and print its result. */
2196
+ function makeTaskIdAction(action, successLabel) {
2197
+ return async (taskId, options) => {
2198
+ try {
2199
+ const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
2200
+ const result = await handleProjectTasks({ action, taskId });
2201
+ if (options.json) {
2202
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
2203
+ }
2204
+ else if (result.success) {
2205
+ console.log(chalk.green(`✓ ${successLabel}`));
2206
+ }
2207
+ else {
2208
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
2209
+ process.exit(1);
2210
+ }
2036
2211
  }
2037
- else {
2038
- console.error(chalk.red(result.error || 'Unknown error'));
2212
+ catch (error) {
2213
+ console.error(chalk.red('Error:'), error);
2039
2214
  process.exit(1);
2040
2215
  }
2041
- }
2042
- catch (error) {
2043
- console.error(chalk.red('Error:'), error);
2044
- process.exit(1);
2045
- }
2046
- });
2216
+ };
2217
+ }
2218
+ tasksCmd
2219
+ .command('complete <taskId>')
2220
+ .description('Mark a task as completed')
2221
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2222
+ .action(makeTaskIdAction('complete_task', 'Task completed'));
2047
2223
  tasksCmd
2048
2224
  .command('delete <taskId>')
2049
2225
  .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
- });
2226
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2227
+ .action(makeTaskIdAction('delete', 'Task deleted'));
2071
2228
  tasksCmd
2072
2229
  .command('from-audit')
2073
2230
  .description('Populate tasks from audit violations')
2074
2231
  .option('--auditJobId <id>', 'Specific audit result ID')
2075
2232
  .option('--severities <severities>', 'Comma-separated severities (critical,severe,high)', 'critical,severe,high')
2076
- .option('--json', 'Output as JSON')
2233
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2077
2234
  .action(async (options) => {
2078
2235
  try {
2079
2236
  const args = { action: 'from_audit' };
@@ -2100,7 +2257,7 @@ tasksCmd
2100
2257
  console.log(chalk.yellow(result.error));
2101
2258
  }
2102
2259
  else {
2103
- console.error(chalk.red(result.error || 'Unknown error'));
2260
+ console.error(chalk.red(result.error || UNKNOWN_ERROR));
2104
2261
  process.exit(1);
2105
2262
  }
2106
2263
  }
@@ -2116,7 +2273,7 @@ program
2116
2273
  .description('Rank files and functions by hotspot score (churn × complexity)')
2117
2274
  .option('--limit <n>', 'Max entries to show')
2118
2275
  .option('--path <dir>', 'Target directory (triggers spot churn extraction if index is empty)')
2119
- .option('--json', 'Output as JSON')
2276
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2120
2277
  .action(async (options) => {
2121
2278
  try {
2122
2279
  const db = CodeIndexDB.getInstance();
@@ -2191,8 +2348,8 @@ program
2191
2348
  .command('risk')
2192
2349
  .description('Rank functions by architectural risk (PageRank × betweenness × complexity × untested)')
2193
2350
  .option('--limit <n>', 'Max entries to show', '20')
2194
- .option('--path <dir>', 'Project path', process.cwd())
2195
- .option('--json', 'Output as JSON')
2351
+ .option('--path <dir>', OPTION_PROJECT_PATH, process.cwd())
2352
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2196
2353
  .option('--format <format>', 'Output format: dot (call-graph neighborhood of top functions)')
2197
2354
  .action(async (options) => {
2198
2355
  try {
@@ -2207,10 +2364,13 @@ program
2207
2364
  if (options.format === 'dot') {
2208
2365
  // DOT output: call-graph neighborhood of top-N risk functions
2209
2366
  const { callGraphToDot } = await import('./graph/outputFormatter.js');
2210
- // Collect IDs for top risk functions
2367
+ // Collect IDs for top risk functions — one query for the whole top-N
2368
+ // set, not one query per function (loop-query).
2211
2369
  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);
2370
+ if (top.length > 0) {
2371
+ const orClause = top.map(() => '(name = ? AND file_path = ?)').join(' OR ');
2372
+ const params = top.flatMap((e) => [e.functionName, e.filePath]);
2373
+ const rows = rawDb.prepare(`SELECT id FROM functions WHERE ${orClause}`).all(...params);
2214
2374
  for (const r of rows)
2215
2375
  topIds.add(r.id);
2216
2376
  }
@@ -2267,7 +2427,7 @@ ledgerCmd
2267
2427
  .command('list')
2268
2428
  .description('List recent audit runs')
2269
2429
  .option('--limit <n>', 'Max runs to show', '20')
2270
- .option('--json', 'Output as JSON')
2430
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2271
2431
  .action(async (options) => {
2272
2432
  try {
2273
2433
  const { listRuns } = await import('./ledger.js');
@@ -2316,7 +2476,7 @@ ledgerCmd
2316
2476
  ledgerCmd
2317
2477
  .command('stats')
2318
2478
  .description('Show aggregated ledger statistics')
2319
- .option('--json', 'Output as JSON')
2479
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2320
2480
  .action(async (options) => {
2321
2481
  try {
2322
2482
  const { getLedgerStats } = await import('./ledger.js');
@@ -2349,7 +2509,7 @@ ledgerCmd
2349
2509
  .command('trends')
2350
2510
  .description('Compare consecutive full-audit runs and report new vs fixed readings per rule')
2351
2511
  .option('--since <runId>', 'Only consider runs after this run ID')
2352
- .option('--json', 'Output as JSON')
2512
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2353
2513
  .action(async (options) => {
2354
2514
  try {
2355
2515
  const { getTrends } = await import('./ledger.js');
@@ -2412,9 +2572,6 @@ ledgerCmd
2412
2572
  // `audit --detach` forks workers/auditJobRunner; `status`/`result`/`jobs` read
2413
2573
  // the persisted ledger. One job model (the ledger run), one write path — these
2414
2574
  // 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
2575
  /** Lease TTL used by read-path reclaim, mirroring mcpAuditJobs.jobLeaseTtlMs. */
2419
2576
  const CLI_JOB_LEASE_TTL_MS = Number(process.env.CODE_AUDITOR_JOB_LEASE_TTL_MS) || 30_000;
2420
2577
  function spec41StatusBadge(status) {
@@ -2496,12 +2653,8 @@ async function runDetachedAudit(options) {
2496
2653
  const logFd = openSync(logPath, 'a');
2497
2654
  const argsJson = JSON.stringify({
2498
2655
  path: auditPath,
2499
- ...(options.partitionStrategy && { partitionStrategy: options.partitionStrategy }),
2500
- ...(options.maxPartitions && { maxPartitions: Number(options.maxPartitions) }),
2501
- ...(options.shardTimeoutMs && { shardTimeoutMs: Number(options.shardTimeoutMs) }),
2502
2656
  });
2503
2657
  const defaultsJson = JSON.stringify({
2504
- defaultAnalyzers: DETACHED_DEFAULT_ANALYZERS,
2505
2658
  defaultMinSeverity: 'high',
2506
2659
  defaultGenerateCodeMap: false,
2507
2660
  });
@@ -2521,7 +2674,7 @@ program
2521
2674
  .command('status <jobId>')
2522
2675
  .description('Show a detached audit job status, progress, and staleness')
2523
2676
  .option('-p, --path <path>', 'Project root containing the job (default: cwd)', process.cwd())
2524
- .option('--json', 'Output as JSON')
2677
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2525
2678
  .action(async (jobId, options) => {
2526
2679
  try {
2527
2680
  const { getLedgerRun, reclaimStaleRunning, computeStaleness } = await import('./ledger.js');
@@ -2584,12 +2737,12 @@ program
2584
2737
  .option('--analyzer <analyzer>', 'Filter by analyzer')
2585
2738
  .option('--file <file>', 'Filter by file path (substring match)')
2586
2739
  .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')
2740
+ .option('--state [state]', 'Query coverage by state (fired|clean|notApplicable|cannot-fire|incomplete); omit value for all')
2588
2741
  .option('--count', 'Group findings by analyzer/rule with counts')
2589
2742
  .option('--limit <n>', 'Max findings to return (0 = unbounded)', '50')
2590
2743
  .option('--offset <n>', 'Findings offset', '0')
2591
2744
  .option('--stale', 'Force full content re-hash for staleness')
2592
- .option('--json', 'Output as JSON')
2745
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2593
2746
  .action(async (jobId, options) => {
2594
2747
  try {
2595
2748
  const { getLedgerRun, queryLedgerFindings, queryLedgerCoverage, computeStaleness } = await import('./ledger.js');
@@ -2682,7 +2835,7 @@ program
2682
2835
  .option('--prune', 'Delete all but the newest runs for this project')
2683
2836
  .option('--keep <n>', 'Runs to keep when --prune is set', '10')
2684
2837
  .option('--limit <n>', 'Runs to display (staleness computed for these)', '20')
2685
- .option('--json', 'Output as JSON')
2838
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2686
2839
  .action(async (options) => {
2687
2840
  try {
2688
2841
  const { listLedgerRuns, reclaimStaleRunning, pruneLedgerRuns, computeStaleness } = await import('./ledger.js');
@@ -2738,7 +2891,7 @@ conventionsCmd
2738
2891
  .command('list')
2739
2892
  .description('List mined conventions from the code index')
2740
2893
  .option('--domain <domain>', 'Filter by domain: usage-pair, import-form, error-handling, export-shape, naming')
2741
- .option('--json', 'Output as JSON')
2894
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2742
2895
  .action(async (options) => {
2743
2896
  try {
2744
2897
  const db = CodeIndexDB.getInstance();
@@ -2802,7 +2955,7 @@ conventionsCmd
2802
2955
  .command('propose')
2803
2956
  .description('Emit ready-to-paste .codeauditor.json rules from mined conventions')
2804
2957
  .option('--domain <domain>', 'Filter by domain: naming or import-form (other domains do not map to rule kinds)')
2805
- .option('--json', 'Output as JSON')
2958
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2806
2959
  .action(async (options) => {
2807
2960
  try {
2808
2961
  const db = CodeIndexDB.getInstance();
@@ -2912,8 +3065,8 @@ program
2912
3065
  program
2913
3066
  .command('architecture')
2914
3067
  .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')
3068
+ .option('--path <dir>', OPTION_PROJECT_PATH, process.cwd())
3069
+ .option('--json', OPTION_OUTPUT_AS_JSON)
2917
3070
  .option('--format <format>', 'Output format: dot or mermaid (import graph colored by community)')
2918
3071
  .action(async (options) => {
2919
3072
  try {
@@ -3111,7 +3264,7 @@ coverageCmd
3111
3264
  coverageCmd
3112
3265
  .command('by-risk', { isDefault: true })
3113
3266
  .description('Show coverage rate by risk decile')
3114
- .option('--json', 'Output as JSON')
3267
+ .option('--json', OPTION_OUTPUT_AS_JSON)
3115
3268
  .action(async (options) => {
3116
3269
  try {
3117
3270
  const { generateCoverageReport } = await import('./coverage/coverageService.js');
@@ -3160,8 +3313,8 @@ coverageCmd
3160
3313
  program
3161
3314
  .command('print-config <file>')
3162
3315
  .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')
3316
+ .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
3317
+ .option('--json', OPTION_OUTPUT_AS_JSON)
3165
3318
  .option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
3166
3319
  .action(async (file, options) => {
3167
3320
  try {
@@ -3192,7 +3345,6 @@ program
3192
3345
  }
3193
3346
  const analyzerConfigs = (config.analyzerConfigs ?? {});
3194
3347
  const pathProfiles = (config.pathProfiles ?? []);
3195
- const enabledAnalyzers = (config.enabledAnalyzers ?? []);
3196
3348
  // Resolve --preset ids to ordered preset objects (Spec 38 R4). Unknown
3197
3349
  // ids are a hard error, not a silent skip.
3198
3350
  const presetIds = Array.isArray(options.preset) ? options.preset : (options.preset ? [options.preset] : []);
@@ -3212,7 +3364,6 @@ program
3212
3364
  projectRoot,
3213
3365
  analyzerConfigs,
3214
3366
  pathProfiles,
3215
- enabledAnalyzers,
3216
3367
  presets,
3217
3368
  lintConfig,
3218
3369
  });
@@ -3325,7 +3476,7 @@ program
3325
3476
  for (const v of sorted) {
3326
3477
  const icon = v.severity === 'critical' ? '🔴' :
3327
3478
  v.severity === 'severe' ? '🟠' : '🟡';
3328
- console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.rule || v.severity}] ${v.message}`);
3479
+ console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.rule || v.severity}] ${v.message}`);
3329
3480
  }
3330
3481
  }
3331
3482
  catch (error) {
@@ -3390,8 +3541,8 @@ const daemonCmd = program
3390
3541
  daemonCmd
3391
3542
  .command('start')
3392
3543
  .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')
3544
+ .option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
3545
+ .option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
3395
3546
  .option('--foreground', 'Run in the foreground (blocking; useful for debugging)')
3396
3547
  .action(async (options) => {
3397
3548
  try {
@@ -3422,7 +3573,7 @@ daemonCmd
3422
3573
  daemonCmd
3423
3574
  .command('stop')
3424
3575
  .description('Ask the daemon to shut down')
3425
- .option('-p, --path <path>', 'Project root', process.cwd())
3576
+ .option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
3426
3577
  .action(async (options) => {
3427
3578
  try {
3428
3579
  const { resolveDaemonSocketPath } = await import('./dataPaths.js');
@@ -3444,8 +3595,8 @@ daemonCmd
3444
3595
  daemonCmd
3445
3596
  .command('status')
3446
3597
  .description('Show daemon status')
3447
- .option('-p, --path <path>', 'Project root', process.cwd())
3448
- .option('--json', 'Output as JSON')
3598
+ .option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
3599
+ .option('--json', OPTION_OUTPUT_AS_JSON)
3449
3600
  .action(async (options) => {
3450
3601
  try {
3451
3602
  const { resolveDaemon } = await import('./daemon/resolve.js');
@@ -3638,6 +3789,136 @@ async function generateConfigurations(options) {
3638
3789
  console.log(chalk.gray(' 2. Run ') + chalk.cyan('code-audit') + chalk.gray(' to enforce your rules'));
3639
3790
  console.log(chalk.gray(' 3. Use ') + chalk.cyan('code-audit changed') + chalk.gray(' in your agent hook (gating rules block on new readings)'));
3640
3791
  }
3792
+ async function promptKindSpecificFields(kind, rule) {
3793
+ switch (kind) {
3794
+ case 'import-ban': {
3795
+ const { module } = await inquirer.prompt({
3796
+ module: {
3797
+ type: 'input',
3798
+ message: 'Banned module specifier (e.g. "lodash" or "@old-lib/*"):',
3799
+ validate: (input) => input.trim() ? true : 'Module specifier is required',
3800
+ },
3801
+ });
3802
+ rule.module = module;
3803
+ const { addExcept } = await inquirer.prompt({
3804
+ addExcept: {
3805
+ type: 'confirm',
3806
+ message: 'Add exception paths (files allowed to import it)?',
3807
+ default: false,
3808
+ },
3809
+ });
3810
+ if (addExcept) {
3811
+ const { except } = await inquirer.prompt({
3812
+ except: {
3813
+ type: 'input',
3814
+ message: 'Exception globs (comma-separated, e.g. "src/migration/**"):',
3815
+ },
3816
+ });
3817
+ const exceptList = except.split(',').map((s) => s.trim()).filter(Boolean);
3818
+ if (exceptList.length > 0)
3819
+ rule.except = exceptList;
3820
+ }
3821
+ break;
3822
+ }
3823
+ case 'call-constraint': {
3824
+ const { callee } = await inquirer.prompt({
3825
+ callee: {
3826
+ type: 'input',
3827
+ message: 'Callee (function name, optionally path-qualified as "path/glob#name"):',
3828
+ validate: (input) => input.trim() ? true : 'Callee is required',
3829
+ },
3830
+ });
3831
+ rule.callee = callee;
3832
+ const { mode } = await inquirer.prompt({
3833
+ mode: {
3834
+ type: 'list',
3835
+ message: 'Restriction mode:',
3836
+ choices: [
3837
+ { name: 'Allow only specific callers (allowFrom)', value: 'allow' },
3838
+ { name: 'Deny specific callers (denyFrom)', value: 'deny' },
3839
+ ],
3840
+ },
3841
+ });
3842
+ const { paths } = await inquirer.prompt({
3843
+ paths: {
3844
+ type: 'input',
3845
+ message: `Path globs (comma-separated) for ${mode === 'allow' ? 'allowFrom' : 'denyFrom'}:`,
3846
+ validate: (input) => input.trim() ? true : 'At least one path glob is required',
3847
+ },
3848
+ });
3849
+ const pathList = paths.split(',').map((s) => s.trim()).filter(Boolean);
3850
+ if (mode === 'allow') {
3851
+ rule.allowFrom = pathList;
3852
+ }
3853
+ else {
3854
+ rule.denyFrom = pathList;
3855
+ }
3856
+ break;
3857
+ }
3858
+ case 'module-boundary': {
3859
+ const { from, to } = await inquirer.prompt({
3860
+ from: {
3861
+ type: 'input',
3862
+ message: 'From (path glob for files that must not import):',
3863
+ validate: (input) => input.trim() ? true : '"from" path glob is required',
3864
+ },
3865
+ to: {
3866
+ type: 'input',
3867
+ message: 'To (path glob for files that must not be imported):',
3868
+ validate: (input) => input.trim() ? true : '"to" path glob is required',
3869
+ },
3870
+ });
3871
+ rule.from = from;
3872
+ rule.to = to;
3873
+ break;
3874
+ }
3875
+ case 'naming': {
3876
+ const { path, exports: exportPattern } = await inquirer.prompt({
3877
+ path: {
3878
+ type: 'input',
3879
+ message: 'Path (glob for files this rule applies to):',
3880
+ validate: (input) => input.trim() ? true : 'Path glob is required',
3881
+ },
3882
+ exports: {
3883
+ type: 'input',
3884
+ message: 'Exports regex (exported symbols must match, e.g. "^use[A-Z]"):',
3885
+ validate: (input) => input.trim() ? true : 'Exports regex is required',
3886
+ },
3887
+ });
3888
+ rule.path = path;
3889
+ rule.exports = exportPattern;
3890
+ break;
3891
+ }
3892
+ case 'ast-pattern': {
3893
+ const { pattern } = await inquirer.prompt({
3894
+ pattern: {
3895
+ type: 'input',
3896
+ message: 'AST pattern (ast-grep syntax, e.g. "new Function($$$)"):',
3897
+ validate: (input) => input.trim() ? true : 'Pattern is required',
3898
+ },
3899
+ });
3900
+ rule.pattern = pattern;
3901
+ const { addLanguage } = await inquirer.prompt({
3902
+ addLanguage: {
3903
+ type: 'confirm',
3904
+ message: 'Restrict to a specific language? (default: typescript)',
3905
+ default: false,
3906
+ },
3907
+ });
3908
+ if (addLanguage) {
3909
+ const { language } = await inquirer.prompt({
3910
+ language: {
3911
+ type: 'list',
3912
+ message: 'Language:',
3913
+ choices: ['typescript', 'javascript', 'go'],
3914
+ },
3915
+ });
3916
+ rule.language = language;
3917
+ }
3918
+ break;
3919
+ }
3920
+ }
3921
+ }
3641
3922
  /**
3642
3923
  * Interactive rule builder — walks the user through adding rules one at a time.
3643
3924
  */
@@ -3696,134 +3977,7 @@ async function buildRulesInteractively() {
3696
3977
  message: common.message,
3697
3978
  };
3698
3979
  // 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
- }
3980
+ await promptKindSpecificFields(kind, rule);
3827
3981
  rules.push(rule);
3828
3982
  console.log(chalk.green(` ✓ Added rule "${rule.id}" [${rule.kind}]`));
3829
3983
  const { cont } = await inquirer.prompt({
@@ -3956,7 +4110,7 @@ async function runSearch(query, options) {
3956
4110
  await db.initialize();
3957
4111
  // --definition: look up a specific symbol
3958
4112
  if (options.definition) {
3959
- const func = await db.findDefinition(query);
4113
+ const func = await db.functionIndex.findDefinition(query);
3960
4114
  if (!func) {
3961
4115
  console.log(chalk.yellow(`No definition found for "${query}"`));
3962
4116
  return;
@@ -3968,9 +4122,6 @@ async function runSearch(query, options) {
3968
4122
  console.log(chalk.blue(`\nDefinition: ${func.name}`));
3969
4123
  console.log(chalk.gray('──────────────────────────────────────────'));
3970
4124
  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
4125
  if (func.purpose) {
3975
4126
  console.log(`${chalk.bold('Purpose:')} ${func.purpose}`);
3976
4127
  }
@@ -4002,7 +4153,7 @@ async function runSearch(query, options) {
4002
4153
  }
4003
4154
  searchOptions.filters.language = options.language;
4004
4155
  }
4005
- const result = await db.searchFunctions(searchOptions);
4156
+ const result = await db.search.searchFunctions(searchOptions);
4006
4157
  if (options.json) {
4007
4158
  process.stdout.write(JSON.stringify({
4008
4159
  query,
@@ -4025,9 +4176,6 @@ async function runSearch(query, options) {
4025
4176
  const lang = func.language ? chalk.dim(` [${func.language}]`) : '';
4026
4177
  const lineInfo = func.lineNumber ? chalk.dim(`:${func.lineNumber}`) : '';
4027
4178
  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
4179
  if (func.purpose) {
4032
4180
  console.log(chalk.dim(` ${func.purpose}`));
4033
4181
  }