@herbertgao/pi-extensions 2026.9.0 → 2026.9.1

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 (293) hide show
  1. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +13 -1
  2. package/node_modules/@czottmann/pi-automode/README.md +20 -0
  3. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +1 -1
  4. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +2 -1
  5. package/node_modules/@czottmann/pi-automode/docs/defaults.md +3 -2
  6. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +38 -2
  7. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +5 -3
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +11 -3
  9. package/node_modules/@czottmann/pi-automode/package.json +1 -1
  10. package/node_modules/@pi-plugins/fast-mode/README.md +5 -3
  11. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
  12. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
  13. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  14. package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
  15. package/node_modules/pi-lens/CHANGELOG.md +988 -0
  16. package/node_modules/pi-lens/README.md +3 -0
  17. package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
  18. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
  19. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -2
  20. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
  21. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
  22. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
  23. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
  24. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -2
  25. package/node_modules/pi-lens/dist/clients/bash-file-access.js +1 -3
  26. package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
  27. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -0
  28. package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
  29. package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
  30. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -4
  31. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
  32. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +2 -2
  33. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
  34. package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
  35. package/node_modules/pi-lens/dist/clients/cache-observability.js +44 -18
  36. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
  37. package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -2
  38. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
  39. package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
  40. package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
  41. package/node_modules/pi-lens/dist/clients/config-core/index.js +47 -0
  42. package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
  43. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
  44. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
  45. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +171 -0
  46. package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
  47. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
  48. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
  49. package/node_modules/pi-lens/dist/clients/config-core/schema.js +167 -0
  50. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
  51. package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
  52. package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
  53. package/node_modules/pi-lens/dist/clients/config-schema.js +197 -0
  54. package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
  55. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
  56. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -2
  57. package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
  58. package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
  59. package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
  60. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
  61. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
  62. package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
  63. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
  64. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +14 -4
  65. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -18
  66. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
  67. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -7
  68. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
  69. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +380 -26
  70. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
  71. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -1
  72. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
  73. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
  74. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
  75. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
  76. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
  77. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
  78. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
  79. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
  80. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
  81. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
  82. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
  83. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
  84. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
  85. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
  86. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -1
  87. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +13 -18
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -1
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -1
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
  121. package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -3
  122. package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
  123. package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
  124. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
  125. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
  126. package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
  127. package/node_modules/pi-lens/dist/clients/file-time.js +6 -1
  128. package/node_modules/pi-lens/dist/clients/file-utils.js +112 -9
  129. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
  130. package/node_modules/pi-lens/dist/clients/format-service.js +6 -1
  131. package/node_modules/pi-lens/dist/clients/formatters.js +276 -102
  132. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
  133. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +114 -19
  134. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
  135. package/node_modules/pi-lens/dist/clients/git-guard.js +1 -1
  136. package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
  137. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
  138. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
  139. package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
  140. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
  141. package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
  142. package/node_modules/pi-lens/dist/clients/installer/index.js +71 -52
  143. package/node_modules/pi-lens/dist/clients/instance-reaper.js +104 -230
  144. package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
  145. package/node_modules/pi-lens/dist/clients/language-registry.js +595 -0
  146. package/node_modules/pi-lens/dist/clients/latency-logger.js +160 -2
  147. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +13 -0
  148. package/node_modules/pi-lens/dist/clients/lens-config.js +141 -30
  149. package/node_modules/pi-lens/dist/clients/lens-engine.js +30 -37
  150. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +59 -6
  151. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -2
  152. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
  153. package/node_modules/pi-lens/dist/clients/lsp/client.js +168 -30
  154. package/node_modules/pi-lens/dist/clients/lsp/config.js +483 -158
  155. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
  156. package/node_modules/pi-lens/dist/clients/lsp/edits.js +15 -2
  157. package/node_modules/pi-lens/dist/clients/lsp/index.js +676 -71
  158. package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -169
  159. package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
  160. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -16
  161. package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
  162. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
  163. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
  164. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +9 -2
  165. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
  166. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
  167. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
  168. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
  169. package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
  170. package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
  171. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +2 -4
  172. package/node_modules/pi-lens/dist/clients/module-report.js +20 -45
  173. package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
  174. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
  175. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
  176. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -20
  177. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
  178. package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
  179. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +68 -22
  180. package/node_modules/pi-lens/dist/clients/opengrep-config.js +4 -0
  181. package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
  182. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
  183. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
  184. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
  185. package/node_modules/pi-lens/dist/clients/path-utils.js +428 -13
  186. package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
  187. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
  188. package/node_modules/pi-lens/dist/clients/pipeline.js +77 -16
  189. package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
  190. package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
  191. package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
  195. package/node_modules/pi-lens/dist/clients/project-lens-config.js +393 -122
  196. package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
  197. package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
  198. package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
  199. package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
  200. package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
  201. package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +15 -4
  203. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
  204. package/node_modules/pi-lens/dist/clients/read-guard.js +21 -0
  205. package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
  206. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +152 -109
  207. package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
  208. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
  209. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
  210. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
  211. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +18 -2
  212. package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
  213. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
  214. package/node_modules/pi-lens/dist/clients/runtime-session.js +139 -22
  215. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +168 -35
  216. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
  217. package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
  218. package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
  219. package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
  220. package/node_modules/pi-lens/dist/clients/sanitize.js +7 -1
  221. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
  222. package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
  223. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
  224. package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
  225. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
  226. package/node_modules/pi-lens/dist/clients/sgconfig.js +6 -1
  227. package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
  228. package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
  229. package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
  230. package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
  231. package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
  232. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +239 -0
  233. package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
  234. package/node_modules/pi-lens/dist/clients/tool-policy.js +91 -21
  235. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +86 -48
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
  241. package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
  242. package/node_modules/pi-lens/dist/clients/user-notify.js +6 -2
  243. package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
  244. package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -2
  245. package/node_modules/pi-lens/dist/clients/word-index-store.js +20 -5
  246. package/node_modules/pi-lens/dist/clients/word-index.js +243 -31
  247. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
  248. package/node_modules/pi-lens/dist/clients/zizmor-config.js +3 -0
  249. package/node_modules/pi-lens/dist/index.js +64335 -54300
  250. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
  251. package/node_modules/pi-lens/dist/mcp/server.js +127 -5
  252. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
  253. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
  254. package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
  255. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +95 -24
  256. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +67 -80
  257. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
  258. package/node_modules/pi-lens/dist/tools/shared.js +0 -1
  259. package/node_modules/pi-lens/docs/agent-guide.md +64 -1
  260. package/node_modules/pi-lens/docs/configuration.md +222 -0
  261. package/node_modules/pi-lens/docs/dependencies.md +3 -3
  262. package/node_modules/pi-lens/docs/features.md +44 -5
  263. package/node_modules/pi-lens/docs/language-coverage.md +2 -2
  264. package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
  265. package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
  266. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +27 -0
  267. package/node_modules/pi-lens/docs/pi-lens-subagent.md +38 -0
  268. package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
  269. package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
  270. package/node_modules/pi-lens/docs/release-qa-baseline.md +182 -0
  271. package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
  272. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
  273. package/node_modules/pi-lens/package.json +15 -23
  274. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  275. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  276. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  277. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
  278. package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
  279. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
  280. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
  281. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  282. package/node_modules/pi-web-access/README.md +62 -15
  283. package/node_modules/pi-web-access/curator-page.ts +3 -1
  284. package/node_modules/pi-web-access/curator-server.ts +5 -1
  285. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  286. package/node_modules/pi-web-access/github-extract.ts +242 -1
  287. package/node_modules/pi-web-access/index.ts +118 -64
  288. package/node_modules/pi-web-access/mistral-search.ts +281 -0
  289. package/node_modules/pi-web-access/package.json +2 -2
  290. package/node_modules/pi-web-access/perplexity.ts +14 -1
  291. package/node_modules/pi-web-access/utils.ts +23 -6
  292. package/node_modules/pi-web-access/xai-search.ts +96 -33
  293. package/package.json +5 -5
