@herbertgao/pi-extensions 2026.8.4 → 2026.8.5

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 (288) hide show
  1. package/README.md +4 -4
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +10 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +10 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +5 -8
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
  13. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  14. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
  15. package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
  16. package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
  17. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
  18. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
  19. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +15 -2
  20. package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
  21. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
  22. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
  23. package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
  24. package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
  25. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
  26. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  27. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
  28. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
  29. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
  30. package/node_modules/@narumitw/pi-btw/README.md +8 -7
  31. package/node_modules/@narumitw/pi-btw/package.json +2 -2
  32. package/node_modules/@narumitw/pi-btw/src/menu.ts +1 -0
  33. package/node_modules/pi-lens/CHANGELOG.md +2440 -954
  34. package/node_modules/pi-lens/README.md +13 -0
  35. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
  36. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
  37. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
  38. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
  39. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
  40. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
  41. package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
  42. package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
  43. package/node_modules/pi-lens/dist/clients/biome-client.js +29 -25
  44. package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
  45. package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
  46. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
  47. package/node_modules/pi-lens/dist/clients/bus-publish.js +10 -3
  48. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
  49. package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
  50. package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
  51. package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
  52. package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
  53. package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
  54. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
  55. package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
  56. package/node_modules/pi-lens/dist/clients/console-guard-install.js +14 -0
  57. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
  58. package/node_modules/pi-lens/dist/clients/dead-code-client.js +2 -1
  59. package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
  60. package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
  61. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
  62. package/node_modules/pi-lens/dist/clients/dependency-checker.js +347 -75
  63. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +61 -11
  64. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  65. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +63 -20
  66. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
  67. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
  68. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  69. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
  70. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
  71. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1085 -681
  72. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
  73. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
  74. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
  75. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
  76. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  77. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
  78. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
  79. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  80. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
  81. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
  82. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
  83. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +14 -6
  84. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
  85. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
  86. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
  87. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +277 -26
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  99. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  100. package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
  101. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
  102. package/node_modules/pi-lens/dist/clients/extension-log.js +139 -0
  103. package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
  104. package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
  105. package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
  106. package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
  107. package/node_modules/pi-lens/dist/clients/format-events-publish.js +16 -3
  108. package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
  109. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
  110. package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
  111. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
  112. package/node_modules/pi-lens/dist/clients/git-guard.js +681 -11
  113. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
  114. package/node_modules/pi-lens/dist/clients/go-client.js +2 -1
  115. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +8 -0
  116. package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
  117. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
  118. package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
  119. package/node_modules/pi-lens/dist/clients/installer/index.js +390 -99
  120. package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
  121. package/node_modules/pi-lens/dist/clients/jscpd-client.js +31 -54
  122. package/node_modules/pi-lens/dist/clients/knip-client.js +15 -36
  123. package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
  124. package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
  125. package/node_modules/pi-lens/dist/clients/latency-logger.js +23 -1
  126. package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
  127. package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
  128. package/node_modules/pi-lens/dist/clients/lens-events.js +27 -4
  129. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  130. package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
  131. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +30 -0
  132. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
  133. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +1 -1
  134. package/node_modules/pi-lens/dist/clients/lsp/client.js +493 -48
  135. package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
  136. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +86 -0
  137. package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
  138. package/node_modules/pi-lens/dist/clients/lsp/index.js +1807 -780
  139. package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
  140. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  141. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  142. package/node_modules/pi-lens/dist/clients/lsp/server.js +196 -32
  143. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  144. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
  145. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
  146. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +123 -32
  147. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
  148. package/node_modules/pi-lens/dist/clients/mcp/session.js +216 -8
  149. package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
  150. package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
  151. package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
  152. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
  153. package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
  154. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
  155. package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
  156. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
  157. package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
  158. package/node_modules/pi-lens/dist/clients/pipeline.js +47 -13
  159. package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
  160. package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
  161. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
  162. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  163. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
  164. package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
  165. package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
  166. package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
  167. package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
  168. package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
  169. package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
  170. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
  171. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
  172. package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
  173. package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
  174. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
  175. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
  176. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
  177. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
  178. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
  179. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
  180. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
  181. package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
  182. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +107 -73
  183. package/node_modules/pi-lens/dist/clients/runtime-context.js +36 -12
  184. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +74 -6
  185. package/node_modules/pi-lens/dist/clients/runtime-session.js +405 -117
  186. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
  187. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +218 -23
  188. package/node_modules/pi-lens/dist/clients/runtime-turn.js +237 -64
  189. package/node_modules/pi-lens/dist/clients/rust-client.js +2 -1
  190. package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
  191. package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -1
  192. package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
  193. package/node_modules/pi-lens/dist/clients/sg-runner.js +284 -174
  194. package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
  195. package/node_modules/pi-lens/dist/clients/smells-rollup.js +224 -0
  196. package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
  197. package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
  198. package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
  199. package/node_modules/pi-lens/dist/clients/startup-timing.js +1 -1
  200. package/node_modules/pi-lens/dist/clients/test-runner-client.js +38 -8
  201. package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
  202. package/node_modules/pi-lens/dist/clients/tool-policy.js +388 -39
  203. package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
  204. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
  205. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
  206. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
  207. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
  208. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
  209. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  210. package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
  211. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
  212. package/node_modules/pi-lens/dist/clients/warm-attach.js +33 -13
  213. package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
  214. package/node_modules/pi-lens/dist/clients/word-index.js +391 -64
  215. package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
  216. package/node_modules/pi-lens/dist/index.js +49361 -36888
  217. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
  218. package/node_modules/pi-lens/dist/mcp/server.js +211 -44
  219. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
  220. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +200 -36
  221. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +153 -40
  222. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
  223. package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
  224. package/node_modules/pi-lens/docs/agent-guide.md +2 -2
  225. package/node_modules/pi-lens/docs/agent-tools.md +21 -10
  226. package/node_modules/pi-lens/docs/analysisall.md +192 -0
  227. package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
  228. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -2
  229. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
  230. package/node_modules/pi-lens/docs/environment-variables.md +44 -0
  231. package/node_modules/pi-lens/docs/fable.md +27 -0
  232. package/node_modules/pi-lens/docs/features.md +31 -2
  233. package/node_modules/pi-lens/docs/globalconfig.md +45 -1
  234. package/node_modules/pi-lens/docs/language-coverage.md +4 -3
  235. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
  236. package/node_modules/pi-lens/docs/mcp.md +37 -4
  237. package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
  238. package/node_modules/pi-lens/docs/settings.md +12 -1
  239. package/node_modules/pi-lens/docs/tools.md +4 -1
  240. package/node_modules/pi-lens/docs/usage.md +18 -0
  241. package/node_modules/pi-lens/package.json +10 -10
  242. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  243. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  244. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  245. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  246. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  247. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  248. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  249. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  250. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  251. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  252. package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  253. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  254. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +55 -2
  255. package/node_modules/pi-mcp-adapter/CHANGELOG.md +25 -0
  256. package/node_modules/pi-mcp-adapter/README.md +34 -3
  257. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
  258. package/node_modules/pi-mcp-adapter/commands.ts +15 -7
  259. package/node_modules/pi-mcp-adapter/direct-tools.ts +54 -7
  260. package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
  261. package/node_modules/pi-mcp-adapter/index.ts +8 -3
  262. package/node_modules/pi-mcp-adapter/init.ts +29 -5
  263. package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
  264. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  265. package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
  266. package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
  267. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
  268. package/node_modules/pi-mcp-adapter/metadata-cache.ts +27 -4
  269. package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
  270. package/node_modules/pi-mcp-adapter/package.json +3 -3
  271. package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
  272. package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
  273. package/node_modules/pi-mcp-adapter/server-manager.ts +36 -7
  274. package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
  275. package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
  276. package/node_modules/pi-mcp-adapter/tool-metadata.ts +42 -4
  277. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
  278. package/node_modules/pi-mcp-adapter/types.ts +93 -27
  279. package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
  280. package/package.json +11 -12
  281. package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
  282. package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
  283. package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
  284. package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
  285. package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
  286. package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
  287. package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
  288. package/node_modules/pi-lens/dist/clients/types.js +0 -11
