@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,522 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tutorial Command
|
|
3
|
+
*
|
|
4
|
+
* CLI command for the interactive tutorial system.
|
|
5
|
+
*
|
|
6
|
+
* @module cli/commands/tutorial
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import {
|
|
11
|
+
ALL_LESSONS,
|
|
12
|
+
createTutorialStorage,
|
|
13
|
+
createTutorialSystem,
|
|
14
|
+
getLessonById,
|
|
15
|
+
type Lesson,
|
|
16
|
+
type ProgressStats,
|
|
17
|
+
type TutorialStep,
|
|
18
|
+
type TutorialSystem,
|
|
19
|
+
} from "../onboarding/index.js";
|
|
20
|
+
import { ICONS } from "../utils/icons.js";
|
|
21
|
+
import type { CommandContext, CommandResult, SlashCommand } from "./types.js";
|
|
22
|
+
import { error, success } from "./types.js";
|
|
23
|
+
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// Module State
|
|
26
|
+
// =============================================================================
|
|
27
|
+
|
|
28
|
+
let tutorialSystem: TutorialSystem | null = null;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Initialize tutorial system lazily
|
|
32
|
+
*/
|
|
33
|
+
async function getTutorialSystem(): Promise<TutorialSystem> {
|
|
34
|
+
if (!tutorialSystem) {
|
|
35
|
+
const storage = createTutorialStorage();
|
|
36
|
+
tutorialSystem = createTutorialSystem(storage);
|
|
37
|
+
await tutorialSystem.initialize();
|
|
38
|
+
}
|
|
39
|
+
return tutorialSystem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Set a custom tutorial system (for testing)
|
|
44
|
+
*/
|
|
45
|
+
export function setTutorialSystem(system: TutorialSystem | null): void {
|
|
46
|
+
tutorialSystem = system;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// Formatters
|
|
51
|
+
// =============================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Format lesson info for display
|
|
55
|
+
*/
|
|
56
|
+
function formatLesson(lesson: Lesson, completed: boolean, inProgress: boolean): string {
|
|
57
|
+
const icon = lesson.icon ?? "📖";
|
|
58
|
+
const status = completed ? chalk.green("✓") : inProgress ? chalk.yellow("▶") : chalk.dim("○");
|
|
59
|
+
const difficulty = formatDifficulty(lesson.difficulty);
|
|
60
|
+
const duration = chalk.dim(`${lesson.estimatedMinutes} min`);
|
|
61
|
+
|
|
62
|
+
return `${status} ${icon} ${chalk.bold(lesson.title)} ${difficulty} ${duration}
|
|
63
|
+
${chalk.dim(lesson.description)}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Format difficulty level
|
|
68
|
+
*/
|
|
69
|
+
function formatDifficulty(difficulty: string): string {
|
|
70
|
+
switch (difficulty) {
|
|
71
|
+
case "beginner":
|
|
72
|
+
return chalk.green("●○○");
|
|
73
|
+
case "intermediate":
|
|
74
|
+
return chalk.yellow("●●○");
|
|
75
|
+
case "advanced":
|
|
76
|
+
return chalk.red("●●●");
|
|
77
|
+
default:
|
|
78
|
+
return chalk.dim("○○○");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Format tutorial step
|
|
84
|
+
*/
|
|
85
|
+
function formatStep(step: TutorialStep, stepIndex: number, totalSteps: number): string {
|
|
86
|
+
const progress = chalk.dim(`[${stepIndex + 1}/${totalSteps}]`);
|
|
87
|
+
const lines: string[] = [];
|
|
88
|
+
|
|
89
|
+
lines.push("");
|
|
90
|
+
lines.push(`${progress} ${chalk.bold.cyan(step.title)}`);
|
|
91
|
+
lines.push("");
|
|
92
|
+
lines.push(step.content);
|
|
93
|
+
|
|
94
|
+
if (step.command) {
|
|
95
|
+
lines.push("");
|
|
96
|
+
lines.push(chalk.dim("Try: ") + chalk.cyan(step.command));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (step.hint) {
|
|
100
|
+
lines.push("");
|
|
101
|
+
lines.push(chalk.dim(`${ICONS.hint} ${step.hint}`));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (step.action === "complete") {
|
|
105
|
+
lines.push("");
|
|
106
|
+
lines.push(chalk.dim("Press Enter to continue..."));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return lines.join("\n");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Format progress stats
|
|
114
|
+
*/
|
|
115
|
+
function formatStats(stats: ProgressStats): string {
|
|
116
|
+
const percent = stats.completionPercent;
|
|
117
|
+
const barLength = 20;
|
|
118
|
+
const filled = Math.round((percent / 100) * barLength);
|
|
119
|
+
const bar = chalk.green("█".repeat(filled)) + chalk.dim("░".repeat(barLength - filled));
|
|
120
|
+
|
|
121
|
+
const lines: string[] = [
|
|
122
|
+
chalk.bold.blue("📊 Tutorial Progress"),
|
|
123
|
+
"",
|
|
124
|
+
`${bar} ${percent}%`,
|
|
125
|
+
"",
|
|
126
|
+
` Lessons: ${chalk.green(stats.completedLessons)}/${stats.totalLessons}`,
|
|
127
|
+
` Steps: ${chalk.green(stats.completedSteps)}/${stats.totalSteps}`,
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
if (stats.totalTimeMinutes > 0) {
|
|
131
|
+
lines.push(` Time: ${stats.totalTimeMinutes} min spent`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (stats.estimatedRemainingMinutes > 0 && stats.completionPercent < 100) {
|
|
135
|
+
lines.push(` Remaining: ~${stats.estimatedRemainingMinutes} min`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return lines.join("\n");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// =============================================================================
|
|
142
|
+
// Subcommand Handlers
|
|
143
|
+
// =============================================================================
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* List all available tutorials
|
|
147
|
+
*/
|
|
148
|
+
async function handleList(): Promise<CommandResult> {
|
|
149
|
+
const system = await getTutorialSystem();
|
|
150
|
+
const stats = await system.getStats();
|
|
151
|
+
const completedIds = (await system.getCompletedLessons()).map((l) => l.id);
|
|
152
|
+
const progress = await system.getProgress();
|
|
153
|
+
|
|
154
|
+
const lines: string[] = [
|
|
155
|
+
chalk.bold.blue("📚 Tutorial Lessons"),
|
|
156
|
+
"",
|
|
157
|
+
formatStats(stats),
|
|
158
|
+
"",
|
|
159
|
+
chalk.bold("Available Lessons:"),
|
|
160
|
+
"",
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
for (const lesson of ALL_LESSONS) {
|
|
164
|
+
const completed = completedIds.includes(lesson.id);
|
|
165
|
+
const inProgress = Boolean(progress.lessons[lesson.id]?.started && !completed);
|
|
166
|
+
lines.push(formatLesson(lesson, completed, inProgress));
|
|
167
|
+
lines.push("");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
lines.push(chalk.dim("Use /tutorial start <lesson-id> to begin a lesson"));
|
|
171
|
+
lines.push(chalk.dim("Use /tutorial next to start the next recommended lesson"));
|
|
172
|
+
|
|
173
|
+
return success(lines.join("\n"));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Start a specific lesson
|
|
178
|
+
*/
|
|
179
|
+
async function handleStart(lessonId: string): Promise<CommandResult> {
|
|
180
|
+
const system = await getTutorialSystem();
|
|
181
|
+
|
|
182
|
+
// Check if lesson exists
|
|
183
|
+
const lesson = getLessonById(lessonId);
|
|
184
|
+
if (!lesson) {
|
|
185
|
+
const available = ALL_LESSONS.map((l) => l.id).join(", ");
|
|
186
|
+
return error("RESOURCE_NOT_FOUND", `Lesson "${lessonId}" not found. Available: ${available}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
await system.start(lessonId);
|
|
191
|
+
const step = system.currentStep();
|
|
192
|
+
|
|
193
|
+
if (!step) {
|
|
194
|
+
return error("INTERNAL_ERROR", "Failed to start lesson");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const output = [
|
|
198
|
+
chalk.bold.green(`🎓 Starting: ${lesson.title}`),
|
|
199
|
+
formatStep(step, 0, lesson.steps.length),
|
|
200
|
+
].join("\n");
|
|
201
|
+
|
|
202
|
+
return success(output);
|
|
203
|
+
} catch (err) {
|
|
204
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
205
|
+
return error("OPERATION_NOT_ALLOWED", message);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Start the next recommended lesson
|
|
211
|
+
*/
|
|
212
|
+
async function handleNext(): Promise<CommandResult> {
|
|
213
|
+
const system = await getTutorialSystem();
|
|
214
|
+
|
|
215
|
+
const started = await system.startNext();
|
|
216
|
+
if (!started) {
|
|
217
|
+
// All complete or no available
|
|
218
|
+
const stats = await system.getStats();
|
|
219
|
+
if (stats.completionPercent === 100) {
|
|
220
|
+
return success(
|
|
221
|
+
chalk.green(`${ICONS.celebration} Congratulations! You've completed all tutorials!\n\n`) +
|
|
222
|
+
formatStats(stats)
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return error("OPERATION_NOT_ALLOWED", "No available lessons. Check prerequisites.");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const lesson = system.getCurrentLesson();
|
|
230
|
+
const step = system.currentStep();
|
|
231
|
+
|
|
232
|
+
if (!lesson || !step) {
|
|
233
|
+
return error("INTERNAL_ERROR", "Failed to start next lesson");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const output = [
|
|
237
|
+
chalk.bold.green(`[Tutorial] Starting: ${lesson.title}`),
|
|
238
|
+
formatStep(step, 0, lesson.steps.length),
|
|
239
|
+
].join("\n");
|
|
240
|
+
|
|
241
|
+
return success(output);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Continue to next step
|
|
246
|
+
*/
|
|
247
|
+
async function handleContinue(): Promise<CommandResult> {
|
|
248
|
+
const system = await getTutorialSystem();
|
|
249
|
+
|
|
250
|
+
if (!system.isLessonActive()) {
|
|
251
|
+
return error(
|
|
252
|
+
"OPERATION_NOT_ALLOWED",
|
|
253
|
+
"No lesson in progress. Use /tutorial start <lesson-id> or /tutorial next"
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const currentStep = system.currentStep();
|
|
258
|
+
if (!currentStep) {
|
|
259
|
+
return error("INTERNAL_ERROR", "No current step");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
const nextStep = await system.completeStep(currentStep.id);
|
|
264
|
+
|
|
265
|
+
if (!nextStep) {
|
|
266
|
+
// Lesson completed
|
|
267
|
+
const lesson = system.getCurrentLesson();
|
|
268
|
+
const stats = await system.getStats();
|
|
269
|
+
|
|
270
|
+
return success(
|
|
271
|
+
[
|
|
272
|
+
chalk.bold.green(`${ICONS.success} Lesson Complete: ${lesson?.title ?? "Unknown"}`),
|
|
273
|
+
"",
|
|
274
|
+
formatStats(stats),
|
|
275
|
+
"",
|
|
276
|
+
chalk.dim("Use /tutorial next for the next lesson"),
|
|
277
|
+
].join("\n")
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Show next step
|
|
282
|
+
const lesson = system.getCurrentLesson();
|
|
283
|
+
if (!lesson) {
|
|
284
|
+
return error("INTERNAL_ERROR", "No active lesson found");
|
|
285
|
+
}
|
|
286
|
+
const stepIndex = (await system.getCurrentLessonProgress())?.currentStepIndex ?? 0;
|
|
287
|
+
|
|
288
|
+
return success(formatStep(nextStep, stepIndex, lesson.steps.length));
|
|
289
|
+
} catch (err) {
|
|
290
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
291
|
+
return error("INTERNAL_ERROR", message);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Skip current step
|
|
297
|
+
*/
|
|
298
|
+
async function handleSkip(): Promise<CommandResult> {
|
|
299
|
+
const system = await getTutorialSystem();
|
|
300
|
+
|
|
301
|
+
if (!system.isLessonActive()) {
|
|
302
|
+
return error("OPERATION_NOT_ALLOWED", "No lesson in progress.");
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const nextStep = await system.skipStep();
|
|
306
|
+
|
|
307
|
+
if (!nextStep) {
|
|
308
|
+
const lesson = system.getCurrentLesson();
|
|
309
|
+
const stats = await system.getStats();
|
|
310
|
+
|
|
311
|
+
return success(
|
|
312
|
+
[
|
|
313
|
+
chalk.bold.yellow(`${ICONS.skip} Lesson Skipped: ${lesson?.title ?? "Unknown"}`),
|
|
314
|
+
"",
|
|
315
|
+
formatStats(stats),
|
|
316
|
+
"",
|
|
317
|
+
chalk.dim("Use /tutorial next for the next lesson"),
|
|
318
|
+
].join("\n")
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const lesson = system.getCurrentLesson();
|
|
323
|
+
if (!lesson) {
|
|
324
|
+
return error("INTERNAL_ERROR", "No active lesson found");
|
|
325
|
+
}
|
|
326
|
+
const stepIndex = (await system.getCurrentLessonProgress())?.currentStepIndex ?? 0;
|
|
327
|
+
|
|
328
|
+
return success(formatStep(nextStep, stepIndex, lesson.steps.length));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Show current status
|
|
333
|
+
*/
|
|
334
|
+
async function handleStatus(): Promise<CommandResult> {
|
|
335
|
+
const system = await getTutorialSystem();
|
|
336
|
+
const stats = await system.getStats();
|
|
337
|
+
const state = await system.getState();
|
|
338
|
+
|
|
339
|
+
const lines: string[] = [formatStats(stats), ""];
|
|
340
|
+
|
|
341
|
+
if (state.isActive && state.currentLesson) {
|
|
342
|
+
const stepIndex = state.currentStepIndex;
|
|
343
|
+
const totalSteps = state.currentLesson.steps.length;
|
|
344
|
+
const percent = Math.round((stepIndex / totalSteps) * 100);
|
|
345
|
+
|
|
346
|
+
lines.push(chalk.bold("Current Lesson:"));
|
|
347
|
+
lines.push(
|
|
348
|
+
` ${state.currentLesson.icon ?? "📖"} ${state.currentLesson.title} - Step ${stepIndex + 1}/${totalSteps} (${percent}%)`
|
|
349
|
+
);
|
|
350
|
+
} else {
|
|
351
|
+
lines.push(chalk.dim("No lesson in progress."));
|
|
352
|
+
lines.push(chalk.dim("Use /tutorial next to start learning!"));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return success(lines.join("\n"));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Reset tutorial progress
|
|
360
|
+
*/
|
|
361
|
+
async function handleReset(lessonId?: string): Promise<CommandResult> {
|
|
362
|
+
const system = await getTutorialSystem();
|
|
363
|
+
|
|
364
|
+
if (lessonId) {
|
|
365
|
+
const lesson = getLessonById(lessonId);
|
|
366
|
+
if (!lesson) {
|
|
367
|
+
return error("RESOURCE_NOT_FOUND", `Lesson "${lessonId}" not found.`);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
await system.resetLesson(lessonId);
|
|
371
|
+
return success(chalk.yellow(`${ICONS.reset} Reset progress for: ${lesson.title}`));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
await system.resetAll();
|
|
375
|
+
return success(chalk.yellow(`${ICONS.reset} All tutorial progress has been reset.`));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// =============================================================================
|
|
379
|
+
// Help
|
|
380
|
+
// =============================================================================
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Get help text
|
|
384
|
+
*/
|
|
385
|
+
function getHelp(): string {
|
|
386
|
+
return [
|
|
387
|
+
chalk.bold.blue("[Tutorial] Commands"),
|
|
388
|
+
"",
|
|
389
|
+
chalk.dim("Interactive tutorials to learn Vellum features."),
|
|
390
|
+
"",
|
|
391
|
+
chalk.bold("Commands:"),
|
|
392
|
+
"",
|
|
393
|
+
` ${chalk.cyan("/tutorial")} List all lessons`,
|
|
394
|
+
` ${chalk.cyan("/tutorial list")} List all lessons with progress`,
|
|
395
|
+
` ${chalk.cyan("/tutorial start <id>")} Start a specific lesson`,
|
|
396
|
+
` ${chalk.cyan("/tutorial next")} Start next recommended lesson`,
|
|
397
|
+
` ${chalk.cyan("/tutorial continue")} Continue to next step`,
|
|
398
|
+
` ${chalk.cyan("/tutorial skip")} Skip current step`,
|
|
399
|
+
` ${chalk.cyan("/tutorial status")} Show current progress`,
|
|
400
|
+
` ${chalk.cyan("/tutorial reset [id]")} Reset progress (all or specific)`,
|
|
401
|
+
"",
|
|
402
|
+
chalk.bold("Available Lessons:"),
|
|
403
|
+
"",
|
|
404
|
+
` ${chalk.cyan("basics")} Getting Started with Vellum`,
|
|
405
|
+
` ${chalk.cyan("tools")} Working with Tools`,
|
|
406
|
+
` ${chalk.cyan("modes")} Mastering Coding Modes`,
|
|
407
|
+
"",
|
|
408
|
+
chalk.bold("Examples:"),
|
|
409
|
+
"",
|
|
410
|
+
chalk.dim(" /tutorial start basics"),
|
|
411
|
+
chalk.dim(" /tutorial next"),
|
|
412
|
+
chalk.dim(" /tutorial continue"),
|
|
413
|
+
chalk.dim(" /tutorial reset basics"),
|
|
414
|
+
].join("\n");
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// =============================================================================
|
|
418
|
+
// Command Definition
|
|
419
|
+
// =============================================================================
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Tutorial slash command
|
|
423
|
+
*/
|
|
424
|
+
export const tutorialCommand: SlashCommand = {
|
|
425
|
+
name: "tutorial",
|
|
426
|
+
aliases: ["learn", "lessons"],
|
|
427
|
+
description: "Interactive tutorials to learn Vellum",
|
|
428
|
+
category: "workflow",
|
|
429
|
+
kind: "builtin",
|
|
430
|
+
positionalArgs: [
|
|
431
|
+
{
|
|
432
|
+
name: "subcommand",
|
|
433
|
+
type: "string",
|
|
434
|
+
description: "Subcommand: list, start, next, continue, skip, status, reset",
|
|
435
|
+
required: false,
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
name: "argument",
|
|
439
|
+
type: "string",
|
|
440
|
+
description: "Lesson ID for start/reset commands",
|
|
441
|
+
required: false,
|
|
442
|
+
},
|
|
443
|
+
],
|
|
444
|
+
examples: [
|
|
445
|
+
"/tutorial - List all lessons",
|
|
446
|
+
"/tutorial start basics - Start basics lesson",
|
|
447
|
+
"/tutorial next - Start next lesson",
|
|
448
|
+
"/tutorial continue - Continue current lesson",
|
|
449
|
+
"/tutorial skip - Skip current step",
|
|
450
|
+
"/tutorial status - Show progress",
|
|
451
|
+
"/tutorial reset - Reset all progress",
|
|
452
|
+
"/tutorial reset basics - Reset specific lesson",
|
|
453
|
+
],
|
|
454
|
+
subcommands: [
|
|
455
|
+
{ name: "start", description: "Start tutorial" },
|
|
456
|
+
{ name: "stop", description: "Stop tutorial" },
|
|
457
|
+
{ name: "next", description: "Next step" },
|
|
458
|
+
{ name: "prev", description: "Previous step" },
|
|
459
|
+
{ name: "status", description: "Show status" },
|
|
460
|
+
{ name: "help", description: "Show help" },
|
|
461
|
+
{ name: "demo", description: "Run demo" },
|
|
462
|
+
],
|
|
463
|
+
|
|
464
|
+
execute: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
465
|
+
const args = ctx.parsedArgs.positional as string[];
|
|
466
|
+
const subcommand = args[0]?.toLowerCase() ?? "list";
|
|
467
|
+
const argument = args[1];
|
|
468
|
+
|
|
469
|
+
switch (subcommand) {
|
|
470
|
+
case "list":
|
|
471
|
+
return handleList();
|
|
472
|
+
|
|
473
|
+
case "start":
|
|
474
|
+
if (!argument) {
|
|
475
|
+
return error(
|
|
476
|
+
"INVALID_ARGUMENT",
|
|
477
|
+
"Please specify a lesson ID. Use /tutorial list to see options."
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
return handleStart(argument);
|
|
481
|
+
|
|
482
|
+
case "next":
|
|
483
|
+
return handleNext();
|
|
484
|
+
|
|
485
|
+
case "continue":
|
|
486
|
+
case "c":
|
|
487
|
+
return handleContinue();
|
|
488
|
+
|
|
489
|
+
case "skip":
|
|
490
|
+
case "s":
|
|
491
|
+
return handleSkip();
|
|
492
|
+
|
|
493
|
+
case "status":
|
|
494
|
+
case "progress":
|
|
495
|
+
return handleStatus();
|
|
496
|
+
|
|
497
|
+
case "reset":
|
|
498
|
+
return handleReset(argument);
|
|
499
|
+
|
|
500
|
+
case "help":
|
|
501
|
+
case "--help":
|
|
502
|
+
case "-h":
|
|
503
|
+
return success(getHelp());
|
|
504
|
+
|
|
505
|
+
default:
|
|
506
|
+
// Maybe they provided a lesson ID directly
|
|
507
|
+
if (getLessonById(subcommand)) {
|
|
508
|
+
return handleStart(subcommand);
|
|
509
|
+
}
|
|
510
|
+
return error(
|
|
511
|
+
"INVALID_ARGUMENT",
|
|
512
|
+
`Unknown subcommand: ${subcommand}. Use /tutorial --help for usage.`
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
// =============================================================================
|
|
519
|
+
// Export
|
|
520
|
+
// =============================================================================
|
|
521
|
+
|
|
522
|
+
export { getTutorialSystem };
|