@oh-my-pi/pi-tui 18.4.2 → 18.4.4

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 (463) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/types/apps/cleanse-board.d.ts +17 -0
  3. package/dist/types/apps/git/help.d.ts +16 -0
  4. package/dist/types/apps/git/sidebar.d.ts +18 -0
  5. package/dist/types/apps/live-visualizer.d.ts +10 -0
  6. package/dist/types/apps/ps-top.d.ts +15 -0
  7. package/dist/types/apps/standalone-picker.d.ts +3 -0
  8. package/dist/types/autocomplete.d.ts +8 -0
  9. package/dist/types/chat/advisor-message.d.ts +12 -2
  10. package/dist/types/chat/assistant-message.d.ts +20 -0
  11. package/dist/types/chat/bash-execution.d.ts +9 -0
  12. package/dist/types/chat/chat-transcript-builder.d.ts +6 -1
  13. package/dist/types/chat/collab-prompt-message.d.ts +4 -0
  14. package/dist/types/chat/compaction-summary-message.d.ts +15 -0
  15. package/dist/types/chat/eval-execution.d.ts +8 -0
  16. package/dist/types/chat/execution-shared.d.ts +58 -0
  17. package/dist/types/chat/late-diagnostics-message.d.ts +15 -1
  18. package/dist/types/chat/read-tool-group.d.ts +15 -0
  19. package/dist/types/chat/skill-message.d.ts +9 -0
  20. package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +3 -0
  21. package/dist/types/chat/thinking-display.d.ts +2 -0
  22. package/dist/types/chat/todo-reminder.d.ts +4 -1
  23. package/dist/types/chat/tool-execution.d.ts +25 -1
  24. package/dist/types/chat/transcript-actions.d.ts +37 -0
  25. package/dist/types/chat/transcript-entry.d.ts +3 -1
  26. package/dist/types/chat/user-message.d.ts +25 -0
  27. package/dist/types/chrome/collab-qrcode.d.ts +7 -0
  28. package/dist/types/chrome/context-thresholds.d.ts +11 -0
  29. package/dist/types/chrome/countdown-timer.d.ts +9 -3
  30. package/dist/types/chrome/diff.d.ts +12 -0
  31. package/dist/types/chrome/dynamic-border.d.ts +3 -0
  32. package/dist/types/chrome/format.d.ts +6 -0
  33. package/dist/types/chrome/message-divider.d.ts +13 -0
  34. package/dist/types/chrome/message-frame.d.ts +10 -0
  35. package/dist/types/chrome/message-notice.d.ts +23 -0
  36. package/dist/types/chrome/overlay-box.d.ts +11 -2
  37. package/dist/types/chrome/qrcode.d.ts +7 -0
  38. package/dist/types/chrome/segment-track.d.ts +7 -0
  39. package/dist/types/chrome/selector-helpers.d.ts +0 -6
  40. package/dist/types/chrome/status-notice.d.ts +14 -0
  41. package/dist/types/chrome/streaming-panel.d.ts +3 -0
  42. package/dist/types/chrome/tool-activity.d.ts +3 -0
  43. package/dist/types/chrome/transcript-container.d.ts +13 -0
  44. package/dist/types/chrome/transcript-status.d.ts +15 -3
  45. package/dist/types/components/box.d.ts +7 -0
  46. package/dist/types/components/disclosure.d.ts +9 -0
  47. package/dist/types/components/editor.d.ts +42 -0
  48. package/dist/types/components/form.d.ts +14 -0
  49. package/dist/types/components/image.d.ts +7 -0
  50. package/dist/types/components/input.d.ts +20 -1
  51. package/dist/types/components/key-value-list.d.ts +3 -0
  52. package/dist/types/components/layout/geometry.d.ts +16 -0
  53. package/dist/types/components/layout/row.d.ts +7 -0
  54. package/dist/types/components/layout/split-pane.d.ts +8 -0
  55. package/dist/types/components/layout/stack.d.ts +6 -0
  56. package/dist/types/components/loader.d.ts +48 -0
  57. package/dist/types/components/markdown.d.ts +8 -0
  58. package/dist/types/components/metric.d.ts +7 -0
  59. package/dist/types/components/progress-bar.d.ts +3 -0
  60. package/dist/types/components/scroll-view.d.ts +6 -0
  61. package/dist/types/components/section.d.ts +3 -0
  62. package/dist/types/components/select-list.d.ts +33 -0
  63. package/dist/types/components/settings-list.d.ts +68 -0
  64. package/dist/types/components/spacer.d.ts +4 -0
  65. package/dist/types/components/tab-bar.d.ts +9 -0
  66. package/dist/types/components/table.d.ts +7 -0
  67. package/dist/types/components/text.d.ts +6 -0
  68. package/dist/types/components/tree-view.d.ts +9 -0
  69. package/dist/types/components/truncated-text.d.ts +3 -0
  70. package/dist/types/components/wizard-step.d.ts +6 -0
  71. package/dist/types/hotkeys-markdown.d.ts +24 -0
  72. package/dist/types/index.d.ts +5 -0
  73. package/dist/types/latex-block.d.ts +7 -0
  74. package/dist/types/loop-watchdog.d.ts +9 -0
  75. package/dist/types/native/apply.d.ts +39 -0
  76. package/dist/types/native/backend.d.ts +94 -0
  77. package/dist/types/native/blobs.d.ts +29 -0
  78. package/dist/types/native/describe.d.ts +56 -0
  79. package/dist/types/native/encode.d.ts +50 -0
  80. package/dist/types/native/icons.d.ts +18 -0
  81. package/dist/types/native/memo.d.ts +27 -0
  82. package/dist/types/native/node.d.ts +107 -0
  83. package/dist/types/native/overlay.d.ts +64 -0
  84. package/dist/types/native/picker.d.ts +124 -0
  85. package/dist/types/native/reconcile.d.ts +41 -0
  86. package/dist/types/native/settle.d.ts +11 -0
  87. package/dist/types/native/spans.d.ts +21 -0
  88. package/dist/types/native/state.d.ts +6 -0
  89. package/dist/types/native/tone.d.ts +24 -0
  90. package/dist/types/overlays/advisor-config.d.ts +20 -0
  91. package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
  92. package/dist/types/overlays/agent-hub.d.ts +10 -0
  93. package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
  94. package/dist/types/overlays/agents-hub.d.ts +8 -0
  95. package/dist/types/overlays/annotation-overlay.d.ts +3 -0
  96. package/dist/types/overlays/ask-dialog.d.ts +23 -0
  97. package/dist/types/overlays/bordered-loader.d.ts +3 -0
  98. package/dist/types/overlays/btw-history-panel.d.ts +9 -0
  99. package/dist/types/overlays/btw-panel.d.ts +3 -0
  100. package/dist/types/overlays/cleanse-panel.d.ts +5 -2
  101. package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
  102. package/dist/types/overlays/copy-selector.d.ts +38 -0
  103. package/dist/types/overlays/error-banner.d.ts +9 -0
  104. package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
  105. package/dist/types/overlays/history-search.d.ts +18 -0
  106. package/dist/types/overlays/hook-editor.d.ts +28 -1
  107. package/dist/types/overlays/hook-input.d.ts +3 -0
  108. package/dist/types/overlays/hook-selector.d.ts +10 -0
  109. package/dist/types/overlays/hub-frame.d.ts +18 -0
  110. package/dist/types/overlays/jobs-panel.d.ts +24 -0
  111. package/dist/types/overlays/login-dialog.d.ts +10 -0
  112. package/dist/types/overlays/logout-account-selector.d.ts +4 -0
  113. package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
  114. package/dist/types/overlays/model-browser.d.ts +69 -0
  115. package/dist/types/overlays/model-hub.d.ts +7 -0
  116. package/dist/types/overlays/model-picker.d.ts +9 -1
  117. package/dist/types/overlays/move-overlay.d.ts +3 -0
  118. package/dist/types/overlays/oauth-selector.d.ts +4 -0
  119. package/dist/types/overlays/omfg-panel.d.ts +3 -0
  120. package/dist/types/overlays/pause-screen.d.ts +12 -0
  121. package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
  122. package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
  123. package/dist/types/overlays/plugin-selector.d.ts +4 -0
  124. package/dist/types/overlays/plugin-settings.d.ts +30 -0
  125. package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
  126. package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
  127. package/dist/types/overlays/rewind-selector.d.ts +5 -0
  128. package/dist/types/overlays/session-info-overlay.d.ts +19 -1
  129. package/dist/types/overlays/session-selector.d.ts +64 -1
  130. package/dist/types/overlays/settings-defs.d.ts +14 -0
  131. package/dist/types/overlays/settings-selector.d.ts +21 -0
  132. package/dist/types/overlays/show-images-selector.d.ts +4 -0
  133. package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
  134. package/dist/types/overlays/stats-notice.d.ts +8 -0
  135. package/dist/types/overlays/theme-selector.d.ts +11 -0
  136. package/dist/types/overlays/thinking-selector.d.ts +4 -0
  137. package/dist/types/overlays/tree-selector.d.ts +60 -2
  138. package/dist/types/overlays/usage-dashboard.d.ts +26 -3
  139. package/dist/types/overlays/usage-row.d.ts +36 -2
  140. package/dist/types/prompt/attachment-chips.d.ts +8 -0
  141. package/dist/types/prompt/composer-attachments.d.ts +15 -3
  142. package/dist/types/prompt/composer.d.ts +18 -1
  143. package/dist/types/prompt/custom-editor.d.ts +48 -2
  144. package/dist/types/prompt/editor-top-gap.d.ts +2 -0
  145. package/dist/types/prompt/macos-spelling.d.ts +6 -0
  146. package/dist/types/prompt/magic-keywords.d.ts +9 -0
  147. package/dist/types/prompt/queued-messages.d.ts +23 -0
  148. package/dist/types/prompt/skill-tokens.d.ts +4 -2
  149. package/dist/types/prompt/welcome.d.ts +16 -0
  150. package/dist/types/render/code-cell.d.ts +10 -0
  151. package/dist/types/render/file-list.d.ts +8 -0
  152. package/dist/types/render/hyperlink.d.ts +14 -0
  153. package/dist/types/render/output-block.d.ts +43 -0
  154. package/dist/types/render/output-pane.d.ts +17 -0
  155. package/dist/types/render/tool-card.d.ts +8 -0
  156. package/dist/types/render/tree-list.d.ts +8 -0
  157. package/dist/types/render/width-aware-text.d.ts +7 -0
  158. package/dist/types/setup/scenes/outro.d.ts +7 -0
  159. package/dist/types/setup/scenes/sign-in.d.ts +16 -9
  160. package/dist/types/setup/scenes/splash.d.ts +8 -0
  161. package/dist/types/setup/scenes/types.d.ts +6 -27
  162. package/dist/types/setup/wizard-overlay.d.ts +9 -0
  163. package/dist/types/status-line/component.d.ts +49 -5
  164. package/dist/types/status-line/context-usage.d.ts +20 -0
  165. package/dist/types/status-line/footer.d.ts +8 -0
  166. package/dist/types/status-line/metrics.d.ts +1 -2
  167. package/dist/types/status-line/segments.d.ts +10 -1
  168. package/dist/types/status-line/types.d.ts +40 -1
  169. package/dist/types/stdin-buffer.d.ts +10 -7
  170. package/dist/types/terminal-capabilities.d.ts +1 -1
  171. package/dist/types/terminal.d.ts +36 -0
  172. package/dist/types/theme/defaults/index.d.ts +100 -0
  173. package/dist/types/theme/schema.d.ts +2 -0
  174. package/dist/types/theme/shimmer.d.ts +8 -0
  175. package/dist/types/theme/theme-class.d.ts +2 -0
  176. package/dist/types/theme/theme.d.ts +33 -1
  177. package/dist/types/tools/ask.d.ts +3 -0
  178. package/dist/types/tools/ast-edit.d.ts +10 -1
  179. package/dist/types/tools/ast-grep.d.ts +10 -1
  180. package/dist/types/tools/autoresearch.d.ts +21 -4
  181. package/dist/types/tools/bash.d.ts +30 -1
  182. package/dist/types/tools/cfg-render.d.ts +5 -1
  183. package/dist/types/tools/debug.d.ts +3 -1
  184. package/dist/types/tools/default-renderer.d.ts +6 -1
  185. package/dist/types/tools/edit.d.ts +14 -1
  186. package/dist/types/tools/eval.d.ts +12 -1
  187. package/dist/types/tools/fetch.d.ts +16 -1
  188. package/dist/types/tools/find.d.ts +15 -1
  189. package/dist/types/tools/github.d.ts +3 -0
  190. package/dist/types/tools/glob.d.ts +14 -1
  191. package/dist/types/tools/goal.d.ts +3 -1
  192. package/dist/types/tools/grep.d.ts +14 -1
  193. package/dist/types/tools/grouped-file-output.d.ts +58 -0
  194. package/dist/types/tools/json-tree.d.ts +20 -0
  195. package/dist/types/tools/lsp.d.ts +3 -10
  196. package/dist/types/tools/mcp.d.ts +16 -1
  197. package/dist/types/tools/memory.d.ts +26 -0
  198. package/dist/types/tools/native-view.d.ts +90 -0
  199. package/dist/types/tools/proc-render.d.ts +7 -1
  200. package/dist/types/tools/read.d.ts +23 -1
  201. package/dist/types/tools/renderer.d.ts +57 -0
  202. package/dist/types/tools/report-tool-issue.d.ts +3 -0
  203. package/dist/types/tools/resolve.d.ts +14 -1
  204. package/dist/types/tools/task.d.ts +3 -0
  205. package/dist/types/tools/think.d.ts +3 -0
  206. package/dist/types/tools/todo.d.ts +12 -0
  207. package/dist/types/tools/vibe.d.ts +3 -1
  208. package/dist/types/tools/wait.d.ts +3 -0
  209. package/dist/types/tools/web-search-types.d.ts +90 -0
  210. package/dist/types/tools/web-search.d.ts +9 -168
  211. package/dist/types/tools/write.d.ts +14 -1
  212. package/dist/types/tools/xdev.d.ts +13 -0
  213. package/dist/types/tui.d.ts +49 -0
  214. package/package.json +9 -9
  215. package/src/apps/autoresearch-dashboard.ts +362 -4
  216. package/src/apps/cleanse-board.ts +219 -1
  217. package/src/apps/cleanse-picker.ts +9 -1
  218. package/src/apps/debug/log-viewer.ts +15 -15
  219. package/src/apps/debug/protocol-probe.ts +2 -20
  220. package/src/apps/git/avatar.ts +2 -22
  221. package/src/apps/git/git-tui.ts +406 -59
  222. package/src/apps/git/help.ts +130 -0
  223. package/src/apps/git/sidebar.ts +491 -0
  224. package/src/apps/live-visualizer.ts +65 -0
  225. package/src/apps/ps-top.ts +448 -17
  226. package/src/apps/session-picker.ts +1 -0
  227. package/src/apps/setup-model-picker.ts +5 -1
  228. package/src/apps/standalone-picker.ts +55 -1
  229. package/src/autocomplete.ts +44 -1
  230. package/src/chat/advisor-message.ts +72 -3
  231. package/src/chat/assistant-message.ts +538 -51
  232. package/src/chat/background-tan-message.ts +4 -4
  233. package/src/chat/bash-execution.ts +81 -0
  234. package/src/chat/cache-invalidation-marker.ts +8 -0
  235. package/src/chat/chat-transcript-builder.ts +37 -6
  236. package/src/chat/collab-prompt-message.ts +15 -3
  237. package/src/chat/compaction-summary-message.ts +74 -1
  238. package/src/chat/custom-message.ts +3 -1
  239. package/src/chat/eval-execution.ts +57 -0
  240. package/src/chat/execution-shared.ts +153 -1
  241. package/src/chat/hook-message.ts +1 -0
  242. package/src/chat/late-diagnostics-message.ts +85 -2
  243. package/src/chat/read-tool-group.ts +341 -1
  244. package/src/chat/served-model-marker.ts +8 -0
  245. package/src/chat/skill-message.ts +63 -0
  246. package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
  247. package/src/chat/thinking-display.ts +73 -11
  248. package/src/chat/todo-reminder.ts +34 -4
  249. package/src/chat/tool-execution.ts +385 -10
  250. package/src/chat/transcript-actions.ts +38 -0
  251. package/src/chat/transcript-browser.ts +2 -9
  252. package/src/chat/transcript-entry.ts +10 -1
  253. package/src/chat/transcript-render-helpers.ts +16 -15
  254. package/src/chat/ttsr-notification.ts +38 -1
  255. package/src/chat/user-message.ts +167 -4
  256. package/src/chrome/collab-qrcode.ts +27 -2
  257. package/src/chrome/context-thresholds.ts +34 -0
  258. package/src/chrome/countdown-timer.ts +24 -1
  259. package/src/chrome/diff.ts +60 -1
  260. package/src/chrome/dynamic-border.ts +7 -0
  261. package/src/chrome/format.ts +12 -0
  262. package/src/chrome/message-divider.ts +33 -1
  263. package/src/chrome/message-frame.ts +53 -10
  264. package/src/chrome/message-notice.ts +71 -0
  265. package/src/chrome/overlay-box.ts +45 -2
  266. package/src/chrome/qrcode.ts +28 -0
  267. package/src/chrome/segment-track.ts +26 -0
  268. package/src/chrome/selector-helpers.ts +1 -17
  269. package/src/chrome/status-notice.ts +46 -0
  270. package/src/chrome/streaming-panel.ts +25 -1
  271. package/src/chrome/tool-activity.ts +20 -0
  272. package/src/chrome/transcript-container.ts +139 -17
  273. package/src/chrome/transcript-status.ts +40 -11
  274. package/src/components/box.ts +51 -0
  275. package/src/components/disclosure.ts +33 -0
  276. package/src/components/editor.ts +239 -36
  277. package/src/components/form.ts +93 -9
  278. package/src/components/image.ts +30 -0
  279. package/src/components/input.ts +77 -26
  280. package/src/components/key-value-list.ts +18 -0
  281. package/src/components/layout/geometry.ts +44 -0
  282. package/src/components/layout/row.ts +30 -0
  283. package/src/components/layout/split-pane.ts +65 -0
  284. package/src/components/layout/stack.ts +27 -0
  285. package/src/components/loader.ts +183 -1
  286. package/src/components/markdown.ts +131 -56
  287. package/src/components/metric.ts +39 -0
  288. package/src/components/progress-bar.ts +28 -0
  289. package/src/components/scroll-view.ts +23 -0
  290. package/src/components/section.ts +16 -0
  291. package/src/components/select-list.ts +174 -25
  292. package/src/components/settings-list.ts +264 -7
  293. package/src/components/spacer.ts +17 -0
  294. package/src/components/tab-bar.ts +42 -0
  295. package/src/components/table.ts +34 -0
  296. package/src/components/text.ts +23 -0
  297. package/src/components/tree-view.ts +65 -0
  298. package/src/components/truncated-text.ts +14 -0
  299. package/src/components/wizard-step.ts +18 -9
  300. package/src/debug-server.ts +14 -7
  301. package/src/hotkeys-markdown.ts +251 -69
  302. package/src/index.ts +6 -0
  303. package/src/latex-block.ts +11 -0
  304. package/src/loop-watchdog.ts +38 -10
  305. package/src/native/apply.ts +275 -0
  306. package/src/native/backend.ts +682 -0
  307. package/src/native/blobs.ts +70 -0
  308. package/src/native/describe.ts +124 -0
  309. package/src/native/encode.ts +233 -0
  310. package/src/native/icons.ts +184 -0
  311. package/src/native/memo.ts +64 -0
  312. package/src/native/node.ts +112 -0
  313. package/src/native/overlay.ts +131 -0
  314. package/src/native/picker.ts +333 -0
  315. package/src/native/reconcile.ts +1063 -0
  316. package/src/native/settle.ts +23 -0
  317. package/src/native/spans.ts +308 -0
  318. package/src/native/state.ts +28 -0
  319. package/src/native/tone.ts +79 -0
  320. package/src/overlays/advisor-config.ts +420 -0
  321. package/src/overlays/agent-hub-renderer.ts +155 -18
  322. package/src/overlays/agent-hub.ts +1037 -44
  323. package/src/overlays/agent-transcript-viewer.ts +129 -0
  324. package/src/overlays/agents-hub.ts +688 -53
  325. package/src/overlays/annotation-overlay.ts +391 -52
  326. package/src/overlays/ask-dialog.ts +435 -24
  327. package/src/overlays/bordered-loader.ts +17 -0
  328. package/src/overlays/btw-history-panel.ts +182 -0
  329. package/src/overlays/btw-panel.ts +68 -2
  330. package/src/overlays/cleanse-panel.ts +105 -15
  331. package/src/overlays/codex-reset-fireworks.ts +65 -5
  332. package/src/overlays/composer-shape-preview.ts +26 -1
  333. package/src/overlays/copy-selector.ts +545 -23
  334. package/src/overlays/error-banner.ts +44 -0
  335. package/src/overlays/extensions/extension-list.ts +23 -17
  336. package/src/overlays/history-search.ts +175 -33
  337. package/src/overlays/hook-editor.ts +113 -6
  338. package/src/overlays/hook-input.ts +22 -1
  339. package/src/overlays/hook-selector.ts +311 -22
  340. package/src/overlays/hub-frame.ts +80 -0
  341. package/src/overlays/jobs-panel.ts +112 -0
  342. package/src/overlays/login-dialog.ts +183 -1
  343. package/src/overlays/logout-account-selector.ts +102 -4
  344. package/src/overlays/mcp-add-wizard.ts +232 -53
  345. package/src/overlays/model-browser.ts +681 -22
  346. package/src/overlays/model-hub.ts +1402 -78
  347. package/src/overlays/model-picker.ts +184 -7
  348. package/src/overlays/move-overlay.ts +45 -0
  349. package/src/overlays/oauth-selector.ts +227 -23
  350. package/src/overlays/omfg-panel.ts +86 -2
  351. package/src/overlays/pause-screen.ts +55 -4
  352. package/src/overlays/plan-review-overlay.ts +352 -0
  353. package/src/overlays/plan-save-overlay.ts +45 -0
  354. package/src/overlays/plugin-selector.ts +21 -1
  355. package/src/overlays/plugin-settings.ts +195 -5
  356. package/src/overlays/queue-mode-selector.ts +19 -1
  357. package/src/overlays/reset-usage-selector.ts +110 -40
  358. package/src/overlays/rewind-selector.ts +458 -44
  359. package/src/overlays/session-account-selector.ts +1 -1
  360. package/src/overlays/session-info-overlay.ts +137 -1
  361. package/src/overlays/session-selector.ts +807 -46
  362. package/src/overlays/settings-defs.ts +41 -0
  363. package/src/overlays/settings-selector.ts +585 -33
  364. package/src/overlays/show-images-selector.ts +18 -1
  365. package/src/overlays/snapcompact-shape-preview.ts +48 -3
  366. package/src/overlays/stats-notice.ts +31 -0
  367. package/src/overlays/theme-selector.ts +31 -1
  368. package/src/overlays/thinking-selector.ts +22 -1
  369. package/src/overlays/tree-selector.ts +570 -110
  370. package/src/overlays/usage-dashboard.ts +587 -12
  371. package/src/overlays/usage-row.ts +161 -25
  372. package/src/prompt/attachment-chips.ts +60 -0
  373. package/src/prompt/composer-attachments.ts +31 -9
  374. package/src/prompt/composer.ts +55 -2
  375. package/src/prompt/custom-editor.ts +302 -4
  376. package/src/prompt/editor-top-gap.ts +8 -0
  377. package/src/prompt/macos-spelling.ts +24 -9
  378. package/src/prompt/magic-keywords.ts +22 -1
  379. package/src/prompt/model-mention-autocomplete.ts +1 -0
  380. package/src/prompt/queued-messages.ts +101 -0
  381. package/src/prompt/skill-tokens.ts +5 -3
  382. package/src/prompt/welcome.ts +213 -6
  383. package/src/render/code-cell.ts +103 -2
  384. package/src/render/file-list.ts +29 -0
  385. package/src/render/hyperlink.ts +32 -0
  386. package/src/render/output-block.ts +110 -0
  387. package/src/render/output-pane.ts +37 -0
  388. package/src/render/tool-card.ts +137 -0
  389. package/src/render/tree-list.ts +39 -0
  390. package/src/render/width-aware-text.ts +20 -0
  391. package/src/setup/scenes/composer.ts +29 -0
  392. package/src/setup/scenes/glyph.ts +15 -0
  393. package/src/setup/scenes/model.ts +36 -8
  394. package/src/setup/scenes/outro.ts +28 -1
  395. package/src/setup/scenes/sign-in.ts +138 -24
  396. package/src/setup/scenes/splash.ts +28 -1
  397. package/src/setup/scenes/theme.ts +88 -3
  398. package/src/setup/scenes/types.ts +7 -28
  399. package/src/setup/startup-splash.ts +46 -9
  400. package/src/setup/wizard-overlay.ts +140 -23
  401. package/src/setup/wizard.ts +1 -1
  402. package/src/status-line/component.ts +414 -18
  403. package/src/status-line/context-usage.ts +279 -50
  404. package/src/status-line/footer.ts +94 -1
  405. package/src/status-line/metrics.ts +3 -6
  406. package/src/status-line/segments.ts +463 -97
  407. package/src/status-line/separators.ts +3 -16
  408. package/src/status-line/types.ts +43 -1
  409. package/src/stdin-buffer.ts +27 -21
  410. package/src/terminal-capabilities.ts +7 -0
  411. package/src/terminal.ts +153 -11
  412. package/src/theme/defaults/dark-neon-noir.json +100 -0
  413. package/src/theme/defaults/index.ts +2 -0
  414. package/src/theme/defaults/light-prism.json +5 -5
  415. package/src/theme/defaults/onyx.json +62 -62
  416. package/src/theme/mermaid-cache.ts +10 -1
  417. package/src/theme/schema.ts +15 -0
  418. package/src/theme/shimmer.ts +33 -0
  419. package/src/theme/theme-class.ts +24 -19
  420. package/src/theme/theme.ts +111 -9
  421. package/src/theme/tui-adapters.ts +14 -1
  422. package/src/tools/ask.ts +157 -1
  423. package/src/tools/ast-edit.ts +174 -1
  424. package/src/tools/ast-grep.ts +82 -2
  425. package/src/tools/autoresearch.ts +149 -1
  426. package/src/tools/bash.ts +233 -62
  427. package/src/tools/cfg-render.ts +66 -1
  428. package/src/tools/debug.ts +78 -1
  429. package/src/tools/default-renderer.ts +35 -1
  430. package/src/tools/edit.ts +357 -31
  431. package/src/tools/eval.ts +257 -1
  432. package/src/tools/fetch.ts +102 -3
  433. package/src/tools/find.ts +127 -1
  434. package/src/tools/github.ts +119 -10
  435. package/src/tools/glob.ts +116 -1
  436. package/src/tools/goal.ts +56 -1
  437. package/src/tools/grep.ts +83 -2
  438. package/src/tools/grouped-file-output.ts +205 -0
  439. package/src/tools/json-tree.ts +116 -1
  440. package/src/tools/lsp.ts +318 -1
  441. package/src/tools/mcp.ts +80 -1
  442. package/src/tools/memory.ts +93 -11
  443. package/src/tools/native-view.ts +292 -0
  444. package/src/tools/proc-render.ts +221 -1
  445. package/src/tools/read.ts +199 -4
  446. package/src/tools/renderer.ts +61 -0
  447. package/src/tools/report-tool-issue.ts +7 -1
  448. package/src/tools/resolve.ts +63 -1
  449. package/src/tools/streaming-output.ts +79 -16
  450. package/src/tools/task.ts +406 -4
  451. package/src/tools/think.ts +19 -1
  452. package/src/tools/todo.ts +89 -0
  453. package/src/tools/vibe.ts +230 -2
  454. package/src/tools/wait.ts +181 -1
  455. package/src/tools/web-search-types.ts +99 -0
  456. package/src/tools/web-search.ts +111 -170
  457. package/src/tools/write.ts +144 -5
  458. package/src/tools/xdev.ts +64 -1
  459. package/src/tui.ts +295 -4
  460. package/dist/types/setup/scenes/providers.d.ts +0 -3
  461. package/dist/types/setup/scenes/web-search.d.ts +0 -22
  462. package/src/setup/scenes/providers.ts +0 -110
  463. package/src/setup/scenes/web-search.ts +0 -165
