@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
|
@@ -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
|
|
@@ -25,7 +25,7 @@ Nothing to set up — the tool is live as soon as Pi restarts. Hand the model a
|
|
|
25
25
|
|
|
26
26
|
> Add caching to the API client.
|
|
27
27
|
|
|
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
|
|
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 to a question — or a global note to the whole questionnaire from the Submit tab — 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
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
31
|
|
|
@@ -33,7 +33,7 @@ When the questionnaire begins waiting in an interactive TTY, it emits one standa
|
|
|
33
33
|
|
|
34
34
|
When the model asks several things at once, `Tab` moves between them and a Submit tab reviews everything before it goes back:
|
|
35
35
|
|
|
36
|
-

|
|
37
37
|
|
|
38
38
|
## What you get
|
|
39
39
|
|
|
@@ -41,9 +41,9 @@ When the model asks several things at once, `Tab` moves between them and a Submi
|
|
|
41
41
|
- **You can always answer in your own words** — a `Type something.` row is appended to every question, single- or multi-select, widens to the full pane while you type, keeps its multiline draft visible in that row while you browse, and supports Pi's `Shift+Enter` newline and `Ctrl+G` external-editor flows.
|
|
42
42
|
- **Compare real artifacts, not just labels** — an option can carry a markdown `preview` (ASCII mockup, code, diagram, config) that renders in a bordered box beside the option list.
|
|
43
43
|
- **One interruption, not five** — up to four questions arrive in a single tabbed dialog, and the Submit tab lists your answers and names anything still blank before you commit.
|
|
44
|
-
- **Notes on any answer** — `n` opens a multiline note editor on any question tab
|
|
44
|
+
- **Notes on any answer — or on all of them** — `n` opens a multiline note editor on any question tab, and on the Submit tab it opens one global note for the whole questionnaire. Per-question notes reach the model as `user notes: <text>`, the global note as `global note: <text>`; neither marks a question answered.
|
|
45
45
|
- **Read the transcript behind the dialog** — `Ctrl+]` collapses the overlay so you can scroll the conversation, then brings it back with your answers intact.
|
|
46
|
-
- **Works outside the terminal too** — in RPC and ACP hosts such as the VS Code pendant or Zed the questionnaire walks through the host's native dialogs, and in non-interactive runs the tool is removed from the model's tool list instead of failing every call.
|
|
46
|
+
- **Works outside the terminal too** — in RPC and ACP hosts such as the VS Code pendant or Zed the questionnaire walks through the host's native dialogs (notes are terminal-only and do not carry over), and in non-interactive runs the tool is removed from the model's tool list instead of failing every call.
|
|
47
47
|
|
|
48
48
|
## Configuration
|
|
49
49
|
|
|
@@ -52,6 +52,7 @@ Optional. Settings live in `~/.config/rpiv-ask-user-question/config.json`; the f
|
|
|
52
52
|
| Setting | What it does | Default |
|
|
53
53
|
| --- | --- | --- |
|
|
54
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 |
|
|
55
56
|
| `guidance.promptSnippet` | One-line description of the tool in the system prompt — tune how eagerly the model asks. | built-in snippet |
|
|
56
57
|
| `guidance.promptGuidelines` | Usage guidelines given to the model, as a list of strings. | 4 built-in guidelines |
|
|
57
58
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { isKeyRelease, isKeyRepeat, matchesKey } from "@earendil-works/pi-tui";
|
|
3
|
-
import {
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { isKeyRelease, isKeyRepeat, matchesKey, type OverlayHandle, type TUI } from "@earendil-works/pi-tui";
|
|
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,
|
|
@@ -9,7 +15,7 @@ import {
|
|
|
9
15
|
} from "./events.js";
|
|
10
16
|
// Static import is fine — rpc-fallback pulls only types + the i18n bridge,
|
|
11
17
|
// none of the ~560ms TUI render graph that QuestionnaireSession lazy-loads.
|
|
12
|
-
import { hasDialogUI, runRpcQuestionnaire } from "./rpc-fallback.js";
|
|
18
|
+
import { type DialogUI, hasDialogUI, runRpcQuestionnaire } from "./rpc-fallback.js";
|
|
13
19
|
import { displayLabel, t } from "./state/i18n-bridge.js";
|
|
14
20
|
import { sentinelsToAppend } from "./state/row-intent.js";
|
|
15
21
|
import { buildQuestionnaireResponse, buildToolResult } from "./tool/response-envelope.js";
|
|
@@ -47,6 +53,22 @@ function emitAskUserBlockedEvent(pi: ExtensionAPI, active: boolean): void {
|
|
|
47
53
|
pi.events.emit(ASK_USER_BLOCKED_EVENT, payload);
|
|
48
54
|
}
|
|
49
55
|
|
|
56
|
+
/** Non-interactive host backstop (the reconciler normally strips the tool first). */
|
|
57
|
+
function rejectWithoutUi() {
|
|
58
|
+
return buildToolResult(ERROR_NO_UI, { answers: [], cancelled: true, error: "no_ui" });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Sequential native-dialog walker for RPC hosts; brackets it with the blocked-event pair + terminal bell. */
|
|
62
|
+
async function runRpcPath(pi: ExtensionAPI, ui: DialogUI, typed: QuestionParams) {
|
|
63
|
+
emitAskUserBlockedEvent(pi, true);
|
|
64
|
+
try {
|
|
65
|
+
emitTerminalAttention();
|
|
66
|
+
return buildQuestionnaireResponse(await runRpcQuestionnaire(ui, typed), typed);
|
|
67
|
+
} finally {
|
|
68
|
+
emitAskUserBlockedEvent(pi, false);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
50
72
|
/** Canonical tool name — single source of truth shared with the reconcile module. */
|
|
51
73
|
export const ASK_USER_QUESTION_TOOL_NAME = "ask_user_question";
|
|
52
74
|
|
|
@@ -84,6 +106,9 @@ export const PREWARM_DELAY_MS = 2000;
|
|
|
84
106
|
|
|
85
107
|
type SessionModule = typeof import("./state/questionnaire-session.js");
|
|
86
108
|
|
|
109
|
+
type SessionRef = { current: import("./state/questionnaire-session.js").QuestionnaireSession | null };
|
|
110
|
+
type OverlayHandleRef = { current: OverlayHandle | undefined };
|
|
111
|
+
|
|
87
112
|
type SessionLoad =
|
|
88
113
|
| { ok: true; module: SessionModule }
|
|
89
114
|
| { ok: false; error: Extract<QuestionnaireError, "session_load_failed" | "stale_module_cache">; message: string };
|
|
@@ -118,6 +143,122 @@ export async function loadQuestionnaireSession(): Promise<SessionLoad> {
|
|
|
118
143
|
return { ok: true, module: mod };
|
|
119
144
|
}
|
|
120
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Register the raw terminal listener that toggles collapse while the overlay is hidden.
|
|
148
|
+
* Returns the remover, or undefined when the key is off / the host has no raw input hook —
|
|
149
|
+
* callers derive `canReopenWhileHidden` from that.
|
|
150
|
+
*/
|
|
151
|
+
function registerCollapseKeyListener(
|
|
152
|
+
ctx: ExtensionContext,
|
|
153
|
+
collapseKey: string,
|
|
154
|
+
sessionRef: SessionRef,
|
|
155
|
+
overlayHandleRef: OverlayHandleRef,
|
|
156
|
+
): (() => void) | undefined {
|
|
157
|
+
if (collapseKey === COLLAPSE_KEY_OFF || typeof ctx.ui.onTerminalInput !== "function") return undefined;
|
|
158
|
+
let hasAnnouncedHide = false;
|
|
159
|
+
return ctx.ui.onTerminalInput((data) => {
|
|
160
|
+
const handle = overlayHandleRef.current;
|
|
161
|
+
if (!handle) return undefined;
|
|
162
|
+
// Only act while the questionnaire is hidden (its handleInput is
|
|
163
|
+
// unreachable) or actually focused. When some other overlay is on
|
|
164
|
+
// top (e.g. `/btw`), leave the keystroke to that overlay instead of
|
|
165
|
+
// toggling the questionnaire from underneath it.
|
|
166
|
+
if (!handle.isHidden() && !handle.isFocused()) return undefined;
|
|
167
|
+
if (!matchesKey(data, collapseKey as Parameters<typeof matchesKey>[1])) return undefined;
|
|
168
|
+
// Kitty-protocol terminals report press, repeat, and release separately.
|
|
169
|
+
// Toggle only on the initial press so a tap does not immediately reopen
|
|
170
|
+
// the overlay and a held key does not toggle it repeatedly.
|
|
171
|
+
if (isKeyRelease(data) || isKeyRepeat(data)) return { consume: true };
|
|
172
|
+
sessionRef.current?.toggleCollapsedExternal();
|
|
173
|
+
if (handle.isHidden() && !hasAnnouncedHide) {
|
|
174
|
+
hasAnnouncedHide = true;
|
|
175
|
+
ctx.ui.notify?.(`ask_user_question hidden — press ${formatKeySpecForDisplay(collapseKey)} to reopen`, "info");
|
|
176
|
+
}
|
|
177
|
+
return { consume: true };
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Build the `ctx.ui.custom` component factory: constructs the session (capturing it in
|
|
183
|
+
* `sessionRef`) and exposes its component. `editInput` keeps its two dynamic imports —
|
|
184
|
+
* they must stay lazy per-invocation.
|
|
185
|
+
*/
|
|
186
|
+
function makeSessionFactory(config: {
|
|
187
|
+
ctx: ExtensionContext;
|
|
188
|
+
typed: QuestionParams;
|
|
189
|
+
itemsByTab: WrappingSelectItem[][];
|
|
190
|
+
collapseKey: string;
|
|
191
|
+
canReopenWhileHidden: boolean;
|
|
192
|
+
sessionRef: SessionRef;
|
|
193
|
+
Session: SessionModule["QuestionnaireSession"];
|
|
194
|
+
}) {
|
|
195
|
+
const { ctx, typed, itemsByTab, collapseKey, canReopenWhileHidden, sessionRef, Session } = config;
|
|
196
|
+
return (
|
|
197
|
+
tui: TUI,
|
|
198
|
+
theme: Theme,
|
|
199
|
+
keybindings: import("./state/questionnaire-session.js").QuestionnaireSessionConfig["keybindings"],
|
|
200
|
+
done: (result: QuestionnaireResult) => void,
|
|
201
|
+
): import("./state/questionnaire-session.js").QuestionnaireSessionComponent => {
|
|
202
|
+
const session = new Session({
|
|
203
|
+
tui,
|
|
204
|
+
theme,
|
|
205
|
+
params: typed,
|
|
206
|
+
itemsByTab,
|
|
207
|
+
done,
|
|
208
|
+
keybindings,
|
|
209
|
+
editInput: async (value) => {
|
|
210
|
+
try {
|
|
211
|
+
const [{ SettingsManager }, { editWithExternalEditor }] = await Promise.all([
|
|
212
|
+
import("@earendil-works/pi-coding-agent"),
|
|
213
|
+
import("./state/external-editor.js"),
|
|
214
|
+
]);
|
|
215
|
+
const editorCommand = SettingsManager.create(ctx.cwd, undefined, {
|
|
216
|
+
projectTrusted: ctx.isProjectTrusted(),
|
|
217
|
+
}).getExternalEditorCommand();
|
|
218
|
+
if (!editorCommand) throw new Error("No external editor command is configured");
|
|
219
|
+
return await editWithExternalEditor(tui, editorCommand, value);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
222
|
+
ctx.ui.notify(`${t("editor.failed", "External editor failed")}: ${message}`, "error");
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
collapseKey,
|
|
227
|
+
canReopenWhileHidden,
|
|
228
|
+
});
|
|
229
|
+
sessionRef.current = session;
|
|
230
|
+
return session.component;
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* A TUI questionnaire ALWAYS resolves a QuestionnaireResult (cancel included), so
|
|
236
|
+
* `undefined` uniquely means "host cannot render", never "user declined". RPC builds
|
|
237
|
+
* that predate ctx.mode land here: run the dialog walker when the host has the
|
|
238
|
+
* primitives; otherwise tell the model the user never saw the questions.
|
|
239
|
+
*/
|
|
240
|
+
async function resolveUndefinedResult(ctx: ExtensionContext, typed: QuestionParams) {
|
|
241
|
+
if (hasDialogUI(ctx.ui)) {
|
|
242
|
+
return buildQuestionnaireResponse(await runRpcQuestionnaire(ctx.ui, typed), typed);
|
|
243
|
+
}
|
|
244
|
+
return buildToolResult(ERROR_NO_CUSTOM_UI, { answers: [], cancelled: true, error: "no_custom_ui" });
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Pre-warm the lazy session graph once startup settles (#107). A graph
|
|
249
|
+
* evaluated while the paths Pi resolved at boot still exist stays in memory
|
|
250
|
+
* for the process lifetime, so later on-disk dependency churn (e.g. `pnpm
|
|
251
|
+
* install --force` replacing the store mid-session) can no longer poison
|
|
252
|
+
* jiti's graph cache. Swallowed failure is safe: the first real call
|
|
253
|
+
* re-imports and surfaces it through loadQuestionnaireSession's structured
|
|
254
|
+
* envelope. unref keeps the timer from holding a non-TUI embedder's process
|
|
255
|
+
* open.
|
|
256
|
+
*/
|
|
257
|
+
function prewarmSessionGraph(): void {
|
|
258
|
+
const timer = setTimeout(() => void loadQuestionnaireSession().catch(() => undefined), PREWARM_DELAY_MS);
|
|
259
|
+
timer.unref?.();
|
|
260
|
+
}
|
|
261
|
+
|
|
121
262
|
export function buildItemsForQuestion(question: QuestionData): WrappingSelectItem[] {
|
|
122
263
|
const items: WrappingSelectItem[] = question.options.map((o) => ({
|
|
123
264
|
kind: "option",
|
|
@@ -138,12 +279,7 @@ export const DEFAULT_PROMPT_GUIDELINES: string[] = [
|
|
|
138
279
|
"Do not stack multiple ask_user_question calls back-to-back — group all clarifying questions into one invocation.",
|
|
139
280
|
];
|
|
140
281
|
|
|
141
|
-
export
|
|
142
|
-
const guidance = validateGuidanceFields(loadConfig().guidance);
|
|
143
|
-
pi.registerTool({
|
|
144
|
-
name: ASK_USER_QUESTION_TOOL_NAME,
|
|
145
|
-
label: "Ask User Question",
|
|
146
|
-
description: `Ask the user one or more structured questions during execution. Use when you need to:
|
|
282
|
+
export const DEFAULT_TOOL_DESCRIPTION = `Ask the user one or more structured questions during execution. Use when you need to:
|
|
147
283
|
1. Gather user preferences or requirements
|
|
148
284
|
2. Clarify ambiguous instructions
|
|
149
285
|
3. Get decisions on implementation choices as you work
|
|
@@ -161,14 +297,21 @@ Use the optional \`preview\` field on options when presenting concrete artifacts
|
|
|
161
297
|
- Diagram variations
|
|
162
298
|
- Configuration examples
|
|
163
299
|
|
|
164
|
-
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)
|
|
300
|
+
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).`;
|
|
301
|
+
|
|
302
|
+
export function registerAskUserQuestionTool(pi: ExtensionAPI): void {
|
|
303
|
+
const guidance = validateGuidanceFields(loadConfig().guidance);
|
|
304
|
+
pi.registerTool({
|
|
305
|
+
name: ASK_USER_QUESTION_TOOL_NAME,
|
|
306
|
+
label: "Ask User Question",
|
|
307
|
+
description: guidance.description ?? DEFAULT_TOOL_DESCRIPTION,
|
|
165
308
|
promptSnippet: guidance.promptSnippet ?? DEFAULT_PROMPT_SNIPPET,
|
|
166
309
|
promptGuidelines: guidance.promptGuidelines ?? DEFAULT_PROMPT_GUIDELINES,
|
|
167
310
|
parameters: QuestionParamsSchema,
|
|
168
311
|
|
|
169
312
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
170
313
|
const typed = params as unknown as QuestionParams;
|
|
171
|
-
if (!ctx.hasUI) return
|
|
314
|
+
if (!ctx.hasUI) return rejectWithoutUi();
|
|
172
315
|
|
|
173
316
|
const validation = validateQuestionnaire(typed);
|
|
174
317
|
if (!validation.ok) {
|
|
@@ -189,13 +332,7 @@ Preview content is rendered as markdown in a monospace box. Multi-line text with
|
|
|
189
332
|
// import entirely; RPC builds that predate ctx.mode are caught by the
|
|
190
333
|
// custom()-resolved-undefined backstop below. See ./rpc-fallback.ts.
|
|
191
334
|
if ((ctx as { mode?: string }).mode === "rpc" && hasDialogUI(ctx.ui)) {
|
|
192
|
-
|
|
193
|
-
try {
|
|
194
|
-
emitTerminalAttention();
|
|
195
|
-
return buildQuestionnaireResponse(await runRpcQuestionnaire(ctx.ui, typed), typed);
|
|
196
|
-
} finally {
|
|
197
|
-
emitAskUserBlockedEvent(pi, false);
|
|
198
|
-
}
|
|
335
|
+
return runRpcPath(pi, ctx.ui, typed);
|
|
199
336
|
}
|
|
200
337
|
|
|
201
338
|
const itemsByTab: WrappingSelectItem[][] = typed.questions.map((q) => buildItemsForQuestion(q));
|
|
@@ -217,72 +354,27 @@ Preview content is rendered as markdown in a monospace box. Multi-line text with
|
|
|
217
354
|
// user toggles collapse, and register a raw terminal input listener for the
|
|
218
355
|
// same key so the toggle still works while the overlay is hidden (pi-tui does
|
|
219
356
|
// not route input to a hidden overlay's `component.handleInput`).
|
|
220
|
-
const sessionRef: {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
let removeOverlayInputListener: (() => void) | undefined;
|
|
228
|
-
|
|
229
|
-
if (collapseKey !== "off" && typeof ctx.ui.onTerminalInput === "function") {
|
|
230
|
-
removeOverlayInputListener = ctx.ui.onTerminalInput((data) => {
|
|
231
|
-
const handle = overlayHandleRef.current;
|
|
232
|
-
if (!handle) return undefined;
|
|
233
|
-
// Only act while the questionnaire is hidden (its handleInput is
|
|
234
|
-
// unreachable) or actually focused. When some other overlay is on
|
|
235
|
-
// top (e.g. `/btw`), leave the keystroke to that overlay instead of
|
|
236
|
-
// toggling the questionnaire from underneath it.
|
|
237
|
-
if (!handle.isHidden() && !handle.isFocused()) return undefined;
|
|
238
|
-
if (!matchesKey(data, collapseKey as Parameters<typeof matchesKey>[1])) return undefined;
|
|
239
|
-
// Kitty-protocol terminals report press, repeat, and release separately.
|
|
240
|
-
// Toggle only on the initial press so a tap does not immediately reopen
|
|
241
|
-
// the overlay and a held key does not toggle it repeatedly.
|
|
242
|
-
if (isKeyRelease(data) || isKeyRepeat(data)) return { consume: true };
|
|
243
|
-
sessionRef.current?.toggleCollapsedExternal();
|
|
244
|
-
if (handle.isHidden() && !hasAnnouncedHide) {
|
|
245
|
-
hasAnnouncedHide = true;
|
|
246
|
-
ctx.ui.notify?.(`ask_user_question hidden — press ${collapseKey} to reopen`, "info");
|
|
247
|
-
}
|
|
248
|
-
return { consume: true };
|
|
249
|
-
});
|
|
250
|
-
}
|
|
357
|
+
const sessionRef: SessionRef = { current: null };
|
|
358
|
+
const overlayHandleRef: OverlayHandleRef = { current: undefined };
|
|
359
|
+
const removeOverlayInputListener = registerCollapseKeyListener(ctx, collapseKey, sessionRef, overlayHandleRef);
|
|
360
|
+
// Hiding the overlay is only reversible through the raw listener above, so
|
|
361
|
+
// the session may emit `setHidden` only when it was actually registered;
|
|
362
|
+
// otherwise collapse falls back to the visible one-line row.
|
|
363
|
+
const canReopenWhileHidden = removeOverlayInputListener !== undefined;
|
|
251
364
|
|
|
252
365
|
emitAskUserBlockedEvent(pi, true);
|
|
253
366
|
try {
|
|
254
367
|
emitTerminalAttention();
|
|
255
368
|
const result = await ctx.ui.custom<QuestionnaireResult>(
|
|
256
|
-
(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
try {
|
|
266
|
-
const [{ SettingsManager }, { editWithExternalEditor }] = await Promise.all([
|
|
267
|
-
import("@earendil-works/pi-coding-agent"),
|
|
268
|
-
import("./state/external-editor.js"),
|
|
269
|
-
]);
|
|
270
|
-
const editorCommand = SettingsManager.create(ctx.cwd, undefined, {
|
|
271
|
-
projectTrusted: ctx.isProjectTrusted(),
|
|
272
|
-
}).getExternalEditorCommand();
|
|
273
|
-
if (!editorCommand) throw new Error("No external editor command is configured");
|
|
274
|
-
return await editWithExternalEditor(tui, editorCommand, value);
|
|
275
|
-
} catch (error) {
|
|
276
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
277
|
-
ctx.ui.notify(`${t("editor.failed", "External editor failed")}: ${message}`, "error");
|
|
278
|
-
return undefined;
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
collapseKey,
|
|
282
|
-
});
|
|
283
|
-
sessionRef.current = session;
|
|
284
|
-
return session.component;
|
|
285
|
-
},
|
|
369
|
+
makeSessionFactory({
|
|
370
|
+
ctx,
|
|
371
|
+
typed,
|
|
372
|
+
itemsByTab,
|
|
373
|
+
collapseKey,
|
|
374
|
+
canReopenWhileHidden,
|
|
375
|
+
sessionRef,
|
|
376
|
+
Session: QuestionnaireSession,
|
|
377
|
+
}),
|
|
286
378
|
{
|
|
287
379
|
overlay: true,
|
|
288
380
|
overlayOptions: {
|
|
@@ -298,17 +390,8 @@ Preview content is rendered as markdown in a monospace box. Multi-line text with
|
|
|
298
390
|
},
|
|
299
391
|
);
|
|
300
392
|
|
|
301
|
-
// A TUI questionnaire ALWAYS resolves a QuestionnaireResult (cancel
|
|
302
|
-
// included — state-reducer emits `{ answers, cancelled }`), so
|
|
303
|
-
// `undefined` uniquely means "host cannot render", never "user
|
|
304
|
-
// declined". RPC builds that predate ctx.mode land here: run the
|
|
305
|
-
// dialog walker when the host has the primitives; otherwise tell the
|
|
306
|
-
// model the user never saw the questions.
|
|
307
393
|
if (result === undefined) {
|
|
308
|
-
|
|
309
|
-
return buildQuestionnaireResponse(await runRpcQuestionnaire(ctx.ui, typed), typed);
|
|
310
|
-
}
|
|
311
|
-
return buildToolResult(ERROR_NO_CUSTOM_UI, { answers: [], cancelled: true, error: "no_custom_ui" });
|
|
394
|
+
return resolveUndefinedResult(ctx, typed);
|
|
312
395
|
}
|
|
313
396
|
|
|
314
397
|
return buildQuestionnaireResponse(result, typed);
|
|
@@ -319,16 +402,7 @@ Preview content is rendered as markdown in a monospace box. Multi-line text with
|
|
|
319
402
|
},
|
|
320
403
|
});
|
|
321
404
|
|
|
322
|
-
|
|
323
|
-
// evaluated while the paths Pi resolved at boot still exist stays in memory
|
|
324
|
-
// for the process lifetime, so later on-disk dependency churn (e.g. `pnpm
|
|
325
|
-
// install --force` replacing the store mid-session) can no longer poison
|
|
326
|
-
// jiti's graph cache. Swallowed failure is safe: the first real call
|
|
327
|
-
// re-imports and surfaces it through loadQuestionnaireSession's structured
|
|
328
|
-
// envelope. unref keeps the timer from holding a non-TUI embedder's process
|
|
329
|
-
// open.
|
|
330
|
-
const timer = setTimeout(() => void loadQuestionnaireSession().catch(() => undefined), PREWARM_DELAY_MS);
|
|
331
|
-
timer.unref?.();
|
|
405
|
+
prewarmSessionGraph();
|
|
332
406
|
}
|
|
333
407
|
|
|
334
408
|
export { buildQuestionnaireResponse, buildToolResult };
|
|
@@ -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
|