@@ -11,10 +11,14 @@ import { access, readFile, readdir, stat } from "node:fs/promises";
11
11
  import os from "node:os";
12
12
  import path from "node:path";
13
13
  import { getGlobalPiLensDir, getProjectIgnoreGlobs, isPathIgnoredByProject, } from "../file-utils.js";
14
+ import { augmentPythonEnvironment, detectPythonEnvironment, detectPythonVenv, pythonEnvironmentToolCandidates, } from "../python-environment.js";
15
+ export { detectPythonVenv };
14
16
  import { STAGE_TMP_PATTERN } from "../atomic-write-staging.js";
15
17
  import { DOTNET_CSHARP_ROOT_MARKERS, DOTNET_FSHARP_ROOT_MARKERS, KIND_EXTENSIONS, } from "../file-kinds.js";
16
- import { direntsHaveMarkerGlobMatch, isAtOrAboveHomeDir, isFullyQualified, isWindowsPath, pathsEqual, } from "../path-utils.js";
18
+ import { CARGO_WORKSPACE_MEMBER_DIALECT, direntsHaveMarkerGlobMatch, isAtOrAboveHomeDir, isFullyQualified, isWindowsPath, matchesWorkspaceMemberPattern, pathsEqual, } from "../path-utils.js";
17
19
  import { ensureTool, findManagedToolBinary, getToolEnvironment, getToolPath, } from "../installer/index.js";
20
+ import * as installer from "../installer/index.js";
21
+ import { classifyProbeFailure, describeInstallAttempt, logAvailabilityDecision, } from "../dispatch/runners/utils/availability-policy.js";
18
22
  import { resolveOpengrepConfig } from "../opengrep-config.js";
19
23
  import { isZizmorAuditTarget, resolveZizmorGitHubToken, } from "../zizmor-config.js";
20
24
  import { logLatency } from "../latency-logger.js";
@@ -22,6 +26,7 @@ import { logSessionStart } from "../sessionstart-logger.js";
22
26
  import { findLocalSgconfig, resolveBaselineSgconfig } from "../sgconfig.js";
23
27
  import { findLocalTyposConfig } from "../typos-config.js";
24
28
  import { resolvePackagePath } from "../package-root.js";
29
+ import { hasCargoWorkspaceTable, readCargoWorkspaceExclude, readCargoWorkspaceMembers, readTextFileOrUndefined, } from "../cargo-manifest.js";
25
30
  import { resolveAstGrepNativeExe } from "./wait-policy/index.js";
26
31
  import { hasSpawnFailureKind, isCommandAvailableAsync, safeSpawnAsync, } from "../safe-spawn.js";
27
32
  import { launchLSP } from "./launch.js";
@@ -30,6 +35,7 @@ import { resolveJavaRuntimeEnv } from "./jvm-runtime.js";
30
35
  import { normalizeMapKey } from "./path-utils.js";
31
36
  import { getRubyVersionDirNamesSync } from "./ruby-drive-dirs.js";
32
37
  import { getProcessSingleton } from "../process-singletons.js";
38
+ import { createGenerationSource, } from "../generation-guard.js";
33
39
  const FIXTURE_ROOT_SEGMENTS = new Set(["__fixtures__", "testdata"]);
