pi-lens 4.1.2 → 4.1.4

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 (359) hide show
  1. package/CHANGELOG.md +1308 -0
  2. package/README.md +3 -0
  3. package/config/biome/core.jsonc +11 -2
  4. package/config/dependency-cruiser-eager-allowlist.json +91 -0
  5. package/dist/clients/actionable-warnings-logger.js +18 -3
  6. package/dist/clients/actionable-warnings.js +927 -80
  7. package/dist/clients/agent-behavior-client.js +13 -4
  8. package/dist/clients/ast-grep-client.js +136 -9
  9. package/dist/clients/ast-grep-rule-manager.js +60 -5
  10. package/dist/clients/ast-grep-tool-logger.js +2 -2
  11. package/dist/clients/availability-probe-flight.js +12 -0
  12. package/dist/clients/bash-file-access.js +1 -3
  13. package/dist/clients/biome-client.js +16 -2
  14. package/dist/clients/blocker-freshness.js +34 -0
  15. package/dist/clients/bootstrap.js +509 -73
  16. package/dist/clients/bounded-cache.js +152 -12
  17. package/dist/clients/bounded-telemetry.js +115 -23
  18. package/dist/clients/build-identity.js +95 -0
  19. package/dist/clients/bundled-resource-health.js +113 -0
  20. package/dist/clients/bus-events-logger.js +2 -2
  21. package/dist/clients/bus-publish.js +3 -0
  22. package/dist/clients/cache/rule-cache.js +33 -5
  23. package/dist/clients/cache-manager.js +105 -6
  24. package/dist/clients/cache-observability.js +46 -19
  25. package/dist/clients/cargo-manifest.js +422 -0
  26. package/dist/clients/cascade-logger.js +17 -3
  27. package/dist/clients/code-quality-warnings.js +13 -3
  28. package/dist/clients/complexity-client.js +16 -5
  29. package/dist/clients/config-core/deny.js +221 -0
  30. package/dist/clients/config-core/index.js +47 -0
  31. package/dist/clients/config-core/merge.js +357 -0
  32. package/dist/clients/config-core/normalize.js +340 -0
  33. package/dist/clients/config-core/process-spec.js +248 -0
  34. package/dist/clients/config-core/provenance.js +171 -0
  35. package/dist/clients/config-core/records.js +218 -0
  36. package/dist/clients/config-core/resolve.js +125 -0
  37. package/dist/clients/config-core/safe-object.js +78 -0
  38. package/dist/clients/config-core/schema.js +167 -0
  39. package/dist/clients/config-diagnostic-codes.js +281 -0
  40. package/dist/clients/config-locations.js +160 -0
  41. package/dist/clients/config-resolve.js +789 -0
  42. package/dist/clients/config-schema.js +197 -0
  43. package/dist/clients/config-warn.js +407 -0
  44. package/dist/clients/dead-code-client.js +33 -25
  45. package/dist/clients/dead-code-logger.js +2 -2
  46. package/dist/clients/deadline-utils.js +178 -0
  47. package/dist/clients/debug-handles.js +2 -2
  48. package/dist/clients/debug-heap.js +3 -3
  49. package/dist/clients/deferred-lsp-work.js +106 -0
  50. package/dist/clients/degradation-ledger.js +166 -8
  51. package/dist/clients/demoted-finding-render.js +14 -0
  52. package/dist/clients/dependency-checker.js +28 -22
  53. package/dist/clients/deps/minimatch.js +1 -1
  54. package/dist/clients/diagnostic-line-freshness.js +12 -9
  55. package/dist/clients/diagnostic-logger.js +14 -4
  56. package/dist/clients/dispatch/collect-later-tier.js +39 -0
  57. package/dist/clients/dispatch/dispatcher.js +304 -19
  58. package/dist/clients/dispatch/fact-store.js +328 -9
  59. package/dist/clients/dispatch/integration.js +214 -107
  60. package/dist/clients/dispatch/pending-runner-findings.js +87 -0
  61. package/dist/clients/dispatch/plan.js +21 -5
  62. package/dist/clients/dispatch/runners/actionlint.js +0 -1
  63. package/dist/clients/dispatch/runners/ast-grep-napi.js +550 -77
  64. package/dist/clients/dispatch/runners/biome-check.js +0 -1
  65. package/dist/clients/dispatch/runners/cpp-check.js +0 -1
  66. package/dist/clients/dispatch/runners/credo.js +0 -1
  67. package/dist/clients/dispatch/runners/cue-vet.js +0 -1
  68. package/dist/clients/dispatch/runners/dart-analyze.js +0 -1
  69. package/dist/clients/dispatch/runners/detekt.js +0 -1
  70. package/dist/clients/dispatch/runners/dotnet-build.js +0 -1
  71. package/dist/clients/dispatch/runners/elixir-check.js +0 -1
  72. package/dist/clients/dispatch/runners/eslint.js +5 -2
  73. package/dist/clients/dispatch/runners/fact-rules.js +0 -1
  74. package/dist/clients/dispatch/runners/fish-indent.js +0 -1
  75. package/dist/clients/dispatch/runners/gleam-check.js +0 -1
  76. package/dist/clients/dispatch/runners/go-vet.js +1 -3
  77. package/dist/clients/dispatch/runners/golangci-lint.js +0 -1
  78. package/dist/clients/dispatch/runners/hadolint.js +0 -1
  79. package/dist/clients/dispatch/runners/helm-lint.js +16 -4
  80. package/dist/clients/dispatch/runners/helm-render.js +54 -10
  81. package/dist/clients/dispatch/runners/htmlhint.js +0 -1
  82. package/dist/clients/dispatch/runners/index.js +1 -3
  83. package/dist/clients/dispatch/runners/javac.js +0 -1
  84. package/dist/clients/dispatch/runners/ktlint.js +0 -1
  85. package/dist/clients/dispatch/runners/lsp.js +37 -2
  86. package/dist/clients/dispatch/runners/markdownlint.js +4 -2
  87. package/dist/clients/dispatch/runners/mypy.js +0 -1
  88. package/dist/clients/dispatch/runners/oxlint.js +24 -22
  89. package/dist/clients/dispatch/runners/php-lint.js +0 -1
  90. package/dist/clients/dispatch/runners/phpstan.js +0 -1
  91. package/dist/clients/dispatch/runners/prisma-validate.js +0 -1
  92. package/dist/clients/dispatch/runners/psscriptanalyzer.js +4 -1
  93. package/dist/clients/dispatch/runners/pyright.js +7 -4
  94. package/dist/clients/dispatch/runners/rubocop.js +0 -1
  95. package/dist/clients/dispatch/runners/ruff.js +0 -1
  96. package/dist/clients/dispatch/runners/rust-clippy.js +9 -6
  97. package/dist/clients/dispatch/runners/shellcheck.js +0 -1
  98. package/dist/clients/dispatch/runners/shfmt.js +0 -1
  99. package/dist/clients/dispatch/runners/spellcheck.js +0 -1
  100. package/dist/clients/dispatch/runners/spotbugs.js +0 -1
  101. package/dist/clients/dispatch/runners/sqlfluff.js +0 -1
  102. package/dist/clients/dispatch/runners/stylelint.js +0 -1
  103. package/dist/clients/dispatch/runners/swiftlint.js +0 -1
  104. package/dist/clients/dispatch/runners/taplo.js +0 -1
  105. package/dist/clients/dispatch/runners/terragrunt.js +0 -1
  106. package/dist/clients/dispatch/runners/tflint.js +0 -1
  107. package/dist/clients/dispatch/runners/tree-sitter.js +0 -1
  108. package/dist/clients/dispatch/runners/trivy-config.js +0 -1
  109. package/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
  110. package/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
  111. package/dist/clients/dispatch/runners/utils/runner-helpers.js +270 -105
  112. package/dist/clients/dispatch/runners/utils/spawn-outcome.js +4 -0
  113. package/dist/clients/dispatch/runners/utils/tool-failure.js +19 -7
  114. package/dist/clients/dispatch/runners/utils/toolchain-availability.js +89 -26
  115. package/dist/clients/dispatch/runners/vale.js +0 -1
  116. package/dist/clients/dispatch/runners/yaml-rule-parser.js +51 -17
  117. package/dist/clients/dispatch/runners/yamllint.js +0 -1
  118. package/dist/clients/dispatch/runners/zig-check.js +0 -1
  119. package/dist/clients/disposition-logger.js +3 -3
  120. package/dist/clients/effective-config.js +403 -0
  121. package/dist/clients/error-class.js +23 -0
  122. package/dist/clients/event-loop-hold.js +274 -0
  123. package/dist/clients/event-loop-monitor.js +66 -1
  124. package/dist/clients/extension-log.js +2 -2
  125. package/dist/clients/feature-hints.js +2 -1
  126. package/dist/clients/file-time.js +6 -1
  127. package/dist/clients/file-utils.js +266 -13
  128. package/dist/clients/finding-delivery-gate.js +55 -13
  129. package/dist/clients/format-service.js +6 -1
  130. package/dist/clients/formatters.js +303 -104
  131. package/dist/clients/freshness-cadence.js +17 -0
  132. package/dist/clients/generated-artifacts.js +114 -19
  133. package/dist/clients/generation-guard.js +4 -9
  134. package/dist/clients/git-guard.js +198 -48
  135. package/dist/clients/git-tracked-ignore.js +32 -1
  136. package/dist/clients/go-client.js +39 -0
  137. package/dist/clients/govulncheck-client.js +5 -1
  138. package/dist/clients/gradle-ktfmt-style.js +252 -0
  139. package/dist/clients/gzip-stage-write.js +7 -0
  140. package/dist/clients/hashline-anchor.js +424 -0
  141. package/dist/clients/hook-budgets.js +76 -0
  142. package/dist/clients/host-edit-normalize.js +5 -2
  143. package/dist/clients/host-ports.js +1 -1
  144. package/dist/clients/installer/index.js +200 -84
  145. package/dist/clients/installer/managed-tool-refresh.js +3 -1
  146. package/dist/clients/instance-reaper.js +108 -243
  147. package/dist/clients/instance-registry-lock.js +145 -0
  148. package/dist/clients/instance-registry.js +435 -122
  149. package/dist/clients/jscpd-client.js +6 -1
  150. package/dist/clients/language-profile.js +4 -2
  151. package/dist/clients/language-registry.js +595 -0
  152. package/dist/clients/latency-logger.js +179 -3
  153. package/dist/clients/ledger-bounds.js +13 -0
  154. package/dist/clients/lens-config.js +141 -30
  155. package/dist/clients/lens-engine.js +30 -37
  156. package/dist/clients/lens-flag-registry.js +67 -6
  157. package/dist/clients/lens-map.js +3 -1
  158. package/dist/clients/log-cleanup.js +2 -2
  159. package/dist/clients/lsp/aggregation.js +3 -1
  160. package/dist/clients/lsp/cascade-tier.js +4 -0
  161. package/dist/clients/lsp/client.js +672 -49
  162. package/dist/clients/lsp/config.js +504 -156
  163. package/dist/clients/lsp/diagnostic-binding.js +10 -5
  164. package/dist/clients/lsp/edits.js +15 -2
  165. package/dist/clients/lsp/index.js +1179 -247
  166. package/dist/clients/lsp/jvm-runtime.js +5 -0
  167. package/dist/clients/lsp/language.js +18 -169
  168. package/dist/clients/lsp/launch.js +38 -9
  169. package/dist/clients/lsp/pending-aux-coverage.js +113 -12
  170. package/dist/clients/lsp/server.js +426 -160
  171. package/dist/clients/lsp/session-roots.js +192 -0
  172. package/dist/clients/lsp/spawn-history.js +2 -7
  173. package/dist/clients/lsp/tsserver-sync.js +9 -2
  174. package/dist/clients/lsp/workspace-diagnostics-cache.js +17 -7
  175. package/dist/clients/lsp/workspace-sweep-hold.js +32 -23
  176. package/dist/clients/lsp-mutation.js +179 -23
  177. package/dist/clients/map-with-concurrency.js +36 -0
  178. package/dist/clients/mcp/analyze.js +45 -10
  179. package/dist/clients/mcp/session.js +19 -17
  180. package/dist/clients/memory-sampler.js +28 -1
  181. package/dist/clients/message-end-attribution.js +30 -0
  182. package/dist/clients/metrics-history.js +21 -4
  183. package/dist/clients/middle-man-analysis.js +2 -4
  184. package/dist/clients/module-report.js +20 -45
  185. package/dist/clients/mutating-tool.js +651 -0
  186. package/dist/clients/mutation-attribution.js +368 -0
  187. package/dist/clients/mutation-bridge.js +240 -0
  188. package/dist/clients/ndjson-logger.js +247 -20
  189. package/dist/clients/observed-mutation-sources.js +101 -0
  190. package/dist/clients/observed-mutation.js +1215 -0
  191. package/dist/clients/opaque-mutation-scan.js +300 -40
  192. package/dist/clients/opengrep-config.js +4 -0
  193. package/dist/clients/package-manager.js +201 -44
  194. package/dist/clients/partial-edit-apply.js +359 -80
  195. package/dist/clients/path-attribution-telemetry.js +23 -7
  196. package/dist/clients/path-keyed-map.js +35 -3
  197. package/dist/clients/path-utils.js +460 -13
  198. package/dist/clients/persist-debounce.js +8 -1
  199. package/dist/clients/php-cs-fixer-config.js +114 -0
  200. package/dist/clients/pipeline.js +77 -24
  201. package/dist/clients/probe-home-state.js +228 -0
  202. package/dist/clients/process-bridge.js +66 -0
  203. package/dist/clients/process-singletons.js +210 -0
  204. package/dist/clients/process-snapshot.js +68 -0
  205. package/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
  206. package/dist/clients/project-diagnostics/runner-adapters/madge.js +2 -1
  207. package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
  208. package/dist/clients/project-diagnostics/scanner.js +61 -24
  209. package/dist/clients/project-lens-config.js +393 -122
  210. package/dist/clients/project-snapshot.js +85 -35
  211. package/dist/clients/python-environment.js +217 -0
  212. package/dist/clients/python-provenance.js +639 -0
  213. package/dist/clients/quiet-window.js +6 -1
  214. package/dist/clients/read-bridge.js +13 -21
  215. package/dist/clients/read-expansion.js +9 -46
  216. package/dist/clients/read-guard-logger.js +39 -6
  217. package/dist/clients/read-guard-tool-lines.js +364 -212
  218. package/dist/clients/read-guard.js +95 -7
  219. package/dist/clients/recent-touches.js +17 -5
  220. package/dist/clients/resource-sampler.js +195 -96
  221. package/dist/clients/review-graph/builder.js +752 -132
  222. package/dist/clients/review-graph/service.js +55 -3
  223. package/dist/clients/review-graph/shared-extraction-ir.js +7 -15
  224. package/dist/clients/review-graph/tsconfig-paths.js +5 -1
  225. package/dist/clients/review-graph/workspace-modules.js +10 -74
  226. package/dist/clients/review-graph-logger.js +30 -4
  227. package/dist/clients/runtime-agent-end.js +18 -2
  228. package/dist/clients/runtime-context.js +49 -16
  229. package/dist/clients/runtime-coordinator.js +93 -6
  230. package/dist/clients/runtime-session.js +169 -34
  231. package/dist/clients/runtime-tool-call.js +183 -35
  232. package/dist/clients/runtime-tool-result.js +894 -241
  233. package/dist/clients/runtime-turn.js +1264 -74
  234. package/dist/clients/rust-client.js +31 -0
  235. package/dist/clients/safe-spawn.js +191 -28
  236. package/dist/clients/sanitize.js +7 -1
  237. package/dist/clients/scratch-tree-policy.js +1 -3
  238. package/dist/clients/security-scan-client.js +51 -17
  239. package/dist/clients/session-lifecycle.js +238 -42
  240. package/dist/clients/session-start-observability.js +79 -0
  241. package/dist/clients/sessionstart-logger.js +12 -3
  242. package/dist/clients/sg-runner.js +27 -15
  243. package/dist/clients/sgconfig.js +6 -1
  244. package/dist/clients/shared-checkout-guard.js +338 -0
  245. package/dist/clients/skills-resolver.js +105 -0
  246. package/dist/clients/smells-rollup.js +2 -2
  247. package/dist/clients/spawn-output-cap.js +37 -0
  248. package/dist/clients/spawn-timeout-cooldown.js +15 -1
  249. package/dist/clients/startup-scan.js +6 -7
  250. package/dist/clients/startup-timing.js +69 -4
  251. package/dist/clients/string-utils.js +33 -0
  252. package/dist/clients/subagent-mode.js +17 -4
  253. package/dist/clients/test-runner-client.js +586 -118
  254. package/dist/clients/test-runner-delivery.js +239 -0
  255. package/dist/clients/tool-definition.js +41 -1
  256. package/dist/clients/tool-policy.js +92 -22
  257. package/dist/clients/tree-sitter-cache.js +40 -40
  258. package/dist/clients/tree-sitter-client.js +97 -53
  259. package/dist/clients/tree-sitter-logger.js +19 -3
  260. package/dist/clients/tree-sitter-query-loader.js +81 -1
  261. package/dist/clients/tree-sitter-shared.js +51 -46
  262. package/dist/clients/tree-sitter-symbol-extractor.js +28 -0
  263. package/dist/clients/typos-config.js +5 -0
  264. package/dist/clients/user-notify.js +6 -2
  265. package/dist/clients/warm-attach.js +8 -10
  266. package/dist/clients/widget-state.js +191 -26
  267. package/dist/clients/word-index-logger.js +15 -3
  268. package/dist/clients/word-index-store.js +463 -0
  269. package/dist/clients/word-index.js +815 -125
  270. package/dist/clients/workspace-topology.js +25 -1
  271. package/dist/clients/zizmor-config.js +29 -6
  272. package/dist/index.js +68577 -53950
  273. package/dist/mcp/analyze-cli.js +3 -1
  274. package/dist/mcp/build-staleness.js +8 -7
  275. package/dist/mcp/cli.js +3 -1
  276. package/dist/mcp/server.js +133 -6
  277. package/dist/scripts/lib/process-scan.mjs +583 -0
  278. package/dist/scripts/lib/skills-predicate.mjs +129 -0
  279. package/dist/tools/effective-config.js +89 -0
  280. package/dist/tools/lens-diagnostics.js +99 -24
  281. package/dist/tools/lsp-diagnostics.js +81 -85
  282. package/dist/tools/lsp-navigation.js +40 -17
  283. package/dist/tools/shared.js +0 -1
  284. package/docs/agent-guide.md +68 -3
  285. package/docs/ast-grep_rules_catalog.md +41 -10
  286. package/docs/astplayground.md +26 -17
  287. package/docs/configuration.md +222 -0
  288. package/docs/dependencies.md +3 -3
  289. package/docs/features.md +44 -5
  290. package/docs/language-coverage.md +2 -2
  291. package/docs/pi-lens-fixer.md +25 -0
  292. package/docs/pi-lens-investigator.md +25 -0
  293. package/docs/pi-lens-reviewer.md +27 -0
  294. package/docs/pi-lens-subagent.md +38 -0
  295. package/docs/pi-lens-warden.md +55 -0
  296. package/docs/public-api-stability.md +359 -0
  297. package/docs/release-qa-baseline.md +182 -0
  298. package/docs/subagent-compat.md +110 -30
  299. package/docs/tree-sitter_rules_catalog.md +2 -2
  300. package/package.json +17 -21
  301. package/rules/ast-grep-rules/rule-tests/c-no-malloc-free-test.yml +20 -0
  302. package/rules/ast-grep-rules/rule-tests/cpp-no-malloc-free-test.yml +25 -0
  303. package/rules/ast-grep-rules/rule-tests/cpp-no-printf-test.yml +19 -0
  304. package/rules/ast-grep-rules/rule-tests/go-no-fmt-println-test.yml +19 -0
  305. package/rules/ast-grep-rules/rule-tests/go-no-panic-in-lib-test.yml +17 -0
  306. package/rules/ast-grep-rules/rule-tests/go-no-underscore-func-name-test.yml +18 -0
  307. package/rules/ast-grep-rules/rule-tests/go-prefer-errors-is-test.yml +15 -0
  308. package/rules/ast-grep-rules/rule-tests/go-prefer-string-builder-test.yml +21 -0
  309. package/rules/ast-grep-rules/rule-tests/hyphenated-svg-attribute-test.yml +11 -0
  310. package/rules/ast-grep-rules/rule-tests/kotlin-no-lateinit-test.yml +17 -0
  311. package/rules/ast-grep-rules/rule-tests/kotlin-no-nullable-boolean-test.yml +15 -0
  312. package/rules/ast-grep-rules/rule-tests/kotlin-no-println-test.yml +19 -0
  313. package/rules/ast-grep-rules/rule-tests/kotlin-no-unnecessary-let-test.yml +7 -0
  314. package/rules/ast-grep-rules/rule-tests/kotlin-no-unsafe-cast-test.yml +7 -0
  315. package/rules/ast-grep-rules/rule-tests/kotlin-no-var-test.yml +7 -0
  316. package/rules/ast-grep-rules/rule-tests/kotlin-prefer-data-class-test.yml +25 -0
  317. package/rules/ast-grep-rules/rule-tests/kotlin-prefer-is-empty-test.yml +11 -0
  318. package/rules/ast-grep-rules/rule-tests/no-important-test.yml +21 -0
  319. package/rules/ast-grep-rules/rule-tests/no-raw-types-test.yml +30 -0
  320. package/rules/ast-grep-rules/rule-tests/no-string-concat-in-loop-test.yml +100 -0
  321. package/rules/ast-grep-rules/rule-tests/no-system-out-println-test.yml +7 -0
  322. package/rules/ast-grep-rules/rule-tests/prefer-string-is-empty-test.yml +7 -0
  323. package/rules/ast-grep-rules/rule-tests/python-optional-type-test.yml +9 -0
  324. package/rules/ast-grep-rules/rule-tests/reducible-list-comprehension-test.yml +11 -0
  325. package/rules/ast-grep-rules/rule-tests/ruby-symbol-to-proc-candidate-test.yml +10 -0
  326. package/rules/ast-grep-rules/rule-tests/rust-no-chars-enumerate-test.yml +7 -0
  327. package/rules/ast-grep-rules/rules/c-no-malloc-free.yml +39 -0
  328. package/rules/ast-grep-rules/rules/cpp-no-malloc-free.yml +45 -0
  329. package/rules/ast-grep-rules/rules/cpp-no-printf.yml +41 -0
  330. package/rules/ast-grep-rules/rules/go-no-fmt-println.yml +43 -0
  331. package/rules/ast-grep-rules/rules/go-no-panic-in-lib.yml +41 -0
  332. package/rules/ast-grep-rules/rules/go-no-underscore-func-name.yml +35 -0
  333. package/rules/ast-grep-rules/rules/go-prefer-errors-is.yml +40 -0
  334. package/rules/ast-grep-rules/rules/go-prefer-string-builder.yml +43 -0
  335. package/rules/ast-grep-rules/rules/hyphenated-svg-attribute.yml +32 -0
  336. package/rules/ast-grep-rules/rules/kotlin-no-lateinit.yml +37 -0
  337. package/rules/ast-grep-rules/rules/kotlin-no-nullable-boolean.yml +33 -0
  338. package/rules/ast-grep-rules/rules/kotlin-no-println.yml +42 -0
  339. package/rules/ast-grep-rules/rules/kotlin-no-unnecessary-let.yml +28 -0
  340. package/rules/ast-grep-rules/rules/kotlin-no-unsafe-cast.yml +28 -0
  341. package/rules/ast-grep-rules/rules/kotlin-no-var.yml +35 -0
  342. package/rules/ast-grep-rules/rules/kotlin-prefer-data-class.yml +66 -0
  343. package/rules/ast-grep-rules/rules/kotlin-prefer-is-empty.yml +35 -0
  344. package/rules/ast-grep-rules/rules/no-important.yml +21 -0
  345. package/rules/ast-grep-rules/rules/no-raw-types.yml +33 -0
  346. package/rules/ast-grep-rules/rules/no-string-concat-in-loop.yml +90 -0
  347. package/rules/ast-grep-rules/rules/no-system-out-println.yml +16 -0
  348. package/rules/ast-grep-rules/rules/prefer-string-is-empty.yml +11 -0
  349. package/rules/ast-grep-rules/rules/python-optional-type.yml +22 -0
  350. package/rules/ast-grep-rules/rules/reducible-list-comprehension.yml +29 -0
  351. package/rules/ast-grep-rules/rules/ruby-symbol-to-proc-candidate.yml +25 -0
  352. package/rules/ast-grep-rules/rules/rust-no-chars-enumerate.yml +19 -0
  353. package/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  354. package/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  355. package/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  356. package/scripts/analyze-pi-lens-logs.mjs +192 -1
  357. package/scripts/install-selftest.mjs +58 -2
  358. package/scripts/lib/skills-predicate.mjs +129 -0
  359. package/scripts/rpc-load-check.mjs +3 -1
