@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,441 @@
1
+ /**
2
+ * Fuzzy Search Service
3
+ *
4
+ * Provides fuzzy matching capabilities using fuzzysort for
5
+ * improved file selection and command search experience.
6
+ *
7
+ * @module tui/services/fuzzy-search
8
+ */
9
+
10
+ import fuzzysort from "fuzzysort";
11
+
12
+ // =============================================================================
13
+ // Types
14
+ // =============================================================================
15
+
16
+ /**
17
+ * A range of character indices for highlighting matched portions.
18
+ */
19
+ export interface HighlightRange {
20
+ /** Start index (inclusive) */
21
+ readonly start: number;
22
+ /** End index (exclusive) */
23
+ readonly end: number;
24
+ }
25
+
26
+ /**
27
+ * Result from a fuzzy match operation.
28
+ */
29
+ export interface FuzzyMatchResult {
30
+ /** Whether the text matches the pattern */
31
+ readonly matches: boolean;
32
+ /** Match score (higher is better, null if no match) */
33
+ readonly score: number | null;
34
+ /** Character index ranges to highlight */
35
+ readonly highlights: readonly HighlightRange[];
36
+ }
37
+
38
+ /**
39
+ * A single fuzzy search result with the original item.
40
+ */
41
+ export interface FuzzyResult<T> {
42
+ /** The original item */
43
+ readonly item: T;
44
+ /** Match score (higher is better) */
45
+ readonly score: number;
46
+ /** Highlighted ranges for the matched field */
47
+ readonly highlights: readonly HighlightRange[];
48
+ /** The matched string target */
49
+ readonly target: string;
50
+ }
51
+
52
+ /**
53
+ * Options for fuzzy search operations.
54
+ */
55
+ export interface FuzzySearchOptions {
56
+ /** Maximum number of results to return (default: unlimited) */
57
+ readonly limit?: number;
58
+ /** Minimum score threshold (default: -10000) */
59
+ readonly threshold?: number;
60
+ /** Whether to allow typos/transpositions (default: false for speed) */
61
+ readonly allowTypo?: boolean;
62
+ }
63
+
64
+ /**
65
+ * Options for multi-field fuzzy search.
66
+ */
67
+ export interface MultiFieldSearchOptions<T> extends FuzzySearchOptions {
68
+ /** Fields to search with optional weights */
69
+ readonly keys: readonly (keyof T | { key: keyof T; weight?: number })[];
70
+ }
71
+
72
+ // =============================================================================
73
+ // Helper Functions
74
+ // =============================================================================
75
+
76
+ /**
77
+ * Convert fuzzysort indexes to highlight ranges.
78
+ * Fuzzysort returns individual matching character indices.
79
+ */
80
+ function indicesToRanges(indices: readonly number[] | null): HighlightRange[] {
81
+ if (!indices || indices.length === 0) return [];
82
+
83
+ const ranges: HighlightRange[] = [];
84
+ const firstIndex = indices[0];
85
+ if (firstIndex === undefined) return [];
86
+
87
+ let start = firstIndex;
88
+ let end = start + 1;
89
+
90
+ for (let i = 1; i < indices.length; i++) {
91
+ const idx = indices[i];
92
+ if (idx === undefined) continue;
93
+ if (idx === end) {
94
+ // Consecutive, extend current range
95
+ end = idx + 1;
96
+ } else {
97
+ // Gap, push current range and start new one
98
+ ranges.push({ start, end });
99
+ start = idx;
100
+ end = idx + 1;
101
+ }
102
+ }
103
+
104
+ // Push final range
105
+ ranges.push({ start, end });
106
+
107
+ return ranges;
108
+ }
109
+
110
+ // =============================================================================
111
+ // Core Functions
112
+ // =============================================================================
113
+
114
+ /**
115
+ * Perform fuzzy matching on a single text string.
116
+ *
117
+ * @param text - The text to match against
118
+ * @param pattern - The search pattern
119
+ * @returns Match result with score and highlight ranges
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * const result = fuzzyMatch("autocomplete", "acp");
124
+ * // { matches: true, score: -20, highlights: [{ start: 0, end: 1 }, { start: 4, end: 6 }] }
125
+ * ```
126
+ */
127
+ export function fuzzyMatch(text: string, pattern: string): FuzzyMatchResult {
128
+ if (!pattern) {
129
+ return { matches: true, score: 0, highlights: [] };
130
+ }
131
+
132
+ if (!text) {
133
+ return { matches: false, score: null, highlights: [] };
134
+ }
135
+
136
+ const result = fuzzysort.single(pattern, text);
137
+
138
+ if (!result) {
139
+ return { matches: false, score: null, highlights: [] };
140
+ }
141
+
142
+ return {
143
+ matches: true,
144
+ score: result.score,
145
+ highlights: indicesToRanges(result.indexes),
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Search an array of items using fuzzy matching on a single field.
151
+ *
152
+ * @param items - Array of items to search
153
+ * @param query - Search query string
154
+ * @param key - Optional key to search (for object arrays)
155
+ * @param options - Search options
156
+ * @returns Sorted array of fuzzy results (best matches first)
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * // String array
161
+ * const commands = ['/help', '/history', '/hello'];
162
+ * const results = fuzzySearch(commands, 'hel');
163
+ *
164
+ * // Object array with key
165
+ * const options = [{ name: 'help' }, { name: 'history' }];
166
+ * const results = fuzzySearch(options, 'hel', 'name');
167
+ * ```
168
+ */
169
+ export function fuzzySearch<T>(
170
+ items: readonly T[],
171
+ query: string,
172
+ key?: keyof T,
173
+ options: FuzzySearchOptions = {}
174
+ ): FuzzyResult<T>[] {
175
+ const { limit, threshold = -10000 } = options;
176
+
177
+ if (!query) {
178
+ // No query - return all items with neutral score
179
+ const results: FuzzyResult<T>[] = items.map((item) => ({
180
+ item,
181
+ score: 0,
182
+ highlights: [],
183
+ target: key ? String(item[key]) : String(item),
184
+ }));
185
+ return limit ? results.slice(0, limit) : results;
186
+ }
187
+
188
+ if (key) {
189
+ // Object array search
190
+ const fuzzysortResults = fuzzysort.go(query, [...items] as object[], {
191
+ key: key as string,
192
+ limit,
193
+ threshold,
194
+ });
195
+
196
+ return fuzzysortResults.map((result) => ({
197
+ item: result.obj as T,
198
+ score: result.score,
199
+ highlights: indicesToRanges(result.indexes),
200
+ target: result.target,
201
+ }));
202
+ }
203
+
204
+ // String array search
205
+ const fuzzysortResults = fuzzysort.go(query, [...items] as string[], {
206
+ limit,
207
+ threshold,
208
+ });
209
+
210
+ return fuzzysortResults.map((result) => ({
211
+ item: result.target as T,
212
+ score: result.score,
213
+ highlights: indicesToRanges(result.indexes),
214
+ target: result.target,
215
+ }));
216
+ }
217
+
218
+ /**
219
+ * Search items across multiple fields with optional weighting.
220
+ *
221
+ * @param items - Array of items to search
222
+ * @param query - Search query string
223
+ * @param options - Multi-field search options including keys to search
224
+ * @returns Sorted array of fuzzy results (best matches first)
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * const options = [
229
+ * { name: 'help', description: 'Show help message', aliases: ['h', '?'] },
230
+ * { name: 'history', description: 'View command history' },
231
+ * ];
232
+ *
233
+ * const results = fuzzySearchMulti(options, 'help', {
234
+ * keys: ['name', { key: 'description', weight: 0.5 }],
235
+ * });
236
+ * ```
237
+ */
238
+ export function fuzzySearchMulti<T extends object>(
239
+ items: readonly T[],
240
+ query: string,
241
+ options: MultiFieldSearchOptions<T>
242
+ ): FuzzyResult<T>[] {
243
+ const { keys, limit, threshold = -10000 } = options;
244
+
245
+ if (!query) {
246
+ // No query - return all items
247
+ const results: FuzzyResult<T>[] = items.map((item) => {
248
+ const firstKey = typeof keys[0] === "object" ? keys[0].key : keys[0];
249
+ return {
250
+ item,
251
+ score: 0,
252
+ highlights: [],
253
+ target: firstKey ? String(item[firstKey as keyof T]) : "",
254
+ };
255
+ });
256
+ return limit ? results.slice(0, limit) : results;
257
+ }
258
+
259
+ // Normalize keys to array of strings for fuzzysort
260
+ const keyStrings = keys.map((k) => (typeof k === "object" ? String(k.key) : String(k)));
261
+
262
+ // Use fuzzysort.go with keys array
263
+ const fuzzysortResults = fuzzysort.go(query, [...items] as object[], {
264
+ keys: keyStrings,
265
+ limit,
266
+ threshold,
267
+ });
268
+
269
+ return fuzzysortResults.map((result) => {
270
+ // Find best matching key result
271
+ let bestKeyIndex = 0;
272
+ let bestScore = -Infinity;
273
+
274
+ for (let i = 0; i < result.length; i++) {
275
+ const keyResult = result[i];
276
+ if (keyResult && keyResult.score > bestScore) {
277
+ bestScore = keyResult.score;
278
+ bestKeyIndex = i;
279
+ }
280
+ }
281
+
282
+ const bestResult = result[bestKeyIndex];
283
+
284
+ return {
285
+ item: result.obj as T,
286
+ score: result.score, // Combined score from fuzzysort
287
+ highlights: bestResult ? indicesToRanges(bestResult.indexes) : [],
288
+ target: bestResult?.target ?? "",
289
+ };
290
+ });
291
+ }
292
+
293
+ /**
294
+ * Quick check if a pattern matches text (no scoring/highlighting).
295
+ * More performant for simple "does it match?" checks.
296
+ *
297
+ * @param text - Text to check
298
+ * @param pattern - Pattern to match
299
+ * @returns True if pattern matches text
300
+ */
301
+ export function fuzzyTest(text: string, pattern: string): boolean {
302
+ if (!pattern) return true;
303
+ if (!text) return false;
304
+
305
+ const result = fuzzysort.single(pattern, text);
306
+ return result !== null;
307
+ }
308
+
309
+ /**
310
+ * Highlight matched characters in a string.
311
+ * Returns an array of segments with highlight flags.
312
+ *
313
+ * @param text - Original text
314
+ * @param highlights - Highlight ranges from fuzzy match
315
+ * @returns Array of text segments with highlight info
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * const segments = getHighlightSegments("autocomplete", [{ start: 0, end: 1 }, { start: 4, end: 6 }]);
320
+ * // [
321
+ * // { text: 'a', highlighted: true },
322
+ * // { text: 'uto', highlighted: false },
323
+ * // { text: 'co', highlighted: true },
324
+ * // { text: 'mplete', highlighted: false }
325
+ * // ]
326
+ * ```
327
+ */
328
+ export interface FuzzyHighlightSegment {
329
+ readonly text: string;
330
+ readonly highlighted: boolean;
331
+ }
332
+
333
+ export function getHighlightSegments(
334
+ text: string,
335
+ highlights: readonly HighlightRange[]
336
+ ): FuzzyHighlightSegment[] {
337
+ if (!highlights.length) {
338
+ return [{ text, highlighted: false }];
339
+ }
340
+
341
+ const segments: FuzzyHighlightSegment[] = [];
342
+ let lastEnd = 0;
343
+
344
+ for (const range of highlights) {
345
+ // Add non-highlighted segment before this range
346
+ if (range.start > lastEnd) {
347
+ segments.push({
348
+ text: text.slice(lastEnd, range.start),
349
+ highlighted: false,
350
+ });
351
+ }
352
+
353
+ // Add highlighted segment
354
+ segments.push({
355
+ text: text.slice(range.start, range.end),
356
+ highlighted: true,
357
+ });
358
+
359
+ lastEnd = range.end;
360
+ }
361
+
362
+ // Add remaining non-highlighted text
363
+ if (lastEnd < text.length) {
364
+ segments.push({
365
+ text: text.slice(lastEnd),
366
+ highlighted: false,
367
+ });
368
+ }
369
+
370
+ return segments;
371
+ }
372
+
373
+ // =============================================================================
374
+ // Prepared Search (for large lists with repeated searches)
375
+ // =============================================================================
376
+
377
+ /**
378
+ * Create a prepared searcher for a static list of items.
379
+ * More efficient when searching the same list multiple times.
380
+ *
381
+ * @param items - Items to prepare for searching
382
+ * @param key - Key to index (for objects)
383
+ * @returns Prepared searcher function
384
+ *
385
+ * @example
386
+ * ```ts
387
+ * const commands = [{ name: 'help' }, { name: 'history' }];
388
+ * const search = createPreparedSearch(commands, 'name');
389
+ *
390
+ * // Efficient repeated searches
391
+ * const results1 = search('hel');
392
+ * const results2 = search('his');
393
+ * ```
394
+ */
395
+ export function createPreparedSearch<T>(
396
+ items: readonly T[],
397
+ key?: keyof T
398
+ ): (query: string, options?: FuzzySearchOptions) => FuzzyResult<T>[] {
399
+ // Pre-prepare all targets for faster searching
400
+ const prepared = key
401
+ ? items.map((item) => ({
402
+ item,
403
+ prepared: fuzzysort.prepare(String(item[key])),
404
+ }))
405
+ : ([...items] as string[]).map((item) => ({
406
+ item: item as T,
407
+ prepared: fuzzysort.prepare(item),
408
+ }));
409
+
410
+ return (query: string, options: FuzzySearchOptions = {}): FuzzyResult<T>[] => {
411
+ const { limit, threshold = -10000 } = options;
412
+
413
+ if (!query) {
414
+ const results: FuzzyResult<T>[] = prepared.map(({ item, prepared: p }) => ({
415
+ item,
416
+ score: 0,
417
+ highlights: [],
418
+ target: p.target,
419
+ }));
420
+ return limit ? results.slice(0, limit) : results;
421
+ }
422
+
423
+ // Search prepared targets
424
+ const targets = prepared.map((p) => p.prepared);
425
+ const fuzzysortResults = fuzzysort.go(query, targets, {
426
+ limit,
427
+ threshold,
428
+ });
429
+
430
+ return fuzzysortResults.map((result) => {
431
+ // Find original item by target match
432
+ const match = prepared.find((p) => p.prepared.target === result.target);
433
+ return {
434
+ item: match?.item ?? (result.target as T),
435
+ score: result.score,
436
+ highlights: indicesToRanges(result.indexes),
437
+ target: result.target,
438
+ };
439
+ });
440
+ };
441
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * TUI Services
3
+ *
4
+ * Shared services for the Vellum CLI terminal UI.
5
+ *
6
+ * @module tui/services
7
+ */
8
+
9
+ export {
10
+ type ClipboardHistoryEntry,
11
+ type ClipboardResult,
12
+ clearHistory as clearClipboardHistory,
13
+ clipboardService,
14
+ copy,
15
+ copySync,
16
+ copyWithMessage,
17
+ getHistory as getClipboardHistory,
18
+ getHistorySize as getClipboardHistorySize,
19
+ getLastEntry as getLastClipboardEntry,
20
+ isSupported as isClipboardSupported,
21
+ paste,
22
+ pasteSync,
23
+ resetSupportCache as resetClipboardSupportCache,
24
+ } from "./clipboard.js";
25
+ export {
26
+ createPreparedSearch,
27
+ type FuzzyHighlightSegment,
28
+ type FuzzyMatchResult,
29
+ type FuzzyResult,
30
+ type FuzzySearchOptions,
31
+ fuzzyMatch,
32
+ fuzzySearch,
33
+ fuzzySearchMulti,
34
+ fuzzyTest,
35
+ getHighlightSegments,
36
+ type HighlightRange,
37
+ type MultiFieldSearchOptions,
38
+ } from "./fuzzy-search.js";
39
+ export {
40
+ containsMarkdown,
41
+ type MarkdownColors,
42
+ type MarkdownRenderOptions,
43
+ renderMarkdown,
44
+ renderMarkdownPlain,
45
+ renderMarkdownSync,
46
+ stripMarkdown,
47
+ } from "./markdown-renderer.js";
48
+
49
+ export {
50
+ isValidUrl,
51
+ type OpenInEditorOptions,
52
+ type OpenResult,
53
+ openDirectory,
54
+ openExternalService,
55
+ openFile,
56
+ openInEditor,
57
+ openUrl,
58
+ } from "./open-external.js";
59
+ export {
60
+ detectLanguage,
61
+ getSupportedLanguages,
62
+ type HighlightOptions,
63
+ highlightCode,
64
+ highlightCodeSync,
65
+ initializeHighlighter,
66
+ isHighlighterReady,
67
+ isLanguageSupported,
68
+ preloadHighlighter,
69
+ resolveLanguage,
70
+ type SupportedLanguage,
71
+ type SyntaxHighlightResult,
72
+ } from "./syntax-highlighter.js";