@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,510 @@
1
+ /**
2
+ * PermissionDialog Component Tests
3
+ *
4
+ * Tests for the PermissionDialog component which handles tool permission
5
+ * requests with approval/rejection flows and risk level display.
6
+ *
7
+ * Note: ink-testing-library's stdin.write() does not synchronously trigger
8
+ * useInput hooks. Tests focus on:
9
+ * - Rendering behavior (verifiable via lastFrame())
10
+ * - Props contract verification
11
+ * - Visual state assertions
12
+ *
13
+ * Behavioral tests (approval/rejection flows) are validated via:
14
+ * - Integration tests at the CLI level
15
+ * - Manual testing documentation
16
+ *
17
+ * @module tui/components/Tools/__tests__/PermissionDialog.test
18
+ */
19
+
20
+ import { render } from "ink-testing-library";
21
+ import type React from "react";
22
+ import { describe, expect, it, vi } from "vitest";
23
+ import type { ToolExecution } from "../../../context/ToolsContext.js";
24
+ import { ThemeProvider } from "../../../theme/index.js";
25
+ import { PermissionDialog, type RiskLevel } from "../PermissionDialog.js";
26
+
27
+ // =============================================================================
28
+ // Test Helpers
29
+ // =============================================================================
30
+
31
+ /**
32
+ * Wrap component with ThemeProvider for testing.
33
+ */
34
+ function renderWithTheme(ui: React.ReactElement) {
35
+ return render(<ThemeProvider>{ui}</ThemeProvider>);
36
+ }
37
+
38
+ /**
39
+ * Create a test tool execution with default values.
40
+ */
41
+ function createTestExecution(overrides: Partial<ToolExecution> = {}): ToolExecution {
42
+ return {
43
+ id: "test-exec-1",
44
+ toolName: "test_tool",
45
+ params: {},
46
+ status: "pending",
47
+ ...overrides,
48
+ };
49
+ }
50
+
51
+ // =============================================================================
52
+ // Tests
53
+ // =============================================================================
54
+
55
+ describe("PermissionDialog", () => {
56
+ describe("risk level display", () => {
57
+ it("displays low risk badge with green styling", () => {
58
+ const execution = createTestExecution();
59
+ const onApprove = vi.fn();
60
+ const onReject = vi.fn();
61
+
62
+ const { lastFrame } = renderWithTheme(
63
+ <PermissionDialog
64
+ execution={execution}
65
+ riskLevel="low"
66
+ onApprove={onApprove}
67
+ onReject={onReject}
68
+ />
69
+ );
70
+
71
+ expect(lastFrame()).toContain("Low Risk");
72
+ expect(lastFrame()).toContain("●");
73
+ });
74
+
75
+ it("displays medium risk badge with yellow styling", () => {
76
+ const execution = createTestExecution();
77
+ const onApprove = vi.fn();
78
+ const onReject = vi.fn();
79
+
80
+ const { lastFrame } = renderWithTheme(
81
+ <PermissionDialog
82
+ execution={execution}
83
+ riskLevel="medium"
84
+ onApprove={onApprove}
85
+ onReject={onReject}
86
+ />
87
+ );
88
+
89
+ expect(lastFrame()).toContain("Medium Risk");
90
+ expect(lastFrame()).toContain("▲");
91
+ });
92
+
93
+ it("displays high risk badge with orange styling", () => {
94
+ const execution = createTestExecution();
95
+ const onApprove = vi.fn();
96
+ const onReject = vi.fn();
97
+
98
+ const { lastFrame } = renderWithTheme(
99
+ <PermissionDialog
100
+ execution={execution}
101
+ riskLevel="high"
102
+ onApprove={onApprove}
103
+ onReject={onReject}
104
+ />
105
+ );
106
+
107
+ expect(lastFrame()).toContain("High Risk");
108
+ expect(lastFrame()).toContain("◆");
109
+ });
110
+
111
+ it("displays critical risk badge with red styling", () => {
112
+ const execution = createTestExecution();
113
+ const onApprove = vi.fn();
114
+ const onReject = vi.fn();
115
+
116
+ const { lastFrame } = renderWithTheme(
117
+ <PermissionDialog
118
+ execution={execution}
119
+ riskLevel="critical"
120
+ onApprove={onApprove}
121
+ onReject={onReject}
122
+ />
123
+ );
124
+
125
+ expect(lastFrame()).toContain("Critical Risk");
126
+ expect(lastFrame()).toContain("⬢");
127
+ });
128
+
129
+ it("displays all risk levels correctly", () => {
130
+ const riskLevels: RiskLevel[] = ["low", "medium", "high", "critical"];
131
+ const expectedLabels = ["Low Risk", "Medium Risk", "High Risk", "Critical Risk"];
132
+ const expectedIcons = ["●", "▲", "◆", "⬢"];
133
+
134
+ riskLevels.forEach((level, index) => {
135
+ const execution = createTestExecution();
136
+ const { lastFrame } = renderWithTheme(
137
+ <PermissionDialog
138
+ execution={execution}
139
+ riskLevel={level}
140
+ onApprove={vi.fn()}
141
+ onReject={vi.fn()}
142
+ />
143
+ );
144
+
145
+ expect(lastFrame()).toContain(expectedLabels[index]);
146
+ expect(lastFrame()).toContain(expectedIcons[index]);
147
+ });
148
+ });
149
+ });
150
+
151
+ describe("dialog content", () => {
152
+ it("displays tool permission request header", () => {
153
+ const execution = createTestExecution();
154
+
155
+ const { lastFrame } = renderWithTheme(
156
+ <PermissionDialog
157
+ execution={execution}
158
+ riskLevel="low"
159
+ onApprove={vi.fn()}
160
+ onReject={vi.fn()}
161
+ />
162
+ );
163
+
164
+ // Translation key is used in tests
165
+ expect(lastFrame()).toContain("permission.requestTitle");
166
+ });
167
+
168
+ it("displays tool name", () => {
169
+ const execution = createTestExecution({ toolName: "read_file" });
170
+
171
+ const { lastFrame } = renderWithTheme(
172
+ <PermissionDialog
173
+ execution={execution}
174
+ riskLevel="low"
175
+ onApprove={vi.fn()}
176
+ onReject={vi.fn()}
177
+ />
178
+ );
179
+
180
+ expect(lastFrame()).toContain("read_file");
181
+ // Translation key is used in tests
182
+ expect(lastFrame()).toContain("permission.tool");
183
+ });
184
+
185
+ it("displays tool parameters when present", () => {
186
+ const execution = createTestExecution({
187
+ toolName: "read_file",
188
+ params: { path: "/test/file.txt", encoding: "utf-8" },
189
+ });
190
+
191
+ const { lastFrame } = renderWithTheme(
192
+ <PermissionDialog
193
+ execution={execution}
194
+ riskLevel="low"
195
+ onApprove={vi.fn()}
196
+ onReject={vi.fn()}
197
+ />
198
+ );
199
+
200
+ // Translation key is used in tests
201
+ expect(lastFrame()).toContain("permission.parameters");
202
+ expect(lastFrame()).toContain("path");
203
+ expect(lastFrame()).toContain("/test/file.txt");
204
+ });
205
+
206
+ it("does not display parameters section when params are empty", () => {
207
+ const execution = createTestExecution({ params: {} });
208
+
209
+ const { lastFrame } = renderWithTheme(
210
+ <PermissionDialog
211
+ execution={execution}
212
+ riskLevel="low"
213
+ onApprove={vi.fn()}
214
+ onReject={vi.fn()}
215
+ />
216
+ );
217
+
218
+ // Translation key should not be present
219
+ expect(lastFrame()).not.toContain("permission.parameters");
220
+ });
221
+ });
222
+
223
+ describe("keybinding hints", () => {
224
+ it("displays approve keybinding hint", () => {
225
+ const execution = createTestExecution();
226
+
227
+ const { lastFrame } = renderWithTheme(
228
+ <PermissionDialog
229
+ execution={execution}
230
+ riskLevel="low"
231
+ onApprove={vi.fn()}
232
+ onReject={vi.fn()}
233
+ />
234
+ );
235
+
236
+ expect(lastFrame()).toContain("[y/Enter]");
237
+ // Translation key is used in tests
238
+ expect(lastFrame()).toContain("permission.approve");
239
+ });
240
+
241
+ it("displays reject keybinding hint", () => {
242
+ const execution = createTestExecution();
243
+
244
+ const { lastFrame } = renderWithTheme(
245
+ <PermissionDialog
246
+ execution={execution}
247
+ riskLevel="low"
248
+ onApprove={vi.fn()}
249
+ onReject={vi.fn()}
250
+ />
251
+ );
252
+
253
+ expect(lastFrame()).toContain("[n/Esc]");
254
+ // Translation key is used in tests
255
+ expect(lastFrame()).toContain("permission.reject");
256
+ });
257
+
258
+ it("displays always allow hint when onApproveAlways is provided", () => {
259
+ const execution = createTestExecution();
260
+
261
+ const { lastFrame } = renderWithTheme(
262
+ <PermissionDialog
263
+ execution={execution}
264
+ riskLevel="low"
265
+ onApprove={vi.fn()}
266
+ onReject={vi.fn()}
267
+ onApproveAlways={vi.fn()}
268
+ />
269
+ );
270
+
271
+ expect(lastFrame()).toContain("[a]");
272
+ // Translation key is used in tests
273
+ expect(lastFrame()).toContain("permission.alwaysAllow");
274
+ });
275
+
276
+ it("does not display always allow hint when onApproveAlways is not provided", () => {
277
+ const execution = createTestExecution();
278
+
279
+ const { lastFrame } = renderWithTheme(
280
+ <PermissionDialog
281
+ execution={execution}
282
+ riskLevel="low"
283
+ onApprove={vi.fn()}
284
+ onReject={vi.fn()}
285
+ />
286
+ );
287
+
288
+ // Translation key should not be present
289
+ expect(lastFrame()).not.toContain("permission.alwaysAllow");
290
+ });
291
+ });
292
+
293
+ describe("approval flow", () => {
294
+ /**
295
+ * Note: ink-testing-library's stdin.write() does not synchronously trigger useInput hooks.
296
+ * The approval flow (y, Y, Enter keys) is tested via integration tests.
297
+ *
298
+ * Expected behavior:
299
+ * - 'y' or 'Y': calls onApprove
300
+ * - Enter key: calls onApprove
301
+ * - 'a' or 'A': calls onApproveAlways (if provided)
302
+ */
303
+ it("renders approve keybinding hint for user guidance", () => {
304
+ const execution = createTestExecution();
305
+
306
+ const { lastFrame } = renderWithTheme(
307
+ <PermissionDialog
308
+ execution={execution}
309
+ riskLevel="low"
310
+ onApprove={vi.fn()}
311
+ onReject={vi.fn()}
312
+ />
313
+ );
314
+
315
+ expect(lastFrame()).toContain("[y/Enter]");
316
+ // Translation key is used in tests
317
+ expect(lastFrame()).toContain("permission.approve");
318
+ });
319
+
320
+ it("renders always allow option when onApproveAlways callback is provided", () => {
321
+ const execution = createTestExecution();
322
+
323
+ const { lastFrame } = renderWithTheme(
324
+ <PermissionDialog
325
+ execution={execution}
326
+ riskLevel="low"
327
+ onApprove={vi.fn()}
328
+ onReject={vi.fn()}
329
+ onApproveAlways={vi.fn()}
330
+ />
331
+ );
332
+
333
+ expect(lastFrame()).toContain("[a]");
334
+ // Translation key is used in tests
335
+ expect(lastFrame()).toContain("permission.alwaysAllow");
336
+ });
337
+
338
+ it("does not render always allow option when callback is not provided", () => {
339
+ const execution = createTestExecution();
340
+
341
+ const { lastFrame } = renderWithTheme(
342
+ <PermissionDialog
343
+ execution={execution}
344
+ riskLevel="low"
345
+ onApprove={vi.fn()}
346
+ onReject={vi.fn()}
347
+ />
348
+ );
349
+
350
+ // Translation key should not be present
351
+ expect(lastFrame()).not.toContain("permission.alwaysAllow");
352
+ });
353
+ });
354
+
355
+ describe("rejection flow", () => {
356
+ /**
357
+ * Note: ink-testing-library's stdin.write() does not synchronously trigger useInput hooks.
358
+ * The rejection flow (n, N, Escape keys) is tested via integration tests.
359
+ *
360
+ * Expected behavior:
361
+ * - 'n' or 'N': calls onReject
362
+ * - Escape key: calls onReject
363
+ */
364
+ it("renders reject keybinding hint for user guidance", () => {
365
+ const execution = createTestExecution();
366
+
367
+ const { lastFrame } = renderWithTheme(
368
+ <PermissionDialog
369
+ execution={execution}
370
+ riskLevel="low"
371
+ onApprove={vi.fn()}
372
+ onReject={vi.fn()}
373
+ />
374
+ );
375
+
376
+ expect(lastFrame()).toContain("[n/Esc]");
377
+ // Translation key is used in tests
378
+ expect(lastFrame()).toContain("permission.reject");
379
+ });
380
+ });
381
+
382
+ describe("focus behavior", () => {
383
+ /**
384
+ * Note: ink-testing-library's stdin.write() does not synchronously trigger useInput hooks.
385
+ * Focus behavior (isFocused prop) is tested via integration tests.
386
+ *
387
+ * Expected behavior:
388
+ * - When isFocused=false: keyboard input is ignored
389
+ * - When isFocused=true (default): keyboard input is processed
390
+ */
391
+ it("accepts isFocused prop for controlling input responsiveness", () => {
392
+ const execution = createTestExecution();
393
+
394
+ // Should render without errors when isFocused is explicitly false
395
+ const { lastFrame: frame1 } = renderWithTheme(
396
+ <PermissionDialog
397
+ execution={execution}
398
+ riskLevel="low"
399
+ onApprove={vi.fn()}
400
+ onReject={vi.fn()}
401
+ isFocused={false}
402
+ />
403
+ );
404
+
405
+ // Translation key is used in tests
406
+ expect(frame1()).toContain("permission.requestTitle");
407
+
408
+ // Should render without errors when isFocused is explicitly true
409
+ const { lastFrame: frame2 } = renderWithTheme(
410
+ <PermissionDialog
411
+ execution={execution}
412
+ riskLevel="low"
413
+ onApprove={vi.fn()}
414
+ onReject={vi.fn()}
415
+ isFocused={true}
416
+ />
417
+ );
418
+
419
+ // Translation key is used in tests
420
+ expect(frame2()).toContain("permission.requestTitle");
421
+ });
422
+
423
+ it("uses isFocused=true by default", () => {
424
+ const execution = createTestExecution();
425
+
426
+ // Should render without errors when isFocused is not provided
427
+ const { lastFrame } = renderWithTheme(
428
+ <PermissionDialog
429
+ execution={execution}
430
+ riskLevel="low"
431
+ onApprove={vi.fn()}
432
+ onReject={vi.fn()}
433
+ />
434
+ );
435
+
436
+ // Translation key is used in tests
437
+ expect(lastFrame()).toContain("permission.requestTitle");
438
+ });
439
+ });
440
+
441
+ describe("double-action prevention", () => {
442
+ /**
443
+ * Note: ink-testing-library's stdin.write() does not synchronously trigger useInput hooks.
444
+ * Double-action prevention is tested via integration tests.
445
+ *
446
+ * Expected behavior:
447
+ * - Only the first valid keypress triggers a callback
448
+ * - Subsequent keypresses are ignored until execution changes
449
+ */
450
+ it("tracks execution ID to reset handled state on execution change", () => {
451
+ const execution1 = createTestExecution({ id: "exec-1" });
452
+ const execution2 = createTestExecution({ id: "exec-2" });
453
+
454
+ // Both should render without errors
455
+ const { lastFrame: frame1 } = renderWithTheme(
456
+ <PermissionDialog
457
+ execution={execution1}
458
+ riskLevel="low"
459
+ onApprove={vi.fn()}
460
+ onReject={vi.fn()}
461
+ />
462
+ );
463
+
464
+ // Translation key is used in tests
465
+ expect(frame1()).toContain("permission.requestTitle");
466
+
467
+ const { lastFrame: frame2 } = renderWithTheme(
468
+ <PermissionDialog
469
+ execution={execution2}
470
+ riskLevel="medium"
471
+ onApprove={vi.fn()}
472
+ onReject={vi.fn()}
473
+ />
474
+ );
475
+
476
+ // Translation key is used in tests
477
+ expect(frame2()).toContain("permission.requestTitle");
478
+ });
479
+ });
480
+
481
+ describe("unknown keys", () => {
482
+ /**
483
+ * Note: ink-testing-library's stdin.write() does not synchronously trigger useInput hooks.
484
+ * Unknown key handling is tested via integration tests.
485
+ *
486
+ * Expected behavior:
487
+ * - Keys other than y, n, a, Enter, Escape are ignored
488
+ * - No callbacks are triggered for unknown keys
489
+ */
490
+ it("only displays valid keybinding hints", () => {
491
+ const execution = createTestExecution();
492
+
493
+ const { lastFrame } = renderWithTheme(
494
+ <PermissionDialog
495
+ execution={execution}
496
+ riskLevel="low"
497
+ onApprove={vi.fn()}
498
+ onReject={vi.fn()}
499
+ />
500
+ );
501
+
502
+ // Should only show documented keybindings
503
+ expect(lastFrame()).toContain("[y/Enter]");
504
+ expect(lastFrame()).toContain("[n/Esc]");
505
+ // Should not suggest random keys
506
+ expect(lastFrame()).not.toContain("[x]");
507
+ expect(lastFrame()).not.toContain("[z]");
508
+ });
509
+ });
510
+ });