@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,456 @@
1
+ /**
2
+ * Permission Checker
3
+ *
4
+ * Security module for validating command permissions against defined policies.
5
+ * Provides file access, network access, and general resource permission checks.
6
+ *
7
+ * @module cli/commands/security/permission-checker
8
+ */
9
+
10
+ import path from "node:path";
11
+
12
+ // =============================================================================
13
+ // T052: Types
14
+ // =============================================================================
15
+
16
+ /**
17
+ * Security policy for command execution
18
+ *
19
+ * Defines what resources a command is allowed to access.
20
+ * All fields are optional - if not specified, the resource type is unrestricted.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const policy: CommandSecurityPolicy = {
25
+ * allowedPaths: ['./src/**', './config/**'],
26
+ * deniedPaths: ['**\/.env', '**\/secrets/**'],
27
+ * allowedHosts: ['api.example.com', 'localhost'],
28
+ * deniedHosts: ['*.evil.com'],
29
+ * requiresAuth: true,
30
+ * maxExecutionTime: 30000,
31
+ * };
32
+ * ```
33
+ */
34
+ export interface CommandSecurityPolicy {
35
+ /** Allowed file paths (glob patterns or absolute paths) */
36
+ readonly allowedPaths?: readonly string[];
37
+ /** Blocked file paths (glob patterns or absolute paths) - takes precedence over allowedPaths */
38
+ readonly deniedPaths?: readonly string[];
39
+ /** Allowed network hosts (domain names or IP addresses, supports wildcards) */
40
+ readonly allowedHosts?: readonly string[];
41
+ /** Blocked network hosts - takes precedence over allowedHosts */
42
+ readonly deniedHosts?: readonly string[];
43
+ /** Whether the command requires an authenticated session */
44
+ readonly requiresAuth?: boolean;
45
+ /** Maximum execution time in milliseconds */
46
+ readonly maxExecutionTime?: number;
47
+ }
48
+
49
+ /**
50
+ * Result of a permission check
51
+ *
52
+ * Discriminated union that indicates whether access is allowed or denied.
53
+ * When denied, includes the reason and optional suggestion for resolution.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const result = checker.checkFileAccess('/etc/passwd', policy);
58
+ * if (!result.allowed) {
59
+ * console.error(`Denied: ${result.reason}`);
60
+ * if (result.suggestion) {
61
+ * console.log(`Suggestion: ${result.suggestion}`);
62
+ * }
63
+ * }
64
+ * ```
65
+ */
66
+ export type PermissionResult =
67
+ | { readonly allowed: true }
68
+ | { readonly allowed: false; readonly reason: string; readonly suggestion?: string };
69
+
70
+ // =============================================================================
71
+ // T052: PermissionChecker Class
72
+ // =============================================================================
73
+
74
+ /**
75
+ * PermissionChecker - Validates command permissions against security policies
76
+ *
77
+ * Provides methods to check if a command is allowed to access specific resources
78
+ * based on the defined security policy.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * const checker = new PermissionChecker();
83
+ *
84
+ * const policy: CommandSecurityPolicy = {
85
+ * allowedPaths: ['./src/**'],
86
+ * deniedPaths: ['**\/.env'],
87
+ * };
88
+ *
89
+ * const result = checker.checkFileAccess('./src/app.ts', policy);
90
+ * if (result.allowed) {
91
+ * // Proceed with file access
92
+ * } else {
93
+ * console.error(result.reason);
94
+ * }
95
+ * ```
96
+ */
97
+ export class PermissionChecker {
98
+ /**
99
+ * The base directory for resolving relative paths
100
+ */
101
+ private readonly baseDir: string;
102
+
103
+ /**
104
+ * Create a new PermissionChecker
105
+ *
106
+ * @param baseDir - Base directory for resolving relative paths (defaults to cwd)
107
+ */
108
+ constructor(baseDir?: string) {
109
+ this.baseDir = baseDir ?? process.cwd();
110
+ }
111
+
112
+ /**
113
+ * Check if file access is allowed by the policy
114
+ *
115
+ * Validates a file path against the allowed and denied path patterns
116
+ * in the security policy.
117
+ *
118
+ * @param filePath - The file path to check (absolute or relative)
119
+ * @param policy - The security policy to check against
120
+ * @returns PermissionResult indicating if access is allowed
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * const result = checker.checkFileAccess('./config.json', {
125
+ * allowedPaths: ['./config/**'],
126
+ * deniedPaths: ['./config/secrets/**'],
127
+ * });
128
+ * ```
129
+ */
130
+ checkFileAccess(filePath: string, policy: CommandSecurityPolicy): PermissionResult {
131
+ if (!filePath) {
132
+ return {
133
+ allowed: false,
134
+ reason: "File path is required",
135
+ suggestion: "Provide a valid file path",
136
+ };
137
+ }
138
+
139
+ // Normalize the path for consistent matching
140
+ const normalizedPath = this.normalizePath(filePath);
141
+
142
+ // Check denied paths first (they take precedence)
143
+ if (policy.deniedPaths && policy.deniedPaths.length > 0) {
144
+ for (const pattern of policy.deniedPaths) {
145
+ if (this.matchesPattern(normalizedPath, pattern)) {
146
+ return {
147
+ allowed: false,
148
+ reason: `Path '${filePath}' is blocked by security policy`,
149
+ suggestion: "Check the command's deniedPaths configuration",
150
+ };
151
+ }
152
+ }
153
+ }
154
+
155
+ // If allowedPaths is defined, path must match at least one pattern
156
+ if (policy.allowedPaths && policy.allowedPaths.length > 0) {
157
+ const isAllowed = policy.allowedPaths.some((pattern) =>
158
+ this.matchesPattern(normalizedPath, pattern)
159
+ );
160
+
161
+ if (!isAllowed) {
162
+ return {
163
+ allowed: false,
164
+ reason: `Path '${filePath}' is not in allowed paths`,
165
+ suggestion: `Allowed paths: ${policy.allowedPaths.join(", ")}`,
166
+ };
167
+ }
168
+ }
169
+
170
+ return { allowed: true };
171
+ }
172
+
173
+ /**
174
+ * Check if network access is allowed by the policy
175
+ *
176
+ * Validates a host against the allowed and denied host patterns
177
+ * in the security policy.
178
+ *
179
+ * @param host - The host to check (domain name or IP address)
180
+ * @param policy - The security policy to check against
181
+ * @returns PermissionResult indicating if access is allowed
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * const result = checker.checkNetworkAccess('api.example.com', {
186
+ * allowedHosts: ['*.example.com', 'localhost'],
187
+ * deniedHosts: ['internal.example.com'],
188
+ * });
189
+ * ```
190
+ */
191
+ checkNetworkAccess(host: string, policy: CommandSecurityPolicy): PermissionResult {
192
+ if (!host) {
193
+ return {
194
+ allowed: false,
195
+ reason: "Host is required",
196
+ suggestion: "Provide a valid host name or IP address",
197
+ };
198
+ }
199
+
200
+ // Normalize the host (lowercase, trim)
201
+ const normalizedHost = host.toLowerCase().trim();
202
+
203
+ // Check denied hosts first (they take precedence)
204
+ if (policy.deniedHosts && policy.deniedHosts.length > 0) {
205
+ for (const pattern of policy.deniedHosts) {
206
+ if (this.matchesHostPattern(normalizedHost, pattern)) {
207
+ return {
208
+ allowed: false,
209
+ reason: `Host '${host}' is blocked by security policy`,
210
+ suggestion: "Check the command's deniedHosts configuration",
211
+ };
212
+ }
213
+ }
214
+ }
215
+
216
+ // If allowedHosts is defined, host must match at least one pattern
217
+ if (policy.allowedHosts && policy.allowedHosts.length > 0) {
218
+ const isAllowed = policy.allowedHosts.some((pattern) =>
219
+ this.matchesHostPattern(normalizedHost, pattern)
220
+ );
221
+
222
+ if (!isAllowed) {
223
+ return {
224
+ allowed: false,
225
+ reason: `Host '${host}' is not in allowed hosts`,
226
+ suggestion: `Allowed hosts: ${policy.allowedHosts.join(", ")}`,
227
+ };
228
+ }
229
+ }
230
+
231
+ return { allowed: true };
232
+ }
233
+
234
+ /**
235
+ * Check if a general action on a resource is allowed
236
+ *
237
+ * Generic permission check for custom resource types not covered
238
+ * by file or network access.
239
+ *
240
+ * @param action - The action being performed (e.g., 'read', 'write', 'execute')
241
+ * @param resource - The resource identifier
242
+ * @param policy - The security policy to check against
243
+ * @returns PermissionResult indicating if the action is allowed
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * const result = checker.checkPolicy('execute', 'shell:rm', {
248
+ * requiresAuth: true,
249
+ * });
250
+ * ```
251
+ */
252
+ checkPolicy(action: string, resource: string, policy: CommandSecurityPolicy): PermissionResult {
253
+ if (!action) {
254
+ return {
255
+ allowed: false,
256
+ reason: "Action is required",
257
+ suggestion: "Specify the action being performed",
258
+ };
259
+ }
260
+
261
+ if (!resource) {
262
+ return {
263
+ allowed: false,
264
+ reason: "Resource is required",
265
+ suggestion: "Specify the resource being accessed",
266
+ };
267
+ }
268
+
269
+ // Handle file-like resources
270
+ if (resource.startsWith("file:")) {
271
+ const filePath = resource.slice(5); // Remove 'file:' prefix
272
+ return this.checkFileAccess(filePath, policy);
273
+ }
274
+
275
+ // Handle network-like resources
276
+ if (resource.startsWith("http://") || resource.startsWith("https://")) {
277
+ try {
278
+ const url = new URL(resource);
279
+ return this.checkNetworkAccess(url.host, policy);
280
+ } catch {
281
+ return {
282
+ allowed: false,
283
+ reason: `Invalid URL: ${resource}`,
284
+ suggestion: "Provide a valid URL",
285
+ };
286
+ }
287
+ }
288
+
289
+ // Handle host-like resources
290
+ if (resource.startsWith("host:")) {
291
+ const host = resource.slice(5); // Remove 'host:' prefix
292
+ return this.checkNetworkAccess(host, policy);
293
+ }
294
+
295
+ // For other resources, allow by default (specific checks should be added as needed)
296
+ return { allowed: true };
297
+ }
298
+
299
+ /**
300
+ * Normalize a file path for consistent matching
301
+ *
302
+ * @param filePath - The path to normalize
303
+ * @returns Normalized path using forward slashes
304
+ */
305
+ private normalizePath(filePath: string): string {
306
+ // Resolve relative paths against base directory
307
+ const absolutePath = path.isAbsolute(filePath)
308
+ ? filePath
309
+ : path.resolve(this.baseDir, filePath);
310
+
311
+ // Normalize path separators to forward slashes for glob matching
312
+ return absolutePath.replace(/\\/g, "/");
313
+ }
314
+
315
+ /**
316
+ * Check if a path matches a glob pattern
317
+ *
318
+ * @param filePath - The normalized file path
319
+ * @param pattern - The glob pattern to match against
320
+ * @returns true if the path matches the pattern
321
+ */
322
+ private matchesPattern(filePath: string, pattern: string): boolean {
323
+ // Normalize the pattern path separators
324
+ const normalizedPattern = pattern.replace(/\\/g, "/");
325
+
326
+ // Handle patterns that start with ** (match from any point in the path)
327
+ if (normalizedPattern.startsWith("**/")) {
328
+ // For **/ patterns, we want to match from any point, not just the start
329
+ return this.globMatch(filePath, normalizedPattern, process.platform === "win32");
330
+ }
331
+
332
+ // If pattern is not absolute and doesn't start with **, resolve it against base directory
333
+ const resolvedPattern = path.isAbsolute(normalizedPattern)
334
+ ? normalizedPattern
335
+ : path.resolve(this.baseDir, normalizedPattern).replace(/\\/g, "/");
336
+
337
+ // Use simple glob matching implementation
338
+ return this.globMatch(filePath, resolvedPattern, process.platform === "win32");
339
+ }
340
+
341
+ /**
342
+ * Simple glob pattern matching
343
+ *
344
+ * Supports:
345
+ * - ** for matching any number of directories
346
+ * - * for matching any characters within a path segment
347
+ * - ? for matching a single character
348
+ *
349
+ * @param filePath - The file path to test
350
+ * @param pattern - The glob pattern
351
+ * @param ignoreCase - Whether to ignore case (for Windows)
352
+ * @returns true if the path matches the pattern
353
+ */
354
+ private globMatch(filePath: string, pattern: string, ignoreCase: boolean): boolean {
355
+ // Normalize case if needed
356
+ const normalizedPath = ignoreCase ? filePath.toLowerCase() : filePath;
357
+ const normalizedGlob = ignoreCase ? pattern.toLowerCase() : pattern;
358
+
359
+ // Handle ** at the beginning specially - it should match from any point
360
+ if (normalizedGlob.startsWith("**/")) {
361
+ const suffix = normalizedGlob.slice(3); // Remove **/
362
+ // Try matching from any position in the path
363
+ const segments = normalizedPath.split("/");
364
+ for (let i = 0; i < segments.length; i++) {
365
+ const subPath = segments.slice(i).join("/");
366
+ if (this.simpleGlobMatch(subPath, suffix, ignoreCase)) {
367
+ return true;
368
+ }
369
+ }
370
+ return false;
371
+ }
372
+
373
+ // Handle /** at the end - it should match the path and anything under it
374
+ if (normalizedGlob.endsWith("/**")) {
375
+ const prefix = normalizedGlob.slice(0, -3); // Remove /**
376
+ // The path should start with the prefix (or equal it)
377
+ if (normalizedPath === prefix || normalizedPath.startsWith(`${prefix}/`)) {
378
+ return true;
379
+ }
380
+ // Also try the regular glob match
381
+ return this.simpleGlobMatch(normalizedPath, normalizedGlob, ignoreCase);
382
+ }
383
+
384
+ return this.simpleGlobMatch(normalizedPath, normalizedGlob, ignoreCase);
385
+ }
386
+
387
+ /**
388
+ * Simple glob to regex matching (non-recursive)
389
+ *
390
+ * @param filePath - The file path to test
391
+ * @param pattern - The glob pattern
392
+ * @param ignoreCase - Whether to ignore case
393
+ * @returns true if the path matches the pattern
394
+ */
395
+ private simpleGlobMatch(filePath: string, pattern: string, ignoreCase: boolean): boolean {
396
+ const normalizedPath = ignoreCase ? filePath.toLowerCase() : filePath;
397
+ const normalizedGlob = ignoreCase ? pattern.toLowerCase() : pattern;
398
+
399
+ // Convert glob pattern to regex
400
+ const regexPattern = normalizedGlob
401
+ // Escape regex special chars except * and ?
402
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
403
+ // ** matches any path (including slashes)
404
+ .replace(/\*\*/g, "\0GLOBSTAR\0")
405
+ // * matches anything except slash
406
+ .replace(/\*/g, "[^/]*")
407
+ // ? matches single char except slash
408
+ .replace(/\?/g, "[^/]")
409
+ // Restore globstar
410
+ .replace(/\0GLOBSTAR\0/g, ".*");
411
+
412
+ const regex = new RegExp(`^${regexPattern}$`);
413
+ return regex.test(normalizedPath);
414
+ }
415
+
416
+ /**
417
+ * Check if a host matches a wildcard pattern
418
+ *
419
+ * Supports:
420
+ * - Exact match: 'example.com'
421
+ * - Wildcard prefix: '*.example.com'
422
+ * - Full wildcard: '*'
423
+ *
424
+ * @param host - The normalized host
425
+ * @param pattern - The pattern to match against
426
+ * @returns true if the host matches the pattern
427
+ */
428
+ private matchesHostPattern(host: string, pattern: string): boolean {
429
+ const normalizedPattern = pattern.toLowerCase().trim();
430
+
431
+ // Full wildcard matches everything
432
+ if (normalizedPattern === "*") {
433
+ return true;
434
+ }
435
+
436
+ // Wildcard prefix pattern (*.example.com)
437
+ if (normalizedPattern.startsWith("*.")) {
438
+ const suffix = normalizedPattern.slice(2); // Remove '*.'
439
+ // Match the exact suffix or any subdomain
440
+ return host === suffix || host.endsWith(`.${suffix}`);
441
+ }
442
+
443
+ // Exact match
444
+ return host === normalizedPattern;
445
+ }
446
+ }
447
+
448
+ /**
449
+ * Create a PermissionChecker with default configuration
450
+ *
451
+ * @param baseDir - Optional base directory for path resolution
452
+ * @returns Configured PermissionChecker instance
453
+ */
454
+ export function createPermissionChecker(baseDir?: string): PermissionChecker {
455
+ return new PermissionChecker(baseDir);
456
+ }