@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
|
@@ -7,7 +7,51 @@
|
|
|
7
7
|
import { logTreeSitterDiagnostic } from "./tree-sitter-logger.js";
|
|
8
8
|
import * as fs from "node:fs";
|
|
9
9
|
import * as path from "node:path";
|
|
10
|
+
import { classifyBundledResourceDir, reportBundledResourceDirHealth, } from "./bundled-resource-health.js";
|
|
11
|
+
import { getDegradationLedgerGeneration } from "./degradation-ledger.js";
|
|
10
12
|
import { resolvePackagePath } from "./package-root.js";
|
|
13
|
+
/**
|
|
14
|
+
* The bundled `rules/tree-sitter-queries` root — the ONE spelling of this
|
|
15
|
+
* path, imported by `clients/cache/rule-cache.ts` (re-exported there as
|
|
16
|
+
* `BUNDLED_RULES_ROOT`, #2636 review F4) rather than computed a second time,
|
|
17
|
+
* so the "one ledger row, not two" claim for `tree-sitter-queries-dir-missing`
|
|
18
|
+
* rests on reference equality, not merely two independently-resolved strings
|
|
19
|
+
* that happen to match.
|
|
20
|
+
*/
|
|
21
|
+
export const BUNDLED_QUERIES_ROOT = resolvePackagePath(import.meta.url, "rules", "tree-sitter-queries");
|
|
22
|
+
let cachedBundledQueriesRootHealth;
|
|
23
|
+
let cachedBundledQueriesRootHealthGeneration;
|
|
24
|
+
/**
|
|
25
|
+
* #2636 review F6/round-2 F3: memoized, but keyed on the degradation
|
|
26
|
+
* ledger's OWN generation counter (bumped by `resetDegradationLedger`, wired
|
|
27
|
+
* into `handleSessionStart`) rather than "compute once, forever" — round 1's
|
|
28
|
+
* version overclaimed that the underlying fact "cannot change mid-process
|
|
29
|
+
* any more than the package's own install location can", but a managed
|
|
30
|
+
* extension cache RELOCATING a live install mid-process is exactly the
|
|
31
|
+
* failure #2587/#2626 investigated: a permanently-cached "absent" verdict
|
|
32
|
+
* from the FIRST probe would never notice the directory coming back (or
|
|
33
|
+
* disappearing later), the same silent-zero shape this issue exists to end.
|
|
34
|
+
* Re-probing once per SESSION (not once per call) is the right middle
|
|
35
|
+
* ground: a real, measured `readdirSync` cost (4.2 µs) is paid once per
|
|
36
|
+
* session rather than on every dispatched file — same generation-keyed
|
|
37
|
+
* pattern `clients/ast-grep-client.ts`'s `ensureRulesHealthReported` uses
|
|
38
|
+
* for its own per-instance re-check.
|
|
39
|
+
*/
|
|
40
|
+
export function getBundledQueriesRootHealth() {
|
|
41
|
+
const generation = getDegradationLedgerGeneration();
|
|
42
|
+
if (cachedBundledQueriesRootHealth === undefined ||
|
|
43
|
+
cachedBundledQueriesRootHealthGeneration !== generation) {
|
|
44
|
+
cachedBundledQueriesRootHealthGeneration = generation;
|
|
45
|
+
cachedBundledQueriesRootHealth =
|
|
46
|
+
classifyBundledResourceDir(BUNDLED_QUERIES_ROOT);
|
|
47
|
+
}
|
|
48
|
+
return cachedBundledQueriesRootHealth;
|
|
49
|
+
}
|
|
50
|
+
/** Test-only: clear the memo so a scenario can simulate a different install layout. */
|
|
51
|
+
export function _resetBundledQueriesRootHealthForTests() {
|
|
52
|
+
cachedBundledQueriesRootHealth = undefined;
|
|
53
|
+
cachedBundledQueriesRootHealthGeneration = undefined;
|
|
54
|
+
}
|
|
11
55
|
export function isDisabledQueryDirectoryName(name) {
|
|
12
56
|
return name.endsWith("-disabled");
|
|
13
57
|
}
|
|
@@ -60,7 +104,7 @@ export function ruleFilesForLanguage(languageId, rootDir = process.cwd()) {
|
|
|
60
104
|
for (const lang of ruleSourceLanguages(languageId)) {
|
|
61
105
|
for (const dir of [
|
|
62
106
|
path.join(resolvedRoot, "rules", "tree-sitter-queries", lang),
|
|
63
|
-
|
|
107
|
+
path.join(BUNDLED_QUERIES_ROOT, lang),
|
|
64
108
|
]) {
|
|
65
109
|
if (!fs.existsSync(dir))
|
|
66
110
|
continue;
|
|
@@ -70,6 +114,42 @@ export function ruleFilesForLanguage(languageId, rootDir = process.cwd()) {
|
|
|
70
114
|
}
|
|
71
115
|
}
|
|
72
116
|
}
|
|
117
|
+
// #2636 (review F2): a language resolving zero files here is NORMAL when
|
|
118
|
+
// nobody has authored bundled/project queries for it — seven REACHABLE
|
|
119
|
+
// grammars have none by design: bash, dart, elixir, lua, ocaml, swift, zig
|
|
120
|
+
// (`.sh`/`.bash`, `.dart`, `.ex`/`.exs`, `.lua`, `.ml`/`.mli`, `.swift`,
|
|
121
|
+
// `.zig` — see `language-registry.ts`'s `EXTENSION_TO_GRAMMAR`). cobol and
|
|
122
|
+
// plsql are NOT in that registry at all — only their `-disabled` query
|
|
123
|
+
// directories exist — so `ruleFilesForLanguage` never actually resolves
|
|
124
|
+
// those two languageIds in production; they are not examples of this
|
|
125
|
+
// case. A BUG looks identical from an empty `files` set alone: the
|
|
126
|
+
// bundled root itself relocated out from under the package (same
|
|
127
|
+
// managed-cache-relocation shape #2626 fixed for skills/). Only pay for
|
|
128
|
+
// the extra classification in this COLD branch (never on the common,
|
|
129
|
+
// non-empty path), key it on the shared ROOT rather than this call's
|
|
130
|
+
// `languageId` (every language hitting an actually-missing root collapses
|
|
131
|
+
// into the SAME ledger row instead of one per language), and only RECORD
|
|
132
|
+
// when the root is actually unhealthy — one of the seven by-design-empty
|
|
133
|
+
// languages is touched routinely (any `.sh`/`.lua` edit).
|
|
134
|
+
//
|
|
135
|
+
// #2636 review round 2, F2: NO separate phase/latency row here (unlike
|
|
136
|
+
// the ast-grep/skills sibling sites, which log one PER CONSTRUCTION or
|
|
137
|
+
// PER REQUEST — a bounded cardinality). This branch runs on EVERY
|
|
138
|
+
// dispatched file while the root stays broken (AGENTS.md's "no raw
|
|
139
|
+
// per-occurrence log for repeats"): 200 touches of a by-design-empty
|
|
140
|
+
// language against a broken root would otherwise write 200 raw
|
|
141
|
+
// `latency.log` rows. `reportBundledResourceDirHealth`'s
|
|
142
|
+
// `incrementDegradationCount` already answers "did this run" AND "how
|
|
143
|
+
// many times" in a BOUNDED way — one row per session at count 1, then
|
|
144
|
+
// only at power-of-two milestones (1, 2, 4, 8, … so 200 occurrences write
|
|
145
|
+
// exactly 8 durable rows) — so a second, unbounded record here would add
|
|
146
|
+
// nothing the ledger row lacks.
|
|
147
|
+
if (files.size === 0) {
|
|
148
|
+
const health = getBundledQueriesRootHealth();
|
|
149
|
+
if (health.status !== "healthy") {
|
|
150
|
+
reportBundledResourceDirHealth("tree-sitter-queries-dir-missing", BUNDLED_QUERIES_ROOT, health, "bundled tree-sitter query rules");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
73
153
|
return [...files];
|
|
74
154
|
}
|
|
75
155
|
/**
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { notifyUserDegradation } from "./user-notify.js";
|
|
17
17
|
import * as path from "node:path";
|
|
18
18
|
import { TreeSitterClient, } from "./tree-sitter-client.js";
|
|
19
|
+
import { EXTENSION_TO_GRAMMAR } from "./language-registry.js";
|
|
19
20
|
import { logTreeSitter } from "./tree-sitter-logger.js";
|
|
20
21
|
let _shared = null;
|
|
21
22
|
let _wasmAborted = false;
|
|
@@ -80,56 +81,60 @@ export function _resetSharedTreeSitterClientForTests() {
|
|
|
80
81
|
_wasmAborted = false;
|
|
81
82
|
_wasmAbortedAt = undefined;
|
|
82
83
|
}
|
|
83
|
-
// Grammar selection by extension —
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
// scanner
|
|
90
|
-
//
|
|
91
|
-
// `
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
".mts": "typescript",
|
|
96
|
-
".cts": "typescript",
|
|
97
|
-
".tsx": "tsx",
|
|
98
|
-
".js": "javascript",
|
|
99
|
-
".mjs": "javascript",
|
|
100
|
-
".cjs": "javascript",
|
|
101
|
-
".jsx": "javascript",
|
|
102
|
-
".py": "python",
|
|
103
|
-
".go": "go",
|
|
104
|
-
".rs": "rust",
|
|
105
|
-
".rb": "ruby",
|
|
106
|
-
".c": "c",
|
|
107
|
-
".h": "c",
|
|
108
|
-
".cc": "cpp",
|
|
109
|
-
".cpp": "cpp",
|
|
110
|
-
".cxx": "cpp",
|
|
111
|
-
".c++": "cpp",
|
|
112
|
-
".hh": "cpp",
|
|
113
|
-
".hpp": "cpp",
|
|
114
|
-
".hxx": "cpp",
|
|
115
|
-
".inl": "cpp",
|
|
116
|
-
".ipp": "cpp",
|
|
117
|
-
".tpp": "cpp",
|
|
118
|
-
".txx": "cpp",
|
|
119
|
-
".cu": "cpp",
|
|
120
|
-
".hip": "cpp",
|
|
121
|
-
".cs": "csharp",
|
|
122
|
-
".php": "php",
|
|
123
|
-
".phtml": "php",
|
|
124
|
-
".php3": "php",
|
|
125
|
-
".php4": "php",
|
|
126
|
-
".php5": "php",
|
|
127
|
-
".css": "css",
|
|
128
|
-
};
|
|
84
|
+
// Grammar selection by extension — projected from the canonical language
|
|
85
|
+
// registry (language-registry.ts, #2424), which is now THE ext→grammar
|
|
86
|
+
// authority. `.tsx` → the tsx grammar (parses JSX); `.jsx` → the javascript
|
|
87
|
+
// grammar. The project scanner (project-diagnostics/scanner.ts), read expansion
|
|
88
|
+
// (read-expansion.ts) and module-report (`tsLangForFile`, #887) all project the
|
|
89
|
+
// same registry column, so none of them can drift from this one: before #2424
|
|
90
|
+
// the scanner spread this map and layered java/kotlin on top while read
|
|
91
|
+
// expansion kept a hand-copied near-copy of it.
|
|
92
|
+
// `Readonly` because the projection is `Object.freeze`d at the source, and the
|
|
93
|
+
// mutable `Record` type let a caller type-check a write that would throw at
|
|
94
|
+
// runtime (#2424 review, S4).
|
|
95
|
+
export const EXT_TO_LANG = EXTENSION_TO_GRAMMAR;
|
|
129
96
|
/** Resolve a tree-sitter grammar/language id from a file path's extension. */
|
|
130
97
|
export function resolveTreeSitterLanguage(filePath) {
|
|
131
98
|
return EXT_TO_LANG[path.extname(filePath).toLowerCase()];
|
|
132
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* The grammars `clients/complexity-client.ts` has a node mapping for.
|
|
102
|
+
*
|
|
103
|
+
* Lives HERE, beside the ext→grammar resolver, because the question "can this
|
|
104
|
+
* path produce a complexity baseline?" has to be answerable WITHOUT loading
|
|
105
|
+
* the analyzer graph (#2467): `clients/runtime-tool-call.ts` asks it on every
|
|
106
|
+
* tool call to decide whether the seventeen-module load is worth starting, and
|
|
107
|
+
* the client that owns the answer is behind that very load.
|
|
108
|
+
*
|
|
109
|
+
* It is not a second extension table — the extension half still comes from the
|
|
110
|
+
* one canonical registry through {@link resolveTreeSitterLanguage}. It is the
|
|
111
|
+
* language-id half, and `LANGUAGE_NODES` is typed
|
|
112
|
+
* `Record<ComplexityLanguageId, …>`, so adding a grammar there without adding
|
|
113
|
+
* it here (or the reverse) is a COMPILE error rather than a predicate that
|
|
114
|
+
* quietly drifts from the client it is supposed to mirror.
|
|
115
|
+
*/
|
|
116
|
+
export const COMPLEXITY_LANGUAGE_IDS = [
|
|
117
|
+
"typescript",
|
|
118
|
+
"tsx",
|
|
119
|
+
"javascript",
|
|
120
|
+
"python",
|
|
121
|
+
"go",
|
|
122
|
+
"rust",
|
|
123
|
+
];
|
|
124
|
+
/** Narrow a resolved grammar id to one complexity can analyze. */
|
|
125
|
+
export function isComplexityLanguageId(languageId) {
|
|
126
|
+
return COMPLEXITY_LANGUAGE_IDS.includes(languageId);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Can a complexity baseline be produced for this path? Pure, synchronous, and
|
|
130
|
+
* free of every analyzer module — the guard `handleToolCallImpl` needs above
|
|
131
|
+
* its demand on the bootstrap seam. `ComplexityClient.isSupportedFile`
|
|
132
|
+
* delegates to it, so there is exactly one answer.
|
|
133
|
+
*/
|
|
134
|
+
export function isComplexitySupportedFile(filePath) {
|
|
135
|
+
const languageId = resolveTreeSitterLanguage(filePath);
|
|
136
|
+
return languageId !== undefined && isComplexityLanguageId(languageId);
|
|
137
|
+
}
|
|
133
138
|
export async function withTreeSitterRoot(filePath, content, consume) {
|
|
134
139
|
const languageId = resolveTreeSitterLanguage(filePath);
|
|
135
140
|
const client = getSharedTreeSitterClient();
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Extracts definitions and references from source files
|
|
4
4
|
*/
|
|
5
5
|
import { logTreeSitterDiagnostic } from "./tree-sitter-logger.js";
|
|
6
|
+
import { EXTENSION_TO_GRAMMAR, KIND_TO_GRAMMAR } from "./language-registry.js";
|
|
6
7
|
import * as path from "node:path";
|
|
7
8
|
import { loadWebTreeSitter } from "./deps/web-tree-sitter.js";
|
|
8
9
|
// Tree-sitter query patterns for symbol extraction
|
|
@@ -563,6 +564,33 @@ SYMBOL_QUERIES.javascript = {
|
|
|
563
564
|
constructor: (identifier) @newIdent) @newRef
|
|
564
565
|
`,
|
|
565
566
|
};
|
|
567
|
+
/**
|
|
568
|
+
* The grammar a KIND-keyed consumer (review-graph builder, module-report
|
|
569
|
+
* outline) should extract `filePath` with, or undefined when this file is not
|
|
570
|
+
* something the symbol extractor can read.
|
|
571
|
+
*
|
|
572
|
+
* Three registry-derived steps, no hand-kept table (#2424 replaced one switch
|
|
573
|
+
* in review-graph/builder.ts and one map in module-report.ts with this):
|
|
574
|
+
* 1. the kind must have a single grammar-bearing language, or declare a
|
|
575
|
+
* `kindFallback` one — so extension-split `jsts` is excluded and its
|
|
576
|
+
* callers keep resolving it by path;
|
|
577
|
+
* 2. the file's own extension wins where it has grammar wiring, which is what
|
|
578
|
+
* splits `cxx` into `.c`/`.h` -> c and the rest -> cpp;
|
|
579
|
+
* 3. the grammar must actually have symbol queries here, which is what keeps
|
|
580
|
+
* css/json/yaml/html/toml (grammars that ship, queries that do not) out.
|
|
581
|
+
*/
|
|
582
|
+
export function symbolExtractionGrammar(kind, filePath) {
|
|
583
|
+
if (!kind)
|
|
584
|
+
return undefined;
|
|
585
|
+
const kindGrammar = KIND_TO_GRAMMAR[kind];
|
|
586
|
+
if (!kindGrammar)
|
|
587
|
+
return undefined;
|
|
588
|
+
const byExtension = filePath
|
|
589
|
+
? EXTENSION_TO_GRAMMAR[path.extname(filePath).toLowerCase()]
|
|
590
|
+
: undefined;
|
|
591
|
+
const grammar = byExtension ?? kindGrammar;
|
|
592
|
+
return grammar in SYMBOL_QUERIES ? grammar : undefined;
|
|
593
|
+
}
|
|
566
594
|
/** Language keys with symbol queries; used by fixture drift guards. */
|
|
567
595
|
export function getSymbolQueryLanguages() {
|
|
568
596
|
return Object.keys(SYMBOL_QUERIES);
|
|
@@ -19,6 +19,11 @@ export const LOCAL_TYPOS_CONFIG_NAMES = [
|
|
|
19
19
|
"_typos.toml",
|
|
20
20
|
".typos.toml",
|
|
21
21
|
];
|
|
22
|
+
// Deliberately UNCEILINGED at $HOME (refs #2472 review round 3, F1): typos
|
|
23
|
+
// discovers `~/typos.toml` as the user's legitimate global config, and
|
|
24
|
+
// `typos-lsp` merges an injected config with its own discovery (injected
|
|
25
|
+
// wins) — a ceilinged search here silently hid the user's config from that
|
|
26
|
+
// merge and let pi-lens's own shipped `_typos.toml` clobber it instead.
|
|
22
27
|
export function findLocalTyposConfig(startDir) {
|
|
23
28
|
return findLocalToolConfig(startDir, LOCAL_TYPOS_CONFIG_NAMES);
|
|
24
29
|
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* is a no-op and the ndjson sink remains the sole destination — the message is
|
|
20
20
|
* never lost, only un-rendered.
|
|
21
21
|
*/
|
|
22
|
+
import { withConfigDiagnosticCode, } from "./config-diagnostic-codes.js";
|
|
22
23
|
let notifierGetter;
|
|
23
24
|
/** Called once from the extension entry with a getter over the live `ctx.ui.notify`. */
|
|
24
25
|
export function wireUserNotifier(portsOrGetter) {
|
|
@@ -40,10 +41,13 @@ export function hasUserNotifier() {
|
|
|
40
41
|
* `ctx` after a session replacement must degrade to a no-op, not break the
|
|
41
42
|
* diagnostic path that called it.
|
|
42
43
|
*/
|
|
43
|
-
export function notifyUserDegradation(message, level = "warning") {
|
|
44
|
+
export function notifyUserDegradation(message, level = "warning", options) {
|
|
44
45
|
try {
|
|
45
46
|
const notify = notifierGetter?.();
|
|
46
|
-
|
|
47
|
+
const coded = options?.code
|
|
48
|
+
? withConfigDiagnosticCode(message, options.code)
|
|
49
|
+
: message;
|
|
50
|
+
notify?.(coded, level);
|
|
47
51
|
}
|
|
48
52
|
catch {
|
|
49
53
|
// The ndjson sink already holds this message; a dead host is not an error.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { stat } from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { BoundedFifoMap } from "./bounded-cache.js";
|
|
4
5
|
import { demotePastEofDiagnostics, } from "./diagnostic-line-freshness.js";
|
|
5
6
|
import { visibleWidth } from "./deps/pi-tui.js";
|
|
6
7
|
import { normalizeEphemeralMapKey, normalizeMapKey } from "./path-utils.js";
|
|
@@ -58,7 +59,6 @@ export function isBlocking(d) {
|
|
|
58
59
|
}
|
|
59
60
|
// ── Module state ─────────────────────────────────────────────────────────────
|
|
60
61
|
const files = new Map();
|
|
61
|
-
const lspServers = new Map();
|
|
62
62
|
let sessionLanguages = [];
|
|
63
63
|
let requestRenderFn = null;
|
|
64
64
|
/**
|
|
@@ -81,7 +81,8 @@ const runnerWriteGuard = new WriteOrderingGuard();
|
|
|
81
81
|
const MAX_STORED_DIAGNOSTICS_PER_FILE = 12;
|
|
82
82
|
const MAX_INACTIVE_FILE_RECORDS = 1024;
|
|
83
83
|
const ACTIVE_FILE_IDLE_MS = 30 * 60_000;
|
|
84
|
-
const MAX_LSP_SERVER_RECORDS = 128;
|
|
84
|
+
export const MAX_LSP_SERVER_RECORDS = 128;
|
|
85
|
+
const lspServers = new BoundedFifoMap(MAX_LSP_SERVER_RECORDS);
|
|
85
86
|
// Pruning is a cold-size-boundary operation. Do not walk the whole file map
|
|
86
87
|
// for every record in a large diagnostics reconciliation; the full-scan path
|
|
87
88
|
// can legitimately create thousands of records in one synchronous batch.
|
|
@@ -110,8 +111,23 @@ function maybePruneInactiveFileRecords() {
|
|
|
110
111
|
export function setRenderCallback(fn) {
|
|
111
112
|
requestRenderFn = fn;
|
|
112
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* #2275 review F1: files whose dependency-drift-demoted rows the footer has
|
|
116
|
+
* ACTUALLY DRAWN since the last turn end — the delivery population
|
|
117
|
+
* `runtime-turn.ts`'s cap step drains.
|
|
118
|
+
*
|
|
119
|
+
* The footer is not a broadcast of the whole store: `renderWidget` picks ONE
|
|
120
|
+
* record (`withBlocking[0]`) and its first five qualifying entries, and it
|
|
121
|
+
* may not be drawn at all (headless, or a turn with no repaint). Counting a
|
|
122
|
+
* delivery per turn end therefore charged the cap for rows the agent never
|
|
123
|
+
* saw, and retired them a delivery early. A Set (not a counter) is the right
|
|
124
|
+
* shape: the widget repaints many times per turn, and all of those repaints
|
|
125
|
+
* are the SAME one delivery.
|
|
126
|
+
*/
|
|
127
|
+
const renderedDependencyDriftFiles = new Set();
|
|
113
128
|
export function clearWidgetState() {
|
|
114
129
|
files.clear();
|
|
130
|
+
renderedDependencyDriftFiles.clear();
|
|
115
131
|
lspServers.clear();
|
|
116
132
|
sessionLanguages = [];
|
|
117
133
|
requestRenderFn = null;
|
|
@@ -174,7 +190,19 @@ export function exportWidgetState() {
|
|
|
174
190
|
*/
|
|
175
191
|
function migrateEntryStamps(entries, recordTouchedAt) {
|
|
176
192
|
return (entries ?? []).map((d) => {
|
|
177
|
-
|
|
193
|
+
// #2275: strip staleDeliveryCount and footerRetired alongside stale — a
|
|
194
|
+
// delivery count (and the footer-hide it earned) without the demotion
|
|
195
|
+
// it counts is meaningless, and a resumed session re-evaluates
|
|
196
|
+
// dependency-drift from scratch (#1631 review F3) same as the `stale`
|
|
197
|
+
// bit itself. A restored footer has shown the row zero times.
|
|
198
|
+
// Fix-round 3 (#2275 review F2): `staleReason` left the round trip
|
|
199
|
+
// unstripped, so a resumed row could carry `staleReason:
|
|
200
|
+
// "dependency-drift"` with `stale` gone — `isDependencyDriftDemoted`
|
|
201
|
+
// reads `stale === true`, so that combination reads as clean today, but
|
|
202
|
+
// the stray reason is still a demotion label with no demotion behind
|
|
203
|
+
// it, the same "meaningless without the bit it counts" shape as the
|
|
204
|
+
// other three fields. Strip it alongside them.
|
|
205
|
+
const { stale: _stale, staleDeliveryCount: _staleDeliveryCount, footerRetired: _footerRetired, staleReason: _staleReason, ...rest } = d;
|
|
178
206
|
return rest.observedAt == null
|
|
179
207
|
? { ...rest, observedAt: recordTouchedAt }
|
|
180
208
|
: rest;
|
|
@@ -271,9 +299,9 @@ export function getFailedLspServerIds() {
|
|
|
271
299
|
}
|
|
272
300
|
return ids;
|
|
273
301
|
}
|
|
274
|
-
export function recordFormatter(filePath, formatter, changed, success) {
|
|
302
|
+
export function recordFormatter(filePath, formatter, changed, success, outcome) {
|
|
275
303
|
const rec = getOrCreate(filePath);
|
|
276
|
-
rec.formatters.set(formatter, { changed, success });
|
|
304
|
+
rec.formatters.set(formatter, { changed, success, outcome });
|
|
277
305
|
rec.touchedAt = Date.now();
|
|
278
306
|
files.set(fileMapKey(filePath), rec);
|
|
279
307
|
requestRender();
|
|
@@ -381,7 +409,19 @@ function commitDiagnostics(rec, filePath, normalized, observedAt, key = fileMapK
|
|
|
381
409
|
files.set(key, rec);
|
|
382
410
|
requestRender();
|
|
383
411
|
}
|
|
384
|
-
|
|
412
|
+
export function classifyDiagnosticTier(d) {
|
|
413
|
+
if (d.severity === "error")
|
|
414
|
+
return "error";
|
|
415
|
+
if (d.severity === "warning")
|
|
416
|
+
return "warning";
|
|
417
|
+
if (d.severity === "hint" || d.severity === "info")
|
|
418
|
+
return "advisory";
|
|
419
|
+
return undefined;
|
|
420
|
+
}
|
|
421
|
+
/** Recompute the {blocking, errors, warnings} tally for a diagnostic set.
|
|
422
|
+
* `hint`/`info` tier findings are tallied separately — see
|
|
423
|
+
* {@link classifyDiagnosticTier} and {@link countAdvisories} — and never
|
|
424
|
+
* inflate `warnings` (#2414). */
|
|
385
425
|
function countDiagnostics(diags) {
|
|
386
426
|
let blocking = 0;
|
|
387
427
|
let errors = 0;
|
|
@@ -398,22 +438,34 @@ function countDiagnostics(diags) {
|
|
|
398
438
|
if (diagnostic.stale &&
|
|
399
439
|
(diagnostic.staleReason ?? "past-eof") === "past-eof")
|
|
400
440
|
continue;
|
|
401
|
-
|
|
441
|
+
const tier = classifyDiagnosticTier(diagnostic);
|
|
442
|
+
if (tier === "error")
|
|
402
443
|
errors++;
|
|
403
|
-
|
|
404
|
-
// now preserves all four ast-grep tiers; before, the runner collapsed
|
|
405
|
-
// them to "warning" here. An exact `=== "warning"` test would drop those
|
|
406
|
-
// findings out of the footer entirely, so a file with real findings would
|
|
407
|
-
// render clean. The footer stays a blocking/error/warning summary on
|
|
408
|
-
// purpose — the tier distinction is rendered by the code-quality-warnings
|
|
409
|
-
// advisory, not by these counters.
|
|
410
|
-
else if (diagnostic.severity === "warning" ||
|
|
411
|
-
diagnostic.severity === "hint" ||
|
|
412
|
-
diagnostic.severity === "info")
|
|
444
|
+
else if (tier === "warning")
|
|
413
445
|
warnings++;
|
|
446
|
+
// tier === "advisory" (hint/info): intentionally excluded from the
|
|
447
|
+
// footer's warning tally (#2414) — see `countAdvisories` for the
|
|
448
|
+
// parallel count `getFileDiagnosticSummaries` exposes to
|
|
449
|
+
// `lens_diagnostics` so a hint-only file still shows up as "has issues".
|
|
414
450
|
}
|
|
415
451
|
return { blocking, errors, warnings };
|
|
416
452
|
}
|
|
453
|
+
/** Count `hint`/`info` tier findings using the same past-EOF exclusion as
|
|
454
|
+
* {@link countDiagnostics} (#2414). Kept as a standalone accessor rather than
|
|
455
|
+
* a new `FileRecord.diagnosticCounts` field — the footer never renders this
|
|
456
|
+
* number, only `getFileDiagnosticSummaries` (mode=all) needs it, to keep a
|
|
457
|
+
* hint-only file from being silently dropped from a detailed listing. */
|
|
458
|
+
function countAdvisories(diags) {
|
|
459
|
+
let advisories = 0;
|
|
460
|
+
for (const diagnostic of diags) {
|
|
461
|
+
if (diagnostic.stale &&
|
|
462
|
+
(diagnostic.staleReason ?? "past-eof") === "past-eof")
|
|
463
|
+
continue;
|
|
464
|
+
if (classifyDiagnosticTier(diagnostic) === "advisory")
|
|
465
|
+
advisories++;
|
|
466
|
+
}
|
|
467
|
+
return advisories;
|
|
468
|
+
}
|
|
417
469
|
/**
|
|
418
470
|
* Apply the #1641 past-EOF gate to `rec` in place: demote (never drop) any
|
|
419
471
|
* stored diagnostic whose cited line exceeds the file's CURRENT on-disk line
|
|
@@ -804,6 +856,102 @@ export function markWidgetFileBlockersStale(filePath, reason) {
|
|
|
804
856
|
}
|
|
805
857
|
return changed;
|
|
806
858
|
}
|
|
859
|
+
/**
|
|
860
|
+
* #2275: a dependency-drift demotion the footer is still allowed to draw —
|
|
861
|
+
* the one predicate the render marker, the delivery counter and the
|
|
862
|
+
* footer-hide all ask, so "which rows does this cap govern" cannot drift
|
|
863
|
+
* apart between them.
|
|
864
|
+
*/
|
|
865
|
+
function isDependencyDriftDemoted(d) {
|
|
866
|
+
return (d.stale === true &&
|
|
867
|
+
d.staleReason === "dependency-drift" &&
|
|
868
|
+
d.footerRetired !== true);
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* #2275 review F1: take (and clear) the files whose dependency-drift
|
|
872
|
+
* demotions the footer actually RENDERED since the last call — the delivery
|
|
873
|
+
* population `runtime-turn.ts`'s per-turn cap step walks at turn end.
|
|
874
|
+
*
|
|
875
|
+
* Deliberately NOT a store-wide query over everything currently demoted.
|
|
876
|
+
* `renderWidget` serves one record per pass, so a store-wide walk charged a
|
|
877
|
+
* delivery to every demoted file every turn — including files the footer had
|
|
878
|
+
* never shown and one demoted moments earlier by the same turn end, which
|
|
879
|
+
* also made the ledger's "capped after N deliveries" overstate N by one.
|
|
880
|
+
* Draining here is what makes many repaints within one turn count as the one
|
|
881
|
+
* delivery they are, and a turn with no repaint count as none.
|
|
882
|
+
*/
|
|
883
|
+
export function drainRenderedDependencyDriftFilePaths() {
|
|
884
|
+
const out = [...renderedDependencyDriftFiles];
|
|
885
|
+
renderedDependencyDriftFiles.clear();
|
|
886
|
+
return out;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* #2275: commit one more RENDERED delivery of `filePath`'s
|
|
890
|
+
* dependency-drift-demoted diagnostics. Every qualifying entry on the record
|
|
891
|
+
* advances together — `markWidgetFileBlockersStale` demotes them as one
|
|
892
|
+
* batch per file, so they share one delivery history, mirroring that
|
|
893
|
+
* write's own file-level scope. Returns the new count, or 0 when the file
|
|
894
|
+
* has nothing this cap governs (never demoted, or already footer-retired —
|
|
895
|
+
* a hidden row is delivered no more, so it must stop advancing).
|
|
896
|
+
*/
|
|
897
|
+
export function incrementWidgetDependencyDriftDelivery(filePath) {
|
|
898
|
+
const rec = files.get(fileMapKey(filePath));
|
|
899
|
+
if (!rec)
|
|
900
|
+
return 0;
|
|
901
|
+
let next = 0;
|
|
902
|
+
for (const d of rec.allDiagnostics) {
|
|
903
|
+
if (isDependencyDriftDemoted(d)) {
|
|
904
|
+
next = (d.staleDeliveryCount ?? 0) + 1;
|
|
905
|
+
d.staleDeliveryCount = next;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
return next;
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* #2275 review F2: retire every dependency-drift-demoted diagnostic on
|
|
912
|
+
* `filePath` once its delivery cap is reached — by HIDING it from the
|
|
913
|
+
* footer, not by dropping it.
|
|
914
|
+
*
|
|
915
|
+
* `RuntimeCoordinator.retireDemotedDependencyDriftBlocker` can delete its
|
|
916
|
+
* whole `_pendingInlineBlockers` record because that store backs exactly one
|
|
917
|
+
* surface (the turn-end advisory text). This store does not: `allDiagnostics`
|
|
918
|
+
* is also what `getFileDiagnosticSummaries` serves to `lens_diagnostics
|
|
919
|
+
* mode=all` and what `lens_diagnostic_mark` cross-checks to reanchor. Removing
|
|
920
|
+
* the entry there turned an unconfirmed LSP error into a CLEAN read and took
|
|
921
|
+
* it out of the error tally #1631 requires it to stay in. So the cap marks
|
|
922
|
+
* `footerRetired` and leaves everything else — severity, counts, the
|
|
923
|
+
* `stale`/`dependency-drift` demotion — exactly as it was; mode=all carries
|
|
924
|
+
* the "capped, still confirmable" wording in its own note. Returns true iff
|
|
925
|
+
* something changed (idempotent on a second call).
|
|
926
|
+
*/
|
|
927
|
+
export function retireWidgetDependencyDriftBlockers(filePath) {
|
|
928
|
+
const rec = files.get(fileMapKey(filePath));
|
|
929
|
+
if (!rec)
|
|
930
|
+
return false;
|
|
931
|
+
let changed = false;
|
|
932
|
+
for (const d of rec.allDiagnostics) {
|
|
933
|
+
if (isDependencyDriftDemoted(d)) {
|
|
934
|
+
d.footerRetired = true;
|
|
935
|
+
changed = true;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
if (!changed)
|
|
939
|
+
return false;
|
|
940
|
+
// The record's severity tallies are unchanged by design (the finding is
|
|
941
|
+
// still an error, still non-blocking) — only what the footer draws moves.
|
|
942
|
+
// Fix-round 3 (#2275 review F1): this delete is live only for the
|
|
943
|
+
// multi-file drain re-render case — `drainRenderedDependencyDriftFilePaths`
|
|
944
|
+
// already clears the whole Set before this loop starts, so by the time a
|
|
945
|
+
// given `wPath` reaches this call its own entry is normally long gone. It
|
|
946
|
+
// only does something when a render for a DIFFERENT file interleaves
|
|
947
|
+
// between the drain and this retire (adding `rec.filePath` back to the
|
|
948
|
+
// Set mid-loop) or a render fires between retirement and the caller's next
|
|
949
|
+
// drain — either way, defensive: it keeps a just-retired file from being
|
|
950
|
+
// double-drained (and double-charged) on the next pass.
|
|
951
|
+
renderedDependencyDriftFiles.delete(rec.filePath);
|
|
952
|
+
requestRenderFn?.();
|
|
953
|
+
return true;
|
|
954
|
+
}
|
|
807
955
|
/**
|
|
808
956
|
* Keep the TUI honest (#298 follow-up). `reconcileStaleWidgetFiles` drops
|
|
809
957
|
* widget entries whose file changed on disk after they were last recorded
|
|
@@ -840,6 +988,7 @@ export function getFileDiagnosticSummaries() {
|
|
|
840
988
|
blocking: rec.diagnosticCounts.blocking,
|
|
841
989
|
errors: rec.diagnosticCounts.errors,
|
|
842
990
|
warnings: rec.diagnosticCounts.warnings,
|
|
991
|
+
advisories: countAdvisories(rec.allDiagnostics),
|
|
843
992
|
hasFinalSnapshot: rec.hasFinalDiagnosticsSnapshot,
|
|
844
993
|
diagnostics: rec.allDiagnostics.map((d) => ({ ...d })),
|
|
845
994
|
};
|
|
@@ -893,12 +1042,6 @@ export function recordLsp(serverId, root, status, durationMs) {
|
|
|
893
1042
|
? "ready"
|
|
894
1043
|
: "failed";
|
|
895
1044
|
lspServers.set(key, { serverId, root, status: mapped, durationMs });
|
|
896
|
-
while (lspServers.size > MAX_LSP_SERVER_RECORDS) {
|
|
897
|
-
const oldest = lspServers.keys().next().value;
|
|
898
|
-
if (oldest === undefined)
|
|
899
|
-
break;
|
|
900
|
-
lspServers.delete(oldest);
|
|
901
|
-
}
|
|
902
1045
|
requestRender();
|
|
903
1046
|
}
|
|
904
1047
|
// ── Render ────────────────────────────────────────────────────────────────────
|
|
@@ -961,7 +1104,12 @@ export function renderWidget(width, theme) {
|
|
|
961
1104
|
// the footer entirely instead of demoting visibly. `isBlockingOrDemoted` keeps
|
|
962
1105
|
// it in the list; the per-entry render below distinguishes the two with a
|
|
963
1106
|
// dimmed marker instead of the red dot.
|
|
964
|
-
|
|
1107
|
+
//
|
|
1108
|
+
// #2275: a demotion that reached its delivery cap is hidden from THIS
|
|
1109
|
+
// surface only (`footerRetired`) — it stays in `allDiagnostics` for
|
|
1110
|
+
// mode=all and lens_diagnostic_mark, which is where the agent is told it
|
|
1111
|
+
// was capped and can still be confirmed by a re-run.
|
|
1112
|
+
const isBlockingOrDemoted = (d) => (isBlocking(d) || d.stale === true) && d.footerRetired !== true;
|
|
965
1113
|
const withBlocking = recencySorted.filter((r) => r.diagnostics.some(isBlockingOrDemoted));
|
|
966
1114
|
if (withBlocking.length > 0) {
|
|
967
1115
|
const rec = withBlocking[0];
|
|
@@ -979,6 +1127,18 @@ export function renderWidget(width, theme) {
|
|
|
979
1127
|
lines.push(fitLine(` ${dim(path.basename(rec.filePath))}`, w));
|
|
980
1128
|
}
|
|
981
1129
|
const blockers = rec.diagnostics.filter(isBlockingOrDemoted).slice(0, 5);
|
|
1130
|
+
// #2275 review F1: THIS is the point where a demoted row is delivered to
|
|
1131
|
+
// a human — after the `withBlocking[0]` pick and the top-five slice, not
|
|
1132
|
+
// before them. Mark the file so the next turn end can charge exactly one
|
|
1133
|
+
// delivery against its cap. File-level, matching
|
|
1134
|
+
// `markWidgetFileBlockersStale`'s own file-level demotion batch (all of a
|
|
1135
|
+
// file's drift demotions share one delivery history); a file holding more
|
|
1136
|
+
// than five demoted rows delivers the visible ones and counts the batch,
|
|
1137
|
+
// which is the conservative direction — it retires no LATER than the rows
|
|
1138
|
+
// the agent actually read.
|
|
1139
|
+
if (blockers.some(isDependencyDriftDemoted)) {
|
|
1140
|
+
renderedDependencyDriftFiles.add(rec.filePath);
|
|
1141
|
+
}
|
|
982
1142
|
for (const d of blockers) {
|
|
983
1143
|
// A past-EOF demotion's coordinate is untrustworthy — render the
|
|
984
1144
|
// marker instead of the line (#1641); drift demotions keep theirs.
|
|
@@ -1062,7 +1222,10 @@ function formatFileRowVertical(rec, theme) {
|
|
|
1062
1222
|
.filter(([, f]) => f.changed && f.success)
|
|
1063
1223
|
.map(([name]) => name);
|
|
1064
1224
|
const failedFormatters = [...rec.formatters.entries()]
|
|
1065
|
-
|
|
1225
|
+
// An `unavailable` tool is not a code failure (#2413): it never earns a red
|
|
1226
|
+
// `fmt-failed` marker. Its result already carries success:true, so this
|
|
1227
|
+
// guard is a belt that keeps the intent explicit at the render seam.
|
|
1228
|
+
.filter(([, f]) => !f.success && f.outcome !== "unavailable")
|
|
1066
1229
|
.map(([name]) => name);
|
|
1067
1230
|
const formatMark = (failedFormatters.length > 0
|
|
1068
1231
|
? red(` fmt-failed:${failedFormatters.join(",")}`)
|
|
@@ -1205,7 +1368,9 @@ function hasChangedFormatter(rec) {
|
|
|
1205
1368
|
return [...rec.formatters.values()].some((f) => f.changed && f.success);
|
|
1206
1369
|
}
|
|
1207
1370
|
function hasFailedFormatter(rec) {
|
|
1208
|
-
|
|
1371
|
+
// `unavailable` (#2413) is success:true and must never count as a failure —
|
|
1372
|
+
// the explicit guard keeps that true even if a future path mislabels it.
|
|
1373
|
+
return [...rec.formatters.values()].some((f) => !f.success && f.outcome !== "unavailable");
|
|
1209
1374
|
}
|
|
1210
1375
|
function shouldRenderFile(rec) {
|
|
1211
1376
|
return (rec.hasFinalDiagnosticsSnapshot ||
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import * as path from "node:path";
|
|
23
23
|
import { isTestMode } from "./env-utils.js";
|
|
24
|
-
import {
|
|
24
|
+
import { getGlobalPiLensLogDir } from "./probe-home-state.js";
|
|
25
25
|
import { getMaxLogSizeMB } from "./log-cleanup.js";
|
|
26
26
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
27
27
|
import { normalizeFilePath } from "./path-utils.js";
|
|
28
|
-
const WORD_INDEX_LOG_FILE = path.join(
|
|
28
|
+
const WORD_INDEX_LOG_FILE = path.join(getGlobalPiLensLogDir(), "word-index.log");
|
|
29
29
|
const writer = createNdjsonLogger({
|
|
30
30
|
filePath: WORD_INDEX_LOG_FILE,
|
|
31
31
|
maxBytes: getMaxLogSizeMB() * 1024 * 1024,
|