@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,588 @@
1
+ /**
2
+ * Command Parser Module
3
+ *
4
+ * Tokenizes and parses slash command input into structured command data.
5
+ * Handles:
6
+ * - Command name extraction (from / prefix)
7
+ * - Positional arguments
8
+ * - Named arguments (flags: --flag, -f)
9
+ * - Quote handling (double quotes with escapes, single quotes literal)
10
+ *
11
+ * @module cli/commands/parser
12
+ */
13
+
14
+ import type { CommandErrorCode } from "./types.js";
15
+
16
+ // =============================================================================
17
+ // T013: Token Types and Tokenizer
18
+ // =============================================================================
19
+
20
+ /**
21
+ * Token type discriminator
22
+ */
23
+ export type TokenType = "command" | "string" | "flag" | "value" | "whitespace";
24
+
25
+ /**
26
+ * Token produced by the tokenizer
27
+ */
28
+ export interface Token {
29
+ /** Token type for discrimination */
30
+ readonly type: TokenType;
31
+ /** Token value */
32
+ readonly value: string;
33
+ /** Start position in input (0-indexed) */
34
+ readonly start: number;
35
+ /** End position in input (exclusive) */
36
+ readonly end: number;
37
+ }
38
+
39
+ /**
40
+ * Tokenizer namespace for command input
41
+ *
42
+ * Breaks input into typed tokens for parsing.
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const tokens = Tokenizer.tokenize('/help --verbose');
47
+ * // [
48
+ * // { type: 'command', value: 'help', start: 0, end: 5 },
49
+ * // { type: 'whitespace', value: ' ', start: 5, end: 6 },
50
+ * // { type: 'flag', value: '--verbose', start: 6, end: 15 },
51
+ * // ]
52
+ * ```
53
+ */
54
+ // biome-ignore lint/complexity/noStaticOnlyClass: Tokenizer provides a logical grouping for tokenization functionality
55
+ export class Tokenizer {
56
+ /**
57
+ * Tokenize input string into Token array
58
+ *
59
+ * @param input - Raw input string
60
+ * @returns Array of tokens with type discrimination
61
+ * @throws Never throws - returns empty array for empty input
62
+ */
63
+ static tokenize(input: string): Token[] {
64
+ const ctx = new TokenizerContext(input);
65
+
66
+ // Handle leading slash for command
67
+ if (input.startsWith("/")) {
68
+ ctx.tokenizeCommand();
69
+ }
70
+
71
+ // Process rest of input
72
+ while (ctx.position < input.length) {
73
+ ctx.tokenizeNext();
74
+ }
75
+
76
+ return ctx.tokens;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Check if character is whitespace
82
+ */
83
+ function isWhitespace(char: string | undefined): boolean {
84
+ return char === " " || char === "\t" || char === "\n" || char === "\r";
85
+ }
86
+
87
+ /**
88
+ * Internal tokenizer context for managing state
89
+ */
90
+ class TokenizerContext {
91
+ readonly input: string;
92
+ readonly tokens: Token[] = [];
93
+ position = 0;
94
+
95
+ constructor(input: string) {
96
+ this.input = input;
97
+ }
98
+
99
+ /** Current character at position */
100
+ get char(): string | undefined {
101
+ return this.input[this.position];
102
+ }
103
+
104
+ /** Next character */
105
+ get nextChar(): string | undefined {
106
+ return this.input[this.position + 1];
107
+ }
108
+
109
+ /** Tokenize the command name after / */
110
+ tokenizeCommand(): void {
111
+ this.position = 1; // Skip the slash
112
+ const commandStart = this.position;
113
+
114
+ while (this.position < this.input.length && !isWhitespace(this.char)) {
115
+ this.position++;
116
+ }
117
+
118
+ if (this.position > commandStart) {
119
+ this.tokens.push({
120
+ type: "command",
121
+ value: this.input.slice(commandStart, this.position),
122
+ start: 0, // Include the slash in position
123
+ end: this.position,
124
+ });
125
+ }
126
+ }
127
+
128
+ /** Tokenize the next token */
129
+ tokenizeNext(): void {
130
+ // Whitespace
131
+ if (isWhitespace(this.char)) {
132
+ this.tokenizeWhitespace();
133
+ return;
134
+ }
135
+
136
+ // Long flag (--flag)
137
+ if (this.char === "-" && this.nextChar === "-") {
138
+ this.tokenizeLongFlag();
139
+ return;
140
+ }
141
+
142
+ // Short flag (-f)
143
+ if (
144
+ this.char === "-" &&
145
+ this.nextChar &&
146
+ !isWhitespace(this.nextChar) &&
147
+ this.nextChar !== "-"
148
+ ) {
149
+ this.tokenizeShortFlag();
150
+ return;
151
+ }
152
+
153
+ // Quoted string
154
+ if (this.char === '"' || this.char === "'") {
155
+ this.tokenizeQuotedString();
156
+ return;
157
+ }
158
+
159
+ // Unquoted value
160
+ this.tokenizeValue();
161
+ }
162
+
163
+ private tokenizeWhitespace(): void {
164
+ const start = this.position;
165
+ while (this.position < this.input.length && isWhitespace(this.char)) {
166
+ this.position++;
167
+ }
168
+ this.tokens.push({
169
+ type: "whitespace",
170
+ value: this.input.slice(start, this.position),
171
+ start,
172
+ end: this.position,
173
+ });
174
+ }
175
+
176
+ private tokenizeLongFlag(): void {
177
+ const start = this.position;
178
+ this.position += 2;
179
+ while (this.position < this.input.length && !isWhitespace(this.char) && this.char !== "=") {
180
+ this.position++;
181
+ }
182
+ this.tokens.push({
183
+ type: "flag",
184
+ value: this.input.slice(start, this.position),
185
+ start,
186
+ end: this.position,
187
+ });
188
+
189
+ // Handle --flag=value
190
+ if (this.char === "=") {
191
+ this.position++; // Skip =
192
+ this.tokenizeFlagValue();
193
+ }
194
+ }
195
+
196
+ private tokenizeShortFlag(): void {
197
+ const start = this.position;
198
+ this.position += 2; // -f
199
+ this.tokens.push({
200
+ type: "flag",
201
+ value: this.input.slice(start, this.position),
202
+ start,
203
+ end: this.position,
204
+ });
205
+ }
206
+
207
+ private tokenizeFlagValue(): void {
208
+ const valueStart = this.position;
209
+ // Value might be quoted
210
+ if (this.char === '"' || this.char === "'") {
211
+ const quoteResult = readQuotedString(this.input, this.position);
212
+ this.tokens.push({
213
+ type: "value",
214
+ value: quoteResult.value,
215
+ start: valueStart,
216
+ end: quoteResult.end,
217
+ });
218
+ this.position = quoteResult.end;
219
+ } else {
220
+ while (this.position < this.input.length && !isWhitespace(this.char)) {
221
+ this.position++;
222
+ }
223
+ this.tokens.push({
224
+ type: "value",
225
+ value: this.input.slice(valueStart, this.position),
226
+ start: valueStart,
227
+ end: this.position,
228
+ });
229
+ }
230
+ }
231
+
232
+ private tokenizeQuotedString(): void {
233
+ const start = this.position;
234
+ const quoteResult = readQuotedString(this.input, this.position);
235
+ this.tokens.push({
236
+ type: "string",
237
+ value: quoteResult.value,
238
+ start,
239
+ end: quoteResult.end,
240
+ });
241
+ this.position = quoteResult.end;
242
+ }
243
+
244
+ private tokenizeValue(): void {
245
+ const start = this.position;
246
+ while (
247
+ this.position < this.input.length &&
248
+ !isWhitespace(this.char) &&
249
+ this.char !== '"' &&
250
+ this.char !== "'"
251
+ ) {
252
+ this.position++;
253
+ }
254
+ if (this.position > start) {
255
+ this.tokens.push({
256
+ type: "value",
257
+ value: this.input.slice(start, this.position),
258
+ start,
259
+ end: this.position,
260
+ });
261
+ }
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Read a quoted string from input
267
+ *
268
+ * @param input - Full input string
269
+ * @param start - Position of opening quote
270
+ * @returns Object with parsed value and end position
271
+ */
272
+ function readQuotedString(
273
+ input: string,
274
+ start: number
275
+ ): { value: string; end: number; error?: boolean } {
276
+ const quoteChar = input[start];
277
+ const isDouble = quoteChar === '"';
278
+ let result = "";
279
+ let i = start + 1;
280
+
281
+ while (i < input.length) {
282
+ const char = input[i];
283
+
284
+ // End of quoted string
285
+ if (char === quoteChar) {
286
+ return { value: result, end: i + 1 };
287
+ }
288
+
289
+ // T014: Escape handling for double quotes only
290
+ if (isDouble && char === "\\") {
291
+ const nextChar = input[i + 1];
292
+ switch (nextChar) {
293
+ case "n":
294
+ result += "\n";
295
+ i += 2;
296
+ continue;
297
+ case "t":
298
+ result += "\t";
299
+ i += 2;
300
+ continue;
301
+ case "\\":
302
+ result += "\\";
303
+ i += 2;
304
+ continue;
305
+ case '"':
306
+ result += '"';
307
+ i += 2;
308
+ continue;
309
+ default:
310
+ // Unknown escape, keep backslash
311
+ result += char;
312
+ i++;
313
+ continue;
314
+ }
315
+ }
316
+
317
+ // Regular character (including backslash in single quotes)
318
+ result += char;
319
+ i++;
320
+ }
321
+
322
+ // Unclosed quote - return what we have with error flag
323
+ return { value: result, end: i, error: true };
324
+ }
325
+
326
+ // =============================================================================
327
+ // T015: ParseResult Types
328
+ // =============================================================================
329
+
330
+ /**
331
+ * Successfully parsed command
332
+ */
333
+ export interface ParsedCommand {
334
+ /** Command name (lowercase, without leading slash) */
335
+ readonly command: string;
336
+ /** Positional arguments in order */
337
+ readonly positional: readonly string[];
338
+ /** Named arguments (flags) */
339
+ readonly named: ReadonlyMap<string, string | boolean>;
340
+ /** Original raw input */
341
+ readonly raw: string;
342
+ }
343
+
344
+ /**
345
+ * Parse error with details
346
+ */
347
+ export interface ParseError {
348
+ /** Error discriminator */
349
+ readonly error: true;
350
+ /** Standardized error code */
351
+ readonly code: CommandErrorCode;
352
+ /** Human-readable error message */
353
+ readonly message: string;
354
+ /** Position in input where error occurred */
355
+ readonly position?: number;
356
+ }
357
+
358
+ /**
359
+ * Result of parsing a command
360
+ */
361
+ export type ParseResult = ParsedCommand | ParseError;
362
+
363
+ /**
364
+ * Type guard for ParseError
365
+ */
366
+ export function isParseError(result: ParseResult): result is ParseError {
367
+ return "error" in result && result.error === true;
368
+ }
369
+
370
+ /**
371
+ * Type guard for ParsedCommand
372
+ */
373
+ export function isParsedCommand(result: ParseResult): result is ParsedCommand {
374
+ return !isParseError(result);
375
+ }
376
+
377
+ // =============================================================================
378
+ // T015 & T016: CommandParser
379
+ // =============================================================================
380
+
381
+ /**
382
+ * Command parser for slash commands
383
+ *
384
+ * Parses command strings into structured ParsedCommand objects.
385
+ *
386
+ * @example
387
+ * ```typescript
388
+ * const parser = new CommandParser();
389
+ *
390
+ * const result = parser.parse('/login provider --store keychain');
391
+ * if (!isParseError(result)) {
392
+ * console.log(result.command); // 'login'
393
+ * console.log(result.positional); // ['provider']
394
+ * console.log(result.named.get('store')); // 'keychain'
395
+ * }
396
+ * ```
397
+ */
398
+ export class CommandParser {
399
+ /**
400
+ * Parse a command input string
401
+ *
402
+ * @param input - Raw command input (should start with /)
403
+ * @returns ParseResult - either ParsedCommand or ParseError
404
+ *
405
+ * @example
406
+ * ```typescript
407
+ * // Basic command
408
+ * parser.parse('/help'); // { command: 'help', positional: [], named: Map {} }
409
+ *
410
+ * // With arguments
411
+ * parser.parse('/login "my provider" --store keychain');
412
+ * // { command: 'login', positional: ['my provider'], named: Map { 'store' => 'keychain' } }
413
+ * ```
414
+ */
415
+ parse(input: string): ParseResult {
416
+ const trimmed = input.trim();
417
+
418
+ // Empty input
419
+ if (!trimmed) {
420
+ return {
421
+ error: true,
422
+ code: "INVALID_ARGUMENT",
423
+ message: "Empty command input",
424
+ position: 0,
425
+ };
426
+ }
427
+
428
+ // Must start with /
429
+ if (!trimmed.startsWith("/")) {
430
+ return {
431
+ error: true,
432
+ code: "INVALID_ARGUMENT",
433
+ message: "Command must start with /",
434
+ position: 0,
435
+ };
436
+ }
437
+
438
+ // Check for unclosed quotes before tokenizing
439
+ const quoteError = this.checkQuotes(trimmed);
440
+ if (quoteError) {
441
+ return quoteError;
442
+ }
443
+
444
+ // Tokenize
445
+ const tokens = Tokenizer.tokenize(trimmed);
446
+
447
+ // Filter out whitespace tokens
448
+ const significantTokens = tokens.filter((t) => t.type !== "whitespace");
449
+
450
+ // Must have at least command
451
+ if (significantTokens.length === 0) {
452
+ return {
453
+ error: true,
454
+ code: "INVALID_ARGUMENT",
455
+ message: "No command specified",
456
+ position: 0,
457
+ };
458
+ }
459
+
460
+ // First token should be command (safe: checked length > 0 above)
461
+ const commandToken = significantTokens.at(0);
462
+ if (!commandToken || commandToken.type !== "command") {
463
+ return {
464
+ error: true,
465
+ code: "INVALID_ARGUMENT",
466
+ message: "Invalid command format",
467
+ position: 0,
468
+ };
469
+ }
470
+
471
+ const command = commandToken.value.toLowerCase();
472
+ const { positional, named } = this.processTokens(significantTokens);
473
+
474
+ return {
475
+ command,
476
+ positional,
477
+ named,
478
+ raw: input,
479
+ };
480
+ }
481
+
482
+ /**
483
+ * Process tokens into positional and named arguments
484
+ */
485
+ private processTokens(tokens: Token[]): {
486
+ positional: string[];
487
+ named: Map<string, string | boolean>;
488
+ } {
489
+ const positional: string[] = [];
490
+ const named = new Map<string, string | boolean>();
491
+
492
+ let i = 1; // Skip command token
493
+ while (i < tokens.length) {
494
+ const token = tokens.at(i);
495
+ if (!token) {
496
+ i++;
497
+ continue;
498
+ }
499
+
500
+ // T016: Flag handling
501
+ if (token.type === "flag") {
502
+ i = this.processFlag(tokens, i, named);
503
+ continue;
504
+ }
505
+
506
+ // String or value = positional argument
507
+ if (token.type === "string" || token.type === "value") {
508
+ positional.push(token.value);
509
+ i++;
510
+ continue;
511
+ }
512
+
513
+ // Unknown token type - skip
514
+ i++;
515
+ }
516
+
517
+ return { positional, named };
518
+ }
519
+
520
+ /**
521
+ * Process a flag token and its potential value
522
+ */
523
+ private processFlag(
524
+ tokens: Token[],
525
+ index: number,
526
+ named: Map<string, string | boolean>
527
+ ): number {
528
+ const token = tokens.at(index);
529
+ if (!token) return index + 1;
530
+
531
+ const flagName = token.value.startsWith("--") ? token.value.slice(2) : token.value.slice(1);
532
+
533
+ // Look ahead for value
534
+ const nextToken = tokens[index + 1];
535
+
536
+ if (nextToken && (nextToken.type === "value" || nextToken.type === "string")) {
537
+ // Flag with value
538
+ named.set(flagName, nextToken.value);
539
+ return index + 2;
540
+ }
541
+ // Boolean flag
542
+ named.set(flagName, true);
543
+ return index + 1;
544
+ }
545
+
546
+ /**
547
+ * Check for unclosed quotes in input
548
+ */
549
+ private checkQuotes(input: string): ParseError | null {
550
+ let inQuote = false;
551
+ let quoteChar = "";
552
+ let quoteStart = -1;
553
+
554
+ for (let i = 0; i < input.length; i++) {
555
+ const char = input[i];
556
+
557
+ if (!inQuote) {
558
+ if (char === '"' || char === "'") {
559
+ inQuote = true;
560
+ quoteChar = char;
561
+ quoteStart = i;
562
+ }
563
+ } else {
564
+ // Check for escape in double quotes
565
+ if (quoteChar === '"' && char === "\\") {
566
+ i++; // Skip escaped character
567
+ continue;
568
+ }
569
+ if (char === quoteChar) {
570
+ inQuote = false;
571
+ quoteChar = "";
572
+ quoteStart = -1;
573
+ }
574
+ }
575
+ }
576
+
577
+ if (inQuote) {
578
+ return {
579
+ error: true,
580
+ code: "INVALID_ARGUMENT",
581
+ message: `Unclosed ${quoteChar === '"' ? "double" : "single"} quote`,
582
+ position: quoteStart,
583
+ };
584
+ }
585
+
586
+ return null;
587
+ }
588
+ }