@herbertgao/pi-extensions 2026.8.5 → 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 +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 +272 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
- 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 +16 -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 +22 -15
- 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 +34 -10
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -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 +8 -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 +119 -103
- 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 +13 -9
- package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
- 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 +78 -15
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/pi-lens/CHANGELOG.md +1004 -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 +427 -0
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
- 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 +176 -37
- 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/bus-events-logger.js +62 -6
- package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
- 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/console-guard-install.js +16 -4
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
- 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 +43 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
- 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 +81 -41
- 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 +488 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
- 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 +943 -93
- 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 +106 -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/disposition-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
- 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/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
- 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 +49 -21
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
- package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
- 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 +86 -15
- package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
- 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 +487 -9
- 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-events.js +56 -25
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
- 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 +517 -69
- 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 +172 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
- 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/analyze.js +4 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- 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 +178 -30
- 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 +85 -15
- 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/runner-findings.js +23 -2
- 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 +76 -4
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
- package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
- package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
- package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
- 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 +224 -8
- package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- 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/warm-attach.js +17 -0
- package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +64677 -49484
- package/node_modules/pi-lens/dist/mcp/server.js +8 -2
- package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +44 -14
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +33 -6
- package/node_modules/pi-lens/docs/globalconfig.md +14 -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 +25 -1
- package/node_modules/pi-lens/docs/usage.md +24 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -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 +22 -0
- 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-conditional-empty-object-spread-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
- 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 +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
- 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-laundering-test.yml +11 -0
- 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 +66 -0
- 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-conditional-empty-object-spread-js.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
- 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 +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
- 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 +32 -0
- 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 +132 -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 +12 -11
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
- package/node_modules/pi-mcp-adapter/README.md +51 -6
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/cli.js +188 -5
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +28 -10
- package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
- 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/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +4 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +145 -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-metadata.ts +38 -18
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +95 -10
- 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 +38 -14
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +8 -3
- package/node_modules/pi-web-access/curator-server.ts +5 -1
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +134 -37
- package/node_modules/pi-web-access/fetch-params.ts +17 -3
- package/node_modules/pi-web-access/firecrawl.ts +172 -12
- package/node_modules/pi-web-access/gemini-search.ts +33 -7
- package/node_modules/pi-web-access/index.ts +160 -104
- package/node_modules/pi-web-access/package.json +2 -2
- 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 +51 -23
- 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/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +13 -13
|
@@ -6,23 +6,151 @@
|
|
|
6
6
|
* stats-derived sum). cacheRead is excluded because each turn's cacheRead is
|
|
7
7
|
* the cumulative cached prefix re-read on that one call — summing across
|
|
8
8
|
* turns counts the prefix N times. See issue #38.
|
|
9
|
+
*
|
|
10
|
+
* That exclusion is about this *display* total, not about what was billed: the
|
|
11
|
+
* prefix really is re-read and re-charged on every call. So `cacheRead` is
|
|
12
|
+
* accumulated here anyway, kept out of `getLifetimeTotal` and used only where
|
|
13
|
+
* billing is the question — reporting to the parent session, whose own messages
|
|
14
|
+
* pi counts the same way (`addUsageToTotals`). Reporting 0 there would make a
|
|
15
|
+
* subagent's rows count differently from every other row in one total.
|
|
16
|
+
*
|
|
17
|
+
* `cost` is a plain sum for the same reason: it is what pi charged for that one
|
|
18
|
+
* message (`usage.cost.total`, priced from the model's rates), not a cumulative
|
|
19
|
+
* figure. Both are optional because a model with no pricing data reports no
|
|
20
|
+
* cost, and because every accumulator predates them; absent reads as 0.
|
|
9
21
|
*/
|
|
10
22
|
export type LifetimeUsage = {
|
|
11
23
|
input: number
|
|
12
24
|
output: number
|
|
13
25
|
cacheWrite: number
|
|
26
|
+
cacheRead?: number
|
|
27
|
+
cost?: number
|
|
14
28
|
}
|
|
15
29
|
|
|
16
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Sum of lifetime *token* components for DISPLAY, or 0 if undefined.
|
|
32
|
+
* Deliberately excludes `cacheRead` (see above) and `cost` — that is money, not
|
|
33
|
+
* tokens, and lives on the same object only because it accumulates on the same
|
|
34
|
+
* events.
|
|
35
|
+
*/
|
|
17
36
|
export function getLifetimeTotal(u?: LifetimeUsage): number {
|
|
18
37
|
return u ? u.input + u.output + u.cacheWrite : 0
|
|
19
38
|
}
|
|
20
39
|
|
|
40
|
+
/** Accumulated cost in USD, or 0 when unpriced/undefined. */
|
|
41
|
+
export function getLifetimeCost(u?: LifetimeUsage): number {
|
|
42
|
+
return u?.cost ?? 0
|
|
43
|
+
}
|
|
44
|
+
|
|
21
45
|
/** Add a usage delta into a target accumulator (mutates target). */
|
|
22
46
|
export function addUsage(into: LifetimeUsage, delta: LifetimeUsage): void {
|
|
23
47
|
into.input += delta.input
|
|
24
48
|
into.output += delta.output
|
|
25
49
|
into.cacheWrite += delta.cacheWrite
|
|
50
|
+
if (delta.cacheRead) into.cacheRead = (into.cacheRead ?? 0) + delta.cacheRead
|
|
51
|
+
if (delta.cost) into.cost = (into.cost ?? 0) + delta.cost
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* A pi `Usage`. Rebuilt here rather than imported so this module stays
|
|
56
|
+
* dependency-free for tests; the fields are pi's, and every one of them must be
|
|
57
|
+
* present: pi's `addUsageToTotals` dereferences `usage.cost.total` with no
|
|
58
|
+
* guard, so a partial object throws inside pi rather than at the call site.
|
|
59
|
+
*
|
|
60
|
+
* This is pi's convention for spend in anything handed to a consumer — every
|
|
61
|
+
* extension-facing payload that carries it takes the whole object
|
|
62
|
+
* (`ToolResultEvent`, `ToolResultEventResult`, `AssistantMessage`, …), never a
|
|
63
|
+
* flattened cost. Pi flattens only in computed read APIs it expects you to
|
|
64
|
+
* render, like `SessionStats`. So both places we hand usage to someone else —
|
|
65
|
+
* `AgentToolResult.usage` and the `subagents:completed` / `subagents:failed`
|
|
66
|
+
* events — carry this, and gain whatever pi adds to `Usage` for free.
|
|
67
|
+
*/
|
|
68
|
+
export type ReportedUsage = {
|
|
69
|
+
input: number
|
|
70
|
+
output: number
|
|
71
|
+
cacheRead: number
|
|
72
|
+
cacheWrite: number
|
|
73
|
+
totalTokens: number
|
|
74
|
+
cost: {
|
|
75
|
+
input: number
|
|
76
|
+
output: number
|
|
77
|
+
cacheRead: number
|
|
78
|
+
cacheWrite: number
|
|
79
|
+
total: number
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Render an accumulator as a pi `Usage`, or undefined when nothing was spent —
|
|
85
|
+
* callers attach nothing rather than a zero, so a consumer can tell "spent
|
|
86
|
+
* nothing" from "never ran".
|
|
87
|
+
*
|
|
88
|
+
* `cacheRead` IS included, unlike in `getLifetimeTotal`: pi sums it across a
|
|
89
|
+
* session's own assistant messages, and the prefix genuinely is re-read and
|
|
90
|
+
* re-billed on every call. Only `total` is populated on the cost breakdown; pi
|
|
91
|
+
* reads nothing else from it, and the per-kind split is not tracked.
|
|
92
|
+
*/
|
|
93
|
+
export function toReportedUsage(u: LifetimeUsage): ReportedUsage | undefined {
|
|
94
|
+
const { input, output, cacheWrite, cacheRead = 0, cost = 0 } = u
|
|
95
|
+
if (
|
|
96
|
+
input === 0 &&
|
|
97
|
+
output === 0 &&
|
|
98
|
+
cacheWrite === 0 &&
|
|
99
|
+
cacheRead === 0 &&
|
|
100
|
+
cost === 0
|
|
101
|
+
)
|
|
102
|
+
return undefined
|
|
103
|
+
return {
|
|
104
|
+
input,
|
|
105
|
+
output,
|
|
106
|
+
cacheRead,
|
|
107
|
+
cacheWrite,
|
|
108
|
+
totalTokens: input + output + cacheRead + cacheWrite,
|
|
109
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: cost },
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Subagent spend that the parent session has not been told about yet.
|
|
115
|
+
*
|
|
116
|
+
* Subagents run in their own pi sessions, so none of what they spend appears in
|
|
117
|
+
* the parent's `getSessionStats()`. Pi does aggregate `toolResult.usage` into
|
|
118
|
+
* those stats, though — so the way back into the parent's footer and `/cost` is
|
|
119
|
+
* to hang the spend on a tool result. Background and scheduled agents finish
|
|
120
|
+
* between tool calls with nothing to hang it on, hence a pool: every assistant
|
|
121
|
+
* message lands here as it happens, and the next tool result we return carries
|
|
122
|
+
* whatever has accumulated.
|
|
123
|
+
*
|
|
124
|
+
* Drain empties it, so each message is reported exactly once no matter how many
|
|
125
|
+
* results are returned or how many agents were running.
|
|
126
|
+
*/
|
|
127
|
+
export class PendingUsagePool {
|
|
128
|
+
private pending: LifetimeUsage = {
|
|
129
|
+
input: 0,
|
|
130
|
+
output: 0,
|
|
131
|
+
cacheWrite: 0,
|
|
132
|
+
cacheRead: 0,
|
|
133
|
+
cost: 0,
|
|
134
|
+
}
|
|
135
|
+
private dirty = false
|
|
136
|
+
|
|
137
|
+
add(delta: LifetimeUsage): void {
|
|
138
|
+
addUsage(this.pending, delta)
|
|
139
|
+
this.dirty = true
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Take everything accumulated so far as a pi `Usage`, resetting the pool.
|
|
144
|
+
* Returns undefined when nothing is pending, so callers can leave the tool
|
|
145
|
+
* result untouched rather than attaching a zero.
|
|
146
|
+
*/
|
|
147
|
+
drain(): ReportedUsage | undefined {
|
|
148
|
+
if (!this.dirty) return undefined
|
|
149
|
+
const drained = toReportedUsage(this.pending)
|
|
150
|
+
this.pending = { input: 0, output: 0, cacheWrite: 0, cacheRead: 0, cost: 0 }
|
|
151
|
+
this.dirty = false
|
|
152
|
+
return drained
|
|
153
|
+
}
|
|
26
154
|
}
|
|
27
155
|
|
|
28
156
|
/** Minimal shape we read from upstream `getSessionStats()`. */
|
|
@@ -28,6 +28,26 @@ export interface WorktreeInfo {
|
|
|
28
28
|
workPath: string
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Project-wide switch for worktree isolation (`worktreeIsolation` in
|
|
33
|
+
* subagents.json). Default `true` — unchanged behaviour.
|
|
34
|
+
*
|
|
35
|
+
* The `"off"` isolation value gives a model a legal way to decline a worktree,
|
|
36
|
+
* but it still depends on the model choosing it. This is the deterministic half
|
|
37
|
+
* of the same fix: on a large repo where every worktree costs real time and
|
|
38
|
+
* disk (#184), turning it off means no caller can create one, whatever it
|
|
39
|
+
* passes.
|
|
40
|
+
*/
|
|
41
|
+
let worktreeIsolationEnabled = true
|
|
42
|
+
|
|
43
|
+
export function setWorktreeIsolationEnabled(enabled: boolean): void {
|
|
44
|
+
worktreeIsolationEnabled = enabled
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function isWorktreeIsolationEnabled(): boolean {
|
|
48
|
+
return worktreeIsolationEnabled
|
|
49
|
+
}
|
|
50
|
+
|
|
31
51
|
export interface WorktreeCleanupResult {
|
|
32
52
|
/** Whether changes were found in the worktree. */
|
|
33
53
|
hasChanges: boolean
|
|
@@ -134,11 +154,15 @@ export function cleanupWorktree(
|
|
|
134
154
|
// Truncate description for commit message (no shell sanitization needed — execFileSync uses argv)
|
|
135
155
|
const safeDesc = agentDescription.slice(0, 200)
|
|
136
156
|
const commitMsg = `pi-agent: ${safeDesc}`
|
|
137
|
-
execFileSync(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
157
|
+
execFileSync(
|
|
158
|
+
"git",
|
|
159
|
+
["commit", "--no-verify", "--no-gpg-sign", "-m", commitMsg],
|
|
160
|
+
{
|
|
161
|
+
cwd: worktree.path,
|
|
162
|
+
stdio: "pipe",
|
|
163
|
+
timeout: 10000,
|
|
164
|
+
},
|
|
165
|
+
)
|
|
142
166
|
} else {
|
|
143
167
|
const currentSha = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
144
168
|
cwd: worktree.path,
|
|
@@ -27,6 +27,8 @@ Nothing to set up — the tool is live as soon as Pi restarts. Hand the model a
|
|
|
27
27
|
|
|
28
28
|
Rather than picking a strategy on your behalf, the model calls `ask_user_question` and a dialog takes over the bottom of your terminal. Move with `↑`/`↓`, choose with `Enter`, press `n` to attach a note, or land on the `Type something.` row to answer in your own words. While typing, `Shift+Enter` adds a line, `Ctrl+G` opens Pi's configured external editor, and `Ctrl+U` clears the draft; browsing another option and returning keeps what you wrote. `Esc` abandons the questionnaire entirely.
|
|
29
29
|
|
|
30
|
+
When the questionnaire begins waiting in an interactive TTY, it emits one standard terminal BEL (`\x07`). Your terminal configuration determines whether that appears as an audible alert, a visual alert, or nothing; redirected and non-TTY output is untouched.
|
|
31
|
+
|
|
30
32
|

|
|
31
33
|
|
|
32
34
|
When the model asks several things at once, `Tab` moves between them and a Submit tab reviews everything before it goes back:
|
|
@@ -50,6 +52,7 @@ Optional. Settings live in `~/.config/rpiv-ask-user-question/config.json`; the f
|
|
|
50
52
|
| Setting | What it does | Default |
|
|
51
53
|
| --- | --- | --- |
|
|
52
54
|
| `collapseKey` | Key that collapses and expands the dialog. Accepts Pi keybinding ids such as `alt+o`; `"off"` disables the shortcut. | `"ctrl+]"` |
|
|
55
|
+
| `guidance.description` | Full replacement for the tool description the model sees. A non-empty string replaces the built-in text entirely — no merging. | built-in description |
|
|
53
56
|
| `guidance.promptSnippet` | One-line description of the tool in the system prompt — tune how eagerly the model asks. | built-in snippet |
|
|
54
57
|
| `guidance.promptGuidelines` | Usage guidelines given to the model, as a list of strings. | 4 built-in guidelines |
|
|
55
58
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { isKeyRelease, isKeyRepeat, matchesKey } from "@earendil-works/pi-tui";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
COLLAPSE_KEY_OFF,
|
|
5
|
+
formatKeySpecForDisplay,
|
|
6
|
+
loadConfig,
|
|
7
|
+
resolveCollapseKey,
|
|
8
|
+
validateGuidanceFields,
|
|
9
|
+
} from "./config.js";
|
|
4
10
|
import {
|
|
5
11
|
ASK_USER_BLOCKED_EVENT,
|
|
6
12
|
ASK_USER_PROMPT_EVENT,
|
|
@@ -61,6 +67,24 @@ const ERROR_SESSION_LOAD_FAILED =
|
|
|
61
67
|
const ERROR_STALE_MODULE_CACHE =
|
|
62
68
|
"Error: the questionnaire UI cannot load — the host's module cache went stale after an earlier failed load (typically dependencies replaced on disk mid-session). This is unrecoverable within the current Pi process. The user never saw the questions — do NOT treat this as a decline. Ask the questions as plain chat text instead, and tell the user to restart Pi to restore this tool.";
|
|
63
69
|
|
|
70
|
+
/** Standard terminal bell — same byte rpiv-warp exports as OSC_TERMINATOR. */
|
|
71
|
+
export const BEL = "\x07";
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Emit one portable terminal attention signal without touching redirected output.
|
|
75
|
+
* Writes to stdout rather than rpiv-warp's `/dev/tty` transport: the `isTTY` gate
|
|
76
|
+
* both proves an interactive terminal owns the coming wait and keeps the byte out
|
|
77
|
+
* of piped RPC transports (VS Code pendant, Zed) — a `/dev/tty` write would ring
|
|
78
|
+
* even when the questionnaire renders in a remote host's own UI.
|
|
79
|
+
*/
|
|
80
|
+
function emitTerminalAttention(): void {
|
|
81
|
+
try {
|
|
82
|
+
if (process.stdout.isTTY) process.stdout.write(BEL);
|
|
83
|
+
} catch {
|
|
84
|
+
// Terminal attention is best effort; the questionnaire must still proceed.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
64
88
|
/** Delay before the background session-graph pre-warm; mirrors rpiv-workflow's /wf prewarm. */
|
|
65
89
|
export const PREWARM_DELAY_MS = 2000;
|
|
66
90
|
|
|
@@ -120,12 +144,7 @@ export const DEFAULT_PROMPT_GUIDELINES: string[] = [
|
|
|
120
144
|
"Do not stack multiple ask_user_question calls back-to-back — group all clarifying questions into one invocation.",
|
|
121
145
|
];
|
|
122
146
|
|
|
123
|
-
export
|
|
124
|
-
const guidance = validateGuidanceFields(loadConfig().guidance);
|
|
125
|
-
pi.registerTool({
|
|
126
|
-
name: ASK_USER_QUESTION_TOOL_NAME,
|
|
127
|
-
label: "Ask User Question",
|
|
128
|
-
description: `Ask the user one or more structured questions during execution. Use when you need to:
|
|
147
|
+
export const DEFAULT_TOOL_DESCRIPTION = `Ask the user one or more structured questions during execution. Use when you need to:
|
|
129
148
|
1. Gather user preferences or requirements
|
|
130
149
|
2. Clarify ambiguous instructions
|
|
131
150
|
3. Get decisions on implementation choices as you work
|
|
@@ -143,7 +162,14 @@ Use the optional \`preview\` field on options when presenting concrete artifacts
|
|
|
143
162
|
- Diagram variations
|
|
144
163
|
- Configuration examples
|
|
145
164
|
|
|
146
|
-
Preview content is rendered as markdown in a monospace box. Multi-line text with newlines is supported. When any option has a preview, the UI switches to a side-by-side layout with a vertical option list on the left and preview on the right. Do not use previews for simple preference questions where labels and descriptions suffice. Note: previews are only supported for single-select questions (not multiSelect)
|
|
165
|
+
Preview content is rendered as markdown in a monospace box. Multi-line text with newlines is supported. When any option has a preview, the UI switches to a side-by-side layout with a vertical option list on the left and preview on the right. Do not use previews for simple preference questions where labels and descriptions suffice. Note: previews are only supported for single-select questions (not multiSelect).`;
|
|
166
|
+
|
|
167
|
+
export function registerAskUserQuestionTool(pi: ExtensionAPI): void {
|
|
168
|
+
const guidance = validateGuidanceFields(loadConfig().guidance);
|
|
169
|
+
pi.registerTool({
|
|
170
|
+
name: ASK_USER_QUESTION_TOOL_NAME,
|
|
171
|
+
label: "Ask User Question",
|
|
172
|
+
description: guidance.description ?? DEFAULT_TOOL_DESCRIPTION,
|
|
147
173
|
promptSnippet: guidance.promptSnippet ?? DEFAULT_PROMPT_SNIPPET,
|
|
148
174
|
promptGuidelines: guidance.promptGuidelines ?? DEFAULT_PROMPT_GUIDELINES,
|
|
149
175
|
parameters: QuestionParamsSchema,
|
|
@@ -173,6 +199,7 @@ Preview content is rendered as markdown in a monospace box. Multi-line text with
|
|
|
173
199
|
if ((ctx as { mode?: string }).mode === "rpc" && hasDialogUI(ctx.ui)) {
|
|
174
200
|
emitAskUserBlockedEvent(pi, true);
|
|
175
201
|
try {
|
|
202
|
+
emitTerminalAttention();
|
|
176
203
|
return buildQuestionnaireResponse(await runRpcQuestionnaire(ctx.ui, typed), typed);
|
|
177
204
|
} finally {
|
|
178
205
|
emitAskUserBlockedEvent(pi, false);
|
|
@@ -207,7 +234,7 @@ Preview content is rendered as markdown in a monospace box. Multi-line text with
|
|
|
207
234
|
let hasAnnouncedHide = false;
|
|
208
235
|
let removeOverlayInputListener: (() => void) | undefined;
|
|
209
236
|
|
|
210
|
-
if (collapseKey !==
|
|
237
|
+
if (collapseKey !== COLLAPSE_KEY_OFF && typeof ctx.ui.onTerminalInput === "function") {
|
|
211
238
|
removeOverlayInputListener = ctx.ui.onTerminalInput((data) => {
|
|
212
239
|
const handle = overlayHandleRef.current;
|
|
213
240
|
if (!handle) return undefined;
|
|
@@ -224,14 +251,22 @@ Preview content is rendered as markdown in a monospace box. Multi-line text with
|
|
|
224
251
|
sessionRef.current?.toggleCollapsedExternal();
|
|
225
252
|
if (handle.isHidden() && !hasAnnouncedHide) {
|
|
226
253
|
hasAnnouncedHide = true;
|
|
227
|
-
ctx.ui.notify?.(
|
|
254
|
+
ctx.ui.notify?.(
|
|
255
|
+
`ask_user_question hidden — press ${formatKeySpecForDisplay(collapseKey)} to reopen`,
|
|
256
|
+
"info",
|
|
257
|
+
);
|
|
228
258
|
}
|
|
229
259
|
return { consume: true };
|
|
230
260
|
});
|
|
231
261
|
}
|
|
262
|
+
// Hiding the overlay is only reversible through the raw listener above, so
|
|
263
|
+
// the session may emit `setHidden` only when it was actually registered;
|
|
264
|
+
// otherwise collapse falls back to the visible one-line row.
|
|
265
|
+
const canReopenWhileHidden = removeOverlayInputListener !== undefined;
|
|
232
266
|
|
|
233
267
|
emitAskUserBlockedEvent(pi, true);
|
|
234
268
|
try {
|
|
269
|
+
emitTerminalAttention();
|
|
235
270
|
const result = await ctx.ui.custom<QuestionnaireResult>(
|
|
236
271
|
(tui, theme, keybindings, done) => {
|
|
237
272
|
const session = new QuestionnaireSession({
|
|
@@ -259,6 +294,7 @@ Preview content is rendered as markdown in a monospace box. Multi-line text with
|
|
|
259
294
|
}
|
|
260
295
|
},
|
|
261
296
|
collapseKey,
|
|
297
|
+
canReopenWhileHidden,
|
|
262
298
|
});
|
|
263
299
|
sessionRef.current = session;
|
|
264
300
|
return session.component;
|
|
@@ -68,6 +68,31 @@ export function resolveCollapseKey(config: Pick<AskUserQuestionConfig, "collapse
|
|
|
68
68
|
return isValidCollapseKeySpec(raw) ? raw : DEFAULT_COLLAPSE_KEY;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// The only compound-word names in SPECIAL_KEYS — first-letter capitalization
|
|
72
|
+
// alone would render them "Pageup"/"Pagedown".
|
|
73
|
+
const COMPOUND_KEY_DISPLAY: Record<string, string> = {
|
|
74
|
+
pageup: "PageUp",
|
|
75
|
+
pagedown: "PageDown",
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Pretty-print a resolved key spec for UI copy: each `+`-part gets its first
|
|
80
|
+
* character uppercased (`"ctrl+]"` → `"Ctrl+]"`, `"alt+o"` → `"Alt+O"`,
|
|
81
|
+
* `"f9"` → `"F9"`, `"ctrl+pagedown"` → `"Ctrl+PageDown"`). Display-only — key
|
|
82
|
+
* matching always uses the raw lowercase spec (`matchesKey` lowercases ids),
|
|
83
|
+
* so never feed the result back into it.
|
|
84
|
+
*/
|
|
85
|
+
export function formatKeySpecForDisplay(spec: CollapseKeySpec): string {
|
|
86
|
+
return spec
|
|
87
|
+
.split("+")
|
|
88
|
+
.map(
|
|
89
|
+
(part) =>
|
|
90
|
+
COMPOUND_KEY_DISPLAY[part] ??
|
|
91
|
+
(part.length <= 1 ? part.toUpperCase() : part.charAt(0).toUpperCase() + part.slice(1)),
|
|
92
|
+
)
|
|
93
|
+
.join("+");
|
|
94
|
+
}
|
|
95
|
+
|
|
71
96
|
export function loadConfig(): AskUserQuestionConfig {
|
|
72
97
|
return loadJsonConfigWithLegacyFallback<AskUserQuestionConfig>("rpiv-ask-user-question");
|
|
73
98
|
}
|
|
@@ -19,6 +19,7 @@ A complete example:
|
|
|
19
19
|
{
|
|
20
20
|
"collapseKey": "alt+o",
|
|
21
21
|
"guidance": {
|
|
22
|
+
"description": "Ask the user structured questions whenever requirements are ambiguous.",
|
|
22
23
|
"promptSnippet": "Ask me before guessing on anything ambiguous",
|
|
23
24
|
"promptGuidelines": [
|
|
24
25
|
"Batch every clarifying question into one ask_user_question call.",
|
|
@@ -58,6 +59,7 @@ type are likewise dropped back to their default without a warning.
|
|
|
58
59
|
| Setting | What it does | Default |
|
|
59
60
|
| --- | --- | --- |
|
|
60
61
|
| `collapseKey` | Key that collapses and expands the dialog overlay. | `"ctrl+]"` |
|
|
62
|
+
| `guidance.description` | Full text of the tool description the model sees. Replaces the built-in default entirely — no merging. | built-in description |
|
|
61
63
|
| `guidance.promptSnippet` | One-line snippet describing the tool in the system prompt. | built-in snippet |
|
|
62
64
|
| `guidance.promptGuidelines` | List of usage guidelines given to the model. | 4 built-in guidelines |
|
|
63
65
|
|
|
@@ -83,11 +85,18 @@ strict on purpose: Pi's parser takes the last `+`-separated part as the key and
|
|
|
83
85
|
unknown parts, so a typo like `"ctr+]"` would otherwise silently capture every bare `]`
|
|
84
86
|
keypress at the terminal level.
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
The footer hint inside the dialog names whatever key you configure (`Alt+O to collapse`
|
|
89
|
+
for `"alt+o"`), as do the collapsed one-line footer and the one-shot notification shown
|
|
90
|
+
when the dialog is first hidden. With `"off"` the collapse hint is dropped from the
|
|
91
|
+
footer entirely, since no shortcut can fire.
|
|
89
92
|
|
|
90
|
-
### `guidance.promptSnippet` and `guidance.promptGuidelines`
|
|
93
|
+
### `guidance.description`, `guidance.promptSnippet` and `guidance.promptGuidelines`
|
|
94
|
+
|
|
95
|
+
`guidance.description` replaces the entire built-in description Pi registers for the
|
|
96
|
+
`ask_user_question` tool — the text the model reads when deciding how to use it. There is
|
|
97
|
+
no merging: a valid value wins wholesale. It is used only when it is a non-empty string;
|
|
98
|
+
anything else falls back to the built-in default. Like the other guidance fields it is read
|
|
99
|
+
once, when the extension registers the tool, so changes take effect on the next Pi restart.
|
|
91
100
|
|
|
92
101
|
These replace the text Pi puts in the system prompt about when to reach for
|
|
93
102
|
`ask_user_question`. Use them to make the model ask more or less often, or to enforce a
|
|
@@ -11,6 +11,12 @@ render at all.
|
|
|
11
11
|
| RPC / ACP host (VS Code pendant, Zed, Paseo) | `ask_user_question` in its tool list | A sequence of the host's own native select and input dialogs |
|
|
12
12
|
| Non-interactive run (no UI) | Nothing — the tool is removed | Nothing |
|
|
13
13
|
|
|
14
|
+
### Terminal attention
|
|
15
|
+
|
|
16
|
+
After UI availability and questionnaire validation succeed, the package emits exactly one standard terminal BEL (`\x07`) immediately before the interactive wait begins. The signal is sent to `stdout` only when `process.stdout.isTTY` is true, so redirected output and non-TTY RPC streams stay untouched. A TTY-backed RPC dialog walker receives the same signal as the TUI path.
|
|
17
|
+
|
|
18
|
+
The BEL is best effort: if the synchronous terminal write fails, the questionnaire continues and its existing prompt/blocked lifecycle and result envelope are unchanged. The terminal configuration decides whether the BEL is audible, visual, or ignored. No BEL is emitted for missing UI, invalid questionnaires, or a failed TUI session load.
|
|
19
|
+
|
|
14
20
|
### Non-interactive runs
|
|
15
21
|
|
|
16
22
|
A `before_agent_start` hook reconciles the active tool set against `ctx.hasUI` before every
|
|
@@ -58,7 +64,8 @@ Some parts of the dialog exist only under the right conditions:
|
|
|
58
64
|
| `Type something.` row | Always |
|
|
59
65
|
| Side-by-side preview | An option carries a `preview`, and terminal and pane are both ≥ 100 columns |
|
|
60
66
|
| Preview pane at all | Single-select questions only |
|
|
61
|
-
| Collapse shortcut | `collapseKey` is not `"off"`
|
|
67
|
+
| Collapse shortcut | `collapseKey` is not `"off"` |
|
|
68
|
+
| Full overlay hide on collapse | The host also exposes raw terminal input (the only path that can reopen a hidden overlay); without it, collapsing shrinks the dialog to a visible one-line row instead |
|
|
62
69
|
| Localized chrome | `@juicesharp/rpiv-i18n` is installed |
|
|
63
70
|
|
|
64
71
|
## Loading and startup cost
|
|
@@ -71,7 +71,9 @@ No code change is required.
|
|
|
71
71
|
1. Copy `locales/en.json` to `locales/<code>.json`, where `<code>` is a locale the SDK
|
|
72
72
|
supports.
|
|
73
73
|
2. Translate the values. Keep every key, and keep the placeholders and leading symbols
|
|
74
|
-
(`↑/↓`, `⚠`) intact.
|
|
74
|
+
(`↑/↓`, `⚠`) intact. `hint.collapse` and `hint.expand_line` contain a literal `{key}`
|
|
75
|
+
placeholder that is replaced at render time with the user's configured `collapseKey` —
|
|
76
|
+
keep it verbatim, positioned wherever your language's word order wants the key name.
|
|
75
77
|
3. Restart Pi and select the language with `/languages`.
|
|
76
78
|
|
|
77
79
|
The loader iterates the SDK's supported-locale list over this directory at startup, so a
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"hint.toggle": "Space to toggle",
|
|
13
13
|
"hint.notes": "n to add notes",
|
|
14
14
|
"hint.tab": "Tab to switch questions",
|
|
15
|
-
"hint.collapse": "
|
|
16
|
-
"hint.expand_line": "
|
|
15
|
+
"hint.collapse": "{key} to collapse",
|
|
16
|
+
"hint.expand_line": "{key} to expand · Esc to cancel",
|
|
17
17
|
"hint.cancel": "Esc to cancel",
|
|
18
18
|
|
|
19
19
|
"review.heading": "Review your answers",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"hint.toggle": "空格切换",
|
|
15
15
|
"hint.notes": "按 n 添加备注",
|
|
16
16
|
"hint.tab": "Tab 切换问题",
|
|
17
|
-
"hint.collapse": "
|
|
18
|
-
"hint.expand_line": "
|
|
17
|
+
"hint.collapse": "{key} 折叠",
|
|
18
|
+
"hint.expand_line": "{key} 展开 · Esc 取消",
|
|
19
19
|
"hint.cancel": "Esc 取消",
|
|
20
20
|
|
|
21
21
|
"review.heading": "检查你的答案",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juicesharp/rpiv-ask-user-question",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.4",
|
|
4
4
|
"description": "Pi extension. A structured questionnaire the model can put to you when it would otherwise guess, with typed options instead of free-form replies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"view/tab-content-strategy.ts"
|
|
79
79
|
],
|
|
80
80
|
"pi": {
|
|
81
|
+
"image": "https://raw.githubusercontent.com/juicesharp/rpiv-mono/main/packages/rpiv-ask-user-question/docs/cover.png",
|
|
81
82
|
"extensions": [
|
|
82
83
|
"./index.ts"
|
|
83
84
|
]
|
|
@@ -93,7 +94,7 @@
|
|
|
93
94
|
}
|
|
94
95
|
},
|
|
95
96
|
"dependencies": {
|
|
96
|
-
"@juicesharp/rpiv-config": "^2.
|
|
97
|
+
"@juicesharp/rpiv-config": "^2.6.4",
|
|
97
98
|
"typebox": "^1.1.24"
|
|
98
99
|
}
|
|
99
100
|
}
|
|
@@ -40,6 +40,13 @@ export interface QuestionnaireBuildConfig {
|
|
|
40
40
|
isMulti: boolean;
|
|
41
41
|
initialState: QuestionnaireState;
|
|
42
42
|
getCurrentTab: () => number;
|
|
43
|
+
/**
|
|
44
|
+
* Resolved collapse key spec (`"ctrl+]"`, `"alt+o"`, or `"off"`). Threaded to
|
|
45
|
+
* `DialogConfig` as construction-time config so the footer hint can name the
|
|
46
|
+
* real key — deliberately NOT part of `QuestionnaireState`, which stays free
|
|
47
|
+
* of runtime context.
|
|
48
|
+
*/
|
|
49
|
+
collapseKey: string;
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
export interface QuestionnaireBuilt {
|
|
@@ -109,6 +116,7 @@ class QuestionnaireBuilder {
|
|
|
109
116
|
private readonly isMulti: boolean;
|
|
110
117
|
private readonly initialState: QuestionnaireState;
|
|
111
118
|
private readonly getCurrentTab: () => number;
|
|
119
|
+
private readonly collapseKey: string;
|
|
112
120
|
|
|
113
121
|
private readonly selectTheme: WrappingSelectTheme;
|
|
114
122
|
private readonly markdownTheme = getMarkdownTheme();
|
|
@@ -125,6 +133,7 @@ class QuestionnaireBuilder {
|
|
|
125
133
|
this.isMulti = config.isMulti;
|
|
126
134
|
this.initialState = config.initialState;
|
|
127
135
|
this.getCurrentTab = config.getCurrentTab;
|
|
136
|
+
this.collapseKey = config.collapseKey;
|
|
128
137
|
|
|
129
138
|
this.selectTheme = this.makeSelectTheme();
|
|
130
139
|
const textEditorTheme = editorTheme(this.theme);
|
|
@@ -258,6 +267,7 @@ class QuestionnaireBuilder {
|
|
|
258
267
|
getBodyHeight: heights.global,
|
|
259
268
|
getCurrentBodyHeight: heights.current,
|
|
260
269
|
getTerminalRows: this.getTerminalRows,
|
|
270
|
+
collapseKey: this.collapseKey,
|
|
261
271
|
},
|
|
262
272
|
{ state: this.initialState, activePreviewPane: this.pickInitialActivePreview(tabs) },
|
|
263
273
|
);
|
|
@@ -174,7 +174,9 @@ export function routeKey(data: string, state: QuestionnaireState, runtime: Quest
|
|
|
174
174
|
// replaces the lazily imported QuestionnaireSession graph on disk. Passing
|
|
175
175
|
// undefined into matchesKey reaches parseKeyId().toLowerCase() and crashes the
|
|
176
176
|
// entire host process, so keep the runtime boundary defensive even though the
|
|
177
|
-
// TypeScript contract requires a string.
|
|
177
|
+
// TypeScript contract requires a string. The "off" literal is deliberate:
|
|
178
|
+
// importing COLLAPSE_KEY_OFF would pull ../config.js (and its rpiv-config
|
|
179
|
+
// loader graph) into this pure module for a string that cannot change.
|
|
178
180
|
if (
|
|
179
181
|
typeof runtime.collapseKey === "string" &&
|
|
180
182
|
runtime.collapseKey !== "off" &&
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { Editor, OverlayHandle, TUI } from "@earendil-works/pi-tui";
|
|
3
|
+
import { COLLAPSE_KEY_OFF, formatKeySpecForDisplay } from "../config.js";
|
|
3
4
|
import type { QuestionData, QuestionnaireResult, QuestionParams } from "../tool/types.js";
|
|
4
5
|
import type { WrappingSelectItem } from "../view/components/wrapping-select.js";
|
|
5
|
-
import {
|
|
6
|
+
import { COLLAPSED_HINT_TEMPLATE, HINT_PART_CANCEL, KEY_PLACEHOLDER } from "../view/dialog-builder.js";
|
|
6
7
|
import type { QuestionnairePropsAdapter } from "../view/props-adapter.js";
|
|
7
8
|
import { buildQuestionnaire } from "./build-questionnaire.js";
|
|
8
9
|
import { t } from "./i18n-bridge.js";
|
|
@@ -21,6 +22,14 @@ export interface QuestionnaireSessionConfig {
|
|
|
21
22
|
editInput: (value: string) => Promise<string | undefined>;
|
|
22
23
|
/** Key spec for the collapse/expand shortcut, e.g. `"ctrl+]"` or `"alt+o"`. */
|
|
23
24
|
collapseKey: string;
|
|
25
|
+
/**
|
|
26
|
+
* True iff `execute()` registered the raw `ctx.ui.onTerminalInput` listener — the only
|
|
27
|
+
* path that can reach a hidden overlay. Gates the `set_overlay_hidden` effect: a host
|
|
28
|
+
* that delivers an `OverlayHandle` but no raw terminal input must fall back to the
|
|
29
|
+
* visible one-line collapsed row (which keeps focus and input routing), or collapsing
|
|
30
|
+
* would hide the overlay into a state nothing can reopen.
|
|
31
|
+
*/
|
|
32
|
+
canReopenWhileHidden: boolean;
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
export interface QuestionnaireSessionComponent {
|
|
@@ -64,6 +73,7 @@ export class QuestionnaireSession {
|
|
|
64
73
|
private readonly keybindings: QuestionnaireRuntime["keybindings"];
|
|
65
74
|
private readonly editInput: QuestionnaireSessionConfig["editInput"];
|
|
66
75
|
private readonly collapseKey: string;
|
|
76
|
+
private readonly canReopenWhileHidden: boolean;
|
|
67
77
|
private inputEditorOpen = false;
|
|
68
78
|
|
|
69
79
|
/**
|
|
@@ -86,6 +96,7 @@ export class QuestionnaireSession {
|
|
|
86
96
|
this.keybindings = config.keybindings;
|
|
87
97
|
this.editInput = config.editInput;
|
|
88
98
|
this.collapseKey = config.collapseKey;
|
|
99
|
+
this.canReopenWhileHidden = config.canReopenWhileHidden;
|
|
89
100
|
|
|
90
101
|
const built = buildQuestionnaire({
|
|
91
102
|
tui: this.tui,
|
|
@@ -95,6 +106,7 @@ export class QuestionnaireSession {
|
|
|
95
106
|
isMulti: this.isMulti,
|
|
96
107
|
initialState: this.state,
|
|
97
108
|
getCurrentTab: () => this.state.currentTab,
|
|
109
|
+
collapseKey: this.collapseKey,
|
|
98
110
|
});
|
|
99
111
|
|
|
100
112
|
this.notesInput = built.notesInput;
|
|
@@ -106,10 +118,18 @@ export class QuestionnaireSession {
|
|
|
106
118
|
// the overlay to `min(lines.length, maxHeight)`, so returning one line shrinks
|
|
107
119
|
// the bottom-anchored overlay from full-height to one row and the transcript
|
|
108
120
|
// behind it becomes readable (#47). The overlay stays focused and in the
|
|
109
|
-
// stack, so
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
// stack, so the collapse key still routes here to expand. `t` stays inside the
|
|
122
|
+
// closure (live locale updates); the key display is static per session.
|
|
123
|
+
//
|
|
124
|
+
// With collapseKey "off" the router and raw listener never toggle `collapsed`,
|
|
125
|
+
// but `toggleCollapsedExternal()` is a public ungated entry — fall back to the
|
|
126
|
+
// cancel-only line rather than rendering a literal "Off to expand".
|
|
127
|
+
const collapseKeyDisplay = formatKeySpecForDisplay(this.collapseKey);
|
|
128
|
+
const collapsedHintLine = (): string =>
|
|
129
|
+
this.collapseKey === COLLAPSE_KEY_OFF
|
|
130
|
+
? t("hint.cancel", HINT_PART_CANCEL)
|
|
131
|
+
: t("hint.expand_line", COLLAPSED_HINT_TEMPLATE).replace(KEY_PLACEHOLDER, collapseKeyDisplay);
|
|
132
|
+
const collapsedRender = (_width: number): string[] => [theme.fg("dim", ` ${collapsedHintLine()} `)];
|
|
113
133
|
|
|
114
134
|
this.component = {
|
|
115
135
|
render: (width) => (this.state.collapsed ? collapsedRender(width) : built.render(width)),
|
|
@@ -178,8 +198,11 @@ export class QuestionnaireSession {
|
|
|
178
198
|
return;
|
|
179
199
|
case "set_overlay_hidden":
|
|
180
200
|
// No-op until `setOverlayHandle` has been called (the handle arrives via
|
|
181
|
-
// `ctx.ui.custom`'s `onHandle` right after the overlay is shown)
|
|
182
|
-
|
|
201
|
+
// `ctx.ui.custom`'s `onHandle` right after the overlay is shown), and
|
|
202
|
+
// suppressed entirely when no raw terminal listener exists — hiding would
|
|
203
|
+
// then be irreversible (pi-tui routes no input to a hidden overlay), so the
|
|
204
|
+
// visible one-line collapsed row serves as the fallback rendering instead.
|
|
205
|
+
if (this.canReopenWhileHidden) this.overlayHandle?.setHidden(effect.hidden);
|
|
183
206
|
return;
|
|
184
207
|
case "done":
|
|
185
208
|
this.done(effect.result);
|
|
@@ -25,12 +25,20 @@ export interface QuestionnaireState {
|
|
|
25
25
|
/** Canonical mirror of the in-flight notes editor; runtime mirrors after `forward_notes_keystroke`. */
|
|
26
26
|
notesDraft: string;
|
|
27
27
|
/**
|
|
28
|
-
* Collapsed mode: the questionnaire
|
|
29
|
-
*
|
|
30
|
-
* while true, every keystroke except cancel is swallowed (see
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
28
|
+
* Collapsed mode: the questionnaire gets out of the way so the agent transcript behind
|
|
29
|
+
* the bottom-anchored overlay becomes readable. Toggled by the configured collapse key
|
|
30
|
+
* from any state; while true, every keystroke except cancel is swallowed (see
|
|
31
|
+
* `routeKey`). Two renderings, chosen by host capability:
|
|
32
|
+
*
|
|
33
|
+
* - Hosts with an `OverlayHandle` AND a raw `onTerminalInput` listener (real pi-tui):
|
|
34
|
+
* the `set_overlay_hidden` effect fully hides the overlay; the raw listener registered
|
|
35
|
+
* in `execute()` reopens it, because pi-tui routes no input to a hidden overlay.
|
|
36
|
+
* - Hosts without the raw listener (or without a handle): the overlay stays visible and
|
|
37
|
+
* shrinks to a single hint row. The row keeps focus and input routing, so the same key
|
|
38
|
+
* expands it and Esc cancels, and the expand key never falls through to an underlying
|
|
39
|
+
* overlay (e.g. `/btw`). The session gates `set_overlay_hidden` on the listener's
|
|
40
|
+
* existence (`canReopenWhileHidden`) so a handle-bearing host without raw input can
|
|
41
|
+
* never hide the overlay into a state nothing can reopen.
|
|
34
42
|
*/
|
|
35
43
|
collapsed: boolean;
|
|
36
44
|
}
|