@@ -4,8 +4,11 @@ import { applyConservativeActionableWarningFixes, checkActionableWarningsReportF
4
4
  import { publishFilesTouched } from "./bus-publish.js";
5
5
  import { publishAutofixStart, publishFormatStart, } from "./format-events-publish.js";
6
6
  import { logLatency } from "./latency-logger.js";
7
+ import { isPathIgnoredByProject } from "./file-utils.js";
8
+ import { newLspMutationCorrelationId } from "./lsp-mutation.js";
7
9
  import { getGlobalActionableWarningMaxFixes, } from "./lens-config.js";
8
10
  import { resyncLspFile, runFormatPhase } from "./pipeline.js";
11
+ import { getAmbientAbortSignal } from "./safe-spawn.js";
9
12
  import { appendProjectChange, } from "./project-changes.js";
10
13
  /**
11
14
  * A queued file is claimed by any flush once it has sat unclaimed by its own
@@ -16,6 +19,7 @@ import { appendProjectChange, } from "./project-changes.js";
16
19
  * extra staleness for guaranteed eventual formatting. (#791)
17
20
  */
18
21
  export const DEFERRED_FORMAT_STALE_AFTER_MS = 10 * 60_000;
22
+ const DEFERRED_FORMAT_CONCURRENCY = 3;
19
23
  function recordProjectChange(args) {
20
24
  const bump = args.runtime.bumpFileSeq;
21
25
  if (!bump)
@@ -138,43 +142,63 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
138
142
  return !disabled;
139
143
  });
