@herbertgao/pi-extensions 2026.8.6 → 2026.8.8

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 (385) hide show
  1. package/README.md +8 -6
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +320 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -17
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  15. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  16. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  17. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  18. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  19. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  21. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +17 -13
  23. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  25. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  26. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  27. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  28. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  29. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  30. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +21 -6
  32. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  33. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +5 -1
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +140 -97
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  51. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
  52. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
  53. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
  54. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
  55. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
  56. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
  57. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
  58. package/node_modules/@narumitw/pi-btw/README.md +120 -111
  59. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  60. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  61. package/node_modules/@narumitw/pi-btw/package.json +10 -6
  62. package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
  63. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  64. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  65. package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
  66. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  67. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
  68. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  69. package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
  70. package/node_modules/pi-lens/CHANGELOG.md +999 -0
  71. package/node_modules/pi-lens/README.md +117 -3
  72. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  73. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  74. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
  75. package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
  76. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  77. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  78. package/node_modules/pi-lens/dist/clients/bash-file-access.js +381 -13
  79. package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
  80. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  81. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -0
  82. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  83. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  84. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +206 -0
  85. package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
  86. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  87. package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
  88. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  89. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  90. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  91. package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
  92. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  93. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  94. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +84 -14
  95. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
  96. package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
  97. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
  98. package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
  99. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
  100. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
  102. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
  103. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  104. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +17 -8
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +188 -0
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  144. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  145. package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
  146. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  147. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
  148. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  149. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  150. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  151. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  152. package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
  153. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  154. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
  155. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  156. package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
  157. package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
  158. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  159. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  160. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  161. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  162. package/node_modules/pi-lens/dist/clients/jscpd-client.js +87 -8
  163. package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
  164. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  165. package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
  166. package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
  167. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  168. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  169. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  170. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
  171. package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
  172. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
  173. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
  175. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  176. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  177. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  178. package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
  179. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  180. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  181. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
  182. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
  183. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  184. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  185. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  186. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  187. package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
  188. package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
  189. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
  190. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  191. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  192. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  193. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  194. package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
  195. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  198. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
  199. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  200. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  201. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
  202. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  203. package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
  204. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  205. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  206. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  207. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +8 -3
  208. package/node_modules/pi-lens/dist/clients/read-guard.js +253 -10
  209. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  210. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  211. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
  212. package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
  213. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
  214. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
  215. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
  216. package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
  217. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
  218. package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
  219. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  220. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
  221. package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
  222. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  223. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  224. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  225. package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
  226. package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
  227. package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
  228. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  229. package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
  230. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  231. package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
  232. package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
  233. package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
  234. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  235. package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  241. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  242. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  243. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  244. package/node_modules/pi-lens/dist/index.js +61407 -48577
  245. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
  246. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
  247. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  248. package/node_modules/pi-lens/docs/agent-guide.md +6 -1
  249. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
  250. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  251. package/node_modules/pi-lens/docs/features.md +19 -5
  252. package/node_modules/pi-lens/docs/globalconfig.md +3 -0
  253. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  254. package/node_modules/pi-lens/docs/mcp.md +2 -2
  255. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  256. package/node_modules/pi-lens/docs/settings.md +19 -1
  257. package/node_modules/pi-lens/docs/usage.md +1 -0
  258. package/node_modules/pi-lens/package.json +27 -10
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  319. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
  320. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  321. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  322. package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
  323. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
  324. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
  325. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
  326. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
  327. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
  328. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
  329. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  330. package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
  331. package/node_modules/pi-mcp-adapter/README.md +47 -5
  332. package/node_modules/pi-mcp-adapter/cli.js +163 -1
  333. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  334. package/node_modules/pi-mcp-adapter/config.ts +25 -7
  335. package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
  336. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  337. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  338. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  339. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  340. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  341. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  342. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  343. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  344. package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
  345. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  346. package/node_modules/pi-mcp-adapter/package.json +3 -1
  347. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  348. package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
  349. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  350. package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
  351. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  352. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  353. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  354. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  355. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  356. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  357. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  358. package/node_modules/pi-web-access/README.md +32 -13
  359. package/node_modules/pi-web-access/anysearch.ts +4 -2
  360. package/node_modules/pi-web-access/brave.ts +16 -4
  361. package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
  362. package/node_modules/pi-web-access/curator-page.ts +6 -3
  363. package/node_modules/pi-web-access/curator-server.ts +4 -1
  364. package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
  365. package/node_modules/pi-web-access/exa.ts +18 -7
  366. package/node_modules/pi-web-access/extract.ts +40 -5
  367. package/node_modules/pi-web-access/firecrawl.ts +18 -2
  368. package/node_modules/pi-web-access/gemini-search.ts +16 -4
  369. package/node_modules/pi-web-access/github-extract.ts +65 -36
  370. package/node_modules/pi-web-access/index.ts +60 -61
  371. package/node_modules/pi-web-access/openai-search.ts +17 -6
  372. package/node_modules/pi-web-access/package.json +1 -1
  373. package/node_modules/pi-web-access/page-query.ts +4 -2
  374. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  375. package/node_modules/pi-web-access/parallel.ts +2 -2
  376. package/node_modules/pi-web-access/pdf-extract.ts +18 -5
  377. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  378. package/node_modules/pi-web-access/serper.ts +211 -0
  379. package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
  380. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  381. package/node_modules/pi-web-access/summary-review.ts +44 -22
  382. package/node_modules/pi-web-access/tavily.ts +16 -4
  383. package/node_modules/pi-web-access/utils.ts +87 -0
  384. package/node_modules/pi-web-access/valyu.ts +199 -0
  385. package/package.json +14 -14
