@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
@@ -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))
@@ -415,17 +462,74 @@ export function findNearestMarkerRoot(startDir, markers, options = {}) {
415
462
  * `=== os.homedir()` check (which a marker found *above* `$HOME` slips past).
416
463
  * A normal project *under* home (e.g. `~/code/app`) is NOT at-or-above home,
417
464
  * so it still resolves fine. Refs #253.
465
+ *
466
+ * ONE expression, deliberately: `path.relative` alone answers "is home inside
467
+ * `dir`, or the same directory as `dir`", and it answers with the PLATFORM's
468
+ * path-equality semantics — case-folding on win32, case-sensitive on POSIX.
469
+ * The `resolvedDir === resolvedHome` shortcut that used to precede it was
470
+ * case-SENSITIVE on every platform, so `c:\Users\jane` (the lowercase-drive
471
+ * form VS Code URIs produce, and the form 46 records in a real `latency.log`
472
+ * carry) missed the equality test AND was then rejected by a `rel !== ""`
473
+ * clause — reporting "not at home" for the home directory itself and letting
474
+ * every ceilinged walker climb straight past HOME (#2544 review F1).
475
+ *
476
+ * rel === "" ⇢ `dir` IS home
477
+ * rel relative, no leading `..` ⇢ home is INSIDE `dir` (an ancestor)
478
+ * rel starts with `..` ⇢ home is elsewhere — sibling, or the
479
+ * prefix trap `C:\Users\jane2` → home
480
+ * `C:\Users\jane` gives `..\jane`
481
+ * rel absolute ⇢ different Windows drive
482
+ *
483
+ * Same shape as `isUnderDir` above; keep the two in step.
484
+ *
485
+ * `pathImpl` exists so the ubuntu Unit tests lane — the authoritative one —
486
+ * can exercise the WIN32 semantics this helper's whole bug was about. Every
487
+ * assertion that matters here (drive-letter folding, cross-drive `rel`) is
488
+ * win32-only, so with the ambient `path` hardcoded the ceiling was enforced
489
+ * only on a maintainer's dev box and CI's diff was structurally zero (#2544
490
+ * review F2). Production never passes it; `path.win32`/`path.posix` are pure,
491
+ * host-independent implementations, so `tests/clients/path-utils.test.ts` runs
492
+ * the same table under both on every lane. Lane-level fix tracked as #2536.
418
493
  */
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);
494
+ export function isAtOrAboveHomeDir(dir, homeDir = os.homedir(), pathImpl = path) {
495
+ const rel = pathImpl.relative(pathImpl.resolve(dir), pathImpl.resolve(homeDir));
496
+ return !rel.startsWith("..") && !pathImpl.isAbsolute(rel);
497
+ }
498
+ /**
499
+ * Rewrite a `$HOME`-anchored path to its `~` form for a DIAGNOSTIC surface.
500
+ *
501
+ * `~/.pi-lens/config.json` says everything an operator needs about which file
502
+ * won, and `C:/Users/jane.doe/.pi-lens/config.json` says that plus the
503
+ * account name. The second is the shape a global config path always has, so any
504
+ * projection that names one leaks an identifier by default rather than by
505
+ * accident (#2440 review finding F5).
506
+ *
507
+ * Deliberately NOT built on `normalizeFilePath`/`isUnderDir`: those resolve
508
+ * through `realpathSync`, and a redaction helper on a diagnostic path must be
509
+ * pure, total, and unable to throw for a file that no longer exists. This is a
510
+ * string rewrite and nothing else.
511
+ *
512
+ * A path that is not under home is returned UNCHANGED — separators included.
513
+ * Normalizing unrelated paths on the way past would make the helper's blast
514
+ * radius every path any projection ever carries, for no redaction benefit.
515
+ */
516
+ export function homeRelativePath(filePath, homeDir = os.homedir()) {
517
+ if (filePath.length === 0)
518
+ return filePath;
519
+ const candidate = toPosix(filePath);
520
+ const home = toPosix(homeDir).replace(/\/+$/, "");
521
+ if (home.length === 0)
522
+ return filePath;
523
+ // Windows paths are case-insensitive, so the COMPARISON folds case there
524
+ // while the returned tail keeps the caller's own casing.
525
+ const fold = (text) => process.platform === "win32" ? text.toLowerCase() : text;
526
+ const foldedHome = fold(home);
527
+ const foldedCandidate = fold(candidate);
528
+ if (foldedCandidate === foldedHome)
529
+ return "~";
530
+ if (!foldedCandidate.startsWith(`${foldedHome}/`))
531
+ return filePath;
532
+ return `~${candidate.slice(home.length)}`;
429
533
  }
430
534
  export function isUnderDir(child, parent) {
431
535
  const normChild = normalizeFilePath(child);
@@ -484,6 +588,317 @@ export function direntsHaveMarkerGlobMatch(entries, pattern) {
484
588
  return entries.some((entry) => (entry.isFile() || entry.isSymbolicLink()) &&
485
589
  nameMatchesMarkerGlob(entry.name, pattern));
486
590
  }
591
+ /**
592
+ * Cargo `[workspace] members`/`exclude` entries.
593
+ *
594
+ * KNOWN LIMITATION (#1671 F6, documented rather than implemented, preserved
595
+ * byte-for-byte by #2591's fold): a recursive `**` component is NOT supported
596
+ * and a pattern containing one never matches — cargo workspaces that rely on
597
+ * `**` to pull in an arbitrarily-nested crate tree under-hoist (the crate stays
598
+ * independently rooted instead of joining the workspace). Folding cargo onto
599
+ * uv's `crosses-components` globstar would silently change Rust-LSP root
600
+ * selection, so the divergence is kept as a dialect value, not resolved.
601
+ *
602
+ * `characterClasses: false` is likewise a preserved divergence, not a
603
+ * considered choice: cargo's pre-fold segment compiler escaped `[` and `]`
604
+ * into literals, so `crates/[ab]` names a directory literally called `[ab]`.
605
+ * Real cargo (the `glob` crate) would read it as a class; changing that here
606
+ * would be the same unreviewed Rust-hoisting change.
607
+ */
608
+ export const CARGO_WORKSPACE_MEMBER_DIALECT = {
609
+ name: "cargo",
610
+ globstar: "pattern-never-matches",
611
+ wildcardCrossesSeparator: false,
612
+ characterClasses: false,
613
+ normalizePattern: (pattern) => pattern.replace(/\/+$/, ""),
614
+ };
615
+ /**
616
+ * uv `[tool.uv.workspace] members`, pinned to
617
+ * `astral-sh/uv@3c979abda4530fe9bf3d92e9bcf5c5575e3b3126`,
618
+ * `crates/uv-workspace/src/workspace.rs` `is_included_in_workspace`: the glob is
619
+ * `normalize_path`d first (so a leading `./` and any interior `.` component are
620
+ * not part of the pattern) and matched with
621
+ * `MatchOptions { require_literal_separator: true, ..MatchOptions::new() }`.
622
+ */
623
+ export const UV_WORKSPACE_MEMBERS_DIALECT = {
624
+ name: "uv-members",
625
+ globstar: "crosses-components",
626
+ wildcardCrossesSeparator: false,
627
+ characterClasses: true,
628
+ normalizePattern: (pattern) => path.posix.normalize(toPosix(pattern)),
629
+ };
630
+ /**
631
+ * uv `[tool.uv.workspace] exclude`, same upstream file and SHA
632
+ * (`WorkspaceExclusions::matches`): an exclusion is matched with
633
+ * `Pattern::matches_path`, i.e. `MatchOptions::new()` defaults, where
634
+ * `require_literal_separator` is FALSE — so a `*` in an exclusion DOES cross
635
+ * `/` (`exclude = ['packages/a*c']` excludes `packages/a/b/c`). Two different
636
+ * option sets inside one tool, which is why the dialect is an object and the
637
+ * two uv constants are not one.
638
+ *
639
+ * Before #2591 this was a documented limitation: minimatch cannot express a
640
+ * separator-crossing `*`, so pi-lens under-excluded. The dialect object can, so
641
+ * it is now implemented rather than documented.
642
+ */
643
+ export const UV_WORKSPACE_EXCLUDE_DIALECT = {
644
+ name: "uv-exclude",
645
+ globstar: "crosses-components",
646
+ wildcardCrossesSeparator: true,
647
+ characterClasses: true,
648
+ normalizePattern: (pattern) => path.posix.normalize(toPosix(pattern)),
649
+ };
650
+ /**
651
+ * Index of the `]` closing the character class opened at `open`, or `-1` when
652
+ * the run is unterminated (in which case the `[` is a literal). A leading `!`
653
+ * or `^` negates, and a `]` in first position is a literal class member — the
654
+ * shape both minimatch and rust `glob` accept.
655
+ */
656
+ function findCharacterClassEnd(segment, open) {
657
+ let i = open + 1;
658
+ if (segment[i] === "!" || segment[i] === "^")
659
+ i += 1;
660
+ if (segment[i] === "]")
661
+ i += 1;
662
+ for (; i < segment.length; i += 1)
663
+ if (segment[i] === "]")
664
+ return i;
665
+ return -1;
666
+ }
667
+ /**
668
+ * May a wildcard with this separator policy consume `ch`?
669
+ *
670
+ * A separator-confined wildcard (`[^/]` in the regex this replaced) takes
671
+ * anything but `/`. A separator-CROSSING one — uv `exclude`'s `*`/`?`, and
672
+ * every `**` in either uv dialect — spelled `.` in that regex, which is every
673
+ * code unit EXCEPT the four line terminators. That exclusion is preserved
674
+ * deliberately rather than "fixed": minimatch's globstar is `.`-based too, so
675
+ * a `\n` inside a directory name has never matched across a `**` in either
676
+ * implementation, and the differential oracle would flag a change here as a
677
+ * divergence rather than an improvement (#2603).
678
+ */
679
+ function stepAcceptsChar(crossesSeparator, ch) {
680
+ return crossesSeparator
681
+ ? ch !== "\n" && ch !== "\r" && ch !== "\u2028" && ch !== "\u2029"
682
+ : ch !== "/";
683
+ }
684
+ /**
685
+ * Compile a normalized workspace-member pattern into a step list, or
686
+ * `undefined` when it cannot be compiled at all.
687
+ *
688
+ * A `**` component consumes zero or more path components — except as the LAST
689
+ * component, where it requires at least one (`a/**` matches `a/b`, not `a`),
690
+ * reproducing both minimatch's and rust `glob`'s answer. It is emitted as
691
+ * `split → one → star → literal "/"`, i.e. "nothing, or one-or-more characters
692
+ * followed by the `/` that ends them", after the separator that precedes it.
693
+ * That is the same language as the `(?:/.+)?/` group the previous compiler
694
+ * emitted — `X(?:/.+)?/Y` and `X/(?:.+/)?Y` both denote `X/Y` ∪ `X/.+/Y` — in a
695
+ * form with no nested quantifier.
696
+ */
697
+ function compileWorkspaceMemberPattern(pattern, dialect) {
698
+ const isGlobstar = (component) => component === "**" && dialect.globstar === "crosses-components";
699
+ const components = pattern.split("/");
700
+ const steps = [];
701
+ let needSeparator = false;
702
+ for (let c = 0; c < components.length; c += 1) {
703
+ if (needSeparator)
704
+ steps.push({ kind: "literal", char: "/" });
705
+ needSeparator = false;
706
+ const component = components[c];
707
+ if (isGlobstar(component)) {
708
+ if (c === components.length - 1) {
709
+ // `.+` — a trailing `**` requires at least one character.
710
+ steps.push({ kind: "one", crossesSeparator: true });
711
+ steps.push({ kind: "star", crossesSeparator: true });
712
+ continue;
713
+ }
714
+ // `(?:.+/)?` — zero or more whole components. The group already ends
715
+ // at a `/`, so the next component must NOT emit one.
716
+ const split = { kind: "split", alternative: -1 };
717
+ steps.push(split);
718
+ steps.push({ kind: "one", crossesSeparator: true });
719
+ steps.push({ kind: "star", crossesSeparator: true });
720
+ steps.push({ kind: "literal", char: "/" });
721
+ split.alternative = steps.length;
722
+ continue;
723
+ }
724
+ for (let i = 0; i < component.length; i += 1) {
725
+ const ch = component[i];
726
+ if (ch === "*") {
727
+ steps.push({
728
+ kind: "star",
729
+ crossesSeparator: dialect.wildcardCrossesSeparator,
730
+ });
731
+ continue;
732
+ }
733
+ if (ch === "?") {
734
+ steps.push({
735
+ kind: "one",
736
+ crossesSeparator: dialect.wildcardCrossesSeparator,
737
+ });
738
+ continue;
739
+ }
740
+ if (ch === "[" && dialect.characterClasses) {
741
+ const close = findCharacterClassEnd(component, i);
742
+ if (close !== -1) {
743
+ // `\` and a first-position `]` are literal class MEMBERS in glob;
744
+ // left alone they would be a regex escape and an empty-class
745
+ // terminator (`[]ab]` is `[]` + `ab]` in JS), so both are escaped.
746
+ const body = component.slice(i + 1, close).replace(/[\\\]]/g, "\\$&");
747
+ const source = body.startsWith("!")
748
+ ? `[^${body.slice(1)}]`
749
+ : `[${body}]`;
750
+ let match;
751
+ try {
752
+ match = new RegExp(`^${source}$`);
753
+ }
754
+ catch {
755
+ // A glob character class is not a JS character class: `[z-a]`
756
+ // is a legal glob (matching nothing, since the range is empty)
757
+ // and an illegal RegExp ("Range out of order"). Fail CLOSED —
758
+ // an uncompilable pattern declares no member and excludes
759
+ // nothing — which is also the answer the deleted minimatch call
760
+ // gave (#2591 review round 2, F2).
761
+ return undefined;
762
+ }
763
+ steps.push({ kind: "class", match });
764
+ i = close;
765
+ continue;
766
+ }
767
+ }
768
+ steps.push({ kind: "literal", char: ch });
769
+ }
770
+ needSeparator = true;
771
+ }
772
+ return steps;
773
+ }
774
+ /**
775
+ * Does the whole of `subject` match the whole of `steps`?
776
+ *
777
+ * A memoized (step index, path index) table, filled once, bottom-up:
778
+ * `table[s][p]` is "steps `s…` match `subject[p…]`". Every cell reads only
779
+ * cells with a larger step index or a larger path index, so one backward
780
+ * double loop fills the table with no recursion and no re-entry — the match is
781
+ * O(steps x characters) in time and space, with no path through it that can
782
+ * take exponential time (#2603).
783
+ *
784
+ * The compiled whole-path regex this replaced was correct but backtracking:
785
+ * every `**` emitted its own nullable `.+`, and N of them explored 2^N splits
786
+ * of a non-matching subject. #2591 collapsed CONSECUTIVE `**`s, which is a
787
+ * normalization that cannot fire across a separating component, so
788
+ * `("**\/*" x12)/zzz` against a 40-component path still took 124900 ms (#2603);
789
+ * the same shapes are microseconds here. There is nothing left to collapse for
790
+ * speed, so no collapse is done: `a/**\/**\/b` compiles to two adjacent
791
+ * `(?:.+/)?` groups, which denote the same language as one and cost the same
792
+ * table.
793
+ */
794
+ function matchesWorkspaceMemberSteps(steps, subject) {
795
+ const stepCount = steps.length;
796
+ const width = subject.length + 1;
797
+ // One byte per (step, position) cell; `1` means "the rest matches from here".
798
+ const table = new Uint8Array((stepCount + 1) * width);
799
+ for (let p = subject.length; p >= 0; p -= 1) {
800
+ const atEnd = p === subject.length;
801
+ // The empty step list matches only the empty remainder.
802
+ table[stepCount * width + p] = atEnd ? 1 : 0;
803
+ for (let s = stepCount - 1; s >= 0; s -= 1) {
804
+ const step = steps[s];
805
+ const next = (s + 1) * width;
806
+ let matched = 0;
807
+ switch (step.kind) {
808
+ case "literal":
809
+ matched =
810
+ !atEnd && subject[p] === step.char ? table[next + p + 1] : 0;
811
+ break;
812
+ case "one":
813
+ matched =
814
+ !atEnd && stepAcceptsChar(step.crossesSeparator, subject[p])
815
+ ? table[next + p + 1]
816
+ : 0;
817
+ break;
818
+ case "class":
819
+ matched =
820
+ !atEnd && step.match.test(subject[p]) ? table[next + p + 1] : 0;
821
+ break;
822
+ case "star":
823
+ // Consume nothing, or one more character and stay on this step.
824
+ matched =
825
+ table[next + p] === 1 ||
826
+ (!atEnd &&
827
+ stepAcceptsChar(step.crossesSeparator, subject[p]) &&
828
+ table[s * width + p + 1] === 1)
829
+ ? 1
830
+ : 0;
831
+ break;
832
+ case "split":
833
+ matched =
834
+ table[step.alternative * width + p] === 1 || table[next + p] === 1
835
+ ? 1
836
+ : 0;
837
+ break;
838
+ }
839
+ table[s * width + p] = matched;
840
+ }
841
+ }
842
+ return table[0] === 1;
843
+ }
844
+ /**
845
+ * Does `relativePath` — a `/`-separated path relative to the workspace root —
846
+ * match one declared workspace-member (or workspace-exclude) `pattern` under
847
+ * `dialect`?
848
+ *
849
+ * This is the ONE workspace-member matcher (#2591). `clients/lsp/server.ts`'s
850
+ * `cargoWorkspaceDeclaresMember` and `clients/python-environment.ts`'s
851
+ * `isUvWorkspaceMember` are thin callers of it; neither keeps a private segment
852
+ * compiler or minimatch options block any more.
853
+ *
854
+ * `clients/review-graph/workspace-modules.ts`'s `expandWorkspacePattern` is
855
+ * deliberately NOT a caller: npm/pnpm `workspaces` entries are EXPANDED against
856
+ * the filesystem (one `readdir` of the directory preceding the first `*`,
857
+ * yielding the directories that exist and hold a manifest) rather than tested
858
+ * against a candidate path. It answers "which directories does this pattern
859
+ * name", not "does this pattern name this directory"; folding it in would mean
860
+ * giving this pure function a filesystem.
861
+ *
862
+ * The supported syntax is the intersection of the two upstream dialects: `*`,
863
+ * `?`, `**`, `[abc]`/`[!abc]` (dialect-gated), and literals. minimatch-only
864
+ * extensions the pre-fold uv path inherited by accident — brace expansion,
865
+ * extglobs, leading-`!` negation, leading-`#` comments — are NOT honored, and
866
+ * uv's own `glob` crate does not honor them either (same pinned SHA), so
867
+ * dropping them moves uv toward upstream rather than away from it.
868
+ *
869
+ * Matching is NON-BACKTRACKING (#2603): the pattern compiles to a list of
870
+ * bounded steps and {@link matchesWorkspaceMemberSteps} decides it with one
871
+ * memoized (step, position) table, O(steps x characters), no matter how many
872
+ * `**`s the pattern carries. The anchored whole-path regex this replaced was
873
+ * correct but explored 2^N splits of a non-matching path for N `**`
874
+ * components — 124900 ms for `("**\/*" x12)/zzz` against a 40-component path,
875
+ * on a call `detectPythonEnvironment` awaits with no timeout, i.e. a wedged
876
+ * turn rather than a slow answer. The wall-clock half of that fix lives in
877
+ * `tests/clients/workspace-glob-nonbacktracking-budget.test.ts`; the answers
878
+ * are unchanged, pinned by the dialect table and the minimatch differential in
879
+ * `tests/clients/path-utils.test.ts`.
880
+ *
881
+ * A pattern that cannot be compiled at all answers `false` for every path —
882
+ * it declares no member and excludes nothing (#2591 review round 2, F2). The
883
+ * only such patterns today carry an empty character-class range (`[z-a]`:
884
+ * legal glob, illegal JS RegExp). Reachability note, recorded rather than
885
+ * assumed away: no such pattern can reach here through a manifest right now,
886
+ * because `parseTomlStringArray` (`clients/cargo-manifest.ts`) captures an
887
+ * array body non-greedily up to the FIRST `]`, so any entry containing a `]`
888
+ * loses its closing quote and is dropped before it becomes a pattern. That is
889
+ * a property of the TOML reader, not of this matcher, and it is not this
890
+ * function's to rely on — the character-class axis stays because upstream
891
+ * cargo and uv both support classes, and the reader's gap may be closed later.
892
+ */
893
+ export function matchesWorkspaceMemberPattern(pattern, relativePath, dialect) {
894
+ const normalized = dialect.normalizePattern(pattern);
895
+ if (dialect.globstar === "pattern-never-matches" &&
896
+ normalized.includes("**")) {
897
+ return false;
898
+ }
899
+ const steps = compileWorkspaceMemberPattern(normalized, dialect);
900
+ return (steps !== undefined && matchesWorkspaceMemberSteps(steps, relativePath));
901
+ }
487
902
  /**
488
903
  * Narrow no-break space, U+202F. macOS writes it before AM/PM in screenshot
489
904
  * file names; users type an ordinary space.
@@ -549,7 +964,7 @@ export function normalizeHostToolPath(input, options = {}) {
549
964
  (process.platform === "win32" && normalized.startsWith("~\\"))) {
550
965
  return path.join(os.homedir(), normalized.slice(2));
551
966
  }
552
- if (/^file:\/\//.test(normalized)) {
967
+ if (normalized.startsWith("file://")) {
553
968
  try {
554
969
  return fileURLToPath(normalized);
555
970
  }
@@ -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) {
@@ -0,0 +1,114 @@
1
+ /**
2
+ * php-cs-fixer ancestor config carriage (#2472).
3
+ *
4
+ * `php-cs-fixer`'s own `ConfigurationResolver` does NOT walk up parent
5
+ * directories looking for a config file — verified against
6
+ * `computeConfigFiles()` in `PHP-CS-Fixer/PHP-CS-Fixer` at tag `v3.64.0`
7
+ * (commit `58dd9c931c785a79739310aef5178928305ffa67`,
8
+ * `src/Console/ConfigurationResolver.php:548-588`). There is no `--path`
9
+ * OPTION on `fix` — `path` is a positional `InputArgument` (the file(s) to
10
+ * format; `--path-mode` only changes how an EXPLICIT `--config`'s own
11
+ * `finder()` intersects with it, per `FixCommand.php:206-207`), and the
12
+ * candidate directory is driven by that positional argument's dirname, not
13
+ * by `$this->cwd` — cwd is only a SECOND, ADDITIONAL probe appended when it
14
+ * differs from the path's directory:
15
+ *
16
+ * $path = $this->getPath();
17
+ *
18
+ * if ($this->isStdIn() || 0 === \count($path)) {
19
+ * $configDir = $this->cwd;
20
+ * } elseif (1 < \count($path)) {
21
+ * throw new InvalidConfigurationException('For multiple paths config parameter is required.');
22
+ * } elseif (!is_file($path[0])) {
23
+ * $configDir = $path[0];
24
+ * } else {
25
+ * $dirName = pathinfo($path[0], PATHINFO_DIRNAME);
26
+ * $configDir = is_dir($dirName) ? $dirName : $path[0];
27
+ * }
28
+ *
29
+ * $candidates = [
30
+ * $configDir.\DIRECTORY_SEPARATOR.'.php-cs-fixer.php',
31
+ * $configDir.\DIRECTORY_SEPARATOR.'.php-cs-fixer.dist.php',
32
+ * $configDir.\DIRECTORY_SEPARATOR.'.php_cs', // old v2 config, present here only to throw nice error message later
33
+ * $configDir.\DIRECTORY_SEPARATOR.'.php_cs.dist', // old v2 config, present here only to throw nice error message later
34
+ * ];
35
+ *
36
+ * if ($configDir !== $this->cwd) {
37
+ * $candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php-cs-fixer.php';
38
+ * $candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php-cs-fixer.dist.php';
39
+ * $candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php_cs'; // old v2 config, present here only to throw nice error message later
40
+ * $candidates[] = $this->cwd.\DIRECTORY_SEPARATOR.'.php_cs.dist'; // old v2 config, present here only to throw nice error message later
41
+ * }
42
+ *
43
+ * return $candidates;
44
+ *
45
+ * We always spawn `fix` with the FILE as a positional argument (not stdin,
46
+ * never a bare directory), so `$path[0]` is always that file and `$configDir`
47
+ * always resolves to `pathinfo($path[0], PATHINFO_DIRNAME)` — the file's OWN
48
+ * directory — REGARDLESS of the spawned process's cwd.
49
+ *
50
+ * #2472 review round 3, F3 correction: an EARLIER version of this comment
51
+ * concluded from the above that "spawning with `cwd = <ancestor config's
52
+ * directory>` would NOT work" and that `--config` is "the only carriage —
53
+ * there is no spawn-option workaround." That conclusion does not follow from
54
+ * the quoted code and is FALSE: the `$configDir !== $this->cwd` branch
55
+ * appends `$this->cwd`-rooted candidates whenever the file's own directory
56
+ * differs from the spawn's cwd — the common case, since we always spawn one
57
+ * file below the project root. Spawning with `cwd` set to the ancestor
58
+ * config's directory WOULD make `computeConfigFiles()` find it, via that
59
+ * second branch. `--config <path>` remains the better fix regardless: it
60
+ * names the exact winning file directly, with no reliance on this
61
+ * inequality quirk (and no need to compute a spawn `cwd` distinct from the
62
+ * file's own directory, which `formatFile` doesn't do) — it is the better
63
+ * carriage, not the only possible one.
64
+ *
65
+ * Unlike prettier/biome/eslint, an ancestor config found by `detect()`'s own
66
+ * climb (`hasPhpCsFixerConfig` in `clients/tool-policy.ts`,
67
+ * `phpCsFixerFormatter.detect` in `clients/formatters.ts`) is invisible to a
68
+ * bare `php-cs-fixer fix <file>` invocation whenever that ancestor directory
69
+ * is neither the file's own directory nor the spawn's cwd — php-cs-fixer
70
+ * silently falls back to its built-in default ruleset instead of the
71
+ * project's configured one.
72
+ *
73
+ * `computeConfigFiles()`'s own candidate order also settles same-directory
74
+ * precedence: `.php-cs-fixer.php` wins over `.php-cs-fixer.dist.php` when
75
+ * both sit in the SAME directory (first match in the array wins). This
76
+ * resolver mirrors that precedence; the two legacy v2 names are not one of
77
+ * pi-lens's detection targets (`hasPhpCsFixerConfig`/
78
+ * `phpCsFixerFormatter.detect` never look for them either) so they are not
79
+ * candidates here.
80
+ *
81
+ * Reuses the shared `findLocalToolConfig` walker (`clients/path-utils.ts`) —
82
+ * the same single source of truth `opengrep-config.ts`, `sgconfig.ts`,
83
+ * `typos-config.ts`, and `zizmor-config.ts` already delegate to for their own
84
+ * "walk up for one of these config filenames" search (refs #680, #2472
85
+ * review F2), rather than a private walker of its own. Called with NO
86
+ * `homeDir` — deliberately UNCEILINGED (#2472 review round 3, F1): this
87
+ * resolver's own gates (`hasPhpCsFixerConfig` in `clients/tool-policy.ts`,
88
+ * `phpCsFixerFormatter.detect` in `clients/formatters.ts`) are both
89
+ * unceilinged too (`findNearestContaining`/`findUp` walk to the filesystem
90
+ * root), so a ceilinged carriage here would disagree with its own gate — the
91
+ * gate says "config exists" while the carriage says "not found", silently
92
+ * dropping the `--config` argv this module exists to carry.
93
+ */
94
+ import * as path from "node:path";
95
+ import { findLocalToolConfig } from "./path-utils.js";
96
+ /**
97
+ * In `computeConfigFiles()`'s own precedence order — `.php-cs-fixer.php`
98
+ * before `.php-cs-fixer.dist.php` — so a same-directory precedence check can
99
+ * just take the first existing name.
100
+ */
101
+ export const PHP_CS_FIXER_CONFIG_NAMES = [
102
+ ".php-cs-fixer.php",
103
+ ".php-cs-fixer.dist.php",
104
+ ];
105
+ /**
106
+ * Resolve the nearest ancestor php-cs-fixer config FILE for `filePath`
107
+ * (climbing from the file's own directory, never the file itself), or
108
+ * `undefined` when none is found up to the filesystem root — unceilinged,
109
+ * agreeing with this tool's own (unceilinged) detection gates.
110
+ */
111
+ export function resolvePhpCsFixerConfig(filePath) {
112
+ const startDir = path.dirname(path.resolve(filePath));
113
+ return findLocalToolConfig(startDir, PHP_CS_FIXER_CONFIG_NAMES);
114
+ }