@herbertgao/pi-extensions 2026.8.6 → 2026.8.8
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 +8 -6
- 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 +320 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -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 +6 -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 +17 -13
- 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 +21 -6
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
- 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 +5 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -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 +140 -97
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
- package/node_modules/@narumitw/pi-btw/README.md +120 -111
- 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 +10 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
- 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 +45 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
- package/node_modules/pi-lens/CHANGELOG.md +999 -0
- package/node_modules/pi-lens/README.md +117 -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 +126 -13
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
- 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 +381 -13
- package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -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 +206 -0
- package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
- 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/dead-code-client.js +123 -31
- 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 +84 -14
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
- package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
- package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
- 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/detekt.js +9 -6
- 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/hadolint.js +9 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
- 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 +60 -53
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
- 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 +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
- 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 +275 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
- 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 +734 -64
- 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 +188 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- 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 +31 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
- 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 +87 -8
- package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
- package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
- 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-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
- 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 +277 -16
- 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 +79 -3
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
- 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/session.js +10 -2
- package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
- 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 +154 -33
- 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 +84 -14
- 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/trivy.js +50 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
- 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 +8 -3
- package/node_modules/pi-lens/dist/clients/read-guard.js +253 -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/builder.js +194 -31
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
- package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
- package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
- package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
- 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 +157 -24
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
- package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
- package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
- 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-logger.js +1 -0
- 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/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +61407 -48577
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/docs/agent-guide.md +6 -1
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +19 -5
- package/node_modules/pi-lens/docs/globalconfig.md +3 -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 +19 -1
- package/node_modules/pi-lens/docs/usage.md +1 -0
- package/node_modules/pi-lens/package.json +27 -10
- 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 +15 -1
- 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-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 +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
- 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-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 +57 -12
- 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-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 +22 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
- 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 +6 -1
- 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 +53 -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/scripts/install-selftest.mjs +99 -11
- package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
- package/node_modules/pi-mcp-adapter/README.md +47 -5
- package/node_modules/pi-mcp-adapter/cli.js +163 -1
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +25 -7
- package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
- 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/metadata-cache.ts +32 -21
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +3 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +140 -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-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +2 -0
- 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 +32 -13
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
- package/node_modules/pi-web-access/curator-page.ts +6 -3
- package/node_modules/pi-web-access/curator-server.ts +4 -1
- package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +40 -5
- package/node_modules/pi-web-access/firecrawl.ts +18 -2
- package/node_modules/pi-web-access/gemini-search.ts +16 -4
- package/node_modules/pi-web-access/github-extract.ts +65 -36
- package/node_modules/pi-web-access/index.ts +60 -61
- package/node_modules/pi-web-access/openai-search.ts +17 -6
- package/node_modules/pi-web-access/package.json +1 -1
- 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/pdf-extract.ts +18 -5
- 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/ssrf-protection.ts +12 -2
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +44 -22
- 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/package.json +14 -14
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single documentation home for the finding-delivery gate stack (#1634).
|
|
3
|
+
*
|
|
4
|
+
* Every store that renders findings to the AGENT — the turn-end blocker/
|
|
5
|
+
* advisory tiers, `lens_diagnostics` mode=full/all/delta, the widget/footer
|
|
6
|
+
* tally, agent nudges, and the persisted project-diagnostics snapshot —
|
|
7
|
+
* invented its own freshness and retirement rules across
|
|
8
|
+
* #1461/#1561/#1622/#1631, so the stores drifted from each other and each new
|
|
9
|
+
* store re-created the class. #1622/#1625/#1627/#1633 converged on two gates;
|
|
10
|
+
* this module names them ONE time so every other file points here instead of
|
|
11
|
+
* re-explaining the contract. `mode=delta` — the tool's DEFAULT mode — was
|
|
12
|
+
* the review round's live catch: it re-served the actionable/quality-warnings
|
|
13
|
+
* caches verbatim, unfixed by the earlier per-store passes because it sits
|
|
14
|
+
* one layer up from any single scanner.
|
|
15
|
+
*
|
|
16
|
+
* ── Freshness (mtime vs scannedAt, onMissing policy) ──────────────────────
|
|
17
|
+
* `gateFindingsByPathFreshness` (advisory-provenance.ts) partitions a cached
|
|
18
|
+
* store's findings by what the cited path looks like NOW versus the scan
|
|
19
|
+
* timestamp (`scannedAt`) the store was captured at:
|
|
20
|
+
* - missing → `onMissing: "drop"` (default) removes the finding outright —
|
|
21
|
+
* right when the finding IS the file's content (a secret in a deleted
|
|
22
|
+
* file cannot be rotated). `onMissing: "demote"` keeps it — right when the
|
|
23
|
+
* cited path is only EVIDENCE for a finding pinned elsewhere (a
|
|
24
|
+
* govulncheck CVE is pinned by go.mod, not by the traced call site).
|
|
25
|
+
* - stale (mtime > scannedAt + MTIME_DRIFT_TOLERANCE_MS,
|
|
26
|
+
* `blocker-freshness.ts`) → DEMOTE, never drop. The finding survives,
|
|
27
|
+
* the now-untrustworthy line number does not.
|
|
28
|
+
* - live → deliver unchanged, full authority.
|
|
29
|
+
* - unknown (unparseable/absent `scannedAt`, unreadable path) → fail OPEN,
|
|
30
|
+
* deliver unchanged. A missed demotion is noise; a wrong drop can hide a
|
|
31
|
+
* live credential or CVE.
|
|
32
|
+
* The turn-end secrets (gitleaks/trivy-secrets) and govulncheck advisory both
|
|
33
|
+
* route through this gate in `clients/runtime-turn.ts`.
|
|
34
|
+
*
|
|
35
|
+
* The SAME freshness family, one level up, covers cross-file drift: a cached
|
|
36
|
+
* inline blocker is a verdict about its file *and everything that file
|
|
37
|
+
* imports* — `clients/blocker-freshness.ts` (`sweepInlineBlockerFreshness`,
|
|
38
|
+
* the widget-store counterpart `reconcileStaleWidgetDependencyBlockers`
|
|
39
|
+
* paired with `reconcileStaleWidgetFiles`) stats the file and its forward
|
|
40
|
+
* imports against the verdict's `recordedAtMs` baseline (#1631/#1633). A
|
|
41
|
+
* drifted entry is demoted with the SAME `[stale — re-run to confirm]`
|
|
42
|
+
* marker (`STALE_LINE_MARKER`, `clients/stale-marker.ts` — its own leaf
|
|
43
|
+
* module so `widget-state.ts` can use it without a circular import back
|
|
44
|
+
* through the turn orchestrator, #1631 review V2) and `isBlocking`
|
|
45
|
+
* answers false for it — the one predicate both the footer tally and
|
|
46
|
+
* `mode=all`'s blocking count consult, which is what keeps `mode=all` from
|
|
47
|
+
* contradicting a same-minute `mode=full` sweep on the same paths (#1631
|
|
48
|
+
* criterion 3 / #1634 criterion 4).
|
|
49
|
+
*
|
|
50
|
+
* Two INDEPENDENT gates share this `stale` field, distinguished by
|
|
51
|
+
* `WidgetDiagnostic.staleReason`: `"dependency-drift"` (#1631, above) and
|
|
52
|
+
* `"past-eof"` (#1641 — a cited line beyond the file's current on-disk length,
|
|
53
|
+
* a stale in-memory LSP document diverged from disk). Each gate only HEALS
|
|
54
|
+
* demotions it made itself (`staleReason` defaults to `"past-eof"` for
|
|
55
|
+
* backward compatibility with pre-#1641 records) — composed, not merged into
|
|
56
|
+
* one undifferentiated flag, so a fix for one hazard can never silently clear
|
|
57
|
+
* the other's verdict.
|
|
58
|
+
*
|
|
59
|
+
* ── Dispositions (strict-only for blocking) ───────────────────────────────
|
|
60
|
+
* `applyDispositions` (diagnostic-dispositions.ts) filters false-positive/
|
|
61
|
+
* suppress/defer/flagged judgments the agent or user already recorded for a
|
|
62
|
+
* finding. Anchoring is STRICT (line-content-hashed) only for false-positive
|
|
63
|
+
* — a judgment about one exact piece of code that must NOT survive a rewrite
|
|
64
|
+
* of that line — and WEAK (no line hash) for defer/flagged/suppress, which
|
|
65
|
+
* are intent-level judgments that must survive incidental nearby edits.
|
|
66
|
+
* `formatFullMode` (tools/lens-diagnostics.ts) applies this on every
|
|
67
|
+
* mode=full pass; the per-edit dispatch path applies the identical filter
|
|
68
|
+
* before writing into widget-state, so mode=all and the footer inherit
|
|
69
|
+
* disposition state for any file that has gone through a live edit this
|
|
70
|
+
* session WITHOUT re-computing it (kept cache-only and instant, #1634
|
|
71
|
+
* criterion 5's documented exception — see "Explicit lag labels" below).
|
|
72
|
+
*
|
|
73
|
+
* ── Demotion rendering (#1419) ─────────────────────────────────────────────
|
|
74
|
+
* A demoted finding is never silently dropped and never re-asserted at full
|
|
75
|
+
* authority: it renders with `STALE_LINE_MARKER` (or the widget's `[stale]`
|
|
76
|
+
* tag) in place of the coordinate the drift invalidated. This is the one
|
|
77
|
+
* rendering convention every gated surface reuses — a store must not invent
|
|
78
|
+
* its own "stale" wording.
|
|
79
|
+
*
|
|
80
|
+
* #1944 completes the rule: demotion must change the BODY, not only the
|
|
81
|
+
* channel and the coordinate. A demoted body carries no STOP banner and no
|
|
82
|
+
* "must be fixed" imperative, and a cited line the file no longer has renders
|
|
83
|
+
* as `L<n> (line no longer exists)`. `clients/demoted-finding-render.ts`
|
|
84
|
+
* (`degradeDemotedFindingBody`) is the one implementation for surfaces that
|
|
85
|
+
* deliver a RENDERED BODY — the turn-end advisory tier. Per-row surfaces
|
|
86
|
+
* (`clients/widget-state.ts`, `tools/lens-diagnostics.ts`) have no body to
|
|
87
|
+
* degrade; they satisfy the same rule by dropping the authority marker
|
|
88
|
+
* alongside the coordinate.
|
|
89
|
+
*
|
|
90
|
+
* ── Retirement (#1944) ────────────────────────────────────────────────────
|
|
91
|
+
* Demote-not-drop assumes the agent CAN re-run to confirm. A past-EOF
|
|
92
|
+
* demotion breaks that assumption: the file shrank past the cited lines, so
|
|
93
|
+
* no re-run can ever speak to them, and the record re-served on every turn
|
|
94
|
+
* end for the life of the session. Such a record is delivered ONCE, degraded,
|
|
95
|
+
* then retired (`RuntimeCoordinator.retireDemotedPastEofBlocker`). The
|
|
96
|
+
* suppression is recorded in the degradation ledger under
|
|
97
|
+
* `demoted-finding-retired`, whose subject keeps the store and file, and the
|
|
98
|
+
* delivered payload says so in its own words — so an empty advisory section
|
|
99
|
+
* can only mean "nothing to say". Dependency-drift demotions keep in-bounds
|
|
100
|
+
* coordinates the agent can act on and are NOT retired.
|
|
101
|
+
*
|
|
102
|
+
* ── Explicit lag labels (the non-gated escape hatch) ──────────────────────
|
|
103
|
+
* A store that cannot afford the freshness/disposition stack — because it
|
|
104
|
+
* has no cited per-file path to stat (a package-pinned finding like a trivy
|
|
105
|
+
* CRITICAL CVE or a license-risk row), or because re-computing dispositions
|
|
106
|
+
* would cost the "instant" cache-only render mode=all/the footer promise —
|
|
107
|
+
* MUST say so explicitly, with the finding's age, rather than presenting as
|
|
108
|
+
* if it were current. `formatCacheAgeLabel` below is the one implementation;
|
|
109
|
+
* every labeled surface in `DELIVERY_SURFACES` calls it instead of hand-
|
|
110
|
+
* rolling an age string. This is the "(b)" arm of #1634's contract: every
|
|
111
|
+
* agent-facing surface is EITHER gated OR explicitly labeled — never neither.
|
|
112
|
+
*/
|
|
113
|
+
const MS_PER_MINUTE = 60_000;
|
|
114
|
+
/**
|
|
115
|
+
* Render a short, honest age suffix for a store that cannot be freshness-
|
|
116
|
+
* gated per-path (see module doc). `undefined`/unparseable `scannedAt`
|
|
117
|
+
* degrades to a neutral "scan age unknown" label — fail open on WORDING the
|
|
118
|
+
* same way the freshness gate fails open on VERDICT: an unlabeled finding is
|
|
119
|
+
* worse than a vague one, but never fabricate a number from a bad timestamp.
|
|
120
|
+
*/
|
|
121
|
+
export function formatCacheAgeLabel(scannedAt, nowMs = Date.now()) {
|
|
122
|
+
if (scannedAt === undefined || scannedAt === null || scannedAt === "") {
|
|
123
|
+
return "scan age unknown";
|
|
124
|
+
}
|
|
125
|
+
const scannedAtMs = typeof scannedAt === "number" ? scannedAt : Date.parse(scannedAt);
|
|
126
|
+
if (!Number.isFinite(scannedAtMs))
|
|
127
|
+
return "scan age unknown";
|
|
128
|
+
const ageMs = Math.max(0, nowMs - scannedAtMs);
|
|
129
|
+
const ageMinutes = Math.round(ageMs / MS_PER_MINUTE);
|
|
130
|
+
if (ageMinutes < 1)
|
|
131
|
+
return "scanned <1m ago";
|
|
132
|
+
if (ageMinutes < 60)
|
|
133
|
+
return `scanned ${ageMinutes}m ago`;
|
|
134
|
+
// Floor the hour, keep the remainder minutes explicit — a naive
|
|
135
|
+
// `Math.round(ageMinutes / 60)` reported "1h" at 89 minutes (F5), rounding
|
|
136
|
+
// a finding's age UP is the wrong direction to err for a staleness label.
|
|
137
|
+
const ageHours = Math.floor(ageMinutes / 60);
|
|
138
|
+
const remMinutes = ageMinutes % 60;
|
|
139
|
+
return remMinutes === 0
|
|
140
|
+
? `scanned ${ageHours}h ago`
|
|
141
|
+
: `scanned ${ageHours}h ${remMinutes}m ago`;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The enumeration (#1634 criterion 1): every surface that renders findings to
|
|
145
|
+
* the agent. Two DIFFERENT seam shapes, each derived structurally rather than
|
|
146
|
+
* hand-listed (#1634 review round F2):
|
|
147
|
+
*
|
|
148
|
+
* - `clients/runtime-turn.ts`: every `blockerParts.push`/`advisoryParts.push`/
|
|
149
|
+
* `staleSecretParts.push` call site carries an `@delivery-surface: <id>`
|
|
150
|
+
* comment naming its entry here. `tests/clients/finding-delivery-gate.test.ts`
|
|
151
|
+
* scans the file for every such push call (comments/strings stripped, so a
|
|
152
|
+
* commented-out or string-embedded false match can't hide a REAL untagged
|
|
153
|
+
* seam) and fails if one has no tag, or a tag names an id not in this
|
|
154
|
+
* registry — a brand-new ungated `advisoryParts.push` cannot pass silently.
|
|
155
|
+
* - `tools/lens-diagnostics.ts`: each `mode=` report is its own function
|
|
156
|
+
* (`formatFullMode`/`formatAllMode`/`formatDeltaMode`), tagged the same way
|
|
157
|
+
* immediately above its `function` line.
|
|
158
|
+
* - `clients/widget-state.ts` (the footer) and `clients/agent-nudge.ts` are
|
|
159
|
+
* hand-registered below — one surface each, not a push-site pattern the
|
|
160
|
+
* scan can derive structurally, so a coverage test asserts these two
|
|
161
|
+
* files are not silently untouched by other tests in the same run.
|
|
162
|
+
*
|
|
163
|
+
* Single source of truth: adding a new render seam without adding/tagging an
|
|
164
|
+
* entry here fails the coverage test, not a human re-reading a hand list.
|
|
165
|
+
*/
|
|
166
|
+
/**
|
|
167
|
+
* Factory for a `gated` entry. #1634 review round (SonarCloud): 20+ entries
|
|
168
|
+
* written as multi-line object literals put the SAME `mode`/`file` key
|
|
169
|
+
* sequence on the same relative lines over and over — the TS/JS duplication
|
|
170
|
+
* detector normalizes string literals to a placeholder token, so those
|
|
171
|
+
* literally-different-content blocks still read as duplicate TOKEN STREAMS.
|
|
172
|
+
* Collapsing each entry to one factory call removes the repeating shape
|
|
173
|
+
* instead of the (irreducible) per-entry data.
|
|
174
|
+
*/
|
|
175
|
+
function gated(file, description, gates, evidence, extra = {}) {
|
|
176
|
+
return { mode: "gated", file, description, gates, evidence, ...extra };
|
|
177
|
+
}
|
|
178
|
+
/** Factory for a `labeled` entry — see {@link gated}'s doc for why this exists. */
|
|
179
|
+
function labeled(file, description, reason, ageSource, evidence = [], extra = {}) {
|
|
180
|
+
return { mode: "labeled", file, description, reason, ageSource, evidence, ...extra };
|
|
181
|
+
}
|
|
182
|
+
const RUNTIME_TURN_FILE = "clients/runtime-turn.ts";
|
|
183
|
+
const LENS_DIAGNOSTICS_FILE = "tools/lens-diagnostics.ts";
|
|
184
|
+
export const DELIVERY_SURFACES = {
|
|
185
|
+
"runtime-turn:secrets-gitleaks": gated(RUNTIME_TURN_FILE, "Turn-end 🔴 secrets blocker, gitleaks cache.", ["gateFindingsByPathFreshness"], ['store: "gitleaks"']),
|
|
186
|
+
"runtime-turn:secrets-trivy": gated(RUNTIME_TURN_FILE, "Turn-end 🔴 secrets blocker, trivy secrets cache.", ["gateFindingsByPathFreshness"], ['store: "trivy-secrets"']),
|
|
187
|
+
"runtime-turn:govulncheck-advisory": gated(RUNTIME_TURN_FILE, "Turn-end 🛡️ Go CVE advisory (call-site line only).", ["gateFindingsByPathFreshness"], ['store: "govulncheck"']),
|
|
188
|
+
// Two tagged seams (the stale and live render branches below the same
|
|
189
|
+
// `sweepInlineBlockerFreshness` call) legitimately share one evidence
|
|
190
|
+
// occurrence — evidenceMin: 2 tells the exclusive-assignment check both
|
|
191
|
+
// are expected claimants, not a rogue seam contesting the real one.
|
|
192
|
+
"runtime-turn:unresolved-inline-blocker": gated(RUNTIME_TURN_FILE, "Turn-end re-surfaced unresolved inline blockers.", ["sweepInlineBlockerFreshness"],
|
|
193
|
+
// #1790: the call now passes `additionalEntries` (widget-store rows) as a
|
|
194
|
+
// third argument, so the literal is the call's opening rather than the
|
|
195
|
+
// whole single-line invocation.
|
|
196
|
+
["sweepInlineBlockerFreshness(runtime, cwd, {"], { evidenceMin: 2 }),
|
|
197
|
+
// Same two gate calls as the live secrets tier — this tier renders their
|
|
198
|
+
// `.stale` arm, so it has no OWN gate call to point at.
|
|
199
|
+
"runtime-turn:stale-secrets-tier": gated(RUNTIME_TURN_FILE, "Turn-end 🔑 demoted-secrets tier (drifted since scan).", ["gateFindingsByPathFreshness"], ['store: "gitleaks"', 'store: "trivy-secrets"']),
|
|
200
|
+
// The evidence below is the literal header FRAGMENT including the
|
|
201
|
+
// interpolation — proves the label is actually rendered, not merely
|
|
202
|
+
// computed and discarded (review round F1's "deleted the age
|
|
203
|
+
// interpolations" attack).
|
|
204
|
+
"runtime-turn:trivy-critical-blocker": labeled(RUNTIME_TURN_FILE, "Turn-end 🔴 CRITICAL dependency CVE blocker (trivy).", "Package-pinned finding with no cited file:line to stat — freshness has " +
|
|
205
|
+
"nothing to check drift against. The session_start cache can be " +
|
|
206
|
+
"arbitrarily old, so its age is stated instead. Also routes through " +
|
|
207
|
+
"`filterFindingsByDisposition` (#1625) first — a suppressed/false-" +
|
|
208
|
+
"positive finding never reaches this render, so the age label and the " +
|
|
209
|
+
"disposition filter compose rather than substitute for each other.", "TrivyResult.scannedAt", ["CRITICAL dependency CVEs (trivy, ${trivyAgeLabel}"]),
|
|
210
|
+
"runtime-turn:trivy-cve-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end 🛡️ non-critical dependency CVE advisory (trivy).", "Same package-pinned shape and #1625 disposition composition as the " +
|
|
211
|
+
"CRITICAL blocker above.", "TrivyResult.scannedAt", ["Dependency CVEs (trivy, ${trivyAgeLabel}"]),
|
|
212
|
+
"runtime-turn:trivy-license-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end 📜 dependency license-risk advisory (trivy).", "Same package-pinned shape as the CRITICAL blocker above.", "TrivyResult.scannedAt", ["Dependency license risk (trivy, ${trivyAgeLabel}"]),
|
|
213
|
+
"runtime-turn:dead-code-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end dead-code delta advisory (knip/vulture/etc).", "The delta shown is computed from THIS turn's own analyze() call, " +
|
|
214
|
+
"diffed against the previous cache — never a replay of a stale cache.", "live"),
|
|
215
|
+
"runtime-turn:knip-blocker": labeled(RUNTIME_TURN_FILE, "Turn-end 🔴 Knip unresolved-imports/deps blocker.", "Same shape as the dead-code advisory: `await knipClient.analyze(cwd, " +
|
|
216
|
+
"...)` runs fresh THIS turn, diffed against the previous cache and " +
|
|
217
|
+
"filtered to files edited this turn — never a replay of a stale cache.", "live"),
|
|
218
|
+
"runtime-turn:knip-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end ⚠️ Knip newly-unused-exports advisory.", "Same live-this-turn shape as the Knip blocker above.", "live"),
|
|
219
|
+
"runtime-turn:actionable-warnings-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end actionable-warnings advisory (ast-grep etc).", "`peekActionableWarnings` reads `_actionableWarningsThisTurn` — recorded " +
|
|
220
|
+
"fresh from this turn's own dispatch, never a cross-turn cache.", "live"),
|
|
221
|
+
"runtime-turn:code-quality-warnings-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end code-quality-warnings advisory.", "`peekCodeQualityWarnings` reads `_codeQualityWarningsThisTurn` — the " +
|
|
222
|
+
"same per-turn accumulator shape as actionable-warnings above.", "live"),
|
|
223
|
+
"runtime-turn:disposition-suppressed-notice": labeled(RUNTIME_TURN_FILE, "Turn-end running disposition-suppressed-count notice (#1616).", "A live running total accumulated from this turn's own gate/disposition " +
|
|
224
|
+
"calls above (`dispositionSuppressedTotal`) — telemetry ABOUT the other " +
|
|
225
|
+
"gated surfaces, not a cache of its own.", "live"),
|
|
226
|
+
"runtime-turn:cascade-blocker": labeled(RUNTIME_TURN_FILE, "Turn-end 🧪 cascade neighbor blocker.", "Cascade results settle synchronously this turn where possible " +
|
|
227
|
+
"(`settleCascadeRuns`), but an unsettled compute can carry over to a " +
|
|
228
|
+
"later turn (bounded by a carry cap) — this round does not freshness-" +
|
|
229
|
+
"gate that carry-over window.", "live", [], {
|
|
230
|
+
status: "partial",
|
|
231
|
+
partialReason: "A carried-over cascade result (run.carriedTurns > 0) is rendered " +
|
|
232
|
+
"without an age label. Follow-up: surface carriedTurns as an explicit " +
|
|
233
|
+
"label when > 0, or route through formatCacheAgeLabel using the run's " +
|
|
234
|
+
"own timestamp.",
|
|
235
|
+
}),
|
|
236
|
+
"runtime-turn:cascade-coverage-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end cascade-coverage-gap advisories (graph/binding/budget).", "Explains what the cascade check could NOT confirm this turn — not a " +
|
|
237
|
+
"finding with a cited path, an absence-of-coverage disclosure computed " +
|
|
238
|
+
"from this turn's own indeterminate-run list.", "live", [], {
|
|
239
|
+
status: "partial",
|
|
240
|
+
partialReason: "Same cascade carry-over caveat as runtime-turn:cascade-blocker.",
|
|
241
|
+
}),
|
|
242
|
+
"runtime-turn:call-graph-advisory": labeled(RUNTIME_TURN_FILE, "Turn-end 📊 call-graph impact advisory.", "`runtime.callGraph` is a session-lifetime in-memory structure; this " +
|
|
243
|
+
"round does not verify or gate ITS OWN freshness policy (how/when it " +
|
|
244
|
+
"incorporates edits made earlier in the session).", "live", [], {
|
|
245
|
+
status: "partial",
|
|
246
|
+
partialReason: "call-graph freshness/invalidation policy is out of this PR's scope — " +
|
|
247
|
+
"tracked as a follow-up, not silently assumed fresh.",
|
|
248
|
+
}),
|
|
249
|
+
"lens-diagnostics:mode-full": gated(LENS_DIAGNOSTICS_FILE, "`lens_diagnostics mode=full` report.", ["applyDispositions", "applyRulePolicy", "reconcileProjectDiagnosticsSnapshot"], ["applyDispositions(", "applyRulePolicy(", "reconcileProjectDiagnosticsSnapshot("]),
|
|
250
|
+
"lens-diagnostics:mode-all": gated(LENS_DIAGNOSTICS_FILE, "`lens_diagnostics mode=all` report (widget-state read).", ["reconcileStaleWidgetFiles", "reconcileStaleWidgetDependencyBlockers"], ["reconcileStaleWidgetFiles(", "reconcileStaleWidgetDependencyBlockers("]),
|
|
251
|
+
// #1634 review round F3: this was the most important gap — the tool's
|
|
252
|
+
// DEFAULT mode rendered cached `file:line` findings with zero freshness
|
|
253
|
+
// check. `applyDeltaFreshnessGate` (this file's sibling in
|
|
254
|
+
// tools/lens-diagnostics.ts) now routes the actionable/quality-warnings
|
|
255
|
+
// caches through the shared gate using each report's own `generatedAt`.
|
|
256
|
+
// Round R3: mode=delta has a THIRD arm — the persisted project-diagnostics
|
|
257
|
+
// delta report, rendered by `appendProjectDiagnosticsDeltaLines` — which
|
|
258
|
+
// carried the identical unfixed shape. It now gates the same way, against
|
|
259
|
+
// its own `generatedAt`.
|
|
260
|
+
"lens-diagnostics:mode-delta": gated(LENS_DIAGNOSTICS_FILE, "`lens_diagnostics mode=delta` report (the tool's DEFAULT mode) — " +
|
|
261
|
+
"re-serves the actionable-warnings/code-quality-warnings caches AND " +
|
|
262
|
+
"the persisted project-diagnostics delta report.", ["gateFindingsByPathFreshness"], [
|
|
263
|
+
'store: "lens-diagnostics-delta"',
|
|
264
|
+
"applyDeltaFreshnessGate(",
|
|
265
|
+
'store: "lens-diagnostics-delta-project"',
|
|
266
|
+
]),
|
|
267
|
+
"widget-state:footer": gated("clients/widget-state.ts", "TUI footer blocking/error/warning tally.", ["reconcileStaleWidgetFiles", "isBlocking"], ["isBlocking(diagnostic)", "reconcileStaleWidgetFiles()"]),
|
|
268
|
+
"agent-nudge:context-message": labeled("clients/agent-nudge.ts", "Post-edit file-touch nudge injected via `context`.", "Not a scanner finding store: no severity, no cited line, no cache — the " +
|
|
269
|
+
"accumulator is drained and cleared at the moment of injection, so the " +
|
|
270
|
+
"content IS the current state by construction.", "live"),
|
|
271
|
+
"project-diagnostics:persisted-snapshot": gated(LENS_DIAGNOSTICS_FILE, "Cross-session persisted project-diagnostics snapshot read.", ["reconcileProjectDiagnosticsSnapshot"], ["reconcileProjectDiagnosticsSnapshot("]),
|
|
272
|
+
};
|
|
273
|
+
function assertPartialStatusHasReason(id, entry) {
|
|
274
|
+
if (entry.status === "partial" && !entry.partialReason) {
|
|
275
|
+
throw new Error(`finding-delivery-gate: surface "${id}" is status=partial but names no partialReason`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function assertGatedShapeIsWellFormed(id, entry) {
|
|
279
|
+
if (!Array.isArray(entry.gates) || entry.gates.length === 0) {
|
|
280
|
+
throw new Error(`finding-delivery-gate: surface "${id}" is mode=gated but names no gate`);
|
|
281
|
+
}
|
|
282
|
+
if (!Array.isArray(entry.evidence) || entry.evidence.length === 0) {
|
|
283
|
+
throw new Error(`finding-delivery-gate: surface "${id}" is mode=gated but names no evidence`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function assertLabeledShapeIsWellFormed(id, entry) {
|
|
287
|
+
if (!entry.reason || !entry.ageSource) {
|
|
288
|
+
throw new Error(`finding-delivery-gate: surface "${id}" is mode=labeled but is missing reason/ageSource`);
|
|
289
|
+
}
|
|
290
|
+
// "live" surfaces call no age-formatting helper — nothing to grep for.
|
|
291
|
+
// Every other ageSource claims a real cache, so it must be provable.
|
|
292
|
+
if (entry.ageSource !== "live" && entry.evidence.length === 0) {
|
|
293
|
+
throw new Error(`finding-delivery-gate: surface "${id}" claims ageSource "${entry.ageSource}" but names no evidence`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Enforce #1634's "no third state" rule: every registered surface is either
|
|
298
|
+
* `gated` (names at least one gate) or `labeled` (names a reason and an age
|
|
299
|
+
* source) — never a malformed/undeclared third shape. Throws naming the
|
|
300
|
+
* offending surface id, so a bad registration fails loudly instead of
|
|
301
|
+
* silently rendering ungated.
|
|
302
|
+
*/
|
|
303
|
+
export function assertNoDeliveryBypass(registry = DELIVERY_SURFACES) {
|
|
304
|
+
for (const [id, entry] of Object.entries(registry)) {
|
|
305
|
+
assertPartialStatusHasReason(id, entry);
|
|
306
|
+
if (entry.mode === "gated") {
|
|
307
|
+
assertGatedShapeIsWellFormed(id, entry);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (entry.mode === "labeled") {
|
|
311
|
+
assertLabeledShapeIsWellFormed(id, entry);
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
throw new Error(`finding-delivery-gate: surface "${id}" has unrecognized mode ${JSON.stringify(entry.mode)} — must be "gated" or "labeled"`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared "finding identity" primitive (#1816 item 2, unifying the #533
|
|
3
|
+
* canonicalization fix that only reached one of three copies).
|
|
4
|
+
*
|
|
5
|
+
* Three call sites independently hand-rolled the same shape — a hash over
|
|
6
|
+
* `relativeFile|tool|rule|...|normalizedMessage` — to derive a stable id for
|
|
7
|
+
* a diagnostic/finding: `diagnostic-dispositions.ts` (dd:/ddw:),
|
|
8
|
+
* `actionable-warnings.ts` (aw:), and `code-quality-warnings.ts` (cq:). Only
|
|
9
|
+
* `diagnostic-dispositions.ts`'s `relativeFile` routed both `cwd` and
|
|
10
|
+
* `filePath` through `normalizeMapKey` before computing the relative path
|
|
11
|
+
* (see the #1024/#210-class comment this module's `relativeFile` carries
|
|
12
|
+
* forward) — the other two hashed the RAW inputs, so a drive-letter-case or
|
|
13
|
+
* slash-variant `filePath` produced a different id in the warning stores
|
|
14
|
+
* than dispositions would compute for the identical file. This module is the
|
|
15
|
+
* single source for all three: one canonicalizing `relativeFile`, one
|
|
16
|
+
* `normalizeMessage`, one `hashText`, and one hash length.
|
|
17
|
+
*
|
|
18
|
+
* Hash length: dispositions used 12 hex chars, the two warning builders used
|
|
19
|
+
* 10. This module picks 12 (dispositions' length — dispositions is the older,
|
|
20
|
+
* more heavily-audited call site) for every finding id going forward.
|
|
21
|
+
* Callers that persist ids in a keyed on-disk store (only
|
|
22
|
+
* `actionable-warnings.ts` does; see its own module doc) are responsible for
|
|
23
|
+
* back-compat lookups against ids computed under their PRE-#1816 formula —
|
|
24
|
+
* this module does not attempt migration itself, since it has no way to know
|
|
25
|
+
* which callers persist and which are ephemeral-per-turn.
|
|
26
|
+
*/
|
|
27
|
+
import { createHash } from "node:crypto";
|
|
28
|
+
import * as path from "node:path";
|
|
29
|
+
import { normalizeMapKey } from "./path-utils.js";
|
|
30
|
+
/** Every finding id in this codebase truncates its sha256 hex digest to this
|
|
31
|
+
* many characters. Single source so the three call sites this module
|
|
32
|
+
* unifies (and any future one) can't drift back apart the way the pre-#1816
|
|
33
|
+
* 10-vs-12 split did. */
|
|
34
|
+
export const FINDING_ID_HASH_LENGTH = 12;
|
|
35
|
+
/** Exported so a cross-check against a live diagnostic's message (e.g.
|
|
36
|
+
* lens-diagnostic-mark's widget match) folds whitespace/case the same way id
|
|
37
|
+
* derivation does — a second, slightly different normalizer would make a
|
|
38
|
+
* real match invisible. */
|
|
39
|
+
export function normalizeMessage(message) {
|
|
40
|
+
return message.replace(/\s+/g, " ").trim().toLowerCase();
|
|
41
|
+
}
|
|
42
|
+
export function hashText(value, length = FINDING_ID_HASH_LENGTH) {
|
|
43
|
+
return createHash("sha256").update(value).digest("hex").slice(0, length);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Canonicalize BOTH `filePath` and `cwd` through `normalizeMapKey` (realpath
|
|
47
|
+
* + case-fold on Windows) BEFORE computing the relative path, so two callers
|
|
48
|
+
* holding different path FORMS of the same file (raw vs. already-normalized,
|
|
49
|
+
* a Windows drive/segment case difference, a slash variant) converge on the
|
|
50
|
+
* identical relative-path component instead of silently diverging into two
|
|
51
|
+
* different finding ids for the same file (the #533 orphaned-record class).
|
|
52
|
+
* `normalizeMapKey` is idempotent, so an already-canonical caller pays no
|
|
53
|
+
* behavior change, only the realpath I/O — acceptable here because finding
|
|
54
|
+
* ids are computed far less often than a per-write hot-path map key.
|
|
55
|
+
*/
|
|
56
|
+
export function relativeFile(filePath, cwd) {
|
|
57
|
+
const canonicalCwd = normalizeMapKey(cwd);
|
|
58
|
+
const canonicalFile = normalizeMapKey(filePath);
|
|
59
|
+
const rel = path.relative(canonicalCwd, canonicalFile).replace(/\\/g, "/");
|
|
60
|
+
return rel && !rel.startsWith("..") ? rel : canonicalFile;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Shared finding-id builder. `prefix` carries the caller's own namespace
|
|
64
|
+
* (`"dd:"`, `"ddw:"`, `"aw:"`, `"cq:"`, ...) so distinct finding families
|
|
65
|
+
* never collide in a shared store. `parts` is the caller's own ordered,
|
|
66
|
+
* domain-specific identity fields (tool/rule/code/normalizedMessage/line/
|
|
67
|
+
* lineContentHash/...) — this function only supplies the canonical
|
|
68
|
+
* `relativeFile` component and the hash/prefix wiring.
|
|
69
|
+
*
|
|
70
|
+
* Both `undefined` AND `null` entries fold to `""` (review-round F4,
|
|
71
|
+
* #1816), matching the `String(x ?? "")` idiom every pre-#1816 id builder
|
|
72
|
+
* used. `null` is in the parts type despite every caller's field being
|
|
73
|
+
* declared `T | undefined` in TypeScript: those fields (`Diagnostic.code`,
|
|
74
|
+
* `.rule`, etc.) are frequently assigned straight from `JSON.parse`'d tool
|
|
75
|
+
* output, which defeats the static type at runtime — a runner that emits
|
|
76
|
+
* `"code": null` produces a real `null`, not `undefined`. Folding only
|
|
77
|
+
* `undefined` would silently hash that as the string `"null"` instead of
|
|
78
|
+
* `""`, diverging from every other id-construction path for the exact same
|
|
79
|
+
* finding — a fresh, narrower instance of the #533 divergence class this
|
|
80
|
+
* module exists to eliminate.
|
|
81
|
+
*/
|
|
82
|
+
export function stableFindingId(prefix, args) {
|
|
83
|
+
const joined = [
|
|
84
|
+
relativeFile(args.filePath, args.cwd),
|
|
85
|
+
...args.parts.map((part) => part === undefined || part === null ? "" : String(part)),
|
|
86
|
+
].join("|");
|
|
87
|
+
return `${prefix}${hashText(joined)}`;
|
|
88
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** Shared lazy formatter catalog seam (#1394). */
|
|
2
|
-
|
|
2
|
+
import { createLazyImport } from "./lazy-import.js";
|
|
3
|
+
const lazyFormatters = createLazyImport(() => import("./formatters.js"));
|
|
3
4
|
export function warmFormatters() {
|
|
4
|
-
return (
|
|
5
|
+
return lazyFormatters.get();
|
|
5
6
|
}
|
|
6
7
|
export function loadFormatters() {
|
|
7
8
|
return warmFormatters();
|