@herbertgao/pi-extensions 2026.9.0 → 2026.9.2

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 (360) 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/@narumitw/pi-btw/README.md +57 -141
  11. package/node_modules/@narumitw/pi-btw/dist/index.ts +466 -41
  12. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
  13. package/node_modules/@narumitw/pi-btw/docs/workflows.md +74 -0
  14. package/node_modules/@narumitw/pi-btw/package.json +2 -1
  15. package/node_modules/@narumitw/pi-btw/src/btw.ts +15 -1
  16. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +32 -2
  17. package/node_modules/@narumitw/pi-btw/src/keybindings.ts +330 -0
  18. package/node_modules/@narumitw/pi-btw/src/menu.ts +136 -19
  19. package/node_modules/@narumitw/pi-btw/src/settings.ts +32 -0
  20. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +57 -5
  21. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +52 -30
  22. package/node_modules/@pi-plugins/fast-mode/README.md +5 -3
  23. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
  24. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
  25. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  26. package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
  27. package/node_modules/pi-lens/CHANGELOG.md +1072 -0
  28. package/node_modules/pi-lens/README.md +3 -0
  29. package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
  30. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
  31. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -9
  32. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
  33. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +1 -1
  34. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
  35. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
  36. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
  37. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -9
  38. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +2 -0
  39. package/node_modules/pi-lens/dist/clients/bash-file-access.js +133 -3
  40. package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
  41. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -6
  42. package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
  43. package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
  44. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +1 -1
  45. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -9
  46. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
  47. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +3 -11
  48. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
  49. package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
  50. package/node_modules/pi-lens/dist/clients/cache-observability.js +40 -19
  51. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
  52. package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -9
  53. package/node_modules/pi-lens/dist/clients/child-unref.js +1 -19
  54. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
  55. package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
  56. package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
  57. package/node_modules/pi-lens/dist/clients/config-core/index.js +50 -0
  58. package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
  59. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
  60. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
  61. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +164 -0
  62. package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
  63. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
  64. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
  65. package/node_modules/pi-lens/dist/clients/config-core/schema.js +165 -0
  66. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
  67. package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
  68. package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
  69. package/node_modules/pi-lens/dist/clients/config-schema.js +234 -0
  70. package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
  71. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
  72. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -6
  73. package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
  74. package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
  75. package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
  76. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
  77. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
  78. package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
  79. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +1 -1
  80. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
  81. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +15 -5
  82. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -4
  83. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -44
  84. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +27 -8
  86. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -6
  87. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +1 -1
  88. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -3
  89. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +2 -2
  90. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +1 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +383 -27
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +4 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +1 -2
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +14 -18
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +6 -3
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +1 -2
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +1 -2
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +4 -3
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -67
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -21
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +2 -20
  144. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +1 -1
  145. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
  146. package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +2 -2
  147. package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -10
  148. package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
  149. package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
  150. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
  151. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +2 -2
  152. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
  153. package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
  154. package/node_modules/pi-lens/dist/clients/file-kinds.js +1 -38
  155. package/node_modules/pi-lens/dist/clients/file-time.js +7 -2
  156. package/node_modules/pi-lens/dist/clients/file-utils.js +117 -10
  157. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
  158. package/node_modules/pi-lens/dist/clients/format-service.js +6 -2
  159. package/node_modules/pi-lens/dist/clients/formatters.js +385 -104
  160. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
  161. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +116 -22
  162. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
  163. package/node_modules/pi-lens/dist/clients/git-guard.js +6 -1
  164. package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
  165. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
  166. package/node_modules/pi-lens/dist/clients/grammar-source.js +3 -3
  167. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
  168. package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
  169. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
  170. package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
  171. package/node_modules/pi-lens/dist/clients/installer/index.js +289 -59
  172. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +84 -18
  173. package/node_modules/pi-lens/dist/clients/instance-reaper.js +110 -236
  174. package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
  175. package/node_modules/pi-lens/dist/clients/language-profile.js +0 -9
  176. package/node_modules/pi-lens/dist/clients/language-registry.js +599 -0
  177. package/node_modules/pi-lens/dist/clients/latency-logger.js +165 -9
  178. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +34 -1
  179. package/node_modules/pi-lens/dist/clients/lens-config.js +160 -30
  180. package/node_modules/pi-lens/dist/clients/lens-engine.js +33 -41
  181. package/node_modules/pi-lens/dist/clients/lens-events.js +1 -1
  182. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +77 -6
  183. package/node_modules/pi-lens/dist/clients/lens-map.js +1 -1
  184. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -38
  185. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
  186. package/node_modules/pi-lens/dist/clients/lsp/client.js +172 -33
  187. package/node_modules/pi-lens/dist/clients/lsp/config.js +484 -158
  188. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
  189. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +1 -1
  190. package/node_modules/pi-lens/dist/clients/lsp/edits.js +18 -5
  191. package/node_modules/pi-lens/dist/clients/lsp/index.js +892 -96
  192. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +1 -1
  193. package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -177
  194. package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
  195. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  196. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -18
  197. package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
  198. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
  199. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
  200. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -1
  201. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +14 -7
  202. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +6 -0
  203. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +2 -1
  204. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
  205. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -1
  206. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
  207. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
  208. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
  209. package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
  210. package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
  211. package/node_modules/pi-lens/dist/clients/metrics-history.js +28 -107
  212. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +3 -5
  213. package/node_modules/pi-lens/dist/clients/module-report.js +21 -46
  214. package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
  215. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
  216. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
  217. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -24
  218. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
  219. package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
  220. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +70 -28
  221. package/node_modules/pi-lens/dist/clients/opengrep-config.js +5 -1
  222. package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
  223. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
  224. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
  225. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
  226. package/node_modules/pi-lens/dist/clients/path-utils.js +451 -15
  227. package/node_modules/pi-lens/dist/clients/performance-report.js +2 -2
  228. package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
  229. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
  230. package/node_modules/pi-lens/dist/clients/pipeline.js +79 -18
  231. package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
  232. package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
  233. package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
  234. package/node_modules/pi-lens/dist/clients/project-changes.js +1 -1
  235. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +1 -1
  236. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
  237. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +1 -1
  238. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +1 -1
  239. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +1 -1
  240. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +1 -1
  241. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +28 -4
  242. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
  243. package/node_modules/pi-lens/dist/clients/project-lens-config.js +438 -122
  244. package/node_modules/pi-lens/dist/clients/project-scan-policy.js +3 -51
  245. package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
  246. package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
  247. package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
  248. package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
  249. package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
  250. package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
  251. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +16 -5
  252. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
  253. package/node_modules/pi-lens/dist/clients/read-guard.js +26 -2
  254. package/node_modules/pi-lens/dist/clients/recent-touches.js +2 -2
  255. package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
  256. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +155 -114
  257. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +1 -1
  258. package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
  259. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
  260. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
  261. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
  262. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +19 -3
  263. package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
  264. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
  265. package/node_modules/pi-lens/dist/clients/runtime-session.js +184 -32
  266. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +179 -36
  267. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
  268. package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
  269. package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
  270. package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
  271. package/node_modules/pi-lens/dist/clients/sanitize.js +7 -12
  272. package/node_modules/pi-lens/dist/clients/scan-utils.js +1 -56
  273. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
  274. package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
  275. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
  276. package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
  277. package/node_modules/pi-lens/dist/clients/session-summary.js +0 -24
  278. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
  279. package/node_modules/pi-lens/dist/clients/sgconfig.js +7 -6
  280. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +1 -1
  281. package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
  282. package/node_modules/pi-lens/dist/clients/smells-rollup.js +4 -4
  283. package/node_modules/pi-lens/dist/clients/startup-scan.js +2 -10
  284. package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
  285. package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
  286. package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
  287. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +244 -0
  288. package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
  289. package/node_modules/pi-lens/dist/clients/tool-policy.js +92 -22
  290. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
  291. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +87 -100
  292. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  293. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
  294. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
  295. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
  296. package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -4
  297. package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
  298. package/node_modules/pi-lens/dist/clients/user-notify.js +6 -6
  299. package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
  300. package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -9
  301. package/node_modules/pi-lens/dist/clients/word-index-store.js +23 -12
  302. package/node_modules/pi-lens/dist/clients/word-index.js +252 -47
  303. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
  304. package/node_modules/pi-lens/dist/clients/zizmor-config.js +4 -1
  305. package/node_modules/pi-lens/dist/index.js +65899 -55340
  306. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
  307. package/node_modules/pi-lens/dist/mcp/build-staleness.js +1 -1
  308. package/node_modules/pi-lens/dist/mcp/server.js +127 -5
  309. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
  310. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
  311. package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
  312. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +96 -25
  313. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +104 -86
  314. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
  315. package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +3 -3
  316. package/node_modules/pi-lens/dist/tools/render-compact.js +1 -1
  317. package/node_modules/pi-lens/dist/tools/shared.js +0 -1
  318. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -2
  319. package/node_modules/pi-lens/docs/agent-guide.md +64 -1
  320. package/node_modules/pi-lens/docs/configuration.md +222 -0
  321. package/node_modules/pi-lens/docs/dependencies.md +3 -3
  322. package/node_modules/pi-lens/docs/features.md +44 -5
  323. package/node_modules/pi-lens/docs/globalconfig.md +20 -0
  324. package/node_modules/pi-lens/docs/language-coverage.md +2 -2
  325. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +9 -1
  326. package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
  327. package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
  328. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +33 -0
  329. package/node_modules/pi-lens/docs/pi-lens-subagent.md +49 -0
  330. package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
  331. package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
  332. package/node_modules/pi-lens/docs/release-qa-baseline.md +190 -0
  333. package/node_modules/pi-lens/docs/servercapabilities.md +7 -3
  334. package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
  335. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
  336. package/node_modules/pi-lens/docs/word-index.md +1 -1
  337. package/node_modules/pi-lens/package.json +18 -35
  338. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  339. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  340. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  341. package/node_modules/pi-lens/rules/typos/_typos.toml +4 -2
  342. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
  343. package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
  344. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
  345. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +1 -1
  346. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
  347. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +4 -2
  348. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  349. package/node_modules/pi-web-access/README.md +62 -15
  350. package/node_modules/pi-web-access/curator-page.ts +3 -1
  351. package/node_modules/pi-web-access/curator-server.ts +5 -1
  352. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  353. package/node_modules/pi-web-access/github-extract.ts +242 -1
  354. package/node_modules/pi-web-access/index.ts +118 -64
  355. package/node_modules/pi-web-access/mistral-search.ts +281 -0
  356. package/node_modules/pi-web-access/package.json +2 -2
  357. package/node_modules/pi-web-access/perplexity.ts +14 -1
  358. package/node_modules/pi-web-access/utils.ts +23 -6
  359. package/node_modules/pi-web-access/xai-search.ts +96 -33
  360. package/package.json +6 -6
