code-auditor-mcp 5.0.1 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +227 -3
  3. package/README.md +24 -2
  4. package/configs/hhra-compat.json +12 -118
  5. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
  6. package/dist/analyzers/cross-language/DependencyGraphBuilder.js +11 -1
  7. package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
  8. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
  9. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +57 -52
  10. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
  11. package/dist/analyzers/handleIdentification.d.ts.map +1 -1
  12. package/dist/analyzers/handleIdentification.js +4 -1
  13. package/dist/analyzers/handleIdentification.js.map +1 -1
  14. package/dist/analyzers/provenance.d.ts +9 -4
  15. package/dist/analyzers/provenance.d.ts.map +1 -1
  16. package/dist/analyzers/provenance.js +8 -25
  17. package/dist/analyzers/provenance.js.map +1 -1
  18. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  19. package/dist/analyzers/reactAnalyzer.js +0 -5
  20. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  21. package/dist/analyzers/receiverRoot.d.ts +20 -0
  22. package/dist/analyzers/receiverRoot.d.ts.map +1 -1
  23. package/dist/analyzers/receiverRoot.js +42 -0
  24. package/dist/analyzers/receiverRoot.js.map +1 -1
  25. package/dist/analyzers/ruleRegistry.js +4 -4
  26. package/dist/analyzers/ruleRegistry.js.map +1 -1
  27. package/dist/analyzers/ruleTiming.d.ts +1 -1
  28. package/dist/analyzers/ruleTiming.js +2 -2
  29. package/dist/analyzers/ruleTiming.js.map +1 -1
  30. package/dist/analyzers/tsEcosystem.d.ts.map +1 -1
  31. package/dist/analyzers/tsEcosystem.js +10 -0
  32. package/dist/analyzers/tsEcosystem.js.map +1 -1
  33. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
  34. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +22 -26
  35. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
  36. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +32 -15
  37. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  38. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +189 -115
  39. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  40. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +0 -2
  41. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  42. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +0 -5
  43. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  44. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +2 -10
  45. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
  46. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +7 -49
  47. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
  48. package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
  49. package/dist/analyzers/universal/schema/config.js +0 -2
  50. package/dist/analyzers/universal/schema/config.js.map +1 -1
  51. package/dist/analyzers/universal/schema/types.d.ts +0 -2
  52. package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
  53. package/dist/analyzers/vendorSurfaces.d.ts +126 -0
  54. package/dist/analyzers/vendorSurfaces.d.ts.map +1 -0
  55. package/dist/analyzers/vendorSurfaces.js +580 -0
  56. package/dist/analyzers/vendorSurfaces.js.map +1 -0
  57. package/dist/auditRunner.d.ts +2 -0
  58. package/dist/auditRunner.d.ts.map +1 -1
  59. package/dist/auditRunner.js +159 -160
  60. package/dist/auditRunner.js.map +1 -1
  61. package/dist/churn/churnExtractor.d.ts +0 -1
  62. package/dist/churn/churnExtractor.d.ts.map +1 -1
  63. package/dist/churn/churnExtractor.js +2 -4
  64. package/dist/churn/churnExtractor.js.map +1 -1
  65. package/dist/cli.d.ts +1 -1
  66. package/dist/cli.d.ts.map +1 -1
  67. package/dist/cli.js +212 -29
  68. package/dist/cli.js.map +1 -1
  69. package/dist/codeIndex/migrations.d.ts.map +1 -1
  70. package/dist/codeIndex/migrations.js +4 -3
  71. package/dist/codeIndex/migrations.js.map +1 -1
  72. package/dist/codeIndexDB.d.ts +11 -2
  73. package/dist/codeIndexDB.d.ts.map +1 -1
  74. package/dist/codeIndexDB.js +27 -2
  75. package/dist/codeIndexDB.js.map +1 -1
  76. package/dist/config/configLoader.d.ts +60 -7
  77. package/dist/config/configLoader.d.ts.map +1 -1
  78. package/dist/config/configLoader.js +252 -60
  79. package/dist/config/configLoader.js.map +1 -1
  80. package/dist/config/defaults.d.ts +77 -18
  81. package/dist/config/defaults.d.ts.map +1 -1
  82. package/dist/config/defaults.js +105 -43
  83. package/dist/config/defaults.js.map +1 -1
  84. package/dist/constants.d.ts +2 -1
  85. package/dist/constants.d.ts.map +1 -1
  86. package/dist/constants.js +4 -0
  87. package/dist/constants.js.map +1 -1
  88. package/dist/daemon/main.d.ts +0 -1
  89. package/dist/daemon/main.d.ts.map +1 -1
  90. package/dist/daemon/main.js +31 -8
  91. package/dist/daemon/main.js.map +1 -1
  92. package/dist/dataPaths.d.ts +17 -11
  93. package/dist/dataPaths.d.ts.map +1 -1
  94. package/dist/dataPaths.js +51 -16
  95. package/dist/dataPaths.js.map +1 -1
  96. package/dist/graph/resolutionEnvironment.d.ts +61 -0
  97. package/dist/graph/resolutionEnvironment.d.ts.map +1 -0
  98. package/dist/graph/resolutionEnvironment.js +57 -0
  99. package/dist/graph/resolutionEnvironment.js.map +1 -0
  100. package/dist/hookLog.d.ts +54 -0
  101. package/dist/hookLog.d.ts.map +1 -0
  102. package/dist/hookLog.js +105 -0
  103. package/dist/hookLog.js.map +1 -0
  104. package/dist/hookLogPath.generated.d.ts +3 -0
  105. package/dist/hookLogPath.generated.d.ts.map +1 -0
  106. package/dist/hookLogPath.generated.js +8 -0
  107. package/dist/hookLogPath.generated.js.map +1 -0
  108. package/dist/index.d.ts +0 -5
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.js +0 -7
  111. package/dist/index.js.map +1 -1
  112. package/dist/installConfig.d.ts +15 -2
  113. package/dist/installConfig.d.ts.map +1 -1
  114. package/dist/installConfig.js +17 -2
  115. package/dist/installConfig.js.map +1 -1
  116. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  117. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +18 -3
  118. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  119. package/dist/mcp-tools/workflowGuide.js +8 -8
  120. package/dist/mcp-tools/workflowGuide.js.map +1 -1
  121. package/dist/mcp-tools-shared.d.ts.map +1 -1
  122. package/dist/mcp-tools-shared.js +5 -2
  123. package/dist/mcp-tools-shared.js.map +1 -1
  124. package/dist/mcp-ui-simple.d.ts +5 -0
  125. package/dist/mcp-ui-simple.d.ts.map +1 -1
  126. package/dist/mcp-ui-simple.js +9 -2
  127. package/dist/mcp-ui-simple.js.map +1 -1
  128. package/dist/mcp.d.ts +0 -1
  129. package/dist/mcp.d.ts.map +1 -1
  130. package/dist/mcp.js +36 -33
  131. package/dist/mcp.js.map +1 -1
  132. package/dist/mcpAuditJobs.d.ts +10 -0
  133. package/dist/mcpAuditJobs.d.ts.map +1 -1
  134. package/dist/mcpAuditJobs.js +10 -1
  135. package/dist/mcpAuditJobs.js.map +1 -1
  136. package/dist/mcpAutoIndex.d.ts.map +1 -1
  137. package/dist/mcpAutoIndex.js +2 -1
  138. package/dist/mcpAutoIndex.js.map +1 -1
  139. package/dist/phase/colorMath.d.ts +18 -5
  140. package/dist/phase/colorMath.d.ts.map +1 -1
  141. package/dist/phase/colorMath.js +88 -5
  142. package/dist/phase/colorMath.js.map +1 -1
  143. package/dist/phase/dataAccessCallsCandidates.js +1 -1
  144. package/dist/phase/dataAccessCallsCandidates.js.map +1 -1
  145. package/dist/phase/goFunctionAnalysis.d.ts +2 -2
  146. package/dist/phase/goFunctionAnalysis.js +2 -2
  147. package/dist/phase/goFunctions.d.ts +2 -2
  148. package/dist/phase/goFunctions.js +2 -2
  149. package/dist/phase/goImports.d.ts +2 -2
  150. package/dist/phase/goImports.js +2 -2
  151. package/dist/phase/goSwitches.d.ts +2 -2
  152. package/dist/phase/goSwitches.js +2 -2
  153. package/dist/phase/loopQueryCandidates.js +1 -1
  154. package/dist/phase/loopQueryCandidates.js.map +1 -1
  155. package/dist/phase/phaseModel.d.ts +37 -8
  156. package/dist/phase/phaseModel.d.ts.map +1 -1
  157. package/dist/phase/phaseModel.js +94 -46
  158. package/dist/phase/phaseModel.js.map +1 -1
  159. package/dist/phase/producers.d.ts.map +1 -1
  160. package/dist/phase/producers.js +27 -17
  161. package/dist/phase/producers.js.map +1 -1
  162. package/dist/phase/reachability.d.ts +44 -1
  163. package/dist/phase/reachability.d.ts.map +1 -1
  164. package/dist/phase/reachability.js +79 -0
  165. package/dist/phase/reachability.js.map +1 -1
  166. package/dist/phase/receiverConsumers.d.ts +8 -1
  167. package/dist/phase/receiverConsumers.d.ts.map +1 -1
  168. package/dist/phase/receiverConsumers.js +59 -21
  169. package/dist/phase/receiverConsumers.js.map +1 -1
  170. package/dist/phase/receiverProvenance.d.ts +23 -0
  171. package/dist/phase/receiverProvenance.d.ts.map +1 -1
  172. package/dist/phase/receiverProvenance.js +40 -0
  173. package/dist/phase/receiverProvenance.js.map +1 -1
  174. package/dist/phase/rules/crossDomain.d.ts.map +1 -1
  175. package/dist/phase/rules/crossDomain.js +16 -6
  176. package/dist/phase/rules/crossDomain.js.map +1 -1
  177. package/dist/phase/rules/dry.d.ts.map +1 -1
  178. package/dist/phase/rules/dry.js +0 -1
  179. package/dist/phase/rules/dry.js.map +1 -1
  180. package/dist/phase/rules/goRules.d.ts +2 -2
  181. package/dist/phase/rules/goRules.js +2 -2
  182. package/dist/phase/rules/secrets.d.ts +22 -8
  183. package/dist/phase/rules/secrets.d.ts.map +1 -1
  184. package/dist/phase/rules/secrets.js +48 -67
  185. package/dist/phase/rules/secrets.js.map +1 -1
  186. package/dist/phase/rules/styles.d.ts +23 -9
  187. package/dist/phase/rules/styles.d.ts.map +1 -1
  188. package/dist/phase/rules/styles.js +47 -15
  189. package/dist/phase/rules/styles.js.map +1 -1
  190. package/dist/phase/rules/unreferencedModule.d.ts.map +1 -1
  191. package/dist/phase/rules/unreferencedModule.js +16 -5
  192. package/dist/phase/rules/unreferencedModule.js.map +1 -1
  193. package/dist/phase/runner.d.ts +16 -7
  194. package/dist/phase/runner.d.ts.map +1 -1
  195. package/dist/phase/runner.js +32 -20
  196. package/dist/phase/runner.js.map +1 -1
  197. package/dist/phase/stylesCss.d.ts.map +1 -1
  198. package/dist/phase/stylesCss.js +4 -0
  199. package/dist/phase/stylesCss.js.map +1 -1
  200. package/dist/phase/stylesMarkup.d.ts.map +1 -1
  201. package/dist/phase/stylesMarkup.js +4 -0
  202. package/dist/phase/stylesMarkup.js.map +1 -1
  203. package/dist/phase/stylesSource.d.ts.map +1 -1
  204. package/dist/phase/stylesSource.js +6 -0
  205. package/dist/phase/stylesSource.js.map +1 -1
  206. package/dist/phase/testProgram.d.ts +57 -0
  207. package/dist/phase/testProgram.d.ts.map +1 -0
  208. package/dist/phase/testProgram.js +244 -0
  209. package/dist/phase/testProgram.js.map +1 -0
  210. package/dist/phase/typeDeclarations.d.ts +2 -2
  211. package/dist/phase/typeDeclarations.js +2 -2
  212. package/dist/phase/types.d.ts +54 -0
  213. package/dist/phase/types.d.ts.map +1 -1
  214. package/dist/pipelineAdapters.d.ts +1 -171
  215. package/dist/pipelineAdapters.d.ts.map +1 -1
  216. package/dist/pipelineAdapters.js +47 -1281
  217. package/dist/pipelineAdapters.js.map +1 -1
  218. package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
  219. package/dist/reporting/csvReportGenerator.js +0 -6
  220. package/dist/reporting/csvReportGenerator.js.map +1 -1
  221. package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
  222. package/dist/reporting/htmlReportGenerator.js +0 -4
  223. package/dist/reporting/htmlReportGenerator.js.map +1 -1
  224. package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
  225. package/dist/reporting/jsonReportGenerator.js +0 -1
  226. package/dist/reporting/jsonReportGenerator.js.map +1 -1
  227. package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
  228. package/dist/reporting/sarifReportGenerator.js +0 -7
  229. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  230. package/dist/sqlite/driver.d.ts +17 -1
  231. package/dist/sqlite/driver.d.ts.map +1 -1
  232. package/dist/sqlite/driver.js +28 -5
  233. package/dist/sqlite/driver.js.map +1 -1
  234. package/dist/styles/cssInJsHarvest.d.ts +36 -0
  235. package/dist/styles/cssInJsHarvest.d.ts.map +1 -0
  236. package/dist/styles/cssInJsHarvest.js +78 -0
  237. package/dist/styles/cssInJsHarvest.js.map +1 -0
  238. package/dist/styles/definedClasses.d.ts +95 -0
  239. package/dist/styles/definedClasses.d.ts.map +1 -0
  240. package/dist/styles/definedClasses.js +129 -0
  241. package/dist/styles/definedClasses.js.map +1 -0
  242. package/dist/styles/markupStyleHarvest.d.ts +33 -0
  243. package/dist/styles/markupStyleHarvest.d.ts.map +1 -0
  244. package/dist/styles/markupStyleHarvest.js +73 -0
  245. package/dist/styles/markupStyleHarvest.js.map +1 -0
  246. package/dist/styles/styleExtractor.d.ts +37 -0
  247. package/dist/styles/styleExtractor.d.ts.map +1 -1
  248. package/dist/styles/styleExtractor.js +89 -33
  249. package/dist/styles/styleExtractor.js.map +1 -1
  250. package/dist/styles/styleIndexer.d.ts.map +1 -1
  251. package/dist/styles/styleIndexer.js +11 -10
  252. package/dist/styles/styleIndexer.js.map +1 -1
  253. package/dist/styles/tailwindExpander.d.ts.map +1 -1
  254. package/dist/styles/tailwindExpander.js +31 -10
  255. package/dist/styles/tailwindExpander.js.map +1 -1
  256. package/dist/telemetry.d.ts +67 -1
  257. package/dist/telemetry.d.ts.map +1 -1
  258. package/dist/telemetry.js +100 -20
  259. package/dist/telemetry.js.map +1 -1
  260. package/dist/types/crossLanguage.d.ts +1 -0
  261. package/dist/types/crossLanguage.d.ts.map +1 -1
  262. package/dist/types.d.ts +36 -56
  263. package/dist/types.d.ts.map +1 -1
  264. package/dist/types.js +4 -4
  265. package/dist/types.js.map +1 -1
  266. package/dist/version.generated.d.ts +1 -1
  267. package/dist/version.generated.js +1 -1
  268. package/examples/.auditrc.example.json +7 -22
  269. package/examples/.auditrc.json +12 -155
  270. package/examples/.code-auditor.json +8 -51
  271. package/examples/nextjs.auditrc.json +7 -34
  272. package/examples/node-api.auditrc.json +6 -46
  273. package/examples/react.auditrc.json +8 -25
  274. package/examples/tolerant-solid.auditrc.json +12 -44
  275. package/package.json +13 -9
  276. package/plugin/.claude-plugin/manifest.json +10 -9
  277. package/plugin/.claude-plugin/plugin.json +1 -1
  278. package/plugin/hooks/hooks.json +4 -4
  279. package/plugin/scripts/hook-audit.sh +14 -2
  280. package/plugin/scripts/hook-common.sh +41 -11
  281. package/plugin/scripts/hook-drift-check.sh +19 -6
  282. package/plugin/scripts/hook-log-path.sh +7 -0
  283. package/plugin/scripts/hook-self-audit.sh +14 -2
  284. package/plugin/scripts/hook-warm.sh +22 -17
  285. package/plugin/skills/code-auditor/SKILL.md +26 -3
  286. package/server.json +10 -4
  287. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts +0 -6
  288. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts.map +0 -1
  289. package/dist/__tests__/fixtures/spec-20/scripts/tool.js +0 -12
  290. package/dist/__tests__/fixtures/spec-20/scripts/tool.js.map +0 -1
  291. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts +0 -7
  292. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts.map +0 -1
  293. package/dist/__tests__/fixtures/spec-20/src/demo-module.js +0 -13
  294. package/dist/__tests__/fixtures/spec-20/src/demo-module.js.map +0 -1
  295. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts +0 -14
  296. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts.map +0 -1
  297. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js +0 -16
  298. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js.map +0 -1
  299. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts +0 -15
  300. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts.map +0 -1
  301. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js +0 -17
  302. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js.map +0 -1
  303. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +0 -14
  304. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +0 -1
  305. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +0 -19
  306. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +0 -1
  307. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +0 -18
  308. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +0 -1
  309. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +0 -19
  310. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +0 -1
  311. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts +0 -19
  312. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts.map +0 -1
  313. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js +0 -19
  314. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js.map +0 -1
  315. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +0 -17
  316. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +0 -1
  317. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js +0 -19
  318. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +0 -1
  319. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +0 -25
  320. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +0 -1
  321. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js +0 -21
  322. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +0 -1
  323. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts +0 -15
  324. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts.map +0 -1
  325. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js +0 -19
  326. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js.map +0 -1
  327. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts +0 -14
  328. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts.map +0 -1
  329. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js +0 -14
  330. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js.map +0 -1
  331. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +0 -15
  332. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +0 -1
  333. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js +0 -16
  334. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +0 -1
  335. package/dist/languages/go/analyzer-darwin-amd64 +0 -0
  336. package/dist/languages/go/analyzer-darwin-arm64 +0 -0
  337. package/dist/languages/go/analyzer-linux-amd64 +0 -0
  338. package/dist/languages/go/analyzer-linux-arm64 +0 -0
  339. package/dist/languages/go/analyzer-src/analyzer.go +0 -643
  340. package/dist/languages/go/analyzer-src/dataaccess.go +0 -388
  341. package/dist/languages/go/analyzer-src/go.mod +0 -3
  342. package/dist/languages/go/analyzer-src/indexer.go +0 -268
  343. package/dist/languages/go/analyzer-src/parser.go +0 -461
  344. package/dist/languages/go/analyzer-src/solid.go +0 -311
  345. package/dist/languages/go/analyzer-src/testconventions.go +0 -25
  346. package/dist/languages/go/analyzer-src/types.go +0 -156
  347. package/dist/languages/go/analyzer-windows-amd64.exe +0 -0
  348. package/dist/languages/go/go.mod +0 -7
  349. package/dist/languages/go/main.go +0 -203
  350. package/dist/native/ast-grep-napi.darwin-arm64.node +0 -0
  351. package/dist/native/ast-grep-napi.darwin-x64.node +0 -0
  352. package/dist/native/ast-grep-napi.linux-arm64-gnu.node +0 -0
  353. package/dist/native/ast-grep-napi.linux-arm64-musl.node +0 -0
  354. package/dist/native/ast-grep-napi.linux-x64-gnu.node +0 -0
  355. package/dist/native/ast-grep-napi.linux-x64-musl.node +0 -0
  356. package/dist/native/ast-grep-napi.win32-x64-msvc.node +0 -0
  357. package/dist/native-bootstrap.d.ts +0 -15
  358. package/dist/native-bootstrap.d.ts.map +0 -1
  359. package/dist/native-bootstrap.js +0 -75
  360. package/dist/native-bootstrap.js.map +0 -1