@@ -18,8 +18,17 @@ import { summarizeLoopCondition } from "./loop";
18
18
  import { formatMetric } from "../components/metric";
19
19
  import { formatBillingSummary } from "./metrics";
20
20
  import { sanitizeStatusText } from "../chrome/shared";
21
- import { formatContextUsage, getContextUsageLevel, getContextUsageThemeColor } from "../chrome/context-thresholds";
22
- import type { RenderedSegment, SegmentContext, StatusLineSegment, StatusLineSegmentId } from "./types";
21
+ import {
22
+ formatContextUsage,
23
+ getContextUsageLevel,
24
+ getContextUsageThemeColor,
25
+ getContextUsageTone,
26
+ } from "../chrome/context-thresholds";
27
+ import type { TspSpan, TspTone } from "@oh-my-pi/pi-wire";
28
+ import { node, span } from "../native/describe";
29
+ import { thinkingLevelToken } from "../theme/theme-class";
30
+ import type { StatusLineSession } from "./host";
31
+ import type { RenderedSegment, SegmentContext, SegmentView, StatusLineSegment, StatusLineSegmentId } from "./types";
23
32
 
24
33
  export type { SegmentContext } from "./types";
25
34
 
@@ -31,6 +40,30 @@ function withIcon(icon: string, text: string): string {
31
40
  return icon ? `${icon} ${text}` : text;
32
41
  }
