@herbertgao/pi-extensions 2026.9.1 → 2026.9.3
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/README.md +3 -2
- package/THIRD_PARTY_NOTICES.md +24 -0
- package/node_modules/@herbertgao/sol-pi/LICENSE +19 -0
- package/node_modules/@herbertgao/sol-pi/README.md +159 -0
- package/node_modules/@herbertgao/sol-pi/SECURITY.md +26 -0
- package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +19 -0
- package/node_modules/@herbertgao/sol-pi/agents-install.md +150 -0
- package/node_modules/@herbertgao/sol-pi/assets/sol-pi-hero.png +0 -0
- package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +69 -0
- package/node_modules/@herbertgao/sol-pi/docs/configuration.md +75 -0
- package/node_modules/@herbertgao/sol-pi/package.json +76 -0
- package/node_modules/@herbertgao/sol-pi/scripts/check-pi-compat.mjs +32 -0
- package/node_modules/@herbertgao/sol-pi/scripts/check-sol-pi-config.mjs +120 -0
- package/node_modules/@herbertgao/sol-pi/sol-pi.example.json +10 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/config.ts +135 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/file-queue.ts +74 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +185 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/then-run.ts +128 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/archive.ts +53 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/candidate.ts +101 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/config.ts +71 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/index.ts +220 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/journal.ts +25 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/provider.ts +164 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/receipt.ts +177 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/index.ts +227 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/ledger.ts +20 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/observation.ts +252 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/economics.ts +237 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +455 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/index.ts +49 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/plan.ts +79 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +208 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/tools.ts +100 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/index.ts +42 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/runtime-paths.ts +17 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/tui.ts +71 -0
- package/node_modules/@narumitw/pi-btw/README.md +57 -141
- package/node_modules/@narumitw/pi-btw/dist/index.ts +478 -43
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
- package/node_modules/@narumitw/pi-btw/docs/workflows.md +74 -0
- package/node_modules/@narumitw/pi-btw/package.json +2 -1
- package/node_modules/@narumitw/pi-btw/src/btw.ts +28 -3
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +32 -2
- package/node_modules/@narumitw/pi-btw/src/keybindings.ts +330 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +136 -19
- package/node_modules/@narumitw/pi-btw/src/settings.ts +32 -0
- package/node_modules/@narumitw/pi-btw/src/side-thread.ts +57 -5
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +52 -30
- package/node_modules/pi-lens/CHANGELOG.md +260 -0
- package/node_modules/pi-lens/README.md +13 -8
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -1
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +1 -1
- package/node_modules/pi-lens/dist/clients/analysed-root.js +1 -0
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +1 -8
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +2 -0
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +133 -1
- package/node_modules/pi-lens/dist/clients/biome-client.js +13 -2
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -6
- package/node_modules/pi-lens/dist/clients/bootstrap.js +4 -4
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +1 -1
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +2 -5
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +1 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +122 -6
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +2 -2
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/child-unref.js +1 -19
- package/node_modules/pi-lens/dist/clients/complexity-client.js +5 -0
- package/node_modules/pi-lens/dist/clients/config-core/index.js +11 -8
- package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +1 -1
- package/node_modules/pi-lens/dist/clients/config-core/provenance.js +2 -9
- package/node_modules/pi-lens/dist/clients/config-core/records.js +1 -1
- package/node_modules/pi-lens/dist/clients/config-core/schema.js +3 -5
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +3 -1
- package/node_modules/pi-lens/dist/clients/config-resolve.js +10 -7
- package/node_modules/pi-lens/dist/clients/config-schema.js +56 -2
- package/node_modules/pi-lens/dist/clients/config-warn.js +2 -2
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +8 -1
- package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -4
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +5 -1
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +1 -1
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +38 -41
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +18 -1
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -6
- package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -3
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +7 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +19 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +15 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +3 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +12 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +5 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +1 -64
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +8 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -21
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +2 -20
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +5 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +2 -2
- package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/effective-config.js +12 -2
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +2 -2
- package/node_modules/pi-lens/dist/clients/extension-log.js +2 -0
- package/node_modules/pi-lens/dist/clients/file-kinds.js +1 -38
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -1
- package/node_modules/pi-lens/dist/clients/file-time.js +1 -1
- package/node_modules/pi-lens/dist/clients/file-utils.js +5 -1
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +4 -0
- package/node_modules/pi-lens/dist/clients/format-service.js +0 -1
- package/node_modules/pi-lens/dist/clients/formatters.js +66 -93
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +3 -4
- package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +5 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +42 -6
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +48 -7
- package/node_modules/pi-lens/dist/clients/grammar-source.js +3 -3
- package/node_modules/pi-lens/dist/clients/installer/index.js +416 -61
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +84 -18
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +6 -6
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +9 -1
- package/node_modules/pi-lens/dist/clients/knip-client.js +9 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +12 -11
- package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
- package/node_modules/pi-lens/dist/clients/latency-logger.js +7 -7
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +23 -0
- package/node_modules/pi-lens/dist/clients/lens-engine.js +3 -4
- package/node_modules/pi-lens/dist/clients/lens-events.js +1 -1
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +18 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +1 -1
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -36
- package/node_modules/pi-lens/dist/clients/lsp/client.js +4 -3
- package/node_modules/pi-lens/dist/clients/lsp/config.js +37 -7
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +24 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +79 -1
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +3 -3
- package/node_modules/pi-lens/dist/clients/lsp/index.js +491 -64
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -8
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -2
- package/node_modules/pi-lens/dist/clients/lsp/server.js +62 -15
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -1
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +5 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +6 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +37 -4
- package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -1
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +3 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +2 -0
- package/node_modules/pi-lens/dist/clients/metrics-history.js +28 -107
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +1 -1
- package/node_modules/pi-lens/dist/clients/module-report.js +1 -1
- package/node_modules/pi-lens/dist/clients/mutating-tool.js +3 -3
- package/node_modules/pi-lens/dist/clients/mutation-attribution.js +3 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -4
- package/node_modules/pi-lens/dist/clients/observed-mutation.js +9 -9
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +2 -6
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +2 -0
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +1 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +2 -1
- package/node_modules/pi-lens/dist/clients/path-utils.js +23 -2
- package/node_modules/pi-lens/dist/clients/performance-report.js +2 -2
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +9 -4
- package/node_modules/pi-lens/dist/clients/pipeline.js +5 -4
- package/node_modules/pi-lens/dist/clients/project-changes.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +38 -10
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +10 -9
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +49 -1
- package/node_modules/pi-lens/dist/clients/project-scan-policy.js +3 -51
- package/node_modules/pi-lens/dist/clients/python-provenance.js +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +3 -1
- package/node_modules/pi-lens/dist/clients/read-guard.js +5 -2
- package/node_modules/pi-lens/dist/clients/recent-touches.js +2 -2
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +3 -5
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +1 -1
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +2 -0
- package/node_modules/pi-lens/dist/clients/ruff-client.js +18 -9
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +10 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +5 -0
- package/node_modules/pi-lens/dist/clients/runtime-session.js +58 -14
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +11 -1
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +4 -1
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +48 -1
- package/node_modules/pi-lens/dist/clients/sanitize.js +0 -11
- package/node_modules/pi-lens/dist/clients/scan-utils.js +1 -56
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -2
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +69 -1
- package/node_modules/pi-lens/dist/clients/session-summary.js +0 -24
- package/node_modules/pi-lens/dist/clients/sg-runner.js +2 -1
- package/node_modules/pi-lens/dist/clients/sgconfig.js +1 -5
- package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +1 -1
- package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +108 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
- package/node_modules/pi-lens/dist/clients/startup-scan.js +2 -10
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +291 -76
- package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +77 -72
- package/node_modules/pi-lens/dist/clients/tool-config.js +199 -0
- package/node_modules/pi-lens/dist/clients/tool-cwd.js +299 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
- package/node_modules/pi-lens/dist/clients/tool-probe.js +53 -0
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +42 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +1 -52
- package/node_modules/pi-lens/dist/clients/trivy-client.js +9 -1
- package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -4
- package/node_modules/pi-lens/dist/clients/turn-context.js +52 -0
- package/node_modules/pi-lens/dist/clients/user-notify.js +0 -4
- package/node_modules/pi-lens/dist/clients/widget-state.js +4 -3
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/word-index-store.js +3 -7
- package/node_modules/pi-lens/dist/clients/word-index.js +9 -16
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +1 -1
- package/node_modules/pi-lens/dist/index.js +19969 -17481
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -2
- package/node_modules/pi-lens/dist/mcp/build-staleness.js +1 -1
- package/node_modules/pi-lens/dist/mcp/server.js +135 -159
- package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +1 -1
- package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +1 -1
- package/node_modules/pi-lens/dist/tools/activate-tools.js +10 -7
- package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +2 -13
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +8 -13
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +83 -39
- package/node_modules/pi-lens/dist/tools/effective-config.js +4 -3
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +1 -10
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +354 -86
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +54 -112
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +20 -44
- package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +3 -3
- package/node_modules/pi-lens/dist/tools/module-report.js +5 -9
- package/node_modules/pi-lens/dist/tools/project-report.js +9 -17
- package/node_modules/pi-lens/dist/tools/render-compact.js +281 -2
- package/node_modules/pi-lens/dist/tools/symbol-search.js +3 -4
- package/node_modules/pi-lens/docs/agent-guide.md +3 -4
- package/node_modules/pi-lens/docs/agent-tools.md +46 -22
- package/node_modules/pi-lens/docs/configuration.md +9 -0
- package/node_modules/pi-lens/docs/environment-variables.md +0 -5
- package/node_modules/pi-lens/docs/features.md +5 -5
- package/node_modules/pi-lens/docs/globalconfig.md +31 -1
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +9 -1
- package/node_modules/pi-lens/docs/mcp.md +13 -1
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +71 -0
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +15 -0
- package/node_modules/pi-lens/docs/pi-lens-monitor.md +88 -0
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +41 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +21 -2
- package/node_modules/pi-lens/docs/public-api-stability.md +1 -0
- package/node_modules/pi-lens/docs/real-harness.md +46 -0
- package/node_modules/pi-lens/docs/release-qa-baseline.md +17 -5
- package/node_modules/pi-lens/docs/servercapabilities.md +2 -3
- package/node_modules/pi-lens/docs/settings.md +37 -2
- package/node_modules/pi-lens/docs/tools_improvement2.md +4 -4
- package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
- package/node_modules/pi-lens/docs/usage.md +13 -3
- package/node_modules/pi-lens/docs/word-index.md +1 -1
- package/node_modules/pi-lens/package.json +15 -15
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +7 -3
- package/node_modules/pi-lens/rules/typos/_typos.toml +4 -2
- package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +1 -1
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +1 -1
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +4 -2
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +8 -6
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +19 -9
- package/node_modules/pi-web-access/CHANGELOG.md +29 -0
- package/node_modules/pi-web-access/README.md +33 -22
- package/node_modules/pi-web-access/abortable.ts +17 -0
- package/node_modules/pi-web-access/crawl4ai.ts +204 -0
- package/node_modules/pi-web-access/credential-source.ts +1 -0
- package/node_modules/pi-web-access/curator-page.ts +20 -2
- package/node_modules/pi-web-access/curator-run.ts +44 -0
- package/node_modules/pi-web-access/curator-server.ts +3 -1
- package/node_modules/pi-web-access/duckduckgo.ts +1 -1
- package/node_modules/pi-web-access/extract.ts +57 -12
- package/node_modules/pi-web-access/fetch-params.ts +1 -1
- package/node_modules/pi-web-access/gemini-search.ts +9 -5
- package/node_modules/pi-web-access/index.ts +25 -18
- package/node_modules/pi-web-access/openai-search.ts +35 -13
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/page-query.ts +5 -2
- package/node_modules/pi-web-access/query-rewrite.ts +5 -2
- package/node_modules/pi-web-access/serpapi.ts +220 -0
- package/node_modules/pi-web-access/ssrf-protection.ts +5 -1
- package/node_modules/pi-web-access/storage.ts +10 -0
- package/node_modules/pi-web-access/summary-review.ts +34 -21
- package/node_modules/pi-web-access/utils.ts +6 -4
- package/package.json +7 -4
- package/node_modules/pi-lens/dist/tools/ast-dump.js +0 -103
|
@@ -18,6 +18,7 @@ import { DEPENDENCY_DRIFT_MAX_DELIVERIES } from "../clients/blocker-freshness.js
|
|
|
18
18
|
import { freshnessFromMtime } from "../clients/freshness.js";
|
|
19
19
|
import { applyInlineSuppressions } from "../clients/dispatch/inline-suppressions.js";
|
|
20
20
|
import { gateFindingsByPathFreshness } from "../clients/advisory-provenance.js";
|
|
21
|
+
import { markUnreconciledFindings } from "../clients/finding-delivery-gate.js";
|
|
21
22
|
import { normalizeRuleId } from "../clients/dispatch/rule-id-normalize.js";
|
|
22
23
|
import { applyRulePolicy, rulePolicyMapFromConfig, } from "../clients/dispatch/rule-policy.js";
|
|
23
24
|
import { loadPiLensProjectConfig } from "../clients/project-lens-config.js";
|
|
@@ -26,7 +27,8 @@ import { combineAbortSignals } from "../clients/deadline-utils.js";
|
|
|
26
27
|
import { getProjectIgnoreMatcher } from "../clients/file-utils.js";
|
|
27
28
|
import { isAtOrAboveHomeDir, normalizeEphemeralMapKey, normalizeFilePath, } from "../clients/path-utils.js";
|
|
28
29
|
import { getLSPService } from "../clients/lsp/index.js";
|
|
29
|
-
import {
|
|
30
|
+
import { retireInlineBlockerAndResyncGuard } from "../clients/git-guard.js";
|
|
31
|
+
import { primaryServerId, resolveLspCwdForFile, } from "../clients/lsp/config.js";
|
|
30
32
|
import { getFullScanWallClockMs } from "../clients/lsp/workspace-sweep-hold.js";
|
|
31
33
|
import { demoteInferredProjectSweepResults } from "../clients/lsp/inferred-project.js";
|
|
32
34
|
import { hashDiagnosticContent, } from "../clients/lsp/diagnostic-binding.js";
|
|
@@ -38,11 +40,14 @@ import { generatedSkipNotice, scanTruncationNotice, } from "../clients/lens-engi
|
|
|
38
40
|
import { scanProjectDiagnostics } from "../clients/project-diagnostics/scanner.js";
|
|
39
41
|
import { classifyDiagnosticTier, getFileDiagnosticSummaries, reconcileCorrelatedScanDiagnostics, reconcileScanDiagnostics, reconcileStaleWidgetDependencyBlockers, reconcileStaleWidgetFiles, widgetDiagnosticUri, } from "../clients/widget-state.js";
|
|
40
42
|
import { logLatency } from "../clients/latency-logger.js";
|
|
43
|
+
import { logExtension } from "../clients/extension-log.js";
|
|
44
|
+
import { recordDegradationOnce } from "../clients/degradation-ledger.js";
|
|
41
45
|
import { convertLspDiagnostics } from "../clients/dispatch/utils/lsp-diagnostics.js";
|
|
42
46
|
import { retagAuxiliaryDiagnostics } from "../clients/dispatch/auxiliary-lsp.js";
|
|
43
47
|
import { detectFileRole } from "../clients/file-role.js";
|
|
44
48
|
import { STALE_LINE_MARKER } from "../clients/stale-marker.js";
|
|
45
49
|
import { makeProgressReporter, scanningSummaryLine } from "./scan-progress.js";
|
|
50
|
+
import { createLspDiagnosticsTool, LSP_SEVERITY_FILTERS, MAX_BATCH_FILES, } from "./lsp-diagnostics.js";
|
|
46
51
|
import { demotePastEofDiagnostics, PAST_EOF_STALE_MARKER, resyncDocumentOnPastEof, } from "../clients/diagnostic-line-freshness.js";
|
|
47
52
|
// The widget state exposes the full per-file diagnostic set; this is the tool's
|
|
48
53
|
// own generous display budget per file (independent of the TUI's 12 cap), to
|
|
@@ -53,7 +58,7 @@ const MAX_DIAGNOSTICS_PER_FILE = 50;
|
|
|
53
58
|
// narrow, not paginate. Erroring (rather than silently truncating) means a
|
|
54
59
|
// caller can never believe it checked files it didn't (issue's stated
|
|
55
60
|
// invariant).
|
|
56
|
-
const MAX_PATHS_ENTRIES =
|
|
61
|
+
const MAX_PATHS_ENTRIES = MAX_BATCH_FILES;
|
|
57
62
|
// #1623: the reason rendered for every heavyweight-analyzer lane (gitleaks,
|
|
58
63
|
// trivy, govulncheck, dead-code, knip, jscpd, madge, opengrep, test-runner)
|
|
59
64
|
// when mode=full is called without refreshRunners=cheap/all/cached — the
|
|
@@ -135,35 +140,75 @@ nextWriteIndex,
|
|
|
135
140
|
// #798/#338: capture host UI methods synchronously from the active tool event.
|
|
136
141
|
// The returned closure is safe for the later async sweep to invoke because it
|
|
137
142
|
// never dereferences the session-guarded ctx.ui getter.
|
|
138
|
-
captureLspStatusRepaint, getRuntime) {
|
|
143
|
+
captureLspStatusRepaint, getRuntime, isLensGuardEnabled = () => true) {
|
|
144
|
+
const lspProbe = createLspDiagnosticsTool(nextWriteIndex, (info) => {
|
|
145
|
+
const runtime = getRuntime?.();
|
|
146
|
+
if (!runtime)
|
|
147
|
+
return;
|
|
148
|
+
const retired = retireInlineBlockerAndResyncGuard({
|
|
149
|
+
runtime,
|
|
150
|
+
cacheManager,
|
|
151
|
+
cwd: info.cwd,
|
|
152
|
+
filePath: info.filePath,
|
|
153
|
+
...(info.writeIndex === undefined
|
|
154
|
+
? {}
|
|
155
|
+
: { writeIndex: info.writeIndex }),
|
|
156
|
+
coveredSources: info.coveredSources,
|
|
157
|
+
lensGuardEnabled: isLensGuardEnabled(),
|
|
158
|
+
});
|
|
159
|
+
if (retired) {
|
|
160
|
+
logExtension({
|
|
161
|
+
subsystem: "git-guard",
|
|
162
|
+
level: "debug",
|
|
163
|
+
message: `inline_blocker: retired for ${info.filePath}`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}, getLspService);
|
|
139
167
|
return {
|
|
140
168
|
name: "lens_diagnostics",
|
|
141
169
|
label: "Project Diagnostics",
|
|
142
|
-
description:
|
|
143
|
-
|
|
144
|
-
"IMPORTANT: unlike lsp_diagnostics (LSP only), this tool covers ALL dispatch " +
|
|
145
|
-
"runners: LSP errors, tree-sitter structural rules, ast-grep security rules, " +
|
|
146
|
-
"biome/ruff/eslint lint findings, complexity violations, and more.\n\n" +
|
|
147
|
-
"mode=delta (default): all warnings for the current agent turn — fixable warnings " +
|
|
148
|
-
"(actionable-warnings cache) AND code quality/style/complexity issues " +
|
|
149
|
-
"(code-quality-warnings cache). Same scope as the turn-end advisory, current turn only.\n\n" +
|
|
150
|
-
"mode=all: blocking errors and warnings — with the actual messages (line, rule, " +
|
|
151
|
-
"text), not just counts — for every file the agent has " +
|
|
152
|
-
"EDITED this session (files that went through the dispatch pipeline). " +
|
|
153
|
-
"NOTE: unedited files with pre-existing errors do NOT appear here — this is " +
|
|
154
|
-
"not a full project scan. Use before declaring work done; stale blocking " +
|
|
155
|
-
"errors from earlier turns are visible even if they dropped from turn-end context.\n\n" +
|
|
156
|
-
"mode=full: EXPENSIVE active scan. Runs project-wide LSP diagnostics for " +
|
|
157
|
-
"all supported files (including unedited files), then merges/deduplicates " +
|
|
158
|
-
"that with mode=all cached runner state. Optional refreshRunners=cheap/all/cached " +
|
|
159
|
-
"folds in project-wide runner findings: the in-process scanners (tree-sitter + " +
|
|
160
|
-
"fact-rules + ast-grep) plus a FRESH run of the heavyweight analyzers — knip, " +
|
|
161
|
-
"jscpd (copy-paste), madge (circular deps), gitleaks (secrets), govulncheck/trivy " +
|
|
162
|
-
"(CVEs), dead-code — rather than a possibly-stale session_start cache; each " +
|
|
163
|
-
"analyzer de-dupes against a concurrent background run of itself, so this can't " +
|
|
164
|
-
"double-spawn. Bounded by the slowest analyzer (trivy's own ~180s ceiling).",
|
|
165
|
-
promptSnippet: "Use lens_diagnostics mode=all to verify no blocking errors remain; use mode=full for expensive project-wide checks",
|
|
170
|
+
description: 'Query pi-lens diagnostics from the session cache or an active LSP probe, at paths or workspace scope. Empty cache is not proof of clean; probe changed paths when findings are absent or stale. Example: `{source: "lsp", scope: "paths", paths: ["src/app.ts"]}`.',
|
|
171
|
+
promptSnippet: "lens_diagnostics source=session reads the session cache and an empty cache is not proof of a clean file; use source=lsp scope=paths for changed files when cached findings are absent or stale",
|
|
166
172
|
renderResult: compactRenderResult(({ details, args, isError, text }) => {
|
|
173
|
+
if (details?.source === "lsp") {
|
|
174
|
+
const count = details.totalDiagnostics ?? 0;
|
|
175
|
+
const files = details.filesChecked ?? details.filesScanned ?? 0;
|
|
176
|
+
const noun = count === 1 ? "diagnostic" : "diagnostics";
|
|
177
|
+
const filePath = typeof details?.filePath === "string"
|
|
178
|
+
? details.filePath
|
|
179
|
+
: Array.isArray(args?.paths) &&
|
|
180
|
+
args.paths.length === 1 &&
|
|
181
|
+
typeof args.paths[0] === "string"
|
|
182
|
+
? args.paths[0]
|
|
183
|
+
: undefined;
|
|
184
|
+
const singleFile = (details?.mode === "file" || files === 1) &&
|
|
185
|
+
typeof filePath === "string"
|
|
186
|
+
? ` ${path.basename(filePath)}`
|
|
187
|
+
: "";
|
|
188
|
+
const scope = files > 1 ? ` across ${files} files` : singleFile;
|
|
189
|
+
if (isError)
|
|
190
|
+
return `lens_diagnostics lsp — ${text.split("\n")[0] ?? "error"}`;
|
|
191
|
+
if ((details.navigationOnlyFiles ?? 0) > 0)
|
|
192
|
+
return `lens_diagnostics${scope} — ${count} ${noun} · ${details.cleanFiles ?? 0} clean · ${details.navigationOnlyFiles} navigation-only`;
|
|
193
|
+
if ((details.unconfirmedFiles ?? 0) > 0)
|
|
194
|
+
return `lens_diagnostics${scope} — ${count} ${noun} · ${details.cleanFiles ?? 0} clean · ${details.unconfirmedFiles} unconfirmed${details.timedOutFiles ? ` (${details.timedOutFiles} timed out)` : ""}`;
|
|
195
|
+
const outcomeCounts = details.outcomeCounts;
|
|
196
|
+
const notConfirmed = outcomeCounts
|
|
197
|
+
? (outcomeCounts.inconclusive ?? 0) +
|
|
198
|
+
(outcomeCounts.unavailable ?? 0) +
|
|
199
|
+
(outcomeCounts.unsupported ?? 0) +
|
|
200
|
+
(outcomeCounts.failed ?? 0)
|
|
201
|
+
: 0;
|
|
202
|
+
if (notConfirmed > 0)
|
|
203
|
+
return `lens_diagnostics${scope} — ${count} ${noun} · ${notConfirmed} checks not confirmed`;
|
|
204
|
+
if ((details.incompleteFiles ?? 0) > 0)
|
|
205
|
+
return `lens_diagnostics${scope} — incomplete (${details.incompleteFiles} files not confirmed)`;
|
|
206
|
+
if (count === 0 && details.unconfirmed)
|
|
207
|
+
return details.timedOut
|
|
208
|
+
? `lens_diagnostics${scope} — timed out (result may be incomplete)`
|
|
209
|
+
: `lens_diagnostics${scope} — unconfirmed (server cannot confirm clean)`;
|
|
210
|
+
return `lens_diagnostics${scope} — ${count} ${noun}`;
|
|
211
|
+
}
|
|
167
212
|
// Streaming progress partials render the live bar (see scanningSummaryLine)
|
|
168
213
|
// instead of the details-driven summary, which would show "0 diagnostics"
|
|
169
214
|
// mid-scan.
|
|
@@ -222,54 +267,124 @@ captureLspStatusRepaint, getRuntime) {
|
|
|
222
267
|
return `lens_diagnostics ${mode} — ${parts.join(" · ")} (${files} files)${coldSuffix}${failedSuffix}`;
|
|
223
268
|
}),
|
|
224
269
|
parameters: Type.Object({
|
|
270
|
+
source: Type.Optional(Type.String({
|
|
271
|
+
enum: ["session", "lsp"],
|
|
272
|
+
description: "Evidence source: session cache (empty cache is not proof of a " +
|
|
273
|
+
"clean file — use source=lsp for changed files) or an active LSP probe.",
|
|
274
|
+
})),
|
|
275
|
+
scope: Type.Optional(Type.String({
|
|
276
|
+
enum: ["paths", "workspace"],
|
|
277
|
+
description:
|
|
278
|
+
// #2860: no schema value maps to "current turn's delta" — the
|
|
279
|
+
// default (omitted scope) already gives that for
|
|
280
|
+
// source=session, and for source=lsp `delta` was byte-identical
|
|
281
|
+
// to `paths` (round-2 verify N/F4), so exposing it would only
|
|
282
|
+
// invite a caller to believe it does something distinct.
|
|
283
|
+
"Coverage scope: explicit paths, or workspace (default: current turn's delta for source=session).",
|
|
284
|
+
})),
|
|
225
285
|
mode: Type.Optional(Type.String({
|
|
226
286
|
enum: ["delta", "all", "full"],
|
|
227
|
-
description: "delta = current turn
|
|
228
|
-
|
|
229
|
-
|
|
287
|
+
description: "delta = current turn; all = cache-only; full = active LSP scan of paths. Empty cache is not proof of clean.",
|
|
288
|
+
})),
|
|
289
|
+
path: Type.Optional(Type.String({
|
|
290
|
+
description: "One file or directory for source=lsp checks.",
|
|
291
|
+
})),
|
|
292
|
+
concurrency: Type.Optional(Type.Number({
|
|
293
|
+
description: "Source=lsp batch concurrency (maximum 16).",
|
|
294
|
+
})),
|
|
295
|
+
waitMs: Type.Optional(Type.Number({
|
|
296
|
+
description: "Source=lsp per-file wait budget in milliseconds.",
|
|
297
|
+
})),
|
|
298
|
+
serverScope: Type.Optional(Type.String({
|
|
299
|
+
enum: ["primary", "all"],
|
|
300
|
+
description: "Source=lsp server coverage: primary or all.",
|
|
230
301
|
})),
|
|
231
302
|
refreshRunners: Type.Optional(Type.Union([
|
|
232
303
|
Type.Boolean(),
|
|
233
304
|
Type.String({ enum: ["cached", "cheap", "all", "none"] }),
|
|
234
305
|
], {
|
|
235
|
-
description: "
|
|
306
|
+
description: "Analyzer refresh mode for full view.",
|
|
236
307
|
})),
|
|
237
308
|
maxProjectFiles: Type.Optional(Type.Number({
|
|
238
|
-
description: "
|
|
309
|
+
description: "Project-file limit for cheap runners.",
|
|
239
310
|
})),
|
|
240
311
|
maxLspFiles: Type.Optional(Type.Number({
|
|
241
|
-
description: "
|
|
312
|
+
description: "File limit for the LSP sweep.",
|
|
242
313
|
})),
|
|
243
314
|
includeGenerated: Type.Optional(Type.Boolean({
|
|
244
|
-
description: "
|
|
315
|
+
description: "Include generated-name paths in full scans.",
|
|
245
316
|
})),
|
|
246
317
|
severity: Type.Optional(Type.String({
|
|
247
|
-
enum: [
|
|
248
|
-
description: "Filter by severity (default: all).",
|
|
318
|
+
enum: [...LSP_SEVERITY_FILTERS],
|
|
319
|
+
description: "Filter by severity threshold (default: all): error shows errors; warning includes errors and warnings; information includes errors, warnings, and information; hint and all show every known tier.",
|
|
249
320
|
})),
|
|
250
321
|
paths: Type.Optional(Type.Array(Type.String(), {
|
|
251
322
|
maxItems: MAX_PATHS_ENTRIES,
|
|
252
|
-
description:
|
|
253
|
-
"more errors instead of silently truncating). Entries may be relative " +
|
|
254
|
-
"(resolved against cwd) or absolute, and a directory entry matches all " +
|
|
255
|
-
'files under it (e.g. "src/"). mode=delta/all are a pure post-filter ' +
|
|
256
|
-
"of cached/session state — they can only show findings for files pi-lens " +
|
|
257
|
-
"has already dispatched, so an unseen file shows nothing (use mode=full " +
|
|
258
|
-
"for an active scan). mode=full actively scans exactly these paths (LSP " +
|
|
259
|
-
"sweep + cheap in-process runners); cached heavyweight analyzers " +
|
|
260
|
-
"(jscpd/madge/gitleaks/knip) and the project snapshot are still post-filtered " +
|
|
261
|
-
"cache reads, never relaunched. Explicitly-listed files are NOT filtered " +
|
|
262
|
-
"through the project ignore matcher (matching lsp_diagnostics' paths " +
|
|
263
|
-
"semantics) — naming a file is assumed to mean it regardless of " +
|
|
264
|
-
".gitignore/.pi-lens.json; a directory entry's expansion still honors " +
|
|
265
|
-
"ignore (and when the list mixes directories and files, mode=full scans " +
|
|
266
|
-
"via the ignore-filtered walk, so an ignore-excluded file entry is only " +
|
|
267
|
-
"guaranteed an active scan in a files-only list). Nonexistent entries " +
|
|
268
|
-
"are skipped (mode=full notes them; useful for git-staged-file wrappers " +
|
|
269
|
-
"where a deleted-but-staged path can appear).",
|
|
323
|
+
description: "Files or directories to filter.",
|
|
270
324
|
})),
|
|
271
325
|
}),
|
|
272
326
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
327
|
+
const requestedSource = params.source;
|
|
328
|
+
const requestedScope = params.scope;
|
|
329
|
+
const legacyMode = params.mode;
|
|
330
|
+
const source = requestedSource ?? "session";
|
|
331
|
+
const scope = requestedScope ??
|
|
332
|
+
(legacyMode === "delta" || legacyMode === undefined
|
|
333
|
+
? source === "lsp"
|
|
334
|
+
? "paths"
|
|
335
|
+
: "delta"
|
|
336
|
+
: "workspace");
|
|
337
|
+
const cwd = ctx.cwd ?? getCwd();
|
|
338
|
+
if (source === "lsp") {
|
|
339
|
+
const lspParams = { ...params };
|
|
340
|
+
delete lspParams.source;
|
|
341
|
+
delete lspParams.scope;
|
|
342
|
+
// #2860: explicit `path`/`paths` always win. `scope=workspace`
|
|
343
|
+
// only supplies a `cwd` default (a full workspace sweep) when the
|
|
344
|
+
// caller gave NEITHER — it must never override an explicit
|
|
345
|
+
// narrower request (the #2052 root-eviction hang and the
|
|
346
|
+
// SKILL.md "check a folder" recipe both depend on this: a
|
|
347
|
+
// directory `path` under scope=workspace scans that directory,
|
|
348
|
+
// not the whole project).
|
|
349
|
+
const hasExplicitPath = typeof lspParams.path === "string" && lspParams.path.length > 0;
|
|
350
|
+
const hasExplicitPaths = Array.isArray(lspParams.paths) && lspParams.paths.length > 0;
|
|
351
|
+
if (scope === "workspace" && !hasExplicitPath && !hasExplicitPaths) {
|
|
352
|
+
lspParams.path = cwd;
|
|
353
|
+
}
|
|
354
|
+
const lspStart = Date.now();
|
|
355
|
+
const result = (await lspProbe.execute(_toolCallId, lspParams, signal, onUpdate, ctx.signal ? { cwd, signal: ctx.signal } : { cwd }));
|
|
356
|
+
// #2860 F11: the folded route's own durable trace — without this,
|
|
357
|
+
// F1's class of defect (a real LSP result rendering as "clean")
|
|
358
|
+
// leaves no record in latency.log distinguishing source=lsp from
|
|
359
|
+
// source=session. One record per tool call (not per file), same
|
|
360
|
+
// convention as the mode=all `blocker_freshness_widget_gate`
|
|
361
|
+
// phase above.
|
|
362
|
+
logLatency({
|
|
363
|
+
type: "phase",
|
|
364
|
+
toolName: "lens_diagnostics",
|
|
365
|
+
filePath: cwd,
|
|
366
|
+
phase: "lens_diagnostics_lsp_route",
|
|
367
|
+
durationMs: Date.now() - lspStart,
|
|
368
|
+
metadata: {
|
|
369
|
+
scope,
|
|
370
|
+
isError: result.isError === true,
|
|
371
|
+
totalDiagnostics: result.details?.totalDiagnostics ?? 0,
|
|
372
|
+
cleanFiles: result.details?.cleanFiles ?? 0,
|
|
373
|
+
unconfirmedFiles: result.details?.unconfirmedFiles ?? 0,
|
|
374
|
+
timedOutFiles: result.details?.timedOutFiles ?? 0,
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
return {
|
|
378
|
+
...result,
|
|
379
|
+
isError: result.isError === true,
|
|
380
|
+
details: { ...result.details, source, scope },
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
if (requestedSource !== undefined) {
|
|
384
|
+
const effectiveMode = legacyMode ??
|
|
385
|
+
(scope === "workspace" ? "all" : scope === "paths" ? "all" : "delta");
|
|
386
|
+
params = { ...params, mode: effectiveMode };
|
|
387
|
+
}
|
|
273
388
|
const repaintLspStatus = captureLspStatusRepaint?.(ctx);
|
|
274
389
|
const mode = params.mode ?? "delta";
|
|
275
390
|
const severity = params.severity ?? "all";
|
|
@@ -280,7 +395,6 @@ captureLspStatusRepaint, getRuntime) {
|
|
|
280
395
|
const maxProjectFiles = parsePositiveInt(params.maxProjectFiles);
|
|
281
396
|
const maxLspFiles = parsePositiveInt(params.maxLspFiles);
|
|
282
397
|
const includeGenerated = params.includeGenerated === true;
|
|
283
|
-
const cwd = ctx.cwd ?? getCwd();
|
|
284
398
|
let pathsScope;
|
|
285
399
|
try {
|
|
286
400
|
pathsScope = resolvePathsScope(cwd, params.paths);
|
|
@@ -633,8 +747,7 @@ function formatDeltaMode(cacheManager, cwd, severity, pathsScope) {
|
|
|
633
747
|
// filtered findings). Weak-anchored like weak disposition — zero I/O.
|
|
634
748
|
const policyMap = loadProjectRulePolicyMap(cwd);
|
|
635
749
|
const projectDelta = filterDeltaReportDispositions(loadProjectDiagnosticsDeltaReport(cwd), cwd, policyMap);
|
|
636
|
-
const
|
|
637
|
-
const includeFile = (filePath) => ignoreFile(filePath) && (!pathsScope || pathsScope.includeFile(filePath));
|
|
750
|
+
const includeFile = createScopedFileFilter(cwd, pathsScope);
|
|
638
751
|
// #755: delta re-serves the actionable/quality caches verbatim, but those
|
|
639
752
|
// were filtered at DISPATCH time — before any lens_diagnostic_mark. Re-apply
|
|
640
753
|
// dispositions here so a mark converges immediately, not only on the next
|
|
@@ -652,10 +765,24 @@ function formatDeltaMode(cacheManager, cwd, severity, pathsScope) {
|
|
|
652
765
|
.filter((file) => file.warnings.length > 0);
|
|
653
766
|
const actionableFiles = applyDeltaFreshnessGate(visibleWarningFiles(actionable?.files), cwd, actionable?.generatedAt);
|
|
654
767
|
const qualityFiles = applyDeltaFreshnessGate(visibleWarningFiles(quality?.files), cwd, quality?.generatedAt);
|
|
768
|
+
const matchingWarnings = (warnings) => warnings.filter((warning) => matchesRecordSeverity(warning.severity, severity));
|
|
769
|
+
const filteredActionableFiles = actionableFiles
|
|
770
|
+
.map((file) => ({
|
|
771
|
+
...file,
|
|
772
|
+
warnings: matchingWarnings(file.warnings),
|
|
773
|
+
}))
|
|
774
|
+
.filter((file) => file.warnings.length > 0);
|
|
775
|
+
const filteredQualityFiles = qualityFiles
|
|
776
|
+
.map((file) => ({
|
|
777
|
+
...file,
|
|
778
|
+
warnings: matchingWarnings(file.warnings),
|
|
779
|
+
}))
|
|
780
|
+
.filter((file) => file.warnings.length > 0);
|
|
655
781
|
const lines = [];
|
|
656
|
-
// Fixable warnings from actionable-warnings
|
|
657
|
-
|
|
658
|
-
|
|
782
|
+
// Fixable warnings from actionable-warnings and quality cache entries retain
|
|
783
|
+
// their own severity tier. Apply the same threshold semantics as the LSP path.
|
|
784
|
+
if (filteredActionableFiles.length > 0) {
|
|
785
|
+
for (const file of filteredActionableFiles) {
|
|
659
786
|
const rel = path.relative(cwd, file.filePath);
|
|
660
787
|
lines.push(`${rel}`);
|
|
661
788
|
for (const w of file.warnings) {
|
|
@@ -665,8 +792,8 @@ function formatDeltaMode(cacheManager, cwd, severity, pathsScope) {
|
|
|
665
792
|
}
|
|
666
793
|
}
|
|
667
794
|
// Quality issues
|
|
668
|
-
if (
|
|
669
|
-
for (const file of
|
|
795
|
+
if (filteredQualityFiles.length > 0) {
|
|
796
|
+
for (const file of filteredQualityFiles) {
|
|
670
797
|
const rel = path.relative(cwd, file.filePath);
|
|
671
798
|
if (!lines.includes(rel))
|
|
672
799
|
lines.push(rel);
|
|
@@ -677,8 +804,10 @@ function formatDeltaMode(cacheManager, cwd, severity, pathsScope) {
|
|
|
677
804
|
}
|
|
678
805
|
}
|
|
679
806
|
const projectDeltaCount = appendProjectDiagnosticsDeltaLines(lines, cwd, projectDelta, severity, includeFile);
|
|
680
|
-
const
|
|
681
|
-
const
|
|
807
|
+
const selectedActionableFiles = filteredActionableFiles;
|
|
808
|
+
const selectedQualityFiles = filteredQualityFiles;
|
|
809
|
+
const aw = selectedActionableFiles.reduce((count, file) => count + file.warnings.length, 0);
|
|
810
|
+
const cq = selectedQualityFiles.reduce((count, file) => count + file.warnings.length, 0);
|
|
682
811
|
if (lines.length === 0) {
|
|
683
812
|
let text = `No ${severity === "all" ? "" : severity + " "}issues in the current turn delta.`;
|
|
684
813
|
// Discoverability (#190): `delta` is current-turn-scoped, so it's empty
|
|
@@ -772,6 +901,7 @@ function resolvePathsScope(cwd, rawPaths) {
|
|
|
772
901
|
}
|
|
773
902
|
const fileKeys = new Set(fileEntries.map((f) => normalizeFilePath(f)));
|
|
774
903
|
const dirPrefixes = dirEntries.map((d) => normalizeFilePath(d));
|
|
904
|
+
const includeExplicitFile = (filePath) => fileKeys.has(normalizeFilePath(path.resolve(filePath)));
|
|
775
905
|
const includeFile = (filePath) => {
|
|
776
906
|
const key = normalizeFilePath(path.resolve(filePath));
|
|
777
907
|
if (fileKeys.has(key))
|
|
@@ -780,6 +910,7 @@ function resolvePathsScope(cwd, rawPaths) {
|
|
|
780
910
|
};
|
|
781
911
|
return {
|
|
782
912
|
includeFile,
|
|
913
|
+
includeExplicitFile,
|
|
783
914
|
missing,
|
|
784
915
|
existingEntries,
|
|
785
916
|
hasDirectoryEntries: dirEntries.length > 0,
|
|
@@ -803,6 +934,16 @@ function pathsScopeMissingNote(scope) {
|
|
|
803
934
|
: "";
|
|
804
935
|
return `\n\n⚠ Skipped ${scope.missing.length} path(s) not found on disk: ${shown.join(", ")}${more}`;
|
|
805
936
|
}
|
|
937
|
+
function createScopedFileFilter(cwd, pathsScope) {
|
|
938
|
+
const ignoreFile = createCurrentIgnoreFilter(cwd);
|
|
939
|
+
return (filePath) => {
|
|
940
|
+
if (!pathsScope)
|
|
941
|
+
return ignoreFile(filePath);
|
|
942
|
+
if (!pathsScope.includeFile(filePath))
|
|
943
|
+
return false;
|
|
944
|
+
return pathsScope.includeExplicitFile(filePath) || ignoreFile(filePath);
|
|
945
|
+
};
|
|
946
|
+
}
|
|
806
947
|
function filterProjectDiagnosticsSnapshot(snapshot, includeFile) {
|
|
807
948
|
if (!snapshot)
|
|
808
949
|
return undefined;
|
|
@@ -845,10 +986,28 @@ function isErrorLike(d) {
|
|
|
845
986
|
return d.semantic === "blocking" || d.severity === "error";
|
|
846
987
|
}
|
|
847
988
|
function matchesSeverity(d, severity) {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
989
|
+
return matchesRecordSeverity(isErrorLike(d) ? "error" : d.severity, severity);
|
|
990
|
+
}
|
|
991
|
+
function matchesRecordSeverity(recordSeverity, requested) {
|
|
992
|
+
if (requested === "all")
|
|
993
|
+
return true;
|
|
994
|
+
if (requested === "error")
|
|
995
|
+
return recordSeverity === "error";
|
|
996
|
+
if (requested === "warning")
|
|
997
|
+
return recordSeverity === "error" || recordSeverity === "warning";
|
|
998
|
+
if (requested === "information")
|
|
999
|
+
return (recordSeverity === "error" ||
|
|
1000
|
+
recordSeverity === "warning" ||
|
|
1001
|
+
recordSeverity === "info" ||
|
|
1002
|
+
recordSeverity === "note" ||
|
|
1003
|
+
recordSeverity === "help");
|
|
1004
|
+
if (requested === "hint")
|
|
1005
|
+
return (recordSeverity === "error" ||
|
|
1006
|
+
recordSeverity === "warning" ||
|
|
1007
|
+
recordSeverity === "info" ||
|
|
1008
|
+
recordSeverity === "note" ||
|
|
1009
|
+
recordSeverity === "help" ||
|
|
1010
|
+
recordSeverity === "hint");
|
|
852
1011
|
return true;
|
|
853
1012
|
}
|
|
854
1013
|
/** Most-important first: blocking → error → warning/other, then by line. */
|
|
@@ -918,6 +1077,14 @@ function diagnosticDedupKey(filePath, diagnostic) {
|
|
|
918
1077
|
const ruleId = normalizeRuleId(diagnostic.rule ?? diagnostic.tool ?? "");
|
|
919
1078
|
return [path.resolve(filePath), diagnostic.line ?? "?", ruleId].join(":");
|
|
920
1079
|
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Which project runner produced a retained row (#2154). ONE derivation, used by
|
|
1082
|
+
* both the retirement filter and the record that counts what it retired — v2's
|
|
1083
|
+
* N2 was two verbatim copies of this expression drifting apart.
|
|
1084
|
+
*/
|
|
1085
|
+
function runnerIdOf(diagnostic) {
|
|
1086
|
+
return diagnostic.tool ?? diagnostic.rule?.split(":", 1)[0] ?? "";
|
|
1087
|
+
}
|
|
921
1088
|
function summarizeDiagnostics(filePath, diagnostics, hasFinalSnapshot) {
|
|
922
1089
|
let blocking = 0;
|
|
923
1090
|
let errors = 0;
|
|
@@ -1107,7 +1274,7 @@ function tallyLspPrimaryVsAuxiliary(results) {
|
|
|
1107
1274
|
for (const result of results) {
|
|
1108
1275
|
const primaryId = primaryServerId(result.filePath);
|
|
1109
1276
|
for (const diagnostic of result.diagnostics ?? []) {
|
|
1110
|
-
if (diagnostic.
|
|
1277
|
+
if (diagnostic.serverId === primaryId)
|
|
1111
1278
|
primary += 1;
|
|
1112
1279
|
else
|
|
1113
1280
|
auxiliary += 1;
|
|
@@ -1115,7 +1282,7 @@ function tallyLspPrimaryVsAuxiliary(results) {
|
|
|
1115
1282
|
}
|
|
1116
1283
|
return { primary, auxiliary };
|
|
1117
1284
|
}
|
|
1118
|
-
|
|
1285
|
+
function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projectSnapshot, projectDelta,
|
|
1119
1286
|
/**
|
|
1120
1287
|
* #1993: resolved paths covered by a CONFIRMED, fully-covered LSP result
|
|
1121
1288
|
* in `lspResults`. The fresh sweep is AUTHORITATIVE for these files - any
|
|
@@ -1125,7 +1292,7 @@ export function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults,
|
|
|
1125
1292
|
* additive behavior (fail-open: unconfirmed/timed-out sweeps never retire
|
|
1126
1293
|
* widget state they did not actually re-check).
|
|
1127
1294
|
*/
|
|
1128
|
-
authoritativeLspFiles) {
|
|
1295
|
+
authoritativeLspFiles, authoritativeRunnerIds) {
|
|
1129
1296
|
const byFile = new Map();
|
|
1130
1297
|
const seen = new Set();
|
|
1131
1298
|
for (const summary of widgetSummaries) {
|
|
@@ -1138,8 +1305,18 @@ authoritativeLspFiles) {
|
|
|
1138
1305
|
// suppression mechanism.
|
|
1139
1306
|
if (authoritativeLspFiles?.has(filePath))
|
|
1140
1307
|
continue;
|
|
1141
|
-
const
|
|
1142
|
-
|
|
1308
|
+
const retained = summary.diagnostics ?? [];
|
|
1309
|
+
const diagnostics = retained
|
|
1310
|
+
.filter((diagnostic) => !authoritativeRunnerIds?.has(runnerIdOf(diagnostic)))
|
|
1311
|
+
.map((d) => ({ ...d }));
|
|
1312
|
+
byFile.set(filePath,
|
|
1313
|
+
// #2154 v4: when the filter retires rows, the stored tallies describe
|
|
1314
|
+
// diagnostics that are no longer delivered — full mode rendered
|
|
1315
|
+
// `main.go 1W … 1 warning` with no row under it. Recompute from what
|
|
1316
|
+
// survived, and only then (an untouched file keeps its own counts).
|
|
1317
|
+
diagnostics.length === retained.length
|
|
1318
|
+
? { ...summary, filePath, diagnostics }
|
|
1319
|
+
: summarizeDiagnostics(filePath, diagnostics, summary.hasFinalSnapshot ?? true));
|
|
1143
1320
|
for (const diagnostic of diagnostics) {
|
|
1144
1321
|
seen.add(diagnosticDedupKey(filePath, diagnostic));
|
|
1145
1322
|
}
|
|
@@ -1302,8 +1479,7 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1302
1479
|
};
|
|
1303
1480
|
}
|
|
1304
1481
|
const { signal, pathsScope, nextWriteIndex } = options;
|
|
1305
|
-
const
|
|
1306
|
-
const includeFile = (filePath) => ignoreFile(filePath) && (!pathsScope || pathsScope.includeFile(filePath));
|
|
1482
|
+
const includeFile = createScopedFileFilter(cwd, pathsScope);
|
|
1307
1483
|
// `paths` (#461): route the active scans at exactly the requested files
|
|
1308
1484
|
// instead of walking the whole project. Three cases:
|
|
1309
1485
|
// - files only → pass them as the explicit list (skips the walk). An EMPTY
|
|
@@ -1335,6 +1511,7 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1335
1511
|
: Promise.resolve({
|
|
1336
1512
|
diagnostics: [],
|
|
1337
1513
|
runners: [],
|
|
1514
|
+
analyzed: [],
|
|
1338
1515
|
// #1623: every heavyweight analyzer is ELIGIBLE for this project but
|
|
1339
1516
|
// this call never asked for it (refreshRunners wasn't cheap/all/
|
|
1340
1517
|
// cached) — the expensive fetch below deliberately never runs in
|
|
@@ -1393,6 +1570,8 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1393
1570
|
const fullyCoveredLspResults = confirmedLspResults.filter((result) => (result.unconfirmedServerIds?.length ?? 0) === 0);
|
|
1394
1571
|
const partiallyCoveredLspResults = confirmedLspResults.filter((result) => (result.unconfirmedServerIds?.length ?? 0) > 0);
|
|
1395
1572
|
const unconfirmedLspResults = lspResults.filter((result) => result.timedOut || result.error || mismatchedLspResults.has(result));
|
|
1573
|
+
const authoritativeLspFiles = new Set();
|
|
1574
|
+
const unreconciledFiles = new Set();
|
|
1396
1575
|
// #571: reconcile this scan's fresh, CONFIRMED per-file results into the
|
|
1397
1576
|
// footer cache. A footer write is never allowed to fail the tool call, so
|
|
1398
1577
|
// any unexpected throw is swallowed.
|
|
@@ -1413,7 +1592,7 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1413
1592
|
// (`applyAuxiliarySuppressions`), so an empty content string here is a
|
|
1414
1593
|
// safe no-op re-check rather than a behavior gap.
|
|
1415
1594
|
const retagged = retagAuxiliaryDiagnostics(diagnostics, result.diagnostics, "", { cwd, fileRole: detectFileRole(result.filePath) });
|
|
1416
|
-
reconcileScanDiagnostics(result.filePath, retagged, true,
|
|
1595
|
+
const retired = reconcileScanDiagnostics(result.filePath, retagged, true,
|
|
1417
1596
|
// The service reserves this token when the file enters the scan. The
|
|
1418
1597
|
// fallback preserves compatibility with older test doubles/services.
|
|
1419
1598
|
result.writeIndex ?? nextWriteIndex?.(),
|
|
@@ -1424,9 +1603,31 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1424
1603
|
// finding on screen by re-arming the mtime gate. Undefined for
|
|
1425
1604
|
// freshly-touched results (observed now).
|
|
1426
1605
|
result.observedAt);
|
|
1606
|
+
// A result rejected by the shared ordering guard is not authoritative
|
|
1607
|
+
// for delivery. Otherwise full mode hides the old widget row while
|
|
1608
|
+
// mode=all still serves it, creating a false clean/full disagreement.
|
|
1609
|
+
// Strict `=== true`: `undefined` (a double that predates the boolean
|
|
1610
|
+
// return) takes the unreconciled arm, never the retiring one.
|
|
1611
|
+
if (retired === true) {
|
|
1612
|
+
authoritativeLspFiles.add(path.resolve(result.filePath));
|
|
1613
|
+
}
|
|
1614
|
+
else {
|
|
1615
|
+
unreconciledFiles.add(path.resolve(result.filePath));
|
|
1616
|
+
recordDegradationOnce({
|
|
1617
|
+
kind: "diagnostic-retained-unreconciled",
|
|
1618
|
+
subject: `${path.resolve(result.filePath)}:lsp`,
|
|
1619
|
+
reason: "confirmed diagnostic result could not replace retained widget state",
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1427
1622
|
}
|
|
1428
1623
|
catch {
|
|
1429
1624
|
// Never let a footer-reconciliation hiccup fail the scan itself.
|
|
1625
|
+
unreconciledFiles.add(path.resolve(result.filePath));
|
|
1626
|
+
recordDegradationOnce({
|
|
1627
|
+
kind: "diagnostic-retained-unreconciled",
|
|
1628
|
+
subject: `${path.resolve(result.filePath)}:lsp`,
|
|
1629
|
+
reason: "confirmed diagnostic result failed during widget reconciliation",
|
|
1630
|
+
});
|
|
1430
1631
|
}
|
|
1431
1632
|
}
|
|
1432
1633
|
// Project rule policy (`.pi-lens.json` `rules.<id>.disable`/`select`) —
|
|
@@ -1454,7 +1655,15 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1454
1655
|
// computed above, in the SAME `Promise.all` as the LSP sweep (#613) — only
|
|
1455
1656
|
// when the caller opted into project-runner state (otherwise it's the
|
|
1456
1657
|
// `Promise.resolve({...})` stub from `analyzersPromise` above).
|
|
1457
|
-
|
|
1658
|
+
// #2154: only ids whose client said it parsed a scan of this root this call
|
|
1659
|
+
// (`FreshProjectDiagnosticsResult.analyzed`). Every exclusion — a skipped
|
|
1660
|
+
// runner, a crash that still reported success, the cache-read test-runner
|
|
1661
|
+
// lane — is decided at the record site in fresh-fetch.ts, not re-derived
|
|
1662
|
+
// here; a second list of "which ids don't count" would be the mirror this
|
|
1663
|
+
// repo's single-source-of-truth rule forbids.
|
|
1664
|
+
const authoritativeRunnerIds = new Set(extracted.analyzed ?? []);
|
|
1665
|
+
const foldedProjectSnapshot = foldExtraDiagnosticsIntoSnapshot(scannedSnapshot, extracted.diagnostics.filter((d) => includeFile(d.filePath)), extracted.runners, cwd);
|
|
1666
|
+
const projectSnapshot = applyProjectRulePolicy(foldedProjectSnapshot, policyMap);
|
|
1458
1667
|
const projectDelta = applyProjectRulePolicy(filterProjectDiagnosticsDeltaReport(loadProjectDiagnosticsDeltaReport(cwd), includeFile), policyMap);
|
|
1459
1668
|
// #630: only the CONFIRMED LSP results contribute diagnostics to the merge
|
|
1460
1669
|
// — an unconfirmed (timed-out/errored) file's placeholder `[]` must not be
|
|
@@ -1464,7 +1673,6 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1464
1673
|
// #1993: files with a CONFIRMED, fully-covered LSP result are authoritative
|
|
1465
1674
|
// - the fresh sweep replaces their widget-store state instead of merging
|
|
1466
1675
|
// additively beside it.
|
|
1467
|
-
const authoritativeLspFiles = new Set(fullyCoveredLspResults.map((result) => path.resolve(result.filePath)));
|
|
1468
1676
|
// #1993 review: retirement must be observable - if a future regression
|
|
1469
1677
|
// makes the set falsely authoritative, findings would vanish silently.
|
|
1470
1678
|
const authoritativeRetiredCount = getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath) &&
|
|
@@ -1479,7 +1687,32 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1479
1687
|
metadata: { files: authoritativeRetiredCount },
|
|
1480
1688
|
});
|
|
1481
1689
|
}
|
|
1482
|
-
|
|
1690
|
+
// #2154 v4 F2: the same rule for the runner arm, in the same shape as its
|
|
1691
|
+
// LSP sibling above — a retirement nobody can see is how a regression
|
|
1692
|
+
// deletes findings silently. Bounded by construction: at most one row per
|
|
1693
|
+
// mode=full call, and only when rows were actually retired.
|
|
1694
|
+
const runnerRetiredRows = getFileDiagnosticSummaries()
|
|
1695
|
+
.filter((summary) => includeFile(summary.filePath))
|
|
1696
|
+
.reduce((total, summary) => total +
|
|
1697
|
+
(summary.diagnostics ?? []).filter((diagnostic) => authoritativeRunnerIds.has(runnerIdOf(diagnostic))).length, 0);
|
|
1698
|
+
if (runnerRetiredRows > 0) {
|
|
1699
|
+
logLatency({
|
|
1700
|
+
type: "phase",
|
|
1701
|
+
phase: "runner_authoritative_widget_retire",
|
|
1702
|
+
filePath: "",
|
|
1703
|
+
durationMs: 0,
|
|
1704
|
+
metadata: {
|
|
1705
|
+
rows: runnerRetiredRows,
|
|
1706
|
+
runners: [...authoritativeRunnerIds].join(","),
|
|
1707
|
+
},
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
let summaries = await applyInlineSuppressionsToSummaries(mergeDiagnosticsWithWidgetSummaries(getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath)), confirmedLspResults, projectSnapshot, projectDelta, authoritativeLspFiles, authoritativeRunnerIds), cwd, policyMap);
|
|
1711
|
+
if (unreconciledFiles.size > 0) {
|
|
1712
|
+
summaries = summaries.map((summary) => unreconciledFiles.has(path.resolve(summary.filePath))
|
|
1713
|
+
? summarizeDiagnostics(summary.filePath, markUnreconciledFindings(summary.diagnostics), summary.hasFinalSnapshot)
|
|
1714
|
+
: summary);
|
|
1715
|
+
}
|
|
1483
1716
|
// #1888: the full-mode summary above is the first seam where every
|
|
1484
1717
|
// producing lane is correlated. The earlier footer loop intentionally writes
|
|
1485
1718
|
// only CONFIRMED LSP results, so an ast-grep backpressure failure left
|
|
@@ -1496,7 +1729,7 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1496
1729
|
for (const summary of summaries) {
|
|
1497
1730
|
reconcileCorrelatedScanDiagnostics(summary.filePath, summary.diagnostics, nextWriteIndex?.(), projectScanObservedAt);
|
|
1498
1731
|
}
|
|
1499
|
-
const result = formatAllMode(cwd, severity, summaries, {
|
|
1732
|
+
const result = await formatAllMode(cwd, severity, summaries, {
|
|
1500
1733
|
mode: "full",
|
|
1501
1734
|
lspFilesChecked: rawLspResults.length,
|
|
1502
1735
|
partial: aborted,
|
|
@@ -1515,7 +1748,7 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1515
1748
|
sources: projectDelta.sources,
|
|
1516
1749
|
turnIndex: projectDelta.turnIndex,
|
|
1517
1750
|
},
|
|
1518
|
-
});
|
|
1751
|
+
}, 0, pathsScope);
|
|
1519
1752
|
const missingNote = pathsScopeMissingNote(pathsScope);
|
|
1520
1753
|
// #630: mirrors `tools/lsp-diagnostics.ts`'s `unconfirmedReasonClause`/
|
|
1521
1754
|
// `tallyConfirmation` semantic guarantee (never silently render
|
|
@@ -1562,6 +1795,21 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1562
1795
|
// so a page of ast-grep/opengrep/marksman noise never buries whether the
|
|
1563
1796
|
// real language server itself found anything in this sweep.
|
|
1564
1797
|
const lspPrimaryVsAuxiliary = tallyLspPrimaryVsAuxiliary(confirmedLspResults);
|
|
1798
|
+
logExtension({
|
|
1799
|
+
subsystem: "lsp-diagnostics",
|
|
1800
|
+
message: "lens_diagnostics verdict",
|
|
1801
|
+
metadata: {
|
|
1802
|
+
server: [
|
|
1803
|
+
...new Set(confirmedLspResults.map((result) => primaryServerId(result.filePath) ?? "unknown")),
|
|
1804
|
+
].join(","),
|
|
1805
|
+
primary: lspPrimaryVsAuxiliary.primary,
|
|
1806
|
+
auxiliary: lspPrimaryVsAuxiliary.auxiliary,
|
|
1807
|
+
total: lspPrimaryVsAuxiliary.primary + lspPrimaryVsAuxiliary.auxiliary,
|
|
1808
|
+
sources: [
|
|
1809
|
+
...new Set(confirmedLspResults.flatMap((result) => (result.diagnostics ?? []).map((diagnostic) => diagnostic.source ?? "unknown"))),
|
|
1810
|
+
].slice(0, 5),
|
|
1811
|
+
},
|
|
1812
|
+
});
|
|
1565
1813
|
const lspPrimaryVsAuxiliaryNote = lspPrimaryVsAuxiliary.primary + lspPrimaryVsAuxiliary.auxiliary > 0
|
|
1566
1814
|
? `\n\nLSP sweep findings: ${lspPrimaryVsAuxiliary.primary} primary (language server), ` +
|
|
1567
1815
|
`${lspPrimaryVsAuxiliary.auxiliary} auxiliary (ast-grep/opengrep/zizmor/typos/marksman/...).`
|
|
@@ -1878,8 +2126,20 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
|
|
|
1878
2126
|
}
|
|
1879
2127
|
return resultWithCold;
|
|
1880
2128
|
}
|
|
2129
|
+
async function projectResolvedCwd(summary, cwd) {
|
|
2130
|
+
// #2777 O1: the whole three-way lookup lives on the LSP seam now. A file
|
|
2131
|
+
// with no primary LSP server is `undefined` here by construction, and
|
|
2132
|
+
// formatAllMode renders that row without a cwd term (N1) — the literal
|
|
2133
|
+
// `cwd=undefined` cannot be produced.
|
|
2134
|
+
const resolvedCwd = await resolveLspCwdForFile(summary.filePath, cwd);
|
|
2135
|
+
return {
|
|
2136
|
+
...summary,
|
|
2137
|
+
...(resolvedCwd === undefined ? {} : { resolvedCwd }),
|
|
2138
|
+
diagnostics: summary.diagnostics ?? [],
|
|
2139
|
+
};
|
|
2140
|
+
}
|
|
1881
2141
|
// @delivery-surface: lens-diagnostics:mode-all
|
|
1882
|
-
function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(), detailOverrides = { mode: "all" }, staleDropped = 0, pathsScope, dependencyDemoted = 0) {
|
|
2142
|
+
async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(), detailOverrides = { mode: "all" }, staleDropped = 0, pathsScope, dependencyDemoted = 0) {
|
|
1883
2143
|
// #2275 review F2: the widget footer stops DRAWING a dependency-drift
|
|
1884
2144
|
// demotion once it hits `DEPENDENCY_DRIFT_MAX_DELIVERIES` unconfirmed
|
|
1885
2145
|
// deliveries, but the record stays here (dropping it would make an
|
|
@@ -1903,8 +2163,7 @@ function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(),
|
|
|
1903
2163
|
// result there IS a legitimate clean read — the cache-only note only applies
|
|
1904
2164
|
// to delta/all, which is why this is gated on detailOverrides.mode !== "full".
|
|
1905
2165
|
const isFullMode = detailOverrides.mode === "full";
|
|
1906
|
-
const
|
|
1907
|
-
const includeFile = (filePath) => ignoreFile(filePath) && (!pathsScope || pathsScope.includeFile(filePath));
|
|
2166
|
+
const includeFile = createScopedFileFilter(cwd, pathsScope);
|
|
1908
2167
|
// Cached summaries predate marks. Every cache-only surface re-applies
|
|
1909
2168
|
// dispositions through the shared applyCachedDispositions seam — strict
|
|
1910
2169
|
// false-positive anchors against current content when the file is
|
|
@@ -1955,7 +2214,7 @@ function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(),
|
|
|
1955
2214
|
// #2414: a "warning" filter must not admit hint/info — that is exactly
|
|
1956
2215
|
// the "present hints as warnings" defect this issue exists to close.
|
|
1957
2216
|
if (severity === "warning")
|
|
1958
|
-
return s.warnings > 0;
|
|
2217
|
+
return s.blocking > 0 || s.errors > 0 || s.warnings > 0;
|
|
1959
2218
|
// severity: "all" — a hint/info-only file (`advisories > 0`,
|
|
1960
2219
|
// warnings === 0) must still surface here, or the #2414 fix that stops
|
|
1961
2220
|
// hints inflating `warnings` would silently drop that file from the
|
|
@@ -1984,12 +2243,17 @@ function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(),
|
|
|
1984
2243
|
}
|
|
1985
2244
|
// Sort: blocking first, then errors, then warnings
|
|
1986
2245
|
const sorted = withIssues.sort((a, b) => b.blocking - a.blocking || b.errors - a.errors || b.warnings - a.warnings);
|
|
2246
|
+
// #2777 O2: only rendered rows resolve their root. The projection sits
|
|
2247
|
+
// below the withIssues filter, so a 500-file session pays the cwd
|
|
2248
|
+
// resolution for the handful of rows listed here, not for every summary
|
|
2249
|
+
// the cache holds.
|
|
2250
|
+
const rendered = await Promise.all(sorted.map((summary) => projectResolvedCwd(summary, cwd)));
|
|
1987
2251
|
const lines = [];
|
|
1988
2252
|
let totalBlocking = 0;
|
|
1989
2253
|
let totalErrors = 0;
|
|
1990
2254
|
let totalWarnings = 0;
|
|
1991
2255
|
let totalAdvisories = 0;
|
|
1992
|
-
for (const s of
|
|
2256
|
+
for (const s of rendered) {
|
|
1993
2257
|
const rel = path.relative(cwd, s.filePath);
|
|
1994
2258
|
const parts = [];
|
|
1995
2259
|
if (s.blocking > 0)
|
|
@@ -2009,6 +2273,10 @@ function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(),
|
|
|
2009
2273
|
if (staleCount > 0) {
|
|
2010
2274
|
parts.push(`${staleCount} stale — re-run to confirm`);
|
|
2011
2275
|
}
|
|
2276
|
+
// #2777 N1: files with no primary LSP server have no resolvedCwd —
|
|
2277
|
+
// omit the term rather than rendering the literal `cwd=undefined`.
|
|
2278
|
+
if (s.resolvedCwd !== undefined)
|
|
2279
|
+
parts.push(`cwd=${s.resolvedCwd}`);
|
|
2012
2280
|
lines.push(`${rel} ${parts.join(" ")}`);
|
|
2013
2281
|
// List the actual diagnostics (not just counts) so the agent can act on
|
|
2014
2282
|
// them without re-running anything — same "L<line>: <message>" shape as the
|