@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
@@ -2,10 +2,12 @@ import * as path from "node:path";
2
2
  import { appendActionableWarningsHistory, buildActionableWarningsReport, formatActionableWarningsAdvisory, writeActionableWarningsReport, } from "./actionable-warnings.js";
3
3
  import { logActionableWarningsEvent } from "./actionable-warnings-logger.js";
4
4
  import { appendCodeQualityWarningsHistory, buildCodeQualityWarningsReport, formatCodeQualityWarningsAdvisory, writeCodeQualityWarningsReport, } from "./code-quality-warnings.js";
5
+ import { clearGitGuardTestFailure, mergeGitGuardTestFailure, writeGitGuardRecord, } from "./git-guard.js";
5
6
  import { logCascade } from "./cascade-logger.js";
6
7
  import { normalizeMapKey } from "./path-utils.js";
7
8
  import { resolveRunnerPath, toRunnerDisplayPath, } from "./dispatch/runner-context.js";
8
9
  import { getKnipIgnorePatterns } from "./file-utils.js";
10
+ import { isTestRoleCollateral } from "./collateral-test-role.js";
9
11
  import { dedupeSecretFindings, fromAstGrepWarnings, fromGitleaks, fromTrivySecrets, isSecretWarning, secretLocationKey, } from "./secret-findings.js";
10
12
  import { formatDeadCodeAdvisory } from "./dead-code-client.js";
11
13
  import { PROJECT_DIAGNOSTICS_CACHE_VERSION, writeProjectDiagnosticsDeltaReport, } from "./project-diagnostics/cache.js";
@@ -86,7 +88,7 @@ function capTurnEndMessage(content) {
86
88
  return out;
87
89
  }
88
90
  export async function handleTurnEnd(deps) {
89
- const { ctxCwd, getFlag, dbg, runtime, cacheManager, knipClient, deadCodeClients, depChecker, testRunnerClient, resetLSPService, resetFormatService, } = deps;
91
+ const { ctxCwd, getFlag, dbg, runtime, cacheManager, knipClient, deadCodeClients, depChecker, testRunnerClient, owner, resetLSPService, resetFormatService, } = deps;
90
92
  // #449 slice 1: piggyback the instance-registry heartbeat on this existing
91
93
  // per-turn touchpoint rather than adding a new timer/interval. Cheap (reads
92
94
  // process.memoryUsage().rss, one read-modify-write of instances.json) and
@@ -106,16 +108,40 @@ export async function handleTurnEnd(deps) {
106
108
  });
107
109
  const cwd = ctxCwd ?? process.cwd();
108
110
  let turnState = cacheManager.readTurnState(cwd);
