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
@@ -2,16 +2,17 @@
2
2
 
3
3
  <!-- GENERATED by scripts/gen-rule-catalogs.mjs — do not edit by hand. Run `npm run docs:rule-catalogs` after changing rules. -->
4
4
 
5
- pi-lens bundles **455 enabled** ast-grep rules (271 pi-lens-authored + 184 vendored from CodeRabbit) across **16 languages**, plus 16 disabled.
5
+ pi-lens bundles **481 enabled** ast-grep rules (297 pi-lens-authored + 184 vendored from CodeRabbit) across **17 languages**, plus 16 disabled.
6
6
 
7
7
  See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `rules/ast-grep-rules/rules/` (pi-lens), `rules/ast-grep-rules/coderabbit/rules/` (vendored).
8
8
 
9
9
  ## Enabled rules
10
10
 
11
- ### C (9)
11
+ ### C (10)
12
12
 
13
13
  | Rule | Severity | Fixable | Source | Description |
14
14
  |---|---|---|---|---|
15
+ | `c-no-malloc-free` | warning | no | pi-lens | Manual malloc/calloc/realloc/free; verify ownership and error handling |
15
16
  | `dont-call-system-c` | warning | no | CodeRabbit | Don't call `system`. It's a high-level wrapper that allows for stacking multiple commands. Always prefer a more restrictive API such as cal… |
16
17
  | `file-access-before-action-c` | warning | no | CodeRabbit | A check is done with `access` and then the file is later used. There is no guarantee that the status of the file has not changed since the… |
17
18
  | `file-stat-before-action-c` | warning | no | CodeRabbit | A check is done with `stat` and then the file is used. There is no guarantee that the status of the file has not changed since the call to… |