@@ -4,6 +4,7 @@ import * as path from "node:path";
4
4
  import { logLatency } from "./latency-logger.js";
5
5
  import { normalizeMapKey, toProjectRelativePath } from "./path-utils.js";
6
6
  import { resolveRunnerPath } from "./dispatch/runner-context.js";
7
+ import { MTIME_DRIFT_TOLERANCE_MS } from "./blocker-freshness.js";
7
8
  export const MAX_ADVISORY_AFFECTED_FILES = 256;
8
9
  export function advisoryPathKey(filePath, cwd) {
9
10
  return normalizeMapKey(path.resolve(cwd, filePath));
@@ -199,9 +200,34 @@ export function validateAdvisoryProvenance(record, cwd, runtime) {
199
200
  export const MAX_FINDING_PATH_STATS = MAX_ADVISORY_AFFECTED_FILES;
200
201
  /** How many dead paths a single drop record names before it stops. */
201
202
  const MAX_LOGGED_DEAD_PATHS = 3;
202
- export function findingPathExistence(resolvedPath) {
203
+ /**
204
+ * One stat, one verdict. With `scannedAtMs` supplied, a surviving file whose
205
+ * mtime is newer than the scan reports `stale`.
206
+ *
207
+ * The boundary is `mtime > scannedAt + MTIME_DRIFT_TOLERANCE_MS`: a file
208
+ * scanned AT `scannedAt`, or written within the tolerance window of the scan
209
+ * timestamp being captured, reads live; genuinely past it reads stale.
210
+ *
211
+ * #1708: a bare +1ms tolerance — the convention `reconcileProjectDiagnosticsSnapshot`
212
+ * (clients/project-diagnostics/cache.ts:98) still uses, and the one this gate
213
+ * originally cited-but-withheld ("over-demoting costs a line number,
214
+ * under-demoting replays a false coordinate — the cheaper error wins the
215
+ * tie") — was not enough: on Windows a file's mtime can LEAD the immediately
216
+ * following `Date.now()` read by up to ~11.4ms, the same host skew
217
+ * `MTIME_DRIFT_TOLERANCE_MS` (`blocker-freshness.ts`) was raised to 50ms for
218
+ * (#1491/#1498). At +1ms this gate still demoted a real STOP-blocker (a
219
+ * trivy secret, #1628) to ACTION NEEDED, flaking
220
+ * tests/clients/runtime-turn-secrets-disposition.test.ts. Reusing the shared
221
+ * constant — rather than a second hand-tuned number — keeps one source of
222
+ * truth for the measured skew.
223
+ */
224
+ export function findingPathFreshness(resolvedPath, scannedAtMs) {
203
225
  try {
204
- fs.statSync(resolvedPath);
226
+ const stat = fs.statSync(resolvedPath);
227
+ if (scannedAtMs !== undefined &&
228
+ stat.mtimeMs > scannedAtMs + MTIME_DRIFT_TOLERANCE_MS) {
229
+ return "stale";
230
+ }
205
231
  return "live";
206
232
  }
207
233
  catch (error) {
@@ -211,14 +237,37 @@ export function findingPathExistence(resolvedPath) {
211
237
  return code === "ENOENT" || code === "ENOTDIR" ? "missing" : "unknown";
212
238
  }
213
239
  }
240
+ /** Existence-only probe. Retained for callers that have no scan timestamp. */
241
+ export function findingPathExistence(resolvedPath) {
242
+ return findingPathFreshness(resolvedPath);
243
+ }
244
+ /**
245
+ * Parse a scan timestamp into epoch ms, or `undefined` when it cannot be
246
+ * trusted. `undefined` disables the staleness arm — never demote on a bad
247
+ * clock.
248
+ */
249
+ export function parseScannedAtMs(scannedAt) {
250
+ if (scannedAt === undefined || scannedAt === null || scannedAt === "") {
251
+ return undefined;
252
+ }
253
+ const ms = typeof scannedAt === "number" ? scannedAt : Date.parse(scannedAt);
254
+ return Number.isFinite(ms) ? ms : undefined;
255
+ }
214
256
  /**
215
- * Partition findings into `{live, dropped}` by whether the path each one names
216
- * still exists. Pure apart from the `fs.statSync` probe, which is injectable so
217
- * the partition rules are unit-testable without a filesystem.
257
+ * Partition findings into `{live, stale, dropped}` by what the path each one
258
+ * names looks like now. Pure apart from the `fs.statSync` probe, which is
259
+ * injectable so the partition rules are unit-testable without a filesystem.
260
+ *
261
+ * The stat memo lives in local `Map`s built fresh on every call — per delivery,
262
+ * never per process. A module-level cache here would be the process-lifetime
263
+ * latch shape: it would pin the first verdict for a path and re-serve it for
264
+ * every later turn, which is the very defect this gate exists to close.
218
265
  */
219
266
  export function partitionFindingsByCitedPath(args) {
220
267
  const limit = args.maxUniquePaths ?? MAX_FINDING_PATH_STATS;
221
- const probe = args.existence ?? findingPathExistence;
268
+ const probe = args.existence ?? findingPathFreshness;
269
+ const scannedAtMs = parseScannedAtMs(args.scannedAt);
270
+ const onMissing = args.onMissing ?? "drop";
222
271
  // Two dedup layers, cheapest first. `rawVerdicts` collapses findings that
223
272
  // cite the IDENTICAL string with zero filesystem work — the dominant case
224
273
  // (#1460's live record: 126 findings over a handful of distinct `file`
@@ -232,8 +281,10 @@ export function partitionFindingsByCitedPath(args) {
232
281
  const rawVerdicts = new Map();
233
282
  const verdicts = new Map();
234
283
  const live = [];
284
+ const stale = [];
235
285
  const dropped = [];
236
286
  const deadPaths = [];
287
+ const stalePaths = [];
237
288
  let truncated = false;
238
289
  for (const finding of args.findings) {
239
290
  const cited = args.citedPath(finding);
@@ -261,20 +312,38 @@ export function partitionFindingsByCitedPath(args) {
261
312
  verdict = "live";
262
313
  }
263
314
  else {
264
- verdict = probe(resolved);
315
+ verdict = probe(resolved, scannedAtMs);
316
+ // A store that treats deletion as evidence-loss rather than
317
+ // remediation-loss folds `missing` into the demote arm here, so
318
+ // every downstream list and record sees one consistent verdict.
319
+ if (verdict === "missing" && onMissing === "demote") {
320
+ verdict = "stale";
321
+ }
265
322
  verdicts.set(key, verdict);
266
323
  if (verdict === "missing")
267
324
  deadPaths.push(resolved);
325
+ else if (verdict === "stale")
326
+ stalePaths.push(resolved);
268
327
  }
269
328
  }
270
329
  rawVerdicts.set(cited, verdict);
271
330
  }
272
331
  if (verdict === "missing")
273
332
  dropped.push(finding);
333
+ else if (verdict === "stale")
334
+ stale.push(finding);
274
335
  else
275
336
  live.push(finding);
276
337
  }
277
- return { live, dropped, deadPaths, statCount: verdicts.size, truncated };
338
+ return {
339
+ live,
340
+ stale,
341
+ dropped,
342
+ deadPaths,
343
+ stalePaths,
344
+ statCount: verdicts.size,
345
+ truncated,
346
+ };
278
347
  }
279
348
  /**
280
349
  * Delivery-seam wrapper: partition, then emit one bounded `finding_dead_path_drop`
@@ -285,27 +354,71 @@ export function partitionFindingsByCitedPath(args) {
285
354
  * store per delivery, with a capped path sample; never one per finding.
286
355
  */
287
356
  export function dropFindingsForMissingPaths(args) {
357
+ // No `scannedAt` — existence-only, so `partition.stale` is always empty.
358
+ const partition = partitionFindingsByCitedPath(args);
359
+ emitDeadPathDropRecord(args.store, args.cwd, partition);
360
+ return partition.live;
361
+ }
362
+ /**
363
+ * Delivery-seam wrapper with the #1622 freshness verdict. Emits at most one
364
+ * `finding_dead_path_drop` and one `finding_stale_line_demote` record per store
365
+ * per delivery, each with a capped path sample; never one per finding.
366
+ *
367
+ * Callers that have no scan timestamp keep using `dropFindingsForMissingPaths`.
368
+ */
369
+ export function gateFindingsByPathFreshness(args) {
288
370
  const partition = partitionFindingsByCitedPath(args);
371
+ emitDeadPathDropRecord(args.store, args.cwd, partition);
372
+ emitStaleLineDemoteRecord(args.store, args.cwd, args.scannedAt, partition);
373
+ return { live: partition.live, stale: partition.stale };
374
+ }
375
+ function emitDeadPathDropRecord(store, cwd, partition) {
289
376
  if (partition.dropped.length === 0)
290
- return partition.live;
377
+ return;
291
378
  logLatency({
292
379
  type: "phase",
293
380
  phase: "finding_dead_path_drop",
294
- filePath: args.cwd,
381
+ filePath: cwd,
295
382
  durationMs: 0,
296
383
  metadata: {
297
- store: args.store,
384
+ store,
298
385
  droppedDeadPaths: partition.dropped.length,
299
386
  deadPathCount: partition.deadPaths.length,
300
387
  deliveredCount: partition.live.length,
301
388
  statCount: partition.statCount,
302
389
  samplePaths: partition.deadPaths
303
390
  .slice(0, MAX_LOGGED_DEAD_PATHS)
304
- .map((deadPath) => toProjectRelativePath(deadPath, args.cwd)),
391
+ .map((deadPath) => toProjectRelativePath(deadPath, cwd)),
392
+ ...(partition.truncated ? { truncated: true } : {}),
393
+ },
394
+ });
395
+ }
396
+ /**
397
+ * #1622 criterion 5: the demote path gets the same record shape as the drop
398
+ * path. Before this, a stale-line replay window was reconstructible only by
399
+ * proxy — the blocker payload is written nowhere.
400
+ */
401
+ function emitStaleLineDemoteRecord(store, cwd, scannedAt, partition) {
402
+ if (partition.stale.length === 0)
403
+ return;
404
+ logLatency({
405
+ type: "phase",
406
+ phase: "finding_stale_line_demote",
407
+ filePath: cwd,
408
+ durationMs: 0,
409
+ metadata: {
410
+ store,
411
+ demotedStalePaths: partition.stale.length,
412
+ stalePathCount: partition.stalePaths.length,
413
+ deliveredCount: partition.live.length,
414
+ statCount: partition.statCount,
415
+ samplePaths: partition.stalePaths
416
+ .slice(0, MAX_LOGGED_DEAD_PATHS)
417
+ .map((stalePath) => toProjectRelativePath(stalePath, cwd)),
418
+ ...(scannedAt === undefined ? {} : { scannedAt: String(scannedAt) }),
305
419
  ...(partition.truncated ? { truncated: true } : {}),
306
420
  },
307
421
  });
308
- return partition.live;
309
422
  }
310
423
  export function provenanceStamp(provenance) {
311
424
  if (!isWellFormed(provenance))
@@ -68,12 +68,17 @@ function recordTouchedEvent(payload, getReadGuard) {
68
68
  // recorded via the cross-process feed, now also reported by this
69
69
  // session's own bus, upgrades to "local".
70
70
  existing.origin = "local";
71
+ // #1464: a fresh touch invalidates any content already delivered for
72
+ // this path (the default deferred format at agent_end, a cascade
73
+ // autofix) — those bytes no longer describe the file.
74
+ existing.contentDelivered = false;
71
75
  }
72
76
  else {
73
77
  _touched.set(mapKey, {
74
78
  displayPath: rawPath,
75
79
  reasons: new Set([payload.reason]),
76
80
  origin: "local",
81
+ contentDelivered: false,
77
82
  });
78
83
  }
79
84
  }
@@ -112,16 +117,47 @@ export function recordCrossProcessTouches(entries) {
112
117
  existing.reasons.add(entry.reason);
113
118
  // "local" is sticky (see AccumulatedFileOrigin) — never downgrade an
114
119
  // already-local entry back to cross-process.
120
+ // #1464: a foreign process touching this file invalidates whatever
121
+ // content this session's write path already delivered for it.
122
+ existing.contentDelivered = false;
115
123
  }
116
124
  else {
117
125
  _touched.set(mapKey, {
118
126
  displayPath: entry.path,
119
127
  reasons: new Set([entry.reason]),
120
128
  origin: "cross-process",
129
+ contentDelivered: false,
121
130
  });
122
131
  }
123
132
  }
124
133
  }
134
+ /**
135
+ * Record the write path's authoritative-content attachment outcome for one
136
+ * path (#1464).
137
+ *
138
+ * `attached` is the SAME boolean that drove the attachment in
139
+ * `handleToolResult` (clients/runtime-tool-result.ts) — this module never
140
+ * re-derives it from config, byte counts, or the aggregate budget. `true`
141
+ * means the post-fix bytes went out in the write's own tool result and the
142
+ * agent already holds them, so "re-read before editing" is noise. `false` is
143
+ * the size cap and the per-command aggregate budget degrading an attachment
144
+ * to a re-read warning: there the nudge is the whole signal, and calling this
145
+ * with `false` re-arms a path an earlier per-file decision had suppressed
146
+ * (the bash multi-file case, where the outer budget loop overrides the inner
147
+ * per-file "attached").
148
+ *
149
+ * Marks an EXISTING accumulator entry only. `publishFilesTouched` fires
150
+ * synchronously inside the `runPipeline` call this decision belongs to (pi's
151
+ * event bus is a plain `EventEmitter`), so the entry is already there for any
152
+ * path the relevance filter admitted; a path with no entry has nothing to
153
+ * suppress. Deferred-edit autofix at `agent_end` never reaches here — it
154
+ * attaches nothing, so it keeps nudging.
155
+ */
156
+ export function noteAuthoritativeContentAttachment(filePath, attached) {
157
+ const entry = _touched.get(normalizeMapKey(filePath));
158
+ if (entry)
159
+ entry.contentDelivered = attached;
160
+ }
125
161
  /**
126
162
  * Subscribe to `pilens:files:touched` on pi's shared event bus. Called once
127
163
  * at extension factory time from index.ts, mirroring `wireBusEmitter`'s
@@ -191,17 +227,29 @@ export function wireAgentNudgeSubscriber(args) {
191
227
  * very first call of a fresh `agent_start` — so the accumulator surviving
192
228
  * across run boundaries is exactly what makes that cross-run delivery work.
193
229
  * Empty accumulator ⇒ returns undefined ⇒ zero bytes injected.
230
+ *
231
+ * #1464: paths whose post-fix content the write path already attached to its
232
+ * OWN tool result are dropped from the batch here (see
233
+ * `noteAuthoritativeContentAttachment`) — the agent holds those bytes, so
234
+ * "re-read before editing" would spend context to re-fetch what it has. A
235
+ * batch that is ENTIRELY such paths injects nothing.
194
236
  */
195
237
  export function consumeAgentNudge(dbg) {
196
- const entries = Array.from(_touched.values());
238
+ const drained = Array.from(_touched.values());
197
239
  _touched.clear();
198
240
  const filesFiltered = _relevanceFilteredCount;
199
241
  _relevanceFilteredCount = 0;
200
242
  if (!isAgentNudgeEnabled())
201
243
  return undefined;
202
- if (entries.length === 0)
244
+ if (drained.length === 0)
203
245
  return undefined;
204
246
  try {
247
+ // #1464: a write whose post-fix bytes were attached to its own tool
248
+ // result already told the agent everything this nudge would. Drop those
249
+ // paths from the message but keep counting them, so over-suppression is
250
+ // visible in telemetry instead of only in the absence of complaints.
251
+ const entries = drained.filter((e) => !e.contentDelivered);
252
+ const filesContentDelivered = drained.length - entries.length;
205
253
  const filesTotal = entries.length;
206
254
  const shown = entries.slice(0, MAX_NAMES_SHOWN);
207
255
  const remaining = filesTotal - shown.length;
@@ -225,15 +273,6 @@ export function consumeAgentNudge(dbg) {
225
273
  : names.join(", ");
226
274
  const crossProcessCount = entries.filter((e) => e.origin === "cross-process").length;
227
275
  const localCount = filesTotal - crossProcessCount;
228
- // Three-way attribution (see the function doc): never assign a local
229
- // file to another instance — a mixed batch keeps the local base framing
230
- // and calls out the cross-process portion by exact count.
231
- const attribution = localCount === 0
232
- ? "by an automatic run outside your turn"
233
- : crossProcessCount === 0
234
- ? "after your last turn"
235
- : `after your last turn (${crossProcessCount} of them by an automatic run outside it)`;
236
- const message = `pi-lens: ${filesTotal} file(s) were ${verbLabel} ${attribution}: ${nameList} — working-tree changes to these are expected; re-read before editing.`;
237
276
  logLatency({
238
277
  type: "phase",
239
278
  filePath: "<pi-lens>",
@@ -246,6 +285,10 @@ export function consumeAgentNudge(dbg) {
246
285
  // never read/edited) — NOT the display overflow, which is
247
286
  // filesTotal - filesShown.
248
287
  filesFiltered,
288
+ // #1464: drops because the write's own tool result already carried
289
+ // the post-fix bytes. A third, distinct count: these paths passed
290
+ // the relevance filter and were accumulated, then suppressed here.
291
+ filesContentDelivered,
249
292
  reasonMix: Array.from(allReasons),
250
293
  // #492: origin mix so cross-process pickup rate is observable
251
294
  // alongside the existing relevance-filter metric.
@@ -253,6 +296,19 @@ export function consumeAgentNudge(dbg) {
253
296
  originCrossProcess: crossProcessCount,
254
297
  },
255
298
  });
299
+ // Everything drained was delivered in a write's own tool result — the
300
+ // row above records that it happened, and zero bytes get injected.
301
+ if (filesTotal === 0)
302
+ return undefined;
303
+ // Three-way attribution (see the function doc): never assign a local
304
+ // file to another instance — a mixed batch keeps the local base framing
305
+ // and calls out the cross-process portion by exact count.
306
+ const attribution = localCount === 0
307
+ ? "by an automatic run outside your turn"
308
+ : crossProcessCount === 0
309
+ ? "after your last turn"
310
+ : `after your last turn (${crossProcessCount} of them by an automatic run outside it)`;
311
+ const message = `pi-lens: ${filesTotal} file(s) were ${verbLabel} ${attribution}: ${nameList} — working-tree changes to these are expected; re-read before editing.`;
256
312
  return {
257
313
  messages: [
258
314
  {
@@ -176,9 +176,12 @@ export class AstGrepClient {
176
176
  * Each path is scanned independently; results are merged.
177
177
  */
178
178
  async tempScanDetailed(dir, ruleYaml, options = {}) {
179
- // Keep tests and older embedders that replace the runner with the historic
180
- // match-only seam working. The production SgRunner always has the detailed
181
- // method, so failures cannot be mistaken for an empty result there.
179
+ // SAFETY: keep tests and older embedders that replace the runner with the
180
+ // historic match-only seam working. The cast only makes
181
+ // `tempScanDetailedAsync` OPTIONAL on the runner surface — it claims
182
+ // nothing about the method existing. The call site below checks for it
183
+ // before invoking, and the production SgRunner always has it, so a
184
+ // failure there cannot be mistaken for an empty result.
182
185
  const detailed = this.runner.tempScanDetailedAsync;
183
186
  if (detailed) {
184
187
  return detailed.call(this.runner, dir, "agent-rule", ruleYaml, 30_000, options);
@@ -568,12 +571,15 @@ message: found
568
571
  return "";
569
572
  const errors = diags.filter((d) => d.severity === "error");
570
573
  const warnings = diags.filter((d) => d.severity === "warning");
574
+ const infos = diags.filter((d) => d.severity === "info");
571
575
  const hints = diags.filter((d) => d.severity === "hint");
572
576
  let output = `[ast-grep] ${diags.length} structural issue(s)`;
573
577
  if (errors.length)
574
578
  output += ` — ${errors.length} error(s)`;
575
579
  if (warnings.length)
576
580
  output += ` — ${warnings.length} warning(s)`;
581
+ if (infos.length)
582
+ output += ` — ${infos.length} info(s)`;
577
583
  if (hints.length)
578
584
  output += ` — ${hints.length} hint(s)`;
579
585
  output += ":\n";
@@ -38,7 +38,7 @@
38
38
  * cycles still lose updates: each writer publishes the state it computed
39
39
  * from the snapshot it read, and last-rename-wins silently discards the
40
40
  * other's mutation. Callers needing this must serialize themselves (see
41
- * `lspChildRemovalTail` in `instance-registry.ts`).
41
+ * `registryChildMutationTail` in `instance-registry.ts`).
42
42
  * - **No ordering.** Nothing sequences concurrent writers, within or across
43
43
  * processes. The winner is whichever `rename` lands last, which is not
44
44
  * necessarily the write that started or finished last.
@@ -143,7 +143,10 @@ export function writeFileAtomic(targetPath, data, options) {
143
143
  const bestEffort = options?.bestEffort ?? true;
144
144
  const tmpPath = stagePathFor(targetPath);
145
145
  try {
146
- fs.writeFileSync(tmpPath, data, typeof data === "string" ? "utf-8" : undefined);
146
+ fs.writeFileSync(tmpPath, data, {
147
+ encoding: typeof data === "string" ? "utf-8" : undefined,
148
+ mode: options?.mode,
149
+ });
147
150
  fs.renameSync(tmpPath, targetPath);
148
151
  }
149
152
  catch (err) {
@@ -167,7 +170,10 @@ export async function writeFileAtomicAsync(targetPath, data, options) {
167
170
  const bestEffort = options?.bestEffort ?? true;
168
171
  const tmpPath = stagePathFor(targetPath);
169
172
  try {
170
- await fs.promises.writeFile(tmpPath, data, typeof data === "string" ? "utf-8" : undefined);
173
+ await fs.promises.writeFile(tmpPath, data, {
174
+ encoding: typeof data === "string" ? "utf-8" : undefined,
175
+ mode: options?.mode,
176
+ });
171
177
  await fs.promises.rename(tmpPath, targetPath);
172
178
  }
173
179
  catch (err) {