@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
|
@@ -9,10 +9,7 @@ import {
|
|
|
9
9
|
BorderedLoader,
|
|
10
10
|
type ExtensionAPI,
|
|
11
11
|
type ExtensionCommandContext,
|
|
12
|
-
type KeybindingsManager,
|
|
13
|
-
type Theme,
|
|
14
12
|
} from "@earendil-works/pi-coding-agent";
|
|
15
|
-
import type { Component, TUI } from "@earendil-works/pi-tui";
|
|
16
13
|
import type { MenuContext, RunMenuResult } from "@narumitw/pi-tui-kit";
|
|
17
14
|
import {
|
|
18
15
|
type BtwBringToMainSegment,
|
|
@@ -26,11 +23,13 @@ import {
|
|
|
26
23
|
summarizeBringToMain,
|
|
27
24
|
} from "./bring-to-main.js";
|
|
28
25
|
import { type RunBtwFullscreen, runBtwFullscreen } from "./fullscreen-ui.js";
|
|
26
|
+
import { pickMainEntry } from "./main-tree-picker.js";
|
|
29
27
|
import {
|
|
30
28
|
type BtwCommandMenuResult,
|
|
31
29
|
type BtwResumeThreadSummary,
|
|
32
30
|
runBtwMenuPreservingEditor,
|
|
33
31
|
showBtwCommandMenu,
|
|
32
|
+
showBtwCustomPreservingEditor,
|
|
34
33
|
} from "./menu.js";
|
|
35
34
|
import {
|
|
36
35
|
type BtwSettings,
|
|
@@ -222,6 +221,7 @@ export interface BtwExtensionDependencies {
|
|
|
222
221
|
ctx: ExtensionCommandContext,
|
|
223
222
|
resumeThreads: readonly BtwResumeThreadSummary[],
|
|
224
223
|
) => Promise<BtwCommandMenuResult>;
|
|
224
|
+
pickMainEntry?: typeof pickMainEntry;
|
|
225
225
|
loadSettings?: typeof loadSettingsForCommand;
|
|
226
226
|
resolveModel?: typeof resolveBtwModelWithLoader;
|
|
227
227
|
runThread?: typeof runBtwThread;
|
|
@@ -230,6 +230,7 @@ export interface BtwExtensionDependencies {
|
|
|
230
230
|
|
|
231
231
|
export default function btw(pi: ExtensionAPI, dependencies: BtwExtensionDependencies = {}) {
|
|
232
232
|
const showCommandMenu = dependencies.showCommandMenu ?? showCommandMenuForBtw;
|
|
233
|
+
const pickEntry = dependencies.pickMainEntry ?? pickMainEntry;
|
|
233
234
|
const loadSettings = dependencies.loadSettings ?? loadSettingsForCommand;
|
|
234
235
|
const resolveModel = dependencies.resolveModel ?? resolveBtwModelWithLoader;
|
|
235
236
|
const runThread = dependencies.runThread ?? runBtwThread;
|
|
@@ -259,9 +260,37 @@ export default function btw(pi: ExtensionAPI, dependencies: BtwExtensionDependen
|
|
|
259
260
|
}
|
|
260
261
|
|
|
261
262
|
let menuResult: BtwCommandMenuResult = "start";
|
|
263
|
+
let selectedConversationContext: string | undefined;
|
|
262
264
|
if (!question) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
+
while (true) {
|
|
266
|
+
menuResult = await showCommandMenu(pi, ctx, listResumeThreads());
|
|
267
|
+
if (menuResult === "closed") return;
|
|
268
|
+
if (menuResult !== "tree") break;
|
|
269
|
+
|
|
270
|
+
const treeResult = await pickEntry(pi, ctx);
|
|
271
|
+
if (treeResult.kind === "closed") return;
|
|
272
|
+
if (treeResult.kind === "back") continue;
|
|
273
|
+
try {
|
|
274
|
+
if (!ctx.sessionManager.getEntry(treeResult.entryId)) {
|
|
275
|
+
notifySafely(ctx, "The selected main-thread entry is no longer available", "warning");
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const branch = ctx.sessionManager.getBranch(treeResult.entryId);
|
|
279
|
+
if (branch.at(-1)?.id !== treeResult.entryId) {
|
|
280
|
+
notifySafely(
|
|
281
|
+
ctx,
|
|
282
|
+
"The selected main-thread branch is no longer available",
|
|
283
|
+
"warning",
|
|
284
|
+
);
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
selectedConversationContext = buildConversationContext(branch);
|
|
288
|
+
menuResult = "start";
|
|
289
|
+
break;
|
|
290
|
+
} catch {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
265
294
|
}
|
|
266
295
|
|
|
267
296
|
const settings = await loadSettings(ctx);
|
|
@@ -291,7 +320,8 @@ export default function btw(pi: ExtensionAPI, dependencies: BtwExtensionDependen
|
|
|
291
320
|
state = {
|
|
292
321
|
id: `btw-${nextThreadNumber}`,
|
|
293
322
|
thread: createSideThread(
|
|
294
|
-
|
|
323
|
+
selectedConversationContext ??
|
|
324
|
+
buildConversationContext(fullscreenCtx.sessionManager.getBranch()),
|
|
295
325
|
),
|
|
296
326
|
thinkingLevel: settings.thinkingLevel ?? pi.getThinkingLevel(),
|
|
297
327
|
createdAt,
|
|
@@ -546,40 +576,6 @@ export async function runBtwThread({
|
|
|
546
576
|
}
|
|
547
577
|
}
|
|
548
578
|
|
|
549
|
-
type BtwCustomFactory<T> = (
|
|
550
|
-
tui: TUI,
|
|
551
|
-
theme: Theme,
|
|
552
|
-
keybindings: KeybindingsManager,
|
|
553
|
-
done: (result: T) => void,
|
|
554
|
-
) => Component;
|
|
555
|
-
|
|
556
|
-
async function showBtwCustomPreservingEditor<T>(
|
|
557
|
-
ctx: ExtensionCommandContext,
|
|
558
|
-
factory: BtwCustomFactory<T>,
|
|
559
|
-
): Promise<T | undefined> {
|
|
560
|
-
let liveEditorText = ctx.ui.getEditorText();
|
|
561
|
-
let completed = false;
|
|
562
|
-
const result = await ctx.ui.custom<T>((tui, theme, keybindings, done) =>
|
|
563
|
-
factory(tui, theme, keybindings, (value) => {
|
|
564
|
-
try {
|
|
565
|
-
liveEditorText = ctx.ui.getEditorText();
|
|
566
|
-
} catch {
|
|
567
|
-
// Keep completion finite if session replacement invalidates the editor context.
|
|
568
|
-
}
|
|
569
|
-
completed = true;
|
|
570
|
-
done(value);
|
|
571
|
-
}),
|
|
572
|
-
);
|
|
573
|
-
if (completed) {
|
|
574
|
-
try {
|
|
575
|
-
if (ctx.ui.getEditorText() !== liveEditorText) ctx.ui.setEditorText(liveEditorText);
|
|
576
|
-
} catch {
|
|
577
|
-
// A replaced context owns a different editor and must not receive stale restoration.
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
return result;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
579
|
interface ChooseBringToMainDependencies {
|
|
584
580
|
showMenu?: typeof showBtwMenu;
|
|
585
581
|
showPreview?: typeof showBringToMainPreview;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import {
|
|
3
|
+
copyToClipboard as copyToHostClipboard,
|
|
4
|
+
type ExtensionCommandContext,
|
|
5
|
+
type KeybindingsManager,
|
|
6
|
+
type Theme,
|
|
6
7
|
} from "@earendil-works/pi-coding-agent";
|
|
7
8
|
import {
|
|
8
9
|
type Component,
|
|
@@ -21,13 +22,21 @@ type BtwCustomFactory<T> = (
|
|
|
21
22
|
done: (result: T) => void,
|
|
22
23
|
) => (Component & { dispose?(): void }) | Promise<Component & { dispose?(): void }>;
|
|
23
24
|
|
|
24
|
-
type BtwFullscreenTui = TUI & {
|
|
25
|
+
type BtwFullscreenTui = TUI & {
|
|
26
|
+
flash?: (message: string, durationMs?: number) => void;
|
|
27
|
+
setLayoutRoot(component: Component | undefined): void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export interface BtwFullscreenLayoutComponent extends Component {
|
|
31
|
+
getFullscreenLayout(): Component;
|
|
32
|
+
}
|
|
25
33
|
|
|
26
|
-
export type BtwFullscreenTuiFactory = (parent: TUI) => BtwFullscreenTui;
|
|
34
|
+
export type BtwFullscreenTuiFactory = (parent: TUI, theme: Theme) => BtwFullscreenTui;
|
|
27
35
|
|
|
28
36
|
export interface BtwFullscreenDependencies {
|
|
29
37
|
createTui?: BtwFullscreenTuiFactory;
|
|
30
38
|
openUrl?: (url: string) => void;
|
|
39
|
+
copyToClipboard?: (text: string) => Promise<void>;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
export type RunBtwFullscreen = <T>(
|
|
@@ -51,7 +60,13 @@ export async function runBtwFullscreen<T>(
|
|
|
51
60
|
): Promise<T> {
|
|
52
61
|
const createTui =
|
|
53
62
|
dependencies.createTui ??
|
|
54
|
-
((parent: TUI
|
|
63
|
+
((parent: TUI, theme: Theme) =>
|
|
64
|
+
createBtwFullscreenTui(
|
|
65
|
+
parent,
|
|
66
|
+
theme,
|
|
67
|
+
dependencies.openUrl ?? openUrlInBrowser,
|
|
68
|
+
dependencies.copyToClipboard ?? copyToHostClipboard,
|
|
69
|
+
));
|
|
55
70
|
let liveEditorText = ctx.ui.getEditorText();
|
|
56
71
|
let restoreEditor = false;
|
|
57
72
|
const outcome = await ctx.ui.custom<FullscreenOutcome<T>>(
|
|
@@ -85,10 +100,27 @@ export async function runBtwFullscreen<T>(
|
|
|
85
100
|
return outcome.value;
|
|
86
101
|
}
|
|
87
102
|
|
|
88
|
-
function createBtwFullscreenTui(
|
|
103
|
+
function createBtwFullscreenTui(
|
|
104
|
+
parent: TUI,
|
|
105
|
+
theme: Theme,
|
|
106
|
+
openUrl: (url: string) => void,
|
|
107
|
+
copyToClipboard: (text: string) => Promise<void>,
|
|
108
|
+
): BtwFullscreenTui {
|
|
109
|
+
const styleSearchMatch = (text: string) =>
|
|
110
|
+
theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
|
|
89
111
|
return new TuiAltScreen(parent.terminal, parent.getShowHardwareCursor(), undefined, {
|
|
90
112
|
mouse: true,
|
|
113
|
+
searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
|
|
114
|
+
searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
|
|
91
115
|
openUrl,
|
|
116
|
+
copySelection: async (text) => {
|
|
117
|
+
try {
|
|
118
|
+
await copyToClipboard(text);
|
|
119
|
+
return true;
|
|
120
|
+
} catch {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
92
124
|
});
|
|
93
125
|
}
|
|
94
126
|
|
|
@@ -147,7 +179,7 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
147
179
|
this.parent.stop({ preserveScreen: true });
|
|
148
180
|
parentStopped = true;
|
|
149
181
|
if (this.disposed) throw new FullscreenUiDisposedError();
|
|
150
|
-
this.fullscreen = this.createTui(this.parent);
|
|
182
|
+
this.fullscreen = this.createTui(this.parent, this.theme);
|
|
151
183
|
fullscreenCreated = true;
|
|
152
184
|
this.fullscreen.start();
|
|
153
185
|
outcome = { kind: "completed", value: await this.run(this.createContext()) };
|
|
@@ -223,6 +255,7 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
223
255
|
let component: (Component & { dispose?(): void }) | undefined;
|
|
224
256
|
let overlay: OverlayHandle | undefined;
|
|
225
257
|
let mounted = false;
|
|
258
|
+
let layoutMounted = false;
|
|
226
259
|
let factorySettled = false;
|
|
227
260
|
let closed = false;
|
|
228
261
|
let promiseSettled = false;
|
|
@@ -243,6 +276,7 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
243
276
|
let cleanupError: unknown;
|
|
244
277
|
try {
|
|
245
278
|
if (overlay) overlay.hide();
|
|
279
|
+
else if (mounted && layoutMounted) fullscreen.setLayoutRoot(undefined);
|
|
246
280
|
else if (mounted && component) fullscreen.removeChild(component);
|
|
247
281
|
} catch (error) {
|
|
248
282
|
cleanupError = error;
|
|
@@ -327,8 +361,13 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
327
361
|
options.onHandle?.(overlay);
|
|
328
362
|
} else {
|
|
329
363
|
fullscreen.clear();
|
|
330
|
-
fullscreen.addChild(component);
|
|
331
364
|
mounted = true;
|
|
365
|
+
if (isFullscreenLayoutComponent(component)) {
|
|
366
|
+
layoutMounted = true;
|
|
367
|
+
fullscreen.setLayoutRoot(component.getFullscreenLayout());
|
|
368
|
+
} else {
|
|
369
|
+
fullscreen.addChild(component);
|
|
370
|
+
}
|
|
332
371
|
fullscreen.setFocus(component);
|
|
333
372
|
fullscreen.requestRender();
|
|
334
373
|
}
|
|
@@ -337,3 +376,9 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
337
376
|
});
|
|
338
377
|
}
|
|
339
378
|
}
|
|
379
|
+
|
|
380
|
+
function isFullscreenLayoutComponent(
|
|
381
|
+
component: Component,
|
|
382
|
+
): component is BtwFullscreenLayoutComponent {
|
|
383
|
+
return "getFullscreenLayout" in component && typeof component.getFullscreenLayout === "function";
|
|
384
|
+
}
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import {
|
|
2
|
+
copyToClipboard,
|
|
3
|
+
type ExtensionAPI,
|
|
4
|
+
type ExtensionCommandContext,
|
|
5
|
+
type SessionEntry,
|
|
6
|
+
type SessionTreeNode,
|
|
7
|
+
TreeSelectorComponent,
|
|
8
|
+
} from "@earendil-works/pi-coding-agent";
|
|
9
|
+
import { type Component, type Focusable, Key, matchesKey } from "@earendil-works/pi-tui";
|
|
10
|
+
import { showBtwCustomPreservingEditor } from "./menu.js";
|
|
11
|
+
import { sanitizeSingleLine } from "./text.js";
|
|
12
|
+
|
|
13
|
+
export type MainEntryPickResult =
|
|
14
|
+
| { kind: "selected"; entryId: string }
|
|
15
|
+
| { kind: "back" }
|
|
16
|
+
| { kind: "closed" };
|
|
17
|
+
|
|
18
|
+
export interface MainThreadTreeSelector extends Component, Focusable {
|
|
19
|
+
onCopy?: (text: string | undefined) => void;
|
|
20
|
+
setViewLabel?(entryId: string, label: string | undefined, labelTimestamp?: string): void;
|
|
21
|
+
dispose?(): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface MainThreadTreeSelectorOptions {
|
|
25
|
+
tree: SessionTreeNode[];
|
|
26
|
+
currentLeafId: string | null;
|
|
27
|
+
terminalRows: number;
|
|
28
|
+
onSelect: (entryId: string) => void;
|
|
29
|
+
onCancel: () => void;
|
|
30
|
+
onCopy: (entryId: string | undefined, displayText: string | undefined) => void;
|
|
31
|
+
onLabelChange: (entryId: string, label: string | undefined) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface MainThreadTreePickerDependencies {
|
|
35
|
+
createSelector?: (options: MainThreadTreeSelectorOptions) => MainThreadTreeSelector;
|
|
36
|
+
copyToClipboard?: (text: string, signal: AbortSignal) => Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class MainThreadTreePickerComponent implements Component, Focusable {
|
|
40
|
+
constructor(
|
|
41
|
+
private readonly selector: MainThreadTreeSelector,
|
|
42
|
+
private readonly onClose: () => void,
|
|
43
|
+
) {}
|
|
44
|
+
|
|
45
|
+
get focused(): boolean {
|
|
46
|
+
return this.selector.focused;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
set focused(value: boolean) {
|
|
50
|
+
this.selector.focused = value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get wantsKeyRelease(): boolean | undefined {
|
|
54
|
+
return this.selector.wantsKeyRelease;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
render(width: number): string[] {
|
|
58
|
+
return this.selector.render(width);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
handleInput(data: string): void {
|
|
62
|
+
if (matchesKey(data, Key.ctrl("c"))) {
|
|
63
|
+
this.onClose();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this.selector.handleInput?.(data);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
invalidate(): void {
|
|
70
|
+
this.selector.invalidate();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
dispose(): void {
|
|
74
|
+
this.selector.dispose?.();
|
|
75
|
+
this.onClose();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function pickMainEntry(
|
|
80
|
+
pi: ExtensionAPI,
|
|
81
|
+
ctx: ExtensionCommandContext,
|
|
82
|
+
dependencies: MainThreadTreePickerDependencies = {},
|
|
83
|
+
): Promise<MainEntryPickResult> {
|
|
84
|
+
let rawTree: SessionTreeNode[];
|
|
85
|
+
let currentLeafId: string | null;
|
|
86
|
+
try {
|
|
87
|
+
rawTree = ctx.sessionManager.getTree();
|
|
88
|
+
currentLeafId = ctx.sessionManager.getLeafId();
|
|
89
|
+
} catch {
|
|
90
|
+
return { kind: "closed" };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (rawTree.length === 0) {
|
|
94
|
+
notifySafely(ctx, "No main-thread entries are available", "warning");
|
|
95
|
+
return { kind: "back" };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const tree = sanitizeTreeForDisplay(rawTree);
|
|
99
|
+
const rawCopyText = collectRawCopyText(rawTree);
|
|
100
|
+
const savedLabels = collectSavedLabels(rawTree);
|
|
101
|
+
const createSelector = dependencies.createSelector ?? createNativeTreeSelector;
|
|
102
|
+
const copy = dependencies.copyToClipboard ?? copyText;
|
|
103
|
+
const copyControllers = new Set<AbortController>();
|
|
104
|
+
const copyTasks = new Set<Promise<void>>();
|
|
105
|
+
const abortCopies = () => {
|
|
106
|
+
for (const controller of copyControllers) {
|
|
107
|
+
controller.abort(new Error("The main-thread tree picker closed"));
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const result = await showBtwCustomPreservingEditor<MainEntryPickResult>(
|
|
111
|
+
ctx,
|
|
112
|
+
(tui, _theme, _keybindings, done) => {
|
|
113
|
+
let settled = false;
|
|
114
|
+
let selector: MainThreadTreeSelector | undefined;
|
|
115
|
+
const finish = (value: MainEntryPickResult) => {
|
|
116
|
+
if (settled) return;
|
|
117
|
+
settled = true;
|
|
118
|
+
abortCopies();
|
|
119
|
+
done(value);
|
|
120
|
+
};
|
|
121
|
+
const onCopy = (entryId: string | undefined, displayText: string | undefined) => {
|
|
122
|
+
if (settled) return;
|
|
123
|
+
const text = entryId ? rawCopyText.get(entryId) : displayText;
|
|
124
|
+
if (!text) {
|
|
125
|
+
notifySafely(ctx, "Selected entry has no text to copy", "warning");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const controller = new AbortController();
|
|
129
|
+
copyControllers.add(controller);
|
|
130
|
+
let operation: Promise<void>;
|
|
131
|
+
try {
|
|
132
|
+
operation = copy(text, controller.signal);
|
|
133
|
+
} catch (error: unknown) {
|
|
134
|
+
operation = Promise.reject(error);
|
|
135
|
+
}
|
|
136
|
+
let task!: Promise<void>;
|
|
137
|
+
task = operation
|
|
138
|
+
.then(() => {
|
|
139
|
+
if (!settled) notifySafely(ctx, "Copied selected message", "info");
|
|
140
|
+
})
|
|
141
|
+
.catch((error: unknown) => {
|
|
142
|
+
if (!settled && !controller.signal.aborted) {
|
|
143
|
+
notifySafely(ctx, `Could not copy selected message: ${formatError(error)}`, "error");
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
.finally(() => {
|
|
147
|
+
copyControllers.delete(controller);
|
|
148
|
+
copyTasks.delete(task);
|
|
149
|
+
});
|
|
150
|
+
copyTasks.add(task);
|
|
151
|
+
};
|
|
152
|
+
const restoreLabel = (entryId: string) => {
|
|
153
|
+
const previous = savedLabels.get(entryId);
|
|
154
|
+
selector?.setViewLabel?.(entryId, previous?.label, previous?.labelTimestamp);
|
|
155
|
+
tui.requestRender();
|
|
156
|
+
};
|
|
157
|
+
const onLabelChange = (entryId: string, label: string | undefined) => {
|
|
158
|
+
if (settled) return;
|
|
159
|
+
try {
|
|
160
|
+
if (!ctx.sessionManager.getEntry(entryId)) {
|
|
161
|
+
restoreLabel(entryId);
|
|
162
|
+
notifySafely(ctx, "The selected main-thread entry is no longer available", "warning");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const persistedLabel = label === undefined ? undefined : sanitizeSingleLine(label);
|
|
166
|
+
pi.setLabel(entryId, persistedLabel);
|
|
167
|
+
savedLabels.set(entryId, { label: persistedLabel });
|
|
168
|
+
selector?.setViewLabel?.(entryId, persistedLabel);
|
|
169
|
+
tui.requestRender();
|
|
170
|
+
} catch (error: unknown) {
|
|
171
|
+
restoreLabel(entryId);
|
|
172
|
+
notifySafely(ctx, `Could not update tree label: ${formatError(error)}`, "error");
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
selector = createSelector({
|
|
176
|
+
tree,
|
|
177
|
+
currentLeafId,
|
|
178
|
+
terminalRows: tui.terminal.rows,
|
|
179
|
+
onSelect: (entryId) => finish({ kind: "selected", entryId }),
|
|
180
|
+
onCancel: () => finish({ kind: "back" }),
|
|
181
|
+
onCopy,
|
|
182
|
+
onLabelChange,
|
|
183
|
+
});
|
|
184
|
+
return new MainThreadTreePickerComponent(selector, () => finish({ kind: "closed" }));
|
|
185
|
+
},
|
|
186
|
+
);
|
|
187
|
+
abortCopies();
|
|
188
|
+
await Promise.allSettled([...copyTasks]);
|
|
189
|
+
|
|
190
|
+
return result ?? { kind: "closed" };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function createNativeTreeSelector(options: MainThreadTreeSelectorOptions): MainThreadTreeSelector {
|
|
194
|
+
const selector = new TreeSelectorComponent(
|
|
195
|
+
options.tree,
|
|
196
|
+
options.currentLeafId,
|
|
197
|
+
options.terminalRows,
|
|
198
|
+
options.onSelect,
|
|
199
|
+
options.onCancel,
|
|
200
|
+
options.onLabelChange,
|
|
201
|
+
);
|
|
202
|
+
selector.onCopy = (displayText) =>
|
|
203
|
+
options.onCopy(selector.getTreeList().getSelectedNode()?.entry.id, displayText);
|
|
204
|
+
const result = selector as MainThreadTreeSelector;
|
|
205
|
+
result.setViewLabel = (entryId, label, labelTimestamp) =>
|
|
206
|
+
selector.getTreeList().updateNodeLabel(entryId, label, labelTimestamp);
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function sanitizeTreeForDisplay(tree: readonly SessionTreeNode[]): SessionTreeNode[] {
|
|
211
|
+
return tree.map((node) => {
|
|
212
|
+
const result: SessionTreeNode = {
|
|
213
|
+
entry: sanitizeEntryForDisplay(node.entry),
|
|
214
|
+
children: sanitizeTreeForDisplay(node.children),
|
|
215
|
+
};
|
|
216
|
+
if (node.label !== undefined) result.label = sanitizeSingleLine(node.label);
|
|
217
|
+
if (node.labelTimestamp !== undefined) {
|
|
218
|
+
result.labelTimestamp = sanitizeSingleLine(node.labelTimestamp);
|
|
219
|
+
}
|
|
220
|
+
return result;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function sanitizeEntryForDisplay(entry: SessionEntry): SessionEntry {
|
|
225
|
+
switch (entry.type) {
|
|
226
|
+
case "message": {
|
|
227
|
+
const message = { ...entry.message } as Record<string, unknown>;
|
|
228
|
+
if ("content" in entry.message)
|
|
229
|
+
message.content = sanitizeDisplayContent(entry.message.content);
|
|
230
|
+
for (const key of ["role", "errorMessage", "command", "toolName"] as const) {
|
|
231
|
+
const value = message[key];
|
|
232
|
+
if (typeof value === "string") message[key] = sanitizeSingleLine(value);
|
|
233
|
+
}
|
|
234
|
+
return { ...entry, message } as unknown as SessionEntry;
|
|
235
|
+
}
|
|
236
|
+
case "custom_message":
|
|
237
|
+
return {
|
|
238
|
+
...entry,
|
|
239
|
+
customType: sanitizeSingleLine(entry.customType),
|
|
240
|
+
content: sanitizeDisplayContent(entry.content) as typeof entry.content,
|
|
241
|
+
};
|
|
242
|
+
case "compaction":
|
|
243
|
+
return { ...entry, summary: sanitizeSingleLine(entry.summary) };
|
|
244
|
+
case "branch_summary":
|
|
245
|
+
return { ...entry, summary: sanitizeSingleLine(entry.summary) };
|
|
246
|
+
case "model_change":
|
|
247
|
+
return {
|
|
248
|
+
...entry,
|
|
249
|
+
provider: sanitizeSingleLine(entry.provider),
|
|
250
|
+
modelId: sanitizeSingleLine(entry.modelId),
|
|
251
|
+
};
|
|
252
|
+
case "thinking_level_change":
|
|
253
|
+
return { ...entry, thinkingLevel: sanitizeSingleLine(entry.thinkingLevel) };
|
|
254
|
+
case "custom":
|
|
255
|
+
return { ...entry, customType: sanitizeSingleLine(entry.customType) };
|
|
256
|
+
case "label":
|
|
257
|
+
return {
|
|
258
|
+
...entry,
|
|
259
|
+
label: entry.label === undefined ? undefined : sanitizeSingleLine(entry.label),
|
|
260
|
+
};
|
|
261
|
+
case "session_info":
|
|
262
|
+
return {
|
|
263
|
+
...entry,
|
|
264
|
+
name: entry.name === undefined ? undefined : sanitizeSingleLine(entry.name),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function sanitizeDisplayContent(content: unknown): unknown {
|
|
270
|
+
if (typeof content === "string") return sanitizeSingleLine(content);
|
|
271
|
+
if (!Array.isArray(content)) return content;
|
|
272
|
+
return content.map((block) => {
|
|
273
|
+
if (block === null || typeof block !== "object" || !("type" in block)) return block;
|
|
274
|
+
if (block.type === "text" && "text" in block && typeof block.text === "string") {
|
|
275
|
+
return { ...block, text: sanitizeSingleLine(block.text) };
|
|
276
|
+
}
|
|
277
|
+
if (block.type === "toolCall") {
|
|
278
|
+
const copy = { ...block } as Record<string, unknown>;
|
|
279
|
+
if (typeof copy.name === "string") copy.name = sanitizeSingleLine(copy.name);
|
|
280
|
+
copy.arguments = sanitizeToolArguments(copy.arguments, new WeakMap());
|
|
281
|
+
return copy;
|
|
282
|
+
}
|
|
283
|
+
return block;
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function sanitizeToolArguments(value: unknown, seen: WeakMap<object, unknown>): unknown {
|
|
288
|
+
if (typeof value === "string") return sanitizeSingleLine(value);
|
|
289
|
+
if (value === null || typeof value !== "object") return value;
|
|
290
|
+
const existing = seen.get(value);
|
|
291
|
+
if (existing !== undefined) return existing;
|
|
292
|
+
if (Array.isArray(value)) {
|
|
293
|
+
const result: unknown[] = [];
|
|
294
|
+
seen.set(value, result);
|
|
295
|
+
for (const item of value) result.push(sanitizeToolArguments(item, seen));
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
const result: Record<string, unknown> = {};
|
|
299
|
+
seen.set(value, result);
|
|
300
|
+
for (const [key, item] of Object.entries(value)) {
|
|
301
|
+
result[key] = sanitizeToolArguments(item, seen);
|
|
302
|
+
}
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function collectRawCopyText(tree: readonly SessionTreeNode[]): Map<string, string> {
|
|
307
|
+
const result = new Map<string, string>();
|
|
308
|
+
const visit = (nodes: readonly SessionTreeNode[]) => {
|
|
309
|
+
for (const node of nodes) {
|
|
310
|
+
const text = getRawCopyText(node.entry);
|
|
311
|
+
if (text !== undefined) result.set(node.entry.id, text);
|
|
312
|
+
visit(node.children);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
visit(tree);
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function getRawCopyText(entry: SessionEntry): string | undefined {
|
|
320
|
+
let text: string | undefined;
|
|
321
|
+
if (entry.type === "message") {
|
|
322
|
+
if (entry.message.role === "bashExecution") text = entry.message.command;
|
|
323
|
+
else if ("content" in entry.message) {
|
|
324
|
+
text = extractRawText(entry.message.content);
|
|
325
|
+
if (!text && entry.message.role === "assistant") text = entry.message.errorMessage;
|
|
326
|
+
}
|
|
327
|
+
} else if (entry.type === "custom_message") text = extractRawText(entry.content);
|
|
328
|
+
else if (entry.type === "compaction" || entry.type === "branch_summary") text = entry.summary;
|
|
329
|
+
return text?.trim() ? text : undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function extractRawText(content: unknown): string {
|
|
333
|
+
if (typeof content === "string") return content;
|
|
334
|
+
if (!Array.isArray(content)) return "";
|
|
335
|
+
return content
|
|
336
|
+
.filter(
|
|
337
|
+
(block): block is { type: "text"; text: string } =>
|
|
338
|
+
block !== null &&
|
|
339
|
+
typeof block === "object" &&
|
|
340
|
+
"type" in block &&
|
|
341
|
+
block.type === "text" &&
|
|
342
|
+
"text" in block &&
|
|
343
|
+
typeof block.text === "string",
|
|
344
|
+
)
|
|
345
|
+
.map((block) => block.text)
|
|
346
|
+
.join("");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
interface SavedLabel {
|
|
350
|
+
label: string | undefined;
|
|
351
|
+
labelTimestamp?: string;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function collectSavedLabels(tree: readonly SessionTreeNode[]): Map<string, SavedLabel> {
|
|
355
|
+
const result = new Map<string, SavedLabel>();
|
|
356
|
+
const visit = (nodes: readonly SessionTreeNode[]) => {
|
|
357
|
+
for (const node of nodes) {
|
|
358
|
+
result.set(node.entry.id, {
|
|
359
|
+
label: node.label === undefined ? undefined : sanitizeSingleLine(node.label),
|
|
360
|
+
labelTimestamp:
|
|
361
|
+
node.labelTimestamp === undefined ? undefined : sanitizeSingleLine(node.labelTimestamp),
|
|
362
|
+
});
|
|
363
|
+
visit(node.children);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
visit(tree);
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async function copyText(text: string, signal: AbortSignal): Promise<void> {
|
|
371
|
+
signal.throwIfAborted();
|
|
372
|
+
await copyToClipboard(text);
|
|
373
|
+
signal.throwIfAborted();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function notifySafely(
|
|
377
|
+
ctx: ExtensionCommandContext,
|
|
378
|
+
message: string,
|
|
379
|
+
level: Parameters<ExtensionCommandContext["ui"]["notify"]>[1],
|
|
380
|
+
): void {
|
|
381
|
+
try {
|
|
382
|
+
ctx.ui.notify(sanitizeSingleLine(message), level);
|
|
383
|
+
} catch {
|
|
384
|
+
// The command context may have been replaced while the selector was open.
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function formatError(error: unknown): string {
|
|
389
|
+
return error instanceof Error ? error.message : String(error);
|
|
390
|
+
}
|