@herbertgao/pi-extensions 2026.8.5 → 2026.8.7
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 +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
- package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
- package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
- package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
- package/node_modules/@narumitw/pi-btw/README.md +119 -103
- package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
- package/node_modules/@narumitw/pi-btw/package.json +13 -9
- package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/pi-lens/CHANGELOG.md +1004 -0
- package/node_modules/pi-lens/README.md +11 -3
- package/node_modules/pi-lens/config/ruff/core.toml +42 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
- package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
- package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.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 +11 -3
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
- package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
- package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
- package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
- package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
- package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
- package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
- package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
- package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
- package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -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 +61 -3
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
- package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
- package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
- 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/runner-adapters/trivy.js +50 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
- package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
- package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
- package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
- package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
- package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
- package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
- package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
- package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
- package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +64677 -49484
- package/node_modules/pi-lens/dist/mcp/server.js +8 -2
- 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/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +44 -14
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +33 -6
- package/node_modules/pi-lens/docs/globalconfig.md +14 -0
- package/node_modules/pi-lens/docs/language-coverage.md +71 -5
- package/node_modules/pi-lens/docs/mcp.md +2 -2
- package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
- package/node_modules/pi-lens/docs/settings.md +25 -1
- package/node_modules/pi-lens/docs/usage.md +24 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -0
- package/node_modules/pi-lens/package.json +10 -6
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
- 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-dupe-class-members-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
- 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-unknown-parameters-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
- 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-dupe-class-members.yml +52 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
- package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
- package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -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 +87 -116
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
- package/node_modules/pi-mcp-adapter/README.md +51 -6
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/cli.js +188 -5
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +28 -10
- package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
- package/node_modules/pi-mcp-adapter/index.ts +219 -32
- package/node_modules/pi-mcp-adapter/init.ts +36 -4
- package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
- package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +4 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
- package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
- package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +95 -10
- package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
- package/node_modules/pi-mcp-adapter/utils.ts +70 -0
- package/node_modules/pi-web-access/CHANGELOG.md +37 -0
- package/node_modules/pi-web-access/README.md +38 -14
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +8 -3
- package/node_modules/pi-web-access/curator-server.ts +5 -1
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +134 -37
- 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 +33 -7
- package/node_modules/pi-web-access/index.ts +160 -104
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/page-query.ts +4 -2
- package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
- package/node_modules/pi-web-access/parallel.ts +2 -2
- package/node_modules/pi-web-access/query-rewrite.ts +51 -0
- package/node_modules/pi-web-access/serper.ts +211 -0
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +51 -23
- package/node_modules/pi-web-access/tavily.ts +16 -4
- package/node_modules/pi-web-access/utils.ts +87 -0
- package/node_modules/pi-web-access/valyu.ts +199 -0
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +13 -13
|
@@ -38,8 +38,15 @@ export function withDeadline(promise, options) {
|
|
|
38
38
|
const onReject = options.onReject ?? "propagate";
|
|
39
39
|
const ms = options.ms ??
|
|
40
40
|
(options.deadlineAt !== undefined ? options.deadlineAt - Date.now() : 0);
|
|
41
|
-
// Past deadline / non-positive budget: settle immediately, no timer.
|
|
41
|
+
// Past deadline / non-positive budget: settle immediately, no timer. This
|
|
42
|
+
// branch returns before the race below ever runs, so — same reason as the
|
|
43
|
+
// loser-leg catch a few lines down — `promise` needs its own no-op catch
|
|
44
|
+
// here too: without it, a `promise` that eventually rejects (the caller
|
|
45
|
+
// already invoked it; this function only decides how long to wait on it)
|
|
46
|
+
// surfaces as an unhandled rejection instead of being silently superseded
|
|
47
|
+
// by the immediate timeout/undefined settlement.
|
|
42
48
|
if (ms <= 0) {
|
|
49
|
+
promise.catch(() => { });
|
|
43
50
|
return onTimeout === "undefined"
|
|
44
51
|
? Promise.resolve(undefined)
|
|
45
52
|
: Promise.reject(new Error(`Timeout after ${Math.max(0, ms)}ms`));
|
|
@@ -140,6 +140,11 @@ export function flushDebugHandlesLog() {
|
|
|
140
140
|
}
|
|
141
141
|
function safeGetActiveResourcesInfo() {
|
|
142
142
|
try {
|
|
143
|
+
// SAFETY: `process.getActiveResourcesInfo` is a real Node API (>=17.3)
|
|
144
|
+
// that @types/node does not declare on `Process`. The cast only widens
|
|
145
|
+
// the type to say the method MIGHT be there; the `typeof fn ===
|
|
146
|
+
// "function"` guard on the next line is what actually decides, so a
|
|
147
|
+
// runtime without it returns [] rather than throwing.
|
|
143
148
|
const fn = process.getActiveResourcesInfo;
|
|
144
149
|
return typeof fn === "function" ? fn.call(process) : [];
|
|
145
150
|
}
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
/** Bounded, process-local telemetry for behavior degraded during one session. */
|
|
2
2
|
import { logExtension } from "./extension-log.js";
|
|
3
|
+
import { LEDGER_FIELD_MAX, truncateForLedger } from "./ledger-bounds.js";
|
|
4
|
+
import { logLatency } from "./latency-logger.js";
|
|
5
|
+
// Re-exported so existing importers keep one name for the ledger's bound.
|
|
6
|
+
export { LEDGER_FIELD_MAX, truncateForLedger };
|
|
3
7
|
const ENTRIES_PER_KIND = 20;
|
|
4
8
|
const MAX_DISTINCT_KINDS = 32;
|
|
5
9
|
const OVERFLOW_KIND = "other";
|
|
6
10
|
const groups = new Map();
|
|
7
11
|
const onceKeys = new Set();
|
|
8
12
|
const tallies = new Map();
|
|
13
|
+
// Monotonic session-boundary counter (#1536 review F5): callers that keep
|
|
14
|
+
// their OWN once-per-session latch outside the ledger (a per-instance Set
|
|
15
|
+
// the ledger itself doesn't own) can compare this lazily at use time and
|
|
16
|
+
// clear their latch on a mismatch — the same clear-on-transition shape as
|
|
17
|
+
// project-trust.ts's trustGeneration, but keyed to the ledger's own reset
|
|
18
|
+
// (resetDegradationLedger, wired into handleSessionStart) rather than a
|
|
19
|
+
// trust change.
|
|
20
|
+
let ledgerGeneration = 0;
|
|
21
|
+
/** Current session generation. Bump on every `resetDegradationLedger()`. */
|
|
22
|
+
export function getDegradationLedgerGeneration() {
|
|
23
|
+
return ledgerGeneration;
|
|
24
|
+
}
|
|
9
25
|
export function recordDegradation(record) {
|
|
10
26
|
try {
|
|
11
27
|
const kind = boundedKind(record.kind);
|
|
@@ -16,6 +32,7 @@ export function recordDegradation(record) {
|
|
|
16
32
|
group = { count: 0, entries: [] };
|
|
17
33
|
groups.set(kind, group);
|
|
18
34
|
}
|
|
35
|
+
const admitted = group.entries.length < ENTRIES_PER_KIND;
|
|
19
36
|
group.count += 1;
|
|
20
37
|
// Bounded at RECORD time (#1366 review): reasons carry arbitrary error
|
|
21
38
|
// text; a 10KB message must never become a 10KB health line or a 10KB
|
|
@@ -23,10 +40,12 @@ export function recordDegradation(record) {
|
|
|
23
40
|
group.entries.push({ subject, reason });
|
|
24
41
|
if (group.entries.length > ENTRIES_PER_KIND)
|
|
25
42
|
group.entries.shift();
|
|
43
|
+
return admitted;
|
|
26
44
|
}
|
|
27
45
|
catch (error) {
|
|
28
46
|
debugLedgerFailure("record", error);
|
|
29
47
|
// Telemetry must never break the observed path.
|
|
48
|
+
return false;
|
|
30
49
|
}
|
|
31
50
|
}
|
|
32
51
|
/** Record at most once per kind/subject during the current session. */
|
|
@@ -38,7 +57,9 @@ export function recordDegradationOnce(record) {
|
|
|
38
57
|
if (onceKeys.has(key))
|
|
39
58
|
return;
|
|
40
59
|
onceKeys.add(key);
|
|
41
|
-
recordDegradation({ kind, subject, reason: record.reason })
|
|
60
|
+
if (recordDegradation({ kind, subject, reason: record.reason })) {
|
|
61
|
+
logDurableDegradation(kind, subject, 1);
|
|
62
|
+
}
|
|
42
63
|
}
|
|
43
64
|
catch (error) {
|
|
44
65
|
debugLedgerFailure("record-once", error);
|
|
@@ -48,6 +69,14 @@ export function recordDegradationOnce(record) {
|
|
|
48
69
|
/**
|
|
49
70
|
* Count a repeated degradation while retaining one latest-reason entry per
|
|
50
71
|
* kind/subject. The group count remains the exact event total.
|
|
72
|
+
*
|
|
73
|
+
* Returns `true` when this call is the FIRST occurrence recorded for this
|
|
74
|
+
* kind/subject pair (the ledger is the single source of truth for that
|
|
75
|
+
* tally already — via `tallies` — so callers that need a once-per-subject
|
|
76
|
+
* "rising edge" signal, e.g. to gate a verbose one-time log line before
|
|
77
|
+
* falling back to the bounded count, read it off this return value instead
|
|
78
|
+
* of hand-rolling their own parallel `Set`/latch). #1716 reuses this same
|
|
79
|
+
* signal to gate `navRequest`'s detailed timeout/late-answer log writes.
|
|
51
80
|
*/
|
|
52
81
|
export function incrementDegradationCount(record) {
|
|
53
82
|
try {
|
|
@@ -62,24 +91,49 @@ export function incrementDegradationCount(record) {
|
|
|
62
91
|
group = { count: 0, entries: [] };
|
|
63
92
|
groups.set(kind, group);
|
|
64
93
|
}
|
|
65
|
-
group.count += 1;
|
|
66
|
-
const entry = { subject, reason: truncateForLedger(`${reason} (count: ${count})`) };
|
|
67
94
|
const existing = group.entries.findIndex((candidate) => candidate.subject === subject);
|
|
95
|
+
const admitted = existing >= 0 || group.entries.length < ENTRIES_PER_KIND;
|
|
96
|
+
group.count += 1;
|
|
97
|
+
// #1816: append the count AFTER truncation, never before. `reason` is
|
|
98
|
+
// already bounded above, so re-truncating the concatenation pushed the
|
|
99
|
+
// suffix past LEDGER_FIELD_MAX and silently ate it — a 200-char reason
|
|
100
|
+
// lost the one field that says how often the degradation fired.
|
|
101
|
+
const entry = { subject, reason: `${reason} (count: ${count})` };
|
|
68
102
|
if (existing >= 0)
|
|
69
103
|
group.entries.splice(existing, 1);
|
|
70
104
|
group.entries.push(entry);
|
|
71
105
|
if (group.entries.length > ENTRIES_PER_KIND)
|
|
72
106
|
group.entries.shift();
|
|
107
|
+
// Durable rows use the summary's admission and emit the first event and
|
|
108
|
+
// power-of-two milestones only, keeping the sink bounded.
|
|
109
|
+
if (admitted && isPowerOfTwo(count)) {
|
|
110
|
+
logDurableDegradation(kind, subject, count);
|
|
111
|
+
}
|
|
112
|
+
return count === 1;
|
|
73
113
|
}
|
|
74
114
|
catch (error) {
|
|
75
115
|
debugLedgerFailure("increment", error);
|
|
76
116
|
// Telemetry must never break the observed path.
|
|
117
|
+
return false;
|
|
77
118
|
}
|
|
78
119
|
}
|
|
79
|
-
/**
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Persist the accepted ledger mutation through the existing rotated NDJSON
|
|
122
|
+
* latency stream. `logLatency` owns the timestamp, PID, serialization, secret
|
|
123
|
+
* redaction, and write queue. The subject and kind were already bounded by the
|
|
124
|
+
* ledger before reaching this seam.
|
|
125
|
+
*/
|
|
126
|
+
function logDurableDegradation(kind, subject, count) {
|
|
127
|
+
logLatency({
|
|
128
|
+
type: "phase",
|
|
129
|
+
phase: "degradation_ledger",
|
|
130
|
+
filePath: subject,
|
|
131
|
+
durationMs: 0,
|
|
132
|
+
metadata: { kind, subject, count, ledgerGeneration },
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function isPowerOfTwo(value) {
|
|
136
|
+
return value > 0 && (value & (value - 1)) === 0;
|
|
83
137
|
}
|
|
84
138
|
function boundedKind(value) {
|
|
85
139
|
const kind = truncateForLedger(value);
|
|
@@ -88,12 +142,6 @@ function boundedKind(value) {
|
|
|
88
142
|
// Keep one slot available for all kinds beyond the cardinality bound.
|
|
89
143
|
return groups.size < MAX_DISTINCT_KINDS - 1 ? kind : OVERFLOW_KIND;
|
|
90
144
|
}
|
|
91
|
-
function truncateForLedger(value) {
|
|
92
|
-
const text = normalizeForLedger(value);
|
|
93
|
-
return text.length > LEDGER_FIELD_MAX
|
|
94
|
-
? `${text.slice(0, LEDGER_FIELD_MAX)}…`
|
|
95
|
-
: text;
|
|
96
|
-
}
|
|
97
145
|
export function getDegradationSummary() {
|
|
98
146
|
return [...groups.entries()].map(([kind, group]) => ({
|
|
99
147
|
kind,
|
|
@@ -148,6 +196,7 @@ export function resetDegradationLedger() {
|
|
|
148
196
|
groups.clear();
|
|
149
197
|
onceKeys.clear();
|
|
150
198
|
tallies.clear();
|
|
199
|
+
ledgerGeneration++;
|
|
151
200
|
}
|
|
152
201
|
export const DEGRADATION_ENTRIES_PER_KIND = ENTRIES_PER_KIND;
|
|
153
202
|
export const DEGRADATION_MAX_DISTINCT_KINDS = MAX_DISTINCT_KINDS;
|
|
@@ -15,6 +15,7 @@ import { findNodeToolBinary } from "./package-manager.js";
|
|
|
15
15
|
import { isFullyQualified } from "./path-utils.js";
|
|
16
16
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
17
17
|
import { createAvailabilityChecker, discoverManagedTool, getManagedToolEnvironment, resolveAvailableOrInstall, } from "./dispatch/runners/utils/runner-helpers.js";
|
|
18
|
+
import { createAvailabilityLatch, } from "./dispatch/runners/utils/availability-policy.js";
|
|
18
19
|
/**
|
|
19
20
|
* Build madge's argv for a circular-dependency scan.
|
|
20
21
|
*
|
|
@@ -135,8 +136,15 @@ async function resolvedCommandIsStale(resolved, spawnableCache) {
|
|
|
135
136
|
spawnableCache.set(resolved.cmd, spawnable);
|
|
136
137
|
return !spawnable;
|
|
137
138
|
}
|
|
138
|
-
/**
|
|
139
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Run `mapper` over `items` with at most `concurrency` in flight at once.
|
|
141
|
+
* Exported (#1810 review F6) so `clients/dispatch/runners/biome-check.ts`'s
|
|
142
|
+
* `resolveBiomeFixKinds` can reuse this exact worker-pool shape for its
|
|
143
|
+
* `biome explain` fan-out instead of carrying a second copy — this file
|
|
144
|
+
* predates that need but is otherwise unrelated to it; a shared home for
|
|
145
|
+
* this one helper wasn't worth a whole new module for two call sites.
|
|
146
|
+
*/
|
|
147
|
+
export async function mapWithConcurrency(items, concurrency, mapper) {
|
|
140
148
|
if (items.length === 0)
|
|
141
149
|
return;
|
|
142
150
|
let nextIndex = 0;
|
|
@@ -158,7 +166,8 @@ export class DependencyChecker {
|
|
|
158
166
|
environment: (cwd) => getManagedToolEnvironment("madge", cwd),
|
|
159
167
|
unclassifiedFailureOutcome: "missing",
|
|
160
168
|
});
|
|
161
|
-
|
|
169
|
+
/** Transient-aware availability memo — see `ensureAvailable` (#1467). */
|
|
170
|
+
availabilityLatch = createAvailabilityLatch();
|
|
162
171
|
ensureInFlight = null;
|
|
163
172
|
checkInFlight = new Map();
|
|
164
173
|
scanInFlight = new Map();
|
|
@@ -320,12 +329,17 @@ export class DependencyChecker {
|
|
|
320
329
|
this.circularFiles = circularFiles;
|
|
321
330
|
}
|
|
322
331
|
/**
|
|
323
|
-
* Check if madge is available, auto-install if not
|
|
332
|
+
* Check if madge is available, auto-install if not.
|
|
333
|
+
*
|
|
334
|
+
* Shares knip's latch policy (#1467): only a durable verdict is memoized, so
|
|
335
|
+
* a probe that timed out — madge's `--version` is ~0.7 s today, but a host
|
|
336
|
+
* event-loop stall can expire any budget — does not disable madge for the
|
|
337
|
+
* life of the process.
|
|
324
338
|
*/
|
|
325
339
|
async ensureAvailable(cwd = process.cwd()) {
|
|
326
|
-
|
|
327
|
-
if (
|
|
328
|
-
return
|
|
340
|
+
const memo = this.availabilityLatch.read();
|
|
341
|
+
if (memo !== null)
|
|
342
|
+
return memo;
|
|
329
343
|
if (this.ensureInFlight)
|
|
330
344
|
return this.ensureInFlight;
|
|
331
345
|
this.ensureInFlight = this.doEnsureAvailable(cwd);
|
|
@@ -338,8 +352,28 @@ export class DependencyChecker {
|
|
|
338
352
|
}
|
|
339
353
|
async doEnsureAvailable(cwd) {
|
|
340
354
|
const resolved = await resolveAvailableOrInstall(this.madgeAvailability, "madge", cwd);
|
|
341
|
-
|
|
342
|
-
|
|
355
|
+
if (resolved !== null) {
|
|
356
|
+
this.availabilityLatch.noteAvailable();
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
const verdict = this.madgeAvailability.getVerdict(cwd);
|
|
360
|
+
this.availabilityLatch.noteUnavailable(verdict.outcome ?? "missing", verdict.cause ?? "not-found");
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* #1623: public availability verdict for callers outside the dispatch
|
|
365
|
+
* graph (mode=full's fresh-fetch) that need to say WHY `ensureAvailable()`
|
|
366
|
+
* most recently returned false, using the SAME outcome/cause this class
|
|
367
|
+
* already latched above — not a re-guessed "madge binary unavailable"
|
|
368
|
+
* that can't tell a transient retry-cooldown probe from a durable
|
|
369
|
+
* absence.
|
|
370
|
+
*/
|
|
371
|
+
getAvailabilityVerdict() {
|
|
372
|
+
return {
|
|
373
|
+
outcome: this.availabilityLatch.getOutcome(),
|
|
374
|
+
cause: this.availabilityLatch.getCause(),
|
|
375
|
+
retryAtMs: this.availabilityLatch.getRetryAtMs(),
|
|
376
|
+
};
|
|
343
377
|
}
|
|
344
378
|
/**
|
|
345
379
|
* Check if a file is part of a circular dependency (from cache)
|
|
@@ -53,26 +53,40 @@
|
|
|
53
53
|
* assumption proves wrong in practice, a surrounding-window hash can be
|
|
54
54
|
* layered on later without changing the store shape.
|
|
55
55
|
*/
|
|
56
|
-
import { createHash } from "node:crypto";
|
|
57
56
|
import * as fs from "node:fs";
|
|
58
57
|
import * as path from "node:path";
|
|
59
58
|
import { commitDurableStore } from "./durable-store.js";
|
|
60
59
|
import { logDispositionEvent } from "./disposition-logger.js";
|
|
61
60
|
import { publishDisposition } from "./disposition-publish.js";
|
|
62
61
|
import { getProjectDataDir } from "./file-utils.js";
|
|
62
|
+
import { normalizeMessage as sharedNormalizeMessage, relativeFile as sharedRelativeFile, stableFindingId, } from "./finding-identity.js";
|
|
63
63
|
import { normalizeMapKey } from "./path-utils.js";
|
|
64
64
|
import { lineContentHash } from "./read-guard.js";
|
|
65
65
|
// "defer" is session-ephemeral by design (#690) — held only in memory so it
|
|
66
66
|
// resurfaces for free on the next process run, with no expiry/pruning logic
|
|
67
67
|
// needed. Stores WEAK anchors (see module doc) so a deferred finding stays
|
|
68
68
|
// hidden all session even if the flagged line itself is edited.
|
|
69
|
+
//
|
|
70
|
+
// Review-round F3 (#1625): this Set is process-GLOBAL, but a weak anchor
|
|
71
|
+
// itself only encodes a RELATIVE path (`relativeFile` derives it from
|
|
72
|
+
// `path.relative(cwd, filePath)`, never the cwd's own identity) plus
|
|
73
|
+
// tool/rule/normalizedMessage — two unrelated projects sharing the same
|
|
74
|
+
// relative path/tool/rule/message (e.g. "src/auth.ts" flagged by the same
|
|
75
|
+
// eslint rule in project A and project B) collapse onto the IDENTICAL weak
|
|
76
|
+
// anchor string. A defer in project A therefore silently suppressed the same
|
|
77
|
+
// finding in project B for the rest of the process's life. Every read/write
|
|
78
|
+
// below goes through `deferredKey`, which folds the resolved cwd into the Set
|
|
79
|
+
// key, so the two projects can no longer collide.
|
|
69
80
|
const deferredThisSession = new Set();
|
|
81
|
+
function deferredKey(cwd, anchor) {
|
|
82
|
+
return `${normalizeMapKey(cwd)}::${anchor}`;
|
|
83
|
+
}
|
|
70
84
|
/** Exported (#802) so lens-diagnostic-mark's cross-check against live widget
|
|
71
85
|
* diagnostics matches a message the same way anchor derivation does — a
|
|
72
|
-
* second, slightly different normalizer would make a real match invisible.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
86
|
+
* second, slightly different normalizer would make a real match invisible.
|
|
87
|
+
* Re-exports the shared `finding-identity.js` normalizer (#1816) so this
|
|
88
|
+
* module keeps its existing public surface. */
|
|
89
|
+
export const normalizeMessage = sharedNormalizeMessage;
|
|
76
90
|
// Anchor derivation chokepoint (#1024, #210 class): the `dd:`/`ddw:` id builders
|
|
77
91
|
// (computeStrictAnchor/computeWeakAnchor) both derive their path component here,
|
|
78
92
|
// so a mark and its later lookup diverge whenever the two callers pass different
|
|
@@ -91,34 +105,35 @@ export function normalizeMessage(message) {
|
|
|
91
105
|
// widget hot path, and the read side already pays exactly this cost. Semantics
|
|
92
106
|
// are unchanged: the `..`-escape fallback still returns the canonical filePath,
|
|
93
107
|
// only now in the same canonical form the non-escape branch uses.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
//
|
|
109
|
+
// #1816: this used to be a local copy; it is now `finding-identity.js`'s
|
|
110
|
+
// `relativeFile` (identical body — canonicalize both inputs through
|
|
111
|
+
// `normalizeMapKey` before relativizing), re-exported so the rest of this
|
|
112
|
+
// file, and the module doc/comments above referencing `relativeFile`, keep
|
|
113
|
+
// working unchanged.
|
|
114
|
+
const relativeFile = sharedRelativeFile;
|
|
100
115
|
/** Site-specific anchor — see module doc. Used only for false-positive. */
|
|
101
116
|
export function computeStrictAnchor(args) {
|
|
102
117
|
const lines = args.content?.split(/\r?\n/);
|
|
103
118
|
const lineText = args.line !== undefined && lines ? (lines[args.line - 1] ?? "") : "";
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
args.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
return stableFindingId("dd:", {
|
|
120
|
+
cwd: args.cwd,
|
|
121
|
+
filePath: args.filePath,
|
|
122
|
+
parts: [
|
|
123
|
+
args.tool ?? "",
|
|
124
|
+
args.rule ?? "",
|
|
125
|
+
normalizeMessage(args.message),
|
|
126
|
+
lineContentHash(lineText),
|
|
127
|
+
],
|
|
128
|
+
});
|
|
112
129
|
}
|
|
113
130
|
/** Intent-level anchor — see module doc. Used for defer/flagged/suppress. */
|
|
114
131
|
export function computeWeakAnchor(args) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
args.
|
|
118
|
-
args.rule ?? "",
|
|
119
|
-
|
|
120
|
-
];
|
|
121
|
-
return `ddw:${createHash("sha256").update(parts.join("|")).digest("hex").slice(0, 12)}`;
|
|
132
|
+
return stableFindingId("ddw:", {
|
|
133
|
+
cwd: args.cwd,
|
|
134
|
+
filePath: args.filePath,
|
|
135
|
+
parts: [args.tool ?? "", args.rule ?? "", normalizeMessage(args.message)],
|
|
136
|
+
});
|
|
122
137
|
}
|
|
123
138
|
/** Both anchors a stored/filtered diagnostic would compute — the one shared
|
|
124
139
|
* derivation both the dispatch-pipeline filter and lens-diagnostics' flagged
|
|
@@ -156,6 +171,15 @@ export function _setBeforeDispositionCacheRefreshForTests(hook) {
|
|
|
156
171
|
export function _setDispositionStatForTests(statSync) {
|
|
157
172
|
dispositionStatSync = statSync ?? fs.statSync;
|
|
158
173
|
}
|
|
174
|
+
// Test seam for `applyDispositionsMultiFile`'s per-group content read (#1625
|
|
175
|
+
// F2) — Vitest can't `vi.spyOn` a bare ESM `node:fs` named export directly
|
|
176
|
+
// ("Module namespace is not configurable in ESM"), so the F2 regression test
|
|
177
|
+
// (proving the empty-store hoist means zero reads, not just fast ones)
|
|
178
|
+
// swaps this indirection instead, mirroring `dispositionStatSync` above.
|
|
179
|
+
let multiFileReadFileSync = fs.readFileSync;
|
|
180
|
+
export function _setMultiFileReadForTests(readFileSync) {
|
|
181
|
+
multiFileReadFileSync = readFileSync ?? fs.readFileSync;
|
|
182
|
+
}
|
|
159
183
|
function readState(cwd) {
|
|
160
184
|
const p = statePath(cwd);
|
|
161
185
|
let stat;
|
|
@@ -278,7 +302,7 @@ export function _resetStateCacheForTests() {
|
|
|
278
302
|
* Neither can throw into the mark path; both are already internally
|
|
279
303
|
* fail-safe, but the try/catch keeps a future regression in either from
|
|
280
304
|
* breaking a mark. */
|
|
281
|
-
function emitMarkTelemetry(cwd, target, disposition, anchor, reason, existing) {
|
|
305
|
+
function emitMarkTelemetry(cwd, target, disposition, anchor, reason, existing, identity) {
|
|
282
306
|
try {
|
|
283
307
|
logDispositionEvent({
|
|
284
308
|
event: "mark",
|
|
@@ -290,6 +314,8 @@ function emitMarkTelemetry(cwd, target, disposition, anchor, reason, existing) {
|
|
|
290
314
|
reason,
|
|
291
315
|
anchor,
|
|
292
316
|
previousDisposition: existing?.disposition,
|
|
317
|
+
model: identity?.model || undefined,
|
|
318
|
+
provider: identity?.provider || undefined,
|
|
293
319
|
});
|
|
294
320
|
publishDisposition({
|
|
295
321
|
cwd,
|
|
@@ -317,7 +343,7 @@ function emitMarkTelemetry(cwd, target, disposition, anchor, reason, existing) {
|
|
|
317
343
|
* hang off it, so the agent tool and any future UI caller are covered without
|
|
318
344
|
* per-caller wiring.
|
|
319
345
|
*/
|
|
320
|
-
export function markDisposition(cwd, target, disposition, reason) {
|
|
346
|
+
export function markDisposition(cwd, target, disposition, reason, identity) {
|
|
321
347
|
const anchor = disposition === "false-positive"
|
|
322
348
|
? computeStrictAnchor(target)
|
|
323
349
|
: computeWeakAnchor(target);
|
|
@@ -326,8 +352,8 @@ export function markDisposition(cwd, target, disposition, reason) {
|
|
|
326
352
|
// mark) — the log should record what this mark shadowed either way.
|
|
327
353
|
const existing = readState(cwd).dispositions?.[anchor];
|
|
328
354
|
if (disposition === "defer") {
|
|
329
|
-
deferredThisSession.add(anchor);
|
|
330
|
-
emitMarkTelemetry(cwd, target, disposition, anchor, reason, existing);
|
|
355
|
+
deferredThisSession.add(deferredKey(cwd, anchor));
|
|
356
|
+
emitMarkTelemetry(cwd, target, disposition, anchor, reason, existing, identity);
|
|
331
357
|
return anchor;
|
|
332
358
|
}
|
|
333
359
|
const now = new Date().toISOString();
|
|
@@ -344,14 +370,18 @@ export function markDisposition(cwd, target, disposition, reason) {
|
|
|
344
370
|
lineText,
|
|
345
371
|
};
|
|
346
372
|
commitDisposition(cwd, anchor, entry);
|
|
347
|
-
emitMarkTelemetry(cwd, target, disposition, anchor, reason, existing);
|
|
373
|
+
emitMarkTelemetry(cwd, target, disposition, anchor, reason, existing, identity);
|
|
348
374
|
return anchor;
|
|
349
375
|
}
|
|
350
376
|
export function getDisposition(cwd, anchor) {
|
|
351
377
|
return readState(cwd).dispositions?.[anchor];
|
|
352
378
|
}
|
|
353
|
-
|
|
354
|
-
|
|
379
|
+
/** #1625 F3: `anchor` alone is ambiguous across projects — a weak anchor
|
|
380
|
+
* encodes only a RELATIVE path plus tool/rule/message, never the project's
|
|
381
|
+
* own identity, so `cwd` must be supplied to disambiguate which project's
|
|
382
|
+
* defer is being checked. */
|
|
383
|
+
export function isDeferredThisSession(cwd, anchor) {
|
|
384
|
+
return deferredThisSession.has(deferredKey(cwd, anchor));
|
|
355
385
|
}
|
|
356
386
|
/** Test-only escape hatch — defer state is module-level (one process = one
|
|
357
387
|
* session), so tests need to reset it between cases. */
|
|
@@ -367,6 +397,14 @@ export function _resetDeferredForTests() {
|
|
|
367
397
|
* Computes both anchors per diagnostic (cheap — same hash primitive, twice)
|
|
368
398
|
* since false-positive is keyed strict while defer/suppress are keyed weak;
|
|
369
399
|
* see module doc for why each disposition binds the way it does.
|
|
400
|
+
*
|
|
401
|
+
* Review-round F1 (#1625): a `d.semantic === "blocking"` diagnostic can be
|
|
402
|
+
* dropped ONLY by the STRICT (false-positive) branch — never by a WEAK-
|
|
403
|
+
* anchored suppress or defer. Two distinct secrets sharing the same
|
|
404
|
+
* rule/message (e.g. two different AWS keys on different lines) collapse
|
|
405
|
+
* onto the SAME weak anchor, so a weak suppress/defer on one would silently
|
|
406
|
+
* silence the other too; unacceptable for a STOP-tier finding. `flagged` is
|
|
407
|
+
* unaffected either way — it never drops anything, blocking or not.
|
|
370
408
|
*/
|
|
371
409
|
export function applyDispositions(diagnostics, cwd, filePath, content) {
|
|
372
410
|
if (!diagnostics.length)
|
|
@@ -376,16 +414,21 @@ export function applyDispositions(diagnostics, cwd, filePath, content) {
|
|
|
376
414
|
return diagnostics;
|
|
377
415
|
return diagnostics.filter((d) => {
|
|
378
416
|
const { strict, weak } = anchorsForDiagnostic(cwd, filePath, d, content);
|
|
379
|
-
|
|
417
|
+
const isBlocking = d.semantic === "blocking";
|
|
418
|
+
if (!isBlocking && deferredThisSession.has(deferredKey(cwd, weak))) {
|
|
380
419
|
return false;
|
|
420
|
+
}
|
|
381
421
|
if (dispositions?.[strict]?.disposition === "false-positive")
|
|
382
422
|
return false;
|
|
383
423
|
// Belt-and-braces: the inline `pi-lens-ignore` comment is the real
|
|
384
424
|
// suppress enforcement (see suppress-writer.ts) and normally already
|
|
385
425
|
// dropped this finding upstream via applyInlineSuppressions. This is a
|
|
386
|
-
// harmless second cover for the store-only audit trail case
|
|
387
|
-
|
|
426
|
+
// harmless second cover for the store-only audit trail case — gated
|
|
427
|
+
// off blocking findings per F1 above (the inline comment, unaffected
|
|
428
|
+
// by this gate, remains the real suppress mechanism for those too).
|
|
429
|
+
if (!isBlocking && dispositions?.[weak]?.disposition === "suppress") {
|
|
388
430
|
return false;
|
|
431
|
+
}
|
|
389
432
|
return true;
|
|
390
433
|
});
|
|
391
434
|
}
|
|
@@ -405,6 +448,13 @@ export function applyDispositions(diagnostics, cwd, filePath, content) {
|
|
|
405
448
|
* content-based filter). suppress/defer, being intent-level and weak-anchored,
|
|
406
449
|
* are the marks that must apply the instant a query re-serves cached findings,
|
|
407
450
|
* and they do so here without any read.
|
|
451
|
+
*
|
|
452
|
+
* Review-round F1 (#1625): this filter is ENTIRELY weak-anchored — it has no
|
|
453
|
+
* strict-anchor branch to fall back to — so a `d.semantic === "blocking"`
|
|
454
|
+
* diagnostic is never dropped here at all, regardless of any suppress/defer
|
|
455
|
+
* mark. The full `applyDispositions` (content in hand, at the next per-edit
|
|
456
|
+
* dispatch or mode=full merge) is the only place a blocking finding can ever
|
|
457
|
+
* be suppressed, and only via its STRICT branch.
|
|
408
458
|
*/
|
|
409
459
|
export function applyWeakDispositions(diagnostics, cwd, filePath) {
|
|
410
460
|
if (!diagnostics.length)
|
|
@@ -413,6 +463,8 @@ export function applyWeakDispositions(diagnostics, cwd, filePath) {
|
|
|
413
463
|
if (!dispositions && deferredThisSession.size === 0)
|
|
414
464
|
return diagnostics;
|
|
415
465
|
return diagnostics.filter((d) => {
|
|
466
|
+
if (d.semantic === "blocking")
|
|
467
|
+
return true;
|
|
416
468
|
const weak = computeWeakAnchor({
|
|
417
469
|
cwd,
|
|
418
470
|
filePath,
|
|
@@ -421,10 +473,74 @@ export function applyWeakDispositions(diagnostics, cwd, filePath) {
|
|
|
421
473
|
message: d.message,
|
|
422
474
|
line: d.line,
|
|
423
475
|
});
|
|
424
|
-
if (deferredThisSession.has(weak))
|
|
476
|
+
if (deferredThisSession.has(deferredKey(cwd, weak)))
|
|
425
477
|
return false;
|
|
426
478
|
if (dispositions?.[weak]?.disposition === "suppress")
|
|
427
479
|
return false;
|
|
428
480
|
return true;
|
|
429
481
|
});
|
|
430
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* Multi-file variant of {@link applyDispositions} for lanes whose findings
|
|
485
|
+
* span many files in ONE report — gitleaks/trivy/govulncheck/opengrep
|
|
486
|
+
* project-wide scans (#1617), unlike the dispatch path's one-`ctx.filePath`-
|
|
487
|
+
* at-a-time shape. Groups by `filePathOf(diagnostic)`, reads each file's
|
|
488
|
+
* CURRENT content once, then delegates to `applyDispositions` per group —
|
|
489
|
+
* same anchor derivation, same strict/weak split, no cloned logic (#1617's
|
|
490
|
+
* single-source-of-truth requirement).
|
|
491
|
+
*
|
|
492
|
+
* Content read is best-effort: a file that no longer exists, or that this
|
|
493
|
+
* process can't read, degrades to `content: ""` for that group rather than
|
|
494
|
+
* dropping its diagnostics outright. `applyDispositions` already treats a
|
|
495
|
+
* missing/empty content argument safely — the STRICT (false-positive) anchor
|
|
496
|
+
* hashes the diagnostic's own line, so an empty content can't collide with a
|
|
497
|
+
* mark made against the file's real content and the finding simply isn't
|
|
498
|
+
* matched (fails OPEN — still reported); the WEAK anchor (suppress/defer)
|
|
499
|
+
* never looks at content at all, so those marks keep applying regardless.
|
|
500
|
+
* This is a deliberate choice, not an oversight: a security finding must
|
|
501
|
+
* never silently vanish just because its file went unreadable at filter
|
|
502
|
+
* time (a security finding staying VISIBLE on a read failure is the safe
|
|
503
|
+
* default; a stale mark quietly reappearing is recoverable, a leaked secret
|
|
504
|
+
* quietly disappearing is not).
|
|
505
|
+
*
|
|
506
|
+
* Review-round F2 (#1625): the empty-store early return is checked HERE,
|
|
507
|
+
* before any per-group file read — not left to `applyDispositions` to notice
|
|
508
|
+
* once already inside the per-group loop. Every mode=full analyzer call
|
|
509
|
+
* (9 lanes) runs through this on every scan, and the overwhelmingly common
|
|
510
|
+
* case is zero marks in the project; measured ~270ms of synchronous
|
|
511
|
+
* `fs.readFileSync` per analyzer per run for that common case before this
|
|
512
|
+
* hoist (reading every finding's file just to discover there was nothing to
|
|
513
|
+
* filter), ~0-1ms after.
|
|
514
|
+
*/
|
|
515
|
+
export function applyDispositionsMultiFile(diagnostics, cwd, filePathOf) {
|
|
516
|
+
if (!diagnostics.length)
|
|
517
|
+
return diagnostics;
|
|
518
|
+
const dispositions = readState(cwd).dispositions;
|
|
519
|
+
if (!dispositions && deferredThisSession.size === 0)
|
|
520
|
+
return diagnostics;
|
|
521
|
+
const groups = new Map();
|
|
522
|
+
for (const d of diagnostics) {
|
|
523
|
+
const filePath = filePathOf(d);
|
|
524
|
+
const group = groups.get(filePath);
|
|
525
|
+
if (group)
|
|
526
|
+
group.push(d);
|
|
527
|
+
else
|
|
528
|
+
groups.set(filePath, [d]);
|
|
529
|
+
}
|
|
530
|
+
const kept = new Set();
|
|
531
|
+
for (const [filePath, group] of groups) {
|
|
532
|
+
let content = "";
|
|
533
|
+
try {
|
|
534
|
+
content = multiFileReadFileSync(filePath, "utf-8");
|
|
535
|
+
}
|
|
536
|
+
catch {
|
|
537
|
+
// Unreadable/missing — fail open, see doc above.
|
|
538
|
+
}
|
|
539
|
+
for (const d of applyDispositions(group, cwd, filePath, content)) {
|
|
540
|
+
kept.add(d);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
// Preserve the caller's original order/dedup by reference rather than the
|
|
544
|
+
// per-group insertion order above.
|
|
545
|
+
return diagnostics.filter((d) => kept.has(d));
|
|
546
|
+
}
|