34
40
  const FALLBACK_PROJECT_MARKERS = [
35
41
  ".git",
@@ -252,6 +258,49 @@ function canInstall(allowInstall) {
252
258
  const DIRECT_LSP_NEGATIVE_TTL_MS = Math.max(30_000, Number.parseInt(process.env.PI_LENS_DIRECT_LSP_NEGATIVE_TTL_MS ?? "600000", 10) || 600_000);
253
259
  const directLspCommandUnavailableUntil = new Map();
254
260
  const directLspCommandSkipLoggedUntil = new Map();
261
+ // Availability rows are emitted from the same async launch path that owns the
262
+ // live LSP generation. A session reset can retire that generation while a
263
+ // managed lookup, install, or launch is still awaiting; stale work must not
264
+ // publish into the replacement session (#2351, shape 22).
265
+ const lspLaunchAvailabilityGeneration = createGenerationSource("lsp-launch-availability");
266
+ export function resetLspLaunchAvailabilityGeneration() {
267
+ lspLaunchAvailabilityGeneration.bump();
268
+ }
269
+ function staleLaunch(generation, subject, proc) {
270
+ if (generation.isCurrent())
271
+ return false;
272
+ try {
273
+ proc?.process?.kill();
274
+ }
275
+ catch {
276
+ // Best-effort cleanup for a process returned after service retirement.
277
+ }
278
+ generation.guardedWrite(subject, () => undefined);
279
+ return true;
280
+ }
281
+ async function installEvidenceForLaunch(toolId, installed, attempt) {
282
+ const evidence = describeInstallAttempt(attempt);
283
+ const confirmedManagedPath = await findManagedToolBinary(toolId);
284
+ return {
285
+ ...evidence,
286
+ binary: path.basename(installed),
287
+ ...(confirmedManagedPath !== undefined &&
288
+ pathsEqual(confirmedManagedPath, installed) && {
289
+ source: "managed-dir",
290
+ }),
291
+ };
292
+ }
293
+ function captureInstallAttempt(toolId) {
294
+ try {
295
+ // Capture synchronously after ensureTool resolves. Reading this later, after
296
+ // launch/evidence awaits, can observe another concurrent ensure's outcome.
297
+ return installer.getInstallAttempt?.(toolId);
298
+ }
299
+ catch {
300
+ // Older test doubles do not expose this production export.
301
+ }
302
+ return undefined;
303
+ }
255
304
  /** Re-arm direct-command availability for the next session. */
256
305
  export function resetDirectLspCommandAvailability() {
257
306
  directLspCommandUnavailableUntil.clear();
@@ -301,6 +350,7 @@ function markDirectLspCommandUnavailable(command) {
301
350
  }
302
351
  const PI_LENS_BIN_DIR = path.join(getGlobalPiLensDir(), "bin");
303
352
  export async function resolveAndLaunch(spec, allowInstall) {
353
+ const generation = lspLaunchAvailabilityGeneration.capture();
304
354
  const toolLabel = spec.managedToolId ??
305
355
  spec.candidates[spec.candidates.length - 1] ??
306
356
  "unknown";
@@ -323,9 +373,13 @@ export async function resolveAndLaunch(spec, allowInstall) {
323
373
  // `findManagedNodeToolBinary`'s npm-managed fast path (runner-helpers.ts)
324
374
  // and `SecurityScanClient.probeVersion`'s equivalent fix for the CLI-scan
325
375
  // half of this same issue (#2140, landed in PR #2148/#2137).
376
+ const managedProbeStartedAt = Date.now();
326
377
  const managedCandidate = spec.managedToolId
327
378
  ? await findManagedToolBinary(spec.managedToolId)
328
379
  : undefined;
380
+ if (staleLaunch(generation, `${toolLabel}:findManagedToolBinary`)) {
381
+ return undefined;
382
+ }
329
383
  const candidates = managedCandidate && !spec.candidates.includes(managedCandidate)
330
384
  ? [managedCandidate, ...spec.candidates]
331
385
  : spec.candidates;
@@ -356,6 +410,9 @@ export async function resolveAndLaunch(spec, allowInstall) {
356
410
  cwd: spec.cwd,
357
411
  env: spec.env,
358
412
  });
413
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:${command}`, proc)) {
414
+ return undefined;
415
+ }
359
416
  logLatency({
360
417
  type: "phase",
361
418
  phase: "lsp_launch_candidate_success",
@@ -369,9 +426,36 @@ export async function resolveAndLaunch(spec, allowInstall) {
369
426
  },
370
427
  });
371
428
  logSessionStart(`lsp launch candidate success tool=${toolLabel} idx=${index} command=${command} source=direct`);
429
+ // The managed-dir fast path (#2140) IS the availability probe for a
430
+ // release-managed tool: a real spawn just confirmed the binary
431
+ // findManagedToolBinary resolved actually launches. Gated on the exact
432
+ // managed candidate (never a later bare-PATH fallback), so a session
433
+ // start with the binary present emits exactly one decision and a
434
+ // developer-PATH-resolved copy (no managed binary at all) emits none —
435
+ // unchanged from before this fix.
436
+ if (managedCandidate !== undefined && command === managedCandidate) {
437
+ logAvailabilityDecision({
438
+ tool: toolLabel,
439
+ verdict: "available",
440
+ outcome: "success",
441
+ cause: "ok",
442
+ elapsedMs: Date.now() - managedProbeStartedAt,
443
+ latched: false,
444
+ producer: "lsp-launch",
445
+ classifiedBy: "probe",
446
+ evidence: {
447
+ binary: path.basename(managedCandidate),
448
+ source: "managed-dir",
449
+ correctsLatchedRow: false,
450
+ },
451
+ });
452
+ }
372
453
  return { process: proc, source: "direct" };
373
454
  }
374
455
  catch (err) {
456
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:${command}`)) {
457
+ return undefined;
458
+ }
375
459
  const message = err instanceof Error ? err.message : String(err);
376
460
  // Defer logging: only a failure if no later candidate/install succeeds.
377
461
  candidateFailures.push({ index, command, message, err });
@@ -419,8 +503,40 @@ export async function resolveAndLaunch(spec, allowInstall) {
419
503
  }
420
504
  // Step 3 — managed install via installer registry
421
505
  if (spec.managedToolId) {
506
+ // Neither the managed-dir stat (#2140) nor any bare-PATH candidate
507
+ // resolved this tool — the negative counterpart to the fast-path
508
+ // "available" decision above, mirroring `SecurityScanClient.probeVersion`'s
509
+ // failure record so the negative case is visible in latency.log rather
510
+ // than swallowed by going straight to the install attempt.
511
+ const failedCandidate = candidateFailures.at(-1);
512
+ const classifiedFailure = classifyProbeFailure({
513
+ error: failedCandidate?.err instanceof Error
514
+ ? failedCandidate.err
515
+ : undefined,
516
+ spawnFailure: {
517
+ kind: hasSpawnFailureKind(failedCandidate?.err, "tool-not-found")
518
+ ? "tool-not-found"
519
+ : undefined,
520
+ },
521
+ }, { command: failedCandidate?.command });
522
+ logAvailabilityDecision({
523
+ tool: spec.managedToolId,
524
+ verdict: "unavailable",
525
+ outcome: classifiedFailure.outcome,
526
+ cause: classifiedFailure.cause,
527
+ elapsedMs: Date.now() - managedProbeStartedAt,
528
+ latched: false,
529
+ classifiedBy: "probe",
530
+ producer: "lsp-launch",
531
+ evidence: classifiedFailure.evidence,
532
+ });
422
533
  logSessionStart(`lsp launch ensure-tool start tool=${spec.managedToolId} cwd=${spec.cwd}`);
534
+ const installStartedAt = Date.now();
423
535
  const installed = await ensureTool(spec.managedToolId);
536
+ const installAttempt = captureInstallAttempt(spec.managedToolId);
537
+ if (staleLaunch(generation, `${toolLabel}:ensureTool`)) {
538
+ return undefined;
539
+ }
424
540
  logSessionStart(`lsp launch ensure-tool result tool=${spec.managedToolId} installed=${installed ? "yes" : "no"} path=${installed ?? ""}`);
425
541
  logLatency({
426
542
  type: "phase",
@@ -439,6 +555,9 @@ export async function resolveAndLaunch(spec, allowInstall) {
439
555
  cwd: spec.cwd,
440
556
  env: spec.env,
441
557
  });
558
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:managed`, proc)) {
559
+ return undefined;
560
+ }
442
561
  logSessionStart(`lsp launch managed success tool=${spec.managedToolId} command=${installed} source=managed`);
443
562
  logLatency({
444
563
  type: "phase",
@@ -450,9 +569,37 @@ export async function resolveAndLaunch(spec, allowInstall) {
450
569
  command: installed,
451
570
  },
452
571
  });
572
+ // Compensating row for the "unavailable" decision logged above (#1606
573
+ // shape): the install fixed exactly what the fast path found missing,
574
+ // so the durable record must not be left saying the tool is off.
575
+ // `source` is only asserted when a fresh managed-dir lookup confirms
576
+ // the install actually landed in ~/.pi-lens/bin (github/maven/archive
577
+ // strategies) rather than an npm/pip/gem install elsewhere, so the
578
+ // evidence never claims a resolution this call didn't derive.
579
+ const evidence = await installEvidenceForLaunch(spec.managedToolId, installed, installAttempt);
580
+ if (staleLaunch(generation, `${toolLabel}:managed-evidence`, proc)) {
581
+ return undefined;
582
+ }
583
+ logAvailabilityDecision({
584
+ tool: spec.managedToolId,
585
+ verdict: "available",
586
+ outcome: "success",
587
+ cause: "ok",
588
+ elapsedMs: Date.now() - installStartedAt,
589
+ latched: false,
590
+ classifiedBy: "caller",
591
+ producer: "lsp-launch",
592
+ evidence: {
593
+ ...evidence,
594
+ correctsLatchedRow: false,
595
+ },
596
+ });
453
597
  return { process: proc, source: "managed" };
454
598
  }
455
599
  catch (err) {
600
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:managed-rejection`)) {
601
+ return undefined;
602
+ }
456
603
  const message = err instanceof Error ? err.message : String(err);
457
604
  logSessionStart(`lsp launch managed failed tool=${spec.managedToolId} command=${installed} error=${message}`);
458
605
  logLatency({
@@ -476,12 +623,19 @@ export async function resolveAndLaunch(spec, allowInstall) {
476
623
  const reinstalled = await ensureTool(spec.managedToolId, {
477
624
  forceReinstall: true,
478
625
  });
626
+ const reinstallAttempt = captureInstallAttempt(spec.managedToolId);
627
+ if (staleLaunch(generation, `${toolLabel}:forceReinstall`)) {
628
+ return undefined;
629
+ }
479
630
  if (reinstalled) {
480
631
  try {
481
632
  const proc = await launchLSP(reinstalled, spec.args, {
482
633
  cwd: spec.cwd,
483
634
  env: spec.env,
484
635
  });
636
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:forceReinstall`, proc)) {
637
+ return undefined;
638
+ }
485
639
  logSessionStart(`lsp launch managed force-reinstall success tool=${spec.managedToolId} command=${reinstalled}`);
486
640
  logLatency({
487
641
  type: "phase",
@@ -493,6 +647,24 @@ export async function resolveAndLaunch(spec, allowInstall) {
493
647
  command: reinstalled,
494
648
  },
495
649
  });
650
+ const evidence = await installEvidenceForLaunch(spec.managedToolId, reinstalled, reinstallAttempt);
651
+ if (staleLaunch(generation, `${toolLabel}:forceReinstall-evidence`, proc)) {
652
+ return undefined;
653
+ }
654
+ logAvailabilityDecision({
655
+ tool: spec.managedToolId,
656
+ verdict: "available",
657
+ outcome: "success",
658
+ cause: "ok",
659
+ elapsedMs: Date.now() - installStartedAt,
660
+ latched: false,
661
+ classifiedBy: "caller",
662
+ producer: "lsp-launch",
663
+ evidence: {
664
+ ...evidence,
665
+ correctsLatchedRow: false,
666
+ },
667
+ });
496
668
  return { process: proc, source: "managed" };
497
669
  }
498
670
  catch (retryErr) {
@@ -505,20 +677,35 @@ export async function resolveAndLaunch(spec, allowInstall) {
505
677
  }
506
678
  }
507
679
  // Step 4 — language-native runtime install (go install, gem install, …)
508
- if (spec.runtimeInstall &&
509
- (await isOnPath(spec.runtimeInstall.runtimeCommand))) {
510
- const ok = await spec.runtimeInstall.install();
680
+ const runtimeInstall = spec.runtimeInstall;
681
+ const runtimeAvailable = runtimeInstall
682
+ ? await isOnPath(runtimeInstall.runtimeCommand)
683
+ : false;
684
+ if (staleLaunch(generation, `${toolLabel}:runtimeAvailability`)) {
685
+ return undefined;
686
+ }
687
+ if (runtimeInstall && runtimeAvailable) {
688
+ const ok = await runtimeInstall.install();
689
+ if (staleLaunch(generation, `${toolLabel}:runtimeInstall`)) {
690
+ return undefined;
691
+ }
511
692
  if (ok) {
512
- const retry = spec.runtimeInstall.retryCandidates ?? spec.candidates;
693
+ const retry = runtimeInstall.retryCandidates ?? spec.candidates;
513
694
  for (const command of retry) {
514
695
  try {
515
696
  const proc = await launchLSP(command, spec.args, {
516
697
  cwd: spec.cwd,
517
698
  env: spec.env,
518
699
  });
700
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:runtime`, proc)) {
701
+ return undefined;
702
+ }
519
703
  return { process: proc, source: "managed" };
520
704
  }
521
705
  catch (err) {
706
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:runtime`)) {
707
+ return undefined;
708
+ }
522
709
  trackRuntimeFailure(err);
523
710
  // try next
524
711
  }
@@ -629,12 +816,15 @@ async function resolveAndLaunchTreeBinary(spec, allowInstall) {
629
816
  return undefined;
630
817
  }
631
818
  }
632
- function nodeBinCandidates(root, baseName) {
819
+ function nodeBinLocalCandidates(root, baseName) {
633
820
  const localBase = path.join(root, "node_modules", ".bin", baseName);
634
821
  if (process.platform === "win32") {
635
- return [`${localBase}.cmd`, `${localBase}.exe`, baseName];
822
+ return [`${localBase}.cmd`, `${localBase}.exe`];
636
823
  }
637
- return [localBase, baseName];
824
+ return [localBase];
825
+ }
826
+ function nodeBinCandidates(root, baseName) {
827
+ return [...nodeBinLocalCandidates(root, baseName), baseName];
638
828
  }
639
829
  function normalizeSlashKey(value) {
640
830
  const normalized = path.resolve(value).replace(/\\/g, "/");
@@ -689,6 +879,7 @@ function createInteractiveServer(spec) {
689
879
  name: spec.name,
690
880
  extensions: spec.extensions,
691
881
  root: spec.root,
882
+ fallbackFor: spec.fallbackFor,
692
883
  availabilityKey: typeof spec.command === "string" && isSimpleCommand(spec.command)
693
884
  ? spec.command
694
885
  : undefined,
@@ -1260,33 +1451,6 @@ export function DenoExcludeRoot(primary) {
1260
1451
  return primary(file);
1261
1452
  };
1262
1453
  }
1263
- /**
1264
- * Find the active Python interpreter inside the nearest virtual environment.
1265
- * Search order: VIRTUAL_ENV → CONDA_PREFIX → .venv → venv (all under root).
1266
- * Returns undefined when no venv python binary is found.
1267
- */
1268
- export async function detectPythonVenv(root) {
1269
- const isWin = process.platform === "win32";
1270
- const candidates = [
1271
- process.env.VIRTUAL_ENV,
1272
- process.env.CONDA_PREFIX,
1273
- path.join(root, ".venv"),
1274
- path.join(root, "venv"),
1275
- ].filter((v) => Boolean(v));
1276
- for (const venv of candidates) {
1277
- const pythonPath = isWin
1278
- ? path.join(venv, "Scripts", "python.exe")
1279
- : path.join(venv, "bin", "python");
1280
- try {
1281
- await access(pythonPath);
1282
- return pythonPath;
1283
- }
1284
- catch {
1285
- // not found — try next candidate
1286
- }
1287
- }
1288
- return undefined;
1289
- }
1290
1454
  // --- Server Definitions ---
1291
1455
  const JS_TS_LSP_EXTENSIONS = KIND_EXTENSIONS["jsts"].filter((ext) => ext !== ".svelte" && ext !== ".vue");
1292
1456
  // TypeScript identity and tooling discovery deliberately use separate marker
@@ -1518,6 +1682,7 @@ export const TypeScriptServer = {
1518
1682
  export const DenoServer = {
1519
1683
  id: "deno",
1520
1684
  name: "Deno Language Server",
1685
+ fallbackFor: "typescript",
1521
1686
  extensions: JS_TS_LSP_EXTENSIONS,
1522
1687
  autoPropagateDiagnostics: true,
1523
1688
  root: createRootDetector(["deno.json", "deno.jsonc"]),
@@ -1544,7 +1709,8 @@ export const PythonServer = {
1544
1709
  "poetry.lock",
1545
1710
  ])),
1546
1711
  async spawn(root, options) {
1547
- const env = await getToolEnvironment();
1712
+ const pythonEnvironment = await detectPythonEnvironment(root);
1713
+ const env = augmentPythonEnvironment(await getToolEnvironment(), pythonEnvironment);
1548
1714
  let source = "direct";
1549
1715
  // openFilesOnly: true — analyse only open files rather than the full workspace.
1550
1716
  // Avoids the 5–14 s cold-start on large projects caused by workspace-wide
@@ -1554,36 +1720,65 @@ export const PythonServer = {
1554
1720
  ...(pythonPath ? { pythonPath } : {}),
1555
1721
  openFilesOnly: true,
1556
1722
  });
1557
- // Prefer pyright-langserver; basedpyright-langserver is a drop-in fork with
1558
- // the same --stdio protocol and additional rules (e.g. reportUnusedExpression).
1559
- const localCandidates = [
1560
- ...nodeBinCandidates(root, "pyright-langserver"),
1561
- ...nodeBinCandidates(root, "basedpyright-langserver"),
1562
- ];
1563
- const direct = await resolveAndLaunch({ candidates: localCandidates, args: ["--stdio"], cwd: root, env }, false);
1564
- if (direct) {
1565
- const pythonPath = await detectPythonVenv(root);
1723
+ // Project ownership outranks checker preference: exhaust explicit project
1724
+ // candidates before a bare command can resolve to pi-lens's managed bin or
1725
+ // the host PATH. Within the project tier, preserve the established
1726
+ // pyright → basedpyright → ty preference.
1727
+ const projectPyright = await resolveAndLaunch({
1728
+ candidates: [
1729
+ ...pythonEnvironmentToolCandidates(pythonEnvironment, "pyright-langserver"),
1730
+ ...nodeBinLocalCandidates(root, "pyright-langserver"),
1731
+ ...pythonEnvironmentToolCandidates(pythonEnvironment, "basedpyright-langserver"),
1732
+ ...nodeBinLocalCandidates(root, "basedpyright-langserver"),
1733
+ ],
1734
+ args: ["--stdio"],
1735
+ cwd: root,
1736
+ env,
1737
+ }, false);
1738
+ if (projectPyright) {
1566
1739
  return {
1567
- process: direct.process,
1568
- source: direct.source,
1569
- initialization: pyrightInit(pythonPath),
1740
+ process: projectPyright.process,
1741
+ source: projectPyright.source,
1742
+ initialization: pyrightInit(pythonEnvironment?.pythonPath),
1570
1743
  };
1571
1744
  }
1572
- // ty (astral-sh/ty, #717) an alternative Python checker/language server,
1573
- // tried ONLY when neither pyright nor basedpyright was found locally, and
1574
- // ONLY on PATH (allowInstall: false below — no managed/auto-install, unlike
1575
- // pyright's fallback right after this block). That keeps ty strictly
1576
- // opt-in: it never displaces an already-installed pyright/basedpyright,
1577
- // and it's never silently auto-installed as a default — a user only gets
1578
- // it by having installed `ty` themselves (e.g. `uv tool install ty` /
1579
- // `pip install ty`). Unlike pyright-langserver's `--stdio` flag, ty's CLI
1580
- // launches its language server via the `server` subcommand; it has no
1581
- // stable initializationOptions equivalent to pyright's `pythonPath` yet
1582
- // (astral-sh/ty#2032) — it auto-discovers `.venv`/`VIRTUAL_ENV` from cwd,
1583
- // so no `initialization` payload is sent.
1584
- const ty = await resolveAndLaunch({ candidates: ["ty"], args: ["server"], cwd: root, env }, false);
1585
- if (ty) {
1586
- return { process: ty.process, source: ty.source };
1745
+ // ty uses `ty server`, so it needs a separate launch phase from the
1746
+ // Pyright-compatible servers. It has no stable initializationOptions
1747
+ // equivalent to pyright's `pythonPath` (astral-sh/ty#2032); the child
1748
+ // environment and cwd provide interpreter discovery instead.
1749
+ const projectTy = await resolveAndLaunch({
1750
+ candidates: pythonEnvironmentToolCandidates(pythonEnvironment, "ty"),
1751
+ args: ["server"],
1752
+ cwd: root,
1753
+ env,
1754
+ }, false);
1755
+ if (projectTy) {
1756
+ return { process: projectTy.process, source: projectTy.source };
1757
+ }
1758
+ // With no project-owned checker, retain the existing PATH preference and
1759
+ // keep ty opt-in: pyright, then basedpyright, then ty. The augmented child
1760
+ // PATH includes pi-lens-managed bins before the inherited global PATH.
1761
+ const pathPyright = await resolveAndLaunch({
1762
+ candidates: ["pyright-langserver", "basedpyright-langserver"],
1763
+ args: ["--stdio"],
1764
+ cwd: root,
1765
+ env,
1766
+ }, false);
1767
+ if (pathPyright) {
1768
+ return {
1769
+ process: pathPyright.process,
1770
+ source: pathPyright.source,
1771
+ initialization: pyrightInit(pythonEnvironment?.pythonPath),
1772
+ };
1773
+ }
1774
+ const pathTy = await resolveAndLaunch({
1775
+ candidates: ["ty"],
1776
+ args: ["server"],
1777
+ cwd: root,
1778
+ env,
1779
+ }, false);
1780
+ if (pathTy) {
1781
+ return { process: pathTy.process, source: pathTy.source };
1587
1782
  }
1588
1783
  // Discover a globally-installed pyright even when install is disabled;
1589
1784
  // only the download is gated by canInstall.
@@ -1605,17 +1800,17 @@ export const PythonServer = {
1605
1800
  const resolved = await resolveAndLaunch({ candidates: managedCandidates, args: ["--stdio"], cwd: root, env }, false);
1606
1801
  if (!resolved)
1607
1802
  return undefined;
1608
- const pythonPath = await detectPythonVenv(root);
1609
1803
  return {
1610
1804
  process: resolved.process,
1611
1805
  source,
1612
- initialization: pyrightInit(pythonPath),
1806
+ initialization: pyrightInit(pythonEnvironment?.pythonPath),
1613
1807
  };
1614
1808
  },
1615
1809
  };
1616
1810
  export const PythonJediServer = {
1617
1811
  id: "python-jedi",
1618
1812
  name: "Jedi Language Server",
1813
+ fallbackFor: "python",
1619
1814
  extensions: KIND_EXTENSIONS["python"],
1620
1815
  root: RootWithFallback(createRootDetector([
1621
1816
  ".git",
@@ -1667,79 +1862,6 @@ export const GoServer = {
1667
1862
  return { ...result, initialization: { ui: { semanticTokens: true } } };
1668
1863
  },
1669
1864
  };
1670
- async function readTextFileOrUndefined(filePath) {
1671
- try {
1672
- return await readFile(filePath, "utf-8");
1673
- }
1674
- catch {
1675
- return undefined;
1676
- }
1677
- }
1678
- /**
1679
- * Slice out ONE top-level TOML table's raw body — from its `[name]` heading
1680
- * to the next top-level `[...]`/`[[...]]` heading or EOF. `members`/`exclude`
1681
- * must be read from the `[workspace]` table specifically: `[package]` has its
1682
- * OWN `exclude` key (the standard cargo-publish exclude list, conventionally
1683
- * written above `[workspace]` in a virtual-manifest-less root crate), and a
1684
- * whole-file regex would misread it as workspace membership (#1671 F4).
1685
- */
1686
- function extractTomlTableSection(content, tableName) {
1687
- const heading = new RegExp(`^\\[${tableName}\\][ \\t]*(?:#.*)?$`, "m");
1688
- const match = heading.exec(content);
1689
- if (!match)
1690
- return "";
1691
- const rest = content.slice(match.index + match[0].length);
1692
- const nextHeading = rest.match(/^\[{1,2}[^\]]+\]{1,2}[ \t]*(?:#.*)?$/m);
1693
- return nextHeading?.index !== undefined
1694
- ? rest.slice(0, nextHeading.index)
1695
- : rest;
1696
- }
1697
- function parseTomlStringArray(content, key) {
1698
- const match = content.match(new RegExp(`^[ \\t]*${key}[ \\t]*=[ \\t]*\\[([\\s\\S]*?)\\]`, "m"));
1699
- if (!match)
1700
- return [];
1701
- return [...match[1].matchAll(/"([^"]*)"|'([^']*)'/g)].map((m) => (m[1] ?? m[2] ?? "").trim());
1702
- }
1703
- /** Turn one `/`-delimited glob SEGMENT into a regex source: `*` matches any
1704
- * run of characters within the segment, `?` matches exactly one character,
1705
- * everything else is escaped and literal. */
1706
- function segmentGlobToRegExpSource(segment) {
1707
- let out = "";
1708
- for (const ch of segment) {
1709
- if (ch === "*")
1710
- out += "[^/]*";
1711
- else if (ch === "?")
1712
- out += "[^/]";
1713
- else
1714
- out += ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
1715
- }
1716
- return out;
1717
- }
1718
- /**
1719
- * A `members`/`exclude` entry names a path exactly, or globs it segment by
1720
- * segment: `*` and `?` inside a segment work at any depth — one wildcard
1721
- * segment (`crates/*`), a bare `*`, or several chained together for a
1722
- * deeper fixed-depth layout — matching cargo's own `glob`-crate semantics
1723
- * for a fixed number of path components.
1724
- *
1725
- * KNOWN LIMITATION (#1671 F6, documented rather than implemented): a
1726
- * recursive `**` segment (matching a variable number of path components) is
1727
- * NOT supported and never matches — cargo workspaces that rely on `**` to
1728
- * pull in an arbitrarily-nested crate tree will under-hoist (the crate stays
1729
- * independently rooted instead of joining the workspace). This is
1730
- * deliberately out of #1671's scope: the common, and the issue's fixture,
1731
- * shape is an explicit fixed-depth `members` list.
1732
- */
1733
- function matchesCargoWorkspacePattern(pattern, relativePath) {
1734
- const normalized = pattern.replace(/\/+$/, "");
1735
- if (normalized.includes("**"))
1736
- return false;
1737
- const patternSegments = normalized.split("/");
1738
- const pathSegments = relativePath.split("/");
1739
- if (patternSegments.length !== pathSegments.length)
1740
- return false;
1741
- return patternSegments.every((segment, i) => new RegExp(`^${segmentGlobToRegExpSource(segment)}$`).test(pathSegments[i]));
1742
- }
1743
1865
  /**
1744
1866
  * Given an ancestor Cargo.toml's raw contents that already contains a
1745
1867
  * `[workspace]` table, decide whether it actually claims `childDir` as a
@@ -1766,13 +1888,21 @@ function cargoWorkspaceDeclaresMember(workspaceContent, parentDir, childDir) {
1766
1888
  .join("/");
1767
1889
  if (relativePath === "" || relativePath.startsWith(".."))
1768
1890
  return false;
1769
- const workspaceSection = extractTomlTableSection(workspaceContent, "workspace");
1770
- const excluded = parseTomlStringArray(workspaceSection, "exclude");
1771
- if (excluded.some((pattern) => matchesCargoWorkspacePattern(pattern, relativePath))) {
1891
+ // Reuse the shared, `[workspace]`-scoped readers (review round 2, F4)
1892
+ // instead of hand-composing `extractTomlTableSection` +
1893
+ // `parseTomlStringArray` here that hand-composition duplicated exactly
1894
+ // what `readCargoWorkspaceMembers`/`readCargoWorkspaceExclude` do, the
1895
+ // single-source-of-truth violation #2473 was filed to close for the OTHER
1896
+ // two Cargo.toml readers. Pattern matching goes through the ONE
1897
+ // workspace-member matcher for the same reason (#2591); cargo's dialect —
1898
+ // `**` never matches (#1671 F6), wildcards confined to one component, so
1899
+ // pattern and path must have the same component count — is
1900
+ // `CARGO_WORKSPACE_MEMBER_DIALECT`, not a private compiler here.
1901
+ const matches = (pattern) => matchesWorkspaceMemberPattern(pattern, relativePath, CARGO_WORKSPACE_MEMBER_DIALECT);
1902
+ const excluded = readCargoWorkspaceExclude(workspaceContent);
1903
+ if (excluded.some(matches))
1772
1904
  return false;
1773
- }
1774
- const members = parseTomlStringArray(workspaceSection, "members");
1775
- return members.some((pattern) => matchesCargoWorkspacePattern(pattern, relativePath));
1905
+ return readCargoWorkspaceMembers(workspaceContent).some(matches);
1776
1906
  }
1777
1907
  /**
1778
1908
  * Bound a monorepo-hoist walk-up by the session ceiling: when `file` is inside
@@ -1811,7 +1941,7 @@ function RustWorkspaceRoot() {
1811
1941
  const parentCargoPath = path.join(parent, "Cargo.toml");
1812
1942
  const parentCargoContent = await readTextFileOrUndefined(parentCargoPath);
1813
1943
  if (parentCargoContent !== undefined &&
1814
- /^\s*\[workspace\]/m.test(parentCargoContent)) {
1944
+ hasCargoWorkspaceTable(parentCargoContent)) {
1815
1945
  // Test membership against the ORIGINAL crate dir (`root`), never the
1816
1946
  // walk cursor (`current`, which may already have climbed through
1817
1947
  // gap directories that have no Cargo.toml of their own) — a
@@ -2119,6 +2249,7 @@ export const CSharpServer = {
2119
2249
  export const OmniSharpServer = createInteractiveServer({
2120
2250
  id: "omnisharp",
2121
2251
  name: "OmniSharp",
2252
+ fallbackFor: "csharp",
2122
2253
  extensions: KIND_EXTENSIONS["csharp"],
2123
2254
  root: createRootDetector([...DOTNET_CSHARP_ROOT_MARKERS]),
2124
2255
  language: "csharp",
@@ -2285,6 +2416,7 @@ export const ElixirServer = createInteractiveServer({
2285
2416
  export const ElixirExpertServer = {
2286
2417
  id: "expert",
2287
2418
  name: "Expert",
2419
+ fallbackFor: "elixir",
2288
2420
  extensions: KIND_EXTENSIONS["elixir"],
2289
2421
  root: RootWithFallback(createRootDetector(["mix.exs"])),
2290
2422
  availabilityKey: "expert",
@@ -2758,7 +2890,13 @@ export const OpengrepServer = {
2758
2890
  // Gate B). NOTE: the napi runner is NOT a subset — it delegates to napi's native
2759
2891
  // engine via root.findAll({rule}) (#206), the SAME Rust core as this LSP and the
2760
2892
  // ast-grep CLI, so rule semantics are identical across all three. The LSP's edge
2761
- // is engine-driven codeAction fixes, not faithfulness of matching.
2893
+ // is engine-driven codeAction fixes, not faithfulness of matching. #2347 closed
2894
+ // the one known divergence under Gate B: the LSP/CLI resolve embedded `<script>`
2895
+ // bodies in HTML and run `language: JavaScript` rules inside them, and the napi
2896
+ // fallback now mirrors that (each script body is reparsed as JS and findings are
2897
+ // translated back to file coordinates). A future ast-grep embedded-content
2898
+ // surface (for example `<style>` bodies, which 0.45.1 does NOT inject) must land
2899
+ // on both routes together or be recorded here as an accepted divergence.
2762
2900
  const AST_GREP_KINDS = [
2763
2901
  "csharp",
2764
2902
  "cxx",
@@ -2779,7 +2917,6 @@ const AST_GREP_KINDS = [
2779
2917
  "rust",
2780
2918
  "scala",
2781
2919
  "shell",
2782
- "solidity",
2783
2920
  "swift",
2784
2921
  "yaml",
2785
2922
  ];