109
- // Evict turn state written by a previous session — it carries stale file
110
- // ranges that no longer reflect the current editing context.
111
- if (turnState.sessionId &&
112
- turnState.sessionId !== runtime.telemetrySessionId) {
113
- dbg(`turn_end: evicting stale turn state (session ${turnState.sessionId} ≠ current ${runtime.telemetrySessionId})`);
114
- cacheManager.clearTurnState(cwd);
111
+ // A live foreign writer owns this worklist. Do not clear or consume another
112
+ // pi/MCP session's files; a dead/aged owner is safely evicted instead.
113
+ const currentOwner = owner ?? {
114
+ kind: "pi",
115
+ id: runtime.telemetrySessionId,
116
+ pid: process.pid,
117
+ lastSeen: new Date().toISOString(),
118
+ };
119
+ const access = cacheManager.getTurnStateAccess(cwd, currentOwner);
120
+ const sameProcessPiSessionHandoff = access === "foreign-live" &&
121
+ currentOwner.kind === "pi" &&
122
+ turnState.owner?.kind === "pi" &&
123
+ turnState.owner.pid === process.pid &&
124
+ turnState.owner.id !== currentOwner.id;
125
+ if (access === "foreign-live" && !sameProcessPiSessionHandoff) {
126
+ dbg(`turn_end: foreign live owner retained (${turnState.owner?.kind ?? "legacy"}:${turnState.owner?.id ?? turnState.sessionId})`);
127
+ return;
128
+ }
129
+ if (access === "available" && (turnState.files || turnState.owner || turnState.sessionId)) {
130
+ dbg("turn_end: evicting stale turn-state owner");
131
+ cacheManager.clearTurnState(cwd, currentOwner);
115
132
  turnState = cacheManager.readTurnState(cwd);
116
133
  }
117
134
  const files = Object.keys(turnState.files);
118
135
  if (files.length === 0) {
136
+ // A genuinely clean session must invalidate the persisted guard record.
137
+ // Blocker records are retained only while the runtime still reports one.
138
+ if (getFlag("lens-guard") && !runtime.gitGuardHasBlockers) {
139
+ const guardRecord = cacheManager.readCache("turn-end-findings", cwd)?.data;
140
+ if (guardRecord?.sessionId === runtime.telemetrySessionId &&
141
+ guardRecord.testFailures !== true) {
142
+ cacheManager.clearCache("turn-end-findings", cwd);
143
+ }
144
+ }
119
145
  // #713: subagent sessions use a shorter idle reset (60s) — a short-lived
120
146
  // task agent holding a warm fleet for 4 minutes after its last turn is
121
147
  // pure waste under fan-out. Classify ONCE here so every tick in this call
@@ -143,7 +169,7 @@ export async function handleTurnEnd(deps) {
143
169
  dbg(`turn_end: ${files.length} file(s) modified, cycles: ${turnState.turnCycles}/${turnState.maxCycles}`);
144
170
  if (cacheManager.isMaxCyclesExceeded(cwd)) {
145
171
  dbg("turn_end: max cycles exceeded, clearing state and forcing through");
146
- cacheManager.clearTurnState(cwd);
172
+ cacheManager.clearTurnState(cwd, currentOwner);
147
173
  runtime.fixedThisTurn.clear();
148
174
  resetFormatService();
149
175
  return;
@@ -155,7 +181,7 @@ export async function handleTurnEnd(deps) {
155
181
  const projectDiagnosticsSources = new Set();
156
182
  // Re-surface inline blockers from this turn that the agent didn't fix.
157
183
  // These were shown inline during write/edit but the agent moved on without resolving them.
158
- const unresolvedBlockers = runtime.consumeInlineBlockers();
184
+ const unresolvedBlockers = runtime.getInlineBlockersSnapshot();
159
185
  for (const { filePath: bPath, summary } of unresolvedBlockers) {
160
186
  const displayPath = toRunnerDisplayPath(cwd, bPath);
161
187
  blockerParts.push(`Unresolved from this turn — ${displayPath}:\n${summary}`);
@@ -180,7 +206,14 @@ export async function handleTurnEnd(deps) {
180
206
  // 2. Neighbor-level: each neighbor is claimed by the latest cascade result
181
207
  // that covers it — suppresses stale neighbor state from earlier writes.
182
208
  const t0 = Date.now();
183
- const cascadeRuns = runtime.consumeCascadeRuns();
209
+ const cascadeRuns = runtime.consumeCascadeRuns().filter((run) => {
210
+ const originSeq = run.origin?.projectSeq;
211
+ const originTurn = run.origin?.turnSeq;
212
+ // A deferred result from before a later write/turn is not current state.
213
+ // Old test fixtures without provenance remain accepted for compatibility.
214
+ return ((originSeq === undefined || originSeq === runtime.projectSeq) &&
215
+ (originTurn === undefined || originTurn === runtime.turnIndex));
216
+ });
184
217
  const cascadeResults = cascadeRuns.flatMap((r) => r.result ? [r.result] : []);
185
218
  if (cascadeResults.length > 0) {
186
219
  const seen = new Map();
@@ -249,32 +282,62 @@ export async function handleTurnEnd(deps) {
249
282
  // (over-correction guard).
250
283
  const indeterminateRuns = cascadeRuns.filter((r) => r.indeterminate);
251
284
  if (indeterminateRuns.length > 0) {
252
- const byDetail = new Map();
253
- for (const r of indeterminateRuns) {
254
- const detail = r.indeterminate?.detail ??
255
- (r.indeterminate?.reason === "missing_node"
256
- ? "changed file not in the review graph"
257
- : "review graph unavailable");
258
- const files = byDetail.get(detail) ?? [];
259
- files.push(toRunnerDisplayPath(cwd, r.filePath));
260
- byDetail.set(detail, files);
261
- }
262
- const lines = [];
263
- for (const [detail, filesRaw] of byDetail) {
264
- const files = [...new Set(filesRaw)];
265
- const shown = files.slice(0, 5).join(", ");
266
- const more = files.length > 5 ? ` (+${files.length - 5} more)` : "";
267
- lines.push(` â€ĸ ${detail}: ${shown}${more}`);
268
- }
269
- const fileCount = new Set(indeterminateRuns.map((r) => normalizeMapKey(r.filePath))).size;
270
- const reasons = [...byDetail.keys()].join("; ");
285
+ // #1104 (review P3 on PR #1143, rides with the resultId main body): this
286
+ // preamble used to hardcode a graph-unavailability frame for EVERY
287
+ // indeterminate reason. That's accurate for `graph_degraded`/
288
+ // `missing_node`/`error` (the graph really couldn't produce a dependent
289
+ // set), but `lsp_binding_rejected` is a DIFFERENT failure shape — the
290
+ // graph WAS available and dependents WERE derived; only their LSP
291
+ // diagnostics display was withheld because a fallback snapshot's content
292
+ // binding didn't match current disk. Saying "the review graph was
293
+ // unavailable" for that case mis-attributes the cause. Bucket by reason
294
+ // family so each gets its own accurate frame.
295
+ const buildAdvisory = (runs, frame) => {
296
+ if (runs.length === 0)
297
+ return undefined;
298
+ const byDetail = new Map();
299
+ for (const r of runs) {
300
+ const detail = r.indeterminate?.detail ?? frame.fallbackDetail(r);
301
+ const files = byDetail.get(detail) ?? [];
302
+ files.push(toRunnerDisplayPath(cwd, r.filePath));
303
+ byDetail.set(detail, files);
304
+ }
305
+ const lines = [];
306
+ for (const [detail, filesRaw] of byDetail) {
307
+ const files = [...new Set(filesRaw)];
308
+ const shown = files.slice(0, 5).join(", ");
309
+ const more = files.length > 5 ? ` (+${files.length - 5} more)` : "";
310
+ lines.push(` â€ĸ ${detail}: ${shown}${more}`);
311
+ }
312
+ const fileCount = new Set(runs.map((r) => normalizeMapKey(r.filePath))).size;
313
+ const reasons = [...byDetail.keys()].join("; ");
314
+ return `${frame.lead(fileCount, reasons)}\n${lines.join("\n")}`;
315
+ };
316
+ const graphRuns = indeterminateRuns.filter((r) => r.indeterminate?.reason !== "lsp_binding_rejected");
317
+ const bindingRuns = indeterminateRuns.filter((r) => r.indeterminate?.reason === "lsp_binding_rejected");
271
318
  // Factual/informational phrasing — the advisory tier wraps this with an
272
319
  // "â„šī¸ Advisory — no action required this turn:" label, so an imperative
273
320
  // ("review dependents manually") would contradict it. The #533 substance
274
321
  // stays: a clean cascade result does NOT cover these files' dependents.
275
- advisoryParts.push(`Cascade could not compute downstream impact for ${fileCount} edited file(s) this turn — ` +
276
- `the review graph was unavailable (${reasons}), so their dependents were not ` +
277
- `cascade-checked and a clean cascade result does not cover them.\n${lines.join("\n")}`);
322
+ const graphAdvisory = buildAdvisory(graphRuns, {
323
+ lead: (fileCount, reasons) => `Cascade could not compute downstream impact for ${fileCount} edited file(s) this turn — ` +
324
+ `the review graph was unavailable (${reasons}), so their dependents were not ` +
325
+ `cascade-checked and a clean cascade result does not cover them.`,
326
+ fallbackDetail: (r) => r.indeterminate?.reason === "missing_node"
327
+ ? "changed file not in the review graph"
328
+ : "review graph unavailable",
329
+ });
330
+ if (graphAdvisory)
331
+ advisoryParts.push(graphAdvisory);
332
+ const bindingAdvisory = buildAdvisory(bindingRuns, {
333
+ lead: (fileCount, reasons) => `Cascade identified dependents for ${fileCount} edited file(s) this turn, but their ` +
334
+ `diagnostics could not be freshly confirmed (${reasons}) and were withheld — a clean ` +
335
+ `cascade result does not cover them.`,
336
+ fallbackDetail: () => "cascade diagnostics withheld (binding rejected)",
337
+ });
338
+ if (bindingAdvisory)
339
+ advisoryParts.push(bindingAdvisory);
340
+ const fileCount = new Set(indeterminateRuns.map((r) => normalizeMapKey(r.filePath))).size;
278
341
  logCascade({
279
342
  phase: "cascade_indeterminate",
280
343
  filePath: files[0] ?? cwd,
@@ -516,6 +579,7 @@ export async function handleTurnEnd(deps) {
516
579
  advisoryParts.push(report);
517
580
  }
518
581
  const t3 = Date.now();
582
+ let madgeStats;
519
583
  if (await depChecker.ensureAvailable()) {
520
584
  const madgeFiles = cacheManager.getFilesForMadge(cwd);
521
585
  if (madgeFiles.length > 0) {
@@ -524,7 +588,9 @@ export async function handleTurnEnd(deps) {
524
588
  // the shared circular-dep state update is deferred/folded inside
525
589
  // checkFilesBatch so concurrent spawns can't clobber each other (#766).
526
590
  const absFiles = madgeFiles.map((file) => path.resolve(cwd, file));
527
- const depResults = await depChecker.checkFilesBatch(absFiles, cwd);
591
+ const batch = await depChecker.checkFilesBatch(absFiles, cwd);
592
+ const depResults = batch.results;
593
+ madgeStats = batch.stats;
528
594
  for (const file of madgeFiles) {
529
595
  const absPath = path.resolve(cwd, file);
530
596
  const depResult = depResults.get(absPath);
@@ -550,6 +616,7 @@ export async function handleTurnEnd(deps) {
550
616
  filePath: cwd,
551
617
  phase: "madge",
552
618
  durationMs: Date.now() - t3,
619
+ ...(madgeStats && { metadata: madgeStats }),
553
620
  });
554
621
  // --- Test runner: fire once per turn after all edits are done ---
555
622
  // Runs for each unique test target across modified files; results appear
@@ -600,6 +667,7 @@ export async function handleTurnEnd(deps) {
600
667
  if (targets.length > 0) {
601
668
  dbg(`turn_end: firing ${targets.length} test target(s) async (non-blocking)`);
602
669
  const firedAtTurn = runtime.turnIndex;
670
+ const firedSessionId = runtime.telemetrySessionId;
603
671
  Promise.allSettled(targets.map((t) => testRunnerClient.runTestFileAsync(t.testFile, cwd, t.runner, t.config)))
604
672
  .then((results) => {
605
673
  // #628: the turn advancing while tests ran no longer means the
@@ -633,9 +701,20 @@ export async function handleTurnEnd(deps) {
633
701
  ? `[from a prior turn — the edit that triggered this run had already been superseded by the time results came back]\n\n${failures.join("\n\n")}`
634
702
  : failures.join("\n\n");
635
703
  cacheManager.writeCache("test-runner-findings", { content, stale, results: resultValues }, cwd);
704
+ if (getFlag("lens-guard") && firedSessionId === runtime.telemetrySessionId) {
705
+ clearGitGuardTestFailure(cacheManager, cwd, runtime, resultValues
706
+ .filter((value) => value.failed === 0)
707
+ .map((value) => value.file));
708
+ mergeGitGuardTestFailure(cacheManager, cwd, runtime, content, resultValues
709
+ .filter((value) => value.failed > 0)
710
+ .map((value) => value.file));
711
+ }
636
712
  dbg(`turn_end: ${failures.length} test failure(s) cached for next context injection${stale ? " (stale — turn advanced while tests ran)" : ""}`);
637
713
  }
638
714
  else if (results.length > 0) {
715
+ if (getFlag("lens-guard") && firedSessionId === runtime.telemetrySessionId) {
716
+ clearGitGuardTestFailure(cacheManager, cwd, runtime, resultValues.map((value) => value.file));
717
+ }
639
718
  dbg(`turn_end: all tests passed${stale ? " (stale — turn advanced while tests ran)" : ""}`);
640
719
  }
641
720
  })
@@ -659,37 +738,68 @@ export async function handleTurnEnd(deps) {
659
738
  // would drop the call-graph entries — so lens_diagnostics (which only reads the
660
739
  // persisted report) would never surface the findings (#179/#533).
661
740
  if (runtime.callGraph && files.length > 0) {
662
- try {
663
- const { impact, formatImpact } = await import("./call-graph.js");
664
- const { callGraphImpactToProjectDiagnostics } = await import("./project-diagnostics/runner-adapters/call-graph-impact.js");
665
- const impactLines = [];
666
- const impactFindings = [];
667
- for (const filePath of files.slice(0, 5)) {
668
- // Find callee keys for this file in the call graph
669
- const fileCallerKeys = [...runtime.callGraph.callers.keys()].filter((k) => k.startsWith(`${filePath}:`));
670
- for (const calleeKey of fileCallerKeys.slice(0, 3)) {
671
- const results = impact(runtime.callGraph, calleeKey);
672
- if (results.length > 0) {
673
- impactFindings.push({ calleeKey, results });
674
- const summary = formatImpact(results, cwd);
675
- if (summary)
676
- impactLines.push(` ${calleeKey.split(":").pop()}: ${summary}`);
741
+ const coverage = runtime.callGraph.coverage;
742
+ if (!coverage || coverage.complete !== true) {
743
+ // An incomplete graph can still contain useful edges, but emitting them
744
+ // as ordinary impact findings would turn unsupported/partial extraction
745
+ // into an authoritative-looking clean result for the rest of the file.
746
+ // Keep the limitation visible and require a complete graph for this
747
+ // user-facing impact surface (#1070).
748
+ advisoryParts.push("Call-graph impact was not emitted because call-graph extraction coverage is incomplete; " +
749
+ "the affected files may have unreported callers.");
750
+ }
751
+ else {
752
+ try {
753
+ const { impact, formatImpact, parseSymbolKey } = await import("./call-graph.js");
754
+ const { callGraphImpactToProjectDiagnostics } = await import("./project-diagnostics/runner-adapters/call-graph-impact.js");
755
+ const impactLines = [];
756
+ const impactFindings = [];
757
+ for (const filePath of files.slice(0, 5)) {
758
+ // Turn-state files may be cwd-relative while graph keys are absolute,
759
+ // and persisted graphs can contain either slash style/casing. Compare
760
+ // through the shared normalized path seam; keep the original filePath
761
+ // only for display and diagnostics.
762
+ const changedFileKey = normalizeMapKey(resolveRunnerPath(cwd, filePath));
763
+ const fileCallerKeys = [...runtime.callGraph.callers.keys()].filter((k) => {
764
+ const graphFilePath = parseSymbolKey(k).filePath;
765
+ return normalizeMapKey(resolveRunnerPath(cwd, graphFilePath)) === changedFileKey;
766
+ });
767
+ for (const calleeKey of fileCallerKeys.slice(0, 3)) {
768
+ // #1080: drop KNOWN test-role callers BEFORE both the human advisory
769
+ // (formatImpact below) and the persisted delta (impactFindings →
770
+ // callGraphImpactToProjectDiagnostics) — the advisory is rendered
771
+ // first, so the filter must reach the shared `results` set that feeds
772
+ // both. A test caller supplied by an old/fixture/expanded graph must
773
+ // appear in neither surface. Fail-open: an unparseable/unclassifiable
774
+ // key is retained (the adapter re-applies the same predicate).
775
+ const results = impact(runtime.callGraph, calleeKey).filter((r) => {
776
+ const callerFile = parseSymbolKey(r.symbolKey).filePath;
777
+ return (!callerFile ||
778
+ !isTestRoleCollateral(resolveRunnerPath(cwd, callerFile)));
779
+ });
780
+ if (results.length > 0) {
781
+ impactFindings.push({ calleeKey, results });
782
+ const summary = formatImpact(results, cwd);
783
+ if (summary)
784
+ impactLines.push(` ${parseSymbolKey(calleeKey).symbolName ?? calleeKey}: ${summary}`);
785
+ }
677
786
  }
678
787
  }
679
- }
680
- if (impactLines.length > 0) {
681
- advisoryParts.push(`📊 Call-graph impact (changed symbols have callers):\n${impactLines.join("\n")}`);
682
- }
683
- if (impactFindings.length > 0) {
684
- const impactDiagnostics = callGraphImpactToProjectDiagnostics(cwd, impactFindings);
685
- if (impactDiagnostics.length > 0) {
686
- projectDiagnosticsDelta.push(...impactDiagnostics);
687
- projectDiagnosticsSources.add("call-graph");
788
+ if (impactLines.length > 0) {
789
+ advisoryParts.push(`📊 Call-graph impact (changed symbols have callers):\n${impactLines.join("\n")}`);
688
790
  }
791
+ if (impactFindings.length > 0) {
792
+ const impactDiagnostics = callGraphImpactToProjectDiagnostics(cwd, impactFindings);
793
+ if (impactDiagnostics.length > 0) {
794
+ projectDiagnosticsDelta.push(...impactDiagnostics);
795
+ projectDiagnosticsSources.add("call-graph");
796
+ }
797
+ }
798
+ // Non-fatal — call graph is best-effort
799
+ }
800
+ catch {
801
+ // Non-fatal — call graph is best-effort
689
802
  }
690
- }
691
- catch {
692
- // Non-fatal — call graph is best-effort
693
803
  }
694
804
  }
695
805
  if (projectDiagnosticsDelta.length > 0) {
@@ -838,7 +948,7 @@ export async function handleTurnEnd(deps) {
838
948
  catch (err) {
839
949
  dbg(`turn_end: dead-code advisory failed: ${err}`);
840
950
  }
841
- cacheManager.incrementTurnCycle(cwd);
951
+ cacheManager.incrementTurnCycle(cwd, currentOwner);
842
952
  const labeledAdvisoryParts = advisoryParts.map((p) => `â„šī¸ Advisory — no action required this turn:\n${p}`);
843
953
  const findingParts = [...blockerParts, ...labeledAdvisoryParts];
844
954
  if (findingParts.length > 0) {
@@ -852,12 +962,68 @@ export async function handleTurnEnd(deps) {
852
962
  if (last?.data?.signature === signature &&
853
963
  last?.data?.sessionId === runtime.telemetrySessionId) {
854
964
  dbg("turn_end: duplicate findings detected (same session), suppressing re-prompt");
855
- cacheManager.clearTurnState(cwd);
965
+ if (getFlag("lens-guard")) {
966
+ const existingGuard = cacheManager.readCache("turn-end-findings", cwd)?.data;
967
+ if (existingGuard) {
968
+ writeGitGuardRecord(cacheManager, runtime, cwd, {
969
+ ...existingGuard,
970
+ content,
971
+ blockerContent: blockerParts.length > 0
972
+ ? capTurnEndMessage(blockerParts.join("\n\n"))
973
+ : undefined,
974
+ hasBlockers: blockerParts.length > 0 || existingGuard.testFailures === true,
975
+ blockingFiles: blockerParts.length > 0 ? existingGuard.affectedFiles : undefined,
976
+ projectSeqStart: runtime.turnStartProjectSeq,
977
+ projectSeqEnd: runtime.projectSeq,
978
+ fileSeqByPath: Object.fromEntries(runtime.getFileSeqEntries().map(([filePath, seq]) => [normalizeMapKey(path.resolve(filePath)), seq])),
979
+ fileContentHashes: {},
980
+ consumed: false,
981
+ });
982
+ }
983
+ }
984
+ cacheManager.clearTurnState(cwd, currentOwner);
856
985
  runtime.fixedThisTurn.clear();
857
986
  resetFormatService();
858
987
  return;
859
988
  }
860
- cacheManager.writeCache("turn-end-findings", { content }, cwd);
989
+ const fileSeqByPath = {};
990
+ for (const [filePath, seq] of runtime.getFileSeqEntries()) {
991
+ fileSeqByPath[normalizeMapKey(path.resolve(filePath))] = seq;
992
+ }
993
+ if (getFlag("lens-guard")) {
994
+ const existingGuard = cacheManager.readCache("turn-end-findings", cwd)?.data;
995
+ const blockingContent = blockerParts.length > 0
996
+ ? capTurnEndMessage(blockerParts.join("\n\n"))
997
+ : undefined;
998
+ const affectedFiles = [
999
+ ...(existingGuard?.affectedFiles ?? []),
1000
+ ...files.map((file) => resolveRunnerPath(cwd, file)),
1001
+ ...cascadeResults.flatMap((result) => result.neighbors
1002
+ .filter((neighbor) => neighbor.diagnostics.length > 0)
1003
+ .map((neighbor) => resolveRunnerPath(cwd, neighbor.filePath))),
1004
+ ];
1005
+ writeGitGuardRecord(cacheManager, runtime, cwd, {
1006
+ content: [content, existingGuard?.testFailureContent]
1007
+ .filter((value) => !!value)
1008
+ .join("\n\n"),
1009
+ blockerContent: blockingContent,
1010
+ blockingFiles: blockerParts.length > 0 ? affectedFiles : undefined,
1011
+ hasBlockers: !!blockingContent || existingGuard?.testFailures === true,
1012
+ affectedFiles,
1013
+ sessionId: runtime.telemetrySessionId,
1014
+ projectSeqStart: runtime.turnStartProjectSeq,
1015
+ projectSeqEnd: runtime.projectSeq,
1016
+ fileSeqByPath,
1017
+ fileContentHashes: {},
1018
+ consumed: false,
1019
+ testFailures: existingGuard?.testFailures,
1020
+ testFailureContent: existingGuard?.testFailureContent,
1021
+ testFailureFiles: existingGuard?.testFailureFiles,
1022
+ });
1023
+ }
1024
+ else {
1025
+ cacheManager.writeCache("turn-end-findings", { content }, cwd);
1026
+ }
861
1027
  cacheManager.writeCache("turn-end-findings-last", {
862
1028
  signature,
863
1029
  sessionId: runtime.telemetrySessionId,
@@ -875,7 +1041,14 @@ export async function handleTurnEnd(deps) {
875
1041
  });
876
1042
  }
877
1043
  if (blockerParts.length === 0) {
878
- cacheManager.clearTurnState(cwd);
1044
+ cacheManager.clearTurnState(cwd, currentOwner);
1045
+ if (getFlag("lens-guard") && advisoryParts.length === 0 && !runtime.gitGuardHasBlockers) {
1046
+ const guardRecord = cacheManager.readCache("turn-end-findings", cwd)?.data;
1047
+ if (guardRecord?.sessionId === runtime.telemetrySessionId &&
1048
+ guardRecord.testFailures !== true) {
1049
+ cacheManager.clearCache("turn-end-findings", cwd);
1050
+ }
1051
+ }
879
1052
  }
880
1053
  runtime.fixedThisTurn.clear();
881
1054
  runtime.clearActionableWarnings();
@@ -6,6 +6,7 @@
6
6
  * Requires: cargo (rustup)
7
7
  * Docs: https://doc.rust-lang.org/cargo/
8
8
  */
9
+ import { createSubsystemLogger } from "./extension-log.js";
9
10
  import * as fs from "node:fs";
10
11
  import * as path from "node:path";
11
12
  import { safeSpawnAsync } from "./safe-spawn.js";
@@ -28,7 +29,7 @@ export class RustClient {
28
29
  log;
29
30
  constructor(verbose = false) {
30
31
  this.log = verbose
31
- ? (msg) => console.error(`[rust] ${msg}`)
32
+ ? createSubsystemLogger("rust")
32
33
  : () => { };
33
34
  }
34
35
  /**