@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
+ * Command Pipe Parser Module
3
+ *
4
+ * Parses command pipes and redirections with shell-like operators:
5
+ * - | (PIPE): Pass output as input to next command
6
+ * - > (WRITE): Write output to file (overwrite)
7
+ * - >> (APPEND): Write output to file (append)
8
+ *
9
+ * @module cli/commands/parser/pipe-parser
10
+ */
11
+
12
+ import type { CommandErrorCode, CommandResult } from "../types.js";
13
+
14
+ // =============================================================================
15
+ // T054: Pipe Operator Types
16
+ // =============================================================================
17
+
18
+ /**
19
+ * Pipe operator discriminator
20
+ *
21
+ * - | (PIPE): Pass stdout of left command to stdin of right command
22
+ * - > (WRITE): Write stdout to file, overwriting existing content
23
+ * - >> (APPEND): Write stdout to file, appending to existing content
24
+ */
25
+ export type PipeOperator = "|" | ">" | ">>";
26
+
27
+ /**
28
+ * Segment type discriminator
29
+ */
30
+ export type PipeSegmentType = "command" | "file";
31
+
32
+ /**
33
+ * Single segment of a piped command
34
+ */
35
+ export interface PipeSegment {
36
+ /** Segment type: command to execute or file target */
37
+ readonly type: PipeSegmentType;
38
+ /** Command string or file path */
39
+ readonly value: string;
40
+ /** Operator connecting to the NEXT segment (undefined for last) */
41
+ readonly operator?: PipeOperator;
42
+ }
43
+
44
+ /**
45
+ * Result of parsing a piped command string
46
+ */
47
+ export interface PipeParseResult {
48
+ /** Whether the input contains pipe operators */
49
+ readonly isPiped: boolean;
50
+ /** Array of command/file segments with their operators */
51
+ readonly segments: readonly PipeSegment[];
52
+ /** Original raw input */
53
+ readonly raw: string;
54
+ /** Whether output redirects to a file */
55
+ readonly hasRedirect: boolean;
56
+ /** Redirect mode if hasRedirect is true */
57
+ readonly redirectMode?: "overwrite" | "append";
58
+ /** Target file if hasRedirect is true */
59
+ readonly redirectTarget?: string;
60
+ }
61
+
62
+ /**
63
+ * Result of pipe execution
64
+ */
65
+ export interface PipeExecutionResult {
66
+ /** Final result from the pipe chain */
67
+ readonly result: CommandResult;
68
+ /** Accumulated output data */
69
+ readonly output: string;
70
+ /** Number of commands executed */
71
+ readonly executedCount: number;
72
+ /** Total segments in pipe */
73
+ readonly totalCount: number;
74
+ /** Whether pipe completed fully */
75
+ readonly completed: boolean;
76
+ /** File written to (if redirected) */
77
+ readonly writtenFile?: string;
78
+ }
79
+
80
+ // =============================================================================
81
+ // T054: Pipe Parser Implementation
82
+ // =============================================================================
83
+
84
+ /**
85
+ * Check if a position is inside quotes
86
+ */
87
+ function isInsideQuotes(input: string, position: number): boolean {
88
+ let inDouble = false;
89
+ let inSingle = false;
90
+
91
+ for (let i = 0; i < position && i < input.length; i++) {
92
+ const char = input[i];
93
+ const prevChar = i > 0 ? input[i - 1] : "";
94
+
95
+ if (char === '"' && !inSingle && prevChar !== "\\") {
96
+ inDouble = !inDouble;
97
+ } else if (char === "'" && !inDouble) {
98
+ inSingle = !inSingle;
99
+ }
100
+ }
101
+
102
+ return inDouble || inSingle;
103
+ }
104
+
105
+ /**
106
+ * Find all pipe operators with their positions
107
+ */
108
+ function findPipeOperators(
109
+ input: string
110
+ ): Array<{ operator: PipeOperator; start: number; end: number }> {
111
+ const operators: Array<{ operator: PipeOperator; start: number; end: number }> = [];
112
+
113
+ let i = 0;
114
+ while (i < input.length) {
115
+ // Skip if inside quotes
116
+ if (isInsideQuotes(input, i)) {
117
+ i++;
118
+ continue;
119
+ }
120
+
121
+ // Check for >> (must check before single >)
122
+ if (input[i] === ">" && input[i + 1] === ">") {
123
+ operators.push({ operator: ">>", start: i, end: i + 2 });
124
+ i += 2;
125
+ continue;
126
+ }
127
+
128
+ // Check for > (single, not part of >>)
129
+ if (input[i] === ">" && input[i + 1] !== ">") {
130
+ operators.push({ operator: ">", start: i, end: i + 1 });
131
+ i++;
132
+ continue;
133
+ }
134
+
135
+ // Check for | (but not || - check both next AND previous char)
136
+ if (input[i] === "|" && input[i + 1] !== "|" && input[i - 1] !== "|") {
137
+ operators.push({ operator: "|", start: i, end: i + 1 });
138
+ i++;
139
+ continue;
140
+ }
141
+
142
+ i++;
143
+ }
144
+
145
+ return operators;
146
+ }
147
+
148
+ /**
149
+ * Pipe Parser
150
+ *
151
+ * Parses command strings containing pipe/redirect operators into segments.
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * const result = PipeParser.parse('/list | /filter pattern > output.txt');
156
+ * // {
157
+ * // isPiped: true,
158
+ * // segments: [
159
+ * // { type: 'command', value: '/list', operator: '|' },
160
+ * // { type: 'command', value: '/filter pattern', operator: '>' },
161
+ * // { type: 'file', value: 'output.txt' }
162
+ * // ],
163
+ * // raw: '/list | /filter pattern > output.txt',
164
+ * // hasRedirect: true,
165
+ * // redirectMode: 'overwrite',
166
+ * // redirectTarget: 'output.txt'
167
+ * // }
168
+ * ```
169
+ */
170
+ // biome-ignore lint/complexity/noStaticOnlyClass: PipeParser provides logical grouping
171
+ export class PipeParser {
172
+ /**
173
+ * Parse a command string for pipe operators
174
+ *
175
+ * @param input - Raw command string
176
+ * @returns PipeParseResult with parsed segments
177
+ */
178
+ static parse(input: string): PipeParseResult {
179
+ const trimmed = input.trim();
180
+
181
+ if (!trimmed) {
182
+ return {
183
+ isPiped: false,
184
+ segments: [],
185
+ raw: input,
186
+ hasRedirect: false,
187
+ };
188
+ }
189
+
190
+ const operators = findPipeOperators(trimmed);
191
+
192
+ // No pipe operators found
193
+ if (operators.length === 0) {
194
+ return {
195
+ isPiped: false,
196
+ segments: [{ type: "command", value: trimmed }],
197
+ raw: input,
198
+ hasRedirect: false,
199
+ };
200
+ }
201
+
202
+ // Split into segments
203
+ const segments: PipeSegment[] = [];
204
+ let lastEnd = 0;
205
+
206
+ for (let i = 0; i < operators.length; i++) {
207
+ const op = operators[i];
208
+ if (!op) continue;
209
+
210
+ const value = trimmed.slice(lastEnd, op.start).trim();
211
+ if (value) {
212
+ // Determine segment type based on what comes before
213
+ const type: PipeSegmentType = "command";
214
+ segments.push({ type, value, operator: op.operator });
215
+ }
216
+ lastEnd = op.end;
217
+ }
218
+
219
+ // Add final segment (after last operator)
220
+ const finalValue = trimmed.slice(lastEnd).trim();
221
+ if (finalValue) {
222
+ // Determine if final segment is a file (after > or >>)
223
+ const lastOp = operators[operators.length - 1];
224
+ const isFile = lastOp?.operator === ">" || lastOp?.operator === ">>";
225
+ segments.push({
226
+ type: isFile ? "file" : "command",
227
+ value: finalValue,
228
+ });
229
+ }
230
+
231
+ // Determine redirect info
232
+ const lastOp = operators[operators.length - 1];
233
+ const hasRedirect = lastOp?.operator === ">" || lastOp?.operator === ">>";
234
+ const redirectMode = hasRedirect
235
+ ? lastOp?.operator === ">>"
236
+ ? "append"
237
+ : "overwrite"
238
+ : undefined;
239
+ const redirectTarget = hasRedirect ? finalValue : undefined;
240
+
241
+ return {
242
+ isPiped: segments.length > 1 || hasRedirect,
243
+ segments,
244
+ raw: input,
245
+ hasRedirect,
246
+ redirectMode,
247
+ redirectTarget,
248
+ };
249
+ }
250
+
251
+ /**
252
+ * Check if input contains pipe operators
253
+ *
254
+ * @param input - Raw command string
255
+ * @returns true if pipe operators are present
256
+ */
257
+ static hasPipeOperators(input: string): boolean {
258
+ const operators = findPipeOperators(input);
259
+ return operators.length > 0;
260
+ }
261
+
262
+ /**
263
+ * Check if input has file redirection
264
+ *
265
+ * @param input - Raw command string
266
+ * @returns true if > or >> is present
267
+ */
268
+ static hasRedirection(input: string): boolean {
269
+ const operators = findPipeOperators(input);
270
+ return operators.some((op) => op.operator === ">" || op.operator === ">>");
271
+ }
272
+ }
273
+
274
+ // =============================================================================
275
+ // T054: Piped Command Executor
276
+ // =============================================================================
277
+
278
+ /**
279
+ * Executor function type for piped commands
280
+ * Returns output string for piping to next command
281
+ */
282
+ export type PipeCommandExecutorFn = (
283
+ command: string,
284
+ input?: string,
285
+ signal?: AbortSignal
286
+ ) => Promise<{ result: CommandResult; output: string }>;
287
+
288
+ /**
289
+ * File writer function type for redirections
290
+ */
291
+ export type FileWriterFn = (
292
+ path: string,
293
+ content: string,
294
+ mode: "overwrite" | "append"
295
+ ) => Promise<void>;
296
+
297
+ /**
298
+ * Piped Command Executor
299
+ *
300
+ * Executes a pipe of commands according to shell semantics:
301
+ * - | : Pass output of left as input to right
302
+ * - > : Write final output to file (overwrite)
303
+ * - >> : Write final output to file (append)
304
+ *
305
+ * @example
306
+ * ```typescript
307
+ * const executor = new PipedCommandExecutor(
308
+ * async (cmd, input) => ({
309
+ * result: { type: 'success', message: `Processed: ${cmd}` },
310
+ * output: `Output from ${cmd} with input: ${input ?? 'none'}`
311
+ * }),
312
+ * async (path, content, mode) => {
313
+ * // Write to file
314
+ * }
315
+ * );
316
+ *
317
+ * const result = await executor.execute('/list | /filter > output.txt');
318
+ * ```
319
+ */
320
+ export class PipedCommandExecutor {
321
+ private readonly executeFn: PipeCommandExecutorFn;
322
+ private readonly writeFileFn?: FileWriterFn;
323
+
324
+ /**
325
+ * Create a PipedCommandExecutor
326
+ *
327
+ * @param executeFn - Function to execute individual commands
328
+ * @param writeFileFn - Optional function to write output to files
329
+ */
330
+ constructor(executeFn: PipeCommandExecutorFn, writeFileFn?: FileWriterFn) {
331
+ this.executeFn = executeFn;
332
+ this.writeFileFn = writeFileFn;
333
+ }
334
+
335
+ /** Create an error result for pipe execution */
336
+ private createErrorResult(
337
+ code: CommandErrorCode,
338
+ message: string,
339
+ output: string,
340
+ executedCount: number,
341
+ totalCount: number
342
+ ): PipeExecutionResult {
343
+ return {
344
+ result: { kind: "error", code, message },
345
+ output,
346
+ executedCount,
347
+ totalCount,
348
+ completed: false,
349
+ };
350
+ }
351
+
352
+ /** Execute command segments in sequence */
353
+ private async executeCommandSequence(
354
+ commandSegments: PipeSegment[],
355
+ signal?: AbortSignal
356
+ ): Promise<{ result: CommandResult; output: string; executedCount: number; aborted: boolean }> {
357
+ let currentOutput = "";
358
+ let lastResult: CommandResult = { kind: "success", message: "" };
359
+ let executedCount = 0;
360
+
361
+ for (let i = 0; i < commandSegments.length; i++) {
362
+ if (signal?.aborted) {
363
+ return {
364
+ result: {
365
+ kind: "error",
366
+ code: "COMMAND_ABORTED",
367
+ message: "Pipe execution was cancelled",
368
+ },
369
+ output: currentOutput,
370
+ executedCount,
371
+ aborted: true,
372
+ };
373
+ }
374
+
375
+ const segment = commandSegments[i];
376
+ if (!segment) continue;
377
+
378
+ const pipeInput = i > 0 ? currentOutput : undefined;
379
+ const { result, output } = await this.executeFn(segment.value, pipeInput, signal);
380
+
381
+ lastResult = result;
382
+ currentOutput = output;
383
+ executedCount++;
384
+
385
+ if (result.kind === "error") {
386
+ return { result, output: currentOutput, executedCount, aborted: false };
387
+ }
388
+ }
389
+
390
+ return { result: lastResult, output: currentOutput, executedCount, aborted: false };
391
+ }
392
+
393
+ /** Handle file redirection after command execution */
394
+ private async handleFileRedirection(
395
+ parsed: PipeParseResult,
396
+ output: string,
397
+ executedCount: number
398
+ ): Promise<{ success: boolean; writtenFile?: string; error?: PipeExecutionResult }> {
399
+ if (!parsed.hasRedirect || !parsed.redirectTarget || !parsed.redirectMode) {
400
+ return { success: true };
401
+ }
402
+
403
+ if (!this.writeFileFn) {
404
+ return {
405
+ success: false,
406
+ error: this.createErrorResult(
407
+ "INTERNAL_ERROR",
408
+ "File redirection not supported: no file writer configured",
409
+ output,
410
+ executedCount,
411
+ parsed.segments.length
412
+ ),
413
+ };
414
+ }
415
+
416
+ try {
417
+ await this.writeFileFn(parsed.redirectTarget, output, parsed.redirectMode);
418
+ return { success: true, writtenFile: parsed.redirectTarget };
419
+ } catch (err) {
420
+ return {
421
+ success: false,
422
+ error: this.createErrorResult(
423
+ "INTERNAL_ERROR",
424
+ `Failed to write to file '${parsed.redirectTarget}': ${err instanceof Error ? err.message : String(err)}`,
425
+ output,
426
+ executedCount,
427
+ parsed.segments.length
428
+ ),
429
+ };
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Execute a piped command string
435
+ *
436
+ * @param input - Command string (may contain pipe operators)
437
+ * @param signal - Optional abort signal for cancellation
438
+ * @returns PipeExecutionResult with execution details
439
+ */
440
+ async execute(input: string, signal?: AbortSignal): Promise<PipeExecutionResult> {
441
+ const parsed = PipeParser.parse(input);
442
+
443
+ if (!parsed.isPiped || parsed.segments.length === 0) {
444
+ const command = parsed.segments[0]?.value ?? input;
445
+ const { result, output } = await this.executeFn(command, undefined, signal);
446
+ return { result, output, executedCount: 1, totalCount: 1, completed: true };
447
+ }
448
+
449
+ const commandSegments = parsed.segments.filter((s) => s.type === "command");
450
+ const seqResult = await this.executeCommandSequence(commandSegments, signal);
451
+
452
+ if (seqResult.aborted || seqResult.result.kind === "error") {
453
+ return {
454
+ result: seqResult.result,
455
+ output: seqResult.output,
456
+ executedCount: seqResult.executedCount,
457
+ totalCount: parsed.segments.length,
458
+ completed: false,
459
+ };
460
+ }
461
+
462
+ const redirectResult = await this.handleFileRedirection(
463
+ parsed,
464
+ seqResult.output,
465
+ seqResult.executedCount
466
+ );
467
+ if (!redirectResult.success && redirectResult.error) {
468
+ return redirectResult.error;
469
+ }
470
+
471
+ return {
472
+ result: seqResult.result,
473
+ output: seqResult.output,
474
+ executedCount: seqResult.executedCount,
475
+ totalCount: parsed.segments.length,
476
+ completed: true,
477
+ writtenFile: redirectResult.writtenFile,
478
+ };
479
+ }
480
+ }