33
42
 
43
+ /** Native segment description helper. */
44
+ function segView(spans: readonly TspSpan[], icon?: string, tone?: TspTone): SegmentView {
45
+ return tone === undefined ? (icon === undefined ? { spans } : { spans, icon }) : { spans, icon, tone };
46
+ }
47
+
48
+ /** Span token for accent-role text; the session accent is a raw hue, so it travels as `accent`. */
49
+ function accentToken(ctx: SegmentContext, color: ThemeColor): string {
50
+ return sessionAccentAnsi(ctx) ? "accent" : color;
51
+ }
52
+
53
+ /** Tone of a status/usage theme color, for the segment chrome. */
54
+ function toneOf(color: ThemeColor): TspTone | undefined {
55
+ switch (color) {
56
+ case "warning":
57
+ return "warning";
58
+ case "error":
59
+ return "error";
60
+ case "success":
61
+ return "success";
62
+ default:
63
+ return undefined;
64
+ }
65
+ }
66
+
34
67
  /**
35
68
  * Hash-derived accent ANSI for the session title (or preview stand-in title).
36
69
  * Undefined when `statusLine.sessionAccent` is off or the session is unnamed,
@@ -91,6 +124,11 @@ function singleStatSegment(
91
124
  });
92
125
  return { content: theme.fg(color, content ?? ""), visible: true };
93
126
  },
127
+ describe(ctx) {
128
+ const value = ctx.usageStats[field];
129
+ if (!value) return null;
130
+ return segView([span(formatNumber(value), color)], iconKey);
131
+ },
94
132
  };
95
133
  }
96
134
 
@@ -187,6 +225,11 @@ const piSegment: StatusLineSegment = {
187
225
  : "";
188
226
  return { content: `${fgAnsi}${content}\x1b[39m`, visible: true };
189
227
  },
228
+ describe(ctx) {
229
+ if (ctx.focusedAgentId) return segView([span(ctx.focusedAgentId, "warning")], "ghost", "warning");
230
+ // The dock's working row owns activity natively: the brand stays still.
231
+ return segView([], "omp", "muted");
232
+ },
190
233
  };
191
234
  /** Current braille-spinner glyph on the shared clock, at the Loader's 80ms cadence. */
