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
@@ -1,4 +1,5 @@
1
1
  import { LEDGER_FIELD_MAX } from "../../../ledger-bounds.js";
2
+ import { truncatedByOutputCap } from "../../../spawn-output-cap.js";
2
3
  /**
3
4
  * The first non-empty line of a tool's output, bounded.
4
5
  *
@@ -58,6 +59,9 @@ export function classifyRunOutcome(input) {
58
59
  firstOutputLine: firstOutputLine(result.stderr) ||
59
60
  firstOutputLine(result.stdout) ||
60
61
  firstOutputLine(result.error?.message),
62
+ // Evidence carried alongside the verdict, never part of deciding it — the
63
+ // branches below are byte-for-byte the pre-#2100 rules.
64
+ outputCapped: truncatedByOutputCap(result),
61
65
  };
62
66
  if (result.error || signal)
63
67
  return { kind: "did-not-run", ...base };
@@ -5,6 +5,23 @@
5
5
  import { incrementDegradationCount } from "../../../degradation-ledger.js";
6
6
  import { truncateForLedger } from "../../../ledger-bounds.js";
7
7
  import { classifyRunOutcome, firstOutputLine, } from "./spawn-outcome.js";
8
+ /**
9
+ * What ended this run, as one clause, most specific cause first.
10
+ *
11
+ * A signal is a stronger explanation than the exit status, so it displaces a
12
+ * `null` one rather than sitting beside it. Our own output cap is stronger
13
+ * still: the signal it killed with is a detail of OUR decision, and naming that
14
+ * signal alone reads as the tool misbehaving (#2100).
15
+ */
16
+ function describeEnding(input) {
17
+ if (input.outputCapped)
18
+ return "was stopped at its output cap";
19
+ if (input.signal)
20
+ return `was killed by ${input.signal}`;
21
+ if (input.status === null)
22
+ return "did not run (spawn failure)";
23
+ return `exited ${input.status}`;
24
+ }
8
25
  /**
9
26
  * One wording for "this tool did not produce a usable result" (#1816).
10
27
  *
@@ -21,13 +38,7 @@ export function formatToolFailure(input) {
21
38
  const identity = entries.length
22
39
  ? `${input.tool} (${entries.map(([key, value]) => `${key}=${value}`).join(", ")})`
23
40
  : input.tool;
24
- // A signal is the strongest available explanation, so it displaces the exit
25
- // status rather than sitting beside a `null` one.
26
- const what = input.signal
27
- ? `was killed by ${input.signal}`
28
- : input.status === null
29
- ? "did not run (spawn failure)"
30
- : `exited ${input.status}`;
41
+ const what = describeEnding(input);
31
42
  const missing = input.reportMissing ? "no report file" : "no output";
32
43
  const detail = firstOutputLine(input.stderr) ||
33
44
  firstOutputLine(input.stdout) ||
@@ -44,6 +55,7 @@ export function formatRunOutcomeFailure(tool, outcome, fields) {
44
55
  tool,
45
56
  status: outcome.status,
46
57
  signal: outcome.signal,
58
+ outputCapped: outcome.outputCapped,
47
59
  stderr: outcome.firstOutputLine,
48
60
  reportMissing: outcome.kind === "report-missing",
49
61
  fields: { outcome: outcome.kind, ...fields },
@@ -13,6 +13,40 @@
13
13
  */
14
14
  import { createAvailabilityLatch, logAvailabilityDecision, } from "./availability-policy.js";
15
15
  import { probeAvailabilityCandidates } from "./candidate-probe.js";