@@ -22,10 +23,12 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
22
23
  | `small-key-size-c` | warning | no | CodeRabbit | $KEY_FUNCTION` is using a key size of only $KEY_BITS bits. This is less than the recommended key size of 2048 bits. |
23
24
  | `world-writable-file-c` | warning | no | CodeRabbit | This call makes a world-writable file which allows any user on a machine to write to the file. This may allow attackers to influence the be… |
24
25
 
25
- ### C++ (15)
26
+ ### C++ (17)
26
27
 
27
28
  | Rule | Severity | Fixable | Source | Description |
28
29
  |---|---|---|---|---|
30
+ | `cpp-no-malloc-free` | warning | no | pi-lens | Manual malloc/calloc/realloc/free in C++; consider RAII (new/unique_ptr/vector) |
31
+ | `cpp-no-printf` | warning | no | pi-lens | Avoid printf in C++; prefer std::cout/std::format for type-safe formatting |
29
32
  | `dont-call-system-cpp` | warning | no | CodeRabbit | Don't call `system`. It's a high-level wrapper that allows for stacking multiple commands. Always prefer a more restrictive API such as cal… |
30
33
  | `file-access-before-action-cpp` | warning | no | CodeRabbit | A check is done with `access` and then the file is later used. There is no guarantee that the status of the file has not changed since the… |
31
34
  | `file-stat-before-action-cpp` | warning | no | CodeRabbit | A check is done with `stat` and then the file is used. There is no guarantee that the status of the file has not changed since the call to… |
@@ -58,13 +61,24 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
58
61
  | `stacktrace-disclosure-csharp` | warning | no | CodeRabbit | Stacktrace information is displayed in a non-Development environment. Accidentally disclosing sensitive stack trace information in a produc… |
59
62
  | `use-ecb-mode-csharp` | warning | no | CodeRabbit | "Usage of the insecure ECB mode detected. You should use an authenticated encryption mode instead, which is implemented by the classes AesG… |
60
63
 
61
- ### Go (23)
64
+ ### CSS (1)
65
+
66
+ | Rule | Severity | Fixable | Source | Description |
67
+ |---|---|---|---|---|
68
+ | `no-important` | warning | no | pi-lens | Avoid `!important` — increase selector specificity instead |
69
+
70
+ ### Go (28)
62
71
 
63
72
  | Rule | Severity | Fixable | Source | Description |
64
73
  |---|---|---|---|---|
65
74
  | `avoid-bind-to-all-interfaces-go` | warning | no | CodeRabbit | "Detected a network listener listening on 0.0.0.0 or an empty string. This could unexpectedly expose the server publicly as it binds to all… |
66
75
  | `defer-in-loop` | warning | no | pi-lens | defer is used inside a loop. This defers execution until after the entire function, not each iteration. |
67
76
  | `go-defer-func-call-antipattern` | warning | yes | pi-lens | defer with function-call args — arguments are evaluated immediately, not at defer time |
77
+ | `go-no-fmt-println` | warning | no | pi-lens | Avoid fmt.Println/Printf-family calls in production code; use a structured logger |
78
+ | `go-no-panic-in-lib` | warning | no | pi-lens | Avoid panic() in library code; return errors instead |
79
+ | `go-no-underscore-func-name` | hint | no | pi-lens | Use camelCase for Go function names, not snake_case |
80
+ | `go-prefer-errors-is` | hint | no | pi-lens | Use errors.Is(err, target) instead of direct comparison for error checking |
81
+ | `go-prefer-string-builder` | hint | no | pi-lens | Use strings.Builder instead of repeated string concatenation |
68
82
  | `go-test-functions` | warning | yes | pi-lens | Go test function detected — verify it follows the project's test naming convention |
69
83
  | `gorilla-cookie-store-hardcoded-session-key-go` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
70
84
  | `gorilla-csrf-hardcoded-auth-key-go` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
@@ -92,7 +106,7 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
92
106
  |---|---|---|---|---|
93
107
  | `plaintext-http-link-html` | warning | no | CodeRabbit | "This link points to a plaintext HTTP URL. Prefer an encrypted HTTPS URL if possible." |
94
108
 
95
- ### Java (36)
109
+ ### Java (40)
96
110
 
97
111
  | Rule | Severity | Fixable | Source | Description |
98
112
  |---|---|---|---|---|
@@ -118,7 +132,11 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
118
132
  | `missing-httponly-java` | warning | no | CodeRabbit | Detected a cookie where the `HttpOnly` flag is either missing or disabled. The `HttpOnly` cookie flag instructs the browser to forbid clien… |
119
133
  | `missing-secure-java` | warning | no | CodeRabbit | Detected a cookie where the `Secure` flag is either missing or disabled. The `Secure` cookie flag instructs the browser to forbid sending t… |
120
134
  | `no-null-cipher-java` | warning | no | CodeRabbit | NullCipher was detected. This will not encrypt anything; the cipher text will be the same as the plain text. Use a valid, secure cipher: Ci… |
135
+ | `no-raw-types` | warning | no | pi-lens | Use parameterized types instead of raw types. |
136
+ | `no-string-concat-in-loop` | warning | no | pi-lens | Use StringBuilder instead of string concatenation in loops. |
137
+ | `no-system-out-println` | warning | no | pi-lens | Avoid System.out.println; use a logging framework instead. |
121
138
  | `passwordauthentication-hardcoded-password-java` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
139
+ | `prefer-string-is-empty` | warning | no | pi-lens | Use .isEmpty() instead of .length() == 0 or .equals(""). |
122
140
  | `rsa-no-padding-java` | warning | no | CodeRabbit | Using RSA without OAEP mode weakens the encryption. |
123
141
  | `simple-command-injection-direct-input-java` | warning | no | CodeRabbit | "Untrusted input might be injected into a command executed by the application, which can lead to a command injection vulnerability. An atta… |
124
142
  | `system-setproperty-hardcoded-secret-java` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
@@ -217,13 +235,21 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
217
235
  | `unchecked-throwing-call-js` | error | no | pi-lens | $CALL without try/catch — throws on invalid input |
218
236
  | `weak-rsa-key-js` | error | no | pi-lens | Weak RSA key size — use at least 2048 bits |
219
237
 
220
- ### Kotlin (5)
238
+ ### Kotlin (13)
221
239
 
222
240
  | Rule | Severity | Fixable | Source | Description |
223
241
  |---|---|---|---|---|
224
242
  | `des-is-deprecated-kotlin` | warning | no | CodeRabbit | DES is considered deprecated. AES is the recommended cipher. Upgrade to use AES. See https://www.nist.gov/news-events/news/2005/06/nist-wit… |
225
243
  | `desede-is-deprecated-kotlin` | warning | no | CodeRabbit | Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES. |
226
244
  | `jwt-hardcode-kotlin` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
245
+ | `kotlin-no-lateinit` | warning | no | pi-lens | Avoid lateinit var; prefer a nullable type or constructor injection |
246
+ | `kotlin-no-nullable-boolean` | warning | no | pi-lens | Avoid a nullable Boolean; a two-value enum or a non-null default is usually clearer |
247
+ | `kotlin-no-println` | warning | no | pi-lens | Avoid println/print; use a structured logger |
248
+ | `kotlin-no-unnecessary-let` | warning | no | pi-lens | let on a non-null-checked receiver adds indirection; use the value directly |
249
+ | `kotlin-no-unsafe-cast` | warning | no | pi-lens | Unsafe cast (as) throws ClassCastException on a mismatch; consider as? |
250
+ | `kotlin-no-var` | warning | no | pi-lens | Prefer val over var; mutability should be explicit and scoped |
251
+ | `kotlin-prefer-data-class` | warning | no | pi-lens | This class looks like a plain value holder; consider a data class |
252
+ | `kotlin-prefer-is-empty` | warning | no | pi-lens | Use isEmpty() instead of comparing size/length to zero |
227
253
  | `rsa-no-padding-kotlin` | warning | no | CodeRabbit | Using RSA without OAEP mode weakens the encryption. |
228
254
  | `system-setproperty-hardcoded-secret-kotlin` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
229
255
 
@@ -234,7 +260,7 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
234
260
  | `openssl-cbc-static-iv-php` | warning | no | CodeRabbit | Static IV used with AES in CBC mode. Static IVs enable chosen-plaintext attacks against encrypted data. |
235
261
  | `search-active-debug-php` | warning | no | CodeRabbit | Debug logging is explicitly enabled. This can potentially disclose sensitive information and should never be active on production systems. |
236
262
 
237
- ### Python (96)
263
+ ### Python (98)
238
264
 
239
265
  | Rule | Severity | Fixable | Source | Description |
240
266
  |---|---|---|---|---|
@@ -308,6 +334,7 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
308
334
  | `python-mysqlclient-hardcoded-secret-python` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
309
335
  | `python-neo4j-empty-password-python` | warning | no | CodeRabbit | The application creates a database connection with an empty password. This can lead to unauthorized access by either an internal or externa… |
310
336
  | `python-neo4j-hardcoded-secret-python` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
337
+ | `python-optional-type` | hint | no | pi-lens | Optional[T] requires Python 3.10+ to rewrite as T \| None |
311
338
  | `python-peewee-mysql-empty-password-python` | warning | no | CodeRabbit | The application creates a database connection with an empty password. This can lead to unauthorized access by either an internal or externa… |
312
339
  | `python-peewee-mysql-hardcoded-secret-python` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
313
340
  | `python-peewee-pg-empty-password-python` | warning | no | CodeRabbit | The application creates a database connection with an empty password. This can lead to unauthorized access by either an internal or externa… |
@@ -333,9 +360,10 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
333
360
  | `python-urllib3-hardcoded-secret-python` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
334
361
  | `python-webrepl-empty-password-python` | warning | no | CodeRabbit | The application creates a database connection with an empty password. This can lead to unauthorized access by either an internal or externa… |
335
362
  | `python-webrepl-hardcoded-secret-python` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
363
+ | `reducible-list-comprehension` | warning | yes | pi-lens | A list comprehension is unnecessary here; use a generator expression |
336
364
  | `unchecked-throwing-call-python` | error | no | pi-lens | Call without try/except — throws on invalid input or missing file |
337
365
 
338
- ### Ruby (19)
366
+ ### Ruby (20)
339
367
 
340
368
  | Rule | Severity | Fixable | Source | Description |
341
369
  |---|---|---|---|---|
@@ -358,8 +386,9 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
358
386
  | `ruby-pg-hardcoded-secret-ruby` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
359
387
  | `ruby-redis-empty-password-ruby` | warning | no | CodeRabbit | The application creates a database connection with an empty password. This can lead to unauthorized access by either an internal or externa… |
360
388
  | `ruby-redis-hardcoded-secret-ruby` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
389
+ | `ruby-symbol-to-proc-candidate` | warning | yes | pi-lens | Use a symbol-to-proc shorthand for this simple iterator block |
361
390
 
362
- ### Rust (11)
391
+ ### Rust (12)
363
392
 
364
393
  | Rule | Severity | Fixable | Source | Description |
365
394
  |---|---|---|---|---|
@@ -370,6 +399,7 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
370
399
  | `redundant-unsafe-function` | error | no | pi-lens | Unsafe function without unsafe block inside |
371
400
  | `reqwest-accept-invalid-rust` | warning | no | CodeRabbit | Dangerously accepting invalid TLS |
372
401
  | `rust-2024-let-chain-candidate` | info | no | pi-lens | nested if conditions can be collapsed into a Rust 2024 let-chain |
402
+ | `rust-no-chars-enumerate` | warning | no | pi-lens | chars().enumerate() may confuse character indices with byte offsets |
373
403
  | `secrets-reqwest-hardcoded-auth-rust` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
374
404
  | `ssl-verify-none-rust` | warning | no | CodeRabbit | SSL verification disabled, this allows for MitM attacks |
375
405
  | `tokio-postgres-empty-password-rust` | warning | no | CodeRabbit | The application uses an empty credential. This can lead to unauthorized access by either an internal or external malicious actor. It is rec… |
@@ -392,10 +422,11 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
392
422
  | `insecure-biometrics-swift` | info | no | CodeRabbit | The application was observed to leverage biometrics via Local Authentication, which returns a simple boolean result for authentication. Thi… |
393
423
  | `rabbit-hardcoded-secret-swift` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
394
424
 
395
- ### TSX (7)
425
+ ### TSX (8)
396
426
 
397
427
  | Rule | Severity | Fixable | Source | Description |
398
428
  |---|---|---|---|---|
429
+ | `hyphenated-svg-attribute` | warning | no | pi-lens | Use camelCase for this known SVG attribute in JSX |
399
430
  | `inline-styles` | hint | no | pi-lens | Inline styles — use CSS modules or styled components |
400
431
  | `jsx-boolean-short-circuit` | error | yes | pi-lens | React JSX && pitfall — potentially renders 0 or NaN |
401
432
  | `no-nested-links` | error | no | pi-lens | Nested <a> tags are invalid HTML and cause unexpected behavior |
@@ -27,38 +27,49 @@ released `ast-grep`), so we get a fresh-engine cross-check.
27
27
 
28
28
  ## What the playground actually does
29
29
 
30
- The playground's source code is **fixed** — the user cannot inject custom
31
- source via the URL hash. The URL hash only carries:
30
+ The verifier encodes `--code` into the playground's URL-hash state so the
31
+ upstream engine matches the rule against it. The state (`ast-grep/ast-
32
+ grep.github.io`, `website/src/components/astGrep/state.ts` +
33
+ `index.ts`) carries:
32
34
 
33
35
  - `mode: "Config"` + `config: <rule YAML>` (the rule)
34
- - `query` (an ast-grep pattern string, only used in `mode: "Pattern"`)
36
+ - `source: <code>` — the code the rule is matched against, in **both**
37
+ modes
38
+ - `query` — an ast-grep pattern string, used only in `mode: "Pattern"`
39
+ (irrelevant here; the verifier always requests Config mode)
35
40
 
36
- So this verifier is a **pattern-level smoke test**: "does the rule's
37
- pattern fire on the playground's hardcoded sample source?" It catches:
41
+ So this verifier checks: "does the rule match `--code` on the upstream
42
+ engine, and does the count agree with the local `ast-grep` CLI?" It catches:
38
43
 
39
44
  - **YAML/pattern rejection** — the playground shows an error if the rule's
40
45
  YAML uses features the upstream engine doesn't support
41
46
  - **Pattern-engine divergence** — local ast-grep matches but upstream
42
47
  doesn't (or vice versa)
43
48
  - **Match-count drift** — `Found N match(es)` count differs between local
44
- and upstream
49
+ and upstream, for the *same* source
45
50
 
46
- It does **not** catch rule-vs-source bugs where the source is what the user
47
- wrote — the local CLI test is the source of truth for that.
51
+ #2208: earlier versions of this verifier wrote `--code` into `query`
52
+ instead of `source`. Since `query` is ignored in Config mode, the hash
53
+ carried no source at all, and the playground's `{...defaultState,
54
+ ...parsed}` state merge silently fell back to its own hardcoded sample —
55
+ every run graded that fixed sample, not `--code`. Every "ok:true
56
+ matches:N" this verifier produced before the fix reflected the playground's
57
+ sample source, never the caller's code.
48
58
 
49
59
  ## Usage
50
60
 
51
61
  ```bash
