@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,621 @@
1
+ /**
2
+ * Prompt Validate Command
3
+ *
4
+ * Validates prompt files across all customization directories.
5
+ *
6
+ * Usage:
7
+ * - `vellum prompt validate` - Validate all prompt sources
8
+ * - `vellum prompt validate --fix` - Auto-fix simple issues
9
+ *
10
+ * @module cli/commands/prompt/validate
11
+ * @see REQ-016
12
+ */
13
+
14
+ import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
15
+ import { extname, join, relative } from "node:path";
16
+
17
+ import chalk from "chalk";
18
+
19
+ import { ICONS } from "../../utils/icons.js";
20
+ import { EXIT_CODES } from "../exit-codes.js";
21
+ import type { CommandContext, CommandResult, SlashCommand } from "../types.js";
22
+ import { error, success } from "../types.js";
23
+
24
+ // =============================================================================
25
+ // Constants
26
+ // =============================================================================
27
+
28
+ /**
29
+ * Directories to scan for prompts
30
+ */
31
+ const PROMPT_DIRECTORIES = [
32
+ { name: "prompts", path: ".vellum/prompts" },
33
+ { name: "commands", path: ".vellum/commands" },
34
+ { name: "workflows", path: ".vellum/workflows" },
35
+ { name: "skills", path: ".vellum/skills" },
36
+ { name: "rules", path: ".vellum/rules" },
37
+ ] as const;
38
+
39
+ /**
40
+ * Valid file extensions for prompt files
41
+ */
42
+ const VALID_EXTENSIONS = new Set([".md", ".markdown"]);
43
+
44
+ /**
45
+ * YAML frontmatter pattern
46
+ */
47
+ const FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---/;
48
+
49
+ // =============================================================================
50
+ // Types
51
+ // =============================================================================
52
+
53
+ /**
54
+ * Validation issue severity
55
+ */
56
+ export type ValidationSeverity = "error" | "warning" | "info";
57
+
58
+ /**
59
+ * Single validation issue
60
+ */
61
+ export interface ValidationIssue {
62
+ /** Issue severity */
63
+ severity: ValidationSeverity;
64
+ /** File path relative to project root */
65
+ file: string;
66
+ /** Line number (1-based) */
67
+ line?: number;
68
+ /** Column number (1-based) */
69
+ column?: number;
70
+ /** Issue code for programmatic handling */
71
+ code: string;
72
+ /** Human-readable message */
73
+ message: string;
74
+ /** Whether this issue can be auto-fixed */
75
+ fixable: boolean;
76
+ }
77
+
78
+ /**
79
+ * Options for validate command
80
+ */
81
+ export interface PromptValidateOptions {
82
+ /** Auto-fix simple issues */
83
+ fix?: boolean;
84
+ /** Show verbose output */
85
+ verbose?: boolean;
86
+ /** Output as JSON */
87
+ json?: boolean;
88
+ /** Working directory (defaults to process.cwd()) */
89
+ cwd?: string;
90
+ }
91
+
92
+ /**
93
+ * Result of validation
94
+ */
95
+ export interface PromptValidateResult {
96
+ /** Whether validation passed (no errors) */
97
+ valid: boolean;
98
+ /** Total files scanned */
99
+ filesScanned: number;
100
+ /** All issues found */
101
+ issues: ValidationIssue[];
102
+ /** Number of issues fixed (if --fix) */
103
+ issuesFixed: number;
104
+ /** Exit code */
105
+ exitCode: number;
106
+ }
107
+
108
+ // =============================================================================
109
+ // Validation Helpers
110
+ // =============================================================================
111
+
112
+ /**
113
+ * Check for trailing whitespace
114
+ */
115
+ function checkTrailingWhitespace(content: string, file: string): ValidationIssue[] {
116
+ const issues: ValidationIssue[] = [];
117
+ const lines = content.split("\n");
118
+
119
+ for (let i = 0; i < lines.length; i++) {
120
+ const line = lines[i];
121
+ if (!line) continue;
122
+ if (line !== line.trimEnd()) {
123
+ issues.push({
124
+ severity: "warning",
125
+ file,
126
+ line: i + 1,
127
+ code: "trailing-whitespace",
128
+ message: "Trailing whitespace",
129
+ fixable: true,
130
+ });
131
+ }
132
+ }
133
+
134
+ return issues;
135
+ }
136
+
137
+ /**
138
+ * Check for missing newline at EOF
139
+ */
140
+ function checkMissingNewlineAtEof(content: string, file: string): ValidationIssue | null {
141
+ if (content.length > 0 && !content.endsWith("\n")) {
142
+ return {
143
+ severity: "warning",
144
+ file,
145
+ line: content.split("\n").length,
146
+ code: "missing-newline-eof",
147
+ message: "Missing newline at end of file",
148
+ fixable: true,
149
+ };
150
+ }
151
+ return null;
152
+ }
153
+
154
+ /**
155
+ * Check for valid frontmatter
156
+ */
157
+ function checkFrontmatter(content: string, file: string): ValidationIssue[] {
158
+ const issues: ValidationIssue[] = [];
159
+
160
+ // Check if file starts with frontmatter
161
+ if (!content.startsWith("---")) {
162
+ issues.push({
163
+ severity: "error",
164
+ file,
165
+ line: 1,
166
+ code: "missing-frontmatter",
167
+ message: "File must start with YAML frontmatter (---)",
168
+ fixable: false,
169
+ });
170
+ return issues;
171
+ }
172
+
173
+ // Check if frontmatter is properly closed
174
+ const match = content.match(FRONTMATTER_PATTERN);
175
+ if (!match) {
176
+ issues.push({
177
+ severity: "error",
178
+ file,
179
+ line: 1,
180
+ code: "unclosed-frontmatter",
181
+ message: "YAML frontmatter is not properly closed",
182
+ fixable: false,
183
+ });
184
+ return issues;
185
+ }
186
+
187
+ // Parse frontmatter for basic validation
188
+ const frontmatterContent = match[1];
189
+ if (!frontmatterContent) return issues;
190
+ const frontmatterLines = frontmatterContent.split("\n");
191
+
192
+ // Check for inconsistent indentation
193
+ let expectedIndent: string | null = null;
194
+ for (let i = 0; i < frontmatterLines.length; i++) {
195
+ const line = frontmatterLines[i];
196
+ if (!line || line.trim() === "") continue;
197
+
198
+ // Check for tabs vs spaces
199
+ const leadingWhitespace = line.match(/^(\s*)/)?.[1] ?? "";
200
+ if (leadingWhitespace.includes("\t") && leadingWhitespace.includes(" ")) {
201
+ issues.push({
202
+ severity: "warning",
203
+ file,
204
+ line: i + 2, // +2 because frontmatter starts at line 2
205
+ code: "mixed-indentation",
206
+ message: "Mixed tabs and spaces in indentation",
207
+ fixable: true,
208
+ });
209
+ }
210
+
211
+ // Detect indent style
212
+ if (leadingWhitespace.length > 0 && expectedIndent === null) {
213
+ expectedIndent = leadingWhitespace.includes("\t") ? "\t" : " ";
214
+ }
215
+ }
216
+
217
+ // Check for required fields based on file location
218
+ const hasName = frontmatterContent.includes("name:");
219
+ const hasId = frontmatterContent.includes("id:");
220
+
221
+ if (!hasName && !hasId) {
222
+ issues.push({
223
+ severity: "warning",
224
+ file,
225
+ line: 1,
226
+ code: "missing-identifier",
227
+ message: "Frontmatter should have 'name' or 'id' field",
228
+ fixable: false,
229
+ });
230
+ }
231
+
232
+ return issues;
233
+ }
234
+
235
+ /**
236
+ * Check for duplicate keys in YAML frontmatter
237
+ */
238
+ function checkDuplicateKeys(content: string, file: string): ValidationIssue[] {
239
+ const issues: ValidationIssue[] = [];
240
+ const match = content.match(FRONTMATTER_PATTERN);
241
+ if (!match || !match[1]) return issues;
242
+
243
+ const frontmatterContent = match[1];
244
+ const lines = frontmatterContent.split("\n");
245
+ const seenKeys = new Map<string, number>();
246
+
247
+ for (let i = 0; i < lines.length; i++) {
248
+ const line = lines[i];
249
+ if (!line) continue;
250
+ const keyMatch = line.match(/^(\w+):/);
251
+ if (keyMatch?.[1]) {
252
+ const key = keyMatch[1];
253
+ const prevLine = seenKeys.get(key);
254
+ if (prevLine !== undefined) {
255
+ issues.push({
256
+ severity: "error",
257
+ file,
258
+ line: i + 2,
259
+ code: "duplicate-key",
260
+ message: `Duplicate key '${key}' (first occurrence at line ${prevLine})`,
261
+ fixable: false,
262
+ });
263
+ } else {
264
+ seenKeys.set(key, i + 2);
265
+ }
266
+ }
267
+ }
268
+
269
+ return issues;
270
+ }
271
+
272
+ // =============================================================================
273
+ // Auto-Fix Functions
274
+ // =============================================================================
275
+
276
+ /**
277
+ * Fix trailing whitespace in content
278
+ */
279
+ function fixTrailingWhitespace(content: string): string {
280
+ return content
281
+ .split("\n")
282
+ .map((line) => line.trimEnd())
283
+ .join("\n");
284
+ }
285
+
286
+ /**
287
+ * Fix missing newline at EOF
288
+ */
289
+ function fixMissingNewlineAtEof(content: string): string {
290
+ if (content.length > 0 && !content.endsWith("\n")) {
291
+ return `${content}\n`;
292
+ }
293
+ return content;
294
+ }
295
+
296
+ /**
297
+ * Fix mixed indentation (convert tabs to spaces)
298
+ */
299
+ function fixMixedIndentation(content: string): string {
300
+ const match = content.match(FRONTMATTER_PATTERN);
301
+ if (!match || !match[1]) return content;
302
+
303
+ const frontmatterContent = match[1];
304
+ const fixedFrontmatter = frontmatterContent.replace(/\t/g, " ");
305
+
306
+ return content.replace(frontmatterContent, fixedFrontmatter);
307
+ }
308
+
309
+ /**
310
+ * Apply all auto-fixes to content
311
+ */
312
+ function applyFixes(content: string, issues: ValidationIssue[]): string {
313
+ let fixed = content;
314
+
315
+ const hasMixedIndent = issues.some((i) => i.code === "mixed-indentation");
316
+ const hasTrailingWhitespace = issues.some((i) => i.code === "trailing-whitespace");
317
+ const hasMissingNewline = issues.some((i) => i.code === "missing-newline-eof");
318
+
319
+ if (hasMixedIndent) {
320
+ fixed = fixMixedIndentation(fixed);
321
+ }
322
+
323
+ if (hasTrailingWhitespace) {
324
+ fixed = fixTrailingWhitespace(fixed);
325
+ }
326
+
327
+ if (hasMissingNewline) {
328
+ fixed = fixMissingNewlineAtEof(fixed);
329
+ }
330
+
331
+ return fixed;
332
+ }
333
+
334
+ // =============================================================================
335
+ // File Scanning
336
+ // =============================================================================
337
+
338
+ /**
339
+ * Get all markdown files in a directory recursively
340
+ */
341
+ function getMarkdownFiles(dir: string, rootDir: string): string[] {
342
+ if (!existsSync(dir)) {
343
+ return [];
344
+ }
345
+
346
+ const files: string[] = [];
347
+ const entries = readdirSync(dir);
348
+
349
+ for (const entry of entries) {
350
+ const fullPath = join(dir, entry);
351
+ const stat = statSync(fullPath);
352
+
353
+ if (stat.isDirectory()) {
354
+ files.push(...getMarkdownFiles(fullPath, rootDir));
355
+ } else if (stat.isFile() && VALID_EXTENSIONS.has(extname(entry))) {
356
+ files.push(fullPath);
357
+ }
358
+ }
359
+
360
+ return files;
361
+ }
362
+
363
+ /**
364
+ * Validate a single file
365
+ */
366
+ function validateFile(filePath: string, rootDir: string): ValidationIssue[] {
367
+ const relativePath = relative(rootDir, filePath);
368
+ const content = readFileSync(filePath, "utf-8");
369
+ const issues: ValidationIssue[] = [];
370
+
371
+ // Run all checks
372
+ issues.push(...checkFrontmatter(content, relativePath));
373
+ issues.push(...checkTrailingWhitespace(content, relativePath));
374
+ issues.push(...checkDuplicateKeys(content, relativePath));
375
+
376
+ const newlineIssue = checkMissingNewlineAtEof(content, relativePath);
377
+ if (newlineIssue) {
378
+ issues.push(newlineIssue);
379
+ }
380
+
381
+ return issues;
382
+ }
383
+
384
+ // =============================================================================
385
+ // Command Execution
386
+ // =============================================================================
387
+
388
+ /**
389
+ * Execute prompt validate command
390
+ *
391
+ * @param options - Command options
392
+ * @returns Validation result
393
+ */
394
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Validation requires checking multiple rules and fixing issues
395
+ export async function executePromptValidate(
396
+ options: PromptValidateOptions = {}
397
+ ): Promise<PromptValidateResult> {
398
+ const rootDir = options.cwd ?? process.cwd();
399
+ const allIssues: ValidationIssue[] = [];
400
+ const filesToFix = new Map<string, ValidationIssue[]>();
401
+ let filesScanned = 0;
402
+ let issuesFixed = 0;
403
+
404
+ // Scan all prompt directories
405
+ for (const { name, path } of PROMPT_DIRECTORIES) {
406
+ const dirPath = join(rootDir, path);
407
+
408
+ if (!existsSync(dirPath)) {
409
+ if (options.verbose) {
410
+ console.log(chalk.gray(`Skipping ${name}: ${path} not found`));
411
+ }
412
+ continue;
413
+ }
414
+
415
+ const files = getMarkdownFiles(dirPath, rootDir);
416
+
417
+ if (options.verbose) {
418
+ console.log(chalk.blue(`\nScanning ${name}/ (${files.length} files)`));
419
+ }
420
+
421
+ for (const file of files) {
422
+ filesScanned++;
423
+ const issues = validateFile(file, rootDir);
424
+
425
+ if (issues.length > 0) {
426
+ allIssues.push(...issues);
427
+
428
+ // Collect fixable issues per file
429
+ const fixableIssues = issues.filter((i) => i.fixable);
430
+ if (fixableIssues.length > 0) {
431
+ filesToFix.set(file, fixableIssues);
432
+ }
433
+ }
434
+ }
435
+ }
436
+
437
+ // Apply fixes if requested
438
+ if (options.fix && filesToFix.size > 0) {
439
+ for (const [filePath, issues] of filesToFix) {
440
+ const content = readFileSync(filePath, "utf-8");
441
+ const fixed = applyFixes(content, issues);
442
+
443
+ if (fixed !== content) {
444
+ writeFileSync(filePath, fixed, "utf-8");
445
+ issuesFixed += issues.length;
446
+
447
+ if (options.verbose) {
448
+ const relativePath = relative(rootDir, filePath);
449
+ console.log(chalk.green(` Fixed: ${relativePath}`));
450
+ }
451
+ }
452
+ }
453
+ }
454
+
455
+ // Calculate results
456
+ const errors = allIssues.filter((i) => i.severity === "error");
457
+ const warnings = allIssues.filter((i) => i.severity === "warning");
458
+ const unfixedIssues = allIssues.filter((i) => !i.fixable || !options.fix);
459
+ const hasErrors = errors.length > 0;
460
+
461
+ // Output results
462
+ if (options.json) {
463
+ console.log(
464
+ JSON.stringify(
465
+ {
466
+ valid: !hasErrors,
467
+ filesScanned,
468
+ errors: errors.length,
469
+ warnings: warnings.length,
470
+ issuesFixed,
471
+ issues: allIssues,
472
+ },
473
+ null,
474
+ 2
475
+ )
476
+ );
477
+ } else {
478
+ // Display issues in file:line format
479
+ if (unfixedIssues.length > 0 || !options.fix) {
480
+ console.log(chalk.bold(`\n${ICONS.workflow} Validation Results\n`));
481
+
482
+ for (const issue of allIssues) {
483
+ if (options.fix && issue.fixable) continue;
484
+
485
+ const location = issue.line ? `:${issue.line}` : "";
486
+ const prefix =
487
+ issue.severity === "error"
488
+ ? chalk.red("x")
489
+ : issue.severity === "warning"
490
+ ? chalk.yellow(ICONS.warning)
491
+ : chalk.blue(ICONS.info);
492
+
493
+ console.log(`${prefix} ${issue.file}${location}: ${issue.message} (${issue.code})`);
494
+ }
495
+ }
496
+
497
+ // Summary
498
+ console.log(chalk.bold("\nSummary"));
499
+ console.log(chalk.gray(` Files scanned: ${filesScanned}`));
500
+ console.log(chalk.gray(` Errors: ${errors.length}`));
501
+ console.log(chalk.gray(` Warnings: ${warnings.length}`));
502
+
503
+ if (options.fix && issuesFixed > 0) {
504
+ console.log(chalk.green(` Issues fixed: ${issuesFixed}`));
505
+ }
506
+
507
+ if (hasErrors) {
508
+ console.log(chalk.red(`\n${ICONS.error} Validation failed`));
509
+ } else if (warnings.length > 0) {
510
+ console.log(chalk.yellow(`\n${ICONS.warning} Validation passed with warnings`));
511
+ } else {
512
+ console.log(chalk.green(`\n${ICONS.success} Validation passed`));
513
+ }
514
+ }
515
+
516
+ return {
517
+ valid: !hasErrors,
518
+ filesScanned,
519
+ issues: allIssues,
520
+ issuesFixed,
521
+ exitCode: hasErrors ? EXIT_CODES.ERROR : EXIT_CODES.SUCCESS,
522
+ };
523
+ }
524
+
525
+ // =============================================================================
526
+ // Slash Command Definition
527
+ // =============================================================================
528
+
529
+ /**
530
+ * Prompt validate slash command for TUI
531
+ */
532
+ export const promptValidateCommand: SlashCommand = {
533
+ name: "prompt-validate",
534
+ description: "Validate prompt files in .vellum/ directories",
535
+ kind: "builtin",
536
+ category: "config",
537
+ aliases: ["validate-prompts"],
538
+ namedArgs: [
539
+ {
540
+ name: "fix",
541
+ shorthand: "f",
542
+ type: "boolean",
543
+ description: "Auto-fix simple issues (trailing whitespace, etc.)",
544
+ required: false,
545
+ default: false,
546
+ },
547
+ {
548
+ name: "verbose",
549
+ shorthand: "v",
550
+ type: "boolean",
551
+ description: "Show verbose output",
552
+ required: false,
553
+ default: false,
554
+ },
555
+ {
556
+ name: "json",
557
+ shorthand: "j",
558
+ type: "boolean",
559
+ description: "Output as JSON",
560
+ required: false,
561
+ default: false,
562
+ },
563
+ ],
564
+ examples: [
565
+ "/prompt-validate - Validate all prompts",
566
+ "/prompt-validate --fix - Auto-fix simple issues",
567
+ "/prompt-validate --json - Output as JSON",
568
+ ],
569
+
570
+ execute: async (ctx: CommandContext): Promise<CommandResult> => {
571
+ const fix = ctx.parsedArgs.named.fix as boolean | undefined;
572
+ const verbose = ctx.parsedArgs.named.verbose as boolean | undefined;
573
+ const json = ctx.parsedArgs.named.json as boolean | undefined;
574
+
575
+ const result = await executePromptValidate({
576
+ fix: fix ?? false,
577
+ verbose: verbose ?? false,
578
+ json: json ?? false,
579
+ });
580
+
581
+ if (result.valid) {
582
+ return success(
583
+ `Validated ${result.filesScanned} files. ${result.issuesFixed} issues fixed.`,
584
+ {
585
+ filesScanned: result.filesScanned,
586
+ issues: result.issues,
587
+ issuesFixed: result.issuesFixed,
588
+ }
589
+ );
590
+ }
591
+
592
+ const errorCount = result.issues.filter((i) => i.severity === "error").length;
593
+ return error(
594
+ "INTERNAL_ERROR",
595
+ `Validation failed with ${errorCount} error(s). Run with --verbose for details.`
596
+ );
597
+ },
598
+ };
599
+
600
+ // =============================================================================
601
+ // CLI Entry Point
602
+ // =============================================================================
603
+
604
+ /**
605
+ * Run prompt validate command from CLI
606
+ *
607
+ * @param options - CLI options
608
+ */
609
+ export async function runPromptValidateCli(options: {
610
+ fix?: boolean;
611
+ verbose?: boolean;
612
+ json?: boolean;
613
+ }): Promise<void> {
614
+ const result = await executePromptValidate({
615
+ fix: options.fix ?? false,
616
+ verbose: options.verbose ?? false,
617
+ json: options.json ?? false,
618
+ });
619
+
620
+ process.exit(result.exitCode);
621
+ }