@@ -10,7 +10,7 @@
10
10
  * - On non-Windows: return path as-is (case-sensitive filesystem)
11
11
  * - Always convert backslashes to forward slashes for Map key consistency
12
12
  */
13
- import { existsSync, realpathSync } from "node:fs";
13
+ import { existsSync, readFileSync, realpathSync, statSync, } from "node:fs";
14
14
  import * as os from "node:os";
15
15
  import * as path from "node:path";
16
16
  import { win32 } from "node:path";
@@ -61,6 +61,26 @@ export function isFullyQualified(filePath) {
61
61
  ? isFullyQualifiedWin32(filePath)
62
62
  : isFullyQualifiedPosix(filePath);
63
63
  }
64
+ /**
65
+ * Shape-based absolute check (mirrors `toProjectRelativePath`'s idiom): a
66
+ * Windows-shaped path (drive-letter/UNC) is parsed with `win32.isAbsolute`
67
+ * regardless of host OS, since the host-default `path.isAbsolute` returns
68
+ * FALSE for a Windows-shaped path on POSIX (#1150 class) and would let a
69
+ * cross-platform-persisted relative path slip past this guard on Linux CI.
70
+ * MUST run on the RAW `filePath`, before `normalizeMapKey`: on Windows,
71
+ * `normalizeMapKey`'s nonexistent-path fallback (`resolveNonExisting`) calls
72
+ * `win32.resolve`, which silently makes any relative path absolute against
73
+ * `process.cwd()` — checking the normalized value would defeat this guard
74
+ * entirely on Windows.
75
+ *
76
+ * Promoted from a private copy in `clients/review-graph/service.ts` (#2477)
77
+ * so `clients/dispatch/dispatcher.ts`'s baseline-key guard (#2489) can share
78
+ * the same sanctioned check instead of hand-rolling a second one.
79
+ */
80
+ export function isAbsoluteFilePath(filePath) {
81
+ const p = isWindowsPath(filePath) ? win32 : path;
82
+ return p.isAbsolute(filePath);
83
+ }
64
84
  /**
65
85
  * Split a path into its non-empty segments on EITHER separator (`\` or `/`),
66
86
  * regardless of the running OS — the shape-safe form of `p.split(path.sep)` /
@@ -346,7 +366,30 @@ export function findNearestContaining(startDir, candidates) {
346
366
  * directory in `names` order. Single source of truth for the "walk up
347
367
  * looking for one of these config filenames" loop that `opengrep-config.ts`,
348
368
  * `typos-config.ts`, `zizmor-config.ts`, and `sgconfig.ts` each hand-rolled
349
- * independently (refs #680).
369
+ * independently (refs #680), and that `php-cs-fixer-config.ts` now delegates
370
+ * to as well (refs #2472 review F2).
371
+ *
372
+ * UNCEILINGED by default (refs #2472 review round 3, F1) — `options.homeDir`
373
+ * is opt-in, not default-on. A prior version applied the SAME `$HOME`
374
+ * ceiling as `findNearestMarkerRoot` unconditionally, which broke every one
375
+ * of these tools' actual discovery contract: each of them treats a config
376
+ * living directly at `$HOME` (`~/typos.toml`, `~/sgconfig.yml`, …) as the
377
+ * user's legitimate GLOBAL config, and reads it itself regardless of pi-lens
378
+ * — the ceiling didn't stop pi-lens from seeing an unrelated ancestor
379
+ * config, it stopped pi-lens from seeing the SAME config the tool was about
380
+ * to read on its own, so pi-lens silently fell back to (or, for typos,
381
+ * injected and let its own shipped `_typos.toml` merge over) the user's
382
+ * config where the tool's own resolver would have honored it. `php-cs-fixer`
383
+ * makes the same mismatch concrete: its detection gates
384
+ * (`hasPhpCsFixerConfig` via `findNearestContaining`, `phpCsFixerFormatter
385
+ * .detect` via its own `findUp`) are both unceilinged, so a ceilinged
386
+ * carriage here disagreed with its own gate — "config exists" from the gate,
387
+ * "config not found" from the resolver — and dropped the very `--config`
388
+ * argv #2472 exists to carry. Pass `options.homeDir` only when a caller
389
+ * affirmatively wants the ceiling (a config found at or above THAT directory
390
+ * is never returned); omitting it walks all the way to the filesystem root,
391
+ * matching `findNearestContaining`'s unceilinged behavior and every
392
+ * underlying tool's own discovery.
350
393
  *
351
394
  * Distinct from `findNearestContaining`, which returns the containing
352
395
  * directory rather than the matched file path — use that one when the caller
@@ -356,8 +399,12 @@ export function findNearestContaining(startDir, candidates) {
356
399
  * findLocalToolConfig(cwd, ["typos.toml", "_typos.toml", ".typos.toml"]);
357
400
  * // → "/repo/typos.toml" if present, else undefined
358
401
  */
