@oh-my-pi/pi-tui 18.2.4 → 18.2.6

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 (748) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +43 -0
  3. package/dist/types/app-keybindings.d.ts +395 -0
  4. package/dist/types/apps/autoresearch-dashboard.d.ts +43 -0
  5. package/dist/types/apps/autoresearch-data.d.ts +15 -0
  6. package/dist/types/apps/cleanse-board.d.ts +66 -0
  7. package/dist/types/apps/cleanse-picker.d.ts +23 -0
  8. package/dist/types/apps/debug/log-formatting.d.ts +6 -0
  9. package/dist/types/apps/debug/log-viewer.d.ts +81 -0
  10. package/dist/types/apps/debug/protocol-probe.d.ts +42 -0
  11. package/dist/types/apps/debug/raw-sse-buffer.d.ts +58 -0
  12. package/dist/types/apps/debug/raw-sse.d.ts +26 -0
  13. package/dist/types/apps/debug/terminal-info.d.ts +34 -0
  14. package/dist/types/apps/debug/viewer-frame.d.ts +52 -0
  15. package/dist/types/apps/git/avatar.d.ts +9 -0
  16. package/dist/types/apps/git/colors.d.ts +49 -0
  17. package/dist/types/apps/git/diff-pane.d.ts +188 -0
  18. package/dist/types/apps/git/git-tui.d.ts +66 -0
  19. package/dist/types/apps/git/sidebar.d.ts +149 -0
  20. package/dist/types/apps/git/state.d.ts +117 -0
  21. package/dist/types/apps/if-bench-board.d.ts +75 -0
  22. package/dist/types/apps/live-visualizer.d.ts +33 -0
  23. package/dist/types/apps/ps-data.d.ts +51 -0
  24. package/dist/types/apps/ps-top.d.ts +33 -0
  25. package/dist/types/apps/session-picker.d.ts +26 -0
  26. package/dist/types/apps/setup-model-picker.d.ts +13 -0
  27. package/dist/types/apps/standalone-picker.d.ts +42 -0
  28. package/dist/types/autocomplete.d.ts +4 -0
  29. package/dist/types/chat/advisor-message.d.ts +11 -0
  30. package/dist/types/chat/assistant-message.d.ts +106 -0
  31. package/dist/types/chat/background-tan-message.d.ts +9 -0
  32. package/dist/types/chat/bash-execution.d.ts +47 -0
  33. package/dist/types/chat/cache-invalidation-marker.d.ts +50 -0
  34. package/dist/types/chat/chat-transcript-builder.d.ts +50 -0
  35. package/dist/types/chat/collab-prompt-message.d.ts +10 -0
  36. package/dist/types/chat/compaction-summary-message.d.ts +54 -0
  37. package/dist/types/chat/custom-message.d.ts +10 -0
  38. package/dist/types/chat/display-preferences.d.ts +13 -0
  39. package/dist/types/chat/eval-execution.d.ts +29 -0
  40. package/dist/types/chat/execution-shared.d.ts +51 -0
  41. package/dist/types/chat/extension-types.d.ts +28 -0
  42. package/dist/types/chat/hook-message.d.ts +10 -0
  43. package/dist/types/chat/image-loading.d.ts +46 -0
  44. package/dist/types/chat/late-diagnostics-message.d.ts +21 -0
  45. package/dist/types/chat/messages.d.ts +189 -0
  46. package/dist/types/chat/reaction.d.ts +30 -0
  47. package/dist/types/chat/read-target.d.ts +4 -0
  48. package/dist/types/chat/read-tool-group.d.ts +67 -0
  49. package/dist/types/chat/served-model-marker.d.ts +47 -0
  50. package/dist/types/chat/skill-message.d.ts +33 -0
  51. package/dist/types/chat/skill-title-input.d.ts +14 -0
  52. package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +12 -0
  53. package/dist/types/chat/thinking-display.d.ts +12 -0
  54. package/dist/types/chat/todo-reminder.d.ts +13 -0
  55. package/dist/types/chat/tool-execution.d.ts +138 -0
  56. package/dist/types/chat/transcript-browser.d.ts +93 -0
  57. package/dist/types/chat/transcript-entry.d.ts +45 -0
  58. package/dist/types/chat/transcript-outline.d.ts +86 -0
  59. package/dist/types/chat/transcript-render-helpers.d.ts +92 -0
  60. package/dist/types/chat/ttsr-notification.d.ts +23 -0
  61. package/dist/types/chat/user-message.d.ts +52 -0
  62. package/dist/types/chrome/chat-block.d.ts +64 -0
  63. package/dist/types/chrome/collab-qrcode.d.ts +28 -0
  64. package/dist/types/chrome/context-thresholds.d.ts +10 -0
  65. package/dist/types/chrome/countdown-timer.d.ts +14 -0
  66. package/dist/types/chrome/diff.d.ts +14 -0
  67. package/dist/types/chrome/dynamic-border.d.ts +16 -0
  68. package/dist/types/chrome/error-block.d.ts +17 -0
  69. package/dist/types/chrome/form-theme.d.ts +3 -0
  70. package/dist/types/chrome/format.d.ts +12 -0
  71. package/dist/types/chrome/index.d.ts +20 -0
  72. package/dist/types/chrome/keybinding-hints.d.ts +40 -0
  73. package/dist/types/chrome/live-board.d.ts +23 -0
  74. package/dist/types/chrome/local-date.d.ts +4 -0
  75. package/dist/types/chrome/message-divider.d.ts +22 -0
  76. package/dist/types/chrome/message-frame.d.ts +52 -0
  77. package/dist/types/chrome/message-notice.d.ts +35 -0
  78. package/dist/types/chrome/overlay-box.d.ts +74 -0
  79. package/dist/types/chrome/qrcode.d.ts +48 -0
  80. package/dist/types/chrome/segment-track.d.ts +27 -0
  81. package/dist/types/chrome/select-list-mouse-routing.d.ts +4 -0
  82. package/dist/types/chrome/selector-helpers.d.ts +52 -0
  83. package/dist/types/chrome/shared.d.ts +5 -0
  84. package/dist/types/chrome/streaming-panel.d.ts +19 -0
  85. package/dist/types/chrome/tool-activity.d.ts +17 -0
  86. package/dist/types/chrome/transcript-container.d.ts +129 -0
  87. package/dist/types/chrome/transcript-status.d.ts +12 -0
  88. package/dist/types/chrome/visual-truncate.d.ts +19 -0
  89. package/dist/types/components/disclosure.d.ts +42 -0
  90. package/dist/types/components/form.d.ts +130 -0
  91. package/dist/types/components/key-value-list.d.ts +24 -0
  92. package/dist/types/components/layout/geometry.d.ts +73 -0
  93. package/dist/types/components/layout/row.d.ts +55 -0
  94. package/dist/types/components/layout/split-pane.d.ts +80 -0
  95. package/dist/types/components/layout/stack.d.ts +50 -0
  96. package/dist/types/components/menu-selection.d.ts +71 -0
  97. package/dist/types/components/metric.d.ts +44 -0
  98. package/dist/types/components/progress-bar.d.ts +36 -0
  99. package/dist/types/components/scroll-view.d.ts +53 -19
  100. package/dist/types/components/scroll-viewport.d.ts +46 -0
  101. package/dist/types/components/section.d.ts +19 -0
  102. package/dist/types/components/select-list.d.ts +46 -2
  103. package/dist/types/components/settings-list.d.ts +20 -0
  104. package/dist/types/components/table.d.ts +32 -0
  105. package/dist/types/components/tree-view.d.ts +130 -0
  106. package/dist/types/components/wizard-step.d.ts +60 -0
  107. package/dist/types/hotkeys-markdown.d.ts +7 -0
  108. package/dist/types/index.d.ts +17 -0
  109. package/dist/types/keybinding-matchers.d.ts +35 -0
  110. package/dist/types/keys.d.ts +2 -2
  111. package/dist/types/lang-from-path.d.ts +9 -0
  112. package/dist/types/overlays/advisor-config.d.ts +114 -0
  113. package/dist/types/overlays/agent-activity.d.ts +41 -0
  114. package/dist/types/overlays/agent-hub-projection.d.ts +33 -0
  115. package/dist/types/overlays/agent-hub-renderer.d.ts +53 -0
  116. package/dist/types/overlays/agent-hub-types.d.ts +99 -0
  117. package/dist/types/overlays/agent-hub.d.ts +126 -0
  118. package/dist/types/overlays/agent-transcript-viewer.d.ts +71 -0
  119. package/dist/types/overlays/agents-hub.d.ts +72 -0
  120. package/dist/types/overlays/ask-dialog.d.ts +88 -0
  121. package/dist/types/overlays/bordered-loader.d.ts +11 -0
  122. package/dist/types/overlays/btw-history-panel.d.ts +29 -0
  123. package/dist/types/overlays/btw-history.d.ts +17 -0
  124. package/dist/types/overlays/btw-panel.d.ts +26 -0
  125. package/dist/types/overlays/cleanse-panel.d.ts +36 -0
  126. package/dist/types/overlays/codex-reset-fireworks.d.ts +52 -0
  127. package/dist/types/overlays/composer-shape-preview.d.ts +47 -0
  128. package/dist/types/overlays/composer-shape-registry.d.ts +50 -0
  129. package/dist/types/overlays/copy-selector.d.ts +48 -0
  130. package/dist/types/overlays/copy-targets.d.ts +63 -0
  131. package/dist/types/overlays/error-banner.d.ts +13 -0
  132. package/dist/types/overlays/extensions/display-text.d.ts +10 -0
  133. package/dist/types/overlays/extensions/extension-dashboard.d.ts +63 -0
  134. package/dist/types/overlays/extensions/extension-list.d.ts +54 -0
  135. package/dist/types/overlays/extensions/inspector-model.d.ts +109 -0
  136. package/dist/types/overlays/extensions/inspector-panel.d.ts +17 -0
  137. package/dist/types/overlays/extensions/live-tool-session.d.ts +28 -0
  138. package/dist/types/overlays/extensions/mcp-runtime.d.ts +107 -0
  139. package/dist/types/overlays/extensions/state-manager.d.ts +42 -0
  140. package/dist/types/overlays/extensions/types.d.ts +165 -0
  141. package/dist/types/overlays/history-search.d.ts +16 -0
  142. package/dist/types/overlays/hook-editor.d.ts +44 -0
  143. package/dist/types/overlays/hook-input.d.ts +20 -0
  144. package/dist/types/overlays/hook-selector.d.ts +71 -0
  145. package/dist/types/overlays/hub-frame.d.ts +77 -0
  146. package/dist/types/overlays/login-dialog.d.ts +43 -0
  147. package/dist/types/overlays/logout-account-selector.d.ts +15 -0
  148. package/dist/types/overlays/mcp-add-wizard.d.ts +87 -0
  149. package/dist/types/overlays/model-browser.d.ts +213 -0
  150. package/dist/types/overlays/model-hub.d.ts +72 -0
  151. package/dist/types/overlays/model-picker.d.ts +74 -0
  152. package/dist/types/overlays/model-selector.d.ts +52 -0
  153. package/dist/types/overlays/move-overlay.d.ts +39 -0
  154. package/dist/types/overlays/oauth-selector.d.ts +41 -0
  155. package/dist/types/overlays/omfg-panel.d.ts +20 -0
  156. package/dist/types/overlays/pause-screen.d.ts +43 -0
  157. package/dist/types/overlays/plan-review-overlay.d.ts +89 -0
  158. package/dist/types/overlays/plan-save-overlay.d.ts +19 -0
  159. package/dist/types/overlays/plan-toc.d.ts +41 -0
  160. package/dist/types/overlays/plugin-selector.d.ts +28 -0
  161. package/dist/types/overlays/plugin-settings.d.ts +180 -0
  162. package/dist/types/overlays/queue-mode-selector.d.ts +11 -0
  163. package/dist/types/overlays/reset-usage-selector.d.ts +23 -0
  164. package/dist/types/overlays/rewind-selector.d.ts +61 -0
  165. package/dist/types/overlays/running-subagent-badge.d.ts +6 -0
  166. package/dist/types/overlays/session-account-selector.d.ts +24 -0
  167. package/dist/types/overlays/session-info-overlay.d.ts +17 -0
  168. package/dist/types/overlays/session-observer-registry.d.ts +77 -0
  169. package/dist/types/overlays/session-selector.d.ts +131 -0
  170. package/dist/types/overlays/settings-defs.d.ts +119 -0
  171. package/dist/types/overlays/settings-selector.d.ts +73 -0
  172. package/dist/types/overlays/show-images-selector.d.ts +11 -0
  173. package/dist/types/overlays/snapcompact-shape-preview.d.ts +31 -0
  174. package/dist/types/overlays/theme-selector.d.ts +12 -0
  175. package/dist/types/overlays/thinking-selector.d.ts +12 -0
  176. package/dist/types/overlays/tiny-title-download-progress.d.ts +21 -0
  177. package/dist/types/overlays/tree-selector.d.ts +92 -0
  178. package/dist/types/overlays/usage-dashboard.d.ts +94 -0
  179. package/dist/types/overlays/usage-display.d.ts +5 -0
  180. package/dist/types/overlays/usage-row.d.ts +17 -0
  181. package/dist/types/prompt/attachment-chips.d.ts +19 -0
  182. package/dist/types/prompt/composer-attachments.d.ts +71 -0
  183. package/dist/types/prompt/composer-cache.d.ts +37 -0
  184. package/dist/types/prompt/composer.d.ts +159 -0
  185. package/dist/types/prompt/custom-editor.d.ts +261 -0
  186. package/dist/types/prompt/editor-top-gap.d.ts +18 -0
  187. package/dist/types/prompt/emoji-autocomplete.d.ts +16 -0
  188. package/dist/types/prompt/github-ref-autocomplete.d.ts +35 -0
  189. package/dist/types/prompt/gradient-highlight.d.ts +32 -0
  190. package/dist/types/prompt/image-format.d.ts +4 -0
  191. package/dist/types/prompt/image-references.d.ts +26 -0
  192. package/dist/types/prompt/interactive-context-helpers.d.ts +43 -0
  193. package/dist/types/prompt/internal-url-autocomplete.d.ts +56 -0
  194. package/dist/types/prompt/macos-spelling.d.ts +44 -0
  195. package/dist/types/prompt/magic-keyword-boundary.d.ts +9 -0
  196. package/dist/types/prompt/magic-keywords.d.ts +26 -0
  197. package/dist/types/prompt/markdown-prose.d.ts +15 -0
  198. package/dist/types/prompt/model-mention-autocomplete.d.ts +25 -0
  199. package/dist/types/prompt/model-mention-syntax.d.ts +24 -0
  200. package/dist/types/prompt/orchestrate.d.ts +13 -0
  201. package/dist/types/prompt/prompt-action-autocomplete.d.ts +56 -0
  202. package/dist/types/prompt/queue-input.d.ts +8 -0
  203. package/dist/types/prompt/skill-tokens.d.ts +13 -0
  204. package/dist/types/prompt/tools-markdown.d.ts +12 -0
  205. package/dist/types/prompt/ultrathink.d.ts +13 -0
  206. package/dist/types/prompt/usage-amounts.d.ts +26 -0
  207. package/dist/types/prompt/video.d.ts +18 -0
  208. package/dist/types/prompt/welcome.d.ts +83 -0
  209. package/dist/types/prompt/workflow.d.ts +13 -0
  210. package/dist/types/render/code-cell.d.ts +51 -0
  211. package/dist/types/render/file-list.d.ts +22 -0
  212. package/dist/types/render/hyperlink.d.ts +80 -0
  213. package/dist/types/render/index.d.ts +16 -0
  214. package/dist/types/render/output-block.d.ts +54 -0
  215. package/dist/types/render/output-pane.d.ts +80 -0
  216. package/dist/types/render/render-utils.d.ts +312 -0
  217. package/dist/types/render/sixel.d.ts +21 -0
  218. package/dist/types/render/status-line.d.ts +23 -0
  219. package/dist/types/render/tool-card.d.ts +71 -0
  220. package/dist/types/render/tree-list.d.ts +29 -0
  221. package/dist/types/render/types.d.ts +17 -0
  222. package/dist/types/render/utils.d.ts +43 -0
  223. package/dist/types/render/width-aware-text.d.ts +23 -0
  224. package/dist/types/setup/lazy.d.ts +3 -0
  225. package/dist/types/setup/scenes/composer.d.ts +3 -0
  226. package/dist/types/setup/scenes/glyph.d.ts +3 -0
  227. package/dist/types/setup/scenes/model.d.ts +3 -0
  228. package/dist/types/setup/scenes/outro.d.ts +2 -0
  229. package/dist/types/setup/scenes/providers.d.ts +3 -0
  230. package/dist/types/setup/scenes/sign-in.d.ts +21 -0
  231. package/dist/types/setup/scenes/splash.d.ts +11 -0
  232. package/dist/types/setup/scenes/theme.d.ts +3 -0
  233. package/dist/types/setup/scenes/types.d.ts +110 -0
  234. package/dist/types/setup/scenes/web-search.d.ts +22 -0
  235. package/dist/types/setup/setup-version.d.ts +11 -0
  236. package/dist/types/setup/startup-splash.d.ts +9 -0
  237. package/dist/types/setup/wizard-overlay.d.ts +16 -0
  238. package/dist/types/setup/wizard.d.ts +25 -0
  239. package/dist/types/status-line/component.d.ts +182 -0
  240. package/dist/types/status-line/context-usage.d.ts +156 -0
  241. package/dist/types/status-line/footer.d.ts +30 -0
  242. package/dist/types/status-line/git-utils.d.ts +22 -0
  243. package/dist/types/status-line/host.d.ts +129 -0
  244. package/dist/types/status-line/index.d.ts +6 -0
  245. package/dist/types/status-line/loop.d.ts +18 -0
  246. package/dist/types/status-line/metrics.d.ts +21 -0
  247. package/dist/types/status-line/presets.d.ts +3 -0
  248. package/dist/types/status-line/schema.d.ts +15 -0
  249. package/dist/types/status-line/segments.d.ts +5 -0
  250. package/dist/types/status-line/separators.d.ts +3 -0
  251. package/dist/types/status-line/types.d.ts +236 -0
  252. package/dist/types/stdin-buffer.d.ts +4 -0
  253. package/dist/types/theme/color.d.ts +24 -0
  254. package/dist/types/theme/defaults/index.d.ts +9489 -0
  255. package/dist/types/theme/index.d.ts +4 -0
  256. package/dist/types/theme/loader.d.ts +23 -0
  257. package/dist/types/theme/mermaid-cache.d.ts +26 -0
  258. package/dist/types/theme/schema-validation.d.ts +3 -0
  259. package/dist/types/theme/schema.d.ts +25 -0
  260. package/dist/types/theme/session-color.d.ts +43 -0
  261. package/dist/types/theme/shimmer.d.ts +44 -0
  262. package/dist/types/theme/symbols.d.ts +34 -0
  263. package/dist/types/theme/theme-class.d.ts +299 -0
  264. package/dist/types/theme/theme.d.ts +111 -0
  265. package/dist/types/theme/tui-adapters.d.ts +30 -0
  266. package/dist/types/thinking.d.ts +165 -0
  267. package/dist/types/tools/agent-tree.d.ts +36 -0
  268. package/dist/types/tools/ask.d.ts +67 -0
  269. package/dist/types/tools/ast-edit.d.ts +53 -0
  270. package/dist/types/tools/ast-grep.d.ts +52 -0
  271. package/dist/types/tools/autoresearch.d.ts +156 -0
  272. package/dist/types/tools/bash-interactive.d.ts +28 -0
  273. package/dist/types/tools/bash.d.ts +103 -0
  274. package/dist/types/tools/conflict-detect.d.ts +43 -0
  275. package/dist/types/tools/debug.d.ts +60 -0
  276. package/dist/types/tools/default-renderer.d.ts +24 -0
  277. package/dist/types/tools/edit.d.ts +158 -0
  278. package/dist/types/tools/eval-format/code-mode-declarations.d.ts +5 -0
  279. package/dist/types/tools/eval-format/index.d.ts +5 -0
  280. package/dist/types/tools/eval-format/javascript.d.ts +2 -0
  281. package/dist/types/tools/eval-format/python.d.ts +2 -0
  282. package/dist/types/tools/eval.d.ts +94 -0
  283. package/dist/types/tools/fetch.d.ts +42 -0
  284. package/dist/types/tools/gh-format.d.ts +8 -0
  285. package/dist/types/tools/github.d.ts +144 -0
  286. package/dist/types/tools/glob.d.ts +44 -0
  287. package/dist/types/tools/goal.d.ts +42 -0
  288. package/dist/types/tools/grep.d.ts +64 -0
  289. package/dist/types/tools/grouped-file-output.d.ts +71 -0
  290. package/dist/types/tools/hashline-format.d.ts +28 -0
  291. package/dist/types/tools/hub.d.ts +340 -0
  292. package/dist/types/tools/index.d.ts +4 -0
  293. package/dist/types/tools/json-tree.d.ts +59 -0
  294. package/dist/types/tools/line-ranges.d.ts +29 -0
  295. package/dist/types/tools/list-limit.d.ts +15 -0
  296. package/dist/types/tools/lsp.d.ts +72 -0
  297. package/dist/types/tools/match-line-format.d.ts +11 -0
  298. package/dist/types/tools/mcp.d.ts +81 -0
  299. package/dist/types/tools/memory.d.ts +67 -0
  300. package/dist/types/tools/output-meta.d.ts +151 -0
  301. package/dist/types/tools/read.d.ts +139 -0
  302. package/dist/types/tools/renderer.d.ts +98 -0
  303. package/dist/types/tools/report-tool-issue.d.ts +8 -0
  304. package/dist/types/tools/resolve.d.ts +47 -0
  305. package/dist/types/tools/streaming-output.d.ts +282 -0
  306. package/dist/types/tools/subprocess.d.ts +24 -0
  307. package/dist/types/tools/task-repair-args.d.ts +53 -0
  308. package/dist/types/tools/task-yield-assembly.d.ts +20 -0
  309. package/dist/types/tools/task.d.ts +425 -0
  310. package/dist/types/tools/terminal-output.d.ts +8 -0
  311. package/dist/types/tools/think.d.ts +13 -0
  312. package/dist/types/tools/todo.d.ts +118 -0
  313. package/dist/types/tools/tool-errors.d.ts +7 -0
  314. package/dist/types/tools/vibe.d.ts +120 -0
  315. package/dist/types/tools/web-search.d.ts +195 -0
  316. package/dist/types/tools/write.d.ts +76 -0
  317. package/dist/types/tools/xd-url.d.ts +13 -0
  318. package/dist/types/tools/xdev.d.ts +52 -0
  319. package/dist/types/tui.d.ts +18 -0
  320. package/dist/types/utils.d.ts +2 -0
  321. package/package.json +37 -3
  322. package/src/app-keybindings.ts +760 -0
  323. package/src/apps/autoresearch-dashboard.ts +500 -0
  324. package/src/apps/autoresearch-data.ts +64 -0
  325. package/src/apps/cleanse-board.ts +341 -0
  326. package/src/apps/cleanse-picker.ts +57 -0
  327. package/src/apps/debug/log-formatting.ts +53 -0
  328. package/src/apps/debug/log-viewer.ts +868 -0
  329. package/src/apps/debug/protocol-probe.ts +267 -0
  330. package/src/apps/debug/raw-sse-buffer.ts +449 -0
  331. package/src/apps/debug/raw-sse.ts +282 -0
  332. package/src/apps/debug/terminal-info.ts +115 -0
  333. package/src/apps/debug/viewer-frame.ts +174 -0
  334. package/src/apps/git/avatar.ts +52 -0
  335. package/src/apps/git/colors.ts +109 -0
  336. package/src/apps/git/diff-pane.ts +1512 -0
  337. package/src/apps/git/git-tui.ts +889 -0
  338. package/src/apps/git/sidebar.ts +1204 -0
  339. package/src/apps/git/state.ts +113 -0
  340. package/src/apps/if-bench-board.ts +324 -0
  341. package/src/apps/live-visualizer.ts +238 -0
  342. package/src/apps/ps-data.ts +116 -0
  343. package/src/apps/ps-top.ts +422 -0
  344. package/src/apps/session-picker.ts +95 -0
  345. package/src/apps/setup-model-picker.ts +21 -0
  346. package/src/apps/standalone-picker.ts +111 -0
  347. package/src/autocomplete.ts +9 -13
  348. package/src/chat/advisor-message.ts +175 -0
  349. package/src/chat/assistant-message.ts +1214 -0
  350. package/src/chat/background-tan-message.ts +35 -0
  351. package/src/chat/bash-execution.ts +394 -0
  352. package/src/chat/cache-invalidation-marker.ts +94 -0
  353. package/src/chat/chat-transcript-builder.ts +593 -0
  354. package/src/chat/collab-prompt-message.ts +34 -0
  355. package/src/chat/compaction-summary-message.ts +261 -0
  356. package/src/chat/custom-message.ts +23 -0
  357. package/src/chat/display-preferences.ts +24 -0
  358. package/src/chat/eval-execution.ts +172 -0
  359. package/src/chat/execution-shared.ts +113 -0
  360. package/src/chat/extension-types.ts +46 -0
  361. package/src/chat/hook-message.ts +20 -0
  362. package/src/chat/image-loading.ts +122 -0
  363. package/src/chat/late-diagnostics-message.ts +92 -0
  364. package/src/chat/messages.ts +305 -0
  365. package/src/chat/reaction.ts +65 -0
  366. package/src/chat/read-target.ts +7 -0
  367. package/src/chat/read-tool-group.ts +873 -0
  368. package/src/chat/served-model-marker.ts +91 -0
  369. package/src/chat/skill-message.ts +220 -0
  370. package/src/chat/skill-title-input.ts +41 -0
  371. package/src/chat/stripped-tool-calls-placeholder.ts +35 -0
  372. package/src/chat/thinking-display.ts +312 -0
  373. package/src/chat/todo-reminder.ts +33 -0
  374. package/src/chat/tool-execution.ts +1395 -0
  375. package/src/chat/transcript-browser.ts +268 -0
  376. package/src/chat/transcript-entry.ts +96 -0
  377. package/src/chat/transcript-outline.ts +260 -0
  378. package/src/chat/transcript-render-helpers.ts +315 -0
  379. package/src/chat/ttsr-notification.ts +123 -0
  380. package/src/chat/user-message.ts +287 -0
  381. package/src/chrome/chat-block.ts +110 -0
  382. package/src/chrome/collab-qrcode.ts +74 -0
  383. package/src/chrome/context-thresholds.ts +85 -0
  384. package/src/chrome/countdown-timer.ts +74 -0
  385. package/src/chrome/diff.ts +266 -0
  386. package/src/chrome/dynamic-border.ts +36 -0
  387. package/src/chrome/error-block.ts +63 -0
  388. package/src/chrome/form-theme.ts +10 -0
  389. package/src/chrome/format.ts +66 -0
  390. package/src/chrome/index.ts +21 -0
  391. package/src/chrome/keybinding-hints.ts +55 -0
  392. package/src/chrome/live-board.ts +123 -0
  393. package/src/chrome/local-date.ts +20 -0
  394. package/src/chrome/message-divider.ts +51 -0
  395. package/src/chrome/message-frame.ts +164 -0
  396. package/src/chrome/message-notice.ts +103 -0
  397. package/src/chrome/overlay-box.ts +301 -0
  398. package/src/chrome/qrcode.ts +539 -0
  399. package/src/chrome/segment-track.ts +86 -0
  400. package/src/chrome/select-list-mouse-routing.ts +11 -0
  401. package/src/chrome/selector-helpers.ts +119 -0
  402. package/src/chrome/shared.ts +27 -0
  403. package/src/chrome/streaming-panel.ts +72 -0
  404. package/src/chrome/tool-activity.ts +44 -0
  405. package/src/chrome/transcript-container.ts +902 -0
  406. package/src/chrome/transcript-status.ts +27 -0
  407. package/src/chrome/visual-truncate.ts +70 -0
  408. package/src/components/disclosure.ts +235 -0
  409. package/src/components/editor.ts +10 -12
  410. package/src/components/form.ts +566 -0
  411. package/src/components/input.ts +6 -27
  412. package/src/components/key-value-list.ts +88 -0
  413. package/src/components/layout/geometry.ts +202 -0
  414. package/src/components/layout/row.ts +283 -0
  415. package/src/components/layout/split-pane.ts +349 -0
  416. package/src/components/layout/stack.ts +261 -0
  417. package/src/components/markdown.ts +16 -12
  418. package/src/components/menu-selection.ts +248 -0
  419. package/src/components/metric.ts +132 -0
  420. package/src/components/progress-bar.ts +140 -0
  421. package/src/components/scroll-view.ts +372 -70
  422. package/src/components/scroll-viewport.ts +154 -0
  423. package/src/components/section.ts +77 -0
  424. package/src/components/select-list.ts +238 -159
  425. package/src/components/settings-list.ts +103 -80
  426. package/src/components/table.ts +127 -0
  427. package/src/components/tree-view.ts +481 -0
  428. package/src/components/wizard-step.ts +229 -0
  429. package/src/deccara.ts +1 -3
  430. package/src/hotkeys-markdown.ts +100 -0
  431. package/src/index.ts +18 -0
  432. package/src/keybinding-matchers.ts +99 -0
  433. package/src/keys.ts +2 -2
  434. package/src/lang-from-path.ts +252 -0
  435. package/src/overlays/advisor-config.ts +761 -0
  436. package/src/overlays/agent-activity.ts +113 -0
  437. package/src/overlays/agent-hub-projection.ts +250 -0
  438. package/src/overlays/agent-hub-renderer.ts +281 -0
  439. package/src/overlays/agent-hub-types.ts +74 -0
  440. package/src/overlays/agent-hub.ts +1562 -0
  441. package/src/overlays/agent-transcript-viewer.ts +669 -0
  442. package/src/overlays/agents-hub.ts +1203 -0
  443. package/src/overlays/ask-dialog.ts +1202 -0
  444. package/src/overlays/bordered-loader.ts +41 -0
  445. package/src/overlays/btw-history-panel.ts +591 -0
  446. package/src/overlays/btw-history.ts +33 -0
  447. package/src/overlays/btw-panel.ts +167 -0
  448. package/src/overlays/cleanse-panel.ts +167 -0
  449. package/src/overlays/codex-reset-fireworks.ts +375 -0
  450. package/src/overlays/composer-shape-preview.ts +137 -0
  451. package/src/overlays/composer-shape-registry.ts +82 -0
  452. package/src/overlays/copy-selector.ts +618 -0
  453. package/src/overlays/copy-targets.ts +210 -0
  454. package/src/overlays/error-banner.ts +39 -0
  455. package/src/overlays/extensions/display-text.ts +39 -0
  456. package/src/overlays/extensions/extension-dashboard.ts +645 -0
  457. package/src/overlays/extensions/extension-list.ts +667 -0
  458. package/src/overlays/extensions/inspector-model.ts +484 -0
  459. package/src/overlays/extensions/inspector-panel.ts +669 -0
  460. package/src/overlays/extensions/live-tool-session.ts +67 -0
  461. package/src/overlays/extensions/mcp-runtime.ts +289 -0
  462. package/src/overlays/extensions/state-manager.ts +337 -0
  463. package/src/overlays/extensions/types.ts +208 -0
  464. package/src/overlays/history-search.ts +268 -0
  465. package/src/overlays/hook-editor.ts +272 -0
  466. package/src/overlays/hook-input.ts +84 -0
  467. package/src/overlays/hook-selector.ts +635 -0
  468. package/src/overlays/hub-frame.ts +262 -0
  469. package/src/overlays/login-dialog.ts +231 -0
  470. package/src/overlays/logout-account-selector.ts +115 -0
  471. package/src/overlays/mcp-add-wizard.ts +1436 -0
  472. package/src/overlays/model-browser.ts +1176 -0
  473. package/src/overlays/model-hub.ts +2279 -0
  474. package/src/overlays/model-picker.ts +285 -0
  475. package/src/overlays/model-selector.ts +107 -0
  476. package/src/overlays/move-overlay.ts +197 -0
  477. package/src/overlays/oauth-selector.ts +446 -0
  478. package/src/overlays/omfg-panel.ts +136 -0
  479. package/src/overlays/pause-screen.ts +198 -0
  480. package/src/overlays/plan-review-overlay.ts +1227 -0
  481. package/src/overlays/plan-save-overlay.ts +74 -0
  482. package/src/overlays/plan-toc.ts +139 -0
  483. package/src/overlays/plugin-selector.ts +97 -0
  484. package/src/overlays/plugin-settings.ts +839 -0
  485. package/src/overlays/queue-mode-selector.ts +55 -0
  486. package/src/overlays/reset-usage-selector.ts +170 -0
  487. package/src/overlays/rewind-selector.ts +499 -0
  488. package/src/overlays/running-subagent-badge.ts +19 -0
  489. package/src/overlays/session-account-selector.ts +68 -0
  490. package/src/overlays/session-info-overlay.ts +96 -0
  491. package/src/overlays/session-observer-registry.ts +289 -0
  492. package/src/overlays/session-selector.ts +1137 -0
  493. package/src/overlays/settings-defs.ts +315 -0
  494. package/src/overlays/settings-selector.ts +1415 -0
  495. package/src/overlays/show-images-selector.ts +44 -0
  496. package/src/overlays/snapcompact-shape-preview-doc.md +14 -0
  497. package/src/overlays/snapcompact-shape-preview.ts +192 -0
  498. package/src/overlays/theme-selector.ts +62 -0
  499. package/src/overlays/thinking-selector.ts +51 -0
  500. package/src/overlays/tiny-title-download-progress.ts +102 -0
  501. package/src/overlays/tree-selector.ts +1121 -0
  502. package/src/overlays/usage-dashboard.ts +629 -0
  503. package/src/overlays/usage-display.ts +45 -0
  504. package/src/overlays/usage-row.ts +118 -0
  505. package/src/prompt/attachment-chips.ts +201 -0
  506. package/src/prompt/composer-attachments.ts +280 -0
  507. package/src/prompt/composer-cache.ts +304 -0
  508. package/src/prompt/composer.ts +934 -0
  509. package/src/prompt/custom-editor.ts +1392 -0
  510. package/src/prompt/data/emojis.json +1 -0
  511. package/src/prompt/editor-top-gap.ts +28 -0
  512. package/src/prompt/emoji-autocomplete.ts +285 -0
  513. package/src/prompt/github-ref-autocomplete.ts +75 -0
  514. package/src/prompt/gradient-highlight.ts +101 -0
  515. package/src/prompt/image-format.ts +28 -0
  516. package/src/prompt/image-references.ts +102 -0
  517. package/src/prompt/interactive-context-helpers.ts +126 -0
  518. package/src/prompt/internal-url-autocomplete.ts +155 -0
  519. package/src/prompt/macos-spelling.ts +456 -0
  520. package/src/prompt/magic-keyword-boundary.ts +23 -0
  521. package/src/prompt/magic-keywords.ts +42 -0
  522. package/src/prompt/markdown-prose.ts +245 -0
  523. package/src/prompt/model-mention-autocomplete.ts +94 -0
  524. package/src/prompt/model-mention-syntax.ts +41 -0
  525. package/src/prompt/orchestrate.ts +26 -0
  526. package/src/prompt/prompt-action-autocomplete.ts +337 -0
  527. package/src/prompt/queue-input.ts +132 -0
  528. package/src/prompt/skill-tokens.ts +36 -0
  529. package/src/prompt/tips.txt +27 -0
  530. package/src/prompt/tools-markdown.ts +29 -0
  531. package/src/prompt/ultrathink.ts +26 -0
  532. package/src/prompt/usage-amounts.ts +99 -0
  533. package/src/prompt/video.ts +47 -0
  534. package/src/prompt/welcome.ts +605 -0
  535. package/src/prompt/workflow.ts +26 -0
  536. package/src/render/code-cell.ts +244 -0
  537. package/src/render/file-list.ts +58 -0
  538. package/src/render/hyperlink.ts +192 -0
  539. package/src/render/index.ts +17 -0
  540. package/src/render/output-block.ts +267 -0
  541. package/src/render/output-pane.ts +299 -0
  542. package/src/render/render-utils.ts +1182 -0
  543. package/src/render/sixel.ts +69 -0
  544. package/src/render/status-line.ts +55 -0
  545. package/src/render/tool-card.ts +272 -0
  546. package/src/render/tree-list.ts +188 -0
  547. package/src/render/types.ts +19 -0
  548. package/src/render/utils.ts +109 -0
  549. package/src/render/width-aware-text.ts +60 -0
  550. package/src/setup/lazy.ts +17 -0
  551. package/src/setup/scenes/composer.ts +127 -0
  552. package/src/setup/scenes/glyph.ts +120 -0
  553. package/src/setup/scenes/model.ts +138 -0
  554. package/src/setup/scenes/outro.ts +24 -0
  555. package/src/setup/scenes/providers.ts +107 -0
  556. package/src/setup/scenes/sign-in.ts +373 -0
  557. package/src/setup/scenes/splash.ts +192 -0
  558. package/src/setup/scenes/theme.ts +343 -0
  559. package/src/setup/scenes/types.ts +111 -0
  560. package/src/setup/scenes/web-search.ts +165 -0
  561. package/src/setup/setup-version.ts +11 -0
  562. package/src/setup/startup-splash.ts +107 -0
  563. package/src/setup/wizard-overlay.ts +319 -0
  564. package/src/setup/wizard.ts +101 -0
  565. package/src/status-line/component.ts +3003 -0
  566. package/src/status-line/context-usage.ts +688 -0
  567. package/src/status-line/footer.ts +323 -0
  568. package/src/status-line/git-utils.ts +42 -0
  569. package/src/status-line/host.ts +97 -0
  570. package/src/status-line/index.ts +6 -0
  571. package/src/status-line/loop.ts +28 -0
  572. package/src/status-line/metrics.ts +94 -0
  573. package/src/status-line/presets.ts +107 -0
  574. package/src/status-line/schema.ts +57 -0
  575. package/src/status-line/segments.ts +934 -0
  576. package/src/status-line/separators.ts +55 -0
  577. package/src/status-line/types.ts +227 -0
  578. package/src/stdin-buffer.ts +4 -2
  579. package/src/theme/color.ts +95 -0
  580. package/src/theme/dark.json +95 -0
  581. package/src/theme/defaults/alabaster.json +93 -0
  582. package/src/theme/defaults/amethyst.json +96 -0
  583. package/src/theme/defaults/anthracite.json +93 -0
  584. package/src/theme/defaults/basalt.json +91 -0
  585. package/src/theme/defaults/birch.json +95 -0
  586. package/src/theme/defaults/dark-abyss.json +91 -0
  587. package/src/theme/defaults/dark-arctic.json +104 -0
  588. package/src/theme/defaults/dark-aurora.json +95 -0
  589. package/src/theme/defaults/dark-catppuccin.json +107 -0
  590. package/src/theme/defaults/dark-cavern.json +91 -0
  591. package/src/theme/defaults/dark-celestial.json +106 -0
  592. package/src/theme/defaults/dark-copper.json +95 -0
  593. package/src/theme/defaults/dark-cosmos.json +90 -0
  594. package/src/theme/defaults/dark-cyberpunk.json +102 -0
  595. package/src/theme/defaults/dark-dracula.json +98 -0
  596. package/src/theme/defaults/dark-eclipse.json +91 -0
  597. package/src/theme/defaults/dark-ember.json +95 -0
  598. package/src/theme/defaults/dark-equinox.json +90 -0
  599. package/src/theme/defaults/dark-forest.json +96 -0
  600. package/src/theme/defaults/dark-github.json +105 -0
  601. package/src/theme/defaults/dark-gruvbox.json +112 -0
  602. package/src/theme/defaults/dark-lavender.json +95 -0
  603. package/src/theme/defaults/dark-lunar.json +89 -0
  604. package/src/theme/defaults/dark-midnight.json +95 -0
  605. package/src/theme/defaults/dark-monochrome.json +94 -0
  606. package/src/theme/defaults/dark-monokai.json +98 -0
  607. package/src/theme/defaults/dark-nebula.json +90 -0
  608. package/src/theme/defaults/dark-nord.json +98 -0
  609. package/src/theme/defaults/dark-ocean.json +101 -0
  610. package/src/theme/defaults/dark-one.json +100 -0
  611. package/src/theme/defaults/dark-poimandres.json +115 -0
  612. package/src/theme/defaults/dark-rainforest.json +91 -0
  613. package/src/theme/defaults/dark-reef.json +91 -0
  614. package/src/theme/defaults/dark-retro.json +92 -0
  615. package/src/theme/defaults/dark-rose-pine.json +96 -0
  616. package/src/theme/defaults/dark-sakura.json +95 -0
  617. package/src/theme/defaults/dark-slate.json +95 -0
  618. package/src/theme/defaults/dark-solarized.json +97 -0
  619. package/src/theme/defaults/dark-solstice.json +90 -0
  620. package/src/theme/defaults/dark-starfall.json +91 -0
  621. package/src/theme/defaults/dark-sunset.json +99 -0
  622. package/src/theme/defaults/dark-swamp.json +90 -0
  623. package/src/theme/defaults/dark-synthwave.json +103 -0
  624. package/src/theme/defaults/dark-taiga.json +91 -0
  625. package/src/theme/defaults/dark-terminal.json +95 -0
  626. package/src/theme/defaults/dark-tokyo-night.json +101 -0
  627. package/src/theme/defaults/dark-tundra.json +91 -0
  628. package/src/theme/defaults/dark-twilight.json +91 -0
  629. package/src/theme/defaults/dark-volcanic.json +91 -0
  630. package/src/theme/defaults/graphite.json +92 -0
  631. package/src/theme/defaults/index.ts +201 -0
  632. package/src/theme/defaults/light-arctic.json +107 -0
  633. package/src/theme/defaults/light-aurora-day.json +91 -0
  634. package/src/theme/defaults/light-canyon.json +91 -0
  635. package/src/theme/defaults/light-catppuccin.json +106 -0
  636. package/src/theme/defaults/light-cirrus.json +90 -0
  637. package/src/theme/defaults/light-coral.json +95 -0
  638. package/src/theme/defaults/light-cyberpunk.json +96 -0
  639. package/src/theme/defaults/light-dawn.json +90 -0
  640. package/src/theme/defaults/light-dunes.json +91 -0
  641. package/src/theme/defaults/light-eucalyptus.json +95 -0
  642. package/src/theme/defaults/light-forest.json +100 -0
  643. package/src/theme/defaults/light-frost.json +95 -0
  644. package/src/theme/defaults/light-github.json +115 -0
  645. package/src/theme/defaults/light-glacier.json +91 -0
  646. package/src/theme/defaults/light-gruvbox.json +108 -0
  647. package/src/theme/defaults/light-haze.json +90 -0
  648. package/src/theme/defaults/light-honeycomb.json +95 -0
  649. package/src/theme/defaults/light-lagoon.json +91 -0
  650. package/src/theme/defaults/light-lavender.json +95 -0
  651. package/src/theme/defaults/light-meadow.json +91 -0
  652. package/src/theme/defaults/light-mint.json +95 -0
  653. package/src/theme/defaults/light-monochrome.json +101 -0
  654. package/src/theme/defaults/light-ocean.json +99 -0
  655. package/src/theme/defaults/light-one.json +99 -0
  656. package/src/theme/defaults/light-opal.json +91 -0
  657. package/src/theme/defaults/light-orchard.json +91 -0
  658. package/src/theme/defaults/light-paper.json +95 -0
  659. package/src/theme/defaults/light-poimandres.json +115 -0
  660. package/src/theme/defaults/light-prism.json +90 -0
  661. package/src/theme/defaults/light-retro.json +98 -0
  662. package/src/theme/defaults/light-sand.json +95 -0
  663. package/src/theme/defaults/light-savanna.json +91 -0
  664. package/src/theme/defaults/light-solarized.json +102 -0
  665. package/src/theme/defaults/light-soleil.json +90 -0
  666. package/src/theme/defaults/light-sunset.json +99 -0
  667. package/src/theme/defaults/light-synthwave.json +98 -0
  668. package/src/theme/defaults/light-tokyo-night.json +111 -0
  669. package/src/theme/defaults/light-wetland.json +91 -0
  670. package/src/theme/defaults/light-zenith.json +89 -0
  671. package/src/theme/defaults/limestone.json +94 -0
  672. package/src/theme/defaults/mahogany.json +97 -0
  673. package/src/theme/defaults/marble.json +93 -0
  674. package/src/theme/defaults/obsidian.json +91 -0
  675. package/src/theme/defaults/onyx.json +91 -0
  676. package/src/theme/defaults/pearl.json +93 -0
  677. package/src/theme/defaults/porcelain.json +91 -0
  678. package/src/theme/defaults/quartz.json +96 -0
  679. package/src/theme/defaults/sandstone.json +95 -0
  680. package/src/theme/defaults/titanium.json +90 -0
  681. package/src/theme/index.ts +5 -0
  682. package/src/theme/light.json +93 -0
  683. package/src/theme/loader.ts +200 -0
  684. package/src/theme/mermaid-cache.ts +92 -0
  685. package/src/theme/schema-validation.ts +119 -0
  686. package/src/theme/schema.ts +168 -0
  687. package/src/theme/session-color.ts +253 -0
  688. package/src/theme/shimmer.ts +307 -0
  689. package/src/theme/symbols.ts +1433 -0
  690. package/src/theme/theme-class.ts +787 -0
  691. package/src/theme/theme-schema.json +463 -0
  692. package/src/theme/theme.ts +810 -0
  693. package/src/theme/tui-adapters.ts +339 -0
  694. package/src/thinking.ts +380 -0
  695. package/src/tools/agent-tree.ts +128 -0
  696. package/src/tools/ask.ts +482 -0
  697. package/src/tools/ast-edit.ts +210 -0
  698. package/src/tools/ast-grep.ts +171 -0
  699. package/src/tools/autoresearch.ts +287 -0
  700. package/src/tools/bash-interactive.ts +295 -0
  701. package/src/tools/bash.ts +468 -0
  702. package/src/tools/conflict-detect.ts +77 -0
  703. package/src/tools/debug.ts +167 -0
  704. package/src/tools/default-renderer.ts +172 -0
  705. package/src/tools/edit.ts +1258 -0
  706. package/src/tools/eval-format/code-mode-declarations.ts +61 -0
  707. package/src/tools/eval-format/index.ts +16 -0
  708. package/src/tools/eval-format/javascript.ts +952 -0
  709. package/src/tools/eval-format/python.ts +544 -0
  710. package/src/tools/eval.ts +835 -0
  711. package/src/tools/fetch.ts +226 -0
  712. package/src/tools/gh-format.ts +18 -0
  713. package/src/tools/github.ts +686 -0
  714. package/src/tools/glob.ts +213 -0
  715. package/src/tools/goal.ts +153 -0
  716. package/src/tools/grep.ts +385 -0
  717. package/src/tools/grouped-file-output.ts +211 -0
  718. package/src/tools/hashline-format.ts +59 -0
  719. package/src/tools/hub.ts +1488 -0
  720. package/src/tools/index.ts +71 -0
  721. package/src/tools/json-tree.ts +388 -0
  722. package/src/tools/line-ranges.ts +98 -0
  723. package/src/tools/list-limit.ts +43 -0
  724. package/src/tools/lsp.ts +702 -0
  725. package/src/tools/match-line-format.ts +20 -0
  726. package/src/tools/mcp.ts +309 -0
  727. package/src/tools/memory.ts +220 -0
  728. package/src/tools/output-meta.ts +367 -0
  729. package/src/tools/read.ts +628 -0
  730. package/src/tools/renderer.ts +107 -0
  731. package/src/tools/report-tool-issue.ts +13 -0
  732. package/src/tools/resolve.ts +139 -0
  733. package/src/tools/streaming-output.ts +1576 -0
  734. package/src/tools/subprocess.ts +39 -0
  735. package/src/tools/task-repair-args.ts +118 -0
  736. package/src/tools/task-yield-assembly.ts +147 -0
  737. package/src/tools/task.ts +2110 -0
  738. package/src/tools/terminal-output.ts +153 -0
  739. package/src/tools/think.ts +28 -0
  740. package/src/tools/todo.ts +580 -0
  741. package/src/tools/tool-errors.ts +15 -0
  742. package/src/tools/vibe.ts +420 -0
  743. package/src/tools/web-search.ts +409 -0
  744. package/src/tools/write.ts +511 -0
  745. package/src/tools/xd-url.ts +28 -0
  746. package/src/tools/xdev.ts +201 -0
  747. package/src/tui.ts +57 -4
  748. package/src/utils.ts +8 -0
