@herbertgao/pi-extensions 2026.9.1 → 2026.9.3
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 +3 -2
- package/THIRD_PARTY_NOTICES.md +24 -0
- package/node_modules/@herbertgao/sol-pi/LICENSE +19 -0
- package/node_modules/@herbertgao/sol-pi/README.md +159 -0
- package/node_modules/@herbertgao/sol-pi/SECURITY.md +26 -0
- package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +19 -0
- package/node_modules/@herbertgao/sol-pi/agents-install.md +150 -0
- package/node_modules/@herbertgao/sol-pi/assets/sol-pi-hero.png +0 -0
- package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +69 -0
- package/node_modules/@herbertgao/sol-pi/docs/configuration.md +75 -0
- package/node_modules/@herbertgao/sol-pi/package.json +76 -0
- package/node_modules/@herbertgao/sol-pi/scripts/check-pi-compat.mjs +32 -0
- package/node_modules/@herbertgao/sol-pi/scripts/check-sol-pi-config.mjs +120 -0
- package/node_modules/@herbertgao/sol-pi/sol-pi.example.json +10 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/config.ts +135 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/file-queue.ts +74 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +185 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/then-run.ts +128 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/archive.ts +53 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/candidate.ts +101 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/config.ts +71 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/index.ts +220 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/journal.ts +25 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/provider.ts +164 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/receipt.ts +177 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/index.ts +227 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/ledger.ts +20 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/observation.ts +252 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/economics.ts +237 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +455 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/index.ts +49 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/plan.ts +79 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +208 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/tools.ts +100 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/index.ts +42 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/runtime-paths.ts +17 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/tui.ts +71 -0
- package/node_modules/@narumitw/pi-btw/README.md +57 -141
- package/node_modules/@narumitw/pi-btw/dist/index.ts +478 -43
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
- package/node_modules/@narumitw/pi-btw/docs/workflows.md +74 -0
- package/node_modules/@narumitw/pi-btw/package.json +2 -1
- package/node_modules/@narumitw/pi-btw/src/btw.ts +28 -3
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +32 -2
- package/node_modules/@narumitw/pi-btw/src/keybindings.ts +330 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +136 -19
- package/node_modules/@narumitw/pi-btw/src/settings.ts +32 -0
- package/node_modules/@narumitw/pi-btw/src/side-thread.ts +57 -5
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +52 -30
- package/node_modules/pi-lens/CHANGELOG.md +260 -0
- package/node_modules/pi-lens/README.md +13 -8
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -1
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +1 -1
- package/node_modules/pi-lens/dist/clients/analysed-root.js +1 -0
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +1 -8
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +2 -0
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +133 -1
- package/node_modules/pi-lens/dist/clients/biome-client.js +13 -2
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -6
- package/node_modules/pi-lens/dist/clients/bootstrap.js +4 -4
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +1 -1
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +2 -5
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +1 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +122 -6
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +2 -2
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/child-unref.js +1 -19
- package/node_modules/pi-lens/dist/clients/complexity-client.js +5 -0
- package/node_modules/pi-lens/dist/clients/config-core/index.js +11 -8
- package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +1 -1
- package/node_modules/pi-lens/dist/clients/config-core/provenance.js +2 -9
- package/node_modules/pi-lens/dist/clients/config-core/records.js +1 -1
- package/node_modules/pi-lens/dist/clients/config-core/schema.js +3 -5
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +3 -1
- package/node_modules/pi-lens/dist/clients/config-resolve.js +10 -7
- package/node_modules/pi-lens/dist/clients/config-schema.js +56 -2
- package/node_modules/pi-lens/dist/clients/config-warn.js +2 -2
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +8 -1
- package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -4
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +5 -1
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +1 -1
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +38 -41
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +18 -1
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -6
- package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -3
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +7 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +19 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +15 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +3 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +12 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +5 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +1 -64
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +8 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -21
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +2 -20
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +5 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +2 -2
- package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/effective-config.js +12 -2
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +2 -2
- package/node_modules/pi-lens/dist/clients/extension-log.js +2 -0
- package/node_modules/pi-lens/dist/clients/file-kinds.js +1 -38
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -1
- package/node_modules/pi-lens/dist/clients/file-time.js +1 -1
- package/node_modules/pi-lens/dist/clients/file-utils.js +5 -1
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +4 -0
- package/node_modules/pi-lens/dist/clients/format-service.js +0 -1
- package/node_modules/pi-lens/dist/clients/formatters.js +66 -93
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +3 -4
- package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +5 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +42 -6
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +48 -7
- package/node_modules/pi-lens/dist/clients/grammar-source.js +3 -3
- package/node_modules/pi-lens/dist/clients/installer/index.js +416 -61
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +84 -18
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +6 -6
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +9 -1
- package/node_modules/pi-lens/dist/clients/knip-client.js +9 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +12 -11
- package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
- package/node_modules/pi-lens/dist/clients/latency-logger.js +7 -7
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +23 -0
- package/node_modules/pi-lens/dist/clients/lens-engine.js +3 -4
- package/node_modules/pi-lens/dist/clients/lens-events.js +1 -1
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +18 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +1 -1
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -36
- package/node_modules/pi-lens/dist/clients/lsp/client.js +4 -3
- package/node_modules/pi-lens/dist/clients/lsp/config.js +37 -7
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +24 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +79 -1
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +3 -3
- package/node_modules/pi-lens/dist/clients/lsp/index.js +491 -64
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -8
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -2
- package/node_modules/pi-lens/dist/clients/lsp/server.js +62 -15
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -1
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +5 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +6 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +37 -4
- package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -1
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +3 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +2 -0
- package/node_modules/pi-lens/dist/clients/metrics-history.js +28 -107
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +1 -1
- package/node_modules/pi-lens/dist/clients/module-report.js +1 -1
- package/node_modules/pi-lens/dist/clients/mutating-tool.js +3 -3
- package/node_modules/pi-lens/dist/clients/mutation-attribution.js +3 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -4
- package/node_modules/pi-lens/dist/clients/observed-mutation.js +9 -9
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +2 -6
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +2 -0
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +1 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +2 -1
- package/node_modules/pi-lens/dist/clients/path-utils.js +23 -2
- package/node_modules/pi-lens/dist/clients/performance-report.js +2 -2
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +9 -4
- package/node_modules/pi-lens/dist/clients/pipeline.js +5 -4
- package/node_modules/pi-lens/dist/clients/project-changes.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +38 -10
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +10 -9
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +49 -1
- package/node_modules/pi-lens/dist/clients/project-scan-policy.js +3 -51
- package/node_modules/pi-lens/dist/clients/python-provenance.js +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +3 -1
- package/node_modules/pi-lens/dist/clients/read-guard.js +5 -2
- package/node_modules/pi-lens/dist/clients/recent-touches.js +2 -2
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +3 -5
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +1 -1
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +2 -0
- package/node_modules/pi-lens/dist/clients/ruff-client.js +18 -9
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +10 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +5 -0
- package/node_modules/pi-lens/dist/clients/runtime-session.js +58 -14
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +11 -1
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +4 -1
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +48 -1
- package/node_modules/pi-lens/dist/clients/sanitize.js +0 -11
- package/node_modules/pi-lens/dist/clients/scan-utils.js +1 -56
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -2
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +69 -1
- package/node_modules/pi-lens/dist/clients/session-summary.js +0 -24
- package/node_modules/pi-lens/dist/clients/sg-runner.js +2 -1
- package/node_modules/pi-lens/dist/clients/sgconfig.js +1 -5
- package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +1 -1
- package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +108 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
- package/node_modules/pi-lens/dist/clients/startup-scan.js +2 -10
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +291 -76
- package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +77 -72
- package/node_modules/pi-lens/dist/clients/tool-config.js +199 -0
- package/node_modules/pi-lens/dist/clients/tool-cwd.js +299 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
- package/node_modules/pi-lens/dist/clients/tool-probe.js +53 -0
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +42 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +1 -52
- package/node_modules/pi-lens/dist/clients/trivy-client.js +9 -1
- package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -4
- package/node_modules/pi-lens/dist/clients/turn-context.js +52 -0
- package/node_modules/pi-lens/dist/clients/user-notify.js +0 -4
- package/node_modules/pi-lens/dist/clients/widget-state.js +4 -3
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/word-index-store.js +3 -7
- package/node_modules/pi-lens/dist/clients/word-index.js +9 -16
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +1 -1
- package/node_modules/pi-lens/dist/index.js +19969 -17481
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -2
- package/node_modules/pi-lens/dist/mcp/build-staleness.js +1 -1
- package/node_modules/pi-lens/dist/mcp/server.js +135 -159
- package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +1 -1
- package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +1 -1
- package/node_modules/pi-lens/dist/tools/activate-tools.js +10 -7
- package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +2 -13
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +8 -13
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +83 -39
- package/node_modules/pi-lens/dist/tools/effective-config.js +4 -3
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +1 -10
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +354 -86
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +54 -112
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +20 -44
- package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +3 -3
- package/node_modules/pi-lens/dist/tools/module-report.js +5 -9
- package/node_modules/pi-lens/dist/tools/project-report.js +9 -17
- package/node_modules/pi-lens/dist/tools/render-compact.js +281 -2
- package/node_modules/pi-lens/dist/tools/symbol-search.js +3 -4
- package/node_modules/pi-lens/docs/agent-guide.md +3 -4
- package/node_modules/pi-lens/docs/agent-tools.md +46 -22
- package/node_modules/pi-lens/docs/configuration.md +9 -0
- package/node_modules/pi-lens/docs/environment-variables.md +0 -5
- package/node_modules/pi-lens/docs/features.md +5 -5
- package/node_modules/pi-lens/docs/globalconfig.md +31 -1
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +9 -1
- package/node_modules/pi-lens/docs/mcp.md +13 -1
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +71 -0
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +15 -0
- package/node_modules/pi-lens/docs/pi-lens-monitor.md +88 -0
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +41 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +21 -2
- package/node_modules/pi-lens/docs/public-api-stability.md +1 -0
- package/node_modules/pi-lens/docs/real-harness.md +46 -0
- package/node_modules/pi-lens/docs/release-qa-baseline.md +17 -5
- package/node_modules/pi-lens/docs/servercapabilities.md +2 -3
- package/node_modules/pi-lens/docs/settings.md +37 -2
- package/node_modules/pi-lens/docs/tools_improvement2.md +4 -4
- package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
- package/node_modules/pi-lens/docs/usage.md +13 -3
- package/node_modules/pi-lens/docs/word-index.md +1 -1
- package/node_modules/pi-lens/package.json +15 -15
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +7 -3
- package/node_modules/pi-lens/rules/typos/_typos.toml +4 -2
- package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +1 -1
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +1 -1
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +4 -2
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +8 -6
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +19 -9
- package/node_modules/pi-web-access/CHANGELOG.md +29 -0
- package/node_modules/pi-web-access/README.md +33 -22
- package/node_modules/pi-web-access/abortable.ts +17 -0
- package/node_modules/pi-web-access/crawl4ai.ts +204 -0
- package/node_modules/pi-web-access/credential-source.ts +1 -0
- package/node_modules/pi-web-access/curator-page.ts +20 -2
- package/node_modules/pi-web-access/curator-run.ts +44 -0
- package/node_modules/pi-web-access/curator-server.ts +3 -1
- package/node_modules/pi-web-access/duckduckgo.ts +1 -1
- package/node_modules/pi-web-access/extract.ts +57 -12
- package/node_modules/pi-web-access/fetch-params.ts +1 -1
- package/node_modules/pi-web-access/gemini-search.ts +9 -5
- package/node_modules/pi-web-access/index.ts +25 -18
- package/node_modules/pi-web-access/openai-search.ts +35 -13
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/page-query.ts +5 -2
- package/node_modules/pi-web-access/query-rewrite.ts +5 -2
- package/node_modules/pi-web-access/serpapi.ts +220 -0
- package/node_modules/pi-web-access/ssrf-protection.ts +5 -1
- package/node_modules/pi-web-access/storage.ts +10 -0
- package/node_modules/pi-web-access/summary-review.ts +34 -21
- package/node_modules/pi-web-access/utils.ts +6 -4
- package/package.json +7 -4
- package/node_modules/pi-lens/dist/tools/ast-dump.js +0 -103
|
@@ -51,15 +51,32 @@ function isTypeStub(content) {
|
|
|
51
51
|
*/
|
|
52
52
|
export function detectFileRole(filePath, content) {
|
|
53
53
|
const windowsShaped = isWindowsPath(filePath);
|
|
54
|
-
const
|
|
54
|
+
const rawBase = windowsShaped ? win32.basename(filePath) : basename(filePath);
|
|
55
|
+
const base = rawBase.toLowerCase();
|
|
55
56
|
const dir = (windowsShaped ? win32.dirname(filePath) : dirname(filePath))
|
|
56
57
|
.replace(/\\/g, "/")
|
|
57
58
|
.toLowerCase();
|
|
58
59
|
// --- Test ---
|
|
60
|
+
// Suffix conventions informed by the test-runner table
|
|
61
|
+
// (SOURCE_TO_TEST_PATTERNS + RUNNERS kinds in
|
|
62
|
+
// clients/test-runner-client.ts); hand-written, not iterated from it —
|
|
63
|
+
// see #2928 for the single-classifier fold:
|
|
64
|
+
// - `_test.` infix: Go (`*_test.go`), pytest (`*_test.py`), ExUnit
|
|
65
|
+
// (`*_test.exs`), Dart (`*_test.dart`). The underscore anchor keeps
|
|
66
|
+
// this precise: `contest.py` and `latest.ts` do not match.
|
|
67
|
+
// - `_spec.` infix: RSpec (`*_spec.rb`).
|
|
68
|
+
// - CamelCase `*Test(s).<ext>` suffix, matched case-SENSITIVELY on the
|
|
69
|
+
// raw basename: JUnit/Surefire (`*Test.java`, `*TestCase.java`),
|
|
70
|
+
// Kotlin (`*Test.kt`), dotnet (`*Test.cs`/`*Tests.cs`), PHPUnit
|
|
71
|
+
// (`*Test.php`). Case matters: Surefire's `*Test.java` does not match
|
|
72
|
+
// `contest.java`, so neither do we.
|
|
59
73
|
if (base.includes(".test.") ||
|
|
60
74
|
base.includes(".spec.") ||
|
|
75
|
+
base.includes("_test.") ||
|
|
76
|
+
base.includes("_spec.") ||
|
|
61
77
|
base.startsWith("test_") ||
|
|
62
78
|
base.startsWith("spec_") ||
|
|
79
|
+
/(Test|Tests|TestCase)\.(java|kt|kts|cs|fs|php)$/.test(rawBase) ||
|
|
63
80
|
dir.includes("/__tests__/") ||
|
|
64
81
|
dir.includes("/test/") ||
|
|
65
82
|
dir.includes("/tests/") ||
|
|
@@ -302,7 +302,7 @@ function matchesGitignorePattern(pattern, relativePath, isDirectory, matchExpand
|
|
|
302
302
|
return candidates.some((value) => matchExpanded(value, expanded));
|
|
303
303
|
});
|
|
304
304
|
}
|
|
305
|
-
|
|
305
|
+
function readGitignorePatterns(rootDir, layer = "gitignore") {
|
|
306
306
|
const gitignorePath = path.join(rootDir, ".gitignore");
|
|
307
307
|
try {
|
|
308
308
|
return parseGitignoreContent(fs.readFileSync(gitignorePath, "utf-8"), layer);
|
|
@@ -762,7 +762,11 @@ export function readGitignoreDirs(rootDir) {
|
|
|
762
762
|
.map((entry) => entry.pattern);
|
|
763
763
|
}
|
|
764
764
|
function globToRegExp(glob) {
|
|
765
|
+
// Directory names use the same `*`-only dialect as read-guard exemptions.
|
|
766
|
+
// Collapse adjacent stars before compiling to avoid nullable-group
|
|
767
|
+
// backtracking on a non-matching name (#2622).
|
|
765
768
|
const escaped = glob
|
|
769
|
+
.replace(/\*+/g, "*")
|
|
766
770
|
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
767
771
|
.replace(/\*/g, ".*")
|
|
768
772
|
.replace(/\?/g, ".");
|
|
@@ -139,6 +139,10 @@
|
|
|
139
139
|
* agent-facing surface is EITHER gated OR explicitly labeled — never neither.
|
|
140
140
|
*/
|
|
141
141
|
const MS_PER_MINUTE = 60_000;
|
|
142
|
+
/** Mark retained findings whose replacement result could not be reconciled. */
|
|
143
|
+
export function markUnreconciledFindings(findings) {
|
|
144
|
+
return findings.map((finding) => ({ ...finding, stale: true }));
|
|
145
|
+
}
|
|
142
146
|
/**
|
|
143
147
|
* Render a short, honest age suffix for a store that cannot be freshness-
|
|
144
148
|
* gated per-path (see module doc). `undefined`/unparseable `scannedAt`
|
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
import { logExtension } from "./extension-log.js";
|
|
12
12
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
13
13
|
import * as fs from "node:fs/promises";
|
|
14
|
+
import * as os from "node:os";
|
|
14
15
|
import * as path from "node:path";
|
|
15
16
|
import { BoundedLruCache } from "./bounded-cache.js";
|
|
16
17
|
import { createGenerationSource } from "./generation-guard.js";
|
|
17
18
|
import { normalizeMapKey } from "./path-utils.js";
|
|
19
|
+
import { resolveToolCwd } from "./tool-cwd.js";
|
|
18
20
|
import { resolveCargoPackageEdition } from "./cargo-manifest.js";
|
|
19
21
|
import { resolveKtfmtGradleStyle } from "./gradle-ktfmt-style.js";
|
|
20
22
|
import { resolvePhpCsFixerConfig } from "./php-cs-fixer-config.js";
|
|
@@ -26,8 +28,10 @@ import { compareOrdinal } from "./string-utils.js";
|
|
|
26
28
|
import { classifyProbeFailure, createAvailabilityLatch, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
|
|
27
29
|
import { findGlobalBinary, findLocalBinUpwards, VENDOR_BIN_DIRS, VENV_BIN_DIRS, } from "./package-manager.js";
|
|
28
30
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
31
|
+
import { probeToolAsync } from "./tool-probe.js";
|
|
29
32
|
import { assertInstallAllowed } from "./project-trust.js";
|
|
30
33
|
import { tryLazyInstallForFormatter } from "./dispatch/runners/utils/lazy-installer.js";
|
|
34
|
+
import { getToolPath } from "./installer/index.js";
|
|
31
35
|
import { findPSScriptAnalyzerConfigPath, getAutoInstallToolIdForFormatter, getFormatterPolicyForFile, getSmartDefaultFormatterName, hasBiomeConfig, hasBlackConfig, hasClangFormatConfig, hasCljfmtConfig, hasCmakeFormatConfig, hasCsharpierConfig, hasFantomasConfig, hasGoogleJavaFormatConfig, hasKtfmtConfig, hasKtlintConfig, hasMixFormatConfig, hasNearestPackageJsonDependency, hasNearestPackageJsonField, hasOcamlformatConfig, hasOrmoluConfig, hasOxfmtConfig, hasOxfmtSvelteConfig, hasPhpCsFixerConfig, hasPrettierConfig, hasPSScriptAnalyzerConfig, hasRubocopConfig, hasRuffConfig, hasSqlfluffConfig, hasStandardrbConfig, hasStyluaConfig, hasSwiftformatConfig, hasTaploConfig, hasTerraformConfig, hasVitePlusConfig, OXFMT_SUPPORTED_EXTENSIONS, } from "./tool-policy.js";
|
|
32
36
|
/**
|
|
33
37
|
* Lazy-install a formatter's tool, through the shared seam (#1537).
|
|
@@ -379,7 +383,7 @@ async function resolveGoFmtBinary() {
|
|
|
379
383
|
const inPath = await which("gofmt");
|
|
380
384
|
if (inPath)
|
|
381
385
|
return inPath;
|
|
382
|
-
const goCheck = await
|
|
386
|
+
const goCheck = await probeToolAsync("go", ["env", "GOROOT"], {
|
|
383
387
|
timeout: 5000,
|
|
384
388
|
});
|
|
385
389
|
if (goCheck.error || goCheck.status !== 0)
|
|
@@ -467,6 +471,22 @@ async function resolveManagedSmartDefaultCommand(formatterName, filePath, args)
|
|
|
467
471
|
return null;
|
|
468
472
|
return [installed, ...args, filePath];
|
|
469
473
|
}
|
|
474
|
+
/** Resolve an explicitly selected formatter from the shared managed-tool seam. */
|
|
475
|
+
async function resolveManagedFormatterCommand(toolId, filePath, args, findLocal, cwd) {
|
|
476
|
+
const local = findLocal && cwd ? await findLocal(toolId, cwd) : null;
|
|
477
|
+
const installed = local ?? (await which(toolId)) ?? (await getToolPath(toolId));
|
|
478
|
+
return installed ? [installed, ...args, filePath] : FORMATTER_UNAVAILABLE;
|
|
479
|
+
}
|
|
480
|
+
function managedFormatterResolver(toolId, args, findLocal) {
|
|
481
|
+
return (filePath, cwd) => resolveManagedFormatterCommand(toolId, filePath, args, findLocal, cwd);
|
|
482
|
+
}
|
|
483
|
+
function managedToolDetect(toolId, configDetect, nativeDetect) {
|
|
484
|
+
return async (cwd) => {
|
|
485
|
+
const available = (nativeDetect && (await nativeDetect(cwd))) ||
|
|
486
|
+
Boolean(await getToolPath(toolId));
|
|
487
|
+
return available && (configDetect ? await configDetect(cwd) : true);
|
|
488
|
+
};
|
|
489
|
+
}
|
|
470
490
|
/**
|
|
471
491
|
* One entry per formatter that can be selected via explicit project config
|
|
472
492
|
* (the `formatterPolicy` "explicit-config" branch of `getFormattersForFile`).
|
|
@@ -602,6 +622,20 @@ async function indentationArgs(filePath, tool, cwd) {
|
|
|
602
622
|
String(indentation.width),
|
|
603
623
|
];
|
|
604
624
|
}
|
|
625
|
+
/**
|
|
626
|
+
* Resolve the cwd for the formatter child, capped at the user's home
|
|
627
|
+
* directory. Files outside a project retain the historical file-directory
|
|
628
|
+
* cwd. The optional homeDir is only for the ceiling regression test.
|
|
629
|
+
*/
|
|
630
|
+
export function resolveFormatterCwd(absolutePath, formatterName, homeDir = os.homedir()) {
|
|
631
|
+
return resolveToolCwd("formatter", formatterName ?? "unknown", absolutePath, {
|
|
632
|
+
// Formatter discovery historically walks above the file directory. The
|
|
633
|
+
// filesystem root is the neutral dispatch boundary; the seam's `$HOME`
|
|
634
|
+
// ceiling prevents it from escaping the user's workspace.
|
|
635
|
+
cwd: path.parse(path.resolve(absolutePath)).root,
|
|
636
|
+
homeDir,
|
|
637
|
+
});
|
|
638
|
+
}
|
|
605
639
|
/**
|
|
606
640
|
* Every biome invocation must carry this. Biome exits 1 with "No files were
|
|
607
641
|
* processed in the specified paths" when the path is ignored by the repo's own
|
|
@@ -783,11 +817,7 @@ export const oxfmtFormatter = {
|
|
|
783
817
|
const found = await which("oxfmt");
|
|
784
818
|
if (found)
|
|
785
819
|
return [found, OXFMT_NO_ERROR_ON_UNMATCHED, filePath];
|
|
786
|
-
|
|
787
|
-
// config-only (it never probes the binary), so selection can reach here
|
|
788
|
-
// with nothing installed. The static command is bare `oxfmt` — spawning it
|
|
789
|
-
// only reproduces the reported `spawn oxfmt ENOENT`. Prove it unavailable.
|
|
790
|
-
return FORMATTER_UNAVAILABLE;
|
|
820
|
+
return ((await resolveManagedFormatterCommand("oxfmt", filePath, [OXFMT_NO_ERROR_ON_UNMATCHED], findInNodeModules, cwd)) ?? FORMATTER_UNAVAILABLE);
|
|
791
821
|
},
|
|
792
822
|
// Single source of truth: OXFMT_SUPPORTED_EXTENSIONS in tool-policy.ts.
|
|
793
823
|
// Do not hand-maintain a second copy of this list (#1134 — previously two
|
|
@@ -836,21 +866,14 @@ export const ruffFormatter = {
|
|
|
836
866
|
return true;
|
|
837
867
|
// No-config fallback: if Ruff is already available, allow formatter usage.
|
|
838
868
|
// This keeps Python default behavior consistent with startup defaults.
|
|
839
|
-
|
|
840
|
-
const installed = await getToolPath("ruff");
|
|
841
|
-
return Boolean(installed);
|
|
869
|
+
return Boolean(await getToolPath("ruff"));
|
|
842
870
|
},
|
|
843
871
|
};
|
|
844
872
|
export const blackFormatter = {
|
|
845
873
|
name: "black",
|
|
846
874
|
command: ["black", "$FILE"],
|
|
847
875
|
extensions: [".py", ".pyi"],
|
|
848
|
-
|
|
849
|
-
const venv = await findInVenv("black", cwd);
|
|
850
|
-
if (venv)
|
|
851
|
-
return [venv, filePath];
|
|
852
|
-
return null;
|
|
853
|
-
},
|
|
876
|
+
resolveCommand: managedFormatterResolver("black", [], findInVenv),
|
|
854
877
|
async detect(cwd) {
|
|
855
878
|
return hasBlackConfig(cwd);
|
|
856
879
|
},
|
|
@@ -949,12 +972,7 @@ export const shfmtFormatter = {
|
|
|
949
972
|
...styleArgs,
|
|
950
973
|
]);
|
|
951
974
|
},
|
|
952
|
-
async
|
|
953
|
-
if ((await which("shfmt")) !== null)
|
|
954
|
-
return true;
|
|
955
|
-
const { getToolPath } = await import("./installer/index.js");
|
|
956
|
-
return Boolean(await getToolPath("shfmt"));
|
|
957
|
-
},
|
|
975
|
+
detect: managedToolDetect("shfmt", undefined, async () => (await which("shfmt")) !== null),
|
|
958
976
|
};
|
|
959
977
|
export const nixfmtFormatter = {
|
|
960
978
|
name: "nixfmt",
|
|
@@ -1016,12 +1034,7 @@ export const ktlintFormatter = {
|
|
|
1016
1034
|
return [inPath, "-F", filePath];
|
|
1017
1035
|
return resolveManagedSmartDefaultCommand("ktlint", filePath, ["-F"]);
|
|
1018
1036
|
},
|
|
1019
|
-
async
|
|
1020
|
-
if ((await which("ktlint")) !== null)
|
|
1021
|
-
return true;
|
|
1022
|
-
const { getToolPath } = await import("./installer/index.js");
|
|
1023
|
-
return Boolean(await getToolPath("ktlint"));
|
|
1024
|
-
},
|
|
1037
|
+
detect: managedToolDetect("ktlint", undefined, async () => (await which("ktlint")) !== null),
|
|
1025
1038
|
};
|
|
1026
1039
|
export const ktfmtFormatter = {
|
|
1027
1040
|
name: "ktfmt",
|
|
@@ -1151,36 +1164,25 @@ export const phpCsFixerFormatter = {
|
|
|
1151
1164
|
const configPath = resolvePhpCsFixerConfig(filePath);
|
|
1152
1165
|
const binary = (await findInVendorBin("php-cs-fixer", cwd)) ??
|
|
1153
1166
|
(await which("php-cs-fixer"));
|
|
1167
|
+
const managed = await resolveManagedFormatterCommand("php-cs-fixer", filePath, [], findInVendorBin, cwd);
|
|
1168
|
+
if (managed === FORMATTER_UNAVAILABLE)
|
|
1169
|
+
return FORMATTER_UNAVAILABLE;
|
|
1170
|
+
const resolved = binary ?? managed[0];
|
|
1154
1171
|
// #2413/#2472 review F4: both probes (vendor/bin, then PATH) have
|
|
1155
1172
|
// PROVEN the binary is absent — returning `null` here would fall back
|
|
1156
1173
|
// to the static `command` above, which is the SAME bare
|
|
1157
1174
|
// `php-cs-fixer` this just failed to find, spawning it only to
|
|
1158
1175
|
// re-observe the ENOENT already known. Report the proven-missing
|
|
1159
1176
|
// state instead so `formatFile` skips the wasted spawn.
|
|
1160
|
-
if (!
|
|
1177
|
+
if (!resolved)
|
|
1161
1178
|
return FORMATTER_UNAVAILABLE;
|
|
1162
1179
|
return configPath
|
|
1163
|
-
? [
|
|
1164
|
-
: [
|
|
1165
|
-
},
|
|
1166
|
-
async detect(cwd) {
|
|
1167
|
-
const vendorBin = await findInVendorBin("php-cs-fixer", cwd);
|
|
1168
|
-
const globalBin = await which("php-cs-fixer");
|
|
1169
|
-
if (!vendorBin && !globalBin)
|
|
1170
|
-
return false;
|
|
1171
|
-
// Only run if project has explicit config. This is a presence-only
|
|
1172
|
-
// climb from the project `cwd` (not necessarily the formatted file's
|
|
1173
|
-
// own directory) via this file's own `findUp` — deliberately NOT
|
|
1174
|
-
// merged with `resolvePhpCsFixerConfig` above (#2472 AC4): that
|
|
1175
|
-
// resolver climbs from the FILE's directory and needs the exact
|
|
1176
|
-
// winning path for `--config`, while this only needs a yes/no answer
|
|
1177
|
-
// from whatever `cwd` the caller passed. `rustfmtFormatter.detect`
|
|
1178
|
-
// keeps the same non-merged shape against `resolveCargoPackageEdition`
|
|
1179
|
-
// for the identical reason.
|
|
1180
|
-
const configs = [".php-cs-fixer.php", ".php-cs-fixer.dist.php"];
|
|
1181
|
-
const found = await findUp(configs, cwd);
|
|
1182
|
-
return found.length > 0;
|
|
1180
|
+
? [resolved, "fix", "--config", configPath, filePath]
|
|
1181
|
+
: [resolved, "fix", filePath];
|
|
1183
1182
|
},
|
|
1183
|
+
detect: managedToolDetect("php-cs-fixer", async (cwd) => (await findUp([".php-cs-fixer.php", ".php-cs-fixer.dist.php"], cwd))
|
|
1184
|
+
.length > 0, async (cwd) => Boolean((await findInVendorBin("php-cs-fixer", cwd)) ||
|
|
1185
|
+
(await which("php-cs-fixer")))),
|
|
1184
1186
|
};
|
|
1185
1187
|
export const csharpierFormatter = {
|
|
1186
1188
|
name: "csharpier",
|
|
@@ -1196,9 +1198,7 @@ export const csharpierFormatter = {
|
|
|
1196
1198
|
}
|
|
1197
1199
|
// CSharpier 0.x: invoked through the dotnet driver.
|
|
1198
1200
|
if ((await which("dotnet")) !== null) {
|
|
1199
|
-
const legacy = await
|
|
1200
|
-
timeout: 5000,
|
|
1201
|
-
});
|
|
1201
|
+
const legacy = await probeToolAsync("dotnet", ["csharpier", "--version"], { timeout: 5000 });
|
|
1202
1202
|
if (!legacy.error && legacy.status === 0) {
|
|
1203
1203
|
return ["dotnet", "csharpier", filePath];
|
|
1204
1204
|
}
|
|
@@ -1212,7 +1212,7 @@ export const csharpierFormatter = {
|
|
|
1212
1212
|
// … or the legacy dotnet-driver form (CSharpier 0.x).
|
|
1213
1213
|
if ((await which("dotnet")) === null)
|
|
1214
1214
|
return false;
|
|
1215
|
-
const result = await
|
|
1215
|
+
const result = await probeToolAsync("dotnet", ["csharpier", "--version"], {
|
|
1216
1216
|
timeout: 5000,
|
|
1217
1217
|
});
|
|
1218
1218
|
return !result.error && result.status === 0;
|
|
@@ -1238,23 +1238,8 @@ export const styluaFormatter = {
|
|
|
1238
1238
|
name: "stylua",
|
|
1239
1239
|
command: ["stylua", "$FILE"],
|
|
1240
1240
|
extensions: [".lua"],
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
// managed install, so before this the ONLY resolution was a bare
|
|
1244
|
-
// `stylua` PATH lookup — a project-local install via npm
|
|
1245
|
-
// `@johnnymorganz/stylua` (`node_modules/.bin/stylua`) was invisible.
|
|
1246
|
-
const local = findLocalBinUpwards("stylua", cwd);
|
|
1247
|
-
return local ? [local, filePath] : null;
|
|
1248
|
-
},
|
|
1249
|
-
async detect(cwd) {
|
|
1250
|
-
const local = findLocalBinUpwards("stylua", cwd);
|
|
1251
|
-
if (!local && (await which("stylua")) === null)
|
|
1252
|
-
return false;
|
|
1253
|
-
// Prefer explicit config but also run if binary is present in a Lua project
|
|
1254
|
-
const configs = ["stylua.toml", ".stylua.toml"];
|
|
1255
|
-
const found = await findUp(configs, cwd);
|
|
1256
|
-
return found.length > 0;
|
|
1257
|
-
},
|
|
1241
|
+
resolveCommand: managedFormatterResolver("stylua", [], findLocalBinUpwards),
|
|
1242
|
+
detect: managedToolDetect("stylua", async (cwd) => (await findUp(["stylua.toml", ".stylua.toml"], cwd)).length > 0, async (cwd) => Boolean(findLocalBinUpwards("stylua", cwd) || (await which("stylua")))),
|
|
1258
1243
|
};
|
|
1259
1244
|
export const ormoluFormatter = {
|
|
1260
1245
|
name: "ormolu",
|
|
@@ -1274,42 +1259,28 @@ export const taploFormatter = {
|
|
|
1274
1259
|
return [inPath, "fmt", filePath];
|
|
1275
1260
|
return resolveManagedSmartDefaultCommand("taplo", filePath, ["fmt"]);
|
|
1276
1261
|
},
|
|
1277
|
-
async
|
|
1278
|
-
if ((await which("taplo")) !== null)
|
|
1279
|
-
return true;
|
|
1280
|
-
const { getToolPath } = await import("./installer/index.js");
|
|
1281
|
-
return Boolean(await getToolPath("taplo"));
|
|
1282
|
-
},
|
|
1262
|
+
detect: managedToolDetect("taplo", undefined, async () => (await which("taplo")) !== null),
|
|
1283
1263
|
};
|
|
1284
1264
|
export const googleJavaFormatFormatter = {
|
|
1285
1265
|
name: "google-java-format",
|
|
1286
1266
|
command: ["google-java-format", "--replace", "$FILE"],
|
|
1287
1267
|
extensions: [".java"],
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
return false;
|
|
1291
|
-
return hasGoogleJavaFormatConfig(cwd);
|
|
1292
|
-
},
|
|
1268
|
+
resolveCommand: managedFormatterResolver("google-java-format", ["--replace"]),
|
|
1269
|
+
detect: managedToolDetect("google-java-format", hasGoogleJavaFormatConfig, async () => (await which("google-java-format")) !== null),
|
|
1293
1270
|
};
|
|
1294
1271
|
export const cljfmtFormatter = {
|
|
1295
1272
|
name: "cljfmt",
|
|
1296
1273
|
command: ["cljfmt", "fix", "$FILE"],
|
|
1297
1274
|
extensions: [".clj", ".cljc", ".cljs"],
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
return false;
|
|
1301
|
-
return hasCljfmtConfig(cwd);
|
|
1302
|
-
},
|
|
1275
|
+
resolveCommand: managedFormatterResolver("cljfmt", ["fix"]),
|
|
1276
|
+
detect: managedToolDetect("cljfmt", hasCljfmtConfig, async () => (await which("cljfmt")) !== null),
|
|
1303
1277
|
};
|
|
1304
1278
|
export const cmakeFormatFormatter = {
|
|
1305
1279
|
name: "cmake-format",
|
|
1306
1280
|
command: ["cmake-format", "-i", "$FILE"],
|
|
1307
1281
|
extensions: [".cmake"],
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
return false;
|
|
1311
|
-
return hasCmakeFormatConfig(cwd);
|
|
1312
|
-
},
|
|
1282
|
+
resolveCommand: managedFormatterResolver("cmake-format", ["-i"]),
|
|
1283
|
+
detect: managedToolDetect("cmake-format", hasCmakeFormatConfig, async () => (await which("cmake-format")) !== null),
|
|
1313
1284
|
};
|
|
1314
1285
|
export const cueFormatter = {
|
|
1315
1286
|
name: "cue",
|
|
@@ -1382,7 +1353,7 @@ export const psscriptanalyzerFormatFormatter = {
|
|
|
1382
1353
|
if (!pwsh)
|
|
1383
1354
|
return false;
|
|
1384
1355
|
// Check PSScriptAnalyzer module is available
|
|
1385
|
-
const result = await
|
|
1356
|
+
const result = await probeToolAsync(pwsh, [
|
|
1386
1357
|
"-NoProfile",
|
|
1387
1358
|
"-Command",
|
|
1388
1359
|
"Get-Module -ListAvailable PSScriptAnalyzer | Select-Object -First 1 -ExpandProperty Name",
|
|
@@ -1438,7 +1409,7 @@ const detectionCache = new BoundedLruCache(32);
|
|
|
1438
1409
|
// The signature is immutable for a cache generation. This memo is separate
|
|
1439
1410
|
// from detectionCache because a cwd can have several extension entries, and a
|
|
1440
1411
|
// warm lookup must not repeat the ancestor walk or stat matched configs.
|
|
1441
|
-
const formatterSignatureFlights = new
|
|
1412
|
+
const formatterSignatureFlights = new BoundedLruCache(32);
|
|
1442
1413
|
const formatterCacheGeneration = createGenerationSource("formatter-cache");
|
|
1443
1414
|
// These are the formatter configuration files consulted by the policy helpers
|
|
1444
1415
|
// above. Their metadata is captured by the cold detection signature. The
|
|
@@ -1810,6 +1781,7 @@ export function _getFormatterResetStateForTests() {
|
|
|
1810
1781
|
whichTransientCommands,
|
|
1811
1782
|
cooldownRecordedForRetryAtMs,
|
|
1812
1783
|
detectionCache,
|
|
1784
|
+
formatterSignatureFlights,
|
|
1813
1785
|
};
|
|
1814
1786
|
}
|
|
1815
1787
|
export function clearFormatterRuntimeState() {
|
|
@@ -1887,6 +1859,7 @@ export async function formatFile(filePath, formatter) {
|
|
|
1887
1859
|
try {
|
|
1888
1860
|
const absolutePath = path.resolve(filePath);
|
|
1889
1861
|
const cwd = path.dirname(absolutePath);
|
|
1862
|
+
const formatterCwd = resolveFormatterCwd(absolutePath, formatter.name);
|
|
1890
1863
|
const contentBefore = await fs.readFile(absolutePath, "utf-8");
|
|
1891
1864
|
// Resolve command: prefer local (venv/vendor/node_modules) over global.
|
|
1892
1865
|
// The shared seam must honor SKIP_FORMATTING before selecting the static
|
|
@@ -1912,7 +1885,7 @@ export async function formatFile(filePath, formatter) {
|
|
|
1912
1885
|
// Run formatter without blocking the event loop.
|
|
1913
1886
|
const result = await safeSpawnAsync(cmd[0], cmd.slice(1), {
|
|
1914
1887
|
timeout: 15000,
|
|
1915
|
-
cwd,
|
|
1888
|
+
cwd: formatterCwd,
|
|
1916
1889
|
});
|
|
1917
1890
|
// A resolver that could NOT prove absence (it never probed PATH — e.g.
|
|
1918
1891
|
// black/sqlfluff, which only look in a venv) legitimately falls back to the
|
|
@@ -205,7 +205,7 @@ function hasGeneratedArtifactContent(content) {
|
|
|
205
205
|
* file is not machine-emitted evidence), and an empty or whitespace-only
|
|
206
206
|
* file yields a zero mean and stays source.
|
|
207
207
|
*/
|
|
208
|
-
|
|
208
|
+
const MACHINE_EMITTED_LINE_SHAPE_MIN_CONTENT = 2048;
|
|
209
209
|
export const MACHINE_EMITTED_LINE_SHAPE_MEAN_THRESHOLD = 2500;
|
|
210
210
|
export function classifyMachineEmittedLineShape(content) {
|
|
211
211
|
const contentLength = content.length;
|
|
@@ -325,8 +325,7 @@ function analyzeFileHeader(filePath, maxBytes) {
|
|
|
325
325
|
* function and short-circuit on a hit, so by the time this function runs for
|
|
326
326
|
* those callers evidence (a) has already come back negative. Non-walk
|
|
327
327
|
* callers with no upstream sibling probe (`file-role.ts`'s content-based
|
|
328
|
-
* check,
|
|
329
|
-
* evaluate evidence (b) only — a documented, narrower guarantee than the
|
|
328
|
+
* check) evaluate evidence (b) only — a documented, narrower guarantee than the
|
|
330
329
|
* walk path's.
|
|
331
330
|
*
|
|
332
331
|
* Tradeoff (documented per the issue): when NEITHER piece of evidence
|
|
@@ -387,7 +386,7 @@ export function classifyGeneratedOrArtifactDetailed(filePath, options = {}) {
|
|
|
387
386
|
: { verdict: "clean" };
|
|
388
387
|
}
|
|
389
388
|
/** Verdict-only convenience wrapper over {@link classifyGeneratedOrArtifactDetailed}. */
|
|
390
|
-
|
|
389
|
+
function classifyGeneratedOrArtifact(filePath, options = {}) {
|
|
391
390
|
return classifyGeneratedOrArtifactDetailed(filePath, options).verdict;
|
|
392
391
|
}
|
|
393
392
|
export function isGeneratedOrArtifact(filePath, options = {}) {
|
|
@@ -141,6 +141,11 @@ function canonicalizeGuardCommand(command) {
|
|
|
141
141
|
let pendingSpace = false;
|
|
142
142
|
quote = undefined;
|
|
143
143
|
for (const ch of result) {
|
|
144
|
+
if (!quote && ch === "\n") {
|
|
145
|
+
collapsed += ch;
|
|
146
|
+
pendingSpace = false;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
144
149
|
if (!quote && /\s/.test(ch)) {
|
|
145
150
|
pendingSpace = collapsed.length > 0;
|
|
146
151
|
continue;
|
|
@@ -45,11 +45,12 @@
|
|
|
45
45
|
* module doc for why blanket gitignore-respect is wrong for a secrets scanner
|
|
46
46
|
* (an untracked `.env` with a real credential is exactly the case gitleaks
|
|
47
47
|
* exists to catch, and `.env` is commonly gitignored). `classifyAndFilterFindings`
|
|
48
|
-
* backstops the config with a TS-side re-classification
|
|
49
|
-
* secrets-lane scratch tier
|
|
50
|
-
* `
|
|
51
|
-
*
|
|
52
|
-
*
|
|
48
|
+
* backstops the config with a TS-side re-classification. Findings under the
|
|
49
|
+
* secrets-lane scratch tier, in gitignored files, or inside nested repositories
|
|
50
|
+
* are DEMOTED (not deleted) to `severity: "info"` / `semantic: "none"` by the
|
|
51
|
+
* project-diagnostics adapter. They remain visible for an explicit audit but
|
|
52
|
+
* can never surface as blocking "leaked secret" alarms or automatic turn
|
|
53
|
+
* context. Tracked and ordinary untracked files remain blocking.
|
|
53
54
|
*
|
|
54
55
|
* Refs: #130, #1562
|
|
55
56
|
*/
|
|
@@ -333,8 +334,10 @@ export class GitleaksClient extends SecurityScanClient {
|
|
|
333
334
|
}
|
|
334
335
|
const rawFindings = parseGitleaksReport(fs.readFileSync(reportPath, "utf-8"));
|
|
335
336
|
const findings = await classifyAndFilterFindings(rawFindings, cwd);
|
|
337
|
+
// #2154: the one gitleaks site that parsed a scan of this root.
|
|
336
338
|
return {
|
|
337
339
|
success: true,
|
|
340
|
+
analyzed: true,
|
|
338
341
|
findings,
|
|
339
342
|
scannedAt,
|
|
340
343
|
};
|
|
@@ -375,7 +378,10 @@ export class GitleaksClient extends SecurityScanClient {
|
|
|
375
378
|
* assert the config we hand it, so this backstop guarantees the demotion
|
|
376
379
|
* even if the generated regex ever mismatches gitleaks's own path
|
|
377
380
|
* normalization.
|
|
378
|
-
* 2.
|
|
381
|
+
* 2. A finding below a nested git boundary is labelled
|
|
382
|
+
* `nested-repository`. Parent scans must not claim ownership of a
|
|
383
|
+
* submodule's or nested repository's files.
|
|
384
|
+
* 3. Every OTHER finding gets `pathStatus` set from git's tracked/ignored
|
|
379
385
|
* view, so a future false-positive triage is a log read.
|
|
380
386
|
*
|
|
381
387
|
* `collectTrackedFiles`/`collectUntrackedIgnoredIds` degrade to `undefined`
|
|
@@ -383,6 +389,32 @@ export class GitleaksClient extends SecurityScanClient {
|
|
|
383
389
|
* keep `pathStatus: undefined` rather than a guessed value (fail-open,
|
|
384
390
|
* matching `git-tracked-ignore.ts`'s own contract). Exported for unit tests.
|
|
385
391
|
*/
|
|
392
|
+
function isInsideNestedGitRepository(file, cwd) {
|
|
393
|
+
const root = path.resolve(cwd);
|
|
394
|
+
const absoluteFile = path.isAbsolute(file)
|
|
395
|
+
? path.resolve(file)
|
|
396
|
+
: path.resolve(root, file);
|
|
397
|
+
const relative = path.relative(root, absoluteFile);
|
|
398
|
+
if (relative === ".." ||
|
|
399
|
+
relative.startsWith(`..${path.sep}`) ||
|
|
400
|
+
path.isAbsolute(relative))
|
|
401
|
+
return false;
|
|
402
|
+
let directory = path.dirname(absoluteFile);
|
|
403
|
+
while (directory !== root) {
|
|
404
|
+
try {
|
|
405
|
+
if (fs.existsSync(path.join(directory, ".git")))
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
const parent = path.dirname(directory);
|
|
412
|
+
if (parent === directory)
|
|
413
|
+
return false;
|
|
414
|
+
directory = parent;
|
|
415
|
+
}
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
386
418
|
export async function classifyAndFilterFindings(findings, cwd) {
|
|
387
419
|
if (findings.length === 0)
|
|
388
420
|
return findings;
|
|
@@ -399,6 +431,10 @@ export async function classifyAndFilterFindings(findings, cwd) {
|
|
|
399
431
|
const absPath = path.isAbsolute(finding.file)
|
|
400
432
|
? finding.file
|
|
401
433
|
: path.resolve(cwd, finding.file);
|
|
434
|
+
if (isInsideNestedGitRepository(absPath, cwd)) {
|
|
435
|
+
classified.push({ ...finding, pathStatus: "nested-repository" });
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
402
438
|
let pathStatus;
|
|
403
439
|
if (trackedIds?.has(normalizeEphemeralMapKey(absPath))) {
|
|
404
440
|
pathStatus = "tracked";
|
|
@@ -20,6 +20,8 @@ import * as fs from "node:fs";
|
|
|
20
20
|
import * as os from "node:os";
|
|
21
21
|
import * as path from "node:path";
|
|
22
22
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
23
|
+
import { probeToolAsync } from "./tool-probe.js";
|
|
24
|
+
import { firstOutputLine, spawnFailedWithNoOutput, } from "./dispatch/runners/utils/spawn-outcome.js";
|
|
23
25
|
import { assertInstallAllowed } from "./project-trust.js";
|
|
24
26
|
import { SecurityScanClient } from "./security-scan-client.js";
|
|
25
27
|
import { classifyProbeFailure, describeProbeEvidence, INSTALL_TRANSIENT_MAX_ATTEMPTS, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
|
|
@@ -86,7 +88,7 @@ export class GovulncheckClient extends SecurityScanClient {
|
|
|
86
88
|
let goOnPath;
|
|
87
89
|
let goHostStallMs;
|
|
88
90
|
try {
|
|
89
|
-
goOnPath = await
|
|
91
|
+
goOnPath = await probeToolAsync("go", ["version"], {
|
|
90
92
|
timeout: 5000,
|
|
91
93
|
});
|
|
92
94
|
}
|
|
@@ -219,7 +221,7 @@ export class GovulncheckClient extends SecurityScanClient {
|
|
|
219
221
|
let reprobe;
|
|
220
222
|
let reprobeHostStallMs;
|
|
221
223
|
try {
|
|
222
|
-
reprobe = await
|
|
224
|
+
reprobe = await probeToolAsync("govulncheck", ["-version"], {
|
|
223
225
|
timeout: 5000,
|
|
224
226
|
});
|
|
225
227
|
}
|
|
@@ -366,21 +368,60 @@ export class GovulncheckClient extends SecurityScanClient {
|
|
|
366
368
|
const bin = this.binaryPath ?? "govulncheck";
|
|
367
369
|
try {
|
|
368
370
|
const result = await safeSpawnAsync(bin, ["-mode=source", "-format=json", "./..."], { cwd, timeout: SCAN_TIMEOUT_MS });
|
|
371
|
+
// #2154 v4 F1: this was the one runner client with no `result.error`
|
|
372
|
+
// check before its parse site. `safeSpawnAsync` reports a timeout or
|
|
373
|
+
// an ambient abort as `{status: null, error, stdout: <partial>}`, and
|
|
374
|
+
// the old guard (`!stdout.trim() && status !== 0 && status !== 3`)
|
|
375
|
+
// let that partial stream through as an authoritative scan. Same
|
|
376
|
+
// order as the seven sibling clients: the process first, its output
|
|
377
|
+
// second.
|
|
378
|
+
if (result.error) {
|
|
379
|
+
this.log(`Scan error: ${result.error.message}`);
|
|
380
|
+
return {
|
|
381
|
+
...EMPTY_RESULT,
|
|
382
|
+
scannedAt,
|
|
383
|
+
summary: result.error.message.slice(0, 200),
|
|
384
|
+
};
|
|
385
|
+
}
|
|
369
386
|
// govulncheck exits non-zero (status 3) when vulnerabilities are
|
|
370
|
-
// found — that's success from our perspective.
|
|
371
|
-
//
|
|
387
|
+
// found — that's success from our perspective. Any other exit code
|
|
388
|
+
// means the scan was not analysed, even if its config preamble or
|
|
389
|
+
// partial stream reached stdout before the failure.
|
|
372
390
|
const rawStdout = result.stdout ?? "";
|
|
373
|
-
if (
|
|
374
|
-
|
|
391
|
+
if (result.status !== 0 && result.status !== 3) {
|
|
392
|
+
return {
|
|
393
|
+
...EMPTY_RESULT,
|
|
394
|
+
scannedAt,
|
|
395
|
+
summary: firstOutputLine(result.stderr) || "scan failed",
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
if (!rawStdout.trim()) {
|
|
399
|
+
// Nothing to parse. `govulncheck -format=json` always writes a
|
|
400
|
+
// stream (a `config` record at minimum), so an empty one is never
|
|
401
|
+
// evidence of "no vulnerabilities" — it must not retire a retained
|
|
402
|
+
// CVE finding, whatever the exit code. `spawnFailedWithNoOutput`
|
|
403
|
+
// is the same shared discriminator knip and dead-code use, in
|
|
404
|
+
// place of this file's hand-rolled status test.
|
|
405
|
+
if (spawnFailedWithNoOutput(result, rawStdout)) {
|
|
406
|
+
this.log(`Scan failed: ${(result.stderr ?? "").slice(0, 200)}`);
|
|
407
|
+
return {
|
|
408
|
+
...EMPTY_RESULT,
|
|
409
|
+
scannedAt,
|
|
410
|
+
summary: firstOutputLine(result.stderr) || "scan failed",
|
|
411
|
+
};
|
|
412
|
+
}
|
|
375
413
|
return {
|
|
376
414
|
...EMPTY_RESULT,
|
|
415
|
+
success: true,
|
|
377
416
|
scannedAt,
|
|
378
|
-
summary:
|
|
417
|
+
summary: "govulncheck produced no output; nothing parsed",
|
|
379
418
|
};
|
|
380
419
|
}
|
|
381
420
|
const findings = parseGovulncheckJson(rawStdout);
|
|
421
|
+
// #2154: the one govulncheck site that parsed a scan of this root.
|
|
382
422
|
return {
|
|
383
423
|
success: true,
|
|
424
|
+
analyzed: true,
|
|
384
425
|
findings,
|
|
385
426
|
scannedAt,
|
|
386
427
|
};
|