@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,558 @@
1
+ /**
2
+ * Spec Command
3
+ *
4
+ * CLI command for managing spec workflows with subcommands:
5
+ * - start: Begin a new spec workflow
6
+ * - continue: Resume from checkpoint
7
+ * - status: Show workflow status
8
+ *
9
+ * Usage:
10
+ * - `vellum spec "feature description"` - Start new spec
11
+ * - `vellum spec --continue` - Resume from latest checkpoint
12
+ * - `vellum spec --status` - Show current status
13
+ * - `vellum spec --from=design` - Start from specific phase
14
+ *
15
+ * @module cli/commands/spec
16
+ */
17
+
18
+ import { existsSync, mkdirSync } from "node:fs";
19
+ import { join, resolve } from "node:path";
20
+ import {
21
+ type PhaseResult,
22
+ SPEC_PHASES,
23
+ type SpecPhase,
24
+ SpecWorkflowEngine,
25
+ type SpecWorkflowEngineConfig,
26
+ type SpecWorkflowStatus,
27
+ type WorkflowResult,
28
+ } from "@vellum/core";
29
+ import chalk from "chalk";
30
+
31
+ import { ICONS } from "../utils/icons.js";
32
+ import { EXIT_CODES } from "./exit-codes.js";
33
+ import type { CommandContext, CommandResult, SlashCommand } from "./types.js";
34
+ import { error, pending, success } from "./types.js";
35
+
36
+ // =============================================================================
37
+ // Types
38
+ // =============================================================================
39
+
40
+ /**
41
+ * Spec command options
42
+ */
43
+ export interface SpecOptions {
44
+ /** Feature description for new spec */
45
+ description?: string;
46
+ /** Continue from checkpoint */
47
+ continue?: boolean;
48
+ /** Show status only */
49
+ status?: boolean;
50
+ /** Start from specific phase */
51
+ from?: SpecPhase;
52
+ /** Phases to skip */
53
+ skip?: SpecPhase[];
54
+ /** Spec directory (defaults to .ouroboros/specs/<name>) */
55
+ specDir?: string;
56
+ }
57
+
58
+ /**
59
+ * Spec command result
60
+ */
61
+ export interface SpecResult {
62
+ /** Whether operation succeeded */
63
+ success: boolean;
64
+ /** Spec directory path */
65
+ specDir?: string;
66
+ /** Workflow result (if executed) */
67
+ workflowResult?: WorkflowResult;
68
+ /** Status information (if requested) */
69
+ status?: SpecWorkflowStatus;
70
+ /** Error message if failed */
71
+ error?: string;
72
+ /** Exit code */
73
+ exitCode: number;
74
+ }
75
+
76
+ // =============================================================================
77
+ // Constants
78
+ // =============================================================================
79
+
80
+ /**
81
+ * Default spec output directory
82
+ */
83
+ const DEFAULT_SPEC_BASE = ".ouroboros/specs";
84
+
85
+ /**
86
+ * Phase display names for formatting
87
+ */
88
+ const PHASE_DISPLAY_NAMES: Record<SpecPhase, string> = {
89
+ research: `${ICONS.phase.research} Research`,
90
+ requirements: `${ICONS.phase.requirements} Requirements`,
91
+ design: `${ICONS.phase.design} Design`,
92
+ tasks: `${ICONS.phase.tasks} Tasks`,
93
+ implementation: `${ICONS.phase.implementation} Implementation`,
94
+ validation: `${ICONS.phase.validation} Validation`,
95
+ };
96
+
97
+ // =============================================================================
98
+ // Helpers
99
+ // =============================================================================
100
+
101
+ /**
102
+ * Generate a slug from a description
103
+ */
104
+ function generateSlug(description: string): string {
105
+ return description
106
+ .toLowerCase()
107
+ .replace(/[^a-z0-9\s-]/g, "")
108
+ .replace(/\s+/g, "-")
109
+ .replace(/-+/g, "-")
110
+ .substring(0, 50)
111
+ .replace(/-$/, "");
112
+ }
113
+
114
+ /**
115
+ * Ensure spec directory exists
116
+ */
117
+ function ensureSpecDir(specDir: string): void {
118
+ if (!existsSync(specDir)) {
119
+ mkdirSync(specDir, { recursive: true });
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Format phase status for display
125
+ */
126
+ function formatPhaseStatus(phase: SpecPhase, status: string): string {
127
+ const indicator =
128
+ status === "completed"
129
+ ? ICONS.success
130
+ : status === "running"
131
+ ? ICONS.running
132
+ : status === "failed"
133
+ ? ICONS.error
134
+ : status === "skipped"
135
+ ? ICONS.skip
136
+ : ICONS.pending;
137
+
138
+ return `${indicator} ${PHASE_DISPLAY_NAMES[phase]}: ${status}`;
139
+ }
140
+
141
+ /**
142
+ * Format workflow status for display
143
+ */
144
+ function formatStatus(status: SpecWorkflowStatus): string {
145
+ const lines: string[] = [];
146
+ const currentPhase = status.state.currentPhase as SpecPhase;
147
+
148
+ lines.push(chalk.bold.blue(`${ICONS.mode.spec} Spec Workflow Status`));
149
+ lines.push("");
150
+ lines.push(`Workflow: ${status.state.name}`);
151
+ lines.push(
152
+ `Progress: ${status.progress.completed}/${status.progress.total} (${status.progress.percentage}%)`
153
+ );
154
+ lines.push(`Current Phase: ${PHASE_DISPLAY_NAMES[currentPhase]}`);
155
+ lines.push("");
156
+ lines.push(chalk.bold("Phase Status:"));
157
+
158
+ for (const phase of SPEC_PHASES) {
159
+ const phaseState = status.state.phases[phase];
160
+ const phaseStatus = phaseState?.status ?? "pending";
161
+ lines.push(` ${formatPhaseStatus(phase, phaseStatus)}`);
162
+ }
163
+
164
+ return lines.join("\n");
165
+ }
166
+
167
+ /**
168
+ * Format workflow result for display
169
+ */
170
+ function formatResult(result: WorkflowResult): string {
171
+ const lines: string[] = [];
172
+
173
+ if (result.success) {
174
+ lines.push(chalk.green.bold(`${ICONS.success} Spec workflow completed successfully!`));
175
+ } else {
176
+ lines.push(chalk.red.bold(`${ICONS.error} Spec workflow failed`));
177
+ if (result.error) {
178
+ lines.push(chalk.red(`Error: ${result.error}`));
179
+ }
180
+ }
181
+
182
+ lines.push("");
183
+ lines.push(`Total duration: ${Math.round(result.totalDuration / 1000)}s`);
184
+ lines.push(`Phases completed: ${result.phases.filter((p: PhaseResult) => p.success).length}`);
185
+
186
+ return lines.join("\n");
187
+ }
188
+
189
+ /**
190
+ * Validate phase name
191
+ */
192
+ function isValidPhase(phase: string): phase is SpecPhase {
193
+ return SPEC_PHASES.includes(phase as SpecPhase);
194
+ }
195
+
196
+ // =============================================================================
197
+ // Spec Command Executor
198
+ // =============================================================================
199
+
200
+ /**
201
+ * Execute spec start command
202
+ */
203
+ async function executeSpecStart(description: string, options: SpecOptions): Promise<SpecResult> {
204
+ const slug = generateSlug(description);
205
+ const specDir = options.specDir ?? resolve(join(process.cwd(), DEFAULT_SPEC_BASE, slug));
206
+
207
+ console.log(chalk.bold.blue(`\n${ICONS.mode.spec} Starting Spec Workflow\n`));
208
+ console.log(`Description: ${description}`);
209
+ console.log(`Directory: ${specDir}`);
210
+ if (options.from) {
211
+ console.log(`Starting from: ${options.from}`);
212
+ }
213
+ console.log("");
214
+
215
+ try {
216
+ ensureSpecDir(specDir);
217
+
218
+ const config: SpecWorkflowEngineConfig = {
219
+ specDir,
220
+ startFromPhase: options.from,
221
+ skipPhases: options.skip,
222
+ };
223
+
224
+ const engine = new SpecWorkflowEngine(config);
225
+
226
+ // Listen for phase events
227
+ engine.on("phase:start", (phase: SpecPhase) => {
228
+ console.log(chalk.cyan(`\n▶ Starting phase: ${PHASE_DISPLAY_NAMES[phase]}`));
229
+ });
230
+
231
+ engine.on("phase:complete", (result: PhaseResult) => {
232
+ if (result.success) {
233
+ console.log(chalk.green(`✓ Phase ${PHASE_DISPLAY_NAMES[result.phase]} complete`));
234
+ } else {
235
+ console.log(
236
+ chalk.red(`✗ Phase ${PHASE_DISPLAY_NAMES[result.phase]} failed: ${result.error}`)
237
+ );
238
+ }
239
+ });
240
+
241
+ engine.on("checkpoint:saved", (checkpointId: string) => {
242
+ console.log(chalk.gray(` ${ICONS.checkpoint} Checkpoint saved: ${checkpointId}`));
243
+ });
244
+
245
+ const result = await engine.start(slug, description);
246
+
247
+ console.log(`\n${formatResult(result)}`);
248
+
249
+ return {
250
+ success: result.success,
251
+ specDir,
252
+ workflowResult: result,
253
+ exitCode: result.success ? EXIT_CODES.SUCCESS : EXIT_CODES.ERROR,
254
+ };
255
+ } catch (err) {
256
+ const message = err instanceof Error ? err.message : String(err);
257
+ console.error(chalk.red(`\n${ICONS.error} Failed to start spec workflow: ${message}`));
258
+ return {
259
+ success: false,
260
+ specDir,
261
+ error: message,
262
+ exitCode: EXIT_CODES.ERROR,
263
+ };
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Execute spec continue command
269
+ */
270
+ async function executeSpecContinue(options: SpecOptions): Promise<SpecResult> {
271
+ const specDir = options.specDir ?? resolve(join(process.cwd(), DEFAULT_SPEC_BASE));
272
+
273
+ console.log(chalk.bold.blue(`\n${ICONS.mode.spec} Resuming Spec Workflow\n`));
274
+ console.log(`Directory: ${specDir}`);
275
+ console.log("");
276
+
277
+ try {
278
+ if (!existsSync(specDir)) {
279
+ return {
280
+ success: false,
281
+ error: `Spec directory not found: ${specDir}`,
282
+ exitCode: EXIT_CODES.ERROR,
283
+ };
284
+ }
285
+
286
+ const config: SpecWorkflowEngineConfig = {
287
+ specDir,
288
+ skipPhases: options.skip,
289
+ };
290
+
291
+ const engine = new SpecWorkflowEngine(config);
292
+
293
+ // Listen for phase events
294
+ engine.on("phase:start", (phase: SpecPhase) => {
295
+ console.log(chalk.cyan(`\n▶ Starting phase: ${PHASE_DISPLAY_NAMES[phase]}`));
296
+ });
297
+
298
+ engine.on("phase:complete", (result: PhaseResult) => {
299
+ if (result.success) {
300
+ console.log(chalk.green(`✓ Phase ${PHASE_DISPLAY_NAMES[result.phase]} complete`));
301
+ } else {
302
+ console.log(
303
+ chalk.red(`✗ Phase ${PHASE_DISPLAY_NAMES[result.phase]} failed: ${result.error}`)
304
+ );
305
+ }
306
+ });
307
+
308
+ const result = await engine.resume();
309
+
310
+ console.log(`\n${formatResult(result)}`);
311
+
312
+ return {
313
+ success: result.success,
314
+ specDir,
315
+ workflowResult: result,
316
+ exitCode: result.success ? EXIT_CODES.SUCCESS : EXIT_CODES.ERROR,
317
+ };
318
+ } catch (err) {
319
+ const message = err instanceof Error ? err.message : String(err);
320
+ console.error(chalk.red(`\n${ICONS.error} Failed to resume spec workflow: ${message}`));
321
+ return {
322
+ success: false,
323
+ specDir,
324
+ error: message,
325
+ exitCode: EXIT_CODES.ERROR,
326
+ };
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Execute spec status command
332
+ */
333
+ async function executeSpecStatus(options: SpecOptions): Promise<SpecResult> {
334
+ const specDir = options.specDir ?? resolve(join(process.cwd(), DEFAULT_SPEC_BASE));
335
+
336
+ try {
337
+ if (!existsSync(specDir)) {
338
+ console.log(chalk.yellow("\nNo spec workflow found in this project."));
339
+ console.log(chalk.gray('Run `vellum spec "description"` to start a new spec.\n'));
340
+ return {
341
+ success: true,
342
+ exitCode: EXIT_CODES.SUCCESS,
343
+ };
344
+ }
345
+
346
+ const config: SpecWorkflowEngineConfig = {
347
+ specDir,
348
+ };
349
+
350
+ const engine = new SpecWorkflowEngine(config);
351
+ const status = engine.getStatus();
352
+
353
+ console.log(`\n${formatStatus(status)}\n`);
354
+
355
+ return {
356
+ success: true,
357
+ specDir,
358
+ status,
359
+ exitCode: EXIT_CODES.SUCCESS,
360
+ };
361
+ } catch (err) {
362
+ const message = err instanceof Error ? err.message : String(err);
363
+ console.error(chalk.red(`\n${ICONS.error} Failed to get spec status: ${message}`));
364
+ return {
365
+ success: false,
366
+ specDir,
367
+ error: message,
368
+ exitCode: EXIT_CODES.ERROR,
369
+ };
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Main spec command executor
375
+ */
376
+ export async function executeSpec(options: SpecOptions = {}): Promise<SpecResult> {
377
+ // Validate --from option if provided
378
+ if (options.from && !isValidPhase(options.from)) {
379
+ console.error(chalk.red(`Invalid phase: ${options.from}`));
380
+ console.log(chalk.gray(`Valid phases: ${SPEC_PHASES.join(", ")}`));
381
+ return {
382
+ success: false,
383
+ error: `Invalid phase: ${options.from}`,
384
+ exitCode: EXIT_CODES.ERROR,
385
+ };
386
+ }
387
+
388
+ // Validate --skip option if provided
389
+ if (options.skip) {
390
+ for (const phase of options.skip) {
391
+ if (!isValidPhase(phase)) {
392
+ console.error(chalk.red(`Invalid skip phase: ${phase}`));
393
+ console.log(chalk.gray(`Valid phases: ${SPEC_PHASES.join(", ")}`));
394
+ return {
395
+ success: false,
396
+ error: `Invalid skip phase: ${phase}`,
397
+ exitCode: EXIT_CODES.ERROR,
398
+ };
399
+ }
400
+ }
401
+ }
402
+
403
+ // Route to appropriate subcommand
404
+ if (options.status) {
405
+ return executeSpecStatus(options);
406
+ }
407
+
408
+ if (options.continue) {
409
+ return executeSpecContinue(options);
410
+ }
411
+
412
+ if (options.description) {
413
+ return executeSpecStart(options.description, options);
414
+ }
415
+
416
+ // No subcommand specified - show help
417
+ console.log(chalk.bold.blue(`\n${ICONS.mode.spec} Spec Workflow Commands\n`));
418
+ console.log(' vellum spec "description" Start a new spec workflow');
419
+ console.log(" vellum spec --continue Resume from checkpoint");
420
+ console.log(" vellum spec --status Show workflow status");
421
+ console.log("");
422
+ console.log(chalk.bold("Options:"));
423
+ console.log(" --from=<phase> Start from specific phase");
424
+ console.log(" --skip=<phase1,phase2> Skip specific phases");
425
+ console.log(" --dir=<path> Custom spec directory");
426
+ console.log("");
427
+ console.log(chalk.gray(`Valid phases: ${SPEC_PHASES.join(", ")}`));
428
+ console.log("");
429
+
430
+ return {
431
+ success: true,
432
+ exitCode: EXIT_CODES.SUCCESS,
433
+ };
434
+ }
435
+
436
+ // =============================================================================
437
+ // Slash Command Definition (for TUI)
438
+ // =============================================================================
439
+
440
+ /**
441
+ * /spec slash command for TUI
442
+ *
443
+ * Manages spec workflows from within the TUI.
444
+ */
445
+ export const specSlashCommand: SlashCommand = {
446
+ name: "spec",
447
+ description: "Manage spec workflows",
448
+ kind: "builtin",
449
+ category: "system",
450
+ aliases: [],
451
+ positionalArgs: [
452
+ {
453
+ name: "description",
454
+ type: "string",
455
+ description: "Feature description for new spec",
456
+ required: false,
457
+ },
458
+ ],
459
+ namedArgs: [
460
+ {
461
+ name: "continue",
462
+ shorthand: "c",
463
+ type: "boolean",
464
+ description: "Resume from checkpoint",
465
+ required: false,
466
+ default: false,
467
+ },
468
+ {
469
+ name: "status",
470
+ shorthand: "s",
471
+ type: "boolean",
472
+ description: "Show workflow status",
473
+ required: false,
474
+ default: false,
475
+ },
476
+ {
477
+ name: "from",
478
+ shorthand: "f",
479
+ type: "string",
480
+ description: "Start from specific phase",
481
+ required: false,
482
+ },
483
+ {
484
+ name: "skip",
485
+ type: "string",
486
+ description: "Comma-separated phases to skip",
487
+ required: false,
488
+ },
489
+ {
490
+ name: "dir",
491
+ shorthand: "d",
492
+ type: "string",
493
+ description: "Custom spec directory",
494
+ required: false,
495
+ },
496
+ ],
497
+ examples: [
498
+ '/spec "Add user authentication" - Start new spec',
499
+ "/spec --continue - Resume from checkpoint",
500
+ "/spec --status - Show status",
501
+ "/spec --from=design - Start from design phase",
502
+ '/spec "Feature" --skip=research - Skip research phase',
503
+ ],
504
+ subcommands: [
505
+ { name: "research", description: "Phase 1: Project research and analysis" },
506
+ { name: "requirements", description: "Phase 2: Requirements definition (EARS)" },
507
+ { name: "design", description: "Phase 3: Architecture and design" },
508
+ { name: "tasks", description: "Phase 4: Task breakdown and planning" },
509
+ { name: "implementation", description: "Phase 5: Implementation execution" },
510
+ { name: "validation", description: "Phase 6: Validation and verification" },
511
+ ],
512
+
513
+ execute: async (ctx: CommandContext): Promise<CommandResult> => {
514
+ const firstArg = ctx.parsedArgs.positional[0] as string | undefined;
515
+ const continueOpt = ctx.parsedArgs.named.continue as boolean | undefined;
516
+ const statusOpt = ctx.parsedArgs.named.status as boolean | undefined;
517
+ const fromOpt = ctx.parsedArgs.named.from as string | undefined;
518
+ const skipOpt = ctx.parsedArgs.named.skip as string | undefined;
519
+ const dirOpt = ctx.parsedArgs.named.dir as string | undefined;
520
+
521
+ // Check if first arg is a phase name (subcommand for direct phase jump)
522
+ const isPhaseArg = firstArg && isValidPhase(firstArg);
523
+ const description = isPhaseArg ? undefined : firstArg;
524
+ const effectiveFrom = isPhaseArg ? (firstArg as SpecPhase) : (fromOpt as SpecPhase | undefined);
525
+
526
+ // Parse skip phases
527
+ const skipPhases = skipOpt ? (skipOpt.split(",") as SpecPhase[]) : undefined;
528
+
529
+ return pending({
530
+ message: "Running spec workflow...",
531
+ showProgress: true,
532
+ promise: (async (): Promise<CommandResult> => {
533
+ const result = await executeSpec({
534
+ description,
535
+ continue: continueOpt || (isPhaseArg ? true : undefined), // Resume if jumping to phase
536
+ status: statusOpt,
537
+ from: effectiveFrom,
538
+ skip: skipPhases,
539
+ specDir: dirOpt,
540
+ });
541
+
542
+ if (result.success) {
543
+ if (result.status) {
544
+ return success(formatStatus(result.status), { status: result.status });
545
+ }
546
+ if (result.workflowResult) {
547
+ return success(formatResult(result.workflowResult), {
548
+ workflowResult: result.workflowResult,
549
+ });
550
+ }
551
+ return success("Spec command completed");
552
+ }
553
+
554
+ return error("INTERNAL_ERROR", result.error ?? "Spec command failed");
555
+ })(),
556
+ });
557
+ },
558
+ };