pi-lens 3.8.73 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/CHANGELOG.md +2614 -12
  2. package/README.md +50 -21
  3. package/dist/clients/actionable-warnings.js +73 -18
  4. package/dist/clients/agent-behavior-client.js +21 -4
  5. package/dist/clients/ast-grep-client.js +68 -16
  6. package/dist/clients/ast-grep-types.js +0 -1
  7. package/dist/clients/ast-grep-yaml-synth.js +37 -10
  8. package/dist/clients/atomic-write-staging.js +63 -0
  9. package/dist/clients/atomic-write.js +116 -16
  10. package/dist/clients/bash-file-access.js +193 -23
  11. package/dist/clients/biome-client.js +29 -25
  12. package/dist/clients/bootstrap.js +11 -2
  13. package/dist/clients/bounded-cache.js +34 -0
  14. package/dist/clients/bounded-pid-file-lock.js +192 -0
  15. package/dist/clients/bus-events-logger.js +5 -1
  16. package/dist/clients/bus-publish.js +10 -3
  17. package/dist/clients/cache/rule-cache.js +89 -6
  18. package/dist/clients/cache-manager.js +97 -9
  19. package/dist/clients/cache-observability.js +498 -0
  20. package/dist/clients/call-graph.js +460 -108
  21. package/dist/clients/cascade-logger.js +5 -1
  22. package/dist/clients/child-unref.js +85 -0
  23. package/dist/clients/codebase-model.js +52 -23
  24. package/dist/clients/collateral-test-role.js +40 -0
  25. package/dist/clients/complexity-client.js +6 -8
  26. package/dist/clients/console-guard-install.js +14 -0
  27. package/dist/clients/cooperative-budget.js +42 -0
  28. package/dist/clients/dead-code-client.js +2 -1
  29. package/dist/clients/debug-handles.js +206 -0
  30. package/dist/clients/debug-heap.js +167 -0
  31. package/dist/clients/degradation-ledger.js +153 -0
  32. package/dist/clients/dependency-checker.js +466 -63
  33. package/dist/clients/diagnostic-dispositions.js +83 -13
  34. package/dist/clients/diagnostics-publish.js +10 -3
  35. package/dist/clients/dispatch/auxiliary-lsp.js +34 -0
  36. package/dist/clients/dispatch/dispatcher.js +71 -13
  37. package/dist/clients/dispatch/fact-store.js +11 -0
  38. package/dist/clients/dispatch/facts/function-facts.js +37 -7
  39. package/dist/clients/dispatch/facts/import-facts.js +108 -119
  40. package/dist/clients/dispatch/facts/tree-sitter-facts.js +12 -12
  41. package/dist/clients/dispatch/facts/try-catch-facts.js +13 -0
  42. package/dist/clients/dispatch/indent-detect.js +35 -0
  43. package/dist/clients/dispatch/inline-suppressions.js +17 -8
  44. package/dist/clients/dispatch/integration.js +1094 -611
  45. package/dist/clients/dispatch/lazy.js +14 -0
  46. package/dist/clients/dispatch/plan.js +11 -1
  47. package/dist/clients/dispatch/rule-id-normalize.js +50 -0
  48. package/dist/clients/dispatch/rule-policy.js +135 -0
  49. package/dist/clients/dispatch/rules/async-noise.js +1 -0
  50. package/dist/clients/dispatch/rules/unsafe-boundary.js +7 -0
  51. package/dist/clients/dispatch/runners/ast-grep-napi.js +47 -6
  52. package/dist/clients/dispatch/runners/biome-check.js +5 -9
  53. package/dist/clients/dispatch/runners/detekt.js +3 -2
  54. package/dist/clients/dispatch/runners/hadolint.js +2 -1
  55. package/dist/clients/dispatch/runners/helm-lint.js +154 -0
  56. package/dist/clients/dispatch/runners/htmlhint.js +3 -2
  57. package/dist/clients/dispatch/runners/index.js +5 -1
  58. package/dist/clients/dispatch/runners/lsp.js +14 -6
  59. package/dist/clients/dispatch/runners/markdownlint.js +4 -9
  60. package/dist/clients/dispatch/runners/psscriptanalyzer.js +10 -1
  61. package/dist/clients/dispatch/runners/pyright.js +9 -5
  62. package/dist/clients/dispatch/runners/ruff.js +4 -8
  63. package/dist/clients/dispatch/runners/shellcheck.js +2 -3
  64. package/dist/clients/dispatch/runners/shfmt.js +2 -3
  65. package/dist/clients/dispatch/runners/spotbugs.js +2 -1
  66. package/dist/clients/dispatch/runners/taplo.js +2 -1
  67. package/dist/clients/dispatch/runners/terragrunt.js +163 -0
  68. package/dist/clients/dispatch/runners/tflint.js +37 -5
  69. package/dist/clients/dispatch/runners/tree-sitter.js +155 -166
  70. package/dist/clients/dispatch/runners/trivy-config.js +13 -7
  71. package/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  72. package/dist/clients/dispatch/runners/utils/runner-helpers.js +284 -34
  73. package/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  74. package/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  75. package/dist/clients/disposition-publish.js +10 -3
  76. package/dist/clients/durable-store.js +97 -0
  77. package/dist/clients/event-loop-monitor.js +94 -3
  78. package/dist/clients/extension-log.js +139 -0
  79. package/dist/clients/extension-mode.js +73 -0
  80. package/dist/clients/feature-hints.js +53 -7
  81. package/dist/clients/file-kinds.js +143 -31
  82. package/dist/clients/file-role.js +18 -3
  83. package/dist/clients/file-utils.js +56 -34
  84. package/dist/clients/fix-worklog.js +14 -2
  85. package/dist/clients/format-events-publish.js +16 -3
  86. package/dist/clients/format-service.js +24 -8
  87. package/dist/clients/formatters-lazy.js +8 -0
  88. package/dist/clients/formatters.js +358 -61
  89. package/dist/clients/generated-artifacts.js +169 -15
  90. package/dist/clients/git-guard.js +681 -11
  91. package/dist/clients/git-tracked-ignore.js +4 -0
  92. package/dist/clients/go-client.js +2 -1
  93. package/dist/clients/govulncheck-client.js +8 -0
  94. package/dist/clients/grammar-source.js +12 -4
  95. package/dist/clients/gzip-stage-write.js +102 -0
  96. package/dist/clients/host-ports.js +31 -0
  97. package/dist/clients/installer/index.js +733 -213
  98. package/dist/clients/instance-reaper.js +142 -172
  99. package/dist/clients/jscpd-client.js +31 -54
  100. package/dist/clients/knip-client.js +78 -36
  101. package/dist/clients/language-policy.js +14 -2
  102. package/dist/clients/language-profile.js +72 -77
  103. package/dist/clients/latency-logger.js +28 -16
  104. package/dist/clients/lens-config.js +140 -166
  105. package/dist/clients/lens-engine.js +124 -21
  106. package/dist/clients/lens-events.js +27 -4
  107. package/dist/clients/lens-flag-registry.js +293 -0
  108. package/dist/clients/lens-map.js +96 -26
  109. package/dist/clients/live-bus-emitter.js +30 -0
  110. package/dist/clients/log-cleanup.js +8 -18
  111. package/dist/clients/lsp/cascade-tier.js +23 -3
  112. package/dist/clients/lsp/client.js +566 -57
  113. package/dist/clients/lsp/config.js +74 -10
  114. package/dist/clients/lsp/diagnostic-binding.js +86 -0
  115. package/dist/clients/lsp/edits.js +1012 -121
  116. package/dist/clients/lsp/index.js +1840 -803
  117. package/dist/clients/lsp/language.js +4 -1
  118. package/dist/clients/lsp/launch.js +40 -32
  119. package/dist/clients/lsp/path-utils.js +1 -1
  120. package/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  121. package/dist/clients/lsp/server.js +279 -60
  122. package/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  123. package/dist/clients/lsp-document-symbols.js +112 -0
  124. package/dist/clients/lsp-lazy.js +8 -0
  125. package/dist/clients/lsp-mutation.js +248 -0
  126. package/dist/clients/mcp/analyze.js +128 -36
  127. package/dist/clients/mcp/ipc.js +157 -24
  128. package/dist/clients/mcp/review.js +36 -3
  129. package/dist/clients/mcp/session.js +216 -8
  130. package/dist/clients/memory-sampler.js +115 -0
  131. package/dist/clients/metrics-client.js +2 -1
  132. package/dist/clients/metrics-history.js +2 -1
  133. package/dist/clients/module-report-lsp.js +5 -0
  134. package/dist/clients/module-report.js +267 -82
  135. package/dist/clients/ndjson-logger.js +334 -103
  136. package/dist/clients/opengrep-client.js +1 -1
  137. package/dist/clients/partial-edit-apply.js +120 -5
  138. package/dist/clients/path-keyed-map.js +76 -0
  139. package/dist/clients/path-utils.js +141 -5
  140. package/dist/clients/performance-report.js +327 -0
  141. package/dist/clients/pipeline.js +54 -13
  142. package/dist/clients/print-mode.js +21 -0
  143. package/dist/clients/project-changes.js +117 -4
  144. package/dist/clients/project-diagnostics/cache.js +3 -2
  145. package/dist/clients/project-diagnostics/extractors.js +26 -115
  146. package/dist/clients/project-diagnostics/fresh-fetch.js +149 -19
  147. package/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +117 -0
  148. package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +37 -5
  149. package/dist/clients/project-diagnostics/scanner.js +268 -128
  150. package/dist/clients/project-lens-config.js +138 -34
  151. package/dist/clients/project-report.js +64 -25
  152. package/dist/clients/project-scale.js +8 -10
  153. package/dist/clients/project-snapshot-persist-worker.js +6 -0
  154. package/dist/clients/project-snapshot.js +767 -8
  155. package/dist/clients/project-trust.js +181 -0
  156. package/dist/clients/quiet-window.js +27 -5
  157. package/dist/clients/read-bridge.js +147 -0
  158. package/dist/clients/read-expansion.js +22 -12
  159. package/dist/clients/read-guard-logger.js +159 -1
  160. package/dist/clients/read-guard-tool-lines.js +150 -29
  161. package/dist/clients/read-guard.js +262 -3
  162. package/dist/clients/redact/secrets.js +449 -0
  163. package/dist/clients/resource-sampler.js +144 -39
  164. package/dist/clients/reverse-deps.js +48 -0
  165. package/dist/clients/review-graph/builder.js +2570 -326
  166. package/dist/clients/review-graph/git-identity.js +2 -1
  167. package/dist/clients/review-graph/import-resolvers.js +21 -10
  168. package/dist/clients/review-graph/persist-worker.js +10 -0
  169. package/dist/clients/review-graph/query.js +5 -0
  170. package/dist/clients/review-graph/shared-extraction-ir.js +90 -0
  171. package/dist/clients/review-graph/symbol-id.js +46 -0
  172. package/dist/clients/review-graph/tsconfig-paths.js +57 -6
  173. package/dist/clients/review-graph/workspace-modules.js +51 -3
  174. package/dist/clients/review-graph-logger.js +53 -0
  175. package/dist/clients/ruff-client.js +27 -44
  176. package/dist/clients/runtime-agent-end.js +111 -75
  177. package/dist/clients/runtime-context.js +36 -12
  178. package/dist/clients/runtime-coordinator.js +76 -8
  179. package/dist/clients/runtime-session.js +799 -137
  180. package/dist/clients/runtime-tool-call.js +117 -62
  181. package/dist/clients/runtime-tool-result.js +218 -23
  182. package/dist/clients/runtime-turn.js +284 -40
  183. package/dist/clients/rust-client.js +2 -1
  184. package/dist/clients/safe-spawn.js +873 -71
  185. package/dist/clients/security-scan-client.js +2 -1
  186. package/dist/clients/session-start-observability.js +15 -0
  187. package/dist/clients/session-state-store.js +5 -3
  188. package/dist/clients/sessionstart-logger.js +18 -0
  189. package/dist/clients/sg-runner.js +284 -159
  190. package/dist/clients/slow-fs.js +2 -1
  191. package/dist/clients/smells-rollup.js +224 -0
  192. package/dist/clients/source-filter.js +231 -43
  193. package/dist/clients/source-walker.js +66 -10
  194. package/dist/clients/startup-scan.js +15 -16
  195. package/dist/clients/startup-timing.js +9 -0
  196. package/dist/clients/test-runner-client.js +38 -8
  197. package/dist/clients/todo-scanner.js +30 -3
  198. package/dist/clients/tool-event.js +35 -0
  199. package/dist/clients/tool-policy.js +388 -39
  200. package/dist/clients/tool-render.js +155 -0
  201. package/dist/clients/tree-sitter-cache.js +153 -140
  202. package/dist/clients/tree-sitter-client.js +1722 -80
  203. package/dist/clients/tree-sitter-logger.js +64 -1
  204. package/dist/clients/tree-sitter-query-loader.js +117 -12
  205. package/dist/clients/tree-sitter-shared.js +48 -19
  206. package/dist/clients/tree-sitter-symbol-extractor.js +115 -11
  207. package/dist/clients/user-notify.js +51 -0
  208. package/dist/clients/vanished-instance-marker.js +57 -0
  209. package/dist/clients/warm-attach.js +33 -13
  210. package/dist/clients/widget-state.js +342 -49
  211. package/dist/clients/word-index-logger.js +44 -0
  212. package/dist/clients/word-index.js +612 -68
  213. package/dist/clients/workspace-topology.js +100 -7
  214. package/dist/index.js +65923 -47161
  215. package/dist/mcp/analyze-cli.js +185 -29
  216. package/dist/mcp/cli.js +110 -0
  217. package/dist/mcp/server.js +272 -50
  218. package/dist/tools/ast-grep-search.js +93 -27
  219. package/dist/tools/lens-diagnostics.js +247 -41
  220. package/dist/tools/lsp-diagnostics.js +153 -40
  221. package/dist/tools/lsp-navigation.js +51 -31
  222. package/dist/tools/module-report.js +9 -0
  223. package/dist/tools/symbol-search.js +21 -2
  224. package/docs/agent-guide.md +247 -0
  225. package/docs/agent-tools.md +21 -10
  226. package/docs/analysisall.md +192 -0
  227. package/docs/api-ports-inventory.md +165 -0
  228. package/docs/ast-grep_rules_catalog.md +5 -2
  229. package/docs/astplayground.md +3 -4
  230. package/docs/audit1.md +5 -5
  231. package/docs/durable-store-audit-1202.md +32 -0
  232. package/docs/environment-variables.md +197 -11
  233. package/docs/fable.md +27 -0
  234. package/docs/features.md +35 -5
  235. package/docs/globalconfig.md +101 -5
  236. package/docs/language-coverage.md +6 -5
  237. package/docs/lsp-capability-matrix.md +2 -2
  238. package/docs/mcp.md +54 -20
  239. package/docs/servercapabilities.md +7 -4
  240. package/docs/settings.md +192 -0
  241. package/docs/tools.md +18 -1
  242. package/docs/tree-sitter_rules_catalog.md +11 -31
  243. package/docs/usage.md +31 -0
  244. package/package.json +139 -138
  245. package/rules/ast-grep-rules/rule-tests/hardcoded-url-js-test.yml +1 -0
  246. package/rules/ast-grep-rules/rule-tests/hardcoded-url-test.yml +2 -0
  247. package/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  248. package/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  249. package/rules/ast-grep-rules/rule-tests/no-case-declarations-js-test.yml +10 -0
  250. package/rules/ast-grep-rules/rule-tests/no-case-declarations-test.yml +10 -0
  251. package/rules/ast-grep-rules/rule-tests/no-javascript-url-js-test.yml +10 -0
  252. package/rules/ast-grep-rules/rule-tests/no-javascript-url-test.yml +10 -0
  253. package/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  254. package/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  255. package/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  256. package/rules/ast-grep-rules/rule-tests/strict-equality-js-test.yml +3 -0
  257. package/rules/ast-grep-rules/rule-tests/strict-equality-test.yml +3 -0
  258. package/rules/ast-grep-rules/rule-tests/strict-inequality-js-test.yml +3 -0
  259. package/rules/ast-grep-rules/rule-tests/strict-inequality-test.yml +4 -0
  260. package/rules/ast-grep-rules/rules/hardcoded-url-js.yml +22 -4
  261. package/rules/ast-grep-rules/rules/hardcoded-url.yml +22 -4
  262. package/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  263. package/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  264. package/rules/ast-grep-rules/rules/no-case-declarations-js.yml +4 -3
  265. package/rules/ast-grep-rules/rules/no-case-declarations.yml +4 -3
  266. package/rules/ast-grep-rules/rules/no-console-except-error-js.yml +10 -0
  267. package/rules/ast-grep-rules/rules/no-console-except-error.yml +10 -0
  268. package/rules/ast-grep-rules/rules/no-javascript-url-js.yml +42 -6
  269. package/rules/ast-grep-rules/rules/no-javascript-url.yml +42 -6
  270. package/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  271. package/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  272. package/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  273. package/rules/ast-grep-rules/rules/strict-equality-js.yml +14 -1
  274. package/rules/ast-grep-rules/rules/strict-equality.yml +14 -1
  275. package/rules/ast-grep-rules/rules/strict-inequality-js.yml +14 -1
  276. package/rules/ast-grep-rules/rules/strict-inequality.yml +14 -1
  277. package/rules/tree-sitter-queries/cpp/no-memset-sensitive-data.yml +9 -3
  278. package/rules/tree-sitter-queries/cpp/no-scoped-lock-without-args.yml +6 -6
  279. package/rules/tree-sitter-queries/css/calc-spacing.yml +8 -6
  280. package/rules/tree-sitter-queries/go/go-mutex-copy.yml +21 -18
  281. package/rules/tree-sitter-queries/go/go-shared-map-write-goroutine.yml +3 -3
  282. package/rules/tree-sitter-queries/java/infinite-loop-java.yml +3 -3
  283. package/rules/tree-sitter-queries/java/junit-call-super.yml +6 -4
  284. package/rules/tree-sitter-queries/java/no-double-checked-locking.yml +10 -8
  285. package/rules/tree-sitter-queries/java/no-field-shadowing.yml +1 -1
  286. package/rules/tree-sitter-queries/java/prepared-statement-valid-indices.yml +2 -3
  287. package/rules/tree-sitter-queries/java/resources-closed.yml +2 -2
  288. package/rules/tree-sitter-queries/java/springboot-default-package.yml +6 -3
  289. package/rules/tree-sitter-queries/java/switch-fall-through.yml +6 -4
  290. package/rules/tree-sitter-queries/java/switch-non-case-labels.yml +4 -3
  291. package/rules/tree-sitter-queries/java/tests-include-assertions.yml +6 -5
  292. package/rules/tree-sitter-queries/javascript/switch-case-termination-js.yml +5 -8
  293. package/rules/tree-sitter-queries/javascript/switch-non-case-labels.yml +3 -2
  294. package/rules/tree-sitter-queries/kotlin/prepared-statement-indices.yml +9 -5
  295. package/rules/tree-sitter-queries/php/this-in-static-context.yml +5 -5
  296. package/rules/tree-sitter-queries/python/exit-signature-check.yml +2 -9
  297. package/rules/tree-sitter-queries/python/in-operator-unsupported.yml +3 -4
  298. package/rules/tree-sitter-queries/python/no-super-torchscript.yml +4 -8
  299. package/rules/tree-sitter-queries/python/notimplemented-boolean-context.yml +1 -5
  300. package/rules/tree-sitter-queries/python/python-empty-except.yml +1 -1
  301. package/rules/tree-sitter-queries/python/return-in-init.yml +0 -2
  302. package/rules/tree-sitter-queries/python/yield-return-outside-function.yml +0 -3
  303. package/rules/tree-sitter-queries/ruby/ruby-command-injection.yml +1 -12
  304. package/rules/tree-sitter-queries/ruby/ruby-eval.yml +1 -1
  305. package/rules/tree-sitter-queries/ruby/ruby-insecure-deserialization.yml +4 -4
  306. package/rules/tree-sitter-queries/ruby/ruby-insecure-random.yml +4 -11
  307. package/rules/tree-sitter-queries/ruby/ruby-open-struct.yml +1 -1
  308. package/rules/tree-sitter-queries/ruby/ruby-string-eval.yml +2 -3
  309. package/rules/tree-sitter-queries/ruby/ruby-weak-hash.yml +4 -4
  310. package/rules/tree-sitter-queries/rust/rust-lock-held-across-await.yml +4 -3
  311. package/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  312. package/rules/tree-sitter-queries/typescript/console-statement.yml +11 -0
  313. package/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +4 -9
  314. package/rules/tree-sitter-queries/typescript/empty-switch-case.yml +13 -9
  315. package/rules/tree-sitter-queries/typescript/infinite-loop.yml +2 -2
  316. package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +10 -1
  317. package/rules/tree-sitter-queries/typescript/switch-case-termination.yml +5 -8
  318. package/rules/tree-sitter-queries/typescript/ts-insecure-random.yml +7 -10
  319. package/rules/tree-sitter-queries/typescript/unsafe-regex.yml +4 -2
  320. package/rules/typos/_typos.toml +17 -0
  321. package/scripts/analyze-pi-lens-logs.mjs +55 -2
  322. package/skills/pi-lens-write-tree-sitter-rule/SKILL.md +5 -0
  323. package/dist/clients/ast-grep-parser.js +0 -86
  324. package/dist/clients/config-enabled-shape.js +0 -39
  325. package/dist/clients/lsp/interactive-install.js +0 -367
  326. package/dist/clients/lsp/lsp-index.js +0 -10
  327. package/dist/clients/lsp/server-strategies.js +0 -5
  328. package/dist/clients/project-metadata.js +0 -690
  329. package/dist/clients/source-groups.js +0 -96
  330. package/dist/clients/types.js +0 -11
  331. package/docs/env_variables.md +0 -10
  332. package/rules/tree-sitter-queries/abap-disabled/delete-where.yml +0 -46
  333. package/rules/tree-sitter-queries/cpp/no-confused-move-forward.yml +0 -59
  334. package/rules/tree-sitter-queries/cpp/noexcept-functions.yml +0 -58
  335. package/rules/tree-sitter-queries/java/mockito-initialized.yml +0 -66
  336. package/rules/tree-sitter-queries/java/no-wait-notify-on-thread.yml +0 -58
  337. package/rules/tree-sitter-queries/java/short-circuit-logic.yml +0 -57
  338. package/rules/tree-sitter-queries/java/spring-session-attributes-setcomplete.yml +0 -75
  339. package/rules/tree-sitter-queries/plsql-disabled/delete-update-where.yml +0 -56
  340. package/rules/tree-sitter-queries/plsql-disabled/end-loop-semicolon.yml +0 -51
  341. package/rules/tree-sitter-queries/plsql-disabled/fetch-bulk-collect-limit.yml +0 -47
  342. package/rules/tree-sitter-queries/plsql-disabled/forallsave-exceptions.yml +0 -51
  343. package/rules/tree-sitter-queries/plsql-disabled/nchar-nvarchar2-bytes.yml +0 -54
  344. package/rules/tree-sitter-queries/plsql-disabled/not-null-initialization.yml +0 -59
  345. package/rules/tree-sitter-queries/plsql-disabled/raise-application-error-codes.yml +0 -50
  346. package/rules/tree-sitter-queries/typescript-disabled/ts-path-traversal.yml +0 -71
