@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
@@ -15,9 +15,10 @@ import { getGlobalPiLensDir } from "../../../file-utils.js";
15
15
  import { createGenerationSource, } from "../../../generation-guard.js";
16
16
  import { PathKeyedMap } from "../../../path-keyed-map.js";
17
17
  import { normalizeEphemeralMapKey, normalizeMapKey, } from "../../../path-utils.js";
18
- import { ensureTool, getInstallAttempt, getLastEnsureResolutionSource, getToolInstallStrategy, isSpawnableCommand, resetPathWalkMemo, } from "../../../installer/index.js";
18
+ import { ensureTool, findManagedToolBinary, getInstallAttempt, getLastEnsureResolutionSource, getToolInstallStrategy, isSpawnableCommand, resetPathWalkMemo, } from "../../../installer/index.js";
19
19
  import { getServersForFileWithConfig, isServerDisabled, } from "../../../lsp/config.js";
20
- import { findGlobalBinary } from "../../../package-manager.js";
20
+ import { findGlobalBinary, findLocalBinAt, findLocalBinsUpwards, findLocalBinUpwards, VENDOR_BIN_DIRS, VENV_BIN_DIRS, } from "../../../package-manager.js";
21
+ import { logLatency } from "../../../latency-logger.js";
21
22
  import { safeSpawnAsync } from "../../../safe-spawn.js";
22
23
  import { compareOrdinal } from "../../../string-utils.js";
23
24
  import { getToolCommandSpec, shouldAutoInstallTool, } from "../../../tool-policy.js";
@@ -45,29 +46,21 @@ export function lspPrimaryCoversFile(ctx, serverId) {
45
46
  const primary = getServersForFileWithConfig(ctx.filePath).find((s) => s.role !== "auxiliary");
46
47
  return primary?.id === serverId;
47
48
  }
49
+ const _realThisDir = (() => {
50
+ if (typeof __dirname !== "undefined")
51
+ return __dirname;
52
+ return path.dirname(fileURLToPath(import.meta.url));
53
+ })();
54
+ let _thisDir = _realThisDir;
48
55
  /**
49
- * Walk up from startDir until we find a directory containing node_modules/.bin.
50
- * Returns all such roots found up to the filesystem root — not just the nearest —
51
- * so callers can search them all for a specific binary.
56
+ * Test-only override for pi-lens's OWN install-tree root (#2544 round 4 F1).
57
+ * `_thisDir` is fixed at module load from the running file's own location, so
58
+ * a test cannot otherwise place it under a synthetic HOME to prove the
59
+ * own-install walk's `ceiling: false` exemption without writing into this
60
+ * process's REAL, shared `node_modules` tree.
52
61
  */