package/dist/cli.js CHANGED
@@ -2,15 +2,14 @@
2
2
  /**
3
3
  * Code Auditor CLI - Enhanced with AI Tool Integration
4
4
  */
5
- // MUST be first import: sets NAPI_RS_NATIVE_LIBRARY_PATH before @ast-grep/napi loads
6
- import './native-bootstrap.js';
7
5
  import { Command } from 'commander';
8
6
  import chalk from 'chalk';
9
7
  import { createAuditRunner } from './auditRunner.js';
8
+ import { readHookNotice } from './hookLog.js';
10
9
  import { promises as fs } from 'fs';
11
10
  import { createInterface } from 'readline';
12
11
  import { dirname, isAbsolute, join, relative, resolve } from 'path';
13
- import { PACKAGE_VERSION } from './constants.js';
12
+ import { PACKAGE_VERSION, DEFAULT_RULE_REQUEST_BASE_URL } from './constants.js';
14
13
  import inquirer from 'inquirer';
15
14
  import { CodeMapGenerator } from './services/CodeMapGenerator.js';
16
15
  import { initParsers } from './languages/index.js';
@@ -20,13 +19,14 @@ import { getFilesProcessed, getFactsConsumed, isVisitorStatus, isReducerStatus }
20
19
  import { createBaselineFromFindings, saveBaseline, loadBaseline, diffBaselines } from './baseline.js';