@@ -0,0 +1,8 @@
1
+ /** Shared lazy LSP service seam (#1394). */
2
+ let lspPromise;
3
+ export function warmLspService() {
4
+ return (lspPromise ??= import("./lsp/index.js"));
5
+ }
6
+ export function loadLspService() {
7
+ return warmLspService();
8
+ }
@@ -0,0 +1,248 @@
1
+ import * as path from "node:path";
2
+ import { createReadGuardEditBatchSummary, getReadGuardCorrelationId, logReadGuardEvent, } from "./read-guard-logger.js";
3
+ import { appendProjectChange, } from "./project-changes.js";
4
+ import { normalizeMapKey } from "./path-utils.js";
5
+ const MAX_SAMPLES = 100;
6
+ const MAX_SUMMARIES_PER_CONTEXT = 100;
7
+ export function newLspMutationCorrelationId(toolCallId) {
8
+ return getReadGuardCorrelationId(toolCallId ? { toolCallId } : {});
9
+ }
10
+ function allResults(options) {
11
+ return options.results ?? [];
12
+ }
13
+ function combineResults(results) {
14
+ const files = new Set();
15
+ const fileDetails = [];
16
+ const appliedIndexes = [];
17
+ const paths = [];
18
+ let requestedTotal = 0;
19
+ let appliedTotal = 0;
20
+ let textEdits = 0;
21
+ let create = 0;
22
+ let rename = 0;
23
+ let deleteCount = 0;
24
+ for (const result of results) {
25
+ requestedTotal += result.operationTotal;
26
+ appliedTotal += result.appliedOperationTotal;
27
+ textEdits += result.operationCounts.textEdits;
28
+ create += result.operationCounts.create;
29
+ rename += result.operationCounts.rename;
30
+ deleteCount += result.operationCounts.delete;
31
+ for (const index of result.appliedOperationIndexes)
32
+ appliedIndexes.push(index);
33
+ for (const file of result.files) {
34
+ if (!files.has(file)) {
35
+ files.add(file);
36
+ paths.push(file);
37
+ }
38
+ }
39
+ fileDetails.push(...result.fileDetails);
40
+ }
41
+ return {
42
+ requestedTotal,
43
+ appliedTotal,
44
+ appliedIndexes,
45
+ files: [...files],
46
+ fileDetails,
47
+ textEdits,
48
+ create,
49
+ rename,
50
+ delete: deleteCount,
51
+ paths,
52
+ };
53
+ }
54
+ function uniqueDetails(files, fileDetails) {
55
+ const byPath = new Map();
56
+ for (const detail of fileDetails) {
57
+ const key = normalizeMapKey(path.resolve(detail.filePath));
58
+ const previous = byPath.get(key);
59
+ if (!previous) {
60
+ byPath.set(key, detail);
61
+ continue;
62
+ }
63
+ byPath.set(key, {
64
+ filePath: previous.filePath,
65
+ range: previous.range && detail.range
66
+ ? {
67
+ start: Math.min(previous.range.start, detail.range.start),
68
+ end: Math.max(previous.range.end, detail.range.end),
69
+ }
70
+ : previous.range ?? detail.range,
71
+ importsChanged: previous.importsChanged || detail.importsChanged,
72
+ });
73
+ }
74
+ return files.map((filePath) => byPath.get(normalizeMapKey(path.resolve(filePath))) ?? {
75
+ filePath,
76
+ // Resource operations have no already-computed text range. A small
77
+ // range is still enough to invalidate the touched-file turn state;
78
+ // never synchronously re-read the whole file here.
79
+ range: { start: 1, end: 1 },
80
+ importsChanged: true,
81
+ });
82
+ }
83
+ function bookkeepLspMutation(context, files, fileDetails) {
84
+ const details = uniqueDetails(files, fileDetails);
85
+ for (const detail of details) {
86
+ const filePath = path.resolve(detail.filePath);
87
+ if (context.readGuard) {
88
+ try {
89
+ context.readGuard.recordWritten(filePath);
90
+ }
91
+ catch (err) {
92
+ context.dbg?.(`lsp mutation read-guard stamp failed for ${filePath}: ${err}`);
93
+ }
94
+ }
95
+ const runtime = context.runtime;
96
+ if (runtime?.bumpFileSeq) {
97
+ const { projectSeq, fileSeq } = runtime.bumpFileSeq(filePath);
98
+ try {
99
+ appendProjectChange(context.cwd, {
100
+ seq: projectSeq,
101
+ timestamp: new Date().toISOString(),
102
+ sessionId: runtime.telemetrySessionId ?? "unknown",
103
+ turnIndex: runtime.turnIndex ?? 0,
104
+ source: context.source,
105
+ filePath,
106
+ fileSeq,
107
+ changedRange: detail.range,
108
+ });
109
+ }
110
+ catch (err) {
111
+ context.dbg?.(`lsp mutation project change append failed for ${filePath}: ${err}`);
112
+ }
113
+ }
114
+ if (context.cacheManager) {
115
+ try {
116
+ context.cacheManager.addModifiedRange(filePath, detail.range ?? { start: 1, end: 1 }, detail.importsChanged ?? true, context.cwd, runtime?.telemetrySessionId);
117
+ }
118
+ catch (err) {
119
+ context.dbg?.(`lsp mutation modified-range tracking failed for ${filePath}: ${err}`);
120
+ }
121
+ }
122
+ if (context.recordAutofix && context.source === "autofix") {
123
+ const key = normalizeMapKey(filePath);
124
+ const seen = context.autofixRecordedPaths ?? new Set();
125
+ context.autofixRecordedPaths = seen;
126
+ if (!seen.has(key)) {
127
+ if (seen.size >= MAX_SAMPLES) {
128
+ const oldest = seen.values().next().value;
129
+ if (oldest)
130
+ seen.delete(oldest);
131
+ }
132
+ seen.add(key);
133
+ context.recordAutofix(filePath);
134
+ }
135
+ }
136
+ }
137
+ if (context.publishFilesTouched && files.length > 0) {
138
+ context.publishFilesTouched(files);
139
+ }
140
+ }
141
+ function telemetryFor(context, options) {
142
+ const combined = combineResults(allResults(options));
143
+ const requestedTotal = options.requestedTotal ?? combined.requestedTotal;
144
+ const appliedTotal = combined.appliedTotal;
145
+ const requestedIndexes = Array.from({ length: Math.min(requestedTotal, MAX_SAMPLES) }, (_, index) => index);
146
+ const rejectedTotal = Math.max(0, requestedTotal - appliedTotal);
147
+ const rejectedReasons = Array.from({ length: Math.min(rejectedTotal, MAX_SAMPLES) }, (_, index) => ({ index, code: "write_failed" }));
148
+ const status = options.status ?? (appliedTotal > 0 ? "success" : "skipped");
149
+ const editBatchSummary = createReadGuardEditBatchSummary({
150
+ requestedIndexes,
151
+ requestedTotal,
152
+ resolvedIndexes: requestedIndexes,
153
+ resolvedTotal: requestedTotal,
154
+ rejectedReasons,
155
+ rejectedTotal,
156
+ appliedIndexes: combined.appliedIndexes,
157
+ appliedTotal,
158
+ participantIds: [context.correlationId],
159
+ participantTotal: 1,
160
+ commitStatus: status === "failed"
161
+ ? "failed"
162
+ : appliedTotal > 0
163
+ ? "committed"
164
+ : "no_changes",
165
+ terminalStatus: status,
166
+ });
167
+ return {
168
+ editBatchSummary,
169
+ operationCounts: {
170
+ requested: requestedTotal,
171
+ applied: appliedTotal,
172
+ textEdits: combined.textEdits,
173
+ create: combined.create,
174
+ rename: combined.rename,
175
+ delete: combined.delete,
176
+ },
177
+ sampledPaths: combined.paths.slice(0, MAX_SAMPLES),
178
+ sampledPathsTotal: combined.paths.length,
179
+ sampledPathsTruncated: combined.paths.length > MAX_SAMPLES,
180
+ considered: options.considered,
181
+ completed: options.completed,
182
+ failedCount: options.failedCount,
183
+ };
184
+ }
185
+ export function recordLspMutation(context, options = {}) {
186
+ const results = allResults(options);
187
+ const combined = combineResults(results);
188
+ if (options.bookkeep !== false && combined.files.length > 0) {
189
+ bookkeepLspMutation(context, combined.files, combined.fileDetails);
190
+ }
191
+ const telemetry = telemetryFor(context, options);
192
+ if (context.emitSummary !== false) {
193
+ const summaryCount = context.summaryCount ?? 0;
194
+ context.summaryEmitted = true;
195
+ if (summaryCount < MAX_SUMMARIES_PER_CONTEXT) {
196
+ context.summaryCount = summaryCount + 1;
197
+ logReadGuardEvent({
198
+ event: "edit_batch_summary",
199
+ correlationId: context.correlationId,
200
+ filePath: combined.files[0] ?? context.cwd,
201
+ metadata: {
202
+ tool: context.tool,
203
+ source: context.source,
204
+ outcome: telemetry.editBatchSummary.terminalStatus,
205
+ // The outer correlation identifies the soliciting tool call; the
206
+ // bounded sequence distinguishes multiple applyEdit requests within it.
207
+ summaryIndex: summaryCount,
208
+ editBatchSummary: telemetry.editBatchSummary,
209
+ operationCounts: telemetry.operationCounts,
210
+ sampledPaths: telemetry.sampledPaths,
211
+ sampledPathsTotal: telemetry.sampledPathsTotal,
212
+ sampledPathsTruncated: telemetry.sampledPathsTruncated,
213
+ considered: telemetry.considered,
214
+ completed: telemetry.completed,
215
+ failedCount: telemetry.failedCount,
216
+ },
217
+ });
218
+ }
219
+ else if (!context.summaryOverflowed) {
220
+ context.summaryOverflowed = true;
221
+ logReadGuardEvent({
222
+ event: "edit_batch_summary_overflow",
223
+ correlationId: context.correlationId,
224
+ filePath: combined.files[0] ?? context.cwd,
225
+ metadata: {
226
+ tool: context.tool,
227
+ source: context.source,
228
+ summaryLimit: MAX_SUMMARIES_PER_CONTEXT,
229
+ suppressedSummaries: "one or more",
230
+ },
231
+ });
232
+ }
233
+ }
234
+ return telemetry;
235
+ }
236
+ export function recordLspMutationBatch(context, options) {
237
+ const previous = context.emitSummary;
238
+ context.emitSummary = true;
239
+ try {
240
+ return recordLspMutation(context, options);
241
+ }
242
+ finally {
243
+ context.emitSummary = previous;
244
+ }
245
+ }
246
+ export function recordLspMutationOutcome(context, status) {
247
+ return recordLspMutation(context, { status, requestedTotal: 0 });
248
+ }
@@ -13,16 +13,19 @@
13
13
  */
