pi-lens 3.8.73 → 4.0.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 (346) hide show
  1. package/CHANGELOG.md +2614 -12
  2. package/README.md +50 -21
  3. package/dist/clients/actionable-warnings.js +73 -18
  4. package/dist/clients/agent-behavior-client.js +21 -4
  5. package/dist/clients/ast-grep-client.js +68 -16
  6. package/dist/clients/ast-grep-types.js +0 -1
  7. package/dist/clients/ast-grep-yaml-synth.js +37 -10
  8. package/dist/clients/atomic-write-staging.js +63 -0
  9. package/dist/clients/atomic-write.js +116 -16
  10. package/dist/clients/bash-file-access.js +193 -23
  11. package/dist/clients/biome-client.js +29 -25
  12. package/dist/clients/bootstrap.js +11 -2
  13. package/dist/clients/bounded-cache.js +34 -0
  14. package/dist/clients/bounded-pid-file-lock.js +192 -0
  15. package/dist/clients/bus-events-logger.js +5 -1
  16. package/dist/clients/bus-publish.js +10 -3
  17. package/dist/clients/cache/rule-cache.js +89 -6
  18. package/dist/clients/cache-manager.js +97 -9
  19. package/dist/clients/cache-observability.js +498 -0
  20. package/dist/clients/call-graph.js +460 -108
  21. package/dist/clients/cascade-logger.js +5 -1
  22. package/dist/clients/child-unref.js +85 -0
  23. package/dist/clients/codebase-model.js +52 -23
  24. package/dist/clients/collateral-test-role.js +40 -0
  25. package/dist/clients/complexity-client.js +6 -8
  26. package/dist/clients/console-guard-install.js +14 -0
  27. package/dist/clients/cooperative-budget.js +42 -0
  28. package/dist/clients/dead-code-client.js +2 -1
  29. package/dist/clients/debug-handles.js +206 -0
  30. package/dist/clients/debug-heap.js +167 -0
  31. package/dist/clients/degradation-ledger.js +153 -0
  32. package/dist/clients/dependency-checker.js +466 -63
  33. package/dist/clients/diagnostic-dispositions.js +83 -13
  34. package/dist/clients/diagnostics-publish.js +10 -3
  35. package/dist/clients/dispatch/auxiliary-lsp.js +34 -0
  36. package/dist/clients/dispatch/dispatcher.js +71 -13
  37. package/dist/clients/dispatch/fact-store.js +11 -0
  38. package/dist/clients/dispatch/facts/function-facts.js +37 -7
  39. package/dist/clients/dispatch/facts/import-facts.js +108 -119
  40. package/dist/clients/dispatch/facts/tree-sitter-facts.js +12 -12
  41. package/dist/clients/dispatch/facts/try-catch-facts.js +13 -0
  42. package/dist/clients/dispatch/indent-detect.js +35 -0
  43. package/dist/clients/dispatch/inline-suppressions.js +17 -8
  44. package/dist/clients/dispatch/integration.js +1094 -611
  45. package/dist/clients/dispatch/lazy.js +14 -0
  46. package/dist/clients/dispatch/plan.js +11 -1
  47. package/dist/clients/dispatch/rule-id-normalize.js +50 -0
  48. package/dist/clients/dispatch/rule-policy.js +135 -0
  49. package/dist/clients/dispatch/rules/async-noise.js +1 -0
  50. package/dist/clients/dispatch/rules/unsafe-boundary.js +7 -0
  51. package/dist/clients/dispatch/runners/ast-grep-napi.js +47 -6
  52. package/dist/clients/dispatch/runners/biome-check.js +5 -9
  53. package/dist/clients/dispatch/runners/detekt.js +3 -2
  54. package/dist/clients/dispatch/runners/hadolint.js +2 -1
  55. package/dist/clients/dispatch/runners/helm-lint.js +154 -0
  56. package/dist/clients/dispatch/runners/htmlhint.js +3 -2
  57. package/dist/clients/dispatch/runners/index.js +5 -1
  58. package/dist/clients/dispatch/runners/lsp.js +14 -6
  59. package/dist/clients/dispatch/runners/markdownlint.js +4 -9
  60. package/dist/clients/dispatch/runners/psscriptanalyzer.js +10 -1
  61. package/dist/clients/dispatch/runners/pyright.js +9 -5
  62. package/dist/clients/dispatch/runners/ruff.js +4 -8
  63. package/dist/clients/dispatch/runners/shellcheck.js +2 -3
  64. package/dist/clients/dispatch/runners/shfmt.js +2 -3
  65. package/dist/clients/dispatch/runners/spotbugs.js +2 -1
  66. package/dist/clients/dispatch/runners/taplo.js +2 -1
  67. package/dist/clients/dispatch/runners/terragrunt.js +163 -0
  68. package/dist/clients/dispatch/runners/tflint.js +37 -5
  69. package/dist/clients/dispatch/runners/tree-sitter.js +155 -166
  70. package/dist/clients/dispatch/runners/trivy-config.js +13 -7
  71. package/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  72. package/dist/clients/dispatch/runners/utils/runner-helpers.js +284 -34
  73. package/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  74. package/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  75. package/dist/clients/disposition-publish.js +10 -3
  76. package/dist/clients/durable-store.js +97 -0
  77. package/dist/clients/event-loop-monitor.js +94 -3
  78. package/dist/clients/extension-log.js +139 -0
  79. package/dist/clients/extension-mode.js +73 -0
  80. package/dist/clients/feature-hints.js +53 -7
  81. package/dist/clients/file-kinds.js +143 -31
  82. package/dist/clients/file-role.js +18 -3
  83. package/dist/clients/file-utils.js +56 -34
  84. package/dist/clients/fix-worklog.js +14 -2
  85. package/dist/clients/format-events-publish.js +16 -3
  86. package/dist/clients/format-service.js +24 -8
  87. package/dist/clients/formatters-lazy.js +8 -0
  88. package/dist/clients/formatters.js +358 -61
  89. package/dist/clients/generated-artifacts.js +169 -15
  90. package/dist/clients/git-guard.js +681 -11
  91. package/dist/clients/git-tracked-ignore.js +4 -0
  92. package/dist/clients/go-client.js +2 -1
  93. package/dist/clients/govulncheck-client.js +8 -0
  94. package/dist/clients/grammar-source.js +12 -4
  95. package/dist/clients/gzip-stage-write.js +102 -0
  96. package/dist/clients/host-ports.js +31 -0
  97. package/dist/clients/installer/index.js +733 -213
  98. package/dist/clients/instance-reaper.js +142 -172
  99. package/dist/clients/jscpd-client.js +31 -54
  100. package/dist/clients/knip-client.js +78 -36
  101. package/dist/clients/language-policy.js +14 -2
  102. package/dist/clients/language-profile.js +72 -77
  103. package/dist/clients/latency-logger.js +28 -16
  104. package/dist/clients/lens-config.js +140 -166
  105. package/dist/clients/lens-engine.js +124 -21
  106. package/dist/clients/lens-events.js +27 -4
  107. package/dist/clients/lens-flag-registry.js +293 -0
  108. package/dist/clients/lens-map.js +96 -26
  109. package/dist/clients/live-bus-emitter.js +30 -0
  110. package/dist/clients/log-cleanup.js +8 -18
  111. package/dist/clients/lsp/cascade-tier.js +23 -3
  112. package/dist/clients/lsp/client.js +566 -57
  113. package/dist/clients/lsp/config.js +74 -10
  114. package/dist/clients/lsp/diagnostic-binding.js +86 -0
  115. package/dist/clients/lsp/edits.js +1012 -121
  116. package/dist/clients/lsp/index.js +1840 -803
  117. package/dist/clients/lsp/language.js +4 -1
  118. package/dist/clients/lsp/launch.js +40 -32
  119. package/dist/clients/lsp/path-utils.js +1 -1
  120. package/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  121. package/dist/clients/lsp/server.js +279 -60
  122. package/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  123. package/dist/clients/lsp-document-symbols.js +112 -0
  124. package/dist/clients/lsp-lazy.js +8 -0
  125. package/dist/clients/lsp-mutation.js +248 -0
  126. package/dist/clients/mcp/analyze.js +128 -36
  127. package/dist/clients/mcp/ipc.js +157 -24
  128. package/dist/clients/mcp/review.js +36 -3
  129. package/dist/clients/mcp/session.js +216 -8
  130. package/dist/clients/memory-sampler.js +115 -0
  131. package/dist/clients/metrics-client.js +2 -1
  132. package/dist/clients/metrics-history.js +2 -1
  133. package/dist/clients/module-report-lsp.js +5 -0
  134. package/dist/clients/module-report.js +267 -82
  135. package/dist/clients/ndjson-logger.js +334 -103
  136. package/dist/clients/opengrep-client.js +1 -1
  137. package/dist/clients/partial-edit-apply.js +120 -5
  138. package/dist/clients/path-keyed-map.js +76 -0
  139. package/dist/clients/path-utils.js +141 -5
  140. package/dist/clients/performance-report.js +327 -0
  141. package/dist/clients/pipeline.js +54 -13
  142. package/dist/clients/print-mode.js +21 -0
  143. package/dist/clients/project-changes.js +117 -4
  144. package/dist/clients/project-diagnostics/cache.js +3 -2
  145. package/dist/clients/project-diagnostics/extractors.js +26 -115
  146. package/dist/clients/project-diagnostics/fresh-fetch.js +149 -19
  147. package/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +117 -0
  148. package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +37 -5
  149. package/dist/clients/project-diagnostics/scanner.js +268 -128
  150. package/dist/clients/project-lens-config.js +138 -34
  151. package/dist/clients/project-report.js +64 -25
  152. package/dist/clients/project-scale.js +8 -10
  153. package/dist/clients/project-snapshot-persist-worker.js +6 -0
  154. package/dist/clients/project-snapshot.js +767 -8
  155. package/dist/clients/project-trust.js +181 -0
  156. package/dist/clients/quiet-window.js +27 -5
  157. package/dist/clients/read-bridge.js +147 -0
  158. package/dist/clients/read-expansion.js +22 -12
  159. package/dist/clients/read-guard-logger.js +159 -1
  160. package/dist/clients/read-guard-tool-lines.js +150 -29
  161. package/dist/clients/read-guard.js +262 -3
  162. package/dist/clients/redact/secrets.js +449 -0
  163. package/dist/clients/resource-sampler.js +144 -39
  164. package/dist/clients/reverse-deps.js +48 -0
  165. package/dist/clients/review-graph/builder.js +2570 -326
  166. package/dist/clients/review-graph/git-identity.js +2 -1
  167. package/dist/clients/review-graph/import-resolvers.js +21 -10
  168. package/dist/clients/review-graph/persist-worker.js +10 -0
  169. package/dist/clients/review-graph/query.js +5 -0
  170. package/dist/clients/review-graph/shared-extraction-ir.js +90 -0
  171. package/dist/clients/review-graph/symbol-id.js +46 -0
  172. package/dist/clients/review-graph/tsconfig-paths.js +57 -6
  173. package/dist/clients/review-graph/workspace-modules.js +51 -3
  174. package/dist/clients/review-graph-logger.js +53 -0
  175. package/dist/clients/ruff-client.js +27 -44
  176. package/dist/clients/runtime-agent-end.js +111 -75
  177. package/dist/clients/runtime-context.js +36 -12
  178. package/dist/clients/runtime-coordinator.js +76 -8
  179. package/dist/clients/runtime-session.js +799 -137
  180. package/dist/clients/runtime-tool-call.js +117 -62
  181. package/dist/clients/runtime-tool-result.js +218 -23
  182. package/dist/clients/runtime-turn.js +284 -40
  183. package/dist/clients/rust-client.js +2 -1
  184. package/dist/clients/safe-spawn.js +873 -71
  185. package/dist/clients/security-scan-client.js +2 -1
  186. package/dist/clients/session-start-observability.js +15 -0
  187. package/dist/clients/session-state-store.js +5 -3
  188. package/dist/clients/sessionstart-logger.js +18 -0
  189. package/dist/clients/sg-runner.js +284 -159
  190. package/dist/clients/slow-fs.js +2 -1
  191. package/dist/clients/smells-rollup.js +224 -0
  192. package/dist/clients/source-filter.js +231 -43
  193. package/dist/clients/source-walker.js +66 -10
  194. package/dist/clients/startup-scan.js +15 -16
  195. package/dist/clients/startup-timing.js +9 -0
  196. package/dist/clients/test-runner-client.js +38 -8
  197. package/dist/clients/todo-scanner.js +30 -3
  198. package/dist/clients/tool-event.js +35 -0
  199. package/dist/clients/tool-policy.js +388 -39
  200. package/dist/clients/tool-render.js +155 -0
  201. package/dist/clients/tree-sitter-cache.js +153 -140
  202. package/dist/clients/tree-sitter-client.js +1722 -80
  203. package/dist/clients/tree-sitter-logger.js +64 -1
  204. package/dist/clients/tree-sitter-query-loader.js +117 -12
  205. package/dist/clients/tree-sitter-shared.js +48 -19
  206. package/dist/clients/tree-sitter-symbol-extractor.js +115 -11
  207. package/dist/clients/user-notify.js +51 -0
  208. package/dist/clients/vanished-instance-marker.js +57 -0
  209. package/dist/clients/warm-attach.js +33 -13
  210. package/dist/clients/widget-state.js +342 -49
  211. package/dist/clients/word-index-logger.js +44 -0
  212. package/dist/clients/word-index.js +612 -68
  213. package/dist/clients/workspace-topology.js +100 -7
  214. package/dist/index.js +65923 -47161
  215. package/dist/mcp/analyze-cli.js +185 -29
  216. package/dist/mcp/cli.js +110 -0
  217. package/dist/mcp/server.js +272 -50
  218. package/dist/tools/ast-grep-search.js +93 -27
  219. package/dist/tools/lens-diagnostics.js +247 -41
  220. package/dist/tools/lsp-diagnostics.js +153 -40
  221. package/dist/tools/lsp-navigation.js +51 -31
  222. package/dist/tools/module-report.js +9 -0
  223. package/dist/tools/symbol-search.js +21 -2
  224. package/docs/agent-guide.md +247 -0
  225. package/docs/agent-tools.md +21 -10
  226. package/docs/analysisall.md +192 -0
  227. package/docs/api-ports-inventory.md +165 -0
  228. package/docs/ast-grep_rules_catalog.md +5 -2
  229. package/docs/astplayground.md +3 -4
  230. package/docs/audit1.md +5 -5
  231. package/docs/durable-store-audit-1202.md +32 -0
  232. package/docs/environment-variables.md +197 -11
  233. package/docs/fable.md +27 -0
  234. package/docs/features.md +35 -5
  235. package/docs/globalconfig.md +101 -5
  236. package/docs/language-coverage.md +6 -5
  237. package/docs/lsp-capability-matrix.md +2 -2
  238. package/docs/mcp.md +54 -20
  239. package/docs/servercapabilities.md +7 -4
  240. package/docs/settings.md +192 -0
  241. package/docs/tools.md +18 -1
  242. package/docs/tree-sitter_rules_catalog.md +11 -31
  243. package/docs/usage.md +31 -0
  244. package/package.json +139 -138
  245. package/rules/ast-grep-rules/rule-tests/hardcoded-url-js-test.yml +1 -0
  246. package/rules/ast-grep-rules/rule-tests/hardcoded-url-test.yml +2 -0
  247. package/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  248. package/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  249. package/rules/ast-grep-rules/rule-tests/no-case-declarations-js-test.yml +10 -0
  250. package/rules/ast-grep-rules/rule-tests/no-case-declarations-test.yml +10 -0
  251. package/rules/ast-grep-rules/rule-tests/no-javascript-url-js-test.yml +10 -0
  252. package/rules/ast-grep-rules/rule-tests/no-javascript-url-test.yml +10 -0
  253. package/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  254. package/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  255. package/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  256. package/rules/ast-grep-rules/rule-tests/strict-equality-js-test.yml +3 -0
  257. package/rules/ast-grep-rules/rule-tests/strict-equality-test.yml +3 -0
  258. package/rules/ast-grep-rules/rule-tests/strict-inequality-js-test.yml +3 -0
  259. package/rules/ast-grep-rules/rule-tests/strict-inequality-test.yml +4 -0
  260. package/rules/ast-grep-rules/rules/hardcoded-url-js.yml +22 -4
  261. package/rules/ast-grep-rules/rules/hardcoded-url.yml +22 -4
  262. package/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  263. package/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  264. package/rules/ast-grep-rules/rules/no-case-declarations-js.yml +4 -3
  265. package/rules/ast-grep-rules/rules/no-case-declarations.yml +4 -3
  266. package/rules/ast-grep-rules/rules/no-console-except-error-js.yml +10 -0
  267. package/rules/ast-grep-rules/rules/no-console-except-error.yml +10 -0
  268. package/rules/ast-grep-rules/rules/no-javascript-url-js.yml +42 -6
  269. package/rules/ast-grep-rules/rules/no-javascript-url.yml +42 -6
  270. package/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  271. package/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  272. package/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  273. package/rules/ast-grep-rules/rules/strict-equality-js.yml +14 -1
  274. package/rules/ast-grep-rules/rules/strict-equality.yml +14 -1
  275. package/rules/ast-grep-rules/rules/strict-inequality-js.yml +14 -1
  276. package/rules/ast-grep-rules/rules/strict-inequality.yml +14 -1
  277. package/rules/tree-sitter-queries/cpp/no-memset-sensitive-data.yml +9 -3
  278. package/rules/tree-sitter-queries/cpp/no-scoped-lock-without-args.yml +6 -6
  279. package/rules/tree-sitter-queries/css/calc-spacing.yml +8 -6
  280. package/rules/tree-sitter-queries/go/go-mutex-copy.yml +21 -18
  281. package/rules/tree-sitter-queries/go/go-shared-map-write-goroutine.yml +3 -3
  282. package/rules/tree-sitter-queries/java/infinite-loop-java.yml +3 -3
  283. package/rules/tree-sitter-queries/java/junit-call-super.yml +6 -4
  284. package/rules/tree-sitter-queries/java/no-double-checked-locking.yml +10 -8
  285. package/rules/tree-sitter-queries/java/no-field-shadowing.yml +1 -1
  286. package/rules/tree-sitter-queries/java/prepared-statement-valid-indices.yml +2 -3
  287. package/rules/tree-sitter-queries/java/resources-closed.yml +2 -2
  288. package/rules/tree-sitter-queries/java/springboot-default-package.yml +6 -3
  289. package/rules/tree-sitter-queries/java/switch-fall-through.yml +6 -4
  290. package/rules/tree-sitter-queries/java/switch-non-case-labels.yml +4 -3
  291. package/rules/tree-sitter-queries/java/tests-include-assertions.yml +6 -5
  292. package/rules/tree-sitter-queries/javascript/switch-case-termination-js.yml +5 -8
  293. package/rules/tree-sitter-queries/javascript/switch-non-case-labels.yml +3 -2
  294. package/rules/tree-sitter-queries/kotlin/prepared-statement-indices.yml +9 -5
  295. package/rules/tree-sitter-queries/php/this-in-static-context.yml +5 -5
  296. package/rules/tree-sitter-queries/python/exit-signature-check.yml +2 -9
  297. package/rules/tree-sitter-queries/python/in-operator-unsupported.yml +3 -4
  298. package/rules/tree-sitter-queries/python/no-super-torchscript.yml +4 -8
  299. package/rules/tree-sitter-queries/python/notimplemented-boolean-context.yml +1 -5
  300. package/rules/tree-sitter-queries/python/python-empty-except.yml +1 -1
  301. package/rules/tree-sitter-queries/python/return-in-init.yml +0 -2
  302. package/rules/tree-sitter-queries/python/yield-return-outside-function.yml +0 -3
  303. package/rules/tree-sitter-queries/ruby/ruby-command-injection.yml +1 -12
  304. package/rules/tree-sitter-queries/ruby/ruby-eval.yml +1 -1
  305. package/rules/tree-sitter-queries/ruby/ruby-insecure-deserialization.yml +4 -4
  306. package/rules/tree-sitter-queries/ruby/ruby-insecure-random.yml +4 -11
  307. package/rules/tree-sitter-queries/ruby/ruby-open-struct.yml +1 -1
  308. package/rules/tree-sitter-queries/ruby/ruby-string-eval.yml +2 -3
  309. package/rules/tree-sitter-queries/ruby/ruby-weak-hash.yml +4 -4
  310. package/rules/tree-sitter-queries/rust/rust-lock-held-across-await.yml +4 -3
  311. package/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  312. package/rules/tree-sitter-queries/typescript/console-statement.yml +11 -0
  313. package/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +4 -9
  314. package/rules/tree-sitter-queries/typescript/empty-switch-case.yml +13 -9
  315. package/rules/tree-sitter-queries/typescript/infinite-loop.yml +2 -2
  316. package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +10 -1
  317. package/rules/tree-sitter-queries/typescript/switch-case-termination.yml +5 -8
  318. package/rules/tree-sitter-queries/typescript/ts-insecure-random.yml +7 -10
  319. package/rules/tree-sitter-queries/typescript/unsafe-regex.yml +4 -2
  320. package/rules/typos/_typos.toml +17 -0
  321. package/scripts/analyze-pi-lens-logs.mjs +55 -2
  322. package/skills/pi-lens-write-tree-sitter-rule/SKILL.md +5 -0
  323. package/dist/clients/ast-grep-parser.js +0 -86
  324. package/dist/clients/config-enabled-shape.js +0 -39
  325. package/dist/clients/lsp/interactive-install.js +0 -367
  326. package/dist/clients/lsp/lsp-index.js +0 -10
  327. package/dist/clients/lsp/server-strategies.js +0 -5
  328. package/dist/clients/project-metadata.js +0 -690
  329. package/dist/clients/source-groups.js +0 -96
  330. package/dist/clients/types.js +0 -11
  331. package/docs/env_variables.md +0 -10
  332. package/rules/tree-sitter-queries/abap-disabled/delete-where.yml +0 -46
  333. package/rules/tree-sitter-queries/cpp/no-confused-move-forward.yml +0 -59
  334. package/rules/tree-sitter-queries/cpp/noexcept-functions.yml +0 -58
  335. package/rules/tree-sitter-queries/java/mockito-initialized.yml +0 -66
  336. package/rules/tree-sitter-queries/java/no-wait-notify-on-thread.yml +0 -58
  337. package/rules/tree-sitter-queries/java/short-circuit-logic.yml +0 -57
  338. package/rules/tree-sitter-queries/java/spring-session-attributes-setcomplete.yml +0 -75
  339. package/rules/tree-sitter-queries/plsql-disabled/delete-update-where.yml +0 -56
  340. package/rules/tree-sitter-queries/plsql-disabled/end-loop-semicolon.yml +0 -51
  341. package/rules/tree-sitter-queries/plsql-disabled/fetch-bulk-collect-limit.yml +0 -47
  342. package/rules/tree-sitter-queries/plsql-disabled/forallsave-exceptions.yml +0 -51
  343. package/rules/tree-sitter-queries/plsql-disabled/nchar-nvarchar2-bytes.yml +0 -54
  344. package/rules/tree-sitter-queries/plsql-disabled/not-null-initialization.yml +0 -59
  345. package/rules/tree-sitter-queries/plsql-disabled/raise-application-error-codes.yml +0 -50
  346. package/rules/tree-sitter-queries/typescript-disabled/ts-path-traversal.yml +0 -71