21
20
  import { fingerprint, buildFingerprintInput } from './fingerprint.js';
22
21
  import { buildDismissalEntry, upsertDismissal } from './dismissals.js';
23
- import { resolveTelemetryConfig, signatureForFinding, buildTelemetryPayload, formatTelemetryPreview, sendTelemetry, languageHint } from './telemetry.js';
22
+ import { resolveTelemetryConfig, signatureForFinding, buildTelemetryPayload, formatTelemetryPreview, sendTelemetry, languageHint, telemetryStatus, telemetryEnable, telemetryDisable, postJson } from './telemetry.js';
24
23
  import { getInstallId } from './installConfig.js';
25
24
  import { computeGatingDecision } from './enforcement/gate.js';
26
25
  import { BLOCKING_SEVERITIES, SEVERITIES } from './types.js';
27
26
  import { rankFilesByPriority, orderFindingsWithinFile } from './nextFile.js';
28
27
  import { runNextFile } from './nextFileIncremental.js';
29
28
  import { describeSqliteBackend } from './sqlite/driver.js';
29
+ import { isRuleTimingEnabled } from './analyzers/ruleTiming.js';
30
30
  const program = new Command();
31
31
  // Legacy severity names that were renamed off Spec-54's "advisory" back to "high"
32
32
  // (Spec-54's third tier rendered as "advisory", which read as optional — the very