14
14
  import * as fs from "node:fs";
15
15
  import * as path from "node:path";
16
- import { CacheManager } from "../cache-manager.js";
16
+ import { CacheManager, MCP_TURN_STATE_OWNER_ID } from "../cache-manager.js";
17
17
  import { CASCADE_GRAPH_KINDS, dispatchLintWithResult, getLatencyReports, } from "../dispatch/integration.js";
18
18
  import { FactStore } from "../dispatch/fact-store.js";
19
19
  import { detectFileKind } from "../file-kinds.js";
20
20
  import { getDiagnosticTracker } from "../diagnostic-tracker.js";
21
21
  import { getLSPService } from "../lsp/index.js";
22
+ import { PathKeyedMap } from "../path-keyed-map.js";
23
+ import { normalizeMapKey } from "../path-utils.js";
22
24
  import { loadProjectSnapshot } from "../project-snapshot.js";
23
25
  import { buildOrUpdateGraph } from "../review-graph/service.js";
24
26
  import { recordDiagnostics } from "../widget-state.js";
25
27
  import { deserializeWordIndex, removeWordIndexDocument, scheduleWordIndexPersist, updateWordIndexDocument, WORD_INDEX_MAX_BYTES, } from "../word-index.js";
28
+ import { logWordIndex } from "../word-index-logger.js";
26
29
  import { createMcpHost } from "./host-shim.js";
