@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,284 @@
1
+ /**
2
+ * System Status Panel Component
3
+ *
4
+ * Displays system status information at the bottom of the sidebar:
5
+ * - MCP server connection status
6
+ * - Rate limit status (if available)
7
+ * - Update availability (if available)
8
+ * - Git snapshot count (if available)
9
+ *
10
+ * @module tui/components/Sidebar/SystemStatusPanel
11
+ */
12
+
13
+ import { Box, Text } from "ink";
14
+ import type React from "react";
15
+ import { useApp } from "../../context/AppContext.js";
16
+ import { useLspOptional } from "../../context/LspContext.js";
17
+ import { useMcp } from "../../context/McpContext.js";
18
+ import { useTheme } from "../../theme/index.js";
19
+ import {
20
+ PersistenceStatusIndicator,
21
+ type PersistenceStatusIndicatorProps,
22
+ } from "../StatusBar/PersistenceStatusIndicator.js";
23
+
24
+ // =============================================================================
25
+ // Types
26
+ // =============================================================================
27
+
28
+ /**
29
+ * Props for the SystemStatusPanel component
30
+ */
31
+ export interface SystemStatusPanelProps {
32
+ /** Compact mode for narrow sidebars */
33
+ readonly compact?: boolean;
34
+ /** Optional snapshot count from session */
35
+ readonly snapshotCount?: number;
36
+ /** Optional update version available */
37
+ readonly updateVersion?: string;
38
+ /** Optional rate limit percentage (0-100) */
39
+ readonly rateLimitPercent?: number;
40
+ /** Optional persistence status for memory indicator */
41
+ readonly persistence?: Pick<
42
+ PersistenceStatusIndicatorProps,
43
+ "status" | "unsavedCount" | "lastSavedAt"
44
+ >;
45
+ }
46
+
47
+ /** Status display tuple: [text, color] */
48
+ type StatusDisplay = readonly [string, string];
49
+
50
+ // =============================================================================
51
+ // Status Item Component
52
+ // =============================================================================
53
+
54
+ interface StatusItemProps {
55
+ readonly label: string;
56
+ readonly value: string;
57
+ readonly color: string;
58
+ readonly labelColor: string;
59
+ readonly compact?: boolean;
60
+ }
61
+
62
+ function StatusItem({
63
+ label,
64
+ value,
65
+ color,
66
+ labelColor,
67
+ compact,
68
+ }: StatusItemProps): React.JSX.Element {
69
+ if (compact) {
70
+ return (
71
+ <Text>
72
+ <Text color={labelColor}>{label.charAt(0)}</Text>
73
+ <Text color={color}>{value}</Text>
74
+ </Text>
75
+ );
76
+ }
77
+
78
+ return (
79
+ <Box>
80
+ <Text color={labelColor}>{label}: </Text>
81
+ <Text color={color}>{value}</Text>
82
+ </Box>
83
+ );
84
+ }
85
+
86
+ // =============================================================================
87
+ // Status Computation Helpers
88
+ // =============================================================================
89
+
90
+ interface ThemeColors {
91
+ success: string;
92
+ warning: string;
93
+ error: string;
94
+ info: string;
95
+ primary: string;
96
+ accent: string;
97
+ muted: string;
98
+ }
99
+
100
+ /**
101
+ * Compute MCP connection status display
102
+ */
103
+ function getMcpStatus(
104
+ mcpError: Error | null,
105
+ isInitializing: boolean,
106
+ isInitialized: boolean,
107
+ connectedCount: number,
108
+ totalCount: number,
109
+ colors: ThemeColors
110
+ ): StatusDisplay {
111
+ if (mcpError) return ["err", colors.error];
112
+ if (isInitializing) return ["...", colors.warning];
113
+ if (!isInitialized) return ["off", colors.muted];
114
+ if (connectedCount === 0 && totalCount === 0) return ["–", colors.muted];
115
+ if (connectedCount === totalCount) return [`${connectedCount} ✓`, colors.success];
116
+ return [`${connectedCount}/${totalCount}`, colors.warning];
117
+ }
118
+
119
+ /**
120
+ * Compute LSP server status display
121
+ */
122
+ function getLspStatus(
123
+ lspError: Error | null,
124
+ isInitialized: boolean,
125
+ runningCount: number,
126
+ totalCount: number,
127
+ colors: ThemeColors
128
+ ): StatusDisplay {
129
+ if (lspError) return ["err", colors.error];
130
+ if (!isInitialized) return ["off", colors.muted];
131
+ if (totalCount === 0) return ["–", colors.muted];
132
+ if (runningCount === totalCount) return [`${runningCount} ✓`, colors.success];
133
+ return [`${runningCount}/${totalCount}`, colors.warning];
134
+ }
135
+
136
+ /**
137
+ * Compute rate limit status display
138
+ */
139
+ function getRateStatus(rateLimitPercent: number | undefined, colors: ThemeColors): StatusDisplay {
140
+ if (rateLimitPercent === undefined) return ["–", colors.muted];
141
+ if (rateLimitPercent >= 90) return [`${rateLimitPercent}%`, colors.error];
142
+ if (rateLimitPercent >= 70) return [`${rateLimitPercent}%`, colors.warning];
143
+ return ["OK", colors.success];
144
+ }
145
+
146
+ /**
147
+ * Compute update status display
148
+ */
149
+ function getUpdateStatus(
150
+ updateVersion: string | undefined,
151
+ compact: boolean,
152
+ colors: ThemeColors
153
+ ): StatusDisplay {
154
+ if (updateVersion) return [compact ? "!" : updateVersion, colors.info];
155
+ return ["✓", colors.success];
156
+ }
157
+
158
+ /**
159
+ * Compute snapshot status display
160
+ */
161
+ function getSnapStatus(snapshotCount: number | undefined, colors: ThemeColors): StatusDisplay {
162
+ if (snapshotCount !== undefined) return [String(snapshotCount), colors.primary];
163
+ return ["–", colors.muted];
164
+ }
165
+
166
+ // =============================================================================
167
+ // Main Component
168
+ // =============================================================================
169
+
170
+ /**
171
+ * SystemStatusPanel displays system status in a compact panel.
172
+ *
173
+ * @example
174
+ * ```tsx
175
+ * <SystemStatusPanel compact={sidebarWidth < 24} />
176
+ * ```
177
+ */
178
+ export function SystemStatusPanel({
179
+ compact = false,
180
+ snapshotCount,
181
+ updateVersion,
182
+ rateLimitPercent,
183
+ persistence,
184
+ }: SystemStatusPanelProps): React.JSX.Element {
185
+ const { theme } = useTheme();
186
+ const { hub, isInitialized, isInitializing, error: mcpError } = useMcp();
187
+ const lsp = useLspOptional();
188
+ const { state } = useApp();
189
+
190
+ // Build colors object for status helpers
191
+ const colors: ThemeColors = {
192
+ success: theme.colors.success,
193
+ warning: theme.colors.warning,
194
+ error: theme.colors.error,
195
+ info: theme.colors.info,
196
+ primary: theme.colors.primary,
197
+ accent: theme.colors.accent,
198
+ muted: theme.semantic.text.muted,
199
+ };
200
+
201
+ // Get MCP server counts
202
+ const mcpServers = hub?.getServers() ?? [];
203
+ const connectedCount = mcpServers.filter((s) => s.statusInfo.status === "connected").length;
204
+ const totalCount = mcpServers.length;
205
+
206
+ // Compute all status displays
207
+ const [mcpStatus, mcpColor] = getMcpStatus(
208
+ mcpError,
209
+ isInitializing,
210
+ isInitialized,
211
+ connectedCount,
212
+ totalCount,
213
+ colors
214
+ );
215
+ const [lspStatus, lspColor] = getLspStatus(
216
+ lsp?.error ?? null,
217
+ lsp?.isInitialized ?? false,
218
+ lsp?.runningServers ?? 0,
219
+ lsp?.totalServers ?? 0,
220
+ colors
221
+ );
222
+ const [rateStatus, rateColor] = getRateStatus(rateLimitPercent, colors);
223
+ const [updateStatus, updateColor] = getUpdateStatus(updateVersion, compact, colors);
224
+ const [snapStatus, snapColor] = getSnapStatus(snapshotCount, colors);
225
+
226
+ const labelColor = theme.semantic.text.secondary;
227
+ const borderColor = theme.semantic.border.default;
228
+
229
+ if (compact) {
230
+ return (
231
+ <Box flexDirection="row" borderStyle="single" borderColor={borderColor} paddingX={0} gap={1}>
232
+ <StatusItem label="M" value={mcpStatus} color={mcpColor} labelColor={labelColor} compact />
233
+ <StatusItem label="L" value={lspStatus} color={lspColor} labelColor={labelColor} compact />
234
+ <StatusItem
235
+ label="R"
236
+ value={rateStatus}
237
+ color={rateColor}
238
+ labelColor={labelColor}
239
+ compact
240
+ />
241
+ <StatusItem
242
+ label="S"
243
+ value={snapStatus}
244
+ color={snapColor}
245
+ labelColor={labelColor}
246
+ compact
247
+ />
248
+ {persistence && (
249
+ <PersistenceStatusIndicator
250
+ status={persistence.status}
251
+ unsavedCount={persistence.unsavedCount}
252
+ lastSavedAt={persistence.lastSavedAt}
253
+ compact
254
+ />
255
+ )}
256
+ {state.vimMode && <Text color={colors.accent}>VIM</Text>}
257
+ </Box>
258
+ );
259
+ }
260
+
261
+ return (
262
+ <Box flexDirection="column" borderStyle="single" borderColor={borderColor} paddingX={1}>
263
+ <Text color={theme.semantic.text.secondary} bold>
264
+ Status
265
+ </Text>
266
+ <StatusItem label="MCP" value={mcpStatus} color={mcpColor} labelColor={labelColor} />
267
+ <StatusItem label="LSP" value={lspStatus} color={lspColor} labelColor={labelColor} />
268
+ <StatusItem label="Rate" value={rateStatus} color={rateColor} labelColor={labelColor} />
269
+ <Box flexDirection="row" gap={1}>
270
+ <StatusItem label="Snap" value={snapStatus} color={snapColor} labelColor={labelColor} />
271
+ <StatusItem label="Upd" value={updateStatus} color={updateColor} labelColor={labelColor} />
272
+ </Box>
273
+ {/* Memory/Persistence status indicator (moved from footer) */}
274
+ {persistence && (
275
+ <PersistenceStatusIndicator
276
+ status={persistence.status}
277
+ unsavedCount={persistence.unsavedCount}
278
+ lastSavedAt={persistence.lastSavedAt}
279
+ />
280
+ )}
281
+ {state.vimMode && <Text color={colors.accent}>[VIM]</Text>}
282
+ </Box>
283
+ );
284
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Sidebar Components
3
+ *
4
+ * Components for the sidebar region of the TUI layout.
5
+ *
6
+ * @module tui/components/Sidebar
7
+ */
8
+
9
+ export { SystemStatusPanel, type SystemStatusPanelProps } from "./SystemStatusPanel.js";
@@ -0,0 +1,270 @@
1
+ /**
2
+ * ModelStatusBar Component
3
+ *
4
+ * Compact status bar showing provider health with circuit breaker states.
5
+ * Uses ASCII-only indicators for terminal compatibility.
6
+ *
7
+ * Display format: `[*] claude | [.] gpt-4 | [x] gemini`
8
+ * - [*] = active provider
9
+ * - [.] = healthy (CLOSED circuit)
10
+ * - [x] = unhealthy (OPEN circuit)
11
+ * - [?] = half-open (testing recovery)
12
+ *
13
+ * @module tui/components/Status/ModelStatusBar
14
+ */
15
+
16
+ import { Box, Text } from "ink";
17
+ import { useMemo } from "react";
18
+ import type { CircuitState, ProviderStatusEntry } from "../../hooks/useProviderStatus.js";
19
+ import { useTheme } from "../../theme/index.js";
20
+
21
+ // =============================================================================
22
+ // Types
23
+ // =============================================================================
24
+
25
+ /**
26
+ * Props for ModelStatusBar component.
27
+ */
28
+ export interface ModelStatusBarProps {
29
+ /** Array of provider status entries to display */
30
+ readonly providers: readonly ProviderStatusEntry[];
31
+ /** Maximum number of providers to show (0 = show all) */
32
+ readonly maxVisible?: number;
33
+ /** Show provider names instead of IDs */
34
+ readonly showNames?: boolean;
35
+ /** Compact mode - shorter provider identifiers */
36
+ readonly compact?: boolean;
37
+ }
38
+
39
+ // =============================================================================
40
+ // Constants
41
+ // =============================================================================
42
+
43
+ /** Status indicators (ASCII only, no emoji) */
44
+ const STATUS_INDICATORS: Record<CircuitState | "active", string> = {
45
+ active: "[*]", // Active provider
46
+ CLOSED: "[.]", // Healthy
47
+ OPEN: "[x]", // Unhealthy
48
+ HALF_OPEN: "[?]", // Testing recovery
49
+ };
50
+
51
+ /** Separator between provider entries */
52
+ const SEPARATOR = " | ";
53
+
54
+ /** Provider ID abbreviations for compact display */
55
+ const PROVIDER_ABBREVIATIONS: Record<string, string> = {
56
+ anthropic: "claude",
57
+ openai: "gpt",
58
+ google: "gemini",
59
+ deepseek: "ds",
60
+ qwen: "qwen",
61
+ groq: "groq",
62
+ xai: "grok",
63
+ openrouter: "or",
64
+ ollama: "local",
65
+ lmstudio: "lms",
66
+ zhipu: "glm",
67
+ moonshot: "moon",
68
+ mistral: "mist",
69
+ yi: "yi",
70
+ baichuan: "bc",
71
+ copilot: "copilot",
72
+ minimax: "mm",
73
+ };
74
+
75
+ // =============================================================================
76
+ // Helper Functions
77
+ // =============================================================================
78
+
79
+ /**
80
+ * Get the status indicator for a provider entry.
81
+ */
82
+ function getStatusIndicator(entry: ProviderStatusEntry): string {
83
+ if (entry.isActive) {
84
+ return STATUS_INDICATORS.active;
85
+ }
86
+ return STATUS_INDICATORS[entry.circuitState];
87
+ }
88
+
89
+ /**
90
+ * Get abbreviated provider name for compact display.
91
+ */
92
+ function getProviderAbbreviation(id: string): string {
93
+ const lowerId = id.toLowerCase();
94
+ return PROVIDER_ABBREVIATIONS[lowerId] ?? id.slice(0, 4);
95
+ }
96
+
97
+ /**
98
+ * Get display label for a provider.
99
+ */
100
+ function getProviderLabel(
101
+ entry: ProviderStatusEntry,
102
+ showNames: boolean,
103
+ compact: boolean
104
+ ): string {
105
+ if (showNames) {
106
+ return compact ? entry.name.slice(0, 8) : entry.name;
107
+ }
108
+ return compact ? getProviderAbbreviation(entry.id) : entry.id;
109
+ }
110
+
111
+ // =============================================================================
112
+ // Component
113
+ // =============================================================================
114
+
115
+ /**
116
+ * ModelStatusBar displays provider health status in a compact format.
117
+ *
118
+ * @example
119
+ * ```tsx
120
+ * <ModelStatusBar
121
+ * providers={[
122
+ * { id: 'anthropic', name: 'Claude', circuitState: 'CLOSED', isActive: true, failureCount: 0, timeUntilReset: 0 },
123
+ * { id: 'openai', name: 'GPT-4', circuitState: 'CLOSED', isActive: false, failureCount: 0, timeUntilReset: 0 },
124
+ * { id: 'google', name: 'Gemini', circuitState: 'OPEN', isActive: false, failureCount: 3, timeUntilReset: 15000 },
125
+ * ]}
126
+ * />
127
+ * // Renders: [*] claude | [.] gpt | [x] gemini
128
+ * ```
129
+ */
130
+ export function ModelStatusBar({
131
+ providers,
132
+ maxVisible = 0,
133
+ showNames = false,
134
+ compact = true,
135
+ }: ModelStatusBarProps): React.ReactElement | null {
136
+ const { theme } = useTheme();
137
+
138
+ // Filter and limit providers to display
139
+ const visibleProviders = useMemo(() => {
140
+ if (providers.length === 0) return [];
141
+
142
+ // Sort: active first, then by circuit state (CLOSED > HALF_OPEN > OPEN)
143
+ const sorted = [...providers].sort((a, b) => {
144
+ if (a.isActive !== b.isActive) return a.isActive ? -1 : 1;
145
+
146
+ const stateOrder: Record<CircuitState, number> = {
147
+ CLOSED: 0,
148
+ HALF_OPEN: 1,
149
+ OPEN: 2,
150
+ };
151
+ return stateOrder[a.circuitState] - stateOrder[b.circuitState];
152
+ });
153
+
154
+ return maxVisible > 0 ? sorted.slice(0, maxVisible) : sorted;
155
+ }, [providers, maxVisible]);
156
+
157
+ // Don't render if no providers
158
+ if (visibleProviders.length === 0) {
159
+ return null;
160
+ }
161
+
162
+ // Get color for circuit state
163
+ const getStateColor = (entry: ProviderStatusEntry): string => {
164
+ if (entry.isActive) {
165
+ return theme.brand.primary;
166
+ }
167
+ switch (entry.circuitState) {
168
+ case "CLOSED":
169
+ return theme.colors.success;
170
+ case "OPEN":
171
+ return theme.colors.error;
172
+ case "HALF_OPEN":
173
+ return theme.colors.warning;
174
+ }
175
+ };
176
+
177
+ // Overflow indicator if some providers are hidden
178
+ const hiddenCount = providers.length - visibleProviders.length;
179
+
180
+ return (
181
+ <Box flexDirection="row">
182
+ {visibleProviders.map((entry, index) => {
183
+ const indicator = getStatusIndicator(entry);
184
+ const label = getProviderLabel(entry, showNames, compact);
185
+ const color = getStateColor(entry);
186
+
187
+ return (
188
+ <Text key={entry.id}>
189
+ {index > 0 && <Text color={theme.semantic.border.muted}>{SEPARATOR}</Text>}
190
+ <Text color={color} bold={entry.isActive}>
191
+ {indicator}
192
+ </Text>
193
+ <Text color={color} dimColor={!entry.isActive}>
194
+ {" "}
195
+ {label}
196
+ </Text>
197
+ </Text>
198
+ );
199
+ })}
200
+ {hiddenCount > 0 && (
201
+ <Text color={theme.semantic.text.muted}>
202
+ {SEPARATOR}+{hiddenCount}
203
+ </Text>
204
+ )}
205
+ </Box>
206
+ );
207
+ }
208
+
209
+ // =============================================================================
210
+ // Compact Variant
211
+ // =============================================================================
212
+
213
+ /**
214
+ * Props for ModelStatusBarCompact component.
215
+ */
216
+ export interface ModelStatusBarCompactProps {
217
+ /** Array of provider status entries */
218
+ readonly providers: readonly ProviderStatusEntry[];
219
+ }
220
+
221
+ /**
222
+ * Ultra-compact variant showing only indicators without labels.
223
+ * Format: `[*][.][x]` - suitable for very narrow terminals.
224
+ *
225
+ * @example
226
+ * ```tsx
227
+ * <ModelStatusBarCompact providers={providers} />
228
+ * // Renders: [*][.][x]
229
+ * ```
230
+ */
231
+ export function ModelStatusBarCompact({
232
+ providers,
233
+ }: ModelStatusBarCompactProps): React.ReactElement | null {
234
+ const { theme } = useTheme();
235
+
236
+ if (providers.length === 0) {
237
+ return null;
238
+ }
239
+
240
+ // Sort: active first, then by ID
241
+ const sorted = [...providers].sort((a, b) => {
242
+ if (a.isActive !== b.isActive) return a.isActive ? -1 : 1;
243
+ return a.id.localeCompare(b.id);
244
+ });
245
+
246
+ return (
247
+ <Box flexDirection="row">
248
+ {sorted.map((entry) => {
249
+ const indicator = getStatusIndicator(entry);
250
+
251
+ let color: string;
252
+ if (entry.isActive) {
253
+ color = theme.brand.primary;
254
+ } else if (entry.circuitState === "CLOSED") {
255
+ color = theme.colors.success;
256
+ } else if (entry.circuitState === "OPEN") {
257
+ color = theme.colors.error;
258
+ } else {
259
+ color = theme.colors.warning;
260
+ }
261
+
262
+ return (
263
+ <Text key={entry.id} color={color} bold={entry.isActive}>
264
+ {indicator}
265
+ </Text>
266
+ );
267
+ })}
268
+ </Box>
269
+ );
270
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Status Components
3
+ *
4
+ * Provider and model status display components.
5
+ */
6
+
7
+ export {
8
+ ModelStatusBar,
9
+ ModelStatusBarCompact,
10
+ type ModelStatusBarCompactProps,
11
+ type ModelStatusBarProps,
12
+ } from "./ModelStatusBar.js";