192
235
  function brandSpinnerFrame(nowMs = Date.now()): string {
@@ -216,38 +259,87 @@ const statusSegment: StatusLineSegment = {
216
259
  visible: text.length > 0,
217
260
  };
218
261
  },
262
+ describe(ctx) {
263
+ let text = "";
264
+ for (const status of ctx.hookStatuses ?? []) {
265
+ const sanitized = sanitizeStatusText(status);
266
+ if (!sanitized) continue;
267
+ text += text ? `${theme.sep.dot}${sanitized}` : sanitized;
268
+ }
269
+ return text ? segView([span(text, accentToken(ctx, "accent"))]) : null;
270
+ },
219
271
  };
220
272
 
273
+ /** Display name of the active model (`Claude ` prefix dropped). */
274
+ function modelDisplayName(ctx: SegmentContext): string {
275
+ const state = ctx.session.state;
276
+ const modelName = state.model?.name || state.model?.id || "no-model";
277
+ return modelName.startsWith("Claude ") ? modelName.slice(7) : modelName;
278
+ }
279
+
280
+ /**
281
+ * The thinking-effort word (`high`, `off`; `auto` until auto thinking resolves
282
+ * a level), or undefined when the model has no thinking. Drives the native
283
+ * model segment and the composer's effort chip.
284
+ */
285
+ export function thinkingLevelWord(
286
+ session: Pick<StatusLineSession, "state" | "isAutoThinking" | "autoResolvedThinkingLevel">,
287
+ ): string | undefined {
288
+ const state = session.state;
289
+ if (!state.model?.thinking) return undefined;
290
+ if (session.isAutoThinking) return session.autoResolvedThinkingLevel() ?? "auto";
291
+ return state.thinkingLevel ?? ThinkingLevel.Off;
292
+ }
293
+
294
+ /** Thinking-level display ("◉ xhigh", "⟳ auto", …), or "" when hidden or unsupported. */
295
+ function modelThinkingDisplay(ctx: SegmentContext): string {
296
+ const state = ctx.session.state;
297
+ const opts = ctx.options.model ?? {};
298
+ if (opts.showThinkingLevel === false || !state.model?.thinking) return "";
299
+ if (ctx.session.isAutoThinking) {
300
+ // Pending (no turn classified yet / classifying) shows a symbol-theme
301
+ // question-box marker; once resolved it shows `<level>`.
302
+ const resolved = ctx.session.autoResolvedThinkingLevel();
303
+ return resolved
304
+ ? (theme.thinking[resolved as keyof Theme["thinking"]] ?? resolved)
305
+ : `${theme.thinking.autoPending} auto`;
306
+ }
307
+ const level = state.thinkingLevel ?? ThinkingLevel.Off;
308
+ return level === ThinkingLevel.Off
309
+ ? `${theme.status.disabled} off`
310
+ : (theme.thinking[level as keyof Theme["thinking"]] ?? level);
311
+ }
312
+
313
+ /**
314
+ * Advisor symbol, colored by the worst status in the roster: success = all
315
+ * running, warning = quota-exhausted, error = failed, dim = everything
316
+ * paused/no-model. Undefined when no advisor is configured.
317
+ */
318
+ function modelAdvisorBadge(ctx: SegmentContext): { icon: string; color: ThemeColor } | undefined {
319
+ // Optional chaining: lightweight session doubles (test mocks) that don't
320
+ // implement getAdvisorStatusOverview skip the badge instead of crashing.
321
+ const advisorStats = ctx.session.getAdvisorStatusOverview?.();
322
+ if (!advisorStats?.configured || advisorStats.advisors.length === 0) return undefined;
323
+ const statuses = advisorStats.advisors.map(a => a.status);
324
+ const color: ThemeColor = statuses.includes("error")
325
+ ? "error"
326
+ : statuses.includes("quota_exhausted")
327
+ ? "warning"
328
+ : statuses.includes("running")
329
+ ? "success"
330
+ : "dim";
331
+ // Closed eye once every advisor has finished reviewing the yielded
332
+ // turn — no more comments until a new primary turn starts.
333
+ const allYielded = advisorStats.advisors.every(a => a.yielded);
334
+ const icon = allYielded ? theme.icon.advisorClosed || theme.icon.advisor : theme.icon.advisor;
335
+ return icon ? { icon, color } : undefined;
336
+ }
337
+
221
338
  const modelSegment: StatusLineSegment = {
222
339
  id: "model",
223
340
  render(ctx) {
224
- const state = ctx.session.state;
225
- const opts = ctx.options.model ?? {};
226
-
227
- let modelName = state.model?.name || state.model?.id || "no-model";
228
- if (modelName.startsWith("Claude ")) {
229
- modelName = modelName.slice(7);
230
- }
231
-
232
- // Resolve the current thinking-level display ("◉ xhigh", "⟳ auto", …)
233
- // when the model supports thinking and the segment isn't hiding it.
234
- let thinkingDisplay = "";
235
- if (opts.showThinkingLevel !== false && state.model?.thinking) {
236
- if (ctx.session.isAutoThinking) {
237
- // Pending (no turn classified yet / classifying) shows a symbol-theme
238
- // question-box marker; once resolved it shows `<level>`.
239
- const resolved = ctx.session.autoResolvedThinkingLevel();
240
- thinkingDisplay = resolved
241
- ? (theme.thinking[resolved as keyof Theme["thinking"]] ?? resolved)
242
- : `${theme.thinking.autoPending} auto`;
243
- } else {
244
- const level = state.thinkingLevel ?? ThinkingLevel.Off;
245
- thinkingDisplay =
246
- level === ThinkingLevel.Off
247
- ? `${theme.status.disabled} off`
248
- : (theme.thinking[level as keyof Theme["thinking"]] ?? level);
249
- }
250
- }
341
+ const modelName = modelDisplayName(ctx);
342
+ const thinkingDisplay = modelThinkingDisplay(ctx);
251
343
 
252
344
  // Compact mode swaps the model icon for the thinking-level glyph and drops
253
345
  // the " · <level>" tail, keeping the level visible as a single icon.
@@ -270,28 +362,9 @@ const modelSegment: StatusLineSegment = {
270
362
  // `statusLineModel` is aliased to `accent` in many themes, so the badge
271
363
  // uses status colors to stay visibly distinct from the model name color.
272
364
  let content = accentFg(ctx, "statusLineModel", withIcon(modelIcon, modelName));
273
- // Advisor symbol, colored by the worst status in the roster:
274
- // success = all running, warning = quota-exhausted, error = failed,
275
- // dim = everything paused/no-model. Per-advisor detail lives in
276
- // `/advisor status`.
277
- // Optional chaining: lightweight session doubles (test mocks) that don't
278
- // implement getAdvisorStatusOverview skip the badge instead of crashing.
279
- const advisorStats = ctx.session.getAdvisorStatusOverview?.();
280
- if (advisorStats?.configured && advisorStats.advisors.length > 0) {
281
- const statuses = advisorStats.advisors.map(a => a.status);
282
- const badgeColor = statuses.includes("error")
283
- ? "error"
284
- : statuses.includes("quota_exhausted")
285
- ? "warning"
286
- : statuses.includes("running")
287
- ? "success"
288
- : "dim";
289
- // Closed eye once every advisor has finished reviewing the yielded
290
- // turn — no more comments until a new primary turn starts.
291
- const allYielded = advisorStats.advisors.every(a => a.yielded);
292
- const advisorIcon = allYielded ? theme.icon.advisorClosed || theme.icon.advisor : theme.icon.advisor;
293
- if (advisorIcon) content += theme.fg(badgeColor, ` ${advisorIcon}`);
294
- }
365
+ // Per-advisor detail lives in `/advisor status`.
366
+ const advisor = modelAdvisorBadge(ctx);
367
+ if (advisor) content += theme.fg(advisor.color, ` ${advisor.icon}`);
295
368
  if (tail) {
296
369
  content += accentFg(ctx, "statusLineModel", tail);
297
370
  }
@@ -302,6 +375,20 @@ const modelSegment: StatusLineSegment = {
302
375
  if (slowModeLabel) content += theme.fg("warning", `${theme.sep.dot}${slowModeLabel}`);
303
376
  return { content, visible: true };
304
377
  },
378
+ describe(ctx) {
379
+ // Name, then the thinking level as a word in its level colour after a
380
+ // quiet separator; the terminal draws the model icon.
381
+ const token = accentToken(ctx, "statusLineModel");
382
+ const spans: TspSpan[] = [span(modelDisplayName(ctx), token)];
383
+ const advisor = modelAdvisorBadge(ctx);
384
+ if (advisor) spans.push(span(` ${advisor.icon}`, advisor.color));
385
+ if (ctx.session.isFastModeActive() && theme.icon.fast) spans.push(span(` ${theme.icon.fast}`, token));
386
+ const level = ctx.options.model?.showThinkingLevel === false ? undefined : thinkingLevelWord(ctx.session);
387
+ if (level !== undefined) spans.push(span(" · ", "dim"), span(level, thinkingLevelToken(level)));
388
+ const slowModeLabel = ctx.session.getAnthropicSlowModeLabel?.();
389
+ if (slowModeLabel) spans.push(span(`${theme.sep.dot}${slowModeLabel}`, "warning"));
390
+ return segView(spans, "model", slowModeLabel ? "warning" : undefined);
391
+ },
305
392
  };
306
393
 
307
394
  function formatGoalBudget(current: number, budget?: number): string {
@@ -310,6 +397,38 @@ function formatGoalBudget(current: number, budget?: number): string {
310
397
  return `${used}/${formatNumber(budget)}`;
311
398
  }
312
399
 
400
+ /** Native twin of {@link renderGoalMode}: the status names its icon instead of drawing a glyph. */
401
+ function describeGoalMode(ctx: SegmentContext, mode: { enabled: boolean; paused: boolean }): SegmentView {
402
+ const goal = ctx.session.getGoalModeState()?.goal;
403
+ const status = goal?.status ?? (mode.paused ? "paused" : "active");
404
+ let icon = "goal";
405
+ let color: ThemeColor = "accent";
406
+ switch (status) {
407
+ case "paused":
408
+ icon = "pause";
409
+ color = "warning";
410
+ break;
411
+ case "complete":
412
+ icon = "success";
413
+ color = "success";
414
+ break;
415
+ case "budget-limited":
416
+ icon = "warning";
417
+ color = "warning";
418
+ break;
419
+ case "dropped":
420
+ icon = "aborted";
421
+ color = "dim";
422
+ break;
423
+ default:
424
+ break;
425
+ }
426
+ let label = "Goal";
427
+ if (ctx.goalStatusInFooter === true && goal) label += ` ${formatGoalBudget(goal.tokensUsed, goal.tokenBudget)}`;
428
+ const token = color === "accent" ? accentToken(ctx, color) : color;
429
+ return segView([span(label, token)], icon, toneOf(color));
430
+ }
431
+
313
432
  function renderGoalMode(ctx: SegmentContext, mode: { enabled: boolean; paused: boolean }): RenderedSegment {
314
433
  const goal = ctx.session.getGoalModeState()?.goal;
315
434
  const status = goal?.status ?? (mode.paused ? "paused" : "active");
@@ -413,6 +532,33 @@ const modeSegment: StatusLineSegment = {
413
532
 
414
533
  return { content: "", visible: false };
415
534
  },
535
+ describe(ctx) {
536
+ const plan = ctx.planMode;
537
+ if (plan && (plan.enabled || plan.paused)) {
538
+ return plan.paused
539
+ ? segView([span("Plan (paused)", "warning")], "plan", "warning")
540
+ : segView([span("Plan", accentToken(ctx, "accent"))], "plan");
541
+ }
542
+ if (ctx.prewalk?.enabled) return segView([span("Prewalk", accentToken(ctx, "accent"))], "prewalk");
543
+ const goal = ctx.goalMode;
544
+ if (goal && (goal.enabled || goal.paused)) return describeGoalMode(ctx, goal);
545
+ if (ctx.vibeMode?.enabled) return segView([span("Vibe", accentToken(ctx, "accent"))], "agents");
546
+ const loop = ctx.loopMode;
547
+ if (loop) {
548
+ const paused = loop.state === "paused";
549
+ const stateLabel = loop.state === "waiting" ? "next prompt repeats" : loop.state;
550
+ const parts = [`Loop${loop.state === "waiting" ? ":" : ""} ${stateLabel}`];
551
+ const limit = formatLoopLimit(loop.limit, ctx.now?.getTime());
552
+ if (limit) parts.push(limit);
553
+ if (loop.condition) parts.push(summarizeLoopCondition(loop.condition, TRUNCATE_LENGTHS.SHORT));
554
+ return segView(
555
+ [span(parts.join(" "), paused ? "warning" : "customMessageLabel")],
556
+ paused ? "pause" : "loop",
557
+ paused ? "warning" : undefined,
558
+ );
559
+ }
560
+ return null;
561
+ },
416
562
  };
417
563
 
418
564
  const pathSegment: StatusLineSegment = {
@@ -449,8 +595,35 @@ const pathSegment: StatusLineSegment = {
449
595
  const content = withIcon(icon, text);
450
596
  return { content: theme.fg("statusLinePath", content), visible: true };
451
597
  },
598
+ describe(ctx) {
599
+ const opts = ctx.options.path ?? {};
600
+ const stripPrefix = opts.stripWorkPrefix !== false;
601
+ // The parent dims and the leaf reads strong; a click opens the directory.
602
+ if (stripPrefix && ctx.worktree) {
603
+ const { projectName, worktreeName } = ctx.worktree;
604
+ const label = ctx.git.branch === worktreeName ? projectName : `${projectName}/${worktreeName}`;
605
+ return segView(pathSpans(label), "worktree");
606
+ }
607
+ const projectDir = ctx.activeRepo?.cwd ?? getProjectDir();
608
+ const { scratch, displayPath } = getProjectDirDisplay(projectDir);
609
+ let pwd = stripPrefix ? displayPath : projectDir;
610
+ if (opts.abbreviate !== false) pwd = shortenPath(pwd);
611
+ // `maxLength` is the user's configured cap; width fitting is the terminal's.
612
+ if (opts.maxLength !== undefined) pwd = clampPathLength(pwd, opts.maxLength);
613
+ const spans = pathSpans(pwd);
614
+ if (ctx.activeRepo) spans.push(span(` ↳ ${ctx.activeRepo.relativeRepoRoot}`, "statusLinePath dim"));
615
+ return segView(spans, scratch && stripPrefix ? "scratch-folder" : "folder");
616
+ },
452
617
  };
453
618
 
619
+ /** A path as its dim parent directories and its strong leaf. */
620
+ function pathSpans(value: string): TspSpan[] {
621
+ const slash = value.lastIndexOf("/", value.length - 2);
622
+ if (slash < 0 || slash + 1 >= value.length) return [span(value, "statusLinePath strong")];
623
+ const leaf = span(value.slice(slash + 1), "statusLinePath strong");
624
+ return [span(value.slice(0, slash + 1), "statusLinePath dim"), leaf];
625
+ }
626
+
454
627
  const gitSegment: StatusLineSegment = {
455
628
  id: "git",
456
629
  render(ctx) {
@@ -494,6 +667,31 @@ const gitSegment: StatusLineSegment = {
494
667
  const colorName = isDirty ? "statusLineGitDirty" : "statusLineGitClean";
495
668
  return { content: theme.fg(colorName, content), visible: true };
496
669
  },
670
+ describe(ctx) {
671
+ const { branch, status } = ctx.git;
672
+ if (!branch && !status) return null;
673
+ const opts = ctx.options.git ?? {};
674
+ const isDirty = status !== null && (status.staged > 0 || status.unstaged > 0 || status.untracked > 0);
675
+ const colorName = isDirty ? "statusLineGitDirty" : "statusLineGitClean";
676
+ const showBranch = opts.showBranch !== false;
677
+ const spans: TspSpan[] = [];
678
+ if (showBranch && branch) spans.push(span(branch, colorName));
679
+ const indicators: TspSpan[] = [];
680
+ if (status) {
681
+ if (opts.showUnstaged !== false && status.unstaged > 0) {
682
+ indicators.push(span(`*${status.unstaged}`, "statusLineDirty"));
683
+ }
684
+ if (opts.showStaged !== false && status.staged > 0) {
685
+ indicators.push(span(`+${status.staged}`, "statusLineStaged"));
686
+ }
687
+ if (opts.showUntracked !== false && status.untracked > 0) {
688
+ indicators.push(span(`?${status.untracked}`, "statusLineUntracked"));
689
+ }
690
+ }
691
+ spans.push(...indicators);
692
+ if (spans.length === 0) return null;
693
+ return segView(spans, showBranch && branch ? "branch" : "git", isDirty ? "warning" : undefined);
694
+ },
497
695
  };
498
696
 
499
697
  const prSegment: StatusLineSegment = {
@@ -506,6 +704,11 @@ const prSegment: StatusLineSegment = {
506
704
  const content = TERMINAL.hyperlinks ? `\x1b]8;;${pr.url}\x07${label}\x1b]8;;\x07` : label;
507
705
  return { content: accentFg(ctx, "accent", content), visible: true };
508
706
  },
707
+ describe(ctx) {
708
+ const { pr } = ctx.git;
709
+ if (!pr) return null;
710
+ return segView([span(`#${pr.number}`, `${accentToken(ctx, "accent")} link`, { href: pr.url })], "pr");
711
+ },
509
712
  };
510
713
 
511
714
  const subagentsSegment: StatusLineSegment = {
@@ -517,6 +720,10 @@ const subagentsSegment: StatusLineSegment = {
517
720
  const content = withIcon(theme.icon.agents, `${ctx.subagentCount}`);
518
721
  return { content: theme.fg("statusLineSubagents", content), visible: true };
519
722
  },
723
+ describe(ctx) {
724
+ if (ctx.subagentCount === 0) return null;
725
+ return segView([span(`${ctx.subagentCount}`, "statusLineSubagents")], "agents");
726
+ },
520
727
  };
521
728
 
522
729
  const tokenInSegment: StatusLineSegment = singleStatSegment("token_in", "input", "input", "statusLineSpend");
@@ -540,6 +747,12 @@ const tokenTotalSegment: StatusLineSegment = {
540
747
  });
541
748
  return { content: theme.fg("statusLineSpend", content ?? ""), visible: true };
542
749
  },
750
+ describe(ctx) {
751
+ const { input, output, cacheWrite, orchestrationInput, orchestrationOutput } = ctx.usageStats;
752
+ const total = input + output + cacheWrite + orchestrationInput + orchestrationOutput;
753
+ if (!total) return null;
754
+ return segView([span(formatNumber(total), "statusLineSpend")], "tokens");
755
+ },
543
756
  };
544
757
 
545
758
  const tokenRateSegment: StatusLineSegment = {
@@ -554,43 +767,58 @@ const tokenRateSegment: StatusLineSegment = {
554
767
  });
555
768
  return { content: theme.fg("statusLineOutput", content ?? ""), visible: true };
556
769
  },