27
30
  // #536: module-scoped FactStore for the warm-analyze graph seam, mirroring the
28
31
  // per-edit cascade path's own module-level `sessionFacts` singleton
@@ -43,7 +46,59 @@ const warmGraphFacts = new FactStore();
43
46
  // nothing usable" (index missing or pre-phase-2/no-forward-map), distinct from
44
47
  // "never checked" (key absent) — avoids re-attempting a snapshot load with no
45
48
  // forward index on every single analyze call.
46
- const warmWordIndexes = new Map();
49
+ const DEFAULT_WORD_INDEX_IDLE_EVICT_MS = 20 * 60_000;
50
+ const DEFAULT_WORD_INDEX_MAX_WARM_ROOTS = 8;
51
+ const warmWordIndexes = new PathKeyedMap(normalizeMapKey);
52
+ function positiveEnv(name, fallback) {
53
+ const parsed = Number.parseInt(process.env[name] ?? "", 10);
54
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
55
+ }
56
+ function idleEvictMs() {
57
+ return positiveEnv("PI_LENS_WORD_INDEX_IDLE_EVICT_MS", DEFAULT_WORD_INDEX_IDLE_EVICT_MS);
58
+ }
59
+ function maxWarmRoots() {
60
+ return positiveEnv("PI_LENS_WORD_INDEX_MAX_WARM_ROOTS", DEFAULT_WORD_INDEX_MAX_WARM_ROOTS);
61
+ }
62
+ function clearWarmWordIndexTimer(entry) {
63
+ if (entry.timer)
64
+ clearTimeout(entry.timer);
65
+ entry.timer = undefined;
66
+ }
67
+ function evictWarmWordIndex(key, entry, reason) {
68
+ if (entry.leases > 0 || warmWordIndexes.get(key) !== entry)
69
+ return false;
70
+ clearWarmWordIndexTimer(entry);
71
+ warmWordIndexes.delete(key);
72
+ logWordIndex({
73
+ phase: "warm_cache_evicted",
74
+ cwd: key,
75
+ trigger: "mcp",
76
+ reason,
77
+ });
78
+ return true;
79
+ }
80
+ function scheduleWarmWordIndexIdleEviction(key, entry) {
81
+ clearWarmWordIndexTimer(entry);
82
+ const generation = ++entry.generation;
83
+ const timer = setTimeout(() => {
84
+ entry.timer = undefined;
85
+ if (warmWordIndexes.get(key) !== entry || entry.generation !== generation)
86
+ return;
87
+ if (!evictWarmWordIndex(key, entry, "idle"))
88
+ scheduleWarmWordIndexIdleEviction(key, entry);
89
+ }, idleEvictMs());
90
+ timer.unref?.();
91
+ entry.timer = timer;
92
+ }
93
+ function enforceWarmWordIndexLruCap() {
94
+ while (warmWordIndexes.size > maxWarmRoots()) {
95
+ const victim = [...warmWordIndexes.entries()]
96
+ .filter(([, entry]) => entry.leases === 0)
97
+ .sort(([, a], [, b]) => a.lastUsedAt - b.lastUsedAt)[0];
98
+ if (!victim || !evictWarmWordIndex(victim[0], victim[1], "lru"))
99
+ return;
100
+ }
101
+ }
47
102
  /**
48
103
  * Look up (loading from the persisted snapshot on first use per cwd) the warm
49
104
  * in-memory word index this analyze facade keeps mutated in place. Exported so
@@ -52,26 +107,57 @@ const warmWordIndexes = new Map();
52
107
  * following a warm `pilens_analyze` call in the SAME process would read a
53
108
  * stale on-disk snapshot until the debounced persist (default 1500ms) flushes.
54
109
  */
