@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,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE canonical pi-lens config schema (#2426).
|
|
3
|
+
*
|
|
4
|
+
* A plain JSON-Schema-shaped object, per `config-core/schema.ts`: the same
|
|
5
|
+
* value the validator runs on is the value #2416 publishes as
|
|
6
|
+
* `docs/schema/pi-lens-config-v1.json`, so there is no compiler between the two
|
|
7
|
+
* for drift to hide in. It is the FIRST real schema through the #2418 harness —
|
|
8
|
+
* `tests/config/pi-lens-config-schema.test.ts` runs `assertSchemaStabilityTiers`
|
|
9
|
+
* and `assertSchemaIdentityAnchor` over this object rather than over the
|
|
10
|
+
* placeholder fixture, which is what makes those two assertions load-bearing
|
|
11
|
+
* instead of self-referential.
|
|
12
|
+
*
|
|
13
|
+
* Three deliberate shapes:
|
|
14
|
+
*
|
|
15
|
+
* 1. THE ROOT IS OPEN (`additionalProperties: true`). pi-lens's default is
|
|
16
|
+
* closed (`docs/public-api-stability.md`), and this is the one place that
|
|
17
|
+
* default is overridden on purpose. Both loaders already have their OWN,
|
|
18
|
+
* richer unknown-key diagnostics — the global loader names the typo, and the
|
|
19
|
+
* project loader distinguishes "typo" from "that is a global-only setting"
|
|
20
|
+
* (#533/#883). Letting `validate()` drop an unknown root key first would
|
|
21
|
+
* replace both messages with a generic `PILENS_CFG_0004`, which is strictly
|
|
22
|
+
* less information for the user. Closing the root belongs to the slice that
|
|
23
|
+
* moves those diagnostics into the schema, not to this one.
|
|
24
|
+
*
|
|
25
|
+
* 2. THE NAMESPACE LIST IS DERIVED, NEVER RESTATED. Every top-level section
|
|
26
|
+
* comes from `lens-flag-registry.ts`'s registries; adding a flag or a
|
|
27
|
+
* section needs no edit here. A hand-copied mirror of a registry is the
|
|
28
|
+
* defect AGENTS.md's single-source-of-truth rule names, and it is the exact
|
|
29
|
+
* thing #883 already removed from the two loaders' unknown-key scans.
|
|
30
|
+
*
|
|
31
|
+
* 3. ONLY `lsp` DECLARES A TYPE, and only one level deep. #2416 owns the
|
|
32
|
+
* `lsp.servers.<id>` shape; this slice RESERVES the namespace and moves the
|
|
33
|
+
* loader. Everything else stays schema-opaque, which `config-core` walks
|
|
34
|
+
* open — the node is still depth-bounded, prototype-key-checked and
|
|
35
|
+
* recorded, but no field's accepted values change in a slice whose
|
|
36
|
+
* acceptance criterion is that they do not.
|
|
37
|
+
*/
|
|
38
|
+
import { CONFIG_SCHEMA_ANCHOR_KEY, CONFIG_SCHEMA_ID, STABILITY_TIER_KEY, } from "./config-diagnostic-codes.js";
|
|
39
|
+
import { LEGACY_ROOT_LSP_KEYS, LSP_NAMESPACE_KEY } from "./config-locations.js";
|
|
40
|
+
// From the owning module, not the barrel — same reason as `config-resolve.ts`:
|
|
41
|
+
// the barrel's width would drag `process-spec.js` -> `project-trust.js` into
|
|
42
|
+
// every config loader's import graph for one type alias.
|
|
43
|
+
import { DENY_KEY, } from "./config-core/schema.js";
|
|
44
|
+
import { flagConfigSectionKeys, GLOBAL_NON_FLAG_CONFIG_SECTIONS, LENS_FLAGS, PROJECT_FOREIGN_CONFIG_NAMESPACES, PROJECT_NON_FLAG_CONFIG_SECTIONS, } from "./lens-flag-registry.js";
|
|
45
|
+
/** The JSON Schema dialect the published artifact declares. */
|
|
46
|
+
const CONFIG_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
|
|
47
|
+
/**
|
|
48
|
+
* A node the schema says nothing else about. `config-core` treats a node with
|
|
49
|
+
* no `type` as opaque and walks its children OPEN, so an undeclared section
|
|
50
|
+
* keeps exactly the values it kept before this schema existed.
|
|
51
|
+
*/
|
|
52
|
+
function opaque(tier) {
|
|
53
|
+
return { [STABILITY_TIER_KEY]: tier };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The `lsp` namespace: reserved here, shaped by #2416.
|
|
57
|
+
*
|
|
58
|
+
* `enabled` is the pre-existing `--no-lsp` toggle and keeps living here, which
|
|
59
|
+
* is why `lsp` is a NAMESPACE rather than a new sibling of the toggle: a config
|
|
60
|
+
* key that already means "is the LSP on" and a key that means "which servers"
|
|
61
|
+
* belong under one heading, and splitting them would have been a second name
|
|
62
|
+
* for the same subsystem.
|
|
63
|
+
*
|
|
64
|
+
* The four catalog keys are `experimental` on purpose. They are the canonical
|
|
65
|
+
* home of the four legacy ROOT keys deprecated by this PR, and #2416 replaces
|
|
66
|
+
* the bare-command server form with the trust-gated `ProcessSpec` — promising
|
|
67
|
+
* stability on a shape that is already scheduled to change would be a promise
|
|
68
|
+
* made only to be broken.
|
|
69
|
+
*/
|
|
70
|
+
/**
|
|
71
|
+
* The JSON type each canonical `lsp.*` key carries. Spelled out rather than
|
|
72
|
+
* inferred from the key's name: this is the ONE piece of validation the slice
|
|
73
|
+
* adds, and it earns its place — pre-#2426 a `disabledServers` that was a
|
|
74
|
+
* STRING reached `new Set(config.disabledServers)` and disabled one server per
|
|
75
|
+
* CHARACTER. `tests/config/pi-lens-config-schema.test.ts` asserts every
|
|
76
|
+
* `LEGACY_ROOT_LSP_KEYS` member has an entry here, so a new registry row cannot
|
|
77
|
+
* land untyped.
|
|
78
|
+
*/
|
|
79
|
+
const LSP_KEY_TYPES = {
|
|
80
|
+
servers: "object",
|
|
81
|
+
serverOverrides: "object",
|
|
82
|
+
disabledServers: "array",
|
|
83
|
+
warmFiles: "array",
|
|
84
|
+
};
|
|
85
|
+
export { LSP_KEY_TYPES };
|
|
86
|
+
/**
|
|
87
|
+
* The canonical `lsp.*` keys that carry a DENIAL rather than an ordinary value
|
|
88
|
+
* (#2427).
|
|
89
|
+
*
|
|
90
|
+
* `config-core/deny.ts` has shipped monotonic deny precedence since #2440 and
|
|
91
|
+
* `merge()` has consulted it since — but it only fires for a node the SCHEMA
|
|
92
|
+
* annotates, and no production node did. The consequence was live and
|
|
93
|
+
* measurable: with `~/.pi-lens/config.json` saying
|
|
94
|
+
* `lsp.disabledServers: ["typos"]` and a repository's `.pi-lens.json` saying
|
|
95
|
+
* `lsp.disabledServers: []`, the resolution returned `[]` attributed to the
|
|
96
|
+
* `project` tier and `loadLSPConfig` handed `initLSPConfig` an empty disable
|
|
97
|
+
* set — repository content silently re-enabling a server the operator turned
|
|
98
|
+
* off, which is the exact scenario `deny.ts`'s module doc says it exists to
|
|
99
|
+
* prevent and #2415 AC 3 forbids. One annotation is the whole fix: the union
|
|
100
|
+
* of every tier's members, attributed to the tier that first denied.
|
|
101
|
+
*
|
|
102
|
+
* `enabled` is deliberately NOT annotated here. `boolean-false` pins an
|
|
103
|
+
* operator denial against every higher tier including `cli`, and `env`/`cli`
|
|
104
|
+
* are still unpopulated (`docs/configuration.md`), so annotating it now would
|
|
105
|
+
* decide what `--lsp` means against a global `lsp.enabled: false` in a slice
|
|
106
|
+
* that cannot test the tiers involved. That belongs to the slice that
|
|
107
|
+
* populates them.
|
|
108
|
+
*/
|
|
109
|
+
const LSP_KEY_DENY = {
|
|
110
|
+
disabledServers: "array-union",
|
|
111
|
+
};
|
|
112
|
+
/** The `x-deny` annotation for a key, or nothing when it carries no denial. */
|
|
113
|
+
function denyAnnotation(key) {
|
|
114
|
+
const policy = LSP_KEY_DENY[key];
|
|
115
|
+
return policy === undefined ? {} : { [DENY_KEY]: policy };
|
|
116
|
+
}
|
|
117
|
+
function lspNamespace() {
|
|
118
|
+
const properties = {
|
|
119
|
+
// Reserved but deliberately UNTYPED. `lens-config.ts` already rejects a
|
|
120
|
+
// non-boolean here with `lsp.enabled must be a boolean` — a message that
|
|
121
|
+
// names the key and the expected type — and it is the one every flag in
|
|
122
|
+
// the registry shares. Declaring `type: "boolean"` would make `validate()`
|
|
123
|
+
// drop the value first and replace that message with the core's generic
|
|
124
|
+
// `expected boolean, got string`, which is strictly less useful. The flag
|
|
125
|
+
// sections move into the schema when their diagnostics do, not before.
|
|
126
|
+
enabled: { [STABILITY_TIER_KEY]: "stable" },
|
|
127
|
+
};
|
|
128
|
+
for (const key of LEGACY_ROOT_LSP_KEYS) {
|
|
129
|
+
const declared = LSP_KEY_TYPES[key];
|
|
130
|
+
properties[key] = {
|
|
131
|
+
...(declared ? { type: declared } : {}),
|
|
132
|
+
[STABILITY_TIER_KEY]: "experimental",
|
|
133
|
+
...(declared === "object" ? { additionalProperties: true } : {}),
|
|
134
|
+
...denyAnnotation(key),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
type: "object",
|
|
139
|
+
additionalProperties: true,
|
|
140
|
+
[STABILITY_TIER_KEY]: "stable",
|
|
141
|
+
properties,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function buildConfigSchema() {
|
|
145
|
+
const properties = {};
|
|
146
|
+
// Every top-level section pi-lens recognizes today, derived from the
|
|
147
|
+
// registries rather than listed. These have shipped and are documented, so
|
|
148
|
+
// they are `stable`; declaring long-published fields experimental would
|
|
149
|
+
// understate a guarantee users already rely on.
|
|
150
|
+
for (const key of [
|
|
151
|
+
...flagConfigSectionKeys(LENS_FLAGS),
|
|
152
|
+
...GLOBAL_NON_FLAG_CONFIG_SECTIONS,
|
|
153
|
+
...PROJECT_NON_FLAG_CONFIG_SECTIONS,
|
|
154
|
+
]) {
|
|
155
|
+
properties[key] = opaque("stable");
|
|
156
|
+
}
|
|
157
|
+
// Namespaces owned by another tool that ride in the same file (`trivy`,
|
|
158
|
+
// `helm`). Reserved so they survive validation, `experimental` because their
|
|
159
|
+
// shape belongs to those runners, not to this schema.
|
|
160
|
+
for (const key of PROJECT_FOREIGN_CONFIG_NAMESPACES) {
|
|
161
|
+
properties[key] ??= opaque("experimental");
|
|
162
|
+
}
|
|
163
|
+
for (const key of [
|
|
164
|
+
"knip",
|
|
165
|
+
"jscpd",
|
|
166
|
+
"madge",
|
|
167
|
+
"gitleaks",
|
|
168
|
+
"govulncheck",
|
|
169
|
+
"deadCode",
|
|
170
|
+
"complexity",
|
|
171
|
+
]) {
|
|
172
|
+
properties[key] = {
|
|
173
|
+
type: "object",
|
|
174
|
+
additionalProperties: true,
|
|
175
|
+
properties: {
|
|
176
|
+
enabled: { type: "boolean", [STABILITY_TIER_KEY]: "experimental" },
|
|
177
|
+
},
|
|
178
|
+
[STABILITY_TIER_KEY]: "stable",
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
properties.startup = {
|
|
182
|
+
type: "object",
|
|
183
|
+
additionalProperties: true,
|
|
184
|
+
properties: {
|
|
185
|
+
mode: {
|
|
186
|
+
type: "string",
|
|
187
|
+
enum: ["quick", "full", "minimal"],
|
|
188
|
+
[STABILITY_TIER_KEY]: "experimental",
|
|
189
|
+
},
|
|
190
|
+
scans: {
|
|
191
|
+
type: "object",
|
|
192
|
+
additionalProperties: true,
|
|
193
|
+
properties: {
|
|
194
|
+
enabled: { type: "boolean", [STABILITY_TIER_KEY]: "experimental" },
|
|
195
|
+
},
|
|
196
|
+
[STABILITY_TIER_KEY]: "experimental",
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
[STABILITY_TIER_KEY]: "stable",
|
|
200
|
+
};
|
|
201
|
+
// The four legacy ROOT LSP keys. Still accepted for their deprecation window
|
|
202
|
+
// (#2418 registry), and `experimental` because they are scheduled for
|
|
203
|
+
// removal — a `stable` tier on a key with a `removeNotBefore` would be two
|
|
204
|
+
// registries contradicting each other.
|
|
205
|
+
//
|
|
206
|
+
// NO deny annotation, unlike round 1 of #2427 (review round 2, F1). Round 1
|
|
207
|
+
// annotated BOTH spellings and got two independent deny unions for one
|
|
208
|
+
// setting, neither seeing the other tiers. Both resolution entry points now
|
|
209
|
+
// normalize a document legacy root LSP keys into the `lsp` namespace before
|
|
210
|
+
// merging, so this node never receives a contribution at all and a policy on
|
|
211
|
+
// it could not fire. The denial is resolved once, at the canonical node,
|
|
212
|
+
// over every tier and every spelling — which is what the second annotation
|
|
213
|
+
// was trying to achieve and structurally could not.
|
|
214
|
+
for (const key of LEGACY_ROOT_LSP_KEYS) {
|
|
215
|
+
properties[key] = opaque("experimental");
|
|
216
|
+
}
|
|
217
|
+
properties[LSP_NAMESPACE_KEY] = lspNamespace();
|
|
218
|
+
properties[CONFIG_SCHEMA_ANCHOR_KEY] = {
|
|
219
|
+
type: "string",
|
|
220
|
+
description: "Identity anchor naming the schema version this config was written against.",
|
|
221
|
+
[STABILITY_TIER_KEY]: "stable",
|
|
222
|
+
};
|
|
223
|
+
return {
|
|
224
|
+
$schema: CONFIG_SCHEMA_DIALECT,
|
|
225
|
+
$id: CONFIG_SCHEMA_ID,
|
|
226
|
+
type: "object",
|
|
227
|
+
// See shape 1 in the module doc: deliberately open, so the loaders keep
|
|
228
|
+
// their own richer unknown-key diagnostics.
|
|
229
|
+
additionalProperties: true,
|
|
230
|
+
properties,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/** The canonical schema both canonical files are validated against. */
|
|
234
|
+
export const PI_LENS_CONFIG_SCHEMA = buildConfigSchema();
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE seam for "a config the user wrote is being ignored" (#2418).
|
|
3
|
+
*
|
|
4
|
+
* Three loaders — `clients/lsp/config.ts`, `clients/lens-config.ts`,
|
|
5
|
+
* `clients/project-lens-config.ts` — each degraded a malformed config to
|
|
6
|
+
* defaults and warned about it, with three near-identical bodies: a warn-once
|
|
7
|
+
* latch, a `logExtension` line, and a `notifyUserDegradation` call. Three
|
|
8
|
+
* copies meant three places to forget the stable diagnostic code, and three
|
|
9
|
+
* places for the message shape to drift. They now share this helper, which
|
|
10
|
+
* owns all four obligations:
|
|
11
|
+
*
|
|
12
|
+
* 1. the warn-once latch, keyed on (subsystem, file, key, reason) so a repeat
|
|
13
|
+
* load of the same broken file does not re-nag the user;
|
|
14
|
+
* 2. the MACHINE-audience `extension.log` line;
|
|
15
|
+
* 3. the durable degradation-ledger row, through the repo's existing
|
|
16
|
+
* `recordDegradationOnce` choke point rather than a parallel tally;
|
|
17
|
+
* 4. the HUMAN-audience notification, carrying the stable `PILENS_CFG_*` code
|
|
18
|
+
* that `docs/public-api-stability.md` makes the user's match key.
|
|
19
|
+
*
|
|
20
|
+
* The message prose is assembled here from a per-subsystem label so every
|
|
21
|
+
* caller's rendered string is byte-identical to what it emitted before the
|
|
22
|
+
* extraction — the prose is not API (the code is), but silently rewording a
|
|
23
|
+
* warning inside a refactor is still not a thing this PR does.
|
|
24
|
+
*/
|
|
25
|
+
import { DEPRECATED_CONFIG_SURFACES, } from "./config-diagnostic-codes.js";
|
|
26
|
+
import { recordDegradationOnce } from "./degradation-ledger.js";
|
|
27
|
+
import { errorClassName } from "./error-class.js";
|
|
28
|
+
import { logExtension } from "./extension-log.js";
|
|
29
|
+
import { redactSecrets } from "./redact/secrets.js";
|
|
30
|
+
import { notifyUserDegradation } from "./user-notify.js";
|
|
31
|
+
/**
|
|
32
|
+
* The noun each subsystem uses for the config it is ignoring. These strings
|
|
33
|
+
* are load-bearing for byte-identical prose: `ignoring invalid LSP config …`,
|
|
34
|
+
* `… global config …`, `… project config …` are what shipped before #2418
|
|
35
|
+
* extracted this helper, and `tests/clients/lsp/config.test.ts` +
|
|
36
|
+
* `tests/clients/project-lens-config.test.ts` still assert on them.
|
|
37
|
+
*/
|
|
38
|
+
const SUBSYSTEM_CONFIG_LABEL = {
|
|
39
|
+
"lsp-config": "LSP config",
|
|
40
|
+
"lens-config": "global config",
|
|
41
|
+
"project-lens-config": "project config",
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The code every ignored-config warning carries: PILENS_CFG_0001, "config file
|
|
45
|
+
* unreadable or unparsable; ignored". Named here rather than repeated at three
|
|
46
|
+
* call sites so the registry entry has exactly one referent in production code.
|
|
47
|
+
*/
|
|
48
|
+
const IGNORED_CONFIG_CODE = "PILENS_CFG_0001";
|
|
49
|
+
/**
|
|
50
|
+
* The degradation kind this helper records under. One kind for all three
|
|
51
|
+
* loaders: the subject discriminates WHICH file, and `metadata.subsystem`
|
|
52
|
+
* discriminates which loader, so aggregation answers both without a kind
|
|
53
|
+
* explosion.
|
|
54
|
+
*/
|
|
55
|
+
const IGNORED_CONFIG_KIND = "config-ignored";
|
|
56
|
+
/**
|
|
57
|
+
* The degradation kind a DEPRECATION notice records under (#2426).
|
|
58
|
+
*
|
|
59
|
+
* Kept apart from `config-ignored` because the two are opposite facts about a
|
|
60
|
+
* session: an ignored config means pi-lens ran on defaults instead of what the
|
|
61
|
+
* user asked for, while a deprecated one means it ran on exactly what the user
|
|
62
|
+
* asked for and the location will stop working. Folding them together would
|
|
63
|
+
* have made "how many sessions ran degraded" un-answerable from the ledger.
|
|
64
|
+
*/
|
|
65
|
+
const DEPRECATED_CONFIG_KIND = "config-deprecated";
|
|
66
|
+
/**
|
|
67
|
+
* The code that says a notice LIST was truncated (#2426 review round 6, F2).
|
|
68
|
+
*
|
|
69
|
+
* It is the one code on this seam that describes the OUTPUT rather than the
|
|
70
|
+
* config, so it is the one that must not inherit either default below.
|
|
71
|
+
*/
|
|
72
|
+
const SUPPRESSED_NOTICE_CODE = "PILENS_CFG_0007";
|
|
73
|
+
/**
|
|
74
|
+
* The degradation kind a SUPPRESSED-NOTICE summary records under (#2426 review
|
|
75
|
+
* round 6, F2).
|
|
76
|
+
*
|
|
77
|
+
* Neither of the other two: nothing was ignored and nothing is deprecated —
|
|
78
|
+
* a legacy file whose 28 settings ALL applied still overflows the bound, and
|
|
79
|
+
* before this it produced a `config-ignored` row and an `ignoring invalid
|
|
80
|
+
* project config` notice for a file with nothing invalid in it. That is the
|
|
81
|
+
* same inversion `DEPRECATED_CONFIG_KIND` exists to prevent, arriving through
|
|
82
|
+
* the default branch instead of through a wrong one.
|
|
83
|
+
*/
|
|
84
|
+
const SUPPRESSED_NOTICE_KIND = "config-notice-suppressed";
|
|
85
|
+
/**
|
|
86
|
+
* The codes that mean "accepted, but deprecated". DERIVED from the deprecation
|
|
87
|
+
* registry rather than listed, so a future `kind` of deprecated surface with a
|
|
88
|
+
* new code cannot render as `ignoring invalid …` by omission.
|
|
89
|
+
*/
|
|
90
|
+
const DEPRECATION_NOUN_BY_CODE = new Map(DEPRECATED_CONFIG_SURFACES.map((row) => [
|
|
91
|
+
row.code,
|
|
92
|
+
row.kind === "file" ? "location" : "key",
|
|
93
|
+
]));
|
|
94
|
+
/** Warn-once latch, keyed on (subsystem, file, key, reason). */
|
|
95
|
+
const warnedIgnoredConfigs = new Set();
|
|
96
|
+
/**
|
|
97
|
+
* The one shape `JSON.parse`'s own `SyntaxError#message` states a position
|
|
98
|
+
* in, on every supported Node (CI pins Node 22; V8 has emitted `at position N
|
|
99
|
+
* (line L column C)` for this shape since ~Node 20). Captures the absolute
|
|
100
|
+
* offset too (group 1), not just V8's own stated line/col (groups 2, 3):
|
|
101
|
+
* with source text in hand, line/col is recomputed by scanning the source
|
|
102
|
+
* ourselves rather than trusted from the message (#2451) — the offset is
|
|
103
|
+
* still just a digit, kept only as the pre-#2451 fallback for a caller that
|
|
104
|
+
* has no source text to scan.
|
|
105
|
+
*
|
|
106
|
+
* V8 also emits a position-free shape for a token error — `Unexpected token
|
|
107
|
+
* 'x', "<snippet>"... is not valid JSON` — which carries no position at all,
|
|
108
|
+
* only a slice of the source text being parsed. That is the exact shape
|
|
109
|
+
* #2431's evidence hit (`ghp_SECRET` sitting in the snippet on Node 24), and
|
|
110
|
+
* `locateSyntaxErrorOffset` below locates it in the source instead.
|
|
111
|
+
*/
|
|
112
|
+
const JSON_PARSE_POSITION = /at position (\d+) \(line (\d+) column (\d+)\)/;
|
|
113
|
+
/**
|
|
114
|
+
* True for `JSON.parse`'s own `SyntaxError`, including one thrown across a
|
|
115
|
+
* realm boundary (e.g. `vm.runInContext`), where BOTH `instanceof SyntaxError`
|
|
116
|
+
* and `instanceof Error` fail — a realm-bound object's prototype chain
|
|
117
|
+
* resolves through that OTHER realm's `Error.prototype`/`Object.prototype`,
|
|
118
|
+
* never this one's. Duck-typed on `name` and `message` instead, which are
|
|
119
|
+
* plain string own/inherited properties readable regardless of which realm's
|
|
120
|
+
* prototype chain the object carries.
|
|
121
|
+
*/
|
|
122
|
+
function isSyntaxError(error) {
|
|
123
|
+
return (typeof error === "object" &&
|
|
124
|
+
error !== null &&
|
|
125
|
+
error.name === "SyntaxError" &&
|
|
126
|
+
typeof error.message === "string");
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Line (1-based) and column (1-based, V8's own convention) of a character
|
|
130
|
+
* offset, scanned directly from the source — never trusted from anything the
|
|
131
|
+
* message states (#2451). `offset` is always in `[0, sourceText.length]`: it
|
|
132
|
+
* comes from either V8's own `at position N` digit (bounded by construction —
|
|
133
|
+
* V8 cannot state a position past the text it just parsed) or a snippet
|
|
134
|
+
* `indexOf` match inside `sourceText` itself, so there is no out-of-range
|
|
135
|
+
* case for this function to guard against.
|
|
136
|
+
*/
|
|
137
|
+
function lineColAt(sourceText, offset) {
|
|
138
|
+
let line = 1;
|
|
139
|
+
let lastNewline = -1;
|
|
140
|
+
for (let i = 0; i < offset; i++) {
|
|
141
|
+
if (sourceText[i] === "\n") {
|
|
142
|
+
line++;
|
|
143
|
+
lastNewline = i;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { line, col: offset - lastNewline };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Split V8's position-free `Unexpected token` message into the offending
|
|
150
|
+
* token and the (possibly truncated) source snippet around it.
|
|
151
|
+
*
|
|
152
|
+
* NOT regex-quote-matched: the snippet is a raw slice of the user's source
|
|
153
|
+
* text and can itself contain `"`, which a `/"([\s\S]*?)"/`-shaped pattern
|
|
154
|
+
* would stop at prematurely (`{"a": ghp_SECRET` has one right after the
|
|
155
|
+
* opening `{`). Fixed prefix/suffix stripping instead, which is exact
|
|
156
|
+
* regardless of what the snippet contains.
|
|
157
|
+
*/
|
|
158
|
+
function parseUnexpectedTokenMessage(message) {
|
|
159
|
+
const prefix = "Unexpected token '";
|
|
160
|
+
if (!message.startsWith(prefix))
|
|
161
|
+
return undefined;
|
|
162
|
+
const closeIdx = message.indexOf("', ", prefix.length);
|
|
163
|
+
// No explicit `closeIdx === -1` branch: when the marker is missing,
|
|
164
|
+
// `closeIdx` is `-1`, so `rest` below starts at `message[2]` — always
|
|
165
|
+
// still inside the fixed `prefix` text the `startsWith` check just
|
|
166
|
+
// confirmed, never `"`. The `rest.startsWith('"')` check a few lines down
|
|
167
|
+
// already rejects that case; a second explicit check here could never be
|
|
168
|
+
// exercised by any input that passes the first, so it stayed untestable
|
|
169
|
+
// dead code rather than a guard (#2451 mutation pass).
|
|
170
|
+
const token = message.slice(prefix.length, closeIdx);
|
|
171
|
+
let rest = message.slice(closeIdx + 3);
|
|
172
|
+
if (rest.startsWith("..."))
|
|
173
|
+
rest = rest.slice(3);
|
|
174
|
+
if (!rest.startsWith('"'))
|
|
175
|
+
return undefined;
|
|
176
|
+
rest = rest.slice(1);
|
|
177
|
+
const truncatedSuffix = '"... is not valid JSON';
|
|
178
|
+
const plainSuffix = '" is not valid JSON';
|
|
179
|
+
const snippet = rest.endsWith(truncatedSuffix)
|
|
180
|
+
? rest.slice(0, -truncatedSuffix.length)
|
|
181
|
+
: rest.endsWith(plainSuffix)
|
|
182
|
+
? rest.slice(0, -plainSuffix.length)
|
|
183
|
+
: undefined;
|
|
184
|
+
// No explicit `snippet.length === 0` branch: `String#indexOf("")` matches
|
|
185
|
+
// at EVERY position, so an empty snippet always fails the caller's
|
|
186
|
+
// uniqueness check (`sourceText.indexOf(snippet, snippetAt + 1) !== -1`
|
|
187
|
+
// is true at every offset, including on an empty `sourceText`) — a second
|
|
188
|
+
// explicit check here could never be exercised by any input the first
|
|
189
|
+
// does not already catch (#2451 mutation pass).
|
|
190
|
+
return snippet === undefined ? undefined : { token, snippet };
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Locate a `JSON.parse` `SyntaxError`'s offset in ITS OWN source text
|
|
194
|
+
* (#2451). Two shapes, both anchored to the source rather than to anything
|
|
195
|
+
* V8 states about it:
|
|
196
|
+
*
|
|
197
|
+
* - `at position N`: N is already an exact offset.
|
|
198
|
+
* - `Unexpected token 'x', "<snippet>"...`: no offset in the message at all,
|
|
199
|
+
* only a slice of the source. `snippet` is searched for in `sourceText`
|
|
200
|
+
* with `indexOf`; a match that is not unique is not trusted (the wrong
|
|
201
|
+
* occurrence would report a false location, and a `config-ignored` reason
|
|
202
|
+
* is not worth that risk). Inside a unique match, the offending token's
|
|
203
|
+
* own OFFSET is used when it too occurs exactly once in the snippet — the
|
|
204
|
+
* common case (JSON punctuation, a typo'd letter) — otherwise the snippet's
|
|
205
|
+
* own start stands in: still the correct line far more often than not
|
|
206
|
+
* (the snippet is a ~20-char window around the real offset), never a
|
|
207
|
+
* snippet the user did not already put in their own file.
|
|
208
|
+
*
|
|
209
|
+
* Returns `undefined` when neither shape locates unambiguously — the honest,
|
|
210
|
+
* safe answer, which the caller degrades to the bare class name for.
|
|
211
|
+
*/
|
|
212
|
+
function locateSyntaxErrorOffset(message, sourceText) {
|
|
213
|
+
const stated = JSON_PARSE_POSITION.exec(message);
|
|
214
|
+
if (stated)
|
|
215
|
+
return Number(stated[1]);
|
|
216
|
+
const parsed = parseUnexpectedTokenMessage(message);
|
|
217
|
+
if (!parsed)
|
|
218
|
+
return undefined;
|
|
219
|
+
const { token, snippet } = parsed;
|
|
220
|
+
const snippetAt = sourceText.indexOf(snippet);
|
|
221
|
+
if (snippetAt === -1 || sourceText.indexOf(snippet, snippetAt + 1) !== -1) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
const tokenAt = snippet.indexOf(token);
|
|
225
|
+
const onlyTokenOccurrence = tokenAt !== -1 && snippet.indexOf(token, tokenAt + 1) === -1;
|
|
226
|
+
return snippetAt + (onlyTokenOccurrence ? tokenAt : 0);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Normalize a caught parse/validation error into a bounded, snippet-free
|
|
230
|
+
* reason: the error's own class plus a position, when one can be derived —
|
|
231
|
+
* NEVER the raw message (#2431), and never anything but the class name at
|
|
232
|
+
* all for a `classOnly` caller (#2451).
|
|
233
|
+
*
|
|
234
|
+
* `JSON.parse`'s `SyntaxError#message` is DOCUMENTED (V8) to embed a slice of
|
|
235
|
+
* the source text being parsed. A user's malformed config that happens to
|
|
236
|
+
* carry a credential next to the syntax error would otherwise leak it into
|
|
237
|
+
* `extension.log`, the user-facing notification, and the durable
|
|
238
|
+
* degradation-ledger row through this one seam — so a `SyntaxError` NEVER
|
|
239
|
+
* keeps its message, position-derived or not.
|
|
240
|
+
*
|
|
241
|
+
* Every other caught error reaching this helper (a loader's own hand-thrown
|
|
242
|
+
* validation error, an `fs` error) is not documented to embed file content,
|
|
243
|
+
* and is additionally routed through the repo's own secret scanner
|
|
244
|
+
* (`redactSecrets`) — but that scanner is a floor, not a guarantee: it only
|
|
245
|
+
* recognizes secrets shaped like its registered patterns and long enough to
|
|
246
|
+
* clear each pattern's `minSuffixLength` (16-40 chars), so a short truncated
|
|
247
|
+
* fragment of a real credential can still pass through untouched. It is
|
|
248
|
+
* still worth calling on this branch because most non-`SyntaxError` messages
|
|
249
|
+
* are hand-authored by this codebase, not engine dumps of file content — but
|
|
250
|
+
* `normalizeParseErrorReason`'s own guarantee (no message survives at all)
|
|
251
|
+
* applies only to the `SyntaxError` branch (and, unconditionally, to a
|
|
252
|
+
* `classOnly` caller). `warnIgnoredConfigOnce` (the one `{ parseError }`
|
|
253
|
+
* caller) additionally redacts EVERY reason, including hand-authored ones,
|
|
254
|
+
* so a caller-composed string that happens to interpolate file content (a
|
|
255
|
+
* user-authored config KEY, a rule id) is still covered.
|
|
256
|
+
*/
|
|
257
|
+
export function normalizeParseErrorReason(error, options = {}) {
|
|
258
|
+
if (options.classOnly) {
|
|
259
|
+
return errorClassName(error);
|
|
260
|
+
}
|
|
261
|
+
if (isSyntaxError(error)) {
|
|
262
|
+
if (options.sourceText !== undefined) {
|
|
263
|
+
const offset = locateSyntaxErrorOffset(error.message, options.sourceText);
|
|
264
|
+
if (offset !== undefined) {
|
|
265
|
+
const { line, col } = lineColAt(options.sourceText, offset);
|
|
266
|
+
return `${error.name} at line ${line} col ${col}`;
|
|
267
|
+
}
|
|
268
|
+
return error.name;
|
|
269
|
+
}
|
|
270
|
+
const match = JSON_PARSE_POSITION.exec(error.message);
|
|
271
|
+
return match
|
|
272
|
+
? `${error.name} at line ${match[2]} col ${match[3]}`
|
|
273
|
+
: error.name;
|
|
274
|
+
}
|
|
275
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
276
|
+
return redactSecrets(message);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Which durable kind a notice records under. Three facts, three kinds, decided
|
|
280
|
+
* in ONE place so a fourth code cannot inherit "ignored" by falling through.
|
|
281
|
+
*/
|
|
282
|
+
function degradationKindFor(suppressedNotice, deprecationNoun) {
|
|
283
|
+
if (suppressedNotice)
|
|
284
|
+
return SUPPRESSED_NOTICE_KIND;
|
|
285
|
+
return deprecationNoun ? DEPRECATED_CONFIG_KIND : IGNORED_CONFIG_KIND;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* The rendered prose for one notice.
|
|
289
|
+
*
|
|
290
|
+
* The two pre-existing shapes are BYTE-IDENTICAL to what shipped — several
|
|
291
|
+
* suites assert on them — and the third is deliberately built from neither
|
|
292
|
+
* vocabulary: a truncation summary describes the notice list, never the
|
|
293
|
+
* config, so it says nothing about the file being invalid or ignored.
|
|
294
|
+
*/
|
|
295
|
+
function noticeMessage(options) {
|
|
296
|
+
const label = SUBSYSTEM_CONFIG_LABEL[options.subsystem];
|
|
297
|
+
if (options.suppressedNotice) {
|
|
298
|
+
return `${label} notices for ${options.file} were summarised: ${options.reason}`;
|
|
299
|
+
}
|
|
300
|
+
return options.deprecationNoun
|
|
301
|
+
? `deprecated ${label} ${options.deprecationNoun} in ${options.file}: ${options.reason}`
|
|
302
|
+
: `ignoring invalid ${label} ${options.file}: ${options.reason}`;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Warn once that a config file (or one key in it) is being ignored.
|
|
306
|
+
*
|
|
307
|
+
* Fires the durable ledger row, the machine log, and the user-facing
|
|
308
|
+
* notification. Every one of the three is bounded, but on two DIFFERENT
|
|
309
|
+
* lifetimes: the latch here bounds the log line and the notification for the
|
|
310
|
+
* life of the PROCESS, while `recordDegradationOnce` bounds the ledger row on
|
|
311
|
+
* (kind, subject) for the life of the SESSION. That is why the ledger call sits
|
|
312
|
+
* in front of the latch's early return — see the comment on it. The ledger
|
|
313
|
+
* bound is also coarser, so a file failing for two different reasons warns
|
|
314
|
+
* twice but is counted as one degraded config.
|
|
315
|
+
*/
|
|
316
|
+
export function warnIgnoredConfigOnce(options) {
|
|
317
|
+
const { subsystem, file, key } = options;
|
|
318
|
+
const code = options.code ?? IGNORED_CONFIG_CODE;
|
|
319
|
+
// The ONLY place a caught parse/read error becomes a string (#2431). A
|
|
320
|
+
// `{ parseError }` is normalized to error class + position, never the raw
|
|
321
|
+
// message. A hand-authored reason is still `redactSecrets`-scrubbed here —
|
|
322
|
+
// review round 2, F1: several callers interpolate a user-authored KEY or
|
|
323
|
+
// rule id straight from the parsed config (`unknown key "${key}" is not a
|
|
324
|
+
// recognized pi-lens setting`), so "hand-authored" does not mean
|
|
325
|
+
// "content-free"; the ONE seam every caller shares is where that gets
|
|
326
|
+
// caught, not each of the ~8 call sites that format one of these strings.
|
|
327
|
+
const reason = redactSecrets(typeof options.reason === "string"
|
|
328
|
+
? options.reason
|
|
329
|
+
: normalizeParseErrorReason(options.reason.parseError, {
|
|
330
|
+
sourceText: options.reason.sourceText,
|
|
331
|
+
}));
|
|
332
|
+
// The durable half (#2418 F6), and it runs BEFORE the latch on purpose
|
|
333
|
+
// (#2418 review round 3, F1). The latch is a PROCESS-lifetime Set; the
|
|
334
|
+
// ledger is per SESSION, reset by `resetDegradationLedger()` at the top of
|
|
335
|
+
// `handleSessionStart`. With the early return in front of this call, every
|
|
336
|
+
// session after the first recorded nothing while the config on disk was
|
|
337
|
+
// still ignored — the exact catalog-shape-17 defect
|
|
338
|
+
// `refreshGrammarSessionLatches` exists to prevent in tree-sitter-client.
|
|
339
|
+
//
|
|
340
|
+
// No second, generation-compared latch is needed to re-arm it, unlike that
|
|
341
|
+
// precedent: `recordDegradationOnce` already dedupes on (kind, subject)
|
|
342
|
+
// through its own once-key set, and that set IS cleared by the ledger
|
|
343
|
+
// reset. Calling it unconditionally makes the ledger the single source of
|
|
344
|
+
// truth for "once per session", instead of a parallel Set here that mirrors
|
|
345
|
+
// it and has to be kept in step. (tree-sitter needs its own gates because
|
|
346
|
+
// they guard `incrementDegradationCount`, which counts EVERY call, plus
|
|
347
|
+
// non-ledger state.)
|
|
348
|
+
//
|
|
349
|
+
// Subject is `<file>\0<key>` when a single key is rejected, and plain
|
|
350
|
+
// `<file>` otherwise, so a per-key rejection and a whole-file rejection are
|
|
351
|
+
// distinct rows for the same file without a trailing separator on every row.
|
|
352
|
+
const suppressedNotice = code === SUPPRESSED_NOTICE_CODE;
|
|
353
|
+
const deprecationNoun = suppressedNotice
|
|
354
|
+
? undefined
|
|
355
|
+
: DEPRECATION_NOUN_BY_CODE.get(code);
|
|
356
|
+
recordDegradationOnce({
|
|
357
|
+
kind: degradationKindFor(suppressedNotice, deprecationNoun),
|
|
358
|
+
subject: key ? `${file}\0${key}` : file,
|
|
359
|
+
reason,
|
|
360
|
+
metadata: { subsystem, configPath: file },
|
|
361
|
+
code,
|
|
362
|
+
});
|
|
363
|
+
const latchKey = `${subsystem}\0${file}\0${key ?? ""}\0${reason}`;
|
|
364
|
+
if (warnedIgnoredConfigs.has(latchKey))
|
|
365
|
+
return;
|
|
366
|
+
warnedIgnoredConfigs.add(latchKey);
|
|
367
|
+
// Three prose shapes for three different facts. A deprecation notice — or a
|
|
368
|
+
// truncation summary — that said "ignoring invalid …" would tell the user
|
|
369
|
+
// their setting is not being applied when it IS: the one thing #2426's
|
|
370
|
+
// "no user is broken silently" rule cannot afford to get backwards.
|
|
371
|
+
const message = noticeMessage({
|
|
372
|
+
subsystem,
|
|
373
|
+
file,
|
|
374
|
+
reason,
|
|
375
|
+
suppressedNotice,
|
|
376
|
+
deprecationNoun,
|
|
377
|
+
});
|
|
378
|
+
logExtension({
|
|
379
|
+
subsystem,
|
|
380
|
+
level: "warn",
|
|
381
|
+
message,
|
|
382
|
+
metadata: { configPath: file, reason, code, ...(key ? { key } : {}) },
|
|
383
|
+
});
|
|
384
|
+
// HUMAN-audience too: a config the user wrote is being ignored. Routed
|
|
385
|
+
// through the host's own render path (#1333), never a raw write. The stable
|
|
386
|
+
// code (#2418) is what a user matches or suppresses on; the prose may still
|
|
387
|
+
// change. Once per PROCESS, deliberately: re-nagging about the same broken
|
|
388
|
+
// file at every session boundary is noise, while the ledger row above is
|
|
389
|
+
// the per-session record.
|
|
390
|
+
notifyUserDegradation(`pi-lens: ${message}`, "warning", { code });
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Clear the warn-once latch. Test-only seam, and the one the loaders' own
|
|
394
|
+
* reset helpers delegate to — a subsystem argument clears just that loader's
|
|
395
|
+
* entries so one loader's test cannot silently un-latch another's.
|
|
396
|
+
*/
|
|
397
|
+
export function resetIgnoredConfigWarnCache(subsystem) {
|
|
398
|
+
if (!subsystem) {
|
|
399
|
+
warnedIgnoredConfigs.clear();
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
for (const latchKey of warnedIgnoredConfigs) {
|
|
403
|
+
if (latchKey.startsWith(`${subsystem}\0`)) {
|
|
404
|
+
warnedIgnoredConfigs.delete(latchKey);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|