@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.
Files changed (389) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  17. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  18. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
  19. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  20. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  21. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  24. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
  25. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  26. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  27. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  28. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  29. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  30. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  32. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  33. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
  34. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  35. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  51. package/node_modules/@narumitw/pi-btw/README.md +119 -103
  52. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  53. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  54. package/node_modules/@narumitw/pi-btw/package.json +13 -9
  55. package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
  56. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  57. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  58. package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
  59. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  60. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  61. package/node_modules/pi-lens/CHANGELOG.md +1004 -0
  62. package/node_modules/pi-lens/README.md +11 -3
  63. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  64. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  65. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
  66. package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
  67. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  68. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  69. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  70. package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
  71. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  72. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  73. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  74. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  75. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  76. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  77. package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
  78. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  79. package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
  80. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  81. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  82. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  83. package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
  84. package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
  85. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  86. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  87. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  88. package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
  89. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
  90. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  91. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
  93. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
  94. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  95. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  129. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  130. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  131. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  132. package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
  133. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  134. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  135. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  136. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  137. package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
  138. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  139. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  140. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  141. package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
  142. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  143. package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
  144. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
  145. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  146. package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
  147. package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
  148. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  149. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  150. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  151. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  152. package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
  153. package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
  154. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  155. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  156. package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
  157. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  158. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  159. package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
  160. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  161. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
  162. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  163. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
  164. package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
  165. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
  166. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  167. package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
  168. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  169. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  170. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  171. package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
  172. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  173. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -0
  175. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  176. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
  177. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  178. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  179. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  180. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  181. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
  182. package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
  183. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  184. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  185. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  186. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  187. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  188. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  189. package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
  190. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  191. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  195. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  198. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  199. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  200. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  201. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  204. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  205. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  206. package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  217. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  218. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  219. package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
  220. package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
  221. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  222. package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
  223. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  224. package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
  225. package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
  226. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  227. package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
  228. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  229. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  230. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  233. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  234. package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
  235. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  236. package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
  237. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  238. package/node_modules/pi-lens/dist/index.js +64677 -49484
  239. package/node_modules/pi-lens/dist/mcp/server.js +8 -2
  240. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  241. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  242. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  243. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
  244. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
  245. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  246. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  247. package/node_modules/pi-lens/docs/agent-guide.md +44 -14
  248. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
  249. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  250. package/node_modules/pi-lens/docs/features.md +33 -6
  251. package/node_modules/pi-lens/docs/globalconfig.md +14 -0
  252. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  253. package/node_modules/pi-lens/docs/mcp.md +2 -2
  254. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  255. package/node_modules/pi-lens/docs/settings.md +25 -1
  256. package/node_modules/pi-lens/docs/usage.md +24 -5
  257. package/node_modules/pi-lens/docs/word-index.md +35 -0
  258. package/node_modules/pi-lens/package.json +10 -6
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  319. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  320. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  321. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  322. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  323. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  324. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
  325. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  326. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  327. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -11
  328. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  329. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
  330. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
  331. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  332. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  333. package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
  334. package/node_modules/pi-mcp-adapter/README.md +51 -6
  335. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  336. package/node_modules/pi-mcp-adapter/cli.js +188 -5
  337. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  338. package/node_modules/pi-mcp-adapter/config.ts +28 -10
  339. package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
  340. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  341. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  342. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  343. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  344. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  345. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  346. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  347. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  348. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  349. package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
  350. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  351. package/node_modules/pi-mcp-adapter/package.json +4 -1
  352. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  353. package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
  354. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  355. package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
  356. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  357. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  358. package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
  359. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  360. package/node_modules/pi-mcp-adapter/types.ts +95 -10
  361. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  362. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  363. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  364. package/node_modules/pi-web-access/README.md +38 -14
  365. package/node_modules/pi-web-access/anysearch.ts +4 -2
  366. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  367. package/node_modules/pi-web-access/brave.ts +16 -4
  368. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  369. package/node_modules/pi-web-access/curator-page.ts +8 -3
  370. package/node_modules/pi-web-access/curator-server.ts +5 -1
  371. package/node_modules/pi-web-access/exa.ts +18 -7
  372. package/node_modules/pi-web-access/extract.ts +134 -37
  373. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  374. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  375. package/node_modules/pi-web-access/gemini-search.ts +33 -7
  376. package/node_modules/pi-web-access/index.ts +160 -104
  377. package/node_modules/pi-web-access/package.json +2 -2
  378. package/node_modules/pi-web-access/page-query.ts +4 -2
  379. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  380. package/node_modules/pi-web-access/parallel.ts +2 -2
  381. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  382. package/node_modules/pi-web-access/serper.ts +211 -0
  383. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  384. package/node_modules/pi-web-access/summary-review.ts +51 -23
  385. package/node_modules/pi-web-access/tavily.ts +16 -4
  386. package/node_modules/pi-web-access/utils.ts +87 -0
  387. package/node_modules/pi-web-access/valyu.ts +199 -0
  388. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  389. 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
- /** Sum of lifetime usage components, or 0 if undefined. */
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("git", ["commit", "--no-verify", "-m", commitMsg], {
138
- cwd: worktree.path,
139
- stdio: "pipe",
140
- timeout: 10000,
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
  ![Single question in the dialog: the tab strip reads Feature Type, Design Tab, Testing, Release, Submit; the question Which real development task are we planning right now? sits above four numbered options — Bug fix (Recommended), New feature, Refactor, Perf tuning — each with a one-line description, followed by the appended Type something. row](https://raw.githubusercontent.com/juicesharp/rpiv-mono/main/packages/rpiv-ask-user-question/docs/single-question.jpg)
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 { loadConfig, resolveCollapseKey, validateGuidanceFields } from "./config.js";
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 function registerAskUserQuestionTool(pi: ExtensionAPI): void {
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 !== "off" && typeof ctx.ui.onTerminalInput === "function") {
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?.(`ask_user_question hidden — press ${collapseKey} to reopen`, "info");
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
- One known rough edge: the footer hint line inside the dialog always reads `Ctrl+] to
87
- collapse` and does not interpolate a custom `collapseKey`. The one-shot notification you
88
- get when the dialog first collapses *does* name your configured key.
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"` and the host exposes raw terminal input |
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": "Ctrl+] to collapse",
16
- "hint.expand_line": "Ctrl+] to expand · Esc to cancel",
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": "Ctrl+] 折叠",
18
- "hint.expand_line": "Ctrl+] 展开 · Esc 取消",
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.5.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.5.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 { COLLAPSED_HINT } from "../view/dialog-builder.js";
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 Ctrl+] still routes here to expand.
110
- const collapsedRender = (_width: number): string[] => [
111
- theme.fg("dim", ` ${t("hint.expand_line", COLLAPSED_HINT)} `),
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
- this.overlayHandle?.setHidden(effect.hidden);
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 shrinks to a single hint row so the agent transcript
29
- * behind the bottom-anchored overlay becomes readable. Toggled by `Ctrl+]` from any state;
30
- * while true, every keystroke except cancel is swallowed (see `routeKey`). The overlay
31
- * stays focused so the expand key never falls through to an underlying overlay (e.g.
32
- * `/btw`) — that focus-restore property is the entire reason we render a collapsed row
33
- * instead of calling `OverlayHandle.setHidden(true)`.
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
  }