@herbertgao/pi-extensions 2026.8.4 → 2026.8.6

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/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
  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/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +53 -14
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
  14. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  15. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +18 -0
  16. package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
  17. package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
  18. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
  19. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +20 -4
  21. package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
  22. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
  25. package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
  26. package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
  27. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
  28. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
  29. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
  30. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
  31. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
  32. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  33. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
  36. package/node_modules/@narumitw/pi-btw/README.md +32 -24
  37. package/node_modules/@narumitw/pi-btw/package.json +6 -6
  38. package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
  39. package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
  40. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  41. package/node_modules/pi-lens/CHANGELOG.md +2535 -954
  42. package/node_modules/pi-lens/README.md +13 -0
  43. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
  44. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
  45. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
  46. package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
  47. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
  48. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
  49. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
  50. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
  51. package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
  52. package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
  53. package/node_modules/pi-lens/dist/clients/biome-client.js +136 -24
  54. package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
  55. package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
  56. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
  57. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  58. package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
  59. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
  60. package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
  61. package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
  62. package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
  63. package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
  64. package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
  65. package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
  66. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
  67. package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
  68. package/node_modules/pi-lens/dist/clients/console-guard-install.js +26 -0
  69. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
  70. package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
  71. package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
  72. package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
  73. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
  74. package/node_modules/pi-lens/dist/clients/dependency-checker.js +361 -77
  75. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
  76. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
  77. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
  78. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
  79. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
  80. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  81. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
  82. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
  83. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1172 -683
  84. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
  86. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
  87. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +47 -6
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +473 -42
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  114. package/node_modules/pi-lens/dist/clients/disposition-publish.js +18 -4
  115. package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
  116. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  117. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
  118. package/node_modules/pi-lens/dist/clients/extension-log.js +432 -0
  119. package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
  120. package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
  121. package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
  122. package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
  123. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  124. package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
  125. package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
  126. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
  127. package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
  128. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
  129. package/node_modules/pi-lens/dist/clients/git-guard.js +678 -11
  130. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
  131. package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
  132. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
  133. package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
  134. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
  135. package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
  136. package/node_modules/pi-lens/dist/clients/installer/index.js +395 -100
  137. package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
  138. package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
  139. package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
  140. package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
  141. package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
  142. package/node_modules/pi-lens/dist/clients/latency-logger.js +72 -1
  143. package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
  144. package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
  145. package/node_modules/pi-lens/dist/clients/lens-events.js +63 -9
  146. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  147. package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
  148. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
  149. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
  150. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  151. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
  152. package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
  153. package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
  154. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
  155. package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
  156. package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
  157. package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
  158. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  159. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  160. package/node_modules/pi-lens/dist/clients/lsp/server.js +437 -86
  161. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
  162. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  163. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
  164. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  165. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
  166. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
  167. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +127 -32
  168. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
  169. package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
  170. package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
  171. package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
  172. package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
  173. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  174. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
  175. package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
  176. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
  177. package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
  178. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
  179. package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
  180. package/node_modules/pi-lens/dist/clients/pipeline.js +76 -15
  181. package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
  182. package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
  183. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
  184. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
  185. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  186. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  187. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
  188. package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
  189. package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
  190. package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
  191. package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
  192. package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
  193. package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
  194. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
  195. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
  196. package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
  197. package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
  198. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
  199. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
  200. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
  201. package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
  202. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
  203. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
  204. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
  205. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
  206. package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +257 -77
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -117
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +342 -33
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
  217. package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
  218. package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
  219. package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
  220. package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
  221. package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -0
  222. package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
  223. package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
  224. package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
  225. package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
  226. package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
  227. package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
  228. package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
  229. package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
  230. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
  233. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
  234. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
  235. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  237. package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
  238. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
  239. package/node_modules/pi-lens/dist/clients/warm-attach.js +50 -13
  240. package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
  241. package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
  242. package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
  243. package/node_modules/pi-lens/dist/index.js +51785 -36118
  244. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
  245. package/node_modules/pi-lens/dist/mcp/server.js +219 -46
  246. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  247. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  248. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
  249. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  250. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
  251. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
  252. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
  253. package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
  254. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  255. package/node_modules/pi-lens/docs/agent-guide.md +40 -15
  256. package/node_modules/pi-lens/docs/agent-tools.md +21 -10
  257. package/node_modules/pi-lens/docs/analysisall.md +192 -0
  258. package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
  259. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +14 -3
  260. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
  261. package/node_modules/pi-lens/docs/environment-variables.md +44 -0
  262. package/node_modules/pi-lens/docs/fable.md +27 -0
  263. package/node_modules/pi-lens/docs/features.md +45 -3
  264. package/node_modules/pi-lens/docs/globalconfig.md +56 -1
  265. package/node_modules/pi-lens/docs/language-coverage.md +4 -3
  266. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
  267. package/node_modules/pi-lens/docs/mcp.md +37 -4
  268. package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
  269. package/node_modules/pi-lens/docs/settings.md +18 -1
  270. package/node_modules/pi-lens/docs/tools.md +4 -1
  271. package/node_modules/pi-lens/docs/usage.md +41 -5
  272. package/node_modules/pi-lens/docs/word-index.md +35 -0
  273. package/node_modules/pi-lens/package.json +10 -10
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  300. package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  301. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  302. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +134 -2
  303. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
  304. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  305. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
  306. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
  307. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  308. package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
  309. package/node_modules/pi-mcp-adapter/README.md +38 -4
  310. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  311. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
  312. package/node_modules/pi-mcp-adapter/cli.js +25 -4
  313. package/node_modules/pi-mcp-adapter/commands.ts +15 -7
  314. package/node_modules/pi-mcp-adapter/config.ts +4 -4
  315. package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
  316. package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
  317. package/node_modules/pi-mcp-adapter/index.ts +8 -3
  318. package/node_modules/pi-mcp-adapter/init.ts +29 -5
  319. package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
  320. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  321. package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
  322. package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
  323. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
  324. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  325. package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
  326. package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
  327. package/node_modules/pi-mcp-adapter/package.json +4 -3
  328. package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
  329. package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
  330. package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
  331. package/node_modules/pi-mcp-adapter/server-manager.ts +41 -7
  332. package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
  333. package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
  334. package/node_modules/pi-mcp-adapter/tool-metadata.ts +62 -4
  335. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
  336. package/node_modules/pi-mcp-adapter/types.ts +176 -27
  337. package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
  338. package/node_modules/pi-web-access/CHANGELOG.md +14 -0
  339. package/node_modules/pi-web-access/README.md +18 -12
  340. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  341. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  342. package/node_modules/pi-web-access/curator-page.ts +5 -3
  343. package/node_modules/pi-web-access/curator-server.ts +2 -1
  344. package/node_modules/pi-web-access/extract.ts +106 -34
  345. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  346. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  347. package/node_modules/pi-web-access/gemini-search.ts +18 -4
  348. package/node_modules/pi-web-access/index.ts +120 -48
  349. package/node_modules/pi-web-access/package.json +2 -2
  350. package/node_modules/pi-web-access/summary-review.ts +11 -5
  351. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  352. package/package.json +12 -13
  353. package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
  354. package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
  355. package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
  356. package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
  357. package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
  358. package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
  359. package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
  360. package/node_modules/pi-lens/dist/clients/types.js +0 -11
