@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
|
@@ -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) {
|
|
@@ -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) {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
import * as fs from "node:fs";
|
|
17
17
|
import * as os from "node:os";
|
|
18
18
|
import * as path from "node:path";
|
|
19
|
-
import {
|
|
19
|
+
import { classifyProbeFailure, createAvailabilityLatch, isTransientDecision, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
|
|
20
|
+
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
20
21
|
/**
|
|
21
22
|
* Fallback preference when nothing is declared (or the declared manager is
|
|
22
23
|
* missing). npm first for maximum compatibility; bun last. A project lockfile
|
|
@@ -72,18 +73,142 @@ function readPackageManagerField(targetPath) {
|
|
|
72
73
|
// ============================================================================
|
|
73
74
|
// AVAILABILITY (cached per process; reset in tests)
|
|
74
75
|
// ============================================================================
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Budget for the `where`/`which <pm>` probe. Matches the previous
|
|
78
|
+
* `isCommandAvailableAsync` default so the observable timing is unchanged.
|
|
79
|
+
*/
|
|
80
|
+
const PROBE_TIMEOUT_MS = 5_000;
|
|
81
|
+
/**
|
|
82
|
+
* A timed-out probe used to be memoized as a permanent `false` — the process
|
|
83
|
+
* would silently downgrade a declared pnpm/yarn manager to npm for its whole
|
|
84
|
+
* life. The blast radius was internal: this resolver only serves installs
|
|
85
|
+
* into pi-lens's own managed tools directory and `pilens_rebuild` on a
|
|
86
|
+
* pi-lens source checkout, never a user project's lockfile (#1496). Each
|
|
87
|
+
* manager now sits behind the shared transient-aware latch (#1467/#1476):
|
|
88
|
+
* only a genuine absence (`where`/`which` exits non-zero, or ENOENT) latches;
|
|
89
|
+
* a timeout, abort or host stall expires on a cooldown and is re-probed.
|
|
90
|
+
*/
|
|
91
|
+
const availabilityLatches = new Map();
|
|
92
|
+
const inFlightProbes = new Map();
|
|
93
|
+
function getLatch(pm) {
|
|
94
|
+
let latch = availabilityLatches.get(pm);
|
|
95
|
+
if (!latch) {
|
|
96
|
+
latch = createAvailabilityLatch();
|
|
97
|
+
availabilityLatches.set(pm, latch);
|
|
98
|
+
}
|
|
99
|
+
return latch;
|
|
100
|
+
}
|
|
101
|
+
async function probeAvailability(pm) {
|
|
102
|
+
const latch = getLatch(pm);
|
|
103
|
+
const startedAt = Date.now();
|
|
104
|
+
const finder = onWindows() ? "where" : "which";
|
|
105
|
+
const sampler = startHostStallSampler();
|
|
106
|
+
let result;
|
|
107
|
+
let hostStallMs;
|
|
108
|
+
try {
|
|
109
|
+
result = await safeSpawnAsync(finder, [pm], { timeout: PROBE_TIMEOUT_MS });
|
|
81
110
|
}
|
|
82
|
-
|
|
111
|
+
finally {
|
|
112
|
+
hostStallMs = sampler.stop();
|
|
113
|
+
}
|
|
114
|
+
const elapsedMs = Date.now() - startedAt;
|
|
115
|
+
if (result.status === 0 && !result.error) {
|
|
116
|
+
latch.noteAvailable();
|
|
117
|
+
logAvailabilityDecision({
|
|
118
|
+
tool: pm,
|
|
119
|
+
verdict: "available",
|
|
120
|
+
outcome: "success",
|
|
121
|
+
cause: "ok",
|
|
122
|
+
elapsedMs,
|
|
123
|
+
latched: true,
|
|
124
|
+
hostStallMs,
|
|
125
|
+
budgetMs: PROBE_TIMEOUT_MS,
|
|
126
|
+
});
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
const { outcome, cause, evidence } = classifyProbeFailure(result, {
|
|
130
|
+
hostStallMs,
|
|
131
|
+
// Preserve pre-#1496 meaning for anything the classifier can't place: a
|
|
132
|
+
// present manager that rejects its probe is durable, same as before.
|
|
133
|
+
unclassifiedFailureOutcome: "missing",
|
|
134
|
+
});
|
|
135
|
+
const retryAfterMs = latch.noteUnavailable(outcome, cause);
|
|
136
|
+
logAvailabilityDecision({
|
|
137
|
+
tool: pm,
|
|
138
|
+
verdict: "unavailable",
|
|
139
|
+
outcome,
|
|
140
|
+
cause,
|
|
141
|
+
elapsedMs,
|
|
142
|
+
latched: outcome !== "transient",
|
|
143
|
+
hostStallMs,
|
|
144
|
+
...(retryAfterMs > 0 && { retryAfterMs }),
|
|
145
|
+
budgetMs: PROBE_TIMEOUT_MS,
|
|
146
|
+
classifiedBy: "probe",
|
|
147
|
+
evidence,
|
|
148
|
+
});
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Resolve whether `pm` is available. `onTransient`, when given, fires
|
|
153
|
+
* whenever the resolved `false` came from a probe that never got a fair
|
|
154
|
+
* hearing (a stall, an abort, a host-level failure classified `transient` by
|
|
155
|
+
* `classifyProbeFailure`) rather than a genuine absence — including when that
|
|
156
|
+
* verdict is served from the latch's own cooldown-bounded memo, not just on a
|
|
157
|
+
* fresh probe (#1585).
|
|
158
|
+
*
|
|
159
|
+
* Before this, the return type was a bare boolean: a caller like
|
|
160
|
+
* `allAvailableGlobalBinDirs` could not tell "pnpm isn't installed" from
|
|
161
|
+
* "the `where pnpm` probe stalled", so a transient miss silently dropped
|
|
162
|
+
* pnpm's global bin dir with no way to flag the result as provisional —
|
|
163
|
+
* exactly the boolean-collapse `getToolPath`'s `onTransient` plumbing (#1569)
|
|
164
|
+
* was built to avoid, one layer up.
|
|
165
|
+
*/
|
|
166
|
+
function isAvailable(pm, onTransient) {
|
|
167
|
+
const latch = getLatch(pm);
|
|
168
|
+
const reportIfTransient = (result) => {
|
|
169
|
+
if (!result && isTransientDecision({ outcome: latch.getOutcome() })) {
|
|
170
|
+
onTransient?.();
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
};
|
|
174
|
+
const memo = latch.read();
|
|
175
|
+
if (memo !== null)
|
|
176
|
+
return Promise.resolve(reportIfTransient(memo));
|
|
177
|
+
// A verdict can now expire, so concurrent callers arriving just after a
|
|
178
|
+
// cooldown must share ONE probe rather than each spawning their own.
|
|
179
|
+
const inFlight = inFlightProbes.get(pm);
|
|
180
|
+
if (inFlight)
|
|
181
|
+
return inFlight.then(reportIfTransient);
|
|
182
|
+
// #1653 review F1: a probe started before a session reset can settle AFTER
|
|
183
|
+
// a later session's own probe for the same manager is already in flight.
|
|
184
|
+
// An unconditional delete-by-key would evict that NEWER entry out from
|
|
185
|
+
// under it, so a third caller in the gap finds nothing in-flight and
|
|
186
|
+
// spawns a duplicate. Only remove the entry if it is still THIS call's
|
|
187
|
+
// promise — the same identity guard `resolveMadge` uses in
|
|
188
|
+
// dependency-checker.ts for the equivalent race.
|
|
189
|
+
const probe = probeAvailability(pm).finally(() => {
|
|
190
|
+
if (inFlightProbes.get(pm) === probe)
|
|
191
|
+
inFlightProbes.delete(pm);
|
|
192
|
+
});
|
|
193
|
+
inFlightProbes.set(pm, probe);
|
|
194
|
+
return probe.then(reportIfTransient);
|
|
83
195
|
}
|
|
84
|
-
/**
|
|
196
|
+
/**
|
|
197
|
+
* Clear the process-wide availability cache: pnpm/yarn/bun/npm each sit
|
|
198
|
+
* behind their own module-local `AvailabilityLatch` in `availabilityLatches`,
|
|
199
|
+
* so `resetDispatchAvailabilityState`'s generation counter (the mechanism
|
|
200
|
+
* most dispatch runners inherit) never reaches them — the same shape as
|
|
201
|
+
* psscriptanalyzer's (#1490) and zizmor's (#1535) module-local latches.
|
|
202
|
+
* Without this wired into `session_start`, a genuine "pnpm is missing"
|
|
203
|
+
* verdict from one session stayed latched into the next: install pnpm mid
|
|
204
|
+
* day, start a fresh session, pi-lens still reports it missing until a
|
|
205
|
+
* process restart (#1653). Called from `handleSessionStart`'s per-session
|
|
206
|
+
* reset block beside `resetZizmorTokenAvailability()` /
|
|
207
|
+
* `resetPsScriptAnalyzerAvailability()`; also used directly by tests.
|
|
208
|
+
*/
|
|
85
209
|
export function _resetPackageManagerCache() {
|
|
86
|
-
|
|
210
|
+
availabilityLatches.clear();
|
|
211
|
+
inFlightProbes.clear();
|
|
87
212
|
}
|
|
88
213
|
// ============================================================================
|
|
89
214
|
// RESOLUTION
|
|
@@ -134,6 +259,24 @@ export function installArgs(pm, pkg, options = {}) {
|
|
|
134
259
|
args.push(pkg);
|
|
135
260
|
return args;
|
|
136
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Args to re-resolve an already-declared dependency to the newest version its
|
|
264
|
+
* recorded range still permits. npm/pnpm/bun spell this `update`; yarn classic
|
|
265
|
+
* spells it `upgrade`.
|
|
266
|
+
*
|
|
267
|
+
* This is the command that repairs a dependency frozen by its own lockfile.
|
|
268
|
+
* `installArgs` re-runs the install, and a lockfile entry that already
|
|
269
|
+
* satisfies the range makes that a no-op: pi-lens's managed tools tree stayed
|
|
270
|
+
* on the version written at first install for the life of the machine even
|
|
271
|
+
* though the declared caret range permitted 28 newer minors (#1730).
|
|
272
|
+
*/
|
|
273
|
+
export function updateArgs(pm, pkg, options = {}) {
|
|
274
|
+
const args = [pm === "yarn" ? "upgrade" : "update"];
|
|
275
|
+
if (options.ignoreScripts)
|
|
276
|
+
args.push("--ignore-scripts");
|
|
277
|
+
args.push(pkg);
|
|
278
|
+
return args;
|
|
279
|
+
}
|
|
137
280
|
/**
|
|
138
281
|
* Args to install a single package **globally** (`-g`). npm/pnpm/bun spell this
|
|
139
282
|
* `install -g` / `add -g`; yarn uses `global add` (yarn classic — Berry removed
|
|
@@ -204,12 +347,18 @@ async function globalBinDirsFor(pm) {
|
|
|
204
347
|
* Global bin directories for every installed manager, deduped. Used to locate a
|
|
205
348
|
* globally-installed tool binary when PATH is stale (e.g. right after an
|
|
206
349
|
* `install -g`) or when the tool was installed via a non-npm manager.
|
|
350
|
+
*
|
|
351
|
+
* `onTransient`, when given, fires once per manager whose `isAvailable` probe
|
|
352
|
+
* was transient (stalled/unspawnable, not a genuine absence) — the boolean
|
|
353
|
+
* `allAvailableGlobalBinDirs` returns can't itself distinguish "not
|
|
354
|
+
* installed" from "couldn't tell", so a caller that persists this result
|
|
355
|
+
* needs the callback to know the answer may be incomplete (#1585).
|
|
207
356
|
*/
|
|
208
|
-
export async function allAvailableGlobalBinDirs() {
|
|
357
|
+
export async function allAvailableGlobalBinDirs(onTransient) {
|
|
209
358
|
const dirs = [];
|
|
210
359
|
const seen = new Set();
|
|
211
360
|
for (const pm of PREFERENCE) {
|
|
212
|
-
if (!(await isAvailable(pm)))
|
|
361
|
+
if (!(await isAvailable(pm, onTransient)))
|
|
213
362
|
continue;
|
|
214
363
|
for (const dir of await globalBinDirsFor(pm)) {
|
|
215
364
|
const normalized = path.resolve(dir);
|
|
@@ -251,8 +400,15 @@ export async function findGlobalBinary(command, windowsExt = ".cmd") {
|
|
|
251
400
|
}
|
|
252
401
|
return undefined;
|
|
253
402
|
}
|
|
254
|
-
/**
|
|
255
|
-
|
|
403
|
+
/**
|
|
404
|
+
* Local `node_modules/.bin/<tool>` walking up from `startDir` to the fs root.
|
|
405
|
+
*
|
|
406
|
+
* Exported so callers that must NOT pay for global-bin discovery can reuse this
|
|
407
|
+
* walk instead of copying it. `findNodeToolBinary` below adds `findGlobalBinary`,
|
|
408
|
+
* which spawns a probe per package manager; a caller resolving a command on
|
|
409
|
+
* every run (knip, #1721) needs the filesystem half only.
|
|
410
|
+
*/
|
|
411
|
+
export function findLocalBinUpwards(tool, startDir, windowsExt = ".cmd") {
|
|
256
412
|
const names = onWindows() ? [`${tool}${windowsExt}`, tool] : [tool];
|
|
257
413
|
let dir = path.resolve(startDir);
|
|
258
414
|
const root = path.parse(dir).root;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { logLatency } from "./latency-logger.js";
|
|
2
|
+
// Verified guesses are expected host behavior, not degradations. Keep their
|
|
3
|
+
// exact session count in memory and publish one bounded row at session end.
|
|
4
|
+
// This tally is intentionally memory-only: a process crash loses its count.
|
|
5
|
+
// The rollup is best-effort session telemetry, not durable accounting.
|
|
6
|
+
let verifiedGuessCount = 0;
|
|
7
|
+
export function recordVerifiedPathAttributionGuess() {
|
|
8
|
+
verifiedGuessCount += 1;
|
|
9
|
+
}
|
|
10
|
+
export function getVerifiedPathAttributionGuessCount() {
|
|
11
|
+
return verifiedGuessCount;
|
|
12
|
+
}
|
|
13
|
+
export function emitVerifiedPathAttributionRollup(filePath) {
|
|
14
|
+
if (verifiedGuessCount === 0)
|
|
15
|
+
return;
|
|
16
|
+
logLatency({
|
|
17
|
+
type: "phase",
|
|
18
|
+
phase: "path_attribution_verified_rollup",
|
|
19
|
+
filePath,
|
|
20
|
+
durationMs: 0,
|
|
21
|
+
metadata: { count: verifiedGuessCount },
|
|
22
|
+
});
|
|
23
|
+
verifiedGuessCount = 0;
|
|
24
|
+
}
|
|
25
|
+
export function resetVerifiedPathAttributionGuessCount() {
|
|
26
|
+
verifiedGuessCount = 0;
|
|
27
|
+
}
|