52
- # Smoke-test a single rule (auto-launches headless Chrome on 9224, then kills it)
53
- node scripts/playground-verify-rule.mjs rules/ast-grep-rules/rules/no-console-except-error.yml --code "ignored"
62
+ # Smoke-test a single rule against a snippet it matches
63
+ # (auto-launches headless Chrome on 9224, then kills it)
64
+ node scripts/playground-verify-rule.mjs rules/ast-grep-rules/rules/no-console-except-error.yml --code "console.log('x');"
54
65
 
55
66
  # Assert a specific match count
56
67
  node scripts/playground-verify-rule.mjs rules/ast-grep-rules/rules/jsx-boolean-short-circuit.yml \
57
- --code "ignored" --expected 0
68
+ --code "const a = 1;" --expected 0
58
69
 
59
70
  # Keep Chrome alive for follow-up runs (avoids the 5–10s cold-start cost)
60
71
  node scripts/playground-verify-rule.mjs rules/ast-grep-rules/rules/no-console-except-error.yml \
61
- --code "ignored" --keep-chrome
72
+ --code "console.log('x');" --keep-chrome
62
73
 
63
74
  ```
64
75
 
@@ -142,7 +153,9 @@ Three scripts, one job each:
142
153
  resolution, accessibility tree, and network tracing stripped out.
143
154
  - `playground-verify-rule.mjs` — the CLI the user invokes. Reads the rule,
144
155
  builds the playground URL, runs the page through CDP, scrapes the
145
- result, prints JSON, kills Chrome.
156
+ result, prints JSON, and kills Chrome. Its source-drift sentinel reads only
157
+ the first `.monaco-editor` in the source pane, not the config editor, so a
158
+ rule note cannot satisfy the check by repeating the fixture text.
146
159
 
147
160
  ## Skipping the test
148
161
 
@@ -165,10 +178,6 @@ auto-skipped when Google Chrome is not on `PATH` and
165
178
 
166
179
  ## Known limitations
167
180
 
168
- - **Source is fixed.** The playground uses its own hardcoded sample
169
- source. Custom source code passed via `--code` is ignored by the
170
- upstream playground in `mode: "Config"`. This is by design — the
171
- playground is a manual tool, not a test harness.
172
181
  - **Slow.** ~11s per rule. Don't enable in the default `npm test` run.
173
182
  - **Line numbers are best-effort.** The playground's gutter layout
174
183
  changes between builds. If the line-extraction JS misses, `lines` is
@@ -0,0 +1,222 @@
1
+ # Configuring pi-lens
2
+
3
+ There are **two** pi-lens config files:
4
+
5
+ | File | Scope | Notes |
6
+ | --- | --- | --- |
7
+ | `.pi-lens.json` | the project | Committed or not, your call. Nearest one wins **per field** — a package can override one setting without restating the repo root's. |
8
+ | `~/.pi-lens/config.json` | the machine | Your defaults across every project. `PI_LENS_CONFIG_PATH` relocates it. |
9
+
10
+ Both files have the same shape, with one exception noted below the example:
11
+ everything LSP-related lives under an `lsp` namespace inside them.
12
+
13
+ ```jsonc
14
+ {
15
+ "$schema": "https://raw.githubusercontent.com/apmantza/pi-lens/master/docs/schema/pi-lens-config-v1.json",
16
+ "ignore": ["dist/**"],
17
+ "maxProjectFiles": 8000,
18
+ "rules": { "high-complexity": { "threshold": 25 } },
19
+ "lsp": {
20
+ "disabledServers": ["typos"],
21
+ "warmFiles": ["src/main.rs"],
22
+ "servers": {
23
+ "my-server": {
24
+ "name": "My Custom LSP",
25
+ "extensions": [".myext"],
26
+ "command": "my-lsp-server",
27
+ "args": ["--stdio"]
28
+ }
29
+ },
30
+ "serverOverrides": {
31
+ "rust": {
32
+ "initializationOptions": { "check": { "command": "clippy" } }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ **Some settings are global-only.** A handful of switches — `lsp.enabled`
40
+ (`--no-lsp`), `tests.enabled`, `delta.enabled` and the other session-wide
41
+ toggles — are decided once for the machine, not per project, so writing one in a
42
+ `.pi-lens.json` does nothing. It is not ignored quietly: the project loader says
43
+ so, naming the key. `docs/settings.md` lists which flags are which.
44
+
45
+ ## Which file wins
46
+
47
+ One order, lowest precedence first. A later tier replaces an earlier tier's
48
+ value **for that field only** — objects are merged field-wise, never replaced
49
+ whole, so setting one key never silently drops the rest of a section.
50
+
51
+ 1. **global** — `~/.pi-lens/config.json`.
52
+ 2. **project root** — the outermost `.pi-lens.json` at or above your working
53
+ directory.
54
+ 3. **nested-project** — every `.pi-lens.json` between that root and your working
55
+ directory, outermost first. The nearest file wins, per field.
56
+
57
+ Those three are the tiers the config **files** resolve through, and they are the
58
+ only ones this resolution populates. Four more tiers are reserved in the
59
+ precedence table — `builtin` below them, and `env`, `cli`, `host` above — and
60
+ nothing writes into them yet; #2427 (env/CLI) and #2416 (host and project trust)
61
+ are what fill them in.
62
+
63
+ Until they do, environment variables and CLI flags are read by their own
64
+ accessors rather than through this resolution, and their effective precedence
65
+ for a pi-lens toggle is:
66
+
67
+ 1. a `PI_LENS_*` environment variable set to `1` — checked first, and it wins
68
+ outright;
69
+ 2. the matching `--lens-*` / `--no-*` CLI flag;
70
+ 3. the nearest project `.pi-lens.json`, then the outer ones (project-scoped
71
+ settings only);
72
+ 4. `~/.pi-lens/config.json`;
73
+ 5. the built-in default.
74
+
75
+ Subsystem-specific env overrides follow the same shape: a
76
+ `PI_LENS_REVIEW_GRAPH_MAX_FILES` beats a `.pi-lens.json`'s
77
+ `reviewGraph.maxFiles`. `docs/environment-variables.md` and `docs/settings.md`
78
+ are the per-setting references.
79
+
80
+ ### One exception: `lsp.disabledServers` is a denial, not a value
81
+
82
+ Ordinary settings are last-tier-wins. A **denial** is not, because the tier
83
+ that made it is usually the one you control and the tier that would override it
84
+ is usually one that arrived with somebody else's checkout.
85
+
86
+ `lsp.disabledServers` resolves as the **union of every tier's entries**. A
87
+ project `.pi-lens.json` can add to it and can never subtract from it, so a
88
+ repository cannot re-enable a server you turned off in
89
+ `~/.pi-lens/config.json`. There is no vocabulary for un-denying an entry: if
90
+ you change your mind, edit the file that denied. The provenance reports, per
91
+ denied server, the tier that contributed it.
92
+
93
+ ```console
94
+ $ # which servers run for this file, and why
95
+ $ pilens_effective_config file=src/main.rs
96
+ ✗ typos — disabled-by-config (global ~/.pi-lens/config.json → /lsp/disabledServers/0)
97
+ ```
98
+
99
+ The union spans **both spellings**: a document's deprecated root keys
100
+ (`servers`, `serverOverrides`, `disabledServers`, `warmFiles`) are read
101
+ into the `lsp` namespace before any tier is merged, so one setting is resolved
102
+ once no matter which spelling each file uses. Migrating does not change the
103
+ answer, staying un-migrated is not a way around the denial, and a half-migrated
104
+ pair of files merges rather than one clobbering the other.
105
+
106
+ Two rules make the rest of the table unambiguous:
107
+
108
+ - **The search stops at `$HOME`.** pi-lens never reads a config file in your
109
+ home directory or above it. A stray `pi-lens.json` in `$HOME` (or at `C:\`)
110
+ is not adopted by every project on the machine. The machine-global file is
111
+ read by its own path, so it is unaffected.
112
+ - **The canonical spelling wins.** Where a legacy file or a legacy key means the
113
+ same thing as the canonical one, the canonical one is used — otherwise the
114
+ migration below could never be completed.
115
+
116
+ ## Legacy locations (still read; being removed)
117
+
118
+ These are read for their deprecation window and then **removed**. Each one you
119
+ still have produces one warning per setting, naming exactly where to move it —
120
+ carrying the stable code `PILENS_CFG_0003` (a deprecated file) or
121
+ `PILENS_CFG_0002` (a deprecated key), so you can match or suppress on the code
122
+ rather than on the prose.
123
+
124
+ | Legacy | Move it to | Code |
125
+ | --- | --- | --- |
126
+ | `.pi-lens/lsp.json` | `.pi-lens.json` → `lsp.*` | `PILENS_CFG_0003` |
127
+ | `pi-lsp.json` | `.pi-lens.json` → `lsp.*` | `PILENS_CFG_0003` |
128
+ | `pi-lens.json` (undotted) | `.pi-lens.json` | `PILENS_CFG_0003` |
129
+ | `~/.pi-lens/lsp.json` | `~/.pi-lens/config.json` → `lsp.*` | `PILENS_CFG_0003` |
130
+ | `servers` at the file root | `lsp.servers` | `PILENS_CFG_0002` |
131
+ | `serverOverrides` at the file root | `lsp.serverOverrides` | `PILENS_CFG_0002` |
132
+ | `disabledServers` at the file root | `lsp.disabledServers` | `PILENS_CFG_0002` |
133
+ | `warmFiles` at the file root | `lsp.warmFiles` | `PILENS_CFG_0002` |
134
+
135
+ **Deprecated since 4.1.4. Read for the last time before 5.0.0.** The window is
136
+ declared as data in `clients/config-diagnostic-codes.ts`
137
+ (`DEPRECATED_CONFIG_SURFACES`) and enforced by test, so the schedule above and
138
+ the code cannot drift apart. `docs/public-api-stability.md` describes the policy
139
+ these dates instantiate.
140
+
141
+ A `.pi-lens.json` that mixes both spellings is fine while you migrate: the
142
+ canonical key wins, and the keys you have not moved yet keep working.
143
+
144
+ Only keys pi-lens actually recognizes get "move it to …" advice. A key in a
145
+ legacy file that is not a pi-lens setting at all — a typo, or a leftover from
146
+ another tool — cannot be migrated anywhere, so it gets the ordinary
147
+ unrecognized-key notice (`PILENS_CFG_0001`) and is counted in ONE whole-file
148
+ `PILENS_CFG_0003` notice for the file rather than being told to move.
149
+
150
+ ## When a config is ignored
151
+
152
+ A file that cannot be read or parsed is **ignored, never partially applied** —
153
+ pi-lens runs on defaults for it and says so once, with the code
154
+ `PILENS_CFG_0001`. A field whose value does not match its declared type is
155
+ dropped on its own (`PILENS_CFG_0005`), and an unrecognized field is dropped
156
+ with a message naming the key (`PILENS_CFG_0004`). If resolving a file fails
157
+ internally the whole file is ignored and said so under its own code
158
+ (`PILENS_CFG_0008`), so "one field went missing" and "none of this file is in
159
+ effect" are never the same code. Nothing about your config is ever ignored
160
+ silently.
161
+
162
+ The number of notices one file can produce is bounded PER NOTICE LIST, because
163
+ the number of keys in a file is not. There are two lists, split by who composes
164
+ them rather than by what they say — both are about values that were rejected:
165
+
166
+ - what **resolving** the file produced — the per-field rejections
167
+ (`PILENS_CFG_0004`, `PILENS_CFG_0005`, `PILENS_CFG_0006`) together with the
168
+ deprecation notices (`PILENS_CFG_0002`, `PILENS_CFG_0003`), which share this
169
+ list;
170
+ - what the **loader** reading the file produced on its own — unknown top-level
171
+ keys and settings it refused (`PILENS_CFG_0001`).
172
+
173
+ Each list is bounded at 20 records: up to 19 notices plus, when the bound bit,
174
+ a single `PILENS_CFG_0007` summary giving the count that was suppressed, so a
175
+ truncated list always says that it is truncated. That summary is about the
176
+ LIST, not about the file: a config whose every setting was applied can still
177
+ overflow the bound, so it is worded and recorded as a summary rather than as an
178
+ ignored config.
179
+
180
+ One notice is never suppressed by that bound: `PILENS_CFG_0008`, which says the
181
+ whole file is out of effect. It is not one more rejected key competing for a
182
+ slot — it is what tells you the rejections above it are no longer the whole
183
+ story — so it is kept however full the list already was.
184
+
185
+ ## Asking what is actually in effect
186
+
187
+ You never have to reconstruct the table above by hand. `pilens_effective_config`
188
+ (MCP) and `effective_config` (pi) return the resolved configuration with the
189
+ provenance of **every** leaf — the tier, the file, the key, and the trust
190
+ decision that applied — plus, for a file you name, its language, every LSP
191
+ server with the reason it was selected or denied, and the runners that would
192
+ dispatch. That is the answer to "why is this running" and to "why is this *not*
193
+ running", without reading a log.
194
+
195
+ Naming a `file` resolves the configuration **at that file's own directory**,
196
+ which is where the runtime decides from — so a nested `repo/sub/.pi-lens.json`
197
+ layer contributes to the answer, appears in the reported document list, and is
198
+ named as the file behind any decision it made. The walk runs upward and is
199
+ **confined to `cwd`**: a `file` that resolves outside `cwd` — including a
200
+ sibling package in the same monorepo — is rejected rather than answered from
201
+ its own unrelated tree, because the per-file answer is only ever correct when
202
+ it is a superset of the workspace's own; the rejection names the `cwd` it was
203
+ measured against and the remedy is to re-query with `cwd` set to that file's
204
+ own workspace. A confined `file` always passes back through the workspace's
205
+ own documents on its way up, so they are always included too.
206
+
207
+ It reports **sources, never values**. Environment values never appear, a custom
208
+ server's command line is cut to the binary itself, and every path is rewritten
209
+ `~`-relative. There is no un-redacted mode: the un-redacted data is the config
210
+ file you already have.
211
+
212
+ `pilens_health` embeds the same provenance as **counts per tier** — how many
213
+ settings each source decided, and which `PILENS_CFG_*` notices the resolution
214
+ produced — so a session's config posture is visible without the detail.
215
+
216
+ ## See also
217
+
218
+ - `docs/globalconfig.md` — every key of `~/.pi-lens/config.json`, in detail.
219
+ - `docs/settings.md` — the CLI flags and what they map to.
220
+ - `docs/environment-variables.md` — the `PI_LENS_*` tier.
221
+ - `docs/public-api-stability.md` — what `x-stability`, the `PILENS_CFG_*` codes,
222
+ and the deprecation windows commit pi-lens to.
@@ -40,9 +40,9 @@ Auto-install behavior depends on gate type:
40
40
  | `intelephense` | PHP LSP | Yes | Flow-gated |
41
41
  | `bash-language-server` | Bash LSP | Yes | Language-default |
42
42
  | `yaml-language-server` | YAML LSP | Yes | Language-default |
43
- | `vscode-langservers-extracted` | JSON/ESLint/CSS/HTML LSP | Yes | Language-default |
44
- | `vscode-css-languageserver` | CSS LSP | Yes | Language-default |
45
- | `vscode-html-languageserver-bin` | HTML LSP | Yes | Language-default |
43
+ | `vscode-json-language-server` | JSON LSP (`vscode-langservers-extracted`) | Yes | Language-default |
44
+ | `vscode-css-languageserver` | CSS LSP (`vscode-langservers-extracted`) | Yes | Language-default |
45
+ | `vscode-html-languageserver-bin` | HTML LSP (`vscode-langservers-extracted`) | Yes | Language-default |
46
46
  | `svelte-language-server` | Svelte LSP | Yes | Flow-gated |
47
47
  | `@vue/language-server` | Vue LSP | Yes | Flow-gated |
48
48
  | `opengrep` | Experimental security dispatch | Auto-install | Local config / explicit opt-in |
package/docs/features.md CHANGED
@@ -53,6 +53,43 @@ so the final state is formatter-stable. A `write` immediately followed by an
53
53
  deferred too. See `clients/pipeline.ts`, `clients/runtime-tool-result.ts`, and
54
54
  `clients/runtime-agent-end.ts`.
55
55
 
56
+ A tool pi-lens does not name is classified by the SHAPE of its arguments
57
+ (`clients/mutating-tool.ts`). A host or extension edit tool called `replace` or
58
+ `insert` therefore gets a turn-state entry, a change-log receipt attributed to
59
+ the tool itself, and the deferred autofix and format pass — the same chain
60
+ `edit` gets. Its lines are resolved when the anchor is unambiguous (roughly
61
+ two-thirds of anchors in practice — `clients/hashline-anchor.ts`); otherwise
62
+ the mutation is recorded whole-file with lines unknown, and the
63
+ read-before-edit guard takes its no-line-info arm rather than guessing.
64
+ Deferred is the default for any edit-shaped tool pi-lens cannot place, because
65
+ formatting between the calls of a multi-step rewrite fights the tool that is
66
+ still writing. A tool whose SHAPE is unrecognized too is caught by observation:
67
+ pi-lens takes a bounded snapshot of the path that call names — and only that
68
+ path, so a change to a neighbouring file is never blamed on it — replays
69
+ whatever actually changed through the same chain, then remembers that tool as
70
+ mutating for the session, and on disk under the project's data directory once a
71
+ second observation confirms it, so later sessions classify it by name with no
72
+ snapshot at all. A tool that names no file is caught at `agent_settled` by an
73
+ incremental content check over the files pi-lens has already read, written,
74
+ diagnosed or opened on a language server: a rotating window of the set each
75
+ turn, reading only the files whose size or timestamp actually moved, so the
76
+ check stays affordable as the set grows. A file it has never seen has no
77
+ baseline and is therefore not covered, and a file it cannot verify is named
78
+ rather than reformatted on a timestamp alone
79
+ (`clients/observed-mutation.ts`).
80
+
81
+ **Bundled fallback lint configs stay conservative.** When a project ships no
82
+ tool config, the package-owned fallback configs set the rules (`config/biome/core.jsonc`,
83
+ `config/ruff/core.toml`, `config/markdownlint/core.json`). The biome fallback
84
+ disables `useImportType`: its safe fix rewrites a value import used only in
85
+ type positions into `import type`, which erases the runtime binding that
86
+ experimental decorator metadata (`emitDecoratorMetadata`) still needs and
87
+ breaks decorator-based dependency injection (refs #2385). Every other
88
+ recommended rule stays on, and an explicit project `biome.json(c)` remains
89
+ authoritative: if you enable `useImportType` there, pi-lens does not override
90
+ it. The ruff fallback selects no flake8-type-checking (TC) rules and applies
91
+ only safe fixes, so it never hoists imports into `TYPE_CHECKING` blocks.
92
+
56
93
  Deferred formatting (the `agent_end` default) runs with **bounded
57
94
  concurrency**: at most three formatter subprocesses in flight at once, with
58
95
  results applied in admission order and cooperative yields between files, so a
@@ -140,12 +177,14 @@ pi-lens MCP server expose the same shape to Claude Code / any MCP client.
140
177
 
141
178
  ### Actionable Warnings
142
179
 
143
- At `turn_end`, pi-lens writes `.pi-lens/cache/actionable-warnings.json` summarizing fixable warnings introduced by the current turn. This powers the optional conservative autofix at `agent_end`.
180
+ At `turn_end`, pi-lens writes `<project-data-dir>/cache/actionable-warnings.json` summarizing fixable warnings introduced by the current turn. This powers the optional conservative autofix at `agent_end`.
181
+
182
+ `<project-data-dir>` is whatever `getProjectDataDir(cwd)` resolves to: `<project>/.pi-lens` only when that legacy directory already exists, otherwise `~/.pi-lens/projects/<project-slug>` (or a `PILENS_DATA_DIR` location). The turn-end advisory points at `lens_diagnostics mode=delta` first and names the resolved file second, so you never have to work the layout out by hand (#2521).
144
183
 
145
184
  **Report contents:**
146
185
 
147
186
  - Warnings are delta-only by default: only diagnostics in lines touched during the current turn are included. Pass `--lens-actionable-warning-all` to report all warnings regardless of location
148
- - Each warning carries a stable `aw:<hash>` ID derived from file, rule, and message, so suppression state persists across turns in `.pi-lens/cache/actionable-warning-state.json`
187
+ - Each warning carries a stable `aw:<hash>` ID derived from file, rule, and message, so suppression state persists across turns in `<project-data-dir>/cache/actionable-warning-state.json`
149
188
  - Sources: pipeline `fixable` diagnostics (always included) and LSP code-action warnings when `--lens-actionable-warning-actions` is set
150
189
  - When warnings are present, a concise advisory is injected into the agent context (no blocker language)
151
190
 
@@ -354,14 +393,14 @@ pi-lens ships an MCP (Model Context Protocol) server so Claude Code — or any M
354
393
 
355
394
  **Why a second host:** the pi extension's tools are registered via the host SDK and run on pi's event loop. Claude Code lives in a different process with no SDK access. The MCP server sits in that gap, speaking JSON-RPC over stdio (or a warm Unix socket / Windows named pipe side-channel for the Claude Code PostToolUse hook). It's the easiest way to live-test, debug, and dogfood pi-lens — including running a **review loop** where Claude commits to pi-lens and re-measures.
356
395
 
357
- **16 tools, grouped by lifecycle layer** (the same three layers the pi agent hooks use):
396
+ **18 tools, grouped by lifecycle layer** (the same three layers the pi agent hooks use):
358
397
 
359
398
  | Layer | MCP tools | What they expose |
360
399
  |---|---|---|
361
- | **Per-edit** | `pilens_analyze`, `pilens_lsp_diagnostics`, `pilens_lsp_navigation`, `pilens_ast_grep_search`, `pilens_ast_grep_replace`, `pilens_module_report`, `pilens_read_symbol` | The fast pipeline (format → autofix → LSP diagnostics → parallel runners) plus the structured read-substitute pair. `analyze` accepts `mode: warm \| fresh` — `warm` reuses the server's in-process LSP, `fresh` forks a worker that loads freshly-built code from disk so the result reflects the latest commit. |
400
+ | **Per-edit** | `pilens_analyze`, `pilens_lsp_diagnostics`, `pilens_lsp_navigation`, `pilens_ast_grep_search`, `pilens_ast_grep_replace`, `pilens_module_report`, `pilens_read_symbol`, `pilens_read_enclosing` | The fast pipeline (format → autofix → LSP diagnostics → parallel runners) plus the structured read-substitute pair. `analyze` accepts `mode: warm \| fresh` — `warm` reuses the server's in-process LSP, `fresh` forks a worker that loads freshly-built code from disk so the result reflects the latest commit. |
362
401
  | **Per-turn** | `pilens_turn_end` | Drives the **real** `handleTurnEnd` (knip incremental, dep-circular, cascade, tests, actionable+code-quality warnings) — not a re-implementation. Caller-supplied edited files are auto-registered into turn-state via `addModifiedRange`. |
363
402
  | **Per-session** | `pilens_session_start` | Drives the **real** `handleSessionStart` — full jscpd/knip/madge/govulncheck/gitleaks/trivy scans + complexity baselines + LSP warm. The error-debt baseline is not currently populated by the production session-start path. |
364
- | **Project / observability** | `pilens_project_scan`, `pilens_diagnostics`, `pilens_health`, `pilens_latency`, `pilens_symbol_search` | Cheap project-wide scans, cached diagnostic state, latency telemetry, ranked identifier search (BM25 over the persisted word index — see [docs/word-index.md](word-index.md)). Cross-file blast radius now lives in `pilens_module_report`'s `blastRadius` option. `pilens_health` (and its pi-side `/lens-health` counterpart) also reports a bounded, process-local **degradation ledger** — trust refusals, mode suppressions, LSP breaker trips, formatter skips/failures, TypeScript/word-index/review-graph/project-snapshot idle evictions, WASM aborts, and diagnostics-timeout tallies — so silently degraded behavior stays visible instead of vanishing into a log. |
403
+ | **Project / observability** | `pilens_project_scan`, `pilens_project_report`, `pilens_diagnostics`, `pilens_health`, `pilens_latency`, `pilens_symbol_search`, `pilens_effective_config` | Cheap project-wide scans, cached diagnostic state, latency telemetry, ranked identifier search (BM25 over the persisted word index — see [docs/word-index.md](word-index.md)). Cross-file blast radius now lives in `pilens_module_report`'s `blastRadius` option. `pilens_health` (and its pi-side `/lens-health` counterpart) also reports a bounded, process-local **degradation ledger** — trust refusals, mode suppressions, LSP breaker trips, formatter skips/failures, TypeScript/word-index/review-graph/project-snapshot idle evictions, WASM aborts, and diagnostics-timeout tallies — so silently degraded behavior stays visible instead of vanishing into a log. `pilens_effective_config` answers **“why is X running / why is X not running”** from one query — the resolved configuration with the provenance of every setting, and for a file you name, its language plus every LSP server with the reason it was selected or denied and the runners that would dispatch. It reports sources, never values. |
365
404
  | **Lifecycle / loop** | `pilens_rebuild` | Runs `npm run build:dist` so `pilens_analyze mode=fresh` reflects the latest commit. Makes the review loop self-contained: commit → `pilens_rebuild` → `pilens_analyze mode=fresh` → `pilens_latency`. |
366
405
 
367
406
  **Honest limits** (live-tested, documented in `mcp.md`):
@@ -17,8 +17,8 @@ Dispatch is diagnostics-oriented: automatic formatting and safe autofix happen i
17
17
  | Shell | ✓ | lsp, shellcheck | shfmt |
18
18
  | Fish | ✓ (fish-lsp) | lsp, fish-indent | fish_indent |
19
19
  | CSS/SCSS/Less | ✓ | lsp, stylelint | biome, prettier |
20
- | HTML | ✓ | lsp, htmlhint | prettier |
21
- | YAML | ✓ | lsp, yamllint, actionlint (GitHub workflows), trivy-config (opt-in; Kubernetes manifests, CloudFormation) | prettier |
20
+ | HTML | ✓ | lsp, htmlhint | prettier (with project config) |
21
+ | YAML | ✓ | lsp, yamllint, actionlint (GitHub workflows), trivy-config (opt-in; Kubernetes manifests, CloudFormation) | prettier (with project config) |
22
22
  | JSON | ✓ | lsp, trivy-config (opt-in; CloudFormation templates only) | biome, prettier |
23
23
  | Svelte | ✓ | lsp | oxfmt (needs `svelte` pkg installed + config `svelte: true`) |
24
24
  | Vue | ✓ | lsp | prettier, oxfmt |
@@ -0,0 +1,25 @@
1
+ # Fixer contract
2
+
3
+ Deliver a root-caused fix with red proof and a reviewable handoff.
4
+
5
+ Read the issue, repository instructions, shared delegated worker contract, and
6
+ relevant architecture before editing. Reuse the shared seam and existing
7
+ machinery. Keep the change localized and compatible with concurrent branches.
8
+
9
+ Build the smallest faithful reproduction first. Preserve its pre-fix failure
10
+ output. After fixing, prove every new guard mutation-sensitive. Run a pattern
11
+ sweep and a population sweep for the defect class. Record per-member verdicts,
12
+ the blast radius, and bounded observability. Add a changelog fragment for a code
13
+ change.
14
+
15
+ ## Tautological tests considered harmful
16
+
17
+ Do not assert a value that the test setup already supplied, duplicate the source
18
+ predicate in the test, or replace a real in-process seam with a fake to keep the
19
+ test green. Drive the production path and assert an independent observable. If
20
+ the test passes after deleting the guard, it is tautological and must be
21
+ redesigned before the fix is complete.
22
+
23
+ Verify the build and every targeted or sibling suite required by repository
24
+ policy. Follow the shared contract's Git authority. Report what ran, what was
25
+ skipped, and why. Use active, plain prose.