@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,483 @@
1
+ /**
2
+ * ToolParams Component (T028)
3
+ *
4
+ * Renders tool parameters as a collapsible JSON tree structure with
5
+ * syntax highlighting for different value types and special formatting
6
+ * for file paths and shell commands.
7
+ *
8
+ * @module tui/components/Tools/ToolParams
9
+ */
10
+
11
+ import { Box, Text } from "ink";
12
+ import type React from "react";
13
+ import { useTheme } from "../../theme/index.js";
14
+
15
+ // =============================================================================
16
+ // Types
17
+ // =============================================================================
18
+
19
+ /**
20
+ * Props for the ToolParams component.
21
+ */
22
+ export interface ToolParamsProps {
23
+ /** The parameters object to render */
24
+ readonly params: Record<string, unknown>;
25
+ /** Whether to show collapsed view (default: false) */
26
+ readonly collapsed?: boolean;
27
+ /** Maximum depth to render nested objects (default: 10) */
28
+ readonly maxDepth?: number;
29
+ /** Highlight file paths with special styling (default: false) */
30
+ readonly highlightPaths?: boolean;
31
+ /** Highlight shell commands with special styling (default: false) */
32
+ readonly highlightCommands?: boolean;
33
+ }
34
+
35
+ /**
36
+ * Internal props for recursive value rendering.
37
+ */
38
+ interface ValueRenderProps {
39
+ /** The value to render */
40
+ readonly value: unknown;
41
+ /** The key name (for detecting command keys) */
42
+ readonly keyName?: string;
43
+ /** Current depth in the tree */
44
+ readonly depth: number;
45
+ /** Maximum depth to render */
46
+ readonly maxDepth: number;
47
+ /** Whether to highlight file paths */
48
+ readonly highlightPaths: boolean;
49
+ /** Whether to highlight commands */
50
+ readonly highlightCommands: boolean;
51
+ }
52
+
53
+ // =============================================================================
54
+ // Constants
55
+ // =============================================================================
56
+
57
+ /** Base indentation per depth level */
58
+ const INDENT_SPACES = 2;
59
+
60
+ /** Keys that typically contain shell commands */
61
+ const COMMAND_KEYS = new Set(["command", "cmd", "shell", "script", "exec", "run", "execute"]);
62
+
63
+ /** File path patterns to detect */
64
+ const PATH_PATTERNS = [
65
+ // Unix-style absolute paths
66
+ /^\/[\w\-./]+$/,
67
+ // Windows-style paths
68
+ /^[A-Za-z]:[\\/][\w\-.\\/]+$/,
69
+ // Relative paths starting with ./ or ../
70
+ /^\.\.?\/[\w\-./]+$/,
71
+ // Common file extensions
72
+ /\.(ts|tsx|js|jsx|json|md|py|rb|go|rs|java|c|cpp|h|hpp|css|scss|html|xml|yaml|yml|toml|sh|bash|zsh|ps1|cmd|bat)$/i,
73
+ ];
74
+
75
+ // =============================================================================
76
+ // Helper Functions
77
+ // =============================================================================
78
+
79
+ /**
80
+ * Check if a string looks like a file path.
81
+ *
82
+ * @param value - The string to check
83
+ * @returns true if the string appears to be a file path
84
+ */
85
+ function isFilePath(value: string): boolean {
86
+ return PATH_PATTERNS.some((pattern) => pattern.test(value));
87
+ }
88
+
89
+ /**
90
+ * Check if a key name indicates a command.
91
+ *
92
+ * @param key - The key name to check
93
+ * @returns true if the key likely contains a command
94
+ */
95
+ function isCommandKey(key: string): boolean {
96
+ return COMMAND_KEYS.has(key.toLowerCase());
97
+ }
98
+
99
+ /**
100
+ * Get the type name of a value for display.
101
+ *
102
+ * @param value - The value to check
103
+ * @returns The type name string
104
+ */
105
+ function getTypeName(value: unknown): string {
106
+ if (value === null) return "null";
107
+ if (Array.isArray(value)) return "array";
108
+ return typeof value;
109
+ }
110
+
111
+ /**
112
+ * Create indentation string for a given depth.
113
+ *
114
+ * @param depth - The current depth level
115
+ * @returns Indentation string
116
+ */
117
+ function indent(depth: number): string {
118
+ return " ".repeat(depth * INDENT_SPACES);
119
+ }
120
+
121
+ // =============================================================================
122
+ // Value Renderer Components
123
+ // =============================================================================
124
+
125
+ /**
126
+ * Renders a null value.
127
+ */
128
+ function NullValue(): React.JSX.Element {
129
+ const { theme } = useTheme();
130
+ return <Text color={theme.semantic.text.muted}>null</Text>;
131
+ }
132
+
133
+ /**
134
+ * Renders a boolean value.
135
+ */
136
+ function BooleanValue({ value }: { readonly value: boolean }): React.JSX.Element {
137
+ const { theme } = useTheme();
138
+ return <Text color={theme.colors.warning}>{String(value)}</Text>;
139
+ }
140
+
141
+ /**
142
+ * Renders a number value.
143
+ */
144
+ function NumberValue({ value }: { readonly value: number }): React.JSX.Element {
145
+ const { theme } = useTheme();
146
+ return <Text color={theme.colors.info}>{String(value)}</Text>;
147
+ }
148
+
149
+ /**
150
+ * Renders a string value with optional path/command highlighting.
151
+ */
152
+ function StringValue({
153
+ value,
154
+ keyName,
155
+ highlightPaths,
156
+ highlightCommands,
157
+ }: {
158
+ readonly value: string;
159
+ readonly keyName?: string;
160
+ readonly highlightPaths: boolean;
161
+ readonly highlightCommands: boolean;
162
+ }): React.JSX.Element {
163
+ const { theme } = useTheme();
164
+
165
+ // Check for command highlighting
166
+ if (highlightCommands && keyName && isCommandKey(keyName)) {
167
+ return (
168
+ <Text color={theme.colors.accent} italic>
169
+ "{value}"
170
+ </Text>
171
+ );
172
+ }
173
+
174
+ // Check for path highlighting
175
+ if (highlightPaths && isFilePath(value)) {
176
+ return (
177
+ <Text color={theme.semantic.syntax.function} underline>
178
+ "{value}"
179
+ </Text>
180
+ );
181
+ }
182
+
183
+ // Default string rendering
184
+ return <Text color={theme.colors.success}>"{value}"</Text>;
185
+ }
186
+
187
+ /**
188
+ * Renders an array value with proper formatting.
189
+ */
190
+ function ArrayValue({
191
+ value,
192
+ depth,
193
+ maxDepth,
194
+ highlightPaths,
195
+ highlightCommands,
196
+ }: {
197
+ readonly value: unknown[];
198
+ readonly depth: number;
199
+ readonly maxDepth: number;
200
+ readonly highlightPaths: boolean;
201
+ readonly highlightCommands: boolean;
202
+ }): React.JSX.Element {
203
+ const { theme } = useTheme();
204
+ const mutedColor = theme.semantic.text.muted;
205
+
206
+ // Max depth reached - show collapsed
207
+ if (depth >= maxDepth) {
208
+ return <Text color={mutedColor}>[{value.length} items...]</Text>;
209
+ }
210
+
211
+ // Empty array
212
+ if (value.length === 0) {
213
+ return <Text color={mutedColor}>[]</Text>;
214
+ }
215
+
216
+ const childIndent = indent(depth + 1);
217
+ const closeIndent = indent(depth);
218
+
219
+ return (
220
+ <Box flexDirection="column">
221
+ <Text color={mutedColor}>[</Text>
222
+ {value.map((item, index) => (
223
+ // biome-ignore lint/suspicious/noArrayIndexKey: Array order is stable in JSON params
224
+ <Box key={index}>
225
+ <Text>{childIndent}</Text>
226
+ <ValueRenderer
227
+ value={item}
228
+ depth={depth + 1}
229
+ maxDepth={maxDepth}
230
+ highlightPaths={highlightPaths}
231
+ highlightCommands={highlightCommands}
232
+ />
233
+ {index < value.length - 1 && <Text color={mutedColor}>,</Text>}
234
+ </Box>
235
+ ))}
236
+ <Text color={mutedColor}>{closeIndent}]</Text>
237
+ </Box>
238
+ );
239
+ }
240
+
241
+ /**
242
+ * Renders an object value with proper formatting.
243
+ */
244
+ function ObjectValue({
245
+ value,
246
+ depth,
247
+ maxDepth,
248
+ highlightPaths,
249
+ highlightCommands,
250
+ }: {
251
+ readonly value: Record<string, unknown>;
252
+ readonly depth: number;
253
+ readonly maxDepth: number;
254
+ readonly highlightPaths: boolean;
255
+ readonly highlightCommands: boolean;
256
+ }): React.JSX.Element {
257
+ const { theme } = useTheme();
258
+ const mutedColor = theme.semantic.text.muted;
259
+ const keyColor = theme.semantic.text.primary;
260
+
261
+ const entries = Object.entries(value);
262
+
263
+ // Max depth reached - show collapsed
264
+ if (depth >= maxDepth) {
265
+ return (
266
+ <Text color={mutedColor}>
267
+ {"{"}...{entries.length} keys{"}"}
268
+ </Text>
269
+ );
270
+ }
271
+
272
+ // Empty object
273
+ if (entries.length === 0) {
274
+ return <Text color={mutedColor}>{"{}"}</Text>;
275
+ }
276
+
277
+ const childIndent = indent(depth + 1);
278
+ const closeIndent = indent(depth);
279
+
280
+ return (
281
+ <Box flexDirection="column">
282
+ <Text color={mutedColor}>{"{"}</Text>
283
+ {entries.map(([key, val], index) => (
284
+ <Box key={key}>
285
+ <Text>{childIndent}</Text>
286
+ <Text bold color={keyColor}>
287
+ {key}
288
+ </Text>
289
+ <Text color={mutedColor}>: </Text>
290
+ <ValueRenderer
291
+ value={val}
292
+ keyName={key}
293
+ depth={depth + 1}
294
+ maxDepth={maxDepth}
295
+ highlightPaths={highlightPaths}
296
+ highlightCommands={highlightCommands}
297
+ />
298
+ {index < entries.length - 1 && <Text color={mutedColor}>,</Text>}
299
+ </Box>
300
+ ))}
301
+ <Text color={mutedColor}>
302
+ {closeIndent}
303
+ {"}"}
304
+ </Text>
305
+ </Box>
306
+ );
307
+ }
308
+
309
+ /**
310
+ * Main value renderer that dispatches to type-specific renderers.
311
+ */
312
+ function ValueRenderer({
313
+ value,
314
+ keyName,
315
+ depth,
316
+ maxDepth,
317
+ highlightPaths,
318
+ highlightCommands,
319
+ }: ValueRenderProps): React.JSX.Element {
320
+ const { theme } = useTheme();
321
+
322
+ // Handle null
323
+ if (value === null) {
324
+ return <NullValue />;
325
+ }
326
+
327
+ // Handle undefined (treat as null)
328
+ if (value === undefined) {
329
+ return <Text color={theme.semantic.text.muted}>undefined</Text>;
330
+ }
331
+
332
+ // Handle primitive types
333
+ const type = getTypeName(value);
334
+
335
+ switch (type) {
336
+ case "boolean":
337
+ return <BooleanValue value={value as boolean} />;
338
+
339
+ case "number":
340
+ return <NumberValue value={value as number} />;
341
+
342
+ case "string":
343
+ return (
344
+ <StringValue
345
+ value={value as string}
346
+ keyName={keyName}
347
+ highlightPaths={highlightPaths}
348
+ highlightCommands={highlightCommands}
349
+ />
350
+ );
351
+
352
+ case "array":
353
+ return (
354
+ <ArrayValue
355
+ value={value as unknown[]}
356
+ depth={depth}
357
+ maxDepth={maxDepth}
358
+ highlightPaths={highlightPaths}
359
+ highlightCommands={highlightCommands}
360
+ />
361
+ );
362
+
363
+ case "object":
364
+ return (
365
+ <ObjectValue
366
+ value={value as Record<string, unknown>}
367
+ depth={depth}
368
+ maxDepth={maxDepth}
369
+ highlightPaths={highlightPaths}
370
+ highlightCommands={highlightCommands}
371
+ />
372
+ );
373
+
374
+ default:
375
+ // For functions, symbols, etc.
376
+ return <Text color={theme.semantic.text.muted}>[{type}]</Text>;
377
+ }
378
+ }
379
+
380
+ // =============================================================================
381
+ // Collapsed View
382
+ // =============================================================================
383
+
384
+ /**
385
+ * Renders a collapsed summary of the params object.
386
+ */
387
+ function CollapsedView({
388
+ params,
389
+ }: {
390
+ readonly params: Record<string, unknown>;
391
+ }): React.JSX.Element {
392
+ const { theme } = useTheme();
393
+ const keys = Object.keys(params);
394
+ const keyCount = keys.length;
395
+
396
+ if (keyCount === 0) {
397
+ return <Text color={theme.semantic.text.muted}>{"{}"}</Text>;
398
+ }
399
+
400
+ // Show first few keys as preview
401
+ const previewKeys = keys.slice(0, 3);
402
+ const hasMore = keyCount > 3;
403
+
404
+ return (
405
+ <Box>
406
+ <Text color={theme.semantic.text.muted}>{"{ "}</Text>
407
+ {previewKeys.map((key, index) => (
408
+ <Box key={key}>
409
+ <Text bold color={theme.semantic.text.primary}>
410
+ {key}
411
+ </Text>
412
+ {index < previewKeys.length - 1 && <Text color={theme.semantic.text.muted}>, </Text>}
413
+ </Box>
414
+ ))}
415
+ {hasMore && <Text color={theme.semantic.text.muted}>, ...+{keyCount - 3}</Text>}
416
+ <Text color={theme.semantic.text.muted}>{" }"}</Text>
417
+ </Box>
418
+ );
419
+ }
420
+
421
+ // =============================================================================
422
+ // Main Component
423
+ // =============================================================================
424
+
425
+ /**
426
+ * ToolParams displays tool parameters as a formatted JSON tree.
427
+ *
428
+ * Features:
429
+ * - Collapsible tree structure for nested objects/arrays
430
+ * - Type-specific color coding:
431
+ * - Keys: bold primary text
432
+ * - Strings: green
433
+ * - Numbers: cyan/info
434
+ * - Booleans: yellow/warning
435
+ * - Null: muted/dim
436
+ * - Optional file path highlighting (underlined)
437
+ * - Optional command highlighting (italic accent)
438
+ * - Proper indentation for nested structures
439
+ * - Collapsed mode shows key preview
440
+ *
441
+ * @example
442
+ * ```tsx
443
+ * // Basic usage
444
+ * <ToolParams params={{ name: "test", count: 42 }} />
445
+ *
446
+ * // With path highlighting
447
+ * <ToolParams
448
+ * params={{ filePath: "/src/index.ts" }}
449
+ * highlightPaths
450
+ * />
451
+ *
452
+ * // Collapsed view
453
+ * <ToolParams params={largeObject} collapsed />
454
+ *
455
+ * // Limited depth
456
+ * <ToolParams params={deepObject} maxDepth={3} />
457
+ * ```
458
+ */
459
+ export function ToolParams({
460
+ params,
461
+ collapsed = false,
462
+ maxDepth = 10,
463
+ highlightPaths = false,
464
+ highlightCommands = false,
465
+ }: ToolParamsProps): React.JSX.Element {
466
+ // Collapsed mode - show summary
467
+ if (collapsed) {
468
+ return <CollapsedView params={params} />;
469
+ }
470
+
471
+ // Full tree rendering
472
+ return (
473
+ <ObjectValue
474
+ value={params}
475
+ depth={0}
476
+ maxDepth={maxDepth}
477
+ highlightPaths={highlightPaths}
478
+ highlightCommands={highlightCommands}
479
+ />
480
+ );
481
+ }
482
+
483
+ export default ToolParams;
@@ -0,0 +1,178 @@
1
+ /**
2
+ * ToolsPanel Component
3
+ *
4
+ * Minimal sidebar panel that renders recent tool executions.
5
+ * Designed for narrow widths (20-40 cols) inside Layout sidebar.
6
+ */
7
+
8
+ import { Box, Text } from "ink";
9
+ import type React from "react";
10
+ import { useMemo } from "react";
11
+ import { useTools } from "../../context/ToolsContext.js";
12
+ import { useTheme } from "../../theme/index.js";
13
+ import { HotkeyHints } from "../common/HotkeyHints.js";
14
+ import { ToolParams } from "./ToolParams.js";
15
+
16
+ /** Shell tool names that support streaming output */
17
+ const SHELL_TOOLS = new Set(["shell", "bash"]);
18
+
19
+ export interface ToolsPanelProps {
20
+ /** Whether this panel is currently focused (reserved for future key handling). */
21
+ readonly isFocused?: boolean;
22
+ /** Max number of executions to show (default: 10). */
23
+ readonly maxItems?: number;
24
+ }
25
+
26
+ function statusLabel(status: string): string {
27
+ switch (status) {
28
+ case "pending":
29
+ return "pending";
30
+ case "approved":
31
+ return "approved";
32
+ case "rejected":
33
+ return "rejected";
34
+ case "running":
35
+ return "running";
36
+ case "complete":
37
+ return "done";
38
+ case "error":
39
+ return "error";
40
+ default:
41
+ return status;
42
+ }
43
+ }
44
+
45
+ function statusGlyph(status: string): string {
46
+ switch (status) {
47
+ case "pending":
48
+ return "?";
49
+ case "approved":
50
+ return "✓";
51
+ case "running":
52
+ return "…";
53
+ case "complete":
54
+ return "✓";
55
+ case "rejected":
56
+ return "✗";
57
+ case "error":
58
+ return "!";
59
+ default:
60
+ return "·";
61
+ }
62
+ }
63
+
64
+ export function ToolsPanel({ maxItems = 10 }: ToolsPanelProps): React.JSX.Element {
65
+ const { theme } = useTheme();
66
+ const { executions, pendingApproval } = useTools();
67
+
68
+ const hints = useMemo(
69
+ () => [
70
+ { keys: "Alt+K", label: "Sidebar" },
71
+ { keys: "Alt+G", label: "Tools" },
72
+ { keys: "Alt+O", label: "MCP" },
73
+ { keys: "Alt+P", label: "Memory" },
74
+ { keys: "Alt+T", label: "Todo" },
75
+ { keys: "Ctrl+S", label: "Sessions" },
76
+ { keys: "Ctrl+Z", label: "Undo" },
77
+ { keys: "Ctrl+Y", label: "Redo" },
78
+ ],
79
+ []
80
+ );
81
+
82
+ const recent = useMemo(() => {
83
+ if (executions.length <= maxItems) {
84
+ return executions;
85
+ }
86
+ return executions.slice(executions.length - maxItems);
87
+ }, [executions, maxItems]);
88
+
89
+ const latest = recent.length > 0 ? recent[recent.length - 1] : undefined;
90
+
91
+ return (
92
+ <Box flexDirection="column">
93
+ <Text color={theme.colors.primary} bold>
94
+ Tools
95
+ </Text>
96
+
97
+ <Text dimColor>
98
+ Pending: {pendingApproval.length} Total: {executions.length}
99
+ </Text>
100
+
101
+ {recent.length === 0 ? (
102
+ <Text dimColor>No tool activity yet</Text>
103
+ ) : (
104
+ <Box flexDirection="column" marginTop={1}>
105
+ {recent.map((execution) => (
106
+ <Text key={execution.id}>
107
+ {statusGlyph(execution.status)} {execution.toolName} ({statusLabel(execution.status)})
108
+ </Text>
109
+ ))}
110
+ </Box>
111
+ )}
112
+
113
+ {/* Minimal details view (most recent execution) */}
114
+ {latest && (
115
+ <Box flexDirection="column" marginTop={1}>
116
+ <Text color={theme.colors.primary} bold>
117
+ Details
118
+ </Text>
119
+ <Text dimColor>
120
+ {latest.toolName} ({statusLabel(latest.status)})
121
+ </Text>
122
+
123
+ {Object.keys(latest.params).length > 0 && (
124
+ <Box marginTop={1} flexDirection="column">
125
+ <Text dimColor>Params:</Text>
126
+ <ToolParams params={latest.params} collapsed highlightPaths highlightCommands />
127
+ </Box>
128
+ )}
129
+
130
+ {/* Shell output streaming preview */}
131
+ {SHELL_TOOLS.has(latest.toolName) &&
132
+ latest.status === "running" &&
133
+ latest.shellOutput &&
134
+ latest.shellOutput.length > 0 && (
135
+ <Box marginTop={1} flexDirection="column">
136
+ <Text color={theme.colors.primary} dimColor>
137
+ Output:
138
+ </Text>
139
+ <Box
140
+ flexDirection="column"
141
+ borderStyle="single"
142
+ borderColor={theme.colors.secondary}
143
+ paddingX={1}
144
+ >
145
+ {latest.shellOutput.slice(-5).map((line, idx) => (
146
+ <Text key={`shell-output-${idx}-${line.slice(0, 20)}`} wrap="truncate">
147
+ {line}
148
+ </Text>
149
+ ))}
150
+ </Box>
151
+ </Box>
152
+ )}
153
+
154
+ {latest.status === "error" && latest.error && (
155
+ <Box marginTop={1}>
156
+ <Text color={theme.colors.error}>Error: {latest.error.message}</Text>
157
+ </Box>
158
+ )}
159
+
160
+ {latest.status === "complete" && latest.result !== undefined && (
161
+ <Box marginTop={1} flexDirection="column">
162
+ <Text dimColor>Result:</Text>
163
+ <Text>
164
+ {typeof latest.result === "string"
165
+ ? latest.result
166
+ : JSON.stringify(latest.result, null, 2)}
167
+ </Text>
168
+ </Box>
169
+ )}
170
+ </Box>
171
+ )}
172
+
173
+ <Box marginTop={1}>
174
+ <HotkeyHints hints={hints} />
175
+ </Box>
176
+ </Box>
177
+ );
178
+ }