@butlerw/vellum 0.1.0

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 (446) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +411 -0
  3. package/__fixtures__/responses/code-generation.json +42 -0
  4. package/__fixtures__/responses/error-response.json +20 -0
  5. package/__fixtures__/responses/hello-world.json +32 -0
  6. package/dist/auth-6MCXESOH.js +26 -0
  7. package/dist/chunk-SECXJGWA.js +597 -0
  8. package/dist/index.js +34023 -0
  9. package/package.json +67 -0
  10. package/src/__tests__/commands.e2e.test.ts +728 -0
  11. package/src/__tests__/credentials.test.ts +713 -0
  12. package/src/__tests__/mode-e2e.test.ts +391 -0
  13. package/src/__tests__/tui-integration.test.tsx +1271 -0
  14. package/src/agents/__tests__/task-persistence.test.ts +235 -0
  15. package/src/agents/commands/delegate.ts +240 -0
  16. package/src/agents/commands/index.ts +10 -0
  17. package/src/agents/commands/resume.ts +335 -0
  18. package/src/agents/index.ts +29 -0
  19. package/src/agents/task-persistence.ts +272 -0
  20. package/src/agents/task-resumption.ts +242 -0
  21. package/src/app.tsx +4737 -0
  22. package/src/commands/__tests__/.gitkeep +1 -0
  23. package/src/commands/__tests__/agents.test.ts +606 -0
  24. package/src/commands/__tests__/auth.test.ts +626 -0
  25. package/src/commands/__tests__/autocomplete.test.ts +683 -0
  26. package/src/commands/__tests__/batch.test.ts +287 -0
  27. package/src/commands/__tests__/chain-pipe-parser.test.ts +654 -0
  28. package/src/commands/__tests__/completion.test.ts +238 -0
  29. package/src/commands/__tests__/core.test.ts +363 -0
  30. package/src/commands/__tests__/executor.test.ts +496 -0
  31. package/src/commands/__tests__/exit-codes.test.ts +220 -0
  32. package/src/commands/__tests__/init.test.ts +243 -0
  33. package/src/commands/__tests__/language.test.ts +353 -0
  34. package/src/commands/__tests__/mode-cli.test.ts +667 -0
  35. package/src/commands/__tests__/model.test.ts +277 -0
  36. package/src/commands/__tests__/parser.test.ts +493 -0
  37. package/src/commands/__tests__/performance.bench.ts +380 -0
  38. package/src/commands/__tests__/registry.test.ts +534 -0
  39. package/src/commands/__tests__/resume.test.ts +449 -0
  40. package/src/commands/__tests__/security.test.ts +845 -0
  41. package/src/commands/__tests__/stream-json.test.ts +372 -0
  42. package/src/commands/__tests__/user-commands.test.ts +597 -0
  43. package/src/commands/adapters.ts +267 -0
  44. package/src/commands/agent.ts +395 -0
  45. package/src/commands/agents/generate.ts +506 -0
  46. package/src/commands/agents/index.ts +272 -0
  47. package/src/commands/agents/show.ts +271 -0
  48. package/src/commands/agents/validate.ts +387 -0
  49. package/src/commands/auth.ts +883 -0
  50. package/src/commands/autocomplete.ts +480 -0
  51. package/src/commands/batch/command.ts +388 -0
  52. package/src/commands/batch/executor.ts +361 -0
  53. package/src/commands/batch/index.ts +12 -0
  54. package/src/commands/commit.ts +235 -0
  55. package/src/commands/completion/index.ts +371 -0
  56. package/src/commands/condense.ts +191 -0
  57. package/src/commands/config.ts +344 -0
  58. package/src/commands/context-provider.ts +173 -0
  59. package/src/commands/copy.ts +329 -0
  60. package/src/commands/core/clear.ts +38 -0
  61. package/src/commands/core/exit.ts +43 -0
  62. package/src/commands/core/help.ts +354 -0
  63. package/src/commands/core/index.ts +15 -0
  64. package/src/commands/cost.ts +179 -0
  65. package/src/commands/credentials.tsx +618 -0
  66. package/src/commands/custom-agents/__tests__/custom-agents.test.ts +709 -0
  67. package/src/commands/custom-agents/create.ts +377 -0
  68. package/src/commands/custom-agents/export.ts +135 -0
  69. package/src/commands/custom-agents/import.ts +199 -0
  70. package/src/commands/custom-agents/index.ts +372 -0
  71. package/src/commands/custom-agents/info.ts +318 -0
  72. package/src/commands/custom-agents/list.ts +267 -0
  73. package/src/commands/custom-agents/validate.ts +388 -0
  74. package/src/commands/diff-mode.ts +241 -0
  75. package/src/commands/env.ts +53 -0
  76. package/src/commands/executor.ts +579 -0
  77. package/src/commands/exit-codes.ts +202 -0
  78. package/src/commands/index.ts +701 -0
  79. package/src/commands/init/index.ts +15 -0
  80. package/src/commands/init/prompts.ts +366 -0
  81. package/src/commands/init/templates/commands-readme.md +80 -0
  82. package/src/commands/init/templates/example-command.md +79 -0
  83. package/src/commands/init/templates/example-skill.md +168 -0
  84. package/src/commands/init/templates/example-workflow.md +101 -0
  85. package/src/commands/init/templates/prompts-readme.md +52 -0
  86. package/src/commands/init/templates/rules-readme.md +63 -0
  87. package/src/commands/init/templates/skills-readme.md +83 -0
  88. package/src/commands/init/templates/workflows-readme.md +94 -0
  89. package/src/commands/init.ts +391 -0
  90. package/src/commands/install.ts +90 -0
  91. package/src/commands/language.ts +191 -0
  92. package/src/commands/loaders/.gitkeep +1 -0
  93. package/src/commands/lsp.ts +199 -0
  94. package/src/commands/markdown-commands.ts +253 -0
  95. package/src/commands/mcp.ts +588 -0
  96. package/src/commands/memory/export.ts +341 -0
  97. package/src/commands/memory/index.ts +148 -0
  98. package/src/commands/memory/list.ts +261 -0
  99. package/src/commands/memory/search.ts +346 -0
  100. package/src/commands/memory/utils.ts +15 -0
  101. package/src/commands/metrics.ts +75 -0
  102. package/src/commands/migrate/index.ts +16 -0
  103. package/src/commands/migrate/prompts.ts +477 -0
  104. package/src/commands/mode.ts +331 -0
  105. package/src/commands/model.ts +298 -0
  106. package/src/commands/onboard.ts +205 -0
  107. package/src/commands/open.ts +169 -0
  108. package/src/commands/output/stream-json.ts +373 -0
  109. package/src/commands/parser/chain-parser.ts +370 -0
  110. package/src/commands/parser/index.ts +29 -0
  111. package/src/commands/parser/pipe-parser.ts +480 -0
  112. package/src/commands/parser.ts +588 -0
  113. package/src/commands/persistence.ts +355 -0
  114. package/src/commands/progress.ts +18 -0
  115. package/src/commands/prompt/index.ts +17 -0
  116. package/src/commands/prompt/validate.ts +621 -0
  117. package/src/commands/prompt-priority.ts +401 -0
  118. package/src/commands/registry.ts +374 -0
  119. package/src/commands/sandbox/index.ts +131 -0
  120. package/src/commands/security/index.ts +21 -0
  121. package/src/commands/security/input-sanitizer.ts +168 -0
  122. package/src/commands/security/permission-checker.ts +456 -0
  123. package/src/commands/security/sensitive-data.ts +350 -0
  124. package/src/commands/session/delete.ts +38 -0
  125. package/src/commands/session/export.ts +39 -0
  126. package/src/commands/session/index.ts +26 -0
  127. package/src/commands/session/list.ts +26 -0
  128. package/src/commands/session/resume.ts +562 -0
  129. package/src/commands/session/search.ts +434 -0
  130. package/src/commands/session/show.ts +26 -0
  131. package/src/commands/settings.ts +368 -0
  132. package/src/commands/setup.ts +23 -0
  133. package/src/commands/shell/index.ts +16 -0
  134. package/src/commands/shell/setup.ts +422 -0
  135. package/src/commands/shell-init.ts +50 -0
  136. package/src/commands/shell-integration/index.ts +194 -0
  137. package/src/commands/skill.ts +1220 -0
  138. package/src/commands/spec.ts +558 -0
  139. package/src/commands/status.ts +246 -0
  140. package/src/commands/theme.ts +211 -0
  141. package/src/commands/think.ts +551 -0
  142. package/src/commands/trust.ts +211 -0
  143. package/src/commands/tutorial.ts +522 -0
  144. package/src/commands/types.ts +512 -0
  145. package/src/commands/update.ts +274 -0
  146. package/src/commands/usage.ts +213 -0
  147. package/src/commands/user-commands.ts +630 -0
  148. package/src/commands/utils.ts +142 -0
  149. package/src/commands/vim.ts +152 -0
  150. package/src/commands/workflow.ts +257 -0
  151. package/src/components/header.tsx +25 -0
  152. package/src/components/input.tsx +25 -0
  153. package/src/components/message-list.tsx +32 -0
  154. package/src/components/status-bar.tsx +23 -0
  155. package/src/index.tsx +614 -0
  156. package/src/onboarding/__tests__/tutorial.test.ts +740 -0
  157. package/src/onboarding/index.ts +69 -0
  158. package/src/onboarding/tips/index.ts +9 -0
  159. package/src/onboarding/tips/tip-engine.ts +459 -0
  160. package/src/onboarding/tutorial/index.ts +88 -0
  161. package/src/onboarding/tutorial/lessons/basics.ts +151 -0
  162. package/src/onboarding/tutorial/lessons/index.ts +151 -0
  163. package/src/onboarding/tutorial/lessons/modes.ts +230 -0
  164. package/src/onboarding/tutorial/lessons/tools.ts +172 -0
  165. package/src/onboarding/tutorial/progress-tracker.ts +350 -0
  166. package/src/onboarding/tutorial/storage.ts +249 -0
  167. package/src/onboarding/tutorial/tutorial-system.ts +462 -0
  168. package/src/onboarding/tutorial/types.ts +310 -0
  169. package/src/orchestrator-singleton.ts +129 -0
  170. package/src/shutdown.ts +33 -0
  171. package/src/test/e2e/assertions.ts +267 -0
  172. package/src/test/e2e/fixtures.ts +204 -0
  173. package/src/test/e2e/harness.ts +575 -0
  174. package/src/test/e2e/index.ts +57 -0
  175. package/src/test/e2e/types.ts +228 -0
  176. package/src/test/fixtures/__tests__/fake-response-loader.test.ts +314 -0
  177. package/src/test/fixtures/fake-response-loader.ts +314 -0
  178. package/src/test/fixtures/index.ts +20 -0
  179. package/src/tui/__tests__/mcp-panel.test.tsx +82 -0
  180. package/src/tui/__tests__/mcp-wiring.test.tsx +78 -0
  181. package/src/tui/__tests__/mode-components.test.tsx +395 -0
  182. package/src/tui/__tests__/permission-ask-flow.test.tsx +138 -0
  183. package/src/tui/__tests__/sidebar-panel-data.test.tsx +148 -0
  184. package/src/tui/__tests__/tools-panel-hotkeys.test.tsx +41 -0
  185. package/src/tui/adapters/agent-adapter.ts +1008 -0
  186. package/src/tui/adapters/index.ts +48 -0
  187. package/src/tui/adapters/message-adapter.ts +315 -0
  188. package/src/tui/adapters/persistence-bridge.ts +331 -0
  189. package/src/tui/adapters/session-adapter.ts +419 -0
  190. package/src/tui/buffered-stdout.ts +223 -0
  191. package/src/tui/components/AgentProgress.tsx +424 -0
  192. package/src/tui/components/Banner/AsciiArt.ts +160 -0
  193. package/src/tui/components/Banner/Banner.tsx +355 -0
  194. package/src/tui/components/Banner/ShimmerContext.tsx +131 -0
  195. package/src/tui/components/Banner/ShimmerText.tsx +193 -0
  196. package/src/tui/components/Banner/TypeWriterGradient.tsx +321 -0
  197. package/src/tui/components/Banner/index.ts +61 -0
  198. package/src/tui/components/Banner/useShimmer.ts +241 -0
  199. package/src/tui/components/ChatView.tsx +11 -0
  200. package/src/tui/components/Checkpoint/CheckpointDiffView.tsx +371 -0
  201. package/src/tui/components/Checkpoint/SnapshotCheckpointPanel.tsx +440 -0
  202. package/src/tui/components/Checkpoint/index.ts +19 -0
  203. package/src/tui/components/CostDisplay.tsx +226 -0
  204. package/src/tui/components/InitErrorBanner.tsx +122 -0
  205. package/src/tui/components/Input/Autocomplete.tsx +603 -0
  206. package/src/tui/components/Input/EnhancedCommandInput.tsx +471 -0
  207. package/src/tui/components/Input/HighlightedText.tsx +236 -0
  208. package/src/tui/components/Input/MentionAutocomplete.tsx +375 -0
  209. package/src/tui/components/Input/TextInput.tsx +1002 -0
  210. package/src/tui/components/Input/__tests__/Autocomplete.test.tsx +374 -0
  211. package/src/tui/components/Input/__tests__/TextInput.test.tsx +241 -0
  212. package/src/tui/components/Input/__tests__/highlight.test.ts +219 -0
  213. package/src/tui/components/Input/__tests__/slash-command-utils.test.ts +104 -0
  214. package/src/tui/components/Input/highlight.ts +362 -0
  215. package/src/tui/components/Input/index.ts +36 -0
  216. package/src/tui/components/Input/slash-command-utils.ts +135 -0
  217. package/src/tui/components/Layout.tsx +432 -0
  218. package/src/tui/components/McpPanel.tsx +137 -0
  219. package/src/tui/components/MemoryPanel.tsx +448 -0
  220. package/src/tui/components/Messages/CodeBlock.tsx +527 -0
  221. package/src/tui/components/Messages/DiffView.tsx +679 -0
  222. package/src/tui/components/Messages/ImageReference.tsx +89 -0
  223. package/src/tui/components/Messages/MarkdownBlock.tsx +228 -0
  224. package/src/tui/components/Messages/MarkdownRenderer.tsx +498 -0
  225. package/src/tui/components/Messages/MessageBubble.tsx +270 -0
  226. package/src/tui/components/Messages/MessageList.tsx +1719 -0
  227. package/src/tui/components/Messages/StreamingText.tsx +216 -0
  228. package/src/tui/components/Messages/ThinkingBlock.tsx +408 -0
  229. package/src/tui/components/Messages/ToolResultPreview.tsx +243 -0
  230. package/src/tui/components/Messages/__tests__/CodeBlock.test.tsx +296 -0
  231. package/src/tui/components/Messages/__tests__/DiffView.test.tsx +239 -0
  232. package/src/tui/components/Messages/__tests__/MarkdownRenderer.test.tsx +303 -0
  233. package/src/tui/components/Messages/__tests__/MessageBubble.test.tsx +268 -0
  234. package/src/tui/components/Messages/__tests__/MessageList.test.tsx +324 -0
  235. package/src/tui/components/Messages/__tests__/StreamingText.test.tsx +215 -0
  236. package/src/tui/components/Messages/index.ts +25 -0
  237. package/src/tui/components/ModeIndicator.tsx +177 -0
  238. package/src/tui/components/ModeSelector.tsx +216 -0
  239. package/src/tui/components/ModelSelector.tsx +339 -0
  240. package/src/tui/components/OnboardingWizard.tsx +670 -0
  241. package/src/tui/components/PhaseProgressIndicator.tsx +270 -0
  242. package/src/tui/components/RateLimitIndicator.tsx +82 -0
  243. package/src/tui/components/ScreenReaderLayout.tsx +295 -0
  244. package/src/tui/components/SettingsPanel.tsx +643 -0
  245. package/src/tui/components/Sidebar/SystemStatusPanel.tsx +284 -0
  246. package/src/tui/components/Sidebar/index.ts +9 -0
  247. package/src/tui/components/Status/ModelStatusBar.tsx +270 -0
  248. package/src/tui/components/Status/index.ts +12 -0
  249. package/src/tui/components/StatusBar/AgentModeIndicator.tsx +257 -0
  250. package/src/tui/components/StatusBar/ContextProgress.tsx +167 -0
  251. package/src/tui/components/StatusBar/FileChangesIndicator.tsx +62 -0
  252. package/src/tui/components/StatusBar/GitIndicator.tsx +89 -0
  253. package/src/tui/components/StatusBar/HeaderBar.tsx +126 -0
  254. package/src/tui/components/StatusBar/ModelIndicator.tsx +157 -0
  255. package/src/tui/components/StatusBar/PersistenceStatusIndicator.tsx +210 -0
  256. package/src/tui/components/StatusBar/ResilienceIndicator.tsx +106 -0
  257. package/src/tui/components/StatusBar/SandboxIndicator.tsx +167 -0
  258. package/src/tui/components/StatusBar/StatusBar.tsx +368 -0
  259. package/src/tui/components/StatusBar/ThinkingModeIndicator.tsx +170 -0
  260. package/src/tui/components/StatusBar/TokenBreakdown.tsx +246 -0
  261. package/src/tui/components/StatusBar/TokenCounter.tsx +135 -0
  262. package/src/tui/components/StatusBar/TrustModeIndicator.tsx +130 -0
  263. package/src/tui/components/StatusBar/WorkspaceIndicator.tsx +86 -0
  264. package/src/tui/components/StatusBar/__tests__/AgentModeIndicator.test.tsx +193 -0
  265. package/src/tui/components/StatusBar/__tests__/StatusBar.test.tsx +729 -0
  266. package/src/tui/components/StatusBar/index.ts +60 -0
  267. package/src/tui/components/TipBanner.tsx +115 -0
  268. package/src/tui/components/TodoItem.tsx +208 -0
  269. package/src/tui/components/TodoPanel.tsx +455 -0
  270. package/src/tui/components/Tools/ApprovalQueue.tsx +407 -0
  271. package/src/tui/components/Tools/OptionSelector.tsx +160 -0
  272. package/src/tui/components/Tools/PermissionDialog.tsx +286 -0
  273. package/src/tui/components/Tools/ToolParams.tsx +483 -0
  274. package/src/tui/components/Tools/ToolsPanel.tsx +178 -0
  275. package/src/tui/components/Tools/__tests__/PermissionDialog.test.tsx +510 -0
  276. package/src/tui/components/Tools/__tests__/ToolParams.test.tsx +432 -0
  277. package/src/tui/components/Tools/index.ts +21 -0
  278. package/src/tui/components/TrustPrompt.tsx +279 -0
  279. package/src/tui/components/UpdateBanner.tsx +166 -0
  280. package/src/tui/components/VimModeIndicator.tsx +112 -0
  281. package/src/tui/components/backtrack/BacktrackControls.tsx +402 -0
  282. package/src/tui/components/backtrack/index.ts +13 -0
  283. package/src/tui/components/common/AutoApprovalStatus.tsx +251 -0
  284. package/src/tui/components/common/CostWarning.tsx +294 -0
  285. package/src/tui/components/common/DynamicShortcutHints.tsx +209 -0
  286. package/src/tui/components/common/EnhancedLoadingIndicator.tsx +305 -0
  287. package/src/tui/components/common/ErrorBoundary.tsx +140 -0
  288. package/src/tui/components/common/GradientText.tsx +224 -0
  289. package/src/tui/components/common/HotkeyHelpModal.tsx +193 -0
  290. package/src/tui/components/common/HotkeyHints.tsx +70 -0
  291. package/src/tui/components/common/MaxSizedBox.tsx +354 -0
  292. package/src/tui/components/common/NewMessagesBadge.tsx +65 -0
  293. package/src/tui/components/common/ProtectedFileLegend.tsx +89 -0
  294. package/src/tui/components/common/ScrollIndicator.tsx +160 -0
  295. package/src/tui/components/common/Spinner.tsx +342 -0
  296. package/src/tui/components/common/StreamingIndicator.tsx +316 -0
  297. package/src/tui/components/common/VirtualizedList/VirtualizedList.tsx +428 -0
  298. package/src/tui/components/common/VirtualizedList/hooks/index.ts +19 -0
  299. package/src/tui/components/common/VirtualizedList/hooks/useBatchedScroll.ts +64 -0
  300. package/src/tui/components/common/VirtualizedList/hooks/useScrollAnchor.ts +290 -0
  301. package/src/tui/components/common/VirtualizedList/hooks/useVirtualization.ts +340 -0
  302. package/src/tui/components/common/VirtualizedList/index.ts +30 -0
  303. package/src/tui/components/common/VirtualizedList/types.ts +107 -0
  304. package/src/tui/components/common/__tests__/NewMessagesBadge.test.tsx +74 -0
  305. package/src/tui/components/common/__tests__/ScrollIndicator.test.tsx +193 -0
  306. package/src/tui/components/common/index.ts +110 -0
  307. package/src/tui/components/index.ts +79 -0
  308. package/src/tui/components/session/CheckpointPanel.tsx +323 -0
  309. package/src/tui/components/session/RollbackDialog.tsx +169 -0
  310. package/src/tui/components/session/SessionItem.tsx +136 -0
  311. package/src/tui/components/session/SessionListPanel.tsx +252 -0
  312. package/src/tui/components/session/SessionPicker.tsx +449 -0
  313. package/src/tui/components/session/SessionPreview.tsx +240 -0
  314. package/src/tui/components/session/__tests__/session.test.tsx +408 -0
  315. package/src/tui/components/session/index.ts +28 -0
  316. package/src/tui/components/session/types.ts +116 -0
  317. package/src/tui/components/theme/__tests__/tokens.test.ts +471 -0
  318. package/src/tui/components/theme/index.ts +227 -0
  319. package/src/tui/components/theme/tokens.ts +484 -0
  320. package/src/tui/config/defaults.ts +134 -0
  321. package/src/tui/config/index.ts +17 -0
  322. package/src/tui/context/AnimationContext.tsx +284 -0
  323. package/src/tui/context/AppContext.tsx +349 -0
  324. package/src/tui/context/BracketedPasteContext.tsx +372 -0
  325. package/src/tui/context/LspContext.tsx +192 -0
  326. package/src/tui/context/McpContext.tsx +325 -0
  327. package/src/tui/context/MessagesContext.tsx +870 -0
  328. package/src/tui/context/OverflowContext.tsx +213 -0
  329. package/src/tui/context/RateLimitContext.tsx +108 -0
  330. package/src/tui/context/ResilienceContext.tsx +275 -0
  331. package/src/tui/context/RootProvider.tsx +136 -0
  332. package/src/tui/context/ScrollContext.tsx +331 -0
  333. package/src/tui/context/ToolsContext.tsx +702 -0
  334. package/src/tui/context/__tests__/BracketedPasteContext.test.tsx +416 -0
  335. package/src/tui/context/index.ts +140 -0
  336. package/src/tui/enterprise-integration.ts +282 -0
  337. package/src/tui/hooks/__tests__/useBacktrack.test.tsx +138 -0
  338. package/src/tui/hooks/__tests__/useBracketedPaste.test.tsx +222 -0
  339. package/src/tui/hooks/__tests__/useCopyMode.test.tsx +336 -0
  340. package/src/tui/hooks/__tests__/useHotkeys.ctrl-input.test.tsx +96 -0
  341. package/src/tui/hooks/__tests__/useHotkeys.test.tsx +454 -0
  342. package/src/tui/hooks/__tests__/useInputHistory.test.tsx +660 -0
  343. package/src/tui/hooks/__tests__/useLineBuffer.test.ts +295 -0
  344. package/src/tui/hooks/__tests__/useModeController.test.ts +137 -0
  345. package/src/tui/hooks/__tests__/useModeShortcuts.test.tsx +142 -0
  346. package/src/tui/hooks/__tests__/useScrollController.test.ts +464 -0
  347. package/src/tui/hooks/__tests__/useVim.test.tsx +531 -0
  348. package/src/tui/hooks/index.ts +252 -0
  349. package/src/tui/hooks/useAgentLoop.ts +712 -0
  350. package/src/tui/hooks/useAlternateBuffer.ts +398 -0
  351. package/src/tui/hooks/useAnimatedScrollbar.ts +241 -0
  352. package/src/tui/hooks/useBacktrack.ts +443 -0
  353. package/src/tui/hooks/useBracketedPaste.ts +104 -0
  354. package/src/tui/hooks/useCollapsible.ts +240 -0
  355. package/src/tui/hooks/useCopyMode.ts +382 -0
  356. package/src/tui/hooks/useCostSummary.ts +75 -0
  357. package/src/tui/hooks/useDesktopNotification.ts +414 -0
  358. package/src/tui/hooks/useDiffMode.ts +44 -0
  359. package/src/tui/hooks/useFileChangeStats.ts +110 -0
  360. package/src/tui/hooks/useFileSuggestions.ts +284 -0
  361. package/src/tui/hooks/useFlickerDetector.ts +250 -0
  362. package/src/tui/hooks/useGitStatus.ts +200 -0
  363. package/src/tui/hooks/useHotkeys.ts +579 -0
  364. package/src/tui/hooks/useImagePaste.ts +114 -0
  365. package/src/tui/hooks/useInputHighlight.ts +145 -0
  366. package/src/tui/hooks/useInputHistory.ts +246 -0
  367. package/src/tui/hooks/useKeyboardScroll.ts +209 -0
  368. package/src/tui/hooks/useLineBuffer.ts +356 -0
  369. package/src/tui/hooks/useMentionAutocomplete.ts +235 -0
  370. package/src/tui/hooks/useModeController.ts +167 -0
  371. package/src/tui/hooks/useModeShortcuts.ts +196 -0
  372. package/src/tui/hooks/usePermissionHandler.ts +146 -0
  373. package/src/tui/hooks/usePersistence.ts +480 -0
  374. package/src/tui/hooks/usePersistenceShortcuts.ts +225 -0
  375. package/src/tui/hooks/usePlaceholderRotation.ts +143 -0
  376. package/src/tui/hooks/useProviderStatus.ts +270 -0
  377. package/src/tui/hooks/useRateLimitStatus.ts +90 -0
  378. package/src/tui/hooks/useScreenReader.ts +315 -0
  379. package/src/tui/hooks/useScrollController.ts +450 -0
  380. package/src/tui/hooks/useScrollEventBatcher.ts +185 -0
  381. package/src/tui/hooks/useSidebarPanelData.ts +115 -0
  382. package/src/tui/hooks/useSmoothScroll.ts +202 -0
  383. package/src/tui/hooks/useSnapshots.ts +300 -0
  384. package/src/tui/hooks/useStateAndRef.ts +50 -0
  385. package/src/tui/hooks/useTerminalSize.ts +206 -0
  386. package/src/tui/hooks/useToolApprovalController.ts +91 -0
  387. package/src/tui/hooks/useVim.ts +334 -0
  388. package/src/tui/hooks/useWorkspace.ts +56 -0
  389. package/src/tui/i18n/__tests__/init.test.ts +278 -0
  390. package/src/tui/i18n/__tests__/language-config.test.ts +199 -0
  391. package/src/tui/i18n/__tests__/locale-detection.test.ts +250 -0
  392. package/src/tui/i18n/__tests__/settings-integration.test.ts +262 -0
  393. package/src/tui/i18n/index.ts +72 -0
  394. package/src/tui/i18n/init.ts +131 -0
  395. package/src/tui/i18n/language-config.ts +106 -0
  396. package/src/tui/i18n/locale-detection.ts +173 -0
  397. package/src/tui/i18n/settings-integration.ts +557 -0
  398. package/src/tui/i18n/tui-namespace.ts +538 -0
  399. package/src/tui/i18n/types.ts +312 -0
  400. package/src/tui/index.ts +43 -0
  401. package/src/tui/lsp-integration.ts +409 -0
  402. package/src/tui/metrics-integration.ts +366 -0
  403. package/src/tui/plugins.ts +383 -0
  404. package/src/tui/resilience.ts +342 -0
  405. package/src/tui/sandbox-integration.ts +317 -0
  406. package/src/tui/services/clipboard.ts +348 -0
  407. package/src/tui/services/fuzzy-search.ts +441 -0
  408. package/src/tui/services/index.ts +72 -0
  409. package/src/tui/services/markdown-renderer.ts +565 -0
  410. package/src/tui/services/open-external.ts +247 -0
  411. package/src/tui/services/syntax-highlighter.ts +483 -0
  412. package/src/tui/slash-commands.ts +12 -0
  413. package/src/tui/theme/index.ts +15 -0
  414. package/src/tui/theme/provider.tsx +206 -0
  415. package/src/tui/tip-integration.ts +300 -0
  416. package/src/tui/types/__tests__/ink-extended.test.ts +121 -0
  417. package/src/tui/types/ink-extended.ts +87 -0
  418. package/src/tui/utils/__tests__/bracketedPaste.test.ts +231 -0
  419. package/src/tui/utils/__tests__/heightEstimator.test.ts +157 -0
  420. package/src/tui/utils/__tests__/text-width.test.ts +158 -0
  421. package/src/tui/utils/__tests__/textSanitizer.test.ts +266 -0
  422. package/src/tui/utils/__tests__/ui-sizing.test.ts +169 -0
  423. package/src/tui/utils/bracketedPaste.ts +107 -0
  424. package/src/tui/utils/cursor-manager.ts +131 -0
  425. package/src/tui/utils/detectTerminal.ts +596 -0
  426. package/src/tui/utils/findLastSafeSplitPoint.ts +92 -0
  427. package/src/tui/utils/heightEstimator.ts +198 -0
  428. package/src/tui/utils/index.ts +91 -0
  429. package/src/tui/utils/isNarrowWidth.ts +52 -0
  430. package/src/tui/utils/stdoutGuard.ts +90 -0
  431. package/src/tui/utils/synchronized-update.ts +70 -0
  432. package/src/tui/utils/text-width.ts +225 -0
  433. package/src/tui/utils/textSanitizer.ts +225 -0
  434. package/src/tui/utils/textUtils.ts +114 -0
  435. package/src/tui/utils/ui-sizing.ts +192 -0
  436. package/src/tui-blessed/app.ts +160 -0
  437. package/src/tui-blessed/index.ts +2 -0
  438. package/src/tui-blessed/neo-blessed.d.ts +6 -0
  439. package/src/tui-blessed/test.ts +21 -0
  440. package/src/tui-blessed/types.ts +14 -0
  441. package/src/utils/icons.ts +130 -0
  442. package/src/utils/index.ts +33 -0
  443. package/src/utils/resume-hint.ts +86 -0
  444. package/src/version.ts +1 -0
  445. package/tsconfig.json +8 -0
  446. package/vitest.config.ts +35 -0