@@ -19,7 +19,7 @@ import { recordFromCodeQualityDiagnostic, } from "./code-quality-warnings.js";
19
19
  import { recordDiagnostics } from "./widget-state.js";
20
20
  import { getDiagnosticLogger } from "./diagnostic-logger.js";
21
21
  import { getDiagnosticTracker } from "./diagnostic-tracker.js";
22
- import { computeCascadeForFile, dispatchLintWithResult, } from "./dispatch/integration.js";
22
+ import { loadDispatchIntegration } from "./dispatch/lazy.js";
23
23
  import { toRunnerDisplayPath } from "./dispatch/runner-context.js";
24
24
  import { createAvailabilityChecker, resolveAvailableOrInstall, resolveCommandArgsWithInstallFallback, resolveToolCommand, resolveToolCommandWithInstallFallback, } from "./dispatch/runners/utils/runner-helpers.js";
25
25
  import { findDetektConfig } from "./dispatch/runners/detekt.js";
@@ -29,12 +29,14 @@ import { logLatency } from "./latency-logger.js";
29
29
  import { emitLensAnalysisComplete } from "./lens-events.js";
30
30
  import { publishFilesTouched } from "./bus-publish.js";
31
31
  import { publishDiagnostics, wasPreviouslyReportedDirty, } from "./diagnostics-publish.js";
