@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
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE config resolution path (#2426).
|
|
3
|
+
*
|
|
4
|
+
* Every pi-lens config file — canonical or legacy, project or global — is read,
|
|
5
|
+
* validated and layered here, through `config-core`'s `resolveConfig` over the
|
|
6
|
+
* canonical schema in `config-schema.ts`. The three exported loaders
|
|
7
|
+
* (`loadLSPConfig`, `loadPiLensGlobalConfig`, `loadPiLensProjectConfig`) keep
|
|
8
|
+
* their names and return types and become PROJECTIONS of what this module
|
|
9
|
+
* resolves, so their ~20 call sites are untouched.
|
|
10
|
+
*
|
|
11
|
+
* THE DOCUMENTED LOOKUP ORDER, lowest precedence first:
|
|
12
|
+
*
|
|
13
|
+
* builtin -> global -> project root -> nested-project -> env -> cli -> host
|
|
14
|
+
*
|
|
15
|
+
* It is `config-core`'s own `SOURCE_TIERS`, not a second ordering: this
|
|
16
|
+
* module places files into tiers and the core sorts them. `docs/configuration.md`
|
|
17
|
+
* documents the same order once, and `tests/clients/config-resolve.test.ts`
|
|
18
|
+
* walks it.
|
|
19
|
+
*
|
|
20
|
+
* Only THREE of those tiers are populated here — `global`, `project` and
|
|
21
|
+
* `nested-project` — because those are the tiers config FILES live in.
|
|
22
|
+
* `builtin`, `env`, `cli` and `host` are reserved (#2427 wires env/CLI, #2416
|
|
23
|
+
* the host tier); today's env vars and CLI flags reach a setting through their
|
|
24
|
+
* own accessors, and `docs/configuration.md` states that precedence rather than
|
|
25
|
+
* implying this resolution already carries it.
|
|
26
|
+
*
|
|
27
|
+
* WITHIN a tier, the canonical file is added LAST and therefore wins, because
|
|
28
|
+
* `merge` keeps caller order on a precedence tie. That is the whole deprecation
|
|
29
|
+
* story in one line: a legacy file is still read, and it loses to the file the
|
|
30
|
+
* user is being told to move to.
|
|
31
|
+
*
|
|
32
|
+
* LEGACY ROOT LSP KEYS ARE NORMALIZED INTO THE `lsp` NAMESPACE HERE, at source
|
|
33
|
+
* injection, and nowhere else (#2427 review round 2, F1). Every LSP setting has
|
|
34
|
+
* two spellings during the deprecation window — `disabledServers` at a
|
|
35
|
+
* document's root and `lsp.disabledServers` inside it — and leaving both in
|
|
36
|
+
* place gave the schema TWO nodes for one setting. `merge()` then resolved each
|
|
37
|
+
* node independently: two deny unions that never saw each other's tiers, and
|
|
38
|
+
* two object merges that never saw each other's entries. A projection could
|
|
39
|
+
* only pick one, and picking the canonical one meant a global denial written in
|
|
40
|
+
* the legacy spelling disappeared the moment any project file mentioned the
|
|
41
|
+
* canonical one — the operator-denial-lifted-by-repository-content defect
|
|
42
|
+
* #2415 AC 3 forbids, arriving through the migration instead of through the
|
|
43
|
+
* merge strategy.
|
|
44
|
+
*
|
|
45
|
+
* An earlier draft rejected this rewrite because it would inject an `lsp` key
|
|
46
|
+
* into `PiLensProjectConfig.raw` that was never in the user's file. That
|
|
47
|
+
* objection is why the normalization lives HERE and not in
|
|
48
|
+
* `resolveOnePiLensConfigDocument`: `.raw` comes from the single-document path,
|
|
49
|
+
* which still merges a document exactly as written. Only this multi-document
|
|
50
|
+
* resolution — whose two consumers are `loadLSPConfig` and `effectiveConfig`,
|
|
51
|
+
* both of which read the `lsp` section and nothing else — normalizes.
|
|
52
|
+
*
|
|
53
|
+
* The mapping is `canonicalKeyFor`'s, the one the deprecation records already
|
|
54
|
+
* publish, so "where does this key move to" is answered once. Within a single
|
|
55
|
+
* document the canonical spelling still wins a collision, which is the rule
|
|
56
|
+
* `lspSectionOf` used to apply at projection time; across documents `merge()`
|
|
57
|
+
* now applies the ordinary tier precedence and the deny union to ONE node.
|
|
58
|
+
*/
|
|
59
|
+
import * as fs from "node:fs";
|
|
60
|
+
import * as os from "node:os";
|
|
61
|
+
import * as path from "node:path";
|
|
62
|
+
import { DEPRECATED_CONFIG_SURFACES, } from "./config-diagnostic-codes.js";
|
|
63
|
+
import { CANONICAL_GLOBAL_CONFIG_FILE, CANONICAL_PROJECT_CONFIG_FILE, GLOBAL_CONFIG_LOCATIONS, LEGACY_ROOT_LSP_KEYS, LSP_NAMESPACE_KEY, PROJECT_CONFIG_LOCATIONS, configSearchDirs, } from "./config-locations.js";
|
|
64
|
+
// The core's halves are imported directly rather than through
|
|
65
|
+
// `config-core/index.js` (#2426). The barrel re-exports the whole vocabulary
|
|
66
|
+
// including `process-spec.js`, which reaches `project-trust.js` and the
|
|
67
|
+
// degradation ledger; a module that only resolves a config has no use for that
|
|
68
|
+
// and, sitting downstream of `file-utils.ts` as every config loader does, would
|
|
69
|
+
// close three import cycles by importing it. `resolveConfig` is still the one
|
|
70
|
+
// supported way into the pipeline — `merge()` is deliberately not imported here.
|
|
71
|
+
import { resolveConfig } from "./config-core/resolve.js";
|
|
72
|
+
import { finalizeRecords, migrationSubject, } from "./config-core/records.js";
|
|
73
|
+
import { SOURCE_TIERS, } from "./config-core/provenance.js";
|
|
74
|
+
import { PI_LENS_CONFIG_SCHEMA } from "./config-schema.js";
|
|
75
|
+
import { normalizeParseErrorReason, warnIgnoredConfigOnce, } from "./config-warn.js";
|
|
76
|
+
import { homeRelativePath } from "./path-utils.js";
|
|
77
|
+
/**
|
|
78
|
+
* Read and JSON-parse one config file.
|
|
79
|
+
*
|
|
80
|
+
* Deliberately does NOT warn: the three loaders each render their own prose
|
|
81
|
+
* (`ignoring invalid LSP config …` / `… global config …` / `… project config …`)
|
|
82
|
+
* and their tests assert on it, so the decision of what to say about a bad file
|
|
83
|
+
* stays with the loader while the reading itself is shared.
|
|
84
|
+
*/
|
|
85
|
+
export function readConfigDocument(file) {
|
|
86
|
+
let text;
|
|
87
|
+
try {
|
|
88
|
+
text = fs.readFileSync(file, "utf-8");
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
92
|
+
return { status: "missing" };
|
|
93
|
+
}
|
|
94
|
+
return { status: "error", error };
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
return { status: "ok", value: JSON.parse(text) };
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return { status: "error", error, sourceText: text };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The canonical GLOBAL config path this resolution uses — for READING it and
|
|
105
|
+
* for naming it in a migration notice, from one expression (#2426 review round
|
|
106
|
+
* 2, F1).
|
|
107
|
+
*
|
|
108
|
+
* The two used to be computed separately: the read came from
|
|
109
|
+
* `options.globalConfigPath` (`getPiLensGlobalConfigPath()`, `$HOME`-derived
|
|
110
|
+
* unless `PI_LENS_CONFIG_PATH` overrides it) while the notice was recomputed as
|
|
111
|
+
* `dirname(<legacy file>) + config.json` — and the legacy file lives under
|
|
112
|
+
* `getGlobalPiLensDir()`, which `PI_LENS_HOME` relocates. Set `PI_LENS_HOME`
|
|
113
|
+
* without `PI_LENS_CONFIG_PATH` and the two diverge, so the notice named a file
|
|
114
|
+
* the resolver never reads: a user who followed it moved their settings into a
|
|
115
|
+
* file that does nothing. Deriving both from here makes "the path we name" and
|
|
116
|
+
* "the path we read" the same value by construction rather than by agreement.
|
|
117
|
+
*/
|
|
118
|
+
function canonicalGlobalFile(options) {
|
|
119
|
+
if (options.globalConfigPath !== undefined)
|
|
120
|
+
return options.globalConfigPath;
|
|
121
|
+
return options.globalDir === undefined
|
|
122
|
+
? undefined
|
|
123
|
+
: path.join(options.globalDir, CANONICAL_GLOBAL_CONFIG_FILE);
|
|
124
|
+
}
|
|
125
|
+
function collectDocuments(dir, locations, tier, onReadError) {
|
|
126
|
+
const documents = [];
|
|
127
|
+
for (const location of locations) {
|
|
128
|
+
const file = path.join(dir, location.relativePath);
|
|
129
|
+
const outcome = readConfigDocument(file);
|
|
130
|
+
if (outcome.status === "missing")
|
|
131
|
+
continue;
|
|
132
|
+
if (outcome.status === "error") {
|
|
133
|
+
onReadError?.({
|
|
134
|
+
file,
|
|
135
|
+
location,
|
|
136
|
+
tier,
|
|
137
|
+
error: outcome.error,
|
|
138
|
+
sourceText: outcome.sourceText,
|
|
139
|
+
});
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
documents.push({ tier, file, location, value: outcome.value });
|
|
143
|
+
}
|
|
144
|
+
return documents;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Assemble every config document for a resolution, lowest precedence first.
|
|
148
|
+
*
|
|
149
|
+
* The project walk is ceiling-bounded by `configSearchDirs`. The OUTERMOST
|
|
150
|
+
* config-bearing directory is the `project` tier and everything nearer the cwd
|
|
151
|
+
* is `nested-project`, which is what makes "nearest wins, per field" fall out
|
|
152
|
+
* of the core's tier precedence instead of being re-implemented here.
|
|
153
|
+
*/
|
|
154
|
+
function collectPiLensConfigDocuments(options) {
|
|
155
|
+
const documents = [];
|
|
156
|
+
const { globalDir, onReadError } = options;
|
|
157
|
+
const globalConfigPath = canonicalGlobalFile(options);
|
|
158
|
+
if (globalDir !== undefined || globalConfigPath !== undefined) {
|
|
159
|
+
for (const location of GLOBAL_CONFIG_LOCATIONS) {
|
|
160
|
+
const file = location.relativePath === CANONICAL_GLOBAL_CONFIG_FILE
|
|
161
|
+
? globalConfigPath
|
|
162
|
+
: globalDir === undefined
|
|
163
|
+
? undefined
|
|
164
|
+
: path.join(globalDir, location.relativePath);
|
|
165
|
+
if (file === undefined)
|
|
166
|
+
continue;
|
|
167
|
+
const outcome = readConfigDocument(file);
|
|
168
|
+
if (outcome.status === "missing")
|
|
169
|
+
continue;
|
|
170
|
+
if (outcome.status === "error") {
|
|
171
|
+
onReadError?.({
|
|
172
|
+
file,
|
|
173
|
+
location,
|
|
174
|
+
tier: "global",
|
|
175
|
+
error: outcome.error,
|
|
176
|
+
sourceText: outcome.sourceText,
|
|
177
|
+
});
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
documents.push({ tier: "global", file, location, value: outcome.value });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (options.cwd !== undefined) {
|
|
184
|
+
// Innermost first from the walk; reversed so the OUTERMOST directory is
|
|
185
|
+
// emitted first and lands in the lower-precedence `project` tier.
|
|
186
|
+
const dirs = configSearchDirs(options.cwd, options.homeDir ?? os.homedir()).reverse();
|
|
187
|
+
const bearing = dirs
|
|
188
|
+
.map((dir) => ({
|
|
189
|
+
dir,
|
|
190
|
+
found: collectDocuments(dir, PROJECT_CONFIG_LOCATIONS, "project", onReadError),
|
|
191
|
+
}))
|
|
192
|
+
.filter((entry) => entry.found.length > 0);
|
|
193
|
+
bearing.forEach((entry, index) => {
|
|
194
|
+
const tier = index === 0 ? "project" : "nested-project";
|
|
195
|
+
for (const document of entry.found)
|
|
196
|
+
documents.push({ ...document, tier });
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return documents;
|
|
200
|
+
}
|
|
201
|
+
/** A zero for every tier, so a consumer never has to test for an absent key. */
|
|
202
|
+
function emptyTierCounts() {
|
|
203
|
+
return Object.fromEntries(SOURCE_TIERS.map((tier) => [tier, 0]));
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Project a resolution onto its redacted summary.
|
|
207
|
+
*
|
|
208
|
+
* Values never appear: a document contributes its PATH (home-relative), its
|
|
209
|
+
* tier and its legacy flag, and nothing else — the same structural redaction
|
|
210
|
+
* `pilens_effective_config` applies, spelled once rather than twice (rule 3 of
|
|
211
|
+
* `clients/effective-config.ts`: the redacted view is the only view).
|
|
212
|
+
*
|
|
213
|
+
* Pure and synchronous: it reads a resolution the caller already performed and
|
|
214
|
+
* resolves nothing itself, so no caller can turn "report what we resolved"
|
|
215
|
+
* into a second walk of the config tree (#2513's facade rule).
|
|
216
|
+
*/
|
|
217
|
+
export function summarizeConfigResolution(resolution, homeDir) {
|
|
218
|
+
const countsByTier = emptyTierCounts();
|
|
219
|
+
for (const entry of resolution.provenance.values()) {
|
|
220
|
+
countsByTier[entry.tier] += 1;
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
documents: resolution.documents.map((document) => ({
|
|
224
|
+
tier: document.tier,
|
|
225
|
+
file: homeRelativePath(document.file, homeDir),
|
|
226
|
+
legacy: document.location.legacy,
|
|
227
|
+
})),
|
|
228
|
+
countsByTier,
|
|
229
|
+
recordCount: resolution.records.length,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/** Resolve every discovered document into one configuration. */
|
|
233
|
+
export function resolvePiLensConfig(options) {
|
|
234
|
+
const documents = collectPiLensConfigDocuments(options);
|
|
235
|
+
const sources = configSources(documents);
|
|
236
|
+
const resolution = resolveConfig({
|
|
237
|
+
sources,
|
|
238
|
+
schema: PI_LENS_CONFIG_SCHEMA,
|
|
239
|
+
});
|
|
240
|
+
return {
|
|
241
|
+
value: resolution.resolved.value ?? {},
|
|
242
|
+
provenance: resolution.resolved.provenance,
|
|
243
|
+
records: finalizeRecords([
|
|
244
|
+
...resolution.records,
|
|
245
|
+
...deprecationRecords(documents, options.homeDir ?? os.homedir(), canonicalGlobalFile(options)),
|
|
246
|
+
], documentAnchor(documents),
|
|
247
|
+
// The core ALREADY truncated (#2426 review round 6, F1). Its drop count
|
|
248
|
+
// is seeded here so the one `PILENS_CFG_0007` a user reads names the
|
|
249
|
+
// whole truncation rather than the part this bound performed.
|
|
250
|
+
resolution.droppedRecordCount),
|
|
251
|
+
documents,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Resolve ONE document — the shape both single-file loaders need.
|
|
256
|
+
*
|
|
257
|
+
* Still the core's pipeline, not a shortcut around it: the value is validated
|
|
258
|
+
* against the canonical schema (depth bound, prototype-key policy, the declared
|
|
259
|
+
* `lsp.*` types) and merged from one source, so a single-file loader and the
|
|
260
|
+
* multi-file one cannot disagree about what a value means.
|
|
261
|
+
*
|
|
262
|
+
* It deliberately does NOT normalize legacy root LSP keys into the `lsp`
|
|
263
|
+
* namespace the way `resolvePiLensConfig` does (#2427 review round 2, F1).
|
|
264
|
+
* This path's two callers project the pi-lens sections of ONE file and hand
|
|
265
|
+
* the resolved value out as `PiLensProjectConfig.raw`, which is documented and
|
|
266
|
+
* tested as the parsed document; relocating a key inside it would put an `lsp`
|
|
267
|
+
* section in `raw` that the user's file never contained. The relocation exists
|
|
268
|
+
* so that ONE key seen at SEVERAL tiers reaches one schema node, and a
|
|
269
|
+
* single-document resolution has no tiers to reconcile.
|
|
270
|
+
*
|
|
271
|
+
* The visible consequence is small and worth naming: a wrong-TYPED legacy root
|
|
272
|
+
* LSP key (`warmFiles: true`) is validated against the typed `lsp.*` node only
|
|
273
|
+
* in the multi-document path, so the LSP loader reports it and the two
|
|
274
|
+
* single-file loaders do not. A session runs both, and the warn-once latch
|
|
275
|
+
* unions their notices, so a user loses nothing — they gain one accurate
|
|
276
|
+
* notice.
|
|
277
|
+
*/
|
|
278
|
+
export function resolveOnePiLensConfigDocument(document, homeDir = os.homedir()) {
|
|
279
|
+
const resolution = resolveConfig({
|
|
280
|
+
sources: [
|
|
281
|
+
{ tier: document.tier, file: document.file, value: document.value },
|
|
282
|
+
],
|
|
283
|
+
schema: PI_LENS_CONFIG_SCHEMA,
|
|
284
|
+
});
|
|
285
|
+
return {
|
|
286
|
+
value: resolution.resolved.value ?? {},
|
|
287
|
+
records: finalizeRecords([...resolution.records, ...deprecationRecords([document], homeDir)], documentAnchor([document]),
|
|
288
|
+
// Same double bound as the multi-document path above (round 6, F1).
|
|
289
|
+
resolution.droppedRecordCount),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* The anchor for a record list produced from these documents: the
|
|
294
|
+
* highest-precedence one — the file whose values would have won, and the one a
|
|
295
|
+
* user reading a config notice is looking at.
|
|
296
|
+
*
|
|
297
|
+
* The list's BOUND, its overflow record, and the reserved slot that record
|
|
298
|
+
* occupies all live in `config-core/records.ts` (#2426 review round 5,
|
|
299
|
+
* F-A/F-B/S-A). This module used to carry its own copy of them, the project
|
|
300
|
+
* loader carried a second, and the project loader's legacy path carried none
|
|
301
|
+
* at all — the shape the single-source rule exists to catch. What is left here
|
|
302
|
+
* is the one thing `config-core` cannot know: which of the caller's documents
|
|
303
|
+
* is the anchor.
|
|
304
|
+
*/
|
|
305
|
+
function documentAnchor(documents) {
|
|
306
|
+
const last = documents[documents.length - 1];
|
|
307
|
+
return last === undefined ? undefined : { file: last.file, tier: last.tier };
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* The `ConfigLocation` for a project-relative basename, for a caller that
|
|
311
|
+
* discovered the file through its own (cached) walk rather than through
|
|
312
|
+
* `collectPiLensConfigDocuments`. Falls back to the canonical location so an
|
|
313
|
+
* unrecognized basename is never reported as deprecated.
|
|
314
|
+
*/
|
|
315
|
+
export function projectLocationFor(file) {
|
|
316
|
+
const base = path.basename(file);
|
|
317
|
+
return (PROJECT_CONFIG_LOCATIONS.find((candidate) => path.posix.basename(candidate.relativePath) === base) ?? PROJECT_CONFIG_LOCATIONS[PROJECT_CONFIG_LOCATIONS.length - 1]);
|
|
318
|
+
}
|
|
319
|
+
// --- deprecation records (#2418 codes 0002 / 0003, #2426 is their producer) ---
|
|
320
|
+
const WINDOW_BY_SURFACE = new Map(DEPRECATED_CONFIG_SURFACES.map((row) => [row.surface, row]));
|
|
321
|
+
function windowSuffix(surface) {
|
|
322
|
+
const row = surface === undefined ? undefined : WINDOW_BY_SURFACE.get(surface);
|
|
323
|
+
return row
|
|
324
|
+
? ` (deprecated since ${row.deprecatedSince}; read for the last time before ${row.removeNotBefore})`
|
|
325
|
+
: "";
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* The canonical file a legacy document's keys belong in.
|
|
329
|
+
*
|
|
330
|
+
* `globalFile` is THREADED from the resolution that read the legacy document,
|
|
331
|
+
* never recomputed from that document's own directory (#2426 review round 2,
|
|
332
|
+
* F1) — see `canonicalGlobalFile`. The sibling fallback survives only for a
|
|
333
|
+
* caller that resolves ONE document in isolation and names no canonical global
|
|
334
|
+
* (no production caller does; `resolveOnePiLensConfigDocument`'s two callers
|
|
335
|
+
* pass a project document and a canonical global one respectively), and it is
|
|
336
|
+
* the only path this module can name at all in that case.
|
|
337
|
+
*/
|
|
338
|
+
function canonicalDestination(document, homeDir, globalFile) {
|
|
339
|
+
if (document.tier === "global") {
|
|
340
|
+
return homeRelativePath(globalFile ??
|
|
341
|
+
path.join(path.dirname(document.file), CANONICAL_GLOBAL_CONFIG_FILE), homeDir);
|
|
342
|
+
}
|
|
343
|
+
return CANONICAL_PROJECT_CONFIG_FILE;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* The `lsp`-namespace key a document ROOT key normalizes to, or `undefined`
|
|
347
|
+
* when the key stays where it is.
|
|
348
|
+
*
|
|
349
|
+
* THE mapping, shared with `canonicalKeyFor` below so the key a deprecation
|
|
350
|
+
* record tells the user to move to and the key the resolution actually merges
|
|
351
|
+
* under are the same key by construction. An LSP-SCOPED legacy file
|
|
352
|
+
* (`pi-lsp.json`, `.pi-lens/lsp.json`) is wholly what a canonical file now
|
|
353
|
+
* carries under `lsp`, so every RECOGNIZED root key of one moves; a canonical
|
|
354
|
+
* or non-LSP legacy file moves only the four registry-derived legacy root keys.
|
|
355
|
+
* An unrecognized key is left alone in both cases — it is not a pi-lens
|
|
356
|
+
* setting, and relocating it would invent a meaning for it.
|
|
357
|
+
*/
|
|
358
|
+
function lspNamespaceKeyFor(document, key) {
|
|
359
|
+
if (key === LSP_NAMESPACE_KEY)
|
|
360
|
+
return undefined;
|
|
361
|
+
if (document.location.lspScoped) {
|
|
362
|
+
return recognizedKeysFor(document.location).has(key) ? key : undefined;
|
|
363
|
+
}
|
|
364
|
+
return LEGACY_ROOT_LSP_KEYS.includes(key) ? key : undefined;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Assign one raw, not-yet-validated value under a user-chosen key.
|
|
368
|
+
*
|
|
369
|
+
* `defineProperty` rather than `=` for the reason `config-core/safe-object.ts`
|
|
370
|
+
* spells out: `out["__proto__"] = x` runs an inherited SETTER instead of
|
|
371
|
+
* creating a property. This split runs BEFORE `validate()`, so it is the one
|
|
372
|
+
* place in this module that touches user-chosen key names, and it hands every
|
|
373
|
+
* such key through untouched for the core to reject and record.
|
|
374
|
+
*/
|
|
375
|
+
function assignRaw(target, key, value) {
|
|
376
|
+
Object.defineProperty(target, key, {
|
|
377
|
+
value,
|
|
378
|
+
writable: true,
|
|
379
|
+
enumerable: true,
|
|
380
|
+
configurable: true,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* The sources a resolution merges, with every document's legacy root LSP keys
|
|
385
|
+
* moved into the `lsp` namespace (#2427 review round 2, F1).
|
|
386
|
+
*
|
|
387
|
+
* A document that carries none is passed through UNCHANGED — the overwhelming
|
|
388
|
+
* majority, and the case that must cost nothing. One that does becomes TWO
|
|
389
|
+
* sources at the same tier and file: the relocated keys first, the rest of the
|
|
390
|
+
* document (its own `lsp` namespace included) second. Two sources rather than
|
|
391
|
+
* one merged object because the second still carries whatever the document
|
|
392
|
+
* spelled at `lsp`, so a malformed `lsp` keeps producing its `PILENS_CFG_0004`
|
|
393
|
+
* while the relocated keys survive beside it; and because caller order at an
|
|
394
|
+
* equal tier is what `merge()` uses to break a tie, putting the canonical
|
|
395
|
+
* spelling second is exactly the canonical-wins-per-key rule `lspSectionOf`
|
|
396
|
+
* used to apply at projection time.
|
|
397
|
+
*
|
|
398
|
+
* The relocated keys are STRIPPED from the second source rather than left in
|
|
399
|
+
* place. Leaving them would keep the legacy schema node populated, and a
|
|
400
|
+
* malformed legacy key would then record the same `PILENS_CFG_0004` twice —
|
|
401
|
+
* once per spelling — for one mistake in one file.
|
|
402
|
+
*
|
|
403
|
+
* A deny key is the one place the intra-document tie is not "canonical wins":
|
|
404
|
+
* `merge()` sends `lsp.disabledServers` to `config-core/deny.ts`, which unions
|
|
405
|
+
* every contribution. A file spelling BOTH `disabledServers` and
|
|
406
|
+
* `lsp.disabledServers` therefore denies both lists. That is the monotonic
|
|
407
|
+
* answer and the only safe one — a denial silently dropped because the user
|
|
408
|
+
* half-migrated the file it was written in is the defect this whole change
|
|
409
|
+
* exists to remove.
|
|
410
|
+
*/
|
|
411
|
+
function configSources(documents) {
|
|
412
|
+
const sources = [];
|
|
413
|
+
for (const document of documents) {
|
|
414
|
+
const value = document.value;
|
|
415
|
+
const namespaced = {};
|
|
416
|
+
const rest = {};
|
|
417
|
+
for (const key of topLevelKeys(value)) {
|
|
418
|
+
const entry = value[key];
|
|
419
|
+
const target = lspNamespaceKeyFor(document, key);
|
|
420
|
+
if (target === undefined)
|
|
421
|
+
assignRaw(rest, key, entry);
|
|
422
|
+
else
|
|
423
|
+
assignRaw(namespaced, target, entry);
|
|
424
|
+
}
|
|
425
|
+
const base = { tier: document.tier, file: document.file };
|
|
426
|
+
if (Object.keys(namespaced).length === 0) {
|
|
427
|
+
sources.push({ ...base, value });
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
sources.push({ ...base, value: { [LSP_NAMESPACE_KEY]: namespaced } });
|
|
431
|
+
sources.push({ ...base, value: rest });
|
|
432
|
+
}
|
|
433
|
+
return sources;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* The canonical KEY a legacy key moves to.
|
|
437
|
+
*
|
|
438
|
+
* Carried on the record (`canonicalKey`) rather than only rendered into the
|
|
439
|
+
* message, so the auto-migrator #2426 explicitly defers can be a pure function
|
|
440
|
+
* over these records instead of re-parsing prose.
|
|
441
|
+
*/
|
|
442
|
+
function canonicalKeyFor(document, key) {
|
|
443
|
+
const namespaced = lspNamespaceKeyFor(document, key);
|
|
444
|
+
return namespaced === undefined ? key : `${LSP_NAMESPACE_KEY}.${namespaced}`;
|
|
445
|
+
}
|
|
446
|
+
function topLevelKeys(value) {
|
|
447
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
448
|
+
? Object.keys(value)
|
|
449
|
+
: [];
|
|
450
|
+
}
|
|
451
|
+
/** The `properties` key names of a JSON-Schema-shaped node. */
|
|
452
|
+
function schemaPropertyKeys(node) {
|
|
453
|
+
const properties = node?.properties;
|
|
454
|
+
return properties !== null &&
|
|
455
|
+
typeof properties === "object" &&
|
|
456
|
+
!Array.isArray(properties)
|
|
457
|
+
? Object.keys(properties)
|
|
458
|
+
: [];
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* The top-level keys a pi-lens config document may legitimately carry — READ
|
|
462
|
+
* OFF the canonical schema rather than restated, so a section added to a
|
|
463
|
+
* registry is recognized here for free (`config-schema.ts` derives its property
|
|
464
|
+
* list from those registries).
|
|
465
|
+
*/
|
|
466
|
+
const CANONICAL_TOP_LEVEL_KEYS = new Set(schemaPropertyKeys(PI_LENS_CONFIG_SCHEMA));
|
|
467
|
+
/**
|
|
468
|
+
* The same, for an LSP-SCOPED legacy file (`pi-lsp.json`, `.pi-lens/lsp.json`,
|
|
469
|
+
* `~/.pi-lens/lsp.json`): that whole file is what a canonical file now holds
|
|
470
|
+
* under `lsp`, so its recognized ROOT keys are the `lsp` namespace's own.
|
|
471
|
+
*/
|
|
472
|
+
const LSP_DOCUMENT_KEYS = new Set(schemaPropertyKeys(PI_LENS_CONFIG_SCHEMA
|
|
473
|
+
.properties?.[LSP_NAMESPACE_KEY]));
|
|
474
|
+
function recognizedKeysFor(location) {
|
|
475
|
+
return location.lspScoped ? LSP_DOCUMENT_KEYS : CANONICAL_TOP_LEVEL_KEYS;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* One record per `(file, key)` — never one per nested leaf, and only for a key
|
|
479
|
+
* pi-lens actually RECOGNIZES (#2426 review round 4, F2/F5).
|
|
480
|
+
*
|
|
481
|
+
* A per-leaf record would re-nag once per custom server. A record for every
|
|
482
|
+
* top-level key, which is what this emitted before, was worse in both
|
|
483
|
+
* directions: the count was user input, so a 100-key legacy file produced 100
|
|
484
|
+
* "move it" notices on top of the 98 typo notices the project loader already
|
|
485
|
+
* emits for the same keys; and each one told the user to MOVE a key that is
|
|
486
|
+
* not a pi-lens setting at all, which is advice that cannot be followed. A key
|
|
487
|
+
* no schema property claims gets the loaders' typo diagnostic and nothing
|
|
488
|
+
* else.
|
|
489
|
+
*
|
|
490
|
+
* The unrecognized keys are still accounted for, in ONE whole-file record
|
|
491
|
+
* naming their count — the file is at a deprecated location whatever its keys
|
|
492
|
+
* spell, and a user who moves only the keys named here has to know the rest
|
|
493
|
+
* were left behind deliberately.
|
|
494
|
+
*/
|
|
495
|
+
export function deprecationRecords(documents, homeDir, globalFile) {
|
|
496
|
+
const records = [];
|
|
497
|
+
for (const document of documents) {
|
|
498
|
+
if (document.location.legacy) {
|
|
499
|
+
const destination = canonicalDestination(document, homeDir, globalFile);
|
|
500
|
+
const recognized = recognizedKeysFor(document.location);
|
|
501
|
+
let unrecognized = 0;
|
|
502
|
+
for (const key of topLevelKeys(document.value)) {
|
|
503
|
+
if (!recognized.has(key)) {
|
|
504
|
+
unrecognized += 1;
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
const canonicalKey = canonicalKeyFor(document, key);
|
|
508
|
+
records.push(record({
|
|
509
|
+
code: "PILENS_CFG_0003",
|
|
510
|
+
file: document.file,
|
|
511
|
+
key,
|
|
512
|
+
canonicalKey,
|
|
513
|
+
tier: document.tier,
|
|
514
|
+
reason: `move "${key}" to ${destination}` +
|
|
515
|
+
(canonicalKey === key ? "" : ` under "${canonicalKey}"`) +
|
|
516
|
+
windowSuffix(document.location.surface),
|
|
517
|
+
}));
|
|
518
|
+
}
|
|
519
|
+
if (unrecognized > 0) {
|
|
520
|
+
const plural = unrecognized === 1 ? "it was" : "they were";
|
|
521
|
+
records.push({
|
|
522
|
+
code: "PILENS_CFG_0003",
|
|
523
|
+
file: document.file,
|
|
524
|
+
key: "",
|
|
525
|
+
// An LSP-scoped legacy file moves WHOLESALE into the `lsp`
|
|
526
|
+
// namespace, so naming that namespace is literally true here — and
|
|
527
|
+
// it is what routes this record to the LSP loader's subsystem, the
|
|
528
|
+
// same key-derived ownership every other record uses.
|
|
529
|
+
...(document.location.lspScoped
|
|
530
|
+
? { canonicalKey: LSP_NAMESPACE_KEY }
|
|
531
|
+
: {}),
|
|
532
|
+
subject: migrationSubject(document.file, ""),
|
|
533
|
+
tier: document.tier,
|
|
534
|
+
reason: `this file is at a deprecated location; move it to ${destination}. ` +
|
|
535
|
+
(unrecognized === 1
|
|
536
|
+
? "1 of its top-level keys is not a recognized pi-lens setting"
|
|
537
|
+
: `${unrecognized} of its top-level keys are not recognized pi-lens settings`) +
|
|
538
|
+
`, and ${plural} not migrated${windowSuffix(document.location.surface)}`,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
// A CANONICAL file carrying legacy ROOT LSP keys: the file stays, the keys
|
|
544
|
+
// move into the `lsp` namespace inside it.
|
|
545
|
+
for (const key of topLevelKeys(document.value)) {
|
|
546
|
+
if (!LEGACY_ROOT_LSP_KEYS.includes(key))
|
|
547
|
+
continue;
|
|
548
|
+
const canonicalKey = `${LSP_NAMESPACE_KEY}.${key}`;
|
|
549
|
+
records.push(record({
|
|
550
|
+
code: "PILENS_CFG_0002",
|
|
551
|
+
file: document.file,
|
|
552
|
+
key,
|
|
553
|
+
canonicalKey,
|
|
554
|
+
tier: document.tier,
|
|
555
|
+
reason: `move "${key}" to "${canonicalKey}" in the same file${windowSuffix(key)}`,
|
|
556
|
+
}));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return records;
|
|
560
|
+
}
|
|
561
|
+
function record(input) {
|
|
562
|
+
return {
|
|
563
|
+
code: input.code,
|
|
564
|
+
file: input.file,
|
|
565
|
+
key: input.key,
|
|
566
|
+
canonicalKey: input.canonicalKey,
|
|
567
|
+
subject: migrationSubject(input.file, input.key),
|
|
568
|
+
reason: input.reason,
|
|
569
|
+
tier: input.tier,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* `undefined` for a record no key can attribute — the whole-file/whole-document
|
|
574
|
+
* failure records (`key: ""`) that both halves of the core emit when a resolution
|
|
575
|
+
* fails internally. Those are reported by EVERY loader rather than by none: a
|
|
576
|
+
* duplicate notice about a config that failed to load is noise, while silence
|
|
577
|
+
* about it is the failure mode this module exists to prevent.
|
|
578
|
+
*/
|
|
579
|
+
function configRecordOwner(record) {
|
|
580
|
+
const key = record.canonicalKey ?? record.key;
|
|
581
|
+
if (key.length === 0)
|
|
582
|
+
return undefined;
|
|
583
|
+
const head = (key.startsWith("/") ? key.slice(1) : key).split(/[./]/)[0] ?? "";
|
|
584
|
+
if (head.length === 0)
|
|
585
|
+
return undefined;
|
|
586
|
+
return head === LSP_NAMESPACE_KEY || LEGACY_ROOT_LSP_KEYS.includes(head)
|
|
587
|
+
? "lsp"
|
|
588
|
+
: "pi-lens";
|
|
589
|
+
}
|
|
590
|
+
/** The pi-lens-config subsystem a GLOBAL vs. PROJECT tier reports under. */
|
|
591
|
+
function piLensSubsystemFor(tier) {
|
|
592
|
+
if (tier === "global")
|
|
593
|
+
return "lens-config";
|
|
594
|
+
if (tier === "project" || tier === "nested-project") {
|
|
595
|
+
return "project-lens-config";
|
|
596
|
+
}
|
|
597
|
+
// `builtin` / `env` / `cli` / `host` never produce a document today, and a
|
|
598
|
+
// merge-level bound refusal (see `MigrationRecord.tier`'s doc comment) names
|
|
599
|
+
// no single source at all — `undefined` in either case.
|
|
600
|
+
return undefined;
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* The subsystem(s) one record must be reported under (#2426 review round 3,
|
|
604
|
+
* F1).
|
|
605
|
+
*
|
|
606
|
+
* DERIVED from the record itself — owner (`configRecordOwner`) plus, for a
|
|
607
|
+
* pi-lens-owned record, the TIER it was read at — rather than from which
|
|
608
|
+
* loader is calling. Before this, a caller filtered to the records it "owned"
|
|
609
|
+
* with `recordsOwnedBy` before reporting, which meant a pi-lens-owned record
|
|
610
|
+
* from a document only the LSP loader's multi-file resolution actually
|
|
611
|
+
* discovered (a legacy sibling file `loadPiLensProjectConfig`'s single-nearest-
|
|
612
|
+
* file walk never opens) was filtered OUT by the LSP loader and never produced
|
|
613
|
+
* by the project loader either: dropped by construction, never reported by
|
|
614
|
+
* anyone.
|
|
615
|
+
*
|
|
616
|
+
* Deriving the subsystem from the record instead of the caller fixes that:
|
|
617
|
+
* EVERY loader that sees the record now reports it, always to the SAME
|
|
618
|
+
* subsystem set, so the warn-once latch (keyed on `subsystem\0file\0key\0
|
|
619
|
+
* reason`) naturally collapses duplicate reports from different callers into
|
|
620
|
+
* one notice — the F2 behavior — while a record no caller happened to "own"
|
|
621
|
+
* before is reported by whichever loader actually resolved it.
|
|
622
|
+
*
|
|
623
|
+
* An `lsp` owner always reports under `lsp-config`, regardless of tier — the
|
|
624
|
+
* LSP loader is the one home for every LSP setting. A `pi-lens` owner reports
|
|
625
|
+
* under the tier-appropriate pi-lens loader.
|
|
626
|
+
*
|
|
627
|
+
* An unattributable record (owner `undefined` — the whole-file failures both
|
|
628
|
+
* halves of the core emit with `key: ""`) reports under the ONE subsystem its
|
|
629
|
+
* TIER names, when a tier is known (#2426 review round 4, S1). Round 3 sent it
|
|
630
|
+
* to every subsystem that could plausibly have produced it, which turned one
|
|
631
|
+
* internal failure into three notices, one of them announcing an "LSP config"
|
|
632
|
+
* problem in a file with no LSP settings in it. `config-core`'s own
|
|
633
|
+
* `resolveConfig` anchors that record to the resolution's highest-precedence
|
|
634
|
+
* source precisely so the tier is known; the report-by-all fallback survives only for
|
|
635
|
+
* a record that names no source at all.
|
|
636
|
+
*/
|
|
637
|
+
function configRecordSubsystems(record) {
|
|
638
|
+
const owner = configRecordOwner(record);
|
|
639
|
+
const piLens = piLensSubsystemFor(record.tier);
|
|
640
|
+
const piLensSubsystems = piLens
|
|
641
|
+
? [piLens]
|
|
642
|
+
: ["lens-config", "project-lens-config"];
|
|
643
|
+
if (owner === "lsp")
|
|
644
|
+
return ["lsp-config"];
|
|
645
|
+
if (owner === "pi-lens")
|
|
646
|
+
return piLensSubsystems;
|
|
647
|
+
if (piLens)
|
|
648
|
+
return [piLens];
|
|
649
|
+
return ["lsp-config", ...piLensSubsystems];
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* WHICH subsystem owns a config file that could not be read or parsed (#2445).
|
|
653
|
+
*
|
|
654
|
+
* Derived from the DOCUMENT — its location and its tier — for the same reason
|
|
655
|
+
* `configRecordSubsystems` derives ownership from the record: `loadLSPConfig`
|
|
656
|
+
* opens the pi-lens global and project configs too, and labelling their parse
|
|
657
|
+
* failures "ignoring invalid LSP config" told a user to look at an LSP setting
|
|
658
|
+
* when what they had broken was `~/.pi-lens/config.json`. An LSP-SCOPED file
|
|
659
|
+
* (`pi-lsp.json`, `lsp.json`) is the LSP loader's whatever tier it sits at.
|
|
660
|
+
*/
|
|
661
|
+
function configFileSubsystem(location, tier) {
|
|
662
|
+
if (location.lspScoped)
|
|
663
|
+
return "lsp-config";
|
|
664
|
+
return tier === "global" ? "lens-config" : "project-lens-config";
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Report an unreadable/unparsable config document under its OWNING subsystem.
|
|
668
|
+
*
|
|
669
|
+
* `{ parseError }`, never a pre-stringified message: `warnIgnoredConfigOnce` is
|
|
670
|
+
* the one seam that decides how much of a `JSON.parse` `SyntaxError#message`
|
|
671
|
+
* (which embeds a snippet of the file being parsed, #2431) survives into the
|
|
672
|
+
* log line, the ledger row and the notification.
|
|
673
|
+
*/
|
|
674
|
+
export function reportConfigReadFailure(failure) {
|
|
675
|
+
warnIgnoredConfigOnce({
|
|
676
|
+
subsystem: configFileSubsystem(failure.location, failure.tier),
|
|
677
|
+
file: failure.file,
|
|
678
|
+
reason: { parseError: failure.error, sourceText: failure.sourceText },
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Thread a resolution's records to the ONE config warning seam (#2418).
|
|
683
|
+
*
|
|
684
|
+
* Every caller passes every record its OWN resolution produced — no filtering
|
|
685
|
+
* (#2426 review round 3, F1; see `subsystemsFor`). Calling this more than once
|
|
686
|
+
* with overlapping records (the LSP loader and a pi-lens loader resolving the
|
|
687
|
+
* same document) is by design: the warn-once latch dedupes across callers, so
|
|
688
|
+
* the user still sees exactly one notice per `(subsystem, file, key, reason)`.
|
|
689
|
+
*
|
|
690
|
+
* It lives here rather than in `config-core/records.ts`, where #2425 first put
|
|
691
|
+
* it (#2426). Putting the sink call inside the library contradicted the
|
|
692
|
+
* library's own stated purity — `config-core` is documented as "no state, no
|
|
693
|
+
* I/O, no ledger writes" — and the import it needed
|
|
694
|
+
* (`records.ts` -> `config-warn.js` -> `degradation-ledger.js`) closed seven
|
|
695
|
+
* cycles the moment a loader resolved through the core, because every config
|
|
696
|
+
* loader sits downstream of `file-utils.ts`. Reporting is a loader decision;
|
|
697
|
+
* this is the loaders' shared module.
|
|
698
|
+
*/
|
|
699
|
+
export function reportPiLensConfigRecords(records) {
|
|
700
|
+
for (const record of records) {
|
|
701
|
+
for (const subsystem of configRecordSubsystems(record)) {
|
|
702
|
+
warnIgnoredConfigOnce({
|
|
703
|
+
subsystem,
|
|
704
|
+
file: record.file,
|
|
705
|
+
key: record.key.length > 0 ? record.key : undefined,
|
|
706
|
+
reason: record.reason,
|
|
707
|
+
code: record.code,
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Buffer a loader's own ignored-config notices for one document, then bound
|
|
714
|
+
* them once.
|
|
715
|
+
*
|
|
716
|
+
* A `NoteIgnored` COLLECTS rather than reports (#2426 review round 4, F3/F5).
|
|
717
|
+
* Two things follow. The notice survives into a loader's cache entry, so a warm
|
|
718
|
+
* cache HIT replays it instead of losing the whole `config-ignored` class from
|
|
719
|
+
* session 2 onwards. And the count is bounded: the number of these a file can
|
|
720
|
+
* produce is the number of keys the user typed — a 100-key file emitted 98
|
|
721
|
+
* unknown-key notifications — so the buffered list goes through the same
|
|
722
|
+
* `finalizeRecords` bound every other config record obeys, with one slot held
|
|
723
|
+
* back to say how many were suppressed.
|
|
724
|
+
*
|
|
725
|
+
* ONE COPY, in the module both loaders already import (#2426 review round 7,
|
|
726
|
+
* F2). Round 6 gave the global loader the bound the project loader already had
|
|
727
|
+
* — by copying it — leaving two spellings of one record literal
|
|
728
|
+
* (`PILENS_CFG_0001`, no key, `migrationSubject(configPath, "")`) and one flush,
|
|
729
|
+
* differing only in a tier literal. Two copies of one policy is the shape round
|
|
730
|
+
* 5 collapsed three copies of this same bound to avoid, so the tier is an
|
|
731
|
+
* argument and the copy is gone. `tests/clients/config-notice-bounds.test.ts`
|
|
732
|
+
* asserts on the SOURCE that no third copy grows back, because duplication is
|
|
733
|
+
* invisible to a behavioral probe until one copy is edited.
|
|
734
|
+
*
|
|
735
|
+
* The buffer is a plain array (round 6, S2). It used to be a pre-subtracted
|
|
736
|
+
* `finalizableCollector`, a second exported shape for the one policy round 5
|
|
737
|
+
* had just collapsed; the notes a single parse can accumulate are bounded by
|
|
738
|
+
* the document already in memory, which is a smaller cost than a second public
|
|
739
|
+
* entry point onto the same arithmetic.
|
|
740
|
+
*/
|
|
741
|
+
export function ignoredRecordCollector(configPath, tier) {
|
|
742
|
+
const noted = [];
|
|
743
|
+
const note = (reason) => {
|
|
744
|
+
noted.push({
|
|
745
|
+
code: "PILENS_CFG_0001",
|
|
746
|
+
file: configPath,
|
|
747
|
+
key: "",
|
|
748
|
+
subject: migrationSubject(configPath, ""),
|
|
749
|
+
reason: typeof reason === "string"
|
|
750
|
+
? reason
|
|
751
|
+
: normalizeParseErrorReason(reason.parseError, {
|
|
752
|
+
sourceText: reason.sourceText,
|
|
753
|
+
}),
|
|
754
|
+
tier,
|
|
755
|
+
});
|
|
756
|
+
};
|
|
757
|
+
return {
|
|
758
|
+
note,
|
|
759
|
+
records: () => finalizeRecords(noted, { file: configPath, tier }),
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* The effective `lsp` section of a resolved configuration.
|
|
764
|
+
*
|
|
765
|
+
* One lookup, because the combination it used to perform now happens at source
|
|
766
|
+
* injection (`configSources`): by the time a value reaches here, every legacy
|
|
767
|
+
* root LSP key a document carried is already inside the namespace and has been
|
|
768
|
+
* resolved against every tier through the SAME schema node as its canonical
|
|
769
|
+
* spelling. Canonical still wins a collision inside one document — that is
|
|
770
|
+
* caller order in `configSources` — but the winner is now chosen by `merge()`,
|
|
771
|
+
* which is the only place that can see all the tiers at once.
|
|
772
|
+
*
|
|
773
|
+
* "Wins" is PER LEAF, not per key. A document that spells both root `servers`
|
|
774
|
+
* and `lsp.servers` no longer has one object replace the other: the two are
|
|
775
|
+
* merged by server id, and canonical wins only the ids they both define. A
|
|
776
|
+
* server defined only in the legacy root spelling therefore survives beside
|
|
777
|
+
* the canonical ones instead of disappearing — which is the same fix, one
|
|
778
|
+
* level down, as the vanishing legacy denial below (#2427 review round 3).
|
|
779
|
+
*
|
|
780
|
+
* The projection-time combination this used to do could only ever see two
|
|
781
|
+
* independently resolved nodes, and picking one of them is how a global denial
|
|
782
|
+
* written in the legacy spelling used to vanish (#2427 review round 2, F1).
|
|
783
|
+
*/
|
|
784
|
+
export function lspSectionOf(value) {
|
|
785
|
+
const namespace = value[LSP_NAMESPACE_KEY];
|
|
786
|
+
return namespace && typeof namespace === "object" && !Array.isArray(namespace)
|
|
787
|
+
? namespace
|
|
788
|
+
: {};
|
|
789
|
+
}
|