770
+ describe(ctx) {
771
+ const { tokensPerSecond } = ctx.usageStats;
772
+ if (!tokensPerSecond) return null;
773
+ // The terminal eases the readout between updates.
774
+ return {
775
+ spans: [],
776
+ icon: "throughput",
777
+ motion: [node("rate", { value: Number(tokensPerSecond.toFixed(1)), unit: "tok/s" }, undefined, "rate")],
778
+ };
779
+ },
557
780
  };
558
781
 
782
+ /** Billing summary for the `cost` segment, or undefined when there is nothing to bill. */
783
+ function costSummary(ctx: SegmentContext): string | undefined {
784
+ const { cost, premiumRequests } = ctx.usageStats;
785
+ const advisorCost = ctx.session.getAdvisorCost?.() ?? 0;
786
+ const state = ctx.session.state;
787
+ const pricingPeriod = state.model?.cost
788
+ ? getTimeBasedPricingPeriod(state.model.cost, ctx.now?.getTime())
789
+ : undefined;
790
+ const usingSubscription = state.model ? (ctx.session.modelRegistry?.isUsingOAuth(state.model) ?? false) : false;
791
+ // Resolve the advisor subscription flag lazily: with no active advisor
792
+ // it walks the whole model catalog (getAvailable → hasAuth per provider
793
+ // → credential-file reads), and the status line re-renders at the
794
+ // working-spinner cadence, so an eager per-frame probe pinned CPU (#10129).
795
+ return formatBillingSummary(
796
+ {
797
+ cost,
798
+ usingSubscription,
799
+ premiumRequests,
800
+ fractionDigits: 2,
801
+ pricingPeriod,
802
+ advisor: advisorCost
803
+ ? { cost: advisorCost, usingSubscription: ctx.session.isAdvisorUsingSubscription?.() ?? false }
804
+ : undefined,
805
+ },
806
+ theme,
807
+ );
808
+ }
809
+
559
810
  const costSegment: StatusLineSegment = {
560
811
  id: "cost",
561
812
  render(ctx) {
562
- const { cost, premiumRequests } = ctx.usageStats;
563
- const advisorCost = ctx.session.getAdvisorCost?.() ?? 0;
564
- const state = ctx.session.state;
565
- const pricingPeriod = state.model?.cost
566
- ? getTimeBasedPricingPeriod(state.model.cost, ctx.now?.getTime())
567
- : undefined;
568
- const usingSubscription = state.model ? (ctx.session.modelRegistry?.isUsingOAuth(state.model) ?? false) : false;
569
-
570
- // Resolve the advisor subscription flag lazily: with no active advisor
571
- // it walks the whole model catalog (getAvailable → hasAuth per provider
572
- // → credential-file reads), and the status line re-renders at the
573
- // working-spinner cadence, so an eager per-frame probe pinned CPU (#10129).
574
- const billing = formatBillingSummary(
575
- {
576
- cost,
577
- usingSubscription,
578
- premiumRequests,
579
- fractionDigits: 2,
580
- pricingPeriod,
581
- advisor: advisorCost
582
- ? {
583
- cost: advisorCost,
584
- usingSubscription: ctx.session.isAdvisorUsingSubscription?.() ?? false,
585
- }
586
- : undefined,
587
- },
588
- theme,
589
- );
813
+ const billing = costSummary(ctx);
590
814
  if (!billing) return { content: "", visible: false };
591
815
 
592
816
  return { content: theme.fg("statusLineCost", billing), visible: true };
593
817
  },
818
+ describe(ctx) {
819
+ const billing = costSummary(ctx);
820
+ return billing ? segView([span(billing, "statusLineCost")]) : null;
821
+ },
594
822
  };
