@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,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1618: a full workspace sweep (`LSPService.runWorkspaceDiagnostics`, the
|
|
3
|
+
* engine behind `lens_diagnostics mode=full`) grants itself a wall-clock
|
|
4
|
+
* ceiling (`FULL_SCAN_WALL_CLOCK_MS`, see {@link getFullScanWallClockMs})
|
|
5
|
+
* that can outlive the detached LSP idle-reset timer armed by
|
|
6
|
+
* `clients/runtime-turn.ts`'s `handleTurnEnd` on a file-less turn. Nothing
|
|
7
|
+
* previously stopped that timer from firing mid-sweep and destroying the
|
|
8
|
+
* very service the sweep was actively touching — every file the sweep had
|
|
9
|
+
* not yet reached then failed with zero trace and got mislabeled as budget
|
|
10
|
+
* exhaustion.
|
|
11
|
+
*
|
|
12
|
+
* This module is the shared hold: `runWorkspaceDiagnostics` acquires it for
|
|
13
|
+
* its own lifetime (try/finally, so a throw or an aborted sweep still
|
|
14
|
+
* releases it) and the idle-reset timer consults it before firing. Holds are
|
|
15
|
+
* tracked as TOKENS (acquire-time-stamped), not a bare counter — two
|
|
16
|
+
* overlapping `lens_diagnostics mode=full` calls (or a sweep started while
|
|
17
|
+
* another is still finishing) must each be free to release independently
|
|
18
|
+
* without the other's hold being dropped early.
|
|
19
|
+
*
|
|
20
|
+
* A leaked hold that never re-arms idle reset is the inverse defect (state
|
|
21
|
+
* that must re-arm cannot hide behind a stuck guard), so this module screens
|
|
22
|
+
* against it on TWO axes, matching the repo's own session-boundary +
|
|
23
|
+
* bounded-lifetime conventions:
|
|
24
|
+
* 1. `clearWorkspaceSweepHoldForSessionStart` — `resetLSPService({reason:
|
|
25
|
+
* "session_start"})` clears every held token unconditionally. A session
|
|
26
|
+
* boundary means no in-flight sweep from a PRIOR generation is still
|
|
27
|
+
* meaningful to wait on.
|
|
28
|
+
* 2. `reapStaleHolds` (internal, called from every liveness check) —
|
|
29
|
+
* force-releases any token older than {@link getWorkspaceSweepMaxHoldAgeMs},
|
|
30
|
+
* derived from the sweep's own wall-clock ceiling plus a safety margin.
|
|
31
|
+
* A real sweep is either done or self-aborted well within that window
|
|
32
|
+
* (`tools/lens-diagnostics.ts` races `FULL_SCAN_WALL_CLOCK_MS`), so a
|
|
33
|
+
* token still held past it can only be a bug — never a legitimate long
|
|
34
|
+
* sweep — and gets force-released with its own distinct log record.
|
|
35
|
+
*
|
|
36
|
+
* `getFullScanWallClockMs` also lives here (rather than duplicated between
|
|
37
|
+
* `tools/lens-diagnostics.ts` and `clients/runtime-turn.ts`) so the idle-reset
|
|
38
|
+
* delay on every path (base AND the subagent/budget-shortened paths) can be
|
|
39
|
+
* DERIVED from the sweep's own ceiling instead of the constants drifting
|
|
40
|
+
* apart again (#1618 acceptance criterion 6).
|
|
41
|
+
*/
|
|
42
|
+
import { logLatency } from "../latency-logger.js";
|
|
43
|
+
/** Extra headroom beyond the sweep's own wall-clock ceiling before an
|
|
44
|
+
* idle-reset delay (or the max-hold-age failsafe) trusts that a real sweep
|
|
45
|
+
* must be done. Single source for both derivations below AND for every
|
|
46
|
+
* idle-reset path `clients/runtime-turn.ts` derives (base, subagent, and
|
|
47
|
+
* budget-shortened) — never a second independently-tunable literal. */
|
|
48
|
+
export const SWEEP_IDLE_SAFETY_MARGIN_MS = 60_000;
|
|
49
|
+
let holds = new Map(); // holdId -> acquiredAt (Date.now())
|
|
50
|
+
let nextHoldId = 1;
|
|
51
|
+
let idleWaiters = [];
|
|
52
|
+
function flushIdleWaitersIfEmpty() {
|
|
53
|
+
if (holds.size > 0)
|
|
54
|
+
return;
|
|
55
|
+
if (idleWaiters.length === 0)
|
|
56
|
+
return;
|
|
57
|
+
const waiters = idleWaiters;
|
|
58
|
+
idleWaiters = [];
|
|
59
|
+
for (const waiter of waiters) {
|
|
60
|
+
try {
|
|
61
|
+
waiter();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Best-effort — a waiter failure must never propagate into the
|
|
65
|
+
// releasing/reaping call stack.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A held token past this age can only be a bug (leaked release, a sweep that
|
|
71
|
+
* hung outside its own abort signal, …) — a legitimate sweep is done or
|
|
72
|
+
* self-aborted well before it. Derived, not a separate literal, so it can't
|
|
73
|
+
* drift out of sync with `getFullScanWallClockMs` the way the idle-reset
|
|
74
|
+
* timer and the sweep's own ceiling once did.
|
|
75
|
+
*/
|
|
76
|
+
export function getWorkspaceSweepMaxHoldAgeMs() {
|
|
77
|
+
return getFullScanWallClockMs() + SWEEP_IDLE_SAFETY_MARGIN_MS;
|
|
78
|
+
}
|
|
79
|
+
function reapStaleHolds() {
|
|
80
|
+
if (holds.size === 0)
|
|
81
|
+
return;
|
|
82
|
+
const maxAgeMs = getWorkspaceSweepMaxHoldAgeMs();
|
|
83
|
+
const now = Date.now();
|
|
84
|
+
for (const [holdId, acquiredAt] of holds) {
|
|
85
|
+
const ageMs = now - acquiredAt;
|
|
86
|
+
if (ageMs <= maxAgeMs)
|
|
87
|
+
continue;
|
|
88
|
+
holds.delete(holdId);
|
|
89
|
+
logLatency({
|
|
90
|
+
type: "phase",
|
|
91
|
+
phase: "lsp_workspace_sweep_hold_force_released",
|
|
92
|
+
filePath: "",
|
|
93
|
+
durationMs: ageMs,
|
|
94
|
+
metadata: { holdId, maxHoldAgeMs: maxAgeMs },
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
flushIdleWaitersIfEmpty();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Acquire the hold for one `runWorkspaceDiagnostics` call. Returns a release
|
|
101
|
+
* function; callers MUST call it from a `finally` block so an overlapping or
|
|
102
|
+
* throwing sweep still releases its hold. Idempotent — calling the returned
|
|
103
|
+
* function more than once past the first call, or after the hold was already
|
|
104
|
+
* force-released (session reset / max-age reap), is a no-op.
|
|
105
|
+
*/
|
|
106
|
+
export function acquireWorkspaceSweepHold() {
|
|
107
|
+
const holdId = nextHoldId++;
|
|
108
|
+
holds.set(holdId, Date.now());
|
|
109
|
+
return () => {
|
|
110
|
+
if (!holds.delete(holdId))
|
|
111
|
+
return;
|
|
112
|
+
flushIdleWaitersIfEmpty();
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** True while at least one `runWorkspaceDiagnostics` sweep is in flight.
|
|
116
|
+
* Reaps stale holds first, so a leaked/hung hold can't permanently read as
|
|
117
|
+
* "active" past its own max age. */
|
|
118
|
+
export function isWorkspaceSweepActive() {
|
|
119
|
+
reapStaleHolds();
|
|
120
|
+
return holds.size > 0;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Run `cb` once the hold count next returns to zero. Runs synchronously and
|
|
124
|
+
* immediately when no sweep is active right now — the common case, and the
|
|
125
|
+
* one every non-sweep idle-reset arm/fire takes.
|
|
126
|
+
*/
|
|
127
|
+
export function runWhenWorkspaceSweepIdle(cb) {
|
|
128
|
+
reapStaleHolds();
|
|
129
|
+
if (holds.size === 0) {
|
|
130
|
+
cb();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
idleWaiters.push(cb);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* `resetLSPService({reason: "session_start"})` calls this: a session
|
|
137
|
+
* boundary means no hold from a PRIOR generation is still meaningful to wait
|
|
138
|
+
* on, so every held token is cleared unconditionally (state that must
|
|
139
|
+
* re-arm at session_start cannot hide behind a leaked/stuck guard from the
|
|
140
|
+
* generation before it). Only logs when there was actually something to
|
|
141
|
+
* clear — the common case (no sweep in flight at session boundary) stays
|
|
142
|
+
* silent.
|
|
143
|
+
*/
|
|
144
|
+
export function clearWorkspaceSweepHoldForSessionStart() {
|
|
145
|
+
if (holds.size === 0)
|
|
146
|
+
return;
|
|
147
|
+
logLatency({
|
|
148
|
+
type: "phase",
|
|
149
|
+
phase: "lsp_workspace_sweep_hold_session_reset",
|
|
150
|
+
filePath: "",
|
|
151
|
+
durationMs: 0,
|
|
152
|
+
metadata: { clearedHolds: holds.size },
|
|
153
|
+
});
|
|
154
|
+
holds.clear();
|
|
155
|
+
flushIdleWaitersIfEmpty();
|
|
156
|
+
}
|
|
157
|
+
/** Test-only: reset the module-scope hold state between tests. */
|
|
158
|
+
export function _resetWorkspaceSweepHoldForTests() {
|
|
159
|
+
holds = new Map();
|
|
160
|
+
idleWaiters = [];
|
|
161
|
+
nextHoldId = 1;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Wall-clock ceiling for one `lens_diagnostics mode=full` scan
|
|
165
|
+
* (`runWorkspaceDiagnostics` plus its concurrent project-runner/analyzer
|
|
166
|
+
* work). Single source for `tools/lens-diagnostics.ts` (the sweep caller),
|
|
167
|
+
* `clients/runtime-turn.ts` (every idle-reset path, whose delay must outlive
|
|
168
|
+
* it), and this module's own max-hold-age failsafe — lazy env read, never
|
|
169
|
+
* memoized, matching this codebase's house style so a test can flip the
|
|
170
|
+
* override mid-run.
|
|
171
|
+
*/
|
|
172
|
+
export function getFullScanWallClockMs() {
|
|
173
|
+
const raw = Number(process.env.PI_LENS_LENS_DIAGNOSTICS_FULL_TIMEOUT_MS);
|
|
174
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 300_000; // 5 min default
|
|
175
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** Shared lazy LSP service seam (#1394). */
|
|
2
|
-
|
|
2
|
+
import { createLazyImport } from "./lazy-import.js";
|
|
3
|
+
const lazyLsp = createLazyImport(() => import("./lsp/index.js"));
|
|
3
4
|
export function warmLspService() {
|
|
4
|
-
return (
|
|
5
|
+
return lazyLsp.get();
|
|
5
6
|
}
|
|
6
7
|
export function loadLspService() {
|
|
7
8
|
return warmLspService();
|
|
@@ -32,7 +32,12 @@ import { handleTurnEnd } from "../runtime-turn.js";
|
|
|
32
32
|
import { createMcpHost } from "./host-shim.js";
|
|
33
33
|
let contextPromise;
|
|
34
34
|
let resolvedRuntime;
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Lazily build (once) the persistent session context shared across MCP
|
|
37
|
+
* calls. A rejected build (e.g. `loadBootstrapClients` hitting a transient
|
|
38
|
+
* fs error) must not poison every later MCP call for the rest of the
|
|
39
|
+
* process — evict the memo on rejection so the next call retries (#1570).
|
|
40
|
+
*/
|
|
36
41
|
export function getMcpSessionContext() {
|
|
37
42
|
contextPromise ??= (async () => {
|
|
38
43
|
const context = {
|
|
@@ -43,7 +48,10 @@ export function getMcpSessionContext() {
|
|
|
43
48
|
};
|
|
44
49
|
resolvedRuntime = context.runtime;
|
|
45
50
|
return context;
|
|
46
|
-
})()
|
|
51
|
+
})().catch((err) => {
|
|
52
|
+
contextPromise = undefined;
|
|
53
|
+
throw err;
|
|
54
|
+
});
|
|
47
55
|
return contextPromise;
|
|
48
56
|
}
|
|
49
57
|
/** Synchronous peek at the session runtime, for construction sites that cannot
|
|
@@ -162,6 +162,14 @@ async function extractFile(absPath, languageId, content) {
|
|
|
162
162
|
let callbacks = [];
|
|
163
163
|
let callbackError;
|
|
164
164
|
try {
|
|
165
|
+
// SAFETY: `ModuleReportNode` is this module's own structural
|
|
166
|
+
// subset of a tree-sitter node: `type`, `text`, `children`,
|
|
167
|
+
// `parent`, `isNamed`, and the two positions. A real node
|
|
168
|
+
// implements all of it, but the two types are declared in
|
|
169
|
+
// unrelated packages so TypeScript sees no relation. The cast
|
|
170
|
+
// asserts only that structural subset; widen
|
|
171
|
+
// `ModuleReportNode` past what tree-sitter nodes provide and
|
|
172
|
+
// it stops holding.
|
|
165
173
|
callbacks = extractCallbacks(tree.rootNode, owners, languageId, warnings);
|
|
166
174
|
}
|
|
167
175
|
catch (error) {
|
|
@@ -1330,10 +1338,19 @@ export async function moduleReport(file, cwd, options) {
|
|
|
1330
1338
|
let graph;
|
|
1331
1339
|
let graphFileCap;
|
|
1332
1340
|
let callGraphIdentity;
|
|
1341
|
+
// #1961 review F4: the blind read now serves a snapshot stamped at a
|
|
1342
|
+
// different commit. project_report says so in its trust notes; this report
|
|
1343
|
+
// reads through the SAME accessor, so without this it would present the
|
|
1344
|
+
// other branch's importers and call edges as current, under a builtAt that
|
|
1345
|
+
// looks fresh. Computed per call, never cached.
|
|
1346
|
+
let revisionDriftNote;
|
|
1333
1347
|
try {
|
|
1334
|
-
const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, } = await import("./review-graph/builder.js");
|
|
1348
|
+
const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, getReviewGraphRevisionDrift, formatReviewGraphRevisionDriftNote, } = await import("./review-graph/builder.js");
|
|
1335
1349
|
graph = getCachedReviewGraph(cwd);
|
|
1336
1350
|
graphFileCap = getReviewGraphSizeSkipVerdict(cwd)?.maxFileCount;
|
|
1351
|
+
const drift = graph ? getReviewGraphRevisionDrift(cwd) : undefined;
|
|
1352
|
+
if (drift)
|
|
1353
|
+
revisionDriftNote = formatReviewGraphRevisionDriftNote(drift);
|
|
1337
1354
|
callGraphIdentity = graph
|
|
1338
1355
|
? (() => {
|
|
1339
1356
|
const identity = getReviewGraphCacheIdentity(cwd, graph);
|
|
@@ -1366,6 +1383,10 @@ export async function moduleReport(file, cwd, options) {
|
|
|
1366
1383
|
const api = topLevel.filter((entry) => entry.exported);
|
|
1367
1384
|
const internal = topLevel.filter((entry) => !entry.exported);
|
|
1368
1385
|
const warnings = [...(extractionWarnings ?? [])];
|
|
1386
|
+
// Every graph-derived section below (who-uses-this, blast radius, call
|
|
1387
|
+
// edges) came from the served snapshot, so the caveat belongs with them.
|
|
1388
|
+
if (revisionDriftNote)
|
|
1389
|
+
warnings.push(revisionDriftNote);
|
|
1369
1390
|
if (callbackError) {
|
|
1370
1391
|
warnings.push(`Failed to extract callbacks: ${callbackError}`);
|
|
1371
1392
|
logLatency({
|
|
@@ -131,6 +131,35 @@ export function getRegisteredLogFiles() {
|
|
|
131
131
|
export function _resetRegisteredLogFilesForTest() {
|
|
132
132
|
registeredLogFiles.clear();
|
|
133
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Snapshot of unrecovered write losses, one entry per sink that has any
|
|
136
|
+
* (#1970). Pure in-memory read — no I/O, so a caller (`degradation-ledger.ts`)
|
|
137
|
+
* can fold this into a durable ledger entry without this module ever writing
|
|
138
|
+
* a record about its own failure through the sink that is failing. See
|
|
139
|
+
* `NdjsonWriterState.writeFailures` for the recursion-hazard rationale.
|
|
140
|
+
*/
|
|
141
|
+
export function getSinkWriteFailures() {
|
|
142
|
+
if (!ndjsonGlobalState)
|
|
143
|
+
return [];
|
|
144
|
+
const result = [];
|
|
145
|
+
for (const state of ndjsonGlobalState.writers.values()) {
|
|
146
|
+
if (state.writeFailures > 0) {
|
|
147
|
+
result.push({ file: state.file, droppedCount: state.writeFailures });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Session-boundary reset (catalog shape 17: a process-lifetime latch must
|
|
154
|
+
* re-arm at session_start). Wired into `resetDegradationLedger()` so both
|
|
155
|
+
* reset together; also used directly by tests.
|
|
156
|
+
*/
|
|
157
|
+
export function resetSinkWriteFailures() {
|
|
158
|
+
if (!ndjsonGlobalState)
|
|
159
|
+
return;
|
|
160
|
+
for (const state of ndjsonGlobalState.writers.values())
|
|
161
|
+
state.writeFailures = 0;
|
|
162
|
+
}
|
|
134
163
|
function requireCurrentGlobalState() {
|
|
135
164
|
if (ndjsonGlobalState)
|
|
136
165
|
return ndjsonGlobalState;
|
|
@@ -159,17 +188,46 @@ function assertCompatibleWriterOptions(existing, maxBytes, backupPath) {
|
|
|
159
188
|
throw new Error(`createNdjsonLogger: incompatible options for shared path ${existing.file}; ` +
|
|
160
189
|
`the first writer's maxBytes/backupPath must be reused`);
|
|
161
190
|
}
|
|
191
|
+
function writeQueueItemSync(state, item) {
|
|
192
|
+
if (item.kind === "truncate") {
|
|
193
|
+
fs.writeFileSync(state.file, "");
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
rotateIfNeeded(state);
|
|
197
|
+
fs.appendFileSync(state.file, item.line);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* The pi-analyze #15 shape (#1970): a write that throws — including the
|
|
202
|
+
* `ERR_STREAM_DESTROYED` a torn-down sink produces — gets one reopen-and-
|
|
203
|
+
* retry before it counts as a loss. There is no persistent handle to close
|
|
204
|
+
* here (every write already opens, writes, and closes in one call), so
|
|
205
|
+
* "reopen" means dropping the cached `ensuredDir` assumption and
|
|
206
|
+
* re-verifying the parent directory before the retry — the one piece of
|
|
207
|
+
* cross-write state this module holds that a destroyed sink could have
|
|
208
|
+
* invalidated. An unrecovered write is counted, never thrown or retried a
|
|
209
|
+
* second time (`applyQueueItemSync`, `applyQueueItemAsync`, and the batched
|
|
210
|
+
* write in `drainLoop` skip DIFFERENT amounts of work per queue item, so
|
|
211
|
+
* each keeps its own copy of this two-step shape rather than sharing one
|
|
212
|
+
* generic retry wrapper across sync/async).
|
|
213
|
+
*/
|
|
162
214
|
function applyQueueItemSync(state, item) {
|
|
163
215
|
ensureDirSync(state);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
216
|
+
try {
|
|
217
|
+
writeQueueItemSync(state, item);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
// fall through to the one reopen-and-retry
|
|
222
|
+
}
|
|
223
|
+
state.ensuredDir = false;
|
|
224
|
+
ensureDirSync(state);
|
|
225
|
+
try {
|
|
226
|
+
writeQueueItemSync(state, item);
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
state.writeFailures += 1;
|
|
230
|
+
}
|
|
173
231
|
}
|
|
174
232
|
function flushStateSync(state) {
|
|
175
233
|
// Drain the in-memory queue synchronously — safe at process exit.
|
|
@@ -208,6 +266,9 @@ function createWriterState(file, maxBytes, backupPath) {
|
|
|
208
266
|
assertCompatibleWriterOptions(existing, maxBytes, backupPath);
|
|
209
267
|
if (!exitFlushers.has(existing.exitFlusher))
|
|
210
268
|
registerWriter(existing);
|
|
269
|
+
// A state adopted from a pre-#1970 module graph predates this field.
|
|
270
|
+
if (typeof existing.writeFailures !== "number")
|
|
271
|
+
existing.writeFailures = 0;
|
|
211
272
|
return existing;
|
|
212
273
|
}
|
|
213
274
|
const state = {};
|
|
@@ -220,6 +281,7 @@ function createWriterState(file, maxBytes, backupPath) {
|
|
|
220
281
|
state.syncRepairItems = null;
|
|
221
282
|
state.ensuredDir = false;
|
|
222
283
|
state.exitFlusher = () => flushStateSync(state);
|
|
284
|
+
state.writeFailures = 0;
|
|
223
285
|
globalState.writers.set(file, state);
|
|
224
286
|
registerWriter(state);
|
|
225
287
|
return state;
|
|
@@ -258,23 +320,36 @@ function rotateIfNeeded(state) {
|
|
|
258
320
|
// no file yet, or rename raced — nothing to rotate
|
|
259
321
|
}
|
|
260
322
|
}
|
|
323
|
+
async function writeQueueItemAsync(state, item) {
|
|
324
|
+
if (item.kind === "truncate") {
|
|
325
|
+
await fs.promises.writeFile(state.file, "");
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
// Rotation is deliberately synchronous here. This function is only
|
|
329
|
+
// reached from the already-deferred drain, and keeping stat/rm/rename
|
|
330
|
+
// in one synchronous section prevents flushSync from racing a late
|
|
331
|
+
// async rename after it has written new data.
|
|
332
|
+
rotateIfNeeded(state);
|
|
333
|
+
await fs.promises.appendFile(state.file, item.line);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/** See `applyQueueItemSync`'s doc comment for the reopen-and-retry-once shape. */
|
|
261
337
|
async function applyQueueItemAsync(state, item) {
|
|
262
338
|
await ensureDirAsync(state);
|
|
263
339
|
try {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
// Rotation is deliberately synchronous here. This function is only
|
|
269
|
-
// reached from the already-deferred drain, and keeping stat/rm/rename
|
|
270
|
-
// in one synchronous section prevents flushSync from racing a late
|
|
271
|
-
// async rename after it has written new data.
|
|
272
|
-
rotateIfNeeded(state);
|
|
273
|
-
await fs.promises.appendFile(state.file, item.line);
|
|
274
|
-
}
|
|
340
|
+
await writeQueueItemAsync(state, item);
|
|
341
|
+
return;
|
|
275
342
|
}
|
|
276
343
|
catch {
|
|
277
|
-
//
|
|
344
|
+
// fall through to the one reopen-and-retry
|
|
345
|
+
}
|
|
346
|
+
state.ensuredDir = false;
|
|
347
|
+
await ensureDirAsync(state);
|
|
348
|
+
try {
|
|
349
|
+
await writeQueueItemAsync(state, item);
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
state.writeFailures += 1;
|
|
278
353
|
}
|
|
279
354
|
}
|
|
280
355
|
async function drainLoop(state) {
|
|
@@ -292,7 +367,7 @@ async function drainLoop(state) {
|
|
|
292
367
|
? [item]
|
|
293
368
|
: state.queue.slice(0, pendingEnd);
|
|
294
369
|
state.inFlightBatch = pending;
|
|
295
|
-
|
|
370
|
+
const writeBatch = async () => {
|
|
296
371
|
if (item.kind === "truncate") {
|
|
297
372
|
await fs.promises.writeFile(state.file, "");
|
|
298
373
|
}
|
|
@@ -305,9 +380,25 @@ async function drainLoop(state) {
|
|
|
305
380
|
.map((queued) => queued.line)
|
|
306
381
|
.join(""));
|
|
307
382
|
}
|
|
383
|
+
};
|
|
384
|
+
try {
|
|
385
|
+
await writeBatch();
|
|
308
386
|
}
|
|
309
387
|
catch {
|
|
310
|
-
//
|
|
388
|
+
// Reopen-and-retry once (#1970, pi-analyze #15 shape): a destroyed
|
|
389
|
+
// sink (ERR_STREAM_DESTROYED) or a directory that vanished mid-session
|
|
390
|
+
// gets exactly one recovery attempt before the batch counts as a loss.
|
|
391
|
+
state.ensuredDir = false;
|
|
392
|
+
await ensureDirAsync(state);
|
|
393
|
+
try {
|
|
394
|
+
await writeBatch();
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
// Unrecovered: count every line in this batch as dropped. Purely
|
|
398
|
+
// in-memory — see `writeFailures`'s doc comment for why this must
|
|
399
|
+
// never itself attempt a durable write through this same sink.
|
|
400
|
+
state.writeFailures += pending.length;
|
|
401
|
+
}
|
|
311
402
|
}
|
|
312
403
|
for (const written of pending) {
|
|
313
404
|
// flushSync may have drained this prefix while the append is in
|
|
@@ -42,13 +42,19 @@
|
|
|
42
42
|
* So `opengrepResultToProjectDiagnostics` deliberately applies NO suppression
|
|
43
43
|
* filtering of its own — doing so would be redundant at best.
|
|
44
44
|
*
|
|
45
|
-
*
|
|
45
|
+
* Scope note (#1562 class fix): the CLI scan is handed `--exclude` per shared
|
|
46
|
+
* scratch/cache tree (`scratch-tree-policy.ts`) so a directory that isn't
|
|
47
|
+
* gitignored (opengrep's own default exclusion) still doesn't reach the
|
|
48
|
+
* agent as a finding — same `EXCLUDED_DIRS`-derived list gitleaks/trivy use.
|
|
49
|
+
*
|
|
50
|
+
* Refs: #584, #111 (opengrep adoption), #387 (workspace-sweep serialization), #1562
|
|
46
51
|
*/
|
|
47
52
|
import * as fs from "node:fs";
|
|
48
53
|
import * as os from "node:os";
|
|
49
54
|
import * as path from "node:path";
|
|
50
55
|
import { mkdtempSync } from "node:fs";
|
|
51
56
|
import { resolveOpengrepConfig } from "./opengrep-config.js";
|
|
57
|
+
import { getScratchTreeDirNames } from "./scratch-tree-policy.js";
|
|
52
58
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
53
59
|
import { SecurityScanClient } from "./security-scan-client.js";
|
|
54
60
|
const EMPTY_RESULT = {
|
|
@@ -116,6 +122,14 @@ export class OpengrepClient extends SecurityScanClient {
|
|
|
116
122
|
"--no-error",
|
|
117
123
|
"--quiet",
|
|
118
124
|
"--disable-version-check",
|
|
125
|
+
// #1562 class fix: opengrep's own `.gitignore` respect covers the
|
|
126
|
+
// common case (scratch trees are usually gitignored), but not a
|
|
127
|
+
// scratch/cache tree that ISN'T (e.g. an un-gitignored worktree
|
|
128
|
+
// cache) — `--exclude` is semgrep-compatible, so a slash-free
|
|
129
|
+
// pattern matches that directory name anywhere in the tree,
|
|
130
|
+
// independent of gitignore. Same `EXCLUDED_DIRS`-derived list
|
|
131
|
+
// gitleaks/trivy use, so the three scanners can't drift apart.
|
|
132
|
+
...getScratchTreeDirNames().flatMap((name) => ["--exclude", name]),
|
|
119
133
|
cwd,
|
|
120
134
|
], { cwd, timeout: SCAN_TIMEOUT_MS });
|
|
121
135
|
if (result.error) {
|