359
- export function findLocalToolConfig(startDir, names) {
402
+ export function findLocalToolConfig(startDir, names, options = {}) {
403
+ const homeDir = options.homeDir !== undefined ? path.resolve(options.homeDir) : undefined;
360
404
  for (const dir of walkUpDirs(startDir || process.cwd())) {
405
+ if (homeDir !== undefined && isAtOrAboveHomeDir(dir, homeDir)) {
406
+ return undefined;
407
+ }
361
408
  for (const name of names) {
362
409
  const candidate = path.join(dir, name);
363
410
  if (existsSync(candidate))
@@ -366,6 +413,25 @@ export function findLocalToolConfig(startDir, names) {
366
413
  }
367
414
  return undefined;
368
415
  }
416
+ /**
417
+ * Accept only a real Git repository marker: a directory with HEAD, or a
418
+ * worktree/submodule marker file whose first line starts with `gitdir:`.
419
+ */
420
+ export function isRealGitMarker(markerPath) {
421
+ try {
422
+ const marker = statSync(markerPath);
423
+ if (marker.isDirectory())
424
+ return existsSync(path.join(markerPath, "HEAD"));
425
+ if (!marker.isFile())
426
+ return false;
427
+ return readFileSync(markerPath, "utf8")
428
+ .split(/\r?\n/, 1)[0]
429
+ .startsWith("gitdir:");
430
+ }
431
+ catch {
432
+ return false;
433
+ }
434
+ }
369
435
  /**
370
436
  * Walk up from `startDir` looking for a directory containing any of
371
437
  * `markers`, the same containment-aware climb `knip-client.ts` and
@@ -391,11 +457,13 @@ export function findLocalToolConfig(startDir, names) {
391
457
  export function findNearestMarkerRoot(startDir, markers, options = {}) {
392
458
  const boundaries = options.boundaries ?? [];
393
459
  const homeDir = path.resolve(options.homeDir ?? os.homedir());
460
+ const markerPredicate = options.markerPredicate ?? (() => true);
394
461
  let current = path.resolve(startDir);
395
462
  for (let depth = 0; depth < 64; depth++) {
396
463
  if (isAtOrAboveHomeDir(current, homeDir))
397
464
  return null;
398
- if (markers.some((m) => existsSync(path.join(current, m))))
465
+ if (markers.some((m) => existsSync(path.join(current, m)) &&
466
+ markerPredicate(path.join(current, m))))
399
467
  return current;
400
468
  if (boundaries.some((m) => existsSync(path.join(current, m))))
401
469
  return null;
@@ -415,17 +483,74 @@ export function findNearestMarkerRoot(startDir, markers, options = {}) {
415
483
  * `=== os.homedir()` check (which a marker found *above* `$HOME` slips past).
416
484
  * A normal project *under* home (e.g. `~/code/app`) is NOT at-or-above home,
417
485
  * so it still resolves fine. Refs #253.
486
+ *
487
+ * ONE expression, deliberately: `path.relative` alone answers "is home inside
488
+ * `dir`, or the same directory as `dir`", and it answers with the PLATFORM's
489
+ * path-equality semantics — case-folding on win32, case-sensitive on POSIX.
490
+ * The `resolvedDir === resolvedHome` shortcut that used to precede it was
491
+ * case-SENSITIVE on every platform, so `c:\Users\jane` (the lowercase-drive
492
+ * form VS Code URIs produce, and the form 46 records in a real `latency.log`
493
+ * carry) missed the equality test AND was then rejected by a `rel !== ""`
494
+ * clause — reporting "not at home" for the home directory itself and letting
495
+ * every ceilinged walker climb straight past HOME (#2544 review F1).
496
+ *
497
+ * rel === "" ⇢ `dir` IS home
498
+ * rel relative, no leading `..` ⇢ home is INSIDE `dir` (an ancestor)
499
+ * rel starts with `..` ⇢ home is elsewhere — sibling, or the
500
+ * prefix trap `C:\Users\jane2` → home
501
+ * `C:\Users\jane` gives `..\jane`
502
+ * rel absolute ⇢ different Windows drive
503
+ *
504
+ * Same shape as `isUnderDir` above; keep the two in step.
505
+ *
506
+ * `pathImpl` exists so the ubuntu Unit tests lane — the authoritative one —
507
+ * can exercise the WIN32 semantics this helper's whole bug was about. Every
508
+ * assertion that matters here (drive-letter folding, cross-drive `rel`) is
509
+ * win32-only, so with the ambient `path` hardcoded the ceiling was enforced
510
+ * only on a maintainer's dev box and CI's diff was structurally zero (#2544
511
+ * review F2). Production never passes it; `path.win32`/`path.posix` are pure,
512
+ * host-independent implementations, so `tests/clients/path-utils.test.ts` runs
513
+ * the same table under both on every lane. Lane-level fix tracked as #2536.
418
514
  */
419
- export function isAtOrAboveHomeDir(dir, homeDir = os.homedir()) {
420
- const resolvedDir = path.resolve(dir);
421
- const resolvedHome = path.resolve(homeDir);
422
- if (resolvedDir === resolvedHome)
423
- return true;
424
- // `dir` is an ancestor of home ⇢ home lies inside dir ⇢ the relative path
425
- // from dir to home has no leading `..` and is not absolute (cross-drive on
426
- // Windows yields an absolute rel, correctly treated as "not above").
427
- const rel = path.relative(resolvedDir, resolvedHome);
428
- return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel);
515
+ export function isAtOrAboveHomeDir(dir, homeDir = os.homedir(), pathImpl = path) {
516
+ const rel = pathImpl.relative(pathImpl.resolve(dir), pathImpl.resolve(homeDir));
517
+ return !rel.startsWith("..") && !pathImpl.isAbsolute(rel);
518
+ }
519
+ /**
520
+ * Rewrite a `$HOME`-anchored path to its `~` form for a DIAGNOSTIC surface.
521
+ *
522
+ * `~/.pi-lens/config.json` says everything an operator needs about which file
523
+ * won, and `C:/Users/jane.doe/.pi-lens/config.json` says that plus the
524
+ * account name. The second is the shape a global config path always has, so any
525
+ * projection that names one leaks an identifier by default rather than by
526
+ * accident (#2440 review finding F5).
527
+ *
528
+ * Deliberately NOT built on `normalizeFilePath`/`isUnderDir`: those resolve
529
+ * through `realpathSync`, and a redaction helper on a diagnostic path must be
530
+ * pure, total, and unable to throw for a file that no longer exists. This is a
531
+ * string rewrite and nothing else.
532
+ *
533
+ * A path that is not under home is returned UNCHANGED — separators included.
534
+ * Normalizing unrelated paths on the way past would make the helper's blast
535
+ * radius every path any projection ever carries, for no redaction benefit.
536
+ */
537
+ export function homeRelativePath(filePath, homeDir = os.homedir()) {
538
+ if (filePath.length === 0)
539
+ return filePath;
540
+ const candidate = toPosix(filePath);
541
+ const home = toPosix(homeDir).replace(/\/+$/, "");
542
+ if (home.length === 0)
543
+ return filePath;
544
+ // Windows paths are case-insensitive, so the COMPARISON folds case there
545
+ // while the returned tail keeps the caller's own casing.
546
+ const fold = (text) => process.platform === "win32" ? text.toLowerCase() : text;
547
+ const foldedHome = fold(home);
548
+ const foldedCandidate = fold(candidate);
549
+ if (foldedCandidate === foldedHome)
550
+ return "~";
551
+ if (!foldedCandidate.startsWith(`${foldedHome}/`))
552
+ return filePath;
553
+ return `~${candidate.slice(home.length)}`;
429
554
  }
430
555
  export function isUnderDir(child, parent) {
431
556
  const normChild = normalizeFilePath(child);
@@ -484,6 +609,317 @@ export function direntsHaveMarkerGlobMatch(entries, pattern) {
484
609
  return entries.some((entry) => (entry.isFile() || entry.isSymbolicLink()) &&
485
610
  nameMatchesMarkerGlob(entry.name, pattern));
486
611
  }
612
+ /**
613
+ * Cargo `[workspace] members`/`exclude` entries.
614
+ *
615
+ * KNOWN LIMITATION (#1671 F6, documented rather than implemented, preserved
616
+ * byte-for-byte by #2591's fold): a recursive `**` component is NOT supported
617
+ * and a pattern containing one never matches — cargo workspaces that rely on
618
+ * `**` to pull in an arbitrarily-nested crate tree under-hoist (the crate stays
619
+ * independently rooted instead of joining the workspace). Folding cargo onto
620
+ * uv's `crosses-components` globstar would silently change Rust-LSP root
621
+ * selection, so the divergence is kept as a dialect value, not resolved.
622
+ *
623
+ * `characterClasses: false` is likewise a preserved divergence, not a
624
+ * considered choice: cargo's pre-fold segment compiler escaped `[` and `]`
625
+ * into literals, so `crates/[ab]` names a directory literally called `[ab]`.
626
+ * Real cargo (the `glob` crate) would read it as a class; changing that here
627
+ * would be the same unreviewed Rust-hoisting change.
628
+ */
629
+ export const CARGO_WORKSPACE_MEMBER_DIALECT = {
630
+ name: "cargo",
631
+ globstar: "pattern-never-matches",
632
+ wildcardCrossesSeparator: false,
633
+ characterClasses: false,
634
+ normalizePattern: (pattern) => pattern.replace(/\/+$/, ""),
635
+ };
636
+ /**
637
+ * uv `[tool.uv.workspace] members`, pinned to
638
+ * `astral-sh/uv@3c979abda4530fe9bf3d92e9bcf5c5575e3b3126`,
639
+ * `crates/uv-workspace/src/workspace.rs` `is_included_in_workspace`: the glob is
640
+ * `normalize_path`d first (so a leading `./` and any interior `.` component are
641
+ * not part of the pattern) and matched with
642
+ * `MatchOptions { require_literal_separator: true, ..MatchOptions::new() }`.
643
+ */
644
+ export const UV_WORKSPACE_MEMBERS_DIALECT = {
645
+ name: "uv-members",
646
+ globstar: "crosses-components",
647
+ wildcardCrossesSeparator: false,
648
+ characterClasses: true,
649
+ normalizePattern: (pattern) => path.posix.normalize(toPosix(pattern)),
650
+ };
651
+ /**
652
+ * uv `[tool.uv.workspace] exclude`, same upstream file and SHA
653
+ * (`WorkspaceExclusions::matches`): an exclusion is matched with
654
+ * `Pattern::matches_path`, i.e. `MatchOptions::new()` defaults, where
655
+ * `require_literal_separator` is FALSE — so a `*` in an exclusion DOES cross
656
+ * `/` (`exclude = ['packages/a*c']` excludes `packages/a/b/c`). Two different
657
+ * option sets inside one tool, which is why the dialect is an object and the
658
+ * two uv constants are not one.
659
+ *
660
+ * Before #2591 this was a documented limitation: minimatch cannot express a
661
+ * separator-crossing `*`, so pi-lens under-excluded. The dialect object can, so
662
+ * it is now implemented rather than documented.
663
+ */
664
+ export const UV_WORKSPACE_EXCLUDE_DIALECT = {
665
+ name: "uv-exclude",
666
+ globstar: "crosses-components",
667
+ wildcardCrossesSeparator: true,
668
+ characterClasses: true,
669
+ normalizePattern: (pattern) => path.posix.normalize(toPosix(pattern)),
670
+ };
671
+ /**
672
+ * Index of the `]` closing the character class opened at `open`, or `-1` when
673
+ * the run is unterminated (in which case the `[` is a literal). A leading `!`
674
+ * or `^` negates, and a `]` in first position is a literal class member — the
675
+ * shape both minimatch and rust `glob` accept.
676
+ */
677
+ function findCharacterClassEnd(segment, open) {
678
+ let i = open + 1;
679
+ if (segment[i] === "!" || segment[i] === "^")
680
+ i += 1;
681
+ if (segment[i] === "]")
682
+ i += 1;
683
+ for (; i < segment.length; i += 1)
684
+ if (segment[i] === "]")
685
+ return i;
686
+ return -1;
687
+ }
688
+ /**
689
+ * May a wildcard with this separator policy consume `ch`?
690
+ *
691
+ * A separator-confined wildcard (`[^/]` in the regex this replaced) takes
692
+ * anything but `/`. A separator-CROSSING one — uv `exclude`'s `*`/`?`, and
693
+ * every `**` in either uv dialect — spelled `.` in that regex, which is every
694
+ * code unit EXCEPT the four line terminators. That exclusion is preserved
695
+ * deliberately rather than "fixed": minimatch's globstar is `.`-based too, so
696
+ * a `\n` inside a directory name has never matched across a `**` in either
697
+ * implementation, and the differential oracle would flag a change here as a
698
+ * divergence rather than an improvement (#2603).
699
+ */
700
+ function stepAcceptsChar(crossesSeparator, ch) {
701
+ return crossesSeparator
702
+ ? ch !== "\n" && ch !== "\r" && ch !== "\u2028" && ch !== "\u2029"
703
+ : ch !== "/";
704
+ }
705
+ /**
706
+ * Compile a normalized workspace-member pattern into a step list, or
707
+ * `undefined` when it cannot be compiled at all.
708
+ *
709
+ * A `**` component consumes zero or more path components — except as the LAST
710
+ * component, where it requires at least one (`a/**` matches `a/b`, not `a`),
711
+ * reproducing both minimatch's and rust `glob`'s answer. It is emitted as
712
+ * `split → one → star → literal "/"`, i.e. "nothing, or one-or-more characters
713
+ * followed by the `/` that ends them", after the separator that precedes it.
714
+ * That is the same language as the `(?:/.+)?/` group the previous compiler
715
+ * emitted — `X(?:/.+)?/Y` and `X/(?:.+/)?Y` both denote `X/Y` ∪ `X/.+/Y` — in a
716
+ * form with no nested quantifier.
717
+ */
718
+ function compileWorkspaceMemberPattern(pattern, dialect) {
719
+ const isGlobstar = (component) => component === "**" && dialect.globstar === "crosses-components";
720
+ const components = pattern.split("/");
721
+ const steps = [];
722
+ let needSeparator = false;
723
+ for (let c = 0; c < components.length; c += 1) {
724
+ if (needSeparator)
725
+ steps.push({ kind: "literal", char: "/" });
726
+ needSeparator = false;
727
+ const component = components[c];
728
+ if (isGlobstar(component)) {
729
+ if (c === components.length - 1) {
730
+ // `.+` — a trailing `**` requires at least one character.
731
+ steps.push({ kind: "one", crossesSeparator: true });
732
+ steps.push({ kind: "star", crossesSeparator: true });
733
+ continue;
734
+ }
735
+ // `(?:.+/)?` — zero or more whole components. The group already ends
736
+ // at a `/`, so the next component must NOT emit one.
737
+ const split = { kind: "split", alternative: -1 };
738
+ steps.push(split);
739
+ steps.push({ kind: "one", crossesSeparator: true });
740
+ steps.push({ kind: "star", crossesSeparator: true });
741
+ steps.push({ kind: "literal", char: "/" });
742
+ split.alternative = steps.length;
743
+ continue;
744
+ }
745
+ for (let i = 0; i < component.length; i += 1) {
746
+ const ch = component[i];
747
+ if (ch === "*") {
748
+ steps.push({
749
+ kind: "star",
750
+ crossesSeparator: dialect.wildcardCrossesSeparator,
751
+ });
752
+ continue;
753
+ }
754
+ if (ch === "?") {
755
+ steps.push({
756
+ kind: "one",
757
+ crossesSeparator: dialect.wildcardCrossesSeparator,
758
+ });
759
+ continue;
760
+ }
761
+ if (ch === "[" && dialect.characterClasses) {
762
+ const close = findCharacterClassEnd(component, i);
763
+ if (close !== -1) {
764
+ // `\` and a first-position `]` are literal class MEMBERS in glob;
765
+ // left alone they would be a regex escape and an empty-class
766
+ // terminator (`[]ab]` is `[]` + `ab]` in JS), so both are escaped.
767
+ const body = component.slice(i + 1, close).replace(/[\\\]]/g, "\\$&");
768
+ const source = body.startsWith("!")
769
+ ? `[^${body.slice(1)}]`
770
+ : `[${body}]`;
771
+ let match;
772
+ try {
773
+ match = new RegExp(`^${source}$`);
774
+ }
775
+ catch {
776
+ // A glob character class is not a JS character class: `[z-a]`
777
+ // is a legal glob (matching nothing, since the range is empty)
778
+ // and an illegal RegExp ("Range out of order"). Fail CLOSED —
779
+ // an uncompilable pattern declares no member and excludes
780
+ // nothing — which is also the answer the deleted minimatch call
781
+ // gave (#2591 review round 2, F2).
782
+ return undefined;
783
+ }
784
+ steps.push({ kind: "class", match });
785
+ i = close;
786
+ continue;
787
+ }
788
+ }
789
+ steps.push({ kind: "literal", char: ch });
790
+ }
791
+ needSeparator = true;
792
+ }
793
+ return steps;
794
+ }
795
+ /**
796
+ * Does the whole of `subject` match the whole of `steps`?
797
+ *
798
+ * A memoized (step index, path index) table, filled once, bottom-up:
799
+ * `table[s][p]` is "steps `s…` match `subject[p…]`". Every cell reads only
800
+ * cells with a larger step index or a larger path index, so one backward
801
+ * double loop fills the table with no recursion and no re-entry — the match is
802
+ * O(steps x characters) in time and space, with no path through it that can
803
+ * take exponential time (#2603).
804
+ *
805
+ * The compiled whole-path regex this replaced was correct but backtracking:
806
+ * every `**` emitted its own nullable `.+`, and N of them explored 2^N splits
807
+ * of a non-matching subject. #2591 collapsed CONSECUTIVE `**`s, which is a
808
+ * normalization that cannot fire across a separating component, so
809
+ * `("**\/*" x12)/zzz` against a 40-component path still took 124900 ms (#2603);
810
+ * the same shapes are microseconds here. There is nothing left to collapse for
811
+ * speed, so no collapse is done: `a/**\/**\/b` compiles to two adjacent
812
+ * `(?:.+/)?` groups, which denote the same language as one and cost the same
813
+ * table.
814
+ */
815
+ function matchesWorkspaceMemberSteps(steps, subject) {
816
+ const stepCount = steps.length;
817
+ const width = subject.length + 1;
818
+ // One byte per (step, position) cell; `1` means "the rest matches from here".
819
+ const table = new Uint8Array((stepCount + 1) * width);
820
+ for (let p = subject.length; p >= 0; p -= 1) {
821
+ const atEnd = p === subject.length;
822
+ // The empty step list matches only the empty remainder.
823
+ table[stepCount * width + p] = atEnd ? 1 : 0;
824
+ for (let s = stepCount - 1; s >= 0; s -= 1) {
825
+ const step = steps[s];
826
+ const next = (s + 1) * width;
827
+ let matched = 0;
828
+ switch (step.kind) {
829
+ case "literal":
830
+ matched =
831
+ !atEnd && subject[p] === step.char ? table[next + p + 1] : 0;
832
+ break;
833
+ case "one":
834
+ matched =
835
+ !atEnd && stepAcceptsChar(step.crossesSeparator, subject[p])
836
+ ? table[next + p + 1]
837
+ : 0;
838
+ break;
839
+ case "class":
840
+ matched =
841
+ !atEnd && step.match.test(subject[p]) ? table[next + p + 1] : 0;
842
+ break;
843
+ case "star":
844
+ // Consume nothing, or one more character and stay on this step.
845
+ matched =
846
+ table[next + p] === 1 ||
847
+ (!atEnd &&
848
+ stepAcceptsChar(step.crossesSeparator, subject[p]) &&
849
+ table[s * width + p + 1] === 1)
850
+ ? 1
851
+ : 0;
852
+ break;
853
+ case "split":
854
+ matched =
855
+ table[step.alternative * width + p] === 1 || table[next + p] === 1
856
+ ? 1
857
+ : 0;
858
+ break;
859
+ }
860
+ table[s * width + p] = matched;
861
+ }
862
+ }
863
+ return table[0] === 1;
864
+ }
865
+ /**
866
+ * Does `relativePath` — a `/`-separated path relative to the workspace root —
867
+ * match one declared workspace-member (or workspace-exclude) `pattern` under
868
+ * `dialect`?
869
+ *
870
+ * This is the ONE workspace-member matcher (#2591). `clients/lsp/server.ts`'s
871
+ * `cargoWorkspaceDeclaresMember` and `clients/python-environment.ts`'s
872
+ * `isUvWorkspaceMember` are thin callers of it; neither keeps a private segment
873
+ * compiler or minimatch options block any more.
874
+ *
875
+ * `clients/review-graph/workspace-modules.ts`'s `expandWorkspacePattern` is
876
+ * deliberately NOT a caller: npm/pnpm `workspaces` entries are EXPANDED against
877
+ * the filesystem (one `readdir` of the directory preceding the first `*`,
878
+ * yielding the directories that exist and hold a manifest) rather than tested
879
+ * against a candidate path. It answers "which directories does this pattern
880
+ * name", not "does this pattern name this directory"; folding it in would mean
881
+ * giving this pure function a filesystem.
882
+ *
883
+ * The supported syntax is the intersection of the two upstream dialects: `*`,
884
+ * `?`, `**`, `[abc]`/`[!abc]` (dialect-gated), and literals. minimatch-only
885
+ * extensions the pre-fold uv path inherited by accident — brace expansion,
886
+ * extglobs, leading-`!` negation, leading-`#` comments — are NOT honored, and
887
+ * uv's own `glob` crate does not honor them either (same pinned SHA), so
888
+ * dropping them moves uv toward upstream rather than away from it.
889
+ *
890
+ * Matching is NON-BACKTRACKING (#2603): the pattern compiles to a list of
891
+ * bounded steps and {@link matchesWorkspaceMemberSteps} decides it with one
892
+ * memoized (step, position) table, O(steps x characters), no matter how many
893
+ * `**`s the pattern carries. The anchored whole-path regex this replaced was
894
+ * correct but explored 2^N splits of a non-matching path for N `**`
895
+ * components — 124900 ms for `("**\/*" x12)/zzz` against a 40-component path,
896
+ * on a call `detectPythonEnvironment` awaits with no timeout, i.e. a wedged
897
+ * turn rather than a slow answer. The wall-clock half of that fix lives in
898
+ * `tests/clients/workspace-glob-nonbacktracking-budget.test.ts`; the answers
899
+ * are unchanged, pinned by the dialect table and the minimatch differential in
900
+ * `tests/clients/path-utils.test.ts`.
901
+ *
902
+ * A pattern that cannot be compiled at all answers `false` for every path —
903
+ * it declares no member and excludes nothing (#2591 review round 2, F2). The
904
+ * only such patterns today carry an empty character-class range (`[z-a]`:
905
+ * legal glob, illegal JS RegExp). Reachability note, recorded rather than
906
+ * assumed away: no such pattern can reach here through a manifest right now,
907
+ * because `parseTomlStringArray` (`clients/cargo-manifest.ts`) captures an
908
+ * array body non-greedily up to the FIRST `]`, so any entry containing a `]`
909
+ * loses its closing quote and is dropped before it becomes a pattern. That is
910
+ * a property of the TOML reader, not of this matcher, and it is not this
911
+ * function's to rely on — the character-class axis stays because upstream
912
+ * cargo and uv both support classes, and the reader's gap may be closed later.
913
+ */
914
+ export function matchesWorkspaceMemberPattern(pattern, relativePath, dialect) {
915
+ const normalized = dialect.normalizePattern(pattern);
916
+ if (dialect.globstar === "pattern-never-matches" &&
917
+ normalized.includes("**")) {
918
+ return false;
919
+ }
920
+ const steps = compileWorkspaceMemberPattern(normalized, dialect);
921
+ return (steps !== undefined && matchesWorkspaceMemberSteps(steps, relativePath));
922
+ }
487
923
  /**
488
924
  * Narrow no-break space, U+202F. macOS writes it before AM/PM in screenshot
489
925
  * file names; users type an ordinary space.
@@ -549,7 +985,7 @@ export function normalizeHostToolPath(input, options = {}) {
549
985
  (process.platform === "win32" && normalized.startsWith("~\\"))) {
550
986
  return path.join(os.homedir(), normalized.slice(2));
551
987
  }
552
- if (/^file:\/\//.test(normalized)) {
988
+ if (normalized.startsWith("file://")) {
553
989
  try {
554
990
  return fileURLToPath(normalized);
555
991
  }
@@ -2,8 +2,8 @@ import * as fs from "node:fs";
2
2
  import * as readline from "node:readline";
3
3
  import { flushLatencyLog, getLatencyLogPath, } from "./latency-logger.js";
4
4
  import { getMaxLogSizeMB } from "./log-cleanup.js";
5
- export const DEFAULT_PERF_TOP_N = 5;
6
- export const MAX_PERF_TOP_N = 50;
5
+ const DEFAULT_PERF_TOP_N = 5;
6
+ const MAX_PERF_TOP_N = 50;
7
7
  export const MAX_PERF_PHASE_SAMPLES = 20_000;
8
8
  const PARSE_YIELD_EVERY = 500;
9
9
  // The window follows the same threshold that rotates the log out from under it.
@@ -40,7 +40,14 @@ export function createDebounceScheduler(args) {
40
40
  write(key, payload);
41
41
  }
42
42
  function flushAll() {
43
- for (const key of [...pending.keys()])
43
+ // Iterates `pending.keys()` live (a `Map` iterator reflects mutation
44
+ // during iteration): `flush` deletes the current key before calling
45
+ // `write`, which is safe, but the injected `write` callback must NOT
46
+ // synchronously call `schedule` again for a key not yet visited — that
47
+ // would insert into `pending` mid-iteration and the live iterator would
48
+ // pick it up, flushing a payload the caller expected to survive as
49
+ // "still pending" after this call returns.
50
+ for (const key of pending.keys())
44
51
  flush(key);
45
52
  }
46
53
  function schedule(key, payload) {