@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,198 @@
1
+ /**
2
+ * Height Estimation Utilities (T002)
3
+ *
4
+ * Functions for estimating rendered line heights of text and messages.
5
+ * Used by virtualization and adaptive rendering systems.
6
+ *
7
+ * CRITICAL: Height estimates MUST be UPPER BOUNDS (never under-estimate).
8
+ * Under-estimation causes terminal overflow and UI "sinking" bugs.
9
+ *
10
+ * @module tui/utils/heightEstimator
11
+ */
12
+
13
+ import stringWidth from "string-width";
14
+ import type { Message } from "../context/MessagesContext.js";
15
+
16
+ // =============================================================================
17
+ // Constants - Upper Bound Safety Margins
18
+ // =============================================================================
19
+
20
+ /**
21
+ * Safety margin added to all height estimates to prevent overflow.
22
+ * Accounts for borders, padding, and rendering variations.
23
+ */
24
+ export const HEIGHT_SAFETY_MARGIN = 2;
25
+
26
+ /**
27
+ * Upper bound lines per tool call.
28
+ * Tool calls render as inline status indicators (Gemini-style):
29
+ * - Status icon + tool name + optional error = typically 1-2 lines
30
+ * Previous value of 10 was too conservative for inline rendering.
31
+ */
32
+ export const TOOL_CALL_UPPER_BOUND = 3;
33
+
34
+ /**
35
+ * Upper bound lines per tool call when a diff preview is shown.
36
+ * Accounts for status line + margin + MaxSizedBox diff preview (12 lines).
37
+ */
38
+ export const TOOL_CALL_DIFF_UPPER_BOUND = 14;
39
+
40
+ /**
41
+ * Upper bound for thinking block header and chrome.
42
+ * Includes collapsible header, borders, and internal padding.
43
+ */
44
+ export const THINKING_HEADER_UPPER_BOUND = 4;
45
+
46
+ /**
47
+ * Default estimated item height for virtualization.
48
+ * Used when no specific estimate is available.
49
+ * Increased from 4 to prevent overflow from under-estimation.
50
+ */
51
+ export const DEFAULT_ESTIMATED_ITEM_HEIGHT = 12;
52
+
53
+ /**
54
+ * Minimum message height (header + minimal content + margin).
55
+ */
56
+ export const MIN_MESSAGE_HEIGHT = 4;
57
+
58
+ // =============================================================================
59
+ // Types
60
+ // =============================================================================
61
+
62
+ interface DiffMetadata {
63
+ readonly diff: string;
64
+ readonly additions: number;
65
+ readonly deletions: number;
66
+ }
67
+
68
+ type ToolCallInfo = NonNullable<Message["toolCalls"]>[number];
69
+
70
+ /**
71
+ * Options for message height estimation.
72
+ */
73
+ export interface HeightEstimatorOptions {
74
+ /** Terminal width for line wrapping calculation */
75
+ readonly width: number;
76
+ /** Whether to include tool calls in height calculation (default: true) */
77
+ readonly includeToolCalls?: boolean;
78
+ /** Padding to subtract from width for content area (default: 4) */
79
+ readonly contentPadding?: number;
80
+ /** Padding to subtract for thinking blocks (default: 6) */
81
+ readonly thinkingPadding?: number;
82
+ }
83
+
84
+ // =============================================================================
85
+ // Functions
86
+ // =============================================================================
87
+
88
+ function isDiffMetadata(value: unknown): value is DiffMetadata {
89
+ if (!value || typeof value !== "object") {
90
+ return false;
91
+ }
92
+ const obj = value as Record<string, unknown>;
93
+ return (
94
+ typeof obj.diff === "string" &&
95
+ typeof obj.additions === "number" &&
96
+ typeof obj.deletions === "number"
97
+ );
98
+ }
99
+
100
+ function getDiffMetadata(result: unknown): DiffMetadata | null {
101
+ if (isDiffMetadata(result)) {
102
+ return result;
103
+ }
104
+ if (!result || typeof result !== "object") {
105
+ return null;
106
+ }
107
+ const diffMeta = (result as Record<string, unknown>).diffMeta;
108
+ return isDiffMetadata(diffMeta) ? diffMeta : null;
109
+ }
110
+
111
+ function getToolCallUpperBound(toolCall: ToolCallInfo): number {
112
+ const diffMeta = getDiffMetadata(toolCall.result);
113
+ const hasDiffContent =
114
+ toolCall.status === "completed" &&
115
+ diffMeta !== null &&
116
+ diffMeta.diff.trim() !== "" &&
117
+ diffMeta.diff !== "(no changes)";
118
+ return hasDiffContent ? TOOL_CALL_DIFF_UPPER_BOUND : TOOL_CALL_UPPER_BOUND;
119
+ }
120
+
121
+ /**
122
+ * Estimate the number of lines a text will occupy when wrapped.
123
+ * Uses string-width for accurate CJK/Emoji/ANSI handling.
124
+ *
125
+ * @param text - The text to measure
126
+ * @param width - The available terminal width
127
+ * @returns Estimated number of lines after wrapping
128
+ */
129
+ export function estimateWrappedLineCount(text: string, width: number): number {
130
+ const safeWidth = Math.max(1, width);
131
+ if (!text) {
132
+ return 1;
133
+ }
134
+
135
+ let total = 0;
136
+ const lines = text.split("\n");
137
+ for (const line of lines) {
138
+ // Use string-width for accurate display width calculation
139
+ // This correctly handles CJK characters (2 cells), Emoji, and ANSI escape codes
140
+ const lineWidth = stringWidth(line);
141
+ if (lineWidth === 0) {
142
+ total += 1;
143
+ continue;
144
+ }
145
+ total += Math.max(1, Math.ceil(lineWidth / safeWidth));
146
+ }
147
+ return total;
148
+ }
149
+
150
+ /**
151
+ * Estimate a message height in lines for virtualized scrolling and layout.
152
+ * Uses UPPER BOUND estimation to prevent terminal overflow.
153
+ *
154
+ * @param message - The message to measure
155
+ * @param options - Height estimation options
156
+ * @returns Estimated height in lines (conservative upper bound)
157
+ */
158
+ export function estimateMessageHeight(message: Message, options: HeightEstimatorOptions): number {
159
+ const { width, includeToolCalls = true, contentPadding = 4, thinkingPadding = 6 } = options;
160
+
161
+ // Use 2 lines for header (role + timestamp) to account for wrapping
162
+ const headerLines = 2;
163
+ // Margin between messages
164
+ const marginLines = 2;
165
+ const contentWidth = Math.max(10, width - contentPadding);
166
+ const thinkingWidth = Math.max(10, width - thinkingPadding);
167
+ const toolCalls = message.toolCalls ?? [];
168
+
169
+ // Tool group messages have special handling
170
+ if (message.role === "tool_group") {
171
+ const toolLines = toolCalls.reduce((sum, call) => sum + getToolCallUpperBound(call), 0);
172
+ // Each tool in a group can expand to multiple lines
173
+ return Math.max(MIN_MESSAGE_HEIGHT, toolLines) + marginLines;
174
+ }
175
+
176
+ const content = message.content || (message.isStreaming ? "" : "(empty)");
177
+ let lines = headerLines;
178
+
179
+ lines += estimateWrappedLineCount(content, contentWidth);
180
+
181
+ // Add thinking block lines if present (with upper bound for chrome)
182
+ if (message.thinking && message.thinking.length > 0) {
183
+ lines += THINKING_HEADER_UPPER_BOUND; // Collapsible header + borders
184
+ lines += estimateWrappedLineCount(message.thinking, thinkingWidth);
185
+ }
186
+
187
+ // Add tool call lines if enabled and present (UPPER BOUND per tool)
188
+ if (includeToolCalls && toolCalls.length > 0) {
189
+ lines += 1; // separator before tool calls (margin already counted)
190
+ lines += toolCalls.reduce((sum, call) => sum + getToolCallUpperBound(call), 0);
191
+ }
192
+
193
+ // FIX: Simplified height calculation to avoid double-counting margins
194
+ // Previous version added marginLines (2) + HEIGHT_SAFETY_MARGIN (2) = 4 extra lines
195
+ // which caused over-estimation and blank spaces in virtualized list
196
+ // Now we only add a single safety buffer
197
+ return Math.max(MIN_MESSAGE_HEIGHT, lines + HEIGHT_SAFETY_MARGIN);
198
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * TUI Utilities
3
+ *
4
+ * Utility functions for the Vellum CLI terminal UI.
5
+ *
6
+ * @module tui/utils
7
+ */
8
+
9
+ export {
10
+ disableBracketedPaste,
11
+ enableBracketedPaste,
12
+ extractPasteContent,
13
+ hasPasteEnd,
14
+ hasPasteStart,
15
+ PASTE_END,
16
+ PASTE_START,
17
+ } from "./bracketedPaste.js";
18
+
19
+ export { CursorManager, type CursorManagerImpl } from "./cursor-manager.js";
20
+
21
+ export {
22
+ clearTerminalCapabilitiesCache,
23
+ createHyperlink,
24
+ type DetectTerminalOptions,
25
+ degradeColor,
26
+ detectTerminal,
27
+ getSymbol,
28
+ getTerminalCapabilities,
29
+ getTerminalName,
30
+ type TerminalCapabilities,
31
+ type TerminalType,
32
+ } from "./detectTerminal.js";
33
+
34
+ export { findLastSafeSplitPoint } from "./findLastSafeSplitPoint.js";
35
+ // Height estimation utilities (T002)
36
+ export {
37
+ // Constants for upper-bound estimation
38
+ DEFAULT_ESTIMATED_ITEM_HEIGHT,
39
+ // Functions
40
+ estimateMessageHeight,
41
+ estimateWrappedLineCount,
42
+ HEIGHT_SAFETY_MARGIN,
43
+ type HeightEstimatorOptions,
44
+ MIN_MESSAGE_HEIGHT,
45
+ THINKING_HEADER_UPPER_BOUND,
46
+ TOOL_CALL_UPPER_BOUND,
47
+ } from "./heightEstimator.js";
48
+ // Narrow width detection
49
+ export { getNarrowBreakpoint, isNarrowWidth } from "./isNarrowWidth.js";
50
+ // Stdout guard for debugging (T002 Hardening)
51
+ export {
52
+ disableStdoutGuard,
53
+ enableStdoutGuard,
54
+ isStdoutGuardActive,
55
+ } from "./stdoutGuard.js";
56
+ export { isSyncUpdateSupported, syncUpdate } from "./synchronized-update.js";
57
+ // Text width utilities (CJK/emoji aware)
58
+ export {
59
+ countLines,
60
+ getVisualWidth,
61
+ padToWidth,
62
+ splitLines,
63
+ type TextAlign,
64
+ truncateToWidth,
65
+ wrapToWidth,
66
+ } from "./text-width.js";
67
+ // Text sanitization utilities (Phase 1 - World-Class TUI)
68
+ export {
69
+ type SanitizeOptions,
70
+ sanitize,
71
+ sanitizeAnsi,
72
+ sanitizeText,
73
+ } from "./textSanitizer.js";
74
+ // Text utilities (T002 Hardening)
75
+ export { hardWrap, truncateToDisplayWidth } from "./textUtils.js";
76
+ // Terminal UI sizing utilities
77
+ export {
78
+ clamp,
79
+ DEFAULT_TERMINAL_HEIGHT,
80
+ DEFAULT_TERMINAL_WIDTH,
81
+ getContentPadding,
82
+ getMaxContentWidth,
83
+ getTerminalHeight,
84
+ getTerminalSize,
85
+ getTerminalWidth,
86
+ lerp,
87
+ NARROW_CONTENT_RATIO,
88
+ NARROW_WIDTH_BREAKPOINT,
89
+ WIDE_CONTENT_RATIO,
90
+ WIDE_WIDTH_BREAKPOINT,
91
+ } from "./ui-sizing.js";
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Narrow Width Detection Utility
3
+ *
4
+ * Simple utility for detecting narrow terminal widths.
5
+ * Used for responsive layout decisions throughout the TUI.
6
+ *
7
+ * @module tui/utils/isNarrowWidth
8
+ */
9
+
10
+ import { getTerminalWidth, NARROW_WIDTH_BREAKPOINT } from "./ui-sizing.js";
11
+
12
+ /**
13
+ * Get the narrow width breakpoint constant.
14
+ *
15
+ * @returns The narrow width breakpoint (80 columns)
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const breakpoint = getNarrowBreakpoint();
20
+ * console.log(`Narrow mode below ${breakpoint} columns`);
21
+ * ```
22
+ */
23
+ export function getNarrowBreakpoint(): number {
24
+ return NARROW_WIDTH_BREAKPOINT;
25
+ }
26
+
27
+ /**
28
+ * Check if the terminal is in narrow width mode.
29
+ *
30
+ * Narrow mode is defined as terminal width <= 80 columns.
31
+ * In narrow mode, UI elements should be more compact and
32
+ * non-essential elements may be hidden.
33
+ *
34
+ * @param width - Terminal width to check (defaults to current terminal)
35
+ * @returns True if terminal is narrow (<= 80 columns)
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * if (isNarrowWidth()) {
40
+ * // Render compact layout
41
+ * } else {
42
+ * // Render full layout with sidebar
43
+ * }
44
+ *
45
+ * // Or with explicit width
46
+ * const narrow = isNarrowWidth(60); // true
47
+ * ```
48
+ */
49
+ export function isNarrowWidth(width?: number): boolean {
50
+ const terminalWidth = width ?? getTerminalWidth();
51
+ return terminalWidth <= NARROW_WIDTH_BREAKPOINT;
52
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Stdout Guard Utilities (T002 Hardening)
3
+ *
4
+ * Development-time utilities for detecting unexpected stdout writes
5
+ * that bypass Ink's rendering and could cause terminal overflow.
6
+ *
7
+ * IMPORTANT: This is for DEBUGGING ONLY. It must NOT block writes
8
+ * or break Ink's stdout handling.
9
+ *
10
+ * @module tui/utils/stdoutGuard
11
+ */
12
+
13
+ // Store the original stdout.write
14
+ let originalStdoutWrite: typeof process.stdout.write | null = null;
15
+ let guardActive = false;
16
+
17
+ /**
18
+ * Enable stdout guard for debugging non-Ink writes.
19
+ *
20
+ * When enabled (via VELLUM_DEBUG_STDOUT env var), this will log
21
+ * any writes to stdout that don't appear to come from Ink.
22
+ *
23
+ * SAFETY: All writes are allowed through - this only logs warnings.
24
+ */
25
+ export function enableStdoutGuard(): void {
26
+ // Only enable in debug mode
27
+ if (!process.env.VELLUM_DEBUG_STDOUT) {
28
+ return;
29
+ }
30
+
31
+ if (guardActive) {
32
+ return; // Already active
33
+ }
34
+
35
+ originalStdoutWrite = process.stdout.write.bind(process.stdout);
36
+ guardActive = true;
37
+
38
+ const guardedWrite = function (
39
+ this: NodeJS.WriteStream,
40
+ chunk: string | Uint8Array,
41
+ encodingOrCallback?: BufferEncoding | ((err?: Error | null) => void),
42
+ callback?: (err?: Error | null) => void
43
+ ): boolean {
44
+ // Try to detect if this is an Ink write by checking the stack
45
+ const stack = new Error().stack ?? "";
46
+ const isInkWrite =
47
+ stack.includes("ink") ||
48
+ stack.includes("render") ||
49
+ stack.includes("reconciler") ||
50
+ stack.includes("yoga");
51
+
52
+ if (!isInkWrite) {
53
+ const chunkStr = typeof chunk === "string" ? chunk : chunk.toString();
54
+ const preview = chunkStr.substring(0, 80).replace(/\n/g, "\\n");
55
+ // Log to stderr to avoid recursion
56
+ process.stderr.write(`[STDOUT GUARD] Non-Ink write detected: "${preview}"\n`);
57
+ }
58
+
59
+ // ALWAYS allow the write through - never block
60
+ if (originalStdoutWrite) {
61
+ if (typeof encodingOrCallback === "function") {
62
+ return originalStdoutWrite(chunk, encodingOrCallback);
63
+ }
64
+ return originalStdoutWrite(chunk, encodingOrCallback, callback);
65
+ }
66
+ return true;
67
+ };
68
+
69
+ process.stdout.write = guardedWrite as typeof process.stdout.write;
70
+ }
71
+
72
+ /**
73
+ * Disable the stdout guard and restore original stdout.write.
74
+ */
75
+ export function disableStdoutGuard(): void {
76
+ if (!guardActive || !originalStdoutWrite) {
77
+ return;
78
+ }
79
+
80
+ process.stdout.write = originalStdoutWrite;
81
+ originalStdoutWrite = null;
82
+ guardActive = false;
83
+ }
84
+
85
+ /**
86
+ * Check if the stdout guard is currently active.
87
+ */
88
+ export function isStdoutGuardActive(): boolean {
89
+ return guardActive;
90
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Synchronized Update - DEC 2026 protocol
3
+ *
4
+ * Prevents terminal flickering by batching output.
5
+ * Supported in VS Code Terminal 1.85+, iTerm2, Kitty
6
+ *
7
+ * @module tui/utils/synchronized-update
8
+ */
9
+
10
+ import { getActiveStdout } from "../buffered-stdout.js";
11
+
12
+ const BEGIN_SYNC = "\x1b[?2026h";
13
+ const END_SYNC = "\x1b[?2026l";
14
+
15
+ let syncDepth = 0;
16
+
17
+ /**
18
+ * Execute a function with synchronized terminal updates.
19
+ * All output within the callback will be batched and rendered atomically.
20
+ *
21
+ * @param fn - The function to execute within synchronized updates
22
+ * @returns The return value of the function
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * syncUpdate(() => {
27
+ * process.stdout.write('Line 1\n');
28
+ * process.stdout.write('Line 2\n');
29
+ * });
30
+ * // Both lines rendered atomically without flicker
31
+ * ```
32
+ */
33
+ export function syncUpdate<T>(fn: () => T): T {
34
+ if (syncDepth === 0) {
35
+ getActiveStdout().write(BEGIN_SYNC);
36
+ }
37
+ syncDepth++;
38
+ try {
39
+ return fn();
40
+ } finally {
41
+ syncDepth--;
42
+ if (syncDepth === 0) {
43
+ getActiveStdout().write(END_SYNC);
44
+ }
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Check if synchronized updates are supported by the current terminal.
50
+ *
51
+ * @returns true if DEC 2026 protocol is supported
52
+ */
53
+ export function isSyncUpdateSupported(): boolean {
54
+ // VS Code 1.85+, iTerm2, Kitty support DEC 2026
55
+ const term = process.env.TERM_PROGRAM?.toLowerCase() ?? "";
56
+ const termVersion = process.env.TERM_PROGRAM_VERSION ?? "";
57
+
58
+ if (term === "vscode") {
59
+ // VS Code 1.85.0 added sync update support
60
+ const [major = 0] = termVersion.split(".").map(Number);
61
+ return major >= 1; // Most recent VS Code versions support it
62
+ }
63
+
64
+ if (term === "iterm.app" || term === "kitty") {
65
+ return true;
66
+ }
67
+
68
+ // Check for explicit support via COLORTERM
69
+ return process.env.COLORTERM === "truecolor";
70
+ }