@@ -125,26 +125,22 @@ function printAuditDiagnostics(result) {
125
125
  const unprovenSiteCount = (result.metadata?.diagnostics ?? [])
126
126
  .filter((d) => d.kind === 'cannot-fire' && d.analyzerName === 'schema')
127
127
  .length;
128
+ const heldOutByProfile = result.summary?.heldOut ?? 0;
128
129
  if (coverage && coverage.length > 0) {
129
130
  const covFired = coverage.filter(c => c.state === 'fired').length;
130
131
  const covClean = coverage.filter(c => c.state === 'clean').length;
131
132
  const covIncomplete = coverage.filter(c => c.state === 'incomplete').length;
132
133
  const covNotApplicable = coverage.filter(c => c.state === 'notApplicable').length;
133
134
  const covCannotFire = coverage.filter(c => c.state === 'cannot-fire').length;
134
- console.log(chalk.gray(`── Coverage panel ── ${covFired} fired · ${covClean} clean · ` +
135
+ // Spec 69 §10 R4 follow-up — "clean" is not a printable verdict while any
136
+ // site is unproven (cannot-fire) or held out by a path profile: a rule that
137
+ // reports "clean" may have under-reported on access it could not observe.
138
+ const cleanSuppressed = unprovenSiteCount > 0 || heldOutByProfile > 0;
139
+ const cleanPart = cleanSuppressed ? '' : `${covClean} clean · `;
140
+ console.log(chalk.gray(`── Coverage panel ── ${covFired} fired · ${cleanPart}` +
135
141
  `${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
- }
142
+ `${covCannotFire} cannot-fire · ${unprovenSiteCount} unproven sites · ` +
143
+ `${heldOutByProfile} held out by profile`));
148
144
  }
149
145
  // ── Ignored config keys (Spec 61 R1.4) ─────────────────────────
150
146
  const ignoredConfigKeys = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'config-key-rejected');
@@ -254,7 +250,14 @@ function printFindingSummary(result, violations, baseline, options) {
254
250
  const dismissedSuffix = dismissedCount > 0 ? `, ${dismissedCount} dismissed` : '';
255
251
  const gateExcludedCount = violations.filter((v) => v.gateExcluded).length;
256
252
  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)` : ''}`;
253
+ // Spec 69 §10 R4 follow-up — the headline states findings, unproven sites,
254
+ // and held-out readings together. Held-out readings are named "held out by
255
+ // profile" (they still count in the total but do not block the gate).
256
+ const heldOutByProfile = result.summary?.heldOut ?? gateExcludedCount;
257
+ const unprovenSites = result.summary?.unprovenSites ?? 0;
258
+ const findingsSuffix = `${dismissedSuffix}` +
259
+ `${unprovenSites > 0 ? ` · ${unprovenSites.toLocaleString()} unproven sites` : ''}` +
260
+ `${heldOutByProfile > 0 ? ` · ${heldOutByProfile.toLocaleString()} held out by profile` : ''}`;
258
261
  const summaryLine = (label, total, severity) => gateExcludedCount > 0
259
262
  ? `${label}: ${total} (${gatingCount(severity)} gating)`
260
263
  : `${label}: ${total}`;
@@ -586,6 +589,13 @@ program
586
589
  outputDirectory: options.output,
587
590
  presets: presetIds
588
591
  }).run();
592
+ // Hook-liveness notice — a top-level notice, not a per-file finding. A live
593
+ // "hook not running" condition means the PostToolUse hook is not enqueueing
594
+ // changes, so cached findings may be stale.
595
+ const hookNotice = readHookNotice();
596
+ if (hookNotice) {
597
+ console.log(chalk.yellow(`\n⚠️ ${hookNotice.message}`));
598
+ }
589
599
  const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
590
600
  const baseline = result.metadata?.baseline;
591
601
  printAuditDiagnostics(result);
@@ -718,7 +728,7 @@ program
718
728
  // Emitted to stderr so it never corrupts --json stdout. Slowest rule first.
719
729
  // The gate metric is CPU time (auditCpuMs), not wall clock: the budget is
720
730
  // about a slow *rule*, and CPU time isolates that from a slow machine.
721
- if (process.env.CODE_AUDIT_RULE_TIMING === '1') {
731
+ if (isRuleTimingEnabled()) {
722
732
  const ruleTiming = result.metadata?.ruleTiming;
723
733
  const gateMs = result.metadata?.auditCpuMs;
724
734
  const gateWallMs = result.metadata?.auditDuration;
@@ -875,7 +885,18 @@ async function emitChangedOutput(result, violations, blocking, dismissedCount, o
875
885
  }
876
886
  }
877
887
  else {
878
- console.log(chalk.green('\n✓ No readings.'));
888
+ // A bare `changed` scope that found no baseline to diff against must not
889
+ // report a false clean. The diagnostic names both causes (never indexed,
890
+ // or the index was discarded) so the agent sees "cannot determine what
891
+ // changed", never "nothing changed".
892
+ const noBaseline = (result.metadata?.diagnostics ?? []).find((d) => d.kind === 'no-baseline');
893
+ if (noBaseline) {
894
+ console.log(chalk.yellow('\n⚠ Cannot determine what changed.'));
895
+ console.log(chalk.yellow(noBaseline.message));
896
+ }
897
+ else {
898
+ console.log(chalk.green('\n✓ No readings.'));
899
+ }
879
900
  }
880
901
  }
881
902
  }
@@ -1292,7 +1313,7 @@ program
1292
1313
  .description('Dismiss one finding by fingerprint — a written reason is required')
1293
1314
  .requiredOption('--reason <reason>', 'Why this finding is dismissed (required — a dismissal without a reason is a config error)')
1294
1315
  .option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
1295
- .option('--telemetry-endpoint <url>', 'Feedback service endpoint override (defaults to the opt-in endpoint recorded by the telemetry tool)')
1316
+ .option('--telemetry-endpoint <url>', 'Feedback service endpoint override (defaults to the opt-in endpoint recorded by the `code-audit telemetry` command)')
1296
1317
  .option('--json', OPTION_OUTPUT_AS_JSON)
1297
1318
  .action(async (fingerprintArg, options) => {
1298
1319
  try {
@@ -1320,9 +1341,9 @@ program
1320
1341
  const entry = buildDismissalEntry(match, reason, PACKAGE_VERSION);
1321
1342
  const dismissals = upsertDismissal(projectRoot, entry);
1322
1343
  // Spec 57 — opt-in telemetry. Nothing is sent unless the user opted in via
1323
- // the `telemetry` MCP tool (default OFF) AND an endpoint is configured. The
1324
- // payload is printed for review before sending; any send failure is silent
1325
- // and never affects the dismissal or the exit code.
1344
+ // the `code-audit telemetry enable` command (default OFF) AND an endpoint is
1345
+ // configured. The payload is printed for review before sending; any send
1346
+ // failure is silent and never affects the dismissal or the exit code.
1326
1347
  const telemetry = resolveTelemetryConfig({ endpoint: options.telemetryEndpoint });
1327
1348
  if (telemetry.enabled && telemetry.endpoint) {
1328
1349
  const signature = signatureForFinding(match.file, match.line ?? match.start?.line ?? 1, match.column ?? match.start?.column ?? 1) ?? 'unknown';
@@ -1370,6 +1391,153 @@ program
1370
1391
  process.exit(1);
1371
1392
  }
1372
1393
  });
1394
+ // Telemetry opt-in (Spec 57) — status/enable/disable call the same three
1395
+ // functions as the telemetry MCP tool (telemetry.ts), so the CLI and the MCP
1396
+ // tool can never disagree about what is enabled or what a submission contains.
1397
+ const telemetry = program
1398
+ .command('telemetry')
1399
+ .description('Manage anonymous dismissal feedback opt-in (default OFF). Actions: status, enable, disable.');
1400
+ telemetry
1401
+ .command('status')
1402
+ .description('Show whether anonymous dismissal feedback is enabled, its endpoint, and the anonymous install ID.')
1403
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1404
+ .action(async (options) => {
1405
+ const s = telemetryStatus();
1406
+ if (options.json) {
1407
+ process.stdout.write(JSON.stringify(s, null, 2) + '\n');
1408
+ }
1409
+ else {
1410
+ console.log(chalk.bold(s.enabled ? 'Telemetry: enabled' : 'Telemetry: disabled'));
1411
+ if (s.enabled) {
1412
+ console.log(chalk.gray(` endpoint: ${s.endpoint}`));
1413
+ console.log(chalk.gray(` install ID: ${s.installId}`));
1414
+ }
1415
+ else {
1416
+ console.log(chalk.gray(' endpoint: (none — enable with `code-audit telemetry enable`)'));
1417
+ }
1418
+ console.log(chalk.gray(`\n${s.message}`));
1419
+ }
1420
+ });
1421
+ telemetry
1422
+ .command('enable')
1423
+ .description('Opt in to anonymous dismissal feedback. Defaults to the built-in service endpoint. Generates the anonymous install ID on first use.')
1424
+ .option('--endpoint <url>', 'Optional ingest URL. Defaults to the built-in service endpoint.')
1425
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1426
+ .action(async (options) => {
1427
+ try {
1428
+ const s = telemetryEnable(options.endpoint);
1429
+ if (options.json) {
1430
+ process.stdout.write(JSON.stringify(s, null, 2) + '\n');
1431
+ }
1432
+ else {
1433
+ console.log(chalk.green('✓ Telemetry enabled.'));
1434
+ console.log(chalk.gray(` endpoint: ${s.endpoint}`));
1435
+ console.log(chalk.gray(` install ID: ${s.installId}`));
1436
+ console.log(chalk.gray(`\n${s.message}`));
1437
+ }
1438
+ }
1439
+ catch (error) {
1440
+ console.error(chalk.red('Error:'), error);
1441
+ process.exit(1);
1442
+ }
1443
+ });
1444
+ telemetry
1445
+ .command('disable')
1446
+ .description('Turn off anonymous dismissal feedback. The endpoint is remembered; nothing further is sent.')
1447
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1448
+ .action(async (options) => {
1449
+ const s = telemetryDisable();
1450
+ if (options.json) {
1451
+ process.stdout.write(JSON.stringify(s, null, 2) + '\n');
1452
+ }
1453
+ else {
1454
+ console.log(chalk.green('✓ Telemetry disabled.'));
1455
+ console.log(chalk.gray(s.message));
1456
+ }
1457
+ });
1458
+ // Accumulate a repeatable option (commander's default keeps only the last value).
1459
+ function collectOption(value, previous) {
1460
+ return [...(previous ?? []), value];
1461
+ }
1462
+ // Request a new rule (rule-request intake). POSTs to the docs site's HTTP tool
1463
+ // API, which validates and forwards to the shared feedback service. The CLI does
1464
+ // no own validation: it reads the example files, sends them with the client
1465
+ // surface, and prints whatever the server decides (receipt id, or field errors).
1466
+ program
1467
+ .command('request-rule')
1468
+ .description('Submit a request for a new Code Auditor rule (an unflagged defect class)')
1469
+ .requiredOption('--title <title>', 'Short rule title (10-120 chars)')
1470
+ .requiredOption('--symptom <text>', 'Symptom this rule should catch (40-2000 chars)')
1471
+ .requiredOption('--signal <text>', 'Signal/pattern the rule should look for (40-2000 chars)')
1472
+ .option('--language <lang>', 'Language the rule applies to (repeatable)', collectOption, [])
1473
+ .option('--fires <path>', 'Example file that SHOULD be flagged (repeatable)', collectOption, [])
1474
+ .option('--clean <path>', 'Example file that should NOT be flagged (repeatable)', collectOption, [])
1475
+ .option('--endpoint <url>', `Docs API base URL (default ${DEFAULT_RULE_REQUEST_BASE_URL})`)
1476
+ .option('--json', OPTION_OUTPUT_AS_JSON)
1477
+ .action(async (options) => {
1478
+ try {
1479
+ const readFiles = async (paths) => {
1480
+ const out = [];
1481
+ for (const p of paths) {
1482
+ out.push({ path: p, content: await fs.readFile(resolve(p), 'utf-8') });
1483
+ }
1484
+ return out;
1485
+ };
1486
+ const [firingExample, cleanExample] = await Promise.all([
1487
+ readFiles(options.fires ?? []),
1488
+ readFiles(options.clean ?? []),
1489
+ ]);
1490
+ const body = {
1491
+ title: options.title,
1492
+ symptom: options.symptom,
1493
+ signal: options.signal,
1494
+ languages: options.language ?? [],
1495
+ firingExample,
1496
+ cleanExample,
1497
+ toolVersion: PACKAGE_VERSION,
1498
+ };
1499
+ const base = (options.endpoint ?? DEFAULT_RULE_REQUEST_BASE_URL).replace(/\/+$/, '');
1500
+ const url = `${base}/api/tools/request_rule`;
1501
+ const result = await postJson(url, body, {
1502
+ timeoutMs: 5000,
1503
+ headers: { 'x-code-auditor-client': 'cli' },
1504
+ });
1505
+ if (result.error) {
1506
+ console.error(chalk.red('Error:'), result.error);
1507
+ process.exit(1);
1508
+ }
1509
+ if (!result.ok) {
1510
+ const res = result.body;
1511
+ if (res?.fields) {
1512
+ console.error(chalk.red(`Error: request rejected (${res.error ?? 'validation failed'})`));
1513
+ for (const [field, msgs] of Object.entries(res.fields)) {
1514
+ console.error(chalk.gray(` ${field}: ${msgs.join('; ')}`));
1515
+ }
1516
+ }
1517
+ else if (res?.retryAfterSeconds !== undefined) {
1518
+ console.error(chalk.red(`Error: rate limited — retry in ${res.retryAfterSeconds}s`));
1519
+ }
1520
+ else {
1521
+ console.error(chalk.red(`Error: ${res?.message ?? res?.error ?? `HTTP ${result.status}`}`));
1522
+ }
1523
+ process.exit(1);
1524
+ }
1525
+ const out = result.body;
1526
+ if (options.json) {
1527
+ process.stdout.write(JSON.stringify(out, null, 2) + '\n');
1528
+ }
1529
+ else {
1530
+ console.log(chalk.green(`✓ Rule request submitted${out.duplicateSubmission ? ' (duplicate — already received)' : ''}`));
1531
+ console.log(chalk.gray(` receipt id: ${out.id}`));
1532
+ const matches = out.existingRuleMatches ?? [];
1533
+ console.log(chalk.gray(` existingRuleMatches: ${matches.length ? matches.join(', ') : '(none)'}`));
1534
+ }
1535
+ }
1536
+ catch (error) {
1537
+ console.error(chalk.red('Error:'), error);
1538
+ process.exit(1);
1539
+ }
1540
+ });
1373
1541
  // Generate configuration command
1374
1542
  program
1375
1543
  .command('generate-config')
@@ -2284,8 +2452,18 @@ program
2284
2452
  if (existing.cnt === 0 && options.path) {
2285
2453
  const { extractChurn } = await import('./churn/churnExtractor.js');
2286
2454
  const { computeHotspots } = await import('./hotspots/hotspotScorer.js');
2455
+ const { findConfigFileUp, loadConfig } = await import('./config/configLoader.js');
2456
+ const { DEFAULT_CHURN_CONFIG } = await import('./config/defaults.js');
2287
2457
  try {
2288
- extractChurn(rawDb, options.path, { churnWindowMonths: 12 });
2458
+ // Read the `churn` config key (Spec 13 R1) — the literal
2459
+ // `{ churnWindowMonths: 12 }` was the orphan; the window now resolves
2460
+ // from `.codeauditor.json` with DEFAULT_CHURN_CONFIG as the default.
2461
+ const configPath = await findConfigFileUp(options.path);
2462
+ const { config } = await loadConfig({
2463
+ configPath: configPath ?? undefined,
2464
+ projectRoot: options.path,
2465
+ });
2466
+ extractChurn(rawDb, options.path, config.churn ?? DEFAULT_CHURN_CONFIG);
2289
2467
  computeHotspots(rawDb);
2290
2468
  }
2291
2469
  catch (err) {
@@ -2572,8 +2750,6 @@ ledgerCmd
2572
2750
  // `audit --detach` forks workers/auditJobRunner; `status`/`result`/`jobs` read
2573
2751
  // the persisted ledger. One job model (the ledger run), one write path — these
2574
2752
  // are thin wrappers over ledger.ts + mcpAuditJobs.ts, not a second store.
2575
- /** Lease TTL used by read-path reclaim, mirroring mcpAuditJobs.jobLeaseTtlMs. */
2576
- const CLI_JOB_LEASE_TTL_MS = Number(process.env.CODE_AUDITOR_JOB_LEASE_TTL_MS) || 30_000;
2577
2753
  function spec41StatusBadge(status) {
2578
2754
  switch (status) {
2579
2755
  case 'completed':
@@ -2678,10 +2854,11 @@ program
2678
2854
  .action(async (jobId, options) => {
2679
2855
  try {
2680
2856
  const { getLedgerRun, reclaimStaleRunning, computeStaleness } = await import('./ledger.js');
2857
+ const { jobLeaseTtlMs } = await import('./mcpAuditJobs.js');
2681
2858
  const projectRoot = resolve(options.path);
2682
2859
  const db = CodeIndexDB.getInstance(undefined, projectRoot);
2683
2860
  await db.initialize();
2684
- reclaimStaleRunning(db.rawDb, projectRoot, CLI_JOB_LEASE_TTL_MS);
2861
+ reclaimStaleRunning(db.rawDb, projectRoot, jobLeaseTtlMs());
2685
2862
  const run = getLedgerRun(db.rawDb, jobId);
2686
2863
  if (!run) {
2687
2864
  console.error(chalk.red(`Job not found: ${jobId}`));
@@ -2839,10 +3016,11 @@ program
2839
3016
  .action(async (options) => {
2840
3017
  try {
2841
3018
  const { listLedgerRuns, reclaimStaleRunning, pruneLedgerRuns, computeStaleness } = await import('./ledger.js');
3019
+ const { jobLeaseTtlMs } = await import('./mcpAuditJobs.js');
2842
3020
  const projectRoot = resolve(options.path);
2843
3021
  const db = CodeIndexDB.getInstance(undefined, projectRoot);
2844
3022
  await db.initialize();
2845
- reclaimStaleRunning(db.rawDb, projectRoot, CLI_JOB_LEASE_TTL_MS);
3023
+ reclaimStaleRunning(db.rawDb, projectRoot, jobLeaseTtlMs());
2846
3024
  if (options.prune) {
2847
3025
  const keep = parseInt(options.keep, 10) || 10;
2848
3026
  const n = pruneLedgerRuns(db.rawDb, projectRoot, keep);
@@ -3055,6 +3233,11 @@ program
3055
3233
  hooks: options.hooks,
3056
3234
  list: options.list,
3057
3235
  });
3236
+ // Nudge an undecided user toward the one-time telemetry choice. Only after
3237
+ // a real install (not `--list`), and never once they've already decided.
3238
+ if (!options.list && !telemetryStatus().decided) {
3239
+ console.log('Anonymous dismissal feedback is off. Run `code-audit telemetry enable` to opt in, or `code-audit telemetry disable` to decline.');
3240
+ }
3058
3241
  }
3059
3242
  catch (error) {
3060
3243
  console.error(chalk.red('Error:'), error);