@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,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Agents Create Command (T021)
|
|
3
|
+
*
|
|
4
|
+
* Creates a new custom agent definition from a template.
|
|
5
|
+
*
|
|
6
|
+
* @module cli/commands/custom-agents/create
|
|
7
|
+
* @see REQ-019
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as fs from "node:fs/promises";
|
|
11
|
+
import * as os from "node:os";
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
import { isValidSlug, SLUG_PATTERN } from "@vellum/core";
|
|
14
|
+
import chalk from "chalk";
|
|
15
|
+
|
|
16
|
+
import type { CommandResult } from "../types.js";
|
|
17
|
+
import { error, interactive, success } from "../types.js";
|
|
18
|
+
import type { CreateOptions } from "./index.js";
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Templates
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Template types available for agent creation
|
|
26
|
+
*/
|
|
27
|
+
type TemplateType = "basic" | "advanced" | "orchestrator";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Basic agent template (minimal configuration)
|
|
31
|
+
*/
|
|
32
|
+
function getBasicTemplate(slug: string, name: string): string {
|
|
33
|
+
return `---
|
|
34
|
+
slug: ${slug}
|
|
35
|
+
name: "${name}"
|
|
36
|
+
mode: code
|
|
37
|
+
description: "Custom agent for specialized tasks"
|
|
38
|
+
icon: "🤖"
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
# ${name}
|
|
42
|
+
|
|
43
|
+
You are a helpful AI assistant.
|
|
44
|
+
|
|
45
|
+
## Instructions
|
|
46
|
+
|
|
47
|
+
Follow these guidelines when assisting users:
|
|
48
|
+
- Be concise and clear
|
|
49
|
+
- Ask for clarification when needed
|
|
50
|
+
- Provide examples when helpful
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Advanced agent template (full configuration)
|
|
56
|
+
*/
|
|
57
|
+
function getAdvancedTemplate(slug: string, name: string): string {
|
|
58
|
+
return `---
|
|
59
|
+
slug: ${slug}
|
|
60
|
+
name: "${name}"
|
|
61
|
+
mode: code
|
|
62
|
+
description: "Advanced custom agent with full configuration"
|
|
63
|
+
icon: "⚡"
|
|
64
|
+
color: "#3b82f6"
|
|
65
|
+
version: "1.0.0"
|
|
66
|
+
author: "user"
|
|
67
|
+
tags:
|
|
68
|
+
- custom
|
|
69
|
+
- advanced
|
|
70
|
+
|
|
71
|
+
# Tool configuration
|
|
72
|
+
toolGroups:
|
|
73
|
+
- group: filesystem
|
|
74
|
+
enabled: true
|
|
75
|
+
- group: shell
|
|
76
|
+
enabled: true
|
|
77
|
+
|
|
78
|
+
# Restrictions
|
|
79
|
+
restrictions:
|
|
80
|
+
fileRestrictions:
|
|
81
|
+
- pattern: "src/**"
|
|
82
|
+
access: write
|
|
83
|
+
- pattern: "*.config.*"
|
|
84
|
+
access: read
|
|
85
|
+
maxTokens: 8192
|
|
86
|
+
timeout: 300000
|
|
87
|
+
|
|
88
|
+
# Runtime settings
|
|
89
|
+
settings:
|
|
90
|
+
temperature: 0.7
|
|
91
|
+
extendedThinking: false
|
|
92
|
+
streamOutput: true
|
|
93
|
+
autoConfirm: false
|
|
94
|
+
|
|
95
|
+
# When to suggest this agent
|
|
96
|
+
whenToUse:
|
|
97
|
+
description: "Use this agent for specialized coding tasks"
|
|
98
|
+
triggers:
|
|
99
|
+
- type: keyword
|
|
100
|
+
pattern: "implement|build|create"
|
|
101
|
+
priority: 10
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
# ${name}
|
|
105
|
+
|
|
106
|
+
You are a specialized AI assistant with advanced capabilities.
|
|
107
|
+
|
|
108
|
+
## Core Responsibilities
|
|
109
|
+
|
|
110
|
+
1. Analyze requirements carefully before implementation
|
|
111
|
+
2. Write clean, well-documented code
|
|
112
|
+
3. Follow best practices and project conventions
|
|
113
|
+
4. Test your implementations thoroughly
|
|
114
|
+
|
|
115
|
+
## Guidelines
|
|
116
|
+
|
|
117
|
+
- Always explain your approach before making changes
|
|
118
|
+
- Ask clarifying questions when requirements are ambiguous
|
|
119
|
+
- Consider edge cases and error handling
|
|
120
|
+
- Document complex logic with comments
|
|
121
|
+
|
|
122
|
+
## Constraints
|
|
123
|
+
|
|
124
|
+
- Only modify files within your allowed scope
|
|
125
|
+
- Prefer existing patterns found in the codebase
|
|
126
|
+
- Keep changes focused and minimal
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Orchestrator agent template (for multi-agent workflows)
|
|
132
|
+
*/
|
|
133
|
+
function getOrchestratorTemplate(slug: string, name: string): string {
|
|
134
|
+
return `---
|
|
135
|
+
slug: ${slug}
|
|
136
|
+
name: "${name}"
|
|
137
|
+
mode: plan
|
|
138
|
+
description: "Orchestrator agent for coordinating multi-agent workflows"
|
|
139
|
+
icon: "🎯"
|
|
140
|
+
color: "#8b5cf6"
|
|
141
|
+
version: "1.0.0"
|
|
142
|
+
level: orchestrator
|
|
143
|
+
|
|
144
|
+
# Multi-agent coordination
|
|
145
|
+
coordination:
|
|
146
|
+
canSpawnAgents:
|
|
147
|
+
- coder
|
|
148
|
+
- reviewer
|
|
149
|
+
- tester
|
|
150
|
+
maxConcurrentSubagents: 3
|
|
151
|
+
|
|
152
|
+
# Settings for orchestration
|
|
153
|
+
settings:
|
|
154
|
+
temperature: 0.5
|
|
155
|
+
extendedThinking: true
|
|
156
|
+
streamOutput: true
|
|
157
|
+
|
|
158
|
+
whenToUse:
|
|
159
|
+
description: "Use for complex tasks requiring multiple agents"
|
|
160
|
+
triggers:
|
|
161
|
+
- type: keyword
|
|
162
|
+
pattern: "complex|multi-step|orchestrate"
|
|
163
|
+
priority: 20
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
# ${name}
|
|
167
|
+
|
|
168
|
+
You are an orchestrator agent responsible for coordinating complex workflows.
|
|
169
|
+
|
|
170
|
+
## Your Role
|
|
171
|
+
|
|
172
|
+
As an orchestrator, you:
|
|
173
|
+
1. Break down complex tasks into subtasks
|
|
174
|
+
2. Delegate subtasks to specialized agents
|
|
175
|
+
3. Coordinate and synthesize results
|
|
176
|
+
4. Ensure overall task completion
|
|
177
|
+
|
|
178
|
+
## Workflow Strategy
|
|
179
|
+
|
|
180
|
+
1. **Analysis**: Understand the full scope of the request
|
|
181
|
+
2. **Planning**: Create a step-by-step execution plan
|
|
182
|
+
3. **Delegation**: Assign tasks to appropriate agents
|
|
183
|
+
4. **Monitoring**: Track progress and handle issues
|
|
184
|
+
5. **Synthesis**: Combine results into final output
|
|
185
|
+
|
|
186
|
+
## Available Agents
|
|
187
|
+
|
|
188
|
+
You can delegate to these specialized agents:
|
|
189
|
+
- \`coder\`: Implementation tasks
|
|
190
|
+
- \`reviewer\`: Code review and analysis
|
|
191
|
+
- \`tester\`: Testing and validation
|
|
192
|
+
|
|
193
|
+
## Constraints
|
|
194
|
+
|
|
195
|
+
- Always create a plan before delegating
|
|
196
|
+
- Monitor agent progress and intervene if needed
|
|
197
|
+
- Synthesize results before presenting to user
|
|
198
|
+
`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get template content by type
|
|
203
|
+
*/
|
|
204
|
+
function getTemplate(type: TemplateType, slug: string, name: string): string {
|
|
205
|
+
switch (type) {
|
|
206
|
+
case "advanced":
|
|
207
|
+
return getAdvancedTemplate(slug, name);
|
|
208
|
+
case "orchestrator":
|
|
209
|
+
return getOrchestratorTemplate(slug, name);
|
|
210
|
+
default:
|
|
211
|
+
return getBasicTemplate(slug, name);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// =============================================================================
|
|
216
|
+
// Helpers
|
|
217
|
+
// =============================================================================
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Convert slug to display name
|
|
221
|
+
*/
|
|
222
|
+
function slugToName(slug: string): string {
|
|
223
|
+
return slug
|
|
224
|
+
.split("-")
|
|
225
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
226
|
+
.join(" ");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Validate slug format
|
|
231
|
+
*/
|
|
232
|
+
function validateSlug(slug: string): { valid: boolean; message?: string } {
|
|
233
|
+
if (!slug || slug.trim().length === 0) {
|
|
234
|
+
return { valid: false, message: "Slug cannot be empty" };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (slug.length > 50) {
|
|
238
|
+
return { valid: false, message: "Slug must be 50 characters or less" };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (!isValidSlug(slug)) {
|
|
242
|
+
return {
|
|
243
|
+
valid: false,
|
|
244
|
+
message: `Slug must be lowercase alphanumeric with hyphens (pattern: ${SLUG_PATTERN.source})`,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return { valid: true };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Get agent file path
|
|
253
|
+
*/
|
|
254
|
+
function getAgentFilePath(slug: string, global: boolean): string {
|
|
255
|
+
const baseDir = global
|
|
256
|
+
? path.join(os.homedir(), ".vellum", "agents")
|
|
257
|
+
: path.join(process.cwd(), ".vellum", "agents");
|
|
258
|
+
|
|
259
|
+
return path.join(baseDir, `${slug}.md`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Check if file already exists
|
|
264
|
+
*/
|
|
265
|
+
async function fileExists(filePath: string): Promise<boolean> {
|
|
266
|
+
try {
|
|
267
|
+
await fs.access(filePath);
|
|
268
|
+
return true;
|
|
269
|
+
} catch {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Ensure directory exists
|
|
276
|
+
*/
|
|
277
|
+
async function ensureDir(dirPath: string): Promise<void> {
|
|
278
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// =============================================================================
|
|
282
|
+
// Command Handler
|
|
283
|
+
// =============================================================================
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Handle create subcommand
|
|
287
|
+
*
|
|
288
|
+
* Creates a new custom agent definition file from a template.
|
|
289
|
+
*
|
|
290
|
+
* @param slug - Agent slug (identifier)
|
|
291
|
+
* @param options - Create options
|
|
292
|
+
* @returns Command result
|
|
293
|
+
*/
|
|
294
|
+
export async function handleCreate(
|
|
295
|
+
slug: string | undefined,
|
|
296
|
+
options: CreateOptions = {}
|
|
297
|
+
): Promise<CommandResult> {
|
|
298
|
+
// If no slug provided, prompt for it (unless no-interactive)
|
|
299
|
+
if (!slug) {
|
|
300
|
+
if (options.noInteractive) {
|
|
301
|
+
return error("MISSING_ARGUMENT", "Agent slug is required", [
|
|
302
|
+
"Provide a slug: /custom-agents create <slug>",
|
|
303
|
+
]);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return interactive({
|
|
307
|
+
inputType: "text",
|
|
308
|
+
message: "Enter agent slug (lowercase, alphanumeric, hyphens):",
|
|
309
|
+
placeholder: "my-custom-agent",
|
|
310
|
+
handler: async (value) => {
|
|
311
|
+
return handleCreate(value, options);
|
|
312
|
+
},
|
|
313
|
+
onCancel: () => success("Agent creation cancelled"),
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Validate slug
|
|
318
|
+
const validation = validateSlug(slug);
|
|
319
|
+
if (!validation.valid) {
|
|
320
|
+
return error("INVALID_ARGUMENT", validation.message ?? "Invalid slug format", [
|
|
321
|
+
"Example valid slugs: my-agent, code-reviewer, test-helper",
|
|
322
|
+
]);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Parse template type
|
|
326
|
+
const templateType: TemplateType = (options.template?.toLowerCase() as TemplateType) || "basic";
|
|
327
|
+
|
|
328
|
+
if (!["basic", "advanced", "orchestrator"].includes(templateType)) {
|
|
329
|
+
return error("INVALID_ARGUMENT", `Unknown template: ${options.template}`, [
|
|
330
|
+
"Available templates: basic, advanced, orchestrator",
|
|
331
|
+
]);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Get file path
|
|
335
|
+
const filePath = getAgentFilePath(slug, options.global ?? false);
|
|
336
|
+
const dirPath = path.dirname(filePath);
|
|
337
|
+
|
|
338
|
+
// Check if file already exists
|
|
339
|
+
if (await fileExists(filePath)) {
|
|
340
|
+
return error("OPERATION_NOT_ALLOWED", `Agent "${slug}" already exists at: ${filePath}`, [
|
|
341
|
+
`Use a different slug, or delete the existing file first`,
|
|
342
|
+
]);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
// Ensure directory exists
|
|
347
|
+
await ensureDir(dirPath);
|
|
348
|
+
|
|
349
|
+
// Generate name from slug
|
|
350
|
+
const name = slugToName(slug);
|
|
351
|
+
|
|
352
|
+
// Get template content
|
|
353
|
+
const content = getTemplate(templateType, slug, name);
|
|
354
|
+
|
|
355
|
+
// Write file
|
|
356
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
357
|
+
|
|
358
|
+
// Success message
|
|
359
|
+
const scope = options.global ? "user" : "project";
|
|
360
|
+
const lines = [
|
|
361
|
+
chalk.green(`✅ Created agent "${slug}" (${scope} scope)`),
|
|
362
|
+
"",
|
|
363
|
+
chalk.gray(`File: ${filePath}`),
|
|
364
|
+
chalk.gray(`Template: ${templateType}`),
|
|
365
|
+
"",
|
|
366
|
+
chalk.cyan("Next steps:"),
|
|
367
|
+
chalk.gray(` 1. Edit the agent file to customize behavior`),
|
|
368
|
+
chalk.gray(` 2. Run: /custom-agents validate ${slug}`),
|
|
369
|
+
chalk.gray(` 3. Use: /mode ${slug}`),
|
|
370
|
+
];
|
|
371
|
+
|
|
372
|
+
return success(lines.join("\n"));
|
|
373
|
+
} catch (err) {
|
|
374
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
375
|
+
return error("INTERNAL_ERROR", `Failed to create agent: ${message}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Agents Export Command (T020a)
|
|
3
|
+
*
|
|
4
|
+
* Exports agent definition to a file.
|
|
5
|
+
*
|
|
6
|
+
* @module cli/commands/custom-agents/export
|
|
7
|
+
* @see REQ-022
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as fs from "node:fs/promises";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { AgentDiscovery, type CustomAgentDefinition } from "@vellum/core";
|
|
13
|
+
import chalk from "chalk";
|
|
14
|
+
import matter from "gray-matter";
|
|
15
|
+
|
|
16
|
+
import type { CommandResult } from "../types.js";
|
|
17
|
+
import { error, success } from "../types.js";
|
|
18
|
+
import type { ExportOptions } from "./index.js";
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Helpers
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Convert agent definition to YAML string using gray-matter stringify
|
|
26
|
+
*/
|
|
27
|
+
function toYaml(agent: CustomAgentDefinition): string {
|
|
28
|
+
// Create a clean object for export (remove undefined values)
|
|
29
|
+
const clean = JSON.parse(JSON.stringify(agent));
|
|
30
|
+
// Use gray-matter to stringify as YAML
|
|
31
|
+
return matter.stringify("", clean).trim();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Convert agent definition to JSON string
|
|
36
|
+
*/
|
|
37
|
+
function toJson(agent: CustomAgentDefinition): string {
|
|
38
|
+
return JSON.stringify(agent, null, 2);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Ensure directory exists
|
|
43
|
+
*/
|
|
44
|
+
async function ensureDir(dirPath: string): Promise<void> {
|
|
45
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// =============================================================================
|
|
49
|
+
// Command Handler
|
|
50
|
+
// =============================================================================
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Handle export subcommand
|
|
54
|
+
*
|
|
55
|
+
* Exports an agent definition to a file.
|
|
56
|
+
*
|
|
57
|
+
* @param slug - Agent slug to export
|
|
58
|
+
* @param options - Export options
|
|
59
|
+
* @returns Command result
|
|
60
|
+
*/
|
|
61
|
+
export async function handleExport(
|
|
62
|
+
slug: string | undefined,
|
|
63
|
+
options: ExportOptions = {}
|
|
64
|
+
): Promise<CommandResult> {
|
|
65
|
+
// Require slug
|
|
66
|
+
if (!slug) {
|
|
67
|
+
return error("MISSING_ARGUMENT", "Agent slug is required", [
|
|
68
|
+
"Usage: /custom-agents export <slug>",
|
|
69
|
+
"Use /custom-agents list to see available agents",
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
// Discover agents
|
|
75
|
+
const discovery = new AgentDiscovery({ watchEnabled: false });
|
|
76
|
+
await discovery.discover();
|
|
77
|
+
|
|
78
|
+
// Find agent
|
|
79
|
+
const agent = discovery.get(slug);
|
|
80
|
+
|
|
81
|
+
if (!agent) {
|
|
82
|
+
return error("RESOURCE_NOT_FOUND", `Agent not found: ${slug}`, [
|
|
83
|
+
"Check the slug is correct",
|
|
84
|
+
"Use /custom-agents list to see available agents",
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Determine format
|
|
89
|
+
const format = options.format ?? "yaml";
|
|
90
|
+
if (format !== "yaml" && format !== "json") {
|
|
91
|
+
return error("INVALID_ARGUMENT", `Invalid format: ${format}`, [
|
|
92
|
+
"Supported formats: yaml, json",
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Convert to string
|
|
97
|
+
const content = format === "json" ? toJson(agent.definition) : toYaml(agent.definition);
|
|
98
|
+
|
|
99
|
+
// If no output specified, print to stdout
|
|
100
|
+
if (!options.output) {
|
|
101
|
+
const lines = [
|
|
102
|
+
chalk.green(`📤 Exporting agent: ${slug}`),
|
|
103
|
+
chalk.gray(`Format: ${format}`),
|
|
104
|
+
chalk.gray(`Source: ${agent.sourcePath}`),
|
|
105
|
+
"",
|
|
106
|
+
chalk.gray("─".repeat(60)),
|
|
107
|
+
content,
|
|
108
|
+
chalk.gray("─".repeat(60)),
|
|
109
|
+
"",
|
|
110
|
+
chalk.cyan("Tip: Use --output to save to a file"),
|
|
111
|
+
];
|
|
112
|
+
return success(lines.join("\n"));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Write to file
|
|
116
|
+
const outputPath = path.resolve(options.output);
|
|
117
|
+
const dirPath = path.dirname(outputPath);
|
|
118
|
+
|
|
119
|
+
await ensureDir(dirPath);
|
|
120
|
+
await fs.writeFile(outputPath, content, "utf-8");
|
|
121
|
+
|
|
122
|
+
const lines = [
|
|
123
|
+
chalk.green(`✅ Exported agent "${slug}"`),
|
|
124
|
+
"",
|
|
125
|
+
chalk.gray(`Format: ${format}`),
|
|
126
|
+
chalk.gray(`Output: ${outputPath}`),
|
|
127
|
+
chalk.gray(`Source: ${agent.sourcePath}`),
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
return success(lines.join("\n"));
|
|
131
|
+
} catch (err) {
|
|
132
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
133
|
+
return error("INTERNAL_ERROR", `Failed to export agent: ${message}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Agents Import Command (T020b)
|
|
3
|
+
*
|
|
4
|
+
* Imports agent definition from a file.
|
|
5
|
+
*
|
|
6
|
+
* @module cli/commands/custom-agents/import
|
|
7
|
+
* @see REQ-022
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as fs from "node:fs/promises";
|
|
11
|
+
import * as os from "node:os";
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
import {
|
|
14
|
+
AgentDiscovery,
|
|
15
|
+
AgentLoader,
|
|
16
|
+
type CustomAgentDefinition,
|
|
17
|
+
validateAgentDefinition,
|
|
18
|
+
} from "@vellum/core";
|
|
19
|
+
import chalk from "chalk";
|
|
20
|
+
import matter from "gray-matter";
|
|
21
|
+
|
|
22
|
+
import type { CommandResult } from "../types.js";
|
|
23
|
+
import { error, interactive, success } from "../types.js";
|
|
24
|
+
import type { ImportOptions } from "./index.js";
|
|
25
|
+
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// Helpers
|
|
28
|
+
// =============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get agent file path for import destination
|
|
32
|
+
*/
|
|
33
|
+
function getAgentFilePath(slug: string, global: boolean): string {
|
|
34
|
+
const baseDir = global
|
|
35
|
+
? path.join(os.homedir(), ".vellum", "agents")
|
|
36
|
+
: path.join(process.cwd(), ".vellum", "agents");
|
|
37
|
+
|
|
38
|
+
return path.join(baseDir, `${slug}.md`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if file exists
|
|
43
|
+
*/
|
|
44
|
+
async function fileExists(filePath: string): Promise<boolean> {
|
|
45
|
+
try {
|
|
46
|
+
await fs.access(filePath);
|
|
47
|
+
return true;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Ensure directory exists
|
|
55
|
+
*/
|
|
56
|
+
async function ensureDir(dirPath: string): Promise<void> {
|
|
57
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Convert agent definition to Markdown with YAML frontmatter
|
|
62
|
+
*/
|
|
63
|
+
function toMarkdown(agent: CustomAgentDefinition): string {
|
|
64
|
+
// Extract systemPrompt as body content
|
|
65
|
+
const { systemPrompt, ...frontmatter } = agent;
|
|
66
|
+
|
|
67
|
+
// Use gray-matter to create markdown with frontmatter
|
|
68
|
+
const body =
|
|
69
|
+
systemPrompt ??
|
|
70
|
+
`# ${agent.name}
|
|
71
|
+
|
|
72
|
+
You are a helpful AI assistant.
|
|
73
|
+
|
|
74
|
+
## Instructions
|
|
75
|
+
|
|
76
|
+
Add your agent instructions here.`;
|
|
77
|
+
|
|
78
|
+
return matter.stringify(body, frontmatter);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// =============================================================================
|
|
82
|
+
// Command Handler
|
|
83
|
+
// =============================================================================
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Handle import subcommand
|
|
87
|
+
*
|
|
88
|
+
* Imports an agent definition from a file.
|
|
89
|
+
*
|
|
90
|
+
* @param options - Import options
|
|
91
|
+
* @returns Command result
|
|
92
|
+
*/
|
|
93
|
+
export async function handleImport(options: ImportOptions): Promise<CommandResult> {
|
|
94
|
+
// Require file path
|
|
95
|
+
if (!options.file) {
|
|
96
|
+
return error("MISSING_ARGUMENT", "Import file path is required", [
|
|
97
|
+
"Usage: /custom-agents import <file>",
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const filePath = path.resolve(options.file);
|
|
103
|
+
|
|
104
|
+
// Check file exists
|
|
105
|
+
if (!(await fileExists(filePath))) {
|
|
106
|
+
return error("FILE_NOT_FOUND", `File not found: ${filePath}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Load agent from file
|
|
110
|
+
const loader = new AgentLoader();
|
|
111
|
+
const loadResult = await loader.loadFile(filePath);
|
|
112
|
+
|
|
113
|
+
if (!loadResult.ok) {
|
|
114
|
+
return error("INVALID_ARGUMENT", `Failed to parse agent file: ${loadResult.error.message}`, [
|
|
115
|
+
"Ensure the file is valid YAML or Markdown with YAML frontmatter",
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const agent = loadResult.value;
|
|
120
|
+
|
|
121
|
+
// Validate agent definition
|
|
122
|
+
const validation = validateAgentDefinition(agent);
|
|
123
|
+
if (!validation.success) {
|
|
124
|
+
const issues = validation.error.issues
|
|
125
|
+
.map((i) => ` - ${i.path.join(".")}: ${i.message}`)
|
|
126
|
+
.join("\n");
|
|
127
|
+
|
|
128
|
+
return error("INVALID_ARGUMENT", `Agent validation failed:\n${issues}`, [
|
|
129
|
+
"Fix the validation errors and try again",
|
|
130
|
+
]);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check if agent already exists
|
|
134
|
+
const discovery = new AgentDiscovery({ watchEnabled: false });
|
|
135
|
+
await discovery.discover();
|
|
136
|
+
const existing = discovery.get(agent.slug);
|
|
137
|
+
|
|
138
|
+
if (existing) {
|
|
139
|
+
// Prompt for confirmation
|
|
140
|
+
return interactive({
|
|
141
|
+
inputType: "confirm",
|
|
142
|
+
message: `Agent "${agent.slug}" already exists. Overwrite?`,
|
|
143
|
+
handler: async (value) => {
|
|
144
|
+
if (value.toLowerCase() !== "yes" && value.toLowerCase() !== "y") {
|
|
145
|
+
return success(chalk.yellow("Import cancelled"));
|
|
146
|
+
}
|
|
147
|
+
return doImport(agent, options.global ?? false, filePath);
|
|
148
|
+
},
|
|
149
|
+
onCancel: () => success(chalk.yellow("Import cancelled")),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return doImport(agent, options.global ?? false, filePath);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
156
|
+
return error("INTERNAL_ERROR", `Failed to import agent: ${message}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Perform the actual import
|
|
162
|
+
*/
|
|
163
|
+
async function doImport(
|
|
164
|
+
agent: CustomAgentDefinition,
|
|
165
|
+
global: boolean,
|
|
166
|
+
sourceFile: string
|
|
167
|
+
): Promise<CommandResult> {
|
|
168
|
+
try {
|
|
169
|
+
// Get destination path
|
|
170
|
+
const destPath = getAgentFilePath(agent.slug, global);
|
|
171
|
+
const dirPath = path.dirname(destPath);
|
|
172
|
+
|
|
173
|
+
// Ensure directory exists
|
|
174
|
+
await ensureDir(dirPath);
|
|
175
|
+
|
|
176
|
+
// Convert to Markdown and write
|
|
177
|
+
const content = toMarkdown(agent);
|
|
178
|
+
await fs.writeFile(destPath, content, "utf-8");
|
|
179
|
+
|
|
180
|
+
// Success message
|
|
181
|
+
const scope = global ? "user" : "project";
|
|
182
|
+
const lines = [
|
|
183
|
+
chalk.green(`✅ Imported agent "${agent.slug}" (${scope} scope)`),
|
|
184
|
+
"",
|
|
185
|
+
chalk.gray(`Source: ${sourceFile}`),
|
|
186
|
+
chalk.gray(`Destination: ${destPath}`),
|
|
187
|
+
"",
|
|
188
|
+
chalk.cyan("Next steps:"),
|
|
189
|
+
chalk.gray(` 1. Review the imported agent: /custom-agents info ${agent.slug}`),
|
|
190
|
+
chalk.gray(` 2. Validate: /custom-agents validate ${agent.slug}`),
|
|
191
|
+
chalk.gray(` 3. Use: /mode ${agent.slug}`),
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
return success(lines.join("\n"));
|
|
195
|
+
} catch (err) {
|
|
196
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
197
|
+
return error("INTERNAL_ERROR", `Failed to write agent file: ${message}`);
|
|
198
|
+
}
|
|
199
|
+
}
|