140
144
  if (formatRecords.length > 0) {
141
- // Run all formatter subprocesses concurrently — no shared state touched here.
142
- // bumpFileSeq / cacheManager mutations happen in the sequential pass below.
143
- const outcomes = await Promise.all(formatRecords.map(async (record) => {
144
- const fileStart = Date.now();
145
- const filePath = path.resolve(record.filePath);
146
- if (!nodeFs.existsSync(filePath)) {
147
- dbg(`agent_end deferred_format skipped missing file: ${filePath}`);
148
- return { kind: "skipped", filePath, reason: "missing" };
149
- }
150
- try {
151
- const result = await runFormatPhase(filePath, getFormatService, dbg);
152
- return { kind: "done", record, filePath, result, fileStart };
153
- }
154
- catch (err) {
155
- const message = err instanceof Error ? err.message : String(err);
156
- dbg(`agent_end deferred_format failed for ${filePath}: ${message}`);
157
- return { kind: "failed", filePath, message, fileStart };
145
+ const work = new Array(formatRecords.length);
146
+ const started = new Set();
147
+ let nextIndex = 0;
148
+ const ambientSignal = getAmbientAbortSignal();
149
+ const worker = async () => {
150
+ while (nextIndex < formatRecords.length) {
151
+ const index = nextIndex++;
152
+ if (ambientSignal?.aborted)
153
+ return;
154
+ const record = formatRecords[index];
155
+ const fileStart = Date.now();
156
+ const filePath = path.resolve(record.filePath);
157
+ started.add(index);
158
+ if (!nodeFs.existsSync(filePath)) {
159
+ work[index] = { record, filePath, fileStart, missing: true };
160
+ continue;
161
+ }
162
+ try {
163
+ work[index] = {
164
+ record,
165
+ filePath,
166
+ fileStart,
167
+ result: await runFormatPhase(filePath, getFormatService, dbg),
168
+ };
169
+ }
170
+ catch (err) {
171
+ work[index] = {
172
+ record,
173
+ filePath,
174
+ fileStart,
175
+ error: err instanceof Error ? err.message : String(err),
176
+ };
177
+ }
158
178
  }
159
- }));
160
- // Process results sequentially bumpFileSeq and cacheManager mutations
161
- // must stay ordered to avoid sequence number races.
162
- for (const outcome of outcomes) {
163
- if (outcome.kind === "skipped") {
164
- summary.skipped.push({
165
- filePath: outcome.filePath,
166
- reason: outcome.reason,
167
- });
179
+ };
180
+ await Promise.all(Array.from({ length: Math.min(DEFERRED_FORMAT_CONCURRENCY, formatRecords.length) }, () => worker()));
181
+ if (ambientSignal?.aborted) {
182
+ runtime.requeueDeferredFormatFiles(formatRecords.filter((_, index) => !started.has(index)));
183
+ }
184
+ for (const entry of work) {
185
+ if (!entry)
186
+ continue;
187
+ await new Promise((resolve) => setImmediate(resolve));
188
+ const { record, filePath, fileStart } = entry;
189
+ if (entry.missing) {
190
+ dbg(`agent_end deferred_format skipped missing file: ${filePath}`);
191
+ summary.skipped.push({ filePath, reason: "missing" });
168
192
  continue;
169
193
  }
170
- if (outcome.kind === "failed") {
171
- summary.failed.push({
172
- filePath: outcome.filePath,
173
- errors: [outcome.message],
174
- });
194
+ if (entry.error) {
195
+ dbg(`agent_end deferred_format failed for ${filePath}: ${entry.error}`);
196
+ summary.failed.push({ filePath, errors: [entry.error] });
175
197
  continue;
176
198
  }
177
- const { record, filePath, result, fileStart } = outcome;
199
+ const result = entry.result;
200
+ if (!result)
201
+ continue;
178
202
  summary.formatted++;
179
203
  if (result.formatFailures.length > 0) {
180
204
  summary.failed.push({ filePath, errors: result.formatFailures });
@@ -202,7 +226,7 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
202
226
  const content = nodeFs.readFileSync(filePath, "utf-8");
203
227
  const lineCount = content.split("\n").length;
204
228
  const hasImports = /^import\s/m.test(content);
205
- cacheManager.addModifiedRange(filePath, { start: 1, end: lineCount }, hasImports, bookkeepingCwd);
229
+ cacheManager.addModifiedRange(filePath, { start: 1, end: lineCount }, hasImports, bookkeepingCwd, currentSessionId ?? runtime.telemetrySessionId, "pi");
206
230
  }
207
231
  catch (err) {
208
232
  dbg(`agent_end deferred_format modified-range tracking failed for ${filePath}: ${err}`);
@@ -251,6 +275,14 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
251
275
  }
252
276
  else {
253
277
  const enabledFiles = actionReport.data.files.filter((file) => {
278
+ // #1247: the per-edit dispatch surface gates ignored files before
279
+ // running; the turn-end autofix must apply the same filter or a
280
+ // `.pi-lens.json` `ignore` entry cannot protect a prose file
281
+ // (CHANGELOG.md / AGENTS.md) from being rewritten here.
282
+ if (isPathIgnoredByProject(file.filePath, ctxCwd ?? runtime.projectRoot)) {
283
+ dbg(`agent_end actionable_warnings_autofix: skipped ${file.filePath} (ignored by project)`);
284
+ return false;
285
+ }
254
286
  const enabled = !!getFlag("lens-actionable-warning-autofix", file.filePath);
255
287
  if (!enabled) {
256
288
  const source = getFlagSource?.("lens-actionable-warning-autofix", file.filePath);
@@ -281,49 +313,42 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
281
313
  if (!freshness.fresh) {
282
314
  dbg(`agent_end actionable_warnings_autofix: stale report (${freshness.reason}; reportProjectSeqEnd=${freshness.reportProjectSeqEnd ?? "missing"}; currentProjectSeq=${freshness.currentProjectSeq}${freshness.filePath ? `; file=${freshness.filePath}; reportFileSeq=${freshness.reportFileSeq}; currentFileSeq=${freshness.currentFileSeq}` : ""}), skipping fixes`);
283
315
  }
284
- else {
316
+ else if (eligibleReport.summary.autoFixEligible > 0) {
285
317
  // #684: same "genuine work about to happen" gate as
286
318
  // publishFormatStart — only fires when the report is fresh AND
287
319
  // has at least one autofix-eligible warning, right before
288
320
  // applyConservativeActionableWarningFixes actually starts.
289
- if (eligibleReport.summary.autoFixEligible > 0) {
290
- publishAutofixStart({
291
- cwd: ctxCwd ?? runtime.projectRoot,
292
- paths: eligibleReport.files
293
- .filter((file) => file.warnings.some((warning) => !warning.suppressed &&
294
- warning.actions.some((action) => action.autoFixEligible)))
295
- .map((file) => file.filePath),
296
- eligibleCount: eligibleReport.summary.autoFixEligible,
297
- dbg,
298
- });
299
- }
321
+ publishAutofixStart({
322
+ cwd: ctxCwd ?? runtime.projectRoot,
323
+ paths: eligibleReport.files
324
+ .filter((file) => file.warnings.some((warning) => !warning.suppressed &&
325
+ warning.actions.some((action) => action.autoFixEligible)))
326
+ .map((file) => file.filePath),
327
+ eligibleCount: eligibleReport.summary.autoFixEligible,
328
+ dbg,
329
+ });
300
330
  const fixStart = Date.now();
331
+ const fixCwd = ctxCwd ?? runtime.projectRoot;
332
+ const mutationContext = {
333
+ cwd: fixCwd,
334
+ correlationId: newLspMutationCorrelationId(),
335
+ tool: "lsp-quickfix",
336
+ source: "autofix",
337
+ runtime,
338
+ cacheManager,
339
+ readGuard: getFlag("no-read-guard") ? undefined : runtime.readGuard,
340
+ recordAutofix: getFlag("lens-turn-summary")
341
+ ? (filePath) => runtime.turnSummary.recordAutofix(filePath, { tool: "lsp-quickfix" })
342
+ : undefined,
343
+ dbg,
344
+ };
301
345
  const fixSummary = await applyConservativeActionableWarningFixes({
302
- cwd: ctxCwd ?? runtime.projectRoot,
346
+ cwd: fixCwd,
303
347
  report: eligibleReport,
304
348
  maxFixes: getGlobalActionableWarningMaxFixes(),
305
349
  dbg,
350
+ mutationContext,
306
351
  });
307
- for (const changedFile of fixSummary.changedFiles) {
308
- if (!nodeFs.existsSync(changedFile))
309
- continue;
310
- recordProjectChange({
311
- runtime,
312
- cwd: ctxCwd ?? runtime.projectRoot,
313
- filePath: changedFile,
314
- source: "autofix",
315
- dbg,
316
- });
317
- if (!getFlag("no-read-guard"))
318
- runtime.readGuard.recordWritten(changedFile);
319
- try {
320
- const content = nodeFs.readFileSync(changedFile, "utf-8");
321
- cacheManager.addModifiedRange(changedFile, { start: 1, end: content.split("\n").length }, /^import\s/m.test(content), ctxCwd ?? runtime.projectRoot);
322
- }
323
- catch (err) {
324
- dbg(`agent_end actionable warning changed-file tracking failed for ${changedFile}: ${err}`);
325
- }
326
- }
327
352
  if (fixSummary.changedFiles.length > 0) {
328
353
  publishFilesTouched({
329
354
  reason: "autofix",
@@ -336,13 +361,6 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
336
361
  kind: "autofix",
337
362
  })),
338
363
  });
339
- if (getFlag("lens-turn-summary")) {
340
- for (const changedFile of fixSummary.changedFiles) {
341
- runtime.turnSummary.recordAutofix(changedFile, {
342
- tool: "lsp-quickfix",
343
- });
344
- }
345
- }
346
364
  }
347
365
  logLatency({
348
366
  type: "phase",
@@ -361,6 +379,9 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
361
379
  notify(`pi-lens applied ${fixSummary.applied} conservative LSP warning quickfix(es)`, "info");
362
380
  }
363
381
  }
382
+ else {
383
+ dbg("agent_end actionable_warnings_autofix: fresh report, 0 autofix-eligible warnings, skipping");
384
+ }
364
385
  }
365
386
  }
366
387
  logLatency({
@@ -377,6 +398,19 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
377
398
  skipped: summary.skipped.length,
378
399
  },
379
400
  });
401
+ logLatency({
402
+ type: "phase",
403
+ toolName: "agent_end",
404
+ filePath: ctxCwd ?? runtime.projectRoot,
405
+ phase: "agent_end_deferred_format_done",
406
+ durationMs: Date.now() - startedAt,
407
+ metadata: {
408
+ formatted: summary.formatted,
409
+ changed: summary.changed.length,
410
+ failed: summary.failed.length,
411
+ skipped: summary.skipped.length,
412
+ },
413
+ });
380
414
  dbg(`agent_end deferred_format complete: formatted=${summary.formatted} changed=${summary.changed.length} failed=${summary.failed.length} skipped=${summary.skipped.length}`);
381
415
  if (summary.failed.length > 0) {
382
416
  notify(`pi-lens deferred format: ${summary.changed.length} changed, ${summary.failed.length} failed`, "warning");
@@ -1,30 +1,54 @@
1
+ // Exported so the Stop-hook bin strips exactly what these bridges prepend.
2
+ export const AUTOMATION_FRAMING = "[pi-lens automated check — not a user request] ";
3
+ function turnEndMessage(content) {
4
+ return {
5
+ role: "user",
6
+ content: `${AUTOMATION_FRAMING}Address 🔴 blockers before continuing; ℹ️ advisories are informational only.\n\n${content}`,
7
+ };
8
+ }
9
+ /** Read a turn-end finding without changing its durable delivery state. */
10
+ export function peekTurnEndFindings(cacheManager, cwd) {
11
+ const findings = cacheManager.readCache("turn-end-findings", cwd);
12
+ if (!findings?.data?.content || findings.data.consumed === true)
13
+ return;
14
+ return { messages: [turnEndMessage(findings.data.content)] };
15
+ }
1
16
  export function consumeTurnEndFindings(cacheManager, cwd) {
2
17
  const findings = cacheManager.readCache("turn-end-findings", cwd);
18
+ if (!findings?.data?.content || findings.data.consumed === true)
19
+ return;
20
+ // A blocker record is also the opt-in commit gate's durable state. Mark the
21
+ // context message consumed without deleting the record; clean/advisory-only
22
+ // records retain the historical consume-and-clear behavior.
23
+ if (findings.data.hasBlockers === true &&
24
+ typeof findings.data.sessionId === "string") {
25
+ cacheManager.writeCache("turn-end-findings", { ...findings.data, consumed: true }, cwd);
26
+ }
27
+ else {
28
+ cacheManager.clearCache("turn-end-findings", cwd);
29
+ }
30
+ return { messages: [turnEndMessage(findings.data.content)] };
31
+ }
32
+ /** Read test findings without consuming them; used by acknowledged IPC delivery. */
33
+ export function peekTestFindings(cacheManager, cwd) {
34
+ const findings = cacheManager.readCache("test-runner-findings", cwd);
3
35
  if (!findings?.data?.content)
4
36
  return;
5
- cacheManager.writeCache("turn-end-findings", null, cwd);
6
37
  return {
7
38
  messages: [
8
39
  {
9
40
  role: "user",
10
- content: `[pi-lens automated check not a user request] Address 🔴 blockers before continuing; ℹ️ advisories are informational only.\n\n${findings.data.content}`,
41
+ content: `${AUTOMATION_FRAMING}Test failures detected last turn fix before continuing:\n\n${findings.data.content}`,
11
42
  },
12
43
  ],
13
44
  };
14
45
  }
15
46
  export function consumeTestFindings(cacheManager, cwd) {
16
- const findings = cacheManager.readCache("test-runner-findings", cwd);
17
- if (!findings?.data?.content)
47
+ const findings = peekTestFindings(cacheManager, cwd);
48
+ if (!findings)
18
49
  return;
19
50
  cacheManager.writeCache("test-runner-findings", null, cwd);
20
- return {
21
- messages: [
22
- {
23
- role: "user",
24
- content: `[pi-lens automated check — not a user request] Test failures detected last turn — fix before continuing:\n\n${findings.data.content}`,
25
- },
26
- ],
27
- };
51
+ return findings;
28
52
  }
29
53
  export function consumeSessionStartGuidance(cacheManager, cwd) {
30
54
  const guidance = cacheManager.readCache("session-start-guidance", cwd);
@@ -1,6 +1,8 @@
1
1
  import { randomBytes } from "node:crypto";
2
+ import * as fs from "node:fs";
2
3
  import * as path from "node:path";
3
4
  import { normalizeMapKey } from "./path-utils.js";
5
+ import { PathKeyedMap } from "./path-keyed-map.js";
4
6
  import { ReadGuard } from "./read-guard.js";
5
7
  import { RUNTIME_CONFIG } from "./runtime-config.js";
6
8
  import { TurnSummaryCollector } from "./turn-summary.js";
@@ -45,12 +47,13 @@ export class RuntimeCoordinator {
45
47
  _fileLastProjectSeq = new Map();
46
48
  _gitGuardHasBlockers = false;
47
49
  _gitGuardSummary = "";
50
+ _gitGuardCacheUnknownReason;
48
51
  callGraph = null;
49
52
  wordIndex = null;
50
53
  _readGuard = null;
51
54
  _pendingDeferredFormatFiles = new Map();
52
55
  _lspReadWarmState = new Map();
53
- _pendingInlineBlockers = new Map();
56
+ _pendingInlineBlockers = new PathKeyedMap(normalizeMapKey);
54
57
  _actionableWarningsThisTurn = new Map();
55
58
  _codeQualityWarningsThisTurn = new Map();
56
59
  // #484: opt-in per-RUN summary of diagnostics/autofixes/formats,
@@ -87,6 +90,7 @@ export class RuntimeCoordinator {
87
90
  this._fileLastProjectSeq.clear();
88
91
  this._gitGuardHasBlockers = false;
89
92
  this._gitGuardSummary = "";
93
+ this._gitGuardCacheUnknownReason = undefined;
90
94
  this._readGuard = null;
91
95
  this._pendingDeferredFormatFiles.clear();
92
96
  this._lspReadWarmState.clear();
@@ -107,8 +111,12 @@ export class RuntimeCoordinator {
107
111
  this._cascadeSessionStats.coldSnapshotTouches += coldSnapshotTouches;
108
112
  }
109
113
  updateGitGuardStatus(hasBlockers, output) {
110
- this._gitGuardHasBlockers = hasBlockers;
111
- if (!hasBlockers) {
114
+ // The status is an aggregate over the current per-file map. A clean B
115
+ // result must not erase an unresolved A result; the pipeline records/clears
116
+ // the edited file immediately before this method runs.
117
+ this._gitGuardHasBlockers =
118
+ hasBlockers || this.getInlineBlockersSnapshot().length > 0;
119
+ if (!this._gitGuardHasBlockers) {
112
120
  this._gitGuardSummary = "";
113
121
  return;
114
122
  }
@@ -116,7 +124,10 @@ export class RuntimeCoordinator {
116
124
  .split("\n")
117
125
  .map((line) => line.trim())
118
126
  .find((line) => line.length > 0);
119
- this._gitGuardSummary = (firstLine ?? "Unresolved blockers detected").slice(0, 160);
127
+ const summaries = this.getInlineBlockersSnapshot()
128
+ .map((entry) => entry.summary.trim())
129
+ .filter(Boolean);
130
+ this._gitGuardSummary = (summaries[0] ?? firstLine ?? "Unresolved blockers detected").slice(0, 160);
120
131
  }
121
132
  get gitGuardHasBlockers() {
122
133
  return this._gitGuardHasBlockers;
@@ -124,6 +135,15 @@ export class RuntimeCoordinator {
124
135
  get gitGuardSummary() {
125
136
  return this._gitGuardSummary;
126
137
  }
138
+ markGitGuardCacheUnknown(reason) {
139
+ this._gitGuardCacheUnknownReason = reason;
140
+ }
141
+ clearGitGuardCacheUnknown() {
142
+ this._gitGuardCacheUnknownReason = undefined;
143
+ }
144
+ get gitGuardCacheUnknownReason() {
145
+ return this._gitGuardCacheUnknownReason;
146
+ }
127
147
  beginTurn() {
128
148
  this._cascadeRuns = [];
129
149
  // _pendingCascadeRuns is deliberately NOT cleared here: a cascade compute
@@ -131,7 +151,9 @@ export class RuntimeCoordinator {
131
151
  // measured up to ~19s) must surface on the NEXT turn_end, not be dropped —
132
152
  // pre-#450 those findings were always awaited, never lost. Session reset
133
153
  // still clears it.
134
- this._pendingInlineBlockers.clear();
154
+ // Inline blockers are session-scoped per-file state. They are cleared only
155
+ // when that file is re-analyzed clean or the session resets; a new turn must
156
+ // not let a clean unrelated file erase an unresolved blocker.
135
157
  this._actionableWarningsThisTurn.clear();
136
158
  this._codeQualityWarningsThisTurn.clear();
137
159
  // _turnSummary is deliberately NOT cleared here (#484 rework): the
@@ -365,8 +387,45 @@ export class RuntimeCoordinator {
365
387
  clearInlineBlockers(filePath) {
366
388
  this._pendingInlineBlockers.delete(path.resolve(filePath));
367
389
  }
390
+ reconcileInlineBlockers() {
391
+ // Rebuild, never delete-in-place: `PathKeyedMap.delete()` re-normalizes
392
+ // the key, and `normalizeMapKey` realpaths a live file but lowercases
393
+ // the tail of a deleted one — so on Windows a mixed-case filename gets
394
+ // a DIFFERENT delete-time key than its set-time key and the delete
395
+ // misses (#1245, verified live: `MyCase.ts` survived reconcile).
396
+ // Existence-checking the display path and rebuilding survivors avoids
397
+ // the key-mismatch entirely; live survivors re-set to identical keys
398
+ // (both realpath), so only the stale entries are dropped.
399
+ const survivors = [];
400
+ for (const [displayPath, value] of this._pendingInlineBlockers.entries()) {
401
+ if (fs.existsSync(displayPath))
402
+ survivors.push([displayPath, value]);
403
+ }
404
+ if (survivors.length !== this._pendingInlineBlockers.size) {
405
+ this._pendingInlineBlockers.clear();
406
+ for (const [displayPath, value] of survivors) {
407
+ this._pendingInlineBlockers.set(displayPath, value);
408
+ }
409
+ }
410
+ }
411
+ /**
412
+ * Stale-entry reconcile (#1245): a blocker recorded for a file that has
413
+ * since been deleted can never be cleared — `clearInlineBlockers` fires
414
+ * only on a LATER dispatch of the same path, which a deleted file never
415
+ * gets. Every read of the blocker map (turn_end injection, git-guard
416
+ * size/summary, `syncGitGuardRecord`) therefore drops entries whose file no
417
+ * longer exists on disk: a blocker for a deleted file is stale by
418
+ * definition (the agent cannot fix it), so it must not re-surface every
419
+ * turn or gate a commit. The map is tiny (per-turn blockers) and reads are
420
+ * bounded (once per turn_end / tool_result), so the probe cost is
421
+ * negligible.
422
+ */
423
+ getInlineBlockersSnapshot() {
424
+ this.reconcileInlineBlockers();
425
+ return [...this._pendingInlineBlockers.values()];
426
+ }
368
427
  consumeInlineBlockers() {
369
- const entries = [...this._pendingInlineBlockers.values()];
428
+ const entries = this.getInlineBlockersSnapshot();
370
429
  this._pendingInlineBlockers.clear();
371
430
  return entries;
372
431
  }
@@ -500,6 +559,15 @@ export class RuntimeCoordinator {
500
559
  }
501
560
  return { claimed, staleClaimed, deferredToOwner };
502
561
  }
562
+ /** Return claimed records that were never started by an aborted drain. */
563
+ requeueDeferredFormatFiles(records) {
564
+ for (const record of records) {
565
+ const key = path.resolve(record.filePath);
566
+ if (!this._pendingDeferredFormatFiles.has(key)) {
567
+ this._pendingDeferredFormatFiles.set(key, record);
568
+ }
569
+ }
570
+ }
503
571
  shouldWarmLspOnRead(filePath, maxAgeMs = 120_000) {
504
572
  const state = this._lspReadWarmState.get(path.resolve(filePath));
505
573
  if (!state)