@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,300 @@
1
+ /**
2
+ * Tip Engine Integration
3
+ *
4
+ * Wires the TipEngine to the TUI application for contextual tips
5
+ * and progressive disclosure of features.
6
+ *
7
+ * @module cli/tui/tip-integration
8
+ */
9
+
10
+ import { useCallback, useMemo, useState } from "react";
11
+ import type { Tip, TipContext, TipState } from "../onboarding/index.js";
12
+ import { BUILTIN_TIPS, createTipEngine, TipEngine } from "../onboarding/index.js";
13
+
14
+ // =============================================================================
15
+ // Types
16
+ // =============================================================================
17
+
18
+ export interface TipIntegrationOptions {
19
+ /** Enable tips display */
20
+ enabled?: boolean;
21
+ /** Maximum tips to show per session */
22
+ maxTipsPerSession?: number;
23
+ /** Minimum interval between tips (ms) */
24
+ tipIntervalMs?: number;
25
+ /** Categories to prioritize */
26
+ priorityCategories?: string[];
27
+ /** Callback when a tip is shown */
28
+ onTipShown?: (tip: Tip) => void;
29
+ /** Callback when a tip is dismissed */
30
+ onTipDismissed?: (tipId: string) => void;
31
+ }
32
+
33
+ export interface TipIntegrationState {
34
+ /** Current tip to display (null if none) */
35
+ currentTip: Tip | null;
36
+ /** Whether tips are enabled */
37
+ tipsEnabled: boolean;
38
+ /** Number of tips shown this session */
39
+ tipsShownCount: number;
40
+ /** Whether a tip is currently visible */
41
+ tipVisible: boolean;
42
+ }
43
+
44
+ export interface TipIntegrationActions {
45
+ /** Show a contextual tip based on current context */
46
+ showTip: (context: TipContext) => Tip | null;
47
+ /** Dismiss the current tip */
48
+ dismissTip: () => void;
49
+ /** Permanently dismiss a tip (won't show again) */
50
+ dismissTipPermanently: (tipId: string) => void;
51
+ /** Enable or disable tips */
52
+ setTipsEnabled: (enabled: boolean) => void;
53
+ /** Get a specific tip by ID */
54
+ getTip: (tipId: string) => Tip | undefined;
55
+ /** Check if a tip has been shown */
56
+ hasTipBeenShown: (tipId: string) => boolean;
57
+ /** Reset tip states (for testing) */
58
+ resetTips: () => void;
59
+ }
60
+
61
+ export type UseTipEngineResult = TipIntegrationState & TipIntegrationActions;
62
+
63
+ // =============================================================================
64
+ // Default Configuration
65
+ // =============================================================================
66
+
67
+ const DEFAULT_OPTIONS: Required<TipIntegrationOptions> = {
68
+ enabled: true,
69
+ maxTipsPerSession: 10,
70
+ tipIntervalMs: 60000, // 1 minute between tips
71
+ priorityCategories: ["shortcuts", "features"],
72
+ onTipShown: () => {},
73
+ onTipDismissed: () => {},
74
+ };
75
+
76
+ // =============================================================================
77
+ // Hook Implementation
78
+ // =============================================================================
79
+
80
+ /**
81
+ * React hook for tip engine integration.
82
+ *
83
+ * @param options - Tip integration options
84
+ * @returns Tip integration state and actions
85
+ *
86
+ * @example
87
+ * ```tsx
88
+ * function AppContent() {
89
+ * const {
90
+ * currentTip,
91
+ * showTip,
92
+ * dismissTip,
93
+ * tipsEnabled,
94
+ * } = useTipEngine({ enabled: true });
95
+ *
96
+ * // Show contextual tips based on user actions
97
+ * useEffect(() => {
98
+ * showTip({
99
+ * currentScreen: "main",
100
+ * featureUsageCount: 5,
101
+ * recentCommands: ["help", "mode"],
102
+ * });
103
+ * }, [showTip]);
104
+ *
105
+ * return (
106
+ * <>
107
+ * {currentTip && (
108
+ * <TipBanner tip={currentTip} onDismiss={dismissTip} />
109
+ * )}
110
+ * <MainContent />
111
+ * </>
112
+ * );
113
+ * }
114
+ * ```
115
+ */
116
+ export function useTipEngine(options: TipIntegrationOptions = {}): UseTipEngineResult {
117
+ // Memoize config to prevent re-renders from changing object reference
118
+ // biome-ignore lint/correctness/useExhaustiveDependencies: We intentionally use specific properties to prevent re-renders when only callbacks change
119
+ const config = useMemo(
120
+ () => ({ ...DEFAULT_OPTIONS, ...options }),
121
+ [options.enabled, options.maxTipsPerSession, options.tipIntervalMs]
122
+ );
123
+ const { onTipShown, onTipDismissed } = options;
124
+
125
+ // Create tip engine instance
126
+ const tipEngine = useMemo(() => createTipEngine(), []);
127
+
128
+ // State
129
+ const [currentTip, setCurrentTip] = useState<Tip | null>(null);
130
+ const [tipsEnabled, setTipsEnabled] = useState(config.enabled);
131
+ const [tipsShownCount, setTipsShownCount] = useState(0);
132
+ const [tipVisible, setTipVisible] = useState(false);
133
+ const [lastTipTime, setLastTipTime] = useState(0);
134
+ const [permanentlyDismissed, setPermanentlyDismissed] = useState<Set<string>>(new Set());
135
+
136
+ // Show a tip based on context
137
+ const showTip = useCallback(
138
+ (context: TipContext): Tip | null => {
139
+ if (!tipsEnabled) return null;
140
+ if (tipsShownCount >= config.maxTipsPerSession) return null;
141
+
142
+ // Check tip interval
143
+ const now = Date.now();
144
+ if (now - lastTipTime < config.tipIntervalMs) return null;
145
+
146
+ // Get a matching tip
147
+ const tip = tipEngine.getTip(context);
148
+
149
+ if (tip && !permanentlyDismissed.has(tip.id)) {
150
+ setCurrentTip(tip);
151
+ setTipVisible(true);
152
+ setTipsShownCount((prev) => prev + 1);
153
+ setLastTipTime(now);
154
+ onTipShown?.(tip);
155
+ return tip;
156
+ }
157
+
158
+ return null;
159
+ },
160
+ [
161
+ tipsEnabled,
162
+ tipsShownCount,
163
+ lastTipTime,
164
+ tipEngine,
165
+ permanentlyDismissed,
166
+ config.maxTipsPerSession,
167
+ config.tipIntervalMs,
168
+ onTipShown,
169
+ ]
170
+ );
171
+
172
+ // Dismiss current tip
173
+ const dismissTip = useCallback(() => {
174
+ if (currentTip) {
175
+ onTipDismissed?.(currentTip.id);
176
+ }
177
+ setCurrentTip(null);
178
+ setTipVisible(false);
179
+ }, [currentTip, onTipDismissed]);
180
+
181
+ // Permanently dismiss a tip
182
+ const dismissTipPermanently = useCallback(
183
+ (tipId: string) => {
184
+ setPermanentlyDismissed((prev) => new Set([...prev, tipId]));
185
+ tipEngine.dismissTip(tipId);
186
+ if (currentTip?.id === tipId) {
187
+ dismissTip();
188
+ }
189
+ },
190
+ [tipEngine, currentTip, dismissTip]
191
+ );
192
+
193
+ // Get a specific tip
194
+ const getTip = useCallback(
195
+ (tipId: string): Tip | undefined => {
196
+ return tipEngine.getAllTips().find((t) => t.id === tipId);
197
+ },
198
+ [tipEngine]
199
+ );
200
+
201
+ // Check if a tip has been shown
202
+ const hasTipBeenShown = useCallback(
203
+ (tipId: string): boolean => {
204
+ const state = tipEngine.getTipState(tipId);
205
+ return state?.showCount !== undefined && state.showCount > 0;
206
+ },
207
+ [tipEngine]
208
+ );
209
+
210
+ // Reset tips (for testing)
211
+ const resetTips = useCallback(() => {
212
+ tipEngine.resetAllStates();
213
+ setCurrentTip(null);
214
+ setTipVisible(false);
215
+ setTipsShownCount(0);
216
+ setPermanentlyDismissed(new Set());
217
+ }, [tipEngine]);
218
+
219
+ return {
220
+ // State
221
+ currentTip,
222
+ tipsEnabled,
223
+ tipsShownCount,
224
+ tipVisible,
225
+ // Actions
226
+ showTip,
227
+ dismissTip,
228
+ dismissTipPermanently,
229
+ setTipsEnabled,
230
+ getTip,
231
+ hasTipBeenShown,
232
+ resetTips,
233
+ };
234
+ }
235
+
236
+ // =============================================================================
237
+ // Context Builders
238
+ // =============================================================================
239
+
240
+ /**
241
+ * Build a tip context from application state.
242
+ *
243
+ * @param options - Context building options
244
+ * @returns TipContext for tip matching
245
+ *
246
+ * @example
247
+ * ```typescript
248
+ * const context = buildTipContext({
249
+ * screen: "main",
250
+ * mode: "vibe",
251
+ * command: "/help",
252
+ * featuresUsedCount: 10,
253
+ * });
254
+ * const tip = tipEngine.getTip(context);
255
+ * ```
256
+ */
257
+ export function buildTipContext(options: {
258
+ screen?: string;
259
+ mode?: string;
260
+ command?: string;
261
+ featuresUsedCount?: number;
262
+ sessionDuration?: number;
263
+ hasError?: boolean;
264
+ experienceLevel?: "new" | "beginner" | "intermediate" | "advanced";
265
+ custom?: Record<string, unknown>;
266
+ }): TipContext {
267
+ return {
268
+ screen: options.screen ?? "main",
269
+ mode: options.mode,
270
+ command: options.command,
271
+ featuresUsedCount: options.featuresUsedCount ?? 0,
272
+ sessionDuration: options.sessionDuration,
273
+ hasError: options.hasError,
274
+ experienceLevel: options.experienceLevel ?? "new",
275
+ custom: options.custom,
276
+ };
277
+ }
278
+
279
+ /**
280
+ * Get tips relevant to a specific feature or command.
281
+ *
282
+ * @param tipEngine - TipEngine instance
283
+ * @param feature - Feature or command name
284
+ * @returns Relevant tips
285
+ */
286
+ export function getTipsForFeature(tipEngine: TipEngine, feature: string): Tip[] {
287
+ const allTips = tipEngine.getAllTips();
288
+ return allTips.filter(
289
+ (tip) =>
290
+ tip.trigger.commands?.includes(feature) ||
291
+ tip.relatedLessonId === feature ||
292
+ tip.content.toLowerCase().includes(feature.toLowerCase())
293
+ );
294
+ }
295
+
296
+ // =============================================================================
297
+ // Exports
298
+ // =============================================================================
299
+
300
+ export { BUILTIN_TIPS, createTipEngine, TipEngine, type Tip, type TipContext, type TipState };
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Tests for Extended Ink Key Utilities
3
+ */
4
+ import type { Key } from "ink";
5
+ import { describe, expect, it } from "vitest";
6
+ import { type ExtendedKey, extendKey, isEndKey, isHomeKey } from "../ink-extended.js";
7
+
8
+ describe("ink-extended", () => {
9
+ // Base key object for testing
10
+ const baseKey: Key = {
11
+ upArrow: false,
12
+ downArrow: false,
13
+ leftArrow: false,
14
+ rightArrow: false,
15
+ pageDown: false,
16
+ pageUp: false,
17
+ return: false,
18
+ escape: false,
19
+ ctrl: false,
20
+ shift: false,
21
+ tab: false,
22
+ backspace: false,
23
+ delete: false,
24
+ meta: false,
25
+ };
26
+
27
+ describe("extendKey", () => {
28
+ it("extends key object with home and end properties", () => {
29
+ const result = extendKey("a", baseKey);
30
+ expect(result).toHaveProperty("home");
31
+ expect(result).toHaveProperty("end");
32
+ });
33
+
34
+ it("preserves original key properties", () => {
35
+ const key: Key = { ...baseKey, ctrl: true, shift: true };
36
+ const result = extendKey("a", key);
37
+ expect(result.ctrl).toBe(true);
38
+ expect(result.shift).toBe(true);
39
+ });
40
+
41
+ it("detects Home key (CSI H)", () => {
42
+ const result = extendKey("\x1b[H", baseKey);
43
+ expect(result.home).toBe(true);
44
+ expect(result.end).toBe(false);
45
+ });
46
+
47
+ it("detects Home key (CSI 1 ~)", () => {
48
+ const result = extendKey("\x1b[1~", baseKey);
49
+ expect(result.home).toBe(true);
50
+ });
51
+
52
+ it("detects Home key (SS3 H)", () => {
53
+ const result = extendKey("\x1bOH", baseKey);
54
+ expect(result.home).toBe(true);
55
+ });
56
+
57
+ it("detects End key (CSI F)", () => {
58
+ const result = extendKey("\x1b[F", baseKey);
59
+ expect(result.end).toBe(true);
60
+ expect(result.home).toBe(false);
61
+ });
62
+
63
+ it("detects End key (CSI 4 ~)", () => {
64
+ const result = extendKey("\x1b[4~", baseKey);
65
+ expect(result.end).toBe(true);
66
+ });
67
+
68
+ it("detects End key (SS3 F)", () => {
69
+ const result = extendKey("\x1bOF", baseKey);
70
+ expect(result.end).toBe(true);
71
+ });
72
+
73
+ it("returns false for both when regular input", () => {
74
+ const result = extendKey("a", baseKey);
75
+ expect(result.home).toBe(false);
76
+ expect(result.end).toBe(false);
77
+ });
78
+
79
+ it("returns false for both on empty string", () => {
80
+ const result = extendKey("", baseKey);
81
+ expect(result.home).toBe(false);
82
+ expect(result.end).toBe(false);
83
+ });
84
+ });
85
+
86
+ describe("isHomeKey", () => {
87
+ it("returns true for Home sequences", () => {
88
+ expect(isHomeKey("\x1b[H")).toBe(true);
89
+ expect(isHomeKey("\x1b[1~")).toBe(true);
90
+ expect(isHomeKey("\x1bOH")).toBe(true);
91
+ });
92
+
93
+ it("returns false for non-Home input", () => {
94
+ expect(isHomeKey("a")).toBe(false);
95
+ expect(isHomeKey("\x1b[F")).toBe(false); // End key
96
+ expect(isHomeKey("")).toBe(false);
97
+ });
98
+ });
99
+
100
+ describe("isEndKey", () => {
101
+ it("returns true for End sequences", () => {
102
+ expect(isEndKey("\x1b[F")).toBe(true);
103
+ expect(isEndKey("\x1b[4~")).toBe(true);
104
+ expect(isEndKey("\x1bOF")).toBe(true);
105
+ });
106
+
107
+ it("returns false for non-End input", () => {
108
+ expect(isEndKey("a")).toBe(false);
109
+ expect(isEndKey("\x1b[H")).toBe(false); // Home key
110
+ expect(isEndKey("")).toBe(false);
111
+ });
112
+ });
113
+
114
+ describe("ExtendedKey type", () => {
115
+ it("is assignable from extendKey result", () => {
116
+ const result: ExtendedKey = extendKey("a", baseKey);
117
+ expect(result.home).toBeDefined();
118
+ expect(result.end).toBeDefined();
119
+ });
120
+ });
121
+ });
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Extended Key type for ink's useInput hook.
3
+ *
4
+ * The @jrichman/ink fork (and some versions of ink) don't include `home` and `end`
5
+ * properties in the Key type. This module provides an extended type and helper
6
+ * function to detect these keys from raw input.
7
+ *
8
+ * @module ink-extended
9
+ */
10
+
11
+ import type { Key } from "ink";
12
+
13
+ /**
14
+ * Extended Key type that includes home and end key detection.
15
+ * These are not present in the base ink Key type but are commonly needed
16
+ * for navigation in TUI applications.
17
+ */
18
+ export interface ExtendedKey extends Key {
19
+ /** Home key was pressed (jump to beginning) */
20
+ home: boolean;
21
+ /** End key was pressed (jump to end) */
22
+ end: boolean;
23
+ }
24
+
25
+ /**
26
+ * ANSI escape sequences for Home and End keys.
27
+ * Different terminal emulators may send different sequences.
28
+ */
29
+ const HOME_SEQUENCES = [
30
+ "\x1b[H", // CSI H
31
+ "\x1b[1~", // CSI 1 ~
32
+ "\x1bOH", // SS3 H (application mode)
33
+ ];
34
+
35
+ const END_SEQUENCES = [
36
+ "\x1b[F", // CSI F
37
+ "\x1b[4~", // CSI 4 ~
38
+ "\x1bOF", // SS3 F (application mode)
39
+ ];
40
+
41
+ /**
42
+ * Extends the ink Key object with home and end key detection.
43
+ * Call this with the raw input string and the key object from useInput.
44
+ *
45
+ * @param input - The raw input string from useInput
46
+ * @param key - The Key object from useInput
47
+ * @returns Extended key object with home and end properties
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * import { useInput } from 'ink';
52
+ * import { extendKey } from '../types/ink-extended';
53
+ *
54
+ * useInput((input, key) => {
55
+ * const extKey = extendKey(input, key);
56
+ * if (extKey.home) {
57
+ * // Handle Home key
58
+ * }
59
+ * if (extKey.end) {
60
+ * // Handle End key
61
+ * }
62
+ * });
63
+ * ```
64
+ */
65
+ export function extendKey(input: string, key: Key): ExtendedKey {
66
+ return {
67
+ ...key,
68
+ home: HOME_SEQUENCES.includes(input),
69
+ end: END_SEQUENCES.includes(input),
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Check if the input represents a Home key press.
75
+ * Use this for simple boolean checks without needing the full extended key.
76
+ */
77
+ export function isHomeKey(input: string): boolean {
78
+ return HOME_SEQUENCES.includes(input);
79
+ }
80
+
81
+ /**
82
+ * Check if the input represents an End key press.
83
+ * Use this for simple boolean checks without needing the full extended key.
84
+ */
85
+ export function isEndKey(input: string): boolean {
86
+ return END_SEQUENCES.includes(input);
87
+ }