@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,449 @@
1
+ /**
2
+ * Resume Command Tests
3
+ *
4
+ * Tests for the session resume command including:
5
+ * - Resume by full ID
6
+ * - Resume by short ID
7
+ * - Resume most recent (--last flag)
8
+ * - Session not found handling
9
+ * - No sessions available
10
+ *
11
+ * @module cli/commands/__tests__/resume
12
+ */
13
+
14
+ import type { Session, SessionListService, SessionMetadata, StorageManager } from "@vellum/core";
15
+ import { describe, expect, it, vi } from "vitest";
16
+ import {
17
+ createResumeCommand,
18
+ findSessionById,
19
+ formatSessionChoice,
20
+ getMostRecentSession,
21
+ groupSessionsByDirectory,
22
+ type SessionLookupOptions,
23
+ SHORT_ID_LENGTH,
24
+ } from "../session/resume.js";
25
+ import type { CommandContext, CommandError, CommandPending, ParsedArgs } from "../types.js";
26
+
27
+ // =============================================================================
28
+ // Test Fixtures
29
+ // =============================================================================
30
+
31
+ /**
32
+ * Create mock session metadata for testing.
33
+ */
34
+ function createMockMetadata(overrides: Partial<SessionMetadata> = {}): SessionMetadata {
35
+ const now = new Date();
36
+ return {
37
+ id: `test-${crypto.randomUUID()}`,
38
+ title: "Test Session",
39
+ createdAt: now,
40
+ updatedAt: now,
41
+ lastActive: now,
42
+ status: "active",
43
+ mode: "chat",
44
+ tags: [],
45
+ workingDirectory: "/test/path",
46
+ tokenCount: 100,
47
+ messageCount: 5,
48
+ summary: "Test session summary",
49
+ ...overrides,
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Create a mock session for testing.
55
+ */
56
+ function createMockSession(metadata: SessionMetadata): Session {
57
+ return {
58
+ metadata,
59
+ messages: [],
60
+ config: {},
61
+ } as unknown as Session;
62
+ }
63
+
64
+ /**
65
+ * Create a mock storage manager for testing.
66
+ */
67
+ function createMockStorage(sessions: SessionMetadata[]): StorageManager {
68
+ const index = new Map<string, SessionMetadata>();
69
+ for (const session of sessions) {
70
+ index.set(session.id, session);
71
+ }
72
+
73
+ return {
74
+ getIndex: vi.fn().mockResolvedValue(new Map(index)),
75
+ load: vi.fn().mockImplementation(async (id: string) => {
76
+ const metadata = index.get(id);
77
+ if (!metadata) {
78
+ throw new Error(`Session not found: ${id}`);
79
+ }
80
+ return createMockSession(metadata);
81
+ }),
82
+ } as unknown as StorageManager;
83
+ }
84
+
85
+ /**
86
+ * Create a mock list service for testing.
87
+ */
88
+ function createMockListService(sessions: SessionMetadata[]): SessionListService {
89
+ return {
90
+ getRecentSessions: vi
91
+ .fn()
92
+ .mockResolvedValue(
93
+ [...sessions].sort((a, b) => b.lastActive.getTime() - a.lastActive.getTime())
94
+ ),
95
+ } as unknown as SessionListService;
96
+ }
97
+
98
+ /**
99
+ * Create a mock CommandContext for testing.
100
+ */
101
+ function createMockContext(overrides: Partial<ParsedArgs> = {}): CommandContext {
102
+ return {
103
+ session: {
104
+ id: "test-session",
105
+ provider: "anthropic",
106
+ cwd: "/test",
107
+ },
108
+ credentials: {
109
+ resolve: vi.fn(),
110
+ store: vi.fn(),
111
+ delete: vi.fn(),
112
+ list: vi.fn(),
113
+ } as unknown as CommandContext["credentials"],
114
+ toolRegistry: {
115
+ get: vi.fn(),
116
+ list: vi.fn(),
117
+ } as unknown as CommandContext["toolRegistry"],
118
+ parsedArgs: {
119
+ command: overrides.command ?? "resume",
120
+ positional: overrides.positional ?? [],
121
+ named: overrides.named ?? {},
122
+ raw: overrides.raw ?? "/resume",
123
+ },
124
+ emit: vi.fn(),
125
+ };
126
+ }
127
+
128
+ // =============================================================================
129
+ // findSessionById Tests
130
+ // =============================================================================
131
+
132
+ describe("findSessionById", () => {
133
+ it("should find session by exact full ID", async () => {
134
+ const sessionId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
135
+ const metadata = createMockMetadata({ id: sessionId, title: "Full ID Session" });
136
+ const storage = createMockStorage([metadata]);
137
+ const listService = createMockListService([metadata]);
138
+
139
+ const options: SessionLookupOptions = { storage, listService };
140
+ const result = await findSessionById(sessionId, options);
141
+
142
+ expect(result.ok).toBe(true);
143
+ expect(result.session).toBeDefined();
144
+ expect(result.session?.metadata.title).toBe("Full ID Session");
145
+ });
146
+
147
+ it("should find session by short ID (first 8 chars)", async () => {
148
+ const sessionId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
149
+ const shortId = sessionId.slice(0, SHORT_ID_LENGTH);
150
+ const metadata = createMockMetadata({ id: sessionId, title: "Short ID Session" });
151
+ const storage = createMockStorage([metadata]);
152
+ const listService = createMockListService([metadata]);
153
+
154
+ const options: SessionLookupOptions = { storage, listService };
155
+ const result = await findSessionById(shortId, options);
156
+
157
+ expect(result.ok).toBe(true);
158
+ expect(result.session).toBeDefined();
159
+ expect(result.session?.metadata.title).toBe("Short ID Session");
160
+ });
161
+
162
+ it("should return error when session not found", async () => {
163
+ const metadata = createMockMetadata({ id: "existing-id-123" });
164
+ const storage = createMockStorage([metadata]);
165
+ const listService = createMockListService([metadata]);
166
+
167
+ const options: SessionLookupOptions = { storage, listService };
168
+ const result = await findSessionById("nonexistent", options);
169
+
170
+ expect(result.ok).toBe(false);
171
+ expect(result.error).toContain("未找到会话");
172
+ });
173
+
174
+ it("should return error when multiple sessions match short ID", async () => {
175
+ const sessions = [
176
+ createMockMetadata({ id: "abc12345-1111-0000-0000-000000000001", title: "Session 1" }),
177
+ createMockMetadata({ id: "abc12345-2222-0000-0000-000000000002", title: "Session 2" }),
178
+ ];
179
+ const storage = createMockStorage(sessions);
180
+ const listService = createMockListService(sessions);
181
+
182
+ const options: SessionLookupOptions = { storage, listService };
183
+ const result = await findSessionById("abc12345", options);
184
+
185
+ expect(result.ok).toBe(false);
186
+ expect(result.error).toContain("多个会话匹配");
187
+ });
188
+
189
+ it("should match short ID case-insensitively", async () => {
190
+ const sessionId = "ABC12345-e5f6-7890-abcd-ef1234567890";
191
+ const metadata = createMockMetadata({ id: sessionId, title: "Case Test" });
192
+ const storage = createMockStorage([metadata]);
193
+ const listService = createMockListService([metadata]);
194
+
195
+ const options: SessionLookupOptions = { storage, listService };
196
+ const result = await findSessionById("abc12345", options);
197
+
198
+ expect(result.ok).toBe(true);
199
+ expect(result.session?.metadata.title).toBe("Case Test");
200
+ });
201
+ });
202
+
203
+ // =============================================================================
204
+ // getMostRecentSession Tests
205
+ // =============================================================================
206
+
207
+ describe("getMostRecentSession", () => {
208
+ it("should return the most recent session", async () => {
209
+ const now = new Date();
210
+ const earlier = new Date(now.getTime() - 3600000);
211
+ const sessions = [
212
+ createMockMetadata({ id: "old-session", lastActive: earlier, title: "Old Session" }),
213
+ createMockMetadata({ id: "new-session", lastActive: now, title: "New Session" }),
214
+ ];
215
+ const storage = createMockStorage(sessions);
216
+ const listService = createMockListService(sessions);
217
+
218
+ const options: SessionLookupOptions = { storage, listService };
219
+ const result = await getMostRecentSession(options);
220
+
221
+ expect(result.ok).toBe(true);
222
+ expect(result.session?.metadata.title).toBe("New Session");
223
+ });
224
+
225
+ it("should return error when no sessions available", async () => {
226
+ const storage = createMockStorage([]);
227
+ const listService = createMockListService([]);
228
+
229
+ const options: SessionLookupOptions = { storage, listService };
230
+ const result = await getMostRecentSession(options);
231
+
232
+ expect(result.ok).toBe(false);
233
+ expect(result.error).toContain("没有可恢复的会话");
234
+ });
235
+ });
236
+
237
+ // =============================================================================
238
+ // createResumeCommand Tests
239
+ // =============================================================================
240
+
241
+ describe("createResumeCommand", () => {
242
+ it("should create a valid slash command", () => {
243
+ const storage = createMockStorage([]);
244
+ const listService = createMockListService([]);
245
+
246
+ const command = createResumeCommand(storage, listService);
247
+
248
+ expect(command.name).toBe("resume");
249
+ expect(command.aliases).toContain("r");
250
+ expect(command.aliases).toContain("restore");
251
+ expect(command.category).toBe("session");
252
+ });
253
+
254
+ describe("execute with --last flag", () => {
255
+ it("should resume most recent session when --last flag is set", async () => {
256
+ const metadata = createMockMetadata({ title: "Last Session" });
257
+ const storage = createMockStorage([metadata]);
258
+ const listService = createMockListService([metadata]);
259
+ const command = createResumeCommand(storage, listService);
260
+
261
+ const ctx = createMockContext({
262
+ positional: [],
263
+ named: { last: true },
264
+ });
265
+
266
+ const result = await command.execute(ctx);
267
+
268
+ expect(result.kind).toBe("pending");
269
+ const pendingResult = result as CommandPending;
270
+
271
+ // Wait for the async promise to resolve
272
+ const finalResult = await pendingResult.operation.promise;
273
+ expect(finalResult.kind).toBe("success");
274
+ });
275
+
276
+ it("should return error when no sessions and --last flag", async () => {
277
+ const storage = createMockStorage([]);
278
+ const listService = createMockListService([]);
279
+ const command = createResumeCommand(storage, listService);
280
+
281
+ const ctx = createMockContext({
282
+ positional: [],
283
+ named: { last: true },
284
+ });
285
+
286
+ const result = await command.execute(ctx);
287
+ expect(result.kind).toBe("pending");
288
+
289
+ const pendingResult = result as CommandPending;
290
+ const finalResult = await pendingResult.operation.promise;
291
+
292
+ expect(finalResult.kind).toBe("error");
293
+ const errorResult = finalResult as CommandError;
294
+ expect(errorResult.message).toContain("没有可恢复的会话");
295
+ });
296
+ });
297
+
298
+ describe("execute with session ID", () => {
299
+ it("should resume session by full ID", async () => {
300
+ const sessionId = "test-full-id-12345";
301
+ const metadata = createMockMetadata({ id: sessionId, title: "Full ID Test" });
302
+ const storage = createMockStorage([metadata]);
303
+ const listService = createMockListService([metadata]);
304
+ const command = createResumeCommand(storage, listService);
305
+
306
+ const ctx = createMockContext({
307
+ positional: [sessionId],
308
+ named: {},
309
+ });
310
+
311
+ const result = await command.execute(ctx);
312
+ expect(result.kind).toBe("pending");
313
+
314
+ const pendingResult = result as CommandPending;
315
+ const finalResult = await pendingResult.operation.promise;
316
+
317
+ expect(finalResult.kind).toBe("success");
318
+ });
319
+
320
+ it("should resume session by short ID", async () => {
321
+ const sessionId = "short123-full-uuid-here";
322
+ const metadata = createMockMetadata({ id: sessionId, title: "Short ID Test" });
323
+ const storage = createMockStorage([metadata]);
324
+ const listService = createMockListService([metadata]);
325
+ const command = createResumeCommand(storage, listService);
326
+
327
+ const ctx = createMockContext({
328
+ positional: ["short123"],
329
+ named: {},
330
+ });
331
+
332
+ const result = await command.execute(ctx);
333
+ expect(result.kind).toBe("pending");
334
+
335
+ const pendingResult = result as CommandPending;
336
+ const finalResult = await pendingResult.operation.promise;
337
+
338
+ expect(finalResult.kind).toBe("success");
339
+ });
340
+
341
+ it("should return error when session not found", async () => {
342
+ const storage = createMockStorage([]);
343
+ const listService = createMockListService([]);
344
+ const command = createResumeCommand(storage, listService);
345
+
346
+ const ctx = createMockContext({
347
+ positional: ["nonexistent"],
348
+ named: {},
349
+ });
350
+
351
+ const result = await command.execute(ctx);
352
+ expect(result.kind).toBe("pending");
353
+
354
+ const pendingResult = result as CommandPending;
355
+ const finalResult = await pendingResult.operation.promise;
356
+
357
+ expect(finalResult.kind).toBe("error");
358
+ });
359
+ });
360
+
361
+ describe("argument validation", () => {
362
+ it("should return error when both session ID and --last are provided", async () => {
363
+ const storage = createMockStorage([]);
364
+ const listService = createMockListService([]);
365
+ const command = createResumeCommand(storage, listService);
366
+
367
+ const ctx = createMockContext({
368
+ positional: ["some-id"],
369
+ named: { last: true },
370
+ });
371
+
372
+ const result = await command.execute(ctx);
373
+
374
+ expect(result.kind).toBe("error");
375
+ const errorResult = result as CommandError;
376
+ expect(errorResult.message).toContain("不能同时指定");
377
+ });
378
+ });
379
+
380
+ describe("event emission", () => {
381
+ it("should emit session:resume event on successful resume", async () => {
382
+ const metadata = createMockMetadata({ title: "Event Test" });
383
+ const storage = createMockStorage([metadata]);
384
+ const listService = createMockListService([metadata]);
385
+ const command = createResumeCommand(storage, listService);
386
+
387
+ const ctx = createMockContext({
388
+ positional: [],
389
+ named: { last: true },
390
+ });
391
+
392
+ const result = await command.execute(ctx);
393
+ const pendingResult = result as CommandPending;
394
+ await pendingResult.operation.promise;
395
+
396
+ expect(ctx.emit).toHaveBeenCalledWith(
397
+ "session:resume",
398
+ expect.objectContaining({
399
+ session: expect.any(Object),
400
+ usedLastFlag: true,
401
+ })
402
+ );
403
+ });
404
+ });
405
+ });
406
+
407
+ // =============================================================================
408
+ // Utility Function Tests
409
+ // =============================================================================
410
+
411
+ describe("formatSessionChoice", () => {
412
+ it("should format session choice correctly", () => {
413
+ const date = new Date("2025-12-30T14:30:00");
414
+ const metadata = createMockMetadata({
415
+ title: "Test Session",
416
+ lastActive: date,
417
+ messageCount: 15,
418
+ });
419
+
420
+ const formatted = formatSessionChoice(metadata, 1);
421
+
422
+ expect(formatted).toContain("1.");
423
+ expect(formatted).toContain("Test Session");
424
+ expect(formatted).toContain("12/30");
425
+ expect(formatted).toContain("14:30");
426
+ expect(formatted).toContain("15条消息");
427
+ });
428
+ });
429
+
430
+ describe("groupSessionsByDirectory", () => {
431
+ it("should group sessions by working directory", () => {
432
+ const sessions = [
433
+ createMockMetadata({ workingDirectory: "/path/a", title: "Session A1" }),
434
+ createMockMetadata({ workingDirectory: "/path/b", title: "Session B1" }),
435
+ createMockMetadata({ workingDirectory: "/path/a", title: "Session A2" }),
436
+ ];
437
+
438
+ const grouped = groupSessionsByDirectory(sessions);
439
+
440
+ expect(grouped.size).toBe(2);
441
+ expect(grouped.get("/path/a")).toHaveLength(2);
442
+ expect(grouped.get("/path/b")).toHaveLength(1);
443
+ });
444
+
445
+ it("should return empty map for empty input", () => {
446
+ const grouped = groupSessionsByDirectory([]);
447
+ expect(grouped.size).toBe(0);
448
+ });
449
+ });