@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.
- package/README.md +5 -5
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
- package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
- package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
- package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
- package/node_modules/@narumitw/pi-btw/README.md +119 -103
- package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
- package/node_modules/@narumitw/pi-btw/package.json +13 -9
- package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/pi-lens/CHANGELOG.md +1004 -0
- package/node_modules/pi-lens/README.md +11 -3
- package/node_modules/pi-lens/config/ruff/core.toml +42 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
- package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
- package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
- package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
- package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
- package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
- package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
- package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
- package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
- package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
- package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
- package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
- package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
- package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
- package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
- package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
- package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
- package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
- package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
- package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
- package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
- package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
- package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
- package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +64677 -49484
- package/node_modules/pi-lens/dist/mcp/server.js +8 -2
- package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +44 -14
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +33 -6
- package/node_modules/pi-lens/docs/globalconfig.md +14 -0
- package/node_modules/pi-lens/docs/language-coverage.md +71 -5
- package/node_modules/pi-lens/docs/mcp.md +2 -2
- package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
- package/node_modules/pi-lens/docs/settings.md +25 -1
- package/node_modules/pi-lens/docs/usage.md +24 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -0
- package/node_modules/pi-lens/package.json +10 -6
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
- package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
- package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -11
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
- package/node_modules/pi-mcp-adapter/README.md +51 -6
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/cli.js +188 -5
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +28 -10
- package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
- package/node_modules/pi-mcp-adapter/index.ts +219 -32
- package/node_modules/pi-mcp-adapter/init.ts +36 -4
- package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
- package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +4 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
- package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
- package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +95 -10
- package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
- package/node_modules/pi-mcp-adapter/utils.ts +70 -0
- package/node_modules/pi-web-access/CHANGELOG.md +37 -0
- package/node_modules/pi-web-access/README.md +38 -14
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +8 -3
- package/node_modules/pi-web-access/curator-server.ts +5 -1
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +134 -37
- package/node_modules/pi-web-access/fetch-params.ts +17 -3
- package/node_modules/pi-web-access/firecrawl.ts +172 -12
- package/node_modules/pi-web-access/gemini-search.ts +33 -7
- package/node_modules/pi-web-access/index.ts +160 -104
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/page-query.ts +4 -2
- package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
- package/node_modules/pi-web-access/parallel.ts +2 -2
- package/node_modules/pi-web-access/query-rewrite.ts +51 -0
- package/node_modules/pi-web-access/serper.ts +211 -0
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +51 -23
- package/node_modules/pi-web-access/tavily.ts +16 -4
- package/node_modules/pi-web-access/utils.ts +87 -0
- package/node_modules/pi-web-access/valyu.ts +199 -0
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- 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
|
|
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\\'
|
|
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 {
|
|
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
|
-
|
|
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...,
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
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
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
167
|
+
result = await this.spawnBiomeAsync(["--version"], PROBE_TIMEOUT_MS);
|
|
112
168
|
}
|
|
113
169
|
finally {
|
|
114
|
-
|
|
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
|
|
206
|
+
const resolved = await resolveManagedToolClient({
|
|
119
207
|
toolId: "biome",
|
|
120
208
|
cwd: process.cwd(),
|
|
121
|
-
probe:
|
|
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
|
-
|
|
133
|
-
|
|
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
|