16
+ import { createAvailabilityProbeFlight } from "../../../availability-probe-flight.js";
17
+ import { createGenerationSource } from "../../../generation-guard.js";
18
+ import { createSingleFlight } from "../../../single-flight.js";
19
+ /**
20
+ * Session generation for toolchain-availability resets (#2455 fix round 3, F2).
21
+ *
22
+ * `reset()` clears the memo and the latch, but a probe that was ALREADY in
23
+ * flight when it ran knows nothing about that. Without a generation, a
24
+ * post-reset caller joins the pre-reset flight and, worse, that flight's own
25
+ * settlement writes its stale verdict back into the freshly cleared latch — so
26
+ * a tool installed between sessions still read "missing" for the whole new
27
+ * session. That is #1674's defect read from both the sharing side and the
28
+ * settle side, and the reason `SingleFlightOptions.generation` exists.
29
+ *
30
+ * This is the repo's extracted primitive (`clients/generation-guard.ts`, #1754)
31
+ * rather than a hand-rolled counter (#2455 fix round 4, F1). Two things come
32
+ * with it that the hand-rolled form did not have: the source is declared by
33
+ * construction, so the #1754 ratchet can see it; and `guardedWrite` puts every
34
+ * superseded write in the degradation ledger as a bounded
35
+ * `generation-guard-stale-write` record per (source, subject), so a dogfood
36
+ * session can tell "the guard is working" from "the guard never fires" instead
37
+ * of watching superseded writes vanish silently (F4).
38
+ *
39
+ * A module-level source rather than a per-instance one, deliberately. It is a
40
+ * counter, so nothing has to be hand-maintained and no flight is retained for
41
+ * the sake of superseding it. And `toolchainProbeFlights` is itself shared
42
+ * ACROSS instances (#2131: two clients probing the same candidate list run one
43
+ * sweep), so its supersede signal cannot live on any one instance. The cost is
44
+ * that resetting one toolchain also supersedes another's in-flight sweep; that
45
+ * is at worst one extra probe, never a wrong verdict, and every registered
46
+ * reset fires in the same `session_start` block anyway.
47
+ */
48
+ const toolchainGenerations = createGenerationSource("toolchain-availability");
49
+ const toolchainProbeFlights = createAvailabilityProbeFlight({ generation: toolchainGenerations.current });
16
50
  /**
17
51
  * Own one toolchain's availability: sweep the platform candidate list, memoize
18
52
  * the path that answered, and park the verdict behind the shared latch so a
@@ -28,33 +62,61 @@ export function createToolchainAvailability(config) {
28
62
  let sweepHostStallMs = 0;
29
63
  /** What the last classified candidate returned, for the decision record. */
30
64
  let sweepEvidence;
31
- let ensureInFlight = null;
65
+ const ensureFlight = createSingleFlight({
66
+ generation: toolchainGenerations.current,
67
+ });
32
68
  async function findPath() {
33
69
  if (toolPath)
34
70
  return toolPath;
35
71
  const paths = process.platform === "win32" ? config.windowsPaths : config.unixPaths;
36
- const sweep = await probeAvailabilityCandidates(paths, config.probeArgs, config.budgetMs);
37
- sweepSawTransient = sweep.sawTransient;
38
- sweepTransientCause = sweep.transientCause;
39
- sweepHostStallMs = sweep.hostStallMs;
40
- sweepEvidence = sweep.evidence;
41
- if (sweep.foundPath)
42
- toolPath = sweep.foundPath;
72
+ const handle = toolchainGenerations.capture();
73
+ const shared = toolchainProbeFlights.run(`toolchain:${config.tool}|${config.probeArgs.join("|")}|${config.windowsPaths.join("|")}|${config.unixPaths.join("|")}`, () => probeAvailabilityCandidates(paths, config.probeArgs, config.budgetMs));
74
+ const sweep = await shared.promise;
75
+ // A reset may have landed while this sweep was running. Its answer then
76
+ // describes the session that just ended, so it must not write itself back
77
+ // into the memo `reset()` just cleared, nor into the classification fields
78
+ // the replacement flight reads to build ITS verdict. The memo is the
79
+ // sharper half: a superseded sweep that writes `foundPath` latches a path
80
+ // the reset may have been called BECAUSE it went away, so the seam that
81
+ // exists to un-latch a false "missing" would instead latch a false
82
+ // "found". Callers already holding this promise still get what the sweep
83
+ // actually found.
84
+ handle.guardedWrite(`findPath:${config.tool}`, () => {
85
+ sweepSawTransient = sweep.sawTransient;
86
+ sweepTransientCause = sweep.transientCause;
87
+ sweepHostStallMs = sweep.hostStallMs;
88
+ sweepEvidence = sweep.evidence;
89
+ if (sweep.foundPath)
90
+ toolPath = sweep.foundPath;
91
+ });
43
92
  return sweep.foundPath;
44
93
  }
