@herbertgao/pi-extensions 2026.8.1 → 2026.8.3

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 (403) hide show
  1. package/README.md +13 -3
  2. package/node_modules/@czottmann/pi-automode/README.md +16 -2
  3. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +35 -0
  4. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +18 -9
  5. package/node_modules/@czottmann/pi-automode/docs/defaults.md +6 -2
  6. package/node_modules/@czottmann/pi-automode/examples/automode.local.json +6 -0
  7. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +11 -7
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +100 -0
  9. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +32 -0
  10. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +78 -3
  11. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +16 -4
  12. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +23 -1
  13. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +22 -1
  14. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/types.ts +18 -2
  15. package/node_modules/@czottmann/pi-automode/package.json +4 -4
  16. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +5 -0
  17. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +5 -0
  18. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +5 -0
  19. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +5 -0
  20. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +5 -0
  21. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +5 -0
  22. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail-frontmatter.cjs +23 -0
  23. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail.mjs +99 -0
  24. package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +30 -0
  25. package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +16 -0
  26. package/node_modules/@dietrichgebert/ponytail/AGENTS.md +32 -0
  27. package/node_modules/@dietrichgebert/ponytail/LICENSE +21 -0
  28. package/node_modules/@dietrichgebert/ponytail/README.es.md +298 -0
  29. package/node_modules/@dietrichgebert/ponytail/README.ko.md +315 -0
  30. package/node_modules/@dietrichgebert/ponytail/README.md +351 -0
  31. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-3model.svg +21 -0
  32. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-agentic.svg +62 -0
  33. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.png +0 -0
  34. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.svg +115 -0
  35. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt-dark.svg +27 -0
  36. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt.svg +27 -0
  37. package/node_modules/@dietrichgebert/ponytail/assets/logo.png +0 -0
  38. package/node_modules/@dietrichgebert/ponytail/assets/social-preview.png +0 -0
  39. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-es.png +0 -0
  40. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-ko.png +0 -0
  41. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner.png +0 -0
  42. package/node_modules/@dietrichgebert/ponytail/hooks/claude-codex-hooks.json +44 -0
  43. package/node_modules/@dietrichgebert/ponytail/hooks/copilot-hooks.json +21 -0
  44. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +96 -0
  45. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-config.js +169 -0
  46. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +98 -0
  47. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-mode-tracker.js +130 -0
  48. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-runtime.js +85 -0
  49. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-statusline.ps1 +24 -0
  50. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-statusline.sh +18 -0
  51. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-subagent.js +77 -0
  52. package/node_modules/@dietrichgebert/ponytail/hooks/qoder-hooks.json +26 -0
  53. package/node_modules/@dietrichgebert/ponytail/package.json +47 -0
  54. package/node_modules/@dietrichgebert/ponytail/pi-extension/index.js +211 -0
  55. package/node_modules/@dietrichgebert/ponytail/pi-extension/package.json +8 -0
  56. package/node_modules/@dietrichgebert/ponytail/scripts/uninstall.js +60 -0
  57. package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +120 -0
  58. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +41 -0
  59. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +44 -0
  60. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +50 -0
  61. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +71 -0
  62. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +57 -0
  63. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +6 -10
  64. package/node_modules/@herbertgao/pi-cc-extensions/README.md +5 -9
  65. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +101 -0
  66. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +1 -17
  67. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +15 -9
  68. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{utils/agent-summary.ts → feature/agent-summary/core.ts} +37 -35
  69. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-summary.ts → agent-summary/index.ts} +3 -3
  70. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -83
  71. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +1 -1
  72. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference.ts → reference/index.ts} +1 -1
  73. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-autocomplete.ts → reference/subagent.ts} +19 -9
  74. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-startup-header.ts → shell/startup-header.ts} +1 -1
  75. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{working-message.ts → shell/working-message.ts} +5 -2
  76. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +32 -28
  77. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +452 -94
  78. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +727 -0
  79. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +66 -776
  80. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{feature → renderer}/markdown-enhance.ts +134 -11
  81. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +130 -0
  82. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-interaction.ts → mouse/interaction.ts} +90 -568
  83. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +144 -0
  84. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +143 -0
  85. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +195 -0
  86. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ansi-utils.ts +56 -1
  87. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-renderer.ts +4 -4
  88. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/line-width-safety.ts +1 -1
  89. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +12 -0
  90. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/write-execution.ts +1 -2
  91. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-grouping.ts → tool/grouping.ts} +27 -10
  92. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-result.ts → tool/result.ts} +4 -13
  93. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +91 -0
  94. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +0 -27
  95. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  96. package/node_modules/@herbertgao/pi-cc-extensions/themes/cc-dark.json +1 -0
  97. package/node_modules/@herbertgao/pi-cc-extensions/themes/cc-light.json +2 -2
  98. package/node_modules/@herbertgao/pi-stash/README.md +9 -9
  99. package/node_modules/@herbertgao/pi-stash/package.json +1 -1
  100. package/node_modules/@herbertgao/pi-stash/src/index.ts +2 -1
  101. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +20 -0
  102. package/node_modules/@herbertgao/pi-subagents/README.md +6 -2
  103. package/node_modules/@herbertgao/pi-subagents/package.json +3 -3
  104. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +1 -0
  105. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +3 -1
  106. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +85 -11
  107. package/node_modules/@herbertgao/pi-subagents/src/index.ts +61 -39
  108. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +26 -35
  109. package/node_modules/@herbertgao/pi-subagents/src/prompts.ts +15 -1
  110. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +12 -0
  111. package/node_modules/@herbertgao/pi-subagents/src/types.ts +2 -0
  112. package/node_modules/@narumitw/pi-btw/LICENSE +21 -0
  113. package/node_modules/@narumitw/pi-btw/README.md +210 -0
  114. package/node_modules/@narumitw/pi-btw/package.json +53 -0
  115. package/node_modules/@narumitw/pi-btw/src/bring-to-main.ts +632 -0
  116. package/node_modules/@narumitw/pi-btw/src/btw.ts +996 -0
  117. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +339 -0
  118. package/node_modules/@narumitw/pi-btw/src/index.ts +1 -0
  119. package/node_modules/@narumitw/pi-btw/src/menu.ts +296 -0
  120. package/node_modules/@narumitw/pi-btw/src/settings.ts +237 -0
  121. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +238 -0
  122. package/node_modules/@narumitw/pi-btw/src/text.ts +10 -0
  123. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +620 -0
  124. package/node_modules/@pi-plugins/fast-mode/LICENSE +21 -0
  125. package/node_modules/@pi-plugins/fast-mode/README.md +92 -0
  126. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts +6 -0
  127. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -0
  128. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +2026 -0
  129. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -0
  130. package/node_modules/@pi-plugins/fast-mode/package.json +73 -0
  131. package/node_modules/pi-mcp-adapter/CHANGELOG.md +78 -0
  132. package/node_modules/pi-mcp-adapter/README.md +80 -16
  133. package/node_modules/pi-mcp-adapter/agent-dir.ts +40 -0
  134. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +377 -0
  135. package/node_modules/pi-mcp-adapter/commands.ts +21 -9
  136. package/node_modules/pi-mcp-adapter/config.ts +110 -49
  137. package/node_modules/pi-mcp-adapter/direct-tools.ts +40 -27
  138. package/node_modules/pi-mcp-adapter/elicitation-handler.ts +37 -36
  139. package/node_modules/pi-mcp-adapter/errors.ts +21 -17
  140. package/node_modules/pi-mcp-adapter/host-html-template.ts +13 -7
  141. package/node_modules/pi-mcp-adapter/index.ts +52 -17
  142. package/node_modules/pi-mcp-adapter/init.ts +9 -7
  143. package/node_modules/pi-mcp-adapter/json-schema-validator.ts +2 -2
  144. package/node_modules/pi-mcp-adapter/lifecycle.ts +8 -8
  145. package/node_modules/pi-mcp-adapter/logger.ts +1 -1
  146. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +122 -25
  147. package/node_modules/pi-mcp-adapter/mcp-auth.ts +120 -2
  148. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +138 -61
  149. package/node_modules/pi-mcp-adapter/mcp-code.ts +13 -11
  150. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +62 -17
  151. package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +15 -11
  152. package/node_modules/pi-mcp-adapter/mcp-panel.ts +34 -15
  153. package/node_modules/pi-mcp-adapter/mcp-probe.ts +131 -34
  154. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +2 -2
  155. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +7 -4
  156. package/node_modules/pi-mcp-adapter/mcp-trace.ts +59 -64
  157. package/node_modules/pi-mcp-adapter/metadata-cache.ts +34 -26
  158. package/node_modules/pi-mcp-adapter/npx-resolver.ts +6 -4
  159. package/node_modules/pi-mcp-adapter/oauth-handler.ts +1 -1
  160. package/node_modules/pi-mcp-adapter/oauth.ts +48 -0
  161. package/node_modules/pi-mcp-adapter/onboarding-state.ts +19 -11
  162. package/node_modules/pi-mcp-adapter/package.json +20 -9
  163. package/node_modules/pi-mcp-adapter/panel-keys.ts +20 -4
  164. package/node_modules/pi-mcp-adapter/prompts.ts +2 -2
  165. package/node_modules/pi-mcp-adapter/proxy-modes.ts +56 -21
  166. package/node_modules/pi-mcp-adapter/sampling-handler.ts +22 -15
  167. package/node_modules/pi-mcp-adapter/search-ranking.ts +2 -1
  168. package/node_modules/pi-mcp-adapter/server-manager.ts +165 -113
  169. package/node_modules/pi-mcp-adapter/session-recovery.ts +11 -12
  170. package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +3 -3
  171. package/node_modules/pi-mcp-adapter/state.ts +3 -1
  172. package/node_modules/pi-mcp-adapter/tool-approval.ts +67 -4
  173. package/node_modules/pi-mcp-adapter/tool-metadata.ts +6 -5
  174. package/node_modules/pi-mcp-adapter/tool-registrar.ts +187 -3
  175. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +14 -2
  176. package/node_modules/pi-mcp-adapter/ts-shape.ts +9 -2
  177. package/node_modules/pi-mcp-adapter/types.ts +111 -22
  178. package/node_modules/pi-mcp-adapter/ui-app-bridge-helpers.ts +40 -0
  179. package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +28 -19
  180. package/node_modules/pi-mcp-adapter/ui-server.ts +90 -58
  181. package/node_modules/pi-mcp-adapter/ui-session.ts +16 -14
  182. package/node_modules/pi-mcp-adapter/unix-socket-transport.ts +4 -4
  183. package/node_modules/pi-mcp-adapter/utils.ts +23 -9
  184. package/node_modules/pi-web-access/CHANGELOG.md +45 -0
  185. package/node_modules/pi-web-access/README.md +104 -29
  186. package/node_modules/pi-web-access/bocha.ts +221 -0
  187. package/node_modules/pi-web-access/curator-page.ts +113 -12
  188. package/node_modules/pi-web-access/curator-server.ts +56 -20
  189. package/node_modules/pi-web-access/datalab-pdf-extract.ts +568 -0
  190. package/node_modules/pi-web-access/duckduckgo.ts +123 -0
  191. package/node_modules/pi-web-access/extract.ts +288 -143
  192. package/node_modules/pi-web-access/feature-config.ts +29 -0
  193. package/node_modules/pi-web-access/gemini-api.ts +4 -0
  194. package/node_modules/pi-web-access/gemini-search.ts +45 -12
  195. package/node_modules/pi-web-access/gemini-web.ts +68 -3
  196. package/node_modules/pi-web-access/index.ts +160 -71
  197. package/node_modules/pi-web-access/jina-search.ts +284 -0
  198. package/node_modules/pi-web-access/kagi.ts +12 -12
  199. package/node_modules/pi-web-access/openai-search.ts +12 -2
  200. package/node_modules/pi-web-access/package.json +4 -3
  201. package/node_modules/pi-web-access/pdf-extract.ts +327 -228
  202. package/node_modules/pi-web-access/storage.ts +440 -2
  203. package/node_modules/pi-web-access/summary-model-scope.ts +1 -1
  204. package/node_modules/pi-web-access/summary-review.ts +4 -2
  205. package/node_modules/pi-web-access/video-extract.ts +10 -8
  206. package/node_modules/pi-web-access/xai-search.ts +12 -2
  207. package/node_modules/pi-web-access/youtube-extract.ts +2 -1
  208. package/node_modules/remote-pi/LICENSE +21 -0
  209. package/node_modules/remote-pi/README.md +675 -0
  210. package/node_modules/remote-pi/dist/actions/handlers.d.ts +122 -0
  211. package/node_modules/remote-pi/dist/actions/handlers.js +160 -0
  212. package/node_modules/remote-pi/dist/actions/handlers.js.map +1 -0
  213. package/node_modules/remote-pi/dist/actions/registry.d.ts +39 -0
  214. package/node_modules/remote-pi/dist/actions/registry.js +58 -0
  215. package/node_modules/remote-pi/dist/actions/registry.js.map +1 -0
  216. package/node_modules/remote-pi/dist/bin/supervisord.d.ts +2 -0
  217. package/node_modules/remote-pi/dist/bin/supervisord.js +86 -0
  218. package/node_modules/remote-pi/dist/bin/supervisord.js.map +1 -0
  219. package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +58 -0
  220. package/node_modules/remote-pi/dist/commands/builtin_mirror.js +71 -0
  221. package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +1 -0
  222. package/node_modules/remote-pi/dist/commands/list_commands.d.ts +60 -0
  223. package/node_modules/remote-pi/dist/commands/list_commands.js +73 -0
  224. package/node_modules/remote-pi/dist/commands/list_commands.js.map +1 -0
  225. package/node_modules/remote-pi/dist/config.d.ts +62 -0
  226. package/node_modules/remote-pi/dist/config.js +115 -0
  227. package/node_modules/remote-pi/dist/config.js.map +1 -0
  228. package/node_modules/remote-pi/dist/daemon/client.d.ts +20 -0
  229. package/node_modules/remote-pi/dist/daemon/client.js +131 -0
  230. package/node_modules/remote-pi/dist/daemon/client.js.map +1 -0
  231. package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +176 -0
  232. package/node_modules/remote-pi/dist/daemon/control_protocol.js +63 -0
  233. package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +1 -0
  234. package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +45 -0
  235. package/node_modules/remote-pi/dist/daemon/cron_log.js +72 -0
  236. package/node_modules/remote-pi/dist/daemon/cron_log.js.map +1 -0
  237. package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +80 -0
  238. package/node_modules/remote-pi/dist/daemon/cron_registry.js +194 -0
  239. package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +1 -0
  240. package/node_modules/remote-pi/dist/daemon/id.d.ts +24 -0
  241. package/node_modules/remote-pi/dist/daemon/id.js +36 -0
  242. package/node_modules/remote-pi/dist/daemon/id.js.map +1 -0
  243. package/node_modules/remote-pi/dist/daemon/install.d.ts +170 -0
  244. package/node_modules/remote-pi/dist/daemon/install.js +547 -0
  245. package/node_modules/remote-pi/dist/daemon/install.js.map +1 -0
  246. package/node_modules/remote-pi/dist/daemon/registry.d.ts +63 -0
  247. package/node_modules/remote-pi/dist/daemon/registry.js +153 -0
  248. package/node_modules/remote-pi/dist/daemon/registry.js.map +1 -0
  249. package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +194 -0
  250. package/node_modules/remote-pi/dist/daemon/rpc_child.js +389 -0
  251. package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +1 -0
  252. package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +93 -0
  253. package/node_modules/remote-pi/dist/daemon/supervisor.js +596 -0
  254. package/node_modules/remote-pi/dist/daemon/supervisor.js.map +1 -0
  255. package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +26 -0
  256. package/node_modules/remote-pi/dist/extension_ui_bridge.js +341 -0
  257. package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +1 -0
  258. package/node_modules/remote-pi/dist/index.d.ts +213 -0
  259. package/node_modules/remote-pi/dist/index.js +4996 -0
  260. package/node_modules/remote-pi/dist/index.js.map +1 -0
  261. package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +10 -0
  262. package/node_modules/remote-pi/dist/mcp/mesh_result.js +42 -0
  263. package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +1 -0
  264. package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +16 -0
  265. package/node_modules/remote-pi/dist/mcp/mesh_server.js +327 -0
  266. package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +1 -0
  267. package/node_modules/remote-pi/dist/mesh/canonical.d.ts +30 -0
  268. package/node_modules/remote-pi/dist/mesh/canonical.js +61 -0
  269. package/node_modules/remote-pi/dist/mesh/canonical.js.map +1 -0
  270. package/node_modules/remote-pi/dist/mesh/client.d.ts +18 -0
  271. package/node_modules/remote-pi/dist/mesh/client.js +125 -0
  272. package/node_modules/remote-pi/dist/mesh/client.js.map +1 -0
  273. package/node_modules/remote-pi/dist/mesh/encoding.d.ts +38 -0
  274. package/node_modules/remote-pi/dist/mesh/encoding.js +216 -0
  275. package/node_modules/remote-pi/dist/mesh/encoding.js.map +1 -0
  276. package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +73 -0
  277. package/node_modules/remote-pi/dist/mesh/self_revoke.js +401 -0
  278. package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +1 -0
  279. package/node_modules/remote-pi/dist/mesh/siblings.d.ts +49 -0
  280. package/node_modules/remote-pi/dist/mesh/siblings.js +159 -0
  281. package/node_modules/remote-pi/dist/mesh/siblings.js.map +1 -0
  282. package/node_modules/remote-pi/dist/mesh/types.d.ts +34 -0
  283. package/node_modules/remote-pi/dist/mesh/types.js +11 -0
  284. package/node_modules/remote-pi/dist/mesh/types.js.map +1 -0
  285. package/node_modules/remote-pi/dist/mesh/verify.d.ts +17 -0
  286. package/node_modules/remote-pi/dist/mesh/verify.js +86 -0
  287. package/node_modules/remote-pi/dist/mesh/verify.js.map +1 -0
  288. package/node_modules/remote-pi/dist/pairing/crypto.d.ts +8 -0
  289. package/node_modules/remote-pi/dist/pairing/crypto.js +22 -0
  290. package/node_modules/remote-pi/dist/pairing/crypto.js.map +1 -0
  291. package/node_modules/remote-pi/dist/pairing/handshake.d.ts +28 -0
  292. package/node_modules/remote-pi/dist/pairing/handshake.js +113 -0
  293. package/node_modules/remote-pi/dist/pairing/handshake.js.map +1 -0
  294. package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +16 -0
  295. package/node_modules/remote-pi/dist/pairing/noise-sha256.js +103 -0
  296. package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +1 -0
  297. package/node_modules/remote-pi/dist/pairing/qr.d.ts +59 -0
  298. package/node_modules/remote-pi/dist/pairing/qr.js +125 -0
  299. package/node_modules/remote-pi/dist/pairing/qr.js.map +1 -0
  300. package/node_modules/remote-pi/dist/pairing/storage.d.ts +120 -0
  301. package/node_modules/remote-pi/dist/pairing/storage.js +604 -0
  302. package/node_modules/remote-pi/dist/pairing/storage.js.map +1 -0
  303. package/node_modules/remote-pi/dist/protocol/codec.d.ts +7 -0
  304. package/node_modules/remote-pi/dist/protocol/codec.js +49 -0
  305. package/node_modules/remote-pi/dist/protocol/codec.js.map +1 -0
  306. package/node_modules/remote-pi/dist/protocol/types.d.ts +393 -0
  307. package/node_modules/remote-pi/dist/protocol/types.js +2 -0
  308. package/node_modules/remote-pi/dist/protocol/types.js.map +1 -0
  309. package/node_modules/remote-pi/dist/rooms.d.ts +29 -0
  310. package/node_modules/remote-pi/dist/rooms.js +57 -0
  311. package/node_modules/remote-pi/dist/rooms.js.map +1 -0
  312. package/node_modules/remote-pi/dist/session/address.d.ts +49 -0
  313. package/node_modules/remote-pi/dist/session/address.js +58 -0
  314. package/node_modules/remote-pi/dist/session/address.js.map +1 -0
  315. package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +55 -0
  316. package/node_modules/remote-pi/dist/session/agent_bridge.js +146 -0
  317. package/node_modules/remote-pi/dist/session/agent_bridge.js.map +1 -0
  318. package/node_modules/remote-pi/dist/session/bridge.d.ts +37 -0
  319. package/node_modules/remote-pi/dist/session/bridge.js +130 -0
  320. package/node_modules/remote-pi/dist/session/bridge.js.map +1 -0
  321. package/node_modules/remote-pi/dist/session/broker.d.ts +189 -0
  322. package/node_modules/remote-pi/dist/session/broker.js +507 -0
  323. package/node_modules/remote-pi/dist/session/broker.js.map +1 -0
  324. package/node_modules/remote-pi/dist/session/broker_remote.d.ts +74 -0
  325. package/node_modules/remote-pi/dist/session/broker_remote.js +583 -0
  326. package/node_modules/remote-pi/dist/session/broker_remote.js.map +1 -0
  327. package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +33 -0
  328. package/node_modules/remote-pi/dist/session/cwd_lock.js +126 -0
  329. package/node_modules/remote-pi/dist/session/cwd_lock.js.map +1 -0
  330. package/node_modules/remote-pi/dist/session/envelope.d.ts +33 -0
  331. package/node_modules/remote-pi/dist/session/envelope.js +117 -0
  332. package/node_modules/remote-pi/dist/session/envelope.js.map +1 -0
  333. package/node_modules/remote-pi/dist/session/global_config.d.ts +33 -0
  334. package/node_modules/remote-pi/dist/session/global_config.js +73 -0
  335. package/node_modules/remote-pi/dist/session/global_config.js.map +1 -0
  336. package/node_modules/remote-pi/dist/session/ipc.d.ts +27 -0
  337. package/node_modules/remote-pi/dist/session/ipc.js +22 -0
  338. package/node_modules/remote-pi/dist/session/ipc.js.map +1 -0
  339. package/node_modules/remote-pi/dist/session/leader_election.d.ts +32 -0
  340. package/node_modules/remote-pi/dist/session/leader_election.js +106 -0
  341. package/node_modules/remote-pi/dist/session/leader_election.js.map +1 -0
  342. package/node_modules/remote-pi/dist/session/local_config.d.ts +55 -0
  343. package/node_modules/remote-pi/dist/session/local_config.js +157 -0
  344. package/node_modules/remote-pi/dist/session/local_config.js.map +1 -0
  345. package/node_modules/remote-pi/dist/session/mesh_message.d.ts +20 -0
  346. package/node_modules/remote-pi/dist/session/mesh_message.js +28 -0
  347. package/node_modules/remote-pi/dist/session/mesh_message.js.map +1 -0
  348. package/node_modules/remote-pi/dist/session/mesh_node.d.ts +116 -0
  349. package/node_modules/remote-pi/dist/session/mesh_node.js +573 -0
  350. package/node_modules/remote-pi/dist/session/mesh_node.js.map +1 -0
  351. package/node_modules/remote-pi/dist/session/peer.d.ts +139 -0
  352. package/node_modules/remote-pi/dist/session/peer.js +451 -0
  353. package/node_modules/remote-pi/dist/session/peer.js.map +1 -0
  354. package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +13 -0
  355. package/node_modules/remote-pi/dist/session/peer_inventory.js +48 -0
  356. package/node_modules/remote-pi/dist/session/peer_inventory.js.map +1 -0
  357. package/node_modules/remote-pi/dist/session/peer_limits.d.ts +9 -0
  358. package/node_modules/remote-pi/dist/session/peer_limits.js +21 -0
  359. package/node_modules/remote-pi/dist/session/peer_limits.js.map +1 -0
  360. package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +39 -0
  361. package/node_modules/remote-pi/dist/session/setup_wizard.js +63 -0
  362. package/node_modules/remote-pi/dist/session/setup_wizard.js.map +1 -0
  363. package/node_modules/remote-pi/dist/session/tool_gate.d.ts +5 -0
  364. package/node_modules/remote-pi/dist/session/tool_gate.js +11 -0
  365. package/node_modules/remote-pi/dist/session/tool_gate.js.map +1 -0
  366. package/node_modules/remote-pi/dist/session/tools.d.ts +24 -0
  367. package/node_modules/remote-pi/dist/session/tools.js +252 -0
  368. package/node_modules/remote-pi/dist/session/tools.js.map +1 -0
  369. package/node_modules/remote-pi/dist/session/wizard.d.ts +13 -0
  370. package/node_modules/remote-pi/dist/session/wizard.js +20 -0
  371. package/node_modules/remote-pi/dist/session/wizard.js.map +1 -0
  372. package/node_modules/remote-pi/dist/settings.d.ts +15 -0
  373. package/node_modules/remote-pi/dist/settings.js +52 -0
  374. package/node_modules/remote-pi/dist/settings.js.map +1 -0
  375. package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +37 -0
  376. package/node_modules/remote-pi/dist/transport/peer_channel.js +98 -0
  377. package/node_modules/remote-pi/dist/transport/peer_channel.js.map +1 -0
  378. package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +29 -0
  379. package/node_modules/remote-pi/dist/transport/pi_forward_client.js +62 -0
  380. package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +1 -0
  381. package/node_modules/remote-pi/dist/transport/relay_client.d.ts +91 -0
  382. package/node_modules/remote-pi/dist/transport/relay_client.js +206 -0
  383. package/node_modules/remote-pi/dist/transport/relay_client.js.map +1 -0
  384. package/node_modules/remote-pi/dist/ui/footer.d.ts +32 -0
  385. package/node_modules/remote-pi/dist/ui/footer.js +34 -0
  386. package/node_modules/remote-pi/dist/ui/footer.js.map +1 -0
  387. package/node_modules/remote-pi/docs/daemon.md +289 -0
  388. package/node_modules/remote-pi/package.json +88 -0
  389. package/node_modules/remote-pi/service-templates/launchd.plist.template +35 -0
  390. package/node_modules/remote-pi/service-templates/systemd.service.template +19 -0
  391. package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +10 -0
  392. package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +38 -0
  393. package/node_modules/remote-pi/skills/agent-network/SKILL.md +310 -0
  394. package/package.json +37 -12
  395. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-diff/write-display-utils.ts +0 -29
  396. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference-core.ts → reference/session.ts} +0 -0
  397. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-aliases.ts → shell/aliases.ts} +0 -0
  398. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ATTRIBUTION.md +0 -0
  399. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-presentation.ts +0 -0
  400. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/index.ts +0 -0
  401. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/render-utils.ts +0 -0
  402. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/shiki-highlight.ts +0 -0
  403. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/types.ts +0 -0
