@herbertgao/pi-extensions 2026.8.5 → 2026.8.7

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 (389) hide show
  1. package/README.md +5 -5
  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 +272 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  17. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  18. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
  19. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  20. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  21. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  24. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
  25. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  26. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  27. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  28. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  29. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  30. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  32. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  33. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
  34. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  35. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -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 +3 -1
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  51. package/node_modules/@narumitw/pi-btw/README.md +119 -103
  52. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  53. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  54. package/node_modules/@narumitw/pi-btw/package.json +13 -9
  55. package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
  56. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  57. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  58. package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
  59. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  60. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  61. package/node_modules/pi-lens/CHANGELOG.md +1004 -0
  62. package/node_modules/pi-lens/README.md +11 -3
  63. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  64. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  65. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
  66. package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
  67. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  68. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  69. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  70. package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
  71. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  72. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  73. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  74. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  75. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  76. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  77. package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
  78. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  79. package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
  80. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  81. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  82. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  83. package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
  84. package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
  85. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  86. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  87. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  88. package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
  89. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
  90. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  91. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
  93. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
  94. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  95. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  129. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  130. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  131. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  132. package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
  133. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  134. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  135. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  136. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  137. package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
  138. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  139. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  140. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  141. package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
  142. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  143. package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
  144. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
  145. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  146. package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
  147. package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
  148. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  149. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  150. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  151. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  152. package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
  153. package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
  154. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  155. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  156. package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
  157. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  158. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  159. package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
  160. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  161. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
  162. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  163. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
  164. package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
  165. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
  166. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  167. package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
  168. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  169. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  170. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  171. package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
  172. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  173. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -0
  175. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  176. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
  177. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  178. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  179. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  180. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  181. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
  182. package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
  183. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  184. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  185. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  186. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  187. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  188. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  189. package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
  190. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  191. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  195. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  198. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  199. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  200. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  201. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  204. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  205. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  206. package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  217. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  218. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  219. package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
  220. package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
  221. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  222. package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
  223. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  224. package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
  225. package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
  226. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  227. package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
  228. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  229. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  230. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  233. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  234. package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
  235. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  236. package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
  237. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  238. package/node_modules/pi-lens/dist/index.js +64677 -49484
  239. package/node_modules/pi-lens/dist/mcp/server.js +8 -2
  240. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  241. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  242. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  243. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
  244. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
  245. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  246. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  247. package/node_modules/pi-lens/docs/agent-guide.md +44 -14
  248. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
  249. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  250. package/node_modules/pi-lens/docs/features.md +33 -6
  251. package/node_modules/pi-lens/docs/globalconfig.md +14 -0
  252. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  253. package/node_modules/pi-lens/docs/mcp.md +2 -2
  254. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  255. package/node_modules/pi-lens/docs/settings.md +25 -1
  256. package/node_modules/pi-lens/docs/usage.md +24 -5
  257. package/node_modules/pi-lens/docs/word-index.md +35 -0
  258. package/node_modules/pi-lens/package.json +10 -6
  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 +22 -0
  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-conditional-empty-object-spread-js-test.yml +9 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  319. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  320. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  321. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  322. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  323. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  324. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
  325. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  326. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  327. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -11
  328. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  329. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
  330. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
  331. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  332. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  333. package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
  334. package/node_modules/pi-mcp-adapter/README.md +51 -6
  335. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  336. package/node_modules/pi-mcp-adapter/cli.js +188 -5
  337. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  338. package/node_modules/pi-mcp-adapter/config.ts +28 -10
  339. package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
  340. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  341. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  342. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  343. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  344. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  345. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  346. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  347. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  348. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  349. package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
  350. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  351. package/node_modules/pi-mcp-adapter/package.json +4 -1
  352. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  353. package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
  354. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  355. package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
  356. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  357. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  358. package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
  359. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  360. package/node_modules/pi-mcp-adapter/types.ts +95 -10
  361. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  362. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  363. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  364. package/node_modules/pi-web-access/README.md +38 -14
  365. package/node_modules/pi-web-access/anysearch.ts +4 -2
  366. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  367. package/node_modules/pi-web-access/brave.ts +16 -4
  368. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  369. package/node_modules/pi-web-access/curator-page.ts +8 -3
  370. package/node_modules/pi-web-access/curator-server.ts +5 -1
  371. package/node_modules/pi-web-access/exa.ts +18 -7
  372. package/node_modules/pi-web-access/extract.ts +134 -37
  373. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  374. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  375. package/node_modules/pi-web-access/gemini-search.ts +33 -7
  376. package/node_modules/pi-web-access/index.ts +160 -104
  377. package/node_modules/pi-web-access/package.json +2 -2
  378. package/node_modules/pi-web-access/page-query.ts +4 -2
  379. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  380. package/node_modules/pi-web-access/parallel.ts +2 -2
  381. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  382. package/node_modules/pi-web-access/serper.ts +211 -0
  383. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  384. package/node_modules/pi-web-access/summary-review.ts +51 -23
  385. package/node_modules/pi-web-access/tavily.ts +16 -4
  386. package/node_modules/pi-web-access/utils.ts +87 -0
  387. package/node_modules/pi-web-access/valyu.ts +199 -0
  388. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  389. package/package.json +13 -13