45
94
  async function resolveAvailability() {
46
95
  const startedAt = Date.now();
47
- const found = (await findPath()) !== null;
96
+ const handle = toolchainGenerations.capture();
97
+ const resolvedPath = await findPath();
98
+ const found = resolvedPath !== null;
99
+ // #2455 fix round 3, F2: a reset during the probe opened a NEW session.
100
+ // This flight answers the OLD one, so writing its verdict into the
101
+ // freshly cleared latch would re-latch a stale "missing" for the whole
102
+ // new session — the bug the reset exists to prevent, reintroduced by the
103
+ // reset's own timing. Report the verdict to this flight's own callers;
104
+ // do not latch it, and say so in the decision record's `latched`.
105
+ const superseded = !handle.isCurrent();
48
106
  if (found) {
49
- availabilityLatch.noteAvailable();
50
- config.log(`${config.label} found: ${toolPath}`);
107
+ handle.guardedWrite(`latch:${config.tool}`, () => availabilityLatch.noteAvailable());
108
+ // The RESOLVED path, not the memo (#2455 fix round 4, F6). A superseded
109
+ // sweep is forbidden from writing `toolPath`, so reading the memo here
110
+ // logged `Go found: null` on exactly the branch this line exists to
111
+ // explain.
112
+ config.log(`${config.label} found: ${resolvedPath}`);
51
113
  logAvailabilityDecision({
52
114
  tool: config.tool,
53
115
  verdict: "available",
54
116
  outcome: "success",
55
117
  cause: "ok",
56
118
  elapsedMs: Date.now() - startedAt,
57
- latched: true,
119
+ latched: !superseded,
58
120
  hostStallMs: sweepHostStallMs,
59
121
  budgetMs: config.budgetMs,
60
122
  classifiedBy: "probe",
@@ -66,14 +128,14 @@ export function createToolchainAvailability(config) {
66
128
  // whether the toolchain is installed; it expires instead of latching.
67
129
  const outcome = sweepSawTransient ? "transient" : "missing";
68
130
  const cause = sweepSawTransient ? sweepTransientCause : "not-found";
69
- const retryAfterMs = availabilityLatch.noteUnavailable(outcome, cause);
131
+ const retryAfterMs = handle.guardedWrite(`latch:${config.tool}`, () => availabilityLatch.noteUnavailable(outcome, cause)) ?? 0;
70
132
  logAvailabilityDecision({
71
133
  tool: config.tool,
72
134
  verdict: "unavailable",
73
135
  outcome,
74
136
  cause,
75
137
  elapsedMs: Date.now() - startedAt,
76
- latched: outcome !== "transient",
138
+ latched: !superseded && outcome !== "transient",
77
139
  hostStallMs: sweepHostStallMs,
78
140
  ...(retryAfterMs > 0 && { retryAfterMs }),
79
141
  budgetMs: config.budgetMs,
@@ -90,17 +152,18 @@ export function createToolchainAvailability(config) {
90
152
  const memo = availabilityLatch.read();
91
153
  if (memo !== null)
92
154
  return memo;
93
- // Now that a verdict can expire, concurrent callers arriving just after a
94
- // cooldown must share ONE sweep rather than each spawning their own.
95
- if (ensureInFlight)
96
- return ensureInFlight;
97
- ensureInFlight = resolveAvailability();
98
- try {
99
- return await ensureInFlight;
100
- }
101
- finally {
102
- ensureInFlight = null;
103
- }
155
+ return ensureFlight.run("availability", resolveAvailability);
156
+ }
157
+ function reset() {
158
+ toolPath = null;
159
+ availabilityLatch.reset();
160
+ // Supersede every sweep and availability flight already running, in this
161
+ // instance and in the shared registry. No `clear()`: the generation bump
162
+ // is what makes a stale flight unjoinable and unable to re-latch, and
163
+ // `SingleFlight.release`'s identity check already stops it from evicting
164
+ // its successor. Clearing the SHARED registry here would additionally
165
+ // drop another toolchain's live claim and cost it a duplicate sweep.
166
+ toolchainGenerations.bump();
104
167
  }
105
- return { findPath, isAvailable };
168
+ return { findPath, isAvailable, reset };
106
169
  }
@@ -70,7 +70,6 @@ const valeRunner = {
70
70
  id: "vale",
71
71
  appliesTo: ["markdown"],
72
72
  priority: PRIORITY.DOC_QUALITY,
73
- enabledByDefault: true,
74
73
  skipTestFiles: false,
75
74
  async run(ctx) {
76
75
  const cwd = ctx.cwd || process.cwd();
@@ -16,6 +16,7 @@ import * as fs from "node:fs";
16
16
  import * as path from "node:path";
17
17
  import yaml from "../../deps/js-yaml.js";
18
18
  import { BoundedLruCache } from "../../bounded-cache.js";
19
+ import { compareOrdinal } from "../../string-utils.js";
19
20
  // --- Constants ---
20
21
  /** Overly broad patterns that match everything (cause false positive explosions) */
21
22
  export const OVERLY_BROAD_PATTERNS = [
@@ -28,18 +29,22 @@ export const OVERLY_BROAD_PATTERNS = [
28
29
  ];
29
30
  /** Maximum complexity score for rules in blockingOnly mode */
30
31
  export const MAX_BLOCKING_RULE_COMPLEXITY = 8;
32
+ /**
33
+ * `getCachedRules` re-stats a rule directory's files at most once per this
34
+ * window (#2262 round 2). `scanner.ts:235` calls `loadYamlRules` per file
35
+ * scanned; a full metadata sweep (readdir + stat every rule file) on every
36
+ * warmed call measured 250x master's per-call cost on a mid-size catalog.
37
+ * Same value and same shape as `file-utils.ts`'s
38
+ * `PROJECT_IGNORE_FRESHNESS_CADENCE_MS` (#2159/#2071): 2s is the accepted
39
+ * staleness bound for a rule tree pi-lens itself did not just write.
40
+ */
41
+ export const RULES_CACHE_FRESHNESS_CADENCE_MS = 2_000;
31
42
  // --- Caches ---
32
43
  const rulesCache = new BoundedLruCache(64);
33
44
  const blockingRulesCache = new BoundedLruCache(64);
34
45
  const contentRulesCache = new BoundedLruCache(64);
35
46
  const contentBlockingRulesCache = new BoundedLruCache(64);
36
47
  // --- Public API ---
37
- export function clearRulesCache() {
38
- rulesCache.clear();
39
- blockingRulesCache.clear();
40
- contentRulesCache.clear();
41
- contentBlockingRulesCache.clear();
42
- }
43
48
  export function loadYamlRules(ruleDir, severityFilter) {
44
49
  return getCachedRules(ruleDir, severityFilter);
45
50
  }
@@ -48,7 +53,14 @@ function findYamlRuleFiles(ruleDir) {
48
53
  try {
49
54
  entries = fs
50
55
  .readdirSync(ruleDir, { withFileTypes: true })
51
- .sort((a, b) => a.name.localeCompare(b.name));
56
+ // Ordinal (code-unit), not locale, comparison: this order feeds the
57
+ // index-aligned file-stamp identity compared in `getCachedRules` and
58
+ // the hash input order in `loadYamlRulesFresh`. `localeCompare` can
59
+ // order the same file set differently across locales/processes,
60
+ // which would desync the two and mint spurious cache misses or
61
+ // (worse) content-hash collisions across differently-ordered runs.
62
+ // Same reasoning as `rule-cache.ts`'s `computeRuleHash` (#2155/#2165).
63
+ .sort((a, b) => compareOrdinal(a.name, b.name));
52
64
  }
53
65
  catch {
54
66
  return [];
@@ -118,20 +130,42 @@ export function getCachedRules(ruleDir, severityFilter) {
118
130
  if (!fs.existsSync(ruleDir)) {
119
131
  return [];
120
132
  }
121
- let currentMtime = 0;
122
- try {
123
- currentMtime = fs.statSync(ruleDir).mtimeMs;
124
- }
125
- catch {
126
- return [];
127
- }
128
133
  const cache = severityFilter === "error" ? blockingRulesCache : rulesCache;
129
134
  const cached = cache.get(ruleDir);
130
- if (cached && cached.mtime === currentMtime) {
135
+ const now = Date.now();
136
+ if (cached && now - cached.checkedAtMs < RULES_CACHE_FRESHNESS_CADENCE_MS) {
137
+ // Inside the cadence window: no stat sweep at all. `getCachedRules` is
138
+ // the bundled-catalog path only (project rules route through
139
+ // `loadYamlRulesFresh` — see `ast-grep-napi.ts`'s route split), and
140
+ // `scanner.ts:235` calls it once per scanned file, so a per-call
141
+ // `readdirSync`+`statSync*N` sweep is the hot-path cost this window
142
+ // exists to remove.
131
143
  return cached.rules;
132
144
  }
133
- const rules = loadYamlRulesUncached(ruleDir, severityFilter);
134
- cache.set(ruleDir, { rules, mtime: currentMtime });
145
+ const files = findYamlRuleFiles(ruleDir);
146
+ const fileStamps = files.flatMap((file) => {
147
+ try {
148
+ const stat = fs.statSync(file);
149
+ return [{ path: file, mtimeMs: stat.mtimeMs, size: stat.size }];
150
+ }
151
+ catch {
152
+ return [];
153
+ }
154
+ });
155
+ const metadataMatches = cached?.files.length === fileStamps.length &&
156
+ cached.files.every((file, index) => file.path === fileStamps[index].path &&
157
+ file.mtimeMs === fileStamps[index].mtimeMs &&
158
+ file.size === fileStamps[index].size);
159
+ if (cached && metadataMatches) {
160
+ // Nothing drifted. Re-arm the window from *now* regardless — otherwise
161
+ // the next call re-stats immediately instead of waiting a full cadence
162
+ // (the #2260/#2071 "checkedAtMs must re-arm on every check, not only on
163
+ // a miss" lesson).
164
+ cached.checkedAtMs = now;
165
+ return cached.rules;
166
+ }
167
+ const rules = loadYamlRuleFiles(files, severityFilter);
168
+ cache.set(ruleDir, { rules, files: fileStamps, checkedAtMs: now });
135
169
  return rules;
136
170
  }
137
171
  export function isOverlyBroadPattern(pattern) {
@@ -33,7 +33,6 @@ const yamllintRunner = {
33
33
  id: "yamllint",
34
34
  appliesTo: ["yaml"],
35
35
  priority: PRIORITY.YAML_LINT,
36
- enabledByDefault: true,
37
36
  skipTestFiles: false,
38
37
  async run(ctx) {
39
38
  const cwd = ctx.cwd || process.cwd();
@@ -44,7 +44,6 @@ const zigCheckRunner = {
44
44
  id: "zig-check",
45
45
  appliesTo: ["zig"],
46
46
  priority: PRIORITY.GENERAL_ANALYSIS,
47
- enabledByDefault: true,
48
47
  skipTestFiles: false,
49
48
  async run(ctx) {
50
49
  const cwd = ctx.cwd || process.cwd();
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * NDJSON telemetry log for diagnostic disposition marks (#690). Same shape as
3
- * clients/actionable-warnings-logger.ts: machine-global (getGlobalPiLensDir),
3
+ * clients/actionable-warnings-logger.ts: machine-global (getGlobalPiLensLogDir),
4
4
  * size-capped with a single `.log.1` backup, isTestMode no-op.
5
5
  *
6
6
  * Why a log at all: #181 identified false-positive marks flowing to telemetry
@@ -13,9 +13,9 @@
13
13
  */
14
14
  import * as path from "node:path";
15
15
  import { isTestMode } from "./env-utils.js";
16
- import { getGlobalPiLensDir } from "./file-utils.js";
16
+ import { getGlobalPiLensLogDir } from "./probe-home-state.js";
17
17
  import { createNdjsonLogger } from "./ndjson-logger.js";
18
- const DISPOSITION_LOG_DIR = getGlobalPiLensDir();
18
+ const DISPOSITION_LOG_DIR = getGlobalPiLensLogDir();
19
19
  const DISPOSITION_LOG_FILE = path.join(DISPOSITION_LOG_DIR, "dispositions.log");
20
20
  const DISPOSITION_LOG_BACKUP_FILE = path.join(DISPOSITION_LOG_DIR, "dispositions.log.1");
21
21
  const MAX_LOG_BYTES = Math.max(128 * 1024, Number.parseInt(process.env.PI_LENS_DISPOSITION_LOG_MAX_BYTES ?? "1048576", 10) || 1048576);