595
823
 
596
824
  const contextPctSegment: StatusLineSegment = {
@@ -625,6 +853,29 @@ const contextPctSegment: StatusLineSegment = {
625
853
 
626
854
  return { content, visible: true };
627
855
  },
856
+ describe(ctx) {
857
+ const pct = ctx.contextPercent;
858
+ const window = ctx.contextWindow;
859
+ const level = getContextUsageLevel(pct ?? 0, window);
860
+ const color = getContextUsageThemeColor(level);
861
+ const spans: TspSpan[] = [
862
+ span(
863
+ pct === null && window > 0 ? formatNumber(window) : formatContextUsage(pct, window, ctx.contextTokens),
864
+ color,
865
+ ),
866
+ ];
867
+ if (ctx.autoCompactEnabled && theme.icon.auto) {
868
+ // Async-compaction indicator: the terminal pulses the auto icon while a
869
+ // background speculation runs; an armed result holds it in accent.
870
+ const speculation = ctx.compactionSpeculation;
871
+ if (speculation === "running") {
872
+ spans.push(span(` ${theme.icon.auto}`, accentToken(ctx, "accent"), { fx: "pulse" }));
873
+ } else {
874
+ spans.push(span(` ${theme.icon.auto}`, speculation === "armed" ? accentToken(ctx, "accent") : color));
875
+ }
876
+ }
877
+ return segView(spans, "context", getContextUsageTone(level));
878
+ },
628
879
  };