@@ -0,0 +1,368 @@
1
+ /**
2
+ * StatusBar Component (T034)
3
+ *
4
+ * Container component that renders all status indicators in a horizontal row.
5
+ * Unified Footer layout: modes | model | context+cost
6
+ *
7
+ * @module tui/components/StatusBar/StatusBar
8
+ */
9
+
10
+ import type { CodingMode, SandboxPolicy } from "@vellum/core";
11
+ import { Box, Text } from "ink";
12
+ import { useMemo } from "react";
13
+ import { useTUITranslation } from "../../i18n/index.js";
14
+ import { useTheme } from "../../theme/index.js";
15
+ import { GradientText } from "../common/GradientText.js";
16
+ import type { AgentLevel } from "./AgentModeIndicator.js";
17
+ import { ContextProgress, type ContextProgressProps } from "./ContextProgress.js";
18
+ import { ModelIndicator } from "./ModelIndicator.js";
19
+ import type { PersistenceStatusIndicatorProps } from "./PersistenceStatusIndicator.js";
20
+ import { ResilienceStatusSegment } from "./ResilienceIndicator.js";
21
+ import { SandboxIndicator } from "./SandboxIndicator.js";
22
+ import { ThinkingModeIndicator, type ThinkingModeIndicatorProps } from "./ThinkingModeIndicator.js";
23
+ import { TokenBreakdown, type TokenStats } from "./TokenBreakdown.js";
24
+ import { TrustModeIndicator, type TrustModeIndicatorProps } from "./TrustModeIndicator.js";
25
+
26
+ // =============================================================================
27
+ // Types
28
+ // =============================================================================
29
+
30
+ /**
31
+ * Extended token usage with granular breakdown.
32
+ * Extends ContextProgressProps with detailed token stats.
33
+ */
34
+ export interface ExtendedTokenProps extends ContextProgressProps {
35
+ /** Detailed breakdown of token usage */
36
+ readonly breakdown?: TokenStats;
37
+ /** Current turn's token usage (for per-turn display) */
38
+ readonly turnUsage?: TokenStats;
39
+ /** Whether to show granular breakdown (default: false) */
40
+ readonly showBreakdown?: boolean;
41
+ }
42
+
43
+ /**
44
+ * Props for the StatusBar component.
45
+ */
46
+ export interface StatusBarProps {
47
+ /** Current coding mode */
48
+ readonly mode?: CodingMode;
49
+ /** Agent name for display (e.g., 'orchestrator', 'coder') */
50
+ readonly agentName?: string;
51
+ /** Agent level for spec mode (0=orchestrator, 1=sub-orchestrator, 2=worker) */
52
+ readonly agentLevel?: AgentLevel;
53
+ /** Model name (e.g., 'claude-sonnet-4') */
54
+ readonly modelName?: string;
55
+ /** Token usage information (displayed with visual progress bar) */
56
+ readonly tokens?: ExtendedTokenProps;
57
+ /** Trust mode setting */
58
+ readonly trustMode?: TrustModeIndicatorProps["mode"];
59
+ /** Sandbox policy for file system access boundaries */
60
+ readonly sandboxPolicy?: SandboxPolicy;
61
+ /** Thinking mode status */
62
+ readonly thinking?: ThinkingModeIndicatorProps;
63
+ /** Current cost in dollars */
64
+ readonly cost?: number;
65
+ /** Whether to show a border (default: false for unified footer) */
66
+ readonly showBorder?: boolean;
67
+ /** Whether to show all modes or only active (default: false) */
68
+ readonly showAllModes?: boolean;
69
+ /** Persistence status for session save indicator */
70
+ readonly persistence?: Pick<
71
+ PersistenceStatusIndicatorProps,
72
+ "status" | "unsavedCount" | "lastSavedAt"
73
+ >;
74
+ }
75
+
76
+ // =============================================================================
77
+ // Constants
78
+ // =============================================================================
79
+
80
+ /** Separator between status items (compact, no extra spaces) */
81
+ const SEPARATOR = "│";
82
+
83
+ /** Mode display configuration */
84
+ const MODES_CONFIG: Array<{ mode: CodingMode; icon: string; label: string }> = [
85
+ { mode: "vibe", icon: "◐", label: "vibe" },
86
+ { mode: "plan", icon: "◑", label: "plan" },
87
+ { mode: "spec", icon: "◒", label: "spec" },
88
+ ];
89
+
90
+ /** Agent name to abbreviation mapping for status bar display */
91
+ const AGENT_ABBREVIATIONS: Record<string, string> = {
92
+ // Core Agents
93
+ "vibe-agent": "Vibe",
94
+ "plan-agent": "Plan",
95
+ "spec-orchestrator": "Spec",
96
+
97
+ // Spec Workflow Workers
98
+ researcher: "Rsrch",
99
+ requirements: "Reqs",
100
+ design: "Dsgn",
101
+ tasks: "Tasks",
102
+ validator: "Valid",
103
+
104
+ // Builtin Workers
105
+ coder: "Code",
106
+ qa: "QA",
107
+ writer: "Write",
108
+ analyst: "Anlst",
109
+ architect: "Arch",
110
+ devops: "DevOp",
111
+ security: "Secur",
112
+ };
113
+
114
+ // =============================================================================
115
+ // Main Component
116
+ // =============================================================================
117
+
118
+ /**
119
+ * Format cost as currency string.
120
+ */
121
+ function formatCost(cost: number): string {
122
+ if (cost < 0.01) {
123
+ return `$${cost.toFixed(4)}`;
124
+ }
125
+ if (cost < 1) {
126
+ return `$${cost.toFixed(2)}`;
127
+ }
128
+ return `$${cost.toFixed(2)}`;
129
+ }
130
+
131
+ /**
132
+ * StatusBar displays all status indicators in a horizontal layout.
133
+ *
134
+ * Unified Footer Layout:
135
+ * ```
136
+ * ◐ vibe ◇ planning ◈ spec L1 │ claude-sonnet │ ▓▓▓░░ 45% │ $0.02
137
+ * ```
138
+ *
139
+ * Features:
140
+ * - All modes shown with active one highlighted
141
+ * - Agent level indicator for spec mode (L0/L1/L2)
142
+ * - Model name only (no provider prefix)
143
+ * - Context progress with visual bar
144
+ * - Cost display
145
+ *
146
+ * @example
147
+ * ```tsx
148
+ * <StatusBar
149
+ * mode="vibe"
150
+ * modelName="claude-sonnet-4"
151
+ * tokens={{ current: 5000, max: 100000 }}
152
+ * cost={0.02}
153
+ * />
154
+ * ```
155
+ */
156
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: StatusBar displays many dynamic UI elements
157
+ export function StatusBar({
158
+ mode = "vibe",
159
+ agentName,
160
+ agentLevel,
161
+ modelName,
162
+ tokens,
163
+ trustMode,
164
+ sandboxPolicy,
165
+ thinking,
166
+ cost,
167
+ showBorder = false,
168
+ showAllModes = false,
169
+ // NOTE: persistence prop kept for API compatibility but no longer rendered in footer
170
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
171
+ persistence: _persistence,
172
+ }: StatusBarProps): React.JSX.Element {
173
+ const { theme } = useTheme();
174
+ const { t } = useTUITranslation();
175
+
176
+ // Log deprecation warning for agentLevel prop (T017)
177
+ if (agentLevel !== undefined && process.env.NODE_ENV !== "production") {
178
+ console.warn(
179
+ "DEPRECATION WARNING: The 'agentLevel' prop is deprecated in StatusBar. " +
180
+ "Agent level is now derived from agent state in spec mode workflows."
181
+ );
182
+ }
183
+
184
+ // Use primary/accent color for status bar border
185
+ const borderColor = theme.colors.primary;
186
+
187
+ // Get agent abbreviation for display (fallback: first 5 chars)
188
+ const agentAbbrev = agentName
189
+ ? (AGENT_ABBREVIATIONS[agentName] ?? agentName.slice(0, 5))
190
+ : undefined;
191
+
192
+ // Render mode selector (all modes shown, active highlighted)
193
+ const visibleModes = showAllModes
194
+ ? MODES_CONFIG
195
+ : MODES_CONFIG.filter((modeConfig) => modeConfig.mode === mode);
196
+
197
+ // Mode-specific gradient colors
198
+ const modeGradients: Record<CodingMode, readonly string[]> = useMemo(
199
+ () => ({
200
+ vibe: [theme.colors.success, "#34d399", "#6ee7b7"], // Green gradient
201
+ plan: [theme.colors.info, "#60a5fa", "#93c5fd"], // Blue gradient
202
+ spec: [theme.colors.primary, "#8b5cf6", "#a78bfa"], // Purple gradient
203
+ }),
204
+ [theme.colors]
205
+ );
206
+
207
+ const modeSection = (
208
+ <Box key="modes" flexDirection="row" alignItems="center">
209
+ {visibleModes.map((modeConfig, index) => {
210
+ const isActive = modeConfig.mode === mode;
211
+ const modeText = `${modeConfig.icon} ${modeConfig.label}`;
212
+
213
+ return (
214
+ <Text key={modeConfig.mode}>
215
+ {index > 0 && " "}
216
+ {isActive ? (
217
+ <GradientText text={modeText} colors={modeGradients[modeConfig.mode]} bold />
218
+ ) : (
219
+ <Text color={theme.semantic.text.muted} dimColor>
220
+ {modeText}
221
+ </Text>
222
+ )}
223
+ </Text>
224
+ );
225
+ })}
226
+ {/* Agent level indicator: │Orch·L0 */}
227
+ {agentAbbrev !== undefined && agentLevel !== undefined && (
228
+ <Text>
229
+ <Text color={theme.semantic.border.muted}>│</Text>
230
+ <GradientText
231
+ text={`${agentAbbrev}·L${agentLevel}`}
232
+ colors={[theme.brand.primary, theme.brand.secondary]}
233
+ bold
234
+ />
235
+ </Text>
236
+ )}
237
+ </Box>
238
+ );
239
+
240
+ // Collect right-side indicators
241
+ const rightIndicators: React.ReactNode[] = [];
242
+
243
+ // Resilience status (rate limiting, retry) - shown first when active
244
+ const resilienceSegment = <ResilienceStatusSegment key="resilience" />;
245
+ // Note: ResilienceStatusSegment returns null when inactive, so we can always include it
246
+ rightIndicators.push(resilienceSegment);
247
+
248
+ // Model indicator (compact, name only)
249
+ if (modelName) {
250
+ rightIndicators.push(<ModelIndicator key="model" model={modelName} compact />);
251
+ }
252
+
253
+ // Sandbox policy indicator (shows file access boundaries)
254
+ if (sandboxPolicy) {
255
+ rightIndicators.push(<SandboxIndicator key="sandbox" policy={sandboxPolicy} />);
256
+ }
257
+
258
+ // Context progress and/or token breakdown
259
+ if (tokens) {
260
+ // Show granular breakdown if requested and data available
261
+ if (tokens.showBreakdown && tokens.breakdown) {
262
+ rightIndicators.push(
263
+ <TokenBreakdown
264
+ key="token-breakdown"
265
+ turn={tokens.turnUsage}
266
+ total={tokens.breakdown}
267
+ compact={true}
268
+ showTurn={!!tokens.turnUsage}
269
+ />
270
+ );
271
+ } else {
272
+ // Default: show progress bar
273
+ rightIndicators.push(
274
+ <ContextProgress
275
+ key="tokens"
276
+ current={tokens.current}
277
+ max={tokens.max}
278
+ showLabel={false}
279
+ barWidth={tokens.barWidth ?? 5}
280
+ />
281
+ );
282
+ }
283
+ }
284
+
285
+ // Trust mode (if provided)
286
+ if (trustMode) {
287
+ rightIndicators.push(<TrustModeIndicator key="trust" mode={trustMode} />);
288
+ }
289
+
290
+ // Thinking mode (if provided)
291
+ if (thinking) {
292
+ rightIndicators.push(
293
+ <ThinkingModeIndicator
294
+ key="thinking"
295
+ active={thinking.active}
296
+ budget={thinking.budget}
297
+ used={thinking.used}
298
+ />
299
+ );
300
+ }
301
+
302
+ // Cost display
303
+ if (cost !== undefined && cost > 0) {
304
+ rightIndicators.push(
305
+ <Box key="cost">
306
+ <Text color={theme.colors.success}>{formatCost(cost)}</Text>
307
+ </Box>
308
+ );
309
+ }
310
+
311
+ // NOTE: Persistence status indicator moved to Sidebar's SystemStatusPanel (T034 UI cleanup)
312
+ // The persistence prop is kept for API compatibility but no longer rendered in footer.
313
+
314
+ // Sidebar toggle hint (show only if there's room - not shown when many indicators present)
315
+ // Skip when there are 4+ indicators to avoid terminal overflow
316
+ if (rightIndicators.length < 4) {
317
+ rightIndicators.push(
318
+ <Box key="sidebar-hint">
319
+ <Text color={theme.semantic.text.muted} dimColor>
320
+ Alt+K sidebar
321
+ </Text>
322
+ </Box>
323
+ );
324
+ }
325
+
326
+ // Render right indicators with separators (compact layout)
327
+ const renderedRightItems: React.ReactNode[] = [];
328
+ for (let i = 0; i < rightIndicators.length; i++) {
329
+ if (i > 0) {
330
+ renderedRightItems.push(
331
+ <Text key={`sep-${i}`} color={theme.semantic.border.muted}>
332
+ {SEPARATOR}
333
+ </Text>
334
+ );
335
+ }
336
+ renderedRightItems.push(rightIndicators[i]);
337
+ }
338
+
339
+ // Empty state
340
+ if (!modelName && !tokens && !cost) {
341
+ return (
342
+ <Box flexDirection="row" justifyContent="space-between" width="100%">
343
+ {modeSection}
344
+ <Text color={theme.semantic.text.muted}>{t("status.noInfo")}</Text>
345
+ </Box>
346
+ );
347
+ }
348
+
349
+ return (
350
+ <Box
351
+ flexDirection="row"
352
+ justifyContent="space-between"
353
+ width="100%"
354
+ paddingX={showBorder ? 1 : 0}
355
+ borderStyle={showBorder ? "round" : undefined}
356
+ borderColor={showBorder ? borderColor : undefined}
357
+ >
358
+ {/* Left: Mode selector */}
359
+ {modeSection}
360
+
361
+ {/* Right: Model, Context, Cost - with separator from mode section */}
362
+ <Box flexDirection="row" alignItems="center">
363
+ <Text color={theme.semantic.border.muted}>{SEPARATOR}</Text>
364
+ {renderedRightItems}
365
+ </Box>
366
+ </Box>
367
+ );
368
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * ThinkingModeIndicator Component (T038)
3
+ *
4
+ * Displays the thinking mode status with optional budget tracking.
5
+ * Shows whether extended thinking is active and usage if budget is set.
6
+ *
7
+ * @module tui/components/StatusBar/ThinkingModeIndicator
8
+ */
9
+
10
+ import { Box, Text } from "ink";
11
+ import { useMemo } from "react";
12
+ import { useTheme } from "../../theme/index.js";
13
+
14
+ // =============================================================================
15
+ // Types
16
+ // =============================================================================
17
+
18
+ /**
19
+ * Props for the ThinkingModeIndicator component.
20
+ */
21
+ export interface ThinkingModeIndicatorProps {
22
+ /** Whether thinking mode is active */
23
+ readonly active: boolean;
24
+ /** Optional thinking budget (max tokens for thinking) */
25
+ readonly budget?: number;
26
+ /** Optional tokens used for thinking */
27
+ readonly used?: number;
28
+ }
29
+
30
+ // =============================================================================
31
+ // Constants
32
+ // =============================================================================
33
+
34
+ /** Icon for active thinking mode */
35
+ const THINKING_ACTIVE_ICON = "◆";
36
+
37
+ /** Icon for inactive thinking mode */
38
+ const THINKING_INACTIVE_ICON = "◇";
39
+
40
+ /** Warning threshold for thinking budget (80%) */
41
+ const BUDGET_WARNING_THRESHOLD = 80;
42
+
43
+ /** Error threshold for thinking budget (95%) */
44
+ const BUDGET_ERROR_THRESHOLD = 95;
45
+
46
+ // =============================================================================
47
+ // Helper Functions
48
+ // =============================================================================
49
+
50
+ /**
51
+ * Formats token count for compact display.
52
+ */
53
+ function formatTokens(count: number): string {
54
+ if (count < 1000) {
55
+ return count.toString();
56
+ }
57
+ if (count < 1000000) {
58
+ const k = count / 1000;
59
+ return k >= 10 ? `${Math.round(k)}K` : `${k.toFixed(1)}K`;
60
+ }
61
+ const m = count / 1000000;
62
+ return m >= 10 ? `${Math.round(m)}M` : `${m.toFixed(1)}M`;
63
+ }
64
+
65
+ /**
66
+ * Calculates percentage of budget used.
67
+ */
68
+ function calculateUsagePercentage(used: number, budget: number): number {
69
+ if (budget <= 0) return 0;
70
+ return Math.min(Math.round((used / budget) * 100), 100);
71
+ }
72
+
73
+ // =============================================================================
74
+ // Main Component
75
+ // =============================================================================
76
+
77
+ /**
78
+ * ThinkingModeIndicator displays thinking mode status and budget usage.
79
+ *
80
+ * Features:
81
+ * - Active/inactive state with icon
82
+ * - Optional budget display
83
+ * - Usage tracking with color-coded warnings
84
+ * - Compact format for status bar
85
+ *
86
+ * @example
87
+ * ```tsx
88
+ * // Active without budget
89
+ * <ThinkingModeIndicator active={true} />
90
+ *
91
+ * // Active with budget tracking
92
+ * <ThinkingModeIndicator
93
+ * active={true}
94
+ * budget={10000}
95
+ * used={5000}
96
+ * />
97
+ *
98
+ * // Inactive
99
+ * <ThinkingModeIndicator active={false} />
100
+ * ```
101
+ */
102
+ export function ThinkingModeIndicator({
103
+ active,
104
+ budget,
105
+ used,
106
+ }: ThinkingModeIndicatorProps): React.JSX.Element {
107
+ const { theme } = useTheme();
108
+
109
+ const icon = active ? THINKING_ACTIVE_ICON : THINKING_INACTIVE_ICON;
110
+
111
+ // Determine icon color
112
+ const iconColor = useMemo(() => {
113
+ if (!active) {
114
+ return theme.semantic.text.muted;
115
+ }
116
+ return theme.colors.primary;
117
+ }, [active, theme.colors.primary, theme.semantic.text.muted]);
118
+
119
+ // Calculate usage if budget is provided
120
+ const usageInfo = useMemo(() => {
121
+ if (budget === undefined || !active) {
122
+ return null;
123
+ }
124
+
125
+ const currentUsed = used ?? 0;
126
+ const percentage = calculateUsagePercentage(currentUsed, budget);
127
+
128
+ // Determine color based on usage
129
+ let color: string;
130
+ if (percentage >= BUDGET_ERROR_THRESHOLD) {
131
+ color = theme.colors.error;
132
+ } else if (percentage >= BUDGET_WARNING_THRESHOLD) {
133
+ color = theme.colors.warning;
134
+ } else {
135
+ color = theme.semantic.text.secondary;
136
+ }
137
+
138
+ return {
139
+ used: formatTokens(currentUsed),
140
+ budget: formatTokens(budget),
141
+ percentage,
142
+ color,
143
+ };
144
+ }, [
145
+ budget,
146
+ used,
147
+ active,
148
+ theme.colors.error,
149
+ theme.colors.warning,
150
+ theme.semantic.text.secondary,
151
+ ]);
152
+
153
+ const label = active ? "Think" : "Think";
154
+
155
+ return (
156
+ <Box>
157
+ <Text color={iconColor}>{icon}</Text>
158
+ <Text color={theme.semantic.text.muted}> </Text>
159
+ <Text color={active ? theme.semantic.text.primary : theme.semantic.text.muted}>{label}</Text>
160
+ {usageInfo && (
161
+ <>
162
+ <Text color={theme.semantic.text.muted}> </Text>
163
+ <Text color={usageInfo.color}>{usageInfo.used}</Text>
164
+ <Text color={theme.semantic.text.muted}>/</Text>
165
+ <Text color={theme.semantic.text.secondary}>{usageInfo.budget}</Text>
166
+ </>
167
+ )}
168
+ </Box>
169
+ );
170
+ }