@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
@@ -17,6 +17,7 @@
17
17
  * `~/.pi-lens/config.json`. Adding a toggle means adding one entry here; there
18
18
  * is no second place to remember.
19
19
  */
20
+ import { DEPRECATED_CONFIG_SURFACES } from "./config-diagnostic-codes.js";
20
21
  export const LENS_FLAGS = [
21
22
  {
22
23
  name: "no-lens",
@@ -188,6 +189,22 @@ export const LENS_FLAGS = [
188
189
  default: false,
189
190
  scope: "global",
190
191
  },
192
+ ...[
193
+ "knip",
194
+ "jscpd",
195
+ "madge",
196
+ "gitleaks",
197
+ "govulncheck",
198
+ "deadCode",
199
+ "complexity",
200
+ ].map((analyzer) => ({
201
+ name: `no-${analyzer.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`,
202
+ description: `Disable the ${analyzer} session-start analyzer. Also via ${analyzer}.enabled=false in config.`,
203
+ configKey: `${analyzer}.enabled`,
204
+ negated: true,
205
+ default: false,
206
+ scope: "project",
207
+ })),
191
208
  ];
192
209
  const byName = new Map(LENS_FLAGS.map((spec) => [spec.name, spec]));
193
210
  export function getLensFlagSpec(name) {
@@ -215,25 +232,79 @@ export const GLOBAL_NON_FLAG_CONFIG_SECTIONS = [
215
232
  "ignore",
216
233
  "dispatch",
217
234
  "widget",
235
+ "startup",
218
236
  "$schema",
219
237
  ];
238
+ /**
239
+ * Recognized TOP-LEVEL sections of a project `.pi-lens.json` that are NOT
240
+ * derived from the flag registry — the project loader's own hand-parsed
241
+ * sections (`ignore`, `rules`, `maxProjectFiles`, `reviewGraph`) plus the two
242
+ * namespaces another pi-lens runner reads off `PiLensProjectConfig.raw`
243
+ * (`trivy` in `trivy-client.ts`, `helm.renderValidation.enabled` in the
244
+ * helm-render runner, #1283).
245
+ *
246
+ * Moved here from `project-lens-config.ts` by #2426 so the canonical schema in
247
+ * `config-schema.ts` derives its namespace list from ONE place instead of
248
+ * importing the project loader (which would close an import cycle, since that
249
+ * loader now resolves through the schema).
250
+ */
251
+ export const PROJECT_NON_FLAG_CONFIG_SECTIONS = [
252
+ "ignore",
253
+ "rules",
254
+ "maxProjectFiles",
255
+ "reviewGraph",
256
+ "trivy",
257
+ "helm",
258
+ "startup",
259
+ ];
260
+ /**
261
+ * The four LSP settings a PROJECT config file may carry — at its root during
262
+ * the deprecation window, and under `lsp` canonically. DERIVED from the
263
+ * registry's `kind: "key"` rows so the accepted set and the removal schedule
264
+ * cannot drift apart, and named once because two places below need it.
265
+ */
266
+ const LSP_PROJECT_HONORED_KEYS = DEPRECATED_CONFIG_SURFACES.filter((row) => row.kind === "key").map((row) => row.surface);
220
267
  /**
221
268
  * Foreign top-level namespaces that legitimately appear in a SHARED
222
269
  * `.pi-lens.json` but belong to a DIFFERENT loader: the LSP config loader
223
- * (`clients/lsp/config.ts`) reads `servers` / `serverOverrides` /
224
- * `disabledServers` / `warmFiles` out of the very same file. The project-lens
270
+ * (`clients/lsp/config.ts`) reads the `lsp` namespace — and, for its
271
+ * deprecation window, the legacy ROOT keys `servers` / `serverOverrides` /
272
+ * `disabledServers` / `warmFiles` — out of the very same file. The project-lens
225
273
  * loader must TOLERATE these (never warn "unknown key") — they are not typos,
226
274
  * just another subsystem's namespace. `$schema` is allowed for editor
227
275
  * JSON-schema association. Declared once, here, for the same #883 reason as
228
276
  * {@link GLOBAL_NON_FLAG_CONFIG_SECTIONS}.
277
+ *
278
+ * `lsp` joined this list in #2426: it is the canonical home of the four legacy
279
+ * keys beside it, so a project config that has already been migrated must not
280
+ * be told its `lsp` section is a global-only setting. Its tolerance is
281
+ * SUB-KEY-SCOPED — see {@link PROJECT_FOREIGN_NAMESPACE_HONORED_KEYS}.
229
282
  */
230
283
  export const PROJECT_FOREIGN_CONFIG_NAMESPACES = [
231
- "servers",
232
- "serverOverrides",
233
- "disabledServers",
234
- "warmFiles",
284
+ "lsp",
285
+ // DERIVED, not restated: the four legacy root keys are exactly the
286
+ // `kind: "key"` rows of the deprecation registry, and a hand-copied second
287
+ // list of them would be a place for the accepted set and the removal
288
+ // schedule to drift apart. `config-diagnostic-codes.ts` is a dependency-free
289
+ // data leaf, so importing it keeps this module's own no-cycle property.
290
+ ...LSP_PROJECT_HONORED_KEYS,
235
291
  "$schema",
236
292
  ];
293
+ /**
294
+ * For a foreign namespace whose PROJECT-honored surface is only PART of what
295
+ * the namespace can hold: which sub-keys a `.pi-lens.json` may set.
296
+ *
297
+ * `lsp` is tolerated at the top level because the LSP loader reads it out of
298
+ * this same file — but only four of its keys are project-scoped. `lsp.enabled`
299
+ * is a `scope: "global"` flag (`--no-lsp`), so blanket namespace tolerance made
300
+ * a project-file `lsp.enabled: false` do nothing with no signal at all, where
301
+ * before #2426 the user got the honest "global-only setting" notice
302
+ * (#2426 review round 2, F3). `docs/configuration.md` promises that nothing
303
+ * about a config is ignored silently, so the namespace is tolerated KEY BY KEY
304
+ * and anything else under it is scanned by the same global-only-vs-typo rules a
305
+ * top-level key gets.
306
+ */
307
+ export const PROJECT_FOREIGN_NAMESPACE_HONORED_KEYS = new Map([["lsp", LSP_PROJECT_HONORED_KEYS]]);
237
308
  /**
238
309
  * The distinct TOP-LEVEL section keys a set of flags lives under (the first
239
310
  * dotted segment of each `configKey`). Both config loaders derive their
@@ -24,7 +24,7 @@ import { normalizeMapKey, toProjectRelativePath } from "./path-utils.js";
24
24
  import { canonicalSourceTwin } from "./source-filter.js";
25
25
  import { buildOrUpdateGraph } from "./review-graph/service.js";
26
26
  import { detectFileRole } from "./file-role.js";
27
- export const DEFAULT_MAX_MAP_NODES = 500;
27
+ const DEFAULT_MAX_MAP_NODES = 500;
28
28
  /**
29
29
  * Aggregates a symbol-level {@link ReviewGraph} into file-level nodes/edges.
30
30
  * Pure and side-effect-free — no graph build here, no filesystem access — so
@@ -22,10 +22,10 @@
22
22
  */
23
23
  import * as fs from "node:fs";
24
24
  import * as path from "node:path";
25
- import { getGlobalPiLensDir } from "./file-utils.js";
25
+ import { getGlobalPiLensLogDir } from "./probe-home-state.js";
26
26
  import { getRegisteredLogFiles } from "./ndjson-logger.js";
27
27
  import { pathsEqual } from "./path-utils.js";
28
- const LOG_DIR = getGlobalPiLensDir();
28
+ const LOG_DIR = getGlobalPiLensLogDir();
29
29
  const LOGS_SUBDIR = path.join(LOG_DIR, "logs");
30
30
  /**
31
31
  * Every global `.log` pi-lens writes under ~/.pi-lens, resolved dynamically so
@@ -220,39 +220,3 @@ export function runLogCleanup(dbg) {
220
220
  }
221
221
  return results;
222
222
  }
223
- /**
224
- * Get current log storage summary
225
- */
226
- export function getLogStorageSummary() {
227
- const files = [];
228
- let totalMB = 0;
229
- // Main logs
230
- for (const name of getManagedLogFiles()) {
231
- const filePath = path.join(LOG_DIR, name);
232
- if (fs.existsSync(filePath)) {
233
- const sizeMB = getFileSizeMB(filePath);
234
- const ageDays = getFileAgeDays(filePath);
235
- files.push({ name, sizeMB, ageDays });
236
- totalMB += sizeMB;
237
- }
238
- }
239
- // Daily logs
240
- try {
241
- if (fs.existsSync(LOGS_SUBDIR)) {
242
- const dailyFiles = fs.readdirSync(LOGS_SUBDIR);
243
- for (const name of dailyFiles) {
244
- if (!name.endsWith(".jsonl"))
245
- continue;
246
- const filePath = path.join(LOGS_SUBDIR, name);
247
- const sizeMB = getFileSizeMB(filePath);
248
- const ageDays = getFileAgeDays(filePath);
249
- files.push({ name: `logs/${name}`, sizeMB, ageDays });
250
- totalMB += sizeMB;
251
- }
252
- }
253
- }
254
- catch {
255
- // Ignore
256
- }
257
- return { totalMB, files };
258
- }
@@ -34,7 +34,9 @@
34
34
  * `PromiseDescriptor.budgetMs`.
35
35
  */
36
36
  export async function raceToCompletion(promises, shouldComplete, options = { timeoutMs: 1500 }) {
37
- const results = new Array(promises.length).fill(undefined);
37
+ const results = [];
38
+ results.length = promises.length;
39
+ results.fill(undefined);
38
40
  let graceTimer;
39
41
  let auxGraceTimer;
40
42
  let completed = false;
@@ -730,7 +730,7 @@ function retirePullSource(state, identifier) {
730
730
  for (const source of [state.pullResultIds, state.pullRequestSequences]) {
731
731
  if (!source)
732
732
  continue;
733
- for (const key of [...source.keys()]) {
733
+ for (const key of source.keys()) {
734
734
  if (key.endsWith(suffix))
735
735
  source.delete(key);
736
736
  }
@@ -947,7 +947,7 @@ function scanSentContent(content) {
947
947
  * read on the notification path (I1). Bounded by the same file-size gates the
948
948
  * caller already applies to the content it hands us.
949
949
  */
950
- function recordSentContent(state, normalizedPath, version, content) {
950
+ function recordSentContent(state, normalizedPath, version, content, coalescedCount = 0) {
951
951
  const scan = scanSentContent(content);
952
952
  const previous = state.documentContentHashes.get(normalizedPath);
953
953
  if (previous && previous.version > version) {
@@ -1049,6 +1049,7 @@ function recordSentContent(state, normalizedPath, version, content) {
1049
1049
  version,
1050
1050
  contentLength: content.length,
1051
1051
  contentLineCount: scan.lfNewlineCount + 1,
1052
+ ...(coalescedCount > 0 && { coalescedCount }),
1052
1053
  },
1053
1054
  });
1054
1055
  }
@@ -1172,7 +1173,7 @@ export function resolveConfigurationSection(initialization, section) {
1172
1173
  // Exported (only) so tests can invoke the publishDiagnostics notification
1173
1174
  // handler directly against a mock LSPClientState/connection without spawning
1174
1175
  // a real language server. Not part of the public client API surface.
1175
- export function setupIncomingHandlers(state, initialization) {
1176
+ export function setupIncomingHandlers(state, initialization, onDiagnosticsPublished) {
1176
1177
  state.connection.onNotification("textDocument/publishDiagnostics", (params) => {
1177
1178
  // #1639: the settle operation's own clock for THIS publish. A
1178
1179
  // "quiet-window" settle's durationMs below measures from here (when
@@ -1187,6 +1188,7 @@ export function setupIncomingHandlers(state, initialization) {
1187
1188
  // that is no longer open on this client.
1188
1189
  if (state.closedDocuments?.has(normalizedPath))
1189
1190
  return;
1191
+ onDiagnosticsPublished?.(state.serverId);
1190
1192
  const newDiags = normalizeLspDiagnostics(params.diagnostics || []);
1191
1193
  const docVersion = params.version;
1192
1194
  if (PUB_DEBUG) {
@@ -1423,14 +1425,33 @@ export function setupIncomingHandlers(state, initialization) {
1423
1425
  if (state.serverEditsAllowed <= 0 || !params?.edit) {
1424
1426
  return { applied: false, failureReason: "edit not solicited" };
1425
1427
  }
1426
- const context = (state.activeMutationDepth ?? 0) === 1
1427
- ? state.activeMutationContext
1428
- : undefined;
1428
+ // #2450 fix round 3 (F3) removed a `depth === 1` re-check here as
1429
+ // dead code. It is not dead — it moved to the WRITE side, where it
1430
+ // is the live invariant: `state.activeMutationContext` is assigned
1431
+ // only by `syncMutationContextSlot` (below in this file), which
1432
+ // populates it exactly when `activeMutationDepth === 1` and
1433
+ // empties it on every other frame transition. The depth test
1434
+ // therefore still runs, on each entry and each unwind; repeating
1435
+ // it here would be a second copy of the same predicate, not an
1436
+ // independent gate. What this handler reads is consequently either
1437
+ // the context of the single frame currently in flight or
1438
+ // `undefined` — never a settled frame's context, and never a
1439
+ // context belonging to some other window that is still open
1440
+ // (#2479 review rounds 2/3).
1441
+ const context = state.activeMutationContext;
1442
+ // `workspace/applyEdit` is only ever honored inside the
1443
+ // `serverEditsAllowed` window this handler just checked, which is
1444
+ // opened exclusively by an executeCommand call — so a fallback here is
1445
+ // always executeCommand-solicited, never a bare edit (#2450). This
1446
+ // fallback context carries no runtime/cacheManager (there is no live
1447
+ // reference to those singletons at this call site); lsp-mutation.ts's
1448
+ // bookkeepLspMutation falls back to the mutation bridge for exactly
1449
+ // that reason rather than silently dropping the write's bookkeeping.
1429
1450
  const telemetryContext = context ?? {
1430
1451
  cwd: state.root,
1431
1452
  correlationId: newLspMutationCorrelationId(),
1432
1453
  tool: "lsp-workspace-applyEdit",
1433
- source: "lsp-edit",
1454
+ source: "lsp-execute-command",
1434
1455
  };
1435
1456
  try {
1436
1457
  await applyWorkspaceEdit(params.edit, state.root, {
@@ -2430,7 +2451,7 @@ export function handleNotifyExternalChange(state, filePath, type) {
2430
2451
  const uri = state.openDocumentUris?.get(normalizedPath) ?? pathToFileURL(filePath).href;
2431
2452
  state.watchQueue.enqueue(uri, type);
2432
2453
  }
2433
- export async function handleNotifyOpen(state, filePath, content, languageId, preserveDiagnostics = false, silent = false) {
2454
+ async function handleNotifyOpenOnce(state, filePath, content, languageId, preserveDiagnostics = false, silent = false, coalescedCount = 0) {
2434
2455
  if (!isClientAlive(state))
2435
2456
  return;
2436
2457
  const normalizedPath = normalizeMapKey(filePath);
@@ -2478,7 +2499,7 @@ export async function handleNotifyOpen(state, filePath, content, languageId, pre
2478
2499
  // #1669 review F7: only mirror the send locally once it actually left
2479
2500
  // the process — see safeSendNotification's doc comment.
2480
2501
  if (reopenSent)
2481
- recordSentContent(state, normalizedPath, version, content);
2502
+ recordSentContent(state, normalizedPath, version, content, coalescedCount);
2482
2503
  state.openDocuments.add(normalizedPath);
2483
2504
  state.openDocumentUris?.set(normalizedPath, uri);
2484
2505
  return;
@@ -2488,7 +2509,7 @@ export async function handleNotifyOpen(state, filePath, content, languageId, pre
2488
2509
  contentChanges: buildContentChanges(state, normalizedPath, content),
2489
2510
  });
2490
2511
  if (changeSent)
2491
- recordSentContent(state, normalizedPath, version, content);
2512
+ recordSentContent(state, normalizedPath, version, content, coalescedCount);
2492
2513
  return;
2493
2514
  }
2494
2515
  state.pendingOpens.add(normalizedPath);
@@ -2523,7 +2544,7 @@ export async function handleNotifyOpen(state, filePath, content, languageId, pre
2523
2544
  return;
2524
2545
  const openSent = await safeSendNotification(state.connection, "textDocument/didOpen", { textDocument: { uri, languageId, version: 0, text: content } });
2525
2546
  if (openSent)
2526
- recordSentContent(state, normalizedPath, 0, content);
2547
+ recordSentContent(state, normalizedPath, 0, content, coalescedCount);
2527
2548
  state.pendingOpens.delete(normalizedPath);
2528
2549
  state.openDocuments.add(normalizedPath);
2529
2550
  state.closedDocuments?.delete(normalizedPath);
@@ -2547,7 +2568,81 @@ export async function handleNotifyOpen(state, filePath, content, languageId, pre
2547
2568
  },
2548
2569
  });
2549
2570
  }
2550
- async function handleNotifyChangeOnce(state, filePath, content, normalizedPath) {
2571
+ /**
2572
+ * Replace only notifications that have not started a transport write. A
2573
+ * microtask starts the first entry so a synchronous burst of callers installs
2574
+ * one latest entry before any bytes are handed to vscode-jsonrpc. Once an
2575
+ * entry starts, it runs to completion; LSP has no cancellation/acknowledgement
2576
+ * for notifications, so dropping a started write could violate didOpen before
2577
+ * didChange ordering or leave a partial message in the pipe.
2578
+ */
2579
+ function enqueueDocumentNotify(state, normalizedPath, run) {
2580
+ let queue = state.notifyChangeQueues.get(normalizedPath);
2581
+ if (!queue) {
2582
+ queue = { running: false };
2583
+ state.notifyChangeQueues.set(normalizedPath, queue);
2584
+ }
2585
+ return new Promise((resolve, reject) => {
2586
+ const previous = queue?.pending;
2587
+ // Keep superseded callers attached to the replacement's completion. The
2588
+ // notification is dropped, but callers such as the auxiliary backlog
2589
+ // ledger must not observe completion before the newest content is sent.
2590
+ const waiters = previous?.waiters ?? [];
2591
+ waiters.push({ resolve, reject });
2592
+ queue.pending = {
2593
+ run,
2594
+ waiters,
2595
+ coalescedCount: (previous?.coalescedCount ?? 0) + (previous ? 1 : 0),
2596
+ };
2597
+ if (queue.running)
2598
+ return;
2599
+ queue.running = true;
2600
+ // Let same-turn callers replace the unwritten entry before the runner
2601
+ // invokes the transport. Different path queues still start independently.
2602
+ void Promise.resolve().then(async () => {
2603
+ try {
2604
+ for (;;) {
2605
+ const next = queue.pending;
2606
+ if (!next)
2607
+ break;
2608
+ queue.pending = undefined;
2609
+ try {
2610
+ await next.run(next.coalescedCount);
2611
+ for (const waiter of next.waiters)
2612
+ waiter.resolve();
2613
+ }
2614
+ catch (error) {
2615
+ for (const waiter of next.waiters)
2616
+ waiter.reject(error);
2617
+ }
2618
+ }
2619
+ }
2620
+ finally {
2621
+ queue.running = false;
2622
+ if (!queue.pending &&
2623
+ state.notifyChangeQueues.get(normalizedPath) === queue) {
2624
+ state.notifyChangeQueues.delete(normalizedPath);
2625
+ }
2626
+ }
2627
+ });
2628
+ });
2629
+ }
2630
+ /** Drop unwritten document notifications when a client is torn down. */
2631
+ function cancelDocumentNotifyQueues(state) {
2632
+ for (const queue of state.notifyChangeQueues.values()) {
2633
+ for (const waiter of queue.pending?.waiters ?? [])
2634
+ waiter.resolve();
2635
+ queue.pending = undefined;
2636
+ }
2637
+ state.notifyChangeQueues.clear();
2638
+ }
2639
+ export function handleNotifyOpen(state, filePath, content, languageId, preserveDiagnostics = false, silent = false) {
2640
+ if (!isClientAlive(state))
2641
+ return Promise.resolve();
2642
+ const normalizedPath = normalizeMapKey(filePath);
2643
+ return enqueueDocumentNotify(state, normalizedPath, (coalescedCount) => handleNotifyOpenOnce(state, filePath, content, languageId, preserveDiagnostics, silent, coalescedCount));
2644
+ }
2645
+ async function handleNotifyChangeOnce(state, filePath, content, normalizedPath, coalescedCount = 0) {
2551
2646
  if (!isClientAlive(state))
2552
2647
  return;
2553
2648
  const uri = state.openDocumentUris?.get(normalizedPath) ?? pathToFileURL(filePath).href;
@@ -2566,7 +2661,7 @@ async function handleNotifyChangeOnce(state, filePath, content, normalizedPath)
2566
2661
  state.documentOpenedAt.set(normalizedPath, Date.now());
2567
2662
  state.diagnosticPublicationCounts.set(normalizedPath, 0);
2568
2663
  if (fallbackOpenSent)
2569
- recordSentContent(state, normalizedPath, 0, content);
2664
+ recordSentContent(state, normalizedPath, 0, content, coalescedCount);
2570
2665
  state.openDocuments.add(normalizedPath);
2571
2666
  state.openDocumentUris?.set(normalizedPath, uri);
2572
2667
  return;
@@ -2581,7 +2676,7 @@ async function handleNotifyChangeOnce(state, filePath, content, normalizedPath)
2581
2676
  contentChanges: buildContentChanges(state, normalizedPath, content),
2582
2677
  });
2583
2678
  if (changeSent)
2584
- recordSentContent(state, normalizedPath, version, content);
2679
+ recordSentContent(state, normalizedPath, version, content, coalescedCount);
2585
2680
  }
2586
2681
  /**
2587
2682
  * #2113: serialize the read/build/send/record transaction per document. The
@@ -2592,17 +2687,7 @@ export function handleNotifyChange(state, filePath, content) {
2592
2687
  if (!isClientAlive(state))
2593
2688
  return Promise.resolve();
2594
2689
  const normalizedPath = normalizeMapKey(filePath);
2595
- const previous = state.notifyChangeQueues.get(normalizedPath) ?? Promise.resolve();
2596
- const queued = previous
2597
- .catch(() => undefined)
2598
- .then(() => handleNotifyChangeOnce(state, filePath, content, normalizedPath));
2599
- const settled = queued.finally(() => {
2600
- if (state.notifyChangeQueues.get(normalizedPath) === settled) {
2601
- state.notifyChangeQueues.delete(normalizedPath);
2602
- }
2603
- });
2604
- state.notifyChangeQueues.set(normalizedPath, settled);
2605
- return settled;
2690
+ return enqueueDocumentNotify(state, normalizedPath, (coalescedCount) => handleNotifyChangeOnce(state, filePath, content, normalizedPath, coalescedCount));
2606
2691
  }
2607
2692
  /** Close a document through the same lifecycle path exposed by the client. */
2608
2693
  export async function closeDocument(state, filePath) {
@@ -2739,6 +2824,9 @@ async function clientShutdownOnce(state, options) {
2739
2824
  state.pendingOpens.clear();
2740
2825
  state.openDocuments.clear();
2741
2826
  state.openDocumentUris?.clear();
2827
+ // #2357: superseded notifications that have not started writing are moot
2828
+ // once this client is dead; resolve their callers and release the queue.
2829
+ cancelDocumentNotifyQueues(state);
2742
2830
  // #1412 L1: mirror openDocuments' clear — a shut-down/evicted client's
2743
2831
  // probe memo is moot along with everything else document-scoped.
2744
2832
  state.projectIdentityProbedFiles?.clear();
@@ -3183,6 +3271,35 @@ async function clientPingLiveness(state, timeoutMs = LIVENESS_PING_TIMEOUT_MS) {
3183
3271
  }
3184
3272
  return isClientAlive(state);
3185
3273
  }
3274
+ // #2479: `activeMutationContext` is DERIVED state, not a stack. The frame that
3275
+ // takes `activeMutationDepth` 0 -> 1 owns the context for its whole lifetime
3276
+ // (`mutationContextOwner`); the slot merely exposes that owner while it is the
3277
+ // ONLY frame in flight. Deeper or overlapping frames therefore empty the slot
3278
+ // for as long as they are open — concurrent and nested commands deliberately
3279
+ // do not cross-correlate — and it refills by itself when they unwind, with no
3280
+ // frame having to remember anything.
3281
+ //
3282
+ // Recomputing beats saving and restoring because these frames do not nest:
3283
+ // `LSPClient.executeCommand` and `LSPService.executeCommand` fan out with no
3284
+ // mutex, parallel `lsp_navigation` calls with `apply: true` overlap, and #449
3285
+ // light mode shares one client across agents. With A (the owner), B and C
3286
+ // overlapping and the MIDDLE one settling first, a per-frame restore hands A's
3287
+ // context back while C's window is the open one — and `LspMutationContext`
3288
+ // carries a cwd plus a directly-threaded runtime/cacheManager, with
3289
+ // `readTurnState` / `appendProjectChange` cwd-scoped, so that routes a live
3290
+ // edit's bookkeeping into another project's change log (strictly worse than
3291
+ // the honest fallback the pre-#2479 code gave). An owner-liveness flag does
3292
+ // not fix it either: it answers "is an owner live", not "does the slot belong
3293
+ // to the window this edit is in" — and C's own unwind then puts back the
3294
+ // `undefined` it saved while the owner is still running, resurrecting #2479
3295
+ // (review round 3). Deriving answers the second question by construction, and
3296
+ // "no command in flight == no context" — the invariant the
3297
+ // `serverEditsAllowed` gate on `workspace/applyEdit` is paired with — falls
3298
+ // out of it with no depth-0 special case.
3299
+ function syncMutationContextSlot(state) {
3300
+ state.activeMutationContext =
3301
+ state.activeMutationDepth === 1 ? state.mutationContextOwner : undefined;
3302
+ }
3186
3303
  // Run an advertised server command via workspace/executeCommand, with the
3187
3304
  // generous EXECUTE_COMMAND_TIMEOUT_MS anti-deadlock backstop. Preserves the
3188
3305
  // hardening invariants: allowlist-by-advertisement (only commands the server
@@ -3201,10 +3318,20 @@ export async function runServerCommand(state, command, args, timeoutMs = EXECUTE
3201
3318
  }
3202
3319
  state.serverEditsAllowed += 1;
3203
3320
  state.activeMutationDepth = (state.activeMutationDepth ?? 0) + 1;
3204
- if (state.activeMutationDepth === 1)
3205
- state.activeMutationContext = mutationContext;
3206
- else
3207
- state.activeMutationContext = undefined;
3321
+ // #2479: the frame that takes the depth to 1 owns the context; the slot is
3322
+ // derived from that owner (see `syncMutationContextSlot` above). The
3323
+ // pre-#2479 shape only ever CLEARED the slot, and only once the depth
3324
+ // returned to 0, so a nested `executeCommand` unwinding from depth 2 back
3325
+ // to 1 left the OUTER call without its own context for the rest of its
3326
+ // window: every server-initiated `applyEdit` it still solicited read
3327
+ // `undefined`, fell to the mutation bridge, and carried the generic
3328
+ // `agent-tool:lsp-workspace-applyEdit` receipt instead of the outer
3329
+ // operation's own (`lsp-rename` / `lsp-execute-command`).
3330
+ const isMutationContextOwner = state.activeMutationDepth === 1;
3331
+ if (isMutationContextOwner) {
3332
+ state.mutationContextOwner = mutationContext;
3333
+ }
3334
+ syncMutationContextSlot(state);
3208
3335
  try {
3209
3336
  let result;
3210
3337
  try {
@@ -3231,8 +3358,15 @@ export async function runServerCommand(state, command, args, timeoutMs = EXECUTE
3231
3358
  finally {
3232
3359
  state.serverEditsAllowed -= 1;
3233
3360
  state.activeMutationDepth = Math.max(0, (state.activeMutationDepth ?? 0) - 1);
3234
- if (state.activeMutationDepth === 0)
3235
- state.activeMutationContext = undefined;
3361
+ // The owner drops its context; every frame then resyncs. Both the
3362
+ // hand-back to a still-live owner once the deeper frames unwind and
3363
+ // the empty slot while more than one frame is open are consequences of
3364
+ // the derivation, not separate rules each frame has to get right
3365
+ // (#2479 review rounds 2/3).
3366
+ if (isMutationContextOwner) {
3367
+ state.mutationContextOwner = undefined;
3368
+ }
3369
+ syncMutationContextSlot(state);
3236
3370
  }
3237
3371
  }
3238
3372
  // #1412 H1/H2: read-only sibling of runServerCommand for telemetry/identity
@@ -3365,7 +3499,7 @@ async function resolveCodeActionBestEffort(state, action) {
3365
3499
  // --- Client Factory ---
3366
3500
  export async function createLSPClient(options) {
3367
3501
  installCrashGuard();
3368
- const { serverId, process: lspProcess, root, sessionCwd, initialization, initializeTimeoutMs = INITIALIZE_TIMEOUT_MS, launchVariant, } = options;
3502
+ const { serverId, process: lspProcess, root, sessionCwd, initialization, initializeTimeoutMs = INITIALIZE_TIMEOUT_MS, launchVariant, onDiagnosticsPublished, } = options;
3369
3503
  // #449/#472: register this LSP child in the cross-process instance registry
3370
3504
  // as soon as we have a live pid — BEFORE `initialize` completes, not after.
3371
3505
  // Registering early means a child that dies/hangs during initialize (the
@@ -3538,7 +3672,7 @@ export async function createLSPClient(options) {
3538
3672
  return;
3539
3673
  void safeSendNotification(state.connection, "workspace/didChangeWatchedFiles", { changes });
3540
3674
  });
3541
- setupIncomingHandlers(state, initialization);
3675
+ setupIncomingHandlers(state, initialization, onDiagnosticsPublished);
3542
3676
  connection.listen();
3543
3677
  setupConnectionLifecycle(state, recentStderr);
3544
3678
  let initResult;
@@ -3694,6 +3828,8 @@ export async function createLSPClient(options) {
3694
3828
  checkAlive: () => checkProcessAlive(),
3695
3829
  /** #1277: cheap request round-trip proving the server still responds. */
3696
3830
  pingLiveness: (timeoutMs) => clientPingLiveness(state, timeoutMs),
3831
+ /** #2358: the OS pid of the live server process (see interface doc). */
3832
+ getProcessPid: () => lspProcess.pid,
3697
3833
  notify: {
3698
3834
  async open(filePath, content, languageId, preserveDiagnostics, silent) {
3699
3835
  return handleNotifyOpen(state, filePath, content, languageId, preserveDiagnostics, silent);
@@ -3842,6 +3978,9 @@ export async function createLSPClient(options) {
3842
3978
  isDocumentOpen(filePath) {
3843
3979
  return state.openDocuments.has(normalizeMapKey(filePath));
3844
3980
  },
3981
+ openDocumentPaths() {
3982
+ return [...state.openDocuments];
3983
+ },
3845
3984
  isBusy() {
3846
3985
  return (activeRequestsByConnection.get(connection) ?? 0) > 0;
3847
3986
  },