@herbertgao/pi-extensions 2026.9.0 → 2026.9.1
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/node_modules/@czottmann/pi-automode/CHANGELOG.md +13 -1
- package/node_modules/@czottmann/pi-automode/README.md +20 -0
- package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +1 -1
- package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +2 -1
- package/node_modules/@czottmann/pi-automode/docs/defaults.md +3 -2
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +38 -2
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +5 -3
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +11 -3
- package/node_modules/@czottmann/pi-automode/package.json +1 -1
- package/node_modules/@pi-plugins/fast-mode/README.md +5 -3
- package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
- package/node_modules/pi-lens/CHANGELOG.md +988 -0
- package/node_modules/pi-lens/README.md +3 -0
- package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
- package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
- package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +1 -3
- package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -4
- package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
- package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
- package/node_modules/pi-lens/dist/clients/cache-observability.js +44 -18
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
- package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
- package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
- package/node_modules/pi-lens/dist/clients/config-core/index.js +47 -0
- package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
- package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
- package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
- package/node_modules/pi-lens/dist/clients/config-core/provenance.js +171 -0
- package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
- package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
- package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
- package/node_modules/pi-lens/dist/clients/config-core/schema.js +167 -0
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
- package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
- package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
- package/node_modules/pi-lens/dist/clients/config-schema.js +197 -0
- package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
- package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
- package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
- package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
- package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +14 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -18
- package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +380 -26
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +13 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -3
- package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
- package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
- package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
- package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
- package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
- package/node_modules/pi-lens/dist/clients/file-time.js +6 -1
- package/node_modules/pi-lens/dist/clients/file-utils.js +112 -9
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
- package/node_modules/pi-lens/dist/clients/format-service.js +6 -1
- package/node_modules/pi-lens/dist/clients/formatters.js +276 -102
- package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +114 -19
- package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
- package/node_modules/pi-lens/dist/clients/git-guard.js +1 -1
- package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
- package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
- package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
- package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
- package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
- package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
- package/node_modules/pi-lens/dist/clients/installer/index.js +71 -52
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +104 -230
- package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
- package/node_modules/pi-lens/dist/clients/language-registry.js +595 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +160 -2
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +13 -0
- package/node_modules/pi-lens/dist/clients/lens-config.js +141 -30
- package/node_modules/pi-lens/dist/clients/lens-engine.js +30 -37
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +59 -6
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -2
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +168 -30
- package/node_modules/pi-lens/dist/clients/lsp/config.js +483 -158
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +15 -2
- package/node_modules/pi-lens/dist/clients/lsp/index.js +676 -71
- package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -169
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -16
- package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
- package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +9 -2
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
- package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
- package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +2 -4
- package/node_modules/pi-lens/dist/clients/module-report.js +20 -45
- package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
- package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
- package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -20
- package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
- package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +68 -22
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +4 -0
- package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
- package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
- package/node_modules/pi-lens/dist/clients/path-utils.js +428 -13
- package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +77 -16
- package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
- package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
- package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +393 -122
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
- package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
- package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
- package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
- package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +15 -4
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
- package/node_modules/pi-lens/dist/clients/read-guard.js +21 -0
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +152 -109
- package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
- package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +18 -2
- package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
- package/node_modules/pi-lens/dist/clients/runtime-session.js +139 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +168 -35
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
- package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
- package/node_modules/pi-lens/dist/clients/sanitize.js +7 -1
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
- package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
- package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
- package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
- package/node_modules/pi-lens/dist/clients/sgconfig.js +6 -1
- package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
- package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
- package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
- package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +239 -0
- package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
- package/node_modules/pi-lens/dist/clients/tool-policy.js +91 -21
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +86 -48
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
- package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
- package/node_modules/pi-lens/dist/clients/user-notify.js +6 -2
- package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/word-index-store.js +20 -5
- package/node_modules/pi-lens/dist/clients/word-index.js +243 -31
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +3 -0
- package/node_modules/pi-lens/dist/index.js +64335 -54300
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
- package/node_modules/pi-lens/dist/mcp/server.js +127 -5
- package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
- package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
- package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +95 -24
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +67 -80
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
- package/node_modules/pi-lens/dist/tools/shared.js +0 -1
- package/node_modules/pi-lens/docs/agent-guide.md +64 -1
- package/node_modules/pi-lens/docs/configuration.md +222 -0
- package/node_modules/pi-lens/docs/dependencies.md +3 -3
- package/node_modules/pi-lens/docs/features.md +44 -5
- package/node_modules/pi-lens/docs/language-coverage.md +2 -2
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +27 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +38 -0
- package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
- package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
- package/node_modules/pi-lens/docs/release-qa-baseline.md +182 -0
- package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
- package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
- package/node_modules/pi-lens/package.json +15 -23
- package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
- package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
- package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
- package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
- package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
- package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
- package/node_modules/pi-web-access/CHANGELOG.md +28 -0
- package/node_modules/pi-web-access/README.md +62 -15
- package/node_modules/pi-web-access/curator-page.ts +3 -1
- package/node_modules/pi-web-access/curator-server.ts +5 -1
- package/node_modules/pi-web-access/gemini-search.ts +8 -4
- package/node_modules/pi-web-access/github-extract.ts +242 -1
- package/node_modules/pi-web-access/index.ts +118 -64
- package/node_modules/pi-web-access/mistral-search.ts +281 -0
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/perplexity.ts +14 -1
- package/node_modules/pi-web-access/utils.ts +23 -6
- package/node_modules/pi-web-access/xai-search.ts +96 -33
- package/package.json +5 -5
|
@@ -56,6 +56,73 @@ export const OPAQUE_MTIME_TOLERANCE_MS = 150;
|
|
|
56
56
|
// answer usefully about, and it is what makes the truncation guard below
|
|
57
57
|
// reachable at all (#2100).
|
|
58
58
|
const MAX_GIT_STATUS_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
59
|
+
/**
|
|
60
|
+
* Stat (and optionally hash) an EXPLICIT file list into a snapshot.
|
|
61
|
+
*
|
|
62
|
+
* Extracted from `captureFileStats` so the observational net (#2430) reuses
|
|
63
|
+
* exactly this identity function — same key form, same hash algorithm, same
|
|
64
|
+
* byte budget — instead of hand-rolling a second scanner whose diff could
|
|
65
|
+
* disagree with `diffFileStats`. The walk-and-cap half stays in
|
|
66
|
+
* `captureFileStats`; a caller that already knows its file set skips it.
|
|
67
|
+
*
|
|
68
|
+
* `hashBudgetBytes` defaults to {@link OPAQUE_HASH_BUDGET_BYTES}. A file past
|
|
69
|
+
* the budget simply carries no hash, and its comparison degrades to
|
|
70
|
+
* mtime+size — the same documented limitation the walking capture has.
|
|
71
|
+
*
|
|
72
|
+
* `deadlineMs` (an absolute epoch stamp) and `signal` are both checked
|
|
73
|
+
* BETWEEN files, so a multi-path capture cannot outrun either bound. The FIRST
|
|
74
|
+
* entry always runs: the observational net's settle (#2430) guarantees the
|
|
75
|
+
* target path it took a baseline for is re-captured no matter what the clock
|
|
76
|
+
* says (#2449 review round 2, F5), and a bound that could cut the one path the
|
|
77
|
+
* caller actually asked about would drop a mutation already measured.
|
|
78
|
+
*
|
|
79
|
+
* There was a synchronous twin of this function until #2449 review round 3
|
|
80
|
+
* (T4). It existed only so `handleToolResult` could settle an observation
|
|
81
|
+
* without yielding; the settle is an ordinary async step now, and blocking the
|
|
82
|
+
* event loop on a directory's worth of `readFileSync` was never worth the
|
|
83
|
+
* ordering shortcut it bought.
|
|
84
|
+
*/
|
|
85
|
+
export async function captureFileStatsForPaths(files, options = {}) {
|
|
86
|
+
const hashBudget = options.hashBudgetBytes ?? OPAQUE_HASH_BUDGET_BYTES;
|
|
87
|
+
const snapshot = new Map();
|
|
88
|
+
let hashBytesSpent = 0;
|
|
89
|
+
let stoppedEarly = false;
|
|
90
|
+
let first = true;
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
if (!first) {
|
|
93
|
+
if (options.signal?.aborted === true) {
|
|
94
|
+
stoppedEarly = true;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
if (options.deadlineMs !== undefined &&
|
|
98
|
+
Date.now() >= options.deadlineMs) {
|
|
99
|
+
stoppedEarly = true;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
first = false;
|
|
104
|
+
try {
|
|
105
|
+
const stat = await fs.promises.stat(file);
|
|
106
|
+
if (!stat.isFile())
|
|
107
|
+
continue;
|
|
108
|
+
const entry = {
|
|
109
|
+
mtimeMs: stat.mtimeMs,
|
|
110
|
+
size: stat.size,
|
|
111
|
+
};
|
|
112
|
+
if (options.withHashes && hashBytesSpent + stat.size <= hashBudget) {
|
|
113
|
+
entry.hash = createHash("sha256")
|
|
114
|
+
.update(await fs.promises.readFile(file))
|
|
115
|
+
.digest("hex");
|
|
116
|
+
hashBytesSpent += stat.size;
|
|
117
|
+
}
|
|
118
|
+
snapshot.set(normalizeMapKey(path.resolve(file)), entry);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Vanished mid-walk: absent from both snapshots = unchanged-by-absence.
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { snapshot, stoppedEarly };
|
|
125
|
+
}
|
|
59
126
|
export async function captureFileStats(root, options = {}) {
|
|
60
127
|
const budgetMs = options.budgetMs ?? 50;
|
|
61
128
|
try {
|
|
@@ -74,28 +141,7 @@ export async function captureFileStats(root, options = {}) {
|
|
|
74
141
|
scannedCount: files.length,
|
|
75
142
|
};
|
|
76
143
|
}
|
|
77
|
-
const snapshot =
|
|
78
|
-
let hashBytesSpent = 0;
|
|
79
|
-
for (const file of files) {
|
|
80
|
-
try {
|
|
81
|
-
const stat = await fs.promises.stat(file);
|
|
82
|
-
const entry = {
|
|
83
|
-
mtimeMs: stat.mtimeMs,
|
|
84
|
-
size: stat.size,
|
|
85
|
-
};
|
|
86
|
-
if (options.withHashes &&
|
|
87
|
-
hashBytesSpent + stat.size <= OPAQUE_HASH_BUDGET_BYTES) {
|
|
88
|
-
entry.hash = createHash("sha256")
|
|
89
|
-
.update(await fs.promises.readFile(file))
|
|
90
|
-
.digest("hex");
|
|
91
|
-
hashBytesSpent += stat.size;
|
|
92
|
-
}
|
|
93
|
-
snapshot.set(normalizeMapKey(path.resolve(file)), entry);
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
// Vanished mid-walk: absent from both snapshots = unchanged-by-absence.
|
|
97
|
-
}
|
|
98
|
-
}
|
|
144
|
+
const { snapshot } = await captureFileStatsForPaths(files, options);
|
|
99
145
|
return { snapshot, scannedCount: snapshot.size };
|
|
100
146
|
}
|
|
101
147
|
catch {
|
|
@@ -13,6 +13,10 @@ export const LOCAL_OPENGREP_CONFIG_NAMES = [
|
|
|
13
13
|
"semgrep.yml",
|
|
14
14
|
"semgrep.yaml",
|
|
15
15
|
];
|
|
16
|
+
// Deliberately UNCEILINGED at $HOME (refs #2472 review round 3, F1):
|
|
17
|
+
// opengrep/semgrep's own resolver reads a rule file wherever it sits, and a
|
|
18
|
+
// user-level `~/.opengrep.yml` is a legitimate global config, not an
|
|
19
|
+
// escaped-workspace accident.
|
|
16
20
|
export function findLocalOpengrepConfig(startDir) {
|
|
17
21
|
return findLocalToolConfig(startDir, LOCAL_OPENGREP_CONFIG_NAMES);
|
|
18
22
|
}
|
|
@@ -19,6 +19,8 @@ import * as path from "node:path";
|
|
|
19
19
|
import { classifyProbeFailure, createAvailabilityLatch, isTransientDecision, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
|
|
20
20
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
21
21
|
import { createAvailabilityProbeFlight } from "./availability-probe-flight.js";
|
|
22
|
+
import { createGenerationSource, } from "./generation-guard.js";
|
|
23
|
+
import { isAtOrAboveHomeDir, walkUpDirs } from "./path-utils.js";
|
|
22
24
|
/**
|
|
23
25
|
* Fallback preference when nothing is declared (or the declared manager is
|
|
24
26
|
* missing). npm first for maximum compatibility; bun last. A project lockfile
|
|
@@ -197,11 +199,16 @@ function isAvailable(pm, onTransient) {
|
|
|
197
199
|
* day, start a fresh session, pi-lens still reports it missing until a
|
|
198
200
|
* process restart (#1653). Called from `handleSessionStart`'s per-session
|
|
199
201
|
* reset block beside `resetZizmorTokenAvailability()` /
|
|
200
|
-
* `resetPsScriptAnalyzerAvailability()`; also used directly by tests.
|
|
202
|
+
* `resetPsScriptAnalyzerAvailability()`; also used directly by tests. Also
|
|
203
|
+
* clears `globalBinDirCache`, whose memo lifetime rides the same reset — it
|
|
204
|
+
* must not outlive the availability verdicts it was derived from (#1602).
|
|
201
205
|
*/
|
|
202
206
|
export function _resetPackageManagerCache() {
|
|
207
|
+
packageManagerCacheGeneration.bump();
|
|
203
208
|
availabilityLatches.clear();
|
|
204
209
|
packageManagerProbeFlights.clear();
|
|
210
|
+
globalBinDirCache.clear();
|
|
211
|
+
globalBinDirProbeFlights.clear();
|
|
205
212
|
}
|
|
206
213
|
// ============================================================================
|
|
207
214
|
// RESOLUTION
|
|
@@ -311,12 +318,39 @@ export function execArgs(pm, pkg, args = []) {
|
|
|
311
318
|
// ============================================================================
|
|
312
319
|
// GLOBAL BIN DISCOVERY
|
|
313
320
|
// ============================================================================
|
|
321
|
+
/**
|
|
322
|
+
* Per-manager global bin dir, memoized for the process (cleared alongside
|
|
323
|
+
* `availabilityLatches` in `_resetPackageManagerCache`). Only a SUCCESSFUL
|
|
324
|
+
* lookup is cached: a spawn failure here is evidence about this call, not
|
|
325
|
+
* about the manager (the manager already passed `isAvailable`'s own probe),
|
|
326
|
+
* so it must not latch an empty result the way a genuine absence would. The
|
|
327
|
+
* generation guard owns post-await cache publication, so a probe crossing
|
|
328
|
+
* a session reset cannot repopulate the fresh memo. Reset clears the flight
|
|
329
|
+
* map separately, so later callers cannot share the pre-reset probe.
|
|
330
|
+
*/
|
|
331
|
+
const globalBinDirCache = new Map();
|
|
332
|
+
const packageManagerCacheGeneration = createGenerationSource("package-manager-global-bin");
|
|
333
|
+
const globalBinDirProbeFlights = createAvailabilityProbeFlight();
|
|
314
334
|
/** Directories where a given manager installs global binaries. */
|
|
315
335
|
async function globalBinDirsFor(pm) {
|
|
336
|
+
const cached = globalBinDirCache.get(pm);
|
|
337
|
+
if (cached)
|
|
338
|
+
return cached;
|
|
339
|
+
const generation = packageManagerCacheGeneration.capture();
|
|
340
|
+
// Concurrent callers (dispatch runs several tools per edit) must share ONE
|
|
341
|
+
// probe rather than each spawning their own `npm config get prefix` (#1602).
|
|
342
|
+
const shared = globalBinDirProbeFlights.run(pm, () => probeGlobalBinDirs(pm, generation));
|
|
343
|
+
return shared.promise;
|
|
344
|
+
}
|
|
345
|
+
async function probeGlobalBinDirs(pm, generation) {
|
|
316
346
|
if (pm === "bun") {
|
|
317
347
|
// bun has no per-call query cost — the global bin dir is deterministic.
|
|
318
348
|
const base = process.env.BUN_INSTALL || path.join(os.homedir(), ".bun");
|
|
319
|
-
|
|
349
|
+
const dirs = [path.join(base, "bin")];
|
|
350
|
+
generation.guardedWrite(pm, () => {
|
|
351
|
+
globalBinDirCache.set(pm, dirs);
|
|
352
|
+
});
|
|
353
|
+
return dirs;
|
|
320
354
|
}
|
|
321
355
|
const query = pm === "npm"
|
|
322
356
|
? ["config", "get", "prefix"]
|
|
@@ -331,10 +365,11 @@ async function globalBinDirsFor(pm) {
|
|
|
331
365
|
return [];
|
|
332
366
|
// npm reports a prefix; binaries live in `<prefix>/bin` on Unix, `<prefix>`
|
|
333
367
|
// on Windows. pnpm/yarn already print the bin dir directly.
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
368
|
+
const dirs = pm === "npm" ? [onWindows() ? out : path.join(out, "bin")] : [out];
|
|
369
|
+
generation.guardedWrite(pm, () => {
|
|
370
|
+
globalBinDirCache.set(pm, dirs);
|
|
371
|
+
});
|
|
372
|
+
return dirs;
|
|
338
373
|
}
|
|
339
374
|
/**
|
|
340
375
|
* Global bin directories for every installed manager, deduped. Used to locate a
|
|
@@ -394,31 +429,165 @@ export async function findGlobalBinary(command, windowsExt = ".cmd") {
|
|
|
394
429
|
return undefined;
|
|
395
430
|
}
|
|
396
431
|
/**
|
|
397
|
-
*
|
|
432
|
+
* The three project-local bin-directory shapes in this repo, most-preferred
|
|
433
|
+
* first. Exported so every caller passes the SAME constant instead of
|
|
434
|
+
* re-listing the directory names next to its own copy of the walk — the venv
|
|
435
|
+
* list alone had three independent spellings before #2544 review F1
|
|
436
|
+
* (`formatters.ts`, `runner-helpers.ts`'s `createVenvFinder`,
|
|
437
|
+
* `dead-code-client.ts`'s `venvCandidates`).
|
|
438
|
+
*
|
|
439
|
+
* `VENV_BIN_DIRS` lists BOTH the POSIX (`bin`) and Windows (`Scripts`) layouts
|
|
440
|
+
* unconditionally rather than branching on `process.platform` at module load:
|
|
441
|
+
* a module-load platform constant cannot be forced per test, so the other
|
|
442
|
+
* branch is never exercised on the ubuntu lane (defect shape 30). The extra
|
|
443
|
+
* two `existsSync` calls per directory are the price of a testable list, and
|
|
444
|
+
* a `.venv/Scripts` on Linux (or `.venv/bin` under msys) resolves correctly
|
|
445
|
+
* instead of being invisible.
|
|
446
|
+
*/
|
|
447
|
+
export const NODE_MODULES_BIN_DIRS = [
|
|
448
|
+
path.join("node_modules", ".bin"),
|
|
449
|
+
];
|
|
450
|
+
export const VENDOR_BIN_DIRS = [path.join("vendor", "bin")];
|
|
451
|
+
export const VENV_BIN_DIRS = [
|
|
452
|
+
path.join(".venv", "bin"),
|
|
453
|
+
path.join("venv", "bin"),
|
|
454
|
+
path.join(".venv", "Scripts"),
|
|
455
|
+
path.join("venv", "Scripts"),
|
|
456
|
+
];
|
|
457
|
+
/**
|
|
458
|
+
* THE project-local tool-bin walk: yields every `<binDir>/<tool>` that exists
|
|
459
|
+
* in `startDir` or one of its ancestors, nearest first, stopping at the HOME
|
|
460
|
+
* ceiling (never the fs root — see below).
|
|
461
|
+
*
|
|
462
|
+
* A generator on purpose. {@link findLocalBinUpwards} takes the first match and
|
|
463
|
+
* the walk suspends there — no ancestor above the hit is ever stat'd, which is
|
|
464
|
+
* what a per-edit resolver needs — while {@link findLocalBinsUpwards} drains it
|
|
465
|
+
* for the ast-grep probe sweep, the one caller that legitimately wants every
|
|
466
|
+
* candidate above `startDir`. One walk body, two arities, no "collect-all"
|
|
467
|
+
* flag whose disabled half nothing can observe.
|
|
468
|
+
* `ceiling: false` opts a walk out of the HOME stop entirely (own-install
|
|
469
|
+
* trees only, see {@link LocalBinWalkOptions.ceiling}); {@link findLocalBinsAt}
|
|
470
|
+
* / {@link findLocalBinAt} below are the non-walking sibling for callers that
|
|
471
|
+
* must check exactly one directory and never climb (`createVenvFinder`,
|
|
472
|
+
* vulture's `venvCandidates`).
|
|
473
|
+
*
|
|
474
|
+
* `tools` is a LIST because the sweep callers probe several spellings of the
|
|
475
|
+
* same tool and the preference order is per-directory: all of a directory's
|
|
476
|
+
* candidates, in `tools` order, before the parent directory is looked at.
|
|
477
|
+
*
|
|
478
|
+
* `binDirs` makes this the ONLY such walker in the repo. Before #2544 there
|
|
479
|
+
* were SIX copies of this loop — `formatters.ts`'s `findInNodeModules` /
|
|
480
|
+
* `findInVendorBin` / `findInVenv`, `oxlint.ts`'s `resolveLocalVp`,
|
|
481
|
+
* `runner-helpers.ts`'s `findNodeBinRoots` and `resolveVendorToolCommand` —
|
|
482
|
+
* and they had drifted apart: only this one resolved a relative `startDir`,
|
|
483
|
+
* only this one had the #2514 HOME ceiling, and `vp` was resolved with
|
|
484
|
+
* OPPOSITE ceiling policies depending on whether `formatters.ts` or `oxlint.ts`
|
|
485
|
+
* asked (#2544 review F1). They all delegate here now.
|
|
486
|
+
*
|
|
487
|
+
* Candidate order is EXT-OUTER, DIR-INNER: on Windows every `binDir` is tried
|
|
488
|
+
* with `windowsExt` before any is tried with the bare name, matching what the
|
|
489
|
+
* `.venv/Scripts` walker did before the fold.
|
|
490
|
+
*/
|
|
491
|
+
/**
|
|
492
|
+
* Expand `tools` into the actual candidate file names for one directory,
|
|
493
|
+
* EXT-OUTER: on Windows every tool's extended name is tried before any tool's
|
|
494
|
+
* bare name (`windowsExt === ""` means the caller already spelled every name
|
|
495
|
+
* it wants — the ast-grep sweep passes `ast-grep.cmd`/`ast-grep.exe`/`ast-grep`
|
|
496
|
+
* itself in a shell-dependent order this option cannot express).
|
|
497
|
+
*/
|
|
498
|
+
function expandCandidateNames(tools, windowsExt, isWindows) {
|
|
499
|
+
return isWindows && windowsExt !== ""
|
|
500
|
+
? tools.flatMap((tool) => [`${tool}${windowsExt}`, tool])
|
|
501
|
+
: [...tools];
|
|
502
|
+
}
|
|
503
|
+
/** Every `<dir>/<binDir>/<name>` that exists, for exactly `dir` — no walk. */
|
|
504
|
+
function* matchesInDir(dir, names, binDirs) {
|
|
505
|
+
for (const name of names) {
|
|
506
|
+
for (const binDir of binDirs) {
|
|
507
|
+
const full = path.join(dir, binDir, name);
|
|
508
|
+
if (fs.existsSync(full))
|
|
509
|
+
yield full;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
function* localBinMatches(tools, startDir, options) {
|
|
514
|
+
const { windowsExt = ".cmd", homeDir = os.homedir(), binDirs = NODE_MODULES_BIN_DIRS, isWindows = onWindows(), ceiling = true, } = options;
|
|
515
|
+
const names = expandCandidateNames(tools, windowsExt, isWindows);
|
|
516
|
+
for (const dir of walkUpDirs(startDir)) {
|
|
517
|
+
// Tool-resolution walker, not a config lookup (#2514/#2517 policy):
|
|
518
|
+
// escaping the project upward past HOME means STOP, not keep reading. A
|
|
519
|
+
// config-file lookup (`findLocalToolConfig`) keeps climbing past HOME
|
|
520
|
+
// because the underlying tool would find a legitimate global config
|
|
521
|
+
// there too; a `node_modules/.bin` match found at or above HOME can
|
|
522
|
+
// never be THIS project's own installed dependency — it is some
|
|
523
|
+
// unrelated home-level manifest (the pi-extensions bin shim, #2514).
|
|
524
|
+
// Default-on (not opt-in like `findLocalToolConfig`'s `options.homeDir`)
|
|
525
|
+
// because there is no legitimate case for this resolver to hand back a
|
|
526
|
+
// bin from outside the project tree. `ceiling: false` (own-install
|
|
527
|
+
// walk only, #2544 round 4 F1) skips this check entirely.
|
|
528
|
+
if (ceiling && isAtOrAboveHomeDir(dir, homeDir))
|
|
529
|
+
return;
|
|
530
|
+
yield* matchesInDir(dir, names, binDirs);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* The nearest project-local `<binDir>/<tool>` at or above `startDir`, or
|
|
535
|
+
* `undefined`. See {@link localBinMatches} for the walk itself.
|
|
398
536
|
*
|
|
399
537
|
* Exported so callers that must NOT pay for global-bin discovery can reuse this
|
|
400
538
|
* walk instead of copying it. `findNodeToolBinary` below adds `findGlobalBinary`,
|
|
401
539
|
* which spawns a probe per package manager; a caller resolving a command on
|
|
402
540
|
* every run (knip, #1721) needs the filesystem half only.
|
|
403
541
|
*/
|
|
404
|
-
export function findLocalBinUpwards(tool, startDir,
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
542
|
+
export function findLocalBinUpwards(tool, startDir, options = {}) {
|
|
543
|
+
// `.next().value`, not a `for…of` that returns on its first iteration: the
|
|
544
|
+
// latter is a single-iteration loop (SonarCloud S1751, and it reads as one
|
|
545
|
+
// too). The generator is simply left suspended after the first match — it
|
|
546
|
+
// holds no resource that needs closing, which is exactly why the walk above
|
|
547
|
+
// never stats an ancestor beyond the hit.
|
|
548
|
+
return localBinMatches([tool], startDir, options).next().value;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* EVERY project-local match for `tools` at or above `startDir`, nearest first.
|
|
552
|
+
*
|
|
553
|
+
* For the ast-grep availability sweep (`runner-helpers.ts`'s `buildSgLocalBins`,
|
|
554
|
+
* which spawns `--version` against each candidate until one answers, both for
|
|
555
|
+
* the project walk and — with `ceiling: false` — pi-lens's own install-tree
|
|
556
|
+
* walk), which previously hand-rolled its own unceilinged climb precisely
|
|
557
|
+
* because the singular `findLocalBinUpwards` could not express "all of them"
|
|
558
|
+
* (#2544 review F1).
|
|
559
|
+
*
|
|
560
|
+
* NOT for a "project's own venv/vendor-bin first" resolver: `createVenvFinder`
|
|
561
|
+
* and vulture's `venvCandidates` looked like this walk's other two callers in
|
|
562
|
+
* round 3, but that was an unrequested behaviour change (round 3 introduced
|
|
563
|
+
* ancestor climbing where a single cwd/root lookup existed before, with no
|
|
564
|
+
* #2514 defect behind it) — reverted in round 4 F2 to {@link findLocalBinsAt}.
|
|
565
|
+
*/
|
|
566
|
+
export function findLocalBinsUpwards(tools, startDir, options = {}) {
|
|
567
|
+
return [...localBinMatches(tools, startDir, options)];
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* EVERY `<dir>/<binDir>/<tool>` that exists for exactly `dir` — no ancestor
|
|
571
|
+
* walk, so the HOME ceiling is moot (a single directory can never climb past
|
|
572
|
+
* it).
|
|
573
|
+
*
|
|
574
|
+
* For the "project's own venv/vendor-bin first" resolvers that must NOT climb
|
|
575
|
+
* to a parent project (#2544 round 4 F2): `createVenvFinder` and vulture's
|
|
576
|
+
* `venvCandidates` were fixed `cwd`/`root` lookups before #2544 review round 3
|
|
577
|
+
* converted them into ancestor walks with no reported defect behind the
|
|
578
|
+
* conversion (an unrelated sibling project's interpreter at `~/code/.venv`
|
|
579
|
+
* could then outrank PATH for `~/code/app`, which has no venv of its own).
|
|
580
|
+
* Reverted here; `binDirs`/`windowsExt` stay shared with the walking helpers
|
|
581
|
+
* above so the three directory shapes have exactly one spelling.
|
|
582
|
+
*/
|
|
583
|
+
export function findLocalBinsAt(tools, dir, options = {}) {
|
|
584
|
+
const { windowsExt = ".cmd", binDirs = NODE_MODULES_BIN_DIRS, isWindows = onWindows(), } = options;
|
|
585
|
+
const names = expandCandidateNames(tools, windowsExt, isWindows);
|
|
586
|
+
return [...matchesInDir(path.resolve(dir), names, binDirs)];
|
|
587
|
+
}
|
|
588
|
+
/** The nearest — i.e. first-listed — `<dir>/<binDir>/<tool>` match, or `undefined`. */
|
|
589
|
+
export function findLocalBinAt(tool, dir, options = {}) {
|
|
590
|
+
return findLocalBinsAt([tool], dir, options)[0];
|
|
422
591
|
}
|
|
423
592
|
/**
|
|
424
593
|
* Locate a Node CLI tool's binary, preferring a local `node_modules/.bin`
|
|
@@ -432,6 +601,6 @@ export function findLocalBinUpwards(tool, startDir, windowsExt = ".cmd") {
|
|
|
432
601
|
* without changing their npx fallback semantics.
|
|
433
602
|
*/
|
|
434
603
|
export async function findNodeToolBinary(tool, cwd, windowsExt = ".cmd") {
|
|
435
|
-
return (findLocalBinUpwards(tool, cwd, windowsExt) ??
|
|
604
|
+
return (findLocalBinUpwards(tool, cwd, { windowsExt }) ??
|
|
436
605
|
(await findGlobalBinary(tool, windowsExt)));
|
|
437
606
|
}
|