@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,377 @@
1
+ /**
2
+ * Custom Agents Create Command (T021)
3
+ *
4
+ * Creates a new custom agent definition from a template.
5
+ *
6
+ * @module cli/commands/custom-agents/create
7
+ * @see REQ-019
8
+ */
9
+
10
+ import * as fs from "node:fs/promises";
11
+ import * as os from "node:os";
12
+ import * as path from "node:path";
13
+ import { isValidSlug, SLUG_PATTERN } from "@vellum/core";
14
+ import chalk from "chalk";
15
+
16
+ import type { CommandResult } from "../types.js";
17
+ import { error, interactive, success } from "../types.js";
18
+ import type { CreateOptions } from "./index.js";
19
+
20
+ // =============================================================================
21
+ // Templates
22
+ // =============================================================================
23
+
24
+ /**
25
+ * Template types available for agent creation
26
+ */
27
+ type TemplateType = "basic" | "advanced" | "orchestrator";
28
+
29
+ /**
30
+ * Basic agent template (minimal configuration)
31
+ */
32
+ function getBasicTemplate(slug: string, name: string): string {
33
+ return `---
34
+ slug: ${slug}
35
+ name: "${name}"
36
+ mode: code
37
+ description: "Custom agent for specialized tasks"
38
+ icon: "🤖"
39
+ ---
40
+
41
+ # ${name}
42
+
43
+ You are a helpful AI assistant.
44
+
45
+ ## Instructions
46
+
47
+ Follow these guidelines when assisting users:
48
+ - Be concise and clear
49
+ - Ask for clarification when needed
50
+ - Provide examples when helpful
51
+ `;
52
+ }
53
+
54
+ /**
55
+ * Advanced agent template (full configuration)
56
+ */
57
+ function getAdvancedTemplate(slug: string, name: string): string {
58
+ return `---
59
+ slug: ${slug}
60
+ name: "${name}"
61
+ mode: code
62
+ description: "Advanced custom agent with full configuration"
63
+ icon: "⚡"
64
+ color: "#3b82f6"
65
+ version: "1.0.0"
66
+ author: "user"
67
+ tags:
68
+ - custom
69
+ - advanced
70
+
71
+ # Tool configuration
72
+ toolGroups:
73
+ - group: filesystem
74
+ enabled: true
75
+ - group: shell
76
+ enabled: true
77
+
78
+ # Restrictions
79
+ restrictions:
80
+ fileRestrictions:
81
+ - pattern: "src/**"
82
+ access: write
83
+ - pattern: "*.config.*"
84
+ access: read
85
+ maxTokens: 8192
86
+ timeout: 300000
87
+
88
+ # Runtime settings
89
+ settings:
90
+ temperature: 0.7
91
+ extendedThinking: false
92
+ streamOutput: true
93
+ autoConfirm: false
94
+
95
+ # When to suggest this agent
96
+ whenToUse:
97
+ description: "Use this agent for specialized coding tasks"
98
+ triggers:
99
+ - type: keyword
100
+ pattern: "implement|build|create"
101
+ priority: 10
102
+ ---
103
+
104
+ # ${name}
105
+
106
+ You are a specialized AI assistant with advanced capabilities.
107
+
108
+ ## Core Responsibilities
109
+
110
+ 1. Analyze requirements carefully before implementation
111
+ 2. Write clean, well-documented code
112
+ 3. Follow best practices and project conventions
113
+ 4. Test your implementations thoroughly
114
+
115
+ ## Guidelines
116
+
117
+ - Always explain your approach before making changes
118
+ - Ask clarifying questions when requirements are ambiguous
119
+ - Consider edge cases and error handling
120
+ - Document complex logic with comments
121
+
122
+ ## Constraints
123
+
124
+ - Only modify files within your allowed scope
125
+ - Prefer existing patterns found in the codebase
126
+ - Keep changes focused and minimal
127
+ `;
128
+ }
129
+
130
+ /**
131
+ * Orchestrator agent template (for multi-agent workflows)
132
+ */
133
+ function getOrchestratorTemplate(slug: string, name: string): string {
134
+ return `---
135
+ slug: ${slug}
136
+ name: "${name}"
137
+ mode: plan
138
+ description: "Orchestrator agent for coordinating multi-agent workflows"
139
+ icon: "🎯"
140
+ color: "#8b5cf6"
141
+ version: "1.0.0"
142
+ level: orchestrator
143
+
144
+ # Multi-agent coordination
145
+ coordination:
146
+ canSpawnAgents:
147
+ - coder
148
+ - reviewer
149
+ - tester
150
+ maxConcurrentSubagents: 3
151
+
152
+ # Settings for orchestration
153
+ settings:
154
+ temperature: 0.5
155
+ extendedThinking: true
156
+ streamOutput: true
157
+
158
+ whenToUse:
159
+ description: "Use for complex tasks requiring multiple agents"
160
+ triggers:
161
+ - type: keyword
162
+ pattern: "complex|multi-step|orchestrate"
163
+ priority: 20
164
+ ---
165
+
166
+ # ${name}
167
+
168
+ You are an orchestrator agent responsible for coordinating complex workflows.
169
+
170
+ ## Your Role
171
+
172
+ As an orchestrator, you:
173
+ 1. Break down complex tasks into subtasks
174
+ 2. Delegate subtasks to specialized agents
175
+ 3. Coordinate and synthesize results
176
+ 4. Ensure overall task completion
177
+
178
+ ## Workflow Strategy
179
+
180
+ 1. **Analysis**: Understand the full scope of the request
181
+ 2. **Planning**: Create a step-by-step execution plan
182
+ 3. **Delegation**: Assign tasks to appropriate agents
183
+ 4. **Monitoring**: Track progress and handle issues
184
+ 5. **Synthesis**: Combine results into final output
185
+
186
+ ## Available Agents
187
+
188
+ You can delegate to these specialized agents:
189
+ - \`coder\`: Implementation tasks
190
+ - \`reviewer\`: Code review and analysis
191
+ - \`tester\`: Testing and validation
192
+
193
+ ## Constraints
194
+
195
+ - Always create a plan before delegating
196
+ - Monitor agent progress and intervene if needed
197
+ - Synthesize results before presenting to user
198
+ `;
199
+ }
200
+
201
+ /**
202
+ * Get template content by type
203
+ */
204
+ function getTemplate(type: TemplateType, slug: string, name: string): string {
205
+ switch (type) {
206
+ case "advanced":
207
+ return getAdvancedTemplate(slug, name);
208
+ case "orchestrator":
209
+ return getOrchestratorTemplate(slug, name);
210
+ default:
211
+ return getBasicTemplate(slug, name);
212
+ }
213
+ }
214
+
215
+ // =============================================================================
216
+ // Helpers
217
+ // =============================================================================
218
+
219
+ /**
220
+ * Convert slug to display name
221
+ */
222
+ function slugToName(slug: string): string {
223
+ return slug
224
+ .split("-")
225
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
226
+ .join(" ");
227
+ }
228
+
229
+ /**
230
+ * Validate slug format
231
+ */
232
+ function validateSlug(slug: string): { valid: boolean; message?: string } {
233
+ if (!slug || slug.trim().length === 0) {
234
+ return { valid: false, message: "Slug cannot be empty" };
235
+ }
236
+
237
+ if (slug.length > 50) {
238
+ return { valid: false, message: "Slug must be 50 characters or less" };
239
+ }
240
+
241
+ if (!isValidSlug(slug)) {
242
+ return {
243
+ valid: false,
244
+ message: `Slug must be lowercase alphanumeric with hyphens (pattern: ${SLUG_PATTERN.source})`,
245
+ };
246
+ }
247
+
248
+ return { valid: true };
249
+ }
250
+
251
+ /**
252
+ * Get agent file path
253
+ */
254
+ function getAgentFilePath(slug: string, global: boolean): string {
255
+ const baseDir = global
256
+ ? path.join(os.homedir(), ".vellum", "agents")
257
+ : path.join(process.cwd(), ".vellum", "agents");
258
+
259
+ return path.join(baseDir, `${slug}.md`);
260
+ }
261
+
262
+ /**
263
+ * Check if file already exists
264
+ */
265
+ async function fileExists(filePath: string): Promise<boolean> {
266
+ try {
267
+ await fs.access(filePath);
268
+ return true;
269
+ } catch {
270
+ return false;
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Ensure directory exists
276
+ */
277
+ async function ensureDir(dirPath: string): Promise<void> {
278
+ await fs.mkdir(dirPath, { recursive: true });
279
+ }
280
+
281
+ // =============================================================================
282
+ // Command Handler
283
+ // =============================================================================
284
+
285
+ /**
286
+ * Handle create subcommand
287
+ *
288
+ * Creates a new custom agent definition file from a template.
289
+ *
290
+ * @param slug - Agent slug (identifier)
291
+ * @param options - Create options
292
+ * @returns Command result
293
+ */
294
+ export async function handleCreate(
295
+ slug: string | undefined,
296
+ options: CreateOptions = {}
297
+ ): Promise<CommandResult> {
298
+ // If no slug provided, prompt for it (unless no-interactive)
299
+ if (!slug) {
300
+ if (options.noInteractive) {
301
+ return error("MISSING_ARGUMENT", "Agent slug is required", [
302
+ "Provide a slug: /custom-agents create <slug>",
303
+ ]);
304
+ }
305
+
306
+ return interactive({
307
+ inputType: "text",
308
+ message: "Enter agent slug (lowercase, alphanumeric, hyphens):",
309
+ placeholder: "my-custom-agent",
310
+ handler: async (value) => {
311
+ return handleCreate(value, options);
312
+ },
313
+ onCancel: () => success("Agent creation cancelled"),
314
+ });
315
+ }
316
+
317
+ // Validate slug
318
+ const validation = validateSlug(slug);
319
+ if (!validation.valid) {
320
+ return error("INVALID_ARGUMENT", validation.message ?? "Invalid slug format", [
321
+ "Example valid slugs: my-agent, code-reviewer, test-helper",
322
+ ]);
323
+ }
324
+
325
+ // Parse template type
326
+ const templateType: TemplateType = (options.template?.toLowerCase() as TemplateType) || "basic";
327
+
328
+ if (!["basic", "advanced", "orchestrator"].includes(templateType)) {
329
+ return error("INVALID_ARGUMENT", `Unknown template: ${options.template}`, [
330
+ "Available templates: basic, advanced, orchestrator",
331
+ ]);
332
+ }
333
+
334
+ // Get file path
335
+ const filePath = getAgentFilePath(slug, options.global ?? false);
336
+ const dirPath = path.dirname(filePath);
337
+
338
+ // Check if file already exists
339
+ if (await fileExists(filePath)) {
340
+ return error("OPERATION_NOT_ALLOWED", `Agent "${slug}" already exists at: ${filePath}`, [
341
+ `Use a different slug, or delete the existing file first`,
342
+ ]);
343
+ }
344
+
345
+ try {
346
+ // Ensure directory exists
347
+ await ensureDir(dirPath);
348
+
349
+ // Generate name from slug
350
+ const name = slugToName(slug);
351
+
352
+ // Get template content
353
+ const content = getTemplate(templateType, slug, name);
354
+
355
+ // Write file
356
+ await fs.writeFile(filePath, content, "utf-8");
357
+
358
+ // Success message
359
+ const scope = options.global ? "user" : "project";
360
+ const lines = [
361
+ chalk.green(`✅ Created agent "${slug}" (${scope} scope)`),
362
+ "",
363
+ chalk.gray(`File: ${filePath}`),
364
+ chalk.gray(`Template: ${templateType}`),
365
+ "",
366
+ chalk.cyan("Next steps:"),
367
+ chalk.gray(` 1. Edit the agent file to customize behavior`),
368
+ chalk.gray(` 2. Run: /custom-agents validate ${slug}`),
369
+ chalk.gray(` 3. Use: /mode ${slug}`),
370
+ ];
371
+
372
+ return success(lines.join("\n"));
373
+ } catch (err) {
374
+ const message = err instanceof Error ? err.message : String(err);
375
+ return error("INTERNAL_ERROR", `Failed to create agent: ${message}`);
376
+ }
377
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Custom Agents Export Command (T020a)
3
+ *
4
+ * Exports agent definition to a file.
5
+ *
6
+ * @module cli/commands/custom-agents/export
7
+ * @see REQ-022
8
+ */
9
+
10
+ import * as fs from "node:fs/promises";
11
+ import * as path from "node:path";
12
+ import { AgentDiscovery, type CustomAgentDefinition } from "@vellum/core";
13
+ import chalk from "chalk";
14
+ import matter from "gray-matter";
15
+
16
+ import type { CommandResult } from "../types.js";
17
+ import { error, success } from "../types.js";
18
+ import type { ExportOptions } from "./index.js";
19
+
20
+ // =============================================================================
21
+ // Helpers
22
+ // =============================================================================
23
+
24
+ /**
25
+ * Convert agent definition to YAML string using gray-matter stringify
26
+ */
27
+ function toYaml(agent: CustomAgentDefinition): string {
28
+ // Create a clean object for export (remove undefined values)
29
+ const clean = JSON.parse(JSON.stringify(agent));
30
+ // Use gray-matter to stringify as YAML
31
+ return matter.stringify("", clean).trim();
32
+ }
33
+
34
+ /**
35
+ * Convert agent definition to JSON string
36
+ */
37
+ function toJson(agent: CustomAgentDefinition): string {
38
+ return JSON.stringify(agent, null, 2);
39
+ }
40
+
41
+ /**
42
+ * Ensure directory exists
43
+ */
44
+ async function ensureDir(dirPath: string): Promise<void> {
45
+ await fs.mkdir(dirPath, { recursive: true });
46
+ }
47
+
48
+ // =============================================================================
49
+ // Command Handler
50
+ // =============================================================================
51
+
52
+ /**
53
+ * Handle export subcommand
54
+ *
55
+ * Exports an agent definition to a file.
56
+ *
57
+ * @param slug - Agent slug to export
58
+ * @param options - Export options
59
+ * @returns Command result
60
+ */
61
+ export async function handleExport(
62
+ slug: string | undefined,
63
+ options: ExportOptions = {}
64
+ ): Promise<CommandResult> {
65
+ // Require slug
66
+ if (!slug) {
67
+ return error("MISSING_ARGUMENT", "Agent slug is required", [
68
+ "Usage: /custom-agents export <slug>",
69
+ "Use /custom-agents list to see available agents",
70
+ ]);
71
+ }
72
+
73
+ try {
74
+ // Discover agents
75
+ const discovery = new AgentDiscovery({ watchEnabled: false });
76
+ await discovery.discover();
77
+
78
+ // Find agent
79
+ const agent = discovery.get(slug);
80
+
81
+ if (!agent) {
82
+ return error("RESOURCE_NOT_FOUND", `Agent not found: ${slug}`, [
83
+ "Check the slug is correct",
84
+ "Use /custom-agents list to see available agents",
85
+ ]);
86
+ }
87
+
88
+ // Determine format
89
+ const format = options.format ?? "yaml";
90
+ if (format !== "yaml" && format !== "json") {
91
+ return error("INVALID_ARGUMENT", `Invalid format: ${format}`, [
92
+ "Supported formats: yaml, json",
93
+ ]);
94
+ }
95
+
96
+ // Convert to string
97
+ const content = format === "json" ? toJson(agent.definition) : toYaml(agent.definition);
98
+
99
+ // If no output specified, print to stdout
100
+ if (!options.output) {
101
+ const lines = [
102
+ chalk.green(`📤 Exporting agent: ${slug}`),
103
+ chalk.gray(`Format: ${format}`),
104
+ chalk.gray(`Source: ${agent.sourcePath}`),
105
+ "",
106
+ chalk.gray("─".repeat(60)),
107
+ content,
108
+ chalk.gray("─".repeat(60)),
109
+ "",
110
+ chalk.cyan("Tip: Use --output to save to a file"),
111
+ ];
112
+ return success(lines.join("\n"));
113
+ }
114
+
115
+ // Write to file
116
+ const outputPath = path.resolve(options.output);
117
+ const dirPath = path.dirname(outputPath);
118
+
119
+ await ensureDir(dirPath);
120
+ await fs.writeFile(outputPath, content, "utf-8");
121
+
122
+ const lines = [
123
+ chalk.green(`✅ Exported agent "${slug}"`),
124
+ "",
125
+ chalk.gray(`Format: ${format}`),
126
+ chalk.gray(`Output: ${outputPath}`),
127
+ chalk.gray(`Source: ${agent.sourcePath}`),
128
+ ];
129
+
130
+ return success(lines.join("\n"));
131
+ } catch (err) {
132
+ const message = err instanceof Error ? err.message : String(err);
133
+ return error("INTERNAL_ERROR", `Failed to export agent: ${message}`);
134
+ }
135
+ }
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Custom Agents Import Command (T020b)
3
+ *
4
+ * Imports agent definition from a file.
5
+ *
6
+ * @module cli/commands/custom-agents/import
7
+ * @see REQ-022
8
+ */
9
+
10
+ import * as fs from "node:fs/promises";
11
+ import * as os from "node:os";
12
+ import * as path from "node:path";
13
+ import {
14
+ AgentDiscovery,
15
+ AgentLoader,
16
+ type CustomAgentDefinition,
17
+ validateAgentDefinition,
18
+ } from "@vellum/core";
19
+ import chalk from "chalk";
20
+ import matter from "gray-matter";
21
+
22
+ import type { CommandResult } from "../types.js";
23
+ import { error, interactive, success } from "../types.js";
24
+ import type { ImportOptions } from "./index.js";
25
+
26
+ // =============================================================================
27
+ // Helpers
28
+ // =============================================================================
29
+
30
+ /**
31
+ * Get agent file path for import destination
32
+ */
33
+ function getAgentFilePath(slug: string, global: boolean): string {
34
+ const baseDir = global
35
+ ? path.join(os.homedir(), ".vellum", "agents")
36
+ : path.join(process.cwd(), ".vellum", "agents");
37
+
38
+ return path.join(baseDir, `${slug}.md`);
39
+ }
40
+
41
+ /**
42
+ * Check if file exists
43
+ */
44
+ async function fileExists(filePath: string): Promise<boolean> {
45
+ try {
46
+ await fs.access(filePath);
47
+ return true;
48
+ } catch {
49
+ return false;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Ensure directory exists
55
+ */
56
+ async function ensureDir(dirPath: string): Promise<void> {
57
+ await fs.mkdir(dirPath, { recursive: true });
58
+ }
59
+
60
+ /**
61
+ * Convert agent definition to Markdown with YAML frontmatter
62
+ */
63
+ function toMarkdown(agent: CustomAgentDefinition): string {
64
+ // Extract systemPrompt as body content
65
+ const { systemPrompt, ...frontmatter } = agent;
66
+
67
+ // Use gray-matter to create markdown with frontmatter
68
+ const body =
69
+ systemPrompt ??
70
+ `# ${agent.name}
71
+
72
+ You are a helpful AI assistant.
73
+
74
+ ## Instructions
75
+
76
+ Add your agent instructions here.`;
77
+
78
+ return matter.stringify(body, frontmatter);
79
+ }
80
+
81
+ // =============================================================================
82
+ // Command Handler
83
+ // =============================================================================
84
+
85
+ /**
86
+ * Handle import subcommand
87
+ *
88
+ * Imports an agent definition from a file.
89
+ *
90
+ * @param options - Import options
91
+ * @returns Command result
92
+ */
93
+ export async function handleImport(options: ImportOptions): Promise<CommandResult> {
94
+ // Require file path
95
+ if (!options.file) {
96
+ return error("MISSING_ARGUMENT", "Import file path is required", [
97
+ "Usage: /custom-agents import <file>",
98
+ ]);
99
+ }
100
+
101
+ try {
102
+ const filePath = path.resolve(options.file);
103
+
104
+ // Check file exists
105
+ if (!(await fileExists(filePath))) {
106
+ return error("FILE_NOT_FOUND", `File not found: ${filePath}`);
107
+ }
108
+
109
+ // Load agent from file
110
+ const loader = new AgentLoader();
111
+ const loadResult = await loader.loadFile(filePath);
112
+
113
+ if (!loadResult.ok) {
114
+ return error("INVALID_ARGUMENT", `Failed to parse agent file: ${loadResult.error.message}`, [
115
+ "Ensure the file is valid YAML or Markdown with YAML frontmatter",
116
+ ]);
117
+ }
118
+
119
+ const agent = loadResult.value;
120
+
121
+ // Validate agent definition
122
+ const validation = validateAgentDefinition(agent);
123
+ if (!validation.success) {
124
+ const issues = validation.error.issues
125
+ .map((i) => ` - ${i.path.join(".")}: ${i.message}`)
126
+ .join("\n");
127
+
128
+ return error("INVALID_ARGUMENT", `Agent validation failed:\n${issues}`, [
129
+ "Fix the validation errors and try again",
130
+ ]);
131
+ }
132
+
133
+ // Check if agent already exists
134
+ const discovery = new AgentDiscovery({ watchEnabled: false });
135
+ await discovery.discover();
136
+ const existing = discovery.get(agent.slug);
137
+
138
+ if (existing) {
139
+ // Prompt for confirmation
140
+ return interactive({
141
+ inputType: "confirm",
142
+ message: `Agent "${agent.slug}" already exists. Overwrite?`,
143
+ handler: async (value) => {
144
+ if (value.toLowerCase() !== "yes" && value.toLowerCase() !== "y") {
145
+ return success(chalk.yellow("Import cancelled"));
146
+ }
147
+ return doImport(agent, options.global ?? false, filePath);
148
+ },
149
+ onCancel: () => success(chalk.yellow("Import cancelled")),
150
+ });
151
+ }
152
+
153
+ return doImport(agent, options.global ?? false, filePath);
154
+ } catch (err) {
155
+ const message = err instanceof Error ? err.message : String(err);
156
+ return error("INTERNAL_ERROR", `Failed to import agent: ${message}`);
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Perform the actual import
162
+ */
163
+ async function doImport(
164
+ agent: CustomAgentDefinition,
165
+ global: boolean,
166
+ sourceFile: string
167
+ ): Promise<CommandResult> {
168
+ try {
169
+ // Get destination path
170
+ const destPath = getAgentFilePath(agent.slug, global);
171
+ const dirPath = path.dirname(destPath);
172
+
173
+ // Ensure directory exists
174
+ await ensureDir(dirPath);
175
+
176
+ // Convert to Markdown and write
177
+ const content = toMarkdown(agent);
178
+ await fs.writeFile(destPath, content, "utf-8");
179
+
180
+ // Success message
181
+ const scope = global ? "user" : "project";
182
+ const lines = [
183
+ chalk.green(`✅ Imported agent "${agent.slug}" (${scope} scope)`),
184
+ "",
185
+ chalk.gray(`Source: ${sourceFile}`),
186
+ chalk.gray(`Destination: ${destPath}`),
187
+ "",
188
+ chalk.cyan("Next steps:"),
189
+ chalk.gray(` 1. Review the imported agent: /custom-agents info ${agent.slug}`),
190
+ chalk.gray(` 2. Validate: /custom-agents validate ${agent.slug}`),
191
+ chalk.gray(` 3. Use: /mode ${agent.slug}`),
192
+ ];
193
+
194
+ return success(lines.join("\n"));
195
+ } catch (err) {
196
+ const message = err instanceof Error ? err.message : String(err);
197
+ return error("INTERNAL_ERROR", `Failed to write agent file: ${message}`);
198
+ }
199
+ }