@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,480 @@
1
+ /**
2
+ * Autocomplete State Management
3
+ *
4
+ * Provides state management and fuzzy scoring for slash command autocomplete.
5
+ * Used by AutocompletePanel component for real-time command suggestions.
6
+ *
7
+ * @module cli/commands/autocomplete
8
+ */
9
+
10
+ import type { CommandRegistry } from "./registry.js";
11
+ import type { SlashCommand } from "./types.js";
12
+
13
+ // =============================================================================
14
+ // T022: State Types
15
+ // =============================================================================
16
+
17
+ /**
18
+ * Autocomplete candidate with scoring information
19
+ */
20
+ export interface AutocompleteCandidate {
21
+ /** The command being suggested */
22
+ readonly command: SlashCommand;
23
+ /** Match score (higher = better match) */
24
+ readonly score: number;
25
+ /** Character ranges that matched the query [start, end) */
26
+ readonly matchRanges: readonly [start: number, end: number][];
27
+ }
28
+
29
+ /**
30
+ * State for autocomplete feature
31
+ */
32
+ export interface AutocompleteState {
33
+ /** Whether autocomplete is currently active */
34
+ readonly active: boolean;
35
+ /** Current search query (text after /) */
36
+ readonly query: string;
37
+ /** Matched command candidates sorted by score */
38
+ readonly candidates: readonly AutocompleteCandidate[];
39
+ /** Currently selected candidate index */
40
+ readonly selectedIndex: number;
41
+ /** Precomputed highlight ranges for each command name */
42
+ readonly highlights: Map<string, readonly [start: number, end: number][]>;
43
+ }
44
+
45
+ /**
46
+ * Initial autocomplete state
47
+ */
48
+ export const initialAutocompleteState: AutocompleteState = {
49
+ active: false,
50
+ query: "",
51
+ candidates: [],
52
+ selectedIndex: 0,
53
+ highlights: new Map(),
54
+ };
55
+
56
+ // =============================================================================
57
+ // T024: Action Types
58
+ // =============================================================================
59
+
60
+ /**
61
+ * Actions for autocomplete reducer
62
+ */
63
+ export type AutocompleteAction =
64
+ | { readonly type: "INPUT_CHANGE"; readonly query: string; readonly registry: CommandRegistry }
65
+ | { readonly type: "SELECT_PREV" }
66
+ | { readonly type: "SELECT_NEXT" }
67
+ | { readonly type: "TAB_COMPLETE" }
68
+ | { readonly type: "CANCEL" };
69
+
70
+ // =============================================================================
71
+ // T023: Fuzzy Scoring Constants
72
+ // =============================================================================
73
+
74
+ /**
75
+ * Scoring weights for fuzzy matching
76
+ */
77
+ const SCORE_WEIGHTS = {
78
+ /** Bonus for exact match (query equals command name) */
79
+ EXACT_MATCH: 100,
80
+ /** Bonus for prefix match (query is prefix of command name) */
81
+ PREFIX_MATCH: 80,
82
+ /** Bonus for matching at word boundary (after - or _) */
83
+ WORD_BOUNDARY: 3,
84
+ /** Minimum bonus for consecutive character match */
85
+ CONSECUTIVE_MIN: 1,
86
+ /** Maximum bonus for consecutive character match */
87
+ CONSECUTIVE_MAX: 5,
88
+ /** Base score per matched character */
89
+ CHAR_MATCH: 10,
90
+ /** Penalty per unmatched character in target */
91
+ UNMATCHED_PENALTY: -1,
92
+ } as const;
93
+
94
+ // =============================================================================
95
+ // T023: Fuzzy Score Function
96
+ // =============================================================================
97
+
98
+ /**
99
+ * Result of fuzzy scoring
100
+ */
101
+ export interface FuzzyScoreResult {
102
+ /** Total match score (higher = better match) */
103
+ readonly score: number;
104
+ /** Character ranges that matched [start, end) */
105
+ readonly ranges: readonly [start: number, end: number][];
106
+ }
107
+
108
+ /**
109
+ * Calculate fuzzy match score between query and target
110
+ *
111
+ * Scoring rules:
112
+ * - Exact match: +100 (query equals command name)
113
+ * - Prefix match: +80 (query is prefix of command name)
114
+ * - Word boundary: +3 (match at start of word after - or _)
115
+ * - Consecutive chars: +1-5 (bonus for consecutive matches)
116
+ * - Base char match: +10 per matched character
117
+ * - Unmatched penalty: -1 per unmatched character in target
118
+ *
119
+ * @param query - Search query (lowercase comparison)
120
+ * @param target - Target string to match against
121
+ * @returns Score and matched character ranges, or null if no match
122
+ *
123
+ * @example
124
+ * ```typescript
125
+ * fuzzyScore('h', 'help');
126
+ * // { score: 90, ranges: [[0, 1]] } // prefix match + char match
127
+ *
128
+ * fuzzyScore('help', 'help');
129
+ * // { score: 140, ranges: [[0, 4]] } // exact match + char matches
130
+ *
131
+ * fuzzyScore('gc', 'git-commit');
132
+ * // { score: 26, ranges: [[0, 1], [4, 5]] } // word boundary bonus
133
+ * ```
134
+ */
135
+ export function fuzzyScore(query: string, target: string): FuzzyScoreResult | null {
136
+ if (!query) {
137
+ // Empty query matches everything with base score
138
+ return { score: 0, ranges: [] };
139
+ }
140
+
141
+ const lowerQuery = query.toLowerCase();
142
+ const lowerTarget = target.toLowerCase();
143
+
144
+ // Check for exact match first
145
+ if (lowerQuery === lowerTarget) {
146
+ return {
147
+ score: SCORE_WEIGHTS.EXACT_MATCH + SCORE_WEIGHTS.CHAR_MATCH * query.length,
148
+ ranges: [[0, target.length]],
149
+ };
150
+ }
151
+
152
+ // Check for prefix match
153
+ if (lowerTarget.startsWith(lowerQuery)) {
154
+ return {
155
+ score:
156
+ SCORE_WEIGHTS.PREFIX_MATCH +
157
+ SCORE_WEIGHTS.CHAR_MATCH * query.length +
158
+ SCORE_WEIGHTS.UNMATCHED_PENALTY * (target.length - query.length),
159
+ ranges: [[0, query.length]],
160
+ };
161
+ }
162
+
163
+ // Fuzzy matching
164
+ return fuzzyMatchCharacters(lowerQuery, lowerTarget, target);
165
+ }
166
+
167
+ /**
168
+ * Internal fuzzy matching for non-exact, non-prefix matches
169
+ */
170
+ function fuzzyMatchCharacters(
171
+ lowerQuery: string,
172
+ lowerTarget: string,
173
+ target: string
174
+ ): FuzzyScoreResult | null {
175
+ const ranges: [number, number][] = [];
176
+ let score = 0;
177
+ let queryIndex = 0;
178
+ let consecutiveCount = 0;
179
+ let lastMatchIndex = -2; // -2 so first match isn't considered consecutive
180
+
181
+ for (let i = 0; i < target.length && queryIndex < lowerQuery.length; i++) {
182
+ if (lowerTarget[i] === lowerQuery[queryIndex]) {
183
+ score += scoreMatchedChar(i, lastMatchIndex, consecutiveCount, target);
184
+ consecutiveCount = i === lastMatchIndex + 1 ? consecutiveCount + 1 : 1;
185
+ updateRanges(ranges, i);
186
+ lastMatchIndex = i;
187
+ queryIndex++;
188
+ } else if (queryIndex > 0) {
189
+ score += SCORE_WEIGHTS.UNMATCHED_PENALTY;
190
+ }
191
+ }
192
+
193
+ // All query characters must be found
194
+ if (queryIndex < lowerQuery.length) {
195
+ return null;
196
+ }
197
+
198
+ return { score, ranges };
199
+ }
200
+
201
+ /**
202
+ * Calculate score for a matched character
203
+ */
204
+ function scoreMatchedChar(
205
+ i: number,
206
+ lastMatchIndex: number,
207
+ consecutiveCount: number,
208
+ target: string
209
+ ): number {
210
+ let charScore = SCORE_WEIGHTS.CHAR_MATCH;
211
+
212
+ // Word boundary bonus
213
+ if (i === 0 || target[i - 1] === "-" || target[i - 1] === "_") {
214
+ charScore += SCORE_WEIGHTS.WORD_BOUNDARY;
215
+ }
216
+
217
+ // Consecutive bonus
218
+ if (i === lastMatchIndex + 1) {
219
+ charScore += Math.min(consecutiveCount + 1, SCORE_WEIGHTS.CONSECUTIVE_MAX);
220
+ } else {
221
+ charScore += SCORE_WEIGHTS.CONSECUTIVE_MIN;
222
+ }
223
+
224
+ return charScore;
225
+ }
226
+
227
+ /**
228
+ * Update match ranges with new match at index i
229
+ */
230
+ function updateRanges(ranges: [number, number][], i: number): void {
231
+ const lastRange = ranges.length > 0 ? ranges[ranges.length - 1] : undefined;
232
+ if (lastRange && lastRange[1] === i) {
233
+ lastRange[1] = i + 1;
234
+ } else {
235
+ ranges.push([i, i + 1]);
236
+ }
237
+ }
238
+
239
+ // =============================================================================
240
+ // T025: Highlight Computation
241
+ // =============================================================================
242
+
243
+ /**
244
+ * Compute highlight ranges for all candidates
245
+ *
246
+ * Creates a Map of command names to their highlight ranges for
247
+ * efficient lookup during rendering.
248
+ *
249
+ * @param candidates - List of autocomplete candidates
250
+ * @returns Map of command name to highlight ranges
251
+ *
252
+ * @example
253
+ * ```typescript
254
+ * const highlights = computeHighlights(candidates);
255
+ * const ranges = highlights.get('help'); // [[0, 1]] for query 'h'
256
+ * ```
257
+ */
258
+ export function computeHighlights(
259
+ candidates: readonly AutocompleteCandidate[]
260
+ ): Map<string, readonly [start: number, end: number][]> {
261
+ const highlights = new Map<string, readonly [start: number, end: number][]>();
262
+
263
+ for (const candidate of candidates) {
264
+ highlights.set(candidate.command.name, candidate.matchRanges);
265
+ }
266
+
267
+ return highlights;
268
+ }
269
+
270
+ // =============================================================================
271
+ // Helper Functions
272
+ // =============================================================================
273
+
274
+ /**
275
+ * Compute candidates from registry for a given query
276
+ */
277
+ function computeCandidates(
278
+ query: string,
279
+ registry: CommandRegistry
280
+ ): readonly AutocompleteCandidate[] {
281
+ const candidates: AutocompleteCandidate[] = [];
282
+
283
+ // Get all commands from registry
284
+ for (const command of registry.list()) {
285
+ // Score against command name
286
+ const result = fuzzyScore(query, command.name);
287
+
288
+ if (result) {
289
+ candidates.push({
290
+ command,
291
+ score: result.score,
292
+ matchRanges: result.ranges,
293
+ });
294
+ }
295
+
296
+ // Also check aliases for better matching
297
+ if (command.aliases) {
298
+ let bestAliasResult: FuzzyScoreResult | null = null;
299
+
300
+ for (const alias of command.aliases) {
301
+ const aliasResult = fuzzyScore(query, alias);
302
+ if (aliasResult && aliasResult.score > (bestAliasResult?.score ?? result?.score ?? 0)) {
303
+ bestAliasResult = aliasResult;
304
+ }
305
+ }
306
+
307
+ if (bestAliasResult && bestAliasResult.score > (result?.score ?? 0)) {
308
+ const existing = candidates.find((c) => c.command === command);
309
+ if (existing) {
310
+ const index = candidates.indexOf(existing);
311
+ candidates.splice(index, 1);
312
+ }
313
+ candidates.push({
314
+ command,
315
+ score: bestAliasResult.score,
316
+ matchRanges: bestAliasResult.ranges,
317
+ });
318
+ }
319
+ }
320
+ }
321
+
322
+ // Sort by score descending, then alphabetically for ties
323
+ candidates.sort((a, b) => {
324
+ if (b.score !== a.score) {
325
+ return b.score - a.score;
326
+ }
327
+ return a.command.name.localeCompare(b.command.name);
328
+ });
329
+
330
+ return candidates;
331
+ }
332
+
333
+ // =============================================================================
334
+ // T024: Autocomplete Reducer
335
+ // =============================================================================
336
+
337
+ /**
338
+ * Reducer for autocomplete state management
339
+ *
340
+ * Handles:
341
+ * - INPUT_CHANGE: Update query and recompute candidates
342
+ * - SELECT_PREV: Move selection up (wraps to bottom)
343
+ * - SELECT_NEXT: Move selection down (wraps to top)
344
+ * - TAB_COMPLETE: Returns state with selected candidate (for completion)
345
+ * - CANCEL: Reset state and deactivate
346
+ *
347
+ * @param state - Current autocomplete state
348
+ * @param action - Action to perform
349
+ * @returns New autocomplete state
350
+ *
351
+ * @example
352
+ * ```typescript
353
+ * let state = initialAutocompleteState;
354
+ *
355
+ * // User types '/h'
356
+ * state = autocompleteReducer(state, {
357
+ * type: 'INPUT_CHANGE',
358
+ * query: 'h',
359
+ * registry,
360
+ * });
361
+ * // state.candidates contains matching commands
362
+ *
363
+ * // User presses down arrow
364
+ * state = autocompleteReducer(state, { type: 'SELECT_NEXT' });
365
+ * // state.selectedIndex is 1
366
+ *
367
+ * // User presses Tab
368
+ * state = autocompleteReducer(state, { type: 'TAB_COMPLETE' });
369
+ * // Use state.candidates[state.selectedIndex] for completion
370
+ * ```
371
+ */
372
+ export function autocompleteReducer(
373
+ state: AutocompleteState,
374
+ action: AutocompleteAction
375
+ ): AutocompleteState {
376
+ switch (action.type) {
377
+ case "INPUT_CHANGE": {
378
+ const { query, registry } = action;
379
+
380
+ // Empty query deactivates autocomplete
381
+ if (!query) {
382
+ return {
383
+ ...initialAutocompleteState,
384
+ active: false,
385
+ };
386
+ }
387
+
388
+ // Compute candidates from registry
389
+ const candidates = computeCandidates(query, registry);
390
+
391
+ // Compute highlights for all candidates
392
+ const highlights = computeHighlights(candidates);
393
+
394
+ return {
395
+ active: candidates.length > 0,
396
+ query,
397
+ candidates,
398
+ selectedIndex: 0, // Reset selection on new query
399
+ highlights,
400
+ };
401
+ }
402
+
403
+ case "SELECT_PREV": {
404
+ if (!state.active || state.candidates.length === 0) {
405
+ return state;
406
+ }
407
+
408
+ // Wrap to bottom if at top
409
+ const newIndex =
410
+ state.selectedIndex <= 0 ? state.candidates.length - 1 : state.selectedIndex - 1;
411
+
412
+ return {
413
+ ...state,
414
+ selectedIndex: newIndex,
415
+ };
416
+ }
417
+
418
+ case "SELECT_NEXT": {
419
+ if (!state.active || state.candidates.length === 0) {
420
+ return state;
421
+ }
422
+
423
+ // Wrap to top if at bottom
424
+ const newIndex =
425
+ state.selectedIndex >= state.candidates.length - 1 ? 0 : state.selectedIndex + 1;
426
+
427
+ return {
428
+ ...state,
429
+ selectedIndex: newIndex,
430
+ };
431
+ }
432
+
433
+ case "TAB_COMPLETE": {
434
+ // State is returned as-is for the caller to read selectedIndex
435
+ // The caller will use candidates[selectedIndex] for completion
436
+ // Then dispatch CANCEL or INPUT_CHANGE with completed text
437
+ return state;
438
+ }
439
+
440
+ case "CANCEL": {
441
+ return {
442
+ ...initialAutocompleteState,
443
+ active: false,
444
+ };
445
+ }
446
+
447
+ default: {
448
+ // Type guard for exhaustiveness checking
449
+ const exhaustiveCheck: never = action;
450
+ throw new Error(`Unknown action type: ${JSON.stringify(exhaustiveCheck)}`);
451
+ }
452
+ }
453
+ }
454
+
455
+ // =============================================================================
456
+ // Utility Functions
457
+ // =============================================================================
458
+
459
+ /**
460
+ * Get the currently selected candidate (if any)
461
+ *
462
+ * @param state - Current autocomplete state
463
+ * @returns Selected candidate or undefined
464
+ */
465
+ export function getSelectedCandidate(state: AutocompleteState): AutocompleteCandidate | undefined {
466
+ if (!state.active || state.candidates.length === 0) {
467
+ return undefined;
468
+ }
469
+ return state.candidates[state.selectedIndex];
470
+ }
471
+
472
+ /**
473
+ * Check if autocomplete should be shown
474
+ *
475
+ * @param state - Current autocomplete state
476
+ * @returns True if autocomplete panel should be visible
477
+ */
478
+ export function shouldShowAutocomplete(state: AutocompleteState): boolean {
479
+ return state.active && state.candidates.length > 0;
480
+ }