629
880
 
630
881
  const contextTotalSegment: StatusLineSegment = {
@@ -637,6 +888,10 @@ const contextTotalSegment: StatusLineSegment = {
637
888
  visible: true,
638
889
  };
639
890
  },
891
+ describe(ctx) {
892
+ const window = ctx.contextWindow;
893
+ return window ? segView([span(formatNumber(window), "statusLineContext")], "context") : null;
894
+ },
640
895
  };
641
896
 
642
897
  /**
@@ -653,29 +908,39 @@ const timeSpentSegment: StatusLineSegment = {
653
908
  if (ctx.activeMs < 1000) return { content: "", visible: false };
654
909
  return { content: withIcon(theme.icon.time, formatDuration(ctx.activeMs)), visible: true };
655
910
  },
911
+ describe(ctx) {
912
+ const running = ctx.turnElapsedMs != null;
913
+ if (!running && ctx.activeMs < 1000) return null;
914
+ // Ticks on the terminal's clock during a turn; frozen between turns.
915
+ const elapsed = running
916
+ ? node("elapsed", { age: ctx.activeMs, format: "short" }, undefined, "timer")
917
+ : node("elapsed", { age: ctx.activeMs, stopped: ctx.activeMs, format: "short" }, undefined, "timer");
918
+ return { spans: [], icon: "time", motion: [elapsed] };
919
+ },
656
920
  };
657
921
 
922
+ /** Wall-clock label for the `time` segment. */
923
+ function formatClock(ctx: SegmentContext): string {
924
+ const opts = ctx.options.time ?? {};
925
+ const now = ctx.now ?? new Date();
926
+ let hours = now.getHours();
927
+ let suffix = "";
928
+ if (opts.format === "12h") {
929
+ suffix = hours >= 12 ? "pm" : "am";
930
+ hours = hours % 12 || 12;
931
+ }
932
+ let timeStr = `${hours}:${now.getMinutes().toString().padStart(2, "0")}`;
933
+ if (opts.showSeconds) timeStr += `:${now.getSeconds().toString().padStart(2, "0")}`;
934
+ return timeStr + suffix;
935
+ }
936
+
658
937
  const timeSegment: StatusLineSegment = {
659
938
  id: "time",
660
939
  render(ctx) {
661
- const opts = ctx.options.time ?? {};
662
- const now = ctx.now ?? new Date();
663
-
664
- let hours = now.getHours();
665
- let suffix = "";
666
- if (opts.format === "12h") {
667
- suffix = hours >= 12 ? "pm" : "am";
668
- hours = hours % 12 || 12;
669
- }
670
-
671
- const mins = now.getMinutes().toString().padStart(2, "0");
672
- let timeStr = `${hours}:${mins}`;
673
- if (opts.showSeconds) {
674
- timeStr += `:${now.getSeconds().toString().padStart(2, "0")}`;
675
- }
676
- timeStr += suffix;
677
-
678
- return { content: withIcon(theme.icon.time, timeStr), visible: true };
940
+ return { content: withIcon(theme.icon.time, formatClock(ctx)), visible: true };
941
+ },
942
+ describe(ctx) {
943
+ return segView([span(formatClock(ctx))], "time");
679
944
  },
680
945
  };
681
946
 
@@ -688,6 +953,10 @@ const sessionSegment: StatusLineSegment = {
688
953
 
689
954
  return { content: withIcon(theme.icon.session, display), visible: true };
690
955
  },
956
+ describe(ctx) {
957
+ const sessionId = ctx.session.sessionManager?.getSessionId?.();
958
+ return segView([span(sessionId?.slice(0, 8) || "new")], "session");
959
+ },
691
960
  };
692
961
 
693
962
  const hostnameSegment: StatusLineSegment = {
@@ -698,6 +967,10 @@ const hostnameSegment: StatusLineSegment = {
698
967
  const ansi = sessionAccentAnsi(ctx);
699
968
  return { content: ansi ? `${ansi}${content}\x1b[39m` : content, visible: true };
700
969
  },
970
+ describe(ctx) {
971
+ const name = ctx.hostname ?? os.hostname().split(".")[0];
972
+ return segView([span(name, sessionAccentAnsi(ctx) ? "accent" : undefined)], "host");
973
+ },
701
974
  };
