@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,551 @@
1
+ /**
2
+ * Think Slash Command (Thinking Mode Toggle)
3
+ *
4
+ * Provides slash command for managing extended thinking/reasoning mode:
5
+ * - /think [on|off] - Toggle or set thinking mode
6
+ * - /think [on] --budget <tokens> - Set thinking budget
7
+ *
8
+ * @module cli/commands/think
9
+ */
10
+
11
+ import { loadConfig } from "@vellum/core";
12
+ import {
13
+ getThinkingDisplayMode,
14
+ getThinkingSettings,
15
+ setThinkingDisplayMode,
16
+ setThinkingSettings,
17
+ type ThinkingDisplayMode,
18
+ type ThinkingSettings,
19
+ } from "../tui/i18n/index.js";
20
+ import type { CommandContext, CommandResult, SlashCommand } from "./types.js";
21
+ import { error, success } from "./types.js";
22
+
23
+ // =============================================================================
24
+ // Types
25
+ // =============================================================================
26
+
27
+ /**
28
+ * Priority for merging thinking configuration.
29
+ * - "global": Global /think state takes precedence
30
+ * - "mode": Mode's extendedThinking setting takes precedence
31
+ * - "merge": Either enabled means thinking is enabled
32
+ */
33
+ export type ThinkingPriority = "global" | "mode" | "merge";
34
+
35
+ /** Re-export ThinkingDisplayMode for consumers */
36
+ export type { ThinkingDisplayMode };
37
+
38
+ /**
39
+ * Effective thinking configuration for LLM calls.
40
+ */
41
+ export interface EffectiveThinkingConfig {
42
+ enabled: boolean;
43
+ budgetTokens?: number;
44
+ }
45
+
46
+ // =============================================================================
47
+ // Module State
48
+ // =============================================================================
49
+
50
+ /**
51
+ * Current thinking configuration state.
52
+ * This is a simple in-memory state for now.
53
+ * In a full implementation, this would be persisted to config.
54
+ */
55
+ interface ThinkingState {
56
+ enabled: boolean;
57
+ budgetTokens: number;
58
+ priority: ThinkingPriority;
59
+ }
60
+
61
+ /**
62
+ * Default thinking state
63
+ */
64
+ const defaultThinkingState: ThinkingState = {
65
+ enabled: false,
66
+ budgetTokens: 10000,
67
+ priority: "merge",
68
+ };
69
+
70
+ /**
71
+ * Load initial thinking state from settings file first, then config file.
72
+ * Settings file takes precedence (user-modified via /think command).
73
+ * Falls back to config, then defaults if neither is available.
74
+ */
75
+ function loadInitialThinkingState(): ThinkingState {
76
+ // First try to load from persisted settings (user preference)
77
+ const savedSettings = getThinkingSettings();
78
+ if (savedSettings) {
79
+ return {
80
+ enabled: savedSettings.enabled ?? defaultThinkingState.enabled,
81
+ budgetTokens: savedSettings.budgetTokens ?? defaultThinkingState.budgetTokens,
82
+ priority: savedSettings.priority ?? defaultThinkingState.priority,
83
+ };
84
+ }
85
+
86
+ // Fall back to config file
87
+ const result = loadConfig({ suppressDeprecationWarnings: true });
88
+ if (!result.ok || !result.value.thinking) {
89
+ return { ...defaultThinkingState };
90
+ }
91
+
92
+ const { enabled, budgetTokens, priority } = result.value.thinking;
93
+ return {
94
+ enabled: enabled ?? defaultThinkingState.enabled,
95
+ budgetTokens: budgetTokens ?? defaultThinkingState.budgetTokens,
96
+ priority: priority ?? defaultThinkingState.priority,
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Current thinking state (initialized from config, managed in memory at runtime)
102
+ */
103
+ let thinkingState: ThinkingState = loadInitialThinkingState();
104
+
105
+ /**
106
+ * Listeners for thinking state changes
107
+ */
108
+ type ThinkingStateListener = (state: ThinkingState) => void;
109
+ const listeners: Set<ThinkingStateListener> = new Set();
110
+
111
+ /**
112
+ * Persist current thinking state to settings file.
113
+ * Called async to avoid blocking UI.
114
+ */
115
+ function persistThinkingState(): void {
116
+ const settings: ThinkingSettings = {
117
+ enabled: thinkingState.enabled,
118
+ budgetTokens: thinkingState.budgetTokens,
119
+ priority: thinkingState.priority,
120
+ };
121
+ // Fire-and-forget - don't block UI for settings persistence
122
+ setThinkingSettings(settings);
123
+ }
124
+
125
+ // =============================================================================
126
+ // Public API for State Management
127
+ // =============================================================================
128
+
129
+ /**
130
+ * Get the current thinking state.
131
+ *
132
+ * @returns Current thinking configuration
133
+ */
134
+ export function getThinkingState(): Readonly<ThinkingState> {
135
+ return { ...thinkingState };
136
+ }
137
+
138
+ /**
139
+ * Set the thinking enabled state.
140
+ *
141
+ * @param enabled - Whether thinking is enabled
142
+ */
143
+ export function setThinkingEnabled(enabled: boolean): void {
144
+ thinkingState = { ...thinkingState, enabled };
145
+ notifyListeners();
146
+ persistThinkingState();
147
+ }
148
+
149
+ /**
150
+ * Set the thinking budget tokens.
151
+ *
152
+ * @param budgetTokens - Number of tokens for thinking budget
153
+ */
154
+ export function setThinkingBudget(budgetTokens: number): void {
155
+ thinkingState = { ...thinkingState, budgetTokens };
156
+ notifyListeners();
157
+ persistThinkingState();
158
+ }
159
+
160
+ /**
161
+ * Toggle thinking mode on/off.
162
+ *
163
+ * @returns New enabled state
164
+ */
165
+ export function toggleThinking(): boolean {
166
+ thinkingState = { ...thinkingState, enabled: !thinkingState.enabled };
167
+ notifyListeners();
168
+ persistThinkingState();
169
+ return thinkingState.enabled;
170
+ }
171
+
172
+ /**
173
+ * Subscribe to thinking state changes.
174
+ *
175
+ * @param listener - Callback function for state changes
176
+ * @returns Unsubscribe function
177
+ */
178
+ export function subscribeToThinkingState(listener: ThinkingStateListener): () => void {
179
+ listeners.add(listener);
180
+ return () => {
181
+ listeners.delete(listener);
182
+ };
183
+ }
184
+
185
+ /**
186
+ * Notify all listeners of state change.
187
+ */
188
+ function notifyListeners(): void {
189
+ const state = getThinkingState();
190
+ for (const listener of listeners) {
191
+ listener(state);
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Reset thinking state to defaults.
197
+ */
198
+ export function resetThinkingState(): void {
199
+ thinkingState = { ...defaultThinkingState };
200
+ notifyListeners();
201
+ }
202
+
203
+ /**
204
+ * Set the thinking priority.
205
+ *
206
+ * @param priority - Priority for merging thinking config
207
+ */
208
+ export function setThinkingPriority(priority: ThinkingPriority): void {
209
+ thinkingState = { ...thinkingState, priority };
210
+ notifyListeners();
211
+ persistThinkingState();
212
+ }
213
+
214
+ // =============================================================================
215
+ // Display Mode State (Separate from thinking state)
216
+ // =============================================================================
217
+
218
+ /**
219
+ * Listeners for display mode changes
220
+ */
221
+ type DisplayModeListener = (mode: ThinkingDisplayMode) => void;
222
+ const displayModeListeners: Set<DisplayModeListener> = new Set();
223
+
224
+ /**
225
+ * Get the current thinking display mode.
226
+ *
227
+ * @returns Current display mode ("full" or "compact")
228
+ */
229
+ export { getThinkingDisplayMode };
230
+
231
+ /**
232
+ * Set the thinking display mode.
233
+ *
234
+ * @param mode - Display mode ("full" or "compact")
235
+ */
236
+ export function setDisplayMode(mode: ThinkingDisplayMode): void {
237
+ setThinkingDisplayMode(mode);
238
+ notifyDisplayModeListeners(mode);
239
+ }
240
+
241
+ /**
242
+ * Subscribe to display mode changes.
243
+ *
244
+ * @param listener - Callback function for mode changes
245
+ * @returns Unsubscribe function
246
+ */
247
+ export function subscribeToDisplayMode(listener: DisplayModeListener): () => void {
248
+ displayModeListeners.add(listener);
249
+ return () => {
250
+ displayModeListeners.delete(listener);
251
+ };
252
+ }
253
+
254
+ /**
255
+ * Notify all display mode listeners.
256
+ */
257
+ function notifyDisplayModeListeners(mode: ThinkingDisplayMode): void {
258
+ for (const listener of displayModeListeners) {
259
+ listener(mode);
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Get the effective thinking configuration for LLM calls.
265
+ *
266
+ * This function merges the global thinking state (from /think command)
267
+ * with the mode's extendedThinking setting based on the priority setting.
268
+ *
269
+ * @param modeExtendedThinking - Mode's extendedThinking setting (optional)
270
+ * @returns Effective thinking config for LLM.stream()
271
+ *
272
+ * @example
273
+ * ```typescript
274
+ * // Get effective config using mode's extendedThinking
275
+ * const thinkingConfig = getEffectiveThinkingConfig(mode.extendedThinking);
276
+ *
277
+ * // Use in LLM.stream()
278
+ * LLM.stream({
279
+ * ...otherParams,
280
+ * thinking: thinkingConfig,
281
+ * });
282
+ * ```
283
+ */
284
+ export function getEffectiveThinkingConfig(
285
+ modeExtendedThinking?: boolean
286
+ ): EffectiveThinkingConfig {
287
+ const { enabled: globalEnabled, budgetTokens, priority } = thinkingState;
288
+ const modeEnabled = modeExtendedThinking ?? false;
289
+
290
+ let effectiveEnabled: boolean;
291
+
292
+ switch (priority) {
293
+ case "global":
294
+ // Global /think state takes precedence
295
+ effectiveEnabled = globalEnabled;
296
+ break;
297
+ case "mode":
298
+ // Mode's extendedThinking takes precedence
299
+ effectiveEnabled = modeEnabled;
300
+ break;
301
+ default:
302
+ // Either enabled means thinking is enabled
303
+ effectiveEnabled = globalEnabled || modeEnabled;
304
+ break;
305
+ }
306
+
307
+ return {
308
+ enabled: effectiveEnabled,
309
+ budgetTokens: effectiveEnabled ? budgetTokens : undefined,
310
+ };
311
+ }
312
+
313
+ // =============================================================================
314
+ // Constants
315
+ // =============================================================================
316
+
317
+ /** Minimum budget tokens */
318
+ const MIN_BUDGET_TOKENS = 1000;
319
+
320
+ /** Maximum budget tokens */
321
+ const MAX_BUDGET_TOKENS = 128000;
322
+
323
+ /** Default budget tokens */
324
+ const DEFAULT_BUDGET_TOKENS = 10000;
325
+
326
+ // =============================================================================
327
+ // Helper Functions
328
+ // =============================================================================
329
+
330
+ /**
331
+ * Parse budget value from string.
332
+ *
333
+ * @param value - String value to parse (e.g., "20000", "20k", "20K")
334
+ * @returns Parsed number or null if invalid
335
+ */
336
+ function parseBudget(value: string): number | null {
337
+ // Handle k/K suffix for thousands
338
+ const normalized = value.toLowerCase().trim();
339
+ let multiplier = 1;
340
+ let numStr = normalized;
341
+
342
+ if (normalized.endsWith("k")) {
343
+ multiplier = 1000;
344
+ numStr = normalized.slice(0, -1);
345
+ }
346
+
347
+ const num = Number.parseFloat(numStr);
348
+ if (Number.isNaN(num) || !Number.isFinite(num)) {
349
+ return null;
350
+ }
351
+
352
+ return Math.round(num * multiplier);
353
+ }
354
+
355
+ /**
356
+ * Format budget for display.
357
+ *
358
+ * @param tokens - Number of tokens
359
+ * @returns Formatted string (e.g., "10K", "128K")
360
+ */
361
+ function formatBudget(tokens: number): string {
362
+ if (tokens >= 1000) {
363
+ return `${(tokens / 1000).toFixed(0)}K`;
364
+ }
365
+ return `${tokens}`;
366
+ }
367
+
368
+ // =============================================================================
369
+ // /think Command
370
+ // =============================================================================
371
+
372
+ /**
373
+ * /think command - Toggle or configure extended thinking mode.
374
+ *
375
+ * Usage:
376
+ * /think - Show current status and toggle
377
+ * /think on - Enable thinking mode
378
+ * /think off - Disable thinking mode
379
+ * /think on --budget 20000 - Enable with custom budget
380
+ * /think --budget 20k - Set budget (keeps current on/off state)
381
+ */
382
+ export const thinkCommand: SlashCommand = {
383
+ name: "think",
384
+ description: "Toggle extended thinking/reasoning mode",
385
+ kind: "builtin",
386
+ category: "workflow",
387
+ aliases: ["thinking"],
388
+ subcommands: [
389
+ { name: "on", description: "Enable extended thinking" },
390
+ { name: "off", description: "Disable extended thinking" },
391
+ { name: "mode", description: "Set display mode (full/compact)" },
392
+ ],
393
+ positionalArgs: [
394
+ {
395
+ name: "state",
396
+ type: "string",
397
+ description: "Enable or disable thinking (on/off) or set display mode",
398
+ required: false,
399
+ },
400
+ ],
401
+ namedArgs: [
402
+ {
403
+ name: "budget",
404
+ shorthand: "b",
405
+ type: "number",
406
+ description: `Token budget for thinking (${MIN_BUDGET_TOKENS}-${MAX_BUDGET_TOKENS})`,
407
+ required: false,
408
+ default: DEFAULT_BUDGET_TOKENS,
409
+ },
410
+ ],
411
+ examples: [
412
+ "/think - Show current thinking status",
413
+ "/think on - Enable thinking mode",
414
+ "/think off - Disable thinking mode",
415
+ "/think on --budget 20000 - Enable with 20K token budget",
416
+ "/think -b 50k - Set budget to 50K tokens",
417
+ "/think mode full - Show full thinking content",
418
+ "/think mode compact - Show compact thinking header only",
419
+ ],
420
+
421
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Command handler with multiple state transitions
422
+ execute: async (ctx: CommandContext): Promise<CommandResult> => {
423
+ const stateArg = ctx.parsedArgs.positional[0] as string | undefined;
424
+ const budgetArg = ctx.parsedArgs.named.budget as string | number | undefined;
425
+
426
+ // Handle budget argument if provided
427
+ let newBudget: number | undefined;
428
+ if (budgetArg !== undefined) {
429
+ const budgetValue =
430
+ typeof budgetArg === "number" ? budgetArg : parseBudget(String(budgetArg));
431
+
432
+ if (budgetValue === null) {
433
+ return error("INVALID_ARGUMENT", `Invalid budget value: ${budgetArg}`, [
434
+ `Budget must be a number between ${formatBudget(MIN_BUDGET_TOKENS)} and ${formatBudget(MAX_BUDGET_TOKENS)}.`,
435
+ "Examples: 10000, 20k, 50K, 128000",
436
+ ]);
437
+ }
438
+
439
+ if (budgetValue < MIN_BUDGET_TOKENS || budgetValue > MAX_BUDGET_TOKENS) {
440
+ return error("INVALID_ARGUMENT", `Budget out of range: ${formatBudget(budgetValue)}`, [
441
+ `Budget must be between ${formatBudget(MIN_BUDGET_TOKENS)} and ${formatBudget(MAX_BUDGET_TOKENS)}.`,
442
+ ]);
443
+ }
444
+
445
+ newBudget = budgetValue;
446
+ }
447
+
448
+ // If no state argument, show current status or toggle
449
+ if (!stateArg) {
450
+ // If budget was provided, just update budget
451
+ if (newBudget !== undefined) {
452
+ setThinkingBudget(newBudget);
453
+ const state = getThinkingState();
454
+ return success(
455
+ `🧠 Thinking budget set to ${formatBudget(state.budgetTokens)} tokens.\n` +
456
+ ` Status: ${state.enabled ? "â—† On" : "â—‡ Off"}`
457
+ );
458
+ }
459
+
460
+ // Show current status
461
+ const state = getThinkingState();
462
+ const displayMode = getThinkingDisplayMode();
463
+ const statusIcon = state.enabled ? "â—†" : "â—‡";
464
+ const statusText = state.enabled ? "On" : "Off";
465
+
466
+ const lines = [
467
+ "🧠 Extended Thinking Mode",
468
+ "",
469
+ ` Status: ${statusIcon} ${statusText}`,
470
+ ` Budget: ${formatBudget(state.budgetTokens)} tokens`,
471
+ ` Display: ${displayMode}`,
472
+ "",
473
+ "Commands:",
474
+ " /think on - Enable thinking",
475
+ " /think off - Disable thinking",
476
+ " /think -b 20k - Set budget to 20K tokens",
477
+ " /think mode full|compact - Set display mode",
478
+ "",
479
+ "Shortcuts:",
480
+ " Ctrl+T - Toggle thinking on/off",
481
+ " T (in /model) - Toggle in model selector",
482
+ ];
483
+
484
+ return success(lines.join("\n"));
485
+ }
486
+
487
+ // Parse state argument
488
+ const normalizedState = stateArg.toLowerCase().trim();
489
+
490
+ // Handle "mode" subcommand
491
+ if (normalizedState === "mode") {
492
+ const modeArg = ctx.parsedArgs.positional[1] as string | undefined;
493
+ const currentMode = getThinkingDisplayMode();
494
+
495
+ if (!modeArg) {
496
+ // Show current display mode
497
+ return success(
498
+ `🧠 Thinking display mode: ${currentMode}\n` +
499
+ " /think mode full - Show full content\n" +
500
+ " /think mode compact - Show header only"
501
+ );
502
+ }
503
+
504
+ const normalizedMode = modeArg.toLowerCase().trim();
505
+ if (normalizedMode === "full" || normalizedMode === "compact") {
506
+ setDisplayMode(normalizedMode);
507
+ return success(`🧠 Thinking display mode set to: ${normalizedMode}`);
508
+ }
509
+
510
+ return error("INVALID_ARGUMENT", `Invalid display mode: ${modeArg}`, [
511
+ "Valid modes: full, compact",
512
+ "Example: /think mode compact",
513
+ ]);
514
+ }
515
+
516
+ if (normalizedState === "on" || normalizedState === "1" || normalizedState === "true") {
517
+ setThinkingEnabled(true);
518
+ if (newBudget !== undefined) {
519
+ setThinkingBudget(newBudget);
520
+ }
521
+ const state = getThinkingState();
522
+ return success(
523
+ `🧠 ◆ Thinking mode enabled.\n Budget: ${formatBudget(state.budgetTokens)} tokens`
524
+ );
525
+ }
526
+
527
+ if (normalizedState === "off" || normalizedState === "0" || normalizedState === "false") {
528
+ setThinkingEnabled(false);
529
+ if (newBudget !== undefined) {
530
+ setThinkingBudget(newBudget);
531
+ }
532
+ return success("🧠 ◇ Thinking mode disabled.");
533
+ }
534
+
535
+ // Invalid state argument
536
+ return error("INVALID_ARGUMENT", `Invalid state: ${stateArg}`, [
537
+ "Valid states: on, off, mode",
538
+ "Example: /think on --budget 20000",
539
+ "Example: /think mode compact",
540
+ ]);
541
+ },
542
+ };
543
+
544
+ // =============================================================================
545
+ // Export All Think Commands
546
+ // =============================================================================
547
+
548
+ /**
549
+ * All think-related slash commands for registration.
550
+ */
551
+ export const thinkSlashCommands: SlashCommand[] = [thinkCommand];