@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,424 @@
1
+ /**
2
+ * AgentProgress Component (T046)
3
+ *
4
+ * TUI component for displaying agent task progress with a visual task tree
5
+ * and progress bar. Shows task status with icons and completion percentage.
6
+ *
7
+ * @module tui/components/AgentProgress
8
+ */
9
+
10
+ import type { TaskChain, TaskChainNode } from "@vellum/core";
11
+ import { Box, Text } from "ink";
12
+ import type React from "react";
13
+ import { useMemo } from "react";
14
+ import { useTheme } from "../theme/index.js";
15
+
16
+ // =============================================================================
17
+ // Types
18
+ // =============================================================================
19
+
20
+ /**
21
+ * Props for the AgentProgress component.
22
+ */
23
+ export interface AgentProgressProps {
24
+ /** The task chain to display */
25
+ readonly chain: TaskChain;
26
+ /** ID of the currently executing task (for highlighting) */
27
+ readonly currentTaskId?: string;
28
+ /** Whether to show detailed task information */
29
+ readonly showDetails?: boolean;
30
+ /** Width of the progress bar in characters (default: 20) */
31
+ readonly progressBarWidth?: number;
32
+ }
33
+
34
+ // =============================================================================
35
+ // Constants
36
+ // =============================================================================
37
+
38
+ /**
39
+ * Status icons for task states.
40
+ * Using ASCII symbols for terminal display.
41
+ */
42
+ const STATUS_ICONS: Record<TaskChainNode["status"], string> = {
43
+ pending: "o",
44
+ running: "~",
45
+ completed: "+",
46
+ failed: "x",
47
+ } as const;
48
+
49
+ /**
50
+ * Tree line characters for visual hierarchy.
51
+ */
52
+ const TREE_CHARS = {
53
+ branch: "├──",
54
+ last: "└──",
55
+ vertical: "│ ",
56
+ space: " ",
57
+ } as const;
58
+
59
+ /** Default progress bar width */
60
+ const DEFAULT_PROGRESS_BAR_WIDTH = 20;
61
+
62
+ /** Progress bar characters */
63
+ const PROGRESS_FILLED = "█";
64
+ const PROGRESS_EMPTY = "░";
65
+
66
+ // =============================================================================
67
+ // Helper Functions
68
+ // =============================================================================
69
+
70
+ /**
71
+ * Get the color for a task status.
72
+ */
73
+ function getStatusColor(
74
+ status: TaskChainNode["status"],
75
+ isCurrent: boolean,
76
+ theme: ReturnType<typeof useTheme>["theme"]
77
+ ): string {
78
+ if (isCurrent) {
79
+ return theme.semantic.status.running;
80
+ }
81
+
82
+ switch (status) {
83
+ case "completed":
84
+ return theme.semantic.status.complete;
85
+ case "failed":
86
+ return theme.semantic.status.error;
87
+ case "running":
88
+ return theme.semantic.status.running;
89
+ default:
90
+ return theme.semantic.text.muted;
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Build a hierarchical tree structure from task chain nodes.
96
+ * Returns nodes organized by parent-child relationships.
97
+ */
98
+ interface TreeNode {
99
+ node: TaskChainNode;
100
+ children: TreeNode[];
101
+ }
102
+
103
+ function buildTree(chain: TaskChain): TreeNode[] {
104
+ const nodeMap = new Map<string, TreeNode>();
105
+ const roots: TreeNode[] = [];
106
+
107
+ // Create TreeNode wrappers
108
+ for (const [, node] of chain.nodes) {
109
+ nodeMap.set(node.taskId, { node, children: [] });
110
+ }
111
+
112
+ // Build parent-child relationships
113
+ for (const [, treeNode] of nodeMap) {
114
+ const parentId = treeNode.node.parentTaskId;
115
+ if (parentId && nodeMap.has(parentId)) {
116
+ nodeMap.get(parentId)?.children.push(treeNode);
117
+ } else {
118
+ // Root node (no parent)
119
+ roots.push(treeNode);
120
+ }
121
+ }
122
+
123
+ // Sort children by creation time
124
+ for (const treeNode of nodeMap.values()) {
125
+ treeNode.children.sort((a, b) => a.node.createdAt.getTime() - b.node.createdAt.getTime());
126
+ }
127
+
128
+ // Sort roots by creation time
129
+ roots.sort((a, b) => a.node.createdAt.getTime() - b.node.createdAt.getTime());
130
+
131
+ return roots;
132
+ }
133
+
134
+ /**
135
+ * Flatten tree into display order with depth info for rendering.
136
+ */
137
+ interface FlattenedNode {
138
+ node: TaskChainNode;
139
+ depth: number;
140
+ isLast: boolean;
141
+ parentIsLast: boolean[];
142
+ }
143
+
144
+ function flattenTree(
145
+ roots: TreeNode[],
146
+ depth: number = 0,
147
+ parentIsLast: boolean[] = []
148
+ ): FlattenedNode[] {
149
+ const result: FlattenedNode[] = [];
150
+
151
+ for (let i = 0; i < roots.length; i++) {
152
+ const treeNode = roots[i];
153
+ if (!treeNode) continue;
154
+ const isLast = i === roots.length - 1;
155
+
156
+ result.push({
157
+ node: treeNode.node,
158
+ depth,
159
+ isLast,
160
+ parentIsLast: [...parentIsLast],
161
+ });
162
+
163
+ if (treeNode.children.length > 0) {
164
+ result.push(...flattenTree(treeNode.children, depth + 1, [...parentIsLast, isLast]));
165
+ }
166
+ }
167
+
168
+ return result;
169
+ }
170
+
171
+ /**
172
+ * Calculate progress statistics from the task chain.
173
+ */
174
+ interface ProgressStats {
175
+ total: number;
176
+ completed: number;
177
+ failed: number;
178
+ running: number;
179
+ pending: number;
180
+ percentage: number;
181
+ }
182
+
183
+ function calculateProgress(chain: TaskChain): ProgressStats {
184
+ const stats: ProgressStats = {
185
+ total: chain.nodes.size,
186
+ completed: 0,
187
+ failed: 0,
188
+ running: 0,
189
+ pending: 0,
190
+ percentage: 0,
191
+ };
192
+
193
+ for (const node of chain.nodes.values()) {
194
+ switch (node.status) {
195
+ case "completed":
196
+ stats.completed++;
197
+ break;
198
+ case "failed":
199
+ stats.failed++;
200
+ break;
201
+ case "running":
202
+ stats.running++;
203
+ break;
204
+ case "pending":
205
+ stats.pending++;
206
+ break;
207
+ }
208
+ }
209
+
210
+ if (stats.total > 0) {
211
+ stats.percentage = Math.round((stats.completed / stats.total) * 100);
212
+ }
213
+
214
+ return stats;
215
+ }
216
+
217
+ /**
218
+ * Generate ASCII progress bar.
219
+ */
220
+ function generateProgressBar(percentage: number, width: number): string {
221
+ const filledCount = Math.round((percentage / 100) * width);
222
+ const emptyCount = width - filledCount;
223
+
224
+ return PROGRESS_FILLED.repeat(filledCount) + PROGRESS_EMPTY.repeat(emptyCount);
225
+ }
226
+
227
+ /**
228
+ * Format task ID for display (truncate if needed).
229
+ */
230
+ function formatTaskId(taskId: string, maxLength: number = 20): string {
231
+ if (taskId.length <= maxLength) {
232
+ return taskId;
233
+ }
234
+ return `${taskId.substring(0, maxLength - 3)}...`;
235
+ }
236
+
237
+ // =============================================================================
238
+ // Sub-Components
239
+ // =============================================================================
240
+
241
+ interface TaskTreeItemProps {
242
+ readonly flatNode: FlattenedNode;
243
+ readonly isCurrent: boolean;
244
+ readonly showDetails: boolean;
245
+ }
246
+
247
+ /**
248
+ * Single task item in the tree view.
249
+ */
250
+ function TaskTreeItem({ flatNode, isCurrent, showDetails }: TaskTreeItemProps): React.JSX.Element {
251
+ const { theme } = useTheme();
252
+ const { node, depth, isLast, parentIsLast } = flatNode;
253
+
254
+ const statusIcon = STATUS_ICONS[node.status];
255
+ const statusColor = getStatusColor(node.status, isCurrent, theme);
256
+
257
+ // Build prefix for tree structure
258
+ let prefix = "";
259
+ for (let i = 0; i < depth; i++) {
260
+ const parentLast = parentIsLast[i];
261
+ prefix += parentLast ? TREE_CHARS.space : TREE_CHARS.vertical;
262
+ }
263
+
264
+ // Add branch character for non-root nodes
265
+ if (depth > 0) {
266
+ prefix = prefix.slice(0, -3) + (isLast ? TREE_CHARS.last : TREE_CHARS.branch);
267
+ }
268
+
269
+ // Format task display
270
+ const taskDisplay = showDetails
271
+ ? `${formatTaskId(node.taskId)} [${node.agentSlug}]`
272
+ : formatTaskId(node.taskId);
273
+
274
+ const currentMarker = isCurrent ? " (current)" : "";
275
+
276
+ return (
277
+ <Box>
278
+ <Text color={theme.semantic.text.muted}>{prefix}</Text>
279
+ <Text color={statusColor}>{statusIcon}</Text>
280
+ <Text> </Text>
281
+ <Text color={isCurrent ? theme.semantic.status.running : theme.semantic.text.primary}>
282
+ {taskDisplay}
283
+ </Text>
284
+ {isCurrent && (
285
+ <Text color={theme.semantic.status.running} italic>
286
+ {currentMarker}
287
+ </Text>
288
+ )}
289
+ </Box>
290
+ );
291
+ }
292
+
293
+ interface ProgressBarDisplayProps {
294
+ readonly stats: ProgressStats;
295
+ readonly width: number;
296
+ }
297
+
298
+ /**
299
+ * Progress bar with completion percentage.
300
+ */
301
+ function ProgressBarDisplay({ stats, width }: ProgressBarDisplayProps): React.JSX.Element {
302
+ const { theme } = useTheme();
303
+
304
+ const progressBar = generateProgressBar(stats.percentage, width);
305
+
306
+ // Determine progress bar color based on completion
307
+ let barColor = theme.semantic.status.running;
308
+ if (stats.failed > 0) {
309
+ barColor = theme.semantic.status.error;
310
+ } else if (stats.percentage === 100) {
311
+ barColor = theme.semantic.status.complete;
312
+ }
313
+
314
+ return (
315
+ <Box marginTop={1}>
316
+ <Text color={theme.semantic.text.muted}>Progress: </Text>
317
+ <Text color={theme.semantic.text.primary}>
318
+ {stats.completed}/{stats.total}
319
+ </Text>
320
+ <Text color={theme.semantic.text.muted}> ({stats.percentage}%) </Text>
321
+ <Text color={barColor}>{progressBar}</Text>
322
+ </Box>
323
+ );
324
+ }
325
+
326
+ // =============================================================================
327
+ // Main Component
328
+ // =============================================================================
329
+
330
+ /**
331
+ * AgentProgress displays a visual task tree with progress tracking.
332
+ *
333
+ * Features:
334
+ * - Hierarchical task tree visualization
335
+ * - Status icons (○ pending, ◐ running, ● completed, ✗ failed)
336
+ * - ASCII progress bar with percentage
337
+ * - Current task highlighting
338
+ * - Optional detailed view with agent slugs
339
+ *
340
+ * @example
341
+ * ```tsx
342
+ * // Basic usage
343
+ * <AgentProgress chain={taskChain} />
344
+ *
345
+ * // With current task highlighting
346
+ * <AgentProgress
347
+ * chain={taskChain}
348
+ * currentTaskId="task-123"
349
+ * />
350
+ *
351
+ * // With details and custom progress bar width
352
+ * <AgentProgress
353
+ * chain={taskChain}
354
+ * currentTaskId="task-123"
355
+ * showDetails={true}
356
+ * progressBarWidth={30}
357
+ * />
358
+ * ```
359
+ *
360
+ * Display format:
361
+ * ```
362
+ * Task Chain: abc123
363
+ * ├── ● T001 - Create schema
364
+ * ├── ● T002 - Implement handler
365
+ * ├── ◐ T003 - Write tests (current)
366
+ * ├── ○ T004 - Documentation
367
+ * └── ○ T005 - Integration
368
+ *
369
+ * Progress: 2/5 (40%) ████████░░░░░░░░░░░░
370
+ * ```
371
+ */
372
+ export function AgentProgress({
373
+ chain,
374
+ currentTaskId,
375
+ showDetails = false,
376
+ progressBarWidth = DEFAULT_PROGRESS_BAR_WIDTH,
377
+ }: AgentProgressProps): React.JSX.Element {
378
+ const { theme } = useTheme();
379
+
380
+ // Build tree structure and flatten for rendering
381
+ const flattenedNodes = useMemo(() => {
382
+ const tree = buildTree(chain);
383
+ return flattenTree(tree);
384
+ }, [chain]);
385
+
386
+ // Calculate progress statistics
387
+ const stats = useMemo(() => calculateProgress(chain), [chain]);
388
+
389
+ // Empty state
390
+ if (chain.nodes.size === 0) {
391
+ return (
392
+ <Box flexDirection="column">
393
+ <Text color={theme.semantic.text.muted}>No tasks in chain</Text>
394
+ </Box>
395
+ );
396
+ }
397
+
398
+ return (
399
+ <Box flexDirection="column">
400
+ {/* Chain header */}
401
+ <Box marginBottom={1}>
402
+ <Text color={theme.semantic.text.muted}>Task Chain: </Text>
403
+ <Text color={theme.semantic.text.primary} bold>
404
+ {formatTaskId(chain.chainId, 30)}
405
+ </Text>
406
+ </Box>
407
+
408
+ {/* Task tree */}
409
+ <Box flexDirection="column">
410
+ {flattenedNodes.map((flatNode) => (
411
+ <TaskTreeItem
412
+ key={flatNode.node.taskId}
413
+ flatNode={flatNode}
414
+ isCurrent={flatNode.node.taskId === currentTaskId}
415
+ showDetails={showDetails}
416
+ />
417
+ ))}
418
+ </Box>
419
+
420
+ {/* Progress bar */}
421
+ <ProgressBarDisplay stats={stats} width={progressBarWidth} />
422
+ </Box>
423
+ );
424
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * ASCII Art Definitions for Vellum Banner
3
+ *
4
+ * Block-style ASCII art with ancient parchment/scroll styling.
5
+ * Multiple sizes for responsive terminal width adaptation.
6
+ *
7
+ * @module tui/components/Banner/AsciiArt
8
+ */
9
+
10
+ // =============================================================================
11
+ // Large ASCII Art (80+ columns)
12
+ // =============================================================================
13
+
14
+ /**
15
+ * Large VELLUM ASCII Art with scroll decorations.
16
+ * Designed for terminals 80+ columns wide.
17
+ * Features: scroll rod decorations (◊), worn edges (·~·), parchment corners (╔╗╚╝)
18
+ */
19
+ export const VELLUM_LARGE = trimAscii(`
20
+ ════◊╠═══════════════════════════════════════════════════════════════════════╣◊════
21
+ ╔ ┌───────────────────────────────────────────────────────────────────┐ ╗
22
+ ║ │ │ ║
23
+ ║ │ ██╗ ██╗ ███████╗ ██╗ ██╗ ██╗ ██╗ ███╗ ███╗ │ ║
24
+ ║ │ ██║ ██║ ██╔════╝ ██║ ██║ ██║ ██║ ████╗ ████║ │ ║
25
+ ║ │ ██║ ██║ █████╗ ██║ ██║ ██║ ██║ ██╔████╔██║ │ ║
26
+ ║ │ ╚██╗ ██╔╝ ██╔══╝ ██║ ██║ ██║ ██║ ██║╚██╔╝██║ │ ║
27
+ ║ │ ╚████╔╝ ███████╗ ███████╗ ███████╗ ╚██████╔╝ ██║ ╚═╝ ██║ │ ║
28
+ ║ │ ╚═══╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝ │ ║
29
+ ║ │ │ ║
30
+ ║ │ ·~· Ancient Wisdom for Modern Code ·~· │ ║
31
+ ║ │ │ ║
32
+ ╚ └───────────────────────────────────────────────────────────────────┘ ╝
33
+ ════◊╠═══════════════════════════════════════════════════════════════════════╣◊════
34
+ `);
35
+
36
+ // =============================================================================
37
+ // Medium ASCII Art (60-79 columns)
38
+ // =============================================================================
39
+
40
+ /**
41
+ * Medium VELLUM ASCII Art.
42
+ * Designed for terminals 60-79 columns wide.
43
+ * Features: scroll rod decorations (◊), worn edges (·~·)
44
+ * Note: Uses ASCII-compatible characters for consistent terminal alignment.
45
+ */
46
+ export const VELLUM_MEDIUM = trimAscii(`
47
+ ══╣ ◊ ╠═══════════════════════════════════════════╣ ◊ ╠══
48
+ ║ ┌─────────────────────────────────────────────────┐ ║
49
+ ║ │ │ ║
50
+ ║ │ ██╗ ██╗ ███████╗ ██╗ ██╗ ██╗ ██╗ │ ║
51
+ ║ │ ██║ ██║ ██╔════╝ ██║ ██║ ██║ ██║ │ ║
52
+ ║ │ ██║ ██║ █████╗ ██║ ██║ ██║ ██║ │ ║
53
+ ║ │ ╚██╗ ██╔╝ ██╔══╝ ██║ ██║ ██║ ██║ │ ║
54
+ ║ │ ╚████╔╝ ███████╗ ███████╗███████╗╚██████╔╝ │ ║
55
+ ║ │ ╚═══╝ ╚══════╝ ╚══════╝╚══════╝ ╚═════╝ │ ║
56
+ ║ │ │ ║
57
+ ║ │ ·~· Ancient Wisdom for Modern Code ·~· │ ║
58
+ ║ └─────────────────────────────────────────────────┘ ║
59
+ ══╣ ◊ ╠═══════════════════════════════════════════╣ ◊ ╠══
60
+ `);
61
+
62
+ // =============================================================================
63
+ // Small ASCII Art (40-59 columns)
64
+ // =============================================================================
65
+
66
+ /**
67
+ * Small VELLUM ASCII Art.
68
+ * Designed for terminals 40-59 columns wide.
69
+ * Features: worn edges (·~·)
70
+ */
71
+ export const VELLUM_SMALL = trimAscii(`
72
+ ╭──◊────────────────────────────◊──╮
73
+ │ ╔══════════════════════════════╗ │
74
+ │ ║ ╦ ╦╔═╗╦ ╦ ╦ ╦╔╦╗ ║ │
75
+ │ ║ ╚╗╔╝║╣ ║ ║ ║ ║║║║ ║ │
76
+ │ ║ ╚╝ ╚═╝╩═╝╩═╝╚═╝╩ ╩ ║ │
77
+ │ ║ ║ │
78
+ │ ║ ·~· Ancient Wisdom ·~· ║ │
79
+ │ ╚══════════════════════════════╝ │
80
+ ╰──◊────────────────────────────◊──╯
81
+ `);
82
+
83
+ // =============================================================================
84
+ // Minimal ASCII Art (<40 columns)
85
+ // =============================================================================
86
+
87
+ /**
88
+ * Minimal VELLUM display.
89
+ * For very narrow terminals.
90
+ * Features: subtle scroll rod decoration (◊)
91
+ */
92
+ export const VELLUM_MINIMAL = trimAscii(`
93
+ ╭───◊──────────◊───╮
94
+ │ ╔══════════════╗ │
95
+ │ ║ V E L L U M ║ │
96
+ │ ╚══════════════╝ │
97
+ ╰───◊──────────◊───╯
98
+ `);
99
+
100
+ // =============================================================================
101
+ // Scroll Decorations
102
+ // =============================================================================
103
+
104
+ /**
105
+ * Top scroll decoration for parchment effect.
106
+ */
107
+ export const SCROLL_TOP = trimAscii(`
108
+ ════╣ ◊ ╠═══════════════════════════════════════════════════════════╣ ◊ ╠════
109
+ ╔═══════════════════════════════════════════════════════════════════════════╗
110
+ `);
111
+
112
+ /**
113
+ * Bottom scroll decoration for parchment effect.
114
+ */
115
+ export const SCROLL_BOTTOM = trimAscii(`
116
+ ╚═══════════════════════════════════════════════════════════════════════════╝
117
+ ════╣ ◊ ╠═══════════════════════════════════════════════════════════╣ ◊ ╠════
118
+ `);
119
+
120
+ // =============================================================================
121
+ // Utility Functions
122
+ // =============================================================================
123
+
124
+ /**
125
+ * Select appropriate ASCII art size based on terminal width.
126
+ *
127
+ * @param terminalWidth - Current terminal width in columns
128
+ * @returns Appropriate ASCII art string
129
+ */
130
+ export function selectAsciiArt(terminalWidth: number): string {
131
+ if (terminalWidth >= 80) {
132
+ return VELLUM_LARGE;
133
+ } else if (terminalWidth >= 60) {
134
+ return VELLUM_MEDIUM;
135
+ } else if (terminalWidth >= 40) {
136
+ return VELLUM_SMALL;
137
+ } else {
138
+ return VELLUM_MINIMAL;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Get all ASCII art variants.
144
+ */
145
+ export const ASCII_VARIANTS = {
146
+ large: VELLUM_LARGE,
147
+ medium: VELLUM_MEDIUM,
148
+ small: VELLUM_SMALL,
149
+ minimal: VELLUM_MINIMAL,
150
+ } as const;
151
+
152
+ export type AsciiVariant = keyof typeof ASCII_VARIANTS;
153
+
154
+ // =============================================================================
155
+ // Internal Helpers
156
+ // =============================================================================
157
+
158
+ function trimAscii(value: string): string {
159
+ return value.replace(/^\n/, "").trimEnd();
160
+ }