@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,267 @@
1
+ /**
2
+ * Custom Agents List Command (T020)
3
+ *
4
+ * Lists custom agents grouped by scope (project/user/system).
5
+ *
6
+ * @module cli/commands/custom-agents/list
7
+ * @see REQ-018
8
+ */
9
+
10
+ import {
11
+ AgentDiscovery,
12
+ type CustomAgentDefinition,
13
+ type DiscoveredAgent,
14
+ DiscoverySource,
15
+ } from "@vellum/core";
16
+ import chalk from "chalk";
17
+
18
+ import type { CommandResult } from "../types.js";
19
+ import { error, success } from "../types.js";
20
+ import type { ListOptions } from "./index.js";
21
+
22
+ // =============================================================================
23
+ // Types
24
+ // =============================================================================
25
+
26
+ /**
27
+ * Scope category for agent grouping
28
+ */
29
+ type ScopeCategory = "project" | "user" | "system";
30
+
31
+ /**
32
+ * Agent entry with source info for display
33
+ */
34
+ interface AgentEntry {
35
+ agent: CustomAgentDefinition;
36
+ sourcePath: string;
37
+ source: DiscoverySource;
38
+ }
39
+
40
+ /**
41
+ * Grouped agents by scope
42
+ */
43
+ interface GroupedAgents {
44
+ project: AgentEntry[];
45
+ user: AgentEntry[];
46
+ system: AgentEntry[];
47
+ }
48
+
49
+ /**
50
+ * JSON output format for list command
51
+ */
52
+ export interface ListJsonOutput {
53
+ success: boolean;
54
+ total: number;
55
+ agents: {
56
+ project: AgentSummary[];
57
+ user: AgentSummary[];
58
+ system: AgentSummary[];
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Agent summary for JSON output
64
+ */
65
+ interface AgentSummary {
66
+ slug: string;
67
+ name: string;
68
+ description?: string;
69
+ mode?: string;
70
+ icon?: string;
71
+ tags?: string[];
72
+ sourcePath: string;
73
+ }
74
+
75
+ // =============================================================================
76
+ // Helpers
77
+ // =============================================================================
78
+
79
+ /**
80
+ * Map DiscoverySource to scope category
81
+ */
82
+ function sourceToScope(source: DiscoverySource): ScopeCategory {
83
+ switch (source) {
84
+ case DiscoverySource.PROJECT:
85
+ return "project";
86
+ case DiscoverySource.USER:
87
+ return "user";
88
+ default:
89
+ return "system";
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Group agents by scope
95
+ */
96
+ function groupAgentsByScope(agents: Map<string, DiscoveredAgent>): GroupedAgents {
97
+ const grouped: GroupedAgents = {
98
+ project: [],
99
+ user: [],
100
+ system: [],
101
+ };
102
+
103
+ for (const [, discovered] of agents) {
104
+ const scope = sourceToScope(discovered.source);
105
+ grouped[scope].push({
106
+ agent: discovered.definition,
107
+ sourcePath: discovered.sourcePath,
108
+ source: discovered.source,
109
+ });
110
+ }
111
+
112
+ // Sort each group alphabetically by slug
113
+ for (const scope of Object.keys(grouped) as ScopeCategory[]) {
114
+ grouped[scope].sort((a, b) => a.agent.slug.localeCompare(b.agent.slug));
115
+ }
116
+
117
+ return grouped;
118
+ }
119
+
120
+ /**
121
+ * Format agent entry for display
122
+ */
123
+ function formatAgentEntry(entry: AgentEntry, verbose = false): string {
124
+ const { agent } = entry;
125
+ const icon = agent.icon ?? "šŸ“¦";
126
+ const name = chalk.cyan(agent.slug);
127
+ const displayName = agent.name !== agent.slug ? chalk.gray(` (${agent.name})`) : "";
128
+ const mode = agent.mode ? chalk.yellow(` [${agent.mode}]`) : "";
129
+ const desc = agent.description ? `\n ${chalk.gray(agent.description)}` : "";
130
+ const tags = agent.tags?.length
131
+ ? `\n ${chalk.blue(agent.tags.map((t) => `#${t}`).join(" "))}`
132
+ : "";
133
+
134
+ let line = ` ${icon} ${name}${displayName}${mode}`;
135
+
136
+ if (verbose) {
137
+ line += desc + tags;
138
+ line += `\n ${chalk.gray("→")} ${chalk.gray(entry.sourcePath)}`;
139
+ }
140
+
141
+ return line;
142
+ }
143
+
144
+ /**
145
+ * Format a scope section for display
146
+ */
147
+ function formatScopeSection(
148
+ title: string,
149
+ color: typeof chalk.bold.green,
150
+ entries: AgentEntry[],
151
+ verbose: boolean
152
+ ): string[] {
153
+ const lines: string[] = [color(title)];
154
+ if (entries.length === 0) {
155
+ lines.push(chalk.gray(" (none)"));
156
+ } else {
157
+ for (const entry of entries) {
158
+ lines.push(formatAgentEntry(entry, verbose));
159
+ }
160
+ }
161
+ lines.push("");
162
+ return lines;
163
+ }
164
+
165
+ /**
166
+ * Format grouped agents for display
167
+ */
168
+ function formatGroupedAgents(grouped: GroupedAgents, options: ListOptions): string {
169
+ const lines: string[] = [];
170
+ const verbose = false; // Could add --verbose flag later
171
+
172
+ // Project scope
173
+ if (!options.global) {
174
+ lines.push(
175
+ ...formatScopeSection("šŸ“ Project Agents", chalk.bold.green, grouped.project, verbose)
176
+ );
177
+ }
178
+
179
+ // User scope
180
+ if (!options.local) {
181
+ lines.push(...formatScopeSection("šŸ‘¤ User Agents", chalk.bold.blue, grouped.user, verbose));
182
+ }
183
+
184
+ // System scope (only if neither --global nor --local)
185
+ if (!options.global && !options.local) {
186
+ lines.push(
187
+ ...formatScopeSection("🌐 System Agents", chalk.bold.magenta, grouped.system, verbose)
188
+ );
189
+ }
190
+
191
+ const total = grouped.project.length + grouped.user.length + grouped.system.length;
192
+ lines.push(chalk.gray(`Total: ${total} agent(s)`));
193
+
194
+ return lines.join("\n");
195
+ }
196
+
197
+ /**
198
+ * Convert grouped agents to JSON output
199
+ */
200
+ function toJsonOutput(grouped: GroupedAgents): ListJsonOutput {
201
+ const toSummary = (entry: AgentEntry): AgentSummary => ({
202
+ slug: entry.agent.slug,
203
+ name: entry.agent.name,
204
+ description: entry.agent.description,
205
+ mode: entry.agent.mode,
206
+ icon: entry.agent.icon,
207
+ tags: entry.agent.tags,
208
+ sourcePath: entry.sourcePath,
209
+ });
210
+
211
+ return {
212
+ success: true,
213
+ total: grouped.project.length + grouped.user.length + grouped.system.length,
214
+ agents: {
215
+ project: grouped.project.map(toSummary),
216
+ user: grouped.user.map(toSummary),
217
+ system: grouped.system.map(toSummary),
218
+ },
219
+ };
220
+ }
221
+
222
+ // =============================================================================
223
+ // Command Handler
224
+ // =============================================================================
225
+
226
+ /**
227
+ * Handle list subcommand
228
+ *
229
+ * Lists all custom agents grouped by scope (project/user/system).
230
+ *
231
+ * @param options - List options
232
+ * @returns Command result
233
+ */
234
+ export async function handleList(options: ListOptions = {}): Promise<CommandResult> {
235
+ try {
236
+ // Create discovery instance
237
+ const discovery = new AgentDiscovery({
238
+ watchEnabled: false, // No need to watch for listing
239
+ });
240
+
241
+ // Discover all agents
242
+ await discovery.discover();
243
+ const allAgents = discovery.getAll();
244
+
245
+ // Group by scope
246
+ const grouped = groupAgentsByScope(allAgents);
247
+
248
+ // Filter by scope if requested
249
+ if (options.global) {
250
+ grouped.project = [];
251
+ grouped.system = [];
252
+ } else if (options.local) {
253
+ grouped.user = [];
254
+ grouped.system = [];
255
+ }
256
+
257
+ // Output format
258
+ if (options.json) {
259
+ return success(JSON.stringify(toJsonOutput(grouped), null, 2));
260
+ }
261
+
262
+ return success(formatGroupedAgents(grouped, options));
263
+ } catch (err) {
264
+ const message = err instanceof Error ? err.message : String(err);
265
+ return error("INTERNAL_ERROR", `Failed to list agents: ${message}`);
266
+ }
267
+ }
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Custom Agents Validate Command (T022)
3
+ *
4
+ * Validates custom agent definition files.
5
+ *
6
+ * @module cli/commands/custom-agents/validate
7
+ * @see REQ-020
8
+ */
9
+
10
+ import * as path from "node:path";
11
+ import {
12
+ AgentDiscovery,
13
+ AgentLoader,
14
+ type CustomAgentDefinition,
15
+ isValidSlug,
16
+ validateAgentDefinition,
17
+ } from "@vellum/core";
18
+ import chalk from "chalk";
19
+
20
+ import type { CommandResult } from "../types.js";
21
+ import { error, success } from "../types.js";
22
+ import type { ValidateOptions } from "./index.js";
23
+
24
+ // =============================================================================
25
+ // Types
26
+ // =============================================================================
27
+
28
+ /**
29
+ * Validation result for a single agent
30
+ */
31
+ interface AgentValidationResult {
32
+ slug: string;
33
+ sourcePath: string;
34
+ valid: boolean;
35
+ errors: ValidationIssue[];
36
+ warnings: ValidationIssue[];
37
+ }
38
+
39
+ /**
40
+ * Validation issue (error or warning)
41
+ */
42
+ interface ValidationIssue {
43
+ message: string;
44
+ field?: string;
45
+ severity: "error" | "warning";
46
+ }
47
+
48
+ /**
49
+ * Overall validation summary
50
+ */
51
+ interface ValidationSummary {
52
+ total: number;
53
+ valid: number;
54
+ invalid: number;
55
+ warnings: number;
56
+ results: AgentValidationResult[];
57
+ }
58
+
59
+ /**
60
+ * JSON output format
61
+ */
62
+ export interface ValidateJsonOutput {
63
+ success: boolean;
64
+ summary: {
65
+ total: number;
66
+ valid: number;
67
+ invalid: number;
68
+ warnings: number;
69
+ };
70
+ results: AgentValidationResult[];
71
+ }
72
+
73
+ // =============================================================================
74
+ // Validation Logic
75
+ // =============================================================================
76
+
77
+ /**
78
+ * Validate a single agent definition
79
+ */
80
+ function validateAgent(agent: CustomAgentDefinition, sourcePath: string): AgentValidationResult {
81
+ const errors: ValidationIssue[] = [];
82
+ const warnings: ValidationIssue[] = [];
83
+
84
+ // Validate using Zod schema
85
+ const schemaResult = validateAgentDefinition(agent);
86
+
87
+ if (!schemaResult.success) {
88
+ for (const issue of schemaResult.error.issues) {
89
+ errors.push({
90
+ message: issue.message,
91
+ field: issue.path.join("."),
92
+ severity: "error",
93
+ });
94
+ }
95
+ }
96
+
97
+ // Additional validation checks
98
+
99
+ // Slug validation
100
+ if (!isValidSlug(agent.slug)) {
101
+ errors.push({
102
+ message: "Invalid slug format",
103
+ field: "slug",
104
+ severity: "error",
105
+ });
106
+ }
107
+
108
+ // Warn about missing description
109
+ if (!agent.description) {
110
+ warnings.push({
111
+ message: "Missing description - recommended for discoverability",
112
+ field: "description",
113
+ severity: "warning",
114
+ });
115
+ }
116
+
117
+ // Warn about missing icon
118
+ if (!agent.icon) {
119
+ warnings.push({
120
+ message: "Missing icon - will use default",
121
+ field: "icon",
122
+ severity: "warning",
123
+ });
124
+ }
125
+
126
+ // Warn about missing whenToUse
127
+ if (!agent.whenToUse) {
128
+ warnings.push({
129
+ message: "Missing whenToUse - agent won't be auto-suggested",
130
+ field: "whenToUse",
131
+ severity: "warning",
132
+ });
133
+ }
134
+
135
+ // Warn about extends pointing to unknown agent (would need registry check)
136
+ if (agent.extends) {
137
+ // This is a soft warning - can't validate without full registry
138
+ warnings.push({
139
+ message: `Extends "${agent.extends}" - ensure parent agent exists`,
140
+ field: "extends",
141
+ severity: "warning",
142
+ });
143
+ }
144
+
145
+ // Warn about circular references in canSpawnAgents
146
+ if (agent.coordination?.canSpawnAgents?.includes(agent.slug)) {
147
+ errors.push({
148
+ message: "Agent cannot spawn itself",
149
+ field: "coordination.canSpawnAgents",
150
+ severity: "error",
151
+ });
152
+ }
153
+
154
+ // Validate temperature range
155
+ if (agent.settings?.temperature !== undefined) {
156
+ if (agent.settings.temperature < 0 || agent.settings.temperature > 1) {
157
+ errors.push({
158
+ message: "Temperature must be between 0 and 1",
159
+ field: "settings.temperature",
160
+ severity: "error",
161
+ });
162
+ }
163
+ }
164
+
165
+ return {
166
+ slug: agent.slug,
167
+ sourcePath,
168
+ valid: errors.length === 0,
169
+ errors,
170
+ warnings,
171
+ };
172
+ }
173
+
174
+ /**
175
+ * Validate agent from file path
176
+ */
177
+ async function validateAgentFile(filePath: string): Promise<AgentValidationResult | null> {
178
+ const loader = new AgentLoader();
179
+ const result = await loader.loadFile(filePath);
180
+
181
+ if (!result.ok) {
182
+ const loadError = result.error;
183
+ return {
184
+ slug: path.basename(filePath, path.extname(filePath)),
185
+ sourcePath: filePath,
186
+ valid: false,
187
+ errors: [
188
+ {
189
+ message: loadError.message,
190
+ severity: "error",
191
+ },
192
+ ],
193
+ warnings: [],
194
+ };
195
+ }
196
+
197
+ return validateAgent(result.value, filePath);
198
+ }
199
+
200
+ // =============================================================================
201
+ // Formatters
202
+ // =============================================================================
203
+
204
+ /**
205
+ * Format validation issue for display
206
+ */
207
+ function formatIssue(issue: ValidationIssue): string {
208
+ const icon = issue.severity === "error" ? chalk.red("āœ—") : chalk.yellow("⚠");
209
+ const field = issue.field ? chalk.gray(`[${issue.field}]`) : "";
210
+ return ` ${icon} ${issue.message} ${field}`;
211
+ }
212
+
213
+ /**
214
+ * Format agent validation result for display
215
+ */
216
+ function formatAgentResult(result: AgentValidationResult): string {
217
+ const lines: string[] = [];
218
+
219
+ const statusIcon = result.valid ? chalk.green("āœ“") : chalk.red("āœ—");
220
+
221
+ const statusText = result.valid ? chalk.green("valid") : chalk.red("invalid");
222
+
223
+ lines.push(`${statusIcon} ${chalk.cyan(result.slug)} - ${statusText}`);
224
+ lines.push(chalk.gray(` ${result.sourcePath}`));
225
+
226
+ // Show errors
227
+ if (result.errors.length > 0) {
228
+ for (const err of result.errors) {
229
+ lines.push(formatIssue(err));
230
+ }
231
+ }
232
+
233
+ // Show warnings
234
+ if (result.warnings.length > 0) {
235
+ for (const warn of result.warnings) {
236
+ lines.push(formatIssue(warn));
237
+ }
238
+ }
239
+
240
+ return lines.join("\n");
241
+ }
242
+
243
+ /**
244
+ * Format validation summary for display
245
+ */
246
+ function formatSummary(summary: ValidationSummary, strict: boolean): string {
247
+ const lines: string[] = [];
248
+
249
+ lines.push(chalk.bold("\nšŸ“‹ Validation Summary"));
250
+ lines.push("");
251
+
252
+ for (const result of summary.results) {
253
+ lines.push(formatAgentResult(result));
254
+ lines.push("");
255
+ }
256
+
257
+ // Summary stats
258
+ const validText = chalk.green(`${summary.valid} valid`);
259
+ const invalidText =
260
+ summary.invalid > 0
261
+ ? chalk.red(`${summary.invalid} invalid`)
262
+ : chalk.gray(`${summary.invalid} invalid`);
263
+ const warningText =
264
+ summary.warnings > 0
265
+ ? chalk.yellow(`${summary.warnings} warnings`)
266
+ : chalk.gray(`${summary.warnings} warnings`);
267
+
268
+ lines.push(`Total: ${summary.total} agents`);
269
+ lines.push(`${validText} | ${invalidText} | ${warningText}`);
270
+
271
+ // Overall status
272
+ const hasErrors = summary.invalid > 0;
273
+ const hasWarnings = summary.warnings > 0;
274
+
275
+ if (hasErrors) {
276
+ lines.push("");
277
+ lines.push(chalk.red("āŒ Validation failed - fix errors above"));
278
+ } else if (strict && hasWarnings) {
279
+ lines.push("");
280
+ lines.push(chalk.yellow("āš ļø Validation failed (strict mode) - fix warnings above"));
281
+ } else {
282
+ lines.push("");
283
+ lines.push(chalk.green("āœ… All agents valid"));
284
+ }
285
+
286
+ return lines.join("\n");
287
+ }
288
+
289
+ // =============================================================================
290
+ // Command Handler
291
+ // =============================================================================
292
+
293
+ /**
294
+ * Validate a single target (file path or slug)
295
+ */
296
+ async function validateTarget(target: string): Promise<AgentValidationResult | null> {
297
+ // Check if it's a file path
298
+ const isFilePath =
299
+ target.includes(path.sep) ||
300
+ target.endsWith(".md") ||
301
+ target.endsWith(".yaml") ||
302
+ target.endsWith(".yml");
303
+
304
+ if (isFilePath) {
305
+ return validateAgentFile(target);
306
+ }
307
+
308
+ // It's a slug - find the agent
309
+ const discovery = new AgentDiscovery({ watchEnabled: false });
310
+ await discovery.discover();
311
+ const agent = discovery.get(target);
312
+
313
+ if (!agent) {
314
+ return null;
315
+ }
316
+
317
+ return validateAgent(agent.definition, agent.sourcePath);
318
+ }
319
+
320
+ /**
321
+ * Validate all discovered agents
322
+ */
323
+ async function validateAllAgents(): Promise<AgentValidationResult[]> {
324
+ const discovery = new AgentDiscovery({ watchEnabled: false });
325
+ await discovery.discover();
326
+ const allAgents = discovery.getAll();
327
+
328
+ const results: AgentValidationResult[] = [];
329
+ for (const [, discovered] of allAgents) {
330
+ results.push(validateAgent(discovered.definition, discovered.sourcePath));
331
+ }
332
+ return results;
333
+ }
334
+
335
+ /**
336
+ * Handle validate subcommand
337
+ *
338
+ * Validates all or specific custom agent definition files.
339
+ *
340
+ * @param options - Validate options
341
+ * @returns Command result with exit code 0 (success) or 1 (failure)
342
+ */
343
+ export async function handleValidate(options: ValidateOptions = {}): Promise<CommandResult> {
344
+ try {
345
+ let results: AgentValidationResult[];
346
+
347
+ if (options.target) {
348
+ const result = await validateTarget(options.target);
349
+ if (!result) {
350
+ return error("RESOURCE_NOT_FOUND", `Agent not found: ${options.target}`, [
351
+ "Use /custom-agents list to see available agents",
352
+ ]);
353
+ }
354
+ results = [result];
355
+ } else {
356
+ results = await validateAllAgents();
357
+ if (results.length === 0) {
358
+ return success(chalk.yellow("No custom agents found to validate"));
359
+ }
360
+ }
361
+
362
+ // Build summary
363
+ const summary: ValidationSummary = {
364
+ total: results.length,
365
+ valid: results.filter((r) => r.valid).length,
366
+ invalid: results.filter((r) => !r.valid).length,
367
+ warnings: results.reduce((acc, r) => acc + r.warnings.length, 0),
368
+ results,
369
+ };
370
+
371
+ // Determine success/failure
372
+ const hasErrors = summary.invalid > 0;
373
+ const hasWarnings = summary.warnings > 0;
374
+ const failed = hasErrors || (options.strict && hasWarnings);
375
+
376
+ // Return result
377
+ const output = formatSummary(summary, options.strict ?? false);
378
+
379
+ if (failed) {
380
+ return { kind: "error", code: "INTERNAL_ERROR", message: output };
381
+ }
382
+
383
+ return success(output);
384
+ } catch (err) {
385
+ const message = err instanceof Error ? err.message : String(err);
386
+ return error("INTERNAL_ERROR", `Validation failed: ${message}`);
387
+ }
388
+ }