@herbertgao/pi-extensions 2026.9.0 → 2026.9.1
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/@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 +988 -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 -2
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
- 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 -2
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +1 -3
- package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -0
- 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-telemetry.js +59 -4
- package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +2 -2
- 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 +44 -18
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -2
- 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 +47 -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 +171 -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 +167 -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 +197 -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 -2
- 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-line-freshness.js +12 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +14 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -18
- package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -7
- 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 +380 -26
- 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 +0 -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 +0 -1
- 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 +13 -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 +0 -1
- 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 +0 -1
- 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 +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -1
- 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 -4
- 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/vale.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -3
- 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/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-time.js +6 -1
- package/node_modules/pi-lens/dist/clients/file-utils.js +112 -9
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
- package/node_modules/pi-lens/dist/clients/format-service.js +6 -1
- package/node_modules/pi-lens/dist/clients/formatters.js +276 -102
- package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +114 -19
- package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
- package/node_modules/pi-lens/dist/clients/git-guard.js +1 -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/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 +71 -52
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +104 -230
- package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
- package/node_modules/pi-lens/dist/clients/language-registry.js +595 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +160 -2
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +13 -0
- package/node_modules/pi-lens/dist/clients/lens-config.js +141 -30
- package/node_modules/pi-lens/dist/clients/lens-engine.js +30 -37
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +59 -6
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -2
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +168 -30
- package/node_modules/pi-lens/dist/clients/lsp/config.js +483 -158
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +15 -2
- package/node_modules/pi-lens/dist/clients/lsp/index.js +676 -71
- package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -169
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -16
- 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/tsserver-sync.js +9 -2
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.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/middle-man-analysis.js +2 -4
- package/node_modules/pi-lens/dist/clients/module-report.js +20 -45
- 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 -20
- 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 +68 -22
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +4 -0
- 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 +428 -13
- 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 +77 -16
- 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-diagnostics/fresh-fetch.js +4 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +393 -122
- 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 +15 -4
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
- package/node_modules/pi-lens/dist/clients/read-guard.js +21 -0
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +152 -109
- 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 +18 -2
- 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 +139 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +168 -35
- 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 -1
- 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/sessionstart-logger.js +12 -3
- package/node_modules/pi-lens/dist/clients/sgconfig.js +6 -1
- package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
- 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 +239 -0
- package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
- package/node_modules/pi-lens/dist/clients/tool-policy.js +91 -21
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +86 -48
- 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/typos-config.js +5 -0
- package/node_modules/pi-lens/dist/clients/user-notify.js +6 -2
- package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/word-index-store.js +20 -5
- package/node_modules/pi-lens/dist/clients/word-index.js +243 -31
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +3 -0
- package/node_modules/pi-lens/dist/index.js +64335 -54300
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -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 +95 -24
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +67 -80
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
- package/node_modules/pi-lens/dist/tools/shared.js +0 -1
- 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/language-coverage.md +2 -2
- 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 +27 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +38 -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 +182 -0
- 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/package.json +15 -23
- 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/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/rpc-load-check.mjs +3 -1
- 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 +5 -5
|
@@ -34,7 +34,8 @@ import { clearGraphCache } from "./review-graph/builder.js";
|
|
|
34
34
|
import { BoundedLruCache } from "./bounded-cache.js";
|
|
35
35
|
import { RUNTIME_CONFIG } from "./runtime-config.js";
|
|
36
36
|
import { getAmbientAbortSignal, safeSpawnAsync } from "./safe-spawn.js";
|
|
37
|
-
import {
|
|
37
|
+
import { bounded } from "./deadline-utils.js";
|
|
38
|
+
import { enabledAuxiliaryLspServerIds } from "./dispatch/auxiliary-lsp.js";
|
|
38
39
|
import { recordDegradationOnce } from "./degradation-ledger.js";
|
|
39
40
|
import { dropFindingsForMissingPaths } from "./advisory-provenance.js";
|
|
40
41
|
import { getAutofixPolicyForFile, getPreferredAutofixTools, getRubocopCommand, hasBiomeConfig, hasDetektConfig, hasEslintConfig, hasGolangciConfig, hasKtfmtConfig, hasKtlintConfig, hasOxlintConfig, hasRubocopConfig, hasSqlfluffConfig, hasStylelintConfig, markdownlintConfigArgs, } from "./tool-policy.js";
|
|
@@ -687,7 +688,6 @@ export async function resyncLspFile(filePath, fileContent, needsContentRefresh,
|
|
|
687
688
|
const abort = getAmbientAbortSignal();
|
|
688
689
|
if (abort?.aborted)
|
|
689
690
|
return;
|
|
690
|
-
const bail = combineAbortSignals(abort, AbortSignal.timeout(budgetMs));
|
|
691
691
|
const startedAt = Date.now();
|
|
692
692
|
const touch = lspService
|
|
693
693
|
.touchFile(filePath, fileContent, {
|
|
@@ -701,13 +701,45 @@ export async function resyncLspFile(filePath, fileContent, needsContentRefresh,
|
|
|
701
701
|
dbg(`LSP resync after autofix error: ${err}`);
|
|
702
702
|
return "done";
|
|
703
703
|
});
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
704
|
+
// #2540: Kick off auxiliary server acquisition concurrently and unawaited
|
|
705
|
+
// with the ambient turn signal so auxiliary warmup overlaps with the primary
|
|
706
|
+
// server during resync. Leaves the single-occupancy deferred slot (#2509)
|
|
707
|
+
// free for actionable-warnings while Escape mid-turn abandons the wait.
|
|
708
|
+
//
|
|
709
|
+
// #2582 F3 — the #1766 F3 invariant re-opened for a different method.
|
|
710
|
+
// #2540 placed this kick-off BEFORE the primary touch, inside
|
|
711
|
+
// resyncLspFile's swallow-all catch, so anything that threw out of
|
|
712
|
+
// auxiliary acquisition abandoned the PRIMARY sync before it started:
|
|
713
|
+
// no touchFile, and no lsp_sync_abandoned record for the stall.
|
|
714
|
+
// Auxiliary warmup is a best-effort OVERLAP, never a precondition:
|
|
715
|
+
// the primary touch is already in flight above, and the async IIFE
|
|
716
|
+
// turns a synchronous throw out of the call into a rejection so
|
|
717
|
+
// `.catch` covers both failure directions. Order and containment are
|
|
718
|
+
// the invariant; do not hoist this back above the touch.
|
|
719
|
+
const auxServerIds = enabledAuxiliaryLspServerIds(getFlag);
|
|
720
|
+
if (auxServerIds.length > 0) {
|
|
721
|
+
void (async () => lspService.getAuxiliaryClientsForFile(filePath, new Set(auxServerIds), undefined, LSP_SPAWN_BUDGET_MS, abort, "tool_result_edit"))().catch(() => { });
|
|
722
|
+
}
|
|
723
|
+
// #2523 slice 2: this was `combineAbortSignals(abort,
|
|
724
|
+
// AbortSignal.timeout(budgetMs))` fed into a hand-rolled
|
|
725
|
+
// `Promise.race` — a private fifth copy of "deadline AND signal".
|
|
726
|
+
// `bounded()` is the same thing with the abandonment recorded, and
|
|
727
|
+
// without the composite signal that #2530 review F4 measured
|
|
728
|
+
// accumulating on the SOURCE.
|
|
729
|
+
//
|
|
730
|
+
// `touch` never rejects (it catches above) and never resolves
|
|
731
|
+
// `undefined`, so `undefined` here means exactly "a bound fired".
|
|
732
|
+
const outcome = await bounded(touch, {
|
|
733
|
+
ms: budgetMs,
|
|
734
|
+
// The ambient turn signal: set for the whole tool_result path and
|
|
735
|
+
// absent only in a bare unit harness, where the wall budget is
|
|
736
|
+
// still live (`bounded()` reads a missing signal as one that
|
|
737
|
+
// never aborts).
|
|
738
|
+
signal: abort,
|
|
739
|
+
hook: "tool_result_edit",
|
|
740
|
+
label: "resyncLspFile",
|
|
708
741
|
});
|
|
709
|
-
|
|
710
|
-
if (outcome === "bailed") {
|
|
742
|
+
if (outcome === undefined) {
|
|
711
743
|
// Abandon the still-pending write; the edit continues. Log it so this
|
|
712
744
|
// stall — previously an invisible hang — is queryable in latency.log.
|
|
713
745
|
//
|
|
@@ -717,12 +749,18 @@ export async function resyncLspFile(filePath, fileContent, needsContentRefresh,
|
|
|
717
749
|
// old wording blamed as "slow/wedged" did not exist yet. Distinguish
|
|
718
750
|
// the two via a fresh, synchronous inFlight lookup so the record keeps
|
|
719
751
|
// the discriminating identity (which server, which lifecycle state).
|
|
720
|
-
//
|
|
721
|
-
//
|
|
722
|
-
//
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
752
|
+
// #2592: the `typeof lspService.isSpawnInFlight === "function"`
|
|
753
|
+
// hedge that used to wrap this call is GONE. It existed for one
|
|
754
|
+
// reason — 19 hand-rolled `getLSPService` doubles that lacked the
|
|
755
|
+
// method, whose TypeError would have landed in the catch below and
|
|
756
|
+
// suppressed this record entirely (#1766 F3) — and that population
|
|
757
|
+
// is now zero: every double is seeded from
|
|
758
|
+
// `makeLspServiceDouble`, `tests/support/lsp-double-baseline.json`
|
|
759
|
+
// is `{}`, and `tests/config/lsp-service-double-sweep.test.ts` reds
|
|
760
|
+
// on a fresh hand-rolled one. The real `LSPService` has always had
|
|
761
|
+
// the method (clients/lsp/index.ts), so the fallback was a test
|
|
762
|
+
// shape leaking into production — AGENTS.md shape 7.
|
|
763
|
+
const spawnInFlight = !abort?.aborted && lspService.isSpawnInFlight(filePath);
|
|
726
764
|
const reason = abort?.aborted
|
|
727
765
|
? "aborted"
|
|
728
766
|
: spawnInFlight
|
|
@@ -783,12 +821,29 @@ export async function runFormatPhase(filePath, getFormatService, dbg) {
|
|
|
783
821
|
let formatChanged = false;
|
|
784
822
|
let formattersUsed = [];
|
|
785
823
|
const formatFailures = [];
|
|
824
|
+
const formatUnavailable = [];
|
|
786
825
|
let fileContent;
|
|
787
826
|
const formatService = getFormatService();
|
|
788
827
|
try {
|
|
789
828
|
formatService.recordRead(filePath);
|
|
790
829
|
const result = await formatService.formatFile(filePath);
|
|
791
|
-
|
|
830
|
+
// An unavailable tool is NOT a formatter that ran (#2413): keep it out of
|
|
831
|
+
// `formattersUsed` (which drives change bookkeeping / turn summaries) and
|
|
832
|
+
// out of `formatFailures` (which requeues). Record it once, distinctly.
|
|
833
|
+
for (const f of result.formatters) {
|
|
834
|
+
if (f.outcome !== "unavailable")
|
|
835
|
+
continue;
|
|
836
|
+
const reason = f.error ?? "formatter executable not found";
|
|
837
|
+
formatUnavailable.push({ formatter: f.name, reason });
|
|
838
|
+
recordDegradationOnce({
|
|
839
|
+
kind: "formatter-unavailable",
|
|
840
|
+
subject: `${f.name}:${path.basename(filePath)}`,
|
|
841
|
+
reason,
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
formattersUsed = result.formatters
|
|
845
|
+
.filter((f) => f.outcome !== "unavailable")
|
|
846
|
+
.map((f) => f.name);
|
|
792
847
|
if (result.anyChanged) {
|
|
793
848
|
formatChanged = true;
|
|
794
849
|
dbg("autoformat: " +
|
|
@@ -828,7 +883,13 @@ export async function runFormatPhase(filePath, getFormatService, dbg) {
|
|
|
828
883
|
catch {
|
|
829
884
|
fileContent = undefined;
|
|
830
885
|
}
|
|
831
|
-
return {
|
|
886
|
+
return {
|
|
887
|
+
formatChanged,
|
|
888
|
+
formattersUsed,
|
|
889
|
+
formatFailures,
|
|
890
|
+
formatUnavailable,
|
|
891
|
+
fileContent,
|
|
892
|
+
};
|
|
832
893
|
}
|
|
833
894
|
/**
|
|
834
895
|
* Build the 🔴 STOP blocker output with an inline code snippet for each
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The #2506 probe-home redirect, whole: the decision, the per-process memo it
|
|
3
|
+
* is cached in, and the degradation event that decision produced.
|
|
4
|
+
*
|
|
5
|
+
* WHAT IT DECIDES. An ad-hoc probe against the BUILT `clients/*.js` — a bare
|
|
6
|
+
* `node -e`, a throwaway `.mjs`, a harness script — has no `PI_LENS_HOME` pin,
|
|
7
|
+
* so every logger and ledger it touches used to write into the maintainer's
|
|
8
|
+
* REAL `~/.pi-lens`. Confirmed live on 2026-09-02: two review probes left 42
|
|
9
|
+
* rows of `/p/.pi-lens.json` fixture garbage in real telemetry. When
|
|
10
|
+
* `PI_LENS_HOME` is unset and the process looks like a probe — `PILENS_PROBE=1`,
|
|
11
|
+
* a `cwd` inside a specific agent worktree, or a `cwd` under `os.tmpdir()` —
|
|
12
|
+
* `resolveProbeHomeDir()` answers with a per-probe directory instead.
|
|
13
|
+
*
|
|
14
|
+
* WHY IT ALL LIVES HERE and not beside its `getGlobalPiLensDir()` sibling in
|
|
15
|
+
* `file-utils.ts`, where round 2 of #2516 left it. `file-utils.ts` sits on an
|
|
16
|
+
* import cycle (it still does, via `safe-spawn.js` -> `resource-sampler.js` ->
|
|
17
|
+
* `instance-reaper.js` -> `file-utils.js`; the shorter route through
|
|
18
|
+
* `degradation-ledger.js` -> `extension-log.js` was this move's doing —
|
|
19
|
+
* `extension-log.ts` imported `file-utils.ts` for THIS function and nothing
|
|
20
|
+
* else, so the edge went with it, and the known-violations baseline dropped
|
|
21
|
+
* from 33 pinned cycles to 12 with none added). Fifteen log-family modules
|
|
22
|
+
* call `getGlobalPiLensLogDir()` at their OWN module
|
|
23
|
+
* top level. Under vitest's vite-SSR transform each import becomes a
|
|
24
|
+
* `__vite_ssr_import_N__` variable assigned in source order, so one of those
|
|
25
|
+
* top-level calls re-enters `file-utils.ts` while its body is only PARTWAY
|
|
26
|
+
* through those assignments — and a resolver sitting there then dereferences a
|
|
27
|
+
* binding that has not been assigned yet, throwing `ReferenceError: Cannot
|
|
28
|
+
* access '__vite_ssr_import_N__' before initialization`. Measured live: the
|
|
29
|
+
* re-entry arrives through `file-utils.ts`'s FIFTH import,
|
|
30
|
+
* `spawn-timeout-cooldown.js`, so every binding declared from the sixth import
|
|
31
|
+
* onward is unsafe on that path — which is most of the file.
|
|
32
|
+
*
|
|
33
|
+
* Round 2 of #2516 papered over that with an `isTestMode()` early return placed
|
|
34
|
+
* ahead of the first unsafe dereference. That was load-order luck, not a
|
|
35
|
+
* design: moving `file-utils.ts`'s own `isTestMode` import from fifth to last
|
|
36
|
+
* makes the GUARD LINE ITSELF throw the same error. It also DEFEATED the
|
|
37
|
+
* redirect for every process that has `VITEST` set but no `PI_LENS_HOME` pin —
|
|
38
|
+
* vitest's own globalSetup/child processes — sending their logs to the real
|
|
39
|
+
* home: the exact hermeticity hole the redirect exists to close.
|
|
40
|
+
*
|
|
41
|
+
* THIS module is off that cycle. It imports only node builtins and
|
|
42
|
+
* `path-utils.js`, itself a leaf (node builtins plus the vendored, zero-import
|
|
43
|
+
* `deps/minimatch.js`). A module off every cycle is fully evaluated before any
|
|
44
|
+
* importer's body runs — in plain ESM and under the vite-SSR transform alike —
|
|
45
|
+
* so none of its own bindings can ever be observed uninitialized, wherever an
|
|
46
|
+
* importer chooses to place the import. That is what makes the guard
|
|
47
|
+
* unnecessary rather than merely well-placed, and it is why module-scope
|
|
48
|
+
* `const`s (including the `Symbol.for` below) are ordinary here where they were
|
|
49
|
+
* a hazard in `file-utils.ts`.
|
|
50
|
+
*
|
|
51
|
+
* `tests/config/global-dir-probe-redirect.test.ts` pins the behaviour through
|
|
52
|
+
* real spawned `node` children, including the VITEST-set-but-unpinned case.
|
|
53
|
+
*/
|
|
54
|
+
import * as fs from "node:fs";
|
|
55
|
+
import * as os from "node:os";
|
|
56
|
+
import * as path from "node:path";
|
|
57
|
+
import { isUnderDir, normalizeFilePath } from "./path-utils.js";
|
|
58
|
+
/**
|
|
59
|
+
* The memo lives in a process-wide `globalThis` slot rather than a plain
|
|
60
|
+
* module-scope `let` so that a bundled `dist/index.js` and the loose
|
|
61
|
+
* `clients/*.js` loaded into the same process — the shape #335 ships — share
|
|
62
|
+
* ONE decision instead of memoizing two. A `Symbol.for` key is interned by
|
|
63
|
+
* string, so both copies of this module resolve to the same slot.
|
|
64
|
+
*/
|
|
65
|
+
const RESOLUTION_SLOT = Symbol.for("pi-lens.probe-home-state.resolution");
|
|
66
|
+
/** The memoized resolution for this process, or `undefined` before first use. */
|
|
67
|
+
export function getProbeHomeResolution() {
|
|
68
|
+
return globalThis[RESOLUTION_SLOT];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The degradation row for a redirect that actually happened, or `undefined`
|
|
72
|
+
* when this process was not redirected (or has not resolved yet).
|
|
73
|
+
* `degradation-ledger.ts`'s `getDegradationSummary()` is the only caller — it
|
|
74
|
+
* folds this at READ time rather than having the resolver push a row, because a
|
|
75
|
+
* direct import between `file-utils.ts` and `degradation-ledger.ts` in EITHER
|
|
76
|
+
* direction (a dynamic `import()` included) adds unpinned `no-client-cycles`
|
|
77
|
+
* violations to the cycle named in this module's doc comment.
|
|
78
|
+
*/
|
|
79
|
+
export function getProbeHomeRedirectEvent() {
|
|
80
|
+
return getProbeHomeResolution()?.event;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Where pi-lens writes its LOGS, ledger rows and debug dumps: `~/.pi-lens/` by
|
|
84
|
+
* default, exactly like `getGlobalPiLensDir()` in `file-utils.ts` — but
|
|
85
|
+
* redirected to a per-probe directory when this process looks like an ad-hoc
|
|
86
|
+
* probe rather than a real pi session (#2506).
|
|
87
|
+
*
|
|
88
|
+
* Every log-family writer routes here: `latency.log`, `extension.log`,
|
|
89
|
+
* `sessionstart.log`, `cascade.log`, `read-guard.log`, `tree-sitter.log`,
|
|
90
|
+
* `word-index.log`, `bus-events.log`, `dispositions.log`, `dead-code.log`,
|
|
91
|
+
* `ast-grep-tools.log`, `actionable-warnings.log`, `review-graph.log`, the
|
|
92
|
+
* `logs/*.jsonl` diagnostic dir, the debug handle/heap dumps,
|
|
93
|
+
* `log-cleanup.ts`'s sweep over all of the above, and `smells-rollup.ts`'s
|
|
94
|
+
* read-side tail of `latency.log`/`bus-events.log` (a reader, but it must
|
|
95
|
+
* follow the same root or it tails a file nobody is writing).
|
|
96
|
+
*
|
|
97
|
+
* WHY IT LIVES IN THIS MODULE and not next to its `getGlobalPiLensDir()`
|
|
98
|
+
* sibling in `file-utils.ts` — see this file's header. Fifteen of the writers
|
|
99
|
+
* listed above call this function at their OWN module top level, and
|
|
100
|
+
* `file-utils.ts` is on an import cycle, so resolving from there could observe
|
|
101
|
+
* `file-utils.ts` mid-initialization. Resolving from a module off every cycle
|
|
102
|
+
* cannot.
|
|
103
|
+
*
|
|
104
|
+
* `PI_LENS_HOME` wins over any redirect, which is how
|
|
105
|
+
* `tests/support/vitest-setup.ts` keeps test workers hermetic. Note the
|
|
106
|
+
* redirect still applies under vitest for processes that setup does not cover
|
|
107
|
+
* (globalSetup, and children spawned without the pin): #2516 round 2's
|
|
108
|
+
* `isTestMode()` guard sent exactly those to the developer's real home.
|
|
109
|
+
*
|
|
110
|
+
* Deliberately does NOT write a line to the terminal when it fires. #1333 gives
|
|
111
|
+
* pi sole ownership of the TTY, and
|
|
112
|
+
* `tests/clients/extension-terminal-silence.test.ts` enforces that nothing
|
|
113
|
+
* under `clients/` calls `process.std*.write` or `console.*` (round 2 of
|
|
114
|
+
* #2506 shipped a `process.stderr.write` here and was red in CI for exactly
|
|
115
|
+
* that). Nor can it log through `extension-log.ts`'s
|
|
116
|
+
* `createSubsystemLogger`: that module imports THIS one, so an import back
|
|
117
|
+
* the other way would close a new, unpinned `no-client-cycles` cycle — and
|
|
118
|
+
* `extension.log` is itself redirected by this very function, so the line
|
|
119
|
+
* would land in the probe home nobody reads. The durable signal is the
|
|
120
|
+
* `global-dir-probe-redirect` degradation row instead, folded into
|
|
121
|
+
* `getDegradationSummary()` at read time via
|
|
122
|
+
* `getProbeHomeRedirectEvent()` above.
|
|
123
|
+
*/
|
|
124
|
+
export function getGlobalPiLensLogDir() {
|
|
125
|
+
const override = process.env.PI_LENS_HOME?.trim();
|
|
126
|
+
if (override)
|
|
127
|
+
return path.resolve(override);
|
|
128
|
+
return resolveProbeHomeDir() ?? path.join(os.homedir(), ".pi-lens");
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Resolve the probe-home redirect at most ONCE per process, caching both the
|
|
132
|
+
* "yes, here" and the "no redirect" answers (#2506 round 3, F5). Round 2 read
|
|
133
|
+
* `process.cwd()` live on every call, which gave a single process up to three
|
|
134
|
+
* different global dirs as it chdir'd, and re-ran the whole decision on the hot
|
|
135
|
+
* logging path.
|
|
136
|
+
*
|
|
137
|
+
* `undefined` means "no redirect applies"; `getGlobalPiLensLogDir()` above
|
|
138
|
+
* falls back to the real home. This function never consults `PI_LENS_HOME` —
|
|
139
|
+
* that pin is handled by its caller, ahead of this decision, so a pinned
|
|
140
|
+
* process never memoizes a probe answer it would then keep after the pin
|
|
141
|
+
* changed.
|
|
142
|
+
*/
|
|
143
|
+
function resolveProbeHomeDir() {
|
|
144
|
+
const slot = globalThis;
|
|
145
|
+
const cached = slot[RESOLUTION_SLOT];
|
|
146
|
+
if (cached)
|
|
147
|
+
return cached.probeHome;
|
|
148
|
+
const cwd = process.cwd();
|
|
149
|
+
const probeHome = computeProbeHomeDir(cwd);
|
|
150
|
+
slot[RESOLUTION_SLOT] = {
|
|
151
|
+
probeHome,
|
|
152
|
+
event: probeHome ? { probeHome, cwd } : undefined,
|
|
153
|
+
};
|
|
154
|
+
return probeHome;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Clears the memoized decision AND the event it produced, so the next
|
|
158
|
+
* `getGlobalPiLensLogDir()` call re-resolves from scratch. Exercised by
|
|
159
|
+
* `tests/config/global-dir-probe-redirect.test.ts`, which drives the resolver
|
|
160
|
+
* repeatedly under different in-process conditions; without it the first call
|
|
161
|
+
* in a worker would pin the answer for every later case in the same file.
|
|
162
|
+
*/
|
|
163
|
+
export function _resetProbeHomeRedirectStateForTests() {
|
|
164
|
+
globalThis[RESOLUTION_SLOT] = undefined;
|
|
165
|
+
}
|
|
166
|
+
/** The redirect decision itself. */
|
|
167
|
+
function computeProbeHomeDir(cwd) {
|
|
168
|
+
// The explicit force wins first: it is how a probe run from an ordinary
|
|
169
|
+
// project checkout opts in, and it must work even under a harness that has
|
|
170
|
+
// set a test-mode marker but no PI_LENS_HOME.
|
|
171
|
+
if (process.env.PILENS_PROBE === "1") {
|
|
172
|
+
return path.join(cwd, ".pi-lens-probe-home");
|
|
173
|
+
}
|
|
174
|
+
const worktreeRoot = findAgentWorktreeRoot(cwd);
|
|
175
|
+
if (worktreeRoot)
|
|
176
|
+
return path.join(worktreeRoot, ".pi-lens-probe-home");
|
|
177
|
+
if (isUnderRealDir(cwd, os.tmpdir())) {
|
|
178
|
+
return path.join(cwd, ".pi-lens-probe-home");
|
|
179
|
+
}
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* The directory of the SPECIFIC agent worktree containing `cwd`, or
|
|
184
|
+
* `undefined` when `cwd` is not inside one.
|
|
185
|
+
*
|
|
186
|
+
* Requires at least one path segment AFTER `worktrees/`, and anchors the
|
|
187
|
+
* probe home at that segment rather than at the live `cwd` (#2506 round 3,
|
|
188
|
+
* F5). Round 2's trailing `(\/|$)` alternative also matched a `cwd` of
|
|
189
|
+
* `.claude/worktrees` ITSELF, which would have put the probe home in the
|
|
190
|
+
* SHARED worktrees parent — every concurrent agent on the box writing into
|
|
191
|
+
* one directory, the cross-agent collision this redirect is supposed to
|
|
192
|
+
* prevent. Anchoring at the worktree also means a probe run from a
|
|
193
|
+
* subdirectory (`<worktree>/clients`) and one run from the worktree root
|
|
194
|
+
* agree on a single probe home instead of scattering one per cwd.
|
|
195
|
+
*/
|
|
196
|
+
function findAgentWorktreeRoot(cwd) {
|
|
197
|
+
const normalized = normalizeFilePath(path.resolve(cwd));
|
|
198
|
+
const match = /(?:^|\/)\.claude\/worktrees\/[^/]+(?:\/|$)/.exec(normalized);
|
|
199
|
+
if (!match)
|
|
200
|
+
return undefined;
|
|
201
|
+
return normalized.slice(0, match.index + match[0].length).replace(/\/$/, "");
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* `isUnderDir`, but with both sides resolved through `realpath` first
|
|
205
|
+
* (#2506 round 3, F7).
|
|
206
|
+
*
|
|
207
|
+
* On macOS `os.tmpdir()` reports `/var/folders/...` while a `cwd` created
|
|
208
|
+
* under it resolves to `/private/var/folders/...` (`/var` is a symlink to
|
|
209
|
+
* `/private/var`), so the two NEVER prefix-match unresolved and the tmpdir
|
|
210
|
+
* branch would be dead on every Mac. Windows has the same shape with 8.3
|
|
211
|
+
* short names and substituted drives. Both sides go through the same
|
|
212
|
+
* function, so `realpathSync.native`'s canonical Windows casing applies
|
|
213
|
+
* consistently; the result is used only for this containment test, never to
|
|
214
|
+
* build the returned path, so no casing change can leak into a log path.
|
|
215
|
+
* Best-effort by design: a path that does not exist yet (or an EPERM) falls
|
|
216
|
+
* back to its literal form rather than throwing on the logging hot path.
|
|
217
|
+
*/
|
|
218
|
+
function isUnderRealDir(child, parent) {
|
|
219
|
+
return isUnderDir(toRealPath(child), toRealPath(parent));
|
|
220
|
+
}
|
|
221
|
+
function toRealPath(target) {
|
|
222
|
+
try {
|
|
223
|
+
return fs.realpathSync.native(target);
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return target;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared process-bridge registration helper (#2437).
|
|
3
|
+
*
|
|
4
|
+
* `clients/read-bridge.ts` (#1265) and `clients/mutation-bridge.ts` (#2423)
|
|
5
|
+
* each mount a versioned, frozen method-table object at a well-known
|
|
6
|
+
* `globalThis[Symbol.for("pi-lens:...")]` key so a same-process producer can
|
|
7
|
+
* reach pi-lens's bookkeeping without importing its internals. Both bridges
|
|
8
|
+
* carried an identical mount body — a first-wins existence check,
|
|
9
|
+
* `Object.freeze`, and a non-writable/non-configurable `Object.defineProperty`
|
|
10
|
+
* — differing only in their key and method table (named by the #2432 review,
|
|
11
|
+
* refs #2423). This module owns that body once.
|
|
12
|
+
*
|
|
13
|
+
* NOT `clients/process-singletons.ts` (#2146): that module keeps a single
|
|
14
|
+
* MUTABLE, adopt-if-version-compatible value per "family" inside ONE shared
|
|
15
|
+
* container (a different, single key mounts the container itself, and a
|
|
16
|
+
* version mismatch there means "discard and rebuild"). A bridge's contract is
|
|
17
|
+
* the opposite: the documented producer protocol reads
|
|
18
|
+
* `globalThis[Symbol.for("pi-lens:mutation-bridge")]` directly as a frozen,
|
|
19
|
+
* first-wins, unreplaceable property, and a version mismatch is the
|
|
20
|
+
* PRODUCER's problem — an unsupported bridge it should not call — never a
|
|
21
|
+
* signal for pi-lens to silently reset its own mount. Reusing the singleton
|
|
22
|
+
* container would change that documented external contract, so this stays a
|
|
23
|
+
* separate, deliberately smaller leaf built for the bridge shape only.
|
|
24
|
+
*
|
|
25
|
+
* STATIC IMPORTS: none, deliberately. Both `read-bridge.ts` and
|
|
26
|
+
* `mutation-bridge.ts` import this module; it must stay a dependency leaf so
|
|
27
|
+
* neither gains an edge onto anything else in `clients/` (`no-client-cycles`).
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Mount `build()`'s result at `globalThis[key]`, once per process.
|
|
31
|
+
*
|
|
32
|
+
* First-wins: if `key` already exists on `globalThis` — from an earlier call
|
|
33
|
+
* in this process, whether this bridge's own prior registration or a
|
|
34
|
+
* redundant re-activation — `build()` is never invoked and the existing
|
|
35
|
+
* mount is left untouched. The mounted value is frozen and installed
|
|
36
|
+
* non-writable/non-configurable so no later code can silently replace or
|
|
37
|
+
* extend it; the `in` check above is what keeps a redundant call from
|
|
38
|
+
* throwing on that now-frozen property.
|
|
39
|
+
*/
|
|
40
|
+
export function registerProcessBridge(key, build) {
|
|
41
|
+
if (key in globalThis)
|
|
42
|
+
return;
|
|
43
|
+
const bridge = Object.freeze(build());
|
|
44
|
+
Object.defineProperty(globalThis, key, {
|
|
45
|
+
value: bridge,
|
|
46
|
+
writable: false,
|
|
47
|
+
configurable: false,
|
|
48
|
+
enumerable: false,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The bridge mounted at `key`, or `undefined` when nothing is mounted, the
|
|
53
|
+
* mounted value is not an object, or its `version` does not equal `version`.
|
|
54
|
+
* A version mismatch means "not a bridge this caller recognizes" — never
|
|
55
|
+
* adopted and never reset; the reset-on-mismatch behavior belongs to
|
|
56
|
+
* `process-singletons.ts`'s different contract, not this one.
|
|
57
|
+
*/
|
|
58
|
+
export function getProcessBridge(key, version) {
|
|
59
|
+
const bridge = globalThis[key];
|
|
60
|
+
if (!bridge || typeof bridge !== "object")
|
|
61
|
+
return undefined;
|
|
62
|
+
const candidate = bridge;
|
|
63
|
+
if (candidate.version !== version)
|
|
64
|
+
return undefined;
|
|
65
|
+
return candidate;
|
|
66
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The extension runtime's door onto the ONE process-table seam (#2443).
|
|
3
|
+
*
|
|
4
|
+
* `scripts/lib/process-scan.mjs` owns what a process listing IS: the field →
|
|
5
|
+
* platform-column table, the Windows CIM/WQL and POSIX `ps` command
|
|
6
|
+
* composition, the WQL escaping, and the row parser. This module owns how
|
|
7
|
+
* clients/ RUNS one, which is genuinely different from how a script runs one
|
|
8
|
+
* and stays different on purpose:
|
|
9
|
+
*
|
|
10
|
+
* - the child and its stdout pipe are `unref`'d (#1155) so a one-shot
|
|
11
|
+
* `pi --print` can settle without waiting on a CIM query;
|
|
12
|
+
* - a child that blows its timeout is terminated through the caller's own
|
|
13
|
+
* tree-kill-and-verify machinery (#1864 F3), injected as `onTimeout` — a
|
|
14
|
+
* sweep that leaks its own scanner is the defect the sweep exists to fix;
|
|
15
|
+
* - the outcome carries a `SpawnCollectStatus`, so "the table is empty" and
|
|
16
|
+
* "the query never ran" stay distinguishable and the caller can record the
|
|
17
|
+
* degradation (#1857).
|
|
18
|
+
*
|
|
19
|
+
* WHY THE SEAM IS IMPORTED FROM scripts/ RATHER THAN LIVING HERE: the .mjs
|
|
20
|
+
* header argues it in full. Short version — `scripts/prune-agent-worktrees.mjs`
|
|
21
|
+
* is a SessionStart/SubagentStop hook that runs inside freshly created agent
|
|
22
|
+
* worktrees, where `clients/*.js` (gitignored build output) does not exist
|
|
23
|
+
* yet, so the shared half has to be the side that needs no build. This file
|
|
24
|
+
* is the single crossing point; nothing else in clients/ imports scripts/.
|
|
25
|
+
*/
|
|
26
|
+
import { spawnCollectStdoutResult, } from "./child-unref.js";
|
|
27
|
+
import { buildProcessQuery, parseProcessTable, } from "../scripts/lib/process-scan.mjs";
|
|
28
|
+
/**
|
|
29
|
+
* Re-exported, not redefined. `windowsExe` resolves an absolute System32
|
|
30
|
+
* interpreter path for a spawn this process is about to make (a bare
|
|
31
|
+
* `powershell.exe`/`taskkill.exe` is resolvable through a PATH a caller can
|
|
32
|
+
* shadow, and these spawns decide what gets killed) — one definition of that
|
|
33
|
+
* rule is the whole point of the seam.
|
|
34
|
+
*/
|
|
35
|
+
export { windowsExe } from "../scripts/lib/process-scan.mjs";
|
|
36
|
+
/**
|
|
37
|
+
* Run one process-table query on the extension's spawn rails.
|
|
38
|
+
*
|
|
39
|
+
* Never throws and never rejects: an unbuildable query (a Windows-only column
|
|
40
|
+
* asked for on POSIX, an empty filter) resolves as `spawn-error` with no rows,
|
|
41
|
+
* the same shape every other failure takes, because every caller here is
|
|
42
|
+
* best-effort instrumentation or a best-effort sweep.
|
|
43
|
+
*/
|
|
44
|
+
export async function queryProcessTable(request, options) {
|
|
45
|
+
let query;
|
|
46
|
+
try {
|
|
47
|
+
query = buildProcessQuery(request.fields, {
|
|
48
|
+
filter: request.filter,
|
|
49
|
+
excludeSelfPid: request.excludeSelfPid,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return { rows: [], status: "spawn-error", serverSideFiltered: false };
|
|
54
|
+
}
|
|
55
|
+
const result = await spawnCollectStdoutResult(query.command, query.args, { shell: false, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }, { timeoutMs: options.timeoutMs, onTimeout: options.onTimeout });
|
|
56
|
+
return {
|
|
57
|
+
// Whatever the collector kept is parsed rather than discarded here: it
|
|
58
|
+
// drops stdout on a non-zero exit (that output is not evidence of what
|
|
59
|
+
// is NOT running) but KEEPS the partial table from a timed-out child,
|
|
60
|
+
// and the caller decides what a partial table is worth by reading
|
|
61
|
+
// `status`. Re-deciding that here would be a second policy.
|
|
62
|
+
rows: parseProcessTable(result.stdout, query.tabSeparated, query.fields),
|
|
63
|
+
status: result.status,
|
|
64
|
+
exitCode: result.exitCode,
|
|
65
|
+
timeoutKill: result.timeoutKill,
|
|
66
|
+
serverSideFiltered: query.serverSideFiltered,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -415,9 +415,10 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
|
|
|
415
415
|
// Deliberately never calls `writeCache`: there is nothing fresher to write
|
|
416
416
|
// back, only what turn_end already produced.
|
|
417
417
|
//
|
|
418
|
-
// No double-count / honesty gap (#533): `
|
|
419
|
-
// (`runtime-context.ts`)
|
|
420
|
-
//
|
|
418
|
+
// No double-count / honesty gap (#533): explicit `pilens_turn_end`
|
|
419
|
+
// delivery may call `consumeTestFindings` (`runtime-context.ts`) to
|
|
420
|
+
// read-and-clear this SAME cache key once. Automatic Pi delivery uses a
|
|
421
|
+
// non-context custom entry and never consumes this pull-diagnostics cache.
|
|
421
422
|
// This task only ever reads (never clears) it, so it can't race that
|
|
422
423
|
// consumption into re-delivering a message twice — at most it surfaces
|
|
423
424
|
// the same underlying failures a second time, through a different
|
package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRunnerErrorResult, } from "../../test-runner-client.js";
|
|
1
2
|
import * as fs from "node:fs";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import { advisoryPathKey, validateAdvisoryProvenance, } from "../../advisory-provenance.js";
|
|
@@ -59,6 +60,31 @@ export function testResultToProjectDiagnostics(result, stale = false) {
|
|
|
59
60
|
source: "project-scan",
|
|
60
61
|
}));
|
|
61
62
|
}
|
|
63
|
+
// #2532: a RUNNER error (timeout, missing provider/binary, a config
|
|
64
|
+
// failure — the suite itself never produced a verdict) is not a finding
|
|
65
|
+
// the agent introduced, exactly like the turn-end delivery framing
|
|
66
|
+
// (#2522) already treats it. `isRunnerErrorResult` is the single seam
|
|
67
|
+
// both surfaces read so the identical `TestResult` cannot classify
|
|
68
|
+
// differently here than it does in the turn-end message.
|
|
69
|
+
if (isRunnerErrorResult(result)) {
|
|
70
|
+
return [
|
|
71
|
+
{
|
|
72
|
+
filePath: result.file,
|
|
73
|
+
severity: "info",
|
|
74
|
+
semantic: "none",
|
|
75
|
+
tool: "test-runner",
|
|
76
|
+
runner: result.runner,
|
|
77
|
+
rule: `test:${result.runner}`,
|
|
78
|
+
message: `${stalePrefix}Test run error: ${result.error}`,
|
|
79
|
+
source: "project-scan",
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
// #2532 review S3: a counted failure stays blocking even when `error` is
|
|
84
|
+
// ALSO set (pytest exit 2 "Interrupted" after `2 failed, 1 passed` — see
|
|
85
|
+
// `isRunnerErrorResult`'s doc). The pre-fix message mentioned `error`
|
|
86
|
+
// here via a ternary this PR's first version dropped; restored so the
|
|
87
|
+
// interruption is not silently lost from a still-blocking finding.
|
|
62
88
|
return [
|
|
63
89
|
{
|
|
64
90
|
filePath: result.file,
|
|
@@ -67,9 +93,7 @@ export function testResultToProjectDiagnostics(result, stale = false) {
|
|
|
67
93
|
tool: "test-runner",
|
|
68
94
|
runner: result.runner,
|
|
69
95
|
rule: `test:${result.runner}`,
|
|
70
|
-
message: `${stalePrefix}${result.error
|
|
71
|
-
? `Test run error: ${result.error}`
|
|
72
|
-
: `${result.failed} test(s) failed`}`,
|
|
96
|
+
message: `${stalePrefix}${result.failed} test(s) failed${result.error ? ` (runner also reported: ${result.error})` : ""}`,
|
|
73
97
|
source: "project-scan",
|
|
74
98
|
},
|
|
75
99
|
];
|