55
- export function getOrLoadWarmWordIndex(cwd) {
110
+ export function acquireWarmWordIndex(cwd) {
56
111
  const key = path.resolve(cwd);
57
- if (warmWordIndexes.has(key))
58
- return warmWordIndexes.get(key);
59
- const snapshot = loadProjectSnapshot(key);
60
- const index = deserializeWordIndex(snapshot?.wordIndex) ?? undefined;
61
- // Same phase-2 rule as updateWordIndexForCascade: no forward index ⇒ no
62
- // incremental primitive available, so don't cache it as "usable" — this
63
- // call site's whole point is the incremental single-doc update.
64
- const usable = index?.forward ? index : undefined;
65
- warmWordIndexes.set(key, usable);
66
- return usable;
112
+ let entry = warmWordIndexes.get(key);
113
+ if (!entry) {
114
+ const snapshot = loadProjectSnapshot(key);
115
+ const index = deserializeWordIndex(snapshot?.wordIndex) ?? undefined;
116
+ entry = {
117
+ index: index?.forward ? index : undefined,
118
+ timer: undefined,
119
+ leases: 0,
120
+ lastUsedAt: Date.now(),
121
+ generation: 0,
122
+ };
123
+ warmWordIndexes.set(key, entry);
124
+ }
125
+ entry.leases++;
126
+ entry.lastUsedAt = Date.now();
127
+ scheduleWarmWordIndexIdleEviction(key, entry);
128
+ enforceWarmWordIndexLruCap();
129
+ let released = false;
130
+ return {
131
+ index: entry.index,
132
+ release() {
133
+ if (released)
134
+ return;
135
+ released = true;
136
+ entry.leases = Math.max(0, entry.leases - 1);
137
+ entry.lastUsedAt = Date.now();
138
+ if (warmWordIndexes.get(key) === entry) {
139
+ scheduleWarmWordIndexIdleEviction(key, entry);
140
+ enforceWarmWordIndexLruCap();
141
+ }
142
+ },
143
+ };
67
144
  }
68
145
  /**
69
146
  * Test-only reset — the module-level warm cache otherwise survives across
70
147
  * unrelated test cases in the same vitest worker.
71
148
  */
72
149
  export function _resetWarmWordIndexCacheForTests() {
150
+ for (const entry of warmWordIndexes.values())
151
+ clearWarmWordIndexTimer(entry);
73
152
  warmWordIndexes.clear();
74
153
  }
154
+ export function _getWarmWordIndexCacheStateForTests() {
155
+ return {
156
+ size: warmWordIndexes.size,
157
+ keys: [...warmWordIndexes.keys()],
158
+ timers: [...warmWordIndexes.values()].flatMap((entry) => entry.timer ? [entry.timer] : []),
159
+ };
160
+ }
75
161
  // Generous warm-up budgets: a cold language server needs to spawn AND publish
76
162
  // diagnostics. The per-edit dispatch runner caps these tightly (spawn budget +
77
163
  // 2500ms) for latency; a review tool prioritises completeness, so we pre-warm
@@ -167,11 +253,11 @@ export async function analyzeFile(filePath, cwd, options = {}) {
167
253
  }
168
254
  if (options.registerTurnState) {
169
255
  // Full-file range, importsChanged=true (conservative → dep/knip re-check
170
- // broadly). No sessionId — leaving it unset avoids turn_end's stale-session
171
- // eviction. Best-effort.
256
+ // broadly). Clear any stale pi session ID because the MCP Stop route owns
257
+ // this workspace-scoped worklist independently. Best-effort.
172
258
  try {
173
259
  const lineCount = fs.readFileSync(absPath, "utf8").split("\n").length;
174
- new CacheManager().addModifiedRange(absPath, { start: 1, end: lineCount }, true, cwd);
260
+ new CacheManager().addModifiedRange(absPath, { start: 1, end: lineCount }, true, cwd, options.ownerId ?? MCP_TURN_STATE_OWNER_ID, "mcp");
175
261
  }
176
262
  catch {
177
263
  // unreadable — skip turn-state registration
@@ -210,28 +296,35 @@ export async function analyzeFile(filePath, cwd, options = {}) {
210
296
  // (`scheduleWordIndexPersist`, `PI_LENS_WORD_INDEX_PERSIST_DEBOUNCE_MS`)
211
297
  // pi's path uses — no second persist mechanism.
212
298
  //
213
- // Key shape: `path.resolve(absPath)`, matching the build path's own keys
214
- // (collectWordIndexDocs → collectSourceFilesAsync), NOT normalizeMapKey —
215
- // see updateWordIndexForCascade's doc comment for why a mismatched key
216
- // silently orphans a duplicate entry instead of replacing it.
217
- const warmIndex = getOrLoadWarmWordIndex(cwd);
218
- if (warmIndex) {
219
- try {
220
- const content = fs.readFileSync(absPath, "utf8");
221
- const byteLength = Buffer.byteLength(content, "utf-8");
222
- if (byteLength > WORD_INDEX_MAX_BYTES) {
223
- removeWordIndexDocument(warmIndex, absPath);
299
+ // Key shape: `absPath` (`path.resolve` of the tool-input path). Since #1025
300
+ // the word index's path maps fold every key through `wordIndexKey`
301
+ // INTERNALLY (PathKeyedMap), so this edit-form key and the build path's
302
+ // walk-derived key converge on the same entry regardless of casing/
303
+ // separator — the old duplicate-orphan hazard is gone at the map layer.
304
+ const warmLease = acquireWarmWordIndex(cwd);
305
+ const warmIndex = warmLease.index;
306
+ try {
307
+ if (warmIndex) {
308
+ try {
309
+ const content = fs.readFileSync(absPath, "utf8");
310
+ const byteLength = Buffer.byteLength(content, "utf-8");
311
+ if (byteLength > WORD_INDEX_MAX_BYTES) {
312
+ removeWordIndexDocument(warmIndex, absPath);
313
+ }
314
+ else {
315
+ updateWordIndexDocument(warmIndex, { path: absPath, content });
316
+ }
317
+ scheduleWordIndexPersist(cwd, warmIndex);
224
318
  }
225
- else {
226
- updateWordIndexDocument(warmIndex, { path: absPath, content });
319
+ catch {
320
+ // unreadable/deleted, or an update failure — best-effort, same as the
321
+ // graph update above.
227
322
  }
228
- scheduleWordIndexPersist(cwd, warmIndex);
229
- }
230
- catch {
231
- // unreadable/deleted, or an update failure — best-effort, same as the
232
- // graph update above.
233
323
  }
234
324
  }
325
+ finally {
326
+ warmLease.release();
327
+ }
235
328
  }
236
329
  // dispatchForFile appended a latency report during the call above. Match the
237
330
  // newly-added report for this exact path; fall back to the most recent new
@@ -242,8 +335,7 @@ export async function analyzeFile(filePath, cwd, options = {}) {
242
335
  const lspRunner = latencyReport?.runners.find((runner) => runner.runnerId === "lsp");
243
336
  const lsp = lspRunner
244
337
  ? {
245
- ran: lspRunner.status !== "skipped" &&
246
- lspRunner.status !== "when_skipped",
338
+ ran: lspRunner.status !== "skipped" && lspRunner.status !== "when_skipped",
247
339
  status: lspRunner.status,
248
340
  diagnosticCount: lspRunner.diagnosticCount,
249
341
  durationMs: lspRunner.durationMs,