@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,579 @@
1
+ /**
2
+ * Command Executor
3
+ *
4
+ * Executes slash commands with:
5
+ * - Command resolution via registry
6
+ * - Argument validation against command definitions
7
+ * - Unknown command handling with suggestions
8
+ * - Context creation via provider pattern
9
+ *
10
+ * @module cli/commands/executor
11
+ */
12
+
13
+ import { CommandParser, isParseError } from "./parser.js";
14
+ import type { CommandRegistry } from "./registry.js";
15
+ import type {
16
+ ArgType,
17
+ CommandContext,
18
+ CommandError,
19
+ CommandErrorCode,
20
+ CommandResult,
21
+ NamedArg,
22
+ ParsedArgs,
23
+ PositionalArg,
24
+ SlashCommand,
25
+ } from "./types.js";
26
+
27
+ // =============================================================================
28
+ // T018: CommandContextProvider Interface
29
+ // =============================================================================
30
+
31
+ /**
32
+ * Provider interface for creating CommandContext instances
33
+ *
34
+ * Abstracts context creation to allow dependency injection and testing.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const contextProvider: CommandContextProvider = {
39
+ * createContext(parsedArgs, signal) {
40
+ * return {
41
+ * session: currentSession,
42
+ * credentials: credentialManager,
43
+ * toolRegistry: tools,
44
+ * parsedArgs,
45
+ * signal,
46
+ * emit: eventEmitter.emit.bind(eventEmitter),
47
+ * };
48
+ * },
49
+ * };
50
+ * ```
51
+ */
52
+ export interface CommandContextProvider {
53
+ /**
54
+ * Create a CommandContext for command execution
55
+ *
56
+ * @param parsedArgs - Parsed command arguments
57
+ * @param signal - Optional abort signal for cancellation
58
+ * @returns CommandContext with all dependencies
59
+ */
60
+ createContext(parsedArgs: ParsedArgs, signal?: AbortSignal): CommandContext;
61
+ }
62
+
63
+ // =============================================================================
64
+ // T020: Levenshtein Distance for Suggestions
65
+ // =============================================================================
66
+
67
+ /**
68
+ * Initialize Levenshtein distance matrix
69
+ */
70
+ function initLevenshteinMatrix(rows: number, cols: number): number[][] {
71
+ const matrix: number[][] = [];
72
+ for (let i = 0; i < rows; i++) {
73
+ const row: number[] = [];
74
+ for (let j = 0; j < cols; j++) {
75
+ if (i === 0) {
76
+ row.push(j);
77
+ } else if (j === 0) {
78
+ row.push(i);
79
+ } else {
80
+ row.push(0);
81
+ }
82
+ }
83
+ matrix.push(row);
84
+ }
85
+ return matrix;
86
+ }
87
+
88
+ /**
89
+ * Fill Levenshtein distance matrix with computed values
90
+ */
91
+ function fillLevenshteinMatrix(matrix: number[][], a: string, b: string): void {
92
+ const rows = a.length + 1;
93
+ const cols = b.length + 1;
94
+
95
+ for (let i = 1; i < rows; i++) {
96
+ const currentRow = matrix[i];
97
+ const prevRow = matrix[i - 1];
98
+ if (!currentRow || !prevRow) continue;
99
+
100
+ for (let j = 1; j < cols; j++) {
101
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
102
+ const deletion = (prevRow[j] ?? 0) + 1;
103
+ const insertion = (currentRow[j - 1] ?? 0) + 1;
104
+ const substitution = (prevRow[j - 1] ?? 0) + cost;
105
+ currentRow[j] = Math.min(deletion, insertion, substitution);
106
+ }
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Calculate Levenshtein distance between two strings
112
+ *
113
+ * Used for finding similar command names when a command is not found.
114
+ *
115
+ * @param a - First string
116
+ * @param b - Second string
117
+ * @returns Edit distance (minimum edits to transform a to b)
118
+ */
119
+ function levenshtein(a: string, b: string): number {
120
+ const rows = a.length + 1;
121
+ const cols = b.length + 1;
122
+
123
+ const matrix = initLevenshteinMatrix(rows, cols);
124
+ fillLevenshteinMatrix(matrix, a, b);
125
+
126
+ return matrix[a.length]?.[b.length] ?? 0;
127
+ }
128
+
129
+ /**
130
+ * Find similar commands using Levenshtein distance
131
+ *
132
+ * @param target - Command name that wasn't found
133
+ * @param commands - List of available commands
134
+ * @param maxSuggestions - Maximum suggestions to return (default: 3)
135
+ * @param maxDistance - Maximum edit distance to consider (default: 3)
136
+ * @returns Array of similar command names, sorted by similarity
137
+ */
138
+ function findSimilarCommands(
139
+ target: string,
140
+ commands: SlashCommand[],
141
+ maxSuggestions = 3,
142
+ maxDistance = 3
143
+ ): string[] {
144
+ const targetLower = target.toLowerCase();
145
+
146
+ const scored: Array<{ name: string; distance: number }> = [];
147
+
148
+ for (const cmd of commands) {
149
+ // Check main name
150
+ const nameLower = cmd.name.toLowerCase();
151
+ const nameDistance = levenshtein(targetLower, nameLower);
152
+
153
+ // Also consider prefix match (e.g., "hel" for "help")
154
+ const isPrefixMatch = nameLower.startsWith(targetLower) || targetLower.startsWith(nameLower);
155
+ const adjustedDistance = isPrefixMatch ? Math.min(nameDistance, 1) : nameDistance;
156
+
157
+ if (adjustedDistance <= maxDistance) {
158
+ scored.push({ name: cmd.name, distance: adjustedDistance });
159
+ }
160
+
161
+ // Check aliases
162
+ if (cmd.aliases) {
163
+ for (const alias of cmd.aliases) {
164
+ const aliasLower = alias.toLowerCase();
165
+ const aliasDistance = levenshtein(targetLower, aliasLower);
166
+ const aliasIsPrefixMatch =
167
+ aliasLower.startsWith(targetLower) || targetLower.startsWith(aliasLower);
168
+ const adjustedAliasDistance = aliasIsPrefixMatch
169
+ ? Math.min(aliasDistance, 1)
170
+ : aliasDistance;
171
+
172
+ if (adjustedAliasDistance <= maxDistance) {
173
+ scored.push({ name: cmd.name, distance: adjustedAliasDistance });
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ // Sort by distance (ascending) and remove duplicates
180
+ const seen = new Set<string>();
181
+ return scored
182
+ .sort((a, b) => a.distance - b.distance)
183
+ .filter((item) => {
184
+ if (seen.has(item.name)) return false;
185
+ seen.add(item.name);
186
+ return true;
187
+ })
188
+ .slice(0, maxSuggestions)
189
+ .map((item) => item.name);
190
+ }
191
+
192
+ // =============================================================================
193
+ // T021: Argument Validation
194
+ // =============================================================================
195
+
196
+ /**
197
+ * Validation error details
198
+ */
199
+ interface ValidationError {
200
+ readonly code: CommandErrorCode;
201
+ readonly message: string;
202
+ readonly argName: string;
203
+ }
204
+
205
+ /**
206
+ * Validate a value against an expected type
207
+ *
208
+ * @param value - Value to validate
209
+ * @param expectedType - Expected ArgType
210
+ * @returns true if valid, false otherwise
211
+ */
212
+ function validateArgType(value: unknown, expectedType: ArgType): boolean {
213
+ if (value === undefined || value === null) {
214
+ return false;
215
+ }
216
+
217
+ switch (expectedType) {
218
+ case "string":
219
+ return typeof value === "string";
220
+
221
+ case "number": {
222
+ if (typeof value === "number") return !Number.isNaN(value);
223
+ if (typeof value === "string") {
224
+ const num = Number(value);
225
+ return !Number.isNaN(num);
226
+ }
227
+ return false;
228
+ }
229
+
230
+ case "boolean": {
231
+ if (typeof value === "boolean") return true;
232
+ if (typeof value === "string") {
233
+ const lower = value.toLowerCase();
234
+ return lower === "true" || lower === "false" || lower === "1" || lower === "0";
235
+ }
236
+ return false;
237
+ }
238
+
239
+ case "path":
240
+ // Path is validated as a non-empty string
241
+ // Actual file existence checking is done by the command itself
242
+ return typeof value === "string" && value.length > 0;
243
+
244
+ default:
245
+ return false;
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Convert a value to the expected type
251
+ *
252
+ * @param value - Value to convert
253
+ * @param expectedType - Expected ArgType
254
+ * @returns Converted value
255
+ */
256
+ function coerceArgType(value: string | boolean, expectedType: ArgType): unknown {
257
+ if (expectedType === "number" && typeof value === "string") {
258
+ return Number(value);
259
+ }
260
+
261
+ if (expectedType === "boolean") {
262
+ if (typeof value === "boolean") return value;
263
+ if (typeof value === "string") {
264
+ const lower = value.toLowerCase();
265
+ return lower === "true" || lower === "1";
266
+ }
267
+ }
268
+
269
+ return value;
270
+ }
271
+
272
+ /**
273
+ * Validate positional arguments against command definition
274
+ *
275
+ * @param positional - Parsed positional arguments
276
+ * @param argDefs - Command's positional argument definitions
277
+ * @returns ValidationError if invalid, undefined if valid
278
+ */
279
+ function validatePositionalArgs(
280
+ positional: readonly string[],
281
+ argDefs: readonly PositionalArg[]
282
+ ): ValidationError | undefined {
283
+ for (let i = 0; i < argDefs.length; i++) {
284
+ const def = argDefs[i];
285
+ if (!def) continue;
286
+
287
+ const value = positional[i];
288
+
289
+ // Check required
290
+ if (def.required && value === undefined && def.default === undefined) {
291
+ return {
292
+ code: "MISSING_ARGUMENT",
293
+ message: `Missing required argument: ${def.name}`,
294
+ argName: def.name,
295
+ };
296
+ }
297
+
298
+ // Check type if value provided
299
+ if (value !== undefined && !validateArgType(value, def.type)) {
300
+ return {
301
+ code: "ARGUMENT_TYPE_ERROR",
302
+ message: `Invalid type for argument '${def.name}': expected ${def.type}`,
303
+ argName: def.name,
304
+ };
305
+ }
306
+ }
307
+
308
+ return undefined;
309
+ }
310
+
311
+ /**
312
+ * Validate named arguments against command definition
313
+ *
314
+ * @param named - Parsed named arguments
315
+ * @param argDefs - Command's named argument definitions
316
+ * @returns ValidationError if invalid, undefined if valid
317
+ */
318
+ function validateNamedArgs(
319
+ named: ReadonlyMap<string, string | boolean>,
320
+ argDefs: readonly NamedArg[]
321
+ ): ValidationError | undefined {
322
+ for (const def of argDefs) {
323
+ const value = named.get(def.name) ?? named.get(def.shorthand ?? "");
324
+
325
+ // Check required
326
+ if (def.required && value === undefined && def.default === undefined) {
327
+ return {
328
+ code: "MISSING_ARGUMENT",
329
+ message: `Missing required argument: --${def.name}`,
330
+ argName: def.name,
331
+ };
332
+ }
333
+
334
+ // Check type if value provided
335
+ if (value !== undefined && !validateArgType(value, def.type)) {
336
+ return {
337
+ code: "ARGUMENT_TYPE_ERROR",
338
+ message: `Invalid type for argument '--${def.name}': expected ${def.type}`,
339
+ argName: def.name,
340
+ };
341
+ }
342
+ }
343
+
344
+ return undefined;
345
+ }
346
+
347
+ /**
348
+ * Build ParsedArgs with type coercion and defaults applied
349
+ *
350
+ * @param command - Command being executed
351
+ * @param parsedCommand - Parsed command from parser
352
+ * @returns ParsedArgs with resolved values
353
+ */
354
+ function buildParsedArgs(
355
+ command: SlashCommand,
356
+ parsedCommand: {
357
+ command: string;
358
+ positional: readonly string[];
359
+ named: ReadonlyMap<string, string | boolean>;
360
+ raw: string;
361
+ }
362
+ ): ParsedArgs {
363
+ // Build positional args with defaults and type coercion
364
+ const positionalValues: unknown[] = [];
365
+ const positionalDefs = command.positionalArgs ?? [];
366
+
367
+ for (let i = 0; i < positionalDefs.length; i++) {
368
+ const def = positionalDefs[i];
369
+ if (!def) continue;
370
+
371
+ const rawValue = parsedCommand.positional[i];
372
+
373
+ if (rawValue !== undefined) {
374
+ positionalValues.push(coerceArgType(rawValue, def.type));
375
+ } else if (def.default !== undefined) {
376
+ positionalValues.push(def.default);
377
+ } else {
378
+ positionalValues.push(undefined);
379
+ }
380
+ }
381
+
382
+ // Add any extra positional args beyond the definition
383
+ for (let i = positionalDefs.length; i < parsedCommand.positional.length; i++) {
384
+ positionalValues.push(parsedCommand.positional[i]);
385
+ }
386
+
387
+ // Build named args with defaults and type coercion
388
+ const namedValues: Record<string, unknown> = {};
389
+ const namedDefs = command.namedArgs ?? [];
390
+
391
+ // First, set defaults
392
+ for (const def of namedDefs) {
393
+ if (def.default !== undefined) {
394
+ namedValues[def.name] = def.default;
395
+ }
396
+ }
397
+
398
+ // Then, apply provided values with type coercion
399
+ for (const [key, value] of parsedCommand.named.entries()) {
400
+ // Find the definition for this arg (by name or shorthand)
401
+ const def = namedDefs.find((d) => d.name === key || d.shorthand === key);
402
+
403
+ if (def) {
404
+ namedValues[def.name] = coerceArgType(value, def.type);
405
+ } else {
406
+ // Unknown flag, pass through as-is
407
+ namedValues[key] = value;
408
+ }
409
+ }
410
+
411
+ return {
412
+ command: parsedCommand.command,
413
+ positional: positionalValues,
414
+ named: namedValues,
415
+ raw: parsedCommand.raw,
416
+ };
417
+ }
418
+
419
+ // =============================================================================
420
+ // T019: CommandExecutor Class
421
+ // =============================================================================
422
+
423
+ /**
424
+ * Command executor for slash commands
425
+ *
426
+ * Orchestrates command execution:
427
+ * 1. Parse input string
428
+ * 2. Resolve command from registry
429
+ * 3. Validate arguments
430
+ * 4. Create execution context
431
+ * 5. Execute command handler
432
+ *
433
+ * @example
434
+ * ```typescript
435
+ * const executor = new CommandExecutor(registry, contextProvider);
436
+ *
437
+ * // Execute a command
438
+ * const result = await executor.execute('/help');
439
+ *
440
+ * // With abort signal
441
+ * const controller = new AbortController();
442
+ * const result = await executor.execute('/long-task', controller.signal);
443
+ * ```
444
+ */
445
+ export class CommandExecutor {
446
+ private readonly registry: CommandRegistry;
447
+ private readonly contextProvider: CommandContextProvider;
448
+ private readonly parser: CommandParser;
449
+
450
+ /**
451
+ * Create a new CommandExecutor
452
+ *
453
+ * @param registry - Command registry for command lookup
454
+ * @param contextProvider - Provider for creating execution contexts
455
+ */
456
+ constructor(registry: CommandRegistry, contextProvider: CommandContextProvider) {
457
+ this.registry = registry;
458
+ this.contextProvider = contextProvider;
459
+ this.parser = new CommandParser();
460
+ }
461
+
462
+ /**
463
+ * Execute a command from input string
464
+ *
465
+ * @param input - Raw command input (e.g., "/help --verbose")
466
+ * @param signal - Optional abort signal for cancellation
467
+ * @returns Command execution result
468
+ *
469
+ * @example
470
+ * ```typescript
471
+ * const result = await executor.execute('/login anthropic --store keychain');
472
+ *
473
+ * switch (result.kind) {
474
+ * case 'success':
475
+ * console.log('Command succeeded:', result.message);
476
+ * break;
477
+ * case 'error':
478
+ * console.error(`[${result.code}] ${result.message}`);
479
+ * break;
480
+ * }
481
+ * ```
482
+ */
483
+ async execute(input: string, signal?: AbortSignal): Promise<CommandResult> {
484
+ // Step 1: Parse input
485
+ const parseResult = this.parser.parse(input);
486
+
487
+ if (isParseError(parseResult)) {
488
+ return {
489
+ kind: "error",
490
+ code: parseResult.code,
491
+ message: parseResult.message,
492
+ };
493
+ }
494
+
495
+ // Step 2: Get command from registry
496
+ const command = this.registry.get(parseResult.command);
497
+
498
+ if (!command) {
499
+ return this.createUnknownCommandError(parseResult.command);
500
+ }
501
+
502
+ // Step 3: Validate arguments
503
+ const validationError = this.validateArguments(command, parseResult);
504
+
505
+ if (validationError) {
506
+ return {
507
+ kind: "error",
508
+ code: validationError.code,
509
+ message: validationError.message,
510
+ helpCommand: `/help ${command.name}`,
511
+ };
512
+ }
513
+
514
+ // Step 4: Build parsed args with type coercion
515
+ const parsedArgs = buildParsedArgs(command, parseResult);
516
+
517
+ // Step 5: Create context
518
+ const context = this.contextProvider.createContext(parsedArgs, signal);
519
+
520
+ // Step 6: Execute command
521
+ try {
522
+ return await command.execute(context);
523
+ } catch (error) {
524
+ return {
525
+ kind: "error",
526
+ code: "INTERNAL_ERROR",
527
+ message: error instanceof Error ? error.message : "Unknown error occurred",
528
+ };
529
+ }
530
+ }
531
+
532
+ /**
533
+ * Create error for unknown command with suggestions
534
+ */
535
+ private createUnknownCommandError(commandName: string): CommandError {
536
+ const allCommands = this.registry.list();
537
+ const suggestions = findSimilarCommands(commandName, allCommands);
538
+
539
+ return {
540
+ kind: "error",
541
+ code: "COMMAND_NOT_FOUND",
542
+ message: `Unknown command: /${commandName}`,
543
+ suggestions: suggestions.length > 0 ? suggestions.map((s) => `/${s}`) : undefined,
544
+ helpCommand: "/help",
545
+ };
546
+ }
547
+
548
+ /**
549
+ * Validate arguments against command definition
550
+ */
551
+ private validateArguments(
552
+ command: SlashCommand,
553
+ parsedCommand: {
554
+ positional: readonly string[];
555
+ named: ReadonlyMap<string, string | boolean>;
556
+ }
557
+ ): ValidationError | undefined {
558
+ // Validate positional args
559
+ if (command.positionalArgs) {
560
+ const positionalError = validatePositionalArgs(
561
+ parsedCommand.positional,
562
+ command.positionalArgs
563
+ );
564
+ if (positionalError) {
565
+ return positionalError;
566
+ }
567
+ }
568
+
569
+ // Validate named args
570
+ if (command.namedArgs) {
571
+ const namedError = validateNamedArgs(parsedCommand.named, command.namedArgs);
572
+ if (namedError) {
573
+ return namedError;
574
+ }
575
+ }
576
+
577
+ return undefined;
578
+ }
579
+ }