@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
@@ -11,10 +11,26 @@
11
11
  * index.ts`'s import surface from `config.js` is mirrored by an explicit
12
12
  * `vi.mock` factory in ~58 test files. Importing a new symbol from `config.js`
13
13
  * breaks every one of them with "No export is defined on the mock", which is a
14
- * standing tax on any future export. The registry is also a different concern
15
- * from `workspaceConfigs`: that map stores per-root CONFIG PAYLOAD and is
16
- * LRU-capped at 32, whereas this set answers "is this root served" and must
17
- * not silently evict a live root and start declining its files.
14
+ * standing tax on any future export.
15
+ *
16
+ * WHY THE CONFIG PAYLOAD LIVES HERE TOO (#2518). Until this fix the payload
17
+ * lived in `config.ts`'s own 32-entry `workspaceConfigs` LRU while this
18
+ * registry held 128, and the two answered halves of ONE question: the registry
19
+ * decides whether a root still needs initializing (`ensureReady`,
20
+ * `mcp/server.ts`), the payload decides whether the operator's
21
+ * `lsp.disabledServers` denial applies (`isServerDisabled`). Thirty-three
22
+ * `ensureReady` calls on other cwds therefore dropped a LIVE root's denial
23
+ * while the registry still reported the root ready, so nothing re-initialized
24
+ * it and a server the operator had turned off came back — #2415 AC 3's exact
25
+ * prohibition, silently.
26
+ *
27
+ * Tying the two CAPS would not have fixed it. `initLSPConfig` registers the
28
+ * root BEFORE its `await` and writes the payload AFTER, so two interleaved
29
+ * initializations enter the two containers in different orders; with separate
30
+ * containers the same cap still evicts different keys from each. One entry per
31
+ * root — the key IS the registration, the value IS the payload — makes a
32
+ * payload miss for a served root impossible by construction instead of by an
33
+ * ordering agreement between two modules.
18
34
  *
19
35
  * WHY IT IS NOT A LATCH. The first attempt at #2052 used a single
20
36
  * last-writer-wins session cwd that fell back to `process.cwd()`. Two bugs
@@ -31,31 +47,108 @@
31
47
  *
32
48
  * This is also the seam #2053 needs: an explicit `analysisRoot` registers here.
33
49
  */
50
+ import { BoundedFifoMap } from "../bounded-cache.js";
51
+ import { incrementDegradationCount } from "../degradation-ledger.js";
34
52
  import { isSameOrWithin } from "./server.js";
35
53
  import path from "node:path";
36
54
  /**
37
- * Insertion-ordered, so eviction drops the OLDEST root. Matches
38
- * `LSP_CONFIG_CWD_CAP` in `index.ts`, the cap on the set of cwds that
39
- * `ensureLSPConfigInitialized` will initialize at all — a root this process
40
- * cannot re-initialize must not still be considered served.
55
+ * Insertion-ordered, so eviction drops the OLDEST root. `BoundedFifoMap`
56
+ * (#2442) owns the eviction; this module no longer hand-rolls it. FIFO and not
57
+ * LRU on purpose: eviction order is REGISTRATION order, so a config write for
58
+ * an already-registered root cannot reorder the registry underneath the
59
+ * `ensureReady` memo that reads it.
60
+ *
61
+ * The value is `undefined` between `registerSessionRoot` and the config write
62
+ * that follows the loader's `await` — the same window in which, before #2518,
63
+ * no payload existed for the root at all. {@link sessionRootConfigEntries}
64
+ * skips those entries, so an in-flight root resolves exactly as it did before:
65
+ * to a shorter ancestor root's config, or to none.
41
66
  */
42
- const sessionRoots = new Set();
43
67
  const SESSION_ROOT_CAP = 128;
44
- /** Record a session cwd as served. Idempotent; re-registering refreshes nothing. */
45
- export function registerSessionRoot(cwd) {
46
- sessionRoots.add(path.resolve(cwd));
47
- while (sessionRoots.size > SESSION_ROOT_CAP) {
48
- const oldest = sessionRoots.values().next().value;
49
- if (oldest === undefined)
50
- break;
51
- sessionRoots.delete(oldest);
68
+ const sessionRoots = new BoundedFifoMap(SESSION_ROOT_CAP);
69
+ /**
70
+ * The cap dropped a root this process was serving. Its files fall back to
71
+ * pre-#2052 clamp behavior and its `lsp.disabledServers` denial stops applying
72
+ * until something initializes the root again — which `shouldInitializeSessionRoot`
73
+ * now guarantees the next SESSION START OR TOOL CALL naming that root does,
74
+ * because the registration went with the payload. Nothing else re-initializes:
75
+ * the readers of the denial (`isServerDisabled`, `getServersForFileWithConfig`,
76
+ * `getServerInitOverride`) ask the store, they never load it.
77
+ *
78
+ * A CONSTANT subject, counted rather than latched (#2518 review F3). The
79
+ * subject cannot be the evicted root: the ledger's `recordDegradationOnce` /
80
+ * `incrementDegradationCount` dedupe keys are unbounded, so a per-root subject
81
+ * would grow one key per root a long-lived process ever cycles through — a
82
+ * container bounded on one axis and unbounded on another, which is the shape
83
+ * this whole fix is about. That argument bounds the SUBJECT; it is not an
84
+ * argument for throwing the tally away, and the number of roots this process
85
+ * has had to drop is exactly what an operator tunes the cap against. The
86
+ * ledger keeps one entry per kind/subject and emits durable rows only on
87
+ * power-of-two counts, so counting is as bounded here as latching.
88
+ *
89
+ * Returns the evicted roots so the caller can drop the per-root session
90
+ * bookkeeping that described them — `initLSPConfig` releases each one's
91
+ * `config_resolved` claim (review F1).
92
+ */
93
+ function noteEvictedRoots(evicted) {
94
+ const roots = evicted.map(([root]) => root);
95
+ if (roots.length > 0) {
96
+ incrementDegradationCount({
97
+ kind: "lsp-session-root-evicted",
98
+ subject: `cap=${SESSION_ROOT_CAP}`,
99
+ reason: `session root registry at capacity; dropped ${roots[0]} and will load it again on the next session start or tool call naming that root`,
100
+ });
52
101
  }
102
+ return roots;
103
+ }
104
+ /**
105
+ * Record a session cwd as served. Idempotent; re-registering refreshes nothing.
106
+ * Returns the roots this insertion dropped at the cap, oldest first.
107
+ */
108
+ export function registerSessionRoot(cwd) {
109
+ const root = path.resolve(cwd);
110
+ // Present already: leave the entry alone. Writing the `undefined`
111
+ // placeholder over a loaded config would blank a served root's denial for
112
+ // the length of the re-initializing load — the #2518 window in miniature.
113
+ if (sessionRoots.has(root))
114
+ return [];
115
+ return noteEvictedRoots(sessionRoots.set(root, undefined));
116
+ }
117
+ /**
118
+ * Store the config `initLSPConfig` just loaded for a root it registered.
119
+ *
120
+ * Re-inserts the root when the cap dropped it while the load was in flight:
121
+ * the caller is a live session declaring this root right now, so the pair
122
+ * belongs in the registry — and it is a PAIR, which is what keeps the payload
123
+ * from outliving the registration or the registration from outliving the
124
+ * payload.
125
+ *
126
+ * Returns the roots this write dropped at the cap, oldest first.
127
+ */
128
+ export function setSessionRootConfig(cwd, config) {
129
+ return noteEvictedRoots(sessionRoots.set(path.resolve(cwd), config));
130
+ }
131
+ /**
132
+ * Every served root that has a loaded config, in registration order —
133
+ * `getConfigForFile`'s (`config.ts`) longest-prefix walk. Entries still
134
+ * awaiting their first load are skipped; see the store's doc comment.
135
+ */
136
+ export function sessionRootConfigEntries() {
137
+ return sessionRoots;
53
138
  }
54
139
  /** Return whether this exact root is still served after cap eviction. */
55
140
  export function isSessionRootRegistered(cwd) {
56
141
  return sessionRoots.has(path.resolve(cwd));
57
142
  }
58
- /** True when a readiness memo must run initialization for this root. */
143
+ /**
144
+ * True when a readiness memo must run initialization for this root.
145
+ *
146
+ * The memo alone is never enough (#2052 R1, #2518): a readiness set caps
147
+ * itself, and `initLSPConfig` has callers (`clients/runtime-session.ts`,
148
+ * `clients/lens-engine.ts`) that no memo sees, so a memo hit can outlive the
149
+ * registration — and with it the config that carries the operator's denial.
150
+ * Every readiness memo asks this function, never its own `has` alone.
151
+ */
59
152
  export function shouldInitializeSessionRoot(cwd, readyRoots) {
60
153
  const normalized = path.resolve(cwd);
61
154
  return !readyRoots.has(normalized) || !isSessionRootRegistered(normalized);
@@ -72,7 +165,7 @@ export function isOutsideAllSessionRoots(filePath) {
72
165
  if (sessionRoots.size === 0)
73
166
  return false;
74
167
  const resolved = path.resolve(filePath);
75
- for (const root of sessionRoots) {
168
+ for (const root of sessionRoots.keys()) {
76
169
  if (isSameOrWithin(root, resolved))
77
170
  return false;
78
171
  }
@@ -85,14 +178,14 @@ export function isOutsideAllSessionRoots(filePath) {
85
178
  * roots must not emit an unbounded path list.
86
179
  */
87
180
  export function getSessionRootsForTelemetry(limit = 4) {
88
- return [...sessionRoots]
181
+ return [...sessionRoots.keys()]
89
182
  .sort((a, b) => Number(a > b) - Number(a < b))
90
183
  .slice(0, limit);
91
184
  }
92
185
  /**
93
- * Clear the registry. Called by `resetLSPConfigStateForTests` so the registry
94
- * and the config store reset together and cannot disagree about which roots
95
- * exist.
186
+ * Clear the registry — the roots AND the configs they carry, which are one
187
+ * store since #2518, so a reset cannot leave the two disagreeing about which
188
+ * roots exist. Called by `resetLSPConfigStateForTests`.
96
189
  */
97
190
  export function resetSessionRootsForTests() {
98
191
  sessionRoots.clear();
@@ -1,17 +1,12 @@
1
1
  /** Process-lifetime history of successful LSP spawn plus initialize durations. */
2
+ import { BoundedFifoMap } from "../bounded-cache.js";
2
3
  const MAX_SERVER_HISTORIES = 64;
3
- const successfulSpawnDurationMs = new Map();
4
+ const successfulSpawnDurationMs = new BoundedFifoMap(MAX_SERVER_HISTORIES);
4
5
  export function recordSuccessfulLspSpawn(serverId, durationMs) {
5
6
  if (!Number.isFinite(durationMs) || durationMs < 0)
6
7
  return;
7
8
  successfulSpawnDurationMs.delete(serverId);
8
9
  successfulSpawnDurationMs.set(serverId, durationMs);
9
- while (successfulSpawnDurationMs.size > MAX_SERVER_HISTORIES) {
10
- const oldest = successfulSpawnDurationMs.keys().next().value;
11
- if (oldest === undefined)
12
- break;
13
- successfulSpawnDurationMs.delete(oldest);
14
- }
15
10
  }
16
11
  export function getSuccessfulLspSpawnDurationMs(serverId) {
17
12
  return successfulSpawnDurationMs.get(serverId);
@@ -16,7 +16,6 @@
16
16
  * always sent, so an unrecognized/absent value never regresses `Full`/`None`
17
17
  * behavior.
18
18
  */
19
- export const TEXT_DOCUMENT_SYNC_KIND_NONE = 0;
20
19
  export const TEXT_DOCUMENT_SYNC_KIND_FULL = 1;
21
20
  export const TEXT_DOCUMENT_SYNC_KIND_INCREMENTAL = 2;
22
21
  function isSyncKind(value) {
@@ -37,7 +37,7 @@ import { normalizeMapKey } from "../path-utils.js";
37
37
  // ---------------------------------------------------------------------------
38
38
  // Constants
39
39
  // ---------------------------------------------------------------------------
40
- export const TSSERVER_REQUEST_COMMAND = "typescript.tsserverRequest";
40
+ const TSSERVER_REQUEST_COMMAND = "typescript.tsserverRequest";
41
41
  /**
42
42
  * tsserver's synthetic inferred-project name, as its own source builds it:
43
43
  * `"/dev/null/inferredProject" + counter + "*"`. `/dev/null` is a deliberate
@@ -219,13 +219,13 @@ export async function fetchTsserverProjectIdentity(svc, file) {
219
219
  // ---------------------------------------------------------------------------
220
220
  // Helpers
221
221
  // ---------------------------------------------------------------------------
222
- export function isTsserverSyncRawDiagnostic(value) {
222
+ function isTsserverSyncRawDiagnostic(value) {
223
223
  if (!value || typeof value !== "object")
224
224
  return false;
225
225
  const v = value;
226
226
  return typeof v.message === "string" && typeof v.category === "string";
227
227
  }
228
- export function tsserverSeverityFromCategory(category) {
228
+ function tsserverSeverityFromCategory(category) {
229
229
  switch (category) {
230
230
  case "error":
231
231
  return 1;
@@ -242,7 +242,7 @@ export function tsserverSeverityFromCategory(category) {
242
242
  * `LSPDiagnostic`. Both `line`/`offset` are 1-based in tsserver's protocol
243
243
  * and 0-based in LSP — this conversion handles that.
244
244
  */
245
- export function tsserverSyncDiagnosticToLsp(d) {
245
+ function tsserverSyncDiagnosticToLsp(d) {
246
246
  const startLine = Math.max(0, (d.startLocation?.line ?? 1) - 1);
247
247
  const startChar = Math.max(0, (d.startLocation?.offset ?? 1) - 1);
248
248
  const endLine = Math.max(0, (d.endLocation?.line ?? d.startLocation?.line ?? 1) - 1);
@@ -265,7 +265,7 @@ export function tsserverSyncDiagnosticToLsp(d) {
265
265
  * executed, the response envelope isn't `{success:true, body:[...]}`, or
266
266
  * any error is thrown.
267
267
  */
268
- export async function runTsserverSyncCommand(svc, file, command) {
268
+ async function runTsserverSyncCommand(svc, file, command) {
269
269
  if (typeof svc.executeCommand !== "function")
270
270
  return undefined;
271
271
  const outcome = await svc.executeCommand(file, TSSERVER_REQUEST_COMMAND, [
@@ -303,11 +303,18 @@ export async function runTsserverSyncCommand(svc, file, command) {
303
303
  * server had computed but never published (silentOnClean) — these must be
304
304
  * surfaced to the caller, not discarded. `confirmed: false` = sync path
305
305
  * unavailable, fall through to existing behavior.
306
+ *
307
+ * #2598: `getAdvertisedCommands` is REQUIRED here rather than probed for. The
308
+ * real `LSPService` defines it unconditionally (clients/lsp/index.ts), and both
309
+ * production callers hand this function that service, so the former
310
+ * `typeof … !== "function"` bail was reachable only from a partial test double
311
+ * (AGENTS.md shape 7). It was also unobservable even there: an absent method
312
+ * throws a `TypeError` that this function's own catch turns into the same
313
+ * `undefined` the bail returned. `executeCommand` stays optional on
314
+ * {@link TsserverSyncCapableService} — see {@link runTsserverSyncCommand}.
306
315
  */
307
316
  export async function attemptTsserverSyncDiagnostics(file, svc) {
308
317
  try {
309
- if (typeof svc.getAdvertisedCommands !== "function")
310
- return undefined;
311
318
  const advertised = await svc.getAdvertisedCommands(file);
312
319
  if (!advertised.includes(TSSERVER_REQUEST_COMMAND))
313
320
  return undefined;
@@ -24,6 +24,12 @@ export function classifyServerWaitTier(serverId, snapshot) {
24
24
  const mode = snapshot.workspaceDiagnosticsSupport?.mode;
25
25
  if (mode === "pull")
26
26
  return "pull-capable";
27
+ if (snapshot.customServer === true &&
28
+ mode === "push-only" &&
29
+ snapshot.diagnosticsUnsupported === true &&
30
+ snapshot.diagnosticsPublished !== true) {
31
+ return "diagnostics-unsupported";
32
+ }
27
33
  if (mode !== "push-only")
28
34
  return "waits";
29
35
  const strategy = getStrategy(serverId, snapshot.launchVariant);
@@ -73,6 +73,7 @@ export function resolveAstGrepNativeExe(platform = process.platform, arch = proc
73
73
  return undefined;
74
74
  }
75
75
  }
76
+ /** @public — loaded by scripts/probe-clean-signal.mjs through a dynamic import wrapper knip cannot see. */
76
77
  export const SERVER_DIAGNOSTIC_STRATEGIES = {
77
78
  typescript: {
78
79
  seedFirstPush: true,
@@ -287,7 +288,7 @@ const NATIVE_TS7_DIAGNOSTIC_STRATEGY = {
287
288
  silentOnClean: false,
288
289
  };
289
290
  /** Fallback for unknown servers. Conservative defaults. */
290
- export const DEFAULT_STRATEGY = {
291
+ const DEFAULT_STRATEGY = {
291
292
  seedFirstPush: false,
292
293
  pullRetryBudgetMs: 250,
293
294
  debounceMs: 150,
@@ -398,7 +398,7 @@ export function createWorkspaceDiagnosticsCacheContext(cwd) {
398
398
  const epoch = _cacheEpochs.capture(root);
399
399
  const existing = loadWorkspaceDiagnosticsCache(root);
400
400
  const entries = {
401
- ...(existing?.entries ?? {}),
401
+ ...existing?.entries,
402
402
  };
403
403
  const reverseDepsIndex = loadReverseDependencyIndexFromSnapshot({
404
404
  cwd: root,
@@ -2,7 +2,7 @@ import { getLSPService } from "./lsp/index.js";
2
2
  // Grounded against this repo's workspace-native TypeScript 7.0.2 server
3
3
  // (`tsc --lsp --stdio`) on 2026-07-30: 20 already-open documentSymbol calls
4
4
  // measured p50 3.70 ms / p95 5.28 ms. Keep generous headroom for slower hosts.
5
- export const LSP_DOCUMENT_SYMBOL_TIMEOUT_MS = 150;
5
+ const LSP_DOCUMENT_SYMBOL_TIMEOUT_MS = 150;
6
6
  export const SYMBOL_KIND_NAMES = {
7
7
  1: "file",
8
8
  2: "module",
@@ -1,6 +1,37 @@
1
1
  import * as path from "node:path";
2
+ import { BoundedSet } from "./bounded-cache.js";
2
3
  import { createReadGuardEditBatchSummary, getReadGuardCorrelationId, logReadGuardEvent, } from "./read-guard-logger.js";
3
4
  import { normalizeMapKey } from "./path-utils.js";
5
+ import { getMutationBridge } from "./mutation-bridge.js";
6
+ import { noteMutationHandled } from "./observed-mutation.js";
7
+ import { recordDegradationOnce } from "./degradation-ledger.js";
8
+ // #2450 fix round 3 (minor): gates the "bridge unavailable" dbg line to fire
9
+ // once per session, not once per FILE — a rename that touches many files in
10
+ // a bridge-less process (e.g. the MCP server) would otherwise spam the same
11
+ // line once per touched file. `recordDegradationOnce` itself has no
12
+ // first-occurrence return value (unlike `incrementDegradationCount`), so this
13
+ // tiny local latch is the minimal way to mirror that "once" intent onto the
14
+ // dbg line without changing the ledger's own once-per-(kind,subject) record.
15
+ //
16
+ // Catalog shape 17 (a process-lifetime latch must re-arm at session_start):
17
+ // wired into `handleSessionStart` (`clients/runtime-session.ts`) directly,
18
+ // NOT into `resetDegradationLedger()` — that lives in `degradation-ledger.ts`,
19
+ // which this module already imports, and reaching back from there to here
20
+ // would be a cycle.
21
+ let noBridgeDbgLogged = false;
22
+ export function resetLspMutationNoBridgeDbgLatch() {
23
+ noBridgeDbgLogged = false;
24
+ }
25
+ /**
26
+ * Test-only peek at the latch's current value (#2450 fix round 5, F2) — lets
27
+ * the session-state registry probe the actual internal state instead of
28
+ * inferring it from captured dbg messages, the same pattern every other
29
+ * process-private-scalar probe in `tests/support/session-state-registry.ts`
30
+ * uses (e.g. `_observedMutationStateForTests`).
31
+ */
32
+ export function _lspMutationNoBridgeDbgLoggedForTests() {
33
+ return noBridgeDbgLogged;
34
+ }
4
35
  const MAX_SAMPLES = 100;
5
36
  const MAX_SUMMARIES_PER_CONTEXT = 100;
6
37
  export function newLspMutationCorrelationId(toolCallId) {
@@ -95,8 +126,35 @@ function uniqueDetails(files, fileDetails) {
95
126
  }
96
127
  function bookkeepLspMutation(context, files, fileDetails) {
97
128
  const details = uniqueDetails(files, fileDetails);
129
+ // The direct path below needs `context.runtime` (the receipt) and
130
+ // `context.cacheManager` (the turn-state entry) threaded in by the caller.
131
+ // One caller structurally cannot: `workspace/applyEdit`'s server-initiated-edit
132
+ // handler (clients/lsp/client.ts) builds its own fallback `LspMutationContext`
133
+ // with neither, because there is no live reference to the runtime/cache-manager
134
+ // singletons at that call site. Rather than silently drop bookkeeping for that
135
+ // write, fall back to the mutation bridge (#2423) — mounted once at extension
136
+ // activation with live getters closing over the SAME singletons — which is
137
+ // exactly the seam built for "a producer that cannot reach the bookkeeping
138
+ // surfaces directly". This keeps `recordLspMutation` the ONE call site every
139
+ // LSP-applied edit bookkeeps through; the bridge is this function's OWN
140
+ // fallback, not a second seam other code reaches for on its behalf (#2450).
141
+ // Only the receipt (recordProjectMutation) and the turn-state entry
142
+ // (cacheManager.addModifiedRange) need the bridge fallback: the read-guard
143
+ // stamp is independent of them and already tolerates either being absent.
144
+ const useBridgeFallback = !context.runtime || !context.cacheManager;
98
145
  for (const detail of details) {
99
146
  const filePath = path.resolve(detail.filePath);
147
+ // #2450 review round 2 (F4): the SAME recordability gate the bridge
148
+ // fallback below already applies internally (`isRecordable`, mounted in
149
+ // index.ts). Applied here too so the direct (deps-threaded) branch
150
+ // cannot record a write the fallback branch would have silently
151
+ // dropped for the exact same reason (`no-read-guard` / ignored /
152
+ // vendor) — the two branches must agree on which files count as
153
+ // project source, not just on how they bookkeep the ones that do.
154
+ if (context.isRecordable && !context.isRecordable(filePath)) {
155
+ context.dbg?.(`lsp mutation not recordable, skipping ${filePath}`);
156
+ continue;
157
+ }
100
158
  if (context.readGuard) {
101
159
  try {
102
160
  context.readGuard.recordWritten(filePath);
@@ -105,34 +163,118 @@ function bookkeepLspMutation(context, files, fileDetails) {
105
163
  context.dbg?.(`lsp mutation read-guard stamp failed for ${filePath}: ${err}`);
106
164
  }
107
165
  }
108
- const runtime = context.runtime;
109
- // One mutation seam (#2000 phase 1): bump + receipt + change-log live in
110
- // RuntimeCoordinator.recordProjectMutation.
111
- runtime?.recordProjectMutation?.({
112
- filePath,
113
- source: context.source,
114
- cwd: context.cwd,
115
- changedRange: detail.range,
116
- onAppendError: (err) => context.dbg?.(`lsp mutation project change append failed for ${filePath}: ${err}`),
117
- });
118
- if (context.cacheManager) {
119
- try {
120
- context.cacheManager.addModifiedRange(filePath, detail.range ?? { start: 1, end: 1 }, detail.importsChanged ?? true, context.cwd, runtime?.telemetrySessionId);
166
+ if (useBridgeFallback) {
167
+ const bridge = getMutationBridge();
168
+ if (!bridge) {
169
+ // #2450 review round 2 (F4)/round 3 (minor): the MCP server process
170
+ // (`mcp/server.ts`) builds `lsp_navigation` with no
171
+ // `mutationDeps` AND never mounts the bridge (that only happens
172
+ // inside pi's own in-process extension activation, `index.ts`) —
173
+ // every LSP-applied edit in that process is unrecorded. Once per
174
+ // session (not once per file — a rename can touch many files in
175
+ // one call, and a session can issue many renames), not once per
176
+ // process lifetime silently. `subject` is a fixed, one-word
177
+ // degradation-ledger key (round 3: was `context.tool`, which
178
+ // varies per LSP operation — "lsp_navigation:rename" vs
179
+ // "...executeCommand" — so the SAME session could log this
180
+ // degradation once per operation kind instead of truly once).
181
+ recordDegradationOnce({
182
+ kind: "lsp-mutation-bridge-unmounted",
183
+ subject: "lsp-mutation:no-bridge",
184
+ reason: "workspace/applyEdit bookkeeping fell back to the mutation " +
185
+ "bridge, but no bridge is mounted in this process (e.g. the " +
186
+ "MCP server, which never runs pi's extension activation) — " +
187
+ "the write is unrecorded (no read-guard stamp, no turn-state " +
188
+ "entry, no change-log receipt).",
189
+ });
190
+ if (!noBridgeDbgLogged) {
191
+ noBridgeDbgLogged = true;
192
+ context.dbg?.(`lsp mutation bridge unavailable, dropping bookkeeping for ${filePath}`);
193
+ }
121
194
  }
122
- catch (err) {
123
- context.dbg?.(`lsp mutation modified-range tracking failed for ${filePath}: ${err}`);
195
+ else {
196
+ try {
197
+ const recorded = bridge.recordMutation({
198
+ filePath,
199
+ kind: "edit",
200
+ editRanges: detail.range
201
+ ? [[detail.range.start, detail.range.end]]
202
+ : undefined,
203
+ consumer: context.tool,
204
+ // Real value threaded through, not the bridge's own
205
+ // historical `false` default (#2450 review round 2, F1) —
206
+ // the tsserver organize-imports/add-import case is exactly
207
+ // the import-changing one.
208
+ importsChanged: detail.importsChanged ?? true,
209
+ // Equivalence with the direct branch below, which never
210
+ // enqueues a deferred autofix/format pass for an LSP-applied
211
+ // edit (#2450 review round 2, F3).
212
+ deferAutofix: false,
213
+ });
214
+ if (!recorded) {
215
+ context.dbg?.(`lsp mutation bridge dropped ${filePath} (recordMutation() ` +
216
+ "returned false — see the mutation_bridge debug output " +
217
+ "for the reason: malformed entry, out-of-scope path, or " +
218
+ "a bookkeeping error)");
219
+ }
220
+ }
221
+ catch (err) {
222
+ context.dbg?.(`lsp mutation bridge fallback failed for ${filePath}: ${err}`);
223
+ }
224
+ }
225
+ }
226
+ else {
227
+ const runtime = context.runtime;
228
+ // One mutation seam (#2000 phase 1): bump + receipt + change-log live in
229
+ // RuntimeCoordinator.recordProjectMutation.
230
+ runtime?.recordProjectMutation?.({
231
+ filePath,
232
+ source: context.source,
233
+ cwd: context.cwd,
234
+ changedRange: detail.range,
235
+ onAppendError: (err) => context.dbg?.(`lsp mutation project change append failed for ${filePath}: ${err}`),
236
+ });
237
+ if (context.cacheManager) {
238
+ try {
239
+ context.cacheManager.addModifiedRange(filePath, detail.range ?? { start: 1, end: 1 }, detail.importsChanged ?? true, context.cwd, runtime?.telemetrySessionId, undefined,
240
+ // #2504 review round 2 (F1). #2504 added a containment
241
+ // filter to `addModifiedRange` and gave it `cwd` as the
242
+ // project root. On THIS path `cwd` is the calling
243
+ // directory, which `tools/lsp-navigation.ts` scopes to a
244
+ // sub-package while the LSP client's root spans the whole
245
+ // monorepo — so a server-initiated edit on a sibling
246
+ // package (#2450) and every edit whose context carries a
247
+ // separate bookkeeping cwd (#2479) were silently dropped
248
+ // from the worklist. Same root the `isRecordable` gate a
249
+ // few frames up already uses: `runtime.projectRoot`,
250
+ // falling back to `cwd` when no runtime is threaded.
251
+ runtime?.projectRoot ?? context.cwd);
252
+ }
253
+ catch (err) {
254
+ context.dbg?.(`lsp mutation modified-range tracking failed for ${filePath}: ${err}`);
255
+ }
124
256
  }
257
+ // #2465: the bridge-fallback branch above gets this for free —
258
+ // `recordMutationThroughSeam` calls `noteMutationHandled` for every
259
+ // entry it takes. The DIRECT branch bypasses the bridge entirely, so
260
+ // it has to say so itself, or the #2430 `agent_settled` sweep finds
261
+ // bytes on disk it has no baseline for and reports every
262
+ // `lsp_navigation` rename as drift no tool call explains. Placed
263
+ // after `addModifiedRange` and OUTSIDE its `try`: the mark is about
264
+ // "pi-lens wrote this file", which is true whether or not the
265
+ // turn-state insert threw.
266
+ noteMutationHandled(filePath);
125
267
  }
268
+ // #2450 review round 2 (Partial-deps): its own surface, independent of
269
+ // whether the receipt/turn-state used the direct path or the bridge
270
+ // fallback above — a half-threaded caller (one that provides
271
+ // `recordAutofix` but not `runtime`/`cacheManager`) must not lose its
272
+ // turn-summary publisher just because SOME other surface fell back.
126
273
  if (context.recordAutofix && context.source === "autofix") {
127
274
  const key = normalizeMapKey(filePath);
128
- const seen = context.autofixRecordedPaths ?? new Set();
275
+ const seen = context.autofixRecordedPaths ?? new BoundedSet(MAX_SAMPLES);
129
276
  context.autofixRecordedPaths = seen;
130
277
  if (!seen.has(key)) {
131
- if (seen.size >= MAX_SAMPLES) {
132
- const oldest = seen.values().next().value;
133
- if (oldest)
134
- seen.delete(oldest);
135
- }
136
278
  seen.add(key);
137
279
  context.recordAutofix(filePath);
138
280
  }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Bounded worker pool — the repo's one shared `mapWithConcurrency`.
3
+ *
4
+ * Lived in `dependency-checker.ts` (its first caller) and was exported from
5
+ * there for `dispatch/runners/biome-check.ts`. #2504 added a third caller,
6
+ * `runtime-turn.ts`'s bounded test-runner batch — and runtime-turn must not
7
+ * drag an ANALYZER client into its eager import graph for a 15-line helper
8
+ * (that is exactly the eager-bootstrap cost #2467 removed). So the value moved
9
+ * to this zero-dependency leaf and `dependency-checker.ts` re-exports it: every
10
+ * existing importer keeps its specifier, and a consumer that only needs the
11
+ * pool no longer loads madge.
12
+ *
13
+ * Same shape as `ledger-bounds.ts` (#2426) for the degradation ledger's bound.
14
+ *
15
+ * NOT consolidated here: `lsp/client.ts` keeps its own file-local copy on
16
+ * purpose (see its comment) — that is a documented decision, not drift.
17
+ */
18
+ /**
19
+ * Run `mapper` over `items` with at most `concurrency` in flight at once.
20
+ */
21
+ export async function mapWithConcurrency(items, concurrency, mapper) {
22
+ if (items.length === 0)
23
+ return;
24
+ let nextIndex = 0;
25
+ const workerCount = Math.max(1, Math.min(concurrency, items.length));
26
+ const worker = async () => {
27
+ while (true) {
28
+ const index = nextIndex++;
29
+ if (index >= items.length)
30
+ return;
31
+ await mapper(items[index]);
32
+ }
33
+ };
34
+ const workers = Array.from({ length: workerCount }, () => worker());
35
+ await Promise.all(workers);
36
+ }
@@ -23,7 +23,7 @@ import { PathKeyedMap } from "../path-keyed-map.js";
23
23
  import { normalizeMapKey } from "../path-utils.js";
24
24
  import { loadProjectSnapshot } from "../project-snapshot.js";
25
25
  import { buildOrUpdateGraph } from "../review-graph/service.js";
26
- import { recordDiagnostics } from "../widget-state.js";
26
+ import { classifyDiagnosticTier, recordDiagnostics } from "../widget-state.js";
27
27
  import { deserializeWordIndex, removeWordIndexDocumentAsync, scheduleWordIndexPersist, updateWordIndexDocumentForEdit, WORD_INDEX_MAX_BYTES, } from "../word-index.js";
28
28
  import { logWordIndex } from "../word-index-logger.js";
29
29
  import { createMcpHost } from "./host-shim.js";
@@ -170,6 +170,28 @@ export function _getWarmWordIndexCacheStateForTests() {
170
170
  // is the persistent warm server's job (see the `lsp` honesty signal + Tier 2).
171
171
  const WARMUP_CLIENT_WAIT_MS = 10_000;
172
172
  const WARMUP_DIAGNOSTICS_WAIT_MS = 6_000;
173
+ /** One diagnostic, flattened to the fields an MCP consumer needs. */
174
+ /**
175
+ * Split the dispatch `warnings` bucket into real warnings vs advisories using
176
+ * the ONE severity-projection policy ({@link classifyDiagnosticTier}, #2414/
177
+ * #2417) so the model-facing counts never present a `hint`/`info`-tier finding
178
+ * as a warning (#2420, remainder of #2414). The dispatch `semantic` axis (which
179
+ * also drives blocking) is intentionally NOT consulted or mutated here: this is
180
+ * a display-count split, not a blocking-decision change. `blockers`
181
+ * (`semantic:"blocking"`) are computed upstream and never enter this bucket, so
182
+ * nothing that blocks can stop blocking through this seam. A non-advisory entry
183
+ * in the bucket (an `error`/`warning`-tier finding routed here as
184
+ * `semantic:"warning"`/`"none"`) stays counted as a warning exactly as before,
185
+ * so a mixed file's real warning count is unchanged.
186
+ */
187
+ export function summarizeWarningTiers(warnings) {
188
+ let advisories = 0;
189
+ for (const w of warnings) {
190
+ if (classifyDiagnosticTier(w) === "advisory")
191
+ advisories++;
192
+ }
193
+ return { warnings: warnings.length - advisories, advisories };
194
+ }
173
195
  function toMcpDiagnostic(diagnostic) {
174
196
  return {
175
197
  line: diagnostic.line,
@@ -235,7 +257,7 @@ export async function analyzeFile(filePath, cwd, options = {}) {
235
257
  : path.resolve(cwd, filePath);
236
258
  // no-delta by default → a full snapshot every call (not delta-filtered);
237
259
  // caller flags win over the default.
238
- const host = createMcpHost({ "no-delta": true, ...(options.flags ?? {}) }, cwd);
260
+ const host = createMcpHost({ "no-delta": true, ...options.flags }, cwd);
239
261
  if (options.warmLsp !== false) {
240
262
  await warmLspForFile(absPath, host);
241
263
  }
@@ -345,7 +367,9 @@ export async function analyzeFile(filePath, cwd, options = {}) {
345
367
  const lspRunner = latencyReport?.runners.find((runner) => runner.runnerId === "lsp");
346
368
  const lsp = lspRunner
347
369
  ? {
348
- ran: lspRunner.status !== "skipped" && lspRunner.status !== "when_skipped",
370
+ ran: lspRunner.status !== "skipped" &&
371
+ lspRunner.status !== "when_skipped" &&
372
+ lspRunner.status !== "test_file_skipped",
349
373
  status: lspRunner.status,
350
374
  diagnosticCount: lspRunner.diagnosticCount,
351
375
  durationMs: lspRunner.durationMs,
@@ -360,7 +384,12 @@ export async function analyzeFile(filePath, cwd, options = {}) {
360
384
  counts: {
361
385
  diagnostics: result.diagnostics.length,
362
386
  blockers: result.blockers.length,
363
- warnings: result.warnings.length,
387
+ // #2420: `result.warnings` is the dispatch warnings bucket
388
+ // (`semantic:"warning"|"none"`), which still carries `hint`/`info`-tier
389
+ // findings folded in by the runner's severity→semantic map. Project
390
+ // them out through the shared tier policy so the model sees real
391
+ // warnings and advisories separately.
392
+ ...summarizeWarningTiers(result.warnings),
364
393
  fixed: result.fixed.length,
365
394
  },
366
395
  lsp,