@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,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mode Slash Commands (T041)
|
|
3
|
+
*
|
|
4
|
+
* Provides slash commands for coding mode management:
|
|
5
|
+
* - /mode - Show current mode and options
|
|
6
|
+
* - /vibe - Switch to vibe mode
|
|
7
|
+
* - /plan - Switch to plan mode
|
|
8
|
+
* - /spec - Switch to spec mode (with confirmation)
|
|
9
|
+
*
|
|
10
|
+
* @module cli/commands/mode
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
BUILTIN_CODING_MODES,
|
|
15
|
+
CODING_MODES,
|
|
16
|
+
type CodingMode,
|
|
17
|
+
type ModeManager,
|
|
18
|
+
} from "@vellum/core";
|
|
19
|
+
import { getModeIcon } from "../utils/icons.js";
|
|
20
|
+
import type { CommandContext, CommandResult, SlashCommand } from "./types.js";
|
|
21
|
+
import { error, interactive, success } from "./types.js";
|
|
22
|
+
|
|
23
|
+
// =============================================================================
|
|
24
|
+
// Module State
|
|
25
|
+
// =============================================================================
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Reference to the active ModeManager instance.
|
|
29
|
+
* Set by the App component when initialized.
|
|
30
|
+
*/
|
|
31
|
+
let modeManager: ModeManager | null = null;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Set the ModeManager instance for mode commands.
|
|
35
|
+
* Called by the App component during initialization.
|
|
36
|
+
*
|
|
37
|
+
* @param manager - The ModeManager instance to use
|
|
38
|
+
*/
|
|
39
|
+
export function setModeCommandsManager(manager: ModeManager | null): void {
|
|
40
|
+
modeManager = manager;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get the current ModeManager instance.
|
|
45
|
+
* Returns null if not yet initialized.
|
|
46
|
+
*/
|
|
47
|
+
export function getModeCommandsManager(): ModeManager | null {
|
|
48
|
+
return modeManager;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Mode Description Helpers
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get a human-readable description for a coding mode.
|
|
57
|
+
*/
|
|
58
|
+
function getModeDescription(mode: CodingMode): string {
|
|
59
|
+
const config = BUILTIN_CODING_MODES[mode];
|
|
60
|
+
return config?.description ?? mode;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get text-based icon indicator for a coding mode.
|
|
65
|
+
*/
|
|
66
|
+
function getModeIndicator(mode: CodingMode): string {
|
|
67
|
+
return getModeIcon(mode);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Format mode information for display.
|
|
72
|
+
*/
|
|
73
|
+
function formatModeInfo(mode: CodingMode, isCurrent: boolean): string {
|
|
74
|
+
const icon = getModeIndicator(mode);
|
|
75
|
+
const desc = getModeDescription(mode);
|
|
76
|
+
const marker = isCurrent ? " (current)" : "";
|
|
77
|
+
return ` ${icon} ${mode}${marker} - ${desc}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// /mode Command - Show Current Mode and Options
|
|
82
|
+
// =============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* /mode command - Display current mode and available options.
|
|
86
|
+
*
|
|
87
|
+
* Shows the current coding mode and lists all available modes
|
|
88
|
+
* with their descriptions. Without a ModeManager, shows a
|
|
89
|
+
* placeholder indicating the mode system is not initialized.
|
|
90
|
+
*/
|
|
91
|
+
export const modeCommand: SlashCommand = {
|
|
92
|
+
name: "mode",
|
|
93
|
+
description: "Show current coding mode and options",
|
|
94
|
+
kind: "builtin",
|
|
95
|
+
category: "workflow",
|
|
96
|
+
aliases: ["modes"],
|
|
97
|
+
positionalArgs: [
|
|
98
|
+
{
|
|
99
|
+
name: "mode",
|
|
100
|
+
type: "string",
|
|
101
|
+
description: "Mode to switch to (vibe, plan, spec)",
|
|
102
|
+
required: false,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
examples: [
|
|
106
|
+
"/mode - Show current mode and options",
|
|
107
|
+
"/mode vibe - Switch to vibe mode",
|
|
108
|
+
"/mode plan - Switch to plan mode",
|
|
109
|
+
"/mode spec - Switch to spec mode",
|
|
110
|
+
],
|
|
111
|
+
subcommands: [
|
|
112
|
+
{ name: "vibe", description: "Quick autonomous mode" },
|
|
113
|
+
{ name: "plan", description: "Plan before execute mode" },
|
|
114
|
+
{ name: "spec", description: "Full specification workflow" },
|
|
115
|
+
],
|
|
116
|
+
|
|
117
|
+
execute: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
118
|
+
const requestedMode = ctx.parsedArgs.positional[0] as string | undefined;
|
|
119
|
+
|
|
120
|
+
// If a mode is specified, delegate to switch logic
|
|
121
|
+
if (requestedMode) {
|
|
122
|
+
return switchToMode(requestedMode, ctx);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Show current mode and options
|
|
126
|
+
if (!modeManager) {
|
|
127
|
+
// No manager available - show static info
|
|
128
|
+
const lines = [
|
|
129
|
+
"Coding Modes",
|
|
130
|
+
"",
|
|
131
|
+
"Available modes:",
|
|
132
|
+
...CODING_MODES.map((m) => formatModeInfo(m, m === "vibe")),
|
|
133
|
+
"",
|
|
134
|
+
"Mode system not yet initialized. Using default: vibe",
|
|
135
|
+
"",
|
|
136
|
+
"Use /vibe, /plan, or /spec to switch modes.",
|
|
137
|
+
];
|
|
138
|
+
return success(lines.join("\n"));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const currentMode = modeManager.getCurrentMode();
|
|
142
|
+
const lines = [
|
|
143
|
+
"Coding Modes",
|
|
144
|
+
"",
|
|
145
|
+
`Current mode: ${getModeIndicator(currentMode)} ${currentMode}`,
|
|
146
|
+
"",
|
|
147
|
+
"Available modes:",
|
|
148
|
+
...CODING_MODES.map((m) => formatModeInfo(m, m === currentMode)),
|
|
149
|
+
"",
|
|
150
|
+
"Use /vibe, /plan, or /spec to switch modes.",
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
return success(lines.join("\n"));
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// =============================================================================
|
|
158
|
+
// Mode Switch Helper
|
|
159
|
+
// =============================================================================
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Switch to a specified mode with validation.
|
|
163
|
+
*
|
|
164
|
+
* @param mode - Mode name to switch to
|
|
165
|
+
* @param ctx - Command context
|
|
166
|
+
* @returns Command result
|
|
167
|
+
*/
|
|
168
|
+
async function switchToMode(mode: string, _ctx: CommandContext): Promise<CommandResult> {
|
|
169
|
+
// Validate mode name
|
|
170
|
+
if (!CODING_MODES.includes(mode as CodingMode)) {
|
|
171
|
+
return error("INVALID_ARGUMENT", `Invalid mode: ${mode}`, [
|
|
172
|
+
`Valid modes: ${CODING_MODES.join(", ")}`,
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const targetMode = mode as CodingMode;
|
|
177
|
+
|
|
178
|
+
// If no manager, return a message about it
|
|
179
|
+
if (!modeManager) {
|
|
180
|
+
return success(
|
|
181
|
+
`Mode system not initialized. Would switch to ${getModeIndicator(targetMode)} ${targetMode}.`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Check if already in this mode
|
|
186
|
+
const currentMode = modeManager.getCurrentMode();
|
|
187
|
+
if (currentMode === targetMode) {
|
|
188
|
+
return success(`Already in ${getModeIndicator(targetMode)} ${targetMode} mode.`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Spec mode requires confirmation
|
|
192
|
+
if (targetMode === "spec") {
|
|
193
|
+
return interactive({
|
|
194
|
+
inputType: "confirm",
|
|
195
|
+
message: `[WARN] Switch to spec mode? This enables a 6-phase structured workflow.`,
|
|
196
|
+
defaultValue: "n",
|
|
197
|
+
handler: async (value: string): Promise<CommandResult> => {
|
|
198
|
+
const confirmed = value.toLowerCase() === "y" || value.toLowerCase() === "yes";
|
|
199
|
+
if (confirmed) {
|
|
200
|
+
return await executeSwitch(targetMode);
|
|
201
|
+
}
|
|
202
|
+
return success("Mode switch cancelled.");
|
|
203
|
+
},
|
|
204
|
+
onCancel: () => success("Mode switch cancelled."),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Direct switch for vibe and plan modes
|
|
209
|
+
return await executeSwitch(targetMode);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Execute the actual mode switch via ModeManager.
|
|
214
|
+
*
|
|
215
|
+
* @param targetMode - Mode to switch to
|
|
216
|
+
* @returns Command result
|
|
217
|
+
*/
|
|
218
|
+
async function executeSwitch(targetMode: CodingMode): Promise<CommandResult> {
|
|
219
|
+
if (!modeManager) {
|
|
220
|
+
return error("OPERATION_NOT_ALLOWED", "Mode system not initialized", []);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const result = await modeManager.switchMode(targetMode, {
|
|
224
|
+
skipConfirmation: targetMode === "spec",
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (result.success) {
|
|
228
|
+
const icon = getModeIndicator(targetMode);
|
|
229
|
+
return success(`${icon} Switched to ${targetMode} mode.`, {
|
|
230
|
+
previousMode: result.previousMode,
|
|
231
|
+
currentMode: result.currentMode,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return error(
|
|
236
|
+
"OPERATION_NOT_ALLOWED",
|
|
237
|
+
`Cannot switch to ${targetMode}: ${result.reason ?? "Unknown error"}`,
|
|
238
|
+
[]
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// =============================================================================
|
|
243
|
+
// /vibe Command - Switch to Vibe Mode
|
|
244
|
+
// =============================================================================
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* /vibe command - Switch to vibe (fast autonomous) mode.
|
|
248
|
+
*
|
|
249
|
+
* Vibe mode enables fast, autonomous coding with full tool access.
|
|
250
|
+
* No checkpoints or confirmations required - the AI executes
|
|
251
|
+
* tasks directly.
|
|
252
|
+
*/
|
|
253
|
+
export const vibeCommand: SlashCommand = {
|
|
254
|
+
name: "vibe",
|
|
255
|
+
description: "Switch to vibe mode (fast autonomous coding)",
|
|
256
|
+
kind: "builtin",
|
|
257
|
+
category: "workflow",
|
|
258
|
+
aliases: [],
|
|
259
|
+
examples: ["/vibe - Switch to fast autonomous mode"],
|
|
260
|
+
|
|
261
|
+
execute: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
262
|
+
return switchToMode("vibe", ctx);
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// =============================================================================
|
|
267
|
+
// /plan Command - Switch to Plan Mode
|
|
268
|
+
// =============================================================================
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* /plan command - Switch to plan mode.
|
|
272
|
+
*
|
|
273
|
+
* Plan mode requires the AI to create a plan before execution.
|
|
274
|
+
* One checkpoint for plan approval, then execution proceeds.
|
|
275
|
+
*/
|
|
276
|
+
export const planCommand: SlashCommand = {
|
|
277
|
+
name: "plan",
|
|
278
|
+
description: "Switch to plan mode (plan-then-execute)",
|
|
279
|
+
kind: "builtin",
|
|
280
|
+
category: "workflow",
|
|
281
|
+
aliases: [],
|
|
282
|
+
examples: ["/plan - Switch to plan-then-execute mode"],
|
|
283
|
+
|
|
284
|
+
execute: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
285
|
+
return switchToMode("plan", ctx);
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// =============================================================================
|
|
290
|
+
// /spec Command - Switch to Spec Mode
|
|
291
|
+
// =============================================================================
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* /spec command - Switch to spec mode.
|
|
295
|
+
*
|
|
296
|
+
* Spec mode enables a 6-phase structured workflow:
|
|
297
|
+
* 1. Research
|
|
298
|
+
* 2. Requirements
|
|
299
|
+
* 3. Design
|
|
300
|
+
* 4. Tasks
|
|
301
|
+
* 5. Implementation
|
|
302
|
+
* 6. Validation
|
|
303
|
+
*
|
|
304
|
+
* Requires confirmation before switching due to its structured nature.
|
|
305
|
+
*/
|
|
306
|
+
export const specCommand: SlashCommand = {
|
|
307
|
+
name: "spec",
|
|
308
|
+
description: "Switch to spec mode (6-phase structured workflow)",
|
|
309
|
+
kind: "builtin",
|
|
310
|
+
category: "workflow",
|
|
311
|
+
aliases: [],
|
|
312
|
+
examples: ["/spec - Switch to 6-phase structured workflow"],
|
|
313
|
+
|
|
314
|
+
execute: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
315
|
+
return switchToMode("spec", ctx);
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// =============================================================================
|
|
320
|
+
// Export All Mode Commands
|
|
321
|
+
// =============================================================================
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* All mode-related slash commands for registration.
|
|
325
|
+
*/
|
|
326
|
+
export const modeSlashCommands: SlashCommand[] = [
|
|
327
|
+
modeCommand,
|
|
328
|
+
vibeCommand,
|
|
329
|
+
planCommand,
|
|
330
|
+
specCommand,
|
|
331
|
+
];
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Slash Command (Chain 22)
|
|
3
|
+
*
|
|
4
|
+
* Provides slash commands for AI model management:
|
|
5
|
+
* - /model - Show current model and options
|
|
6
|
+
* - /model <provider>/<model> - Switch to a specific model
|
|
7
|
+
*
|
|
8
|
+
* @module cli/commands/model
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
getModelInfo,
|
|
13
|
+
getProviderModels,
|
|
14
|
+
getSupportedProviders,
|
|
15
|
+
type ModelInfo,
|
|
16
|
+
} from "@vellum/provider";
|
|
17
|
+
import type { CommandContext, CommandResult, SlashCommand } from "./types.js";
|
|
18
|
+
import { error, success } from "./types.js";
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Module State
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Current model configuration.
|
|
26
|
+
* Set by the App component when initialized.
|
|
27
|
+
*/
|
|
28
|
+
interface ModelConfig {
|
|
29
|
+
provider: string;
|
|
30
|
+
model: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let currentConfig: ModelConfig | null = null;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Callback to update model selection.
|
|
37
|
+
*/
|
|
38
|
+
let onModelChange: ((provider: string, model: string) => void) | null = null;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Set the current model configuration.
|
|
42
|
+
*
|
|
43
|
+
* @param provider - Current provider
|
|
44
|
+
* @param model - Current model ID
|
|
45
|
+
* @param onChange - Callback when model changes
|
|
46
|
+
*/
|
|
47
|
+
export function setModelCommandConfig(
|
|
48
|
+
provider: string,
|
|
49
|
+
model: string,
|
|
50
|
+
onChange?: (provider: string, model: string) => void
|
|
51
|
+
): void {
|
|
52
|
+
currentConfig = { provider, model };
|
|
53
|
+
onModelChange = onChange ?? null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get the current model configuration.
|
|
58
|
+
*/
|
|
59
|
+
export function getModelCommandConfig(): ModelConfig | null {
|
|
60
|
+
return currentConfig;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// =============================================================================
|
|
64
|
+
// Helper Functions
|
|
65
|
+
// =============================================================================
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Format context window for display.
|
|
69
|
+
*/
|
|
70
|
+
function formatContextWindow(tokens: number): string {
|
|
71
|
+
if (tokens >= 1_000_000) {
|
|
72
|
+
return `${(tokens / 1_000_000).toFixed(1)}M`;
|
|
73
|
+
}
|
|
74
|
+
if (tokens >= 1_000) {
|
|
75
|
+
return `${(tokens / 1_000).toFixed(0)}K`;
|
|
76
|
+
}
|
|
77
|
+
return `${tokens}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Format price for display.
|
|
82
|
+
*/
|
|
83
|
+
function formatPrice(pricePer1M: number): string {
|
|
84
|
+
if (pricePer1M === 0) {
|
|
85
|
+
return "Free";
|
|
86
|
+
}
|
|
87
|
+
return `$${pricePer1M.toFixed(2)}/M`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Format model info for display.
|
|
92
|
+
*/
|
|
93
|
+
function formatModelInfo(_provider: string, model: ModelInfo, isCurrent: boolean): string {
|
|
94
|
+
const marker = isCurrent ? " (current)" : "";
|
|
95
|
+
const ctx = formatContextWindow(model.contextWindow);
|
|
96
|
+
const priceIn = formatPrice(model.inputPrice ?? 0);
|
|
97
|
+
const priceOut = formatPrice(model.outputPrice ?? 0);
|
|
98
|
+
return ` ${model.name}${marker} [${ctx} ctx, ${priceIn}/${priceOut}]`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// =============================================================================
|
|
102
|
+
// /model Command
|
|
103
|
+
// =============================================================================
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* /model command - Display current model and available options.
|
|
107
|
+
*
|
|
108
|
+
* Usage:
|
|
109
|
+
* - /model - Show current model and list all available models
|
|
110
|
+
* - /model <provider>/<model> - Switch to a specific model
|
|
111
|
+
*/
|
|
112
|
+
export const modelCommand: SlashCommand = {
|
|
113
|
+
name: "model",
|
|
114
|
+
description: "Show or change the current AI model",
|
|
115
|
+
kind: "builtin",
|
|
116
|
+
category: "system",
|
|
117
|
+
aliases: ["models"],
|
|
118
|
+
positionalArgs: [
|
|
119
|
+
{
|
|
120
|
+
name: "model",
|
|
121
|
+
type: "string",
|
|
122
|
+
description: "Model to switch to (format: provider/model-id)",
|
|
123
|
+
required: false,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
examples: [
|
|
127
|
+
"/model - Show current model and options",
|
|
128
|
+
"/model anthropic/claude-sonnet-4-20250514 - Switch to Claude Sonnet 4",
|
|
129
|
+
"/model openai/gpt-4o - Switch to GPT-4o",
|
|
130
|
+
],
|
|
131
|
+
// Dynamic subcommands: list all providers for autocomplete
|
|
132
|
+
subcommands: getSupportedProviders().map((provider) => ({
|
|
133
|
+
name: provider,
|
|
134
|
+
description: `${provider} models`,
|
|
135
|
+
})),
|
|
136
|
+
|
|
137
|
+
execute: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
138
|
+
const positional = ctx.parsedArgs.positional as (string | undefined)[];
|
|
139
|
+
|
|
140
|
+
let requestedModel: string | undefined;
|
|
141
|
+
|
|
142
|
+
if (positional[0] && positional[1]) {
|
|
143
|
+
// Two args: provider and model from autocomplete flow
|
|
144
|
+
requestedModel = `${positional[0]}/${positional[1]}`;
|
|
145
|
+
} else if (positional[0]?.includes("/")) {
|
|
146
|
+
// Single arg with / separator (backwards compatible)
|
|
147
|
+
requestedModel = positional[0];
|
|
148
|
+
} else if (positional[0]) {
|
|
149
|
+
// Provider only - show available models for that provider
|
|
150
|
+
return showProviderModels(positional[0]);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (requestedModel) {
|
|
154
|
+
return switchToModel(requestedModel);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Show current model and options
|
|
158
|
+
return showModelInfo();
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Show current model and available options.
|
|
164
|
+
*/
|
|
165
|
+
function showModelInfo(): CommandResult {
|
|
166
|
+
const providers = getSupportedProviders();
|
|
167
|
+
const lines: string[] = ["AI Models", ""];
|
|
168
|
+
|
|
169
|
+
// Show current model if configured
|
|
170
|
+
if (currentConfig) {
|
|
171
|
+
const info = getModelInfo(currentConfig.provider, currentConfig.model);
|
|
172
|
+
lines.push(`Current: ${currentConfig.provider}/${info.name}`);
|
|
173
|
+
lines.push(
|
|
174
|
+
` Context: ${formatContextWindow(info.contextWindow)} | ` +
|
|
175
|
+
`Input: ${formatPrice(info.inputPrice ?? 0)} | ` +
|
|
176
|
+
`Output: ${formatPrice(info.outputPrice ?? 0)}`
|
|
177
|
+
);
|
|
178
|
+
lines.push("");
|
|
179
|
+
} else {
|
|
180
|
+
lines.push("Current: Not configured");
|
|
181
|
+
lines.push("");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
lines.push("Available models:");
|
|
185
|
+
lines.push("");
|
|
186
|
+
|
|
187
|
+
// List all providers and their models
|
|
188
|
+
for (const provider of providers) {
|
|
189
|
+
const models = getProviderModels(provider);
|
|
190
|
+
|
|
191
|
+
if (models.length === 0) continue;
|
|
192
|
+
|
|
193
|
+
lines.push(` ${provider}`);
|
|
194
|
+
|
|
195
|
+
for (const model of models) {
|
|
196
|
+
const isCurrent = currentConfig?.provider === provider && currentConfig?.model === model.id;
|
|
197
|
+
lines.push(formatModelInfo(provider, model, isCurrent));
|
|
198
|
+
}
|
|
199
|
+
lines.push("");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
lines.push("Usage: /model <provider>/<model-id>");
|
|
203
|
+
|
|
204
|
+
return success(lines.join("\n"));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Show available models for a specific provider.
|
|
209
|
+
*/
|
|
210
|
+
function showProviderModels(provider: string): CommandResult {
|
|
211
|
+
const providers = getSupportedProviders();
|
|
212
|
+
const normalizedProvider = provider.toLowerCase();
|
|
213
|
+
|
|
214
|
+
if (!providers.includes(normalizedProvider)) {
|
|
215
|
+
return error("INVALID_ARGUMENT", `Unknown provider: ${provider}`, [
|
|
216
|
+
`Valid providers: ${providers.join(", ")}`,
|
|
217
|
+
]);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const models = getProviderModels(normalizedProvider);
|
|
221
|
+
|
|
222
|
+
if (models.length === 0) {
|
|
223
|
+
return error("INVALID_ARGUMENT", `No models available for ${provider}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const lines: string[] = [`${provider} Models`, ""];
|
|
227
|
+
|
|
228
|
+
for (const model of models) {
|
|
229
|
+
const isCurrent =
|
|
230
|
+
currentConfig?.provider === normalizedProvider && currentConfig?.model === model.id;
|
|
231
|
+
lines.push(formatModelInfo(normalizedProvider, model, isCurrent));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
lines.push("");
|
|
235
|
+
lines.push(`Usage: /model ${provider}/<model-id>`);
|
|
236
|
+
lines.push(`Example: /model ${provider}/${models[0]?.id ?? "model-id"}`);
|
|
237
|
+
|
|
238
|
+
return success(lines.join("\n"));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Switch to a specified model.
|
|
243
|
+
*/
|
|
244
|
+
function switchToModel(modelSpec: string): CommandResult {
|
|
245
|
+
// Parse provider/model format
|
|
246
|
+
const parts = modelSpec.split("/");
|
|
247
|
+
if (parts.length < 2) {
|
|
248
|
+
return error("INVALID_ARGUMENT", `Invalid model format: ${modelSpec}`, [
|
|
249
|
+
"Expected format: provider/model-id",
|
|
250
|
+
"Example: anthropic/claude-sonnet-4-20250514",
|
|
251
|
+
]);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const [provider, ...modelParts] = parts;
|
|
255
|
+
const modelId = modelParts.join("/"); // Handle models with / in name
|
|
256
|
+
|
|
257
|
+
if (!provider || !modelId) {
|
|
258
|
+
return error("INVALID_ARGUMENT", `Invalid model format: ${modelSpec}`, [
|
|
259
|
+
"Expected format: provider/model-id",
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Validate provider
|
|
264
|
+
const providers = getSupportedProviders();
|
|
265
|
+
if (!providers.includes(provider.toLowerCase())) {
|
|
266
|
+
return error("INVALID_ARGUMENT", `Unknown provider: ${provider}`, [
|
|
267
|
+
`Valid providers: ${providers.join(", ")}`,
|
|
268
|
+
]);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Get model info (validates model exists or returns default)
|
|
272
|
+
const info = getModelInfo(provider, modelId);
|
|
273
|
+
|
|
274
|
+
// Check if already using this model
|
|
275
|
+
if (currentConfig?.provider === provider.toLowerCase() && currentConfig?.model === modelId) {
|
|
276
|
+
return success(`Already using ${info.name}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Update via callback if available
|
|
280
|
+
if (onModelChange) {
|
|
281
|
+
onModelChange(provider.toLowerCase(), modelId);
|
|
282
|
+
return success(
|
|
283
|
+
`Switched to ${info.name}\n` +
|
|
284
|
+
` Context: ${formatContextWindow(info.contextWindow)} | ` +
|
|
285
|
+
`Input: ${formatPrice(info.inputPrice ?? 0)} | ` +
|
|
286
|
+
`Output: ${formatPrice(info.outputPrice ?? 0)}`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// No callback - just report what would happen
|
|
291
|
+
return success(`Would switch to ${info.name}\n(Model system not fully initialized)`);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// =============================================================================
|
|
295
|
+
// Exports
|
|
296
|
+
// =============================================================================
|
|
297
|
+
|
|
298
|
+
export default modelCommand;
|