@herbertgao/pi-extensions 2026.8.4 → 2026.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +53 -14
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
  14. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  15. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +18 -0
  16. package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
  17. package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
  18. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
  19. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +20 -4
  21. package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
  22. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
  25. package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
  26. package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
  27. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
  28. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
  29. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
  30. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
  31. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
  32. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  33. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
  36. package/node_modules/@narumitw/pi-btw/README.md +32 -24
  37. package/node_modules/@narumitw/pi-btw/package.json +6 -6
  38. package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
  39. package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
  40. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  41. package/node_modules/pi-lens/CHANGELOG.md +2535 -954
  42. package/node_modules/pi-lens/README.md +13 -0
  43. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
  44. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
  45. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
  46. package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
  47. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
  48. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
  49. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
  50. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
  51. package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
  52. package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
  53. package/node_modules/pi-lens/dist/clients/biome-client.js +136 -24
  54. package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
  55. package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
  56. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
  57. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  58. package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
  59. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
  60. package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
  61. package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
  62. package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
  63. package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
  64. package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
  65. package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
  66. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
  67. package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
  68. package/node_modules/pi-lens/dist/clients/console-guard-install.js +26 -0
  69. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
  70. package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
  71. package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
  72. package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
  73. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
  74. package/node_modules/pi-lens/dist/clients/dependency-checker.js +361 -77
  75. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
  76. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
  77. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
  78. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
  79. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
  80. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  81. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
  82. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
  83. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1172 -683
  84. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
  86. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
  87. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +47 -6
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +473 -42
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  114. package/node_modules/pi-lens/dist/clients/disposition-publish.js +18 -4
  115. package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
  116. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  117. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
  118. package/node_modules/pi-lens/dist/clients/extension-log.js +432 -0
  119. package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
  120. package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
  121. package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
  122. package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
  123. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  124. package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
  125. package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
  126. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
  127. package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
  128. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
  129. package/node_modules/pi-lens/dist/clients/git-guard.js +678 -11
  130. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
  131. package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
  132. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
  133. package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
  134. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
  135. package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
  136. package/node_modules/pi-lens/dist/clients/installer/index.js +395 -100
  137. package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
  138. package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
  139. package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
  140. package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
  141. package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
  142. package/node_modules/pi-lens/dist/clients/latency-logger.js +72 -1
  143. package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
  144. package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
  145. package/node_modules/pi-lens/dist/clients/lens-events.js +63 -9
  146. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  147. package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
  148. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
  149. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
  150. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  151. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
  152. package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
  153. package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
  154. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
  155. package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
  156. package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
  157. package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
  158. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  159. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  160. package/node_modules/pi-lens/dist/clients/lsp/server.js +437 -86
  161. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
  162. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  163. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
  164. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  165. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
  166. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
  167. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +127 -32
  168. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
  169. package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
  170. package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
  171. package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
  172. package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
  173. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  174. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
  175. package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
  176. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
  177. package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
  178. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
  179. package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
  180. package/node_modules/pi-lens/dist/clients/pipeline.js +76 -15
  181. package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
  182. package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
  183. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
  184. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
  185. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  186. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  187. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
  188. package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
  189. package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
  190. package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
  191. package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
  192. package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
  193. package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
  194. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
  195. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
  196. package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
  197. package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
  198. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
  199. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
  200. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
  201. package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
  202. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
  203. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
  204. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
  205. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
  206. package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +257 -77
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -117
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +342 -33
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
  217. package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
  218. package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
  219. package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
  220. package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
  221. package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -0
  222. package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
  223. package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
  224. package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
  225. package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
  226. package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
  227. package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
  228. package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
  229. package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
  230. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
  233. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
  234. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
  235. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  237. package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
  238. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
  239. package/node_modules/pi-lens/dist/clients/warm-attach.js +50 -13
  240. package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
  241. package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
  242. package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
  243. package/node_modules/pi-lens/dist/index.js +51785 -36118
  244. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
  245. package/node_modules/pi-lens/dist/mcp/server.js +219 -46
  246. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  247. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  248. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
  249. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  250. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
  251. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
  252. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
  253. package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
  254. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  255. package/node_modules/pi-lens/docs/agent-guide.md +40 -15
  256. package/node_modules/pi-lens/docs/agent-tools.md +21 -10
  257. package/node_modules/pi-lens/docs/analysisall.md +192 -0
  258. package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
  259. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +14 -3
  260. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
  261. package/node_modules/pi-lens/docs/environment-variables.md +44 -0
  262. package/node_modules/pi-lens/docs/fable.md +27 -0
  263. package/node_modules/pi-lens/docs/features.md +45 -3
  264. package/node_modules/pi-lens/docs/globalconfig.md +56 -1
  265. package/node_modules/pi-lens/docs/language-coverage.md +4 -3
  266. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
  267. package/node_modules/pi-lens/docs/mcp.md +37 -4
  268. package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
  269. package/node_modules/pi-lens/docs/settings.md +18 -1
  270. package/node_modules/pi-lens/docs/tools.md +4 -1
  271. package/node_modules/pi-lens/docs/usage.md +41 -5
  272. package/node_modules/pi-lens/docs/word-index.md +35 -0
  273. package/node_modules/pi-lens/package.json +10 -10
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  300. package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  301. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  302. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +134 -2
  303. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
  304. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  305. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
  306. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
  307. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  308. package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
  309. package/node_modules/pi-mcp-adapter/README.md +38 -4
  310. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  311. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
  312. package/node_modules/pi-mcp-adapter/cli.js +25 -4
  313. package/node_modules/pi-mcp-adapter/commands.ts +15 -7
  314. package/node_modules/pi-mcp-adapter/config.ts +4 -4
  315. package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
  316. package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
  317. package/node_modules/pi-mcp-adapter/index.ts +8 -3
  318. package/node_modules/pi-mcp-adapter/init.ts +29 -5
  319. package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
  320. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  321. package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
  322. package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
  323. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
  324. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  325. package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
  326. package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
  327. package/node_modules/pi-mcp-adapter/package.json +4 -3
  328. package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
  329. package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
  330. package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
  331. package/node_modules/pi-mcp-adapter/server-manager.ts +41 -7
  332. package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
  333. package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
  334. package/node_modules/pi-mcp-adapter/tool-metadata.ts +62 -4
  335. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
  336. package/node_modules/pi-mcp-adapter/types.ts +176 -27
  337. package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
  338. package/node_modules/pi-web-access/CHANGELOG.md +14 -0
  339. package/node_modules/pi-web-access/README.md +18 -12
  340. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  341. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  342. package/node_modules/pi-web-access/curator-page.ts +5 -3
  343. package/node_modules/pi-web-access/curator-server.ts +2 -1
  344. package/node_modules/pi-web-access/extract.ts +106 -34
  345. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  346. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  347. package/node_modules/pi-web-access/gemini-search.ts +18 -4
  348. package/node_modules/pi-web-access/index.ts +120 -48
  349. package/node_modules/pi-web-access/package.json +2 -2
  350. package/node_modules/pi-web-access/summary-review.ts +11 -5
  351. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  352. package/package.json +12 -13
  353. package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
  354. package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
  355. package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
  356. package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
  357. package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
  358. package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
  359. package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
  360. package/node_modules/pi-lens/dist/clients/types.js +0 -11
