@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,14 +1,17 @@
|
|
|
1
1
|
/** Lazy dispatch integration seam for startup-cost control (#1394). */
|
|
2
|
-
|
|
2
|
+
import { createLazyImport } from "../lazy-import.js";
|
|
3
|
+
const lazyIntegration = createLazyImport(() => import("./integration.js"));
|
|
3
4
|
/** Start loading the runner graph once; callers may fire-and-forget this. */
|
|
4
5
|
export function warmDispatchIntegration() {
|
|
5
|
-
return (
|
|
6
|
+
return lazyIntegration.get();
|
|
6
7
|
}
|
|
7
8
|
/** Await the same promise used by session-start warming and first use. */
|
|
8
9
|
export function loadDispatchIntegration() {
|
|
9
10
|
return warmDispatchIntegration();
|
|
10
11
|
}
|
|
11
|
-
/** Test-only reset; production sessions intentionally retain the promise
|
|
12
|
+
/** Test-only reset; production sessions intentionally retain the promise
|
|
13
|
+
* across successful loads (a rejection already evicts itself — see
|
|
14
|
+
* `createLazyImport`). */
|
|
12
15
|
export function resetDispatchIntegrationForTests() {
|
|
13
|
-
|
|
16
|
+
lazyIntegration.resetForTests();
|
|
14
17
|
}
|
|
@@ -221,6 +221,11 @@ export const LANGUAGE_CAPABILITY_MATRIX = {
|
|
|
221
221
|
capabilities: ["types", "lint"],
|
|
222
222
|
writeGroups: [primary("clojure")],
|
|
223
223
|
},
|
|
224
|
+
cue: {
|
|
225
|
+
name: "CUE Linting",
|
|
226
|
+
capabilities: ["types", "lint"],
|
|
227
|
+
writeGroups: [primary("cue")],
|
|
228
|
+
},
|
|
224
229
|
terraform: {
|
|
225
230
|
name: "Terraform Linting",
|
|
226
231
|
capabilities: ["types", "lint", "security"],
|
|
@@ -15,6 +15,7 @@ import { logLatency } from "../../latency-logger.js";
|
|
|
15
15
|
import { hasEslintConfig } from "../../tool-policy.js";
|
|
16
16
|
import { enabledAuxiliaryLspServerIds } from "../auxiliary-lsp.js";
|
|
17
17
|
import { classifyDefect } from "../diagnostic-taxonomy.js";
|
|
18
|
+
import { recordDegradationOnce } from "../../degradation-ledger.js";
|
|
18
19
|
import { isAuxiliaryLspAlive } from "../../lsp/index.js";
|
|
19
20
|
import { resolveAstGrepNativeExe } from "../../lsp/wait-policy/index.js";
|
|
20
21
|
import { PRIORITY } from "../priorities.js";
|
|
@@ -25,22 +26,179 @@ const UNSUPPORTED_RULE_ID_SAMPLE_SIZE = 5;
|
|
|
25
26
|
export function resetAstGrepUnsupportedLanguageLog() {
|
|
26
27
|
defaultUnsupportedLanguageLog.clear();
|
|
27
28
|
}
|
|
28
|
-
// Lazy load the napi package
|
|
29
|
+
// Lazy load the napi package.
|
|
29
30
|
let sg;
|
|
30
|
-
|
|
31
|
+
/**
|
|
32
|
+
* In-flight load, shared by every caller (#1567). The per-edit fallback
|
|
33
|
+
* runner and the session-start scanner (clients/project-diagnostics/scanner.ts)
|
|
34
|
+
* both call `loadSg()` and can race. Pre-fix, the "attempted" flag was set
|
|
35
|
+
* before the load and read by the SECOND caller as "already tried" while the
|
|
36
|
+
* first load was still pending and about to succeed — a false-negative
|
|
37
|
+
* STARVATION, not a duplicate load: the second caller got back `undefined`
|
|
38
|
+
* for a load that was in flight and would have succeeded, not a second
|
|
39
|
+
* redundant `import()`. Sharing one promise means every concurrent caller
|
|
40
|
+
* observes the SAME outcome instead of a subset of them starving.
|
|
41
|
+
*
|
|
42
|
+
* Evicted on settle (#1536's pattern, see clients/tree-sitter-client.ts) — a
|
|
43
|
+
* rejected load must not be remembered as the permanent answer; only
|
|
44
|
+
* concurrent callers during the SAME attempt observe this promise.
|
|
45
|
+
*/
|
|
46
|
+
let sgLoadPromise;
|
|
47
|
+
/**
|
|
48
|
+
* Set on ANY load failure and cleared only by `resetAstGrepNapiLoadState()`
|
|
49
|
+
* at session_start (#1567 review round 2, F2).
|
|
50
|
+
*
|
|
51
|
+
* This holds for BOTH genuine and classified-transient failures, which is
|
|
52
|
+
* narrower than the original design (a cooldown-then-retry for transient
|
|
53
|
+
* failures within the same session). The reason: `loadAstGrepNapi()`
|
|
54
|
+
* (clients/deps/ast-grep-napi.ts) resolves the addon to a `file://` URL and
|
|
55
|
+
* dynamically `import()`s it. Node's ESM loader permanently memoizes a
|
|
56
|
+
* module record that threw during evaluation — re-importing the SAME
|
|
57
|
+
* resolved URL replays the cached rejection, it does not re-run the load.
|
|
58
|
+
* A cooldown-then-retry loop would therefore call `loadAstGrepNapi()` again
|
|
59
|
+
* after the cooldown and get back the identical cached rejection every
|
|
60
|
+
* time: a retry that LOOKS like it tries again but structurally cannot
|
|
61
|
+
* succeed within the process. Rather than ship that, every failure holds
|
|
62
|
+
* until the next `session_start`, which is the point this cache can
|
|
63
|
+
* actually be expected to have moved on (a fresh session re-arms the latch;
|
|
64
|
+
* whether the underlying Node module cache also gets a fresh start depends
|
|
65
|
+
* on whether the host recycles the process between sessions, but re-arming
|
|
66
|
+
* the latch is the most this in-process code can honestly promise).
|
|
67
|
+
*/
|
|
68
|
+
let sgSessionHold = false;
|
|
69
|
+
/**
|
|
70
|
+
* Why `sgSessionHold` was set — feeds the degradation-ledger message and log
|
|
71
|
+
* line only. It does not change retry behavior: both classes hold
|
|
72
|
+
* identically for the session, per `sgSessionHold`'s doc above.
|
|
73
|
+
*/
|
|
74
|
+
let sgHoldReason;
|
|
75
|
+
/** A positively-identified, narrow errno family for a native-addon load: the
|
|
76
|
+
* kind of momentary FS contention (too many open files, a file mid-write,
|
|
77
|
+
* a transient permission/resource block) that says nothing durable about
|
|
78
|
+
* whether the addon itself can load. Everything NOT in this allowlist is
|
|
79
|
+
* genuine by default (#1567 review round 2, F1) — the original classifier
|
|
80
|
+
* inverted this: it matched a handful of message patterns as "genuine" and
|
|
81
|
+
* treated every OTHER error, including the real native-binding failure
|
|
82
|
+
* family (ABI mismatch, a missing platform package, a napi version
|
|
83
|
+
* mismatch, an unsupported arch), as transient. None of those recover on
|
|
84
|
+
* their own; an unrecognized error is far more likely to be one of them
|
|
85
|
+
* than a genuine FS hiccup. */
|
|
86
|
+
const TRANSIENT_ERRNO_ALLOWLIST = new Set([
|
|
87
|
+
"EMFILE",
|
|
88
|
+
"EBUSY",
|
|
89
|
+
"EAGAIN",
|
|
90
|
+
"EPERM",
|
|
91
|
+
"ETXTBSY",
|
|
92
|
+
]);
|
|
93
|
+
/**
|
|
94
|
+
* `@ast-grep/napi`'s own terminal "this machine cannot run this addon"
|
|
95
|
+
* messages (from its `index.js` platform-detection shim). Already covered
|
|
96
|
+
* by the genuine-by-default policy above; listed explicitly so the
|
|
97
|
+
* classification is legible at the call site instead of an emergent
|
|
98
|
+
* property of "didn't match the allowlist".
|
|
99
|
+
*/
|
|
100
|
+
const KNOWN_GENUINE_NATIVE_LOAD_MESSAGES = [
|
|
101
|
+
/cannot find native binding/i,
|
|
102
|
+
/failed to load native binding/i,
|
|
103
|
+
];
|
|
104
|
+
/**
|
|
105
|
+
* Walk an error's `.cause` chain — Node/napi wrap the real dlopen/ABI
|
|
106
|
+
* failure there rather than on the top-level thrown Error — collecting
|
|
107
|
+
* every `code` and `message` seen along the way, so classification isn't
|
|
108
|
+
* blind to a nested cause (#1567 review round 2, F1).
|
|
109
|
+
*/
|
|
110
|
+
function collectErrorChain(err) {
|
|
111
|
+
const codes = [];
|
|
112
|
+
const messages = [];
|
|
113
|
+
let current = err;
|
|
114
|
+
const seen = new Set();
|
|
115
|
+
while (current !== undefined && current !== null && !seen.has(current)) {
|
|
116
|
+
seen.add(current);
|
|
117
|
+
if (current instanceof Error) {
|
|
118
|
+
messages.push(current.message);
|
|
119
|
+
const code = current.code;
|
|
120
|
+
if (typeof code === "string")
|
|
121
|
+
codes.push(code);
|
|
122
|
+
current = current.cause;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
messages.push(String(current));
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { codes, messages };
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Classify a `loadAstGrepNapi()` rejection. Transient requires a positively
|
|
133
|
+
* identified errno match somewhere in the cause chain AND no known-genuine
|
|
134
|
+
* native-load message anywhere in that same chain (an errno wrapping a
|
|
135
|
+
* "cannot find native binding" cause is genuine, not transient — the errno
|
|
136
|
+
* describes how the OS reported it, not what actually failed). Everything
|
|
137
|
+
* else — including an error with no `code` at all — is genuine.
|
|
138
|
+
*/
|
|
139
|
+
function classifyAstGrepLoadFailure(err) {
|
|
140
|
+
const { codes, messages } = collectErrorChain(err);
|
|
141
|
+
const hasKnownGenuineMessage = messages.some((message) => KNOWN_GENUINE_NATIVE_LOAD_MESSAGES.some((pattern) => pattern.test(message)));
|
|
142
|
+
if (hasKnownGenuineMessage)
|
|
143
|
+
return "genuine";
|
|
144
|
+
const hasTransientErrno = codes.some((code) => TRANSIENT_ERRNO_ALLOWLIST.has(code));
|
|
145
|
+
return hasTransientErrno ? "transient" : "genuine";
|
|
146
|
+
}
|
|
147
|
+
function recordAstGrepUnavailableOnce(reason) {
|
|
148
|
+
recordDegradationOnce({
|
|
149
|
+
kind: "ast-grep-napi-unavailable",
|
|
150
|
+
subject: "ast-grep-napi",
|
|
151
|
+
reason: reason === "genuine"
|
|
152
|
+
? "native addon failed to load (durable for this session)"
|
|
153
|
+
: "native addon load hit a transient error (durable for this session — see sgSessionHold doc)",
|
|
154
|
+
});
|
|
155
|
+
}
|
|
31
156
|
export async function loadSg() {
|
|
32
157
|
if (sg)
|
|
33
158
|
return sg;
|
|
34
|
-
if (
|
|
35
|
-
return
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
159
|
+
if (sgLoadPromise)
|
|
160
|
+
return sgLoadPromise;
|
|
161
|
+
if (sgSessionHold) {
|
|
162
|
+
// #1567 review F4: a held/degraded load must be distinguishable from a
|
|
163
|
+
// clean scan that found nothing to report, not silently read as
|
|
164
|
+
// "skipped/empty" by every caller (clients/project-diagnostics/scanner.ts,
|
|
165
|
+
// this file's own runner) — otherwise the two are indistinguishable in
|
|
166
|
+
// the degradation ledger.
|
|
167
|
+
recordAstGrepUnavailableOnce(sgHoldReason ?? "genuine");
|
|
42
168
|
return undefined;
|
|
43
169
|
}
|
|
170
|
+
const task = (async () => {
|
|
171
|
+
try {
|
|
172
|
+
const loaded = await loadAstGrepNapi();
|
|
173
|
+
sg = loaded;
|
|
174
|
+
return loaded;
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
const reason = classifyAstGrepLoadFailure(err);
|
|
178
|
+
sgSessionHold = true;
|
|
179
|
+
sgHoldReason = reason;
|
|
180
|
+
recordAstGrepUnavailableOnce(reason);
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
})();
|
|
184
|
+
sgLoadPromise = task;
|
|
185
|
+
task.finally(() => {
|
|
186
|
+
if (sgLoadPromise === task)
|
|
187
|
+
sgLoadPromise = undefined;
|
|
188
|
+
});
|
|
189
|
+
return task;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Re-arm the napi load latch for a new session (#1567). `sg` itself is left
|
|
193
|
+
* alone — a successful load stays valid and cached for the process — but a
|
|
194
|
+
* session hold (transient or genuine) is session-scoped state and must not
|
|
195
|
+
* outlive the session that set it. Called from `resetDispatchBaselines()`
|
|
196
|
+
* (clients/dispatch/integration.ts) beside `resetAstGrepUnsupportedLanguageLog`,
|
|
197
|
+
* this module's other session latch.
|
|
198
|
+
*/
|
|
199
|
+
export function resetAstGrepNapiLoadState() {
|
|
200
|
+
sgSessionHold = false;
|
|
201
|
+
sgHoldReason = undefined;
|
|
44
202
|
}
|
|
45
203
|
// Supported extensions for NAPI
|
|
46
204
|
const SUPPORTED_EXTS = [".ts", ".tsx", ".js", ".jsx", ".css", ".html", ".htm"];
|
|
@@ -156,11 +314,21 @@ export function canHandle(filePath) {
|
|
|
156
314
|
* file. Without this, `language:` reads as a real filter but isn't one for
|
|
157
315
|
* ts↔js pairs, so twin rules sharing a base name (e.g. `hardcoded-url` /
|
|
158
316
|
* `hardcoded-url-js`) both match the same construct in the SAME runner
|
|
159
|
-
* invocation (#657). TSX is
|
|
160
|
-
* ast-grep CLI/LSP also treats `tsx` as distinct from `typescript
|
|
161
|
-
* language-
|
|
162
|
-
*
|
|
163
|
-
*
|
|
317
|
+
* invocation (#657). TSX is its own grammar here too — the primary
|
|
318
|
+
* ast-grep CLI/LSP also treats `tsx` as distinct from `typescript` — but
|
|
319
|
+
* the caller's language-match check adds ONE deliberate exception on top
|
|
320
|
+
* of the exact-match rule: a `TypeScript`-tagged rule also runs against
|
|
321
|
+
* a `.tsx` file's fileLang (#1608). This is grounded empirically, not by
|
|
322
|
+
* analogy — tsx's grammar is a syntactic superset of typescript's for
|
|
323
|
+
* every construct the shipped catalog's rules target (JSX productions
|
|
324
|
+
* added, plus the removal of the `<T>expr` cast form, which cannot
|
|
325
|
+
* appear in valid `.tsx` source), and every `language: TypeScript`
|
|
326
|
+
* rule's fixture-test `invalid:` snippet is asserted to still match
|
|
327
|
+
* parsed as tsx (ast-grep-tsx-coverage.test.ts) rather than assumed.
|
|
328
|
+
* Without this exception the entire TS ruleset silently never runs on
|
|
329
|
+
* `.tsx` files. `TSX`-tagged rules stay `.tsx`-exclusive; the exception
|
|
330
|
+
* is TS→TSX only. Returns undefined for extensions this scoping doesn't
|
|
331
|
+
* apply to (css/html), where no filtering is added.
|
|
164
332
|
*/
|
|
165
333
|
export function ruleLanguageForFile(filePath) {
|
|
166
334
|
const ext = path.extname(filePath).toLowerCase();
|
|
@@ -229,6 +397,29 @@ function appendDuplicateRuleDiagnostics(diagnostics, seenRuleIds, duplicateIds,
|
|
|
229
397
|
}
|
|
230
398
|
return false;
|
|
231
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* The four tiers `Diagnostic.severity` accepts (clients/dispatch/types.ts).
|
|
402
|
+
* `YamlRule.severity` is a free-form string straight off disk, so an unknown
|
|
403
|
+
* or missing value must land somewhere deliberate rather than being cast.
|
|
404
|
+
*/
|
|
405
|
+
const DIAGNOSTIC_SEVERITY_TIERS = new Set([
|
|
406
|
+
"error",
|
|
407
|
+
"warning",
|
|
408
|
+
"info",
|
|
409
|
+
"hint",
|
|
410
|
+
]);
|
|
411
|
+
/**
|
|
412
|
+
* Map a rule's declared YAML severity onto a `Diagnostic.severity` tier (#1777).
|
|
413
|
+
*
|
|
414
|
+
* A rule that declares nothing, or declares a value pi-lens does not model
|
|
415
|
+
* (ast-grep also accepts `off`), falls back to `warning` — the tier every such
|
|
416
|
+
* rule already reported at before #1777, so reviving hint/info never silently
|
|
417
|
+
* demotes an existing rule.
|
|
418
|
+
*/
|
|
419
|
+
export function normalizeRuleSeverity(raw) {
|
|
420
|
+
const tier = raw;
|
|
421
|
+
return tier && DIAGNOSTIC_SEVERITY_TIERS.has(tier) ? tier : "warning";
|
|
422
|
+
}
|
|
232
423
|
/**
|
|
233
424
|
* Run the shipped ast-grep YAML ruleset against a parsed file via napi's native
|
|
234
425
|
* engine, applying the same suppression policy (linter/tree-sitter overlap,
|
|
@@ -343,9 +534,27 @@ export function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}
|
|
|
343
534
|
}
|
|
344
535
|
// Scope TypeScript/JavaScript-tagged rules to the file's actual
|
|
345
536
|
// grammar (#657) — otherwise a `-js` twin sharing generic node
|
|
346
|
-
// kinds with its TS sibling double-fires on every .ts file.
|
|
537
|
+
// kinds with its TS sibling double-fires on every .ts file. TSX
|
|
538
|
+
// is the one deliberate exception (#1608): the tsx grammar is a
|
|
539
|
+
// syntactic superset of typescript's for every non-JSX construct
|
|
540
|
+
// (the `<T>expr` cast form is the only TS-only production, and
|
|
541
|
+
// it can't appear in valid .tsx source anyway), so a
|
|
542
|
+
// `language: TypeScript` rule still matches a tsx-parsed root
|
|
543
|
+
// and must run there too, or the entire TS ruleset (120 of 263
|
|
544
|
+
// rules) goes dark on every .tsx file. `language: TSX` rules
|
|
545
|
+
// stay tsx-exclusive — they're already scoped to fileLang
|
|
546
|
+
// "tsx" by the exact-match check.
|
|
347
547
|
if (lang && fileLang && lang !== fileLang) {
|
|
348
|
-
|
|
548
|
+
const runsAsTsOnTsx = fileLang === "tsx" && lang === "typescript";
|
|
549
|
+
if (!runsAsTsOnTsx) {
|
|
550
|
+
const key = `mismatch:${lang}->${fileLang}`;
|
|
551
|
+
if (!unsupportedLanguageLog.has(key)) {
|
|
552
|
+
const ids = newlyUnsupported.get(key) ?? [];
|
|
553
|
+
ids.push(rule.id);
|
|
554
|
+
newlyUnsupported.set(key, ids);
|
|
555
|
+
}
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
349
558
|
}
|
|
350
559
|
if (blockingOnly && rule.rule) {
|
|
351
560
|
const complexity = calculateRuleComplexity(rule.rule);
|
|
@@ -387,7 +596,14 @@ export function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}
|
|
|
387
596
|
break;
|
|
388
597
|
const node = match;
|
|
389
598
|
const range = node.range();
|
|
390
|
-
|
|
599
|
+
// #1777: carry the rule's own tier through. The old collapse
|
|
600
|
+
// (`=== "error" ? "error" : "warning"`) erased hint and info,
|
|
601
|
+
// so the quiet tier the #1727 anti-slop rules ship at did not
|
|
602
|
+
// exist anywhere downstream. `Diagnostic.severity` has been
|
|
603
|
+
// 4-valued all along (clients/dispatch/types.ts). The BLOCKING
|
|
604
|
+
// gate is unchanged and deliberately narrower: only `error`
|
|
605
|
+
// blocks, so hint and info stay advisory exactly like warning.
|
|
606
|
+
const severity = normalizeRuleSeverity(rule.severity);
|
|
391
607
|
const semantic = severity === "error" ? "blocking" : "warning";
|
|
392
608
|
const defectClass = classifyDefect(rule.id, "ast-grep-napi", rule.message || rule.id);
|
|
393
609
|
const ruleFix = explicitRuleFixSuggestion(rule);
|
|
@@ -6,33 +6,80 @@
|
|
|
6
6
|
* mutating files mid-dispatch after LSP sync has already happened.
|
|
7
7
|
*/
|
|
8
8
|
import * as path from "node:path";
|
|
9
|
+
import { incrementDegradationCount } from "../../degradation-ledger.js";
|
|
10
|
+
import { mapWithConcurrency } from "../../dependency-checker.js";
|
|
9
11
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
10
12
|
import { biomeConfigArgs, getAutofixCapability, getJstsLintPolicyForCwd, } from "../../tool-policy.js";
|
|
11
13
|
import { PRIORITY } from "../priorities.js";
|
|
12
14
|
import { resolveToolCommandWithInstallFallback } from "./utils/runner-helpers.js";
|
|
13
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Map biome's own severity vocabulary onto the four-tier `Diagnostic.severity`
|
|
17
|
+
* (clients/dispatch/types.ts), the same way `normalizeRuleSeverity` does for
|
|
18
|
+
* ast-grep-napi post-#1787. Biome's real `lint --reporter=json` output
|
|
19
|
+
* (re-probed live for #1810's review round) spells its info tier `"info"`,
|
|
20
|
+
* not `"information"` — the value this switch matched before, which meant a
|
|
21
|
+
* real info-tier diagnostic fell through the `default` and was silently
|
|
22
|
+
* promoted to `"warning"`, one tier too loud. Every other tier is a direct
|
|
23
|
+
* pass-through; an unrecognized value still falls back to `"warning"` — the
|
|
24
|
+
* tier every biome diagnostic reported at before this fix, so reviving
|
|
25
|
+
* hint/info never silently demotes an existing finding.
|
|
26
|
+
*/
|
|
27
|
+
export function normalizeBiomeSeverity(raw) {
|
|
28
|
+
switch (raw) {
|
|
29
|
+
case "error":
|
|
30
|
+
return "error";
|
|
31
|
+
case "info":
|
|
32
|
+
return "info";
|
|
33
|
+
case "hint":
|
|
34
|
+
return "hint";
|
|
35
|
+
case "warning":
|
|
36
|
+
return "warning";
|
|
37
|
+
default:
|
|
38
|
+
return "warning";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A diagnostic's `category` is `"lint/<group>/<ruleName>"` — extracts the
|
|
43
|
+
* bare rule name `biome explain` expects. Returns `undefined` for anything
|
|
44
|
+
* that isn't a `lint/` category (e.g. a parse-error pseudo-category), so
|
|
45
|
+
* callers never look up fixability for a rule that was never a real rule.
|
|
46
|
+
*/
|
|
47
|
+
export function biomeRuleNameFromCategory(category) {
|
|
48
|
+
if (!category?.startsWith("lint/"))
|
|
49
|
+
return undefined;
|
|
50
|
+
const parts = category.split("/");
|
|
51
|
+
return parts[parts.length - 1] || undefined;
|
|
52
|
+
}
|
|
53
|
+
export function parseBiomeJson(raw, filePath, fixKindByRule) {
|
|
14
54
|
try {
|
|
15
55
|
const result = JSON.parse(raw);
|
|
16
56
|
const diagnostics = result.diagnostics || [];
|
|
17
57
|
const autofix = getAutofixCapability("biome");
|
|
18
58
|
return {
|
|
19
|
-
diagnostics: diagnostics.map((d) =>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
59
|
+
diagnostics: diagnostics.map((d) => {
|
|
60
|
+
const ruleName = biomeRuleNameFromCategory(d.category);
|
|
61
|
+
const fixKind = ruleName ? fixKindByRule?.get(ruleName) : undefined;
|
|
62
|
+
const isFixable = fixKind === "safe" || fixKind === "unsafe";
|
|
63
|
+
return {
|
|
64
|
+
id: `biome:${d.category}:${d.location.start.line}`,
|
|
65
|
+
message: d.message,
|
|
66
|
+
filePath,
|
|
67
|
+
line: d.location.start.line,
|
|
68
|
+
column: d.location.start.column,
|
|
69
|
+
severity: normalizeBiomeSeverity(d.severity),
|
|
70
|
+
semantic: d.severity === "error" ? "blocking" : "warning",
|
|
71
|
+
tool: "biome",
|
|
72
|
+
rule: d.category,
|
|
73
|
+
fixable: isFixable,
|
|
74
|
+
// Mirrors `biomeClient.fixFileAsync`'s own `lint --write` call
|
|
75
|
+
// (no `--unsafe`, clients/biome-client.ts): the pipeline only
|
|
76
|
+
// ever applies SAFE fixes, so only "safe" earns autoFixAvailable.
|
|
77
|
+
autoFixAvailable: fixKind === "safe" && (autofix?.safePipelineAutofix ?? false),
|
|
78
|
+
fixKind: isFixable && autofix?.fixKind !== "none"
|
|
79
|
+
? autofix?.fixKind
|
|
80
|
+
: undefined,
|
|
81
|
+
};
|
|
82
|
+
}),
|
|
36
83
|
};
|
|
37
84
|
}
|
|
38
85
|
catch (err) {
|
|
@@ -42,6 +89,109 @@ function parseBiomeJson(raw, filePath) {
|
|
|
42
89
|
};
|
|
43
90
|
}
|
|
44
91
|
}
|
|
92
|
+
// Process-lifetime cache: a rule's fix kind is a static property of the
|
|
93
|
+
// (biome binary, rule name) pair, not of any one diagnostic occurrence, so
|
|
94
|
+
// it's resolved once and reused. Keyed on the resolved `cmd` so a different
|
|
95
|
+
// biome binary (e.g. a different project's pinned version) never reuses
|
|
96
|
+
// another binary's answer. Registered in
|
|
97
|
+
// tests/support/session-state-registry.ts as `process_lifetime` — the
|
|
98
|
+
// answer cannot change without a different biome binary, which is a
|
|
99
|
+
// different cache key, so there is nothing for a session boundary to
|
|
100
|
+
// invalidate.
|
|
101
|
+
const biomeFixKindCache = new Map();
|
|
102
|
+
/** Test-only: drop every cached verdict so a test starts from a clean cache. */
|
|
103
|
+
export function _resetBiomeFixKindCacheForTests() {
|
|
104
|
+
biomeFixKindCache.clear();
|
|
105
|
+
}
|
|
106
|
+
const BIOME_FIX_KIND_PATTERN = /^-\s*Fix:\s*(safe|unsafe)\s*$/m;
|
|
107
|
+
// The explicit "no fix" statement `biome explain` prints for a rule with no
|
|
108
|
+
// autofix at all (e.g. `noConstantCondition`). Matching this SEPARATELY from
|
|
109
|
+
// "the safe/unsafe pattern didn't match" is the point (#1810 review F1/F2):
|
|
110
|
+
// only a POSITIVE read — either a stated fix tier or this explicit negative
|
|
111
|
+
// — is trustworthy enough to cache. Anything else (a differently-shaped
|
|
112
|
+
// `explain` output, e.g. a biome 1.x install) is a parse failure, not a
|
|
113
|
+
// verdict, and must never be cached as one.
|
|
114
|
+
const BIOME_NO_FIX_PATTERN = /^-\s*No fix available\.\s*$/m;
|
|
115
|
+
/** At most this many `biome explain` spawns run concurrently (#1810 review F6). */
|
|
116
|
+
const EXPLAIN_CONCURRENCY = 4;
|
|
117
|
+
/**
|
|
118
|
+
* Resolves each rule referenced by `categories` to its fix kind via
|
|
119
|
+
* `biome explain <rule>` — the one place biome's shipped CLI states
|
|
120
|
+
* fixability as structured text (`- Fix: safe|unsafe`, or `- No fix
|
|
121
|
+
* available.` when absent), sourced live from the running binary's own rule
|
|
122
|
+
* registry rather than a hand-maintained table (#1810; real diagnostics
|
|
123
|
+
* carry no per-occurrence fixability field — see `BiomeDiagnostic`).
|
|
124
|
+
*
|
|
125
|
+
* Only a POSITIVE read is cached: either `- Fix: safe|unsafe` or the
|
|
126
|
+
* explicit `- No fix available.` statement. A spawn failure, a nonzero
|
|
127
|
+
* exit, or output that matches NEITHER pattern (e.g. a biome 1.x install —
|
|
128
|
+
* its `explain` text differs and would otherwise poison every rule as
|
|
129
|
+
* permanently unfixable, #1810 review F1) resolves to `"none"` for THIS
|
|
130
|
+
* call only and is never written to the cache — caching an unparsed result
|
|
131
|
+
* would permanently under-report a genuinely fixable rule for the rest of
|
|
132
|
+
* the process's life (the never-cache-a-transient-failure screen).
|
|
133
|
+
*/
|
|
134
|
+
export async function resolveBiomeFixKinds(cmd, cwd, categories) {
|
|
135
|
+
const resolved = new Map();
|
|
136
|
+
const ruleNames = new Set();
|
|
137
|
+
for (const category of categories) {
|
|
138
|
+
const ruleName = biomeRuleNameFromCategory(category);
|
|
139
|
+
if (ruleName)
|
|
140
|
+
ruleNames.add(ruleName);
|
|
141
|
+
}
|
|
142
|
+
const toResolve = [];
|
|
143
|
+
for (const ruleName of ruleNames) {
|
|
144
|
+
const cacheKey = `${cmd}::${ruleName}`;
|
|
145
|
+
const cached = biomeFixKindCache.get(cacheKey);
|
|
146
|
+
if (cached === undefined) {
|
|
147
|
+
toResolve.push(ruleName);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
resolved.set(ruleName, cached);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
await mapWithConcurrency(toResolve, EXPLAIN_CONCURRENCY, async (ruleName) => {
|
|
154
|
+
const cacheKey = `${cmd}::${ruleName}`;
|
|
155
|
+
const spawned = await safeSpawnAsync(cmd, ["explain", ruleName], {
|
|
156
|
+
timeout: 10000,
|
|
157
|
+
cwd,
|
|
158
|
+
});
|
|
159
|
+
if (spawned.error || spawned.status !== 0) {
|
|
160
|
+
incrementDegradationCount({
|
|
161
|
+
kind: "biome-explain-unavailable",
|
|
162
|
+
subject: ruleName,
|
|
163
|
+
reason: spawned.error
|
|
164
|
+
? `spawn failed: ${spawned.error}`
|
|
165
|
+
: `explain exited ${spawned.status}`,
|
|
166
|
+
});
|
|
167
|
+
resolved.set(ruleName, "none");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const stdout = spawned.stdout || "";
|
|
171
|
+
const fixMatch = stdout.match(BIOME_FIX_KIND_PATTERN);
|
|
172
|
+
if (fixMatch) {
|
|
173
|
+
const kind = fixMatch[1] === "safe" ? "safe" : "unsafe";
|
|
174
|
+
biomeFixKindCache.set(cacheKey, kind);
|
|
175
|
+
resolved.set(ruleName, kind);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (BIOME_NO_FIX_PATTERN.test(stdout)) {
|
|
179
|
+
biomeFixKindCache.set(cacheKey, "none");
|
|
180
|
+
resolved.set(ruleName, "none");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Neither pattern matched — an unrecognized `explain` output shape, not
|
|
184
|
+
// a confirmed negative. Resolve to "none" for this call, but leave the
|
|
185
|
+
// cache untouched so the next batch retries against real output.
|
|
186
|
+
incrementDegradationCount({
|
|
187
|
+
kind: "biome-explain-unavailable",
|
|
188
|
+
subject: ruleName,
|
|
189
|
+
reason: "explain output matched neither the Fix nor No-fix pattern",
|
|
190
|
+
});
|
|
191
|
+
resolved.set(ruleName, "none");
|
|
192
|
+
});
|
|
193
|
+
return resolved;
|
|
194
|
+
}
|
|
45
195
|
const biomeCheckJsonRunner = {
|
|
46
196
|
id: "biome-check-json",
|
|
47
197
|
appliesTo: ["jsts"],
|
|
@@ -75,8 +225,26 @@ const biomeCheckJsonRunner = {
|
|
|
75
225
|
if (checkResult.error) {
|
|
76
226
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
77
227
|
}
|
|
228
|
+
let fixKindByRule;
|
|
229
|
+
if (checkResult.status === 0 || checkResult.status === 1) {
|
|
230
|
+
// Cheap pre-parse just to collect rule categories — the real parse
|
|
231
|
+
// (with parseError handling) happens below via parseBiomeJson. A
|
|
232
|
+
// malformed-JSON pre-parse simply yields no categories, and
|
|
233
|
+
// `resolveBiomeFixKinds` no-ops on an empty list (no spawn, an
|
|
234
|
+
// immediately-resolved empty map) — the real parse still surfaces
|
|
235
|
+
// the parseError diagnostic as before.
|
|
236
|
+
let categories = [];
|
|
237
|
+
try {
|
|
238
|
+
const preParsed = JSON.parse(checkResult.stdout || "{}");
|
|
239
|
+
categories = (preParsed.diagnostics || []).map((d) => d.category);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
categories = [];
|
|
243
|
+
}
|
|
244
|
+
fixKindByRule = await resolveBiomeFixKinds(cmd, cwd, categories);
|
|
245
|
+
}
|
|
78
246
|
const parsed = checkResult.status === 0 || checkResult.status === 1
|
|
79
|
-
? parseBiomeJson(checkResult.stdout || "", ctx.filePath)
|
|
247
|
+
? parseBiomeJson(checkResult.stdout || "", ctx.filePath, fixKindByRule)
|
|
80
248
|
: { diagnostics: [] };
|
|
81
249
|
if (parsed.parseError) {
|
|
82
250
|
const raw = checkResult.stdout || checkResult.stderr || "";
|
|
@@ -124,7 +124,9 @@ function parseGccLikeOutput(raw, filePath) {
|
|
|
124
124
|
line: Number.parseInt(lineStr, 10) || 1,
|
|
125
125
|
column: Number.parseInt(colStr || "1", 10) || 1,
|
|
126
126
|
severity,
|
|
127
|
-
|
|
127
|
+
// This single-file syntax check has no compile database, include path,
|
|
128
|
+
// or build flags. It can inform, but cannot prove a project blocker.
|
|
129
|
+
semantic: "warning",
|
|
128
130
|
tool: "cpp-check",
|
|
129
131
|
rule: severityLabel.toLowerCase(),
|
|
130
132
|
fixable: false,
|
|
@@ -153,7 +155,8 @@ function parseMsvcOutput(raw, filePath) {
|
|
|
153
155
|
line: Number.parseInt(lineStr, 10) || 1,
|
|
154
156
|
column: Number.parseInt(colStr || "1", 10) || 1,
|
|
155
157
|
severity,
|
|
156
|
-
|
|
158
|
+
// MSVC is invoked with the same context-free single-file contract.
|
|
159
|
+
semantic: "warning",
|
|
157
160
|
tool: "cpp-check",
|
|
158
161
|
rule,
|
|
159
162
|
fixable: false,
|
|
@@ -217,7 +220,7 @@ const cppCheckRunner = {
|
|
|
217
220
|
return {
|
|
218
221
|
status: hasErrors ? "failed" : "succeeded",
|
|
219
222
|
diagnostics,
|
|
220
|
-
semantic:
|
|
223
|
+
semantic: "warning",
|
|
221
224
|
rawOutput: raw,
|
|
222
225
|
};
|
|
223
226
|
},
|
|
@@ -3,15 +3,17 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
4
4
|
import { PRIORITY } from "../priorities.js";
|
|
5
5
|
import { createCwdCachedProbe } from "./utils/runner-helpers.js";
|
|
6
|
-
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
const CREDO_PROBE_BUDGET_MS = 10_000;
|
|
7
|
+
// Per-cwd cached `mix credo --version` probe (#120), governed by the shared
|
|
8
|
+
// availability policy (#1494). The probe returns the spawn result so the policy
|
|
9
|
+
// can tell a cold-BEAM timeout from a genuinely absent credo: the 10 s budget
|
|
10
|
+
// here is the widest in the repo and the most likely to expire on a slow first
|
|
11
|
+
// compile, and latching that as "not installed" used to disable credo for the
|
|
12
|
+
// rest of the session.
|
|
13
|
+
const probeCredo = createCwdCachedProbe((cwd) => safeSpawnAsync("mix", ["credo", "--version"], {
|
|
14
|
+
timeout: CREDO_PROBE_BUDGET_MS,
|
|
15
|
+
cwd,
|
|
16
|
+
}), { tool: "credo", budgetMs: CREDO_PROBE_BUDGET_MS });
|
|
15
17
|
// `mix credo <file>` is single-file-scoped today, so blanket attribution would
|
|
16
18
|
// be correct — but map issue.filename (resolved against cwd) for robustness so
|
|
17
19
|
// this stays correct if the invocation ever widens to a directory (#265 A4).
|