@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,361 @@
1
+ /**
2
+ * Batch Executor (T-048)
3
+ *
4
+ * Executes multiple commands in sequence from a batch script.
5
+ * Supports continue-on-error option for resilient batch processing.
6
+ *
7
+ * @module cli/commands/batch/executor
8
+ */
9
+
10
+ import type { CommandExecutor } from "../executor.js";
11
+ import type { CommandResult } from "../types.js";
12
+
13
+ // =============================================================================
14
+ // Interfaces
15
+ // =============================================================================
16
+
17
+ /**
18
+ * Configuration for batch execution
19
+ */
20
+ export interface BatchConfig {
21
+ /** Continue executing remaining commands if one fails */
22
+ continueOnError?: boolean;
23
+ /** Abort signal for cancellation */
24
+ signal?: AbortSignal;
25
+ /** Callback invoked before each command */
26
+ onBeforeCommand?: (command: string, index: number) => void;
27
+ /** Callback invoked after each command */
28
+ onAfterCommand?: (command: string, index: number, result: CommandResult) => void;
29
+ /** Skip empty lines and comments (lines starting with #) */
30
+ skipComments?: boolean;
31
+ }
32
+
33
+ /**
34
+ * Result of a single command in batch execution
35
+ */
36
+ export interface BatchCommandResult {
37
+ /** Original command string */
38
+ command: string;
39
+ /** Command index in batch (0-based) */
40
+ index: number;
41
+ /** Execution result */
42
+ result: CommandResult;
43
+ /** Whether command was skipped (comment or empty) */
44
+ skipped: boolean;
45
+ }
46
+
47
+ /**
48
+ * Result of batch execution
49
+ */
50
+ export interface BatchResult {
51
+ /** Results for each command */
52
+ commands: BatchCommandResult[];
53
+ /** Total commands processed */
54
+ total: number;
55
+ /** Number of successful commands */
56
+ succeeded: number;
57
+ /** Number of failed commands */
58
+ failed: number;
59
+ /** Number of skipped commands */
60
+ skipped: number;
61
+ /** Whether batch completed (not aborted early) */
62
+ completed: boolean;
63
+ /** Error that caused early abort (if any) */
64
+ abortError?: Error;
65
+ }
66
+
67
+ /**
68
+ * Result of batch script validation
69
+ */
70
+ export interface BatchValidationResult {
71
+ /** Whether script is valid */
72
+ valid: boolean;
73
+ /** Number of commands found */
74
+ commandCount: number;
75
+ /** Validation warnings */
76
+ warnings: string[];
77
+ }
78
+
79
+ // =============================================================================
80
+ // Batch Script Parser
81
+ // =============================================================================
82
+
83
+ /**
84
+ * Parses batch scripts into individual commands
85
+ *
86
+ * Handles:
87
+ * - Newline-separated commands
88
+ * - Comment lines (starting with #)
89
+ * - Empty lines
90
+ * - Leading/trailing whitespace
91
+ */
92
+ // biome-ignore lint/complexity/noStaticOnlyClass: Parser provides a logical grouping for batch parsing functionality
93
+ export class BatchScriptParser {
94
+ /**
95
+ * Parse a batch script into command lines
96
+ *
97
+ * @param script - Batch script content
98
+ * @param skipComments - Whether to filter out comments and empty lines
99
+ * @returns Array of command strings
100
+ */
101
+ static parse(script: string, skipComments = true): string[] {
102
+ const lines = script.split(/\r?\n/);
103
+
104
+ if (!skipComments) {
105
+ return lines;
106
+ }
107
+
108
+ const result: string[] = [];
109
+ for (const line of lines) {
110
+ const trimmed = line.trim();
111
+ // Skip empty lines
112
+ if (trimmed === "") continue;
113
+ // Skip comment lines
114
+ if (trimmed.startsWith("#")) continue;
115
+ result.push(trimmed);
116
+ }
117
+ return result;
118
+ }
119
+
120
+ /**
121
+ * Check if a line is a comment
122
+ *
123
+ * @param line - Line to check
124
+ * @returns true if line is a comment
125
+ */
126
+ static isComment(line: string): boolean {
127
+ return line.trim().startsWith("#");
128
+ }
129
+
130
+ /**
131
+ * Check if a line is empty (whitespace only)
132
+ *
133
+ * @param line - Line to check
134
+ * @returns true if line is empty
135
+ */
136
+ static isEmpty(line: string): boolean {
137
+ return line.trim() === "";
138
+ }
139
+
140
+ /**
141
+ * Validate batch script for common issues
142
+ *
143
+ * @param script - Batch script content
144
+ * @returns Validation result with any warnings
145
+ */
146
+ static validate(script: string): BatchValidationResult {
147
+ const lines = script.split(/\r?\n/);
148
+ const warnings: string[] = [];
149
+ let commandCount = 0;
150
+
151
+ for (let i = 0; i < lines.length; i++) {
152
+ const rawLine = lines[i];
153
+ if (rawLine === undefined) continue;
154
+ const line = rawLine.trim();
155
+
156
+ if (BatchScriptParser.isEmpty(line) || BatchScriptParser.isComment(line)) {
157
+ continue;
158
+ }
159
+
160
+ commandCount++;
161
+
162
+ // Check for common issues
163
+ if (!line.startsWith("/")) {
164
+ warnings.push(`Line ${i + 1}: Command should start with /`);
165
+ }
166
+ }
167
+
168
+ return {
169
+ valid: commandCount > 0,
170
+ commandCount,
171
+ warnings,
172
+ };
173
+ }
174
+ }
175
+
176
+ // =============================================================================
177
+ // Batch Executor
178
+ // =============================================================================
179
+
180
+ /**
181
+ * Executes batch scripts with error handling
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * const batch = new BatchExecutor(executor);
186
+ *
187
+ * const script = `
188
+ * # Setup commands
189
+ * /login anthropic
190
+ * /config set theme dark
191
+ *
192
+ * # Main operation
193
+ * /help
194
+ * `;
195
+ *
196
+ * const result = await batch.execute(script, {
197
+ * continueOnError: true,
198
+ * onBeforeCommand: (cmd, i) => console.log(`Running ${i + 1}: ${cmd}`),
199
+ * });
200
+ *
201
+ * console.log(`Completed: ${result.succeeded}/${result.total}`);
202
+ * ```
203
+ */
204
+ export class BatchExecutor {
205
+ constructor(private readonly executor: CommandExecutor) {}
206
+
207
+ /**
208
+ * Execute a batch script
209
+ *
210
+ * @param script - Batch script content (newline-separated commands)
211
+ * @param config - Batch execution configuration
212
+ * @returns Batch execution result
213
+ */
214
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Core batch execution logic requires sequential processing
215
+ async execute(script: string, config: BatchConfig = {}): Promise<BatchResult> {
216
+ const {
217
+ continueOnError = false,
218
+ signal,
219
+ onBeforeCommand,
220
+ onAfterCommand,
221
+ skipComments = true,
222
+ } = config;
223
+
224
+ const lines = script.split(/\r?\n/);
225
+ const commands: BatchCommandResult[] = [];
226
+ let completed = true;
227
+ let abortError: Error | undefined;
228
+
229
+ for (let i = 0; i < lines.length; i++) {
230
+ // Check for abort signal
231
+ if (signal?.aborted) {
232
+ completed = false;
233
+ abortError = new Error("Batch execution aborted");
234
+ break;
235
+ }
236
+
237
+ const line = lines[i];
238
+ if (line === undefined) continue;
239
+ const trimmedLine = line.trim();
240
+
241
+ // Handle comments and empty lines
242
+ if (
243
+ skipComments &&
244
+ (BatchScriptParser.isEmpty(trimmedLine) || BatchScriptParser.isComment(trimmedLine))
245
+ ) {
246
+ commands.push({
247
+ command: line,
248
+ index: i,
249
+ result: { kind: "success" },
250
+ skipped: true,
251
+ });
252
+ continue;
253
+ }
254
+
255
+ // Execute command
256
+ onBeforeCommand?.(trimmedLine, i);
257
+
258
+ try {
259
+ const result = await this.executor.execute(trimmedLine, signal);
260
+
261
+ commands.push({
262
+ command: trimmedLine,
263
+ index: i,
264
+ result,
265
+ skipped: false,
266
+ });
267
+
268
+ onAfterCommand?.(trimmedLine, i, result);
269
+
270
+ // Check for failure
271
+ if (result.kind === "error" && !continueOnError) {
272
+ completed = false;
273
+ abortError = new Error(`Command failed: ${result.message}`);
274
+ break;
275
+ }
276
+ } catch (error) {
277
+ const errorResult: CommandResult = {
278
+ kind: "error",
279
+ code: "INTERNAL_ERROR",
280
+ message: error instanceof Error ? error.message : String(error),
281
+ };
282
+
283
+ commands.push({
284
+ command: trimmedLine,
285
+ index: i,
286
+ result: errorResult,
287
+ skipped: false,
288
+ });
289
+
290
+ onAfterCommand?.(trimmedLine, i, errorResult);
291
+
292
+ if (!continueOnError) {
293
+ completed = false;
294
+ abortError = error instanceof Error ? error : new Error(String(error));
295
+ break;
296
+ }
297
+ }
298
+ }
299
+
300
+ // Calculate summary
301
+ const executed = commands.filter((c) => !c.skipped);
302
+ const succeeded = executed.filter((c) => c.result.kind === "success").length;
303
+ const failed = executed.filter((c) => c.result.kind === "error").length;
304
+ const skipped = commands.filter((c) => c.skipped).length;
305
+
306
+ return {
307
+ commands,
308
+ total: executed.length,
309
+ succeeded,
310
+ failed,
311
+ skipped,
312
+ completed,
313
+ abortError,
314
+ };
315
+ }
316
+
317
+ /**
318
+ * Execute commands from an array
319
+ *
320
+ * @param commands - Array of command strings
321
+ * @param config - Batch execution configuration
322
+ * @returns Batch execution result
323
+ */
324
+ async executeCommands(commands: string[], config: BatchConfig = {}): Promise<BatchResult> {
325
+ const script = commands.join("\n");
326
+ return this.execute(script, { ...config, skipComments: false });
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Create a batch script from an array of commands
332
+ *
333
+ * @param commands - Array of command strings
334
+ * @param options - Script creation options
335
+ * @returns Formatted batch script
336
+ */
337
+ export function createBatchScript(
338
+ commands: string[],
339
+ options: { header?: string; comments?: Record<number, string> } = {}
340
+ ): string {
341
+ const { header, comments = {} } = options;
342
+ const lines: string[] = [];
343
+
344
+ if (header) {
345
+ lines.push(`# ${header}`);
346
+ lines.push("");
347
+ }
348
+
349
+ for (let i = 0; i < commands.length; i++) {
350
+ const comment = comments[i];
351
+ const cmd = commands[i];
352
+ if (comment) {
353
+ lines.push(`# ${comment}`);
354
+ }
355
+ if (cmd !== undefined) {
356
+ lines.push(cmd);
357
+ }
358
+ }
359
+
360
+ return lines.join("\n");
361
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Batch Module
3
+ *
4
+ * Barrel file for batch execution functionality.
5
+ *
6
+ * @module cli/commands/batch
7
+ */
8
+
9
+ // Re-export command
10
+ export * from "./command.js";
11
+ // Re-export executor components
12
+ export * from "./executor.js";
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Commit Command
3
+ *
4
+ * Quick Git commit command with optional auto-generated message.
5
+ * Inspired by Aider's /commit command pattern.
6
+ *
7
+ * Usage:
8
+ * - /commit - Stage all changes and commit with auto-generated message
9
+ * - /commit "message" - Stage all changes and commit with specified message
10
+ * - /commit --amend - Amend the last commit
11
+ *
12
+ * @module cli/commands/commit
13
+ */
14
+
15
+ import { execSync } from "node:child_process";
16
+ import type { CommandContext, CommandResult, SlashCommand } from "./types.js";
17
+ import { error, success } from "./types.js";
18
+
19
+ // =============================================================================
20
+ // Git Utilities
21
+ // =============================================================================
22
+
23
+ /**
24
+ * Execute a git command and return the result.
25
+ */
26
+ function runGitCommand(command: string, cwd?: string): { success: boolean; output: string } {
27
+ try {
28
+ const output = execSync(command, {
29
+ encoding: "utf-8",
30
+ cwd: cwd ?? process.cwd(),
31
+ stdio: ["pipe", "pipe", "pipe"],
32
+ });
33
+ return { success: true, output: output.trim() };
34
+ } catch (err) {
35
+ const error = err as { stderr?: string; message?: string };
36
+ return { success: false, output: error.stderr?.trim() ?? error.message ?? "Unknown error" };
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Check if we're in a git repository.
42
+ */
43
+ function isGitRepo(): boolean {
44
+ const result = runGitCommand("git rev-parse --is-inside-work-tree");
45
+ return result.success && result.output === "true";
46
+ }
47
+
48
+ /**
49
+ * Get list of staged files.
50
+ */
51
+ function getStagedFiles(): string[] {
52
+ const result = runGitCommand("git diff --cached --name-only");
53
+ if (!result.success || !result.output) return [];
54
+ return result.output.split("\n").filter((f) => f.trim().length > 0);
55
+ }
56
+
57
+ /**
58
+ * Get list of unstaged/untracked changes.
59
+ */
60
+ function getUnstagedChanges(): { modified: string[]; untracked: string[] } {
61
+ const statusResult = runGitCommand("git status --porcelain");
62
+ if (!statusResult.success) return { modified: [], untracked: [] };
63
+
64
+ const modified: string[] = [];
65
+ const untracked: string[] = [];
66
+
67
+ for (const line of statusResult.output.split("\n")) {
68
+ if (!line.trim()) continue;
69
+ const status = line.slice(0, 2);
70
+ const file = line.slice(3);
71
+
72
+ if (status.includes("?")) {
73
+ untracked.push(file);
74
+ } else if (status[1] !== " " && status[1] !== "?") {
75
+ modified.push(file);
76
+ }
77
+ }
78
+
79
+ return { modified, untracked };
80
+ }
81
+
82
+ /**
83
+ * Generate a simple commit message from staged changes.
84
+ */
85
+ function generateCommitMessage(stagedFiles: string[]): string {
86
+ if (stagedFiles.length === 0) return "Update files";
87
+ if (stagedFiles.length === 1) {
88
+ const file = stagedFiles[0];
89
+ return `Update ${file}`;
90
+ }
91
+ // Group by directory or type
92
+ const extensions = new Set(stagedFiles.map((f) => f.split(".").pop() ?? "file"));
93
+ if (extensions.size === 1) {
94
+ const ext = [...extensions][0];
95
+ return `Update ${stagedFiles.length} ${ext} files`;
96
+ }
97
+ return `Update ${stagedFiles.length} files`;
98
+ }
99
+
100
+ // =============================================================================
101
+ // Command Definition
102
+ // =============================================================================
103
+
104
+ /**
105
+ * /commit command - Quick git commit with optional message.
106
+ *
107
+ * Stages all changes and commits. If no message is provided,
108
+ * generates a simple descriptive message based on changed files.
109
+ */
110
+ export const commitCommand: SlashCommand = {
111
+ name: "commit",
112
+ description: "Stage and commit all changes with optional message",
113
+ kind: "builtin",
114
+ category: "tools",
115
+ aliases: ["ci"],
116
+ positionalArgs: [
117
+ {
118
+ name: "message",
119
+ type: "string",
120
+ description: "Commit message (auto-generated if omitted)",
121
+ required: false,
122
+ },
123
+ ],
124
+ namedArgs: [
125
+ {
126
+ name: "amend",
127
+ shorthand: "a",
128
+ type: "boolean",
129
+ description: "Amend the last commit",
130
+ required: false,
131
+ default: false,
132
+ },
133
+ {
134
+ name: "no-stage",
135
+ shorthand: "n",
136
+ type: "boolean",
137
+ description: "Skip staging (commit only already staged files)",
138
+ required: false,
139
+ default: false,
140
+ },
141
+ ],
142
+ examples: [
143
+ "/commit - Auto-generate message and commit all",
144
+ '/commit "Fix login bug" - Commit with specific message',
145
+ "/commit --amend - Amend last commit",
146
+ '/commit --amend "New msg" - Amend with new message',
147
+ ],
148
+
149
+ execute: async (ctx: CommandContext): Promise<CommandResult> => {
150
+ // Check if we're in a git repository
151
+ if (!isGitRepo()) {
152
+ return error("OPERATION_NOT_ALLOWED", "Not in a git repository", [
153
+ "Navigate to a git repository first",
154
+ "Use `git init` to initialize a new repository",
155
+ ]);
156
+ }
157
+
158
+ const message = ctx.parsedArgs.positional[0] as string | undefined;
159
+ const amend = ctx.parsedArgs.named["amend"] as boolean | undefined;
160
+ const noStage = ctx.parsedArgs.named["no-stage"] as boolean | undefined;
161
+
162
+ // Stage all changes unless --no-stage is specified
163
+ if (!noStage) {
164
+ const stageResult = runGitCommand("git add -A");
165
+ if (!stageResult.success) {
166
+ return error("INTERNAL_ERROR", `Failed to stage changes: ${stageResult.output}`);
167
+ }
168
+ }
169
+
170
+ // Check for staged changes
171
+ const stagedFiles = getStagedFiles();
172
+ if (stagedFiles.length === 0 && !amend) {
173
+ const unstaged = getUnstagedChanges();
174
+ if (unstaged.modified.length === 0 && unstaged.untracked.length === 0) {
175
+ return error("OPERATION_NOT_ALLOWED", "Nothing to commit - working tree is clean");
176
+ }
177
+ return error("OPERATION_NOT_ALLOWED", "No staged changes to commit", [
178
+ "Use /commit without --no-stage to auto-stage all changes",
179
+ "Stage files manually with `git add <file>`",
180
+ ]);
181
+ }
182
+
183
+ // Generate or use provided message
184
+ const commitMessage = message ?? generateCommitMessage(stagedFiles);
185
+
186
+ // Build commit command
187
+ let gitCmd = `git commit -m "${commitMessage.replace(/"/g, '\\"')}"`;
188
+ if (amend) {
189
+ gitCmd = message
190
+ ? `git commit --amend -m "${commitMessage.replace(/"/g, '\\"')}"`
191
+ : "git commit --amend --no-edit";
192
+ }
193
+
194
+ // Execute commit
195
+ const commitResult = runGitCommand(gitCmd);
196
+ if (!commitResult.success) {
197
+ return error("INTERNAL_ERROR", `Commit failed: ${commitResult.output}`);
198
+ }
199
+
200
+ // Get the commit hash
201
+ const hashResult = runGitCommand("git rev-parse --short HEAD");
202
+ const commitHash = hashResult.success ? hashResult.output : "unknown";
203
+
204
+ // Build success message
205
+ const lines = [
206
+ amend ? "📝 Amended commit" : "✅ Committed successfully",
207
+ "",
208
+ ` Commit: ${commitHash}`,
209
+ ` Message: ${commitMessage}`,
210
+ ];
211
+
212
+ if (!amend && stagedFiles.length > 0) {
213
+ lines.push(` Files: ${stagedFiles.length} changed`);
214
+ if (stagedFiles.length <= 5) {
215
+ for (const file of stagedFiles) {
216
+ lines.push(` • ${file}`);
217
+ }
218
+ } else {
219
+ for (const file of stagedFiles.slice(0, 3)) {
220
+ lines.push(` • ${file}`);
221
+ }
222
+ lines.push(` • ... and ${stagedFiles.length - 3} more`);
223
+ }
224
+ }
225
+
226
+ return success(lines.join("\n"), {
227
+ hash: commitHash,
228
+ message: commitMessage,
229
+ files: stagedFiles,
230
+ amended: amend ?? false,
231
+ });
232
+ },
233
+ };
234
+
235
+ export default commitCommand;