@@ -4,7 +4,8 @@
4
4
  *
5
5
  * - VIEW commands (cat/head/tail/sed -n) → reads, recorded with the exact line
6
6
  * range shown (like the Read tool's delivered range).
7
- * - WRITE commands (redirects, tee, sed -i, cp/mv dest, touch) → the agent
7
+ * - WRITE commands (redirects, tee, sed -i, cp/mv dest, touch, and explicit
8
+ * in-place formatter/fixer targets) → the agent
8
9
  * authored/owns the resulting file, exactly like the Write tool — these are
9
10
  * registered via noteCreatedFile + recordWritten so a follow-up edit is not
10
11
  * blocked.
@@ -90,7 +91,7 @@ export function tokenizeShellCommand(command) {
90
91
  // Windows routinely contain native paths (C:\\src\\file.ts). Preserve
91
92
  // backslashes before ordinary path characters while still honoring
92
93
  // shell escapes and line continuations.
93
- if (next === "\n" || /[\s\\'\";$|&<>]/.test(next ?? "")) {
94
+ if (next === "\n" || /[\s\\'";$|&<>]/.test(next ?? "")) {
94
95
  escaped = true;
95
96
  }
96
97
  else {
@@ -162,6 +163,37 @@ function parseCountFlag(token) {
162
163
  function commandSegments(command) {
163
164
  return tokenizeShellCommand(command).map((segment) => segment.tokens);
164
165
  }
166
+ /** Unwrap the common agent-authored `npx <tool>` launcher form. */
167
+ function unwrapCommand(tokens) {
168
+ let verb = path.basename(tokens[0] ?? "");
169
+ let args = tokens.slice(1);
170
+ if (verb === "npx") {
171
+ while (args[0] === "-y" || args[0] === "--yes")
172
+ args = args.slice(1);
173
+ verb = path.basename(args[0] ?? "");
174
+ args = args.slice(1);
175
+ }
176
+ return { verb, args };
177
+ }
178
+ /**
179
+ * Return source-file operands while excluding values consumed by known options.
180
+ * This stays deliberately narrower than a general CLI parser: only formatter
181
+ * options whose values can themselves look like source files belong here.
182
+ */
183
+ function formatterFileArgs(args, start = 0, valueOptions = new Set()) {
184
+ const files = [];
185
+ for (let i = start; i < args.length; i += 1) {
186
+ const arg = args[i] ?? "";
187
+ if (valueOptions.has(arg)) {
188
+ i += 1;
189
+ continue;
190
+ }
191
+ if (arg === "--" || arg.startsWith("-"))
192
+ continue;
193
+ files.push(arg);
194
+ }
195
+ return files;
196
+ }
165
197
  /** Find redirect targets without treating quoted `>` characters as syntax. */
166
198
  function extractRedirectTargets(command) {
167
199
  const targets = [];
@@ -344,6 +376,80 @@ const GREP_OPTIONS_WITH_VALUE = new Set([
344
376
  "--before-context",
345
377
  "--context",
346
378
  ]);
379
+ function parseContextValue(inline, next) {
380
+ const raw = inline !== "" ? inline : next;
381
+ if (raw === undefined)
382
+ return undefined;
383
+ const n = Number.parseInt(raw, 10);
384
+ if (!Number.isFinite(n) || n < 0)
385
+ return undefined;
386
+ // Bound the credit: a huge -C would credit a whole file from one hit.
387
+ return Math.min(n, 100);
388
+ }
389
+ /**
390
+ * Read the context lines a `grep` invocation actually PRINTS around each hit
391
+ * (#1904 item 2). Only these lines were delivered to the model, so only these
392
+ * may be credited as read. Handles `-A N`, `-A3`, clustered short flags such as
393
+ * `-rnA3`, `--after-context=N`, the `-B`/`--before-context` mirror, `-C`
394
+ * (both sides), and the bare `-NUM` form.
395
+ */
396
+ export function parseGrepContextLines(args) {
397
+ let before = 0;
398
+ let after = 0;
399
+ for (let i = 0; i < args.length; i++) {
400
+ const token = stripQuotes(args[i]);
401
+ if (token === "--")
402
+ break;
403
+ const nextToken = args[i + 1] === undefined ? undefined : stripQuotes(args[i + 1]);
404
+ const long = /^--(after|before)-context(?:=(\d+))?$/.exec(token);
405
+ if (long) {
406
+ const value = parseContextValue(long[2] ?? "", nextToken);
407
+ if (value === undefined)
408
+ continue;
409
+ if (long[1] === "after")
410
+ after = Math.max(after, value);
411
+ else
412
+ before = Math.max(before, value);
413
+ continue;
414
+ }
415
+ const longBoth = /^--context(?:=(\d+))?$/.exec(token);
416
+ if (longBoth) {
417
+ const value = parseContextValue(longBoth[1] ?? "", nextToken);
418
+ if (value === undefined)
419
+ continue;
420
+ before = Math.max(before, value);
421
+ after = Math.max(after, value);
422
+ continue;
423
+ }
424
+ // Bare numeric context form: `grep -3 pattern file`.
425
+ const bare = /^-(\d+)$/.exec(token);
426
+ if (bare) {
427
+ const value = parseContextValue(bare[1], undefined);
428
+ if (value === undefined)
429
+ continue;
430
+ before = Math.max(before, value);
431
+ after = Math.max(after, value);
432
+ continue;
433
+ }
434
+ // Short flags, possibly clustered: the context flag must be LAST in the
435
+ // cluster because it consumes the remaining characters as its value.
436
+ const short = /^-[A-Za-z]*([ABC])(\d*)$/.exec(token);
437
+ if (!short)
438
+ continue;
439
+ const value = parseContextValue(short[2], nextToken);
440
+ if (value === undefined)
441
+ continue;
442
+ if (short[1] === "A")
443
+ after = Math.max(after, value);
444
+ else if (short[1] === "B")
445
+ before = Math.max(before, value);
446
+ else {
447
+ before = Math.max(before, value);
448
+ after = Math.max(after, value);
449
+ }
450
+ }
451
+ return { before, after };
452
+ }
347
453
  function extractGrepSearchFiles(args, cwd) {
348
454
  const files = [];
349
455
  let patternSeen = false;
@@ -411,6 +517,11 @@ function parseGrepLineWithoutFile(line, file) {
411
517
  function collectGrepCommandFiles(command, cwd) {
412
518
  const files = new Set();
413
519
  let hasLineNumberGrep = false;
520
+ // A command can chain several greps whose output all reaches the model, but
521
+ // the parsed hits carry no segment identity. Credit the SMALLEST context any
522
+ // contributing grep printed, so no line is credited that a hit from the
523
+ // narrowest grep never delivered.
524
+ let context;
414
525
  for (const tokens of commandSegments(command)) {
415
526
  const verb = path.basename(stripQuotes(tokens[0] ?? ""));
416
527
  if (verb !== "grep" && verb !== "egrep" && verb !== "fgrep")
@@ -419,10 +530,21 @@ function collectGrepCommandFiles(command, cwd) {
419
530
  if (!grepHasLineNumbers(args))
420
531
  continue;
421
532
  hasLineNumberGrep = true;
533
+ const segmentContext = parseGrepContextLines(args);
534
+ context = context
535
+ ? {
536
+ before: Math.min(context.before, segmentContext.before),
537
+ after: Math.min(context.after, segmentContext.after),
538
+ }
539
+ : segmentContext;
422
540
  for (const file of extractGrepSearchFiles(args, cwd))
423
541
  files.add(file);
424
542
  }
425
- return { hasLineNumberGrep, files };
543
+ return {
544
+ hasLineNumberGrep,
545
+ files,
546
+ context: context ?? { before: 0, after: 0 },
547
+ };
426
548
  }
427
549
  function dedupePushSearchRead(out, seen, loc) {
428
550
  if (!loc)
@@ -452,17 +574,25 @@ function parseGrepOutputSearchReads(output, cwd, singleFile) {
452
574
  * so the latter is only accepted when the command names exactly one source file.
453
575
  */
454
576
  export function extractGrepSearchReadsFromOutput(command, cwd, output) {
455
- const { hasLineNumberGrep, files } = collectGrepCommandFiles(command, cwd);
577
+ const { hasLineNumberGrep, files, context } = collectGrepCommandFiles(command, cwd);
456
578
  if (!hasLineNumberGrep)
457
579
  return [];
458
580
  const singleFile = files.size === 1 ? [...files][0] : undefined;
459
- return parseGrepOutputSearchReads(output, cwd, singleFile);
581
+ const locations = parseGrepOutputSearchReads(output, cwd, singleFile);
582
+ // Only the printed context lines were delivered to the model, so only they
583
+ // are credited. A bare grep credits its match line alone (#1904 item 2).
584
+ for (const loc of locations) {
585
+ loc.contextBefore = context.before;
586
+ loc.contextAfter = context.after;
587
+ }
588
+ return locations;
460
589
  }
461
590
  /**
462
591
  * Extract files a bash command WROTE/created, so the read-guard can treat them
463
592
  * as authored by the agent (mirrors the Write tool). Handles:
464
593
  * redirects: `> FILE`, `>> FILE`, `N> FILE`, `&> FILE` (with or without space)
465
- * tee [-a] FILE..., sed -i ... FILE, cp/mv/install ... DEST, touch FILE...
594
+ * tee [-a] FILE..., sed -i ... FILE, cp/mv/install ... DEST, touch FILE...,
595
+ * and common in-place formatter/fixer invocations with explicit file targets.
466
596
  *
467
597
  * Returns absolute paths. The file need not exist yet (it may be created) —
468
598
  * existence is confirmed later by recordWritten at tool_result time.
@@ -490,8 +620,7 @@ export function extractWrittenPathsFromCommand(command, cwd) {
490
620
  for (const tokens of commandSegments(command)) {
491
621
  if (tokens.length === 0)
492
622
  continue;
493
- const verb = path.basename(tokens[0] ?? "");
494
- const args = tokens.slice(1);
623
+ const { verb, args } = unwrapCommand(tokens);
495
624
  if (verb === "tee" || verb === "touch") {
496
625
  for (const a of args)
497
626
  if (!a.startsWith("-"))
@@ -502,10 +631,89 @@ export function extractWrittenPathsFromCommand(command, cwd) {
502
631
  add(a);
503
632
  }
504
633
  else if (verb === "cp" || verb === "mv" || verb === "install") {
634
+ // Known miss (#1668 review F3): the GNU `-t DEST`/`--target-directory`
635
+ // form puts the destination BEFORE the sources — this always treats
636
+ // the LAST non-flag argument as the destination, so `-t DEST SRC` and
637
+ // multi-source `-t DEST SRC1 SRC2...` are misread. Rare in agent-
638
+ // authored bash (source-before-dest is the common form); documented
639
+ // rather than fixed, since correctly parsing `-t` means recognizing it
640
+ // takes a value while telling it apart from every other single-letter
641
+ // flag this same branch already ignores.
505
642
  const files = args.filter((a) => !a.startsWith("-"));
506
643
  if (files.length >= 1)
507
644
  add(files[files.length - 1]); // destination
508
645
  }
646
+ else if (verb === "biome") {
647
+ const sub = args[0];
648
+ if ((sub === "format" || sub === "check") && args.includes("--write")) {
649
+ for (const file of formatterFileArgs(args, 1, new Set(["--config-path"])))
650
+ add(file);
651
+ }
652
+ }
653
+ else if (verb === "prettier" && args.includes("--write")) {
654
+ for (const file of formatterFileArgs(args, 0, new Set(["--config", "--ignore-path", "--parser", "--plugin"])))
655
+ add(file);
656
+ }
657
+ else if (verb === "eslint" && args.includes("--fix")) {
658
+ for (const file of formatterFileArgs(args, 0, new Set(["--config", "--ignore-path", "--parser", "--plugin"])))
659
+ add(file);
660
+ }
661
+ else if (verb === "ruff") {
662
+ const sub = args[0];
663
+ if (sub === "format" || args.includes("--fix")) {
664
+ const start = sub === "format" || sub === "check" ? 1 : 0;
665
+ for (const file of formatterFileArgs(args, start, new Set(["--config"])))
666
+ add(file);
667
+ }
668
+ }
669
+ else if (verb === "gofmt" && args.includes("-w")) {
670
+ for (const file of formatterFileArgs(args))
671
+ add(file);
672
+ }
673
+ else if (verb === "cargo" && args[0] === "fmt") {
674
+ // Bare cargo fmt is project-scoped and cannot be enumerated without a
675
+ // filesystem walk. Only explicit rustfmt operands after `--` are safe.
676
+ const separator = args.indexOf("--");
677
+ if (separator >= 0) {
678
+ for (const file of formatterFileArgs(args, separator + 1, new Set(["--edition", "--config-path"])))
679
+ add(file);
680
+ }
681
+ }
682
+ else if (verb === "rustfmt") {
683
+ for (const file of formatterFileArgs(args, 0, new Set(["--edition", "--config-path"])))
684
+ add(file);
685
+ }
686
+ else if (verb === "black") {
687
+ for (const file of formatterFileArgs(args, 0, new Set([
688
+ "--config",
689
+ "--exclude",
690
+ "--include",
691
+ "--line-length",
692
+ "--target-version",
693
+ ])))
694
+ add(file);
695
+ }
696
+ else if (verb === "clang-format" && args.includes("-i")) {
697
+ for (const file of formatterFileArgs(args, 0, new Set(["--style", "--fallback-style", "--assume-filename"])))
698
+ add(file);
699
+ }
700
+ else if (verb === "dotnet" && args[0] === "format") {
701
+ // dotnet format is normally solution-scoped. `--include` is the one
702
+ // invocation form that provides attributable source paths.
703
+ for (let i = 1; i < args.length; i += 1) {
704
+ if (args[i] === "--include" && args[i + 1]) {
705
+ for (const file of (args[i + 1] ?? "").split(","))
706
+ add(file);
707
+ i += 1;
708
+ }
709
+ else if (args[i]?.startsWith("--include=")) {
710
+ for (const file of (args[i] ?? "")
711
+ .slice("--include=".length)
712
+ .split(","))
713
+ add(file);
714
+ }
715
+ }
716
+ }
509
717
  else if (verb === "git") {
510
718
  // git ops that REWRITE working-tree files with explicit paths:
511
719
  // git checkout [<ref>] -- <files> git restore [opts] <files>
@@ -524,6 +732,75 @@ export function extractWrittenPathsFromCommand(command, cwd) {
524
732
  for (const a of fileArgs)
525
733
  add(a);
526
734
  }
735
+ else if (sub === "mv") {
736
+ // git mv SRC... DEST — the destination is a write, exactly like
737
+ // plain `mv` above (#1668 review F2). The source side is a delete,
738
+ // handled by extractDeletedPathsFromCommand.
739
+ const files = args.slice(1).filter((a) => !a.startsWith("-"));
740
+ if (files.length >= 1)
741
+ add(files[files.length - 1]);
742
+ }
743
+ }
744
+ }
745
+ return Array.from(out);
746
+ }
747
+ /**
748
+ * Extract files a bash command likely DELETED, for the type-3 (Deleted)
749
+ * watched-files gap (#1668): `rm FILE...`, `git rm FILE...`, and the SOURCE
750
+ * side of `mv SRC DEST` / `git mv SRC DEST` (the destination is a write,
751
+ * covered by `extractWrittenPathsFromCommand` — including for `git mv`,
752
+ * #1668 review F2).
753
+ *
754
+ * Deliberately narrow: only commands naming an explicit file target are
755
+ * handled. A bare directory op (`rm -rf dir/`, `git clean`) is skipped —
756
+ * resolving what vanished inside a directory would mean listing it before
757
+ * and after, the "stat the world" cost this module exists to avoid. The
758
+ * caller confirms each candidate by checking it no longer exists on disk;
759
+ * this function only proposes what the command named.
760
+ */
761
+ export function extractDeletedPathsFromCommand(command, cwd) {
762
+ const out = new Set();
763
+ const add = (token) => {
764
+ const abs = resolveCandidate(token, cwd);
765
+ if (abs)
766
+ out.add(abs);
767
+ };
768
+ // mv SRC... DEST / git mv SRC... DEST — every argument except the last is
769
+ // a source that vanishes from its original path once the move lands.
770
+ // Known miss (#1668 review F3): the same `-t DEST`/`--target-directory`
771
+ // form documented in `extractWrittenPathsFromCommand` misreads here too —
772
+ // this assumes source-before-destination order.
773
+ const addMoveSources = (args) => {
774
+ const files = args.filter((a) => !a.startsWith("-"));
775
+ if (files.length >= 2) {
776
+ for (const src of files.slice(0, -1))
777
+ add(src);
778
+ }
779
+ };
780
+ for (const tokens of commandSegments(command)) {
781
+ if (tokens.length === 0)
782
+ continue;
783
+ const verb = path.basename(tokens[0] ?? "");
784
+ const args = tokens.slice(1);
785
+ if (verb === "rm") {
786
+ for (const a of args)
787
+ if (!a.startsWith("-"))
788
+ add(a);
789
+ }
790
+ else if (verb === "git" && args[0] === "rm") {
791
+ const rmArgs = args.slice(1);
792
+ const dashDash = rmArgs.indexOf("--");
793
+ const fileArgs = dashDash >= 0
794
+ ? rmArgs.slice(dashDash + 1)
795
+ : rmArgs.filter((a) => !a.startsWith("-"));
796
+ for (const a of fileArgs)
797
+ add(a);
798
+ }
799
+ else if (verb === "git" && args[0] === "mv") {
800
+ addMoveSources(args.slice(1));
801
+ }
802
+ else if (verb === "mv") {
803
+ addMoveSources(args);
527
804
  }
528
805
  }
529
806
  return Array.from(out);
@@ -14,11 +14,34 @@ import { isFileKind } from "./file-kinds.js";
14
14
  import { getGlobalPiLensDir } from "./file-utils.js";
15
15
  import { findGlobalBinary } from "./package-manager.js";
16
16
  import { safeSpawnAsync } from "./safe-spawn.js";
17
+ import { createSingleFlight } from "./single-flight.js";
17
18
  import { biomeConfigArgs } from "./tool-policy.js";
18
- import { resolveManagedToolClient } from "./dispatch/runners/utils/runner-helpers.js";
19
+ import { resolveManagedToolClient, } from "./dispatch/runners/utils/runner-helpers.js";
20
+ import { classifyProbeFailure, describeInstallAttempt, createAvailabilityLatch, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
19
21
  // --- Client ---
22
+ const PROBE_TIMEOUT_MS = 10_000;
20
23
  export class BiomeClient {
21
- biomeAvailable = null;
24
+ /**
25
+ * Availability memo, backed by the shared transient-aware latch (#1476).
26
+ *
27
+ * Biome is the hot-path formatter: before this, ANY probe failure — a
28
+ * timeout included — collapsed to `{ outcome: "missing" }`, which both
29
+ * triggered an install and latched `false` for the life of the process. One
30
+ * slow first format then disabled formatting until restart.
31
+ */
32
+ availabilityLatch = createAvailabilityLatch();
33
+ /** Cause of the last probe, read when the resolution comes back. */
34
+ lastProbeCause = null;
35
+ /**
36
+ * Measurements of the last failed probe, held until the latch decides how
37
+ * long the verdict lasts. The decision record is emitted after that, so it
38
+ * can carry `retryAfterMs` — a latch you can see is worth little if the
39
+ * retry schedule is not greppable beside it (#1474).
40
+ */
41
+ lastProbeElapsedMs = 0;
42
+ lastProbeHostStallMs = 0;
43
+ /** Raw facts from the last probe, carried into the decision record (#1500). */
44
+ lastProbeEvidence;
22
45
  // Per-cwd cache of the resolved biome binary. Keying by cwd matters in
23
46
  // monorepos where different sub-packages each ship their own biome
24
47
  // installation; sharing one slot across the whole client would cause
@@ -28,7 +51,11 @@ export class BiomeClient {
28
51
  // (lives under ~/.pi-lens/tools), so it's stored separately from the
29
52
  // per-cwd cache and used as a final fallback before npx.
30
53
  autoInstalledBinaryPath = null;
31
- ensureInFlight = null;
54
+ /**
55
+ * At-most-one probe/auto-install in flight, via the shared primitive
56
+ * (#1753). One instance owns one question, so the key is a constant.
57
+ */
58
+ ensureFlight = createSingleFlight();
32
59
  log;
33
60
  constructor(verbose = false) {
34
61
  this.log = verbose
@@ -50,13 +77,14 @@ export class BiomeClient {
50
77
  const cached = this.localBinaryByCwd.get(resolveCwd);
51
78
  if (cached)
52
79
  return { cmd: cached, args: [] };
53
- if (this.autoInstalledBinaryPath) {
54
- return { cmd: this.autoInstalledBinaryPath, args: [] };
55
- }
56
- // Walk up from cwd looking for node_modules/.bin/biome.
57
- // Also check ~/.pi-lens/tools (where ensureTool("biome") auto-installs),
58
- // so we avoid the ~1.5s `npx @biomejs/biome --version` fallback when
59
- // the tool is already installed but not in the project's node_modules.
80
+ // Walk up from cwd looking for node_modules/.bin/biome BEFORE trusting
81
+ // `autoInstalledBinaryPath` (#1731). That field is set once, the first
82
+ // time `ensureAvailable()` auto-installs for ANY cwd this session, and
83
+ // every later call for every OTHER cwd short-circuited on it — so a
84
+ // project that ships its own biome never won once the session's first
85
+ // managed install had already happened. Project-local-first (discipline
86
+ // B, #1721) means the project's pinned version and config resolution
87
+ // always outrank a managed copy, autoinstalled or not.
60
88
  // On Windows prefer .cmd (native batch) over the sh wrapper — 2x faster.
61
89
  const isWin = process.platform === "win32";
62
90
  const piLensBin = path.join(getGlobalPiLensDir(), "tools", "node_modules", ".bin");
@@ -64,20 +92,29 @@ export class BiomeClient {
64
92
  ? [
65
93
  path.join(resolveCwd, "node_modules", ".bin", "biome.cmd"),
66
94
  path.join(resolveCwd, "node_modules", ".bin", "biome"),
67
- path.join(piLensBin, "biome.cmd"),
68
- path.join(piLensBin, "biome"),
69
95
  ]
70
- : [
71
- path.join(resolveCwd, "node_modules", ".bin", "biome"),
72
- path.join(resolveCwd, "node_modules", ".bin", "biome.cmd"),
73
- path.join(piLensBin, "biome"),
74
- ];
96
+ : [path.join(resolveCwd, "node_modules", ".bin", "biome")];
75
97
  for (const p of candidates) {
76
98
  if (fs.existsSync(p)) {
77
99
  this.localBinaryByCwd.set(resolveCwd, p);
78
100
  return { cmd: p, args: [] };
79
101
  }
80
102
  }
103
+ if (this.autoInstalledBinaryPath) {
104
+ return { cmd: this.autoInstalledBinaryPath, args: [] };
105
+ }
106
+ // Also check ~/.pi-lens/tools (where ensureTool("biome") auto-installs),
107
+ // so we avoid the ~1.5s `npx @biomejs/biome --version` fallback when the
108
+ // tool is already installed but not in the project's node_modules.
109
+ const managedCandidates = isWin
110
+ ? [path.join(piLensBin, "biome.cmd"), path.join(piLensBin, "biome")]
111
+ : [path.join(piLensBin, "biome")];
112
+ for (const p of managedCandidates) {
113
+ if (fs.existsSync(p)) {
114
+ this.localBinaryByCwd.set(resolveCwd, p);
115
+ return { cmd: p, args: [] };
116
+ }
117
+ }
81
118
  // Any package manager's global bin dir (npm/pnpm/yarn/bun) before npx —
82
119
  // catches `pnpm add -g @biomejs/biome` installs that PATH misses (#375).
83
120
  const global = await findGlobalBinary("biome");
@@ -96,41 +133,143 @@ export class BiomeClient {
96
133
  * Ensure Biome is available, auto-installing if necessary.
97
134
  * Prefer this over isAvailable() for auto-install behavior.
98
135
  *
99
- * Re-entrancy safe: concurrent first-time callers share a single
100
- * `ensureInFlight` promise so probing/auto-install isn't duplicated.
101
- * Mirrors the dedupe pattern in `SgRunner` / `KnipClient` /
102
- * `DependencyChecker`.
136
+ * Re-entrancy safe: concurrent first-time callers share one flight, so
137
+ * probing/auto-install isn't duplicated. The share and the clear-in-finally
138
+ * belong to `singleFlight` (#1753); this method owns only the latch
139
+ * short-circuit above it. #1690 exists because the hand-rolled clear here
140
+ * was never exercised by a test.
141
+ *
142
+ * The memo returns `null` when the last verdict was transient and its
143
+ * cooldown expired, which re-enters the probe: "biome is not installed" is a
144
+ * fact worth caching, "the probe timed out" is a moment worth retrying.
103
145
  */
104
146
  async ensureAvailable() {
105
- if (this.biomeAvailable !== null)
106
- return this.biomeAvailable;
107
- if (this.ensureInFlight)
108
- return this.ensureInFlight;
109
- this.ensureInFlight = this.doEnsureAvailable();
147
+ const memo = this.availabilityLatch.read();
148
+ if (memo !== null)
149
+ return memo;
150
+ return this.ensureFlight.run("biome", () => this.doEnsureAvailable());
151
+ }
152
+ /**
153
+ * Probe `biome --version` and classify the failure through the shared
154
+ * policy. Only a durable verdict (`missing` / `non-installable`) may reach
155
+ * the install branch of `resolveManagedToolClient`; a transient one returns
156
+ * as `transient`, which that seam passes straight back without installing.
157
+ */
158
+ async probeBiome() {
159
+ // The probe budget is enforced by a HOST-side timer, so a stalled event
160
+ // loop expires it while the child is still healthy. Measure the stall
161
+ // that overlapped the window and let the classifier see it (#1467).
162
+ const sampler = startHostStallSampler();
163
+ const startedAt = Date.now();
164
+ let result;
165
+ let hostStallMs;
110
166
  try {
111
- return await this.ensureInFlight;
167
+ result = await this.spawnBiomeAsync(["--version"], PROBE_TIMEOUT_MS);
112
168
  }
113
169
  finally {
114
- this.ensureInFlight = null;
170
+ hostStallMs = sampler.stop();
115
171
  }
172
+ const elapsedMs = Date.now() - startedAt;
173
+ if (!result.error && result.status === 0) {
174
+ this.lastProbeCause = "ok";
175
+ logAvailabilityDecision({
176
+ tool: "biome",
177
+ verdict: "available",
178
+ outcome: "success",
179
+ cause: "ok",
180
+ elapsedMs,
181
+ latched: true,
182
+ hostStallMs,
183
+ budgetMs: PROBE_TIMEOUT_MS,
184
+ });
185
+ return { outcome: "success", value: true };
186
+ }
187
+ // `unclassifiedFailureOutcome: "missing"` preserves the pre-#1476
188
+ // meaning of a plain non-zero exit (npx reporting no biome package):
189
+ // still "missing", still installable. Only the timeout/abort arm changes.
190
+ const { outcome, cause, evidence } = classifyProbeFailure(result, {
191
+ hostStallMs,
192
+ unclassifiedFailureOutcome: "missing",
193
+ });
194
+ // `classifyProbeFailure` is typed over the full outcome union but never
195
+ // returns "success" for a failed probe; narrow it for the seam's type.
196
+ const failureOutcome = outcome === "success" ? "non-installable" : outcome;
197
+ this.lastProbeCause = cause;
198
+ this.lastProbeElapsedMs = elapsedMs;
199
+ this.lastProbeHostStallMs = hostStallMs;
200
+ this.lastProbeEvidence = evidence;
201
+ // The record is emitted by `doEnsureAvailable`, once the latch has said how
202
+ // long this verdict holds.
203
+ return { outcome: failureOutcome };
116
204
  }
117
205
  async doEnsureAvailable() {
118
- const outcome = await resolveManagedToolClient({
206
+ const resolved = await resolveManagedToolClient({
119
207
  toolId: "biome",
120
208
  cwd: process.cwd(),
121
- probe: async () => {
122
- const result = await this.spawnBiomeAsync(["--version"], 10000);
123
- return !result.error && result.status === 0
124
- ? { outcome: "success", value: true }
125
- : { outcome: "missing" };
126
- },
209
+ probe: () => this.probeBiome(),
127
210
  acceptInstalled: (installedPath) => {
128
211
  this.autoInstalledBinaryPath = installedPath;
129
212
  return true;
130
213
  },
131
214
  });
132
- this.biomeAvailable = outcome.outcome === "success";
133
- return this.biomeAvailable;
215
+ if (resolved.outcome === "success") {
216
+ this.availabilityLatch.noteAvailable();
217
+ // The probe itself logs a clean hit. This arm is the other way to
218
+ // succeed — the install repaired a durable miss — and it needs its own
219
+ // record, or "biome went missing and came back" reads as silence.
220
+ if (this.lastProbeCause !== null && this.lastProbeCause !== "ok") {
221
+ logAvailabilityDecision({
222
+ tool: "biome",
223
+ verdict: "available",
224
+ outcome: "success",
225
+ cause: "ok",
226
+ elapsedMs: this.lastProbeElapsedMs,
227
+ latched: true,
228
+ hostStallMs: this.lastProbeHostStallMs,
229
+ budgetMs: PROBE_TIMEOUT_MS,
230
+ });
231
+ }
232
+ return true;
233
+ }
234
+ // A transient probe expires; a durable verdict is remembered for the
235
+ // session exactly as before.
236
+ const cause = this.lastProbeCause ?? "not-found";
237
+ const retryAfterMs = this.availabilityLatch.noteUnavailable(resolved.outcome, cause);
238
+ // `missing` IS the install-failure arm (#1500 review). `resolveManagedToolClient`
239
+ // only reaches the installer when the probe said `missing`, so every `missing`
240
+ // verdict here has already been through it — declined, suppressed, or tried
241
+ // and failed, which is what `describeInstallAttempt` separates. The earlier
242
+ // marker sat on the `non-installable` arm instead, and that arm cannot happen
243
+ // for biome at all: its `acceptInstalled` always accepts. So the one row that
244
+ // needed the fact was the one shipping without it.
245
+ let installEvidence;
246
+ if (resolved.outcome === "missing") {
247
+ const { getInstallAttempt } = await import("./installer/index.js");
248
+ installEvidence = describeInstallAttempt(getInstallAttempt("biome"));
249
+ }
250
+ const evidence = { ...this.lastProbeEvidence, ...installEvidence };
251
+ logAvailabilityDecision({
252
+ tool: "biome",
253
+ verdict: "unavailable",
254
+ outcome: resolved.outcome,
255
+ cause,
256
+ elapsedMs: this.lastProbeElapsedMs,
257
+ latched: resolved.outcome !== "transient",
258
+ hostStallMs: this.lastProbeHostStallMs,
259
+ ...(retryAfterMs > 0 && { retryAfterMs }),
260
+ budgetMs: PROBE_TIMEOUT_MS,
261
+ // Per arm: a probe-derived verdict says `probe`, while a verdict asserted
262
+ // by the install seam — or a cause that fell back because no probe ran —
263
+ // says `caller`.
264
+ classifiedBy: this.lastProbeCause === null || resolved.outcome === "non-installable"
265
+ ? "caller"
266
+ : "probe",
267
+ ...(Object.keys(evidence).length > 0 && { evidence }),
268
+ });
269
+ if (resolved.outcome === "transient") {
270
+ this.log("biome availability probe timed out; will retry (not installing)");
271
+ }
272
+ return false;
134
273
  }
135
274
  /**
136
275
  * Check if a file is supported by Biome