@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.
- package/LICENSE +21 -0
- package/README.md +411 -0
- package/__fixtures__/responses/code-generation.json +42 -0
- package/__fixtures__/responses/error-response.json +20 -0
- package/__fixtures__/responses/hello-world.json +32 -0
- package/dist/auth-6MCXESOH.js +26 -0
- package/dist/chunk-SECXJGWA.js +597 -0
- package/dist/index.js +34023 -0
- package/package.json +67 -0
- package/src/__tests__/commands.e2e.test.ts +728 -0
- package/src/__tests__/credentials.test.ts +713 -0
- package/src/__tests__/mode-e2e.test.ts +391 -0
- package/src/__tests__/tui-integration.test.tsx +1271 -0
- package/src/agents/__tests__/task-persistence.test.ts +235 -0
- package/src/agents/commands/delegate.ts +240 -0
- package/src/agents/commands/index.ts +10 -0
- package/src/agents/commands/resume.ts +335 -0
- package/src/agents/index.ts +29 -0
- package/src/agents/task-persistence.ts +272 -0
- package/src/agents/task-resumption.ts +242 -0
- package/src/app.tsx +4737 -0
- package/src/commands/__tests__/.gitkeep +1 -0
- package/src/commands/__tests__/agents.test.ts +606 -0
- package/src/commands/__tests__/auth.test.ts +626 -0
- package/src/commands/__tests__/autocomplete.test.ts +683 -0
- package/src/commands/__tests__/batch.test.ts +287 -0
- package/src/commands/__tests__/chain-pipe-parser.test.ts +654 -0
- package/src/commands/__tests__/completion.test.ts +238 -0
- package/src/commands/__tests__/core.test.ts +363 -0
- package/src/commands/__tests__/executor.test.ts +496 -0
- package/src/commands/__tests__/exit-codes.test.ts +220 -0
- package/src/commands/__tests__/init.test.ts +243 -0
- package/src/commands/__tests__/language.test.ts +353 -0
- package/src/commands/__tests__/mode-cli.test.ts +667 -0
- package/src/commands/__tests__/model.test.ts +277 -0
- package/src/commands/__tests__/parser.test.ts +493 -0
- package/src/commands/__tests__/performance.bench.ts +380 -0
- package/src/commands/__tests__/registry.test.ts +534 -0
- package/src/commands/__tests__/resume.test.ts +449 -0
- package/src/commands/__tests__/security.test.ts +845 -0
- package/src/commands/__tests__/stream-json.test.ts +372 -0
- package/src/commands/__tests__/user-commands.test.ts +597 -0
- package/src/commands/adapters.ts +267 -0
- package/src/commands/agent.ts +395 -0
- package/src/commands/agents/generate.ts +506 -0
- package/src/commands/agents/index.ts +272 -0
- package/src/commands/agents/show.ts +271 -0
- package/src/commands/agents/validate.ts +387 -0
- package/src/commands/auth.ts +883 -0
- package/src/commands/autocomplete.ts +480 -0
- package/src/commands/batch/command.ts +388 -0
- package/src/commands/batch/executor.ts +361 -0
- package/src/commands/batch/index.ts +12 -0
- package/src/commands/commit.ts +235 -0
- package/src/commands/completion/index.ts +371 -0
- package/src/commands/condense.ts +191 -0
- package/src/commands/config.ts +344 -0
- package/src/commands/context-provider.ts +173 -0
- package/src/commands/copy.ts +329 -0
- package/src/commands/core/clear.ts +38 -0
- package/src/commands/core/exit.ts +43 -0
- package/src/commands/core/help.ts +354 -0
- package/src/commands/core/index.ts +15 -0
- package/src/commands/cost.ts +179 -0
- package/src/commands/credentials.tsx +618 -0
- package/src/commands/custom-agents/__tests__/custom-agents.test.ts +709 -0
- package/src/commands/custom-agents/create.ts +377 -0
- package/src/commands/custom-agents/export.ts +135 -0
- package/src/commands/custom-agents/import.ts +199 -0
- package/src/commands/custom-agents/index.ts +372 -0
- package/src/commands/custom-agents/info.ts +318 -0
- package/src/commands/custom-agents/list.ts +267 -0
- package/src/commands/custom-agents/validate.ts +388 -0
- package/src/commands/diff-mode.ts +241 -0
- package/src/commands/env.ts +53 -0
- package/src/commands/executor.ts +579 -0
- package/src/commands/exit-codes.ts +202 -0
- package/src/commands/index.ts +701 -0
- package/src/commands/init/index.ts +15 -0
- package/src/commands/init/prompts.ts +366 -0
- package/src/commands/init/templates/commands-readme.md +80 -0
- package/src/commands/init/templates/example-command.md +79 -0
- package/src/commands/init/templates/example-skill.md +168 -0
- package/src/commands/init/templates/example-workflow.md +101 -0
- package/src/commands/init/templates/prompts-readme.md +52 -0
- package/src/commands/init/templates/rules-readme.md +63 -0
- package/src/commands/init/templates/skills-readme.md +83 -0
- package/src/commands/init/templates/workflows-readme.md +94 -0
- package/src/commands/init.ts +391 -0
- package/src/commands/install.ts +90 -0
- package/src/commands/language.ts +191 -0
- package/src/commands/loaders/.gitkeep +1 -0
- package/src/commands/lsp.ts +199 -0
- package/src/commands/markdown-commands.ts +253 -0
- package/src/commands/mcp.ts +588 -0
- package/src/commands/memory/export.ts +341 -0
- package/src/commands/memory/index.ts +148 -0
- package/src/commands/memory/list.ts +261 -0
- package/src/commands/memory/search.ts +346 -0
- package/src/commands/memory/utils.ts +15 -0
- package/src/commands/metrics.ts +75 -0
- package/src/commands/migrate/index.ts +16 -0
- package/src/commands/migrate/prompts.ts +477 -0
- package/src/commands/mode.ts +331 -0
- package/src/commands/model.ts +298 -0
- package/src/commands/onboard.ts +205 -0
- package/src/commands/open.ts +169 -0
- package/src/commands/output/stream-json.ts +373 -0
- package/src/commands/parser/chain-parser.ts +370 -0
- package/src/commands/parser/index.ts +29 -0
- package/src/commands/parser/pipe-parser.ts +480 -0
- package/src/commands/parser.ts +588 -0
- package/src/commands/persistence.ts +355 -0
- package/src/commands/progress.ts +18 -0
- package/src/commands/prompt/index.ts +17 -0
- package/src/commands/prompt/validate.ts +621 -0
- package/src/commands/prompt-priority.ts +401 -0
- package/src/commands/registry.ts +374 -0
- package/src/commands/sandbox/index.ts +131 -0
- package/src/commands/security/index.ts +21 -0
- package/src/commands/security/input-sanitizer.ts +168 -0
- package/src/commands/security/permission-checker.ts +456 -0
- package/src/commands/security/sensitive-data.ts +350 -0
- package/src/commands/session/delete.ts +38 -0
- package/src/commands/session/export.ts +39 -0
- package/src/commands/session/index.ts +26 -0
- package/src/commands/session/list.ts +26 -0
- package/src/commands/session/resume.ts +562 -0
- package/src/commands/session/search.ts +434 -0
- package/src/commands/session/show.ts +26 -0
- package/src/commands/settings.ts +368 -0
- package/src/commands/setup.ts +23 -0
- package/src/commands/shell/index.ts +16 -0
- package/src/commands/shell/setup.ts +422 -0
- package/src/commands/shell-init.ts +50 -0
- package/src/commands/shell-integration/index.ts +194 -0
- package/src/commands/skill.ts +1220 -0
- package/src/commands/spec.ts +558 -0
- package/src/commands/status.ts +246 -0
- package/src/commands/theme.ts +211 -0
- package/src/commands/think.ts +551 -0
- package/src/commands/trust.ts +211 -0
- package/src/commands/tutorial.ts +522 -0
- package/src/commands/types.ts +512 -0
- package/src/commands/update.ts +274 -0
- package/src/commands/usage.ts +213 -0
- package/src/commands/user-commands.ts +630 -0
- package/src/commands/utils.ts +142 -0
- package/src/commands/vim.ts +152 -0
- package/src/commands/workflow.ts +257 -0
- package/src/components/header.tsx +25 -0
- package/src/components/input.tsx +25 -0
- package/src/components/message-list.tsx +32 -0
- package/src/components/status-bar.tsx +23 -0
- package/src/index.tsx +614 -0
- package/src/onboarding/__tests__/tutorial.test.ts +740 -0
- package/src/onboarding/index.ts +69 -0
- package/src/onboarding/tips/index.ts +9 -0
- package/src/onboarding/tips/tip-engine.ts +459 -0
- package/src/onboarding/tutorial/index.ts +88 -0
- package/src/onboarding/tutorial/lessons/basics.ts +151 -0
- package/src/onboarding/tutorial/lessons/index.ts +151 -0
- package/src/onboarding/tutorial/lessons/modes.ts +230 -0
- package/src/onboarding/tutorial/lessons/tools.ts +172 -0
- package/src/onboarding/tutorial/progress-tracker.ts +350 -0
- package/src/onboarding/tutorial/storage.ts +249 -0
- package/src/onboarding/tutorial/tutorial-system.ts +462 -0
- package/src/onboarding/tutorial/types.ts +310 -0
- package/src/orchestrator-singleton.ts +129 -0
- package/src/shutdown.ts +33 -0
- package/src/test/e2e/assertions.ts +267 -0
- package/src/test/e2e/fixtures.ts +204 -0
- package/src/test/e2e/harness.ts +575 -0
- package/src/test/e2e/index.ts +57 -0
- package/src/test/e2e/types.ts +228 -0
- package/src/test/fixtures/__tests__/fake-response-loader.test.ts +314 -0
- package/src/test/fixtures/fake-response-loader.ts +314 -0
- package/src/test/fixtures/index.ts +20 -0
- package/src/tui/__tests__/mcp-panel.test.tsx +82 -0
- package/src/tui/__tests__/mcp-wiring.test.tsx +78 -0
- package/src/tui/__tests__/mode-components.test.tsx +395 -0
- package/src/tui/__tests__/permission-ask-flow.test.tsx +138 -0
- package/src/tui/__tests__/sidebar-panel-data.test.tsx +148 -0
- package/src/tui/__tests__/tools-panel-hotkeys.test.tsx +41 -0
- package/src/tui/adapters/agent-adapter.ts +1008 -0
- package/src/tui/adapters/index.ts +48 -0
- package/src/tui/adapters/message-adapter.ts +315 -0
- package/src/tui/adapters/persistence-bridge.ts +331 -0
- package/src/tui/adapters/session-adapter.ts +419 -0
- package/src/tui/buffered-stdout.ts +223 -0
- package/src/tui/components/AgentProgress.tsx +424 -0
- package/src/tui/components/Banner/AsciiArt.ts +160 -0
- package/src/tui/components/Banner/Banner.tsx +355 -0
- package/src/tui/components/Banner/ShimmerContext.tsx +131 -0
- package/src/tui/components/Banner/ShimmerText.tsx +193 -0
- package/src/tui/components/Banner/TypeWriterGradient.tsx +321 -0
- package/src/tui/components/Banner/index.ts +61 -0
- package/src/tui/components/Banner/useShimmer.ts +241 -0
- package/src/tui/components/ChatView.tsx +11 -0
- package/src/tui/components/Checkpoint/CheckpointDiffView.tsx +371 -0
- package/src/tui/components/Checkpoint/SnapshotCheckpointPanel.tsx +440 -0
- package/src/tui/components/Checkpoint/index.ts +19 -0
- package/src/tui/components/CostDisplay.tsx +226 -0
- package/src/tui/components/InitErrorBanner.tsx +122 -0
- package/src/tui/components/Input/Autocomplete.tsx +603 -0
- package/src/tui/components/Input/EnhancedCommandInput.tsx +471 -0
- package/src/tui/components/Input/HighlightedText.tsx +236 -0
- package/src/tui/components/Input/MentionAutocomplete.tsx +375 -0
- package/src/tui/components/Input/TextInput.tsx +1002 -0
- package/src/tui/components/Input/__tests__/Autocomplete.test.tsx +374 -0
- package/src/tui/components/Input/__tests__/TextInput.test.tsx +241 -0
- package/src/tui/components/Input/__tests__/highlight.test.ts +219 -0
- package/src/tui/components/Input/__tests__/slash-command-utils.test.ts +104 -0
- package/src/tui/components/Input/highlight.ts +362 -0
- package/src/tui/components/Input/index.ts +36 -0
- package/src/tui/components/Input/slash-command-utils.ts +135 -0
- package/src/tui/components/Layout.tsx +432 -0
- package/src/tui/components/McpPanel.tsx +137 -0
- package/src/tui/components/MemoryPanel.tsx +448 -0
- package/src/tui/components/Messages/CodeBlock.tsx +527 -0
- package/src/tui/components/Messages/DiffView.tsx +679 -0
- package/src/tui/components/Messages/ImageReference.tsx +89 -0
- package/src/tui/components/Messages/MarkdownBlock.tsx +228 -0
- package/src/tui/components/Messages/MarkdownRenderer.tsx +498 -0
- package/src/tui/components/Messages/MessageBubble.tsx +270 -0
- package/src/tui/components/Messages/MessageList.tsx +1719 -0
- package/src/tui/components/Messages/StreamingText.tsx +216 -0
- package/src/tui/components/Messages/ThinkingBlock.tsx +408 -0
- package/src/tui/components/Messages/ToolResultPreview.tsx +243 -0
- package/src/tui/components/Messages/__tests__/CodeBlock.test.tsx +296 -0
- package/src/tui/components/Messages/__tests__/DiffView.test.tsx +239 -0
- package/src/tui/components/Messages/__tests__/MarkdownRenderer.test.tsx +303 -0
- package/src/tui/components/Messages/__tests__/MessageBubble.test.tsx +268 -0
- package/src/tui/components/Messages/__tests__/MessageList.test.tsx +324 -0
- package/src/tui/components/Messages/__tests__/StreamingText.test.tsx +215 -0
- package/src/tui/components/Messages/index.ts +25 -0
- package/src/tui/components/ModeIndicator.tsx +177 -0
- package/src/tui/components/ModeSelector.tsx +216 -0
- package/src/tui/components/ModelSelector.tsx +339 -0
- package/src/tui/components/OnboardingWizard.tsx +670 -0
- package/src/tui/components/PhaseProgressIndicator.tsx +270 -0
- package/src/tui/components/RateLimitIndicator.tsx +82 -0
- package/src/tui/components/ScreenReaderLayout.tsx +295 -0
- package/src/tui/components/SettingsPanel.tsx +643 -0
- package/src/tui/components/Sidebar/SystemStatusPanel.tsx +284 -0
- package/src/tui/components/Sidebar/index.ts +9 -0
- package/src/tui/components/Status/ModelStatusBar.tsx +270 -0
- package/src/tui/components/Status/index.ts +12 -0
- package/src/tui/components/StatusBar/AgentModeIndicator.tsx +257 -0
- package/src/tui/components/StatusBar/ContextProgress.tsx +167 -0
- package/src/tui/components/StatusBar/FileChangesIndicator.tsx +62 -0
- package/src/tui/components/StatusBar/GitIndicator.tsx +89 -0
- package/src/tui/components/StatusBar/HeaderBar.tsx +126 -0
- package/src/tui/components/StatusBar/ModelIndicator.tsx +157 -0
- package/src/tui/components/StatusBar/PersistenceStatusIndicator.tsx +210 -0
- package/src/tui/components/StatusBar/ResilienceIndicator.tsx +106 -0
- package/src/tui/components/StatusBar/SandboxIndicator.tsx +167 -0
- package/src/tui/components/StatusBar/StatusBar.tsx +368 -0
- package/src/tui/components/StatusBar/ThinkingModeIndicator.tsx +170 -0
- package/src/tui/components/StatusBar/TokenBreakdown.tsx +246 -0
- package/src/tui/components/StatusBar/TokenCounter.tsx +135 -0
- package/src/tui/components/StatusBar/TrustModeIndicator.tsx +130 -0
- package/src/tui/components/StatusBar/WorkspaceIndicator.tsx +86 -0
- package/src/tui/components/StatusBar/__tests__/AgentModeIndicator.test.tsx +193 -0
- package/src/tui/components/StatusBar/__tests__/StatusBar.test.tsx +729 -0
- package/src/tui/components/StatusBar/index.ts +60 -0
- package/src/tui/components/TipBanner.tsx +115 -0
- package/src/tui/components/TodoItem.tsx +208 -0
- package/src/tui/components/TodoPanel.tsx +455 -0
- package/src/tui/components/Tools/ApprovalQueue.tsx +407 -0
- package/src/tui/components/Tools/OptionSelector.tsx +160 -0
- package/src/tui/components/Tools/PermissionDialog.tsx +286 -0
- package/src/tui/components/Tools/ToolParams.tsx +483 -0
- package/src/tui/components/Tools/ToolsPanel.tsx +178 -0
- package/src/tui/components/Tools/__tests__/PermissionDialog.test.tsx +510 -0
- package/src/tui/components/Tools/__tests__/ToolParams.test.tsx +432 -0
- package/src/tui/components/Tools/index.ts +21 -0
- package/src/tui/components/TrustPrompt.tsx +279 -0
- package/src/tui/components/UpdateBanner.tsx +166 -0
- package/src/tui/components/VimModeIndicator.tsx +112 -0
- package/src/tui/components/backtrack/BacktrackControls.tsx +402 -0
- package/src/tui/components/backtrack/index.ts +13 -0
- package/src/tui/components/common/AutoApprovalStatus.tsx +251 -0
- package/src/tui/components/common/CostWarning.tsx +294 -0
- package/src/tui/components/common/DynamicShortcutHints.tsx +209 -0
- package/src/tui/components/common/EnhancedLoadingIndicator.tsx +305 -0
- package/src/tui/components/common/ErrorBoundary.tsx +140 -0
- package/src/tui/components/common/GradientText.tsx +224 -0
- package/src/tui/components/common/HotkeyHelpModal.tsx +193 -0
- package/src/tui/components/common/HotkeyHints.tsx +70 -0
- package/src/tui/components/common/MaxSizedBox.tsx +354 -0
- package/src/tui/components/common/NewMessagesBadge.tsx +65 -0
- package/src/tui/components/common/ProtectedFileLegend.tsx +89 -0
- package/src/tui/components/common/ScrollIndicator.tsx +160 -0
- package/src/tui/components/common/Spinner.tsx +342 -0
- package/src/tui/components/common/StreamingIndicator.tsx +316 -0
- package/src/tui/components/common/VirtualizedList/VirtualizedList.tsx +428 -0
- package/src/tui/components/common/VirtualizedList/hooks/index.ts +19 -0
- package/src/tui/components/common/VirtualizedList/hooks/useBatchedScroll.ts +64 -0
- package/src/tui/components/common/VirtualizedList/hooks/useScrollAnchor.ts +290 -0
- package/src/tui/components/common/VirtualizedList/hooks/useVirtualization.ts +340 -0
- package/src/tui/components/common/VirtualizedList/index.ts +30 -0
- package/src/tui/components/common/VirtualizedList/types.ts +107 -0
- package/src/tui/components/common/__tests__/NewMessagesBadge.test.tsx +74 -0
- package/src/tui/components/common/__tests__/ScrollIndicator.test.tsx +193 -0
- package/src/tui/components/common/index.ts +110 -0
- package/src/tui/components/index.ts +79 -0
- package/src/tui/components/session/CheckpointPanel.tsx +323 -0
- package/src/tui/components/session/RollbackDialog.tsx +169 -0
- package/src/tui/components/session/SessionItem.tsx +136 -0
- package/src/tui/components/session/SessionListPanel.tsx +252 -0
- package/src/tui/components/session/SessionPicker.tsx +449 -0
- package/src/tui/components/session/SessionPreview.tsx +240 -0
- package/src/tui/components/session/__tests__/session.test.tsx +408 -0
- package/src/tui/components/session/index.ts +28 -0
- package/src/tui/components/session/types.ts +116 -0
- package/src/tui/components/theme/__tests__/tokens.test.ts +471 -0
- package/src/tui/components/theme/index.ts +227 -0
- package/src/tui/components/theme/tokens.ts +484 -0
- package/src/tui/config/defaults.ts +134 -0
- package/src/tui/config/index.ts +17 -0
- package/src/tui/context/AnimationContext.tsx +284 -0
- package/src/tui/context/AppContext.tsx +349 -0
- package/src/tui/context/BracketedPasteContext.tsx +372 -0
- package/src/tui/context/LspContext.tsx +192 -0
- package/src/tui/context/McpContext.tsx +325 -0
- package/src/tui/context/MessagesContext.tsx +870 -0
- package/src/tui/context/OverflowContext.tsx +213 -0
- package/src/tui/context/RateLimitContext.tsx +108 -0
- package/src/tui/context/ResilienceContext.tsx +275 -0
- package/src/tui/context/RootProvider.tsx +136 -0
- package/src/tui/context/ScrollContext.tsx +331 -0
- package/src/tui/context/ToolsContext.tsx +702 -0
- package/src/tui/context/__tests__/BracketedPasteContext.test.tsx +416 -0
- package/src/tui/context/index.ts +140 -0
- package/src/tui/enterprise-integration.ts +282 -0
- package/src/tui/hooks/__tests__/useBacktrack.test.tsx +138 -0
- package/src/tui/hooks/__tests__/useBracketedPaste.test.tsx +222 -0
- package/src/tui/hooks/__tests__/useCopyMode.test.tsx +336 -0
- package/src/tui/hooks/__tests__/useHotkeys.ctrl-input.test.tsx +96 -0
- package/src/tui/hooks/__tests__/useHotkeys.test.tsx +454 -0
- package/src/tui/hooks/__tests__/useInputHistory.test.tsx +660 -0
- package/src/tui/hooks/__tests__/useLineBuffer.test.ts +295 -0
- package/src/tui/hooks/__tests__/useModeController.test.ts +137 -0
- package/src/tui/hooks/__tests__/useModeShortcuts.test.tsx +142 -0
- package/src/tui/hooks/__tests__/useScrollController.test.ts +464 -0
- package/src/tui/hooks/__tests__/useVim.test.tsx +531 -0
- package/src/tui/hooks/index.ts +252 -0
- package/src/tui/hooks/useAgentLoop.ts +712 -0
- package/src/tui/hooks/useAlternateBuffer.ts +398 -0
- package/src/tui/hooks/useAnimatedScrollbar.ts +241 -0
- package/src/tui/hooks/useBacktrack.ts +443 -0
- package/src/tui/hooks/useBracketedPaste.ts +104 -0
- package/src/tui/hooks/useCollapsible.ts +240 -0
- package/src/tui/hooks/useCopyMode.ts +382 -0
- package/src/tui/hooks/useCostSummary.ts +75 -0
- package/src/tui/hooks/useDesktopNotification.ts +414 -0
- package/src/tui/hooks/useDiffMode.ts +44 -0
- package/src/tui/hooks/useFileChangeStats.ts +110 -0
- package/src/tui/hooks/useFileSuggestions.ts +284 -0
- package/src/tui/hooks/useFlickerDetector.ts +250 -0
- package/src/tui/hooks/useGitStatus.ts +200 -0
- package/src/tui/hooks/useHotkeys.ts +579 -0
- package/src/tui/hooks/useImagePaste.ts +114 -0
- package/src/tui/hooks/useInputHighlight.ts +145 -0
- package/src/tui/hooks/useInputHistory.ts +246 -0
- package/src/tui/hooks/useKeyboardScroll.ts +209 -0
- package/src/tui/hooks/useLineBuffer.ts +356 -0
- package/src/tui/hooks/useMentionAutocomplete.ts +235 -0
- package/src/tui/hooks/useModeController.ts +167 -0
- package/src/tui/hooks/useModeShortcuts.ts +196 -0
- package/src/tui/hooks/usePermissionHandler.ts +146 -0
- package/src/tui/hooks/usePersistence.ts +480 -0
- package/src/tui/hooks/usePersistenceShortcuts.ts +225 -0
- package/src/tui/hooks/usePlaceholderRotation.ts +143 -0
- package/src/tui/hooks/useProviderStatus.ts +270 -0
- package/src/tui/hooks/useRateLimitStatus.ts +90 -0
- package/src/tui/hooks/useScreenReader.ts +315 -0
- package/src/tui/hooks/useScrollController.ts +450 -0
- package/src/tui/hooks/useScrollEventBatcher.ts +185 -0
- package/src/tui/hooks/useSidebarPanelData.ts +115 -0
- package/src/tui/hooks/useSmoothScroll.ts +202 -0
- package/src/tui/hooks/useSnapshots.ts +300 -0
- package/src/tui/hooks/useStateAndRef.ts +50 -0
- package/src/tui/hooks/useTerminalSize.ts +206 -0
- package/src/tui/hooks/useToolApprovalController.ts +91 -0
- package/src/tui/hooks/useVim.ts +334 -0
- package/src/tui/hooks/useWorkspace.ts +56 -0
- package/src/tui/i18n/__tests__/init.test.ts +278 -0
- package/src/tui/i18n/__tests__/language-config.test.ts +199 -0
- package/src/tui/i18n/__tests__/locale-detection.test.ts +250 -0
- package/src/tui/i18n/__tests__/settings-integration.test.ts +262 -0
- package/src/tui/i18n/index.ts +72 -0
- package/src/tui/i18n/init.ts +131 -0
- package/src/tui/i18n/language-config.ts +106 -0
- package/src/tui/i18n/locale-detection.ts +173 -0
- package/src/tui/i18n/settings-integration.ts +557 -0
- package/src/tui/i18n/tui-namespace.ts +538 -0
- package/src/tui/i18n/types.ts +312 -0
- package/src/tui/index.ts +43 -0
- package/src/tui/lsp-integration.ts +409 -0
- package/src/tui/metrics-integration.ts +366 -0
- package/src/tui/plugins.ts +383 -0
- package/src/tui/resilience.ts +342 -0
- package/src/tui/sandbox-integration.ts +317 -0
- package/src/tui/services/clipboard.ts +348 -0
- package/src/tui/services/fuzzy-search.ts +441 -0
- package/src/tui/services/index.ts +72 -0
- package/src/tui/services/markdown-renderer.ts +565 -0
- package/src/tui/services/open-external.ts +247 -0
- package/src/tui/services/syntax-highlighter.ts +483 -0
- package/src/tui/slash-commands.ts +12 -0
- package/src/tui/theme/index.ts +15 -0
- package/src/tui/theme/provider.tsx +206 -0
- package/src/tui/tip-integration.ts +300 -0
- package/src/tui/types/__tests__/ink-extended.test.ts +121 -0
- package/src/tui/types/ink-extended.ts +87 -0
- package/src/tui/utils/__tests__/bracketedPaste.test.ts +231 -0
- package/src/tui/utils/__tests__/heightEstimator.test.ts +157 -0
- package/src/tui/utils/__tests__/text-width.test.ts +158 -0
- package/src/tui/utils/__tests__/textSanitizer.test.ts +266 -0
- package/src/tui/utils/__tests__/ui-sizing.test.ts +169 -0
- package/src/tui/utils/bracketedPaste.ts +107 -0
- package/src/tui/utils/cursor-manager.ts +131 -0
- package/src/tui/utils/detectTerminal.ts +596 -0
- package/src/tui/utils/findLastSafeSplitPoint.ts +92 -0
- package/src/tui/utils/heightEstimator.ts +198 -0
- package/src/tui/utils/index.ts +91 -0
- package/src/tui/utils/isNarrowWidth.ts +52 -0
- package/src/tui/utils/stdoutGuard.ts +90 -0
- package/src/tui/utils/synchronized-update.ts +70 -0
- package/src/tui/utils/text-width.ts +225 -0
- package/src/tui/utils/textSanitizer.ts +225 -0
- package/src/tui/utils/textUtils.ts +114 -0
- package/src/tui/utils/ui-sizing.ts +192 -0
- package/src/tui-blessed/app.ts +160 -0
- package/src/tui-blessed/index.ts +2 -0
- package/src/tui-blessed/neo-blessed.d.ts +6 -0
- package/src/tui-blessed/test.ts +21 -0
- package/src/tui-blessed/types.ts +14 -0
- package/src/utils/icons.ts +130 -0
- package/src/utils/index.ts +33 -0
- package/src/utils/resume-hint.ts +86 -0
- package/src/version.ts +1 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +35 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BacktrackControls Component (T058)
|
|
3
|
+
*
|
|
4
|
+
* UI component for displaying and controlling conversation backtracking.
|
|
5
|
+
* Shows undo/redo status, current branch, and branch selector.
|
|
6
|
+
*
|
|
7
|
+
* @module tui/components/backtrack/BacktrackControls
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Box, Text, useInput } from "ink";
|
|
11
|
+
import type React from "react";
|
|
12
|
+
import { memo, useCallback, useState } from "react";
|
|
13
|
+
import type { BacktrackState, Branch } from "../../hooks/useBacktrack.js";
|
|
14
|
+
import { useTUITranslation } from "../../i18n/index.js";
|
|
15
|
+
import { useTheme } from "../../theme/index.js";
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// Types
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Props for the BacktrackControls component.
|
|
23
|
+
*/
|
|
24
|
+
export interface BacktrackControlsProps {
|
|
25
|
+
/** Current backtrack state */
|
|
26
|
+
readonly backtrackState: BacktrackState;
|
|
27
|
+
/** Available branches */
|
|
28
|
+
readonly branches: ReadonlyArray<Branch<unknown>>;
|
|
29
|
+
/** Handler for undo action */
|
|
30
|
+
readonly onUndo: () => void;
|
|
31
|
+
/** Handler for redo action */
|
|
32
|
+
readonly onRedo: () => void;
|
|
33
|
+
/** Handler for creating a new branch */
|
|
34
|
+
readonly onCreateBranch: () => void;
|
|
35
|
+
/** Handler for switching branches */
|
|
36
|
+
readonly onSwitchBranch: (branchId: string) => void;
|
|
37
|
+
/** Whether the component is focused for input */
|
|
38
|
+
readonly isFocused?: boolean;
|
|
39
|
+
/** Whether to show in compact mode */
|
|
40
|
+
readonly compact?: boolean;
|
|
41
|
+
/** Whether to show keyboard hints */
|
|
42
|
+
readonly showHints?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Props for the BranchSelector sub-component.
|
|
47
|
+
*/
|
|
48
|
+
interface BranchSelectorProps {
|
|
49
|
+
readonly branches: ReadonlyArray<Branch<unknown>>;
|
|
50
|
+
readonly currentBranch: string;
|
|
51
|
+
readonly onSelect: (branchId: string) => void;
|
|
52
|
+
readonly onClose: () => void;
|
|
53
|
+
readonly isFocused: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// =============================================================================
|
|
57
|
+
// Sub-Components
|
|
58
|
+
// =============================================================================
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Branch selector popup for choosing between branches.
|
|
62
|
+
*/
|
|
63
|
+
function BranchSelector({
|
|
64
|
+
branches,
|
|
65
|
+
currentBranch,
|
|
66
|
+
onSelect,
|
|
67
|
+
onClose,
|
|
68
|
+
isFocused,
|
|
69
|
+
}: BranchSelectorProps): React.JSX.Element {
|
|
70
|
+
const { theme } = useTheme();
|
|
71
|
+
const { t } = useTUITranslation();
|
|
72
|
+
const [selectedIndex, setSelectedIndex] = useState(() => {
|
|
73
|
+
const index = branches.findIndex((b) => b.name === currentBranch);
|
|
74
|
+
return index >= 0 ? index : 0;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
useInput(
|
|
78
|
+
(input, key) => {
|
|
79
|
+
if (!isFocused) return;
|
|
80
|
+
|
|
81
|
+
if (key.upArrow || input === "k") {
|
|
82
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
83
|
+
} else if (key.downArrow || input === "j") {
|
|
84
|
+
setSelectedIndex((prev) => Math.min(branches.length - 1, prev + 1));
|
|
85
|
+
} else if (key.return) {
|
|
86
|
+
const branch = branches[selectedIndex];
|
|
87
|
+
if (branch) {
|
|
88
|
+
onSelect(branch.id);
|
|
89
|
+
}
|
|
90
|
+
onClose();
|
|
91
|
+
} else if (key.escape || input === "q") {
|
|
92
|
+
onClose();
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{ isActive: isFocused }
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<Box flexDirection="column" borderStyle="single" borderColor={theme.colors.muted} paddingX={1}>
|
|
100
|
+
<Text bold color={theme.colors.primary}>
|
|
101
|
+
{t("backtrack.selectBranch")}
|
|
102
|
+
</Text>
|
|
103
|
+
<Box marginTop={1} flexDirection="column">
|
|
104
|
+
{branches.map((branch, index) => {
|
|
105
|
+
const isSelected = index === selectedIndex;
|
|
106
|
+
const isCurrent = branch.name === currentBranch;
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<Box key={branch.id}>
|
|
110
|
+
<Text color={isSelected ? theme.colors.primary : undefined} inverse={isSelected}>
|
|
111
|
+
{isSelected ? "▸ " : " "}
|
|
112
|
+
{branch.name}
|
|
113
|
+
{isCurrent ? ` ${t("backtrack.current")}` : ""}
|
|
114
|
+
</Text>
|
|
115
|
+
</Box>
|
|
116
|
+
);
|
|
117
|
+
})}
|
|
118
|
+
</Box>
|
|
119
|
+
<Box marginTop={1}>
|
|
120
|
+
<Text dimColor>{t("backtrack.keybindings")}</Text>
|
|
121
|
+
</Box>
|
|
122
|
+
</Box>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Status indicator showing undo/redo availability.
|
|
128
|
+
*/
|
|
129
|
+
function StatusIndicator({
|
|
130
|
+
canUndo,
|
|
131
|
+
canRedo,
|
|
132
|
+
compact,
|
|
133
|
+
t,
|
|
134
|
+
}: {
|
|
135
|
+
canUndo: boolean;
|
|
136
|
+
canRedo: boolean;
|
|
137
|
+
compact: boolean;
|
|
138
|
+
t: (key: string) => string;
|
|
139
|
+
}): React.JSX.Element {
|
|
140
|
+
const { theme } = useTheme();
|
|
141
|
+
|
|
142
|
+
if (compact) {
|
|
143
|
+
return (
|
|
144
|
+
<Box>
|
|
145
|
+
<Text color={canUndo ? theme.colors.success : theme.colors.muted}>◀</Text>
|
|
146
|
+
<Text color={canRedo ? theme.colors.success : theme.colors.muted}>▶</Text>
|
|
147
|
+
</Box>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<Box gap={1}>
|
|
153
|
+
<Text color={canUndo ? theme.colors.success : theme.colors.muted}>
|
|
154
|
+
{"[<]"} {t("backtrack.undo")} {canUndo ? "+" : "x"}
|
|
155
|
+
</Text>
|
|
156
|
+
<Text color={canRedo ? theme.colors.success : theme.colors.muted}>
|
|
157
|
+
{"[>]"} {t("backtrack.redo")} {canRedo ? "+" : "x"}
|
|
158
|
+
</Text>
|
|
159
|
+
</Box>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Branch indicator showing current branch and fork count.
|
|
165
|
+
*/
|
|
166
|
+
function BranchIndicator({
|
|
167
|
+
currentBranch,
|
|
168
|
+
forkCount,
|
|
169
|
+
compact,
|
|
170
|
+
t,
|
|
171
|
+
}: {
|
|
172
|
+
currentBranch: string;
|
|
173
|
+
forkCount: number;
|
|
174
|
+
compact: boolean;
|
|
175
|
+
onClick?: () => void;
|
|
176
|
+
t: (key: string) => string;
|
|
177
|
+
}): React.JSX.Element {
|
|
178
|
+
const { theme } = useTheme();
|
|
179
|
+
|
|
180
|
+
if (compact) {
|
|
181
|
+
return (
|
|
182
|
+
<Box>
|
|
183
|
+
<Text color={theme.colors.info}>⎇{forkCount > 0 ? `+${forkCount}` : ""}</Text>
|
|
184
|
+
</Box>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<Box gap={1}>
|
|
190
|
+
<Text color={theme.colors.info}>⎇ {currentBranch}</Text>
|
|
191
|
+
{forkCount > 0 && (
|
|
192
|
+
<Text dimColor>
|
|
193
|
+
({forkCount} {forkCount !== 1 ? t("backtrack.forks") : t("backtrack.fork")})
|
|
194
|
+
</Text>
|
|
195
|
+
)}
|
|
196
|
+
</Box>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* History position indicator.
|
|
202
|
+
*/
|
|
203
|
+
function HistoryIndicator({
|
|
204
|
+
currentIndex,
|
|
205
|
+
historyLength,
|
|
206
|
+
}: {
|
|
207
|
+
currentIndex: number;
|
|
208
|
+
historyLength: number;
|
|
209
|
+
}): React.JSX.Element {
|
|
210
|
+
const position = currentIndex + 1;
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<Text dimColor>
|
|
214
|
+
[{position}/{historyLength}]
|
|
215
|
+
</Text>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// =============================================================================
|
|
220
|
+
// Main Component
|
|
221
|
+
// =============================================================================
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* BacktrackControls displays undo/redo status and branch management UI.
|
|
225
|
+
*
|
|
226
|
+
* Features:
|
|
227
|
+
* - Visual indicators for undo/redo availability
|
|
228
|
+
* - Current branch display with fork count
|
|
229
|
+
* - Branch selector popup (Ctrl+B to toggle)
|
|
230
|
+
* - History position indicator
|
|
231
|
+
* - Keyboard shortcuts: Ctrl+Z (undo), Ctrl+Y (redo), Ctrl+B (branch)
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```tsx
|
|
235
|
+
* const {
|
|
236
|
+
* backtrackState,
|
|
237
|
+
* branches,
|
|
238
|
+
* undo,
|
|
239
|
+
* redo,
|
|
240
|
+
* createBranch,
|
|
241
|
+
* switchBranch,
|
|
242
|
+
* } = useBacktrack({ initialState: messages });
|
|
243
|
+
*
|
|
244
|
+
* <BacktrackControls
|
|
245
|
+
* backtrackState={backtrackState}
|
|
246
|
+
* branches={branches}
|
|
247
|
+
* onUndo={undo}
|
|
248
|
+
* onRedo={redo}
|
|
249
|
+
* onCreateBranch={() => createBranch()}
|
|
250
|
+
* onSwitchBranch={switchBranch}
|
|
251
|
+
* isFocused={true}
|
|
252
|
+
* showHints={true}
|
|
253
|
+
* />
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
export const BacktrackControls = memo(function BacktrackControls({
|
|
257
|
+
backtrackState,
|
|
258
|
+
branches,
|
|
259
|
+
onUndo,
|
|
260
|
+
onRedo,
|
|
261
|
+
onCreateBranch,
|
|
262
|
+
onSwitchBranch,
|
|
263
|
+
isFocused = false,
|
|
264
|
+
compact = false,
|
|
265
|
+
showHints = true,
|
|
266
|
+
}: BacktrackControlsProps): React.JSX.Element {
|
|
267
|
+
const { t } = useTUITranslation();
|
|
268
|
+
const [showBranchSelector, setShowBranchSelector] = useState(false);
|
|
269
|
+
|
|
270
|
+
const { canUndo, canRedo, forkCount, currentBranch, currentIndex, historyLength } =
|
|
271
|
+
backtrackState;
|
|
272
|
+
|
|
273
|
+
// Handle keyboard input
|
|
274
|
+
useInput(
|
|
275
|
+
(input, key) => {
|
|
276
|
+
if (!isFocused || showBranchSelector) return;
|
|
277
|
+
|
|
278
|
+
// Ctrl+Z for undo
|
|
279
|
+
if (key.ctrl && input === "z") {
|
|
280
|
+
onUndo();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Ctrl+Y for redo
|
|
285
|
+
if (key.ctrl && input === "y") {
|
|
286
|
+
onRedo();
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Ctrl+B for branch selector or create branch
|
|
291
|
+
if (key.ctrl && input === "b") {
|
|
292
|
+
if (branches.length > 1) {
|
|
293
|
+
setShowBranchSelector(true);
|
|
294
|
+
} else {
|
|
295
|
+
onCreateBranch();
|
|
296
|
+
}
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Ctrl+Shift+B to always create new branch
|
|
301
|
+
if (key.ctrl && key.shift && input === "B") {
|
|
302
|
+
onCreateBranch();
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
{ isActive: isFocused && !showBranchSelector }
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const handleBranchSelect = useCallback(
|
|
310
|
+
(branchId: string) => {
|
|
311
|
+
onSwitchBranch(branchId);
|
|
312
|
+
setShowBranchSelector(false);
|
|
313
|
+
},
|
|
314
|
+
[onSwitchBranch]
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
const handleBranchClose = useCallback(() => {
|
|
318
|
+
setShowBranchSelector(false);
|
|
319
|
+
}, []);
|
|
320
|
+
|
|
321
|
+
// Branch selector overlay
|
|
322
|
+
if (showBranchSelector) {
|
|
323
|
+
return (
|
|
324
|
+
<BranchSelector
|
|
325
|
+
branches={branches}
|
|
326
|
+
currentBranch={currentBranch}
|
|
327
|
+
onSelect={handleBranchSelect}
|
|
328
|
+
onClose={handleBranchClose}
|
|
329
|
+
isFocused={isFocused}
|
|
330
|
+
/>
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Compact mode - single line
|
|
335
|
+
if (compact) {
|
|
336
|
+
return (
|
|
337
|
+
<Box gap={1}>
|
|
338
|
+
<StatusIndicator canUndo={canUndo} canRedo={canRedo} compact={true} t={t} />
|
|
339
|
+
<BranchIndicator
|
|
340
|
+
currentBranch={currentBranch}
|
|
341
|
+
forkCount={forkCount}
|
|
342
|
+
compact={true}
|
|
343
|
+
onClick={() => setShowBranchSelector(true)}
|
|
344
|
+
t={t}
|
|
345
|
+
/>
|
|
346
|
+
<HistoryIndicator currentIndex={currentIndex} historyLength={historyLength} />
|
|
347
|
+
</Box>
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Full mode
|
|
352
|
+
return (
|
|
353
|
+
<Box flexDirection="column">
|
|
354
|
+
<Box gap={2}>
|
|
355
|
+
<StatusIndicator canUndo={canUndo} canRedo={canRedo} compact={false} t={t} />
|
|
356
|
+
<Text dimColor>│</Text>
|
|
357
|
+
<BranchIndicator
|
|
358
|
+
currentBranch={currentBranch}
|
|
359
|
+
forkCount={forkCount}
|
|
360
|
+
compact={false}
|
|
361
|
+
onClick={() => setShowBranchSelector(true)}
|
|
362
|
+
t={t}
|
|
363
|
+
/>
|
|
364
|
+
<Text dimColor>│</Text>
|
|
365
|
+
<HistoryIndicator currentIndex={currentIndex} historyLength={historyLength} />
|
|
366
|
+
</Box>
|
|
367
|
+
|
|
368
|
+
{showHints && (
|
|
369
|
+
<Box marginTop={1}>
|
|
370
|
+
<Text dimColor>
|
|
371
|
+
^Z {t("backtrack.undo").toLowerCase()} • ^Y {t("backtrack.redo").toLowerCase()} • ^B{" "}
|
|
372
|
+
{branches.length > 1 ? t("backtrack.switchBranch") : t("backtrack.newBranch")}
|
|
373
|
+
</Text>
|
|
374
|
+
</Box>
|
|
375
|
+
)}
|
|
376
|
+
</Box>
|
|
377
|
+
);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Compact status bar variant of BacktrackControls.
|
|
382
|
+
* Designed to fit in the status bar area.
|
|
383
|
+
*/
|
|
384
|
+
export function BacktrackStatusBar({
|
|
385
|
+
backtrackState,
|
|
386
|
+
}: {
|
|
387
|
+
backtrackState: BacktrackState;
|
|
388
|
+
}): React.JSX.Element {
|
|
389
|
+
const { theme } = useTheme();
|
|
390
|
+
const { canUndo, canRedo, currentBranch, currentIndex, historyLength } = backtrackState;
|
|
391
|
+
|
|
392
|
+
return (
|
|
393
|
+
<Box gap={1}>
|
|
394
|
+
<Text color={canUndo ? theme.colors.info : theme.colors.muted}>◀</Text>
|
|
395
|
+
<Text dimColor>
|
|
396
|
+
{currentIndex + 1}/{historyLength}
|
|
397
|
+
</Text>
|
|
398
|
+
<Text color={canRedo ? theme.colors.info : theme.colors.muted}>▶</Text>
|
|
399
|
+
{currentBranch !== "Main" && <Text color={theme.colors.warning}>⎇ {currentBranch}</Text>}
|
|
400
|
+
</Box>
|
|
401
|
+
);
|
|
402
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backtrack Components
|
|
3
|
+
*
|
|
4
|
+
* Components for conversation backtracking and branching UI.
|
|
5
|
+
*
|
|
6
|
+
* @module tui/components/backtrack
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
BacktrackControls,
|
|
11
|
+
type BacktrackControlsProps,
|
|
12
|
+
BacktrackStatusBar,
|
|
13
|
+
} from "./BacktrackControls.js";
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AutoApprovalStatus Component (Phase 35+)
|
|
3
|
+
*
|
|
4
|
+
* Displays auto-approval status and limits in the TUI.
|
|
5
|
+
* Shows consecutive requests/cost for automatic tool approvals.
|
|
6
|
+
* Uses ASCII symbols instead of emoji for terminal compatibility.
|
|
7
|
+
*
|
|
8
|
+
* @module tui/components/common/AutoApprovalStatus
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Box, Text } from "ink";
|
|
12
|
+
import type React from "react";
|
|
13
|
+
import { memo } from "react";
|
|
14
|
+
import { useTheme } from "../../theme/index.js";
|
|
15
|
+
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Types
|
|
18
|
+
// =============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Status severity level.
|
|
22
|
+
*/
|
|
23
|
+
export type AutoApprovalSeverity = "info" | "warning" | "error";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Props for the AutoApprovalStatus component.
|
|
27
|
+
*/
|
|
28
|
+
export interface AutoApprovalStatusProps {
|
|
29
|
+
/** Number of consecutive auto-approved requests */
|
|
30
|
+
readonly consecutiveRequests: number;
|
|
31
|
+
/** Request limit for auto-approvals */
|
|
32
|
+
readonly requestLimit: number;
|
|
33
|
+
/** Cumulative cost of consecutive auto-approved operations in USD */
|
|
34
|
+
readonly consecutiveCost: number;
|
|
35
|
+
/** Cost limit for auto-approvals in USD */
|
|
36
|
+
readonly costLimit: number;
|
|
37
|
+
/** Percentage of request limit used (0-100) */
|
|
38
|
+
readonly requestPercentUsed: number;
|
|
39
|
+
/** Percentage of cost limit used (0-100) */
|
|
40
|
+
readonly costPercentUsed: number;
|
|
41
|
+
/** Whether any limit has been reached */
|
|
42
|
+
readonly limitReached?: boolean;
|
|
43
|
+
/** Which limit type was reached */
|
|
44
|
+
readonly limitType?: "requests" | "cost";
|
|
45
|
+
/** Whether to show in compact mode */
|
|
46
|
+
readonly compact?: boolean;
|
|
47
|
+
/** Override severity level */
|
|
48
|
+
readonly severity?: AutoApprovalSeverity;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Constants - ASCII Symbols (NO EMOJI)
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* ASCII symbols for different severity levels.
|
|
57
|
+
*/
|
|
58
|
+
const SEVERITY_SYMBOLS: Record<AutoApprovalSeverity, string> = {
|
|
59
|
+
info: "[i]",
|
|
60
|
+
warning: "[!]",
|
|
61
|
+
error: "[X]",
|
|
62
|
+
} as const;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* ASCII symbol for auto-approval indicator.
|
|
66
|
+
*/
|
|
67
|
+
const AUTO_SYMBOL = "[>]";
|
|
68
|
+
|
|
69
|
+
// =============================================================================
|
|
70
|
+
// Helper Functions
|
|
71
|
+
// =============================================================================
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Format cost as USD string.
|
|
75
|
+
*/
|
|
76
|
+
function formatCostUSD(cost: number): string {
|
|
77
|
+
return `$${cost.toFixed(2)}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Determine severity based on usage percentage.
|
|
82
|
+
*/
|
|
83
|
+
function getSeverityFromPercent(
|
|
84
|
+
requestPercent: number,
|
|
85
|
+
costPercent: number,
|
|
86
|
+
limitReached: boolean
|
|
87
|
+
): AutoApprovalSeverity {
|
|
88
|
+
if (limitReached) return "error";
|
|
89
|
+
const maxPercent = Math.max(requestPercent, costPercent);
|
|
90
|
+
if (maxPercent >= 80) return "warning";
|
|
91
|
+
return "info";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// =============================================================================
|
|
95
|
+
// Component
|
|
96
|
+
// =============================================================================
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* AutoApprovalStatus - Displays auto-approval limits and status.
|
|
100
|
+
*
|
|
101
|
+
* Features:
|
|
102
|
+
* - Shows consecutive requests vs limit
|
|
103
|
+
* - Shows consecutive cost vs limit
|
|
104
|
+
* - Different severity levels (info, warning, error)
|
|
105
|
+
* - Compact and full display modes
|
|
106
|
+
* - ASCII-only symbols for terminal compatibility
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```tsx
|
|
110
|
+
* // Normal status
|
|
111
|
+
* <AutoApprovalStatus
|
|
112
|
+
* consecutiveRequests={45}
|
|
113
|
+
* requestLimit={50}
|
|
114
|
+
* consecutiveCost={3.50}
|
|
115
|
+
* costLimit={5.00}
|
|
116
|
+
* requestPercentUsed={90}
|
|
117
|
+
* costPercentUsed={70}
|
|
118
|
+
* />
|
|
119
|
+
*
|
|
120
|
+
* // Limit reached
|
|
121
|
+
* <AutoApprovalStatus
|
|
122
|
+
* consecutiveRequests={50}
|
|
123
|
+
* requestLimit={50}
|
|
124
|
+
* consecutiveCost={4.00}
|
|
125
|
+
* costLimit={5.00}
|
|
126
|
+
* requestPercentUsed={100}
|
|
127
|
+
* costPercentUsed={80}
|
|
128
|
+
* limitReached
|
|
129
|
+
* limitType="requests"
|
|
130
|
+
* />
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
export const AutoApprovalStatus: React.FC<AutoApprovalStatusProps> = memo(
|
|
134
|
+
function AutoApprovalStatus({
|
|
135
|
+
consecutiveRequests,
|
|
136
|
+
requestLimit,
|
|
137
|
+
consecutiveCost,
|
|
138
|
+
costLimit,
|
|
139
|
+
requestPercentUsed,
|
|
140
|
+
costPercentUsed,
|
|
141
|
+
limitReached = false,
|
|
142
|
+
limitType,
|
|
143
|
+
compact = false,
|
|
144
|
+
severity: severityProp,
|
|
145
|
+
}) {
|
|
146
|
+
const { theme } = useTheme();
|
|
147
|
+
|
|
148
|
+
// Determine severity from props or calculate from percentage
|
|
149
|
+
const severity =
|
|
150
|
+
severityProp ?? getSeverityFromPercent(requestPercentUsed, costPercentUsed, limitReached);
|
|
151
|
+
|
|
152
|
+
// Get colors based on severity
|
|
153
|
+
const getColor = () => {
|
|
154
|
+
switch (severity) {
|
|
155
|
+
case "error":
|
|
156
|
+
return theme.colors.error;
|
|
157
|
+
case "warning":
|
|
158
|
+
return theme.colors.warning;
|
|
159
|
+
default:
|
|
160
|
+
return theme.colors.info;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const symbol = SEVERITY_SYMBOLS[severity];
|
|
165
|
+
const color = getColor();
|
|
166
|
+
|
|
167
|
+
// Compact display: single line
|
|
168
|
+
// [i] Auto-approved: 45/50 requests ($3.50/$5.00)
|
|
169
|
+
if (compact) {
|
|
170
|
+
return (
|
|
171
|
+
<Box>
|
|
172
|
+
<Text color={color}>
|
|
173
|
+
{AUTO_SYMBOL} Auto-approved: {consecutiveRequests}/{requestLimit} requests (
|
|
174
|
+
{formatCostUSD(consecutiveCost)}/{formatCostUSD(costLimit)})
|
|
175
|
+
</Text>
|
|
176
|
+
</Box>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Full display with limit reached message
|
|
181
|
+
if (limitReached) {
|
|
182
|
+
const limitMessage =
|
|
183
|
+
limitType === "requests"
|
|
184
|
+
? `Request limit reached (${consecutiveRequests}/${requestLimit})`
|
|
185
|
+
: `Cost limit reached (${formatCostUSD(consecutiveCost)}/${formatCostUSD(costLimit)})`;
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<Box flexDirection="column" marginY={1}>
|
|
189
|
+
{/* Header line */}
|
|
190
|
+
<Box>
|
|
191
|
+
<Text color={color} bold>
|
|
192
|
+
{symbol} AUTO-APPROVAL LIMIT: {limitMessage}
|
|
193
|
+
</Text>
|
|
194
|
+
</Box>
|
|
195
|
+
|
|
196
|
+
{/* Instructions */}
|
|
197
|
+
<Box marginLeft={4}>
|
|
198
|
+
<Text dimColor>Waiting for user confirmation to continue...</Text>
|
|
199
|
+
</Box>
|
|
200
|
+
|
|
201
|
+
{/* Details */}
|
|
202
|
+
<Box marginLeft={4} marginTop={1}>
|
|
203
|
+
<Text>
|
|
204
|
+
{AUTO_SYMBOL} Requests: {consecutiveRequests}/{requestLimit} | Cost:{" "}
|
|
205
|
+
{formatCostUSD(consecutiveCost)}/{formatCostUSD(costLimit)}
|
|
206
|
+
</Text>
|
|
207
|
+
</Box>
|
|
208
|
+
</Box>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Full display for warning state
|
|
213
|
+
return (
|
|
214
|
+
<Box flexDirection="column" marginY={1}>
|
|
215
|
+
{/* Header line */}
|
|
216
|
+
<Box>
|
|
217
|
+
<Text color={color} bold>
|
|
218
|
+
{symbol} Auto-Approval Status
|
|
219
|
+
</Text>
|
|
220
|
+
</Box>
|
|
221
|
+
|
|
222
|
+
{/* Request details */}
|
|
223
|
+
<Box marginLeft={4}>
|
|
224
|
+
<Text>
|
|
225
|
+
Requests:{" "}
|
|
226
|
+
<Text color={requestPercentUsed >= 80 ? theme.colors.warning : undefined}>
|
|
227
|
+
{consecutiveRequests}/{requestLimit}
|
|
228
|
+
</Text>{" "}
|
|
229
|
+
({requestPercentUsed.toFixed(0)}%)
|
|
230
|
+
</Text>
|
|
231
|
+
</Box>
|
|
232
|
+
|
|
233
|
+
{/* Cost details */}
|
|
234
|
+
<Box marginLeft={4}>
|
|
235
|
+
<Text>
|
|
236
|
+
Cost:{" "}
|
|
237
|
+
<Text color={costPercentUsed >= 80 ? theme.colors.warning : undefined}>
|
|
238
|
+
{formatCostUSD(consecutiveCost)}/{formatCostUSD(costLimit)}
|
|
239
|
+
</Text>{" "}
|
|
240
|
+
({costPercentUsed.toFixed(0)}%)
|
|
241
|
+
</Text>
|
|
242
|
+
</Box>
|
|
243
|
+
</Box>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Default export for convenience.
|
|
250
|
+
*/
|
|
251
|
+
export default AutoApprovalStatus;
|