@herbertgao/pi-extensions 2026.9.0 → 2026.9.2
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/@narumitw/pi-btw/README.md +57 -141
- package/node_modules/@narumitw/pi-btw/dist/index.ts +466 -41
- 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 +15 -1
- 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-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 +1072 -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 -9
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +1 -1
- 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 -9
- 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 -3
- package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -6
- 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-pid-file-lock.js +1 -1
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -9
- package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +3 -11
- 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 +40 -19
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -9
- package/node_modules/pi-lens/dist/clients/child-unref.js +1 -19
- 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 +50 -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 +164 -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 +165 -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 +234 -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 -6
- 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-dispositions.js +1 -1
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +15 -5
- package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -44
- package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +27 -8
- 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 +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +383 -27
- 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 +4 -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 +1 -2
- 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 +14 -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 +6 -3
- 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 +1 -2
- 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 +1 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +4 -3
- 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 -67
- 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/utils.js +0 -21
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -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 +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -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 +3 -10
- 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/event-loop-monitor.js +2 -2
- 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-kinds.js +1 -38
- package/node_modules/pi-lens/dist/clients/file-time.js +7 -2
- package/node_modules/pi-lens/dist/clients/file-utils.js +117 -10
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
- package/node_modules/pi-lens/dist/clients/format-service.js +6 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +385 -104
- package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +116 -22
- package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
- package/node_modules/pi-lens/dist/clients/git-guard.js +6 -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/grammar-source.js +3 -3
- 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 +289 -59
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +84 -18
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +110 -236
- package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
- package/node_modules/pi-lens/dist/clients/language-profile.js +0 -9
- package/node_modules/pi-lens/dist/clients/language-registry.js +599 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +165 -9
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +34 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +160 -30
- package/node_modules/pi-lens/dist/clients/lens-engine.js +33 -41
- package/node_modules/pi-lens/dist/clients/lens-events.js +1 -1
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +77 -6
- package/node_modules/pi-lens/dist/clients/lens-map.js +1 -1
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -38
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +172 -33
- package/node_modules/pi-lens/dist/clients/lsp/config.js +484 -158
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +18 -5
- package/node_modules/pi-lens/dist/clients/lsp/index.js +892 -96
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -177
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
- 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 +5 -18
- 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/sync-kind.js +0 -1
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +14 -7
- 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 +1 -1
- package/node_modules/pi-lens/dist/clients/lsp-document-symbols.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/metrics-history.js +28 -107
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +3 -5
- package/node_modules/pi-lens/dist/clients/module-report.js +21 -46
- 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 -24
- 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 +70 -28
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +5 -1
- 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 +451 -15
- package/node_modules/pi-lens/dist/clients/performance-report.js +2 -2
- 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 +79 -18
- 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-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 +4 -3
- 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 +28 -4
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +438 -122
- package/node_modules/pi-lens/dist/clients/project-scan-policy.js +3 -51
- 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 +16 -5
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
- package/node_modules/pi-lens/dist/clients/read-guard.js +26 -2
- package/node_modules/pi-lens/dist/clients/recent-touches.js +2 -2
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +155 -114
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +1 -1
- 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 +19 -3
- 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 +184 -32
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +179 -36
- 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 -12
- package/node_modules/pi-lens/dist/clients/scan-utils.js +1 -56
- 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/session-summary.js +0 -24
- package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
- package/node_modules/pi-lens/dist/clients/sgconfig.js +7 -6
- package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +1 -1
- package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +4 -4
- package/node_modules/pi-lens/dist/clients/startup-scan.js +2 -10
- 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 +244 -0
- package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
- package/node_modules/pi-lens/dist/clients/tool-policy.js +92 -22
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +87 -100
- 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/tui-fit.js +0 -4
- package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
- package/node_modules/pi-lens/dist/clients/user-notify.js +6 -6
- package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -9
- package/node_modules/pi-lens/dist/clients/word-index-store.js +23 -12
- package/node_modules/pi-lens/dist/clients/word-index.js +252 -47
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +4 -1
- package/node_modules/pi-lens/dist/index.js +65899 -55340
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
- package/node_modules/pi-lens/dist/mcp/build-staleness.js +1 -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 +96 -25
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +104 -86
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
- package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +3 -3
- package/node_modules/pi-lens/dist/tools/render-compact.js +1 -1
- package/node_modules/pi-lens/dist/tools/shared.js +0 -1
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -2
- 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/globalconfig.md +20 -0
- package/node_modules/pi-lens/docs/language-coverage.md +2 -2
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +9 -1
- 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 +33 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +49 -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 +190 -0
- package/node_modules/pi-lens/docs/servercapabilities.md +7 -3
- 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/docs/word-index.md +1 -1
- package/node_modules/pi-lens/package.json +18 -35
- 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/rules/typos/_typos.toml +4 -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/lib/warm-loader-cache.mjs +1 -1
- package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +4 -2
- 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 +6 -6
|
@@ -4,35 +4,44 @@
|
|
|
4
4
|
* Allows users to define custom LSP servers and override initialization options
|
|
5
5
|
* for built-in servers via configuration.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* CANONICAL LOCATION (#2426): the `lsp` namespace of `.pi-lens.json` (project)
|
|
8
|
+
* and `~/.pi-lens/config.json` (machine-global). `.pi-lens/lsp.json`,
|
|
9
|
+
* `pi-lsp.json` and `~/.pi-lens/lsp.json` — and the four LSP keys at the ROOT
|
|
10
|
+
* of a canonical file — are still read for their deprecation window
|
|
11
|
+
* (`DEPRECATED_CONFIG_SURFACES`) and emit one migration warning per
|
|
12
|
+
* `(file, key)` naming where the setting moves. The canonical spelling wins
|
|
13
|
+
* every collision. `docs/configuration.md` documents the full lookup order;
|
|
14
|
+
* discovery itself lives in `clients/config-resolve.ts`, which this module and
|
|
15
|
+
* the other two loaders now share.
|
|
8
16
|
*
|
|
9
|
-
* Example — custom server:
|
|
17
|
+
* Example — custom server (canonical spelling, inside `.pi-lens.json`):
|
|
10
18
|
* {
|
|
11
|
-
* "
|
|
12
|
-
* "
|
|
13
|
-
* "
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
19
|
+
* "lsp": {
|
|
20
|
+
* "servers": {
|
|
21
|
+
* "my-server": {
|
|
22
|
+
* "name": "My Custom LSP",
|
|
23
|
+
* "extensions": [".myext"],
|
|
24
|
+
* "command": "my-lsp-server",
|
|
25
|
+
* "args": ["--stdio"],
|
|
26
|
+
* "rootMarkers": ["package.json"]
|
|
27
|
+
* }
|
|
18
28
|
* }
|
|
19
29
|
* }
|
|
20
30
|
* }
|
|
21
31
|
*
|
|
22
32
|
* Example — override initializationOptions for a built-in server:
|
|
23
33
|
* {
|
|
24
|
-
* "
|
|
25
|
-
* "
|
|
26
|
-
* "
|
|
27
|
-
* "
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* "
|
|
33
|
-
* "
|
|
34
|
-
*
|
|
35
|
-
* "home_manager": { "expr": "(builtins.getFlake (toString ./.)).homeConfigurations.me.options" }
|
|
34
|
+
* "lsp": {
|
|
35
|
+
* "serverOverrides": {
|
|
36
|
+
* "rust": {
|
|
37
|
+
* "initializationOptions": {
|
|
38
|
+
* "check": { "command": "clippy", "allTargets": true },
|
|
39
|
+
* "cargo": { "features": "all", "targetDir": true }
|
|
40
|
+
* }
|
|
41
|
+
* },
|
|
42
|
+
* "nix": {
|
|
43
|
+
* "initializationOptions": {
|
|
44
|
+
* "nixpkgs": { "expr": "import <nixpkgs> {}" }
|
|
36
45
|
* }
|
|
37
46
|
* }
|
|
38
47
|
* }
|
|
@@ -46,98 +55,278 @@
|
|
|
46
55
|
* Server IDs match the `id` field of each built-in server definition in
|
|
47
56
|
* clients/lsp/server.ts (e.g. "rust", "nix", "bash", "python", "go", "ts").
|
|
48
57
|
*/
|
|
49
|
-
import {
|
|
50
|
-
import
|
|
51
|
-
import fs from "node:fs/promises";
|
|
58
|
+
import { resetIgnoredConfigWarnCache } from "../config-warn.js";
|
|
59
|
+
import * as os from "node:os";
|
|
52
60
|
import path from "node:path";
|
|
53
|
-
import {
|
|
61
|
+
import { lspSectionOf, reportConfigReadFailure, reportPiLensConfigRecords, resolvePiLensConfig, summarizeConfigResolution, } from "../config-resolve.js";
|
|
54
62
|
import { getGlobalPiLensDir } from "../file-utils.js";
|
|
63
|
+
import { claimPhaseOncePerSession, currentSessionRecordId, logLatency, releaseOncePerSessionPhase, releasePhaseClaim, } from "../latency-logger.js";
|
|
64
|
+
import { getPiLensGlobalConfigPath } from "../lens-config.js";
|
|
65
|
+
import { normalizeFilePath } from "../path-utils.js";
|
|
66
|
+
import { logSessionStart } from "../sessionstart-logger.js";
|
|
55
67
|
import { launchLSP } from "./launch.js";
|
|
56
|
-
import { registerSessionRoot, resetSessionRootsForTests, } from "./session-roots.js";
|
|
68
|
+
import { registerSessionRoot, resetSessionRootsForTests, sessionRootConfigEntries, setSessionRootConfig, } from "./session-roots.js";
|
|
57
69
|
import { createRootDetector, LSP_SERVERS, resetLSPCaseSensitivityState, } from "./server.js";
|
|
58
70
|
// --- Config Loading ---
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// HUMAN-audience too: the user's own lsp.json is being ignored (#1333).
|
|
70
|
-
notifyUserDegradation(`pi-lens: ${message}`);
|
|
71
|
+
/**
|
|
72
|
+
* For tests that need to force the warn-once cache to reset between cases —
|
|
73
|
+
* the LSP loader's counterpart to `resetGlobalConfigWarnCache` in
|
|
74
|
+
* lens-config.ts and to the clear folded into `resetProjectLensConfigCache`
|
|
75
|
+
* (#2418 review round 3, S3). Without it, this loader's cases had to lean on
|
|
76
|
+
* every fixture landing in a fresh temp path to stay unlatched, which is a
|
|
77
|
+
* property of the fixture rather than of the test.
|
|
78
|
+
*/
|
|
79
|
+
export function resetLSPConfigWarnCache() {
|
|
80
|
+
resetIgnoredConfigWarnCache("lsp-config");
|
|
71
81
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
warnInvalidLSPConfig(configPath, error);
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
|
-
try {
|
|
85
|
-
const parsed = JSON.parse(content);
|
|
86
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
87
|
-
throw new TypeError("expected a JSON object");
|
|
88
|
-
}
|
|
89
|
-
return parsed;
|
|
90
|
-
}
|
|
91
|
-
catch (error) {
|
|
92
|
-
warnInvalidLSPConfig(configPath, error);
|
|
93
|
-
return undefined;
|
|
94
|
-
}
|
|
82
|
+
function asRecord(value) {
|
|
83
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
84
|
+
? value
|
|
85
|
+
: undefined;
|
|
95
86
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
87
|
+
/** The session-scoped phase this loader claims (#2526). */
|
|
88
|
+
const CONFIG_RESOLVED_PHASE = "config_resolved";
|
|
89
|
+
/**
|
|
90
|
+
* Write the session's ONE `config_resolved` record (#2526).
|
|
91
|
+
*
|
|
92
|
+
* Positive observability for the Phase 0 config stack: before this, a correct
|
|
93
|
+
* canonical-only resolution proved itself only by the ABSENCE of
|
|
94
|
+
* `PILENS_CFG_*` rows — the silent-success gap AGENTS.md warns about.
|
|
95
|
+
*
|
|
96
|
+
* REDACTED by construction, not by mode. Everything it can say comes from
|
|
97
|
+
* `summarizeConfigResolution`, the same projection `pilens_effective_config`
|
|
98
|
+
* embeds: document PATHS (home-relative), tiers, the legacy flag, per-tier
|
|
99
|
+
* leaf counts, and a record COUNT. No config value, and no absolute `$HOME`,
|
|
100
|
+
* can reach the log through it.
|
|
101
|
+
*
|
|
102
|
+
* NO SECOND RESOLUTION (#2513's facade rule): it reads the resolution its
|
|
103
|
+
* caller already performed. And it adds no `await` — `logLatency` and
|
|
104
|
+
* `logSessionStart` are synchronous buffered writers — so the session-start
|
|
105
|
+
* hook path gains nothing to wait on (#2523).
|
|
106
|
+
*
|
|
107
|
+
* ONE row per session AND SERVED ROOT, claimed through the logger's own
|
|
108
|
+
* session-scoped bookkeeping rather than a latch kept here. `loadLSPConfig` is
|
|
109
|
+
* the funnel every config resolution goes through (`initLSPConfig` at session
|
|
110
|
+
* start and at each served root, the MCP `ensureReady` boot,
|
|
111
|
+
* `ensureLSPConfigInitialized` on the first edit), so an unconditional write
|
|
112
|
+
* would emit one row per resolution and the per-session count the smell
|
|
113
|
+
* analyzer joins on would be unrecoverable from the log.
|
|
114
|
+
*
|
|
115
|
+
* The ROOT is part of the claim (#2526 review round 2, F3). A warm MCP server
|
|
116
|
+
* calls `ensureReady` per served root (`mcp/server.ts`), and a phase-only
|
|
117
|
+
* claim recorded the FIRST root's documents and nothing else — project B's
|
|
118
|
+
* legacy config document never reached a row, so the
|
|
119
|
+
* "legacy-document-with-no-records" smell structurally could not fire for it.
|
|
120
|
+
* `configResolutionKey` keys it (fold, canonicalize, fold — see below), the
|
|
121
|
+
* repo-wide rule for every path-keyed map (#210): a `/`-vs-`\` spelling of
|
|
122
|
+
* one root must not buy a second row.
|
|
123
|
+
*/
|
|
124
|
+
/**
|
|
125
|
+
* THE (session, root) key of every config-resolution record: the
|
|
126
|
+
* `config_resolution_pending` mark, the `config_resolved` row and claim, and
|
|
127
|
+
* the release of that claim when the root is evicted.
|
|
128
|
+
*
|
|
129
|
+
* Fold separators, THEN canonicalize, then fold again — in that order, and
|
|
130
|
+
* neither step alone is enough (#2518 review F6). `normalizeFilePath` folds
|
|
131
|
+
* separators and Windows casing but is the IDENTITY on a POSIX path that
|
|
132
|
+
* merely needs canonicalizing, so `"/proj/"` and `"/proj"` produced two
|
|
133
|
+
* different claim keys. `path.resolve` fixes that and nothing else — and it
|
|
134
|
+
* cannot run first: on POSIX a backslash is an ordinary filename character,
|
|
135
|
+
* so resolving `"/proj\\sub"` before folding yields
|
|
136
|
+
* `<process.cwd()>/proj\sub`, a different root entirely. That is the #2526 R2
|
|
137
|
+
* spelling case (`tests/clients/config-resolved-phase.test.ts`, "a /-vs-\
|
|
138
|
+
* spelling of one root does not buy a second row"), which caught exactly this
|
|
139
|
+
* ordering while it was wrong. Resolving here rather than at
|
|
140
|
+
* each caller is what makes the keys identical BY CONSTRUCTION: the callers
|
|
141
|
+
* do not agree today (`initLSPConfig` passes its registry-resolved cwd,
|
|
142
|
+
* `clients/runtime-session.ts`'s two warm-path `loadLSPConfig` calls pass the
|
|
143
|
+
* session cwd verbatim, and `analysisRoot` from `.pi-lens.json` can carry a
|
|
144
|
+
* trailing slash), and a per-caller normalization is exactly the shape 1
|
|
145
|
+
* defect this key keeps being bitten by — the write form and the read form
|
|
146
|
+
* diverging because two sites each folded the path their own way.
|
|
147
|
+
*
|
|
148
|
+
* It is also the key `clients/lsp/session-roots.ts` stores its roots under,
|
|
149
|
+
* once composed: the registry key is `path.resolve(cwd)`, and this function
|
|
150
|
+
* applied to it is idempotent, so `forgetConfigResolvedClaims` releases
|
|
151
|
+
* exactly the claim `recordConfigResolved` took.
|
|
152
|
+
*/
|
|
153
|
+
function configResolutionKey(cwd) {
|
|
154
|
+
// Fold first (separators, Windows casing), canonicalize second (trailing
|
|
155
|
+
// separators, relative segments), fold again so the result keeps the
|
|
156
|
+
// forward-slash shape every producer and the analyzer already compare on.
|
|
157
|
+
return normalizeFilePath(path.resolve(normalizeFilePath(cwd)));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Announce, at the instant a resolution is actually about to be attempted,
|
|
161
|
+
* that THIS session expects a `config_resolved` row (#2526 review round 3,
|
|
162
|
+
* S1).
|
|
163
|
+
*
|
|
164
|
+
* Round 2 PREDICTED this from three flags in `runtime-session.ts`
|
|
165
|
+
* (`no-lsp`/`subagent`/`warm-attach`), each mirroring one gate the resolution
|
|
166
|
+
* paths themselves check — and the mirror drifted: quick and minimal mode's
|
|
167
|
+
* SECOND session in one process schedule no resolution at all
|
|
168
|
+
* (`ensureLSPConfigInitialized`'s `_lspConfigInitializedCwds` memo skips the
|
|
169
|
+
* synchronous resolve past the first session, and only FULL mode's deferred
|
|
170
|
+
* `setImmediate` reschedules one), so the predicate kept saying
|
|
171
|
+
* `expected=true` for a session that was never going to resolve, and the
|
|
172
|
+
* analyzer flagged a real session every time under `PI_LENS_STARTUP_MODE=quick`
|
|
173
|
+
* or `=minimal`. STOP PREDICTING: this function is called from `loadLSPConfig`
|
|
174
|
+
* itself rather than from any of its callers' decision points, because
|
|
175
|
+
* `loadLSPConfig` is the ONE funnel every production caller reaches —
|
|
176
|
+
* `initLSPConfig` (`ensureLSPConfigInitialized`'s first-session-per-cwd path
|
|
177
|
+
* in index.ts, the MCP `ensureReady` boot via `ensureLspConfig`,
|
|
178
|
+
* `igniteWarmFiles`/`igniteDominantLanguageWarm`) and the two direct calls in
|
|
179
|
+
* `runtime-session.ts` (the full-mode deferred `setImmediate` load and the
|
|
180
|
+
* quick-mode warm-up's LSP pre-warm). Placing the mark here rather than at
|
|
181
|
+
* each of those four sites buys three things: (a) it fires exactly when a
|
|
182
|
+
* resolution is genuinely attempted, never merely decided likely; (b) a
|
|
183
|
+
* session that never reaches ANY of those sites — quick or minimal mode's
|
|
184
|
+
* second-and-later session in a process, for the same root — never gets a
|
|
185
|
+
* mark, so the analyzer silently excludes it instead of counting it against;
|
|
186
|
+
* (c) this mark and {@link recordConfigResolved}'s row are stamped from the
|
|
187
|
+
* SAME `currentSessionRecordId()` read, a few lines apart in one function
|
|
188
|
+
* call, so they can never disagree about which session they belong to —
|
|
189
|
+
* writing the mark at each scheduling site instead (e.g. before a
|
|
190
|
+
* `setImmediate`) would let a session boundary between scheduling and
|
|
191
|
+
* execution attribute the two halves to different sessions.
|
|
192
|
+
*
|
|
193
|
+
* Written unconditionally — before {@link resolvePiLensConfig} can throw —
|
|
194
|
+
* so a resolution that is entered but fails mid-flight leaves a mark with no
|
|
195
|
+
* row, which the analyzer's join reads as "expected and never happened"
|
|
196
|
+
* rather than silently matching "never expected at all".
|
|
197
|
+
*
|
|
198
|
+
* Carries `root=<configResolutionKey(cwd)>` (#2552 review round 4, MEDIUM): the
|
|
199
|
+
* warm MCP server keeps ONE session id for the life of the process but calls
|
|
200
|
+
* this once per SERVED ROOT (`ensureReady` per root, `mcp/server.ts`) — a
|
|
201
|
+
* session-id-only mark let one root's `config_resolved` row silently clear
|
|
202
|
+
* every OTHER root's deficit under the same id, reintroducing review round
|
|
203
|
+
* 2's F3 defect one layer up, at the analyzer's join instead of the claim.
|
|
204
|
+
* The value is the SAME {@link configResolutionKey} string
|
|
205
|
+
* {@link recordConfigResolved} uses for its claim scope and its row's
|
|
206
|
+
* `filePath`, so the mark and the row compare equal without the analyzer
|
|
207
|
+
* re-deriving any path normalization of its own.
|
|
208
|
+
*/
|
|
209
|
+
function publishConfigResolutionPending(cwd) {
|
|
210
|
+
logSessionStart(`session_start config_resolution_pending session=${currentSessionRecordId()} ` +
|
|
211
|
+
`root=${configResolutionKey(cwd)}`);
|
|
212
|
+
}
|
|
213
|
+
function recordConfigResolved(cwd, resolution, lspConfig, homeDir, resolveMs) {
|
|
214
|
+
// #2552 review round 4: computed ONCE and reused for the claim scope, the
|
|
215
|
+
// row's `filePath`, and the sessionstart line's `root=` — one normalization
|
|
216
|
+
// of `cwd`, so the pending mark, the row, and the claim can never disagree
|
|
217
|
+
// about which root they name. #2518 review F6 moved that one normalization
|
|
218
|
+
// into `configResolutionKey`, so callers passing the same root spelled
|
|
219
|
+
// differently cannot disagree either.
|
|
220
|
+
const root = configResolutionKey(cwd);
|
|
221
|
+
if (!claimPhaseOncePerSession(CONFIG_RESOLVED_PHASE, root)) {
|
|
222
|
+
return;
|
|
113
223
|
}
|
|
114
|
-
|
|
224
|
+
const summary = summarizeConfigResolution(resolution, homeDir);
|
|
225
|
+
// #2526 R2 F2: the session identity the analyzer joins on. Written on both
|
|
226
|
+
// sinks from this ONE call, so the latency row and the sessionstart line
|
|
227
|
+
// carry the same id by construction rather than by agreement.
|
|
228
|
+
const sessionId = currentSessionRecordId();
|
|
229
|
+
// The deny UNION's size, read off the same projection the gates consume
|
|
230
|
+
// (`lspConfigOf`) rather than re-read from the raw value — one definition of
|
|
231
|
+
// "which servers are denied", so the record cannot describe a different
|
|
232
|
+
// deny set than the one that actually suppresses a server.
|
|
233
|
+
const deniedServers = lspConfig.disabledServers?.length ?? 0;
|
|
234
|
+
const legacyDocuments = summary.documents.filter((document) => document.legacy).length;
|
|
235
|
+
logLatency({
|
|
236
|
+
type: "phase",
|
|
237
|
+
phase: CONFIG_RESOLVED_PHASE,
|
|
238
|
+
filePath: root,
|
|
239
|
+
durationMs: resolveMs,
|
|
240
|
+
metadata: {
|
|
241
|
+
sessionId,
|
|
242
|
+
documents: summary.documents,
|
|
243
|
+
countsByTier: summary.countsByTier,
|
|
244
|
+
recordCount: summary.recordCount,
|
|
245
|
+
deniedServers,
|
|
246
|
+
resolveMs,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
logSessionStart(`config resolved documents=${summary.documents.length} legacy=${legacyDocuments} ` +
|
|
250
|
+
`records=${summary.recordCount} deniedServers=${deniedServers} resolveMs=${resolveMs} ` +
|
|
251
|
+
`session=${sessionId} root=${root}`);
|
|
252
|
+
}
|
|
253
|
+
export async function loadLSPConfig(cwd, homeDir = os.homedir(), options = {}) {
|
|
254
|
+
const reporting = options.report !== false;
|
|
255
|
+
// #2526 review round 3, S1: announce the attempt before anything that
|
|
256
|
+
// resolves it can throw — see `publishConfigResolutionPending`'s doc
|
|
257
|
+
// comment for why this lives here rather than at each caller.
|
|
258
|
+
publishConfigResolutionPending(cwd);
|
|
259
|
+
const resolveStartedAt = Date.now();
|
|
260
|
+
const resolution = resolvePiLensConfig({
|
|
261
|
+
cwd,
|
|
262
|
+
globalDir: getGlobalPiLensDir(),
|
|
263
|
+
// `homeDir` is threaded, not dropped: it is the `$HOME` this call resolves
|
|
264
|
+
// against, and the canonical global config is `$HOME/.pi-lens/config.json`
|
|
265
|
+
// whenever `PI_LENS_CONFIG_PATH` does not override it. Production behavior
|
|
266
|
+
// is unchanged (the default IS `os.homedir()`); what it buys is that the
|
|
267
|
+
// seam means the same `$HOME` on both sides of the resolution, so a test
|
|
268
|
+
// can exercise a relocated `PI_LENS_HOME` without reaching the real one.
|
|
269
|
+
globalConfigPath: getPiLensGlobalConfigPath(homeDir),
|
|
270
|
+
homeDir,
|
|
271
|
+
// The subsystem comes from the failing DOCUMENT, not from this loader
|
|
272
|
+
// (#2445). This resolution opens `~/.pi-lens/config.json` and
|
|
273
|
+
// `.pi-lens.json` as well as the LSP-scoped files, and reporting all of
|
|
274
|
+
// them as `lsp-config` announced an "invalid LSP config" for a file whose
|
|
275
|
+
// contents are pi-lens settings. An LSP-scoped file still reports here.
|
|
276
|
+
...(reporting ? { onReadError: reportConfigReadFailure } : {}),
|
|
277
|
+
});
|
|
278
|
+
// EVERY record this resolution produced (#2426 review round 3, F1) — not
|
|
279
|
+
// filtered to what this loader "owns". `reportPiLensConfigRecords` derives
|
|
280
|
+
// the reporting subsystem per record; the warn-once latch collapses this
|
|
281
|
+
// loader's report with the pi-lens loaders' report of the SAME record into
|
|
282
|
+
// one notice. Filtering here (as round 2 did) silently dropped a pi-lens-
|
|
283
|
+
// owned record from a document only this multi-file resolution discovered.
|
|
284
|
+
if (reporting)
|
|
285
|
+
reportPiLensConfigRecords(resolution.records);
|
|
286
|
+
const config = lspConfigOf(resolution.value);
|
|
287
|
+
// #2526: the session's one positive record that config resolution HAPPENED,
|
|
288
|
+
// written where the resolution actually exists. `report: false` is not a
|
|
289
|
+
// gate here — the option suppresses USER-FACING notices, and a record in
|
|
290
|
+
// the latency log is not a notice; gating on it would let a session whose
|
|
291
|
+
// only resolution was a quiet one look, in the log, like a session that
|
|
292
|
+
// never resolved config at all.
|
|
293
|
+
recordConfigResolved(cwd, resolution, config, homeDir, Date.now() - resolveStartedAt);
|
|
294
|
+
return config;
|
|
115
295
|
}
|
|
116
296
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
297
|
+
* THE resolved-value → {@link LSPConfig} projection: read the `lsp` namespace
|
|
298
|
+
* and keep the four keys the gates consume, each only when the resolution
|
|
299
|
+
* actually produced it in the right shape.
|
|
300
|
+
*
|
|
301
|
+
* Exported and named in #2427 review round 5 (F-R4-1). `effectiveConfig`
|
|
302
|
+
* needs the LSP config AND the provenance of the same resolution, and
|
|
303
|
+
* `loadLSPConfig` returns only the former — it discards the resolution it
|
|
304
|
+
* just performed. Round 4 therefore had the query call `loadLSPConfig` for
|
|
305
|
+
* the gates and run a SECOND `resolvePiLensConfig` for the provenance, at a
|
|
306
|
+
* different root, and the two disagreed: the gates answered from the file's
|
|
307
|
+
* own directory while the reported spec, provenance and document list came
|
|
308
|
+
* from the workspace root. With the projection spelled here the query performs
|
|
309
|
+
* ONE resolution and derives both halves from it, and the projection is still
|
|
310
|
+
* a single definition, so a derived config and a session-registered one cannot
|
|
311
|
+
* disagree about what a document means.
|
|
119
312
|
*/
|
|
120
|
-
export
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (projectConfig)
|
|
133
|
-
break;
|
|
134
|
-
const parent = path.dirname(dir);
|
|
135
|
-
if (parent === dir)
|
|
136
|
-
break;
|
|
137
|
-
dir = parent;
|
|
313
|
+
export function lspConfigOf(value) {
|
|
314
|
+
const section = lspSectionOf(value);
|
|
315
|
+
const config = {};
|
|
316
|
+
const servers = asRecord(section.servers);
|
|
317
|
+
if (servers)
|
|
318
|
+
config.servers = servers;
|
|
319
|
+
const serverOverrides = asRecord(section.serverOverrides);
|
|
320
|
+
if (serverOverrides) {
|
|
321
|
+
config.serverOverrides = serverOverrides;
|
|
322
|
+
}
|
|
323
|
+
if (Array.isArray(section.disabledServers)) {
|
|
324
|
+
config.disabledServers = section.disabledServers;
|
|
138
325
|
}
|
|
139
|
-
|
|
140
|
-
|
|
326
|
+
if (Array.isArray(section.warmFiles)) {
|
|
327
|
+
config.warmFiles = section.warmFiles;
|
|
328
|
+
}
|
|
329
|
+
return config;
|
|
141
330
|
}
|
|
142
331
|
// --- Custom Server Factory ---
|
|
143
332
|
/**
|
|
@@ -147,6 +336,7 @@ export function createCustomServer(config, id) {
|
|
|
147
336
|
return {
|
|
148
337
|
id,
|
|
149
338
|
name: config.name,
|
|
339
|
+
custom: true,
|
|
150
340
|
extensions: config.extensions,
|
|
151
341
|
root: config.rootMarkers
|
|
152
342
|
? createRootDetector(config.rootMarkers)
|
|
@@ -166,7 +356,6 @@ const EMPTY_CONFIG = {
|
|
|
166
356
|
disabledServerIds: new Set(),
|
|
167
357
|
serverOverrides: new Map(),
|
|
168
358
|
};
|
|
169
|
-
const workspaceConfigs = new BoundedLruCache(32);
|
|
170
359
|
/** In-flight config initialization promises to prevent duplicate concurrent loads */
|
|
171
360
|
const configInFlight = new Map();
|
|
172
361
|
function normalizeWorkspacePath(cwd) {
|
|
@@ -180,7 +369,13 @@ function isSameOrChildPath(filePath, candidateRoot) {
|
|
|
180
369
|
function getConfigForFile(filePath) {
|
|
181
370
|
const resolvedFilePath = path.resolve(filePath);
|
|
182
371
|
let bestMatch;
|
|
183
|
-
|
|
372
|
+
// #2518: the per-root configs ARE the session-root registry's values, so
|
|
373
|
+
// this walk sees a config for exactly the roots that registry still serves.
|
|
374
|
+
// `undefined` is a root whose first load is still in flight — the same
|
|
375
|
+
// "no entry yet" state this walk skipped before the two stores merged.
|
|
376
|
+
for (const [root, config] of sessionRootConfigEntries()) {
|
|
377
|
+
if (config === undefined)
|
|
378
|
+
continue;
|
|
184
379
|
if (!isSameOrChildPath(resolvedFilePath, root))
|
|
185
380
|
continue;
|
|
186
381
|
if (!bestMatch || root.length > bestMatch.root.length) {
|
|
@@ -190,55 +385,113 @@ function getConfigForFile(filePath) {
|
|
|
190
385
|
return bestMatch?.config ?? EMPTY_CONFIG;
|
|
191
386
|
}
|
|
192
387
|
/**
|
|
193
|
-
*
|
|
388
|
+
* THE `LSPConfig` → `RegisteredLSPConfig` conversion: construct the custom
|
|
389
|
+
* servers, index the deny list, index the overrides. Pure — it reads no
|
|
390
|
+
* module state and writes none.
|
|
391
|
+
*
|
|
392
|
+
* Extracted from `initLSPConfig`'s body in #2427 review round 3 so that
|
|
393
|
+
* `effectiveConfig` can build the config its question needs WITHOUT calling
|
|
394
|
+
* `initLSPConfig`. That call was the finding: a read-only query ran a full
|
|
395
|
+
* session initialization, which (a) registered the caller's cwd as a served
|
|
396
|
+
* session root, widening the #2052 access gate for a tree the session never
|
|
397
|
+
* opened, and (b) wrote the per-root config store, which was then a separate
|
|
398
|
+
* 32-entry LRU, so ~40 queries against other directories evicted a live root's
|
|
399
|
+
* config and silently lifted the operator's `disabledServers` denial — the
|
|
400
|
+
* exact inversion the surface promises cannot happen. With the conversion
|
|
401
|
+
* spelled here, both writes stop being something the query has to opt out of:
|
|
402
|
+
* it never reaches them. (#2518 later removed the second cap by making the
|
|
403
|
+
* config the registry's own value; a query that skips the registry still skips
|
|
404
|
+
* both.)
|
|
405
|
+
*
|
|
406
|
+
* Still ONE definition, so the derived config and the session-registered one
|
|
407
|
+
* cannot disagree about what a document means.
|
|
408
|
+
*/
|
|
409
|
+
export function registerLSPConfig(config) {
|
|
410
|
+
const customServers = [];
|
|
411
|
+
const disabledServerIds = new Set(config.disabledServers ?? []);
|
|
412
|
+
if (config.servers) {
|
|
413
|
+
for (const [id, serverConfig] of Object.entries(config.servers)) {
|
|
414
|
+
try {
|
|
415
|
+
const server = createCustomServer(serverConfig, id);
|
|
416
|
+
customServers.push(server);
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
// pi-lens-ignore: missing-error-propagation — per-server registration, skip bad entries
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const serverOverrides = new Map();
|
|
424
|
+
if (config.serverOverrides) {
|
|
425
|
+
for (const [id, entry] of Object.entries(config.serverOverrides)) {
|
|
426
|
+
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
427
|
+
const initOpts = entry
|
|
428
|
+
.initializationOptions;
|
|
429
|
+
if (initOpts !== undefined &&
|
|
430
|
+
typeof initOpts === "object" &&
|
|
431
|
+
initOpts !== null &&
|
|
432
|
+
!Array.isArray(initOpts)) {
|
|
433
|
+
serverOverrides.set(id, {
|
|
434
|
+
initializationOptions: initOpts,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return { customServers, disabledServerIds, serverOverrides };
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Drop the `config_resolved` claim of every root the registry just evicted
|
|
444
|
+
* (#2518 review F1).
|
|
445
|
+
*
|
|
446
|
+
* `recordConfigResolved` claims that row once per (session, root), which is
|
|
447
|
+
* right while the resolved config is still in the store: a second
|
|
448
|
+
* `loadLSPConfig` for a root already resolved this session re-derives the same
|
|
449
|
+
* answer and needs no second row. An EVICTED root is the other case — its
|
|
450
|
+
* resolved config is gone, the reload is a genuine second resolution, and its
|
|
451
|
+
* row is the only record of what the reloaded config was. Without this release
|
|
452
|
+
* the reload publishes a `config_resolution_pending` mark that no row ever
|
|
453
|
+
* answers, which reads in the analyzer exactly like a resolution that never
|
|
454
|
+
* finished.
|
|
455
|
+
*
|
|
456
|
+
* Rows stay bounded because evictions do, and evictions are counted:
|
|
457
|
+
* `lsp-session-root-evicted` in the degradation ledger.
|
|
458
|
+
*
|
|
459
|
+
* The release derives its key with {@link configResolutionKey}, the ONE
|
|
460
|
+
* expression the claim itself is taken with — not a second normalization that
|
|
461
|
+
* happens to match. Round 2 shipped `normalizeFilePath(root)` here against a
|
|
462
|
+
* claim keyed on the caller's own `normalizeFilePath(cwd)`, which is the same
|
|
463
|
+
* string only when `path.resolve` is the identity on that cwd: a trailing
|
|
464
|
+
* slash (what `analysisRoot` from `.pi-lens.json` and the warm paths can pass)
|
|
465
|
+
* made the release miss and the reload silent again (review F6).
|
|
466
|
+
*/
|
|
467
|
+
function forgetConfigResolvedClaims(evictedRoots) {
|
|
468
|
+
for (const root of evictedRoots) {
|
|
469
|
+
releasePhaseClaim(CONFIG_RESOLVED_PHASE, configResolutionKey(root));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Initialize LSP configuration (call at session start).
|
|
194
474
|
* Deduplicates concurrent calls for the same workspace.
|
|
475
|
+
*
|
|
476
|
+
* It takes no options on purpose. Every one of its callers is a session
|
|
477
|
+
* DECLARING a root it will serve — `ensureLSPConfigInitialized`, `ensureReady`,
|
|
478
|
+
* `runtime-session.ts`, `lens-engine.ts` — and a session-start load is exactly
|
|
479
|
+
* the caller that must report its config notices. There is no mode in which
|
|
480
|
+
* this function runs silently, because there is no caller that is not a
|
|
481
|
+
* session (#2427 review round 3).
|
|
195
482
|
*/
|
|
196
483
|
export async function initLSPConfig(cwd) {
|
|
197
484
|
const normalizedCwd = normalizeWorkspacePath(cwd);
|
|
198
485
|
// #2052: this cwd is now a served session root. Registered BEFORE the
|
|
199
486
|
// in-flight dedup return below, so a concurrent duplicate init still
|
|
200
487
|
// registers it rather than returning early with the root unrecorded.
|
|
201
|
-
registerSessionRoot(normalizedCwd);
|
|
488
|
+
forgetConfigResolvedClaims(registerSessionRoot(normalizedCwd));
|
|
202
489
|
const existing = configInFlight.get(normalizedCwd);
|
|
203
490
|
if (existing)
|
|
204
491
|
return existing;
|
|
205
492
|
const promise = (async () => {
|
|
206
|
-
const config = await loadLSPConfig(cwd);
|
|
207
|
-
|
|
208
|
-
const disabledServerIds = new Set(config.disabledServers ?? []);
|
|
209
|
-
if (config.servers) {
|
|
210
|
-
for (const [id, serverConfig] of Object.entries(config.servers)) {
|
|
211
|
-
try {
|
|
212
|
-
const server = createCustomServer(serverConfig, id);
|
|
213
|
-
customServers.push(server);
|
|
214
|
-
}
|
|
215
|
-
catch {
|
|
216
|
-
// pi-lens-ignore: missing-error-propagation — per-server registration, skip bad entries
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
const serverOverrides = new Map();
|
|
221
|
-
if (config.serverOverrides) {
|
|
222
|
-
for (const [id, entry] of Object.entries(config.serverOverrides)) {
|
|
223
|
-
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
224
|
-
const initOpts = entry
|
|
225
|
-
.initializationOptions;
|
|
226
|
-
if (initOpts !== undefined &&
|
|
227
|
-
typeof initOpts === "object" &&
|
|
228
|
-
initOpts !== null &&
|
|
229
|
-
!Array.isArray(initOpts)) {
|
|
230
|
-
serverOverrides.set(id, {
|
|
231
|
-
initializationOptions: initOpts,
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
workspaceConfigs.set(normalizedCwd, {
|
|
238
|
-
customServers,
|
|
239
|
-
disabledServerIds,
|
|
240
|
-
serverOverrides,
|
|
241
|
-
});
|
|
493
|
+
const config = registerLSPConfig(await loadLSPConfig(cwd, os.homedir()));
|
|
494
|
+
forgetConfigResolvedClaims(setSessionRootConfig(normalizedCwd, config));
|
|
242
495
|
})();
|
|
243
496
|
configInFlight.set(normalizedCwd, promise);
|
|
244
497
|
try {
|
|
@@ -254,13 +507,24 @@ export async function initLSPConfig(cwd) {
|
|
|
254
507
|
}
|
|
255
508
|
}
|
|
256
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* Every server a workspace knows about, in registry-then-custom order and
|
|
512
|
+
* BEFORE any gate is applied.
|
|
513
|
+
*
|
|
514
|
+
* Spelled once because two callers need the same list for opposite purposes:
|
|
515
|
+
* `getAllServers` DROPS the disabled ones, `explainServersForFile` REPORTS
|
|
516
|
+
* them. A custom server that only one of the two composed would be a server
|
|
517
|
+
* the runtime runs and the introspection cannot see, or the reverse.
|
|
518
|
+
*/
|
|
519
|
+
function registeredServers(config) {
|
|
520
|
+
return [...LSP_SERVERS, ...config.customServers];
|
|
521
|
+
}
|
|
257
522
|
/**
|
|
258
523
|
* Get all available servers (built-in + custom, minus disabled)
|
|
259
524
|
*/
|
|
260
525
|
export function getAllServers(filePath) {
|
|
261
526
|
const config = filePath ? getConfigForFile(filePath) : EMPTY_CONFIG;
|
|
262
|
-
|
|
263
|
-
return all.filter((s) => !config.disabledServerIds.has(s.id));
|
|
527
|
+
return registeredServers(config).filter((s) => !config.disabledServerIds.has(s.id));
|
|
264
528
|
}
|
|
265
529
|
/**
|
|
266
530
|
* Check if a server is disabled
|
|
@@ -269,22 +533,73 @@ export function isServerDisabled(serverId, filePath) {
|
|
|
269
533
|
const config = filePath ? getConfigForFile(filePath) : EMPTY_CONFIG;
|
|
270
534
|
return config.disabledServerIds.has(serverId);
|
|
271
535
|
}
|
|
272
|
-
|
|
273
|
-
|
|
536
|
+
/**
|
|
537
|
+
* THE server-selection gate: why this server does or does not attach to this
|
|
538
|
+
* file (#2427).
|
|
539
|
+
*
|
|
540
|
+
* One evaluation, two projections. `getServersForFileWithConfig` asks it for a
|
|
541
|
+
* verdict and `explainServersForFile` asks it for a reason; before #2427 the
|
|
542
|
+
* verdict lived here and the reason did not exist, so answering "why is server
|
|
543
|
+
* X not running" meant re-implementing these three gates at the asking site —
|
|
544
|
+
* a second copy of a filter is a copy that drifts, which is what AGENTS.md's
|
|
545
|
+
* single-source-of-truth rule forbids.
|
|
546
|
+
*
|
|
547
|
+
* Returning a REASON rather than a boolean is also what keeps the verdict path
|
|
548
|
+
* allocation-free: `getServersForFileWithConfig` runs per file on the dispatch
|
|
549
|
+
* and cascade paths, and materializing one decision object per registered
|
|
550
|
+
* server per call would put ~46 short-lived objects on a hot path to serve a
|
|
551
|
+
* question only the introspection surface asks.
|
|
552
|
+
*
|
|
553
|
+
* Gate order is the ANSWER order, not just an implementation detail: a server
|
|
554
|
+
* the operator disabled reports `disabled-by-config` even when the file's
|
|
555
|
+
* extension would not have matched it anyway, because "you turned it off" is
|
|
556
|
+
* the fact the asker can act on.
|
|
557
|
+
*/
|
|
558
|
+
function selectionReason(server, config, filePath, ext, base) {
|
|
559
|
+
if (config.disabledServerIds.has(server.id))
|
|
560
|
+
return "disabled-by-config";
|
|
561
|
+
let matched = false;
|
|
562
|
+
for (const value of server.extensions) {
|
|
563
|
+
const lower = value.toLowerCase();
|
|
564
|
+
if (lower === ext || lower === base) {
|
|
565
|
+
matched = true;
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (!matched)
|
|
570
|
+
return "extension-mismatch";
|
|
571
|
+
// #636: a server's extension match can be intentionally broader than what
|
|
572
|
+
// it can usefully act on (zizmor attaches to "yaml" but only ever reports
|
|
573
|
+
// on GitHub Actions workflow/action/dependabot paths). `pathFilter`, when
|
|
574
|
+
// present, is an ADDITIONAL narrowing gate — never a widening one.
|
|
575
|
+
if (server.pathFilter && !server.pathFilter(filePath))
|
|
576
|
+
return "path-filter";
|
|
577
|
+
return "selected";
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Every registered server's decision for a file, with the reason for each.
|
|
581
|
+
*
|
|
582
|
+
* `config` defaults to the SESSION's registered config for the file's tree —
|
|
583
|
+
* what the runtime would actually use. An explicit one is for a caller that
|
|
584
|
+
* must not touch session state to ask: `effectiveConfig` derives its own from
|
|
585
|
+
* `loadLSPConfig(..., { report: false })` rather than initializing the
|
|
586
|
+
* workspace (#2427 review round 3). Both spellings run the identical gate, and
|
|
587
|
+
* `tests/clients/effective-config.test.ts` pins them equal for the same cwd.
|
|
588
|
+
*/
|
|
589
|
+
export function explainServersForFile(filePath, config = getConfigForFile(filePath)) {
|
|
274
590
|
const ext = path.extname(filePath).toLowerCase();
|
|
275
591
|
const base = path.basename(filePath).toLowerCase();
|
|
276
|
-
return
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
if (!extensionMatch)
|
|
280
|
-
return false;
|
|
281
|
-
// #636: a server's extension match can be intentionally broader than what
|
|
282
|
-
// it can usefully act on (zizmor attaches to "yaml" but only ever reports
|
|
283
|
-
// on GitHub Actions workflow/action/dependabot paths). `pathFilter`, when
|
|
284
|
-
// present, is an ADDITIONAL narrowing gate — never a widening one.
|
|
285
|
-
return server.pathFilter ? server.pathFilter(filePath) : true;
|
|
592
|
+
return registeredServers(config).map((server) => {
|
|
593
|
+
const reason = selectionReason(server, config, filePath, ext, base);
|
|
594
|
+
return { server, selected: reason === "selected", reason };
|
|
286
595
|
});
|
|
287
596
|
}
|
|
597
|
+
export function getServersForFileWithConfig(filePath) {
|
|
598
|
+
const config = getConfigForFile(filePath);
|
|
599
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
600
|
+
const base = path.basename(filePath).toLowerCase();
|
|
601
|
+
return registeredServers(config).filter((server) => selectionReason(server, config, filePath, ext, base) === "selected");
|
|
602
|
+
}
|
|
288
603
|
/**
|
|
289
604
|
* The primary language server for a file (e.g. "typescript"), as opposed to a
|
|
290
605
|
* cross-cutting auxiliary scanner attached via clientScope "all"/
|
|
@@ -316,11 +631,22 @@ export function getServerInitOverride(serverId, filePath) {
|
|
|
316
631
|
return getConfigForFile(filePath).serverOverrides.get(serverId);
|
|
317
632
|
}
|
|
318
633
|
export function resetLSPConfigStateForTests() {
|
|
319
|
-
workspaceConfigs.clear();
|
|
320
634
|
resetLSPCaseSensitivityState();
|
|
321
|
-
//
|
|
322
|
-
//
|
|
635
|
+
// One call clears both the served roots and their configs: since #2518 they
|
|
636
|
+
// are one store, so a reset cannot leave a cleared config store beside a
|
|
637
|
+
// live session-root registry declining files for roots nothing can serve.
|
|
323
638
|
resetSessionRootsForTests();
|
|
639
|
+
// The warn latch is loader state too: a test that re-reads the same broken
|
|
640
|
+
// path after this reset must see the warning again, not a latched silence.
|
|
641
|
+
resetLSPConfigWarnCache();
|
|
642
|
+
// #2526: same reasoning for the per-session `config_resolved` claim — a
|
|
643
|
+
// test that resolves again after this reset must produce its record, not
|
|
644
|
+
// inherit a previous test's "already recorded" claim. Narrowed to THIS
|
|
645
|
+
// loader's own phase (review round 2, S1): the blanket
|
|
646
|
+
// `resetOncePerSessionPhases()` this used to call is the SESSION boundary's
|
|
647
|
+
// call to make — it also re-mints the session identity — and reaching for
|
|
648
|
+
// it from one producer's test reset cleared claims this module does not own.
|
|
649
|
+
releaseOncePerSessionPhase(CONFIG_RESOLVED_PHASE);
|
|
324
650
|
}
|
|
325
651
|
/**
|
|
326
652
|
* Test hook — read the `initLSPConfig` in-flight map directly (#1968's ABA
|