@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
|
@@ -822,6 +822,27 @@ export class ReadGuard {
|
|
|
822
822
|
return (stored !== undefined &&
|
|
823
823
|
(this.reads.has(stored) || this.writtenThisSession.has(stored)));
|
|
824
824
|
}
|
|
825
|
+
/**
|
|
826
|
+
* Every path this guard has a read or a write record for, as normalized keys
|
|
827
|
+
* (#2430).
|
|
828
|
+
*
|
|
829
|
+
* This is the read-guard half of the observational net's TRACKED-FILE SET:
|
|
830
|
+
* the files pi-lens has actually seen this session, which the `agent_settled`
|
|
831
|
+
* sweep hash-checks instead of walking the workspace. It reads state already
|
|
832
|
+
* held in memory and stats nothing; both backing containers are capped
|
|
833
|
+
* (`READ_GUARD_MAX_UNCONSUMED_FILES`), so the result is bounded by
|
|
834
|
+
* construction.
|
|
835
|
+
*
|
|
836
|
+
* `touchFile` is deliberately NOT called: enumerating is not using, and
|
|
837
|
+
* refreshing every entry's idle clock on each sweep would make the idle
|
|
838
|
+
* eviction unreachable.
|
|
839
|
+
*/
|
|
840
|
+
getTrackedPaths() {
|
|
841
|
+
const tracked = new Set(this.reads.keys());
|
|
842
|
+
for (const written of this.writtenThisSession)
|
|
843
|
+
tracked.add(written);
|
|
844
|
+
return [...tracked];
|
|
845
|
+
}
|
|
825
846
|
/**
|
|
826
847
|
* Drop all record of a path pi-lens confirmed no longer exists on disk
|
|
827
848
|
* (#1668, external delete). Without this a later write reusing the same
|
|
@@ -1375,8 +1396,11 @@ export class ReadGuard {
|
|
|
1375
1396
|
return filePath.endsWith(suffix);
|
|
1376
1397
|
}
|
|
1377
1398
|
if (pattern.includes("*")) {
|
|
1378
|
-
//
|
|
1379
|
-
|
|
1399
|
+
// Adjacent stars are equivalent to one star in this dialect. Collapse
|
|
1400
|
+
// them before compiling so a non-match cannot backtrack over every
|
|
1401
|
+
// nullable `.*` group (#2622).
|
|
1402
|
+
const collapsedPattern = pattern.replace(/\*+/g, "*");
|
|
1403
|
+
const regex = new RegExp(`^${collapsedPattern.replace(/\\/g, "\\\\").replace(/\./g, "\\.").replace(/\*/g, ".*")}$`);
|
|
1380
1404
|
return regex.test(filePath);
|
|
1381
1405
|
}
|
|
1382
1406
|
return filePath === pattern;
|
|
@@ -37,7 +37,7 @@ import { getProjectDataDir } from "./file-utils.js";
|
|
|
37
37
|
import { normalizeFilePath } from "./path-utils.js";
|
|
38
38
|
export const RECENT_TOUCHES_MAX_ENTRIES = 50;
|
|
39
39
|
/** Freshness window for the CHILD session_start consumer (15 minutes). */
|
|
40
|
-
|
|
40
|
+
const RECENT_TOUCHES_FRESHNESS_MS = 15 * 60 * 1000;
|
|
41
41
|
function recentTouchesPath(cwd) {
|
|
42
42
|
return path.join(getProjectDataDir(cwd), "recent-touches.json");
|
|
43
43
|
}
|
|
@@ -46,7 +46,7 @@ function recentTouchesPath(cwd) {
|
|
|
46
46
|
// INTO the same nudge pipeline, so one switch governs the whole path
|
|
47
47
|
// (producer, both consumers) as specified in the issue.
|
|
48
48
|
let _enabledCache;
|
|
49
|
-
|
|
49
|
+
function isRecentTouchesEnabled() {
|
|
50
50
|
if (_enabledCache === undefined) {
|
|
51
51
|
_enabledCache = process.env.PI_LENS_AGENT_NUDGE !== "0";
|
|
52
52
|
}
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
* **synchronously in that detached callback**, which no `try { await pidusage }
|
|
26
26
|
* catch {}` at the call site can catch → uncaughtException → the pi host
|
|
27
27
|
* crashes (#620, #533). pidusage 4.0.1 exposes no option to avoid the gwmi
|
|
28
|
-
* path. So on Windows this module
|
|
29
|
-
* `
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
28
|
+
* path. So on Windows this module asks the shared, fully guarded process-table
|
|
29
|
+
* seam (`clients/process-snapshot.ts` over `scripts/lib/process-scan.mjs`,
|
|
30
|
+
* #2443) for the CPU/RSS columns instead, and computes CPU% from the same
|
|
31
|
+
* KernelModeTime/UserModeTime delta-over-elapsed formula gwmi uses — so a
|
|
32
|
+
* spawn failure can only ever lose a data point, never throw.
|
|
33
33
|
*
|
|
34
34
|
* Every export here is best-effort: a sampling failure (pid already exited,
|
|
35
35
|
* `pidusage` throwing, permission denied, etc.) must never throw into the
|
|
@@ -43,11 +43,10 @@
|
|
|
43
43
|
* process/pidusage involvement — mirrors the pure/impure split in
|
|
44
44
|
* clients/instance-reaper.ts (`decideOrphanReaping` vs `sweepOrphans`).
|
|
45
45
|
*/
|
|
46
|
-
import * as path from "node:path";
|
|
47
46
|
import pidusage from "pidusage";
|
|
48
|
-
import { spawnCollectStdoutResult } from "./child-unref.js";
|
|
49
47
|
import { recordDegradationOnce } from "./degradation-ledger.js";
|
|
50
48
|
import { terminateScannerChild } from "./instance-reaper.js";
|
|
49
|
+
import { queryProcessTable } from "./process-snapshot.js";
|
|
51
50
|
export const RESOURCE_SAMPLE_QUERY_TIMEOUT_MS = 2_000;
|
|
52
51
|
function recordQueryFailure(subject, status, exitCode) {
|
|
53
52
|
const exitReason = status === "exit-error" ? ` (exit code ${exitCode ?? "unknown"})` : "";
|
|
@@ -112,41 +111,31 @@ export function walkDescendantPids(rootPid, pairs) {
|
|
|
112
111
|
async function findDescendantPidsWindows(rootPid) {
|
|
113
112
|
if (!runningOnWindows() || !Number.isFinite(rootPid) || rootPid <= 0)
|
|
114
113
|
return [];
|
|
115
|
-
// One
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
// works normally in an interactive/long-lived session: unref only means
|
|
126
|
-
// "don't hold the loop open FOR this alone," the collected stdout is still
|
|
127
|
-
// delivered whenever `close` fires. The result status keeps a failed query
|
|
128
|
-
// distinct from a successful empty process table.
|
|
129
|
-
const result = await spawnCollectStdoutResult(powershell, ["-NoProfile", "-NonInteractive", "-Command", psScript], { shell: false, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }, {
|
|
114
|
+
// One query pulls every process's (pid, parentPid) pair; walk the BFS in
|
|
115
|
+
// JS rather than issuing N queries for N tree levels. The listing itself is
|
|
116
|
+
// the shared seam (#2443), which also supplies the fire-and-forget spawn
|
|
117
|
+
// rails this call has always needed (#1155): the child AND its piped stdout
|
|
118
|
+
// are unref'd, so this one-shot query can never keep a settled
|
|
119
|
+
// `pi --print` process alive past its own close, and a scanner child that
|
|
120
|
+
// blows the timeout is tree-killed and verified rather than abandoned. The
|
|
121
|
+
// result status keeps a failed query distinct from a successful empty
|
|
122
|
+
// process table.
|
|
123
|
+
const result = await queryProcessTable({ fields: ["pid", "ppid"] }, {
|
|
130
124
|
timeoutMs: RESOURCE_SAMPLE_QUERY_TIMEOUT_MS,
|
|
131
|
-
onTimeout: (child) => terminateScannerChild(child, {
|
|
125
|
+
onTimeout: (child) => terminateScannerChild(child, {
|
|
126
|
+
kind: "resource-sampler-scanner-escalated",
|
|
127
|
+
timeoutMs: RESOURCE_SAMPLE_QUERY_TIMEOUT_MS,
|
|
128
|
+
}),
|
|
132
129
|
});
|
|
133
130
|
if (result.status !== "ok") {
|
|
134
131
|
recordQueryFailure("windows-descendant-process-table", result.status, result.exitCode);
|
|
135
132
|
return null;
|
|
136
133
|
}
|
|
137
|
-
|
|
138
|
-
for (const line of result.stdout.split(/\r?\n/)) {
|
|
139
|
-
const [pidStr, ppidStr] = line.split(",");
|
|
140
|
-
const pid = Number(pidStr);
|
|
141
|
-
const ppid = Number(ppidStr);
|
|
142
|
-
if (Number.isFinite(pid) && Number.isFinite(ppid)) {
|
|
143
|
-
pairs.push([pid, ppid]);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return walkDescendantPids(rootPid, pairs);
|
|
134
|
+
return walkDescendantPids(rootPid, result.rows.map((row) => [row.pid, row.ppid]));
|
|
147
135
|
}
|
|
148
136
|
const windowsCpuHistory = new Map();
|
|
149
137
|
const CPU_HISTORY_MAX_AGE_MS = 60_000;
|
|
138
|
+
const CPU_HISTORY_MAX_ENTRIES = 4_096;
|
|
150
139
|
/**
|
|
151
140
|
* TEST-ONLY: clear the Windows CPU%-history so a test's two-sample CPU%
|
|
152
141
|
* assertion starts from a known-empty state (module-level state otherwise
|
|
@@ -155,43 +144,57 @@ const CPU_HISTORY_MAX_AGE_MS = 60_000;
|
|
|
155
144
|
export function __resetWindowsCpuHistoryForTests() {
|
|
156
145
|
windowsCpuHistory.clear();
|
|
157
146
|
}
|
|
147
|
+
export function __windowsCpuHistorySizeForTests() {
|
|
148
|
+
return windowsCpuHistory.size;
|
|
149
|
+
}
|
|
150
|
+
export function __windowsCpuHistoryHasForTests(pid, processIdentity) {
|
|
151
|
+
return windowsCpuHistory.has(`${pid}:${processIdentity}`);
|
|
152
|
+
}
|
|
158
153
|
/**
|
|
159
|
-
* Windows-only CPU%/RSS sampling
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
154
|
+
* Windows-only CPU%/RSS sampling through the FULLY GUARDED process-table
|
|
155
|
+
* seam (mirrors `findDescendantPidsWindows`): a synchronous throw from
|
|
156
|
+
* `spawn` (the `spawn UNKNOWN` crash vector, #620), a `child` `error` event,
|
|
157
|
+
* or a non-zero/garbage exit all resolve to an errored/absent map — this
|
|
158
|
+
* function can NEVER throw or reject. Deliberately does NOT call `pidusage`,
|
|
159
|
+
* whose unguarded internal `gwmi` spawn is the crash we're fixing.
|
|
165
160
|
*
|
|
166
|
-
* RSS comes from `WorkingSetSize
|
|
167
|
-
* (both in 100 ns units → ms via `/1e4`) differenced
|
|
168
|
-
* sample over the elapsed wall time — the same
|
|
169
|
-
* path uses. The first time a pid is seen it has
|
|
170
|
-
* reported as 0 for that tick and a real rate
|
|
161
|
+
* RSS comes from `WorkingSetSize` (`rssBytes`); CPU% from `KernelModeTime`
|
|
162
|
+
* plus `UserModeTime` (both in 100 ns units → ms via `/1e4`) differenced
|
|
163
|
+
* against this pid's prior sample over the elapsed wall time — the same
|
|
164
|
+
* computation pidusage's gwmi path uses. The first time a pid is seen it has
|
|
165
|
+
* no prior sample, so CPU% is reported as 0 for that tick and a real rate
|
|
166
|
+
* lands on the next one. The process creation date (`startedAt`) is the
|
|
167
|
+
* pid-reuse discriminator: a recycled pid must not inherit the previous
|
|
168
|
+
* process's CPU baseline.
|
|
171
169
|
*/
|
|
172
170
|
async function sampleProcessesWindows(valid) {
|
|
173
171
|
const samples = new Map();
|
|
174
172
|
if (valid.length === 0)
|
|
175
173
|
return samples;
|
|
176
|
-
// pids are pre-validated finite positive integers,
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
174
|
+
// pids are pre-validated finite positive integers, and the seam validates
|
|
175
|
+
// them again before they reach the query text, so the filter is
|
|
176
|
+
// injection-safe. The seam also supplies the fire-and-forget spawn rails
|
|
177
|
+
// (#1155: the child and its piped stdout are unref'd, so this one-shot
|
|
178
|
+
// query cannot keep a settled `pi --print` alive past its own close) and
|
|
179
|
+
// absorbs every failure mode this function used to guard inline — a
|
|
180
|
+
// synchronous `spawn` throw (the `spawn UNKNOWN` crash vector, #620), an
|
|
181
|
+
// async `error` event, a timeout, or a non-zero exit — reporting each
|
|
182
|
+
// through `status` rather than as an indistinguishable empty table.
|
|
183
|
+
const query = await queryProcessTable({
|
|
184
|
+
fields: [
|
|
185
|
+
"pid",
|
|
186
|
+
"rssBytes",
|
|
187
|
+
"cpuKernel100ns",
|
|
188
|
+
"cpuUser100ns",
|
|
189
|
+
"startedAt",
|
|
190
|
+
],
|
|
191
|
+
filter: { column: "ProcessId", op: "eq", values: valid },
|
|
192
|
+
}, {
|
|
193
193
|
timeoutMs: RESOURCE_SAMPLE_QUERY_TIMEOUT_MS,
|
|
194
|
-
onTimeout: (child) => terminateScannerChild(child, {
|
|
194
|
+
onTimeout: (child) => terminateScannerChild(child, {
|
|
195
|
+
kind: "resource-sampler-scanner-escalated",
|
|
196
|
+
timeoutMs: RESOURCE_SAMPLE_QUERY_TIMEOUT_MS,
|
|
197
|
+
}),
|
|
195
198
|
});
|
|
196
199
|
if (query.status !== "ok") {
|
|
197
200
|
recordQueryFailure("windows-process-table", query.status, query.exitCode);
|
|
@@ -200,20 +203,37 @@ async function sampleProcessesWindows(valid) {
|
|
|
200
203
|
try {
|
|
201
204
|
const now = Date.now();
|
|
202
205
|
const seen = new Set();
|
|
203
|
-
for (const
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
206
|
+
for (const row of query.rows) {
|
|
207
|
+
const pid = row.pid;
|
|
208
|
+
const workingSet = row.rssBytes;
|
|
209
|
+
const kernel100ns = row.cpuKernel100ns;
|
|
210
|
+
const user100ns = row.cpuUser100ns;
|
|
211
|
+
const processIdentity = row.startedAt ?? "";
|
|
212
|
+
// The seam already rejects a non-integer or negative column as
|
|
213
|
+
// UNKNOWN (undefined), so an absent value here means the row cannot
|
|
214
|
+
// be sampled — never that the process used zero.
|
|
215
|
+
if (workingSet === undefined ||
|
|
216
|
+
kernel100ns === undefined ||
|
|
217
|
+
user100ns === undefined ||
|
|
218
|
+
processIdentity.length === 0)
|
|
214
219
|
continue;
|
|
215
220
|
const cpuMs = Math.round(kernel100ns / 1e4) + Math.round(user100ns / 1e4);
|
|
216
|
-
const
|
|
221
|
+
const historyKey = `${pid}:${processIdentity}`;
|
|
222
|
+
// A reused PID must start a fresh rate window. Drop every prior identity
|
|
223
|
+
// for this PID before looking up the current one.
|
|
224
|
+
for (const [key, prior] of windowsCpuHistory) {
|
|
225
|
+
if (prior.processIdentity !== processIdentity &&
|
|
226
|
+
key.startsWith(`${pid}:`)) {
|
|
227
|
+
windowsCpuHistory.delete(key);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const prev = windowsCpuHistory.get(historyKey);
|
|
231
|
+
if (prev && cpuMs < prev.cpuMs) {
|
|
232
|
+
// A counter reset is not a flat sample. Retire the baseline so the
|
|
233
|
+
// next valid observation starts a new rate window.
|
|
234
|
+
windowsCpuHistory.delete(historyKey);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
217
237
|
let cpuPercent = 0;
|
|
218
238
|
if (prev) {
|
|
219
239
|
const wallMs = now - prev.ts;
|
|
@@ -223,16 +243,30 @@ async function sampleProcessesWindows(valid) {
|
|
|
223
243
|
cpuPercent = 0;
|
|
224
244
|
}
|
|
225
245
|
}
|
|
226
|
-
windowsCpuHistory.set(
|
|
246
|
+
windowsCpuHistory.set(historyKey, { processIdentity, cpuMs, ts: now });
|
|
227
247
|
seen.add(pid);
|
|
228
248
|
samples.set(pid, { rssBytes: workingSet, cpuPercent });
|
|
229
249
|
}
|
|
230
250
|
// Prune stale history so pids that have gone away don't accumulate.
|
|
231
|
-
for (const [
|
|
251
|
+
for (const [key, entry] of windowsCpuHistory) {
|
|
252
|
+
const pid = Number(key.slice(0, key.indexOf(":")));
|
|
232
253
|
if (!seen.has(pid) && now - entry.ts > CPU_HISTORY_MAX_AGE_MS) {
|
|
233
|
-
windowsCpuHistory.delete(
|
|
254
|
+
windowsCpuHistory.delete(key);
|
|
234
255
|
}
|
|
235
256
|
}
|
|
257
|
+
while (windowsCpuHistory.size > CPU_HISTORY_MAX_ENTRIES) {
|
|
258
|
+
let oldestKey;
|
|
259
|
+
let oldestTs = Number.POSITIVE_INFINITY;
|
|
260
|
+
for (const [key, entry] of windowsCpuHistory) {
|
|
261
|
+
if (entry.ts < oldestTs) {
|
|
262
|
+
oldestKey = key;
|
|
263
|
+
oldestTs = entry.ts;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (oldestKey === undefined)
|
|
267
|
+
break;
|
|
268
|
+
windowsCpuHistory.delete(oldestKey);
|
|
269
|
+
}
|
|
236
270
|
}
|
|
237
271
|
catch {
|
|
238
272
|
// Parsing must never throw into the caller; best-effort.
|
|
@@ -264,6 +298,11 @@ export async function sampleProcesses(pids) {
|
|
|
264
298
|
const stat = stats[String(pid)];
|
|
265
299
|
if (!stat)
|
|
266
300
|
continue; // pidusage couldn't resolve this pid — leave absent
|
|
301
|
+
if (!Number.isFinite(stat.cpu) ||
|
|
302
|
+
stat.cpu < 0 ||
|
|
303
|
+
!Number.isFinite(stat.memory) ||
|
|
304
|
+
stat.memory < 0)
|
|
305
|
+
continue;
|
|
267
306
|
result.set(pid, {
|
|
268
307
|
rssBytes: stat.memory,
|
|
269
308
|
cpuPercent: stat.cpu,
|
|
@@ -313,28 +352,88 @@ export class UsageAccumulator {
|
|
|
313
352
|
}
|
|
314
353
|
}
|
|
315
354
|
/**
|
|
316
|
-
*
|
|
355
|
+
* #2358: sample a live process tree twice across a short window and answer
|
|
356
|
+
* whether it burned CPU above `floorPercent`.
|
|
317
357
|
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
358
|
+
* The notify-stall breaker uses this to tell a BUSY server (burning a core
|
|
359
|
+
* while it drains a burst) from a genuinely DEAD input path (flat CPU), and
|
|
360
|
+
* only tears the latter down. It reuses the same platform machinery as
|
|
361
|
+
* `startSpawnUsageSampler`: on Windows the direct child may be a `cmd`/`.cmd`
|
|
362
|
+
* shim that does no work itself, so the live descendant tree resolves once per
|
|
363
|
+
* read and CPU% is summed across it.
|
|
322
364
|
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
* always safe to call even if zero samples ever landed.
|
|
329
|
-
*
|
|
330
|
-
* Windows note: `clients/safe-spawn.ts` spawns with `shell: true` on Windows,
|
|
331
|
-
* so `pid` here is `cmd.exe`'s pid, not the real tool's — sampling it alone
|
|
332
|
-
* would report near-zero usage for the whole invocation. Each Windows tick
|
|
333
|
-
* resolves `pid`'s live descendant tree (`findDescendantPidsWindows`) and
|
|
334
|
-
* sums usage across `pid` + every descendant, so a `node`/`npx`-wrapped tool
|
|
335
|
-
* (or one that re-execs itself) is actually captured. POSIX spawns are
|
|
336
|
-
* unwrapped (`shell: false`), so `pid` there is already the real tool.
|
|
365
|
+
* Best-effort like every other export here: a failed query loses a data point,
|
|
366
|
+
* it never throws, and it never blocks for longer than the query timeouts plus
|
|
367
|
+
* `windowMs`. The target itself must resolve for `measured` to be true;
|
|
368
|
+
* missing descendants are partial but valid evidence. An unmeasurable target
|
|
369
|
+
* answers `{ busy: false, measured: false }` for the caller to classify.
|
|
337
370
|
*/
|
|
371
|
+
export async function sampleProcessTreeCpuPercent(pid, windowMs = 1200, floorPercent = 10) {
|
|
372
|
+
if (!Number.isFinite(pid) || pid <= 0) {
|
|
373
|
+
return { busy: false, measured: false, cpuPercent: null };
|
|
374
|
+
}
|
|
375
|
+
const targetPid = pid;
|
|
376
|
+
const readOnce = async () => {
|
|
377
|
+
try {
|
|
378
|
+
const descendants = runningOnWindows()
|
|
379
|
+
? await findDescendantPidsWindows(targetPid)
|
|
380
|
+
: [];
|
|
381
|
+
if (descendants === null)
|
|
382
|
+
return null;
|
|
383
|
+
const pids = runningOnWindows()
|
|
384
|
+
? [targetPid, ...descendants]
|
|
385
|
+
: [targetPid];
|
|
386
|
+
const usageByPid = await sampleProcesses(pids);
|
|
387
|
+
if (usageByPid === null)
|
|
388
|
+
return null;
|
|
389
|
+
const targetUsage = usageByPid.get(targetPid);
|
|
390
|
+
if (!targetUsage)
|
|
391
|
+
return { cpuPercent: 0, measured: false };
|
|
392
|
+
let cpuPercent = 0;
|
|
393
|
+
for (const usage of usageByPid.values())
|
|
394
|
+
cpuPercent += usage.cpuPercent;
|
|
395
|
+
return { cpuPercent, measured: true };
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
// The FIRST read is a BASELINE, not evidence: it re-anchors this pid's CPU
|
|
402
|
+
// history (Windows' own map, pidusage's on POSIX) so the second read is a
|
|
403
|
+
// rate over `windowMs` and nothing else. Both reads must retain the target;
|
|
404
|
+
// disappearance or query failure is explicitly unmeasured, never flat.
|
|
405
|
+
const first = await readOnce();
|
|
406
|
+
const second = await new Promise((resolve) => {
|
|
407
|
+
const timer = setTimeout(() => {
|
|
408
|
+
void readOnce().then(resolve);
|
|
409
|
+
}, windowMs);
|
|
410
|
+
timer.unref?.();
|
|
411
|
+
});
|
|
412
|
+
if (first === null && second === null) {
|
|
413
|
+
return { busy: false, measured: false, cpuPercent: null };
|
|
414
|
+
}
|
|
415
|
+
if (first === null ||
|
|
416
|
+
second === null ||
|
|
417
|
+
!first.measured ||
|
|
418
|
+
!second.measured) {
|
|
419
|
+
return { busy: false, measured: false, cpuPercent: null };
|
|
420
|
+
}
|
|
421
|
+
// #2358 (post-#2382): the verdict is the WINDOW read alone. Whatever the
|
|
422
|
+
// baseline read reports is a rate since the LAST caller's observation —
|
|
423
|
+
// clients/quiet-window.ts's heartbeat samples every recorded LSP child once
|
|
424
|
+
// per tick, and pidusage keeps 60 s of per-pid history — so folding it in
|
|
425
|
+
// (`Math.max(first, second)`) let CPU the process had already stopped
|
|
426
|
+
// burning vote "busy". A scanner that drained its burst and then wedged (the
|
|
427
|
+
// issue's own opengrep evidence) was therefore deferred as progressing and
|
|
428
|
+
// died at the hard cap, misrecorded as `cap-exceeded`, instead of being torn
|
|
429
|
+
// down on its budget as `budget-exceeded-cpu-flat`.
|
|
430
|
+
const observed = second.cpuPercent;
|
|
431
|
+
return {
|
|
432
|
+
busy: observed > floorPercent,
|
|
433
|
+
measured: true,
|
|
434
|
+
cpuPercent: observed,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
338
437
|
export function startSpawnUsageSampler(pid, intervalMs = 750) {
|
|
339
438
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
340
439
|
return { stop: () => null };
|