@@ -0,0 +1,934 @@
1
+ import * as os from "node:os";
2
+ import * as path from "node:path";
3
+ import { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
4
+ import { getTimeBasedPricingPeriod } from "@oh-my-pi/pi-catalog/models";
5
+ import { SPINNER_ADVANCE_MS, TERMINAL } from "../index";
6
+ import {
7
+ formatDuration,
8
+ formatNumber,
9
+ getProjectDir,
10
+ normalizePathForComparison,
11
+ relativePathWithinNormalizedRoot,
12
+ relativePathWithinRoot,
13
+ } from "@oh-my-pi/pi-utils";
14
+ import { type SymbolKey, type Theme, type ThemeColor, theme } from "../theme";
15
+ import { shortenPath, TRUNCATE_LENGTHS, truncateToWidth } from "../render/render-utils";
16
+ import { fileHyperlink } from "../render/hyperlink";
17
+ import { getSessionAccentAnsi, getSessionAccentHex } from "../theme/session-color";
18
+ import { summarizeLoopCondition } from "./loop";
19
+ import { formatMetric } from "../components/metric";
20
+ import { formatBillingSummary } from "./metrics";
21
+ import { sanitizeStatusText } from "../chrome/shared";
22
+ import { formatContextUsage, getContextUsageLevel, getContextUsageThemeColor } from "../chrome/context-thresholds";
23
+ import type { RenderedSegment, SegmentContext, StatusLineSegment, StatusLineSegmentId } from "./types";
24
+
25
+ export type { SegmentContext } from "./types";
26
+
27
+ // ═══════════════════════════════════════════════════════════════════════════
28
+ // Helpers
29
+ // ═══════════════════════════════════════════════════════════════════════════
30
+
31
+ const STARTUP_PLACEHOLDER = "…";
32
+
33
+ function withIcon(icon: string, text: string): string {
34
+ return icon ? `${icon} ${text}` : text;
35
+ }
36
+
37
+ function statusValue(ctx: SegmentContext, value: string): string {
38
+ return ctx.startupPlaceholder ? STARTUP_PLACEHOLDER : value;
39
+ }
40
+ /**
41
+ * Hash-derived accent ANSI for the session title (or preview stand-in title).
42
+ * Undefined when `statusLine.sessionAccent` is off or the session is unnamed,
43
+ * so callers fall back to their theme color.
44
+ */
45
+ function sessionAccentAnsi(ctx: SegmentContext): string | undefined {
46
+ if (ctx.sessionAccent === false) return undefined;
47
+ const name = ctx.session?.sessionManager?.getSessionName() || ctx.previewTitle;
48
+ if (!name) return undefined;
49
+ return getSessionAccentAnsi(getSessionAccentHex(name, theme.sessionAccentInputs));
50
+ }
51
+ /**
52
+ * `theme.fg` for accent-role text: the hash-derived session accent when
53
+ * enabled, else the given theme color. Callers route only the parts that
54
+ * should carry the session identity color through this (pi icon, model name,
55
+ * PR link, mode badges, session title) — status colors stay `theme.fg`.
56
+ */
57
+ function accentFg(ctx: SegmentContext, color: ThemeColor, text: string): string {
58
+ return `${sessionAccentAnsi(ctx) ?? theme.getFgAnsi(color)}${text}\x1b[39m`;
59
+ }
60
+
61
+ /** Left-truncate a path/label to `maxLen`, prefixing an ellipsis when clipped. */
62
+ function clampPathLength(pwd: string, maxLen: number): string {
63
+ if (pwd.length <= maxLen) return pwd;
64
+ const ellipsis = "…";
65
+ return `${ellipsis}${pwd.slice(-Math.max(0, maxLen - ellipsis.length))}`;
66
+ }
67
+
68
+ /**
69
+ * Leading glyph of a thinking-level display string (e.g. "◉ xhigh" → "◉").
70
+ * Compact mode promotes this glyph to the model-segment icon so the level
71
+ * stays visible without the verbose " · <level>" tail.
72
+ */
73
+ function leadingGlyph(display: string): string {
74
+ const space = display.indexOf(" ");
75
+ return space === -1 ? display : display.slice(0, space);
76
+ }
77
+
78
+ function stripDisplayRoot(pwd: string): string {
79
+ for (const root of [path.join(os.homedir(), "Projects"), "/work"]) {
80
+ const relative = relativePathWithinRoot(root, pwd);
81
+ if (relative) return relative;
82
+ }
83
+ return pwd;
84
+ }
85
+
86
+ /**
87
+ * Single-field usage counter (`token_in`, `token_out`, `cache_read`,
88
+ * `cache_write`). Hidden on zero, startup-placeholder aware, icon omitted when
89
+ * the symbol preset leaves it empty — mirroring {@link withIcon}.
90
+ */
91
+ function singleStatSegment(
92
+ id: StatusLineSegmentId,
93
+ field: "input" | "output" | "cacheRead" | "cacheWrite",
94
+ iconKey: "input" | "output" | "cache",
95
+ color: ThemeColor,
96
+ ): StatusLineSegment {
97
+ return {
98
+ id,
99
+ render(ctx) {
100
+ const value = ctx.usageStats[field];
101
+ if (!value) return { content: "", visible: false };
102
+ const content = formatMetric({
103
+ leading: theme.icon[iconKey] || undefined,
104
+ value: statusValue(ctx, formatNumber(value)),
105
+ });
106
+ return { content: theme.fg(color, content ?? ""), visible: true };
107
+ },
108
+ };
109
+ }
110
+
111
+ const NORMALIZED_SCRATCH_ROOTS: readonly string[] = (() => {
112
+ const roots = new Set<string>([os.tmpdir(), path.join(os.homedir(), "tmp")]);
113
+ if (process.platform === "win32") {
114
+ const { TEMP, TMP, SystemRoot } = process.env;
115
+ if (TEMP) roots.add(TEMP);
116
+ if (TMP) roots.add(TMP);
117
+ if (SystemRoot) roots.add(path.join(SystemRoot, "Temp"));
118
+ } else {
119
+ roots.add("/tmp");
120
+ roots.add("/var/tmp");
121
+ if (process.platform === "darwin") {
122
+ roots.add("/private/tmp");
123
+ roots.add("/private/var/tmp");
124
+ }
125
+ }
126
+ return [...new Set(Array.from(roots, normalizePathForComparison))];
127
+ })();
128
+
129
+ interface ProjectDirClassification {
130
+ scratch: boolean;
131
+ relative: string | null;
132
+ }
133
+
134
+ const PROJECT_DIR_CLASSIFICATIONS = new Map<string, ProjectDirClassification>();
135
+
136
+ function classifyProjectDir(projectDir: string): ProjectDirClassification {
137
+ const cached = PROJECT_DIR_CLASSIFICATIONS.get(projectDir);
138
+ if (cached) return cached;
139
+
140
+ const normalizedProjectDir = normalizePathForComparison(projectDir);
141
+ let classification: ProjectDirClassification = { scratch: false, relative: null };
142
+ for (const normalizedRoot of NORMALIZED_SCRATCH_ROOTS) {
143
+ const relative = relativePathWithinNormalizedRoot(normalizedRoot, normalizedProjectDir);
144
+ if (relative !== null) {
145
+ classification = { scratch: true, relative: relative || null };
146
+ break;
147
+ }
148
+ }
149
+ PROJECT_DIR_CLASSIFICATIONS.set(projectDir, classification);
150
+ return classification;
151
+ }
152
+
153
+ // ═══════════════════════════════════════════════════════════════════════════
154
+ // Segment Implementations
155
+ // ═══════════════════════════════════════════════════════════════════════════
156
+
157
+ const piSegment: StatusLineSegment = {
158
+ id: "pi",
159
+ render(ctx) {
160
+ if (ctx.focusedAgentId) {
161
+ const icon = theme.icon.ghost ? `${theme.icon.ghost} ` : "";
162
+ return {
163
+ content: theme.fg("warning", `${icon}${statusValue(ctx, ctx.focusedAgentId)}`),
164
+ visible: true,
165
+ };
166
+ }
167
+ // Brand fg fades between dim gray (idle) and the accent (working) across
168
+ // turn edges; the component samples the tween into `brandFgAnsi`.
169
+ const fgAnsi = ctx.brandFgAnsi ?? theme.getFgAnsi("dim");
170
+ // While a turn runs the brand icon becomes a braille spinner plus a
171
+ // whole-unit turn timer (port of rust omp's status-band active brand).
172
+ // No trailing pad: the group renderer owns inter-segment spacing, so a
173
+ // trailing space here would double the gap at the first separator (#11103).
174
+ const content =
175
+ ctx.turnElapsedMs != null
176
+ ? `${brandSpinnerFrame(ctx.now?.getTime())} ${statusValue(ctx, brandTimer(ctx.turnElapsedMs))}`
177
+ : theme.icon.omp
178
+ ? theme.icon.omp
179
+ : "";
180
+ return { content: `${fgAnsi}${content}\x1b[39m`, visible: true };
181
+ },
182
+ };
183
+ /** Current braille-spinner glyph on the shared clock, at the Loader's 80ms cadence. */
184
+ function brandSpinnerFrame(nowMs = Date.now()): string {
185
+ const frames = theme.getSpinnerFrames("activity");
186
+ return frames[Math.floor(nowMs / SPINNER_ADVANCE_MS) % frames.length] ?? "";
187
+ }
188
+
189
+ /** Turn timer in omp's brand format: whole seconds → minutes → hours (capped at 99h). */
190
+ function brandTimer(elapsedMs: number): string {
191
+ const seconds = Math.floor(elapsedMs / 1000);
192
+ if (seconds < 60) return `${seconds}s`;
193
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
194
+ return `${Math.min(99, Math.floor(seconds / 3600))}h`;
195
+ }
196
+
197
+ const statusSegment: StatusLineSegment = {
198
+ id: "status",
199
+ render(ctx) {
200
+ let text = "";
201
+ for (const status of ctx.hookStatuses ?? []) {
202
+ const sanitized = sanitizeStatusText(status);
203
+ if (!sanitized) continue;
204
+ text += text ? `${theme.sep.dot}${sanitized}` : sanitized;
205
+ }
206
+ return {
207
+ content: text ? accentFg(ctx, "accent", text) : "",
208
+ visible: text.length > 0,
209
+ };
210
+ },
211
+ };
212
+
213
+ const modelSegment: StatusLineSegment = {
214
+ id: "model",
215
+ render(ctx) {
216
+ const state = ctx.session.state;
217
+ const opts = ctx.options.model ?? {};
218
+
219
+ let modelName = state.model?.name || state.model?.id || "no-model";
220
+ if (modelName.startsWith("Claude ")) {
221
+ modelName = modelName.slice(7);
222
+ }
223
+ modelName = statusValue(ctx, modelName);
224
+
225
+ // Resolve the current thinking-level display ("◉ xhigh", "⟳ auto", …)
226
+ // when the model supports thinking and the segment isn't hiding it.
227
+ let thinkingDisplay = "";
228
+ if (opts.showThinkingLevel !== false && state.model?.thinking) {
229
+ if (ctx.session.isAutoThinking) {
230
+ // Pending (no turn classified yet / classifying) shows a symbol-theme
231
+ // question-box marker; once resolved it shows `<level>`.
232
+ const resolved = ctx.session.autoResolvedThinkingLevel();
233
+ thinkingDisplay = resolved
234
+ ? (theme.thinking[resolved as keyof Theme["thinking"]] ?? resolved)
235
+ : `${theme.thinking.autoPending} auto`;
236
+ } else {
237
+ const level = state.thinkingLevel ?? ThinkingLevel.Off;
238
+ thinkingDisplay =
239
+ level === ThinkingLevel.Off
240
+ ? `${theme.status.disabled} off`
241
+ : (theme.thinking[level as keyof Theme["thinking"]] ?? level);
242
+ }
243
+ }
244
+
245
+ if (ctx.startupPlaceholder && thinkingDisplay) {
246
+ thinkingDisplay = withIcon(leadingGlyph(thinkingDisplay), STARTUP_PLACEHOLDER);
247
+ }
248
+
249
+ // Compact mode swaps the model icon for the thinking-level glyph and drops
250
+ // the " · <level>" tail, keeping the level visible as a single icon.
251
+ const compact = ctx.compactThinkingLevel && thinkingDisplay !== "";
252
+ const modelIcon = compact ? leadingGlyph(thinkingDisplay) : theme.icon.model;
253
+
254
+ // Fast-mode icon and thinking-level suffix trail the model name and are
255
+ // colored together with it as `statusLineModel`. The advisor symbol sits
256
+ // between the name and that tail, so it reads as a distinct marker.
257
+ // theme.fg resets only the fg, so the spans are concatenated (not
258
+ // nested) to keep each color intact.
259
+ let tail = "";
260
+ if (ctx.session.isFastModeActive() && theme.icon.fast) {
261
+ tail += ` ${theme.icon.fast}`;
262
+ }
263
+ if (!compact && thinkingDisplay) {
264
+ tail += `${theme.sep.dot}${thinkingDisplay}`;
265
+ }
266
+
267
+ // `statusLineModel` is aliased to `accent` in many themes, so the badge
268
+ // uses status colors to stay visibly distinct from the model name color.
269
+ let content = accentFg(ctx, "statusLineModel", withIcon(modelIcon, modelName));
270
+ // Advisor symbol, colored by the worst status in the roster:
271
+ // success = all running, warning = quota-exhausted, error = failed,
272
+ // dim = everything paused/no-model. Per-advisor detail lives in
273
+ // `/advisor status`.
274
+ // Optional chaining: lightweight session doubles (test mocks) that don't
275
+ // implement getAdvisorStatusOverview skip the badge instead of crashing.
276
+ const advisorStats = ctx.session.getAdvisorStatusOverview?.();
277
+ if (advisorStats?.configured && advisorStats.advisors.length > 0) {
278
+ const statuses = advisorStats.advisors.map(a => a.status);
279
+ const badgeColor = statuses.includes("error")
280
+ ? "error"
281
+ : statuses.includes("quota_exhausted")
282
+ ? "warning"
283
+ : statuses.includes("running")
284
+ ? "success"
285
+ : "dim";
286
+ // Closed eye once every advisor has finished reviewing the yielded
287
+ // turn — no more comments until a new primary turn starts.
288
+ const allYielded = advisorStats.advisors.every(a => a.yielded);
289
+ const advisorIcon = allYielded ? theme.icon.advisorClosed || theme.icon.advisor : theme.icon.advisor;
290
+ if (advisorIcon) content += theme.fg(badgeColor, ` ${advisorIcon}`);
291
+ }
292
+ if (tail) {
293
+ content += accentFg(ctx, "statusLineModel", tail);
294
+ }
295
+
296
+ return { content, visible: true };
297
+ },
298
+ };
299
+
300
+ function formatGoalBudget(current: number, budget?: number): string {
301
+ const used = formatNumber(current);
302
+ if (budget === undefined) return used;
303
+ return `${used}/${formatNumber(budget)}`;
304
+ }
305
+
306
+ function renderGoalMode(ctx: SegmentContext, mode: { enabled: boolean; paused: boolean }): RenderedSegment {
307
+ const goal = ctx.session.getGoalModeState()?.goal;
308
+ const status = goal?.status ?? (mode.paused ? "paused" : "active");
309
+
310
+ let icon: string = theme.icon.goal;
311
+ let color: ThemeColor = "accent";
312
+ switch (status) {
313
+ case "paused":
314
+ icon = theme.icon.pause || theme.symbol("status.pending");
315
+ color = "warning";
316
+ break;
317
+ case "complete":
318
+ icon = theme.symbol("status.success");
319
+ color = "success";
320
+ break;
321
+ case "budget-limited":
322
+ icon = theme.symbol("status.warning");
323
+ color = "warning";
324
+ break;
325
+ case "dropped":
326
+ icon = theme.symbol("status.aborted");
327
+ color = "dim";
328
+ break;
329
+ default:
330
+ break;
331
+ }
332
+
333
+ const parts: string[] = [withIcon(icon, "Goal")];
334
+ const showBudget = ctx.goalStatusInFooter === true;
335
+ if (showBudget && goal) {
336
+ parts.push(statusValue(ctx, formatGoalBudget(goal.tokensUsed, goal.tokenBudget)));
337
+ }
338
+ return {
339
+ content: color === "accent" ? accentFg(ctx, color, parts.join(" ")) : theme.fg(color, parts.join(" ")),
340
+ visible: true,
341
+ };
342
+ }
343
+
344
+ function formatLoopLimit(
345
+ limit: NonNullable<SegmentContext["loopMode"]>["limit"],
346
+ nowMs = Date.now(),
347
+ ): string | undefined {
348
+ if (!limit) return undefined;
349
+ if (limit.kind === "iterations") return `${limit.remaining}/${limit.initial}`;
350
+
351
+ const totalSeconds = Math.max(0, Math.ceil((limit.deadlineMs - nowMs) / 1_000));
352
+ const hours = Math.floor(totalSeconds / 3_600);
353
+ const minutes = Math.floor((totalSeconds % 3_600) / 60);
354
+ const seconds = totalSeconds % 60;
355
+ if (hours > 0) return `${hours}h${minutes > 0 ? `${minutes}m` : ""} left`;
356
+ if (minutes > 0) return `${minutes}m${seconds > 0 ? `${seconds}s` : ""} left`;
357
+ return `${seconds}s left`;
358
+ }
359
+
360
+ const modeSegment: StatusLineSegment = {
361
+ id: "mode",
362
+ render(ctx) {
363
+ const pauseSuffix = theme.icon.pause ? ` ${theme.icon.pause}` : " (paused)";
364
+
365
+ const plan = ctx.planMode;
366
+ if (plan && (plan.enabled || plan.paused)) {
367
+ const label = plan.paused ? `Plan${pauseSuffix}` : "Plan";
368
+ const content = withIcon(theme.icon.plan, label);
369
+ return {
370
+ content: plan.paused ? theme.fg("warning", content) : accentFg(ctx, "accent", content),
371
+ visible: true,
372
+ };
373
+ }
374
+
375
+ const prewalk = ctx.prewalk;
376
+ if (prewalk?.enabled) {
377
+ const content = withIcon(theme.icon.prewalk, "Prewalk");
378
+ return { content: accentFg(ctx, "accent", content), visible: true };
379
+ }
380
+
381
+ const goal = ctx.goalMode;
382
+ if (goal && (goal.enabled || goal.paused)) {
383
+ return renderGoalMode(ctx, goal);
384
+ }
385
+
386
+ const vibe = ctx.vibeMode;
387
+ if (vibe?.enabled) {
388
+ const content = withIcon(theme.icon.agents, "Vibe");
389
+ return { content: accentFg(ctx, "accent", content), visible: true };
390
+ }
391
+
392
+ const loop = ctx.loopMode;
393
+ if (loop) {
394
+ const icon = loop.state === "paused" ? theme.icon.pause || theme.icon.loop : theme.icon.loop;
395
+ const color: ThemeColor = loop.state === "paused" ? "warning" : "customMessageLabel";
396
+ const parts = [withIcon(icon, `Loop ${statusValue(ctx, loop.state)}`)];
397
+ const limit = formatLoopLimit(loop.limit, ctx.now?.getTime());
398
+ if (limit) parts.push(statusValue(ctx, limit));
399
+ if (loop.condition) {
400
+ parts.push(statusValue(ctx, summarizeLoopCondition(loop.condition, TRUNCATE_LENGTHS.SHORT)));
401
+ }
402
+ return { content: theme.fg(color, parts.join(" ")), visible: true };
403
+ }
404
+
405
+ return { content: "", visible: false };
406
+ },
407
+ };
408
+
409
+ const pathSegment: StatusLineSegment = {
410
+ id: "path",
411
+ render(ctx) {
412
+ const opts = ctx.options.path ?? {};
413
+ const stripPrefix = opts.stripWorkPrefix !== false;
414
+
415
+ // Linked git worktree: the on-disk path nests the worktree base, the
416
+ // project, and a worktree dir that usually duplicates the branch (already
417
+ // shown by the git segment). Collapse to the project name, appending the
418
+ // worktree dir only when it diverges from the branch.
419
+ if (stripPrefix && ctx.worktree) {
420
+ const { projectName, worktreeName } = ctx.worktree;
421
+ const label = ctx.git.branch === worktreeName ? projectName : `${projectName}/${worktreeName}`;
422
+ const text = ctx.startupPlaceholder
423
+ ? STARTUP_PLACEHOLDER
424
+ : fileHyperlink(getProjectDir(), clampPathLength(label, opts.maxLength ?? 40));
425
+ const content = withIcon(theme.icon.worktree, text);
426
+ return { content: theme.fg("statusLinePath", content), visible: true };
427
+ }
428
+
429
+ const projectDir = ctx.activeRepo?.cwd ?? getProjectDir();
430
+ const { scratch, relative } = classifyProjectDir(projectDir);
431
+ let pwd = projectDir;
432
+
433
+ if (stripPrefix) {
434
+ if (scratch) {
435
+ if (relative) pwd = relative;
436
+ } else {
437
+ pwd = stripDisplayRoot(pwd);
438
+ }
439
+ }
440
+ const repoSuffix = ctx.activeRepo ? ` ↳ ${ctx.activeRepo.relativeRepoRoot}` : "";
441
+ if (opts.abbreviate !== false) {
442
+ pwd = shortenPath(pwd);
443
+ }
444
+
445
+ pwd = clampPathLength(pwd, opts.maxLength ?? 40);
446
+
447
+ const showScratchIcon = scratch && stripPrefix;
448
+ const icon = showScratchIcon ? theme.icon.scratchFolder : theme.icon.folder;
449
+ const text = ctx.startupPlaceholder ? STARTUP_PLACEHOLDER : `${fileHyperlink(projectDir, pwd)}${repoSuffix}`;
450
+ const content = withIcon(icon, text);
451
+ return { content: theme.fg("statusLinePath", content), visible: true };
452
+ },
453
+ };
454
+
455
+ const gitSegment: StatusLineSegment = {
456
+ id: "git",
457
+ render(ctx) {
458
+ const { branch, status } = ctx.git;
459
+ if (!branch && !status) return { content: "", visible: false };
460
+
461
+ const opts = ctx.options.git ?? {};
462
+ const gitStatus = status;
463
+ const isDirty = gitStatus && (gitStatus.staged > 0 || gitStatus.unstaged > 0 || gitStatus.untracked > 0);
464
+
465
+ const showBranch = opts.showBranch !== false;
466
+ let content = "";
467
+ if (showBranch && branch) {
468
+ content = withIcon(theme.icon.branch, statusValue(ctx, branch));
469
+ }
470
+
471
+ // Add status indicators
472
+ if (gitStatus) {
473
+ const indicators: string[] = [];
474
+ if (opts.showUnstaged !== false && gitStatus.unstaged > 0) {
475
+ indicators.push(theme.fg("statusLineDirty", `*${statusValue(ctx, `${gitStatus.unstaged}`)}`));
476
+ }
477
+ if (opts.showStaged !== false && gitStatus.staged > 0) {
478
+ indicators.push(theme.fg("statusLineStaged", `+${statusValue(ctx, `${gitStatus.staged}`)}`));
479
+ }
480
+ if (opts.showUntracked !== false && gitStatus.untracked > 0) {
481
+ indicators.push(theme.fg("statusLineUntracked", `?${statusValue(ctx, `${gitStatus.untracked}`)}`));
482
+ }
483
+ if (indicators.length > 0) {
484
+ const indicatorText = indicators.join(" ");
485
+ if (!content && showBranch === false) {
486
+ content = withIcon(theme.icon.git, indicatorText);
487
+ } else {
488
+ content += content ? ` ${indicatorText}` : indicatorText;
489
+ }
490
+ }
491
+ }
492
+
493
+ if (!content) return { content: "", visible: false };
494
+
495
+ const colorName = isDirty ? "statusLineGitDirty" : "statusLineGitClean";
496
+ return { content: theme.fg(colorName, content), visible: true };
497
+ },
498
+ };
499
+
500
+ const prSegment: StatusLineSegment = {
501
+ id: "pr",
502
+ render(ctx) {
503
+ const { pr } = ctx.git;
504
+ if (!pr) return { content: "", visible: false };
505
+
506
+ const label = withIcon(theme.icon.pr, `#${statusValue(ctx, `${pr.number}`)}`);
507
+ const content =
508
+ !ctx.startupPlaceholder && TERMINAL.hyperlinks ? `\x1b]8;;${pr.url}\x07${label}\x1b]8;;\x07` : label;
509
+ return { content: accentFg(ctx, "accent", content), visible: true };
510
+ },
511
+ };
512
+
513
+ const subagentsSegment: StatusLineSegment = {
514
+ id: "subagents",
515
+ render(ctx) {
516
+ if (ctx.subagentCount === 0) {
517
+ return { content: "", visible: false };
518
+ }
519
+ const content = withIcon(theme.icon.agents, statusValue(ctx, `${ctx.subagentCount}`));
520
+ return { content: theme.fg("statusLineSubagents", content), visible: true };
521
+ },
522
+ };
523
+
524
+ const tokenInSegment: StatusLineSegment = singleStatSegment("token_in", "input", "input", "statusLineSpend");
525
+
526
+ const tokenOutSegment: StatusLineSegment = singleStatSegment("token_out", "output", "output", "statusLineOutput");
527
+
528
+ const tokenTotalSegment: StatusLineSegment = {
529
+ id: "token_total",
530
+ render(ctx) {
531
+ // Excludes cacheRead: that field re-reads the full cached context every
532
+ // turn, making the cumulative sum N×context_size. Orchestration cache read
533
+ // follows the same rule; orchestration input/output remain in the total so
534
+ // provider-side service work is preserved without labeling it prompt input.
535
+ const { input, output, cacheWrite, orchestrationInput, orchestrationOutput } = ctx.usageStats;
536
+ const total = input + output + cacheWrite + orchestrationInput + orchestrationOutput;
537
+ if (!total) return { content: "", visible: false };
538
+
539
+ const content = formatMetric({
540
+ leading: theme.icon.tokens || undefined,
541
+ value: statusValue(ctx, formatNumber(total)),
542
+ });
543
+ return { content: theme.fg("statusLineSpend", content ?? ""), visible: true };
544
+ },
545
+ };
546
+
547
+ const tokenRateSegment: StatusLineSegment = {
548
+ id: "token_rate",
549
+ render(ctx) {
550
+ const { tokensPerSecond } = ctx.usageStats;
551
+ if (!tokensPerSecond) return { content: "", visible: false };
552
+
553
+ const content = formatMetric({
554
+ leading: theme.icon.throughput || undefined,
555
+ value: `${statusValue(ctx, tokensPerSecond.toFixed(1))} tok/s`,
556
+ });
557
+ return { content: theme.fg("statusLineOutput", content ?? ""), visible: true };
558
+ },
559
+ };
560
+
561
+ const costSegment: StatusLineSegment = {
562
+ id: "cost",
563
+ render(ctx) {
564
+ const { cost, premiumRequests } = ctx.usageStats;
565
+ const advisorCost = ctx.session.getAdvisorCost?.() ?? 0;
566
+ const state = ctx.session.state;
567
+ const pricingPeriod = state.model?.cost
568
+ ? getTimeBasedPricingPeriod(state.model.cost, ctx.now?.getTime())
569
+ : undefined;
570
+ const usingSubscription = state.model ? (ctx.session.modelRegistry?.isUsingOAuth(state.model) ?? false) : false;
571
+
572
+ // Resolve the advisor subscription flag lazily: with no active advisor
573
+ // it walks the whole model catalog (getAvailable → hasAuth per provider
574
+ // → credential-file reads), and the status line re-renders at the
575
+ // working-spinner cadence, so an eager per-frame probe pinned CPU (#10129).
576
+ const billing = formatBillingSummary(
577
+ {
578
+ cost,
579
+ usingSubscription,
580
+ premiumRequests,
581
+ fractionDigits: 2,
582
+ startupPlaceholder: ctx.startupPlaceholder,
583
+ pricingPeriod,
584
+ advisor: advisorCost
585
+ ? {
586
+ cost: advisorCost,
587
+ usingSubscription: ctx.session.isAdvisorUsingSubscription?.() ?? false,
588
+ }
589
+ : undefined,
590
+ },
591
+ theme,
592
+ );
593
+ if (!billing) return { content: "", visible: false };
594
+
595
+ return { content: theme.fg("statusLineCost", billing), visible: true };
596
+ },
597
+ };
598
+
599
+ const contextPctSegment: StatusLineSegment = {
600
+ id: "context_pct",
601
+ render(ctx) {
602
+ const pct = ctx.contextPercent;
603
+ const window = ctx.contextWindow;
604
+
605
+ const color = getContextUsageThemeColor(getContextUsageLevel(pct ?? 0, window));
606
+ // Async-compaction indicator: pulse the auto icon while a background
607
+ // speculation runs, hold it in accent once a result is armed.
608
+ let autoIcon = "";
609
+ if (ctx.autoCompactEnabled && theme.icon.auto) {
610
+ const speculation = ctx.compactionSpeculation;
611
+ const accentIcon = accentFg(ctx, "accent", theme.icon.auto);
612
+ autoIcon = ` ${
613
+ speculation === "running"
614
+ ? ctx.speculationBlinkOn
615
+ ? accentIcon
616
+ : theme.fg("muted", theme.icon.auto)
617
+ : speculation === "armed"
618
+ ? accentIcon
619
+ : theme.fg(color, theme.icon.auto)
620
+ }`;
621
+ }
622
+ const text = theme.fg(
623
+ color,
624
+ ctx.startupPlaceholder ? STARTUP_PLACEHOLDER : formatContextUsage(pct, window, ctx.contextTokens),
625
+ );
626
+ const content = withIcon(theme.icon.context, `${text}${autoIcon}`);
627
+
628
+ return { content, visible: true };
629
+ },
630
+ };
631
+
632
+ const contextTotalSegment: StatusLineSegment = {
633
+ id: "context_total",
634
+ render(ctx) {
635
+ const window = ctx.contextWindow;
636
+ if (!window) return { content: "", visible: false };
637
+ return {
638
+ content: theme.fg("statusLineContext", withIcon(theme.icon.context, statusValue(ctx, formatNumber(window)))),
639
+ visible: true,
640
+ };
641
+ },
642
+ };
643
+
644
+ /**
645
+ * Total time the agent was actively processing this session — the union of
646
+ * every `agent_start`→`agent_end` window plus the currently-running window,
647
+ * sourced from {@link SegmentContext.activeMs}. Idle wall-clock between turns
648
+ * never accumulates, so the displayed total reflects how long the agent has
649
+ * been working for the user, not how long the session has been open. Hidden
650
+ * before the first second of activity to avoid flashing `0s` at session start.
651
+ */
652
+ const timeSpentSegment: StatusLineSegment = {
653
+ id: "time_spent",
654
+ render(ctx) {
655
+ if (ctx.activeMs < 1000) return { content: "", visible: false };
656
+ return { content: withIcon(theme.icon.time, statusValue(ctx, formatDuration(ctx.activeMs))), visible: true };
657
+ },
658
+ };
659
+
660
+ const timeSegment: StatusLineSegment = {
661
+ id: "time",
662
+ render(ctx) {
663
+ const opts = ctx.options.time ?? {};
664
+ const now = ctx.now ?? new Date();
665
+
666
+ let hours = now.getHours();
667
+ let suffix = "";
668
+ if (opts.format === "12h") {
669
+ suffix = hours >= 12 ? "pm" : "am";
670
+ hours = hours % 12 || 12;
671
+ }
672
+
673
+ const mins = now.getMinutes().toString().padStart(2, "0");
674
+ let timeStr = `${hours}:${mins}`;
675
+ if (opts.showSeconds) {
676
+ timeStr += `:${now.getSeconds().toString().padStart(2, "0")}`;
677
+ }
678
+ timeStr += suffix;
679
+
680
+ return { content: withIcon(theme.icon.time, statusValue(ctx, timeStr)), visible: true };
681
+ },
682
+ };
683
+
684
+ const sessionSegment: StatusLineSegment = {
685
+ id: "session",
686
+ render(ctx) {
687
+ const sessionManager = ctx.session.sessionManager;
688
+ const sessionId = sessionManager?.getSessionId?.();
689
+ const display = statusValue(ctx, sessionId?.slice(0, 8) || "new");
690
+
691
+ return { content: withIcon(theme.icon.session, display), visible: true };
692
+ },
693
+ };
694
+
695
+ const hostnameSegment: StatusLineSegment = {
696
+ id: "hostname",
697
+ render(ctx) {
698
+ const name = statusValue(ctx, ctx.hostname ?? os.hostname().split(".")[0]);
699
+ const content = withIcon(theme.icon.host, name);
700
+ const ansi = sessionAccentAnsi(ctx);
701
+ return { content: ansi ? `${ansi}${content}\x1b[39m` : content, visible: true };
702
+ },
703
+ };
704
+
705
+ const cacheReadSegment: StatusLineSegment = singleStatSegment("cache_read", "cacheRead", "cache", "statusLineSpend");
706
+
707
+ const cacheWriteSegment: StatusLineSegment = singleStatSegment(
708
+ "cache_write",
709
+ "cacheWrite",
710
+ "cache",
711
+ "statusLineOutput",
712
+ );
713
+
714
+ const cacheHitSegment: StatusLineSegment = {
715
+ id: "cache_hit",
716
+ render(ctx) {
717
+ const { cacheRead, cacheWrite, input } = ctx.usageStats;
718
+ if (!cacheRead) return { content: "", visible: false };
719
+
720
+ // Hit rate = cacheRead / total prompt tokens. The prompt is the sum of
721
+ // cacheRead (served from cache), cacheWrite (newly cached this turn) and
722
+ // input (uncached). Including uncached input keeps the denominator honest
723
+ // for Anthropic/OpenRouter; DeepSeek reports its miss as input with
724
+ // cacheWrite 0, so this still yields hit/(hit+miss).
725
+ const total = cacheRead + cacheWrite + input;
726
+
727
+ const rate = (cacheRead / total) * 100;
728
+ const rateStr = statusValue(ctx, rate.toFixed(2));
729
+
730
+ const parts: string[] = [theme.icon.cache];
731
+ parts.push(theme.fg("statusLineSpend", `${rateStr}%`));
732
+ return { content: parts.join(" "), visible: true };
733
+ },
734
+ };
735
+
736
+ const sessionNameSegment: StatusLineSegment = {
737
+ id: "session_name",
738
+ render(ctx) {
739
+ const sessionManager = ctx.session.sessionManager;
740
+ const name = sessionManager?.getSessionName() || ctx.previewTitle;
741
+ if (!name) return { content: "", visible: false };
742
+
743
+ const content = ctx.startupPlaceholder ? STARTUP_PLACEHOLDER : sanitizeStatusText(name);
744
+ return { content: accentFg(ctx, "accent", content), visible: true };
745
+ },
746
+ };
747
+
748
+ const collabSegment: StatusLineSegment = {
749
+ id: "collab",
750
+ render(ctx) {
751
+ if (!ctx.collab) return { content: "", visible: false };
752
+ const participants = statusValue(ctx, `${ctx.collab.participantCount}`);
753
+ const label = ctx.collab.role === "host" ? `⇄ collab:${participants}` : `⇄ collab guest:${participants}`;
754
+ return { content: accentFg(ctx, "accent", label), visible: true };
755
+ },
756
+ };
757
+
758
+ const streamSegment: StatusLineSegment = {
759
+ id: "stream",
760
+ render(ctx) {
761
+ if (!ctx.stream) return { content: "", visible: false };
762
+ const viewers = statusValue(ctx, `${ctx.stream.viewers}`);
763
+ return { content: theme.fg("thinkingHigh", `● LIVE ${viewers}`), visible: true };
764
+ },
765
+ };
766
+
767
+ /**
768
+ * Vim modal state, in the shape Vim itself uses: the mode, the half-typed command echoed beside it
769
+ * (`showcmd`), and the Visual selection size. Hidden entirely when `tui.vimMode` is off, so it
770
+ * costs nothing for everyone else. `tui.vimModeDisplay` picks the mode's presentation.
771
+ */
772
+ const VIM_MODE_LABELS: Record<NonNullable<SegmentContext["vim"]>["mode"], string> = {
773
+ insert: "INSERT",
774
+ normal: "NORMAL",
775
+ visual: "VISUAL",
776
+ "visual-line": "V-LINE",
777
+ };
778
+
779
+ /**
780
+ * The `icon` display resolves through the theme's symbol map, so each mode picks up the active
781
+ * symbol preset (nerd / unicode / ascii) and honours per-theme `symbols` overrides — same mechanism
782
+ * as every other status-line icon. Glyph choices live in `SYMBOL_PRESETS`.
783
+ */
784
+ const VIM_MODE_ICON_KEYS: Record<NonNullable<SegmentContext["vim"]>["mode"], SymbolKey> = {
785
+ insert: "icon.vimInsert",
786
+ normal: "icon.vimNormal",
787
+ visual: "icon.vimVisual",
788
+ "visual-line": "icon.vimVisualLine",
789
+ };
790
+
791
+ const VIM_MODE_COLORS: Record<NonNullable<SegmentContext["vim"]>["mode"], ThemeColor> = {
792
+ insert: "success",
793
+ normal: "accent",
794
+ visual: "warning",
795
+ "visual-line": "warning",
796
+ };
797
+
798
+ const vimSegment: StatusLineSegment = {
799
+ id: "vim",
800
+ render(ctx) {
801
+ const vim = ctx.vim;
802
+ if (!vim || vim.display === "none") return { content: "", visible: false };
803
+ let label = vim.display === "icon" ? theme.symbol(VIM_MODE_ICON_KEYS[vim.mode]) : VIM_MODE_LABELS[vim.mode];
804
+ // The selection height rides along in both presentations — it is the one part of the
805
+ // indicator with no other on-screen source.
806
+ if (vim.selectedLines > 1) label += ` ${vim.selectedLines}L`;
807
+ const content = theme.fg(VIM_MODE_COLORS[vim.mode], label);
808
+ // Pending echoes to the right of the mode, dimmed, exactly like Vim's showcmd.
809
+ const pending = vim.pending ? theme.fg("muted", ` ${vim.pending}`) : "";
810
+ return { content: `${content}${pending}`, visible: true };
811
+ },
812
+ };
813
+
814
+ function pickUsageColor(percent: number): "muted" | "warning" | "error" {
815
+ if (percent >= 80) return "error";
816
+ if (percent >= 50) return "warning";
817
+ return "muted";
818
+ }
819
+
820
+ /**
821
+ * One quota window (`5h`, `1d`, `7d`, `mo`). The integer policy (round vs
822
+ * floor) and the reset unit (minutes vs hours) stay explicit at each call site:
823
+ * monthly floors like the Cursor/OpenCode dashboards, the rest round, and
824
+ * short windows reset in minutes while long windows reset in hours.
825
+ */
826
+ function formatQuotaWindow(
827
+ ctx: SegmentContext,
828
+ label: string,
829
+ percent: number,
830
+ reset: number | undefined,
831
+ resetUnit: "m" | "h",
832
+ integer: "round" | "floor",
833
+ ): string {
834
+ const whole = integer === "floor" ? Math.floor(percent) : Math.round(percent);
835
+ const pctText = theme.fg(pickUsageColor(percent), `${statusValue(ctx, `${whole}`)}%`);
836
+ const resetText =
837
+ reset !== undefined
838
+ ? theme.fg("muted", ctx.startupPlaceholder ? " (…)" : ` (${formatUsageReset(reset, resetUnit)})`)
839
+ : "";
840
+ return `${label} ${pctText}${resetText}`;
841
+ }
842
+
843
+ function formatUsageReset(value: number, unit: "m" | "h"): string {
844
+ if (unit === "m") {
845
+ // Short-window reset timers retain minute precision.
846
+ if (value < 60) return `${value}m`;
847
+ const hours = Math.floor(value / 60);
848
+ const mins = value % 60;
849
+ return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
850
+ }
851
+ // total hours (7d window: max 168)
852
+ if (value < 24) return `${value}h`;
853
+ const days = Math.floor(value / 24);
854
+ const hours = value % 24;
855
+ return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
856
+ }
857
+
858
+ const usageSegment: StatusLineSegment = {
859
+ id: "usage",
860
+ render(ctx) {
861
+ const u = ctx.usage;
862
+ if (!u || (!u.fiveHour && !u.daily && !u.sevenDay && !u.monthly)) {
863
+ return { content: "", visible: false };
864
+ }
865
+ const parts: string[] = [];
866
+ if (u.tier) {
867
+ const tier = ctx.startupPlaceholder
868
+ ? STARTUP_PLACEHOLDER
869
+ : truncateToWidth(sanitizeStatusText(u.tier), TRUNCATE_LENGTHS.SHORT);
870
+ if (tier) parts.push(accentFg(ctx, "accent", tier));
871
+ }
872
+ if (u.fiveHour) {
873
+ parts.push(formatQuotaWindow(ctx, "5h", u.fiveHour.percent, u.fiveHour.resetMinutes, "m", "round"));
874
+ }
875
+ if (u.daily) {
876
+ parts.push(formatQuotaWindow(ctx, "1d", u.daily.percent, u.daily.resetMinutes, "m", "round"));
877
+ }
878
+ if (u.sevenDay) {
879
+ parts.push(formatQuotaWindow(ctx, "7d", u.sevenDay.percent, u.sevenDay.resetHours, "h", "round"));
880
+ }
881
+ if (u.monthly) {
882
+ // Cursor and OpenCode Go (normalize gates monthly to those providers).
883
+ // Both floor used percents upstream (Cursor's dashboard shows 1.88 →
884
+ // "1% used"; OpenCode's endpoint already emits floored integers).
885
+ parts.push(formatQuotaWindow(ctx, "mo", u.monthly.percent, u.monthly.resetHours, "h", "floor"));
886
+ }
887
+ const content = withIcon(theme.icon.time, parts.join(theme.sep.dot));
888
+ return { content, visible: true };
889
+ },
890
+ };
891
+
892
+ // ═══════════════════════════════════════════════════════════════════════════
893
+ // Segment Registry
894
+ // ═══════════════════════════════════════════════════════════════════════════
895
+
896
+ export const SEGMENTS: Record<StatusLineSegmentId, StatusLineSegment> = {
897
+ pi: piSegment,
898
+ status: statusSegment,
899
+ model: modelSegment,
900
+ mode: modeSegment,
901
+ path: pathSegment,
902
+ git: gitSegment,
903
+ pr: prSegment,
904
+ subagents: subagentsSegment,
905
+ token_in: tokenInSegment,
906
+ token_out: tokenOutSegment,
907
+ token_total: tokenTotalSegment,
908
+ token_rate: tokenRateSegment,
909
+ cost: costSegment,
910
+ context_pct: contextPctSegment,
911
+ context_total: contextTotalSegment,
912
+ time_spent: timeSpentSegment,
913
+ time: timeSegment,
914
+ session: sessionSegment,
915
+ hostname: hostnameSegment,
916
+ cache_read: cacheReadSegment,
917
+ cache_write: cacheWriteSegment,
918
+ cache_hit: cacheHitSegment,
919
+ session_name: sessionNameSegment,
920
+ usage: usageSegment,
921
+ collab: collabSegment,
922
+ stream: streamSegment,
923
+ vim: vimSegment,
924
+ };
925
+
926
+ export function renderSegment(id: StatusLineSegmentId, ctx: SegmentContext): RenderedSegment {
927
+ const segment = SEGMENTS[id];
928
+ if (!segment) {
929
+ return { content: "", visible: false };
930
+ }
931
+ return segment.render(ctx);
932
+ }
933
+
934
+ export const ALL_SEGMENT_IDS: StatusLineSegmentId[] = Object.keys(SEGMENTS) as StatusLineSegmentId[];