@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
|
@@ -15,6 +15,7 @@ import { createSubsystemLogger } from "./extension-log.js";
|
|
|
15
15
|
import { incrementDegradationCount } from "./degradation-ledger.js";
|
|
16
16
|
import * as fs from "node:fs";
|
|
17
17
|
import * as path from "node:path";
|
|
18
|
+
import { findLocalBinsAt, VENV_BIN_DIRS } from "./package-manager.js";
|
|
18
19
|
import { findNearestMarkerRoot } from "./path-utils.js";
|
|
19
20
|
import { getScratchTreeFnmatchPatterns } from "./scratch-tree-policy.js";
|
|
20
21
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
@@ -171,22 +172,26 @@ export class PythonDeadCodeClient {
|
|
|
171
172
|
* whatever happens to be active in the CALLING shell, not the project's
|
|
172
173
|
* (#1731, discipline B — the same venv-first shape sqlfluff's binary
|
|
173
174
|
* resolution already uses, `runner-helpers.ts` `createVenvFinder`).
|
|
175
|
+
*
|
|
176
|
+
* The directory list is the shared `VENV_BIN_DIRS` — this was a third
|
|
177
|
+
* private spelling of the same four venv directories, with a
|
|
178
|
+
* `process.platform` branch that meant the ubuntu lane never saw the
|
|
179
|
+
* `Scripts` half at all (defect shape 30).
|
|
180
|
+
*
|
|
181
|
+
* The search is a fixed lookup at `root` ONLY (`findLocalBinsAt`, no
|
|
182
|
+
* ancestor walk) — #2544 review round 3 folded this onto the shared
|
|
183
|
+
* ancestor walker, reasoning a monorepo package should find its
|
|
184
|
+
* repo-root venv, but that was an unrequested behaviour change with no
|
|
185
|
+
* #2514 defect behind it. Reverted in round 4 F2. `findLocalBinsAt`
|
|
186
|
+
* returns EVERY match at `root` rather than the first, because
|
|
187
|
+
* `doEnsureAvailable` probes candidates in order and must keep the
|
|
188
|
+
* `venv/` fallback when a `.venv/` vulture exists but cannot run.
|
|
174
189
|
*/
|
|
175
190
|
venvCandidates(root) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
path.join("venv", "Scripts", "vulture.exe"),
|
|
181
|
-
]
|
|
182
|
-
: [
|
|
183
|
-
path.join(".venv", "bin", "vulture"),
|
|
184
|
-
path.join("venv", "bin", "vulture"),
|
|
185
|
-
];
|
|
186
|
-
return relPaths
|
|
187
|
-
.map((rel) => path.join(root, rel))
|
|
188
|
-
.filter((full) => fs.existsSync(full))
|
|
189
|
-
.map((full) => ({ cmd: full, prefix: [] }));
|
|
191
|
+
return findLocalBinsAt(["vulture"], root, {
|
|
192
|
+
windowsExt: ".exe",
|
|
193
|
+
binDirs: VENV_BIN_DIRS,
|
|
194
|
+
}).map((full) => ({ cmd: full, prefix: [] }));
|
|
190
195
|
}
|
|
191
196
|
async doEnsureAvailable(root) {
|
|
192
197
|
// Presence-gated, NOT auto-installed. vulture is a pure-Python package
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import { isTestMode } from "./env-utils.js";
|
|
10
|
-
import {
|
|
10
|
+
import { getGlobalPiLensLogDir } from "./probe-home-state.js";
|
|
11
11
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
12
|
-
const LOG_DIR =
|
|
12
|
+
const LOG_DIR = getGlobalPiLensLogDir();
|
|
13
13
|
const LOG_FILE = path.join(LOG_DIR, "dead-code.log");
|
|
14
14
|
const LOG_BACKUP_FILE = path.join(LOG_DIR, "dead-code.log.1");
|
|
15
15
|
const MAX_LOG_BYTES = Math.max(128 * 1024, Number.parseInt(process.env.PI_LENS_DEAD_CODE_LOG_MAX_BYTES ?? "1048576", 10) || 1048576);
|
|
@@ -28,7 +28,3 @@ export function logDeadCodeScan(event) {
|
|
|
28
28
|
return;
|
|
29
29
|
writer.log({ ts: new Date().toISOString(), ...event });
|
|
30
30
|
}
|
|
31
|
-
/** Resolve once all enqueued dead-code writes are on disk (tests/shutdown). */
|
|
32
|
-
export function flushDeadCodeLog() {
|
|
33
|
-
return writer.flush();
|
|
34
|
-
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* promise's late rejection (an unhandled rejection if the timer won first), and
|
|
11
11
|
* `withinRemaining` never cleared its timer.
|
|
12
12
|
*/
|
|
13
|
+
import { recordDegradationOnce } from "./degradation-ledger.js";
|
|
13
14
|
/**
|
|
14
15
|
* Combine multiple abort signals into one that aborts when ANY of them does.
|
|
15
16
|
* Returns the single signal unchanged when only one is live, and `undefined`
|
|
@@ -100,3 +101,180 @@ export function withinRemaining(promise, deadlineAt) {
|
|
|
100
101
|
onReject: "undefined",
|
|
101
102
|
});
|
|
102
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* The abort half of a bound, as a value the race can settle on. A symbol, not
|
|
106
|
+
* `undefined`, so `bounded` can tell "the signal fired" from "the awaited work
|
|
107
|
+
* genuinely resolved `undefined`" — the distinction AGENTS.md defect shape 10
|
|
108
|
+
* (silencing counted as fixing) exists to protect.
|
|
109
|
+
*/
|
|
110
|
+
const BOUND_ABORTED = Symbol("pi-lens/bounded/aborted");
|
|
111
|
+
/**
|
|
112
|
+
* Await `promise` under BOTH bounds a hook needs: a wall-clock budget AND the
|
|
113
|
+
* hook's own abort signal (#2523 AC2).
|
|
114
|
+
*
|
|
115
|
+
* ## Why the type refuses one bound
|
|
116
|
+
*
|
|
117
|
+
* Every bound in this codebase lived at a LEAF — a spawn timeout, an LSP wait
|
|
118
|
+
* — so a dependency that wedged before reaching the leaf was unreachable by
|
|
119
|
+
* all of them, and `wrapSessionEventHandler` adds neither. The two halves fail
|
|
120
|
+
* differently and neither substitutes for the other: a deadline alone means
|
|
121
|
+
* Escape cannot release the hook (measured: still blocked 30s after the abort
|
|
122
|
+
* fired), and a signal alone means a dependency that wedges without anyone
|
|
123
|
+
* pressing Escape never returns at all (measured: 400s, the harness ceiling).
|
|
124
|
+
* `ms` and `signal` are therefore both non-optional properties, so
|
|
125
|
+
* `bounded(p, { ms: 500, hook, label })` is a COMPILE error, not a review
|
|
126
|
+
* catch. `tests/clients/bounded-hook-await.test.ts` pins that with
|
|
127
|
+
* `@ts-expect-error`: weaken either property and those directives become
|
|
128
|
+
* unused, which `tsc` reports as TS2578.
|
|
129
|
+
*
|
|
130
|
+
* ## Semantics
|
|
131
|
+
*
|
|
132
|
+
* - Resolves to the promise's value when it settles inside both bounds.
|
|
133
|
+
* - Resolves to `undefined` when the budget elapses OR a signal aborts,
|
|
134
|
+
* whichever comes first — and firing a signal settles IMMEDIATELY, it
|
|
135
|
+
* never waits the remaining deadline out.
|
|
136
|
+
* - Rejections propagate: this helper decides how LONG to wait, never what an
|
|
137
|
+
* error means. Once a bound has fired, a later rejection from the
|
|
138
|
+
* superseded promise is suppressed (a no-op catch is attached to the work
|
|
139
|
+
* up front) so it cannot surface as an unhandled rejection.
|
|
140
|
+
*
|
|
141
|
+
* ## What reaches the ledger, and what deliberately does not
|
|
142
|
+
*
|
|
143
|
+
* Which bound fired decides this, via {@link BoundCause}. Only the DEADLINE
|
|
144
|
+
* records `hook-await-exceeded` — ONE rising-edge row per (hook, label) via
|
|
145
|
+
* `recordDegradationOnce`, naming the hook, the await, the budget, the actual
|
|
146
|
+
* elapsed ms and the cause. Bounded by construction: a hook that blows its
|
|
147
|
+
* budget on every turn of a long session writes one ledger row, not one per
|
|
148
|
+
* turn. Promote it to `incrementDegradationCount` if slice 2 finds the exact
|
|
149
|
+
* tally is needed.
|
|
150
|
+
*
|
|
151
|
+
* A `"caller-abort"` records NOTHING. The caller's signal is the user pressing
|
|
152
|
+
* Escape or a turn being cancelled — a deliberate action, not a degraded
|
|
153
|
+
* environment. `clients/bootstrap.ts` already carries this lesson at its
|
|
154
|
+
* `if (unavailableReason !== "aborted")` guard: writing a cancel to the ledger
|
|
155
|
+
* made it indistinguishable from an unhealthy analyzer graph in
|
|
156
|
+
* `pilens_health`. This helper is meant to become THE bound primitive at
|
|
157
|
+
* ~187 call sites, so recording a cancel here would ship that inversion
|
|
158
|
+
* everywhere at once.
|
|
159
|
+
*
|
|
160
|
+
* A `"shutdown"` records `hook-await-abandoned`, which is in
|
|
161
|
+
* `INFORMATIONAL_DEGRADATION_KINDS` — teardown abandoning in-flight work is
|
|
162
|
+
* the design, so it is a tally rather than a `⚠`.
|
|
163
|
+
*
|
|
164
|
+
* `undefined` therefore means "no answer inside the bound" and must never be
|
|
165
|
+
* read as an empty/clean ANSWER (defect shape 10, and the #240 lesson that a
|
|
166
|
+
* failed diagnostic pull is not a clean file). The caller decides what the
|
|
167
|
+
* absence means; the ledger row is what makes it visible either way.
|
|
168
|
+
*
|
|
169
|
+
* The promise is not cancelled — nothing here can cancel work already
|
|
170
|
+
* started. It is abandoned, with the abandonment recorded. Work that must not
|
|
171
|
+
* outlive its hook needs the signal threaded INTO it as well (#2523 slice 2's
|
|
172
|
+
* `TurnEndDeps`/`AgentEndDeps`/`SessionStartDeps` change); wrapping the await
|
|
173
|
+
* bounds the HOOK, not the work.
|
|
174
|
+
*/
|
|
175
|
+
export async function bounded(promise, options) {
|
|
176
|
+
const { signal, shutdownSignal, hook, label } = options;
|
|
177
|
+
// A non-finite budget settles immediately rather than throwing. The type
|
|
178
|
+
// forbids it, but a JS caller (or a `@ts-expect-error` probe) can still
|
|
179
|
+
// hand one over, and `setTimeout(fn, NaN)` would fire on the next tick
|
|
180
|
+
// with a warning — a helper whose whole job is keeping a hook from blowing
|
|
181
|
+
// up must not itself misbehave inside one. Same
|
|
182
|
+
// `Number.isFinite`-before-`Math.max` gate the runtime-config readers use.
|
|
183
|
+
const ms = Number.isFinite(options.ms) ? Math.max(0, options.ms) : 0;
|
|
184
|
+
const startedAt = Date.now();
|
|
185
|
+
// Late rejection from work a bound has already abandoned must not surface
|
|
186
|
+
// as an unhandled rejection. Attached to the BOXED promise, which is what
|
|
187
|
+
// the race below actually subscribes to; the caller still sees a rejection
|
|
188
|
+
// that arrives inside the bounds, because `Promise.race` attaches its own
|
|
189
|
+
// handler to the same promise.
|
|
190
|
+
const boxed = promise.then((value) => ({ value }));
|
|
191
|
+
void boxed.catch(() => { });
|
|
192
|
+
// ONE timer and ONE listener per source signal, all released in `finally`
|
|
193
|
+
// (#2530 review F2/F4).
|
|
194
|
+
//
|
|
195
|
+
// The round-1 implementation folded the wall clock in as
|
|
196
|
+
// `AbortSignal.timeout(ms)` and combined it through `AbortSignal.any`,
|
|
197
|
+
// then ALSO passed `ms` to `withDeadline` — two timers for one budget, and
|
|
198
|
+
// a composite signal per call. `AbortSignal.any` does not add a listener
|
|
199
|
+
// to its sources: it appends the composite to an internal dependant list
|
|
200
|
+
// on each of them, which lives as long as the SOURCE does. The hook signal
|
|
201
|
+
// outlives one await by a whole turn, so 20 000 successful `bounded()`
|
|
202
|
+
// calls on one hook signal left 20 000 entries on it, and the `finally`'s
|
|
203
|
+
// `removeEventListener` — aimed at the throwaway composite — removed
|
|
204
|
+
// nothing from the source. Listening on the sources directly is what makes
|
|
205
|
+
// the cleanup reach the thing that outlives the call.
|
|
206
|
+
let cause;
|
|
207
|
+
let timer;
|
|
208
|
+
let onCallerAbort;
|
|
209
|
+
let onShutdownAbort;
|
|
210
|
+
const abandoned = new Promise((resolve) => {
|
|
211
|
+
// `abandonedError`'s precedence, unchanged: the caller's own signal
|
|
212
|
+
// first, then teardown, then the clock. A turn cancelled while the
|
|
213
|
+
// session also happens to be tearing down is still a cancel.
|
|
214
|
+
const fire = (reason) => {
|
|
215
|
+
cause ??= reason;
|
|
216
|
+
resolve(BOUND_ABORTED);
|
|
217
|
+
};
|
|
218
|
+
// `signal?.` — a missing signal is treated as one that never aborts.
|
|
219
|
+
// The KEY is required (so a call that never mentions a signal does not
|
|
220
|
+
// compile), but its type admits `undefined` because several seams
|
|
221
|
+
// genuinely hold `AbortSignal | undefined` — see `BoundedOptions.signal`
|
|
222
|
+
// for which ones and why. This branch is what lets them pass it straight
|
|
223
|
+
// through instead of each spelling its own never-aborting stand-in
|
|
224
|
+
// (#2557 review F2), and it is also what keeps a JS caller, or a
|
|
225
|
+
// `@ts-expect-error` probe, from making a helper whose whole job is
|
|
226
|
+
// keeping a hook from blowing up throw `Cannot read properties of
|
|
227
|
+
// undefined` (#2530 round 3 F5).
|
|
228
|
+
if (signal?.aborted)
|
|
229
|
+
return fire("caller-abort");
|
|
230
|
+
if (shutdownSignal?.aborted)
|
|
231
|
+
return fire("shutdown");
|
|
232
|
+
// A zero budget ("this hook may not await at all") is a bound that has
|
|
233
|
+
// already elapsed, not a timer to arm.
|
|
234
|
+
if (ms <= 0)
|
|
235
|
+
return fire("deadline");
|
|
236
|
+
timer = setTimeout(() => fire("deadline"), ms);
|
|
237
|
+
// Never hold the event loop open past the hook this bounds.
|
|
238
|
+
timer.unref?.();
|
|
239
|
+
onCallerAbort = () => fire("caller-abort");
|
|
240
|
+
signal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
241
|
+
if (shutdownSignal) {
|
|
242
|
+
onShutdownAbort = () => fire("shutdown");
|
|
243
|
+
shutdownSignal.addEventListener("abort", onShutdownAbort, {
|
|
244
|
+
once: true,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
try {
|
|
249
|
+
// The value is boxed so a promise that legitimately resolves
|
|
250
|
+
// `undefined` stays distinguishable from a bound firing (defect shape
|
|
251
|
+
// 10: a silenced answer must not read as a clean one).
|
|
252
|
+
const settled = await Promise.race([boxed, abandoned]);
|
|
253
|
+
if (settled !== BOUND_ABORTED)
|
|
254
|
+
return settled.value;
|
|
255
|
+
const elapsedMs = Date.now() - startedAt;
|
|
256
|
+
const fired = cause ?? "deadline";
|
|
257
|
+
// A caller's own abort is Escape or a cancelled turn — deliberate, not
|
|
258
|
+
// a degradation, and deliberately absent from the ledger.
|
|
259
|
+
if (fired === "deadline" || fired === "shutdown") {
|
|
260
|
+
recordDegradationOnce({
|
|
261
|
+
kind: fired === "deadline" ? "hook-await-exceeded" : "hook-await-abandoned",
|
|
262
|
+
subject: `${hook}:${label}`,
|
|
263
|
+
reason: fired === "deadline"
|
|
264
|
+
? `exceeded ${ms}ms budget after ${elapsedMs}ms`
|
|
265
|
+
: `abandoned after ${elapsedMs}ms: session is shutting down (budget ${ms}ms)`,
|
|
266
|
+
metadata: { hook, label, budgetMs: ms, elapsedMs, cause: fired },
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
if (timer !== undefined)
|
|
273
|
+
clearTimeout(timer);
|
|
274
|
+
if (onCallerAbort)
|
|
275
|
+
signal?.removeEventListener("abort", onCallerAbort);
|
|
276
|
+
if (onShutdownAbort) {
|
|
277
|
+
shutdownSignal?.removeEventListener("abort", onShutdownAbort);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
*/
|
|
44
44
|
import * as asyncHooks from "node:async_hooks";
|
|
45
45
|
import * as path from "node:path";
|
|
46
|
-
import {
|
|
46
|
+
import { getGlobalPiLensLogDir } from "./probe-home-state.js";
|
|
47
47
|
import { getMaxLogSizeMB } from "./log-cleanup.js";
|
|
48
48
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
49
49
|
/** Read once at module load — see module docstring. Not re-read per call. */
|
|
50
50
|
const DEBUG_HANDLES_ENABLED = process.env.PI_LENS_DEBUG_HANDLES === "1";
|
|
51
|
-
const DEBUG_HANDLES_LOG_FILE = path.join(
|
|
51
|
+
const DEBUG_HANDLES_LOG_FILE = path.join(getGlobalPiLensLogDir(), "debug-handles.log");
|
|
52
52
|
/** Cap on tracked in-flight creation sites (bounded past this). */
|
|
53
53
|
export const TRACKER_MAX_ENTRIES = 500;
|
|
54
54
|
/** The first N insertion-order entries are never evicted — see the module
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
import * as fs from "node:fs";
|
|
51
51
|
import * as path from "node:path";
|
|
52
52
|
import * as v8 from "node:v8";
|
|
53
|
-
import {
|
|
53
|
+
import { getGlobalPiLensLogDir } from "./probe-home-state.js";
|
|
54
54
|
import { getMaxLogSizeMB } from "./log-cleanup.js";
|
|
55
55
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
56
56
|
/** Read once at module load — see module docstring. Not re-read per call. */
|
|
@@ -61,7 +61,7 @@ const DEBUG_HEAP_ENABLED = process.env.PI_LENS_DEBUG_HEAP === "1";
|
|
|
61
61
|
export const SNAPSHOT_RETENTION = 3;
|
|
62
62
|
const SNAPSHOT_PREFIX = "heap-";
|
|
63
63
|
const SNAPSHOT_SUFFIX = ".heapsnapshot";
|
|
64
|
-
const HEAP_SNAPSHOT_LOG_FILE = path.join(
|
|
64
|
+
const HEAP_SNAPSHOT_LOG_FILE = path.join(getGlobalPiLensLogDir(), "heap-snapshots.log");
|
|
65
65
|
// Only ever constructed when the flag is on — the entire cost of this module
|
|
66
66
|
// when unset is the one boolean check at the top of each export below.
|
|
67
67
|
const writer = DEBUG_HEAP_ENABLED
|
|
@@ -138,7 +138,7 @@ export function pruneOldSnapshots(dir, keep = SNAPSHOT_RETENTION) {
|
|
|
138
138
|
export function writeHeapSnapshotNow(label) {
|
|
139
139
|
if (!DEBUG_HEAP_ENABLED)
|
|
140
140
|
return null;
|
|
141
|
-
const dir =
|
|
141
|
+
const dir = getGlobalPiLensLogDir();
|
|
142
142
|
try {
|
|
143
143
|
fs.mkdirSync(dir, { recursive: true });
|
|
144
144
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one owner of "work that keeps talking to the LSP service after the hook
|
|
3
|
+
* that started it has returned" (#2504 review round 2, F3).
|
|
4
|
+
*
|
|
5
|
+
* #2504 moved the cold-cache actionable-warnings fresh-pull loop off the
|
|
6
|
+
* awaited `turn_end` hook. That loop then had effectively ONE bound: it
|
|
7
|
+
* captured the COMPLETED turn's `ctx.signal` — which `index.ts` clears from
|
|
8
|
+
* the ambient slot in its `finally`, so it can never fire — plus a 60 s wall
|
|
9
|
+
* deadline checked only BETWEEN files. A wedged `getDiagnostics` was therefore
|
|
10
|
+
* unbounded, the loop kept opening files (and could re-spawn servers) after
|
|
11
|
+
* `turn_end` returned and after the LSP idle reset, a `session_shutdown`
|
|
12
|
+
* landing mid-loop hit the #234 spawn-at-teardown shape, and the handle was a
|
|
13
|
+
* module-level `let` with no reset that a second deferral simply overwrote.
|
|
14
|
+
*
|
|
15
|
+
* This module supplies the missing halves: ONE registered handle (held by
|
|
16
|
+
* whichever deferral got there first, released when its work settles — see
|
|
17
|
+
* `armDeferredLspWork`) and ONE abort signal that
|
|
18
|
+
* `resetLSPService` fires — the single choke point through which
|
|
19
|
+
* `session_shutdown`, `session_start` and the idle reset all retire the
|
|
20
|
+
* service, so no caller has to remember to wire each lifecycle event
|
|
21
|
+
* separately. Aborting only ever settles a promise; it never spawns, which is
|
|
22
|
+
* what AGENTS.md's #234 teardown rule requires of anything reachable from
|
|
23
|
+
* `session_shutdown`.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately a zero-dependency leaf, for the same reason
|
|
26
|
+
* `map-with-concurrency.ts` is one: `clients/lsp/index.ts` must not gain an
|
|
27
|
+
* import edge to `actionable-warnings.ts` (and through it `lsp/edits.ts`,
|
|
28
|
+
* `lsp-mutation.ts`, the durable store …) for a 20-line handle registry.
|
|
29
|
+
*/
|
|
30
|
+
let deferredController;
|
|
31
|
+
let deferredWork;
|
|
32
|
+
/**
|
|
33
|
+
* Abort the in-flight deferred LSP work, if any, and drop the slot.
|
|
34
|
+
*
|
|
35
|
+
* Safe at teardown: it settles a promise and spawns nothing. Idempotent — a
|
|
36
|
+
* second call with no armed work is a no-op.
|
|
37
|
+
*/
|
|
38
|
+
export function abortDeferredLspWork(reason) {
|
|
39
|
+
const controller = deferredController;
|
|
40
|
+
deferredController = undefined;
|
|
41
|
+
deferredWork = undefined;
|
|
42
|
+
controller?.abort(new Error(reason));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Claim the deferred-work slot, or `undefined` when an incumbent still holds
|
|
46
|
+
* it. THE INCUMBENT WINS (#2504 review round 3, F-A(d)).
|
|
47
|
+
*
|
|
48
|
+
* Round 2 had arming ABORT the previous deferral, reasoning that two loops
|
|
49
|
+
* must not run concurrently against one LSP service. The first half of that is
|
|
50
|
+
* right and still holds — there is exactly one slot. The second half was
|
|
51
|
+
* backwards about which loop to keep. A deferral is armed by a turn that
|
|
52
|
+
* primed NO LSP cache, which in a live editing session is every turn; so every
|
|
53
|
+
* turn cancelled its predecessor, and an aborted loop publishes nothing by
|
|
54
|
+
* design (its service is presumed gone). Back-to-back editing turns therefore
|
|
55
|
+
* delivered NOTHING AT ALL — the exact delivery AC #2504 exists to preserve.
|
|
56
|
+
*
|
|
57
|
+
* Keeping the incumbent inverts that: the loop that is already talking to the
|
|
58
|
+
* service runs to completion and publishes, and the newcomer is declined and
|
|
59
|
+
* says so out loud. The cost is stated rather than silent — the declining
|
|
60
|
+
* turn's cold files go unchecked on this channel until a later turn defers.
|
|
61
|
+
*
|
|
62
|
+
* Declining cannot latch. `registerDeferredLspWork` releases the slot the
|
|
63
|
+
* moment the work settles, and the loop itself is bounded twice over
|
|
64
|
+
* (`ACTIONABLE_WARNINGS_DEFERRED_BUDGET_MS` between files, a per-round-trip
|
|
65
|
+
* timeout inside them), so an incumbent always yields the slot in bounded
|
|
66
|
+
* time. Teardown is unaffected: `abortDeferredLspWork` still evicts the
|
|
67
|
+
* incumbent unconditionally, which is how `resetLSPService` retires it.
|
|
68
|
+
*/
|
|
69
|
+
export function armDeferredLspWork() {
|
|
70
|
+
if (isDeferredLspWorkArmed())
|
|
71
|
+
return undefined;
|
|
72
|
+
const controller = new AbortController();
|
|
73
|
+
deferredController = controller;
|
|
74
|
+
deferredWork = undefined;
|
|
75
|
+
return controller.signal;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Register the armed work's promise so callers (and tests) have something to
|
|
79
|
+
* await. Ignored when the slot has already been re-armed or aborted since — a
|
|
80
|
+
* late registration must not resurrect a retired handle.
|
|
81
|
+
*
|
|
82
|
+
* Registration also arranges the RELEASE (#2504 review round 3, F-A(d)):
|
|
83
|
+
* because the slot now turns a newcomer away rather than evicting the
|
|
84
|
+
* incumbent, something has to hand it back, and the only correct moment is
|
|
85
|
+
* when the work settles. Guarded on identity, so a slot re-armed or aborted in
|
|
86
|
+
* the meantime is never cleared by a stale finalizer.
|
|
87
|
+
*/
|
|
88
|
+
export function registerDeferredLspWork(signal, work) {
|
|
89
|
+
if (deferredController?.signal !== signal)
|
|
90
|
+
return;
|
|
91
|
+
deferredWork = work;
|
|
92
|
+
void work.finally(() => {
|
|
93
|
+
if (deferredController?.signal !== signal)
|
|
94
|
+
return;
|
|
95
|
+
deferredController = undefined;
|
|
96
|
+
deferredWork = undefined;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/** The in-flight deferred work, or an already-resolved promise when idle. */
|
|
100
|
+
export function awaitDeferredLspWork() {
|
|
101
|
+
return deferredWork ?? Promise.resolve();
|
|
102
|
+
}
|
|
103
|
+
/** True while a deferral holds the slot and has not been aborted. */
|
|
104
|
+
export function isDeferredLspWorkArmed() {
|
|
105
|
+
return deferredController?.signal.aborted === false;
|
|
106
|
+
}
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
/** Bounded, process-local telemetry for behavior degraded during one session. */
|
|
2
2
|
import { logExtension } from "./extension-log.js";
|
|
3
|
-
import { LEDGER_FIELD_MAX, truncateForLedger } from "./ledger-bounds.js";
|
|
3
|
+
import { DEGRADATION_ENTRIES_PER_KIND, LEDGER_FIELD_MAX, truncateForLedger, } from "./ledger-bounds.js";
|
|
4
4
|
import { logLatency } from "./latency-logger.js";
|
|
5
|
-
import { getSinkWriteFailures, resetSinkWriteFailures, } from "./ndjson-logger.js";
|
|
5
|
+
import { getSinkRotations, getSinkWriteFailures, resetSinkRotations, resetSinkWriteFailures, } from "./ndjson-logger.js";
|
|
6
6
|
// #2146: pulled at READ time, never pushed. `process-singletons.ts` is a
|
|
7
7
|
// dependency leaf on purpose — it cannot import this module without closing a
|
|
8
8
|
// no-client-cycles cycle through instance-registry/instance-reaper — so the
|
|
9
9
|
// ledger reaches IN for its reset log, the same inversion `getSinkWriteFailures`
|
|
10
10
|
// above uses.
|
|
11
11
|
import { getProcessSingletonResets, PROCESS_SINGLETON_RESET_KIND, } from "./process-singletons.js";
|
|
12
|
+
// #2506: same inversion, same reason — the log-dir resolver cannot import this
|
|
13
|
+
// module (directly OR dynamically). THIS module is on a no-client-cycles cycle
|
|
14
|
+
// via extension-log.ts; the resolver's whole correctness argument is that it
|
|
15
|
+
// sits off every cycle, so an edge in that direction would put it back on one.
|
|
16
|
+
// `getGlobalPiLensLogDir()`'s probe-home-redirect event is therefore written
|
|
17
|
+
// into `probe-home-state.ts` and read back here instead. See that module's doc
|
|
18
|
+
// comment for the full account.
|
|
19
|
+
import { getProbeHomeRedirectEvent } from "./probe-home-state.js";
|
|
12
20
|
// Re-exported so existing importers keep one name for the ledger's bound.
|
|
13
21
|
export { LEDGER_FIELD_MAX, truncateForLedger };
|
|
14
|
-
const ENTRIES_PER_KIND =
|
|
22
|
+
const ENTRIES_PER_KIND = DEGRADATION_ENTRIES_PER_KIND;
|
|
15
23
|
const MAX_DISTINCT_KINDS = 32;
|
|
16
24
|
const OVERFLOW_KIND = "other";
|
|
17
25
|
const groups = new Map();
|
|
@@ -65,7 +73,7 @@ export function recordDegradationOnce(record) {
|
|
|
65
73
|
return;
|
|
66
74
|
onceKeys.add(key);
|
|
67
75
|
if (recordDegradation({ kind, subject, reason: record.reason })) {
|
|
68
|
-
logDurableDegradation(kind, subject, 1, record.metadata);
|
|
76
|
+
logDurableDegradation(kind, subject, 1, record.metadata, record.code);
|
|
69
77
|
}
|
|
70
78
|
}
|
|
71
79
|
catch (error) {
|
|
@@ -114,7 +122,7 @@ export function incrementDegradationCount(record) {
|
|
|
114
122
|
// Durable rows use the summary's admission and emit the first event and
|
|
115
123
|
// power-of-two milestones only, keeping the sink bounded.
|
|
116
124
|
if (admitted && isPowerOfTwo(count)) {
|
|
117
|
-
logDurableDegradation(kind, subject, count, record.metadata);
|
|
125
|
+
logDurableDegradation(kind, subject, count, record.metadata, record.code);
|
|
118
126
|
}
|
|
119
127
|
return count === 1;
|
|
120
128
|
}
|
|
@@ -130,7 +138,7 @@ export function incrementDegradationCount(record) {
|
|
|
130
138
|
* redaction, and write queue. The subject and kind were already bounded by the
|
|
131
139
|
* ledger before reaching this seam.
|
|
132
140
|
*/
|
|
133
|
-
function logDurableDegradation(kind, subject, count, metadata) {
|
|
141
|
+
function logDurableDegradation(kind, subject, count, metadata, code) {
|
|
134
142
|
const boundedMetadata = boundLedgerMetadata(metadata);
|
|
135
143
|
logLatency({
|
|
136
144
|
type: "phase",
|
|
@@ -143,6 +151,9 @@ function logDurableDegradation(kind, subject, count, metadata) {
|
|
|
143
151
|
subject,
|
|
144
152
|
count,
|
|
145
153
|
ledgerGeneration,
|
|
154
|
+
// Reserved row field, written AFTER the bounded caller metadata so the
|
|
155
|
+
// `MAX_METADATA_KEYS` cap cannot evict the stable code (#2418).
|
|
156
|
+
...(code ? { code } : {}),
|
|
146
157
|
},
|
|
147
158
|
});
|
|
148
159
|
}
|
|
@@ -190,6 +201,38 @@ export function getDegradationSummary() {
|
|
|
190
201
|
})),
|
|
191
202
|
});
|
|
192
203
|
}
|
|
204
|
+
// #2505, same read-time fold: a mid-session rotation is visible without
|
|
205
|
+
// this module writing about it through the very sink family it is
|
|
206
|
+
// reporting on — see the `log-sink-rotated` doc comment on
|
|
207
|
+
// `DegradationKind`.
|
|
208
|
+
const sinkRotations = getSinkRotations();
|
|
209
|
+
const rotated = sinkRotations.filter((sink) => sink.rotationCount > 0);
|
|
210
|
+
if (rotated.length > 0) {
|
|
211
|
+
summary.push({
|
|
212
|
+
kind: "log-sink-rotated",
|
|
213
|
+
count: rotated.reduce((total, sink) => total + sink.rotationCount, 0),
|
|
214
|
+
droppedCount: 0,
|
|
215
|
+
latestReasons: rotated.map((sink) => ({
|
|
216
|
+
subject: truncateForLedger(sink.file),
|
|
217
|
+
reason: truncateForLedger(`${sink.rotationCount} rotation(s) at the configured byte bound`),
|
|
218
|
+
})),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
// A rotation the writer ATTEMPTED and could not complete is a different
|
|
222
|
+
// fact from a rotation that happened, and the only signal that a sink is
|
|
223
|
+
// growing past its bound right now (#2505 review F2).
|
|
224
|
+
const rotateFailed = sinkRotations.filter((sink) => sink.failureCount > 0);
|
|
225
|
+
if (rotateFailed.length > 0) {
|
|
226
|
+
summary.push({
|
|
227
|
+
kind: "log-sink-rotate-failed",
|
|
228
|
+
count: rotateFailed.reduce((total, sink) => total + sink.failureCount, 0),
|
|
229
|
+
droppedCount: 0,
|
|
230
|
+
latestReasons: rotateFailed.map((sink) => ({
|
|
231
|
+
subject: truncateForLedger(sink.file),
|
|
232
|
+
reason: truncateForLedger(`${sink.failureCount} failed rotation attempt(s); this sink is growing past its byte bound`),
|
|
233
|
+
})),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
193
236
|
// #2146, same read-time fold: process-singleton resets live in the leaf
|
|
194
237
|
// module's own bounded log. One entry per family, so this group's count is
|
|
195
238
|
// the number of families this build could not adopt, never an event tally.
|
|
@@ -205,6 +248,23 @@ export function getDegradationSummary() {
|
|
|
205
248
|
})),
|
|
206
249
|
});
|
|
207
250
|
}
|
|
251
|
+
// #2506, same read-time fold: `getGlobalPiLensLogDir()`'s probe-home redirect
|
|
252
|
+
// fires at most once per process (see `probe-home-state.ts`), so this is a
|
|
253
|
+
// presence check, not a tally.
|
|
254
|
+
const probeHomeRedirect = getProbeHomeRedirectEvent();
|
|
255
|
+
if (probeHomeRedirect) {
|
|
256
|
+
summary.push({
|
|
257
|
+
kind: "global-dir-probe-redirect",
|
|
258
|
+
count: 1,
|
|
259
|
+
droppedCount: 0,
|
|
260
|
+
latestReasons: [
|
|
261
|
+
{
|
|
262
|
+
subject: truncateForLedger(probeHomeRedirect.probeHome),
|
|
263
|
+
reason: truncateForLedger(`PI_LENS_HOME unset with cwd in an agent worktree/tmp probe context (${probeHomeRedirect.cwd}), or PILENS_PROBE=1 forced it; LOGS redirected away from the real home directory (tools, bin and instances.json are unaffected)`),
|
|
264
|
+
},
|
|
265
|
+
],
|
|
266
|
+
});
|
|
267
|
+
}
|
|
208
268
|
return summary;
|
|
209
269
|
}
|
|
210
270
|
function isRenderableSummary(value) {
|
|
@@ -223,6 +283,34 @@ function isRenderableSummary(value) {
|
|
|
223
283
|
typeof entry.reason === "string"));
|
|
224
284
|
});
|
|
225
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* Kinds that record something the system did ON PURPOSE, correctly, and
|
|
288
|
+
* that a reader only needs a tally of — never a call to action (#2505
|
|
289
|
+
* review). A routine log rotation at the configured bound is the writer
|
|
290
|
+
* working as designed; giving it the same warning marker a real
|
|
291
|
+
* degradation gets trains the reader to ignore the marker. The FAILED
|
|
292
|
+
* rotation is the line that has to stand out, so it is deliberately NOT
|
|
293
|
+
* in this set.
|
|
294
|
+
*/
|
|
295
|
+
const INFORMATIONAL_DEGRADATION_KINDS = new Set([
|
|
296
|
+
"log-sink-rotated",
|
|
297
|
+
// #2504 review round 8 (S1): both fire on the ordinary re-edit cadence
|
|
298
|
+
// (a carry-forward window closing, or a session boundary) and are
|
|
299
|
+
// self-healing -- the next turn's analysis or deferral re-observes the
|
|
300
|
+
// file -- so a `⚠` overstates them the same way a routine log rotation
|
|
301
|
+
// would.
|
|
302
|
+
"actionable-warnings-inband-superseded",
|
|
303
|
+
"actionable-warnings-deferred-superseded",
|
|
304
|
+
// #2523: a hook await abandoned because the session is tearing down. The
|
|
305
|
+
// teardown signal firing is the design working, so it is a tally; the
|
|
306
|
+
// BUDGET blow-out (`hook-await-exceeded`) is the line that has to stand
|
|
307
|
+
// out, and a caller's own Escape records nothing at all.
|
|
308
|
+
"hook-await-abandoned",
|
|
309
|
+
// #2524: can fire on a query that ultimately settles `ok` (see the kind's
|
|
310
|
+
// doc comment above) and is frequent/self-healing by design — a `⚠` would
|
|
311
|
+
// cry wolf on the sampler's ordinary best-effort data loss.
|
|
312
|
+
"resource-sampler-scanner-escalated",
|
|
313
|
+
]);
|
|
226
314
|
export function renderDegradationLines(summary = getDegradationSummary()) {
|
|
227
315
|
if (!isRenderableSummary(summary))
|
|
228
316
|
return [];
|
|
@@ -231,6 +319,9 @@ export function renderDegradationLines(summary = getDegradationSummary()) {
|
|
|
231
319
|
return [
|
|
232
320
|
"Degradations:",
|
|
233
321
|
...summary.map((group) => {
|
|
322
|
+
if (INFORMATIONAL_DEGRADATION_KINDS.has(group.kind)) {
|
|
323
|
+
return ` ${group.kind}: ${group.count}`;
|
|
324
|
+
}
|
|
234
325
|
const latest = group.latestReasons.at(-1);
|
|
235
326
|
return ` ⚠ ${group.kind}: ${group.count}${latest ? ` — ${latest.subject}: ${latest.reason}` : ""}`;
|
|
236
327
|
}),
|
|
@@ -258,6 +349,10 @@ export function resetDegradationLedger() {
|
|
|
258
349
|
// process-lifetime latch too — it re-arms alongside the rest of the
|
|
259
350
|
// ledger rather than surviving past the session that observed it.
|
|
260
351
|
resetSinkWriteFailures();
|
|
352
|
+
// #2505, same catalog shape 17 re-arm: a rotation tally recurs (new writes
|
|
353
|
+
// keep crossing the bound), so clearing it costs nothing and a later
|
|
354
|
+
// session re-observes the fact fresh.
|
|
355
|
+
resetSinkRotations();
|
|
261
356
|
// #2146 review F3: the OTHER pulled source, `getProcessSingletonResets()`,
|
|
262
357
|
// deliberately does NOT re-arm here, and the difference from its neighbour
|
|
263
358
|
// above is the point. A sink write failure recurs — new writes fail, so
|
|
@@ -270,6 +365,17 @@ export function resetDegradationLedger() {
|
|
|
270
365
|
// session (one entry per family, capped at 16), so leaving it costs a fixed
|
|
271
366
|
// handful of lines and keeps a process-scope fact visible for the process's
|
|
272
367
|
// life. Deliberate exception to catalog shape 17, not an oversight.
|
|
368
|
+
//
|
|
369
|
+
// `getProbeHomeRedirectEvent()` (#2506) is the same shape as the
|
|
370
|
+
// process-singleton case above and for the same reason: the redirect is
|
|
371
|
+
// resolved at most once per PROCESS (memoized in the `globalThis` slot
|
|
372
|
+
// `probe-home-state.ts` owns, not a session-scoped one), so it cannot
|
|
373
|
+
// recur within this process's life either — clearing it here would hide
|
|
374
|
+
// the fact from every session after the first in the same probe process.
|
|
273
375
|
}
|
|
274
|
-
|
|
376
|
+
// Re-exported so every existing importer keeps its specifier. The value now
|
|
377
|
+
// lives in the `ledger-bounds.js` leaf (#2426), so a producer that only needs
|
|
378
|
+
// the BOUND does not have to import the ledger — and cannot be broken by a test
|
|
379
|
+
// that mocks it wholesale.
|
|
380
|
+
export { DEGRADATION_ENTRIES_PER_KIND };
|
|
275
381
|
export const DEGRADATION_MAX_DISTINCT_KINDS = MAX_DISTINCT_KINDS;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { createSubsystemLogger } from "./extension-log.js";
|
|
12
12
|
import * as fs from "node:fs";
|
|
13
13
|
import * as path from "node:path";
|
|
14
|
+
import { mapWithConcurrency } from "./map-with-concurrency.js";
|
|
14
15
|
import { findNodeToolBinary } from "./package-manager.js";
|
|
15
16
|
import { isFullyQualified } from "./path-utils.js";
|
|
16
17
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
@@ -138,28 +139,16 @@ async function resolvedCommandIsStale(resolved, spawnableCache) {
|
|
|
138
139
|
}
|
|
139
140
|
/**
|
|
140
141
|
* Run `mapper` over `items` with at most `concurrency` in flight at once.
|
|
142
|
+
*
|
|
141
143
|
* Exported (#1810 review F6) so `clients/dispatch/runners/biome-check.ts`'s
|
|
142
144
|
* `resolveBiomeFixKinds` can reuse this exact worker-pool shape for its
|
|
143
|
-
* `biome explain` fan-out instead of carrying a second copy
|
|
144
|
-
*
|
|
145
|
-
*
|
|
145
|
+
* `biome explain` fan-out instead of carrying a second copy. #2504 moved the
|
|
146
|
+
* VALUE to the zero-dependency `map-with-concurrency.js` leaf — a third caller
|
|
147
|
+
* (`runtime-turn.ts`) needs the pool without pulling this analyzer client into
|
|
148
|
+
* the eager startup graph — and re-exports it here so every existing importer
|
|
149
|
+
* keeps its specifier. This stays the helper's documented home.
|
|
146
150
|
*/
|
|
147
|
-
export
|
|
148
|
-
if (items.length === 0)
|
|
149
|
-
return;
|
|
150
|
-
let nextIndex = 0;
|
|
151
|
-
const workerCount = Math.max(1, Math.min(concurrency, items.length));
|
|
152
|
-
const worker = async () => {
|
|
153
|
-
while (true) {
|
|
154
|
-
const index = nextIndex++;
|
|
155
|
-
if (index >= items.length)
|
|
156
|
-
return;
|
|
157
|
-
await mapper(items[index]);
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
const workers = Array.from({ length: workerCount }, () => worker());
|
|
161
|
-
await Promise.all(workers);
|
|
162
|
-
}
|
|
151
|
+
export { mapWithConcurrency };
|
|
163
152
|
// --- Client ---
|
|
164
153
|
export class DependencyChecker {
|
|
165
154
|
madgeAvailability = createAvailabilityChecker("madge", ".cmd", ["--version"], {
|
|
@@ -128,7 +128,7 @@ export function computeStrictAnchor(args) {
|
|
|
128
128
|
});
|
|
129
129
|
}
|
|
130
130
|
/** Intent-level anchor — see module doc. Used for defer/flagged/suppress. */
|
|
131
|
-
|
|
131
|
+
function computeWeakAnchor(args) {
|
|
132
132
|
return stableFindingId("ddw:", {
|
|
133
133
|
cwd: args.cwd,
|
|
134
134
|
filePath: args.filePath,
|