@herbertgao/pi-extensions 2026.8.6 → 2026.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- 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 +320 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -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 +6 -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 +17 -13
- 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 +21 -6
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
- 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 +5 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -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 +140 -97
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
- package/node_modules/@narumitw/pi-btw/README.md +120 -111
- 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 +10 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
- 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 +45 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
- package/node_modules/pi-lens/CHANGELOG.md +999 -0
- package/node_modules/pi-lens/README.md +117 -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 +126 -13
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
- 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 +381 -13
- package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -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 +206 -0
- package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
- 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/dead-code-client.js +123 -31
- 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 +84 -14
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
- package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
- package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
- 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/detekt.js +9 -6
- 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/hadolint.js +9 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
- 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 +60 -53
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
- 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 +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
- 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 +275 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
- 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 +734 -64
- 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 +188 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- 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 +31 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
- 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 +87 -8
- package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
- package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
- 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-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
- 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 +277 -16
- 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 +79 -3
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
- 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/session.js +10 -2
- package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
- 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 +154 -33
- 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 +84 -14
- 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/trivy.js +50 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
- 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 +8 -3
- package/node_modules/pi-lens/dist/clients/read-guard.js +253 -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/builder.js +194 -31
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
- package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
- package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
- package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
- 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 +157 -24
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
- package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
- package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
- 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-logger.js +1 -0
- 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/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +61407 -48577
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/docs/agent-guide.md +6 -1
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +19 -5
- package/node_modules/pi-lens/docs/globalconfig.md +3 -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 +19 -1
- package/node_modules/pi-lens/docs/usage.md +1 -0
- package/node_modules/pi-lens/package.json +27 -10
- 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 +15 -1
- 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-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 +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
- 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-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 +57 -12
- 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-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 +22 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
- 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 +6 -1
- 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 +53 -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/scripts/install-selftest.mjs +99 -11
- package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
- package/node_modules/pi-mcp-adapter/README.md +47 -5
- package/node_modules/pi-mcp-adapter/cli.js +163 -1
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +25 -7
- package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
- 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/metadata-cache.ts +32 -21
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +3 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +140 -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-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +2 -0
- 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 +32 -13
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
- package/node_modules/pi-web-access/curator-page.ts +6 -3
- package/node_modules/pi-web-access/curator-server.ts +4 -1
- package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +40 -5
- package/node_modules/pi-web-access/firecrawl.ts +18 -2
- package/node_modules/pi-web-access/gemini-search.ts +16 -4
- package/node_modules/pi-web-access/github-extract.ts +65 -36
- package/node_modules/pi-web-access/index.ts +60 -61
- package/node_modules/pi-web-access/openai-search.ts +17 -6
- package/node_modules/pi-web-access/package.json +1 -1
- 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/pdf-extract.ts +18 -5
- 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/ssrf-protection.ts +12 -2
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +44 -22
- 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/package.json +14 -14
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { incrementDegradationCount } from "../../degradation-ledger.js";
|
|
5
|
+
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
5
6
|
import { PRIORITY } from "../priorities.js";
|
|
6
|
-
|
|
7
|
-
//
|
|
8
|
-
//
|
|
7
|
+
import { classifyProbeFailure, createAvailabilityLatch, isLatchingOutcome, logAvailabilityDecision, startHostStallSampler, } from "./utils/availability-policy.js";
|
|
8
|
+
// The PS script written to a temp file each run. `-File` with a temp script
|
|
9
|
+
// keeps the analysis argv free of embedded script text; safe-spawn itself never
|
|
10
|
+
// uses a shell (#817), so nothing here is quoting-sensitive.
|
|
9
11
|
const PS_SCRIPT = `
|
|
10
12
|
param([string]$FilePath)
|
|
11
13
|
Import-Module PSScriptAnalyzer -ErrorAction Stop
|
|
@@ -13,82 +15,229 @@ $results = @(Invoke-ScriptAnalyzer -Path $FilePath | Select-Object RuleName,@{N=
|
|
|
13
15
|
if ($results.Count -eq 0) { Write-Output '[]'; exit 0 }
|
|
14
16
|
$results | ConvertTo-Json -Depth 3 -Compress
|
|
15
17
|
`.trim();
|
|
16
|
-
// Cache resolved powershell binary and module availability per process
|
|
17
|
-
let psCmd = undefined;
|
|
18
|
-
let psAnalyzerAvailable = undefined;
|
|
19
18
|
const PS_TIMEOUT_MS = 30000;
|
|
19
|
+
/**
|
|
20
|
+
* PowerShell spawns go through `safeSpawnAsync` (#1490).
|
|
21
|
+
*
|
|
22
|
+
* The hand-rolled version returned `status: null` for a timeout, a synchronous
|
|
23
|
+
* `spawn UNKNOWN` and a missing binary alike, so the call site had nothing to
|
|
24
|
+
* classify and remembered every one of them as "PowerShell analysis is not
|
|
25
|
+
* available". `safeSpawnAsync` carries the standard taxonomy — `failure`,
|
|
26
|
+
* `spawnFailure.kind` and the errno on `error` — which is what lets the
|
|
27
|
+
* availability policy tell a stall from an absence.
|
|
28
|
+
*/
|
|
20
29
|
function spawnPs(cmd, args, timeoutMs = PS_TIMEOUT_MS) {
|
|
21
|
-
return
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
child = spawn(cmd, args, { windowsHide: true, shell: false });
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
// SYNCHRONOUS spawn throw (Windows `spawn UNKNOWN`/EINVAL, the pidusage
|
|
28
|
-
// bug class, #533) — this best-effort runner must resolve, not reject.
|
|
29
|
-
resolve({ stdout: "", stderr: "", status: null });
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
let stdout = "";
|
|
33
|
-
let stderr = "";
|
|
34
|
-
let settled = false;
|
|
35
|
-
const done = (status) => {
|
|
36
|
-
if (settled)
|
|
37
|
-
return;
|
|
38
|
-
settled = true;
|
|
39
|
-
clearTimeout(timer);
|
|
40
|
-
resolve({ stdout, stderr, status });
|
|
41
|
-
};
|
|
42
|
-
const timer = setTimeout(() => {
|
|
43
|
-
if (!settled) {
|
|
44
|
-
child.kill("SIGTERM");
|
|
45
|
-
setTimeout(() => { if (!settled)
|
|
46
|
-
child.kill("SIGKILL"); }, 1000);
|
|
47
|
-
done(null);
|
|
48
|
-
}
|
|
49
|
-
}, timeoutMs);
|
|
50
|
-
child.stdout?.setEncoding("utf-8");
|
|
51
|
-
child.stderr?.setEncoding("utf-8");
|
|
52
|
-
child.stdout?.on("data", (d) => (stdout += d));
|
|
53
|
-
child.stderr?.on("data", (d) => (stderr += d));
|
|
54
|
-
child.on("close", (code) => done(code));
|
|
55
|
-
child.on("error", () => done(null));
|
|
30
|
+
return safeSpawnAsync(cmd, args, {
|
|
31
|
+
timeout: timeoutMs,
|
|
32
|
+
resourceLabel: "psscriptanalyzer",
|
|
56
33
|
});
|
|
57
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* The two verdicts this runner memoizes, both owned by the shared policy.
|
|
37
|
+
*
|
|
38
|
+
* The availability-policy coverage gate never flagged either of them, and the
|
|
39
|
+
* reason is not a bug in the gate: it recognises a probe by a quoted VERSION
|
|
40
|
+
* FLAG, and neither probe below asks for a version. One runs `exit 0` to see if
|
|
41
|
+
* an interpreter answers; the other asks `Get-Module -ListAvailable`. A
|
|
42
|
+
* capability probe with no version flag is invisible to that pre-filter, so this
|
|
43
|
+
* file was skipped before any unit was analysed. The residual gate blindness is
|
|
44
|
+
* tracked with #1489's gate work; the migration here does not depend on it.
|
|
45
|
+
*/
|
|
46
|
+
const psCmdLatch = createAvailabilityLatch();
|
|
47
|
+
const psAnalyzerLatchByCmd = new Map();
|
|
48
|
+
/**
|
|
49
|
+
* Whether `-File` actually executes, keyed by interpreter (#1540).
|
|
50
|
+
*
|
|
51
|
+
* Neither probe above asks `-File` a question -- both use `-Command`, which
|
|
52
|
+
* PowerShell's execution policy does not gate. Under `Restricted`/`AllSigned`
|
|
53
|
+
* that lets both probes report "available" while the real analysis spawn
|
|
54
|
+
* (which DOES go through `-File`) gets a policy `SecurityError` on stderr and
|
|
55
|
+
* exits nonzero with empty stdout. This latch is deliberately populated by
|
|
56
|
+
* the real analysis run rather than a third `-Command`-shaped probe, so the
|
|
57
|
+
* verdict it records is direct evidence about `-File`, not an inference from
|
|
58
|
+
* a proxy that has already been shown to disagree with it.
|
|
59
|
+
*/
|
|
60
|
+
const psExecLatchByCmd = new Map();
|
|
61
|
+
let psCmd = null;
|
|
62
|
+
/** Record one decision, and report whether the caller may retry later. */
|
|
63
|
+
function notePsDecision(latch, tool, verdict) {
|
|
64
|
+
let retryAfterMs;
|
|
65
|
+
if (verdict.available) {
|
|
66
|
+
latch.noteAvailable();
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const delay = latch.noteUnavailable(verdict.outcome, verdict.cause);
|
|
70
|
+
if (delay > 0)
|
|
71
|
+
retryAfterMs = delay;
|
|
72
|
+
}
|
|
73
|
+
logAvailabilityDecision({
|
|
74
|
+
tool,
|
|
75
|
+
verdict: verdict.available ? "available" : "unavailable",
|
|
76
|
+
outcome: verdict.available ? "success" : verdict.outcome,
|
|
77
|
+
cause: verdict.available ? "ok" : verdict.cause,
|
|
78
|
+
elapsedMs: verdict.elapsedMs,
|
|
79
|
+
latched: verdict.available || isLatchingOutcome(verdict.outcome),
|
|
80
|
+
hostStallMs: verdict.hostStallMs,
|
|
81
|
+
...(retryAfterMs !== undefined && { retryAfterMs }),
|
|
82
|
+
budgetMs: PS_TIMEOUT_MS,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Drop both verdicts at the session boundary (#1490 review).
|
|
87
|
+
*
|
|
88
|
+
* Every other runner gets this for free through
|
|
89
|
+
* `resetDispatchAvailabilityState`'s generation counter; these latches are local
|
|
90
|
+
* to the module, so `session_start` clears them explicitly. Without it, a
|
|
91
|
+
* PowerShell or module install performed mid-process is invisible until the host
|
|
92
|
+
* restarts — the same "suppression outliving its session" defect #1222 fixed for
|
|
93
|
+
* install attempts.
|
|
94
|
+
*/
|
|
95
|
+
export function resetPsScriptAnalyzerAvailability() {
|
|
96
|
+
psCmdLatch.reset();
|
|
97
|
+
psAnalyzerLatchByCmd.clear();
|
|
98
|
+
psExecLatchByCmd.clear();
|
|
99
|
+
psCmd = null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the PowerShell binary, once per session per verdict.
|
|
103
|
+
*
|
|
104
|
+
* A candidate that times out is NOT evidence that PowerShell is absent, so the
|
|
105
|
+
* loop only concludes "missing" when every candidate answered with a durable
|
|
106
|
+
* failure. One transient candidate makes the whole verdict transient, and the
|
|
107
|
+
* next turn after the cooldown probes again.
|
|
108
|
+
*/
|
|
58
109
|
async function resolvePowerShellCmd() {
|
|
59
|
-
|
|
60
|
-
|
|
110
|
+
const memo = psCmdLatch.read();
|
|
111
|
+
if (memo !== null)
|
|
112
|
+
return memo ? psCmd : null;
|
|
113
|
+
// Rank what the candidates said, rather than collapsing every failure to
|
|
114
|
+
// "not-found" (#1490 review). A stall outranks everything: it means we never
|
|
115
|
+
// got an answer. A present-but-rejecting interpreter (nonzero exit, EACCES)
|
|
116
|
+
// is durable but is NOT a missing install, and reporting it as one both sends
|
|
117
|
+
// the user to install PowerShell they already have and fabricates the cause in
|
|
118
|
+
// `availability_decision`. Only an all-candidates ENOENT sweep is `missing`.
|
|
119
|
+
let transient = null;
|
|
120
|
+
let rejected = null;
|
|
121
|
+
let elapsedTotalMs = 0;
|
|
122
|
+
let stallTotalMs = 0;
|
|
61
123
|
for (const candidate of ["pwsh", "powershell"]) {
|
|
62
|
-
const
|
|
63
|
-
|
|
124
|
+
const sampler = startHostStallSampler();
|
|
125
|
+
const startedAt = Date.now();
|
|
126
|
+
const result = await spawnPs(candidate, [
|
|
127
|
+
"-NoProfile",
|
|
128
|
+
"-NonInteractive",
|
|
129
|
+
"-Command",
|
|
130
|
+
"exit 0",
|
|
131
|
+
]);
|
|
132
|
+
const hostStallMs = sampler.stop();
|
|
133
|
+
const elapsedMs = Date.now() - startedAt;
|
|
134
|
+
elapsedTotalMs += elapsedMs;
|
|
135
|
+
stallTotalMs += hostStallMs;
|
|
136
|
+
if (!result.error && result.status === 0) {
|
|
64
137
|
psCmd = candidate;
|
|
65
|
-
|
|
138
|
+
notePsDecision(psCmdLatch, "powershell", {
|
|
139
|
+
available: true,
|
|
140
|
+
elapsedMs,
|
|
141
|
+
hostStallMs,
|
|
142
|
+
});
|
|
143
|
+
return candidate;
|
|
66
144
|
}
|
|
145
|
+
const classified = classifyProbeFailure(result, { hostStallMs });
|
|
146
|
+
if (classified.outcome === "transient")
|
|
147
|
+
transient ??= classified;
|
|
148
|
+
else if (classified.outcome !== "missing")
|
|
149
|
+
rejected ??= classified;
|
|
67
150
|
}
|
|
68
151
|
psCmd = null;
|
|
152
|
+
const verdict = transient ??
|
|
153
|
+
rejected ?? {
|
|
154
|
+
outcome: "missing",
|
|
155
|
+
cause: "not-found",
|
|
156
|
+
};
|
|
157
|
+
notePsDecision(psCmdLatch, "powershell", {
|
|
158
|
+
available: false,
|
|
159
|
+
outcome: verdict.outcome,
|
|
160
|
+
cause: verdict.cause,
|
|
161
|
+
elapsedMs: elapsedTotalMs,
|
|
162
|
+
hostStallMs: stallTotalMs,
|
|
163
|
+
});
|
|
69
164
|
return null;
|
|
70
165
|
}
|
|
71
166
|
async function checkModuleAvailable(cmd) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
167
|
+
let latch = psAnalyzerLatchByCmd.get(cmd);
|
|
168
|
+
if (!latch) {
|
|
169
|
+
latch = createAvailabilityLatch();
|
|
170
|
+
psAnalyzerLatchByCmd.set(cmd, latch);
|
|
171
|
+
}
|
|
172
|
+
const memo = latch.read();
|
|
173
|
+
if (memo !== null)
|
|
174
|
+
return memo;
|
|
175
|
+
const sampler = startHostStallSampler();
|
|
176
|
+
const startedAt = Date.now();
|
|
177
|
+
const result = await spawnPs(cmd, [
|
|
75
178
|
"-NoProfile",
|
|
76
179
|
"-NonInteractive",
|
|
77
180
|
"-Command",
|
|
78
181
|
"if (Get-Module -ListAvailable PSScriptAnalyzer) { exit 0 } else { exit 1 }",
|
|
79
182
|
]);
|
|
80
|
-
|
|
81
|
-
|
|
183
|
+
const hostStallMs = sampler.stop();
|
|
184
|
+
const elapsedMs = Date.now() - startedAt;
|
|
185
|
+
if (!result.error && result.status === 0) {
|
|
186
|
+
notePsDecision(latch, "psscriptanalyzer", {
|
|
187
|
+
available: true,
|
|
188
|
+
elapsedMs,
|
|
189
|
+
hostStallMs,
|
|
190
|
+
});
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
// A CLEAN `exit 1` is the module genuinely not being installed — the one
|
|
194
|
+
// outcome an install fixes, so that alone earns the `missing` override.
|
|
195
|
+
//
|
|
196
|
+
// Everything else did not answer the question (#1490 review). A `spawn
|
|
197
|
+
// UNKNOWN`, an EMFILE, an EACCES, or a PowerShell that vanished between the
|
|
198
|
+
// two probes all arrive with an error and no status, and calling those
|
|
199
|
+
// "PSScriptAnalyzer is not installed" latched PowerShell analysis off for the
|
|
200
|
+
// session on hosts where the module was present the whole time.
|
|
201
|
+
const moduleAbsent = result.status === 1 && !result.failure && !result.error;
|
|
202
|
+
const classified = classifyProbeFailure(result, {
|
|
203
|
+
hostStallMs,
|
|
204
|
+
...(moduleAbsent && { unclassifiedFailureOutcome: "missing" }),
|
|
205
|
+
});
|
|
206
|
+
let { outcome, cause } = classified;
|
|
207
|
+
if (!moduleAbsent && outcome !== "transient") {
|
|
208
|
+
// Nothing here is evidence about the module, so the verdict must expire.
|
|
209
|
+
outcome = "transient";
|
|
210
|
+
cause = "probe-rejected";
|
|
211
|
+
}
|
|
212
|
+
notePsDecision(latch, "psscriptanalyzer", {
|
|
213
|
+
available: false,
|
|
214
|
+
outcome,
|
|
215
|
+
cause,
|
|
216
|
+
elapsedMs,
|
|
217
|
+
hostStallMs,
|
|
218
|
+
});
|
|
219
|
+
return false;
|
|
82
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* `null` means "unreadable" (#1598): empty stdout, or stdout that fails to
|
|
223
|
+
* parse as JSON. `PS_SCRIPT` always writes either the literal `[]` marker or
|
|
224
|
+
* a `ConvertTo-Json` array/object on a genuine run, so anything else on an
|
|
225
|
+
* exit-0 run is not a clean-file answer -- it is a lost or truncated write
|
|
226
|
+
* between the child and us. The caller must not read `null` the same way it
|
|
227
|
+
* reads a real `[]`.
|
|
228
|
+
*/
|
|
83
229
|
function parsePSAnalyzerOutput(raw, filePath) {
|
|
84
|
-
|
|
230
|
+
const trimmed = raw.trim();
|
|
231
|
+
if (!trimmed)
|
|
232
|
+
return null;
|
|
233
|
+
if (trimmed === "[]")
|
|
85
234
|
return [];
|
|
86
235
|
let parsed;
|
|
87
236
|
try {
|
|
88
|
-
parsed = JSON.parse(
|
|
237
|
+
parsed = JSON.parse(trimmed);
|
|
89
238
|
}
|
|
90
239
|
catch {
|
|
91
|
-
return
|
|
240
|
+
return null;
|
|
92
241
|
}
|
|
93
242
|
const items = Array.isArray(parsed) ? parsed : [parsed];
|
|
94
243
|
return items
|
|
@@ -124,22 +273,149 @@ const psScriptAnalyzerRunner = {
|
|
|
124
273
|
if (!(await checkModuleAvailable(cmd))) {
|
|
125
274
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
126
275
|
}
|
|
276
|
+
let execLatch = psExecLatchByCmd.get(cmd);
|
|
277
|
+
if (!execLatch) {
|
|
278
|
+
execLatch = createAvailabilityLatch();
|
|
279
|
+
psExecLatchByCmd.set(cmd, execLatch);
|
|
280
|
+
}
|
|
281
|
+
// A durably blocked `-File` execution (an execution policy, typically) is
|
|
282
|
+
// a fact about the interpreter, not about any one file -- re-spawning on
|
|
283
|
+
// every save would just re-confirm the same policy every time (#1540).
|
|
284
|
+
if (execLatch.read() === false) {
|
|
285
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
286
|
+
}
|
|
127
287
|
const cwd = ctx.cwd || process.cwd();
|
|
128
288
|
const absPath = path.resolve(cwd, ctx.filePath);
|
|
129
289
|
// Write script to temp file so we avoid cmd.exe quoting entirely
|
|
130
290
|
const tmpScript = path.join(os.tmpdir(), `pi-lens-psa-${process.pid}.ps1`);
|
|
131
291
|
await fs.writeFile(tmpScript, PS_SCRIPT, "utf-8");
|
|
132
292
|
try {
|
|
293
|
+
const sampler = startHostStallSampler();
|
|
294
|
+
const startedAt = Date.now();
|
|
133
295
|
const result = await spawnPs(cmd, [
|
|
134
296
|
"-NoProfile",
|
|
135
297
|
"-NonInteractive",
|
|
136
298
|
"-File", tmpScript,
|
|
137
299
|
"-FilePath", absPath,
|
|
138
300
|
]);
|
|
301
|
+
const hostStallMs = sampler.stop();
|
|
302
|
+
const elapsedMs = Date.now() - startedAt;
|
|
303
|
+
// The two probes above use `-Command`, which PowerShell's execution
|
|
304
|
+
// policy does not gate; this run uses `-File`, which it does. A
|
|
305
|
+
// crashed/signal-killed process (#1540 second gap) or a blocked/
|
|
306
|
+
// erroring run (#1540 primary gap) must both produce a recorded,
|
|
307
|
+
// legible availability_decision -- never a silent "clean" read of
|
|
308
|
+
// empty stdout, and never a silent skip.
|
|
139
309
|
if (result.status === null) {
|
|
310
|
+
const classified = classifyProbeFailure(result, { hostStallMs });
|
|
311
|
+
let { outcome, cause } = classified;
|
|
312
|
+
// A `spawn UNKNOWN`/EMFILE/crash never ran the script, so it is not
|
|
313
|
+
// evidence that `-File` is broken (mirrors the nonzero-exit guard
|
|
314
|
+
// below, and checkModuleAvailable's own guard against over-latching
|
|
315
|
+
// an unclassified failure). Latching an unspawnable prober's
|
|
316
|
+
// verdict would durably disable analysis for the whole session on
|
|
317
|
+
// a host where `-File` works fine.
|
|
318
|
+
if (outcome !== "transient") {
|
|
319
|
+
outcome = "transient";
|
|
320
|
+
cause = "probe-rejected";
|
|
321
|
+
}
|
|
322
|
+
notePsDecision(execLatch, "psscriptanalyzer-exec", {
|
|
323
|
+
available: false,
|
|
324
|
+
outcome,
|
|
325
|
+
cause,
|
|
326
|
+
elapsedMs,
|
|
327
|
+
hostStallMs,
|
|
328
|
+
});
|
|
329
|
+
incrementDegradationCount({
|
|
330
|
+
kind: "grammar-blocked",
|
|
331
|
+
subject: "psscriptanalyzer",
|
|
332
|
+
reason: `PowerShell (${cmd}) -File analysis exited with no status -- crash or signal-killed process`,
|
|
333
|
+
});
|
|
334
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
335
|
+
}
|
|
336
|
+
if (result.status !== 0) {
|
|
337
|
+
const stderrText = result.stderr ?? "";
|
|
338
|
+
// Require the SecurityError/UnauthorizedAccess pairing that a real
|
|
339
|
+
// script-load block produces, not a bare "unauthorizedaccess"
|
|
340
|
+
// match (#1556 review F4) -- that word alone also appears in
|
|
341
|
+
// unrelated file-permission and access-denied stderr that has
|
|
342
|
+
// nothing to do with the execution policy, and misclassifying it
|
|
343
|
+
// as `policy-denied` would latch analysis off durably for a
|
|
344
|
+
// transient, per-file permission error.
|
|
345
|
+
const policyDenied = /securityerror/i.test(stderrText) &&
|
|
346
|
+
(/execution polic(y|ies)/i.test(stderrText) ||
|
|
347
|
+
/running scripts is disabled/i.test(stderrText) ||
|
|
348
|
+
/unauthorizedaccess/i.test(stderrText));
|
|
349
|
+
const classified = classifyProbeFailure(result, { hostStallMs });
|
|
350
|
+
let { outcome, cause } = classified;
|
|
351
|
+
if (policyDenied) {
|
|
352
|
+
// Direct, unambiguous evidence: PowerShell's own execution
|
|
353
|
+
// policy refused to load the script. Durable until the policy
|
|
354
|
+
// changes, so this is the one case allowed to latch.
|
|
355
|
+
outcome = "non-installable";
|
|
356
|
+
cause = "policy-denied";
|
|
357
|
+
}
|
|
358
|
+
else if (outcome !== "transient") {
|
|
359
|
+
// A single nonzero exit is not durable evidence that `-File`
|
|
360
|
+
// is broken for every file (mirrors checkModuleAvailable's own
|
|
361
|
+
// guard against over-latching an unclassified failure).
|
|
362
|
+
outcome = "transient";
|
|
363
|
+
cause = "probe-rejected";
|
|
364
|
+
}
|
|
365
|
+
notePsDecision(execLatch, "psscriptanalyzer-exec", {
|
|
366
|
+
available: false,
|
|
367
|
+
outcome,
|
|
368
|
+
cause,
|
|
369
|
+
elapsedMs,
|
|
370
|
+
hostStallMs,
|
|
371
|
+
});
|
|
372
|
+
incrementDegradationCount({
|
|
373
|
+
kind: "grammar-blocked",
|
|
374
|
+
subject: "psscriptanalyzer",
|
|
375
|
+
reason: policyDenied
|
|
376
|
+
? `PowerShell execution policy blocked -File analysis for ${cmd}: ${stderrText.trim().split("\n")[0] || "SecurityError"}`
|
|
377
|
+
: `PowerShell -File analysis for ${cmd} exited ${result.status}: ${stderrText.trim().slice(0, 200) || "no stderr"}`,
|
|
378
|
+
});
|
|
140
379
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
141
380
|
}
|
|
381
|
+
// `-File` ran and exited 0: direct proof it works. Log the decision
|
|
382
|
+
// once (memoized by the latch) rather than on every save.
|
|
383
|
+
if (execLatch.read() === null) {
|
|
384
|
+
notePsDecision(execLatch, "psscriptanalyzer-exec", {
|
|
385
|
+
available: true,
|
|
386
|
+
elapsedMs,
|
|
387
|
+
hostStallMs,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
142
390
|
const diagnostics = parsePSAnalyzerOutput(result.stdout, ctx.filePath);
|
|
391
|
+
if (diagnostics === null) {
|
|
392
|
+
// Exit 0 proves `-File` itself ran, so this does NOT reuse the
|
|
393
|
+
// exec latch above -- one bad read says nothing durable about the
|
|
394
|
+
// interpreter (mirrors the nonzero-exit branch's own guard against
|
|
395
|
+
// over-latching an unclassified failure), and it is logged rather
|
|
396
|
+
// than latched. It must still never be read as a clean file
|
|
397
|
+
// (#1598): empty/malformed stdout on a successful exit is a lost
|
|
398
|
+
// or truncated write, not evidence of zero diagnostics.
|
|
399
|
+
const unreadableReason = result.stdout.trim()
|
|
400
|
+
? "unparseable"
|
|
401
|
+
: "empty";
|
|
402
|
+
logAvailabilityDecision({
|
|
403
|
+
tool: "psscriptanalyzer-stdout",
|
|
404
|
+
verdict: "unavailable",
|
|
405
|
+
outcome: "transient",
|
|
406
|
+
cause: "empty-result",
|
|
407
|
+
elapsedMs,
|
|
408
|
+
latched: false,
|
|
409
|
+
hostStallMs,
|
|
410
|
+
budgetMs: PS_TIMEOUT_MS,
|
|
411
|
+
});
|
|
412
|
+
incrementDegradationCount({
|
|
413
|
+
kind: "grammar-blocked",
|
|
414
|
+
subject: "psscriptanalyzer",
|
|
415
|
+
reason: `PowerShell -File analysis for ${cmd} exited 0 but stdout was ${unreadableReason}`,
|
|
416
|
+
});
|
|
417
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
418
|
+
}
|
|
143
419
|
if (diagnostics.length === 0) {
|
|
144
420
|
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
145
421
|
}
|
|
@@ -99,20 +99,53 @@ const pyrightRunner = {
|
|
|
99
99
|
}
|
|
100
100
|
},
|
|
101
101
|
};
|
|
102
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Map pyright's own severity vocabulary onto the four-tier `Diagnostic.severity`
|
|
104
|
+
* (clients/dispatch/types.ts), the same way `normalizeBiomeSeverity` does for
|
|
105
|
+
* biome-check (#1791) and `normalizeRuleSeverity` does for ast-grep-napi
|
|
106
|
+
* (#1787). Pyright names its info tier `"information"`; `"error"`/`"warning"`
|
|
107
|
+
* pass through as-is. An unrecognized value falls back to `"warning"` — the
|
|
108
|
+
* tier every pyright diagnostic reported at before this fix, so reviving the
|
|
109
|
+
* info tier never silently demotes an existing finding. Pyright has no
|
|
110
|
+
* `"hint"` tier in its own vocabulary, so that tier is unreachable here.
|
|
111
|
+
*/
|
|
112
|
+
export function normalizePyrightSeverity(raw) {
|
|
113
|
+
switch (raw) {
|
|
114
|
+
case "error":
|
|
115
|
+
return "error";
|
|
116
|
+
case "information":
|
|
117
|
+
return "info";
|
|
118
|
+
// "warning" and any unrecognized value fall back below — the tier
|
|
119
|
+
// every pyright diagnostic reported at before this fix, so reviving
|
|
120
|
+
// the info tier never silently demotes an existing finding. There is
|
|
121
|
+
// no separate `case "warning"` branch: it would be redundant with
|
|
122
|
+
// this default and unprovable as its own branch.
|
|
123
|
+
default:
|
|
124
|
+
return "warning";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export function parsePyrightOutput(data, _filePath) {
|
|
103
128
|
const diagnostics = [];
|
|
104
129
|
// Pyright JSON output has generalDiagnostics array
|
|
105
130
|
const generalDiags = data.generalDiagnostics || [];
|
|
106
131
|
for (const diag of generalDiags) {
|
|
107
132
|
// Skip if not for this file (pyright may output diagnostics for imports)
|
|
108
133
|
// For now, include all - caller will filter if needed
|
|
134
|
+
// pyright's `range.start.line`/`character` are zero-based (see the
|
|
135
|
+
// `PyrightDiagnostic` note above); `Diagnostic.line`/`column` are
|
|
136
|
+
// one-based, the same convention ast-grep-napi and taplo already
|
|
137
|
+
// convert to (`range.start.line + 1`). `range` itself is omitted when
|
|
138
|
+
// pyright has nothing to point at, so both fall back to line 1.
|
|
139
|
+
const start = diag.range?.start;
|
|
109
140
|
diagnostics.push({
|
|
110
|
-
id: `pyright-${diag.rule ||
|
|
141
|
+
id: `pyright-${diag.rule || start?.line || "unknown"}`,
|
|
111
142
|
message: diag.message || "Type error",
|
|
112
143
|
filePath: diag.file || _filePath,
|
|
113
|
-
line:
|
|
114
|
-
column:
|
|
115
|
-
severity: diag.severity
|
|
144
|
+
line: (start?.line ?? 0) + 1,
|
|
145
|
+
column: (start?.character ?? 0) + 1,
|
|
146
|
+
severity: normalizePyrightSeverity(diag.severity),
|
|
147
|
+
// Blocking classification stays error-only — reviving the info tier
|
|
148
|
+
// must never widen what fails a turn.
|
|
116
149
|
semantic: diag.severity === "error" ? "blocking" : "warning",
|
|
117
150
|
tool: "pyright",
|
|
118
151
|
rule: diag.rule,
|
|
@@ -8,7 +8,8 @@ import { findNearestContaining } from "../../path-utils.js";
|
|
|
8
8
|
import { RustClient } from "../../rust-client.js";
|
|
9
9
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
10
10
|
import { stripAnsi } from "../../sanitize.js";
|
|
11
|
-
import { tryLazyInstall } from "./utils/lazy-installer.js";
|
|
11
|
+
import { getLazyInstallAttempt, tryLazyInstall, } from "./utils/lazy-installer.js";
|
|
12
|
+
import { describeInstallAttempt, logAvailabilityDecision, } from "./utils/availability-policy.js";
|
|
12
13
|
import { PRIORITY } from "../priorities.js";
|
|
13
14
|
import { createCwdCachedProbe } from "./utils/runner-helpers.js";
|
|
14
15
|
const rustClient = new RustClient();
|
|
@@ -21,20 +22,22 @@ const rustClient = new RustClient();
|
|
|
21
22
|
// after install. We can't `delete` a Promise mid-flight, so the safe path
|
|
22
23
|
// is: if the cached probe resolves false AND the install just succeeded,
|
|
23
24
|
// fall back to a one-shot fresh probe rather than reusing the cached false.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
//
|
|
26
|
+
// The verdict is governed by the shared availability policy (#1494): a probe
|
|
27
|
+
// timeout expires on a cooldown, so only a genuinely absent clippy latches.
|
|
28
|
+
const CLIPPY_PROBE_BUDGET_MS = 8000;
|
|
29
|
+
const makeClippyProbe = (cargoExe) => createCwdCachedProbe((cwd) => safeSpawnAsync(cargoExe, ["clippy", "--version"], {
|
|
30
|
+
timeout: CLIPPY_PROBE_BUDGET_MS,
|
|
31
|
+
cwd,
|
|
32
|
+
}), { tool: "clippy", budgetMs: CLIPPY_PROBE_BUDGET_MS });
|
|
33
|
+
const clippyAvailabilityByCargo = new Map();
|
|
32
34
|
function getClippyProbe(cargoExe) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
return clippyAvailabilityByCargo.get(cargoExe) ?? refreshClippyProbe(cargoExe);
|
|
36
|
+
}
|
|
37
|
+
/** Replace the cached probe so a post-install state is observed. */
|
|
38
|
+
function refreshClippyProbe(cargoExe) {
|
|
39
|
+
const created = makeClippyProbe(cargoExe);
|
|
40
|
+
clippyAvailabilityByCargo.set(cargoExe, created);
|
|
38
41
|
return created;
|
|
39
42
|
}
|
|
40
43
|
const rustClippyRunner = {
|
|
@@ -51,10 +54,35 @@ const rustClippyRunner = {
|
|
|
51
54
|
}
|
|
52
55
|
const clippyProbe = getClippyProbe(cargoExe);
|
|
53
56
|
if (!(await clippyProbe(ctx.cwd))) {
|
|
57
|
+
// A timed-out probe is not evidence that clippy is missing, so it must
|
|
58
|
+
// not drive an install. Skip this turn and let the policy re-probe once
|
|
59
|
+
// its cooldown expires (#1494).
|
|
60
|
+
if (clippyProbe.getVerdict(ctx.cwd).outcome === "transient") {
|
|
61
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
62
|
+
}
|
|
54
63
|
await tryLazyInstall("rust-clippy", ctx.cwd);
|
|
55
|
-
// Bust the cwd-keyed cache so the post-install state is observed.
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
// Bust the cwd-keyed cache so the post-install state is observed. Held in
|
|
65
|
+
// a local: `refreshClippyProbe` REPLACES the cached probe, so calling it
|
|
66
|
+
// again to read the verdict would spend a second probe.
|
|
67
|
+
const refreshedProbe = refreshClippyProbe(cargoExe);
|
|
68
|
+
if (!(await refreshedProbe(ctx.cwd))) {
|
|
69
|
+
// #1537: clippy is still absent, and the interesting question is WHY —
|
|
70
|
+
// "we tried `rustup component add` and the network failed" is a
|
|
71
|
+
// different fact from "this machine has no rustup", and until now the
|
|
72
|
+
// lazy installers recorded neither. Put the attempt beside the verdict
|
|
73
|
+
// it produced (#1500), so a reader does not have to infer the install
|
|
74
|
+
// from a runner that silently skipped.
|
|
75
|
+
const verdict = refreshedProbe.getVerdict(ctx.cwd);
|
|
76
|
+
logAvailabilityDecision({
|
|
77
|
+
tool: "rust-clippy",
|
|
78
|
+
verdict: "unavailable",
|
|
79
|
+
outcome: verdict.outcome ?? "missing",
|
|
80
|
+
cause: verdict.cause ?? "not-found",
|
|
81
|
+
classifiedBy: "caller",
|
|
82
|
+
evidence: describeInstallAttempt(getLazyInstallAttempt("rust-clippy", ctx.cwd)),
|
|
83
|
+
elapsedMs: 0,
|
|
84
|
+
latched: false,
|
|
85
|
+
});
|
|
58
86
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
59
87
|
}
|
|
60
88
|
}
|
|
@@ -107,6 +135,49 @@ function findCargoToml(filePath) {
|
|
|
107
135
|
const dir = findNearestContaining(dirname(filePath), ["Cargo.toml"]);
|
|
108
136
|
return dir ? join(dir, "Cargo.toml") : undefined;
|
|
109
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* #1802 fix round: the original review claimed rustc/clippy's top-level
|
|
140
|
+
* `compiler-message.level` is genuinely two-valued (error/warning). A live
|
|
141
|
+
* `cargo clippy --message-format=json` repro falsified that: rustc_errors's
|
|
142
|
+
* `Level` serializes SIX values, and top-level messages with a real primary
|
|
143
|
+
* span are NOT limited to error/warning —
|
|
144
|
+
*
|
|
145
|
+
* - `"error"` — a hard compiler/lint error.
|
|
146
|
+
* - `"warning"` — the common clippy-lint case.
|
|
147
|
+
* - `"note"` — DOES appear as a top-level message with a non-empty primary
|
|
148
|
+
* span (repro: an erroneous-constant note pointing at the offending
|
|
149
|
+
* expression), not only as a `message.children[].level` annotation.
|
|
150
|
+
* - `"help"` — a top-level suggestion-carrying message.
|
|
151
|
+
* - `"failure-note"` — observed with an empty `spans` array, so it is
|
|
152
|
+
* already filtered out by the `if (!span) continue` guard below; no
|
|
153
|
+
* diagnostic is ever built from it. Verified, not assumed.
|
|
154
|
+
* - `"error: internal compiler error"` — an ICE. The old `level === "error"`
|
|
155
|
+
* exact-match ternary silently mapped this to `"warning"`, because the
|
|
156
|
+
* string doesn't equal `"error"` — an ICE must never be under-reported,
|
|
157
|
+
* so it is normalized to `"error"` here explicitly.
|
|
158
|
+
*
|
|
159
|
+
* `note`/`help` don't have a `"blocking"`-worthy tier of their own in the
|
|
160
|
+
* four-valued `Diagnostic.severity`, so they land on the two quiet tiers:
|
|
161
|
+
* `note` → `hint`, `help` → `info`. Blocking stays derived from the
|
|
162
|
+
* *normalized* severity (not the raw string), which is what makes the ICE
|
|
163
|
+
* case blocking too instead of silently passing.
|
|
164
|
+
*/
|
|
165
|
+
export function normalizeClippyLevel(raw) {
|
|
166
|
+
if (raw === "error")
|
|
167
|
+
return "error";
|
|
168
|
+
if (typeof raw === "string" &&
|
|
169
|
+
raw.startsWith("error: internal compiler error")) {
|
|
170
|
+
return "error";
|
|
171
|
+
}
|
|
172
|
+
if (raw === "note")
|
|
173
|
+
return "hint";
|
|
174
|
+
if (raw === "help")
|
|
175
|
+
return "info";
|
|
176
|
+
// "warning", "failure-note" (unreachable here, see above), and anything
|
|
177
|
+
// unrecognized fall back to "warning" — the tier every clippy diagnostic
|
|
178
|
+
// reported at before this fix.
|
|
179
|
+
return "warning";
|
|
180
|
+
}
|
|
110
181
|
/**
|
|
111
182
|
* Find a machine-applicable suggested replacement across the message's spans.
|
|
112
183
|
* Clippy emits one diagnostic per warning but can attach the auto-fix to a
|
|
@@ -145,14 +216,21 @@ export function parseClippyOutput(raw, fallbackPath, cargoDir) {
|
|
|
145
216
|
? resolve(cargoDir, rawFile)
|
|
146
217
|
: rawFile
|
|
147
218
|
: fallbackPath;
|
|
219
|
+
// #1802: preserve clippy's real six-level vocabulary instead of
|
|
220
|
+
// collapsing it. See `normalizeClippyLevel` above for the verified
|
|
221
|
+
// mapping and the repro that falsified the original "two-valued"
|
|
222
|
+
// claim. Blocking is derived from the NORMALIZED severity so an ICE
|
|
223
|
+
// (`"error: internal compiler error"`) is blocking too, not just an
|
|
224
|
+
// exact-match `"error"` string.
|
|
225
|
+
const normalizedSeverity = normalizeClippyLevel(message.level);
|
|
148
226
|
diagnostics.push({
|
|
149
227
|
id: `clippy-${message.code?.code || "unknown"}`,
|
|
150
228
|
message: message.message || "Clippy warning",
|
|
151
229
|
filePath,
|
|
152
230
|
line: span.line_start || 0,
|
|
153
231
|
column: span.column_start || 0,
|
|
154
|
-
severity:
|
|
155
|
-
semantic:
|
|
232
|
+
severity: normalizedSeverity,
|
|
233
|
+
semantic: normalizedSeverity === "error" ? "blocking" : "warning",
|
|
156
234
|
tool: "rust-clippy",
|
|
157
235
|
rule: message.code?.code,
|
|
158
236
|
defectClass: "correctness",
|