@herbertgao/pi-extensions 2026.8.4 → 2026.8.6
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 +5 -5
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +53 -14
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +18 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
- package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +20 -4
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
- package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
- package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
- package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
- package/node_modules/@narumitw/pi-btw/README.md +32 -24
- package/node_modules/@narumitw/pi-btw/package.json +6 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
- package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/pi-lens/CHANGELOG.md +2535 -954
- package/node_modules/pi-lens/README.md +13 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
- package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
- package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
- package/node_modules/pi-lens/dist/clients/biome-client.js +136 -24
- package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
- package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
- package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
- package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
- package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
- package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
- package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
- package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
- package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +26 -0
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
- package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
- package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +361 -77
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
- package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
- package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1172 -683
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
- package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
- package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
- 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 +154 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +47 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.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 +163 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +473 -42
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +18 -4
- package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
- package/node_modules/pi-lens/dist/clients/extension-log.js +432 -0
- package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
- package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
- package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
- package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
- package/node_modules/pi-lens/dist/clients/git-guard.js +678 -11
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
- package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
- package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
- package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +395 -100
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
- package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
- package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
- package/node_modules/pi-lens/dist/clients/latency-logger.js +72 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
- package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
- package/node_modules/pi-lens/dist/clients/lens-events.js +63 -9
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
- package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
- package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
- package/node_modules/pi-lens/dist/clients/lsp/server.js +437 -86
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +127 -32
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
- package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
- package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
- package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
- package/node_modules/pi-lens/dist/clients/pipeline.js +76 -15
- package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
- package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
- package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
- package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
- package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
- package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
- package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
- package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +257 -77
- package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
- package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -117
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +342 -33
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
- package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
- package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
- package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
- package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
- package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
- package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
- package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
- package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
- package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
- package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
- package/node_modules/pi-lens/dist/clients/warm-attach.js +50 -13
- package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
- package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
- package/node_modules/pi-lens/dist/index.js +51785 -36118
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
- package/node_modules/pi-lens/dist/mcp/server.js +219 -46
- package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
- package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +40 -15
- package/node_modules/pi-lens/docs/agent-tools.md +21 -10
- package/node_modules/pi-lens/docs/analysisall.md +192 -0
- package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +14 -3
- package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
- package/node_modules/pi-lens/docs/environment-variables.md +44 -0
- package/node_modules/pi-lens/docs/fable.md +27 -0
- package/node_modules/pi-lens/docs/features.md +45 -3
- package/node_modules/pi-lens/docs/globalconfig.md +56 -1
- package/node_modules/pi-lens/docs/language-coverage.md +4 -3
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
- package/node_modules/pi-lens/docs/mcp.md +37 -4
- package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
- package/node_modules/pi-lens/docs/settings.md +18 -1
- package/node_modules/pi-lens/docs/tools.md +4 -1
- package/node_modules/pi-lens/docs/usage.md +41 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -0
- package/node_modules/pi-lens/package.json +10 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
- package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +134 -2
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
- package/node_modules/pi-mcp-adapter/README.md +38 -4
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
- package/node_modules/pi-mcp-adapter/cli.js +25 -4
- package/node_modules/pi-mcp-adapter/commands.ts +15 -7
- package/node_modules/pi-mcp-adapter/config.ts +4 -4
- package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
- package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
- package/node_modules/pi-mcp-adapter/index.ts +8 -3
- package/node_modules/pi-mcp-adapter/init.ts +29 -5
- package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
- package/node_modules/pi-mcp-adapter/package.json +4 -3
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
- package/node_modules/pi-mcp-adapter/server-manager.ts +41 -7
- package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
- package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +62 -4
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
- package/node_modules/pi-mcp-adapter/types.ts +176 -27
- package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
- package/node_modules/pi-web-access/CHANGELOG.md +14 -0
- package/node_modules/pi-web-access/README.md +18 -12
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +5 -3
- package/node_modules/pi-web-access/curator-server.ts +2 -1
- package/node_modules/pi-web-access/extract.ts +106 -34
- package/node_modules/pi-web-access/fetch-params.ts +17 -3
- package/node_modules/pi-web-access/firecrawl.ts +172 -12
- package/node_modules/pi-web-access/gemini-search.ts +18 -4
- package/node_modules/pi-web-access/index.ts +120 -48
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/summary-review.ts +11 -5
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +12 -13
- package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
- package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
- package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
- package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
- package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
- package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
- package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
- package/node_modules/pi-lens/dist/clients/types.js +0 -11
|
@@ -2,7 +2,7 @@ import { stat } from "node:fs/promises";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
import { visibleWidth } from "./deps/pi-tui.js";
|
|
5
|
-
import { normalizeEphemeralMapKey } from "./path-utils.js";
|
|
5
|
+
import { normalizeEphemeralMapKey, normalizeMapKey } from "./path-utils.js";
|
|
6
6
|
import { fitLine } from "./tui-fit.js";
|
|
7
7
|
import { WriteOrderingGuard } from "./write-ordering-guard.js";
|
|
8
8
|
/**
|
|
@@ -57,7 +57,42 @@ let requestRenderFn = null;
|
|
|
57
57
|
* `clients/runtime-tool-result.ts:nextWriteIndex`).
|
|
58
58
|
*/
|
|
59
59
|
const diagnosticsWriteGuard = new WriteOrderingGuard();
|
|
60
|
+
/**
|
|
61
|
+
* Runner completions also mutate the shared file record: they mark the
|
|
62
|
+
* diagnostics snapshot pending. Keep that mutation in the same per-file order
|
|
63
|
+
* as the final diagnostic replacement, or an older pipeline can set a newer
|
|
64
|
+
* confirmed-clean record back to `(pending)` (#1198).
|
|
65
|
+
*/
|
|
66
|
+
const runnerWriteGuard = new WriteOrderingGuard();
|
|
60
67
|
const MAX_STORED_DIAGNOSTICS_PER_FILE = 12;
|
|
68
|
+
const MAX_INACTIVE_FILE_RECORDS = 1024;
|
|
69
|
+
const ACTIVE_FILE_IDLE_MS = 30 * 60_000;
|
|
70
|
+
const MAX_LSP_SERVER_RECORDS = 128;
|
|
71
|
+
// Pruning is a cold-size-boundary operation. Do not walk the whole file map
|
|
72
|
+
// for every record in a large diagnostics reconciliation; the full-scan path
|
|
73
|
+
// can legitimately create thousands of records in one synchronous batch.
|
|
74
|
+
let nextInactivePruneSize = MAX_INACTIVE_FILE_RECORDS + 1;
|
|
75
|
+
function pruneInactiveFileRecords(now = Date.now()) {
|
|
76
|
+
if (files.size <= MAX_INACTIVE_FILE_RECORDS)
|
|
77
|
+
return;
|
|
78
|
+
const victims = [...files.entries()]
|
|
79
|
+
.filter(([, rec]) => now - rec.touchedAt > ACTIVE_FILE_IDLE_MS &&
|
|
80
|
+
!hasLiveDiagnostic(rec))
|
|
81
|
+
.sort(([, a], [, b]) => a.touchedAt - b.touchedAt);
|
|
82
|
+
for (const [key] of victims) {
|
|
83
|
+
if (files.size <= MAX_INACTIVE_FILE_RECORDS)
|
|
84
|
+
break;
|
|
85
|
+
files.delete(key);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function maybePruneInactiveFileRecords() {
|
|
89
|
+
if (files.size < nextInactivePruneSize)
|
|
90
|
+
return;
|
|
91
|
+
pruneInactiveFileRecords();
|
|
92
|
+
// A live-heavy map may remain above the soft bound. Do not rescan it for
|
|
93
|
+
// every subsequent file; the next lifecycle starts with a fresh state map.
|
|
94
|
+
nextInactivePruneSize = Number.POSITIVE_INFINITY;
|
|
95
|
+
}
|
|
61
96
|
// ── Public API ────────────────────────────────────────────────────────────────
|
|
62
97
|
export function setRenderCallback(fn) {
|
|
63
98
|
requestRenderFn = fn;
|
|
@@ -68,8 +103,15 @@ export function clearWidgetState() {
|
|
|
68
103
|
sessionLanguages = [];
|
|
69
104
|
requestRenderFn = null;
|
|
70
105
|
diagnosticsWriteGuard.clear();
|
|
71
|
-
|
|
72
|
-
|
|
106
|
+
runnerWriteGuard.clear();
|
|
107
|
+
nextInactivePruneSize = MAX_INACTIVE_FILE_RECORDS + 1;
|
|
108
|
+
}
|
|
109
|
+
// v1 → v2 (#1186): per-entry `WidgetDiagnostic.observedAt`. v2 is a SUPERSET of
|
|
110
|
+
// v1 (the field is additive/optional), so `importWidgetState` accepts either and
|
|
111
|
+
// migrates a v1 record by inheriting each entry's `observedAt` from the record's
|
|
112
|
+
// `touchedAt`. A v1 file must never be rejected (that would silently drop resume
|
|
113
|
+
// diagnostics) nor crash.
|
|
114
|
+
export const WIDGET_STATE_VERSION = 2;
|
|
73
115
|
/**
|
|
74
116
|
* Snapshot the per-file widget diagnostics for persistence (#190). Excludes
|
|
75
117
|
* `lspServers` — those are process-bound (servers re-spawn fresh on the next
|
|
@@ -96,9 +138,29 @@ export function exportWidgetState() {
|
|
|
96
138
|
* Replaces the in-memory `files` map; ignores snapshots from a different
|
|
97
139
|
* version. Triggers a re-render if a callback is registered.
|
|
98
140
|
*/
|
|
141
|
+
/**
|
|
142
|
+
* #1186 v1→v2 migration: stamp each entry that lacks a per-entry `observedAt`
|
|
143
|
+
* with the record's `touchedAt` (the single stamp the whole record shared under
|
|
144
|
+
* v1). Non-mutating; entries that already carry a stamp (a v2 record) pass
|
|
145
|
+
* through untouched.
|
|
146
|
+
*/
|
|
147
|
+
function migrateEntryStamps(entries, recordTouchedAt) {
|
|
148
|
+
return (entries ?? []).map((d) => d.observedAt == null ? { ...d, observedAt: recordTouchedAt } : d);
|
|
149
|
+
}
|
|
99
150
|
export function importWidgetState(state) {
|
|
100
|
-
|
|
151
|
+
// Accept any known-or-older version and migrate (#1186): reject a missing
|
|
152
|
+
// snapshot, a missing/non-numeric `version` (NaN/undefined/null — the
|
|
153
|
+
// pre-#1186 guard `version !== WIDGET_STATE_VERSION` rejected these, and
|
|
154
|
+
// loosening that would silently admit a malformed/foreign snapshot), or a
|
|
155
|
+
// FUTURE version this build can't understand. Rejecting a v1
|
|
156
|
+
// (pre-per-entry-stamp) file, by contrast, would silently drop all resume
|
|
157
|
+
// diagnostics — so v1..current are accepted and migrated.
|
|
158
|
+
if (!state ||
|
|
159
|
+
typeof state.version !== "number" ||
|
|
160
|
+
state.version < 1 ||
|
|
161
|
+
state.version > WIDGET_STATE_VERSION) {
|
|
101
162
|
return false;
|
|
163
|
+
}
|
|
102
164
|
files.clear();
|
|
103
165
|
// A resumed session's writeIndex counter starts fresh (#190 rehydration is
|
|
104
166
|
// process-bound like lspServers, see the export above) — any ordering
|
|
@@ -106,26 +168,39 @@ export function importWidgetState(state) {
|
|
|
106
168
|
// drop them rather than risk a legitimate post-resume write being read as
|
|
107
169
|
// "superseded" against a stale token.
|
|
108
170
|
diagnosticsWriteGuard.clear();
|
|
171
|
+
runnerWriteGuard.clear();
|
|
109
172
|
for (const f of state.files ?? []) {
|
|
110
173
|
// Fold persisted keys through the same normalizer as live writes (#1020),
|
|
111
174
|
// or a persisted forward-slash key stays split from a fresh backslash key
|
|
112
175
|
// across a resumed session — a primary repro condition. Keep a readable
|
|
113
176
|
// display path on the record.
|
|
177
|
+
// #1186 migration: a v1 record's entries have no per-entry `observedAt`.
|
|
178
|
+
// Inherit the record's `touchedAt` (a safe, over-conservative default —
|
|
179
|
+
// the whole record shared that one stamp before), so the per-entry stale
|
|
180
|
+
// gate has a concrete observation time and never treats `undefined` as
|
|
181
|
+
// epoch-0 (which would drop every migrated entry on the first sweep).
|
|
182
|
+
const recordTouchedAt = f.touchedAt ?? Date.now();
|
|
114
183
|
files.set(fileMapKey(f.filePath), {
|
|
115
184
|
filePath: f.filePath,
|
|
116
185
|
runners: new Map(f.runners ?? []),
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
186
|
+
// Failure entries do NOT survive a session restore (#1348 review):
|
|
187
|
+
// a fmt-failed marker is live advice about THIS session's last
|
|
188
|
+
// attempt; rehydrating one from a snapshot shows a stale failure the
|
|
189
|
+
// current session never observed (and same-mtime fixes would never
|
|
190
|
+
// clear it). Successes rehydrate as before.
|
|
191
|
+
formatters: new Map((f.formatters ?? []).filter(([, outcome]) => outcome?.success !== false)),
|
|
192
|
+
diagnostics: migrateEntryStamps(f.diagnostics, recordTouchedAt),
|
|
193
|
+
allDiagnostics: migrateEntryStamps(f.allDiagnostics, recordTouchedAt),
|
|
120
194
|
diagnosticCounts: f.diagnosticCounts ?? {
|
|
121
195
|
blocking: 0,
|
|
122
196
|
errors: 0,
|
|
123
197
|
warnings: 0,
|
|
124
198
|
},
|
|
125
199
|
hasFinalDiagnosticsSnapshot: f.hasFinalDiagnosticsSnapshot ?? false,
|
|
126
|
-
touchedAt:
|
|
200
|
+
touchedAt: recordTouchedAt,
|
|
127
201
|
});
|
|
128
202
|
}
|
|
203
|
+
pruneInactiveFileRecords();
|
|
129
204
|
sessionLanguages = state.sessionLanguages ?? [];
|
|
130
205
|
requestRenderFn?.();
|
|
131
206
|
return true;
|
|
@@ -161,7 +236,15 @@ export function recordFormatter(filePath, formatter, changed, success) {
|
|
|
161
236
|
files.set(fileMapKey(filePath), rec);
|
|
162
237
|
requestRender();
|
|
163
238
|
}
|
|
164
|
-
export function recordRunner(filePath, runnerId, status, diagnosticCount, durationMs) {
|
|
239
|
+
export function recordRunner(filePath, runnerId, status, diagnosticCount, durationMs, writeIndex) {
|
|
240
|
+
const key = fileMapKey(filePath);
|
|
241
|
+
if (!runnerWriteGuard.shouldWrite(key, writeIndex))
|
|
242
|
+
return;
|
|
243
|
+
// Advance the final-diagnostics guard too. A runner completion is part of
|
|
244
|
+
// this pipeline's ordered write, even though its immediate effect is only to
|
|
245
|
+
// mark the record pending. This prevents an older final replacement from
|
|
246
|
+
// landing after a newer runner state (and vice versa).
|
|
247
|
+
diagnosticsWriteGuard.shouldWrite(key, writeIndex);
|
|
165
248
|
const rec = getOrCreate(filePath);
|
|
166
249
|
rec.runners.set(runnerId, { status, count: diagnosticCount, durationMs });
|
|
167
250
|
rec.hasFinalDiagnosticsSnapshot = false;
|
|
@@ -179,7 +262,16 @@ export function recordRunner(filePath, runnerId, status, diagnosticCount, durati
|
|
|
179
262
|
function toSingleLineMessage(message) {
|
|
180
263
|
return (message ?? "").replace(/\s+/g, " ").trim();
|
|
181
264
|
}
|
|
182
|
-
export function recordDiagnostics(filePath, diagnostics, writeIndex
|
|
265
|
+
export function recordDiagnostics(filePath, diagnostics, writeIndex,
|
|
266
|
+
// #1093: when the truth was OBSERVED, not when it's being written. Defaults
|
|
267
|
+
// to `Date.now()` for the per-edit/live path (observed now). A reconcile
|
|
268
|
+
// replaying a CACHED view (e.g. the workspace-diagnostics cache-hit branch in
|
|
269
|
+
// `tools/lsp-diagnostics.ts`) must pass the cache entry's own scan timestamp
|
|
270
|
+
// here — otherwise a repeat "fresh check" that merely re-serves a stale
|
|
271
|
+
// cached view keeps bumping `touchedAt` to now(), permanently disarming
|
|
272
|
+
// `reconcileStaleWidgetFiles`'s `mtimeMs > touchedAt` gate so a resolved
|
|
273
|
+
// finding renders forever (the #1092 touchedAt-re-arming defect).
|
|
274
|
+
observedAt) {
|
|
183
275
|
// Drop a write that's superseded by a later same-turn edit to this file
|
|
184
276
|
// whose pipeline finished first (same race class as #555). No cache write,
|
|
185
277
|
// no count/timestamp update, no render trigger — the recorded state must
|
|
@@ -187,11 +279,27 @@ export function recordDiagnostics(filePath, diagnostics, writeIndex) {
|
|
|
187
279
|
// the `clients/mcp/analyze.ts` on-demand call site, which has no per-edit
|
|
188
280
|
// ordering token) always proceeds, same as version-less LSP servers in the
|
|
189
281
|
// #555 guard.
|
|
190
|
-
|
|
282
|
+
const key = fileMapKey(filePath);
|
|
283
|
+
if (!diagnosticsWriteGuard.shouldWrite(key, writeIndex))
|
|
191
284
|
return;
|
|
192
|
-
|
|
285
|
+
// Keep runner state ordered with the final diagnostic replacement. The
|
|
286
|
+
// guards are deliberately advanced in both directions because either verb
|
|
287
|
+
// may be the first completion from a pipeline.
|
|
288
|
+
runnerWriteGuard.shouldWrite(key, writeIndex);
|
|
289
|
+
// Resolve the observation time ONCE (#1186): every incoming entry is stamped
|
|
290
|
+
// with it, and it also seeds the record's `touchedAt`. A fresh write (no
|
|
291
|
+
// `observedAt`) is observed now.
|
|
292
|
+
const observedTs = observedAt ?? Date.now();
|
|
293
|
+
const rec = getOrCreate(filePath, key);
|
|
294
|
+
commitDiagnostics(rec, filePath, normalizeDiagnostics(filePath, diagnostics, observedTs), observedTs, key);
|
|
295
|
+
}
|
|
296
|
+
/** Map the raw diagnostic shape callers pass into stored {@link WidgetDiagnostic}s.
|
|
297
|
+
* Every produced entry is stamped with `observedTs` (#1186) — the time THIS batch
|
|
298
|
+
* of diagnostics was observed — so the per-entry stale gate can later drop just
|
|
299
|
+
* the entries older than the file's mtime rather than the whole record. */
|
|
300
|
+
function normalizeDiagnostics(filePath, diagnostics, observedTs) {
|
|
193
301
|
const base = pathToFileURL(filePath).href;
|
|
194
|
-
|
|
302
|
+
return diagnostics.map((d) => {
|
|
195
303
|
const rule = d.rule ?? d.id;
|
|
196
304
|
const uri = d.line != null
|
|
197
305
|
? `${base}#L${d.line}${d.column != null ? `:${d.column}` : ""}`
|
|
@@ -205,12 +313,35 @@ export function recordDiagnostics(filePath, diagnostics, writeIndex) {
|
|
|
205
313
|
rule,
|
|
206
314
|
tool: d.tool,
|
|
207
315
|
uri,
|
|
316
|
+
observedAt: observedTs,
|
|
208
317
|
};
|
|
209
318
|
});
|
|
319
|
+
}
|
|
320
|
+
/** Store `normalized` as the record's complete diagnostic set: recompute counts,
|
|
321
|
+
* cap the display list, stamp `touchedAt` (at `observedAt` when given, else now
|
|
322
|
+
* — #1093), persist, and re-render. The caller decides what `normalized`
|
|
323
|
+
* contains (a full replace, or a merge — see `reconcileCascadeNeighborLspErrors`). */
|
|
324
|
+
function commitDiagnostics(rec, filePath, normalized, observedAt, key = fileMapKey(filePath)) {
|
|
325
|
+
rec.diagnosticCounts = countDiagnostics(normalized);
|
|
326
|
+
rec.diagnostics = capStoredDiagnostics(normalized);
|
|
327
|
+
rec.allDiagnostics = normalized;
|
|
328
|
+
rec.hasFinalDiagnosticsSnapshot = true;
|
|
329
|
+
// Record-level `touchedAt` is the FRESHEST per-entry observation in the merged
|
|
330
|
+
// set (#1186) — drives render recency and the empty-record stale gate. On a
|
|
331
|
+
// merge (`reconcileCascadeNeighborLspErrors`) this is the newest of the
|
|
332
|
+
// preserved + incoming entries, not the (possibly aging) incoming stamp, so a
|
|
333
|
+
// record holding a fresh preserved entry doesn't sort/gate as stale. Empty set
|
|
334
|
+
// falls back to the passed `observedAt` (or now).
|
|
335
|
+
rec.touchedAt = freshestObservation(normalized, observedAt ?? Date.now());
|
|
336
|
+
files.set(key, rec);
|
|
337
|
+
requestRender();
|
|
338
|
+
}
|
|
339
|
+
/** Recompute the {blocking, errors, warnings} tally for a diagnostic set. */
|
|
340
|
+
function countDiagnostics(diags) {
|
|
210
341
|
let blocking = 0;
|
|
211
342
|
let errors = 0;
|
|
212
343
|
let warnings = 0;
|
|
213
|
-
for (const diagnostic of
|
|
344
|
+
for (const diagnostic of diags) {
|
|
214
345
|
if (isBlocking(diagnostic))
|
|
215
346
|
blocking++;
|
|
216
347
|
if (diagnostic.severity === "error")
|
|
@@ -218,13 +349,69 @@ export function recordDiagnostics(filePath, diagnostics, writeIndex) {
|
|
|
218
349
|
else if (diagnostic.severity === "warning")
|
|
219
350
|
warnings++;
|
|
220
351
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
352
|
+
return { blocking, errors, warnings };
|
|
353
|
+
}
|
|
354
|
+
/** The newest per-entry `observedAt` in `diags`, or `fallback` when empty (or no
|
|
355
|
+
* entry carries a stamp). Used as the record-level `touchedAt` (#1186). */
|
|
356
|
+
function freshestObservation(diags, fallback) {
|
|
357
|
+
let newest;
|
|
358
|
+
for (const d of diags) {
|
|
359
|
+
if (d.observedAt != null && (newest === undefined || d.observedAt > newest)) {
|
|
360
|
+
newest = d.observedAt;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return newest ?? fallback;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* A stored diagnostic that came from a language SERVER (tsserver, pyright, …)
|
|
367
|
+
* AND is an error/blocking finding. `convertLspDiagnostics` tags every
|
|
368
|
+
* language-server diagnostic with `tool: "lsp"` and `retagAuxiliaryDiagnostics`
|
|
369
|
+
* re-tags auxiliary-LSP findings (opengrep/ast-grep/zizmor/typos) to their real
|
|
370
|
+
* tool id, so `tool === "lsp"` uniquely identifies a genuine language-server
|
|
371
|
+
* entry. Used by the cascade merge below to decide which existing entries an
|
|
372
|
+
* errors-only LSP re-check is entitled to replace.
|
|
373
|
+
*/
|
|
374
|
+
function isLspErrorEntry(d) {
|
|
375
|
+
return d.tool === "lsp" && (d.semantic === "blocking" || d.severity === "error");
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Reconcile a CONFIRMED cascade neighbor re-check (#1093) into the footer,
|
|
379
|
+
* MERGING rather than whole-replacing (unlike `recordDiagnostics`).
|
|
380
|
+
*
|
|
381
|
+
* The cascade only re-checks a neighbor through its LANGUAGE SERVER, and only
|
|
382
|
+
* for ERRORS (`severity === 1`; see `clients/dispatch/integration.ts`). A plain
|
|
383
|
+
* full-replace would therefore erase the neighbor's live findings from OTHER
|
|
384
|
+
* sources that the cascade never re-examined — biome/ruff/ast-grep runner
|
|
385
|
+
* findings, and even the language server's own WARNINGS — turning a
|
|
386
|
+
* cross-file-error re-check into a silent false-clean for everything else
|
|
387
|
+
* (#533). So we replace ONLY the existing LSP-error entries
|
|
388
|
+
* (`isLspErrorEntry`) with the cascade's fresh LSP errors and preserve
|
|
389
|
+
* everything else verbatim. A stale LSP warning or biome finding therefore
|
|
390
|
+
* survives an errors-only cascade — correct, because this check never looked at
|
|
391
|
+
* it; it self-corrects on the next per-edit dispatch or a `lens_diagnostics`
|
|
392
|
+
* scan (which DO re-examine every source).
|
|
393
|
+
*
|
|
394
|
+
* Only ever call this for a CONFIRMED result (a valid passive snapshot or a
|
|
395
|
+
* completed, NON-inconclusive active touch — #571). `writeIndex` and
|
|
396
|
+
* `observedAt` behave exactly as in `recordDiagnostics`.
|
|
397
|
+
*/
|
|
398
|
+
export function reconcileCascadeNeighborLspErrors(filePath, lspErrorDiagnostics, writeIndex, observedAt) {
|
|
399
|
+
const key = fileMapKey(filePath);
|
|
400
|
+
if (!diagnosticsWriteGuard.shouldWrite(key, writeIndex))
|
|
401
|
+
return;
|
|
402
|
+
runnerWriteGuard.shouldWrite(key, writeIndex);
|
|
403
|
+
// #1186: the INCOMING LSP errors are stamped at THIS observation time
|
|
404
|
+
// (`observedAt`, e.g. an aging passive snapshot's `entry.ts`, or now for a
|
|
405
|
+
// fresh active touch). The PRESERVED entries keep their OWN prior per-entry
|
|
406
|
+
// `observedAt` — a fresh per-edit finding preserved through this errors-only
|
|
407
|
+
// merge is NOT re-aged to the incoming stamp. That per-entry split is exactly
|
|
408
|
+
// what lets `reconcileStaleWidgetFiles` drop the stale incoming entry while
|
|
409
|
+
// keeping the newer preserved one, instead of dropping the whole record.
|
|
410
|
+
const observedTs = observedAt ?? Date.now();
|
|
411
|
+
const rec = getOrCreate(filePath);
|
|
412
|
+
const incoming = normalizeDiagnostics(filePath, lspErrorDiagnostics, observedTs);
|
|
413
|
+
const preserved = rec.allDiagnostics.filter((d) => !isLspErrorEntry(d));
|
|
414
|
+
commitDiagnostics(rec, filePath, [...incoming, ...preserved], observedTs);
|
|
228
415
|
}
|
|
229
416
|
/**
|
|
230
417
|
* Reconcile a diagnostics result obtained OUTSIDE the per-edit dispatch
|
|
@@ -253,11 +440,18 @@ export function recordDiagnostics(filePath, diagnostics, writeIndex) {
|
|
|
253
440
|
* write for the same file — an omitted `writeIndex` always proceeds (same
|
|
254
441
|
* version-less fallback `recordDiagnostics` already documents), which is
|
|
255
442
|
* only safe for callers with no ordering token to give (e.g. tests).
|
|
443
|
+
*
|
|
444
|
+
* `observedAt` (#1093) is the wall-clock time the diagnostics were actually
|
|
445
|
+
* OBSERVED — pass it whenever the reconciled result is a replay of an older
|
|
446
|
+
* CACHED observation (the workspace-diagnostics cache-hit branch), so
|
|
447
|
+
* `touchedAt` records when the truth was seen, not when it was written. Omit
|
|
448
|
+
* it for genuinely fresh observations (a just-completed touch/scan), which are
|
|
449
|
+
* observed now.
|
|
256
450
|
*/
|
|
257
|
-
export function reconcileScanDiagnostics(filePath, diagnostics, confirmed, writeIndex) {
|
|
451
|
+
export function reconcileScanDiagnostics(filePath, diagnostics, confirmed, writeIndex, observedAt) {
|
|
258
452
|
if (!confirmed)
|
|
259
453
|
return;
|
|
260
|
-
recordDiagnostics(filePath, diagnostics, writeIndex);
|
|
454
|
+
recordDiagnostics(filePath, diagnostics, writeIndex, observedAt);
|
|
261
455
|
}
|
|
262
456
|
/**
|
|
263
457
|
* Drop widget entries whose file changed on disk after pi-lens last recorded
|
|
@@ -271,25 +465,60 @@ export function reconcileScanDiagnostics(filePath, diagnostics, confirmed, write
|
|
|
271
465
|
*/
|
|
272
466
|
export async function reconcileStaleWidgetFiles() {
|
|
273
467
|
const entries = [...files.entries()];
|
|
274
|
-
const
|
|
468
|
+
const verdicts = await Promise.all(
|
|
275
469
|
// `mapKey` is the normalized `files` key (used for deletion); stat the
|
|
276
470
|
// record's real display path, not the lowercased key (#1020).
|
|
277
471
|
entries.map(async ([mapKey, rec]) => {
|
|
472
|
+
let mtimeMs;
|
|
278
473
|
try {
|
|
279
|
-
|
|
280
|
-
// +1ms tolerance: a freshly-recorded file has touchedAt >= mtime.
|
|
281
|
-
return st.mtimeMs > rec.touchedAt + 1 ? mapKey : undefined;
|
|
474
|
+
mtimeMs = (await stat(rec.filePath)).mtimeMs;
|
|
282
475
|
}
|
|
283
476
|
catch {
|
|
284
|
-
return mapKey; // deleted / unreadable → drop
|
|
477
|
+
return { mapKey, action: "drop" }; // deleted / unreadable → drop
|
|
285
478
|
}
|
|
479
|
+
// A clean record (no findings) has no per-entry stamps to consult —
|
|
480
|
+
// gate it on the record's own `touchedAt` exactly as before, so a ✓
|
|
481
|
+
// entry for a file that changed on disk still drops.
|
|
482
|
+
if (rec.allDiagnostics.length === 0) {
|
|
483
|
+
return mtimeMs > rec.touchedAt + 1
|
|
484
|
+
? { mapKey, action: "drop" }
|
|
485
|
+
: { mapKey, action: "keep" };
|
|
486
|
+
}
|
|
487
|
+
// #1186 per-ENTRY gate: drop only the entries observed BEFORE the file's
|
|
488
|
+
// current mtime; keep the rest. A merged record can hold a fresh
|
|
489
|
+
// preserved entry beside an entry replayed from an aging snapshot, so a
|
|
490
|
+
// per-RECORD gate over-cleared the whole record (the residual documented
|
|
491
|
+
// at dispatch/integration.ts). A missing per-entry stamp (a migrated
|
|
492
|
+
// pre-#1186 record) inherits the record's `touchedAt`. +1ms tolerance:
|
|
493
|
+
// a freshly-recorded file has observedAt >= mtime.
|
|
494
|
+
const survivors = rec.allDiagnostics.filter((d) => !(mtimeMs > (d.observedAt ?? rec.touchedAt) + 1));
|
|
495
|
+
if (survivors.length === rec.allDiagnostics.length) {
|
|
496
|
+
return { mapKey, action: "keep" }; // nothing stale
|
|
497
|
+
}
|
|
498
|
+
if (survivors.length === 0) {
|
|
499
|
+
return { mapKey, action: "drop" }; // every entry stale → drop record
|
|
500
|
+
}
|
|
501
|
+
return { mapKey, action: "prune", survivors };
|
|
286
502
|
}));
|
|
287
503
|
let dropped = 0;
|
|
288
|
-
for (const
|
|
289
|
-
if (
|
|
290
|
-
|
|
504
|
+
for (const v of verdicts) {
|
|
505
|
+
if (v.action === "keep")
|
|
506
|
+
continue;
|
|
507
|
+
if (v.action === "drop") {
|
|
508
|
+
files.delete(v.mapKey);
|
|
291
509
|
dropped += 1;
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
// prune: the file changed and shed its stale entries but retains fresher
|
|
513
|
+
// ones — keep the record, recompute counts/cap from the survivors, and
|
|
514
|
+
// still count it as a changed file so the agent is told to rescan.
|
|
515
|
+
const rec = files.get(v.mapKey);
|
|
516
|
+
if (rec) {
|
|
517
|
+
rec.allDiagnostics = v.survivors;
|
|
518
|
+
rec.diagnostics = capStoredDiagnostics(v.survivors);
|
|
519
|
+
rec.diagnosticCounts = countDiagnostics(v.survivors);
|
|
292
520
|
}
|
|
521
|
+
dropped += 1;
|
|
293
522
|
}
|
|
294
523
|
if (dropped > 0)
|
|
295
524
|
requestRenderFn?.();
|
|
@@ -372,13 +601,20 @@ export const __testing = {
|
|
|
372
601
|
},
|
|
373
602
|
};
|
|
374
603
|
export function recordLsp(serverId, root, status, durationMs) {
|
|
375
|
-
const
|
|
604
|
+
const normalizedRoot = normalizeMapKey(root);
|
|
605
|
+
const key = `${serverId}@${normalizedRoot}`;
|
|
376
606
|
const mapped = status === "spawn_start"
|
|
377
607
|
? "spawning"
|
|
378
608
|
: status === "spawn_success"
|
|
379
609
|
? "ready"
|
|
380
610
|
: "failed";
|
|
381
611
|
lspServers.set(key, { serverId, root, status: mapped, durationMs });
|
|
612
|
+
while (lspServers.size > MAX_LSP_SERVER_RECORDS) {
|
|
613
|
+
const oldest = lspServers.keys().next().value;
|
|
614
|
+
if (oldest === undefined)
|
|
615
|
+
break;
|
|
616
|
+
lspServers.delete(oldest);
|
|
617
|
+
}
|
|
382
618
|
requestRender();
|
|
383
619
|
}
|
|
384
620
|
// ── Render ────────────────────────────────────────────────────────────────────
|
|
@@ -488,11 +724,17 @@ function formatFileRowVertical(rec, theme) {
|
|
|
488
724
|
const blocking = rec.diagnosticCounts.blocking;
|
|
489
725
|
const errors = rec.diagnosticCounts.errors;
|
|
490
726
|
const warnings = rec.diagnosticCounts.warnings;
|
|
727
|
+
const formatterFailed = hasFailedFormatter(rec);
|
|
728
|
+
// Diagnostic severity outranks formatter failure (#1348 review): a file
|
|
729
|
+
// with blocking diagnostics shows the blocking dot even if a format also
|
|
730
|
+
// failed -- same precedence as the horizontal renderer.
|
|
491
731
|
const dot = blocking > 0
|
|
492
732
|
? red("●")
|
|
493
|
-
:
|
|
494
|
-
?
|
|
495
|
-
:
|
|
733
|
+
: formatterFailed
|
|
734
|
+
? red("x")
|
|
735
|
+
: warnings > 0 || errors > 0
|
|
736
|
+
? yellow("!")
|
|
737
|
+
: green("✓");
|
|
496
738
|
const runnerNames = [...rec.runners.entries()]
|
|
497
739
|
.filter(([, r]) => r.status !== "skipped")
|
|
498
740
|
.map(([id]) => id)
|
|
@@ -505,11 +747,15 @@ function formatFileRowVertical(rec, theme) {
|
|
|
505
747
|
? " " + yellow(`${warnings}W`)
|
|
506
748
|
: " " + dim("clean");
|
|
507
749
|
const changedFormatters = [...rec.formatters.entries()]
|
|
508
|
-
.filter(([, f]) => f.changed)
|
|
750
|
+
.filter(([, f]) => f.changed && f.success)
|
|
509
751
|
.map(([name]) => name);
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
752
|
+
const failedFormatters = [...rec.formatters.entries()]
|
|
753
|
+
.filter(([, f]) => !f.success)
|
|
754
|
+
.map(([name]) => name);
|
|
755
|
+
const formatMark = (failedFormatters.length > 0
|
|
756
|
+
? red(` fmt-failed:${failedFormatters.join(",")}`)
|
|
757
|
+
: "") +
|
|
758
|
+
(changedFormatters.length > 0 ? dim(` fmt:${changedFormatters.join(",")}`) : "");
|
|
513
759
|
return ` ${dot} ${base} ${dim(runnerNames)}${formatMark}${counts}`;
|
|
514
760
|
}
|
|
515
761
|
function packHorizontalRow(recs, totalWidth, theme) {
|
|
@@ -577,6 +823,7 @@ function formatFileTokenHorizontal(rec, remainingWidth, theme) {
|
|
|
577
823
|
const errors = rec.diagnosticCounts.errors;
|
|
578
824
|
const warnings = rec.diagnosticCounts.warnings;
|
|
579
825
|
const formatterChanged = hasChangedFormatter(rec);
|
|
826
|
+
const formatterFailed = hasFailedFormatter(rec);
|
|
580
827
|
let dotChar;
|
|
581
828
|
if (blocking > 0)
|
|
582
829
|
dotChar = red("●");
|
|
@@ -586,6 +833,9 @@ function formatFileTokenHorizontal(rec, remainingWidth, theme) {
|
|
|
586
833
|
dotChar = dim("✎");
|
|
587
834
|
else
|
|
588
835
|
dotChar = dim("·");
|
|
836
|
+
if (formatterFailed && blocking === 0 && errors === 0 && warnings === 0) {
|
|
837
|
+
dotChar = red("x");
|
|
838
|
+
}
|
|
589
839
|
let countsStyled = "";
|
|
590
840
|
if (errors > 0 && warnings > 0) {
|
|
591
841
|
const eColor = blocking > 0 ? red : yellow;
|
|
@@ -622,10 +872,11 @@ function truncateBasename(name, maxWidth) {
|
|
|
622
872
|
return stem.slice(0, keep) + "…" + ext;
|
|
623
873
|
}
|
|
624
874
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
625
|
-
function getOrCreate(filePath) {
|
|
875
|
+
function getOrCreate(filePath, key = fileMapKey(filePath)) {
|
|
876
|
+
maybePruneInactiveFileRecords();
|
|
626
877
|
// Look up by the normalized key so mixed path forms of the same file share
|
|
627
878
|
// ONE record (#1020); keep the caller's verbatim path as the display path.
|
|
628
|
-
return (files.get(
|
|
879
|
+
return (files.get(key) ?? {
|
|
629
880
|
filePath,
|
|
630
881
|
runners: new Map(),
|
|
631
882
|
formatters: new Map(),
|
|
@@ -637,10 +888,16 @@ function getOrCreate(filePath) {
|
|
|
637
888
|
});
|
|
638
889
|
}
|
|
639
890
|
function hasChangedFormatter(rec) {
|
|
640
|
-
return [...rec.formatters.values()].some((f) => f.changed);
|
|
891
|
+
return [...rec.formatters.values()].some((f) => f.changed && f.success);
|
|
892
|
+
}
|
|
893
|
+
function hasFailedFormatter(rec) {
|
|
894
|
+
return [...rec.formatters.values()].some((f) => !f.success);
|
|
641
895
|
}
|
|
642
896
|
function shouldRenderFile(rec) {
|
|
643
|
-
return rec.hasFinalDiagnosticsSnapshot || hasChangedFormatter(rec);
|
|
897
|
+
return rec.hasFinalDiagnosticsSnapshot || hasChangedFormatter(rec) || hasFailedFormatter(rec);
|
|
898
|
+
}
|
|
899
|
+
function hasLiveDiagnostic(rec) {
|
|
900
|
+
return rec.hasFinalDiagnosticsSnapshot && rec.diagnostics.length > 0;
|
|
644
901
|
}
|
|
645
902
|
function isPendingAnalysis(rec) {
|
|
646
903
|
return rec.runners.size > 0 && !rec.hasFinalDiagnosticsSnapshot;
|