32
- import { getLSPService } from "./lsp/index.js";
32
+ import { loadLspService } from "./lsp-lazy.js";
33
33
  import { clearGraphCache } from "./review-graph/builder.js";
34
+ import { BoundedLruCache } from "./bounded-cache.js";
34
35
  import { RUNTIME_CONFIG } from "./runtime-config.js";
35
36
  import { getAmbientAbortSignal, safeSpawnAsync } from "./safe-spawn.js";
36
37
  import { combineAbortSignals } from "./deadline-utils.js";
37
- import { getAutofixPolicyForFile, getPreferredAutofixTools, getRubocopCommand, hasBiomeConfig, hasDetektConfig, hasEslintConfig, hasGolangciConfig, hasKtfmtConfig, hasOxlintConfig, hasRubocopConfig, hasSqlfluffConfig, hasStylelintConfig, } from "./tool-policy.js";
38
+ import { recordDegradationOnce } from "./degradation-ledger.js";
39
+ import { getAutofixPolicyForFile, getPreferredAutofixTools, getRubocopCommand, hasBiomeConfig, hasDetektConfig, hasEslintConfig, hasGolangciConfig, hasKtfmtConfig, hasKtlintConfig, hasOxlintConfig, hasRubocopConfig, hasSqlfluffConfig, hasStylelintConfig, markdownlintConfigArgs, } from "./tool-policy.js";
38
40
  const LSP_MAX_FILE_BYTES = RUNTIME_CONFIG.pipeline.lspMaxFileBytes;
