@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,729 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusBar Component Tests (T040)
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive tests for the StatusBar component and its sub-components:
|
|
5
|
+
* - ModelIndicator: Provider icons and model display
|
|
6
|
+
* - TokenCounter: Token usage with color thresholds
|
|
7
|
+
* - TrustModeIndicator: Trust mode icons and colors
|
|
8
|
+
* - ThinkingModeIndicator: Thinking mode states and budget
|
|
9
|
+
*
|
|
10
|
+
* Tests focus on:
|
|
11
|
+
* - All indicators display correctly
|
|
12
|
+
* - Token color thresholds (warning > 80%, error > 95%)
|
|
13
|
+
* - Trust mode icons (ask/auto/full)
|
|
14
|
+
* - Thinking mode states with optional budget
|
|
15
|
+
*
|
|
16
|
+
* @module tui/components/StatusBar/__tests__/StatusBar.test
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { render } from "ink-testing-library";
|
|
20
|
+
import type React from "react";
|
|
21
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
22
|
+
import { ThemeProvider } from "../../../theme/index.js";
|
|
23
|
+
import { ModelIndicator } from "../ModelIndicator.js";
|
|
24
|
+
import { StatusBar } from "../StatusBar.js";
|
|
25
|
+
import { ThinkingModeIndicator } from "../ThinkingModeIndicator.js";
|
|
26
|
+
import { TokenCounter } from "../TokenCounter.js";
|
|
27
|
+
import { TrustModeIndicator } from "../TrustModeIndicator.js";
|
|
28
|
+
|
|
29
|
+
// =============================================================================
|
|
30
|
+
// Test Helpers
|
|
31
|
+
// =============================================================================
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Wrapper to provide theme context for tests
|
|
35
|
+
*/
|
|
36
|
+
function renderWithTheme(element: React.ReactElement) {
|
|
37
|
+
return render(<ThemeProvider>{element}</ThemeProvider>);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// ModelIndicator Tests
|
|
42
|
+
// =============================================================================
|
|
43
|
+
|
|
44
|
+
describe("ModelIndicator", () => {
|
|
45
|
+
describe("Provider Icons", () => {
|
|
46
|
+
it("should render Anthropic provider with diamond icon", () => {
|
|
47
|
+
const { lastFrame } = renderWithTheme(
|
|
48
|
+
<ModelIndicator provider="anthropic" model="claude-3-opus" />
|
|
49
|
+
);
|
|
50
|
+
const frame = lastFrame() ?? "";
|
|
51
|
+
expect(frame).toContain("◈");
|
|
52
|
+
expect(frame).toContain("Anthropic");
|
|
53
|
+
expect(frame).toContain("claude-3-opus");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should render OpenAI provider with circle icon", () => {
|
|
57
|
+
const { lastFrame } = renderWithTheme(
|
|
58
|
+
<ModelIndicator provider="openai" model="gpt-4-turbo" />
|
|
59
|
+
);
|
|
60
|
+
const frame = lastFrame() ?? "";
|
|
61
|
+
expect(frame).toContain("◉");
|
|
62
|
+
expect(frame).toContain("OpenAI");
|
|
63
|
+
expect(frame).toContain("gpt-4-turbo");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should render Google provider with ring icon", () => {
|
|
67
|
+
const { lastFrame } = renderWithTheme(
|
|
68
|
+
<ModelIndicator provider="google" model="gemini-pro" />
|
|
69
|
+
);
|
|
70
|
+
const frame = lastFrame() ?? "";
|
|
71
|
+
expect(frame).toContain("◎");
|
|
72
|
+
expect(frame).toContain("Google");
|
|
73
|
+
expect(frame).toContain("gemini-pro");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should render Azure provider with diamond outline icon", () => {
|
|
77
|
+
const { lastFrame } = renderWithTheme(<ModelIndicator provider="azure" model="gpt-4" />);
|
|
78
|
+
const frame = lastFrame() ?? "";
|
|
79
|
+
expect(frame).toContain("◇");
|
|
80
|
+
expect(frame).toContain("Azure");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should render Bedrock provider with filled square icon", () => {
|
|
84
|
+
const { lastFrame } = renderWithTheme(
|
|
85
|
+
<ModelIndicator provider="bedrock" model="claude-v2" />
|
|
86
|
+
);
|
|
87
|
+
const frame = lastFrame() ?? "";
|
|
88
|
+
expect(frame).toContain("▣");
|
|
89
|
+
expect(frame).toContain("Bedrock");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should render Mistral provider with filled diamond icon", () => {
|
|
93
|
+
const { lastFrame } = renderWithTheme(
|
|
94
|
+
<ModelIndicator provider="mistral" model="mistral-large" />
|
|
95
|
+
);
|
|
96
|
+
const frame = lastFrame() ?? "";
|
|
97
|
+
expect(frame).toContain("◆");
|
|
98
|
+
expect(frame).toContain("Mistral");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should render Ollama provider with circle outline icon", () => {
|
|
102
|
+
const { lastFrame } = renderWithTheme(<ModelIndicator provider="ollama" model="llama2" />);
|
|
103
|
+
const frame = lastFrame() ?? "";
|
|
104
|
+
expect(frame).toContain("○");
|
|
105
|
+
expect(frame).toContain("Ollama");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should render default icon for unknown provider", () => {
|
|
109
|
+
const { lastFrame } = renderWithTheme(<ModelIndicator provider="custom" model="my-model" />);
|
|
110
|
+
const frame = lastFrame() ?? "";
|
|
111
|
+
expect(frame).toContain("●");
|
|
112
|
+
expect(frame).toContain("Custom");
|
|
113
|
+
expect(frame).toContain("my-model");
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("Model Name Display", () => {
|
|
118
|
+
it("should display short model names fully", () => {
|
|
119
|
+
const { lastFrame } = renderWithTheme(
|
|
120
|
+
<ModelIndicator provider="anthropic" model="claude-3" />
|
|
121
|
+
);
|
|
122
|
+
expect(lastFrame()).toContain("claude-3");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("should truncate very long model names", () => {
|
|
126
|
+
const longModelName = "claude-3-5-sonnet-20241022-extra-long-suffix";
|
|
127
|
+
const { lastFrame } = renderWithTheme(
|
|
128
|
+
<ModelIndicator provider="anthropic" model={longModelName} />
|
|
129
|
+
);
|
|
130
|
+
const frame = lastFrame() ?? "";
|
|
131
|
+
expect(frame).toContain("...");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("should render provider/model format", () => {
|
|
135
|
+
const { lastFrame } = renderWithTheme(<ModelIndicator provider="openai" model="gpt-4" />);
|
|
136
|
+
const frame = lastFrame() ?? "";
|
|
137
|
+
expect(frame).toContain("OpenAI");
|
|
138
|
+
expect(frame).toContain("/");
|
|
139
|
+
expect(frame).toContain("gpt-4");
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("Case Insensitivity", () => {
|
|
144
|
+
it("should handle uppercase provider names", () => {
|
|
145
|
+
const { lastFrame } = renderWithTheme(<ModelIndicator provider="ANTHROPIC" model="claude" />);
|
|
146
|
+
expect(lastFrame()).toContain("◈");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("should handle mixed case provider names", () => {
|
|
150
|
+
const { lastFrame } = renderWithTheme(<ModelIndicator provider="OpenAI" model="gpt-4" />);
|
|
151
|
+
expect(lastFrame()).toContain("◉");
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// =============================================================================
|
|
157
|
+
// TokenCounter Tests
|
|
158
|
+
// =============================================================================
|
|
159
|
+
|
|
160
|
+
describe("TokenCounter", () => {
|
|
161
|
+
describe("Basic Display", () => {
|
|
162
|
+
it("should render token counts", () => {
|
|
163
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={5000} max={100000} />);
|
|
164
|
+
const frame = lastFrame() ?? "";
|
|
165
|
+
expect(frame).toContain("◊");
|
|
166
|
+
expect(frame).toContain("5.0K");
|
|
167
|
+
expect(frame).toContain("100K");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("should render small token counts without suffix", () => {
|
|
171
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={500} max={1000} />);
|
|
172
|
+
const frame = lastFrame() ?? "";
|
|
173
|
+
expect(frame).toContain("500");
|
|
174
|
+
expect(frame).toContain("1.0K");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("should render large token counts with M suffix", () => {
|
|
178
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={1500000} max={2000000} />);
|
|
179
|
+
const frame = lastFrame() ?? "";
|
|
180
|
+
expect(frame).toContain("1.5M");
|
|
181
|
+
expect(frame).toContain("2.0M");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("should display percentage", () => {
|
|
185
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={5000} max={10000} />);
|
|
186
|
+
expect(lastFrame()).toContain("50%");
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe("Color Thresholds", () => {
|
|
191
|
+
it("should display normal color below 80%", () => {
|
|
192
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={7900} max={10000} />);
|
|
193
|
+
const frame = lastFrame() ?? "";
|
|
194
|
+
expect(frame).toContain("79%");
|
|
195
|
+
// Normal state renders without warning/error colors
|
|
196
|
+
expect(frame).toBeDefined();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("should display warning color at 80%", () => {
|
|
200
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={8000} max={10000} />);
|
|
201
|
+
const frame = lastFrame() ?? "";
|
|
202
|
+
expect(frame).toContain("80%");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("should display warning color at 85%", () => {
|
|
206
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={8500} max={10000} />);
|
|
207
|
+
const frame = lastFrame() ?? "";
|
|
208
|
+
expect(frame).toContain("85%");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("should display warning color at 94%", () => {
|
|
212
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={9400} max={10000} />);
|
|
213
|
+
const frame = lastFrame() ?? "";
|
|
214
|
+
expect(frame).toContain("94%");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("should display error color at 95%", () => {
|
|
218
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={9500} max={10000} />);
|
|
219
|
+
const frame = lastFrame() ?? "";
|
|
220
|
+
expect(frame).toContain("95%");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("should display error color at 100%", () => {
|
|
224
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={10000} max={10000} />);
|
|
225
|
+
const frame = lastFrame() ?? "";
|
|
226
|
+
expect(frame).toContain("100%");
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("should cap percentage at 100% even if over limit", () => {
|
|
230
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={12000} max={10000} />);
|
|
231
|
+
const frame = lastFrame() ?? "";
|
|
232
|
+
expect(frame).toContain("100%");
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe("Edge Cases", () => {
|
|
237
|
+
it("should handle zero max tokens gracefully", () => {
|
|
238
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={100} max={0} />);
|
|
239
|
+
const frame = lastFrame() ?? "";
|
|
240
|
+
expect(frame).toContain("0%");
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("should handle zero current tokens", () => {
|
|
244
|
+
const { lastFrame } = renderWithTheme(<TokenCounter current={0} max={10000} />);
|
|
245
|
+
const frame = lastFrame() ?? "";
|
|
246
|
+
expect(frame).toContain("0%");
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// =============================================================================
|
|
252
|
+
// TrustModeIndicator Tests
|
|
253
|
+
// =============================================================================
|
|
254
|
+
|
|
255
|
+
describe("TrustModeIndicator", () => {
|
|
256
|
+
describe("Trust Mode Icons", () => {
|
|
257
|
+
it("should render ask mode with ring icon", () => {
|
|
258
|
+
const { lastFrame } = renderWithTheme(<TrustModeIndicator mode="ask" />);
|
|
259
|
+
const frame = lastFrame() ?? "";
|
|
260
|
+
expect(frame).toContain("◎");
|
|
261
|
+
expect(frame).toContain("Approval: Ask");
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("should render auto mode with filled circle icon", () => {
|
|
265
|
+
const { lastFrame } = renderWithTheme(<TrustModeIndicator mode="auto" />);
|
|
266
|
+
const frame = lastFrame() ?? "";
|
|
267
|
+
expect(frame).toContain("◉");
|
|
268
|
+
expect(frame).toContain("Approval: Auto");
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("should render full mode with solid circle icon", () => {
|
|
272
|
+
const { lastFrame } = renderWithTheme(<TrustModeIndicator mode="full" />);
|
|
273
|
+
const frame = lastFrame() ?? "";
|
|
274
|
+
expect(frame).toContain("●");
|
|
275
|
+
expect(frame).toContain("Approval: Full");
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe("Trust Mode Display", () => {
|
|
280
|
+
it("should display icon and label together", () => {
|
|
281
|
+
const { lastFrame } = renderWithTheme(<TrustModeIndicator mode="ask" />);
|
|
282
|
+
const frame = lastFrame() ?? "";
|
|
283
|
+
expect(frame).toContain("◎");
|
|
284
|
+
expect(frame).toContain("Approval: Ask");
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("should display auto mode correctly", () => {
|
|
288
|
+
const { lastFrame } = renderWithTheme(<TrustModeIndicator mode="auto" />);
|
|
289
|
+
const frame = lastFrame() ?? "";
|
|
290
|
+
expect(frame).toMatch(/◉.*Approval: Auto/);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("should display full mode correctly", () => {
|
|
294
|
+
const { lastFrame } = renderWithTheme(<TrustModeIndicator mode="full" />);
|
|
295
|
+
const frame = lastFrame() ?? "";
|
|
296
|
+
expect(frame).toMatch(/●.*Approval: Full/);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// =============================================================================
|
|
302
|
+
// ThinkingModeIndicator Tests
|
|
303
|
+
// =============================================================================
|
|
304
|
+
|
|
305
|
+
describe("ThinkingModeIndicator", () => {
|
|
306
|
+
describe("Active/Inactive States", () => {
|
|
307
|
+
it("should render active state with filled diamond icon", () => {
|
|
308
|
+
const { lastFrame } = renderWithTheme(<ThinkingModeIndicator active={true} />);
|
|
309
|
+
const frame = lastFrame() ?? "";
|
|
310
|
+
expect(frame).toContain("◆");
|
|
311
|
+
expect(frame).toContain("Think");
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("should render inactive state with diamond outline icon", () => {
|
|
315
|
+
const { lastFrame } = renderWithTheme(<ThinkingModeIndicator active={false} />);
|
|
316
|
+
const frame = lastFrame() ?? "";
|
|
317
|
+
expect(frame).toContain("◇");
|
|
318
|
+
expect(frame).toContain("Think");
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
describe("Budget Display", () => {
|
|
323
|
+
it("should show budget when active with budget provided", () => {
|
|
324
|
+
const { lastFrame } = renderWithTheme(
|
|
325
|
+
<ThinkingModeIndicator active={true} budget={10000} used={5000} />
|
|
326
|
+
);
|
|
327
|
+
const frame = lastFrame() ?? "";
|
|
328
|
+
expect(frame).toContain("5.0K");
|
|
329
|
+
expect(frame).toContain("10K");
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it("should not show budget when inactive even if provided", () => {
|
|
333
|
+
const { lastFrame } = renderWithTheme(
|
|
334
|
+
<ThinkingModeIndicator active={false} budget={10000} used={5000} />
|
|
335
|
+
);
|
|
336
|
+
const frame = lastFrame() ?? "";
|
|
337
|
+
expect(frame).toContain("Think");
|
|
338
|
+
expect(frame).not.toContain("10K");
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("should show zero used when not provided", () => {
|
|
342
|
+
const { lastFrame } = renderWithTheme(<ThinkingModeIndicator active={true} budget={10000} />);
|
|
343
|
+
const frame = lastFrame() ?? "";
|
|
344
|
+
expect(frame).toContain("0");
|
|
345
|
+
expect(frame).toContain("10K");
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
describe("Budget Thresholds", () => {
|
|
350
|
+
it("should display normal color below 80% budget usage", () => {
|
|
351
|
+
const { lastFrame } = renderWithTheme(
|
|
352
|
+
<ThinkingModeIndicator active={true} budget={10000} used={7900} />
|
|
353
|
+
);
|
|
354
|
+
const frame = lastFrame() ?? "";
|
|
355
|
+
expect(frame).toContain("7.9K");
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it("should handle warning state at 80% budget usage", () => {
|
|
359
|
+
const { lastFrame } = renderWithTheme(
|
|
360
|
+
<ThinkingModeIndicator active={true} budget={10000} used={8000} />
|
|
361
|
+
);
|
|
362
|
+
const frame = lastFrame() ?? "";
|
|
363
|
+
expect(frame).toContain("8.0K");
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it("should handle error state at 95% budget usage", () => {
|
|
367
|
+
const { lastFrame } = renderWithTheme(
|
|
368
|
+
<ThinkingModeIndicator active={true} budget={10000} used={9500} />
|
|
369
|
+
);
|
|
370
|
+
const frame = lastFrame() ?? "";
|
|
371
|
+
expect(frame).toContain("9.5K");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("should handle 100% budget usage", () => {
|
|
375
|
+
const { lastFrame } = renderWithTheme(
|
|
376
|
+
<ThinkingModeIndicator active={true} budget={10000} used={10000} />
|
|
377
|
+
);
|
|
378
|
+
const frame = lastFrame() ?? "";
|
|
379
|
+
expect(frame).toContain("10K");
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe("Token Formatting", () => {
|
|
384
|
+
it("should format small token counts without suffix", () => {
|
|
385
|
+
const { lastFrame } = renderWithTheme(
|
|
386
|
+
<ThinkingModeIndicator active={true} budget={500} used={100} />
|
|
387
|
+
);
|
|
388
|
+
const frame = lastFrame() ?? "";
|
|
389
|
+
expect(frame).toContain("100");
|
|
390
|
+
expect(frame).toContain("500");
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it("should format large token counts with K suffix", () => {
|
|
394
|
+
const { lastFrame } = renderWithTheme(
|
|
395
|
+
<ThinkingModeIndicator active={true} budget={50000} used={25000} />
|
|
396
|
+
);
|
|
397
|
+
const frame = lastFrame() ?? "";
|
|
398
|
+
expect(frame).toContain("25K");
|
|
399
|
+
expect(frame).toContain("50K");
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it("should format million token counts with M suffix", () => {
|
|
403
|
+
const { lastFrame } = renderWithTheme(
|
|
404
|
+
<ThinkingModeIndicator active={true} budget={2000000} used={1000000} />
|
|
405
|
+
);
|
|
406
|
+
const frame = lastFrame() ?? "";
|
|
407
|
+
expect(frame).toContain("1.0M");
|
|
408
|
+
expect(frame).toContain("2.0M");
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// =============================================================================
|
|
414
|
+
// StatusBar Integration Tests
|
|
415
|
+
// =============================================================================
|
|
416
|
+
|
|
417
|
+
describe("StatusBar", () => {
|
|
418
|
+
describe("All Indicators Display", () => {
|
|
419
|
+
it("should render with all indicators", () => {
|
|
420
|
+
const { lastFrame } = renderWithTheme(
|
|
421
|
+
<StatusBar
|
|
422
|
+
mode="vibe"
|
|
423
|
+
modelName="claude-3-opus"
|
|
424
|
+
tokens={{ current: 5000, max: 100000 }}
|
|
425
|
+
trustMode="auto"
|
|
426
|
+
thinking={{ active: true, budget: 10000, used: 2500 }}
|
|
427
|
+
cost={0.02}
|
|
428
|
+
/>
|
|
429
|
+
);
|
|
430
|
+
const frame = lastFrame() ?? "";
|
|
431
|
+
// Mode selector shows only current mode icon by default
|
|
432
|
+
expect(frame).toContain("◐"); // vibe mode icon (current mode)
|
|
433
|
+
// Model indicator (name may be truncated)
|
|
434
|
+
expect(frame).toMatch(/claude-3/);
|
|
435
|
+
// Context progress (shows progress bar and percentage)
|
|
436
|
+
expect(frame).toContain("5%");
|
|
437
|
+
// Cost display
|
|
438
|
+
expect(frame).toMatch(/\$0\.0/);
|
|
439
|
+
// Trust mode (with "Approval: " prefix - core assertion for this component)
|
|
440
|
+
expect(frame).toContain("Approval:");
|
|
441
|
+
// Thinking mode
|
|
442
|
+
expect(frame).toContain("Think");
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it("should render separators between indicators", () => {
|
|
446
|
+
const { lastFrame } = renderWithTheme(
|
|
447
|
+
<StatusBar mode="vibe" modelName="gpt-4" tokens={{ current: 1000, max: 10000 }} />
|
|
448
|
+
);
|
|
449
|
+
const frame = lastFrame() ?? "";
|
|
450
|
+
expect(frame).toContain("│");
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
describe("Partial Indicators", () => {
|
|
455
|
+
it("should render with only model indicator", () => {
|
|
456
|
+
const { lastFrame } = renderWithTheme(
|
|
457
|
+
<StatusBar mode="vibe" modelName="claude-3" showBorder={false} />
|
|
458
|
+
);
|
|
459
|
+
const frame = lastFrame() ?? "";
|
|
460
|
+
expect(frame).toContain("claude-3");
|
|
461
|
+
// Mode selector is always shown
|
|
462
|
+
expect(frame).toContain("vibe");
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it("should render with only token counter", () => {
|
|
466
|
+
const { lastFrame } = renderWithTheme(
|
|
467
|
+
<StatusBar mode="vibe" tokens={{ current: 5000, max: 10000 }} />
|
|
468
|
+
);
|
|
469
|
+
const frame = lastFrame() ?? "";
|
|
470
|
+
expect(frame).toContain("50%");
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it("should render with trust mode when model is present", () => {
|
|
474
|
+
const { lastFrame } = renderWithTheme(
|
|
475
|
+
<StatusBar mode="vibe" modelName="claude-3" trustMode="full" />
|
|
476
|
+
);
|
|
477
|
+
const frame = lastFrame() ?? "";
|
|
478
|
+
expect(frame).toContain("●");
|
|
479
|
+
expect(frame).toContain("Approval: Full");
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it("should render with thinking indicator when model is present", () => {
|
|
483
|
+
const { lastFrame } = renderWithTheme(
|
|
484
|
+
<StatusBar
|
|
485
|
+
mode="vibe"
|
|
486
|
+
modelName="claude-3"
|
|
487
|
+
thinking={{ active: true, budget: 5000, used: 1000 }}
|
|
488
|
+
/>
|
|
489
|
+
);
|
|
490
|
+
const frame = lastFrame() ?? "";
|
|
491
|
+
expect(frame).toContain("◆");
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it("should render with model and tokens", () => {
|
|
495
|
+
const { lastFrame } = renderWithTheme(
|
|
496
|
+
<StatusBar mode="vibe" modelName="gemini-pro" tokens={{ current: 2000, max: 8000 }} />
|
|
497
|
+
);
|
|
498
|
+
const frame = lastFrame() ?? "";
|
|
499
|
+
expect(frame).toContain("gemini-pro");
|
|
500
|
+
expect(frame).toContain("│");
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
describe("Empty State", () => {
|
|
505
|
+
it("should render empty state when no model or tokens", () => {
|
|
506
|
+
const { lastFrame } = renderWithTheme(<StatusBar mode="vibe" />);
|
|
507
|
+
// Translation key is used in tests
|
|
508
|
+
expect(lastFrame()).toContain("status.noInfo");
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
describe("Border Option", () => {
|
|
513
|
+
it("should render without border by default", () => {
|
|
514
|
+
const { lastFrame } = renderWithTheme(<StatusBar mode="vibe" modelName="gpt-4" />);
|
|
515
|
+
const frame = lastFrame() ?? "";
|
|
516
|
+
expect(frame).toContain("gpt-4");
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it("should render with border when showBorder is true", () => {
|
|
520
|
+
const { lastFrame } = renderWithTheme(
|
|
521
|
+
<StatusBar mode="vibe" modelName="gpt-4" showBorder={true} />
|
|
522
|
+
);
|
|
523
|
+
const frame = lastFrame() ?? "";
|
|
524
|
+
expect(frame).toContain("gpt-4");
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
describe("Combined Token Threshold Display", () => {
|
|
529
|
+
it("should show warning state tokens in status bar", () => {
|
|
530
|
+
const { lastFrame } = renderWithTheme(
|
|
531
|
+
<StatusBar mode="vibe" modelName="claude-3" tokens={{ current: 85000, max: 100000 }} />
|
|
532
|
+
);
|
|
533
|
+
const frame = lastFrame() ?? "";
|
|
534
|
+
expect(frame).toContain("85%");
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it("should show error state tokens in status bar", () => {
|
|
538
|
+
const { lastFrame } = renderWithTheme(
|
|
539
|
+
<StatusBar mode="vibe" modelName="claude-3" tokens={{ current: 98000, max: 100000 }} />
|
|
540
|
+
);
|
|
541
|
+
const frame = lastFrame() ?? "";
|
|
542
|
+
expect(frame).toContain("98%");
|
|
543
|
+
});
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
describe("Combined Trust Modes in Status Bar", () => {
|
|
547
|
+
it("should show ask mode in full status bar", () => {
|
|
548
|
+
const { lastFrame } = renderWithTheme(
|
|
549
|
+
<StatusBar mode="vibe" modelName="claude-3" trustMode="ask" />
|
|
550
|
+
);
|
|
551
|
+
const frame = lastFrame() ?? "";
|
|
552
|
+
expect(frame).toContain("◎");
|
|
553
|
+
expect(frame).toContain("Approval: Ask");
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it("should show auto mode in full status bar", () => {
|
|
557
|
+
const { lastFrame } = renderWithTheme(
|
|
558
|
+
<StatusBar mode="vibe" modelName="claude-3" trustMode="auto" />
|
|
559
|
+
);
|
|
560
|
+
const frame = lastFrame() ?? "";
|
|
561
|
+
expect(frame).toContain("◉");
|
|
562
|
+
expect(frame).toContain("Approval: Auto");
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it("should show full mode in full status bar", () => {
|
|
566
|
+
const { lastFrame } = renderWithTheme(
|
|
567
|
+
<StatusBar mode="vibe" modelName="claude-3" trustMode="full" />
|
|
568
|
+
);
|
|
569
|
+
const frame = lastFrame() ?? "";
|
|
570
|
+
expect(frame).toContain("●");
|
|
571
|
+
expect(frame).toContain("Approval: Full");
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
describe("Combined Thinking Modes in Status Bar", () => {
|
|
576
|
+
it("should show active thinking without budget", () => {
|
|
577
|
+
const { lastFrame } = renderWithTheme(
|
|
578
|
+
<StatusBar mode="vibe" modelName="claude-3" thinking={{ active: true }} />
|
|
579
|
+
);
|
|
580
|
+
const frame = lastFrame() ?? "";
|
|
581
|
+
expect(frame).toContain("◆");
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it("should show active thinking with budget", () => {
|
|
585
|
+
const { lastFrame } = renderWithTheme(
|
|
586
|
+
<StatusBar
|
|
587
|
+
mode="vibe"
|
|
588
|
+
modelName="claude-3"
|
|
589
|
+
thinking={{ active: true, budget: 10000, used: 5000 }}
|
|
590
|
+
/>
|
|
591
|
+
);
|
|
592
|
+
const frame = lastFrame() ?? "";
|
|
593
|
+
expect(frame).toContain("◆");
|
|
594
|
+
expect(frame).toContain("5.0K");
|
|
595
|
+
expect(frame).toContain("10K");
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
it("should show inactive thinking", () => {
|
|
599
|
+
const { lastFrame } = renderWithTheme(
|
|
600
|
+
<StatusBar mode="vibe" modelName="claude-3" thinking={{ active: false }} />
|
|
601
|
+
);
|
|
602
|
+
const frame = lastFrame() ?? "";
|
|
603
|
+
expect(frame).toContain("◇"); // inactive thinking icon
|
|
604
|
+
});
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
describe("Mode Selector", () => {
|
|
608
|
+
it("should highlight active mode", () => {
|
|
609
|
+
const { lastFrame } = renderWithTheme(
|
|
610
|
+
<StatusBar mode="plan" modelName="claude-3" showAllModes={true} />
|
|
611
|
+
);
|
|
612
|
+
const frame = lastFrame() ?? "";
|
|
613
|
+
// All modes should be visible
|
|
614
|
+
expect(frame).toContain("vibe");
|
|
615
|
+
expect(frame).toContain("plan");
|
|
616
|
+
expect(frame).toContain("spec");
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it("should show spec mode correctly", () => {
|
|
620
|
+
const { lastFrame } = renderWithTheme(<StatusBar mode="spec" modelName="claude-3" />);
|
|
621
|
+
const frame = lastFrame() ?? "";
|
|
622
|
+
expect(frame).toContain("◒");
|
|
623
|
+
expect(frame).toContain("spec");
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
describe("Cost Display", () => {
|
|
628
|
+
it("should show cost when provided", () => {
|
|
629
|
+
const { lastFrame } = renderWithTheme(
|
|
630
|
+
<StatusBar mode="vibe" modelName="claude-3" cost={1.5} />
|
|
631
|
+
);
|
|
632
|
+
const frame = lastFrame() ?? "";
|
|
633
|
+
expect(frame).toContain("$1.50");
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
it("should format small costs correctly", () => {
|
|
637
|
+
const { lastFrame } = renderWithTheme(
|
|
638
|
+
<StatusBar mode="vibe" modelName="claude-3" cost={0.001} />
|
|
639
|
+
);
|
|
640
|
+
const frame = lastFrame() ?? "";
|
|
641
|
+
expect(frame).toContain("$0.0010");
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
// ===========================================================================
|
|
646
|
+
// T016, T017, T018: Mode-Agent Decoupling Tests
|
|
647
|
+
// ===========================================================================
|
|
648
|
+
|
|
649
|
+
describe("Mode-Agent Decoupling (T016-T018)", () => {
|
|
650
|
+
describe("T016: No Level Display", () => {
|
|
651
|
+
it("should render mode name only, no level indicators", () => {
|
|
652
|
+
const { lastFrame } = renderWithTheme(<StatusBar mode="spec" modelName="claude-3" />);
|
|
653
|
+
const frame = lastFrame() ?? "";
|
|
654
|
+
// Mode name should be visible
|
|
655
|
+
expect(frame).toContain("spec");
|
|
656
|
+
// Level indicators should NOT be visible
|
|
657
|
+
expect(frame).not.toContain("L0");
|
|
658
|
+
expect(frame).not.toContain("L1");
|
|
659
|
+
expect(frame).not.toContain("L2");
|
|
660
|
+
expect(frame).not.toContain("orchestrator");
|
|
661
|
+
expect(frame).not.toContain("workflow");
|
|
662
|
+
expect(frame).not.toContain("worker");
|
|
663
|
+
expect(frame).not.toContain("orch");
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
it("should NOT render level even when agentLevel prop is provided", () => {
|
|
667
|
+
const { lastFrame } = renderWithTheme(
|
|
668
|
+
<StatusBar mode="spec" modelName="claude-3" agentLevel={0} />
|
|
669
|
+
);
|
|
670
|
+
const frame = lastFrame() ?? "";
|
|
671
|
+
// Mode name should be visible
|
|
672
|
+
expect(frame).toContain("spec");
|
|
673
|
+
// Level indicators should NOT be visible
|
|
674
|
+
expect(frame).not.toContain("L0");
|
|
675
|
+
expect(frame).not.toContain("orch");
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
it("should render all modes without level indicators", () => {
|
|
679
|
+
for (const mode of ["vibe", "plan", "spec"] as const) {
|
|
680
|
+
const { lastFrame } = renderWithTheme(<StatusBar mode={mode} modelName="claude-3" />);
|
|
681
|
+
const frame = lastFrame() ?? "";
|
|
682
|
+
expect(frame).toContain(mode);
|
|
683
|
+
expect(frame).not.toMatch(/L[0-2]/);
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
describe("T017: Deprecation Warning", () => {
|
|
689
|
+
let consoleWarnSpy: ReturnType<typeof vi.spyOn>;
|
|
690
|
+
const originalNodeEnv = process.env.NODE_ENV;
|
|
691
|
+
|
|
692
|
+
beforeEach(() => {
|
|
693
|
+
consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
694
|
+
// Set to development mode for deprecation warnings
|
|
695
|
+
process.env.NODE_ENV = "development";
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
afterEach(() => {
|
|
699
|
+
consoleWarnSpy.mockRestore();
|
|
700
|
+
process.env.NODE_ENV = originalNodeEnv;
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it("should log deprecation warning when agentLevel prop is provided", () => {
|
|
704
|
+
renderWithTheme(<StatusBar mode="spec" modelName="claude-3" agentLevel={1} />);
|
|
705
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining("DEPRECATION WARNING"));
|
|
706
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining("agentLevel"));
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
it("should NOT log deprecation warning when agentLevel prop is not provided", () => {
|
|
710
|
+
renderWithTheme(<StatusBar mode="spec" modelName="claude-3" />);
|
|
711
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it("should log deprecation warning for all agentLevel values", () => {
|
|
715
|
+
for (const level of [0, 1, 2] as const) {
|
|
716
|
+
consoleWarnSpy.mockClear();
|
|
717
|
+
renderWithTheme(<StatusBar mode="spec" modelName="claude-3" agentLevel={level} />);
|
|
718
|
+
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
it("should NOT log deprecation warning in production mode", () => {
|
|
723
|
+
process.env.NODE_ENV = "production";
|
|
724
|
+
renderWithTheme(<StatusBar mode="spec" modelName="claude-3" agentLevel={1} />);
|
|
725
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
726
|
+
});
|
|
727
|
+
});
|
|
728
|
+
});
|
|
729
|
+
});
|