702
975
 
703
976
  const cacheReadSegment: StatusLineSegment = singleStatSegment("cache_read", "cacheRead", "cache", "statusLineSpend");
@@ -729,6 +1002,12 @@ const cacheHitSegment: StatusLineSegment = {
729
1002
  parts.push(theme.fg("statusLineSpend", `${rateStr}%`));
730
1003
  return { content: parts.join(" "), visible: true };
731
1004
  },
1005
+ describe(ctx) {
1006
+ const { cacheRead, cacheWrite, input } = ctx.usageStats;
1007
+ if (!cacheRead) return null;
1008
+ const rate = (cacheRead / (cacheRead + cacheWrite + input)) * 100;
1009
+ return segView([span(`${rate.toFixed(2)}%`, "statusLineSpend")], "cache");
1010
+ },
732
1011
  };
733
1012
 
734
1013
  const sessionNameSegment: StatusLineSegment = {
@@ -741,6 +1020,12 @@ const sessionNameSegment: StatusLineSegment = {
741
1020
  const content = sanitizeStatusText(name);
742
1021
  return { content: accentFg(ctx, "accent", content), visible: true };
743
1022
  },
1023
+ describe(ctx) {
1024
+ const name = ctx.session.sessionManager?.getSessionName() || ctx.previewTitle;
1025
+ const content = name ? sanitizeStatusText(name) : "";
1026
+ // Plain: the terminal styles the title by its role.
1027
+ return content ? segView([span(content)]) : null;
1028
+ },
744
1029
  };
745
1030
 
746
1031
  const collabSegment: StatusLineSegment = {
@@ -751,6 +1036,12 @@ const collabSegment: StatusLineSegment = {
751
1036
  const label = ctx.collab.role === "host" ? `⇄ collab:${participants}` : `⇄ collab guest:${participants}`;
752
1037
  return { content: accentFg(ctx, "accent", label), visible: true };
753
1038
  },
1039
+ describe(ctx) {
1040
+ if (!ctx.collab) return null;
1041
+ const participants = `${ctx.collab.participantCount}`;
1042
+ const label = ctx.collab.role === "host" ? `collab:${participants}` : `collab guest:${participants}`;
1043
+ return segView([span(label, accentToken(ctx, "accent"))], "collab");
1044
+ },
754
1045
  };
755
1046
 
756
1047
  const streamSegment: StatusLineSegment = {
@@ -762,6 +1053,13 @@ const streamSegment: StatusLineSegment = {
762
1053
  if (badges.length === 0) return { content: "", visible: false };
763
1054
  return { content: theme.fg("thinkingHigh", badges.join(" ")), visible: true };
764
1055
  },
1056
+ describe(ctx) {
1057
+ const badges: string[] = [];
1058
+ if (ctx.stream) badges.push(`LIVE ${ctx.stream.viewers}`);
1059
+ if (ctx.recording) badges.push("REC");
1060
+ if (badges.length === 0) return null;
1061
+ return segView([span(badges.join(" "), "thinkingHigh")], "live", "error");
1062
+ },
765
1063
  };
766
1064
 
767
1065
  /**
@@ -809,6 +1107,20 @@ const vimSegment: StatusLineSegment = {
809
1107
  const pending = vim.pending ? theme.fg("muted", ` ${vim.pending}`) : "";
810
1108
  return { content: `${content}${pending}`, visible: true };
811
1109
  },
1110
+ describe(ctx) {
1111
+ const vim = ctx.vim;
1112
+ if (!vim || vim.display === "none") return null;
1113
+ const color = VIM_MODE_COLORS[vim.mode];
1114
+ const spans: TspSpan[] = [];
1115
+ const lines = vim.selectedLines > 1 ? `${vim.selectedLines}L` : "";
1116
+ if (vim.display === "icon") {
1117
+ if (lines) spans.push(span(lines, color));
1118
+ } else {
1119
+ spans.push(span(lines ? `${VIM_MODE_LABELS[vim.mode]} ${lines}` : VIM_MODE_LABELS[vim.mode], color));
1120
+ }
1121
+ if (vim.pending) spans.push(span(` ${vim.pending}`, "muted"));
1122
+ return segView(spans, vim.display === "icon" ? `vim-${vim.mode}` : undefined, toneOf(color));
1123
+ },
812
1124
  };
813
1125
 
814
1126
  function pickUsageColor(percent: number): "muted" | "warning" | "error" {
@@ -837,6 +1149,20 @@ function formatQuotaWindow(
837
1149
  return `${label} ${pctText}${resetText}`;
838
1150
  }
839
1151
 
1152
+ /** Native twin of {@link formatQuotaWindow}. */
1153
+ function describeQuotaWindow(
1154
+ label: string,
1155
+ percent: number,
1156
+ reset: number | undefined,
1157
+ resetUnit: "m" | "h",
1158
+ integer: "round" | "floor",
1159
+ ): TspSpan[] {
1160
+ const whole = integer === "floor" ? Math.floor(percent) : Math.round(percent);
1161
+ const spans = [span(`${label} `), span(`${whole}%`, pickUsageColor(percent))];
1162
+ if (reset !== undefined) spans.push(span(` (${formatUsageReset(reset, resetUnit)})`, "muted"));
1163
+ return spans;
1164
+ }
1165
+
840
1166
  function formatUsageReset(value: number, unit: "m" | "h"): string {
841
1167
  if (unit === "m") {
842
1168
  // Short-window reset timers retain minute precision.
@@ -899,6 +1225,41 @@ const usageSegment: StatusLineSegment = {
899
1225
  const content = withIcon(theme.icon.time, parts.join(theme.sep.dot));
900
1226
  return { content, visible: true };
901
1227
  },
1228
+ describe(ctx) {
1229
+ const u = ctx.usage;
1230
+ if (!u || (!u.fiveHour && !u.daily && !u.sevenDay && !u.monthly && !u.resetCredits)) return null;
1231
+ const parts: TspSpan[][] = [];
1232
+ if (u.tier) {
1233
+ const tier = sanitizeStatusText(u.tier);
1234
+ if (tier) parts.push([span(tier, accentToken(ctx, "accent"))]);
1235
+ }
1236
+ if (u.fiveHour) {
1237
+ parts.push(describeQuotaWindow("5h", u.fiveHour.percent, u.fiveHour.resetMinutes, "m", "round"));
1238
+ }
1239
+ if (u.daily) parts.push(describeQuotaWindow("1d", u.daily.percent, u.daily.resetMinutes, "m", "round"));
1240
+ if (u.sevenDay) {
1241
+ parts.push(describeQuotaWindow("7d", u.sevenDay.percent, u.sevenDay.resetHours, "h", "round"));
1242
+ }
1243
+ if (u.monthly) parts.push(describeQuotaWindow("mo", u.monthly.percent, u.monthly.resetHours, "h", "floor"));
1244
+ if (u.resetCredits) {
1245
+ const resets = u.resetCredits;
1246
+ let resetText = `✦ ${resets.bankedCount}`;
1247
+ if (resets.redeemableCount !== resets.bankedCount) resetText += ` (${resets.redeemableCount} usable)`;
1248
+ if (resets.expiryHours !== undefined) resetText += ` exp ${formatUsageReset(resets.expiryHours, "h")}`;
1249
+ else if (resets.expired) resetText += " expired";
1250
+ if (resets.redeemableCount === 0 && resets.unavailableReason) {
1251
+ const reason = sanitizeStatusText(resets.unavailableReason);
1252
+ if (reason) resetText += ` ${reason}`;
1253
+ }
1254
+ parts.push([span(resetText, resets.redeemableCount > 0 ? "success" : "warning")]);
1255
+ }
1256
+ const spans: TspSpan[] = [];
1257
+ for (const part of parts) {
1258
+ if (spans.length > 0) spans.push(span(theme.sep.dot, "dim"));
1259
+ spans.push(...part);
1260
+ }
1261
+ return segView(spans, "time");
1262
+ },
902
1263
  };
903
1264
 
904
1265
  // ═══════════════════════════════════════════════════════════════════════════
@@ -943,4 +1304,9 @@ export function renderSegment(id: StatusLineSegmentId, ctx: SegmentContext): Ren
943
1304
  return segment.render(ctx);
944
1305
  }
945
1306
 
1307
+ /** Native description of one segment; null when it is hidden or unknown. */
1308
+ export function describeSegment(id: StatusLineSegmentId, ctx: SegmentContext): SegmentView | null {
1309
+ return SEGMENTS[id]?.describe(ctx) ?? null;
1310
+ }
1311
+
946
1312
  export const ALL_SEGMENT_IDS: StatusLineSegmentId[] = Object.keys(SEGMENTS) as StatusLineSegmentId[];