39
41
  const LSP_MAX_FILE_LINES = RUNTIME_CONFIG.pipeline.lspMaxFileLines;
40
42
  const LSP_SPAWN_BUDGET_MS = RUNTIME_CONFIG.pipeline.lspSpawnBudgetMs;
@@ -66,7 +68,10 @@ const SNAPSHOT_YIELD_EVERY = 500;
66
68
  async function snapshotDirInto(dir, ignoreMatcher, stack, snapshot, counter) {
67
69
  let entries;
68
70
  try {
69
- entries = nodeFs.readdirSync(dir, { withFileTypes: true });
71
+ // Async (#1137): a synchronous per-directory read blocks the loop for the
72
+ // whole stall on a slow cloud-backed (OneDrive) directory; fs.promises
73
+ // keeps this tool_result-path walk off the event loop.
74
+ entries = await nodeFs.promises.readdir(dir, { withFileTypes: true });
70
75
  }
71
76
  catch {
72
77
  return;
@@ -85,7 +90,7 @@ async function snapshotDirInto(dir, ignoreMatcher, stack, snapshot, counter) {
85
90
  if (ignoreMatcher.isIgnored(fullPath, false))
86
91
  continue;
87
92
  try {
88
- const stat = nodeFs.statSync(fullPath);
93
+ const stat = await nodeFs.promises.stat(fullPath);
89
94
  snapshot.set(path.resolve(fullPath), {
90
95
  mtimeMs: stat.mtimeMs,
91
96
  size: stat.size,
@@ -173,7 +178,7 @@ function createPhaseTracker(toolName, filePath) {
173
178
  }
174
179
  // --- ESLint autofix helpers ---
175
180
  export { hasEslintConfig, hasRubocopConfig, hasSqlfluffConfig, hasStylelintConfig, };
176
- const _eslintCache = new Map();
181
+ const _eslintCache = new BoundedLruCache(32);
177
182
  /**
178
183
  * Run eslint --fix on a file. Runs a single spawn and diffs the file before/after,
179
184
  * same idiom as the other autofix helpers below. Exit code 1 (unfixable problems
@@ -186,7 +191,9 @@ async function tryEslintFix(filePath, cwd) {
186
191
  const userHasConfig = hasEslintConfig(cwd);
187
192
  if (!userHasConfig)
188
193
  return 0;
189
- const cacheKey = path.resolve(cwd);
194
+ // PATH is part of command resolution; include it so an install or PATH
195
+ // refresh cannot leave a negative eslint result live for the session.
196
+ const cacheKey = `${path.resolve(cwd)}|${process.env.PATH ?? ""}`;
190
197
  let cached = _eslintCache.get(cacheKey);
191
198
  if (!cached) {
192
199
  const candidate = resolveToolCommand(cwd, "eslint") ?? "eslint";
@@ -274,8 +281,12 @@ async function tryMarkdownlintFix(filePath, cwd) {
274
281
  const cmd = await resolveToolCommandWithInstallFallback(cwd, "markdownlint");
275
282
  if (!cmd)
276
283
  return 0;
284
+ // Shared config-args seam (#1247): the lint runner consumes the same
285
+ // builder, so the bare --fix here can never fall back to markdownlint's
286
+ // default all-rules-on config again (the whole-file CHANGELOG/AGENTS
287
+ // reformat + `#946` → `# 946` corruption).
277
288
  // markdownlint-cli2 --fix exits non-zero when unfixable violations remain.
278
- return detectFileChangedAfterCommand(filePath, cmd, ["--fix", filePath], cwd, [1]);
289
+ return detectFileChangedAfterCommand(filePath, cmd, [...markdownlintConfigArgs(cwd), "--fix", filePath], cwd, [1]);
279
290
  }
280
291
  async function tryOxlintFix(filePath, cwd) {
281
292
  const cmd = await resolveToolCommandWithInstallFallback(cwd, "oxlint");
@@ -356,6 +367,7 @@ export async function runAutofix(filePath, cwd, getFlag, dbg, deps, getFlagSourc
356
367
  hasGolangciConfig: hasGolangciConfig(cwd),
357
368
  hasDetektConfig: hasDetektConfig(cwd),
358
369
  hasKtfmtConfig: hasKtfmtConfig(cwd),
370
+ hasKtlintConfig: hasKtlintConfig(cwd),
359
371
  hasOxlintConfig: hasOxlintConfig(cwd),
360
372
  };
361
373
  const autofixPolicy = getAutofixPolicyForFile(filePath, autofixContext);
@@ -396,7 +408,7 @@ export async function runAutofix(filePath, cwd, getFlag, dbg, deps, getFlagSourc
396
408
  dbg(`autofix: ruff unavailable for ${filePath}`);
397
409
  continue;
398
410
  }
399
- const result = await ruffClient.fixFileAsync(filePath);
411
+ const result = await ruffClient.fixFileAsync(filePath, cwd);
400
412
  if (result.success && result.fixed > 0) {
401
413
  fixedCount += result.fixed;
402
414
  autofixTools.push(`ruff:${result.fixed}`);
@@ -415,7 +427,7 @@ export async function runAutofix(filePath, cwd, getFlag, dbg, deps, getFlagSourc
415
427
  dbg(`autofix: biome unavailable or unsupported for ${filePath}`);
416
428
  continue;
417
429
  }
418
- const result = await biomeClient.fixFileAsync(filePath);
430
+ const result = await biomeClient.fixFileAsync(filePath, cwd);
419
431
  if (result.success && result.fixed > 0) {
420
432
  fixedCount += result.fixed;
421
433
  autofixTools.push(`biome:${result.fixed}`);
@@ -570,7 +582,6 @@ export async function runAutofix(filePath, cwd, getFlag, dbg, deps, getFlagSourc
570
582
  dbg(`autofix: oxlint --fix fixed ${filePath}`);
571
583
  needsContentRefresh = true;
572
584
  }
573
- continue;
574
585
  }
575
586
  }
576
587
  if (attemptedTools.length > 0 && autofixTools.length === 0) {
@@ -593,7 +604,7 @@ export async function resyncLspFile(filePath, fileContent, needsContentRefresh,
593
604
  if (limitCheck.tooLarge)
594
605
  return;
595
606
  try {
596
- const lspService = getLSPService();
607
+ const lspService = (await loadLspService()).getLSPService();
597
608
  if (lspService.supportsLSP(filePath)) {
598
609
  // Push the final post-format/post-fix content through touchFile (not the
599
610
  // bare openFile) so it registers in the touch-debounce map via
@@ -704,6 +715,13 @@ export async function runFormatPhase(filePath, getFormatService, dbg) {
704
715
  }
705
716
  if (!result.allSucceeded) {
706
717
  const failures = result.formatters.filter((f) => !f.success);
718
+ for (const failure of failures) {
719
+ recordDegradationOnce({
720
+ kind: "formatter-failure",
721
+ subject: `${failure.name}:${path.basename(filePath)}`,
722
+ reason: failure.error ?? "unknown error",
723
+ });
724
+ }
707
725
  formatFailures.push(...failures.map((f) => `${f.name}: ${f.error ?? "unknown error"}`));
708
726
  dbg("autoformat: " +
709
727
  failures
@@ -713,6 +731,11 @@ export async function runFormatPhase(filePath, getFormatService, dbg) {
713
731
  }
714
732
  catch (err) {
715
733
  const message = err instanceof Error ? err.message : String(err);
734
+ recordDegradationOnce({
735
+ kind: "formatter-failure",
736
+ subject: `format-service:${path.basename(filePath)}`,
737
+ reason: message,
738
+ });
716
739
  formatFailures.push(message);
717
740
  dbg(`autoformat error: ${err}`);
718
741
  }
@@ -871,11 +894,15 @@ export async function runPipeline(ctx, deps) {
871
894
  const piApi = {
872
895
  getFlag: getFlag,
873
896
  };
897
+ const { dispatchLintWithResult, computeCascadeForFile } = await loadDispatchIntegration();
874
898
  const dispatchResult = await dispatchLintWithResult(filePath, cwd, piApi, ctx.modifiedRanges, {
875
899
  model: ctx.telemetry?.model ?? "unknown",
876
900
  sessionId: ctx.telemetry?.sessionId ?? "unknown",
877
901
  turnIndex: ctx.telemetry?.turnIndex ?? 0,
878
902
  writeIndex: ctx.telemetry?.writeIndex ?? 0,
903
+ }, {
904
+ projectRoot: ctx.projectRoot,
905
+ writeIndex: ctx.telemetry?.writeIndex,
879
906
  });
880
907
  recordDiagnostics(filePath, dispatchResult.diagnostics, ctx.telemetry?.writeIndex);
881
908
  // #502: emit the write batch's FINAL diagnostic state immediately after
@@ -984,6 +1011,11 @@ export async function runPipeline(ctx, deps) {
984
1011
  // result is never shown inline — settled (bounded) and surfaced at turn_end.
985
1012
  // The stored promise must never reject: an unhandled rejection is fatal, so a
986
1013
  // failing compute resolves to an "error" skip-run instead.
1014
+ const cascadeOrigin = {
1015
+ turnSeq: ctx.telemetry?.turnIndex,
1016
+ writeSeq: ctx.telemetry?.writeIndex,
1017
+ projectSeq: ctx.seqState?.projectSeq(),
1018
+ };
987
1019
  const cascadePromise = getFlag("no-lsp")
988
1020
  ? undefined
989
1021
  : computeCascadeForFile(filePath, cwd, {
@@ -995,14 +1027,23 @@ export async function runPipeline(ctx, deps) {
995
1027
  fileContent,
996
1028
  wordIndex: ctx.wordIndex,
997
1029
  onWordIndexUpdated: ctx.onWordIndexUpdated,
998
- }).catch((err) => {
1030
+ }).then((run) => ({ ...run, origin: cascadeOrigin }))
1031
+ .catch((err) => {
999
1032
  dbg(`cascade compute failed for ${filePath}: ${err}`);
1000
1033
  return {
1001
1034
  filePath,
1035
+ origin: cascadeOrigin,
1002
1036
  result: undefined,
1003
1037
  neighborCount: 0,
1004
1038
  diagnosticCount: 0,
1005
1039
  skipReason: "error",
1040
+ // #1023: a thrown compute is ALSO "couldn't compute downstream
1041
+ // impact" — carry an indeterminate marker so turn_end surfaces an
1042
+ // honest advisory instead of the error skip being logs-only.
1043
+ indeterminate: {
1044
+ reason: "error",
1045
+ detail: "cascade computation failed",
1046
+ },
1006
1047
  };
1007
1048
  });
1008
1049
  // --- Final timing + all-clear ---
@@ -0,0 +1,21 @@
1
+ /**
2
+ * One-shot invocation detection, shared across the session_start-scheduled
3
+ * background work that must NOT run — or retain the process — in print mode
4
+ * (#1154 quick-mode warmup, #1153 orphan-reaper).
5
+ *
6
+ * `pi -p` / `pi --print` is a one-shot: the host computes a single turn and
7
+ * exits. Work scheduled to warm caches / clean up state for a *future* session
8
+ * in the same process is pure waste in this mode AND — when it arms a
9
+ * referenced libuv handle (a non-`unref`'d timer, an LSP child, an fs walk)
10
+ * with no session_shutdown teardown — keeps the already-settled one-shot
11
+ * process alive. That is the referenced-handle retention class of
12
+ * #1097/#1110/#1148/#1149 and the located #1122 hypothesis-A concern.
13
+ *
14
+ * argv is read live (not captured at module load) so tests can drive it; the
15
+ * flag never changes within a real process, and there is no hot-path caller,
16
+ * so recomputing is free. This mirrors the print detection in
17
+ * `runtime-session.ts`'s `resolveStartupMode` — keep them a single source.
18
+ */
19
+ export function isPrintMode(argv = process.argv) {
20
+ return argv.includes("--print") || argv.includes("-p");
21
+ }
@@ -44,6 +44,31 @@ export function readProjectChanges(cwd) {
44
44
  return [];
45
45
  }
46
46
  }
47
+ /**
48
+ * Async twin of `readProjectChanges` (#1162). `fs.promises.readFile` — unlike
49
+ * the sync `readFileSync` above — YIELDS to the event loop, which is what lets
50
+ * a caller bound it with a `Promise.race` timeout: a `setTimeout` can never
51
+ * preempt a synchronous blocking read (the thread doesn't return to the event
52
+ * loop until the OS does), so a bounded read on the session_start hot path
53
+ * requires the async form. Same parse/error-swallow semantics as the sync
54
+ * version — a missing/corrupt log is the normal cold-start case, not a
55
+ * caller-visible error.
56
+ */
57
+ export async function readProjectChangesAsync(cwd) {
58
+ const logPath = getProjectChangeLogPath(cwd);
59
+ try {
60
+ const content = await fs.promises.readFile(logPath, "utf-8");
61
+ return content
62
+ .split(/\r?\n/)
63
+ .map((line) => line.trim())
64
+ .filter(Boolean)
65
+ .map(parseChangeLine)
66
+ .filter((entry) => Boolean(entry));
67
+ }
68
+ catch {
69
+ return [];
70
+ }
71
+ }
47
72
  export function readChangesSince(cwd, seq, maxEntries = 200) {
48
73
  const limit = Math.max(1, maxEntries);
49
74
  return readProjectChanges(cwd)
@@ -51,16 +76,104 @@ export function readChangesSince(cwd, seq, maxEntries = 200) {
51
76
  .sort((a, b) => a.seq - b.seq)
52
77
  .slice(-limit);
53
78
  }
54
- export function readLatestProjectSequence(cwd) {
79
+ // Test-observable count of the EXPENSIVE per-entry folds (each one does a
80
+ // `path.resolve` + `normalizeMapKey`/`realpathSync.native` syscall). Lets the
81
+ // #1019 equivalence tests prove the partial path folds strictly FEWER entries
82
+ // than the full replay for the same log — i.e. that the bound actually holds —
83
+ // without a brittle spy on the path helpers.
84
+ let _sequenceFoldCountForTests = 0;
85
+ export function getSequenceFoldCountForTests() {
86
+ return _sequenceFoldCountForTests;
87
+ }
88
+ export function resetSequenceFoldCountForTests() {
89
+ _sequenceFoldCountForTests = 0;
90
+ }
91
+ function foldEntry(entry, fileSeqByPath) {
92
+ _sequenceFoldCountForTests++;
93
+ const key = normalizeMapKey(path.resolve(entry.filePath));
94
+ fileSeqByPath.set(key, Math.max(fileSeqByPath.get(key) ?? 0, entry.fileSeq));
95
+ }
96
+ function fullReplay(entries) {
55
97
  let projectSeq = 0;
56
98
  const fileSeqByPath = new Map();
57
- for (const entry of readProjectChanges(cwd)) {
99
+ for (const entry of entries) {
100
+ projectSeq = Math.max(projectSeq, entry.seq);
101
+ foldEntry(entry, fileSeqByPath);
102
+ }
103
+ return { projectSeq, fileSeqByPath };
104
+ }
105
+ /**
106
+ * Attempt the bounded partial replay: hydrate `base` (pre-normalized keys, no
107
+ * `realpath` cost) and fold ONLY entries with `seq > base.sinceSeq`. Returns
108
+ * `null` — signalling the caller to fall back to a full replay — whenever the
109
+ * base cannot be trusted to be byte-identical to a full replay:
110
+ *
111
+ * - `base.sinceSeq` is AHEAD of the log's max seq (the log was truncated /
112
+ * rotated below the snapshot, or the snapshot seq is simply ahead). Serving
113
+ * the base would over-report the seq; the full replay reflects the real log.
114
+ *
115
+ * The result is provably equal to a full replay under the append-only,
116
+ * single-writer invariant (`base` == fold of the log up to `sinceSeq`): every
117
+ * historical file's seq is already in `base`, and folding the strictly-newer
118
+ * entries with the SAME `Math.max` the full replay uses reproduces the max over
119
+ * the whole log for both `projectSeq` and each file. `Math.max` makes the fold
120
+ * order-independent, so gaps / out-of-order entries are handled exactly as the
121
+ * full replay handles them.
122
+ */
123
+ function partialReplay(entries, base) {
124
+ let logMaxSeq = 0;
125
+ for (const entry of entries) {
126
+ if (entry.seq > logMaxSeq)
127
+ logMaxSeq = entry.seq;
128
+ }
129
+ // Snapshot ahead of / newer than the log (truncation, rotation, or a stale
130
+ // log): the base describes a state the log no longer backs — never serve it.
131
+ if (base.sinceSeq > logMaxSeq)
132
+ return null;
133
+ const fileSeqByPath = new Map(base.fileSeqByPath);
134
+ let projectSeq = Math.max(0, base.projectSeq);
135
+ for (const entry of entries) {
136
+ if (entry.seq <= base.sinceSeq)
137
+ continue; // covered by the base
58
138
  projectSeq = Math.max(projectSeq, entry.seq);
59
- const key = normalizeMapKey(path.resolve(entry.filePath));
60
- fileSeqByPath.set(key, Math.max(fileSeqByPath.get(key) ?? 0, entry.fileSeq));
139
+ foldEntry(entry, fileSeqByPath);
61
140
  }
62
141
  return { projectSeq, fileSeqByPath };
63
142
  }
143
+ /**
144
+ * Compute the current `{ projectSeq, fileSeqByPath }` from the append-only
145
+ * change log. With no `base`, this is a full replay of the entire log. With a
146
+ * snapshot-embedded `base` (#1019), it folds only entries newer than
147
+ * `base.sinceSeq` on top of the base — O(changes since snapshot) instead of
148
+ * O(entire log) — and transparently falls back to a full replay when the base
149
+ * cannot be trusted (see `partialReplay`). A legacy/stale/missing snapshot
150
+ * simply passes no `base` (or one the guard rejects), so correctness never
151
+ * depends on the optimization being applicable.
152
+ */
153
+ export function readLatestProjectSequence(cwd, base) {
154
+ const entries = readProjectChanges(cwd);
155
+ if (base) {
156
+ const partial = partialReplay(entries, base);
157
+ if (partial)
158
+ return partial;
159
+ }
160
+ return fullReplay(entries);
161
+ }
162
+ /**
163
+ * Async twin of `readLatestProjectSequence` (#1162) — same fold logic, but
164
+ * reads the log via `readProjectChangesAsync` so a caller can bound the wait
165
+ * with a `Promise.race` timeout (see that function's doc comment for why the
166
+ * sync form can't be bounded at all).
167
+ */
168
+ export async function readLatestProjectSequenceAsync(cwd, base) {
169
+ const entries = await readProjectChangesAsync(cwd);
170
+ if (base) {
171
+ const partial = partialReplay(entries, base);
172
+ if (partial)
173
+ return partial;
174
+ }
175
+ return fullReplay(entries);
176
+ }
64
177
  export function appendProjectChange(cwd, entry) {
65
178
  const logPath = getProjectChangeLogPath(cwd);
66
179
  fs.mkdirSync(path.dirname(logPath), { recursive: true });
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { getProjectDataDir } from "../file-utils.js";
4
+ import { writeFileAtomic } from "../atomic-write.js";
4
5
  import { readJsonCache } from "../json-cache-read.js";
5
6
  // v2: cheap-tier scan now also runs ast-grep-napi (#308); invalidate older
6
7
  // snapshots so a pre-ast-grep cache isn't served as complete via refreshRunners=cached.
@@ -25,7 +26,7 @@ export function loadProjectDiagnosticsSnapshot(cwd) {
25
26
  export function saveProjectDiagnosticsSnapshot(cwd, snapshot) {
26
27
  const filePath = cachePath(cwd, SNAPSHOT_CACHE_FILE);
27
28
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
28
- fs.writeFileSync(filePath, JSON.stringify(snapshot, null, 2));
29
+ writeFileAtomic(filePath, JSON.stringify(snapshot, null, 2));
29
30
  }
30
31
  export function loadProjectDiagnosticsDeltaReport(cwd) {
31
32
  return readJsonCache(cachePath(cwd, DELTA_CACHE_FILE), (parsed) => {
@@ -42,7 +43,7 @@ export function loadProjectDiagnosticsDeltaReport(cwd) {
42
43
  export function writeProjectDiagnosticsDeltaReport(cwd, report) {
43
44
  const filePath = cachePath(cwd, DELTA_CACHE_FILE);
44
45
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
45
- fs.writeFileSync(filePath, JSON.stringify(report, null, 2));
46
+ writeFileAtomic(filePath, JSON.stringify(report, null, 2));
46
47
  }
47
48
  /**
48
49
  * Drop diagnostics whose underlying file changed on disk after the snapshot was
@@ -1,83 +1,30 @@
1
1
  /**
2
- * Extractor registry — the single place that turns the heavyweight project
3
- * analyzers' CACHED results into `ProjectDiagnostic`s for `lens_diagnostics
4
- * mode=full`.
2
+ * Provenance metadata for the heavyweight project analyzers surfaced in
3
+ * `lens_diagnostics mode=full`.
5
4
  *
6
- * Each analyzer (knip, jscpd, madge, gitleaks, …) runs on its own cadence
7
- * (session-start / turn-end) and writes a result to `cacheManager`. This module
8
- * READS those caches and adapts them via the pure `runner-adapters/*` functions.
9
- * It never launches a scan — so mode=full can't relaunch or contend with the
10
- * background runs. Adding a new analyzer = write one adapter + one registry row.
5
+ * NOTE (#585 regression cleanup): this module USED to also own
6
+ * `extractCachedProjectDiagnostics` — a CACHE-ONLY reader that adapted each
7
+ * analyzer's cached result to `ProjectDiagnostic[]` via a parallel `EXTRACTORS`
8
+ * registry. #585 replaced that reader in production with
9
+ * `fetchFreshProjectDiagnostics` (./fresh-fetch.ts), which triggers-or-joins a
10
+ * fresh run instead of settling for a possibly-hours-stale session_start
11
+ * snapshot. The old reader lingered here wired only to a test — a SECOND,
12
+ * dead surfacing path whose registry could (and did) silently diverge from
13
+ * fresh-fetch's `ANALYZER_IDS`: opengrep was registered here but missing there,
14
+ * so opengrep scanned+cached yet never reached the agent (the exact #585-class
15
+ * regression this cleanup removes). The parallel registry is gone; fresh-fetch
16
+ * is the single source of truth (#883).
17
+ *
18
+ * What survives is the honesty metadata (#533): given an analyzer id that
19
+ * fresh-fetch reported `cold` (not applicable / unavailable this run),
20
+ * `warmTriggerFor` names what WOULD warm it, so the rendered note is actionable
21
+ * rather than a bare "not run".
11
22
  */
12
- import { deadCodeResultToProjectDiagnostics } from "./runner-adapters/dead-code.js";
13
- import { gitleaksResultToProjectDiagnostics } from "./runner-adapters/gitleaks.js";
14
- import { govulncheckResultToProjectDiagnostics } from "./runner-adapters/govulncheck.js";
15
- import { jscpdResultToProjectDiagnostics } from "./runner-adapters/jscpd.js";
16
- import { knipIssuesToProjectDiagnostics } from "./runner-adapters/knip.js";
17
- import { circularDepsToProjectDiagnostics } from "./runner-adapters/madge.js";
18
- import { opengrepResultToProjectDiagnostics } from "./runner-adapters/opengrep.js";
19
- import { testRunnerFindingsToProjectDiagnostics } from "./runner-adapters/runner-findings.js";
20
- import { trivyResultToProjectDiagnostics } from "./runner-adapters/trivy.js";
21
- // biome-ignore lint/suspicious/noExplicitAny: heterogeneous result types per row
22
- const EXTRACTORS = [
23
- {
24
- id: "knip",
25
- cacheKeys: ["knip"],
26
- adapt: (cwd, r) => knipIssuesToProjectDiagnostics(cwd, r.issues ?? []),
27
- },
28
- {
29
- id: "jscpd",
30
- cacheKeys: ["jscpd-ts", "jscpd"],
31
- adapt: (cwd, r) => jscpdResultToProjectDiagnostics(cwd, r),
32
- },
33
- {
34
- id: "madge",
35
- cacheKeys: ["madge"],
36
- adapt: (cwd, r) => circularDepsToProjectDiagnostics(cwd, r.circular ?? []),
37
- },
38
- {
39
- id: "gitleaks",
40
- cacheKeys: ["gitleaks"],
41
- adapt: (cwd, r) => gitleaksResultToProjectDiagnostics(cwd, r),
42
- },
43
- {
44
- id: "govulncheck",
45
- cacheKeys: ["govulncheck"],
46
- adapt: (cwd, r) => govulncheckResultToProjectDiagnostics(cwd, r),
47
- },
48
- {
49
- // #584: opengrep's full-workspace findings, sourced from a single
50
- // project-wide CLI scan (`opengrep-client.ts`) instead of the per-file
51
- // LSP sweep — see the constant's doc in `clients/lsp/index.ts`.
52
- id: "opengrep",
53
- cacheKeys: ["opengrep"],
54
- adapt: (cwd, r) => opengrepResultToProjectDiagnostics(cwd, r),
55
- },
56
- {
57
- id: "trivy",
58
- cacheKeys: ["trivy"],
59
- adapt: (cwd, r) => trivyResultToProjectDiagnostics(cwd, r),
60
- },
61
- {
62
- // Per-language cache (`dead-code-<id>`). Only Python (vulture) exists today;
63
- // add a key here as each new dead-code language lands.
64
- id: "dead-code",
65
- cacheKeys: ["dead-code-python"],
66
- adapt: (cwd, r) => deadCodeResultToProjectDiagnostics(cwd, r),
67
- },
68
- {
69
- // #628 item 4: per-edit test-runner findings (turn_end, `runtime-turn.ts`).
70
- // Cache-only like every row here — never relaunches a test run. `cwd` is
71
- // unused (results already carry absolute file paths).
72
- id: "test-runner",
73
- cacheKeys: ["test-runner-findings"],
74
- adapt: (_cwd, r) => testRunnerFindingsToProjectDiagnostics(r),
75
- },
76
- ];
77
23
  /**
78
- * #533: which trigger warms each extractor's cache, surfaced in the "cold"
79
- * honesty note so the note is actionable (names what to do), matching the
80
- * #511/#514 house shape. Keep in sync with `EXTRACTORS` — one line per id.
24
+ * #533: which trigger warms each analyzer, surfaced in the "cold" honesty note
25
+ * so the note is actionable (names what to do), matching the #511/#514 house
26
+ * shape. Keyed by the ids `fetchFreshProjectDiagnostics` (./fresh-fetch.ts)
27
+ * reports in its `cold` list — keep in sync with that module's `ANALYZER_IDS`.
81
28
  */
82
29
  const WARM_TRIGGER = {
83
30
  knip: "runs at session-start",
@@ -85,47 +32,11 @@ const WARM_TRIGGER = {
85
32
  madge: "runs at session-start",
86
33
  gitleaks: "runs at session-start (config opt-in), or on any git repo via mode=full (#608)",
87
34
  govulncheck: "runs at session-start (Go projects only)",
35
+ opengrep: "runs at session-start",
88
36
  trivy: "runs at session-start",
89
37
  "dead-code": "runs at session-start (Python projects only)",
90
- opengrep: "runs at session-start",
91
38
  "test-runner": "fires per-edit at turn_end (only after a source file with a discoverable test companion is edited)",
92
39
  };
93
- /** All registered extractor ids, in registry order — exported for tools/tests
94
- * that need to enumerate "what could this section include" without reaching
95
- * into the private `EXTRACTORS` table. */
96
- export const PROJECT_DIAGNOSTIC_EXTRACTOR_IDS = EXTRACTORS.map((e) => e.id);
97
- export function warmTriggerFor(extractorId) {
98
- return WARM_TRIGGER[extractorId] ?? "runs at session-start";
99
- }
100
- /**
101
- * Read every registered analyzer's cached result and adapt it to project
102
- * diagnostics. Returns the merged diagnostics, the ids of the analyzers that
103
- * actually contributed findings (for the snapshot's `runners` list), and the
104
- * ids of analyzers with NO cache entry at all yet (`cold`) — distinct from an
105
- * analyzer that ran and found nothing. Cache-only: no scans.
106
- */
107
- export function extractCachedProjectDiagnostics(cacheManager, cwd) {
108
- const diagnostics = [];
109
- const runners = [];
110
- const cold = [];
111
- for (const extractor of EXTRACTORS) {
112
- let data;
113
- for (const key of extractor.cacheKeys) {
114
- const entry = cacheManager.readCache(key, cwd);
115
- if (entry?.data) {
116
- data = entry.data;
117
- break;
118
- }
119
- }
120
- if (data === undefined) {
121
- cold.push(extractor.id);
122
- continue;
123
- }
124
- const adapted = extractor.adapt(cwd, data);
125
- if (adapted.length > 0) {
126
- diagnostics.push(...adapted);
127
- runners.push(extractor.id);
128
- }
129
- }
130
- return { diagnostics, runners, cold };
40
+ export function warmTriggerFor(analyzerId) {
41
+ return WARM_TRIGGER[analyzerId] ?? "runs at session-start";
131
42
  }