@@ -15,10 +15,8 @@
15
15
  */
16
16
  import * as nodeFs from "node:fs";
17
17
  import * as path from "node:path";
18
+ import { isReadableSourceFile } from "./file-kinds.js";
18
19
  import { countFileLines } from "./read-guard-tool-lines.js";
19
- // Source-ish extensions worth registering. Anchored end-check → linear (no
20
- // catastrophic backtracking).
21
- const READABLE_EXT_RE = /\.(?:ts|tsx|js|jsx|mjs|cjs|py|sh|rs|go|cs|java|kt|rb|php|c|cpp|cc|h|hpp|json|jsonc|yaml|yml|toml|md|txt|env|cfg|conf|ini|html|css|scss|less|xml|sql|vue|svelte)$/i;
22
20
  function stripQuotes(token) {
23
21
  if (token.length >= 2) {
24
22
  const first = token[0];
@@ -32,14 +30,123 @@ function stripQuotes(token) {
32
30
  function stripAnsi(value) {
33
31
  return value.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
34
32
  }
35
- function tokenizeSegment(segment) {
36
- const matches = segment.match(/'[^']*'|"(?:[^"\\]|\\.)*"|\S+/g);
37
- return matches ?? [];
33
+ export function tokenizeShellCommand(command) {
34
+ const segments = [];
35
+ let tokens = [];
36
+ let word = "";
37
+ let quote;
38
+ let escaped = false;
39
+ let unsupported = false;
40
+ let atTokenStart = true;
41
+ const flushWord = () => {
42
+ if (word)
43
+ tokens.push(word);
44
+ word = "";
45
+ atTokenStart = true;
46
+ };
47
+ const flushSegment = () => {
48
+ flushWord();
49
+ if (tokens.length > 0 || unsupported)
50
+ segments.push({ tokens, unsupported });
51
+ tokens = [];
52
+ unsupported = false;
53
+ atTokenStart = true;
54
+ };
55
+ for (let i = 0; i < command.length; i++) {
56
+ const ch = command[i];
57
+ const next = command[i + 1];
58
+ if (quote === "single") {
59
+ if (ch === "'")
60
+ quote = undefined;
61
+ else
62
+ word += ch;
63
+ continue;
64
+ }
65
+ if (quote === "double") {
66
+ if (escaped) {
67
+ word += ch;
68
+ escaped = false;
69
+ }
70
+ else if (ch === "\\")
71
+ escaped = true;
72
+ else if (ch === '"')
73
+ quote = undefined;
74
+ else
75
+ word += ch;
76
+ continue;
77
+ }
78
+ if (escaped) {
79
+ // A backslash-newline is a shell line continuation, not part of the
80
+ // command argument. Keeping the newline here makes a continued git command
81
+ // visible to command guards.
82
+ if (ch !== "\n")
83
+ word += ch;
84
+ escaped = false;
85
+ atTokenStart = false;
86
+ continue;
87
+ }
88
+ if (ch === "\\") {
89
+ // Bash uses backslash as a general escape, but command inputs on
90
+ // Windows routinely contain native paths (C:\\src\\file.ts). Preserve
91
+ // backslashes before ordinary path characters while still honoring
92
+ // shell escapes and line continuations.
93
+ if (next === "\n" || /[\s\\'\";$|&<>]/.test(next ?? "")) {
94
+ escaped = true;
95
+ }
96
+ else {
97
+ word += ch;
98
+ atTokenStart = false;
99
+ }
100
+ atTokenStart = false;
101
+ continue;
102
+ }
103
+ if (ch === "'") {
104
+ quote = "single";
105
+ atTokenStart = false;
106
+ continue;
107
+ }
108
+ if (ch === '"') {
109
+ quote = "double";
110
+ atTokenStart = false;
111
+ continue;
112
+ }
113
+ if (ch === "#" && atTokenStart) {
114
+ while (i + 1 < command.length && command[i + 1] !== "\n")
115
+ i++;
116
+ continue;
117
+ }
118
+ if (/\s/.test(ch)) {
119
+ flushWord();
120
+ continue;
121
+ }
122
+ if (ch === ";" || ch === "\n" || ch === "|" || ch === "&") {
123
+ flushWord();
124
+ if (ch === "|" && next === "|")
125
+ i++;
126
+ else if (ch === "&" && next === "&")
127
+ i++;
128
+ else if (ch === "|" || ch === "&")
129
+ unsupported = true;
130
+ flushSegment();
131
+ continue;
132
+ }
133
+ if (ch === "<" || ch === ">") {
134
+ flushWord();
135
+ unsupported = true;
136
+ continue;
137
+ }
138
+ word += ch;
139
+ atTokenStart = false;
140
+ }
141
+ if (quote || escaped)
142
+ unsupported = true;
143
+ flushSegment();
144
+ return segments;
38
145
  }
39
146
  /** Resolve a token to an absolute path if it looks like a source file. */
40
147
  function resolveCandidate(token, cwd) {
41
148
  const cleaned = stripQuotes(token);
42
- if (!cleaned || cleaned.startsWith("-") || !READABLE_EXT_RE.test(cleaned)) {
149
+ if (!cleaned || cleaned.startsWith("-") || !isReadableSourceFile(cleaned)) {
43
150
  return null;
44
151
  }
45
152
  return path.isAbsolute(cleaned) ? cleaned : path.resolve(cwd, cleaned);
@@ -52,8 +159,67 @@ function parseCountFlag(token) {
52
159
  const n = Number.parseInt(digits, 10);
53
160
  return Number.isFinite(n) && n > 0 ? n : undefined;
54
161
  }
55
- function splitSegments(command) {
56
- return command.split(/&&|\|\||[;|\n]/);
162
+ function commandSegments(command) {
163
+ return tokenizeShellCommand(command).map((segment) => segment.tokens);
164
+ }
165
+ /** Find redirect targets without treating quoted `>` characters as syntax. */
166
+ function extractRedirectTargets(command) {
167
+ const targets = [];
168
+ let quote;
169
+ let escaped = false;
170
+ for (let i = 0; i < command.length; i += 1) {
171
+ const ch = command[i];
172
+ if (quote === "single") {
173
+ if (ch === "'")
174
+ quote = undefined;
175
+ continue;
176
+ }
177
+ if (quote === "double") {
178
+ if (escaped)
179
+ escaped = false;
180
+ else if (ch === "\\")
181
+ escaped = true;
182
+ else if (ch === '"')
183
+ quote = undefined;
184
+ continue;
185
+ }
186
+ if (escaped) {
187
+ escaped = false;
188
+ continue;
189
+ }
190
+ if (ch === "\\") {
191
+ escaped = true;
192
+ continue;
193
+ }
194
+ if (ch === "'") {
195
+ quote = "single";
196
+ continue;
197
+ }
198
+ if (ch === '"') {
199
+ quote = "double";
200
+ continue;
201
+ }
202
+ if (ch !== ">")
203
+ continue;
204
+ while (i + 1 < command.length && /\s/.test(command[i + 1]))
205
+ i += 1;
206
+ if (command[i + 1] === ">")
207
+ i += 1;
208
+ while (i + 1 < command.length && /\s/.test(command[i + 1]))
209
+ i += 1;
210
+ let target = "";
211
+ for (i += 1; i < command.length; i += 1) {
212
+ const next = command[i];
213
+ if (/\s/.test(next) || next === ";" || next === "|" || next === "&") {
214
+ i -= 1;
215
+ break;
216
+ }
217
+ target += next;
218
+ }
219
+ if (target)
220
+ targets.push(target);
221
+ }
222
+ return targets;
57
223
  }
58
224
  /**
59
225
  * Extract the line ranges a bash command explicitly showed the agent.
@@ -93,11 +259,9 @@ export function extractReadPathsFromCommand(command, cwd) {
93
259
  seen.add(key);
94
260
  spans.push({ filePath: file.abs, offset, limit });
95
261
  };
96
- for (const rawSegment of splitSegments(command)) {
97
- const segment = rawSegment.trim();
98
- if (!segment)
262
+ for (const tokens of commandSegments(command)) {
263
+ if (tokens.length === 0)
99
264
  continue;
100
- const tokens = segment.split(/\s+/);
101
265
  const verb = path.basename(tokens[0] ?? "");
102
266
  const args = tokens.slice(1);
103
267
  if (["cat", "bat", "less", "more", "nl"].includes(verb)) {
@@ -247,8 +411,7 @@ function parseGrepLineWithoutFile(line, file) {
247
411
  function collectGrepCommandFiles(command, cwd) {
248
412
  const files = new Set();
249
413
  let hasLineNumberGrep = false;
250
- for (const rawSegment of splitSegments(command)) {
251
- const tokens = tokenizeSegment(rawSegment.trim());
414
+ for (const tokens of commandSegments(command)) {
252
415
  const verb = path.basename(stripQuotes(tokens[0] ?? ""));
253
416
  if (verb !== "grep" && verb !== "egrep" && verb !== "fgrep")
254
417
  continue;
@@ -311,15 +474,22 @@ export function extractWrittenPathsFromCommand(command, cwd) {
311
474
  if (abs)
312
475
  out.add(abs);
313
476
  };
314
- for (const rawSegment of splitSegments(command)) {
315
- const segment = rawSegment.trim();
316
- if (!segment)
477
+ for (const tokens of commandSegments(command)) {
478
+ if (tokens.length === 0)
479
+ continue;
480
+ // Redirect targets are collected by a quote-aware scanner. The shared
481
+ // tokenizer supplies the normalized command arguments; it deliberately
482
+ // does not expose shell redirection operators as arguments.
483
+ for (const target of extractRedirectTargets(command))
484
+ add(target);
485
+ // A command may contain multiple segments; only the first pass should
486
+ // attach redirects, otherwise targets would be duplicated harmlessly but
487
+ // needlessly rescanned.
488
+ break;
489
+ }
490
+ for (const tokens of commandSegments(command)) {
491
+ if (tokens.length === 0)
317
492
  continue;
318
- // Redirect targets: the token after `>` / `>>` (optionally prefixed by a
319
- // file descriptor like `2>` or `&>`, with or without a space).
320
- for (const m of segment.matchAll(/>>?\s*([^\s>|&]+)/g))
321
- add(m[1]);
322
- const tokens = segment.split(/\s+/);
323
493
  const verb = path.basename(tokens[0] ?? "");
324
494
  const args = tokens.slice(1);
325
495
  if (verb === "tee" || verb === "touch") {
@@ -7,15 +7,38 @@
7
7
  * Requires: npm install @biomejs/biome (or npx @biomejs/biome)
8
8
  * Docs: https://biomejs.dev/
9
9
  */
10
+ import { createSubsystemLogger } from "./extension-log.js";
10
11
  import * as fs from "node:fs";
11
12
  import * as path from "node:path";
12
13
  import { isFileKind } from "./file-kinds.js";
13
14
  import { getGlobalPiLensDir } from "./file-utils.js";
14
15
  import { findGlobalBinary } from "./package-manager.js";
15
16
  import { safeSpawnAsync } from "./safe-spawn.js";
17
+ import { biomeConfigArgs } from "./tool-policy.js";
18
+ import { resolveManagedToolClient, } from "./dispatch/runners/utils/runner-helpers.js";
19
+ import { classifyProbeFailure, createAvailabilityLatch, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
16
20
  // --- Client ---
21
+ const PROBE_TIMEOUT_MS = 10_000;
17
22
  export class BiomeClient {
18
- biomeAvailable = null;
23
+ /**
24
+ * Availability memo, backed by the shared transient-aware latch (#1476).
25
+ *
26
+ * Biome is the hot-path formatter: before this, ANY probe failure — a
27
+ * timeout included — collapsed to `{ outcome: "missing" }`, which both
28
+ * triggered an install and latched `false` for the life of the process. One
29
+ * slow first format then disabled formatting until restart.
30
+ */
31
+ availabilityLatch = createAvailabilityLatch();
32
+ /** Cause of the last probe, read when the resolution comes back. */
33
+ lastProbeCause = null;
34
+ /**
35
+ * Measurements of the last failed probe, held until the latch decides how
36
+ * long the verdict lasts. The decision record is emitted after that, so it
37
+ * can carry `retryAfterMs` — a latch you can see is worth little if the
38
+ * retry schedule is not greppable beside it (#1474).
39
+ */
40
+ lastProbeElapsedMs = 0;
41
+ lastProbeHostStallMs = 0;
19
42
  // Per-cwd cache of the resolved biome binary. Keying by cwd matters in
20
43
  // monorepos where different sub-packages each ship their own biome
21
44
  // installation; sharing one slot across the whole client would cause
@@ -29,7 +52,7 @@ export class BiomeClient {
29
52
  log;
30
53
  constructor(verbose = false) {
31
54
  this.log = verbose
32
- ? (msg) => console.error(`[biome] ${msg}`)
55
+ ? createSubsystemLogger("biome")
33
56
  : () => { };
34
57
  }
35
58
  /**
@@ -87,7 +110,7 @@ export class BiomeClient {
87
110
  }
88
111
  async spawnBiomeAsync(args, timeout = 15000, cwd) {
89
112
  const { cmd, args: prefix } = await this.getBiomeBinary(cwd);
90
- return safeSpawnAsync(cmd, [...prefix, ...args], { timeout });
113
+ return safeSpawnAsync(cmd, [...prefix, ...args], { timeout, cwd });
91
114
  }
92
115
  /**
93
116
  * Ensure Biome is available, auto-installing if necessary.
@@ -97,10 +120,15 @@ export class BiomeClient {
97
120
  * `ensureInFlight` promise so probing/auto-install isn't duplicated.
98
121
  * Mirrors the dedupe pattern in `SgRunner` / `KnipClient` /
99
122
  * `DependencyChecker`.
123
+ *
124
+ * The memo returns `null` when the last verdict was transient and its
125
+ * cooldown expired, which re-enters the probe: "biome is not installed" is a
126
+ * fact worth caching, "the probe timed out" is a moment worth retrying.
100
127
  */
101
128
  async ensureAvailable() {
102
- if (this.biomeAvailable !== null)
103
- return this.biomeAvailable;
129
+ const memo = this.availabilityLatch.read();
130
+ if (memo !== null)
131
+ return memo;
104
132
  if (this.ensureInFlight)
105
133
  return this.ensureInFlight;
106
134
  this.ensureInFlight = this.doEnsureAvailable();
@@ -111,27 +139,105 @@ export class BiomeClient {
111
139
  this.ensureInFlight = null;
112
140
  }
113
141
  }
114
- async doEnsureAvailable() {
115
- // Check if already available
116
- const result = await this.spawnBiomeAsync(["--version"], 10000);
142
+ /**
143
+ * Probe `biome --version` and classify the failure through the shared
144
+ * policy. Only a durable verdict (`missing` / `non-installable`) may reach
145
+ * the install branch of `resolveManagedToolClient`; a transient one returns
146
+ * as `transient`, which that seam passes straight back without installing.
147
+ */
148
+ async probeBiome() {
149
+ // The probe budget is enforced by a HOST-side timer, so a stalled event
150
+ // loop expires it while the child is still healthy. Measure the stall
151
+ // that overlapped the window and let the classifier see it (#1467).
152
+ const sampler = startHostStallSampler();
153
+ const startedAt = Date.now();
154
+ let result;
155
+ let hostStallMs;
156
+ try {
157
+ result = await this.spawnBiomeAsync(["--version"], PROBE_TIMEOUT_MS);
158
+ }
159
+ finally {
160
+ hostStallMs = sampler.stop();
161
+ }
162
+ const elapsedMs = Date.now() - startedAt;
117
163
  if (!result.error && result.status === 0) {
118
- this.biomeAvailable = true;
119
- return true;
164
+ this.lastProbeCause = "ok";
165
+ logAvailabilityDecision({
166
+ tool: "biome",
167
+ verdict: "available",
168
+ outcome: "success",
169
+ cause: "ok",
170
+ elapsedMs,
171
+ latched: true,
172
+ hostStallMs,
173
+ budgetMs: PROBE_TIMEOUT_MS,
174
+ });
175
+ return { outcome: "success", value: true };
120
176
  }
121
- // Auto-install via pi-lens installer
122
- this.log("Biome not found, attempting auto-install...");
123
- const { ensureTool } = await import("./installer/index.js");
124
- const installedPath = await ensureTool("biome");
125
- if (installedPath) {
126
- this.log(`Biome auto-installed: ${installedPath}`);
127
- // Set the installed path as the global fallback so every cwd
128
- // reaches it after its own per-package lookup misses.
129
- this.autoInstalledBinaryPath = installedPath;
130
- this.biomeAvailable = true;
177
+ // `unclassifiedFailureOutcome: "missing"` preserves the pre-#1476
178
+ // meaning of a plain non-zero exit (npx reporting no biome package):
179
+ // still "missing", still installable. Only the timeout/abort arm changes.
180
+ const { outcome, cause } = classifyProbeFailure(result, {
181
+ hostStallMs,
182
+ unclassifiedFailureOutcome: "missing",
183
+ });
184
+ // `classifyProbeFailure` is typed over the full outcome union but never
185
+ // returns "success" for a failed probe; narrow it for the seam's type.
186
+ const failureOutcome = outcome === "success" ? "non-installable" : outcome;
187
+ this.lastProbeCause = cause;
188
+ this.lastProbeElapsedMs = elapsedMs;
189
+ this.lastProbeHostStallMs = hostStallMs;
190
+ // The record is emitted by `doEnsureAvailable`, once the latch has said how
191
+ // long this verdict holds.
192
+ return { outcome: failureOutcome };
193
+ }
194
+ async doEnsureAvailable() {
195
+ const resolved = await resolveManagedToolClient({
196
+ toolId: "biome",
197
+ cwd: process.cwd(),
198
+ probe: () => this.probeBiome(),
199
+ acceptInstalled: (installedPath) => {
200
+ this.autoInstalledBinaryPath = installedPath;
201
+ return true;
202
+ },
203
+ });
204
+ if (resolved.outcome === "success") {
205
+ this.availabilityLatch.noteAvailable();
206
+ // The probe itself logs a clean hit. This arm is the other way to
207
+ // succeed — the install repaired a durable miss — and it needs its own
208
+ // record, or "biome went missing and came back" reads as silence.
209
+ if (this.lastProbeCause !== null && this.lastProbeCause !== "ok") {
210
+ logAvailabilityDecision({
211
+ tool: "biome",
212
+ verdict: "available",
213
+ outcome: "success",
214
+ cause: "ok",
215
+ elapsedMs: this.lastProbeElapsedMs,
216
+ latched: true,
217
+ hostStallMs: this.lastProbeHostStallMs,
218
+ budgetMs: PROBE_TIMEOUT_MS,
219
+ });
220
+ }
131
221
  return true;
132
222
  }
133
- this.log("Biome auto-install failed");
134
- this.biomeAvailable = false;
223
+ // A transient probe expires; a durable verdict is remembered for the
224
+ // session exactly as before.
225
+ const cause = this.lastProbeCause ?? "not-found";
226
+ const retryAfterMs = this.availabilityLatch.noteUnavailable(resolved.outcome, cause);
227
+ logAvailabilityDecision({
228
+ tool: "biome",
229
+ verdict: "unavailable",
230
+ outcome: resolved.outcome,
231
+ cause,
232
+ elapsedMs: this.lastProbeElapsedMs,
233
+ latched: resolved.outcome !== "transient",
234
+ hostStallMs: this.lastProbeHostStallMs,
235
+ ...(retryAfterMs > 0 && { retryAfterMs }),
236
+ budgetMs: PROBE_TIMEOUT_MS,
237
+ });
238
+ if (resolved.outcome === "transient") {
239
+ this.log("biome availability probe timed out; will retry (not installing)");
240
+ }
135
241
  return false;
136
242
  }
137
243
  /**
@@ -142,8 +248,10 @@ export class BiomeClient {
142
248
  }
143
249
  /**
144
250
  * Async auto-fix variant for pipeline use (non-blocking spawn).
251
+ * `cwd` is the dispatch language root (used for config discovery); when
252
+ * omitted it defaults to the file's directory.
145
253
  */
146
- async fixFileAsync(filePath) {
254
+ async fixFileAsync(filePath, cwd) {
147
255
  if (!(await this.ensureAvailable())) {
148
256
  return {
149
257
  success: false,
@@ -163,7 +271,11 @@ export class BiomeClient {
163
271
  }
164
272
  try {
165
273
  const before = await fs.promises.readFile(absolutePath, "utf-8");
166
- const result = await this.spawnBiomeAsync(["lint", "--write", absolutePath], 15000, path.dirname(absolutePath));
274
+ const configCwd = cwd ?? path.dirname(absolutePath);
275
+ // Shared config-args seam (#1247): the lint runner consumes the same
276
+ // builder, so `lint --write` can never drift to biome's default
277
+ // config when a user config or the package fallback exists.
278
+ const result = await this.spawnBiomeAsync(["lint", "--write", ...biomeConfigArgs(configCwd), absolutePath], 15000, configCwd);
167
279
  if (result.error) {
168
280
  return {
169
281
  success: false,
@@ -1,3 +1,4 @@
1
+ import { logExtension } from "./extension-log.js";
1
2
  let bootstrapPromise = null;
2
3
  /**
3
4
  * A stand-in for an analysis client whose module failed to load (an unresolved
@@ -28,11 +29,19 @@ export function degradedClient() {
28
29
  */
29
30
  async function logBootstrapFailures(failures) {
30
31
  for (const { name, err } of failures) {
31
- console.error(`[pi-lens] analyzer "${name}" disabled (degraded mode): ${err?.message ?? String(err)}`);
32
+ logExtension({
33
+ subsystem: "bootstrap",
34
+ message: `analyzer "${name}" disabled (degraded mode): ${err?.message ?? String(err)}`,
35
+ metadata: { analyzer: name },
36
+ });
32
37
  }
33
38
  try {
34
39
  const { collectInstallDiagnostics, formatInstallDiagnostics } = await import("./install-diagnostics.js");
35
- console.error(formatInstallDiagnostics(collectInstallDiagnostics(), failures[0]?.err));
40
+ logExtension({
41
+ subsystem: "bootstrap",
42
+ message: formatInstallDiagnostics(collectInstallDiagnostics(), failures[0]?.err),
43
+ metadata: { kind: "install_diagnostics" },
44
+ });
36
45
  }
37
46
  catch {
38
47
  // the per-analyzer lines above already named the failures
@@ -0,0 +1,34 @@
1
+ /** Small insertion-ordered LRU used for process-lifetime memo tables. */
2
+ export class BoundedLruCache {
3
+ maxEntries;
4
+ entries = new Map();
5
+ constructor(maxEntries) {
6
+ this.maxEntries = maxEntries;
7
+ }
8
+ get(key) {
9
+ const value = this.entries.get(key);
10
+ if (value !== undefined) {
11
+ this.entries.delete(key);
12
+ this.entries.set(key, value);
13
+ }
14
+ return value;
15
+ }
16
+ has(key) { return this.entries.has(key); }
17
+ set(key, value) {
18
+ this.entries.delete(key);
19
+ this.entries.set(key, value);
20
+ while (this.entries.size > this.maxEntries) {
21
+ const oldest = this.entries.keys().next().value;
22
+ if (oldest === undefined)
23
+ break;
24
+ this.entries.delete(oldest);
25
+ }
26
+ }
27
+ delete(key) { return this.entries.delete(key); }
28
+ clear() { this.entries.clear(); }
29
+ get size() { return this.entries.size; }
30
+ keys() { return this.entries.keys(); }
31
+ values() { return this.entries.values(); }
32
+ entriesArray() { return [...this.entries.entries()]; }
33
+ [Symbol.iterator]() { return this.entries[Symbol.iterator](); }
34
+ }