@@ -0,0 +1,620 @@
1
+ import type { AssistantMessage } from "@earendil-works/pi-ai";
2
+ import {
3
+ AssistantMessageComponent,
4
+ getMarkdownTheme,
5
+ type KeybindingsManager,
6
+ type Theme,
7
+ UserMessageComponent,
8
+ } from "@earendil-works/pi-coding-agent";
9
+ import {
10
+ type Component,
11
+ CURSOR_MARKER,
12
+ Editor,
13
+ type EditorTheme,
14
+ type Focusable,
15
+ Key,
16
+ Loader,
17
+ Markdown,
18
+ matchesKey,
19
+ type TUI,
20
+ truncateToWidth,
21
+ visibleWidth,
22
+ } from "@earendil-works/pi-tui";
23
+ import type { BtwThinkingLevel, SideThreadTurn } from "./side-thread.js";
24
+ import { sanitizeSingleLine } from "./text.js";
25
+
26
+ const TRANSCRIPT_CHROME_LINES = 2;
27
+ const MAX_STEERING_DISPLAY_LINES = 3;
28
+ const OSC133_MARKERS = ["\u001b]133;A\u0007", "\u001b]133;B\u0007", "\u001b]133;C\u0007"];
29
+ // Pi renders a spacer above the custom component and a two-line built-in footer below it.
30
+ const RESERVED_APP_LINES = 3;
31
+
32
+ export type TranscriptPagerAction =
33
+ | { kind: "submit"; question: string }
34
+ | { kind: "bringToMain"; questionDraft: string }
35
+ | { kind: "close" };
36
+
37
+ export interface BtwThinkingControl {
38
+ level: BtwThinkingLevel;
39
+ levels: readonly BtwThinkingLevel[];
40
+ keybindings: KeybindingsManager;
41
+ onChange: (level: BtwThinkingLevel) => void;
42
+ }
43
+
44
+ export interface BtwAnsweringViewOptions {
45
+ steering?: {
46
+ questions: readonly string[];
47
+ onSubmit: (question: string) => void;
48
+ thinking?: BtwThinkingControl;
49
+ };
50
+ }
51
+
52
+ export class BtwTranscriptPager implements Component, Focusable {
53
+ private readonly transcriptComponents: Component[];
54
+ private readonly editor: Editor;
55
+ private readonly canBringToMain: boolean;
56
+ private scrollOffset = 0;
57
+ private lastContentLineCount = 0;
58
+ private lastViewportHeight = 1;
59
+ private followBottom: boolean;
60
+ private warning: string | undefined;
61
+ private finished = false;
62
+ private isFocused = false;
63
+ private thinkingLevel: BtwThinkingLevel | undefined;
64
+
65
+ constructor(
66
+ private readonly tui: TUI,
67
+ private readonly theme: Theme,
68
+ turns: readonly SideThreadTurn[],
69
+ private readonly onAction: (action: TranscriptPagerAction) => void,
70
+ private readonly options: {
71
+ startAtBottom?: boolean;
72
+ initialQuestion?: string;
73
+ thinking?: BtwThinkingControl;
74
+ } = {},
75
+ ) {
76
+ this.transcriptComponents = buildTranscriptComponents(turns, this.theme);
77
+ this.canBringToMain = turns.some((turn) => turn.kind === "answered");
78
+ this.followBottom = options.startAtBottom ?? false;
79
+ this.thinkingLevel = options.thinking?.level;
80
+ const editorTheme: EditorTheme = {
81
+ borderColor: (text) => this.theme.fg("accent", text),
82
+ selectList: {
83
+ selectedPrefix: (text) => this.theme.fg("accent", text),
84
+ selectedText: (text) => this.theme.fg("accent", text),
85
+ description: (text) => this.theme.fg("muted", text),
86
+ scrollInfo: (text) => this.theme.fg("dim", text),
87
+ noMatch: (text) => this.theme.fg("warning", text),
88
+ },
89
+ };
90
+ this.editor = new Editor(this.tui, editorTheme);
91
+ if (options.initialQuestion) this.editor.setText(options.initialQuestion);
92
+ this.editor.onChange = () => {
93
+ this.warning = undefined;
94
+ };
95
+ this.editor.onSubmit = (text) => {
96
+ const question = text.trim();
97
+ if (!question) {
98
+ this.warning = "Question cannot be empty";
99
+ return;
100
+ }
101
+ this.finished = true;
102
+ this.onAction({ kind: "submit", question });
103
+ };
104
+ }
105
+
106
+ get focused(): boolean {
107
+ return this.isFocused;
108
+ }
109
+
110
+ set focused(value: boolean) {
111
+ this.isFocused = value;
112
+ this.editor.focused = value;
113
+ }
114
+
115
+ render(width: number): string[] {
116
+ const safeWidth = Math.max(1, width);
117
+ const editorLines = this.editor.render(safeWidth);
118
+ const availableRows = Math.max(1, this.tui.terminal.rows - RESERVED_APP_LINES);
119
+ const viewportHeight = Math.max(
120
+ 0,
121
+ availableRows - editorLines.length - TRANSCRIPT_CHROME_LINES,
122
+ );
123
+ const contentLines = renderTranscriptLines(this.transcriptComponents, safeWidth);
124
+ this.lastContentLineCount = contentLines.length;
125
+ this.lastViewportHeight = viewportHeight;
126
+ if (this.followBottom) this.scrollOffset = this.getMaxScrollOffset();
127
+ this.clampScrollOffset();
128
+
129
+ return fitComposerLayout(
130
+ renderSideThreadHeader(safeWidth, this.theme, this.thinkingLevel),
131
+ contentLines.slice(this.scrollOffset, this.scrollOffset + viewportHeight),
132
+ this.renderFooter(safeWidth),
133
+ editorLines,
134
+ availableRows,
135
+ );
136
+ }
137
+
138
+ handleInput(data: string): void {
139
+ if (this.finished) return;
140
+ if (matchesKey(data, Key.ctrl("c"))) {
141
+ this.finished = true;
142
+ this.onAction({ kind: "close" });
143
+ return;
144
+ }
145
+ if (this.canBringToMain && matchesKey(data, Key.ctrl("r"))) {
146
+ this.finished = true;
147
+ this.onAction({ kind: "bringToMain", questionDraft: this.editor.getExpandedText() });
148
+ return;
149
+ }
150
+ const thinking = this.options.thinking;
151
+ if (
152
+ thinking &&
153
+ thinking.levels.length > 1 &&
154
+ thinking.keybindings.matches(data, "app.thinking.cycle")
155
+ ) {
156
+ const currentIndex = thinking.levels.indexOf(this.thinkingLevel ?? thinking.level);
157
+ const nextLevel = thinking.levels[(currentIndex + 1) % thinking.levels.length];
158
+ if (nextLevel) {
159
+ this.thinkingLevel = nextLevel;
160
+ thinking.onChange(nextLevel);
161
+ this.warning = undefined;
162
+ this.tui.requestRender();
163
+ }
164
+ return;
165
+ }
166
+ if (matchesKey(data, Key.pageUp)) {
167
+ const previousOffset = this.scrollOffset;
168
+ this.scrollBy(-this.lastViewportHeight);
169
+ if (this.scrollOffset < previousOffset) this.followBottom = false;
170
+ this.tui.requestRender();
171
+ return;
172
+ }
173
+ if (matchesKey(data, Key.pageDown)) {
174
+ this.scrollBy(this.lastViewportHeight);
175
+ this.followBottom = this.scrollOffset >= this.getMaxScrollOffset();
176
+ this.tui.requestRender();
177
+ return;
178
+ }
179
+ this.editor.handleInput(data);
180
+ if (!this.finished) this.tui.requestRender();
181
+ }
182
+
183
+ invalidate(): void {
184
+ for (const component of this.transcriptComponents) component.invalidate();
185
+ this.editor.invalidate();
186
+ }
187
+
188
+ dispose(): void {
189
+ if (this.finished) return;
190
+ this.finished = true;
191
+ this.onAction({ kind: "close" });
192
+ }
193
+
194
+ private renderFooter(width: number): string {
195
+ if (this.warning) {
196
+ const warning = width < 32 ? "Empty • Ctrl+C" : `${this.warning} • Ctrl+C exit`;
197
+ return truncateToWidth(this.theme.fg("warning", warning), width);
198
+ }
199
+ const scrollable = this.getMaxScrollOffset() > 0;
200
+ const thinking = this.options.thinking;
201
+ const cycleHint =
202
+ thinking && thinking.levels.length > 1 && this.thinkingLevel
203
+ ? ` • thinking ${this.thinkingLevel} • ${thinkingKeyLabel(thinking.keybindings)} cycle`
204
+ : "";
205
+ const base = this.canBringToMain
206
+ ? "btw • Enter send • Ctrl+R bring to main • Ctrl+C exit"
207
+ : "btw • Enter send • Ctrl+C exit";
208
+ const fullBase = `${base}${cycleHint}`;
209
+ const fallbackBase = "btw • Enter • Ctrl+C";
210
+ const compactBase = this.canBringToMain ? "btw • Enter • Ctrl+R • Ctrl+C" : fallbackBase;
211
+ const compactWithThinking = `${compactBase}${cycleHint}`;
212
+ let hints =
213
+ visibleWidth(fullBase) <= width
214
+ ? fullBase
215
+ : visibleWidth(compactWithThinking) <= width
216
+ ? compactWithThinking
217
+ : visibleWidth(compactBase) <= width
218
+ ? compactBase
219
+ : fallbackBase;
220
+ if (scrollable) {
221
+ const history = ` • ${this.scrollOffset > 0 ? "↑ older" : "↓ newer"} • PgUp/PgDn history`;
222
+ const compactHistory = " • PgUp/PgDn";
223
+ const compactScrollable = this.canBringToMain
224
+ ? "Enter • Ctrl+R • Ctrl+C • PgUp/PgDn"
225
+ : `${fallbackBase}${compactHistory}`;
226
+ if (visibleWidth(`${hints}${history}`) <= width) {
227
+ hints += history;
228
+ } else if (visibleWidth(`${compactBase}${history}`) <= width) {
229
+ hints = `${compactBase}${history}`;
230
+ } else if (visibleWidth(`${hints}${compactHistory}`) <= width) {
231
+ hints += compactHistory;
232
+ } else if (visibleWidth(`${compactBase}${compactHistory}`) <= width) {
233
+ hints = `${compactBase}${compactHistory}`;
234
+ } else if (visibleWidth(compactScrollable) <= width) {
235
+ hints = compactScrollable;
236
+ }
237
+ }
238
+ return truncateToWidth(this.theme.fg("muted", hints), width);
239
+ }
240
+
241
+ private scrollBy(delta: number): void {
242
+ this.scrollOffset += delta;
243
+ this.clampScrollOffset();
244
+ }
245
+
246
+ private clampScrollOffset(): void {
247
+ this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, this.getMaxScrollOffset()));
248
+ }
249
+
250
+ private getMaxScrollOffset(): number {
251
+ return Math.max(0, this.lastContentLineCount - this.lastViewportHeight);
252
+ }
253
+ }
254
+
255
+ export class BtwAnsweringView implements Component, Focusable {
256
+ private readonly transcriptComponents: Component[];
257
+ private readonly loader: Loader;
258
+ private readonly editor: Editor | undefined;
259
+ private readonly controller = new AbortController();
260
+ private scrollOffset = 0;
261
+ private lastContentLineCount = 0;
262
+ private lastViewportHeight = 1;
263
+ private followBottom = true;
264
+ private warning: string | undefined;
265
+ private finished = false;
266
+ private isFocused = false;
267
+ private thinkingLevel: BtwThinkingLevel | undefined;
268
+
269
+ constructor(
270
+ private readonly tui: TUI,
271
+ private readonly theme: Theme,
272
+ turns: readonly SideThreadTurn[],
273
+ pendingQuestion: string,
274
+ private readonly onCancel: () => void,
275
+ thinkingLevel?: BtwThinkingLevel,
276
+ private readonly options: BtwAnsweringViewOptions = {},
277
+ ) {
278
+ this.transcriptComponents = buildTranscriptComponents(turns, this.theme, pendingQuestion);
279
+ this.thinkingLevel = options.steering?.thinking?.level ?? thinkingLevel;
280
+ this.loader = new Loader(
281
+ this.tui,
282
+ (text) => this.theme.fg("accent", text),
283
+ (text) => this.theme.fg("muted", text),
284
+ "Answering…",
285
+ );
286
+ if (options.steering) {
287
+ const editorTheme: EditorTheme = {
288
+ borderColor: (text) => this.theme.fg("accent", text),
289
+ selectList: {
290
+ selectedPrefix: (text) => this.theme.fg("accent", text),
291
+ selectedText: (text) => this.theme.fg("accent", text),
292
+ description: (text) => this.theme.fg("muted", text),
293
+ scrollInfo: (text) => this.theme.fg("dim", text),
294
+ noMatch: (text) => this.theme.fg("warning", text),
295
+ },
296
+ };
297
+ this.editor = new Editor(this.tui, editorTheme);
298
+ this.editor.onChange = () => {
299
+ this.warning = undefined;
300
+ };
301
+ this.editor.onSubmit = (text) => {
302
+ const question = text.trim();
303
+ if (!question) {
304
+ this.warning = "Question cannot be empty";
305
+ return;
306
+ }
307
+ options.steering?.onSubmit(question);
308
+ this.warning = undefined;
309
+ };
310
+ }
311
+ }
312
+
313
+ get focused(): boolean {
314
+ return this.isFocused;
315
+ }
316
+
317
+ set focused(value: boolean) {
318
+ this.isFocused = value;
319
+ if (this.editor) this.editor.focused = value;
320
+ }
321
+
322
+ get signal(): AbortSignal {
323
+ return this.controller.signal;
324
+ }
325
+
326
+ render(width: number): string[] {
327
+ const safeWidth = Math.max(1, width);
328
+ const availableRows = Math.max(1, this.tui.terminal.rows - RESERVED_APP_LINES);
329
+ const editorLines = this.editor?.render(safeWidth) ?? [];
330
+ const steeringCapacity = Math.max(
331
+ 0,
332
+ availableRows - editorLines.length - TRANSCRIPT_CHROME_LINES,
333
+ );
334
+ const steeringLines = renderSteeringLines(
335
+ this.options.steering?.questions ?? [],
336
+ safeWidth,
337
+ this.theme,
338
+ Math.min(MAX_STEERING_DISPLAY_LINES, steeringCapacity),
339
+ );
340
+ const viewportHeight = Math.max(
341
+ 0,
342
+ availableRows - editorLines.length - TRANSCRIPT_CHROME_LINES - steeringLines.length,
343
+ );
344
+ const contentLines = renderTranscriptLines(this.transcriptComponents, safeWidth);
345
+ this.lastContentLineCount = contentLines.length;
346
+ this.lastViewportHeight = viewportHeight;
347
+ if (this.followBottom) this.scrollOffset = this.getMaxScrollOffset();
348
+ this.clampScrollOffset();
349
+
350
+ return fitComposerLayout(
351
+ renderSideThreadHeader(safeWidth, this.theme, this.thinkingLevel),
352
+ contentLines.slice(this.scrollOffset, this.scrollOffset + viewportHeight),
353
+ this.renderFooter(safeWidth),
354
+ editorLines,
355
+ availableRows,
356
+ steeringLines,
357
+ );
358
+ }
359
+
360
+ handleInput(data: string): void {
361
+ if (this.finished) return;
362
+ if (matchesKey(data, Key.ctrl("c"))) {
363
+ this.finished = true;
364
+ this.loader.stop();
365
+ this.controller.abort();
366
+ this.onCancel();
367
+ return;
368
+ }
369
+ const thinking = this.options.steering?.thinking;
370
+ if (
371
+ thinking &&
372
+ thinking.levels.length > 1 &&
373
+ thinking.keybindings.matches(data, "app.thinking.cycle")
374
+ ) {
375
+ const currentIndex = thinking.levels.indexOf(this.thinkingLevel ?? thinking.level);
376
+ const nextLevel = thinking.levels[(currentIndex + 1) % thinking.levels.length];
377
+ if (nextLevel) {
378
+ this.thinkingLevel = nextLevel;
379
+ thinking.onChange(nextLevel);
380
+ this.warning = undefined;
381
+ this.tui.requestRender();
382
+ }
383
+ return;
384
+ }
385
+ if (matchesKey(data, Key.pageUp)) {
386
+ const previousOffset = this.scrollOffset;
387
+ this.scrollBy(-this.lastViewportHeight);
388
+ if (this.scrollOffset < previousOffset) this.followBottom = false;
389
+ this.tui.requestRender();
390
+ return;
391
+ }
392
+ if (matchesKey(data, Key.pageDown)) {
393
+ this.scrollBy(this.lastViewportHeight);
394
+ this.followBottom = this.scrollOffset >= this.getMaxScrollOffset();
395
+ this.tui.requestRender();
396
+ return;
397
+ }
398
+ this.editor?.handleInput(data);
399
+ this.tui.requestRender();
400
+ }
401
+
402
+ invalidate(): void {
403
+ for (const component of this.transcriptComponents) component.invalidate();
404
+ this.loader.invalidate();
405
+ this.editor?.invalidate();
406
+ }
407
+
408
+ finish(): void {
409
+ this.finished = true;
410
+ this.loader.stop();
411
+ }
412
+
413
+ dispose(): void {
414
+ if (this.finished) {
415
+ this.loader.stop();
416
+ this.controller.abort();
417
+ return;
418
+ }
419
+ this.finished = true;
420
+ this.loader.stop();
421
+ this.controller.abort();
422
+ this.onCancel();
423
+ }
424
+
425
+ private renderFooter(width: number): string {
426
+ if (this.warning) {
427
+ const warning = width < 32 ? "Empty • Ctrl+C" : `${this.warning} • Ctrl+C cancel`;
428
+ return truncateToWidth(this.theme.fg("warning", warning), width);
429
+ }
430
+ const baseHint = this.editor ? "Enter steer • Ctrl+C cancel" : "Ctrl+C cancel";
431
+ const thinking = this.options.steering?.thinking;
432
+ const cycleHint =
433
+ thinking && thinking.levels.length > 1 && this.thinkingLevel
434
+ ? ` • thinking ${this.thinkingLevel} • ${thinkingKeyLabel(thinking.keybindings)} cycle`
435
+ : "";
436
+ const scrollHint = this.getMaxScrollOffset() > 0 ? " • PgUp/PgDn history" : "";
437
+ const hints = `${baseHint}${cycleHint}${scrollHint}`;
438
+ const compactHints = this.editor ? "Enter • Ctrl+C" : "Ctrl+C";
439
+ const selectedHints = visibleWidth(hints) <= width ? hints : compactHints;
440
+ const loaderWidth = Math.max(1, width - visibleWidth(selectedHints) - 3);
441
+ const loaderLine = this.loader.render(loaderWidth).at(-1) ?? "Answering…";
442
+ return truncateToWidth(`${loaderLine} • ${this.theme.fg("muted", selectedHints)}`, width);
443
+ }
444
+
445
+ private scrollBy(delta: number): void {
446
+ this.scrollOffset += delta;
447
+ this.clampScrollOffset();
448
+ }
449
+
450
+ private clampScrollOffset(): void {
451
+ this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, this.getMaxScrollOffset()));
452
+ }
453
+
454
+ private getMaxScrollOffset(): number {
455
+ return Math.max(0, this.lastContentLineCount - this.lastViewportHeight);
456
+ }
457
+ }
458
+
459
+ export function formatSideTranscript(turns: readonly SideThreadTurn[]): string {
460
+ return turns
461
+ .map((turn) => {
462
+ const question = escapeTerminalControls(turn.question);
463
+ const rawAnswer = escapeTerminalControls(turn.answer);
464
+ const answer = turn.kind === "error" ? `Error: ${rawAnswer}` : rawAnswer;
465
+ return `${question}\n\n${answer}`;
466
+ })
467
+ .join("\n\n");
468
+ }
469
+
470
+ function buildTranscriptComponents(
471
+ turns: readonly SideThreadTurn[],
472
+ theme: Theme,
473
+ pendingQuestion?: string,
474
+ ): Component[] {
475
+ const components = turns.flatMap((turn): Component[] => {
476
+ const question = new UserMessageComponent(
477
+ escapeTerminalControls(turn.question),
478
+ getMarkdownTheme(),
479
+ 1,
480
+ );
481
+ if (turn.kind === "error") {
482
+ const error = new Markdown(
483
+ `Error: ${escapeTerminalControls(turn.answer)}`,
484
+ 1,
485
+ 1,
486
+ getMarkdownTheme(),
487
+ { color: (text) => theme.fg("error", text) },
488
+ );
489
+ return [question, error];
490
+ }
491
+ const response: AssistantMessage = {
492
+ ...turn.response,
493
+ content: [{ type: "text", text: escapeTerminalControls(turn.answer) }],
494
+ stopReason: "stop",
495
+ errorMessage: undefined,
496
+ };
497
+ return [question, new AssistantMessageComponent(response, true, getMarkdownTheme(), "", 1)];
498
+ });
499
+ if (pendingQuestion) {
500
+ components.push(
501
+ new UserMessageComponent(escapeTerminalControls(pendingQuestion), getMarkdownTheme(), 1),
502
+ );
503
+ }
504
+ return components;
505
+ }
506
+
507
+ function renderTranscriptLines(components: readonly Component[], width: number): string[] {
508
+ return components
509
+ .flatMap((component) => component.render(width))
510
+ .map(stripShellIntegrationMarkers);
511
+ }
512
+
513
+ function renderSideThreadHeader(
514
+ width: number,
515
+ theme: Theme,
516
+ thinkingLevel?: BtwThinkingLevel,
517
+ ): string {
518
+ const thinking = thinkingLevel ? ` · thinking ${thinkingLevel}` : "";
519
+ const title = truncateToWidth(`─ btw · side thread${thinking} `, width);
520
+ const ruleWidth = Math.max(0, width - visibleWidth(title));
521
+ return theme.fg("muted", `${title}${"─".repeat(ruleWidth)}`);
522
+ }
523
+
524
+ function thinkingKeyLabel(keybindings: KeybindingsManager): string {
525
+ const key =
526
+ sanitizeSingleLine(String(keybindings.getKeys("app.thinking.cycle")[0] ?? "shift+tab")) ||
527
+ "Shift+Tab";
528
+ return key
529
+ .split("+")
530
+ .map((part) => {
531
+ const lower = part.toLowerCase();
532
+ if (lower === "shift") return "Shift";
533
+ if (lower === "ctrl") return "Ctrl";
534
+ if (lower === "alt") return "Alt";
535
+ return part.length === 1
536
+ ? part.toUpperCase()
537
+ : `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`;
538
+ })
539
+ .join("+");
540
+ }
541
+
542
+ function fitComposerLayout(
543
+ header: string,
544
+ contentLines: string[],
545
+ footer: string,
546
+ editorLines: string[],
547
+ availableRows: number,
548
+ statusLines: string[] = [],
549
+ ): string[] {
550
+ const lines = [header, ...contentLines, ...statusLines, footer, ...editorLines];
551
+ if (lines.length <= availableRows) return lines;
552
+ if (availableRows <= 1) return [header];
553
+ const editorBudget = Math.max(0, availableRows - 2);
554
+ return [header, footer, ...fitEditorLines(editorLines, editorBudget)];
555
+ }
556
+
557
+ function fitEditorLines(editorLines: string[], budget: number): string[] {
558
+ if (budget <= 0) return [];
559
+ if (editorLines.length <= budget) return editorLines;
560
+ const cursorIndex = editorLines.findIndex((line) => line.includes(CURSOR_MARKER));
561
+ if (cursorIndex < 0) return editorLines.slice(-budget);
562
+ const start = Math.min(cursorIndex, editorLines.length - budget);
563
+ return editorLines.slice(start, start + budget);
564
+ }
565
+
566
+ function renderSteeringLines(
567
+ questions: readonly string[],
568
+ width: number,
569
+ theme: Theme,
570
+ maxLines: number,
571
+ ): string[] {
572
+ if (questions.length === 0 || maxLines <= 0) return [];
573
+ const formatQuestion = (question: string) =>
574
+ sanitizeSingleLine(question) || "(non-printing message)";
575
+ if (maxLines === 1 && questions.length > 1) {
576
+ return [
577
+ truncateToWidth(
578
+ theme.fg(
579
+ "dim",
580
+ `Steering (+${questions.length - 1} more): ${formatQuestion(questions[0] ?? "")}`,
581
+ ),
582
+ width,
583
+ ),
584
+ ];
585
+ }
586
+ const hasOverflow = questions.length > maxLines;
587
+ const questionLimit = hasOverflow ? Math.max(1, maxLines - 1) : maxLines;
588
+ const lines = questions
589
+ .slice(0, questionLimit)
590
+ .map((question) =>
591
+ truncateToWidth(theme.fg("dim", `Steering: ${formatQuestion(question)}`), width),
592
+ );
593
+ if (hasOverflow) {
594
+ lines.push(
595
+ truncateToWidth(
596
+ theme.fg("dim", `Steering: … +${questions.length - questionLimit} more`),
597
+ width,
598
+ ),
599
+ );
600
+ }
601
+ return lines;
602
+ }
603
+
604
+ function stripShellIntegrationMarkers(line: string): string {
605
+ return OSC133_MARKERS.reduce((result, marker) => result.replaceAll(marker, ""), line);
606
+ }
607
+
608
+ function escapeTerminalControls(text: string): string {
609
+ return [...text]
610
+ .map((character) => {
611
+ if (character === "\n") return character;
612
+ if (character === "\t") return " ";
613
+ const code = character.charCodeAt(0);
614
+ if (code <= 31 || (code >= 127 && code <= 159)) {
615
+ return `\\x${code.toString(16).padStart(2, "0")}`;
616
+ }
617
+ return character;
618
+ })
619
+ .join("");
620
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kedom
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.