@herbertgao/pi-extensions 2026.8.6 → 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 +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
- 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/renderer/compact-mode.ts +94 -77
- 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 +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 +1 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +26 -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 +2 -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 +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-lens/CHANGELOG.md +909 -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 +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 +285 -8
- 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 +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/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 +62 -13
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -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/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 +60 -53
- 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 +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 +72 -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 +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/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 +294 -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 +82 -6
- 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 +2 -0
- 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 +102 -7
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1516 -159
- 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 +2274 -147
- 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 +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 +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 +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 +52 -4
- 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 +308 -45
- package/node_modules/pi-lens/dist/clients/runtime-session.js +332 -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 +575 -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/sg-runner.js +116 -22
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- 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-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 +67738 -55739
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +466 -54
- 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 +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 +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/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 +23 -0
- package/node_modules/pi-web-access/README.md +27 -9
- 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/curator-page.ts +6 -3
- package/node_modules/pi-web-access/curator-server.ts +4 -1
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +28 -3
- package/node_modules/pi-web-access/gemini-search.ts +16 -4
- package/node_modules/pi-web-access/index.ts +41 -57
- 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/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 +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 +13 -13
|
@@ -213,11 +213,19 @@ Thanks goes to these wonderful people:
|
|
|
213
213
|
</tr>
|
|
214
214
|
<tr>
|
|
215
215
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pppobear"><img src="https://avatars.githubusercontent.com/u/21952175?v=4" width="100px;" alt=""/><br /><sub><b>pppobear</b></sub></a><br /><a href="#bug-pppobear" title="Bug reports">🐛</a></td>
|
|
216
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bcachet"><img src="https://avatars.githubusercontent.com/u/45542?v=4" width="100px;" alt=""/><br /><sub><b>Bertrand Cachet</b></sub></a><br /><a href="#code-bcachet" title="Code">💻</a></td>
|
|
217
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/EarthChen"><img src="https://avatars.githubusercontent.com/u/20179425?v=4" width="100px;" alt=""/><br /><sub><b>earthchen</b></sub></a><br /><a href="#code-EarthChen" title="Code">💻</a></td>
|
|
218
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Don-Yin"><img src="https://avatars.githubusercontent.com/u/65135356?v=4" width="100px;" alt=""/><br /><sub><b>Don Yin</b></sub></a><br /><a href="#code-Don-Yin" title="Code">💻</a></td>
|
|
219
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ELA718"><img src="https://avatars.githubusercontent.com/u/193304463?v=4" width="100px;" alt=""/><br /><sub><b>ELA718</b></sub></a><br /><a href="#doc-ELA718" title="Documentation">📖</a></td>
|
|
220
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sujeito-operator/pilot"><img src="https://avatars.githubusercontent.com/u/313599463?v=4" width="100px;" alt=""/><br /><sub><b>Sujeito Operator</b></sub></a><br /><a href="#code-sujeito-operator" title="Code">💻</a></td>
|
|
221
|
+
<td align="center" valign="top" width="14.28%"><a href="http://www.blahs.life/"><img src="https://avatars.githubusercontent.com/u/620336?v=4" width="100px;" alt=""/><br /><sub><b>Dan Blah</b></sub></a><br /><a href="#bug-danblah" title="Bug reports">🐛</a></td>
|
|
222
|
+
</tr>
|
|
223
|
+
<tr>
|
|
216
224
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/3choBoomer"><img src="https://avatars.githubusercontent.com/u/521828?v=4" width="100px;" alt=""/><br /><sub><b>Nathan Cooke</b></sub></a><br /><a href="#code-3choBoomer" title="Code">💻</a></td>
|
|
217
225
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/white-hat"><img src="https://avatars.githubusercontent.com/u/922988?v=4" width="100px;" alt=""/><br /><sub><b>Eli Stark</b></sub></a><br /><a href="#code-white-hat" title="Code">💻</a></td>
|
|
218
|
-
<td align="center" valign="top" width="14.28%"><a href="https://
|
|
219
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mjfaga"><img src="https://avatars.githubusercontent.com/u/7584015?v=4" width="100px;" alt=""/><br /><sub><b>Mark
|
|
220
|
-
<td align="center" valign="top" width="14.28%"><a href="https://
|
|
226
|
+
<td align="center" valign="top" width="14.28%"><a href="https://marvinaziz.de/"><img src="https://avatars.githubusercontent.com/u/33159023?v=4" width="100px;" alt=""/><br /><sub><b>Marvin Aziz</b></sub></a><br /><a href="#code-marvtub" title="Code">💻</a></td>
|
|
227
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mjfaga"><img src="https://avatars.githubusercontent.com/u/7584015?v=4" width="100px;" alt=""/><br /><sub><b>Mark</b></sub></a><br /><a href="#code-mjfaga" title="Code">💻</a></td>
|
|
228
|
+
<td align="center" valign="top" width="14.28%"><a href="https://aeturnal.io/"><img src="https://avatars.githubusercontent.com/u/219271200?v=4" width="100px;" alt=""/><br /><sub><b>aeturnal</b></sub></a><br /><a href="#code-aeturnal" title="Code">💻</a></td>
|
|
221
229
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/floatGray"><img src="https://avatars.githubusercontent.com/u/44295302?v=4" width="100px;" alt=""/><br /><sub><b>floatGray</b></sub></a><br /><a href="#code-floatGray" title="Code">💻</a></td>
|
|
222
230
|
</tr>
|
|
223
231
|
</tbody>
|
|
@@ -16,10 +16,52 @@ select = [
|
|
|
16
16
|
"SIM", # flake8-simplify (simplifiable constructs)
|
|
17
17
|
"C4", # flake8-comprehensions (unnecessary comprehension patterns)
|
|
18
18
|
"PIE", # flake8-pie (misc lints)
|
|
19
|
+
"S", # flake8-bandit (Bandit-equivalent security checks; refs #1757)
|
|
19
20
|
]
|
|
20
21
|
|
|
22
|
+
# C90 (mccabe cyclomatic complexity) — considered and NOT enabled (refs #1757).
|
|
23
|
+
# Measured against `requests` (mature, well-styled: 2 hits over the default
|
|
24
|
+
# threshold of 10, both genuinely complex) and `pandas` (379 hits) — the
|
|
25
|
+
# signal is real complexity, not false positives, but the volume on a large
|
|
26
|
+
# pre-existing codebase would drown the security signal `S` adds in the same
|
|
27
|
+
# pass. C90 is a maintainability metric, not a Bandit-equivalent security
|
|
28
|
+
# check, so it stays out of scope for this issue. Revisit as its own opt-in
|
|
29
|
+
# if pi-lens grows a "code health" ruleset.
|
|
30
|
+
|
|
31
|
+
# S sub-rules excluded below were triaged by running `ruff check --select S`
|
|
32
|
+
# against tests/fixtures/real-bugs/python/silent-and-security.py (this repo's
|
|
33
|
+
# own security fixture — 3/3 true positives: S110, S105, S307) and against the
|
|
34
|
+
# `requests` and `pip` packages installed under a local Python 3.11 (394
|
|
35
|
+
# combined S findings across two mature, widely-audited real-world
|
|
36
|
+
# codebases) — chosen as the "2-3 real fixtures" because pi-lens's own repo
|
|
37
|
+
# only has ~15 Python files, too small to expose over-firing sub-rules.
|
|
21
38
|
ignore = [
|
|
22
39
|
"E501", # line too long — formatter handles this
|
|
23
40
|
"B008", # function call in default argument — common FastAPI/typer pattern
|
|
24
41
|
"SIM108", # ternary operator — readability is subjective
|
|
42
|
+
|
|
43
|
+
# S101 assert: 354 of 394 S hits (90%) across requests+pip were bare
|
|
44
|
+
# `assert` statements — the risk `S101` warns about (asserts stripped under
|
|
45
|
+
# `python -O`) is real only for security-enforcement code, not general
|
|
46
|
+
# application or test code, where `assert` is idiomatic (pytest). Pure
|
|
47
|
+
# noise at this volume.
|
|
48
|
+
"S101",
|
|
49
|
+
# S311 suspicious-non-cryptographic-random-usage: every hit observed
|
|
50
|
+
# (e.g. pip/_internal/utils/filesystem.py building a temp-file suffix) was
|
|
51
|
+
# a non-security use of `random` — file names, jitter, sampling. Ruff
|
|
52
|
+
# cannot distinguish security-sensitive randomness from ordinary use.
|
|
53
|
+
"S311",
|
|
54
|
+
# S603 subprocess-without-shell-equals-true: fires on every list-argument
|
|
55
|
+
# `subprocess.run(...)`/`Popen(...)` call regardless of whether the
|
|
56
|
+
# arguments are attacker-controlled — this is the RECOMMENDED safe form
|
|
57
|
+
# (no shell=True), yet it was the rule's only trigger condition in every
|
|
58
|
+
# one of the 10 hits observed in pip. Bandit's own docs note this rule's
|
|
59
|
+
# high false-positive rate; excluded for the same reason upstream projects
|
|
60
|
+
# commonly disable it.
|
|
61
|
+
"S603",
|
|
62
|
+
# S607 start-process-with-partial-path: fires whenever a subprocess call
|
|
63
|
+
# names its executable by PATH lookup (`["rustc", "--version"]`) instead of
|
|
64
|
+
# an absolute path — standard practice for any CLI-wrapping code, and both
|
|
65
|
+
# observed hits in pip were exactly that pattern, not a real vulnerability.
|
|
66
|
+
"S607",
|
|
25
67
|
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { hashText, normalizeMessage, stableFindingId, } from "./finding-identity.js";
|
|
4
5
|
import { applyWorkspaceEdit } from "./lsp/edits.js";
|
|
5
6
|
import { getLSPService } from "./lsp/index.js";
|
|
6
7
|
import { normalizeMapKey } from "./path-utils.js";
|
|
@@ -14,19 +15,47 @@ let beforeWarningStateLockForTests = null;
|
|
|
14
15
|
export function _setBeforeWarningStateLockForTests(hook) {
|
|
15
16
|
beforeWarningStateLockForTests = hook;
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const rel = path.relative(cwd, filePath).replace(/\\/g, "/");
|
|
25
|
-
return rel && !rel.startsWith("..") ? rel : normalizeMapKey(filePath);
|
|
26
|
-
}
|
|
18
|
+
/** #1816: was a local `relativeFile|tool|source|code|rule|normalizedMessage|
|
|
19
|
+
* line` hash, hand-rolled independently of `diagnostic-dispositions.ts`'s
|
|
20
|
+
* canonicalizing version — this is now the shared `finding-identity.js`
|
|
21
|
+
* builder (which DOES canonicalize both `cwd` and `filePath` through
|
|
22
|
+
* `normalizeMapKey` before relativizing, and hashes to 12 chars, matching
|
|
23
|
+
* dispositions). See `legacyActionableWarningId` below for the pre-#1816
|
|
24
|
+
* formula, kept only for on-disk suppression-store migration. */
|
|
27
25
|
export function createActionableWarningId(args) {
|
|
26
|
+
return stableFindingId("aw:", {
|
|
27
|
+
cwd: args.cwd,
|
|
28
|
+
filePath: args.filePath,
|
|
29
|
+
parts: [
|
|
30
|
+
args.tool,
|
|
31
|
+
args.source,
|
|
32
|
+
args.code,
|
|
33
|
+
args.rule,
|
|
34
|
+
normalizeMessage(args.message),
|
|
35
|
+
args.line,
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** PRE-#1816 id formula (raw, non-canonicalized `relativeFile`; 10-char
|
|
40
|
+
* hash). `actionable-warning-state.json` is a keyed, persisted store — a
|
|
41
|
+
* warning suppressed under the old formula must not silently reappear as
|
|
42
|
+
* unsuppressed just because this module unified onto the canonical,
|
|
43
|
+
* 12-char id. `suppressionFor`/`updateWarningState` use this ONLY to look up
|
|
44
|
+
* and migrate a still-pending old entry forward; nothing ever WRITES under
|
|
45
|
+
* this id. Do not canonicalize this function — that would make it identical
|
|
46
|
+
* to `createActionableWarningId` and silently defeat the migration lookup
|
|
47
|
+
* for every path that actually needed canonicalizing (the #533 class this
|
|
48
|
+
* whole item exists to fix). Review-round F3 (#1816): this guard is only
|
|
49
|
+
* provable under a MIS-CASED path fixture — a fixture whose raw and
|
|
50
|
+
* canonical forms coincide (a bare mkdtempSync path) makes canonicalizing
|
|
51
|
+
* this function a no-op, so the regression test must seed under a mis-cased
|
|
52
|
+
* segment (see `actionable-warnings.test.ts`'s migration describe block) or
|
|
53
|
+
* the guard passes vacuously either way. */
|
|
54
|
+
function legacyActionableWarningId(args) {
|
|
55
|
+
const rel = path.relative(args.cwd, args.filePath).replace(/\\/g, "/");
|
|
56
|
+
const legacyRelativeFile = rel && !rel.startsWith("..") ? rel : normalizeMapKey(args.filePath);
|
|
28
57
|
const parts = [
|
|
29
|
-
|
|
58
|
+
legacyRelativeFile,
|
|
30
59
|
args.tool ?? "",
|
|
31
60
|
args.source ?? "",
|
|
32
61
|
String(args.code ?? ""),
|
|
@@ -34,7 +63,7 @@ export function createActionableWarningId(args) {
|
|
|
34
63
|
normalizeMessage(args.message),
|
|
35
64
|
String(args.line ?? ""),
|
|
36
65
|
];
|
|
37
|
-
return `aw:${hashText(parts.join("|"))}`;
|
|
66
|
+
return `aw:${hashText(parts.join("|"), 10)}`;
|
|
38
67
|
}
|
|
39
68
|
function actionSafety(action) {
|
|
40
69
|
const kind = action.kind ?? "";
|
|
@@ -93,7 +122,28 @@ function updateWarningState(cwd, warnings) {
|
|
|
93
122
|
const now = new Date().toISOString();
|
|
94
123
|
state.warnings ??= {};
|
|
95
124
|
for (const warning of warnings) {
|
|
96
|
-
|
|
125
|
+
// #1816 migration: this warning may still be recorded under the
|
|
126
|
+
// pre-#1816 id (raw relativeFile, 10-char hash) from before this
|
|
127
|
+
// store unified onto the canonical id. Fold that entry forward
|
|
128
|
+
// onto the current id and drop the stale key, so a warning
|
|
129
|
+
// suppressed before the migration stays suppressed, and repeated
|
|
130
|
+
// re-encounters converge the store onto one id per warning
|
|
131
|
+
// instead of accumulating both forever.
|
|
132
|
+
//
|
|
133
|
+
// Review-round F1: this MUST read `warning.legacyId` — the value
|
|
134
|
+
// `suppressionFor` already computed from the exact identity args
|
|
135
|
+
// used at lookup time — and must NEVER re-derive a legacy id from
|
|
136
|
+
// `warning.rule`/`tool`/`source`/`code`. Those fields hold
|
|
137
|
+
// DISPLAY values that diverge from the id-construction args for
|
|
138
|
+
// LSP-origin records (see `ActionableWarningRecord.legacyId`'s
|
|
139
|
+
// doc comment), so a re-derivation here would compute a
|
|
140
|
+
// different legacy id than the one `suppressionFor` checked,
|
|
141
|
+
// permanently bifurcating the store.
|
|
142
|
+
const legacyId = warning.legacyId;
|
|
143
|
+
const legacyEntry = legacyId && legacyId !== warning.id
|
|
144
|
+
? state.warnings[legacyId]
|
|
145
|
+
: undefined;
|
|
146
|
+
const existing = state.warnings[warning.id] ?? legacyEntry ?? {};
|
|
97
147
|
state.warnings[warning.id] = {
|
|
98
148
|
...existing,
|
|
99
149
|
status: existing.status ?? "active",
|
|
@@ -101,6 +151,8 @@ function updateWarningState(cwd, warnings) {
|
|
|
101
151
|
lastSeenAt: now,
|
|
102
152
|
seenCount: (existing.seenCount ?? 0) + 1,
|
|
103
153
|
};
|
|
154
|
+
if (legacyEntry && legacyId)
|
|
155
|
+
delete state.warnings[legacyId];
|
|
104
156
|
}
|
|
105
157
|
return state;
|
|
106
158
|
},
|
|
@@ -115,36 +167,64 @@ function updateWarningState(cwd, warnings) {
|
|
|
115
167
|
}),
|
|
116
168
|
});
|
|
117
169
|
}
|
|
118
|
-
|
|
119
|
-
|
|
170
|
+
/** Looks up suppression under the current id, falling back to the pre-#1816
|
|
171
|
+
* id (see `legacyActionableWarningId`) so a warning suppressed before this
|
|
172
|
+
* migration doesn't silently reappear as unsuppressed. `args` is the exact
|
|
173
|
+
* identity shape both id builders take.
|
|
174
|
+
*
|
|
175
|
+
* Also RETURNS the `legacyId` it computed (review-round F1, #1816): the
|
|
176
|
+
* caller carries it onto the record's `legacyId` field so
|
|
177
|
+
* `updateWarningState` can migrate the SAME legacy id this lookup used,
|
|
178
|
+
* instead of re-deriving one from the record's own `rule`/`tool`/`source`/
|
|
179
|
+
* `code` fields later. Re-deriving is unsound for LSP-origin records —
|
|
180
|
+
* `recordFromLspDiagnostic` passes no `rule` into this function (LSP
|
|
181
|
+
* diagnostics don't have one), then sets `record.rule` afterward to
|
|
182
|
+
* `${source}:${code}` purely for display. Recomputing from that display
|
|
183
|
+
* value would silently compute a DIFFERENT legacy id than the one actually
|
|
184
|
+
* checked here, permanently bifurcating the store: a suppression written
|
|
185
|
+
* this turn under the current id would never be found again next turn. */
|
|
186
|
+
function suppressionFor(cwd, id, args) {
|
|
187
|
+
const state = readSuppressionState(cwd);
|
|
188
|
+
const legacyId = legacyActionableWarningId({ cwd, ...args });
|
|
189
|
+
const entry = state.warnings?.[id] ??
|
|
190
|
+
(legacyId !== id ? state.warnings?.[legacyId] : undefined);
|
|
120
191
|
return {
|
|
121
192
|
suppressed: entry?.status === "suppressed",
|
|
122
193
|
reason: entry?.reason,
|
|
194
|
+
legacyId,
|
|
123
195
|
};
|
|
124
196
|
}
|
|
125
197
|
export function recordFromDispatchDiagnostic(diagnostic, cwd) {
|
|
126
|
-
if (diagnostic.semantic !== "warning"
|
|
198
|
+
if (diagnostic.semantic !== "warning")
|
|
199
|
+
return undefined;
|
|
200
|
+
// #1777: the old gate demanded `severity === "warning"` exactly, which was
|
|
201
|
+
// invisible while the ast-grep runner collapsed every non-error tier to
|
|
202
|
+
// "warning". Now that hint and info survive the dispatch path, admit them:
|
|
203
|
+
// a fix is a fix, and the tier governs how loudly a finding renders, not
|
|
204
|
+
// whether its fix is worth offering. `error` still routes to the blocking
|
|
205
|
+
// path, so it stays out even when a runner leaves `semantic` at "warning".
|
|
206
|
+
if (diagnostic.severity === "error")
|
|
127
207
|
return undefined;
|
|
128
208
|
if (!diagnostic.fixable && !diagnostic.fixSuggestion)
|
|
129
209
|
return undefined;
|
|
130
210
|
const filePath = path.resolve(cwd, diagnostic.filePath);
|
|
131
|
-
const
|
|
132
|
-
cwd,
|
|
211
|
+
const identityArgs = {
|
|
133
212
|
filePath,
|
|
134
213
|
tool: diagnostic.tool,
|
|
135
214
|
code: diagnostic.code,
|
|
136
215
|
rule: diagnostic.rule,
|
|
137
216
|
message: diagnostic.message,
|
|
138
217
|
line: diagnostic.line,
|
|
139
|
-
}
|
|
140
|
-
const
|
|
218
|
+
};
|
|
219
|
+
const id = createActionableWarningId({ cwd, ...identityArgs });
|
|
220
|
+
const suppression = suppressionFor(cwd, id, identityArgs);
|
|
141
221
|
return {
|
|
142
222
|
id,
|
|
143
223
|
filePath,
|
|
144
224
|
displayPath: toRunnerDisplayPath(cwd, filePath),
|
|
145
225
|
line: diagnostic.line,
|
|
146
226
|
column: diagnostic.column,
|
|
147
|
-
severity:
|
|
227
|
+
severity: diagnostic.severity,
|
|
148
228
|
tool: diagnostic.tool,
|
|
149
229
|
code: diagnostic.code,
|
|
150
230
|
rule: diagnostic.rule,
|
|
@@ -155,6 +235,7 @@ export function recordFromDispatchDiagnostic(diagnostic, cwd) {
|
|
|
155
235
|
actions: [],
|
|
156
236
|
suppressed: suppression.suppressed,
|
|
157
237
|
suppressionReason: suppression.reason,
|
|
238
|
+
legacyId: suppression.legacyId,
|
|
158
239
|
origin: "dispatch",
|
|
159
240
|
};
|
|
160
241
|
}
|
|
@@ -170,16 +251,16 @@ function recordFromLspDiagnostic(diag, filePath, cwd) {
|
|
|
170
251
|
const column = diag.range.start.character + 1;
|
|
171
252
|
const source = diag.source ?? "lsp";
|
|
172
253
|
const code = diag.code === undefined ? undefined : String(diag.code);
|
|
173
|
-
const
|
|
174
|
-
cwd,
|
|
254
|
+
const identityArgs = {
|
|
175
255
|
filePath,
|
|
176
256
|
tool: "lsp",
|
|
177
257
|
source,
|
|
178
258
|
code,
|
|
179
259
|
message: diag.message,
|
|
180
260
|
line,
|
|
181
|
-
}
|
|
182
|
-
const
|
|
261
|
+
};
|
|
262
|
+
const id = createActionableWarningId({ cwd, ...identityArgs });
|
|
263
|
+
const suppression = suppressionFor(cwd, id, identityArgs);
|
|
183
264
|
return {
|
|
184
265
|
id,
|
|
185
266
|
filePath,
|
|
@@ -195,6 +276,7 @@ function recordFromLspDiagnostic(diag, filePath, cwd) {
|
|
|
195
276
|
actions: [],
|
|
196
277
|
suppressed: suppression.suppressed,
|
|
197
278
|
suppressionReason: suppression.reason,
|
|
279
|
+
legacyId: suppression.legacyId,
|
|
198
280
|
origin: "lsp",
|
|
199
281
|
};
|
|
200
282
|
}
|
|
@@ -329,8 +411,13 @@ export async function buildActionableWarningsReport(args) {
|
|
|
329
411
|
}
|
|
330
412
|
const merged = mergeWarnings(records);
|
|
331
413
|
updateWarningState(cwd, merged);
|
|
414
|
+
// legacyId is #1816 migration bookkeeping for updateWarningState above —
|
|
415
|
+
// strip it before the report leaves this function, so it never lands in
|
|
416
|
+
// the `.pi-lens/cache/actionable-warnings.json` cache file or any
|
|
417
|
+
// agent-facing rendering of a warning record.
|
|
418
|
+
const reportWarnings = merged.map(({ legacyId: _legacyId, ...rest }) => rest);
|
|
332
419
|
const byFile = new Map();
|
|
333
|
-
for (const warning of
|
|
420
|
+
for (const warning of reportWarnings) {
|
|
334
421
|
const arr = byFile.get(warning.filePath) ?? [];
|
|
335
422
|
arr.push(warning);
|
|
336
423
|
byFile.set(warning.filePath, arr);
|
|
@@ -342,9 +429,16 @@ export async function buildActionableWarningsReport(args) {
|
|
|
342
429
|
warnings,
|
|
343
430
|
}));
|
|
344
431
|
const allActions = merged.flatMap((warning) => warning.actions);
|
|
432
|
+
const unsuppressed = merged.filter((warning) => !warning.suppressed);
|
|
433
|
+
const countTier = (tier) => unsuppressed.filter((warning) => warning.severity === tier).length;
|
|
345
434
|
const summary = {
|
|
346
435
|
warnings: merged.length,
|
|
347
|
-
unsuppressed:
|
|
436
|
+
unsuppressed: unsuppressed.length,
|
|
437
|
+
byTier: {
|
|
438
|
+
warning: countTier("warning"),
|
|
439
|
+
info: countTier("info"),
|
|
440
|
+
hint: countTier("hint"),
|
|
441
|
+
},
|
|
348
442
|
suppressed: merged.filter((warning) => warning.suppressed).length,
|
|
349
443
|
files: files.length,
|
|
350
444
|
actions: allActions.length,
|
|
@@ -586,8 +680,17 @@ export function formatActionableWarningsAdvisory(report) {
|
|
|
586
680
|
const safe = report.summary.autoFixEligible > 0
|
|
587
681
|
? ` ${report.summary.autoFixEligible} appear to have conservative preferred quickfixes.`
|
|
588
682
|
: "";
|
|
683
|
+
// #1777: hint and info are style opinions, so say how much of the count is
|
|
684
|
+
// opinion. The line appears only when a quiet tier is actually present —
|
|
685
|
+
// an all-warning turn already says everything in the count above.
|
|
686
|
+
const byTier = report.summary.byTier;
|
|
687
|
+
const quiet = byTier ? byTier.hint + byTier.info : 0;
|
|
688
|
+
const tierLine = quiet > 0
|
|
689
|
+
? `${quiet} of those are hint/info tier — style opinions, worth fixing only while you are already in that code.`
|
|
690
|
+
: undefined;
|
|
589
691
|
return [
|
|
590
692
|
`🟡 Fixable warnings introduced this turn: ${report.summary.unsuppressed}.${safe}`,
|
|
693
|
+
tierLine,
|
|
591
694
|
`Details written to .pi-lens/cache/actionable-warnings.json`,
|
|
592
695
|
fileList ? `Files:\n${fileList}${more}` : undefined,
|
|
593
696
|
"If continuing in these files, read that JSON and resolve warnings that are safe and relevant. Do not apply broad refactors unless requested.",
|
|
@@ -4,6 +4,7 @@ import * as path from "node:path";
|
|
|
4
4
|
import { logLatency } from "./latency-logger.js";
|
|
5
5
|
import { normalizeMapKey, toProjectRelativePath } from "./path-utils.js";
|
|
6
6
|
import { resolveRunnerPath } from "./dispatch/runner-context.js";
|
|
7
|
+
import { MTIME_DRIFT_TOLERANCE_MS } from "./blocker-freshness.js";
|
|
7
8
|
export const MAX_ADVISORY_AFFECTED_FILES = 256;
|
|
8
9
|
export function advisoryPathKey(filePath, cwd) {
|
|
9
10
|
return normalizeMapKey(path.resolve(cwd, filePath));
|
|
@@ -199,9 +200,34 @@ export function validateAdvisoryProvenance(record, cwd, runtime) {
|
|
|
199
200
|
export const MAX_FINDING_PATH_STATS = MAX_ADVISORY_AFFECTED_FILES;
|
|
200
201
|
/** How many dead paths a single drop record names before it stops. */
|
|
201
202
|
const MAX_LOGGED_DEAD_PATHS = 3;
|
|
202
|
-
|
|
203
|
+
/**
|
|
204
|
+
* One stat, one verdict. With `scannedAtMs` supplied, a surviving file whose
|
|
205
|
+
* mtime is newer than the scan reports `stale`.
|
|
206
|
+
*
|
|
207
|
+
* The boundary is `mtime > scannedAt + MTIME_DRIFT_TOLERANCE_MS`: a file
|
|
208
|
+
* scanned AT `scannedAt`, or written within the tolerance window of the scan
|
|
209
|
+
* timestamp being captured, reads live; genuinely past it reads stale.
|
|
210
|
+
*
|
|
211
|
+
* #1708: a bare +1ms tolerance — the convention `reconcileProjectDiagnosticsSnapshot`
|
|
212
|
+
* (clients/project-diagnostics/cache.ts:98) still uses, and the one this gate
|
|
213
|
+
* originally cited-but-withheld ("over-demoting costs a line number,
|
|
214
|
+
* under-demoting replays a false coordinate — the cheaper error wins the
|
|
215
|
+
* tie") — was not enough: on Windows a file's mtime can LEAD the immediately
|
|
216
|
+
* following `Date.now()` read by up to ~11.4ms, the same host skew
|
|
217
|
+
* `MTIME_DRIFT_TOLERANCE_MS` (`blocker-freshness.ts`) was raised to 50ms for
|
|
218
|
+
* (#1491/#1498). At +1ms this gate still demoted a real STOP-blocker (a
|
|
219
|
+
* trivy secret, #1628) to ACTION NEEDED, flaking
|
|
220
|
+
* tests/clients/runtime-turn-secrets-disposition.test.ts. Reusing the shared
|
|
221
|
+
* constant — rather than a second hand-tuned number — keeps one source of
|
|
222
|
+
* truth for the measured skew.
|
|
223
|
+
*/
|
|
224
|
+
export function findingPathFreshness(resolvedPath, scannedAtMs) {
|
|
203
225
|
try {
|
|
204
|
-
fs.statSync(resolvedPath);
|
|
226
|
+
const stat = fs.statSync(resolvedPath);
|
|
227
|
+
if (scannedAtMs !== undefined &&
|
|
228
|
+
stat.mtimeMs > scannedAtMs + MTIME_DRIFT_TOLERANCE_MS) {
|
|
229
|
+
return "stale";
|
|
230
|
+
}
|
|
205
231
|
return "live";
|
|
206
232
|
}
|
|
207
233
|
catch (error) {
|
|
@@ -211,14 +237,37 @@ export function findingPathExistence(resolvedPath) {
|
|
|
211
237
|
return code === "ENOENT" || code === "ENOTDIR" ? "missing" : "unknown";
|
|
212
238
|
}
|
|
213
239
|
}
|
|
240
|
+
/** Existence-only probe. Retained for callers that have no scan timestamp. */
|
|
241
|
+
export function findingPathExistence(resolvedPath) {
|
|
242
|
+
return findingPathFreshness(resolvedPath);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Parse a scan timestamp into epoch ms, or `undefined` when it cannot be
|
|
246
|
+
* trusted. `undefined` disables the staleness arm — never demote on a bad
|
|
247
|
+
* clock.
|
|
248
|
+
*/
|
|
249
|
+
export function parseScannedAtMs(scannedAt) {
|
|
250
|
+
if (scannedAt === undefined || scannedAt === null || scannedAt === "") {
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
const ms = typeof scannedAt === "number" ? scannedAt : Date.parse(scannedAt);
|
|
254
|
+
return Number.isFinite(ms) ? ms : undefined;
|
|
255
|
+
}
|
|
214
256
|
/**
|
|
215
|
-
* Partition findings into `{live, dropped}` by
|
|
216
|
-
*
|
|
217
|
-
* the partition rules are unit-testable without a filesystem.
|
|
257
|
+
* Partition findings into `{live, stale, dropped}` by what the path each one
|
|
258
|
+
* names looks like now. Pure apart from the `fs.statSync` probe, which is
|
|
259
|
+
* injectable so the partition rules are unit-testable without a filesystem.
|
|
260
|
+
*
|
|
261
|
+
* The stat memo lives in local `Map`s built fresh on every call — per delivery,
|
|
262
|
+
* never per process. A module-level cache here would be the process-lifetime
|
|
263
|
+
* latch shape: it would pin the first verdict for a path and re-serve it for
|
|
264
|
+
* every later turn, which is the very defect this gate exists to close.
|
|
218
265
|
*/
|
|
219
266
|
export function partitionFindingsByCitedPath(args) {
|
|
220
267
|
const limit = args.maxUniquePaths ?? MAX_FINDING_PATH_STATS;
|
|
221
|
-
const probe = args.existence ??
|
|
268
|
+
const probe = args.existence ?? findingPathFreshness;
|
|
269
|
+
const scannedAtMs = parseScannedAtMs(args.scannedAt);
|
|
270
|
+
const onMissing = args.onMissing ?? "drop";
|
|
222
271
|
// Two dedup layers, cheapest first. `rawVerdicts` collapses findings that
|
|
223
272
|
// cite the IDENTICAL string with zero filesystem work — the dominant case
|
|
224
273
|
// (#1460's live record: 126 findings over a handful of distinct `file`
|
|
@@ -232,8 +281,10 @@ export function partitionFindingsByCitedPath(args) {
|
|
|
232
281
|
const rawVerdicts = new Map();
|
|
233
282
|
const verdicts = new Map();
|
|
234
283
|
const live = [];
|
|
284
|
+
const stale = [];
|
|
235
285
|
const dropped = [];
|
|
236
286
|
const deadPaths = [];
|
|
287
|
+
const stalePaths = [];
|
|
237
288
|
let truncated = false;
|
|
238
289
|
for (const finding of args.findings) {
|
|
239
290
|
const cited = args.citedPath(finding);
|
|
@@ -261,20 +312,38 @@ export function partitionFindingsByCitedPath(args) {
|
|
|
261
312
|
verdict = "live";
|
|
262
313
|
}
|
|
263
314
|
else {
|
|
264
|
-
verdict = probe(resolved);
|
|
315
|
+
verdict = probe(resolved, scannedAtMs);
|
|
316
|
+
// A store that treats deletion as evidence-loss rather than
|
|
317
|
+
// remediation-loss folds `missing` into the demote arm here, so
|
|
318
|
+
// every downstream list and record sees one consistent verdict.
|
|
319
|
+
if (verdict === "missing" && onMissing === "demote") {
|
|
320
|
+
verdict = "stale";
|
|
321
|
+
}
|
|
265
322
|
verdicts.set(key, verdict);
|
|
266
323
|
if (verdict === "missing")
|
|
267
324
|
deadPaths.push(resolved);
|
|
325
|
+
else if (verdict === "stale")
|
|
326
|
+
stalePaths.push(resolved);
|
|
268
327
|
}
|
|
269
328
|
}
|
|
270
329
|
rawVerdicts.set(cited, verdict);
|
|
271
330
|
}
|
|
272
331
|
if (verdict === "missing")
|
|
273
332
|
dropped.push(finding);
|
|
333
|
+
else if (verdict === "stale")
|
|
334
|
+
stale.push(finding);
|
|
274
335
|
else
|
|
275
336
|
live.push(finding);
|
|
276
337
|
}
|
|
277
|
-
return {
|
|
338
|
+
return {
|
|
339
|
+
live,
|
|
340
|
+
stale,
|
|
341
|
+
dropped,
|
|
342
|
+
deadPaths,
|
|
343
|
+
stalePaths,
|
|
344
|
+
statCount: verdicts.size,
|
|
345
|
+
truncated,
|
|
346
|
+
};
|
|
278
347
|
}
|
|
279
348
|
/**
|
|
280
349
|
* Delivery-seam wrapper: partition, then emit one bounded `finding_dead_path_drop`
|
|
@@ -285,27 +354,71 @@ export function partitionFindingsByCitedPath(args) {
|
|
|
285
354
|
* store per delivery, with a capped path sample; never one per finding.
|
|
286
355
|
*/
|
|
287
356
|
export function dropFindingsForMissingPaths(args) {
|
|
357
|
+
// No `scannedAt` — existence-only, so `partition.stale` is always empty.
|
|
358
|
+
const partition = partitionFindingsByCitedPath(args);
|
|
359
|
+
emitDeadPathDropRecord(args.store, args.cwd, partition);
|
|
360
|
+
return partition.live;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Delivery-seam wrapper with the #1622 freshness verdict. Emits at most one
|
|
364
|
+
* `finding_dead_path_drop` and one `finding_stale_line_demote` record per store
|
|
365
|
+
* per delivery, each with a capped path sample; never one per finding.
|
|
366
|
+
*
|
|
367
|
+
* Callers that have no scan timestamp keep using `dropFindingsForMissingPaths`.
|
|
368
|
+
*/
|
|
369
|
+
export function gateFindingsByPathFreshness(args) {
|
|
288
370
|
const partition = partitionFindingsByCitedPath(args);
|
|
371
|
+
emitDeadPathDropRecord(args.store, args.cwd, partition);
|
|
372
|
+
emitStaleLineDemoteRecord(args.store, args.cwd, args.scannedAt, partition);
|
|
373
|
+
return { live: partition.live, stale: partition.stale };
|
|
374
|
+
}
|
|
375
|
+
function emitDeadPathDropRecord(store, cwd, partition) {
|
|
289
376
|
if (partition.dropped.length === 0)
|
|
290
|
-
return
|
|
377
|
+
return;
|
|
291
378
|
logLatency({
|
|
292
379
|
type: "phase",
|
|
293
380
|
phase: "finding_dead_path_drop",
|
|
294
|
-
filePath:
|
|
381
|
+
filePath: cwd,
|
|
295
382
|
durationMs: 0,
|
|
296
383
|
metadata: {
|
|
297
|
-
store
|
|
384
|
+
store,
|
|
298
385
|
droppedDeadPaths: partition.dropped.length,
|
|
299
386
|
deadPathCount: partition.deadPaths.length,
|
|
300
387
|
deliveredCount: partition.live.length,
|
|
301
388
|
statCount: partition.statCount,
|
|
302
389
|
samplePaths: partition.deadPaths
|
|
303
390
|
.slice(0, MAX_LOGGED_DEAD_PATHS)
|
|
304
|
-
.map((deadPath) => toProjectRelativePath(deadPath,
|
|
391
|
+
.map((deadPath) => toProjectRelativePath(deadPath, cwd)),
|
|
392
|
+
...(partition.truncated ? { truncated: true } : {}),
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* #1622 criterion 5: the demote path gets the same record shape as the drop
|
|
398
|
+
* path. Before this, a stale-line replay window was reconstructible only by
|
|
399
|
+
* proxy — the blocker payload is written nowhere.
|
|
400
|
+
*/
|
|
401
|
+
function emitStaleLineDemoteRecord(store, cwd, scannedAt, partition) {
|
|
402
|
+
if (partition.stale.length === 0)
|
|
403
|
+
return;
|
|
404
|
+
logLatency({
|
|
405
|
+
type: "phase",
|
|
406
|
+
phase: "finding_stale_line_demote",
|
|
407
|
+
filePath: cwd,
|
|
408
|
+
durationMs: 0,
|
|
409
|
+
metadata: {
|
|
410
|
+
store,
|
|
411
|
+
demotedStalePaths: partition.stale.length,
|
|
412
|
+
stalePathCount: partition.stalePaths.length,
|
|
413
|
+
deliveredCount: partition.live.length,
|
|
414
|
+
statCount: partition.statCount,
|
|
415
|
+
samplePaths: partition.stalePaths
|
|
416
|
+
.slice(0, MAX_LOGGED_DEAD_PATHS)
|
|
417
|
+
.map((stalePath) => toProjectRelativePath(stalePath, cwd)),
|
|
418
|
+
...(scannedAt === undefined ? {} : { scannedAt: String(scannedAt) }),
|
|
305
419
|
...(partition.truncated ? { truncated: true } : {}),
|
|
306
420
|
},
|
|
307
421
|
});
|
|
308
|
-
return partition.live;
|
|
309
422
|
}
|
|
310
423
|
export function provenanceStamp(provenance) {
|
|
311
424
|
if (!isWellFormed(provenance))
|