@@ -197,7 +197,6 @@ const biomeCheckJsonRunner = {
197
197
  id: "biome-check-json",
198
198
  appliesTo: ["jsts"],
199
199
  priority: PRIORITY.FORMAT_AND_LINT_PRIMARY,
200
- enabledByDefault: true,
201
200
  async run(ctx) {
202
201
  const cwd = ctx.cwd || path.dirname(ctx.filePath);
203
202
  const policy = getJstsLintPolicyForCwd(cwd);
@@ -175,7 +175,6 @@ const cppCheckRunner = {
175
175
  id: "cpp-check",
176
176
  appliesTo: ["cxx"],
177
177
  priority: PRIORITY.GENERAL_ANALYSIS,
178
- enabledByDefault: true,
179
178
  skipTestFiles: false,
180
179
  async run(ctx) {
181
180
  const cwd = ctx.cwd || process.cwd();
@@ -48,7 +48,6 @@ const credoRunner = {
48
48
  id: "credo",
49
49
  appliesTo: ["elixir"],
50
50
  priority: PRIORITY.GENERAL_ANALYSIS,
51
- enabledByDefault: true,
52
51
  skipTestFiles: false,
53
52
  async run(ctx) {
54
53
  const cwd = ctx.cwd || process.cwd();
@@ -278,7 +278,6 @@ const cueVetRunner = {
278
278
  id: "cue-vet",
279
279
  appliesTo: ["cue"],
280
280
  priority: PRIORITY.GENERAL_ANALYSIS,
281
- enabledByDefault: true,
282
281
  skipTestFiles: false,
283
282
  timeoutMs: 30_000,
284
283
  async run(ctx) {
@@ -145,7 +145,6 @@ const dartAnalyzeRunner = {
145
145
  id: "dart-analyze",
146
146
  appliesTo: ["dart"],
147
147
  priority: PRIORITY.GENERAL_ANALYSIS,
148
- enabledByDefault: true,
149
148
  skipTestFiles: false,
150
149
  async run(ctx) {
151
150
  const cwd = ctx.cwd || process.cwd();
@@ -126,7 +126,6 @@ const detektRunner = {
126
126
  id: "detekt",
127
127
  appliesTo: ["kotlin"],
128
128
  priority: PRIORITY.GENERAL_ANALYSIS,
129
- enabledByDefault: true,
130
129
  timeoutMs: 90_000,
131
130
  skipTestFiles: false,
132
131
  async run(ctx) {
@@ -115,7 +115,6 @@ const dotnetBuildRunner = {
115
115
  id: "dotnet-build",
116
116
  appliesTo: ["csharp"],
117
117
  priority: PRIORITY.GENERAL_ANALYSIS,
118
- enabledByDefault: true,
119
118
  timeoutMs: 90_000,
120
119
  skipTestFiles: false,
121
120
  async run(ctx) {
@@ -139,7 +139,6 @@ const elixirCheckRunner = {
139
139
  id: "elixir-check",
140
140
  appliesTo: ["elixir"],
141
141
  priority: PRIORITY.GENERAL_ANALYSIS,
142
- enabledByDefault: true,
143
142
  skipTestFiles: false,
144
143
  async run(ctx) {
145
144
  const cwd = ctx.cwd || process.cwd();
@@ -24,7 +24,11 @@ function makeEslintProbe(cmd) {
24
24
  return createCwdCachedProbe((cwd) => safeSpawnAsync(cmd, ["--version"], {
25
25
  timeout: ESLINT_PROBE_BUDGET_MS,
26
26
  cwd,
27
- }), { tool: "eslint", budgetMs: ESLINT_PROBE_BUDGET_MS });
27
+ }), {
28
+ tool: "eslint",
29
+ budgetMs: ESLINT_PROBE_BUDGET_MS,
30
+ flightKeyComponent: cmd,
31
+ });
28
32
  }
29
33
  const eslintAvailabilityByCmd = new Map();
30
34
  function getEslintProbe(cmd) {
@@ -74,7 +78,6 @@ const eslintRunner = {
74
78
  id: "eslint",
75
79
  appliesTo: ["jsts"],
76
80
  priority: PRIORITY.LINT_SECONDARY,
77
- enabledByDefault: true,
78
81
  async run(ctx) {
79
82
  const cwd = ctx.cwd || process.cwd();
80
83
  const userHasConfig = hasEslintConfig(cwd);
@@ -13,7 +13,6 @@ const factRulesRunner = {
13
13
  id: "fact-rules",
14
14
  appliesTo: ["jsts", "python", "go", "rust", "ruby", "shell", "cmake"],
15
15
  priority: PRIORITY.GENERAL_ANALYSIS + 1,
16
- enabledByDefault: true,
17
16
  async run(ctx) {
18
17
  const diagnostics = evaluateRules(ctx);
19
18
  if (diagnostics.length === 0) {
@@ -7,7 +7,6 @@ const fishIndentRunner = {
7
7
  id: "fish-indent",
8
8
  appliesTo: ["fish"],
9
9
  priority: PRIORITY.FORMAT_AND_LINT_PRIMARY,
10
- enabledByDefault: true,
11
10
  skipTestFiles: false,
12
11
  async run(ctx) {
13
12
  const cwd = ctx.cwd || process.cwd();
@@ -39,7 +39,6 @@ const gleamCheckRunner = {
39
39
  id: "gleam-check",
40
40
  appliesTo: ["gleam"],
41
41
  priority: PRIORITY.GENERAL_ANALYSIS,
42
- enabledByDefault: true,
43
42
  skipTestFiles: false,
44
43
  async run(ctx) {
45
44
  const cwd = ctx.cwd || process.cwd();
@@ -4,18 +4,16 @@
4
4
  * Runs `go vet` for Go files to catch common mistakes.
5
5
  */
6
6
  import { relative, resolve, sep, posix } from "node:path";
7
- import { GoClient } from "../../go-client.js";
7
+ import { goClient } from "../../go-client.js";
8
8
  import { safeSpawnAsync } from "../../safe-spawn.js";
9
9
  import { stripAnsi } from "../../sanitize.js";
10
10
  import { skipUnlessToolRan } from "./utils/tool-failure.js";
11
11
  import { parseGoVetOutput } from "./utils/diagnostic-parsers.js";
12
12
  import { PRIORITY } from "../priorities.js";
13
- const goClient = new GoClient();
14
13
  const goVetRunner = {
15
14
  id: "go-vet",
16
15
  appliesTo: ["go"],
17
16
  priority: PRIORITY.SPECIALIZED_ANALYSIS,
18
- enabledByDefault: true,
19
17
  timeoutMs: 40_000,
20
18
  async run(ctx) {
21
19
  // Resolve go path using platform-aware lookup (handles system install paths on Windows)
@@ -78,7 +78,6 @@ const golangciRunner = {
78
78
  id: "golangci-lint",
79
79
  appliesTo: ["go"],
80
80
  priority: PRIORITY.GENERAL_ANALYSIS,
81
- enabledByDefault: true,
82
81
  timeoutMs: 90_000,
83
82
  async run(ctx) {
84
83
  const cwd = ctx.cwd || process.cwd();
@@ -35,7 +35,6 @@ const hadolintRunner = {
35
35
  id: "hadolint",
36
36
  appliesTo: ["docker"],
37
37
  priority: PRIORITY.GENERAL_ANALYSIS,
38
- enabledByDefault: true,
39
38
  skipTestFiles: false,
40
39
  async run(ctx) {
41
40
  const cwd = ctx.cwd || process.cwd();
@@ -2,12 +2,18 @@ import * as path from "node:path";
2
2
  import { PathKeyedMap } from "../../path-keyed-map.js";
3
3
  import { normalizeMapKey } from "../../path-utils.js";
4
4
  import { safeSpawnAsync } from "../../safe-spawn.js";
5
+ import { truncatedByOutputCap } from "../../spawn-output-cap.js";
5
6
  import { findNearestDirWithMarker } from "../../workspace-topology.js";
6
7
  import { PRIORITY } from "../priorities.js";
7
8
  import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
8
9
  const helm = createAvailabilityChecker("helm", ".exe");
9
10
  const inFlightByChartRoot = new PathKeyedMap(normalizeMapKey);
10
11
  const HELM_LINT_TIMEOUT_MS = 30_000;
12
+ // `helm lint` prints one `[LEVEL]` line per finding for one chart, so nothing
13
+ // legitimate approaches 8 MiB: this bounds a wedged or runaway helm rather than
14
+ // real lint output, and it is what makes `outputTruncated` reachable for this
15
+ // runner at all (#2100). Same value as the sibling report caps.
16
+ const MAX_HELM_LINT_OUTPUT_BYTES = 8 * 1024 * 1024;
11
17
  const SKIPPED = {
12
18
  status: "skipped",
13
19
  diagnostics: [],
@@ -93,7 +99,17 @@ async function lintChart(chartRoot, cwd) {
93
99
  timeout: HELM_LINT_TIMEOUT_MS,
94
100
  deadlineAt: Date.now() + HELM_LINT_TIMEOUT_MS,
95
101
  resourceLabel: "helm-lint",
102
+ maxOutputBytes: MAX_HELM_LINT_OUTPUT_BYTES,
96
103
  });
104
+ // FIRST (#2100): the cap kill is OUR SIGTERM, so it also sets
105
+ // `spawnFailure.kind === "killed"` and `failure === "signal"`. Read after
106
+ // those two, a truncated lint was reported as a server_error — helm blamed
107
+ // for output we threw away. A timed-out or aborted run carries
108
+ // `outputTruncated` too, and `truncatedByOutputCap` leaves those to the
109
+ // typed-failure block below so they keep reporting timeout/aborted.
110
+ if (truncatedByOutputCap(result)) {
111
+ return failedResult("parser_error", "helm lint output was truncated before parsing completed");
112
+ }
97
113
  const output = [result.stdout, result.stderr].filter(Boolean).join("\n");
98
114
  const diagnostics = parseHelmLintOutput(output, chartRoot);
99
115
  const typedFailure = result.spawnFailure?.kind;
@@ -110,9 +126,6 @@ async function lintChart(chartRoot, cwd) {
110
126
  if (result.failure) {
111
127
  return failedResult(result.failure === "timeout" ? "timeout" : result.failure, result.error?.message || `helm lint ${result.failure}`);
112
128
  }
113
- if (result.outputTruncated) {
114
- return failedResult("parser_error", "helm lint output was truncated before parsing completed");
115
- }
116
129
  const hasErrorDiagnostic = diagnostics.some((diagnostic) => diagnostic.severity === "error");
117
130
  if (result.status !== 0 && !hasErrorDiagnostic) {
118
131
  return failedResult(output.trim() ? "parser_error" : "server_error", output.trim() || "helm lint exited without an error diagnostic");
@@ -131,7 +144,6 @@ const helmLintRunner = {
131
144
  id: "helm-lint",
132
145
  appliesTo: ["yaml", "helm-template"],
133
146
  priority: PRIORITY.GENERAL_ANALYSIS,
134
- enabledByDefault: true,
135
147
  skipTestFiles: false,
136
148
  timeoutMs: 35_000,
137
149
  async run(ctx) {
@@ -78,6 +78,7 @@ import { normalizeMapKey } from "../../path-utils.js";
78
78
  import { loadPiLensProjectConfig } from "../../project-lens-config.js";
79
79
  import { getProjectTrustState, projectTrustDenialReason, } from "../../project-trust.js";
80
80
  import { safeSpawnAsync } from "../../safe-spawn.js";
81
+ import { killedForOutputCap, truncatedByOutputCap, } from "../../spawn-output-cap.js";
81
82
  import { isTrivyEnabled, resolveSeverityFloor } from "../../trivy-client.js";
82
83
  import { findNearestDirWithMarker } from "../../workspace-topology.js";
83
84
  import { PRIORITY } from "../priorities.js";
@@ -100,6 +101,15 @@ const TRIVY_TIMEOUT_MS = 45_000;
100
101
  const MAX_RENDERED_FILES = 400;
101
102
  /** Bytes of trivy report we retain. Truncation is reported, never parsed past. */
102
103
  const MAX_REPORT_BYTES = 8 * 1024 * 1024;
104
+ /**
105
+ * Bytes of `helm template` output we retain. The manifests go to `--output-dir`,
106
+ * so stdout is one short "wrote <path>" line per file — bounded near
107
+ * {@link MAX_RENDERED_FILES} — and stderr carries template errors. 8 MiB is
108
+ * therefore a blast-radius bound on a chart that loops or a helm that wedges,
109
+ * not a working limit, and it is what makes `render.outputTruncated` reachable
110
+ * at all (#2100).
111
+ */
112
+ const MAX_RENDER_OUTPUT_BYTES = 8 * 1024 * 1024;
103
113
  const SKIPPED = {
104
114
  status: "skipped",
105
115
  diagnostics: [],
@@ -661,6 +671,16 @@ export async function runIacPass(options) {
661
671
  // hold in memory rather than trusting the chart's size (shape 9).
662
672
  maxOutputBytes: MAX_REPORT_BYTES,
663
673
  });
674
+ // FIRST (#2100): hitting the cap makes safe-spawn SIGTERM trivy, so a
675
+ // truncated report also arrives as a killed spawn with a null status. Read
676
+ // after the gate below, this said "the IaC pass failed" when what actually
677
+ // happened is that WE stopped reading. A timed-out or aborted scan carries
678
+ // the flag too and stays with the failure gate, which names its real cause.
679
+ if (truncatedByOutputCap(scan)) {
680
+ return [
681
+ coverageGap(chartRoot, "iac-report-unreadable", `Rendered-manifest IaC checks did not run: the trivy report exceeded ${MAX_REPORT_BYTES} bytes and was truncated before parsing.`),
682
+ ];
683
+ }
664
684
  const scanFailure = spawnFailureKind(scan);
665
685
  // `trivy config` is not given `--exit-code`, so it exits 0 whenever it
666
686
  // completed. ANY nonzero status is therefore a real error, with or without
@@ -673,11 +693,6 @@ export async function runIacPass(options) {
673
693
  coverageGap(chartRoot, "iac-pass-failed", `Rendered-manifest IaC checks failed to complete (${why}): ${detail}`),
674
694
  ];
675
695
  }
676
- if (scan.outputTruncated) {
677
- return [
678
- coverageGap(chartRoot, "iac-report-unreadable", `Rendered-manifest IaC checks did not run: the trivy report exceeded ${MAX_REPORT_BYTES} bytes and was truncated before parsing.`),
679
- ];
680
- }
681
696
  const sourceByRendered = new PathKeyedMap(normalizeMapKey);
682
697
  for (const manifest of options.manifests) {
683
698
  sourceByRendered.set(manifest.renderedPath, {
@@ -754,7 +769,30 @@ async function renderAndValidate(chartRoot, cwd, filePath) {
754
769
  timeout: RENDER_TIMEOUT_MS,
755
770
  deadlineAt: Date.now() + RENDER_TIMEOUT_MS,
756
771
  resourceLabel: "helm-render",
772
+ maxOutputBytes: MAX_RENDER_OUTPUT_BYTES,
757
773
  });
774
+ const renderOutput = [render.stdout, render.stderr]
775
+ .filter(Boolean)
776
+ .join("\n");
777
+ // `killedForOutputCap` is platform-neutral. POSIX usually reports SIGTERM,
778
+ // but Windows reports status 1 with no failure or signal. A timed-out or
779
+ // aborted render retains its own classification.
780
+ const renderCapped = truncatedByOutputCap(render);
781
+ if (killedForOutputCap(render)) {
782
+ // helm was cut off mid-render, so the scratch tree is a PREFIX —
783
+ // reading it back would report the manifests we never saw as clean
784
+ // (shape 10). Not parsed as a chart failure either: helm never reported
785
+ // a verdict, and `parseHelmTemplateFailure` synthesizes a BLOCKING
786
+ // "helm template failed" finding when it sees no `Error:` line, which
787
+ // would blame the chart for our own cap (the #1487 inversion).
788
+ logRenderPass(filePath, chartRoot, startedAt, {
789
+ outcome: "render-truncated",
790
+ diagnostics: 1,
791
+ });
792
+ return summarize([
793
+ coverageGap(chartRoot, "render-truncated", `helm template produced more than ${MAX_RENDER_OUTPUT_BYTES} bytes and was stopped mid-render, so the rendered manifests are UNCHECKED rather than clean.`),
794
+ ]);
795
+ }
758
796
  const startupFailure = spawnFailureKind(render);
759
797
  if (startupFailure) {
760
798
  // The runner never got a verdict about the chart. Reporting this as a
@@ -766,9 +804,6 @@ async function renderAndValidate(chartRoot, cwd, filePath) {
766
804
  });
767
805
  return failedResult(startupFailure, render.error?.message || `helm template ${startupFailure}`);
768
806
  }
769
- const renderOutput = [render.stdout, render.stderr]
770
- .filter(Boolean)
771
- .join("\n");
772
807
  if (render.status !== 0) {
773
808
  // Before blaming the chart: did helm reject OUR OWN command line? An old
774
809
  // helm (v2 has no `helm template --output-dir`) or a wrapper shim exits
@@ -785,7 +820,9 @@ async function renderAndValidate(chartRoot, cwd, filePath) {
785
820
  }
786
821
  // A failed RENDER is a real diagnostic: the chart cannot be installed.
787
822
  const diagnostics = parseHelmTemplateFailure(renderOutput, chartRoot);
788
- if (render.outputTruncated) {
823
+ if (renderCapped) {
824
+ // helm reported this failure itself, but we kept only a prefix of
825
+ // what it said, so the report above may be missing errors.
789
826
  diagnostics.push(coverageGap(chartRoot, "render-truncated", "helm template output was truncated, so this failure report may be incomplete."));
790
827
  }
791
828
  logRenderPass(filePath, chartRoot, startedAt, {
@@ -804,6 +841,14 @@ async function renderAndValidate(chartRoot, cwd, filePath) {
804
841
  sourceMapped: manifest.sourceMapped,
805
842
  }));
806
843
  }
844
+ if (renderCapped) {
845
+ // helm exited 0, so `--output-dir` holds the COMPLETE tree and every
846
+ // manifest above was validated from disk — the cap only cost us the
847
+ // "wrote <path>" lines on stdout, which this runner never parses.
848
+ // Recorded rather than silent (shape 8), but deliberately not an
849
+ // UNCHECKED claim: nothing was skipped (review F1).
850
+ diagnostics.push(coverageGap(chartRoot, "render-output-truncated", `helm template printed more than ${MAX_RENDER_OUTPUT_BYTES} bytes and its stdout was truncated. The rendered manifests were still validated from disk; only helm's own progress output was lost.`));
851
+ }
807
852
  if (tree.truncated) {
808
853
  // A partly-validated chart reported as clean is the shape-10 trap.
809
854
  diagnostics.push(coverageGap(chartRoot, "render-truncated", `This chart rendered more than ${MAX_RENDERED_FILES} manifests; only the first ${MAX_RENDERED_FILES} were validated, so the rest are UNCHECKED rather than clean.`));
@@ -848,7 +893,6 @@ const helmRenderRunner = {
848
893
  id: "helm-render",
849
894
  appliesTo: ["yaml", "helm-template"],
850
895
  priority: PRIORITY.GENERAL_ANALYSIS,
851
- enabledByDefault: true,
852
896
  skipTestFiles: false,
853
897
  timeoutMs: RENDER_TIMEOUT_MS + TRIVY_TIMEOUT_MS + 10_000,
854
898
  async run(ctx) {
@@ -47,7 +47,6 @@ const htmlhintRunner = {
47
47
  id: "htmlhint",
48
48
  appliesTo: ["html"],
49
49
  priority: PRIORITY.GENERAL_ANALYSIS,
50
- enabledByDefault: true,
51
50
  skipTestFiles: false,
52
51
  async run(ctx) {
53
52
  const cwd = ctx.cwd || process.cwd();
@@ -57,9 +57,7 @@ export function registerDefaultRunners(registry) {
57
57
  registry.register(lspRunner); // Unified LSP type-checking for all languages (priority 4)
58
58
  registry.register(pyrightRunner); // Python type-checking (priority 5) - fallback when --lens-lsp disabled
59
59
  registry.register(biomeCheckJsonRunner); // Biome check with JSON output for diagnostic capture (priority 9)
60
- // DISABLED in post-write dispatch - ast-grep-napi can crash. Runs in the
61
- // project-wide pass via lens_diagnostics mode=full (refreshRunners) instead.
62
- registry.register(astGrepNapiRunner); // TS/JS structural analysis via NAPI (priority 15, post-write disabled)
60
+ registry.register(astGrepNapiRunner); // JS/TS, CSS, and HTML structural analysis via NAPI (priority 15)
63
61
  registry.register(treeSitterRunner); // Tree-sitter structural analysis (priority 14)
64
62
  registry.register(ruffRunner); // Python linting (priority 10)
65
63
  registry.register(shellcheckRunner); // Shell script linting (priority 20)
@@ -39,7 +39,6 @@ const javacRunner = {
39
39
  id: "javac",
40
40
  appliesTo: ["java"],
41
41
  priority: PRIORITY.GENERAL_ANALYSIS,
42
- enabledByDefault: true,
43
42
  skipTestFiles: false,
44
43
  async run(ctx) {
45
44
  const cwd = ctx.cwd || process.cwd();
@@ -56,7 +56,6 @@ const ktlintRunner = {
56
56
  id: "ktlint",
57
57
  appliesTo: ["kotlin"],
58
58
  priority: PRIORITY.FORMAT_AND_LINT_PRIMARY,
59
- enabledByDefault: true,
60
59
  skipTestFiles: false,
61
60
  async run(ctx) {
62
61
  const cwd = ctx.cwd || process.cwd();
@@ -15,6 +15,7 @@ import { logExtension } from "../../extension-log.js";
15
15
  import { getLspCapableKinds } from "../../language-policy.js";
16
16
  import { touchCoverageGap } from "../../lsp/diagnostic-binding.js";
17
17
  import { getLSPService } from "../../lsp/index.js";
18
+ import { LSP_SERVERS } from "../../lsp/server.js";
18
19
  import { RUNTIME_CONFIG } from "../../runtime-config.js";
19
20
  import { PRIORITY } from "../priorities.js";
20
21
  import { resolveRunnerPath } from "../runner-context.js";
@@ -32,8 +33,42 @@ const LSP_SPAWN_BUDGET_MS = RUNTIME_CONFIG.pipeline.lspSpawnBudgetMs;
32
33
  // can't dominate the per-edit pipeline budget. Diagnostics that arrive
33
34
  // after the cap still land in the client's cache and surface on the
34
35
  // next edit. Overridable via PI_LENS_LSP_DIAGNOSTICS_MAX_WAIT_MS.
35
- const LSP_DIAGNOSTICS_WAIT_MS = 2500;
36
+ export const LSP_DIAGNOSTICS_WAIT_MS = 2500;
36
37
  const MAX_CODE_ACTION_TITLES = 3;
38
+ /**
39
+ * Fixed margin above the worst-case cold-spawn-plus-diagnostics wait, so
40
+ * scheduling jitter alone can't reopen the F2 race below.
41
+ */
42
+ const LSP_COLD_SPAWN_MARGIN_MS = 5_000;
43
+ /**
44
+ * This runner's own dispatch wall-clock budget. `runRunner` in
45
+ * `dispatcher.ts` races `runner.timeoutMs ?? RUNNER_TIMEOUT_MS` (30s)
46
+ * against `runner.run()` for the WHOLE call — reading the file, waiting for
47
+ * a client (bounded by `LSP_SPAWN_BUDGET_MS`, raised per-server via
48
+ * `LSPServerInfo.clientWaitTimeoutMs`), then waiting for diagnostics
49
+ * (`LSP_DIAGNOSTICS_WAIT_MS`).
50
+ *
51
+ * Left undeclared, this runner inherited the shared 30s default. A server
52
+ * whose `clientWaitTimeoutMs` exceeds that — Prisma's 40s, Vue's 30s
53
+ * (#2169, #2176) — could never actually use its own raised wait: the outer
54
+ * race always fired first, at 30s, so raising a server's floor without also
55
+ * raising this ceiling was a no-op on the real dispatch path (fix-round F2,
56
+ * #2233). Derive the ceiling from the registry instead of a second
57
+ * hand-picked literal, so the next slow server's `clientWaitTimeoutMs` keeps
58
+ * this budget correct automatically: the highest declared
59
+ * `clientWaitTimeoutMs` (or the shared 5s default, whichever is larger),
60
+ * plus the diagnostics wait that still runs after a cold spawn succeeds,
61
+ * plus a fixed margin.
62
+ *
63
+ * This raises the failure-detection latency for every OTHER language's
64
+ * hung-server case too (30s -> the value below), not just the five servers
65
+ * that need the headroom — the tradeoff is stated in PR #2233's body. It
66
+ * remains well inside the spread other dispatch runners already use
67
+ * (pyright 75s, golangci-lint/dotnet-build/rust-clippy 90s).
68
+ */
69
+ export const LSP_RUNNER_TIMEOUT_MS = Math.max(LSP_SPAWN_BUDGET_MS, ...LSP_SERVERS.map((server) => server.clientWaitTimeoutMs ?? 0)) +
70
+ LSP_DIAGNOSTICS_WAIT_MS +
71
+ LSP_COLD_SPAWN_MARGIN_MS;
37
72
  function normalizeActionTitle(title) {
38
73
  return title.replace(/\s+/g, " ").trim();
39
74
  }
@@ -58,7 +93,7 @@ const lspRunner = {
58
93
  // is now the only step this seam needs (#1545).
59
94
  appliesTo: getLspCapableKinds(),
60
95
  priority: PRIORITY.LSP_PRIMARY,
61
- enabledByDefault: true,
96
+ timeoutMs: LSP_RUNNER_TIMEOUT_MS,
62
97
  async run(ctx) {
63
98
  const diagnosticPath = resolveRunnerPath(ctx.cwd, ctx.filePath);
64
99
  // Only run if LSP is not disabled via --no-lsp
@@ -5,7 +5,10 @@ import { createAvailabilityChecker, resolveToolCommandWithInstallFallback, } fro
5
5
  import { parseToolRun } from "./utils/tool-failure.js";
6
6
  import { isInSpawnTimeoutCooldown, noteSpawnTimeout, } from "../../spawn-timeout-cooldown.js";
7
7
  import { finishParsedRun } from "./utils/tool-failure.js";
8
- const markdownlint = createAvailabilityChecker("markdownlint-cli2", ".cmd");
8
+ const markdownlint = createAvailabilityChecker("markdownlint-cli2", ".cmd", [
9
+ "--no-globs",
10
+ "-",
11
+ ]);
9
12
  // markdownlint-cli2 exit codes (its README's "Exit Codes" section): 0 = no
10
13
  // findings, 1 = lint findings, 2 = unexpected error (unreadable config, bad
11
14
  // glob, crash). Only 2 is a rejected invocation.
@@ -91,7 +94,6 @@ const markdownlintRunner = {
91
94
  id: "markdownlint",
92
95
  appliesTo: ["markdown"],
93
96
  priority: PRIORITY.DOC_QUALITY,
94
- enabledByDefault: true,
95
97
  skipTestFiles: false,
96
98
  async run(ctx) {
97
99
  const cwd = ctx.cwd || process.cwd();
@@ -59,7 +59,6 @@ const mypyRunner = {
59
59
  id: "mypy",
60
60
  appliesTo: ["python"],
61
61
  priority: PRIORITY.GENERAL_ANALYSIS,
62
- enabledByDefault: true,
63
62
  skipTestFiles: false,
64
63
  async run(ctx) {
65
64
  const cwd = ctx.cwd || process.cwd();
@@ -6,14 +6,18 @@
6
6
  *
7
7
  * Requires: oxlint (npm install -g oxlint)
8
8
  */
9
- import * as fs from "node:fs";
10
- import * as path from "node:path";
11
- import { walkUpDirs } from "../../path-utils.js";
9
+ import { findLocalBinUpwards } from "../../package-manager.js";
12
10
  import { safeSpawnAsync } from "../../safe-spawn.js";
11
+ import { truncatedByOutputCap } from "../../spawn-output-cap.js";
13
12
  import { getJstsLintPolicyForCwd, hasVitePlusConfig, } from "../../tool-policy.js";
14
13
  import { PRIORITY } from "../priorities.js";
15
14
  import { resolveToolCommand, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
16
15
  import { finishParsedRun, parseToolRun } from "./utils/tool-failure.js";
16
+ // One file's JSON report. Nothing legitimate approaches 8 MiB here, so this is
17
+ // a blast-radius bound on a runaway or wedged oxlint rather than a working
18
+ // limit — the same value MAX_SG_OUTPUT_BYTES and MAX_REPORT_BYTES use, and what
19
+ // makes `outputTruncated` reachable for this runner at all (#2100).
20
+ const MAX_OXLINT_OUTPUT_BYTES = 8 * 1024 * 1024;
17
21
  const OXLINT_NO_FILES = Symbol("oxlint-no-files");
18
22
  const OXLINT_NO_FILES_UNCONFIRMED = Symbol("oxlint-no-files-unconfirmed");
19
23
  const OXLINT_NO_FILES_BANNER = "No files found to lint. Please check your paths and ignore patterns.";
@@ -24,21 +28,19 @@ const OXLINT_NO_FILES_REPORT_KEYS = new Set([
24
28
  "threads_count",
25
29
  "start_time",
26
30
  ]);
31
+ /**
32
+ * Vite+'s `vp` from the project's own `node_modules/.bin`.
33
+ *
34
+ * Delegates to THE shared bin walker (#2544 review F1). This was a private,
35
+ * UNCEILINGED copy of that walk, while `formatters.ts`'s `oxfmtFormatter`
36
+ * resolved the SAME binary — `vp` — through the ceilinged `findInNodeModules`:
37
+ * one `vp` resolver stopped at `$HOME` and the other climbed past it to the
38
+ * filesystem root, so which policy applied depended on whether the format path
39
+ * or the lint path asked. A `node_modules/.bin/vp` at or above `$HOME` can
40
+ * never be this project's own Vite+ install (#2514).
41
+ */
27
42
  function resolveLocalVp(cwd) {
28
- const isWin = process.platform === "win32";
29
- for (const dir of walkUpDirs(cwd)) {
30
- const candidates = isWin
31
- ? [
32
- path.join(dir, "node_modules", ".bin", "vp.cmd"),
33
- path.join(dir, "node_modules", ".bin", "vp"),
34
- ]
35
- : [path.join(dir, "node_modules", ".bin", "vp")];
36
- for (const candidate of candidates) {
37
- if (fs.existsSync(candidate))
38
- return candidate;
39
- }
40
- }
41
- return null;
43
+ return findLocalBinUpwards("vp", cwd) ?? null;
42
44
  }
43
45
  async function resolveVitePlusCommand(cwd) {
44
46
  const local = resolveLocalVp(cwd);
@@ -54,7 +56,6 @@ const oxlintRunner = {
54
56
  id: "oxlint",
55
57
  appliesTo: ["jsts"],
56
58
  priority: PRIORITY.LINT_SECONDARY,
57
- enabledByDefault: true,
58
59
  skipTestFiles: false,
59
60
  async run(ctx) {
60
61
  const cwd = ctx.cwd || process.cwd();
@@ -86,6 +87,7 @@ const oxlintRunner = {
86
87
  // Run oxlint (or Vite+'s vp lint wrapper) on the file.
87
88
  const result = await safeSpawnAsync(cmd, args, {
88
89
  timeout: 30000,
90
+ maxOutputBytes: MAX_OXLINT_OUTPUT_BYTES,
89
91
  });
90
92
  // Oxlint exits 0 whenever nothing at ERROR severity was found — that
91
93
  // includes a run that found only warnings, its own default severity
@@ -194,16 +196,16 @@ const oxlintRunner = {
194
196
  };
195
197
  function oxlintProcessState(result) {
196
198
  const failureKind = result.spawnFailure?.kind;
197
- if (result.signal || result.failure === "signal")
198
- return "signal";
199
+ if (truncatedByOutputCap(result))
200
+ return "truncated";
199
201
  if (result.failure === "timeout" || failureKind === "timeout")
200
202
  return "timeout";
201
203
  if (result.failure === "aborted" || failureKind === "killed")
202
204
  return "killed";
205
+ if (result.signal || result.failure === "signal")
206
+ return "signal";
203
207
  if (result.failure === "spawn" || result.error)
204
208
  return "spawn";
205
- if (result.outputTruncated)
206
- return "truncated";
207
209
  return "normal";
208
210
  }
209
211
  function isNonnegativeInteger(value) {
@@ -29,7 +29,6 @@ const phpLintRunner = {
29
29
  id: "php-lint",
30
30
  appliesTo: ["php"],
31
31
  priority: PRIORITY.GENERAL_ANALYSIS,
32
- enabledByDefault: true,
33
32
  skipTestFiles: false,
34
33
  async run(ctx) {
35
34
  const cwd = ctx.cwd || process.cwd();
@@ -82,7 +82,6 @@ const phpstanRunner = {
82
82
  id: "phpstan",
83
83
  appliesTo: ["php"],
84
84
  priority: PRIORITY.GENERAL_ANALYSIS,
85
- enabledByDefault: true,
86
85
  skipTestFiles: false,
87
86
  async run(ctx) {
88
87
  const cwd = ctx.cwd || process.cwd();
@@ -35,7 +35,6 @@ const prismaValidateRunner = {
35
35
  id: "prisma-validate",
36
36
  appliesTo: ["prisma"],
37
37
  priority: PRIORITY.GENERAL_ANALYSIS,
38
- enabledByDefault: true,
39
38
  skipTestFiles: false,
40
39
  async run(ctx) {
41
40
  const cwd = ctx.cwd || process.cwd();
@@ -77,6 +77,7 @@ function notePsDecision(latch, tool, verdict) {
77
77
  cause: verdict.available ? "ok" : verdict.cause,
78
78
  elapsedMs: verdict.elapsedMs,
79
79
  latched: verdict.available || isLatchingOutcome(verdict.outcome),
80
+ classifiedBy: "probe",
80
81
  hostStallMs: verdict.hostStallMs,
81
82
  ...(retryAfterMs !== undefined && { retryAfterMs }),
82
83
  budgetMs: PS_TIMEOUT_MS,
@@ -268,7 +269,6 @@ const psScriptAnalyzerRunner = {
268
269
  id: "psscriptanalyzer",
269
270
  appliesTo: ["powershell"],
270
271
  priority: PRIORITY.GENERAL_ANALYSIS,
271
- enabledByDefault: true,
272
272
  skipTestFiles: false,
273
273
  async run(ctx) {
274
274
  const cmd = await resolvePowerShellCmd();
@@ -412,6 +412,9 @@ const psScriptAnalyzerRunner = {
412
412
  latched: false,
413
413
  hostStallMs,
414
414
  budgetMs: PS_TIMEOUT_MS,
415
+ // A read of the process's own stdout, not classifyProbeFailure, is
416
+ // what decided this (#2209).
417
+ classifiedBy: "caller",
415
418
  });
416
419
  incrementDegradationCount({
417
420
  kind: "grammar-blocked",