@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,667 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mode CLI Integration Tests (T042)
|
|
3
|
+
*
|
|
4
|
+
* Tests for:
|
|
5
|
+
* - T037: --mode CLI flag parsing
|
|
6
|
+
* - T038: --approval CLI flag parsing
|
|
7
|
+
* - T039: --sandbox CLI flag parsing
|
|
8
|
+
* - T040: --full-auto shortcut flag
|
|
9
|
+
* - T041: Mode slash commands (/mode, /vibe, /plan, /spec)
|
|
10
|
+
* - T014: Verify mode commands work with new structure
|
|
11
|
+
* - T015: Integration tests for mode switching with agent resolution
|
|
12
|
+
*
|
|
13
|
+
* @module cli/commands/__tests__/mode-cli.test
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
BuiltinAgentRegistry,
|
|
18
|
+
type ModeManager,
|
|
19
|
+
PLAN_AGENT,
|
|
20
|
+
SPEC_ORCHESTRATOR,
|
|
21
|
+
VIBE_AGENT,
|
|
22
|
+
} from "@vellum/core";
|
|
23
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
24
|
+
import {
|
|
25
|
+
getModeCommandsManager,
|
|
26
|
+
modeCommand,
|
|
27
|
+
modeSlashCommands,
|
|
28
|
+
planCommand,
|
|
29
|
+
setModeCommandsManager,
|
|
30
|
+
specCommand,
|
|
31
|
+
vibeCommand,
|
|
32
|
+
} from "../mode.js";
|
|
33
|
+
import type { CommandContext, ParsedArgs } from "../types.js";
|
|
34
|
+
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// Mock Helpers
|
|
37
|
+
// =============================================================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a mock CommandContext for testing.
|
|
41
|
+
*/
|
|
42
|
+
function createMockContext(
|
|
43
|
+
positional: (string | number | boolean)[] = [],
|
|
44
|
+
named: Record<string, string | number | boolean> = {}
|
|
45
|
+
): CommandContext {
|
|
46
|
+
const parsedArgs: ParsedArgs = {
|
|
47
|
+
raw: "",
|
|
48
|
+
command: "test",
|
|
49
|
+
positional,
|
|
50
|
+
named,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
parsedArgs,
|
|
55
|
+
session: {
|
|
56
|
+
id: "test-session",
|
|
57
|
+
provider: "anthropic",
|
|
58
|
+
cwd: "/test/cwd",
|
|
59
|
+
},
|
|
60
|
+
emit: vi.fn(),
|
|
61
|
+
credentials: {} as never,
|
|
62
|
+
toolRegistry: {} as never,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create a mock ModeManager for testing.
|
|
68
|
+
*/
|
|
69
|
+
function createMockModeManager(currentMode: "vibe" | "plan" | "spec" = "vibe"): ModeManager {
|
|
70
|
+
return {
|
|
71
|
+
getCurrentMode: vi.fn().mockReturnValue(currentMode),
|
|
72
|
+
getCurrentConfig: vi.fn().mockReturnValue({
|
|
73
|
+
codingMode: currentMode,
|
|
74
|
+
name: `${currentMode}-mode`,
|
|
75
|
+
description: `${currentMode} mode description`,
|
|
76
|
+
}),
|
|
77
|
+
switchMode: vi.fn().mockImplementation(async (mode, _options) => ({
|
|
78
|
+
success: true,
|
|
79
|
+
previousMode: currentMode,
|
|
80
|
+
currentMode: mode,
|
|
81
|
+
})),
|
|
82
|
+
on: vi.fn(),
|
|
83
|
+
off: vi.fn(),
|
|
84
|
+
once: vi.fn(),
|
|
85
|
+
emit: vi.fn(),
|
|
86
|
+
removeAllListeners: vi.fn(),
|
|
87
|
+
} as unknown as ModeManager;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// =============================================================================
|
|
91
|
+
// T037: --mode Flag Tests
|
|
92
|
+
// =============================================================================
|
|
93
|
+
|
|
94
|
+
describe("T037: --mode CLI Flag", () => {
|
|
95
|
+
it("should accept valid mode values", () => {
|
|
96
|
+
// Test validation by importing and calling parse functions
|
|
97
|
+
// The actual parsing is done by Commander.js, but we test the schema validation
|
|
98
|
+
const validModes = ["vibe", "plan", "spec"];
|
|
99
|
+
for (const mode of validModes) {
|
|
100
|
+
// These would pass Commander validation
|
|
101
|
+
expect(validModes).toContain(mode);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("should have default mode of vibe", () => {
|
|
106
|
+
// Default is set in Commander option definition
|
|
107
|
+
// This test documents the expected default
|
|
108
|
+
const defaultMode = "vibe";
|
|
109
|
+
expect(defaultMode).toBe("vibe");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should reject invalid mode values", () => {
|
|
113
|
+
const invalidModes = ["invalid", "fast", "slow", "code"];
|
|
114
|
+
const validModes = ["vibe", "plan", "spec"];
|
|
115
|
+
for (const mode of invalidModes) {
|
|
116
|
+
expect(validModes).not.toContain(mode);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// =============================================================================
|
|
122
|
+
// T038: --approval Flag Tests
|
|
123
|
+
// =============================================================================
|
|
124
|
+
|
|
125
|
+
describe("T038: --approval CLI Flag", () => {
|
|
126
|
+
it("should accept valid approval policy values", () => {
|
|
127
|
+
const validPolicies = ["suggest", "auto-edit", "on-request", "full-auto"];
|
|
128
|
+
for (const policy of validPolicies) {
|
|
129
|
+
expect(validPolicies).toContain(policy);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should reject invalid approval policy values", () => {
|
|
134
|
+
const invalidPolicies = ["auto", "manual", "never", "always"];
|
|
135
|
+
const validPolicies = ["suggest", "auto-edit", "on-request", "full-auto"];
|
|
136
|
+
for (const policy of invalidPolicies) {
|
|
137
|
+
expect(validPolicies).not.toContain(policy);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// =============================================================================
|
|
143
|
+
// T039: --sandbox Flag Tests
|
|
144
|
+
// =============================================================================
|
|
145
|
+
|
|
146
|
+
describe("T039: --sandbox CLI Flag", () => {
|
|
147
|
+
it("should accept valid sandbox policy values", () => {
|
|
148
|
+
const validPolicies = [
|
|
149
|
+
"workspace-read",
|
|
150
|
+
"workspace-write",
|
|
151
|
+
"cwd-read",
|
|
152
|
+
"cwd-write",
|
|
153
|
+
"full-access",
|
|
154
|
+
];
|
|
155
|
+
for (const policy of validPolicies) {
|
|
156
|
+
expect(validPolicies).toContain(policy);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should reject invalid sandbox policy values", () => {
|
|
161
|
+
const invalidPolicies = ["read", "write", "restricted", "unrestricted"];
|
|
162
|
+
const validPolicies = [
|
|
163
|
+
"workspace-read",
|
|
164
|
+
"workspace-write",
|
|
165
|
+
"cwd-read",
|
|
166
|
+
"cwd-write",
|
|
167
|
+
"full-access",
|
|
168
|
+
];
|
|
169
|
+
for (const policy of invalidPolicies) {
|
|
170
|
+
expect(validPolicies).not.toContain(policy);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// =============================================================================
|
|
176
|
+
// T040: --full-auto Shortcut Tests
|
|
177
|
+
// =============================================================================
|
|
178
|
+
|
|
179
|
+
describe("T040: --full-auto Shortcut Flag", () => {
|
|
180
|
+
it("should set mode to vibe when --full-auto is used", () => {
|
|
181
|
+
// This tests the logic that would be applied in index.tsx
|
|
182
|
+
const options = { fullAuto: true, mode: "plan" as "vibe" | "plan" | "spec" };
|
|
183
|
+
let effectiveMode: "vibe" | "plan" | "spec" = options.mode;
|
|
184
|
+
let effectiveApproval: string | undefined;
|
|
185
|
+
|
|
186
|
+
if (options.fullAuto) {
|
|
187
|
+
effectiveMode = "vibe";
|
|
188
|
+
effectiveApproval = "full-auto";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
expect(effectiveMode).toBe("vibe");
|
|
192
|
+
expect(effectiveApproval).toBe("full-auto");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should not override mode when --full-auto is not used", () => {
|
|
196
|
+
const options = { fullAuto: false, mode: "plan" as "vibe" | "plan" | "spec" };
|
|
197
|
+
let effectiveMode: "vibe" | "plan" | "spec" = options.mode;
|
|
198
|
+
let effectiveApproval: string | undefined;
|
|
199
|
+
|
|
200
|
+
if (options.fullAuto) {
|
|
201
|
+
effectiveMode = "vibe";
|
|
202
|
+
effectiveApproval = "full-auto";
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
expect(effectiveMode).toBe("plan");
|
|
206
|
+
expect(effectiveApproval).toBeUndefined();
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// =============================================================================
|
|
211
|
+
// T041: Mode Slash Commands Tests
|
|
212
|
+
// =============================================================================
|
|
213
|
+
|
|
214
|
+
describe("T041: Mode Slash Commands", () => {
|
|
215
|
+
beforeEach(() => {
|
|
216
|
+
setModeCommandsManager(null);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
afterEach(() => {
|
|
220
|
+
setModeCommandsManager(null);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe("Command Registration", () => {
|
|
224
|
+
it("should export all mode commands", () => {
|
|
225
|
+
expect(modeSlashCommands).toHaveLength(4);
|
|
226
|
+
expect(modeSlashCommands).toContain(modeCommand);
|
|
227
|
+
expect(modeSlashCommands).toContain(vibeCommand);
|
|
228
|
+
expect(modeSlashCommands).toContain(planCommand);
|
|
229
|
+
expect(modeSlashCommands).toContain(specCommand);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("should have correct command metadata", () => {
|
|
233
|
+
expect(modeCommand.name).toBe("mode");
|
|
234
|
+
expect(modeCommand.kind).toBe("builtin");
|
|
235
|
+
expect(modeCommand.category).toBe("workflow");
|
|
236
|
+
|
|
237
|
+
expect(vibeCommand.name).toBe("vibe");
|
|
238
|
+
expect(planCommand.name).toBe("plan");
|
|
239
|
+
expect(specCommand.name).toBe("spec");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("modeCommand should have aliases", () => {
|
|
243
|
+
expect(modeCommand.aliases).toContain("modes");
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe("/mode Command", () => {
|
|
248
|
+
it("should show mode info without ModeManager", async () => {
|
|
249
|
+
const ctx = createMockContext();
|
|
250
|
+
const result = await modeCommand.execute(ctx);
|
|
251
|
+
|
|
252
|
+
expect(result.kind).toBe("success");
|
|
253
|
+
if (result.kind === "success") {
|
|
254
|
+
expect(result.message).toContain("Coding Modes");
|
|
255
|
+
expect(result.message).toContain("vibe");
|
|
256
|
+
expect(result.message).toContain("plan");
|
|
257
|
+
expect(result.message).toContain("spec");
|
|
258
|
+
expect(result.message).toContain("not yet initialized");
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("should show current mode with ModeManager", async () => {
|
|
263
|
+
const mockManager = createMockModeManager("plan");
|
|
264
|
+
setModeCommandsManager(mockManager);
|
|
265
|
+
|
|
266
|
+
const ctx = createMockContext();
|
|
267
|
+
const result = await modeCommand.execute(ctx);
|
|
268
|
+
|
|
269
|
+
expect(result.kind).toBe("success");
|
|
270
|
+
if (result.kind === "success") {
|
|
271
|
+
expect(result.message).toContain("Current mode:");
|
|
272
|
+
expect(result.message).toContain("plan");
|
|
273
|
+
}
|
|
274
|
+
expect(mockManager.getCurrentMode).toHaveBeenCalled();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("should switch mode when argument provided", async () => {
|
|
278
|
+
const mockManager = createMockModeManager("vibe");
|
|
279
|
+
setModeCommandsManager(mockManager);
|
|
280
|
+
|
|
281
|
+
const ctx = createMockContext(["plan"]);
|
|
282
|
+
const result = await modeCommand.execute(ctx);
|
|
283
|
+
|
|
284
|
+
expect(result.kind).toBe("success");
|
|
285
|
+
expect(mockManager.switchMode).toHaveBeenCalledWith("plan", { skipConfirmation: false });
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it("should error on invalid mode argument", async () => {
|
|
289
|
+
const mockManager = createMockModeManager();
|
|
290
|
+
setModeCommandsManager(mockManager);
|
|
291
|
+
|
|
292
|
+
const ctx = createMockContext(["invalid"]);
|
|
293
|
+
const result = await modeCommand.execute(ctx);
|
|
294
|
+
|
|
295
|
+
expect(result.kind).toBe("error");
|
|
296
|
+
if (result.kind === "error") {
|
|
297
|
+
expect(result.message).toContain("Invalid mode");
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe("/vibe Command", () => {
|
|
303
|
+
it("should switch to vibe mode", async () => {
|
|
304
|
+
const mockManager = createMockModeManager("plan");
|
|
305
|
+
setModeCommandsManager(mockManager);
|
|
306
|
+
|
|
307
|
+
const ctx = createMockContext();
|
|
308
|
+
const result = await vibeCommand.execute(ctx);
|
|
309
|
+
|
|
310
|
+
expect(result.kind).toBe("success");
|
|
311
|
+
expect(mockManager.switchMode).toHaveBeenCalledWith("vibe", { skipConfirmation: false });
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("should report already in vibe mode", async () => {
|
|
315
|
+
const mockManager = createMockModeManager("vibe");
|
|
316
|
+
setModeCommandsManager(mockManager);
|
|
317
|
+
|
|
318
|
+
const ctx = createMockContext();
|
|
319
|
+
const result = await vibeCommand.execute(ctx);
|
|
320
|
+
|
|
321
|
+
expect(result.kind).toBe("success");
|
|
322
|
+
if (result.kind === "success") {
|
|
323
|
+
expect(result.message).toContain("Already in");
|
|
324
|
+
expect(result.message).toContain("vibe");
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
describe("/plan Command", () => {
|
|
330
|
+
it("should switch to plan mode", async () => {
|
|
331
|
+
const mockManager = createMockModeManager("vibe");
|
|
332
|
+
setModeCommandsManager(mockManager);
|
|
333
|
+
|
|
334
|
+
const ctx = createMockContext();
|
|
335
|
+
const result = await planCommand.execute(ctx);
|
|
336
|
+
|
|
337
|
+
expect(result.kind).toBe("success");
|
|
338
|
+
expect(mockManager.switchMode).toHaveBeenCalledWith("plan", { skipConfirmation: false });
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
describe("/spec Command", () => {
|
|
343
|
+
it("should require confirmation for spec mode", async () => {
|
|
344
|
+
const mockManager = createMockModeManager("vibe");
|
|
345
|
+
setModeCommandsManager(mockManager);
|
|
346
|
+
|
|
347
|
+
const ctx = createMockContext();
|
|
348
|
+
const result = await specCommand.execute(ctx);
|
|
349
|
+
|
|
350
|
+
// Spec mode requires confirmation - returns interactive result
|
|
351
|
+
expect(result.kind).toBe("interactive");
|
|
352
|
+
if (result.kind === "interactive") {
|
|
353
|
+
expect(result.prompt.message).toContain("spec mode");
|
|
354
|
+
expect(result.prompt.inputType).toBe("confirm");
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it("should switch after confirmation", async () => {
|
|
359
|
+
const mockManager = createMockModeManager("vibe");
|
|
360
|
+
setModeCommandsManager(mockManager);
|
|
361
|
+
|
|
362
|
+
const ctx = createMockContext();
|
|
363
|
+
const result = await specCommand.execute(ctx);
|
|
364
|
+
|
|
365
|
+
expect(result.kind).toBe("interactive");
|
|
366
|
+
if (result.kind === "interactive" && result.prompt.handler) {
|
|
367
|
+
// Simulate user confirming
|
|
368
|
+
const confirmResult = await result.prompt.handler("y");
|
|
369
|
+
expect(confirmResult.kind).toBe("success");
|
|
370
|
+
expect(mockManager.switchMode).toHaveBeenCalledWith("spec", { skipConfirmation: true });
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("should cancel on rejection", async () => {
|
|
375
|
+
const mockManager = createMockModeManager("vibe");
|
|
376
|
+
setModeCommandsManager(mockManager);
|
|
377
|
+
|
|
378
|
+
const ctx = createMockContext();
|
|
379
|
+
const result = await specCommand.execute(ctx);
|
|
380
|
+
|
|
381
|
+
expect(result.kind).toBe("interactive");
|
|
382
|
+
if (result.kind === "interactive" && result.prompt.handler) {
|
|
383
|
+
// Simulate user rejecting
|
|
384
|
+
const cancelResult = await result.prompt.handler("n");
|
|
385
|
+
expect(cancelResult.kind).toBe("success");
|
|
386
|
+
if (cancelResult.kind === "success") {
|
|
387
|
+
expect(cancelResult.message).toContain("cancelled");
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
describe("ModeManager Integration", () => {
|
|
394
|
+
it("should set and get ModeManager", () => {
|
|
395
|
+
expect(getModeCommandsManager()).toBeNull();
|
|
396
|
+
|
|
397
|
+
const mockManager = createMockModeManager();
|
|
398
|
+
setModeCommandsManager(mockManager);
|
|
399
|
+
expect(getModeCommandsManager()).toBe(mockManager);
|
|
400
|
+
|
|
401
|
+
setModeCommandsManager(null);
|
|
402
|
+
expect(getModeCommandsManager()).toBeNull();
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it("should handle switch failure gracefully", async () => {
|
|
406
|
+
const mockManager = createMockModeManager();
|
|
407
|
+
(mockManager.switchMode as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
408
|
+
success: false,
|
|
409
|
+
reason: "Active operation in progress",
|
|
410
|
+
});
|
|
411
|
+
setModeCommandsManager(mockManager);
|
|
412
|
+
|
|
413
|
+
const ctx = createMockContext();
|
|
414
|
+
// Calling vibeCommand when already in vibe mode returns "Already in" message
|
|
415
|
+
// Let's test from plan mode
|
|
416
|
+
const mockManager2 = createMockModeManager("plan");
|
|
417
|
+
(mockManager2.switchMode as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
418
|
+
success: false,
|
|
419
|
+
reason: "Active operation in progress",
|
|
420
|
+
});
|
|
421
|
+
setModeCommandsManager(mockManager2);
|
|
422
|
+
|
|
423
|
+
const result2 = await vibeCommand.execute(ctx);
|
|
424
|
+
expect(result2.kind).toBe("error");
|
|
425
|
+
if (result2.kind === "error") {
|
|
426
|
+
expect(result2.message).toContain("Active operation");
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// =============================================================================
|
|
433
|
+
// Integration Tests
|
|
434
|
+
// =============================================================================
|
|
435
|
+
|
|
436
|
+
describe("CLI Mode Integration", () => {
|
|
437
|
+
it("should work end-to-end with valid inputs", async () => {
|
|
438
|
+
const mockManager = createMockModeManager("vibe");
|
|
439
|
+
setModeCommandsManager(mockManager);
|
|
440
|
+
|
|
441
|
+
// Simulate full flow: /mode -> see options -> /plan -> switch
|
|
442
|
+
const modeCtx = createMockContext();
|
|
443
|
+
const modeResult = await modeCommand.execute(modeCtx);
|
|
444
|
+
expect(modeResult.kind).toBe("success");
|
|
445
|
+
|
|
446
|
+
const planCtx = createMockContext();
|
|
447
|
+
const planResult = await planCommand.execute(planCtx);
|
|
448
|
+
expect(planResult.kind).toBe("success");
|
|
449
|
+
expect(mockManager.switchMode).toHaveBeenCalledWith("plan", { skipConfirmation: false });
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it("mode commands should have proper descriptions", () => {
|
|
453
|
+
for (const cmd of modeSlashCommands) {
|
|
454
|
+
expect(cmd.description).toBeTruthy();
|
|
455
|
+
expect(cmd.description.length).toBeGreaterThan(10);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it("mode commands should have examples", () => {
|
|
460
|
+
expect(modeCommand.examples).toBeDefined();
|
|
461
|
+
expect(modeCommand.examples?.length).toBeGreaterThan(0);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// =============================================================================
|
|
466
|
+
// T014: Verify Mode Commands Work with New Structure
|
|
467
|
+
// =============================================================================
|
|
468
|
+
|
|
469
|
+
describe("T014: Mode Commands with New Agent Structure", () => {
|
|
470
|
+
beforeEach(() => {
|
|
471
|
+
setModeCommandsManager(null);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
afterEach(() => {
|
|
475
|
+
setModeCommandsManager(null);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
describe("/vibe switches to VIBE_MODE", () => {
|
|
479
|
+
it("should switch to vibe mode successfully", async () => {
|
|
480
|
+
const mockManager = createMockModeManager("plan");
|
|
481
|
+
setModeCommandsManager(mockManager);
|
|
482
|
+
|
|
483
|
+
const ctx = createMockContext();
|
|
484
|
+
const result = await vibeCommand.execute(ctx);
|
|
485
|
+
|
|
486
|
+
expect(result.kind).toBe("success");
|
|
487
|
+
expect(mockManager.switchMode).toHaveBeenCalledWith("vibe", { skipConfirmation: false });
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
describe("/plan switches to PLAN_MODE", () => {
|
|
492
|
+
it("should switch to plan mode successfully", async () => {
|
|
493
|
+
const mockManager = createMockModeManager("vibe");
|
|
494
|
+
setModeCommandsManager(mockManager);
|
|
495
|
+
|
|
496
|
+
const ctx = createMockContext();
|
|
497
|
+
const result = await planCommand.execute(ctx);
|
|
498
|
+
|
|
499
|
+
expect(result.kind).toBe("success");
|
|
500
|
+
expect(mockManager.switchMode).toHaveBeenCalledWith("plan", { skipConfirmation: false });
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
describe("/spec switches to SPEC_MODE", () => {
|
|
505
|
+
it("should request confirmation for spec mode", async () => {
|
|
506
|
+
const mockManager = createMockModeManager("vibe");
|
|
507
|
+
setModeCommandsManager(mockManager);
|
|
508
|
+
|
|
509
|
+
const ctx = createMockContext();
|
|
510
|
+
const result = await specCommand.execute(ctx);
|
|
511
|
+
|
|
512
|
+
expect(result.kind).toBe("interactive");
|
|
513
|
+
if (result.kind === "interactive") {
|
|
514
|
+
expect(result.prompt.inputType).toBe("confirm");
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it("should switch to spec mode after confirmation", async () => {
|
|
519
|
+
const mockManager = createMockModeManager("vibe");
|
|
520
|
+
setModeCommandsManager(mockManager);
|
|
521
|
+
|
|
522
|
+
const ctx = createMockContext();
|
|
523
|
+
const result = await specCommand.execute(ctx);
|
|
524
|
+
|
|
525
|
+
if (result.kind === "interactive" && result.prompt.handler) {
|
|
526
|
+
const confirmResult = await result.prompt.handler("y");
|
|
527
|
+
expect(confirmResult.kind).toBe("success");
|
|
528
|
+
expect(mockManager.switchMode).toHaveBeenCalledWith("spec", { skipConfirmation: true });
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
describe("legacy mode names still work", () => {
|
|
534
|
+
it("should reject legacy mode 'code' (not a valid new mode)", async () => {
|
|
535
|
+
const mockManager = createMockModeManager("vibe");
|
|
536
|
+
setModeCommandsManager(mockManager);
|
|
537
|
+
|
|
538
|
+
// 'code' is not in CODING_MODES, so it should fail
|
|
539
|
+
const ctx = createMockContext(["code"]);
|
|
540
|
+
const result = await modeCommand.execute(ctx);
|
|
541
|
+
|
|
542
|
+
expect(result.kind).toBe("error");
|
|
543
|
+
if (result.kind === "error") {
|
|
544
|
+
expect(result.message).toContain("Invalid mode");
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it("should accept valid new mode names", async () => {
|
|
549
|
+
const mockManager = createMockModeManager("vibe");
|
|
550
|
+
setModeCommandsManager(mockManager);
|
|
551
|
+
|
|
552
|
+
for (const mode of ["vibe", "plan"]) {
|
|
553
|
+
const ctx = createMockContext([mode]);
|
|
554
|
+
const result = await modeCommand.execute(ctx);
|
|
555
|
+
expect(result.kind).toBe("success");
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
// =============================================================================
|
|
562
|
+
// T015: Integration Tests for Mode Switching with Agent Resolution
|
|
563
|
+
// =============================================================================
|
|
564
|
+
|
|
565
|
+
describe("T015: Mode Switching with Agent Resolution", () => {
|
|
566
|
+
beforeEach(() => {
|
|
567
|
+
// Reset and reinitialize registry for clean state
|
|
568
|
+
BuiltinAgentRegistry.getInstance().reset();
|
|
569
|
+
BuiltinAgentRegistry.getInstance().reinitialize();
|
|
570
|
+
setModeCommandsManager(null);
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
afterEach(() => {
|
|
574
|
+
setModeCommandsManager(null);
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
describe("/mode vibe activates VIBE_MODE", () => {
|
|
578
|
+
it("should activate vibe mode with vibe-agent", async () => {
|
|
579
|
+
const mockManager = createMockModeManager("plan");
|
|
580
|
+
setModeCommandsManager(mockManager);
|
|
581
|
+
|
|
582
|
+
const ctx = createMockContext(["vibe"]);
|
|
583
|
+
const result = await modeCommand.execute(ctx);
|
|
584
|
+
|
|
585
|
+
expect(result.kind).toBe("success");
|
|
586
|
+
expect(mockManager.switchMode).toHaveBeenCalledWith("vibe", { skipConfirmation: false });
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
describe("/mode plan activates PLAN_MODE", () => {
|
|
591
|
+
it("should activate plan mode with plan-agent", async () => {
|
|
592
|
+
const mockManager = createMockModeManager("vibe");
|
|
593
|
+
setModeCommandsManager(mockManager);
|
|
594
|
+
|
|
595
|
+
const ctx = createMockContext(["plan"]);
|
|
596
|
+
const result = await modeCommand.execute(ctx);
|
|
597
|
+
|
|
598
|
+
expect(result.kind).toBe("success");
|
|
599
|
+
expect(mockManager.switchMode).toHaveBeenCalledWith("plan", { skipConfirmation: false });
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
describe("/mode spec activates SPEC_MODE", () => {
|
|
604
|
+
it("should activate spec mode with spec-orchestrator (after confirmation)", async () => {
|
|
605
|
+
const mockManager = createMockModeManager("vibe");
|
|
606
|
+
setModeCommandsManager(mockManager);
|
|
607
|
+
|
|
608
|
+
const ctx = createMockContext(["spec"]);
|
|
609
|
+
const result = await modeCommand.execute(ctx);
|
|
610
|
+
|
|
611
|
+
// Spec mode requires confirmation
|
|
612
|
+
expect(result.kind).toBe("interactive");
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
describe("BuiltinAgentRegistry.get returns correct agent for mode", () => {
|
|
617
|
+
it("returns vibe-agent for vibe mode", () => {
|
|
618
|
+
const registry = BuiltinAgentRegistry.getInstance();
|
|
619
|
+
const agent = registry.get("vibe-agent");
|
|
620
|
+
|
|
621
|
+
expect(agent).toBeDefined();
|
|
622
|
+
expect(agent).toEqual(VIBE_AGENT);
|
|
623
|
+
expect(agent?.level).toBe(2);
|
|
624
|
+
expect(agent?.canSpawnAgents).toBe(false);
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
it("returns plan-agent for plan mode", () => {
|
|
628
|
+
const registry = BuiltinAgentRegistry.getInstance();
|
|
629
|
+
const agent = registry.get("plan-agent");
|
|
630
|
+
|
|
631
|
+
expect(agent).toBeDefined();
|
|
632
|
+
expect(agent).toEqual(PLAN_AGENT);
|
|
633
|
+
expect(agent?.level).toBe(1);
|
|
634
|
+
expect(agent?.canSpawnAgents).toBe(true);
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it("returns spec-orchestrator for spec mode", () => {
|
|
638
|
+
const registry = BuiltinAgentRegistry.getInstance();
|
|
639
|
+
const agent = registry.get("spec-orchestrator");
|
|
640
|
+
|
|
641
|
+
expect(agent).toBeDefined();
|
|
642
|
+
expect(agent).toEqual(SPEC_ORCHESTRATOR);
|
|
643
|
+
expect(agent?.level).toBe(0);
|
|
644
|
+
expect(agent?.canSpawnAgents).toBe(true);
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
it("returns undefined for unknown agent", () => {
|
|
648
|
+
const registry = BuiltinAgentRegistry.getInstance();
|
|
649
|
+
const agent = registry.get("unknown-agent");
|
|
650
|
+
|
|
651
|
+
expect(agent).toBeUndefined();
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
describe("legacy /mode code maps to vibe (via legacy-modes.ts)", () => {
|
|
656
|
+
it("legacy mode 'code' is not directly valid in mode commands", async () => {
|
|
657
|
+
const mockManager = createMockModeManager("vibe");
|
|
658
|
+
setModeCommandsManager(mockManager);
|
|
659
|
+
|
|
660
|
+
// Direct 'code' should fail - legacy mapping is done at a higher level
|
|
661
|
+
const ctx = createMockContext(["code"]);
|
|
662
|
+
const result = await modeCommand.execute(ctx);
|
|
663
|
+
|
|
664
|
+
expect(result.kind).toBe("error");
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
});
|