53
- function findNodeBinRoots(startDir) {
54
- const roots = [];
55
- let current = startDir;
56
- const fsRoot = path.parse(current).root;
57
- while (current !== fsRoot) {
58
- if (fs.existsSync(path.join(current, "node_modules", ".bin"))) {
59
- roots.push(current);
60
- }
61
- const parent = path.dirname(current);
62
- if (parent === current)
63
- break;
64
- current = parent;
65
- }
66
- return roots;
67
- }
68
- let _thisDir = path.dirname(fileURLToPath(import.meta.url));
69
- if (typeof __dirname !== "undefined") {
70
- _thisDir = __dirname;
62
+ export function _setThisDirForTests(dir) {
63
+ _thisDir = dir ?? _realThisDir;
71
64
  }
72
65
  // Managed tools directory (~/.pi-lens/tools) — where ensureTool() installs binaries
73
66
  const _managedToolsDir = path.join(getGlobalPiLensDir(), "tools");
@@ -89,9 +82,10 @@ function managedNodeToolCandidates(tool) {
89
82
  * Verification verdicts for managed shims, keyed by path + mtime + size, so a
90
83
  * reinstall re-verifies and a session start re-arms.
91
84
  *
92
- * A plain `Map`, not a `PathKeyedMap`: `managedNodeToolCandidates` is the ONLY
93
- * producer of these paths, so the write and read forms are the same string by
94
- * construction and cannot diverge on case or separator.
85
+ * A plain `Map`, not a `PathKeyedMap`: every path keyed here is produced by
86
+ * `path.join` from one of two producers — `managedNodeToolCandidates` and the
87
+ * installer's `findManagedToolBinary` (#2140) — so the write and read forms are
88
+ * the same string by construction and cannot diverge on case or separator.
95
89
  */
96
90
  const managedBinaryVerdicts = new Map();
97
91
  /**
@@ -229,6 +223,32 @@ export async function findManagedNodeToolBinary(tool, verificationArgs = ["--ver
229
223
  }
230
224
  return null;
231
225
  }
226
+ /**
227
+ * The release-managed binary for a tool (`~/.pi-lens/bin/<tool>`), or null when
228
+ * the registry does not put this tool there / nothing on disk actually runs.
229
+ *
230
+ * `findManagedToolBinary` is the INSTALLER's own lookup — the one `getToolPath`
231
+ * runs before PATH for every github/maven/archive-strategy tool, and the one
232
+ * `getToolEnvironment` puts at the front of a spawn's PATH. Calling it here is
233
+ * what makes probe and spawn resolve through a single definition instead of two
234
+ * (#2140): before this, the probe fell straight to the bare command name, missed
235
+ * PATH, and latched `missing` — while the install fallback a few hundred ms
236
+ * later handed the caller the very binary sitting in that directory. Every
237
+ * session, for every tool the user already had (7 unavailable/available pairs in
238
+ * one 3h dogfood window).
239
+ *
240
+ * Verification is the same `verifyManagedCandidate` the npm-shim rung above
241
+ * uses, for the same reason (#1657): `findManagedToolBinary` answers from a bare
242
+ * `fs.access`, and an on-disk binary that cannot run must not shadow a working
243
+ * PATH binary — it falls through instead.
244
+ */
245
+ async function findManagedReleaseBinary(tool, verificationArgs = ["--version"]) {
246
+ const candidate = await findManagedToolBinary(tool);
247
+ if (!candidate)
248
+ return null;
249
+ const verdict = await verifyManagedCandidate(candidate, verificationArgs);
250
+ return verdict === "ok" || verdict === "unverified" ? candidate : null;
251
+ }
232
252
  // =============================================================================
233
253
  // VENV-AWARE COMMAND FINDER
234
254
  // =============================================================================
@@ -239,21 +259,30 @@ export async function findManagedNodeToolBinary(tool, verificationArgs = ["--ver
239
259
  * The resolved path is returned verbatim: every spawn consumer runs with
240
260
  * `shell: false` (safe-spawn, #817), so wrapping it in quotes would make it
241
261
  * a literal filename that ENOENTs on every platform (#1508).
262
+ *
263
+ * The venv directory list is `VENV_BIN_DIRS` (`package-manager.ts`), shared
264
+ * with `formatters.ts`'s `findInVenv` and `dead-code-client.ts`'s vulture
265
+ * candidates — three independent spellings of the same four directories before
266
+ * #2544 review F1.
267
+ *
268
+ * Resolution is a fixed lookup at `cwd` ONLY (`findLocalBinAt`, no ancestor
269
+ * walk). #2544 review round 3 folded this onto the shared ANCESTOR walker,
270
+ * on the reasoning that a monorepo package should find its repo-root venv —
271
+ * but that was an unrequested behaviour change with no #2514 defect behind
272
+ * it (#2514 is about the HOME ceiling, not about climbing at all), and the
273
+ * inversion is real: `~/code/app` with no venv of its own would resolve
274
+ * `~/code/.venv/bin/ruff` — an unrelated SIBLING project's interpreter —
275
+ * ahead of PATH. Reverted in round 4 F2. The HOME ceiling is moot here: a
276
+ * lookup at exactly `cwd` can never climb past it.
242
277
  */
243
278
  export function createVenvFinder(command, windowsExt = "", verificationArgs = ["--version"]) {
244
279
  return async (cwd) => {
245
- const venvPaths = [
246
- `.venv/bin/${command}`,
247
- `venv/bin/${command}`,
248
- `.venv/Scripts/${command}${windowsExt}`,
249
- `venv/Scripts/${command}${windowsExt}`,
250
- ];
251
- for (const venvPath of venvPaths) {
252
- const fullPath = path.join(cwd, venvPath);
253
- if (fs.existsSync(fullPath)) {
254
- return fullPath;
255
- }
256
- }
280
+ const venvBin = findLocalBinAt(command, cwd, {
281
+ windowsExt,
282
+ binDirs: VENV_BIN_DIRS,
283
+ });
284
+ if (venvBin)
285
+ return venvBin;
257
286
  // Managed-dir install (~/.pi-lens/tools/node_modules/.bin/<command>) — the
258
287
  // same shim `ensureTool()` installs npm-strategy tools into. Checked BEFORE
259
288
  // the bare-name PATH fallback (#1638): without this, a tool that only
@@ -267,6 +296,12 @@ export function createVenvFinder(command, windowsExt = "", verificationArgs = ["
267
296
  const managed = await findManagedNodeToolBinary(command, verificationArgs);
268
297
  if (managed)
269
298
  return managed;
299
+ // Release-managed install (`~/.pi-lens/bin/<command>`) — where `ensureTool`
300
+ // puts every github/maven/archive-strategy tool. Same rung, same reason as
301
+ // the npm shim above, for the other half of the managed families (#2140).
302
+ const release = await findManagedReleaseBinary(command, verificationArgs);
303
+ if (release)
304
+ return release;
270
305
  // Fall back to global
271
306
  return command;
272
307
  };
@@ -504,6 +539,41 @@ function sourceTagForToolId(toolId) {
504
539
  return undefined;
505
540
  }
506
541
  }
542
+ /**
543
+ * `binary`/`source` for a probe that resolved through one of pi-lens's OWN
544
+ * managed installs rather than through PATH or a project venv (#2140). A reader
545
+ * of latency.log could otherwise not tell the two apart, and the whole point of
546
+ * the fix is that the managed directories now answer where PATH used to miss.
547
+ *
548
+ * BOTH managed rungs are asked, in the order `createVenvFinder` walks them
549
+ * (#2140 review F1). The first version asked only about the release directory,
550
+ * so every npm-shim hit — knip, jscpd, madge, pyright, biome, htmlhint,
551
+ * stylelint — logged an evidence-free row indistinguishable from a PATH hit,
552
+ * while the doc claimed the opposite. Each rung is asked of THE function that
553
+ * produced its own paths (`managedNodeToolCandidates`, `findManagedToolBinary`)
554
+ * and settled by string identity, never by a path-prefix predicate over a
555
+ * directory: a second opinion about which paths are managed is exactly the
556
+ * parallel-list drift `sourceTagForToolId` exists to avoid, and it would answer
557
+ * differently for case or separator variants.
558
+ *
559
+ * `binary` present IS the managed hit; `source` names the family whenever the
560
+ * command is a registry id (`sourceTagForToolId` reads the registry's own
561
+ * `installStrategy`). A managed shim whose COMMAND is not itself a registry id
562
+ * — `markdownlint-cli2`, installed under registry id `markdownlint` — carries
563
+ * `binary` alone rather than a guessed family. `binary` is a BASENAME, never
564
+ * the absolute path — same rule as every other evidence field (#1568 review).
565
+ */
566
+ async function describeManagedResolution(tool, resolved) {
567
+ const managed = managedNodeToolCandidates(tool).includes(resolved) ||
568
+ (await findManagedToolBinary(tool)) === resolved;
569
+ if (!managed)
570
+ return {};
571
+ const source = sourceTagForToolId(tool);
572
+ return {
573
+ binary: path.basename(resolved),
574
+ ...(source !== undefined && { source }),
575
+ };
576
+ }
507
577
  /**
508
578
  * How to tag a not-fresh install resolution in the compensating row's
509
579
  * evidence (#1636 review, carried over from #1615's verify). `getInstallAttempt`
@@ -799,7 +869,16 @@ export function createAvailabilityChecker(command, windowsExt = "", versionArgs
799
869
  });
800
870
  return false;
801
871
  }
872
+ // Resolution is measured separately from the spawn (#2140 review F2).
873
+ // It is no longer free — the managed rungs stat two directories and,
874
+ // on a first touch, pay their own verification spawn — and it runs
875
+ // BEFORE `startedAt`, so neither `durationMs` nor
876
+ // `recordAvailabilityProbeOverrun` can see it. Folding it into
877
+ // `durationMs` would instead charge the probe budget for work that
878
+ // budget does not govern, so the two spans are reported side by side.
879
+ const resolveStartedAt = Date.now();
802
880
  const cmd = await findCommand(resolvedCwd);
881
+ const resolveMs = Date.now() - resolveStartedAt;
803
882
  // #1995: a command cooling down after a RUNTIME timeout (lint or
804
883
  // autofix lane blew its real budget) must not re-probe on every
805
884
  // edit - the positive verdict is effectively cooled. Consult-only:
@@ -854,7 +933,11 @@ export function createAvailabilityChecker(command, windowsExt = "", versionArgs
854
933
  elapsedMs,
855
934
  hostStallMs,
856
935
  classifiedBy: probeJoined ? "joined" : "probe",
857
- evidence: describeProbeEvidence(result),
936
+ evidence: {
937
+ ...describeProbeEvidence(result),
938
+ ...(await describeManagedResolution(command, cmd)),
939
+ resolveMs,
940
+ },
858
941
  });
859
942
  return true;
860
943
  }
@@ -1105,30 +1188,20 @@ export function resolveToolCommand(cwd, toolId) {
1105
1188
  return null;
1106
1189
  return resolveNodeToolCommand(cwd, spec.command, spec.windowsExt ?? ".cmd");
1107
1190
  }
1191
+ /**
1192
+ * Composer's `vendor/bin/<tool>` from the nearest ancestor that has one.
1193
+ *
1194
+ * Delegates to THE shared bin walker (#2544 review F1): this was a fifth
1195
+ * private copy of that climb, unceilinged, so a `vendor/bin` at or above
1196
+ * `$HOME` — which can never be this project's own Composer install — was
1197
+ * resolved as the project's tool (#2514, same class as `findInVendorBin`,
1198
+ * which had already been folded).
1199
+ */
1108
1200
  export function resolveVendorToolCommand(cwd, toolName, windowsExt = ".bat") {
1109
- const isWin = process.platform === "win32";
1110
- const candidates = isWin
1111
- ? [
1112
- path.join("vendor", "bin", `${toolName}${windowsExt}`),
1113
- path.join("vendor", "bin", toolName),
1114
- ]
1115
- : [path.join("vendor", "bin", toolName)];
1116
- let dir = cwd;
1117
- const root = path.parse(dir).root;
1118
- while (true) {
1119
- for (const candidate of candidates) {
1120
- const full = path.join(dir, candidate);
1121
- if (fs.existsSync(full))
1122
- return full;
1123
- }
1124
- if (dir === root)
1125
- break;
1126
- const parent = path.dirname(dir);
1127
- if (parent === dir)
1128
- break;
1129
- dir = parent;
1130
- }
1131
- return null;
1201
+ return (findLocalBinUpwards(toolName, cwd, {
1202
+ windowsExt,
1203
+ binDirs: VENDOR_BIN_DIRS,
1204
+ }) ?? null);
1132
1205
  }
1133
1206
  export async function resolveToolCommandWithInstallFallback(cwd, toolId, timeout = 5000) {
1134
1207
  const spec = getToolCommandSpec(toolId);
@@ -1355,17 +1428,40 @@ function buildSgLocalBins() {
1355
1428
  : [".cmd", ".exe", ""]
1356
1429
  : [""];
1357
1430
  const binaryCandidates = ["ast-grep", "sg"].flatMap((base) => extensions.map((ext) => `${base}${ext}`));
1358
- const binRoots = [
1359
- ...findNodeBinRoots(_thisDir),
1360
- ...findNodeBinRoots(process.cwd()),
1361
- _managedToolsDir,
1431
+ // THE shared bin walk (#2544 review F1). This used to be a private
1432
+ // `findNodeBinRoots` climb with no HOME ceiling, so on a box with a
1433
+ // home-level `node_modules` (the pi-extensions manifest installs its own
1434
+ // bins, #2514) `$HOME` itself came back as a bin root and an unrelated
1435
+ // ast-grep was probed as if it were this project's. `windowsExt: ""`
1436
+ // because the candidate names already carry their extensions — in a
1437
+ // shell-dependent ORDER (`hasBash` above) that a single `windowsExt`
1438
+ // cannot express.
1439
+ //
1440
+ // The PROJECT walk (from `process.cwd()`) keeps the default HOME ceiling —
1441
+ // a `node_modules/.bin/ast-grep` at or above `$HOME` can never be THIS
1442
+ // project's own dependency. pi-lens's OWN install-tree walk (from
1443
+ // `_thisDir`) is exempt (`ceiling: false`, #2544 round 4 F1): `pi install
1444
+ // npm:pi-lens` installs into the HOME-level manifest
1445
+ // (`~/package.json` → `~/node_modules/pi-lens`), so pi-lens's bundled
1446
+ // `@ast-grep/cli` shim can sit AT `~/node_modules/.bin/ast-grep` — exactly
1447
+ // the directory the PROJECT ceiling exists to stop at. A previous version
1448
+ // of this comment claimed the own install always lives "under"
1449
+ // `~/.pi/extensions/…`, below the ceiling; that is false for the
1450
+ // HOME-level manifest layout, and the round-2 ceiling silently made this
1451
+ // walk return nothing for it (PATH, `findGlobalBinary` and npx do not
1452
+ // cover this location either). `_managedToolsDir` (`~/.pi-lens/tools`) is
1453
+ // a fixed location, not a walk, so it is checked directly as before —
1454
+ // also own-install, since `ensureTool()` puts npm-strategy installs there.
1455
+ const projectWalkOptions = { windowsExt: "" };
1456
+ const ownInstallWalkOptions = { windowsExt: "", ceiling: false };
1457
+ const bins = [
1458
+ ...findLocalBinsUpwards(binaryCandidates, _thisDir, ownInstallWalkOptions).map((binPath) => ({ path: binPath, source: "own-install" })),
1459
+ ...findLocalBinsUpwards(binaryCandidates, process.cwd(), projectWalkOptions).map((binPath) => ({ path: binPath, source: "project" })),
1362
1460
  ];
1363
- const bins = [];
1364
- for (const root of binRoots) {
1365
- for (const candidate of binaryCandidates) {
1366
- const localBin = path.join(root, "node_modules", ".bin", candidate);
1367
- if (fs.existsSync(localBin))
1368
- bins.push(localBin);
1461
+ for (const candidate of binaryCandidates) {
1462
+ const managedBin = path.join(_managedToolsDir, "node_modules", ".bin", candidate);
1463
+ if (fs.existsSync(managedBin)) {
1464
+ bins.push({ path: managedBin, source: "own-install" });
1369
1465
  }
1370
1466
  }
1371
1467
  return bins;
@@ -1397,12 +1493,12 @@ export async function isSgAvailableAsync() {
1397
1493
  sgSweepHostStallMs = 0;
1398
1494
  sgSweepUnreachable = [];
1399
1495
  sgSweepDurablyMissing = [];
1400
- // 1. Local node_modules/.bin
1401
- for (const localBin of buildSgLocalBins()) {
1402
- if (await probeAstGrepCommandAsync(localBin)) {
1403
- sgCmd = localBin;
1496
+ // 1. Local node_modules/.bin (own-install tree, then project tree)
1497
+ for (const candidate of buildSgLocalBins()) {
1498
+ if (await probeAstGrepCommandAsync(candidate.path)) {
1499
+ sgCmd = candidate.path;
1404
1500
  sgCmdArgs = [];
1405
- noteSgAvailable(startedAt);
1501
+ noteSgAvailable(startedAt, { source: candidate.source });
1406
1502
  return true;
1407
1503
  }
1408
1504
  }
@@ -1411,7 +1507,7 @@ export async function isSgAvailableAsync() {
1411
1507
  if (await probeAstGrepCommandAsync(cmd)) {
1412
1508
  sgCmd = cmd;
1413
1509
  sgCmdArgs = [];
1414
- noteSgAvailable(startedAt);
1510
+ noteSgAvailable(startedAt, { source: "global" });
1415
1511
  return true;
1416
1512
  }
1417
1513
  }
@@ -1422,7 +1518,7 @@ export async function isSgAvailableAsync() {
1422
1518
  if (globalBin && (await probeAstGrepCommandAsync(globalBin))) {
1423
1519
  sgCmd = globalBin;
1424
1520
  sgCmdArgs = [];
1425
- noteSgAvailable(startedAt);
1521
+ noteSgAvailable(startedAt, { source: "global" });
1426
1522
  return true;
1427
1523
  }
1428
1524
  }
@@ -1430,7 +1526,7 @@ export async function isSgAvailableAsync() {
1430
1526
  if (await probeAstGrepCommandAsync("npx", ["--no", "--", "ast-grep"])) {
1431
1527
  sgCmd = "npx";
1432
1528
  sgCmdArgs = ["--no", "--", "ast-grep"];
1433
- noteSgAvailable(startedAt);
1529
+ noteSgAvailable(startedAt, { source: "npx" });
1434
1530
  return true;
1435
1531
  }
1436
1532
  // Nothing answered, and nothing answered TRANSIENTLY, while the verdict we
@@ -1479,6 +1575,11 @@ export async function isSgAvailableAsync() {
1479
1575
  * `retained` marks the other provisional case (#1568 review F1): no candidate
1480
1576
  * answered at all, so the winner being reported is the one the previous sweep
1481
1577
  * found, kept rather than discarded on a timeout.
1578
+ *
1579
+ * `source` (omitted for a `retained` win, which resolved nothing new this
1580
+ * sweep) logs one `tool_bin_resolved` phase so F1's own-install-vs-project
1581
+ * ceiling distinction is visible in `latency.log` instead of only inferable
1582
+ * from a bin's absence (#2544 round 4 F4).
1482
1583
  */
1483
1584
  function noteSgAvailable(startedAt, opts = {}) {
1484
1585
  const provisional = sgSweepSawTransient;
@@ -1489,6 +1590,19 @@ function noteSgAvailable(startedAt, opts = {}) {
1489
1590
  else {
1490
1591
  sgLatch.noteAvailable();
1491
1592
  }
1593
+ if (opts.source) {
1594
+ logLatency({
1595
+ type: "phase",
1596
+ phase: "tool_bin_resolved",
1597
+ filePath: "<pi-lens>",
1598
+ durationMs: Math.round(Date.now() - startedAt),
1599
+ metadata: {
1600
+ tool: "ast-grep",
1601
+ source: opts.source,
1602
+ ceilingApplied: opts.source === "project",
1603
+ },
1604
+ });
1605
+ }
1492
1606
  logAvailabilityDecision({
1493
1607
  tool: "ast-grep",
1494
1608
  verdict: "available",
@@ -14,8 +14,39 @@
14
14
  import { createAvailabilityLatch, logAvailabilityDecision, } from "./availability-policy.js";
15
15
  import { probeAvailabilityCandidates } from "./candidate-probe.js";
16
16
  import { createAvailabilityProbeFlight } from "../../../availability-probe-flight.js";
17
+ import { createGenerationSource } from "../../../generation-guard.js";
17
18
  import { createSingleFlight } from "../../../single-flight.js";
18
- const toolchainProbeFlights = createAvailabilityProbeFlight();
19
+ /**
20
+ * Session generation for toolchain-availability resets (#2455 fix round 3, F2).
21
+ *
22
+ * `reset()` clears the memo and the latch, but a probe that was ALREADY in
23
+ * flight when it ran knows nothing about that. Without a generation, a
24
+ * post-reset caller joins the pre-reset flight and, worse, that flight's own
25
+ * settlement writes its stale verdict back into the freshly cleared latch — so
26
+ * a tool installed between sessions still read "missing" for the whole new
27
+ * session. That is #1674's defect read from both the sharing side and the
28
+ * settle side, and the reason `SingleFlightOptions.generation` exists.
29
+ *
30
+ * This is the repo's extracted primitive (`clients/generation-guard.ts`, #1754)
31
+ * rather than a hand-rolled counter (#2455 fix round 4, F1). Two things come
32
+ * with it that the hand-rolled form did not have: the source is declared by
33
+ * construction, so the #1754 ratchet can see it; and `guardedWrite` puts every
34
+ * superseded write in the degradation ledger as a bounded
35
+ * `generation-guard-stale-write` record per (source, subject), so a dogfood
36
+ * session can tell "the guard is working" from "the guard never fires" instead
37
+ * of watching superseded writes vanish silently (F4).
38
+ *
39
+ * A module-level source rather than a per-instance one, deliberately. It is a
40
+ * counter, so nothing has to be hand-maintained and no flight is retained for
41
+ * the sake of superseding it. And `toolchainProbeFlights` is itself shared
42
+ * ACROSS instances (#2131: two clients probing the same candidate list run one
43
+ * sweep), so its supersede signal cannot live on any one instance. The cost is
44
+ * that resetting one toolchain also supersedes another's in-flight sweep; that
45
+ * is at worst one extra probe, never a wrong verdict, and every registered
46
+ * reset fires in the same `session_start` block anyway.
47
+ */
48
+ const toolchainGenerations = createGenerationSource("toolchain-availability");
49
+ const toolchainProbeFlights = createAvailabilityProbeFlight({ generation: toolchainGenerations.current });
19
50
  /**
20
51
  * Own one toolchain's availability: sweep the platform candidate list, memoize
21
52
  * the path that answered, and park the verdict behind the shared latch so a
@@ -31,34 +62,61 @@ export function createToolchainAvailability(config) {
31
62
  let sweepHostStallMs = 0;
32
63
  /** What the last classified candidate returned, for the decision record. */
33
64
  let sweepEvidence;
34
- const ensureFlight = createSingleFlight();
65
+ const ensureFlight = createSingleFlight({
66
+ generation: toolchainGenerations.current,
67
+ });
35
68
  async function findPath() {
36
69
  if (toolPath)
37
70
  return toolPath;
38
71
  const paths = process.platform === "win32" ? config.windowsPaths : config.unixPaths;
72
+ const handle = toolchainGenerations.capture();
39
73
  const shared = toolchainProbeFlights.run(`toolchain:${config.tool}|${config.probeArgs.join("|")}|${config.windowsPaths.join("|")}|${config.unixPaths.join("|")}`, () => probeAvailabilityCandidates(paths, config.probeArgs, config.budgetMs));
40
74
  const sweep = await shared.promise;
41
- sweepSawTransient = sweep.sawTransient;
42
- sweepTransientCause = sweep.transientCause;
43
- sweepHostStallMs = sweep.hostStallMs;
44
- sweepEvidence = sweep.evidence;
45
- if (sweep.foundPath)
46
- toolPath = sweep.foundPath;
75
+ // A reset may have landed while this sweep was running. Its answer then
76
+ // describes the session that just ended, so it must not write itself back
77
+ // into the memo `reset()` just cleared, nor into the classification fields
78
+ // the replacement flight reads to build ITS verdict. The memo is the
79
+ // sharper half: a superseded sweep that writes `foundPath` latches a path
80
+ // the reset may have been called BECAUSE it went away, so the seam that
81
+ // exists to un-latch a false "missing" would instead latch a false
82
+ // "found". Callers already holding this promise still get what the sweep
83
+ // actually found.
84
+ handle.guardedWrite(`findPath:${config.tool}`, () => {
85
+ sweepSawTransient = sweep.sawTransient;
86
+ sweepTransientCause = sweep.transientCause;
87
+ sweepHostStallMs = sweep.hostStallMs;
88
+ sweepEvidence = sweep.evidence;
89
+ if (sweep.foundPath)
90
+ toolPath = sweep.foundPath;
91
+ });
47
92
  return sweep.foundPath;
48
93
  }
49
94
  async function resolveAvailability() {
50
95
  const startedAt = Date.now();
51
- const found = (await findPath()) !== null;
96
+ const handle = toolchainGenerations.capture();
97
+ const resolvedPath = await findPath();
98
+ const found = resolvedPath !== null;
99
+ // #2455 fix round 3, F2: a reset during the probe opened a NEW session.
100
+ // This flight answers the OLD one, so writing its verdict into the
101
+ // freshly cleared latch would re-latch a stale "missing" for the whole
102
+ // new session — the bug the reset exists to prevent, reintroduced by the
103
+ // reset's own timing. Report the verdict to this flight's own callers;
104
+ // do not latch it, and say so in the decision record's `latched`.
105
+ const superseded = !handle.isCurrent();
52
106
  if (found) {
53
- availabilityLatch.noteAvailable();
54
- config.log(`${config.label} found: ${toolPath}`);
107
+ handle.guardedWrite(`latch:${config.tool}`, () => availabilityLatch.noteAvailable());
108
+ // The RESOLVED path, not the memo (#2455 fix round 4, F6). A superseded
109
+ // sweep is forbidden from writing `toolPath`, so reading the memo here
110
+ // logged `Go found: null` on exactly the branch this line exists to
111
+ // explain.
112
+ config.log(`${config.label} found: ${resolvedPath}`);
55
113
  logAvailabilityDecision({
56
114
  tool: config.tool,
57
115
  verdict: "available",
58
116
  outcome: "success",
59
117
  cause: "ok",
60
118
  elapsedMs: Date.now() - startedAt,
61
- latched: true,
119
+ latched: !superseded,
62
120
  hostStallMs: sweepHostStallMs,
63
121
  budgetMs: config.budgetMs,
64
122
  classifiedBy: "probe",
@@ -70,14 +128,14 @@ export function createToolchainAvailability(config) {
70
128
  // whether the toolchain is installed; it expires instead of latching.
71
129
  const outcome = sweepSawTransient ? "transient" : "missing";
72
130
  const cause = sweepSawTransient ? sweepTransientCause : "not-found";
73
- const retryAfterMs = availabilityLatch.noteUnavailable(outcome, cause);
131
+ const retryAfterMs = handle.guardedWrite(`latch:${config.tool}`, () => availabilityLatch.noteUnavailable(outcome, cause)) ?? 0;
74
132
  logAvailabilityDecision({
75
133
  tool: config.tool,
76
134
  verdict: "unavailable",
77
135
  outcome,
78
136
  cause,
79
137
  elapsedMs: Date.now() - startedAt,
80
- latched: outcome !== "transient",
138
+ latched: !superseded && outcome !== "transient",
81
139
  hostStallMs: sweepHostStallMs,
82
140
  ...(retryAfterMs > 0 && { retryAfterMs }),
83
141
  budgetMs: config.budgetMs,
@@ -96,5 +154,16 @@ export function createToolchainAvailability(config) {
96
154
  return memo;
97
155
  return ensureFlight.run("availability", resolveAvailability);
98
156
  }
99
- return { findPath, isAvailable };
157
+ function reset() {
158
+ toolPath = null;
159
+ availabilityLatch.reset();
160
+ // Supersede every sweep and availability flight already running, in this
161
+ // instance and in the shared registry. No `clear()`: the generation bump
162
+ // is what makes a stale flight unjoinable and unable to re-latch, and
163
+ // `SingleFlight.release`'s identity check already stops it from evicting
164
+ // its successor. Clearing the SHARED registry here would additionally
165
+ // drop another toolchain's live claim and cost it a duplicate sweep.
166
+ toolchainGenerations.bump();
167
+ }
168
+ return { findPath, isAvailable, reset };
100
169
  }
@@ -13,24 +13,3 @@ export function readFileContent(filePath) {
13
13
  return undefined;
14
14
  }
15
15
  }
16
- /**
17
- * Parse common tool output line into a diagnostic
18
- * Format: file:line:col message (category)
19
- */
20
- export function parseToolLine(line, toolName, filePath) {
21
- const match = line.match(/^(.+?):(\d+):(\d+)\s+(.+?)\s*\((.+?)\)/);
22
- if (!match)
23
- return null;
24
- return {
25
- id: `${toolName}-${match[2]}-${match[5]}`,
26
- message: `${match[5]}: ${match[4]}`,
27
- filePath,
28
- line: parseInt(match[2], 10),
29
- column: parseInt(match[3], 10),
30
- severity: line.includes("error") ? "error" : "warning",
31
- semantic: "warning",
32
- tool: toolName,
33
- rule: match[5],
34
- fixable: false,
35
- };
36
- }
@@ -70,7 +70,6 @@ const valeRunner = {
70
70
  id: "vale",
71
71
  appliesTo: ["markdown"],
72
72
  priority: PRIORITY.DOC_QUALITY,
73
- enabledByDefault: true,
74
73
  skipTestFiles: false,
75
74
  async run(ctx) {
76
75
  const cwd = ctx.cwd || process.cwd();
@@ -19,14 +19,7 @@ import { BoundedLruCache } from "../../bounded-cache.js";
19
19
  import { compareOrdinal } from "../../string-utils.js";
20
20
  // --- Constants ---
21
21
  /** Overly broad patterns that match everything (cause false positive explosions) */
22
- export const OVERLY_BROAD_PATTERNS = [
23
- "$NAME",
24
- "$FIELD",
25
- "$_",
26
- "$X",
27
- "$VAR",
28
- "$EXPR",
29
- ];
22
+ const OVERLY_BROAD_PATTERNS = ["$NAME", "$FIELD", "$_", "$X", "$VAR", "$EXPR"];
30
23
  /** Maximum complexity score for rules in blockingOnly mode */
31
24
  export const MAX_BLOCKING_RULE_COMPLEXITY = 8;
32
25
  /**
@@ -126,7 +119,7 @@ export function loadYamlRulesFresh(ruleDir, severityFilter) {
126
119
  cache.set(ruleDir, { rules, signature });
127
120
  return rules;
128
121
  }
129
- export function getCachedRules(ruleDir, severityFilter) {
122
+ function getCachedRules(ruleDir, severityFilter) {
130
123
  if (!fs.existsSync(ruleDir)) {
131
124
  return [];
132
125
  }
@@ -179,17 +172,6 @@ export function isOverlyBroadPattern(pattern) {
179
172
  return true;
180
173
  return /^\$[A-Z_]+$/i.test(pattern.trim());
181
174
  }
182
- export function isValidCondition(condition) {
183
- if (!condition)
184
- return false;
185
- if (condition.all !== undefined && condition.all.length === 0)
186
- return false;
187
- if (condition.any !== undefined && condition.any.length === 0)
188
- return false;
189
- if (isOverlyBroadPattern(condition.pattern))
190
- return false;
191
- return true;
192
- }
193
175
  export function isStructuredRule(rule) {
194
176
  if (!rule.rule)
195
177
  return false;
@@ -33,7 +33,6 @@ const yamllintRunner = {
33
33
  id: "yamllint",
34
34
  appliesTo: ["yaml"],
35
35
  priority: PRIORITY.YAML_LINT,
36
- enabledByDefault: true,
37
36
  skipTestFiles: false,
38
37
  async run(ctx) {
39
38
  const cwd = ctx.cwd || process.cwd();
@@ -55,6 +54,7 @@ const yamllintRunner = {
55
54
  if (!cmd)
56
55
  return { status: "skipped", diagnostics: [], semantic: "none" };
57
56
  const result = await safeSpawnAsync(cmd, ["-f", "parsable", ctx.filePath], {
57
+ cwd,
58
58
  timeout: 15000,
59
59
  });
60
60
  // #1816: this runner read `result.status` zero times, so a yamllint
@@ -44,7 +44,6 @@ const zigCheckRunner = {
44
44
  id: "zig-check",
45
45
  appliesTo: ["zig"],
46
46
  priority: PRIORITY.GENERAL_ANALYSIS,
47
- enabledByDefault: true,
48
47
  skipTestFiles: false,
49
48
  async run(ctx) {
50
49
  const cwd = ctx.cwd || process.cwd();