@@ -1,9 +1,13 @@
1
1
  import { randomBytes } from "node:crypto";
2
+ import * as fs from "node:fs";
2
3
  import * as path from "node:path";
4
+ import { logCascade } from "./cascade-logger.js";
3
5
  import { normalizeMapKey } from "./path-utils.js";
6
+ import { PathKeyedMap } from "./path-keyed-map.js";
4
7
  import { ReadGuard } from "./read-guard.js";
5
8
  import { RUNTIME_CONFIG } from "./runtime-config.js";
6
9
  import { TurnSummaryCollector } from "./turn-summary.js";
10
+ import { deriveProviderFromModelId } from "./model-provider.js";
7
11
  export class RuntimeCoordinator {
8
12
  _projectRoot = normalizeMapKey(process.cwd());
9
13
  _sessionGeneration = 0;
@@ -23,7 +27,9 @@ export class RuntimeCoordinator {
23
27
  coldSnapshotTouches: 0,
24
28
  };
25
29
  _complexityBaselines = new Map();
26
- _fixedThisTurn = new Set();
30
+ _fixedThisTurn = new PathKeyedMap(normalizeMapKey);
31
+ _writtenThisTurn = new PathKeyedMap(normalizeMapKey);
32
+ _autofixDemotedThisTurn = new PathKeyedMap(normalizeMapKey);
27
33
  _reportedThisTurn = new Set();
28
34
  _projectRulesScan = {
29
35
  rules: [],
@@ -33,6 +39,19 @@ export class RuntimeCoordinator {
33
39
  _lifecycleReason;
34
40
  _hasStableSessionId = false;
35
41
  _telemetryModel = "unknown";
42
+ // Raw model/provider identity, separate from the combined `provider/model`
43
+ // display string above — worklog/disposition attribution (#1448) wants the
44
+ // two fields apart, blank when the host never supplied them. `_telemetryProvider`
45
+ // is the explicit host value when given, else derived from the model id
46
+ // (deriveProviderFromModelId, blank on ambiguity — never guessed).
47
+ _telemetryModelId = "";
48
+ _telemetryProvider = "";
49
+ // True once a host has supplied an explicit provider this session. An
50
+ // explicit provider is never downgraded by a derivation from a later
51
+ // model-only call; a DERIVED provider, by contrast, is re-derived on
52
+ // every model-only call so a mid-session model switch (e.g. gpt-5-mini →
53
+ // claude-sonnet-4-5) doesn't leave a stale provider from the old model.
54
+ _telemetryProviderIsExplicit = false;
36
55
  _turnIndex = 0;
37
56
  _writeIndex = 0;
38
57
  _projectSeq = 0;
@@ -45,12 +64,13 @@ export class RuntimeCoordinator {
45
64
  _fileLastProjectSeq = new Map();
46
65
  _gitGuardHasBlockers = false;
47
66
  _gitGuardSummary = "";
67
+ _gitGuardCacheUnknownReason;
48
68
  callGraph = null;
49
69
  wordIndex = null;
50
70
  _readGuard = null;
51
- _pendingDeferredFormatFiles = new Map();
71
+ _pendingDeferredMutations = new PathKeyedMap(normalizeMapKey);
52
72
  _lspReadWarmState = new Map();
53
- _pendingInlineBlockers = new Map();
73
+ _pendingInlineBlockers = new PathKeyedMap(normalizeMapKey);
54
74
  _actionableWarningsThisTurn = new Map();
55
75
  _codeQualityWarningsThisTurn = new Map();
56
76
  // #484: opt-in per-RUN summary of diagnostics/autofixes/formats,
@@ -75,10 +95,15 @@ export class RuntimeCoordinator {
75
95
  coldSnapshotTouches: 0,
76
96
  };
77
97
  this._fixedThisTurn.clear();
98
+ this._writtenThisTurn.clear();
99
+ this._autofixDemotedThisTurn.clear();
78
100
  this._reportedThisTurn.clear();
79
101
  this._telemetrySessionId = `lens-${Date.now().toString(36)}-${randomBytes(4).toString("hex")}`;
80
102
  this._hasStableSessionId = false;
81
103
  this._telemetryModel = "unknown";
104
+ this._telemetryModelId = "";
105
+ this._telemetryProvider = "";
106
+ this._telemetryProviderIsExplicit = false;
82
107
  this._turnIndex = 0;
83
108
  this._writeIndex = 0;
84
109
  this._projectSeq = 0;
@@ -87,8 +112,9 @@ export class RuntimeCoordinator {
87
112
  this._fileLastProjectSeq.clear();
88
113
  this._gitGuardHasBlockers = false;
89
114
  this._gitGuardSummary = "";
115
+ this._gitGuardCacheUnknownReason = undefined;
90
116
  this._readGuard = null;
91
- this._pendingDeferredFormatFiles.clear();
117
+ this._pendingDeferredMutations.clear();
92
118
  this._lspReadWarmState.clear();
93
119
  this._pendingInlineBlockers.clear();
94
120
  this._actionableWarningsThisTurn.clear();
@@ -107,8 +133,12 @@ export class RuntimeCoordinator {
107
133
  this._cascadeSessionStats.coldSnapshotTouches += coldSnapshotTouches;
108
134
  }
109
135
  updateGitGuardStatus(hasBlockers, output) {
110
- this._gitGuardHasBlockers = hasBlockers;
111
- if (!hasBlockers) {
136
+ // The status is an aggregate over the current per-file map. A clean B
137
+ // result must not erase an unresolved A result; the pipeline records/clears
138
+ // the edited file immediately before this method runs.
139
+ this._gitGuardHasBlockers =
140
+ hasBlockers || this.getInlineBlockersSnapshot().length > 0;
141
+ if (!this._gitGuardHasBlockers) {
112
142
  this._gitGuardSummary = "";
113
143
  return;
114
144
  }
@@ -116,7 +146,10 @@ export class RuntimeCoordinator {
116
146
  .split("\n")
117
147
  .map((line) => line.trim())
118
148
  .find((line) => line.length > 0);
119
- this._gitGuardSummary = (firstLine ?? "Unresolved blockers detected").slice(0, 160);
149
+ const summaries = this.getInlineBlockersSnapshot()
150
+ .map((entry) => entry.summary.trim())
151
+ .filter(Boolean);
152
+ this._gitGuardSummary = (summaries[0] ?? firstLine ?? "Unresolved blockers detected").slice(0, 160);
120
153
  }
121
154
  get gitGuardHasBlockers() {
122
155
  return this._gitGuardHasBlockers;
@@ -124,14 +157,48 @@ export class RuntimeCoordinator {
124
157
  get gitGuardSummary() {
125
158
  return this._gitGuardSummary;
126
159
  }
160
+ markGitGuardCacheUnknown(reason) {
161
+ this._gitGuardCacheUnknownReason = reason;
162
+ }
163
+ clearGitGuardCacheUnknown() {
164
+ this._gitGuardCacheUnknownReason = undefined;
165
+ }
166
+ get gitGuardCacheUnknownReason() {
167
+ return this._gitGuardCacheUnknownReason;
168
+ }
127
169
  beginTurn() {
128
- this._cascadeRuns = [];
170
+ // #1443: runs sitting here at turn_start were appended AFTER the last
171
+ // turn_end drained them (consumeCascadeRuns) — the quiet-window reconcile's
172
+ // late re-injection (`onResolvedFound`, clients/lsp/cascade-tier.ts) lands
173
+ // in exactly that window. Wiping them dead-ended that delivery path: the
174
+ // finding was computed, formatted, appended, and then deleted before any
175
+ // turn_end could render it. Carry them into THIS turn instead, exactly
176
+ // once: `carriedTurns` is stamped on the way through and a run that the
177
+ // next turn_end still did not consume is dropped here with a log line
178
+ // rather than queued forever (a stale finding must not outlive the state
179
+ // it describes, and an unbounded queue would replay it every turn).
180
+ this._cascadeRuns = this._cascadeRuns.flatMap((run) => {
181
+ const carriedTurns = (run.carriedTurns ?? 0) + 1;
182
+ if (carriedTurns > 1) {
183
+ logCascade({
184
+ phase: "cascade_carry_over_drop",
185
+ filePath: run.filePath,
186
+ neighborCount: run.neighborCount,
187
+ diagnosticCount: run.diagnosticCount,
188
+ metadata: { carriedTurns, turnIndex: this._turnIndex },
189
+ });
190
+ return [];
191
+ }
192
+ return [{ ...run, carriedTurns }];
193
+ });
129
194
  // _pendingCascadeRuns is deliberately NOT cleared here: a cascade compute
130
195
  // still in flight past last turn_end's settle cap (fresh graph builds have
131
196
  // measured up to ~19s) must surface on the NEXT turn_end, not be dropped —
132
197
  // pre-#450 those findings were always awaited, never lost. Session reset
133
198
  // still clears it.
134
- this._pendingInlineBlockers.clear();
199
+ // Inline blockers are session-scoped per-file state. They are cleared only
200
+ // when that file is re-analyzed clean or the session resets; a new turn must
201
+ // not let a clean unrelated file erase an unresolved blocker.
135
202
  this._actionableWarningsThisTurn.clear();
136
203
  this._codeQualityWarningsThisTurn.clear();
137
204
  // _turnSummary is deliberately NOT cleared here (#484 rework): the
@@ -144,6 +211,25 @@ export class RuntimeCoordinator {
144
211
  this._turnIndex += 1;
145
212
  this._writeIndex = 0;
146
213
  this._reportedThisTurn.clear();
214
+ this._writtenThisTurn.clear();
215
+ this._autofixDemotedThisTurn.clear();
216
+ }
217
+ /** Atomically records write/edit ordering before debounce can coalesce it. */
218
+ recordMutationToolReceipt(filePath, toolName) {
219
+ if (toolName === "write") {
220
+ this._writtenThisTurn.set(filePath, true);
221
+ }
222
+ else if (this._writtenThisTurn.has(filePath)) {
223
+ this._autofixDemotedThisTurn.set(filePath, true);
224
+ // A later edit establishes a new final state that must be eligible for
225
+ // the deferred pass even if the preceding write was fixed immediately.
226
+ this._fixedThisTurn.delete(filePath);
227
+ }
228
+ return {
229
+ autofixMode: toolName === "edit" || this._autofixDemotedThisTurn.has(filePath)
230
+ ? "deferred"
231
+ : "immediate",
232
+ };
147
233
  }
148
234
  get reportedThisTurn() {
149
235
  return this._reportedThisTurn;
@@ -170,6 +256,23 @@ export class RuntimeCoordinator {
170
256
  else if (provider) {
171
257
  this._telemetryModel = provider;
172
258
  }
259
+ if (model)
260
+ this._telemetryModelId = model;
261
+ if (provider) {
262
+ this._telemetryProvider = provider;
263
+ this._telemetryProviderIsExplicit = true;
264
+ }
265
+ else if (model && !this._telemetryProviderIsExplicit) {
266
+ // No explicit provider has ever been reported this session, so the
267
+ // provider is (still) a derivation — re-derive it from the CURRENT
268
+ // model id every time. Without this, a stale derived provider from
269
+ // an earlier model would survive a mid-session model switch (e.g.
270
+ // gpt-5-mini → claude-sonnet-4-5 with no explicit provider on
271
+ // either call) because the old "has any provider ever been set"
272
+ // guard treated the derived value as sticky. An explicit provider,
273
+ // once set, is never touched here regardless of later model calls.
274
+ this._telemetryProvider = deriveProviderFromModelId(model);
275
+ }
173
276
  }
174
277
  get telemetrySessionId() {
175
278
  return this._telemetrySessionId;
@@ -198,6 +301,17 @@ export class RuntimeCoordinator {
198
301
  get telemetryModel() {
199
302
  return this._telemetryModel;
200
303
  }
304
+ /** Raw model id (never the combined `provider/model` display string), blank
305
+ * when the host hasn't reported one this session. Worklog/disposition
306
+ * attribution (#1448) reads this, not {@link telemetryModel}. */
307
+ get telemetryModelId() {
308
+ return this._telemetryModelId;
309
+ }
310
+ /** Explicit host-reported provider, or a conservative derivation from the
311
+ * model id (see clients/model-provider.ts), blank when neither is known. */
312
+ get telemetryProviderId() {
313
+ return this._telemetryProvider;
314
+ }
201
315
  get turnIndex() {
202
316
  return this._turnIndex;
203
317
  }
@@ -356,6 +470,26 @@ export class RuntimeCoordinator {
356
470
  this._cascadeRuns = [];
357
471
  return runs;
358
472
  }
473
+ /**
474
+ * R1 (#1443 follow-up): non-destructive peek used by turn_end's read-only
475
+ * fast path. A carried cascade run (or one still in flight) represents a
476
+ * DELIVERY OPPORTUNITY, not turn activity — an agent that answers a question
477
+ * without editing anything must still get yesterday's late finding. Before
478
+ * this, the files-empty early return skipped `settleCascadeRuns` /
479
+ * `consumeCascadeRuns` entirely on a read-only turn, so `beginTurn`'s next
480
+ * carry pass saw the run as having survived a turn_start with no offsetting
481
+ * drain and dropped it — burning the one-turn carry allowance on a turn that
482
+ * never had a chance to deliver.
483
+ */
484
+ hasCascadeRuns() {
485
+ // Carried, ALREADY-BUILT runs only. Pending (still-settling) computes are
486
+ // deliberately excluded: a read-only turn that fell through for a pending
487
+ // run would block on the full settle cap — every turn, forever, when the
488
+ // compute never resolves (re-review finding F1). A pending run loses
489
+ // nothing by waiting: settleCascadeRuns re-parks it and the next turn
490
+ // that actually settles it delivers it.
491
+ return this._cascadeRuns.length > 0;
492
+ }
359
493
  recordInlineBlockers(filePath, summary) {
360
494
  this._pendingInlineBlockers.set(path.resolve(filePath), {
361
495
  filePath,
@@ -365,8 +499,45 @@ export class RuntimeCoordinator {
365
499
  clearInlineBlockers(filePath) {
366
500
  this._pendingInlineBlockers.delete(path.resolve(filePath));
367
501
  }
502
+ reconcileInlineBlockers() {
503
+ // Rebuild, never delete-in-place: `PathKeyedMap.delete()` re-normalizes
504
+ // the key, and `normalizeMapKey` realpaths a live file but lowercases
505
+ // the tail of a deleted one — so on Windows a mixed-case filename gets
506
+ // a DIFFERENT delete-time key than its set-time key and the delete
507
+ // misses (#1245, verified live: `MyCase.ts` survived reconcile).
508
+ // Existence-checking the display path and rebuilding survivors avoids
509
+ // the key-mismatch entirely; live survivors re-set to identical keys
510
+ // (both realpath), so only the stale entries are dropped.
511
+ const survivors = [];
512
+ for (const [displayPath, value] of this._pendingInlineBlockers.entries()) {
513
+ if (fs.existsSync(displayPath))
514
+ survivors.push([displayPath, value]);
515
+ }
516
+ if (survivors.length !== this._pendingInlineBlockers.size) {
517
+ this._pendingInlineBlockers.clear();
518
+ for (const [displayPath, value] of survivors) {
519
+ this._pendingInlineBlockers.set(displayPath, value);
520
+ }
521
+ }
522
+ }
523
+ /**
524
+ * Stale-entry reconcile (#1245): a blocker recorded for a file that has
525
+ * since been deleted can never be cleared — `clearInlineBlockers` fires
526
+ * only on a LATER dispatch of the same path, which a deleted file never
527
+ * gets. Every read of the blocker map (turn_end injection, git-guard
528
+ * size/summary, `syncGitGuardRecord`) therefore drops entries whose file no
529
+ * longer exists on disk: a blocker for a deleted file is stale by
530
+ * definition (the agent cannot fix it), so it must not re-surface every
531
+ * turn or gate a commit. The map is tiny (per-turn blockers) and reads are
532
+ * bounded (once per turn_end / tool_result), so the probe cost is
533
+ * negligible.
534
+ */
535
+ getInlineBlockersSnapshot() {
536
+ this.reconcileInlineBlockers();
537
+ return [...this._pendingInlineBlockers.values()];
538
+ }
368
539
  consumeInlineBlockers() {
369
- const entries = [...this._pendingInlineBlockers.values()];
540
+ const entries = this.getInlineBlockersSnapshot();
370
541
  this._pendingInlineBlockers.clear();
371
542
  return entries;
372
543
  }
@@ -403,7 +574,19 @@ export class RuntimeCoordinator {
403
574
  return this._complexityBaselines;
404
575
  }
405
576
  get fixedThisTurn() {
406
- return this._fixedThisTurn;
577
+ // Self-referencing local so chained add() returns the same facade
578
+ // instead of re-entering this getter and allocating a new one per call
579
+ // (sonar S7725).
580
+ const facade = {
581
+ add: (filePath) => {
582
+ this._fixedThisTurn.set(filePath, true);
583
+ return facade;
584
+ },
585
+ has: (filePath) => this._fixedThisTurn.has(filePath),
586
+ delete: (filePath) => this._fixedThisTurn.delete(filePath),
587
+ clear: () => this._fixedThisTurn.clear(),
588
+ };
589
+ return facade;
407
590
  }
408
591
  get projectRulesScan() {
409
592
  return this._projectRulesScan;
@@ -416,39 +599,47 @@ export class RuntimeCoordinator {
416
599
  return this._readGuard;
417
600
  }
418
601
  /**
419
- * Queue `filePath` for deferred formatting at `agent_end`. Returns `true`
420
- * when this call created a NEW pending entry, `false` when it re-touched
421
- * an already-queued file. #673: the caller uses this to publish
422
- * `pilens:format:queued` only on first queue entry, so repeated edits to
423
- * the same already-queued file before `agent_end` don't spam the bus.
602
+ * Queue one mutation kind for `filePath` at `agent_end`. Returns `true`
603
+ * when this call created a pending entry or added a new kind, and `false`
604
+ * for a same-kind re-touch. Callers publish each kind's first transition
605
+ * without spamming repeated edits before `agent_end`.
424
606
  */
425
- deferFormat(filePath, cwd, toolName, turnStateCwd, ownerSessionId) {
607
+ deferMutation(filePath, cwd, toolName, turnStateCwd, kind, ownerSessionId) {
426
608
  const key = path.resolve(filePath);
427
609
  const now = Date.now();
428
- const existing = this._pendingDeferredFormatFiles.get(key);
610
+ const existing = this._pendingDeferredMutations.get(key);
429
611
  if (existing) {
612
+ const addedKind = !existing.kinds.has(kind);
430
613
  existing.lastTouchedAt = now;
431
614
  existing.cwd = cwd;
432
615
  existing.turnStateCwd = turnStateCwd;
433
616
  existing.toolNames.add(toolName);
617
+ existing.kinds.add(kind);
434
618
  existing.queuedTurnIndex = this._turnIndex;
435
619
  existing.ownerSessionId = ownerSessionId;
436
- return false;
620
+ return addedKind;
437
621
  }
438
- this._pendingDeferredFormatFiles.set(key, {
622
+ this._pendingDeferredMutations.set(key, {
439
623
  filePath: key,
440
624
  cwd,
441
625
  turnStateCwd,
442
626
  firstTouchedAt: now,
443
627
  lastTouchedAt: now,
444
628
  toolNames: new Set([toolName]),
629
+ kinds: new Set([kind]),
445
630
  queuedTurnIndex: this._turnIndex,
446
631
  ownerSessionId,
447
632
  });
448
633
  return true;
449
634
  }
635
+ deferFormat(filePath, cwd, toolName, turnStateCwd, ownerSessionId) {
636
+ return this.deferMutation(filePath, cwd, toolName, turnStateCwd, "format", ownerSessionId);
637
+ }
450
638
  get pendingDeferredFormatCount() {
451
- return this._pendingDeferredFormatFiles.size;
639
+ return this._pendingDeferredMutations.size;
640
+ }
641
+ get pendingDeferredMutationCount() {
642
+ return this._pendingDeferredMutations.size;
452
643
  }
453
644
  /**
454
645
  * Legacy unconditional drain — still exposed for any caller that
@@ -456,8 +647,8 @@ export class RuntimeCoordinator {
456
647
  * flush call sites should prefer {@link claimDeferredFormatFiles}.
457
648
  */
458
649
  consumeDeferredFormatFiles() {
459
- const records = [...this._pendingDeferredFormatFiles.values()];
460
- this._pendingDeferredFormatFiles.clear();
650
+ const records = [...this._pendingDeferredMutations.values()];
651
+ this._pendingDeferredMutations.clear();
461
652
  return records;
462
653
  }
463
654
  /**
@@ -481,25 +672,50 @@ export class RuntimeCoordinator {
481
672
  const claimed = [];
482
673
  const staleClaimed = [];
483
674
  const deferredToOwner = [];
484
- for (const [key, record] of this._pendingDeferredFormatFiles) {
675
+ for (const [key, record] of this._pendingDeferredMutations) {
485
676
  const sameSession = record.ownerSessionId === undefined ||
486
677
  currentSessionId === undefined ||
487
678
  record.ownerSessionId === currentSessionId;
488
679
  if (sameSession) {
489
680
  claimed.push(record);
490
- this._pendingDeferredFormatFiles.delete(key);
681
+ this._pendingDeferredMutations.delete(key);
491
682
  continue;
492
683
  }
493
684
  const age = now - record.lastTouchedAt;
494
685
  if (age > staleAfterMs) {
495
686
  staleClaimed.push(record);
496
- this._pendingDeferredFormatFiles.delete(key);
687
+ this._pendingDeferredMutations.delete(key);
497
688
  continue;
498
689
  }
499
690
  deferredToOwner.push(record);
500
691
  }
501
692
  return { claimed, staleClaimed, deferredToOwner };
502
693
  }
694
+ /** Return claimed records that were never started by an aborted drain. */
695
+ requeueDeferredFormatFiles(records) {
696
+ for (const record of records) {
697
+ const key = path.resolve(record.filePath);
698
+ const existing = this._pendingDeferredMutations.get(key);
699
+ if (existing) {
700
+ for (const kind of record.kinds)
701
+ existing.kinds.add(kind);
702
+ for (const toolName of record.toolNames)
703
+ existing.toolNames.add(toolName);
704
+ continue;
705
+ }
706
+ this._pendingDeferredMutations.set(key, {
707
+ ...record,
708
+ kinds: new Set(record.kinds),
709
+ toolNames: new Set(record.toolNames),
710
+ });
711
+ }
712
+ }
713
+ claimDeferredMutations(currentSessionId, now, staleAfterMs) {
714
+ return this.claimDeferredFormatFiles(currentSessionId, now, staleAfterMs);
715
+ }
716
+ requeueDeferredMutations(records) {
717
+ this.requeueDeferredFormatFiles(records);
718
+ }
503
719
  shouldWarmLspOnRead(filePath, maxAgeMs = 120_000) {
504
720
  const state = this._lspReadWarmState.get(path.resolve(filePath));
505
721
  if (!state)