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
@@ -18,6 +18,9 @@ import * as os from "node:os";
18
18
  import * as path from "node:path";
19
19
  import { classifyProbeFailure, createAvailabilityLatch, isTransientDecision, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
20
20
  import { safeSpawnAsync } from "./safe-spawn.js";
21
+ import { createAvailabilityProbeFlight } from "./availability-probe-flight.js";
22
+ import { createGenerationSource, } from "./generation-guard.js";
23
+ import { isAtOrAboveHomeDir, walkUpDirs } from "./path-utils.js";
21
24
  /**
22
25
  * Fallback preference when nothing is declared (or the declared manager is
23
26
  * missing). npm first for maximum compatibility; bun last. A project lockfile
@@ -29,6 +32,7 @@ const PREFERENCE = [
29
32
  "yarn",
30
33
  "bun",
31
34
  ];
35
+ const packageManagerProbeFlights = createAvailabilityProbeFlight();
32
36
  function onWindows() {
33
37
  return process.platform === "win32";
34
38
  }
@@ -94,7 +98,6 @@ const PROBE_TIMEOUT_MS = 5_000;
94
98
  * a timeout, abort or host stall expires on a cooldown and is re-probed.
95
99
  */
96
100
  const availabilityLatches = new Map();
97
- const inFlightProbes = new Map();
98
101
  function getLatch(pm) {
99
102
  let latch = availabilityLatches.get(pm);
100
103
  if (!latch) {
@@ -128,6 +131,7 @@ async function probeAvailability(pm) {
128
131
  latched: true,
129
132
  hostStallMs,
130
133
  budgetMs: PROBE_TIMEOUT_MS,
134
+ classifiedBy: "probe",
131
135
  });
132
136
  return true;
133
137
  }
@@ -181,22 +185,8 @@ function isAvailable(pm, onTransient) {
181
185
  return Promise.resolve(reportIfTransient(memo));
182
186
  // A verdict can now expire, so concurrent callers arriving just after a
183
187
  // cooldown must share ONE probe rather than each spawning their own.
184
- const inFlight = inFlightProbes.get(pm);
185
- if (inFlight)
186
- return inFlight.then(reportIfTransient);
187
- // #1653 review F1: a probe started before a session reset can settle AFTER
188
- // a later session's own probe for the same manager is already in flight.
189
- // An unconditional delete-by-key would evict that NEWER entry out from
190
- // under it, so a third caller in the gap finds nothing in-flight and
191
- // spawns a duplicate. Only remove the entry if it is still THIS call's
192
- // promise — the same identity guard `resolveMadge` uses in
193
- // dependency-checker.ts for the equivalent race.
194
- const probe = probeAvailability(pm).finally(() => {
195
- if (inFlightProbes.get(pm) === probe)
196
- inFlightProbes.delete(pm);
197
- });
198
- inFlightProbes.set(pm, probe);
199
- return probe.then(reportIfTransient);
188
+ const shared = packageManagerProbeFlights.run(`package-manager:${pm}`, () => probeAvailability(pm));
189
+ return shared.promise.then(reportIfTransient);
200
190
  }
201
191
  /**
202
192
  * Clear the process-wide availability cache: pnpm/yarn/bun/npm each sit
@@ -209,11 +199,16 @@ function isAvailable(pm, onTransient) {
209
199
  * day, start a fresh session, pi-lens still reports it missing until a
210
200
  * process restart (#1653). Called from `handleSessionStart`'s per-session
211
201
  * reset block beside `resetZizmorTokenAvailability()` /
212
- * `resetPsScriptAnalyzerAvailability()`; also used directly by tests.
202
+ * `resetPsScriptAnalyzerAvailability()`; also used directly by tests. Also
203
+ * clears `globalBinDirCache`, whose memo lifetime rides the same reset — it
204
+ * must not outlive the availability verdicts it was derived from (#1602).
213
205
  */
214
206
  export function _resetPackageManagerCache() {
207
+ packageManagerCacheGeneration.bump();
215
208
  availabilityLatches.clear();
216
- inFlightProbes.clear();
209
+ packageManagerProbeFlights.clear();
210
+ globalBinDirCache.clear();
211
+ globalBinDirProbeFlights.clear();
217
212
  }
218
213
  // ============================================================================
219
214
  // RESOLUTION
@@ -323,12 +318,39 @@ export function execArgs(pm, pkg, args = []) {
323
318
  // ============================================================================
324
319
  // GLOBAL BIN DISCOVERY
325
320
  // ============================================================================
321
+ /**
322
+ * Per-manager global bin dir, memoized for the process (cleared alongside
323
+ * `availabilityLatches` in `_resetPackageManagerCache`). Only a SUCCESSFUL
324
+ * lookup is cached: a spawn failure here is evidence about this call, not
325
+ * about the manager (the manager already passed `isAvailable`'s own probe),
326
+ * so it must not latch an empty result the way a genuine absence would. The
327
+ * generation guard owns post-await cache publication, so a probe crossing
328
+ * a session reset cannot repopulate the fresh memo. Reset clears the flight
329
+ * map separately, so later callers cannot share the pre-reset probe.
330
+ */
331
+ const globalBinDirCache = new Map();
332
+ const packageManagerCacheGeneration = createGenerationSource("package-manager-global-bin");
333
+ const globalBinDirProbeFlights = createAvailabilityProbeFlight();
326
334
  /** Directories where a given manager installs global binaries. */
327
335
  async function globalBinDirsFor(pm) {
336
+ const cached = globalBinDirCache.get(pm);
337
+ if (cached)
338
+ return cached;
339
+ const generation = packageManagerCacheGeneration.capture();
340
+ // Concurrent callers (dispatch runs several tools per edit) must share ONE
341
+ // probe rather than each spawning their own `npm config get prefix` (#1602).
342
+ const shared = globalBinDirProbeFlights.run(pm, () => probeGlobalBinDirs(pm, generation));
343
+ return shared.promise;
344
+ }
345
+ async function probeGlobalBinDirs(pm, generation) {
328
346
  if (pm === "bun") {
329
347
  // bun has no per-call query cost — the global bin dir is deterministic.
330
348
  const base = process.env.BUN_INSTALL || path.join(os.homedir(), ".bun");
331
- return [path.join(base, "bin")];
349
+ const dirs = [path.join(base, "bin")];
350
+ generation.guardedWrite(pm, () => {
351
+ globalBinDirCache.set(pm, dirs);
352
+ });
353
+ return dirs;
332
354
  }
333
355
  const query = pm === "npm"
334
356
  ? ["config", "get", "prefix"]
@@ -343,10 +365,11 @@ async function globalBinDirsFor(pm) {
343
365
  return [];
344
366
  // npm reports a prefix; binaries live in `<prefix>/bin` on Unix, `<prefix>`
345
367
  // on Windows. pnpm/yarn already print the bin dir directly.
346
- if (pm === "npm") {
347
- return [onWindows() ? out : path.join(out, "bin")];
348
- }
349
- return [out];
368
+ const dirs = pm === "npm" ? [onWindows() ? out : path.join(out, "bin")] : [out];
369
+ generation.guardedWrite(pm, () => {
370
+ globalBinDirCache.set(pm, dirs);
371
+ });
372
+ return dirs;
350
373
  }
351
374
  /**
352
375
  * Global bin directories for every installed manager, deduped. Used to locate a
@@ -406,31 +429,165 @@ export async function findGlobalBinary(command, windowsExt = ".cmd") {
406
429
  return undefined;
407
430
  }
408
431
  /**
409
- * Local `node_modules/.bin/<tool>` walking up from `startDir` to the fs root.
432
+ * The three project-local bin-directory shapes in this repo, most-preferred
433
+ * first. Exported so every caller passes the SAME constant instead of
434
+ * re-listing the directory names next to its own copy of the walk — the venv
435
+ * list alone had three independent spellings before #2544 review F1
436
+ * (`formatters.ts`, `runner-helpers.ts`'s `createVenvFinder`,
437
+ * `dead-code-client.ts`'s `venvCandidates`).
438
+ *
439
+ * `VENV_BIN_DIRS` lists BOTH the POSIX (`bin`) and Windows (`Scripts`) layouts
440
+ * unconditionally rather than branching on `process.platform` at module load:
441
+ * a module-load platform constant cannot be forced per test, so the other
442
+ * branch is never exercised on the ubuntu lane (defect shape 30). The extra
443
+ * two `existsSync` calls per directory are the price of a testable list, and
444
+ * a `.venv/Scripts` on Linux (or `.venv/bin` under msys) resolves correctly
445
+ * instead of being invisible.
446
+ */
447
+ export const NODE_MODULES_BIN_DIRS = [
448
+ path.join("node_modules", ".bin"),
449
+ ];
450
+ export const VENDOR_BIN_DIRS = [path.join("vendor", "bin")];
451
+ export const VENV_BIN_DIRS = [
452
+ path.join(".venv", "bin"),
453
+ path.join("venv", "bin"),
454
+ path.join(".venv", "Scripts"),
455
+ path.join("venv", "Scripts"),
456
+ ];
457
+ /**
458
+ * THE project-local tool-bin walk: yields every `<binDir>/<tool>` that exists
459
+ * in `startDir` or one of its ancestors, nearest first, stopping at the HOME
460
+ * ceiling (never the fs root — see below).
461
+ *
462
+ * A generator on purpose. {@link findLocalBinUpwards} takes the first match and
463
+ * the walk suspends there — no ancestor above the hit is ever stat'd, which is
464
+ * what a per-edit resolver needs — while {@link findLocalBinsUpwards} drains it
465
+ * for the ast-grep probe sweep, the one caller that legitimately wants every
466
+ * candidate above `startDir`. One walk body, two arities, no "collect-all"
467
+ * flag whose disabled half nothing can observe.
468
+ * `ceiling: false` opts a walk out of the HOME stop entirely (own-install
469
+ * trees only, see {@link LocalBinWalkOptions.ceiling}); {@link findLocalBinsAt}
470
+ * / {@link findLocalBinAt} below are the non-walking sibling for callers that
471
+ * must check exactly one directory and never climb (`createVenvFinder`,
472
+ * vulture's `venvCandidates`).
473
+ *
474
+ * `tools` is a LIST because the sweep callers probe several spellings of the
475
+ * same tool and the preference order is per-directory: all of a directory's
476
+ * candidates, in `tools` order, before the parent directory is looked at.
477
+ *
478
+ * `binDirs` makes this the ONLY such walker in the repo. Before #2544 there
479
+ * were SIX copies of this loop — `formatters.ts`'s `findInNodeModules` /
480
+ * `findInVendorBin` / `findInVenv`, `oxlint.ts`'s `resolveLocalVp`,
481
+ * `runner-helpers.ts`'s `findNodeBinRoots` and `resolveVendorToolCommand` —
482
+ * and they had drifted apart: only this one resolved a relative `startDir`,
483
+ * only this one had the #2514 HOME ceiling, and `vp` was resolved with
484
+ * OPPOSITE ceiling policies depending on whether `formatters.ts` or `oxlint.ts`
485
+ * asked (#2544 review F1). They all delegate here now.
486
+ *
487
+ * Candidate order is EXT-OUTER, DIR-INNER: on Windows every `binDir` is tried
488
+ * with `windowsExt` before any is tried with the bare name, matching what the
489
+ * `.venv/Scripts` walker did before the fold.
490
+ */
491
+ /**
492
+ * Expand `tools` into the actual candidate file names for one directory,
493
+ * EXT-OUTER: on Windows every tool's extended name is tried before any tool's
494
+ * bare name (`windowsExt === ""` means the caller already spelled every name
495
+ * it wants — the ast-grep sweep passes `ast-grep.cmd`/`ast-grep.exe`/`ast-grep`
496
+ * itself in a shell-dependent order this option cannot express).
497
+ */
498
+ function expandCandidateNames(tools, windowsExt, isWindows) {
499
+ return isWindows && windowsExt !== ""
500
+ ? tools.flatMap((tool) => [`${tool}${windowsExt}`, tool])
501
+ : [...tools];
502
+ }
503
+ /** Every `<dir>/<binDir>/<name>` that exists, for exactly `dir` — no walk. */
504
+ function* matchesInDir(dir, names, binDirs) {
505
+ for (const name of names) {
506
+ for (const binDir of binDirs) {
507
+ const full = path.join(dir, binDir, name);
508
+ if (fs.existsSync(full))
509
+ yield full;
510
+ }
511
+ }
512
+ }
513
+ function* localBinMatches(tools, startDir, options) {
514
+ const { windowsExt = ".cmd", homeDir = os.homedir(), binDirs = NODE_MODULES_BIN_DIRS, isWindows = onWindows(), ceiling = true, } = options;
515
+ const names = expandCandidateNames(tools, windowsExt, isWindows);
516
+ for (const dir of walkUpDirs(startDir)) {
517
+ // Tool-resolution walker, not a config lookup (#2514/#2517 policy):
518
+ // escaping the project upward past HOME means STOP, not keep reading. A
519
+ // config-file lookup (`findLocalToolConfig`) keeps climbing past HOME
520
+ // because the underlying tool would find a legitimate global config
521
+ // there too; a `node_modules/.bin` match found at or above HOME can
522
+ // never be THIS project's own installed dependency — it is some
523
+ // unrelated home-level manifest (the pi-extensions bin shim, #2514).
524
+ // Default-on (not opt-in like `findLocalToolConfig`'s `options.homeDir`)
525
+ // because there is no legitimate case for this resolver to hand back a
526
+ // bin from outside the project tree. `ceiling: false` (own-install
527
+ // walk only, #2544 round 4 F1) skips this check entirely.
528
+ if (ceiling && isAtOrAboveHomeDir(dir, homeDir))
529
+ return;
530
+ yield* matchesInDir(dir, names, binDirs);
531
+ }
532
+ }
533
+ /**
534
+ * The nearest project-local `<binDir>/<tool>` at or above `startDir`, or
535
+ * `undefined`. See {@link localBinMatches} for the walk itself.
410
536
  *
411
537
  * Exported so callers that must NOT pay for global-bin discovery can reuse this
412
538
  * walk instead of copying it. `findNodeToolBinary` below adds `findGlobalBinary`,
413
539
  * which spawns a probe per package manager; a caller resolving a command on
414
540
  * every run (knip, #1721) needs the filesystem half only.
415
541
  */
416
- export function findLocalBinUpwards(tool, startDir, windowsExt = ".cmd") {
417
- const names = onWindows() ? [`${tool}${windowsExt}`, tool] : [tool];
418
- let dir = path.resolve(startDir);
419
- const root = path.parse(dir).root;
420
- while (true) {
421
- for (const name of names) {
422
- const full = path.join(dir, "node_modules", ".bin", name);
423
- if (fs.existsSync(full))
424
- return full;
425
- }
426
- if (dir === root)
427
- break;
428
- const parent = path.dirname(dir);
429
- if (parent === dir)
430
- break;
431
- dir = parent;
432
- }
433
- return undefined;
542
+ export function findLocalBinUpwards(tool, startDir, options = {}) {
543
+ // `.next().value`, not a `for…of` that returns on its first iteration: the
544
+ // latter is a single-iteration loop (SonarCloud S1751, and it reads as one
545
+ // too). The generator is simply left suspended after the first match — it
546
+ // holds no resource that needs closing, which is exactly why the walk above
547
+ // never stats an ancestor beyond the hit.
548
+ return localBinMatches([tool], startDir, options).next().value;
549
+ }
550
+ /**
551
+ * EVERY project-local match for `tools` at or above `startDir`, nearest first.
552
+ *
553
+ * For the ast-grep availability sweep (`runner-helpers.ts`'s `buildSgLocalBins`,
554
+ * which spawns `--version` against each candidate until one answers, both for
555
+ * the project walk and — with `ceiling: false` — pi-lens's own install-tree
556
+ * walk), which previously hand-rolled its own unceilinged climb precisely
557
+ * because the singular `findLocalBinUpwards` could not express "all of them"
558
+ * (#2544 review F1).
559
+ *
560
+ * NOT for a "project's own venv/vendor-bin first" resolver: `createVenvFinder`
561
+ * and vulture's `venvCandidates` looked like this walk's other two callers in
562
+ * round 3, but that was an unrequested behaviour change (round 3 introduced
563
+ * ancestor climbing where a single cwd/root lookup existed before, with no
564
+ * #2514 defect behind it) — reverted in round 4 F2 to {@link findLocalBinsAt}.
565
+ */
566
+ export function findLocalBinsUpwards(tools, startDir, options = {}) {
567
+ return [...localBinMatches(tools, startDir, options)];
568
+ }
569
+ /**
570
+ * EVERY `<dir>/<binDir>/<tool>` that exists for exactly `dir` — no ancestor
571
+ * walk, so the HOME ceiling is moot (a single directory can never climb past
572
+ * it).
573
+ *
574
+ * For the "project's own venv/vendor-bin first" resolvers that must NOT climb
575
+ * to a parent project (#2544 round 4 F2): `createVenvFinder` and vulture's
576
+ * `venvCandidates` were fixed `cwd`/`root` lookups before #2544 review round 3
577
+ * converted them into ancestor walks with no reported defect behind the
578
+ * conversion (an unrelated sibling project's interpreter at `~/code/.venv`
579
+ * could then outrank PATH for `~/code/app`, which has no venv of its own).
580
+ * Reverted here; `binDirs`/`windowsExt` stay shared with the walking helpers
581
+ * above so the three directory shapes have exactly one spelling.
582
+ */
583
+ export function findLocalBinsAt(tools, dir, options = {}) {
584
+ const { windowsExt = ".cmd", binDirs = NODE_MODULES_BIN_DIRS, isWindows = onWindows(), } = options;
585
+ const names = expandCandidateNames(tools, windowsExt, isWindows);
586
+ return [...matchesInDir(path.resolve(dir), names, binDirs)];
587
+ }
588
+ /** The nearest — i.e. first-listed — `<dir>/<binDir>/<tool>` match, or `undefined`. */
589
+ export function findLocalBinAt(tool, dir, options = {}) {
590
+ return findLocalBinsAt([tool], dir, options)[0];
434
591
  }
435
592
  /**
436
593
  * Locate a Node CLI tool's binary, preferring a local `node_modules/.bin`
@@ -444,6 +601,6 @@ export function findLocalBinUpwards(tool, startDir, windowsExt = ".cmd") {
444
601
  * without changing their npx fallback semantics.
445
602
  */
446
603
  export async function findNodeToolBinary(tool, cwd, windowsExt = ".cmd") {
447
- return (findLocalBinUpwards(tool, cwd, windowsExt) ??
604
+ return (findLocalBinUpwards(tool, cwd, { windowsExt }) ??
448
605
  (await findGlobalBinary(tool, windowsExt)));
449
606
  }