@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,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Agents List Command (T020)
|
|
3
|
+
*
|
|
4
|
+
* Lists custom agents grouped by scope (project/user/system).
|
|
5
|
+
*
|
|
6
|
+
* @module cli/commands/custom-agents/list
|
|
7
|
+
* @see REQ-018
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
AgentDiscovery,
|
|
12
|
+
type CustomAgentDefinition,
|
|
13
|
+
type DiscoveredAgent,
|
|
14
|
+
DiscoverySource,
|
|
15
|
+
} from "@vellum/core";
|
|
16
|
+
import chalk from "chalk";
|
|
17
|
+
|
|
18
|
+
import type { CommandResult } from "../types.js";
|
|
19
|
+
import { error, success } from "../types.js";
|
|
20
|
+
import type { ListOptions } from "./index.js";
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// Types
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Scope category for agent grouping
|
|
28
|
+
*/
|
|
29
|
+
type ScopeCategory = "project" | "user" | "system";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Agent entry with source info for display
|
|
33
|
+
*/
|
|
34
|
+
interface AgentEntry {
|
|
35
|
+
agent: CustomAgentDefinition;
|
|
36
|
+
sourcePath: string;
|
|
37
|
+
source: DiscoverySource;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Grouped agents by scope
|
|
42
|
+
*/
|
|
43
|
+
interface GroupedAgents {
|
|
44
|
+
project: AgentEntry[];
|
|
45
|
+
user: AgentEntry[];
|
|
46
|
+
system: AgentEntry[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* JSON output format for list command
|
|
51
|
+
*/
|
|
52
|
+
export interface ListJsonOutput {
|
|
53
|
+
success: boolean;
|
|
54
|
+
total: number;
|
|
55
|
+
agents: {
|
|
56
|
+
project: AgentSummary[];
|
|
57
|
+
user: AgentSummary[];
|
|
58
|
+
system: AgentSummary[];
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Agent summary for JSON output
|
|
64
|
+
*/
|
|
65
|
+
interface AgentSummary {
|
|
66
|
+
slug: string;
|
|
67
|
+
name: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
mode?: string;
|
|
70
|
+
icon?: string;
|
|
71
|
+
tags?: string[];
|
|
72
|
+
sourcePath: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// Helpers
|
|
77
|
+
// =============================================================================
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Map DiscoverySource to scope category
|
|
81
|
+
*/
|
|
82
|
+
function sourceToScope(source: DiscoverySource): ScopeCategory {
|
|
83
|
+
switch (source) {
|
|
84
|
+
case DiscoverySource.PROJECT:
|
|
85
|
+
return "project";
|
|
86
|
+
case DiscoverySource.USER:
|
|
87
|
+
return "user";
|
|
88
|
+
default:
|
|
89
|
+
return "system";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Group agents by scope
|
|
95
|
+
*/
|
|
96
|
+
function groupAgentsByScope(agents: Map<string, DiscoveredAgent>): GroupedAgents {
|
|
97
|
+
const grouped: GroupedAgents = {
|
|
98
|
+
project: [],
|
|
99
|
+
user: [],
|
|
100
|
+
system: [],
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
for (const [, discovered] of agents) {
|
|
104
|
+
const scope = sourceToScope(discovered.source);
|
|
105
|
+
grouped[scope].push({
|
|
106
|
+
agent: discovered.definition,
|
|
107
|
+
sourcePath: discovered.sourcePath,
|
|
108
|
+
source: discovered.source,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Sort each group alphabetically by slug
|
|
113
|
+
for (const scope of Object.keys(grouped) as ScopeCategory[]) {
|
|
114
|
+
grouped[scope].sort((a, b) => a.agent.slug.localeCompare(b.agent.slug));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return grouped;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Format agent entry for display
|
|
122
|
+
*/
|
|
123
|
+
function formatAgentEntry(entry: AgentEntry, verbose = false): string {
|
|
124
|
+
const { agent } = entry;
|
|
125
|
+
const icon = agent.icon ?? "š¦";
|
|
126
|
+
const name = chalk.cyan(agent.slug);
|
|
127
|
+
const displayName = agent.name !== agent.slug ? chalk.gray(` (${agent.name})`) : "";
|
|
128
|
+
const mode = agent.mode ? chalk.yellow(` [${agent.mode}]`) : "";
|
|
129
|
+
const desc = agent.description ? `\n ${chalk.gray(agent.description)}` : "";
|
|
130
|
+
const tags = agent.tags?.length
|
|
131
|
+
? `\n ${chalk.blue(agent.tags.map((t) => `#${t}`).join(" "))}`
|
|
132
|
+
: "";
|
|
133
|
+
|
|
134
|
+
let line = ` ${icon} ${name}${displayName}${mode}`;
|
|
135
|
+
|
|
136
|
+
if (verbose) {
|
|
137
|
+
line += desc + tags;
|
|
138
|
+
line += `\n ${chalk.gray("ā")} ${chalk.gray(entry.sourcePath)}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return line;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Format a scope section for display
|
|
146
|
+
*/
|
|
147
|
+
function formatScopeSection(
|
|
148
|
+
title: string,
|
|
149
|
+
color: typeof chalk.bold.green,
|
|
150
|
+
entries: AgentEntry[],
|
|
151
|
+
verbose: boolean
|
|
152
|
+
): string[] {
|
|
153
|
+
const lines: string[] = [color(title)];
|
|
154
|
+
if (entries.length === 0) {
|
|
155
|
+
lines.push(chalk.gray(" (none)"));
|
|
156
|
+
} else {
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
lines.push(formatAgentEntry(entry, verbose));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
lines.push("");
|
|
162
|
+
return lines;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Format grouped agents for display
|
|
167
|
+
*/
|
|
168
|
+
function formatGroupedAgents(grouped: GroupedAgents, options: ListOptions): string {
|
|
169
|
+
const lines: string[] = [];
|
|
170
|
+
const verbose = false; // Could add --verbose flag later
|
|
171
|
+
|
|
172
|
+
// Project scope
|
|
173
|
+
if (!options.global) {
|
|
174
|
+
lines.push(
|
|
175
|
+
...formatScopeSection("š Project Agents", chalk.bold.green, grouped.project, verbose)
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// User scope
|
|
180
|
+
if (!options.local) {
|
|
181
|
+
lines.push(...formatScopeSection("š¤ User Agents", chalk.bold.blue, grouped.user, verbose));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// System scope (only if neither --global nor --local)
|
|
185
|
+
if (!options.global && !options.local) {
|
|
186
|
+
lines.push(
|
|
187
|
+
...formatScopeSection("š System Agents", chalk.bold.magenta, grouped.system, verbose)
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const total = grouped.project.length + grouped.user.length + grouped.system.length;
|
|
192
|
+
lines.push(chalk.gray(`Total: ${total} agent(s)`));
|
|
193
|
+
|
|
194
|
+
return lines.join("\n");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Convert grouped agents to JSON output
|
|
199
|
+
*/
|
|
200
|
+
function toJsonOutput(grouped: GroupedAgents): ListJsonOutput {
|
|
201
|
+
const toSummary = (entry: AgentEntry): AgentSummary => ({
|
|
202
|
+
slug: entry.agent.slug,
|
|
203
|
+
name: entry.agent.name,
|
|
204
|
+
description: entry.agent.description,
|
|
205
|
+
mode: entry.agent.mode,
|
|
206
|
+
icon: entry.agent.icon,
|
|
207
|
+
tags: entry.agent.tags,
|
|
208
|
+
sourcePath: entry.sourcePath,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
success: true,
|
|
213
|
+
total: grouped.project.length + grouped.user.length + grouped.system.length,
|
|
214
|
+
agents: {
|
|
215
|
+
project: grouped.project.map(toSummary),
|
|
216
|
+
user: grouped.user.map(toSummary),
|
|
217
|
+
system: grouped.system.map(toSummary),
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// =============================================================================
|
|
223
|
+
// Command Handler
|
|
224
|
+
// =============================================================================
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Handle list subcommand
|
|
228
|
+
*
|
|
229
|
+
* Lists all custom agents grouped by scope (project/user/system).
|
|
230
|
+
*
|
|
231
|
+
* @param options - List options
|
|
232
|
+
* @returns Command result
|
|
233
|
+
*/
|
|
234
|
+
export async function handleList(options: ListOptions = {}): Promise<CommandResult> {
|
|
235
|
+
try {
|
|
236
|
+
// Create discovery instance
|
|
237
|
+
const discovery = new AgentDiscovery({
|
|
238
|
+
watchEnabled: false, // No need to watch for listing
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Discover all agents
|
|
242
|
+
await discovery.discover();
|
|
243
|
+
const allAgents = discovery.getAll();
|
|
244
|
+
|
|
245
|
+
// Group by scope
|
|
246
|
+
const grouped = groupAgentsByScope(allAgents);
|
|
247
|
+
|
|
248
|
+
// Filter by scope if requested
|
|
249
|
+
if (options.global) {
|
|
250
|
+
grouped.project = [];
|
|
251
|
+
grouped.system = [];
|
|
252
|
+
} else if (options.local) {
|
|
253
|
+
grouped.user = [];
|
|
254
|
+
grouped.system = [];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Output format
|
|
258
|
+
if (options.json) {
|
|
259
|
+
return success(JSON.stringify(toJsonOutput(grouped), null, 2));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return success(formatGroupedAgents(grouped, options));
|
|
263
|
+
} catch (err) {
|
|
264
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
265
|
+
return error("INTERNAL_ERROR", `Failed to list agents: ${message}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Agents Validate Command (T022)
|
|
3
|
+
*
|
|
4
|
+
* Validates custom agent definition files.
|
|
5
|
+
*
|
|
6
|
+
* @module cli/commands/custom-agents/validate
|
|
7
|
+
* @see REQ-020
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import {
|
|
12
|
+
AgentDiscovery,
|
|
13
|
+
AgentLoader,
|
|
14
|
+
type CustomAgentDefinition,
|
|
15
|
+
isValidSlug,
|
|
16
|
+
validateAgentDefinition,
|
|
17
|
+
} from "@vellum/core";
|
|
18
|
+
import chalk from "chalk";
|
|
19
|
+
|
|
20
|
+
import type { CommandResult } from "../types.js";
|
|
21
|
+
import { error, success } from "../types.js";
|
|
22
|
+
import type { ValidateOptions } from "./index.js";
|
|
23
|
+
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// Types
|
|
26
|
+
// =============================================================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validation result for a single agent
|
|
30
|
+
*/
|
|
31
|
+
interface AgentValidationResult {
|
|
32
|
+
slug: string;
|
|
33
|
+
sourcePath: string;
|
|
34
|
+
valid: boolean;
|
|
35
|
+
errors: ValidationIssue[];
|
|
36
|
+
warnings: ValidationIssue[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Validation issue (error or warning)
|
|
41
|
+
*/
|
|
42
|
+
interface ValidationIssue {
|
|
43
|
+
message: string;
|
|
44
|
+
field?: string;
|
|
45
|
+
severity: "error" | "warning";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Overall validation summary
|
|
50
|
+
*/
|
|
51
|
+
interface ValidationSummary {
|
|
52
|
+
total: number;
|
|
53
|
+
valid: number;
|
|
54
|
+
invalid: number;
|
|
55
|
+
warnings: number;
|
|
56
|
+
results: AgentValidationResult[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* JSON output format
|
|
61
|
+
*/
|
|
62
|
+
export interface ValidateJsonOutput {
|
|
63
|
+
success: boolean;
|
|
64
|
+
summary: {
|
|
65
|
+
total: number;
|
|
66
|
+
valid: number;
|
|
67
|
+
invalid: number;
|
|
68
|
+
warnings: number;
|
|
69
|
+
};
|
|
70
|
+
results: AgentValidationResult[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// =============================================================================
|
|
74
|
+
// Validation Logic
|
|
75
|
+
// =============================================================================
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Validate a single agent definition
|
|
79
|
+
*/
|
|
80
|
+
function validateAgent(agent: CustomAgentDefinition, sourcePath: string): AgentValidationResult {
|
|
81
|
+
const errors: ValidationIssue[] = [];
|
|
82
|
+
const warnings: ValidationIssue[] = [];
|
|
83
|
+
|
|
84
|
+
// Validate using Zod schema
|
|
85
|
+
const schemaResult = validateAgentDefinition(agent);
|
|
86
|
+
|
|
87
|
+
if (!schemaResult.success) {
|
|
88
|
+
for (const issue of schemaResult.error.issues) {
|
|
89
|
+
errors.push({
|
|
90
|
+
message: issue.message,
|
|
91
|
+
field: issue.path.join("."),
|
|
92
|
+
severity: "error",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Additional validation checks
|
|
98
|
+
|
|
99
|
+
// Slug validation
|
|
100
|
+
if (!isValidSlug(agent.slug)) {
|
|
101
|
+
errors.push({
|
|
102
|
+
message: "Invalid slug format",
|
|
103
|
+
field: "slug",
|
|
104
|
+
severity: "error",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Warn about missing description
|
|
109
|
+
if (!agent.description) {
|
|
110
|
+
warnings.push({
|
|
111
|
+
message: "Missing description - recommended for discoverability",
|
|
112
|
+
field: "description",
|
|
113
|
+
severity: "warning",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Warn about missing icon
|
|
118
|
+
if (!agent.icon) {
|
|
119
|
+
warnings.push({
|
|
120
|
+
message: "Missing icon - will use default",
|
|
121
|
+
field: "icon",
|
|
122
|
+
severity: "warning",
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Warn about missing whenToUse
|
|
127
|
+
if (!agent.whenToUse) {
|
|
128
|
+
warnings.push({
|
|
129
|
+
message: "Missing whenToUse - agent won't be auto-suggested",
|
|
130
|
+
field: "whenToUse",
|
|
131
|
+
severity: "warning",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Warn about extends pointing to unknown agent (would need registry check)
|
|
136
|
+
if (agent.extends) {
|
|
137
|
+
// This is a soft warning - can't validate without full registry
|
|
138
|
+
warnings.push({
|
|
139
|
+
message: `Extends "${agent.extends}" - ensure parent agent exists`,
|
|
140
|
+
field: "extends",
|
|
141
|
+
severity: "warning",
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Warn about circular references in canSpawnAgents
|
|
146
|
+
if (agent.coordination?.canSpawnAgents?.includes(agent.slug)) {
|
|
147
|
+
errors.push({
|
|
148
|
+
message: "Agent cannot spawn itself",
|
|
149
|
+
field: "coordination.canSpawnAgents",
|
|
150
|
+
severity: "error",
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Validate temperature range
|
|
155
|
+
if (agent.settings?.temperature !== undefined) {
|
|
156
|
+
if (agent.settings.temperature < 0 || agent.settings.temperature > 1) {
|
|
157
|
+
errors.push({
|
|
158
|
+
message: "Temperature must be between 0 and 1",
|
|
159
|
+
field: "settings.temperature",
|
|
160
|
+
severity: "error",
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
slug: agent.slug,
|
|
167
|
+
sourcePath,
|
|
168
|
+
valid: errors.length === 0,
|
|
169
|
+
errors,
|
|
170
|
+
warnings,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Validate agent from file path
|
|
176
|
+
*/
|
|
177
|
+
async function validateAgentFile(filePath: string): Promise<AgentValidationResult | null> {
|
|
178
|
+
const loader = new AgentLoader();
|
|
179
|
+
const result = await loader.loadFile(filePath);
|
|
180
|
+
|
|
181
|
+
if (!result.ok) {
|
|
182
|
+
const loadError = result.error;
|
|
183
|
+
return {
|
|
184
|
+
slug: path.basename(filePath, path.extname(filePath)),
|
|
185
|
+
sourcePath: filePath,
|
|
186
|
+
valid: false,
|
|
187
|
+
errors: [
|
|
188
|
+
{
|
|
189
|
+
message: loadError.message,
|
|
190
|
+
severity: "error",
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
warnings: [],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return validateAgent(result.value, filePath);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// =============================================================================
|
|
201
|
+
// Formatters
|
|
202
|
+
// =============================================================================
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Format validation issue for display
|
|
206
|
+
*/
|
|
207
|
+
function formatIssue(issue: ValidationIssue): string {
|
|
208
|
+
const icon = issue.severity === "error" ? chalk.red("ā") : chalk.yellow("ā ");
|
|
209
|
+
const field = issue.field ? chalk.gray(`[${issue.field}]`) : "";
|
|
210
|
+
return ` ${icon} ${issue.message} ${field}`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Format agent validation result for display
|
|
215
|
+
*/
|
|
216
|
+
function formatAgentResult(result: AgentValidationResult): string {
|
|
217
|
+
const lines: string[] = [];
|
|
218
|
+
|
|
219
|
+
const statusIcon = result.valid ? chalk.green("ā") : chalk.red("ā");
|
|
220
|
+
|
|
221
|
+
const statusText = result.valid ? chalk.green("valid") : chalk.red("invalid");
|
|
222
|
+
|
|
223
|
+
lines.push(`${statusIcon} ${chalk.cyan(result.slug)} - ${statusText}`);
|
|
224
|
+
lines.push(chalk.gray(` ${result.sourcePath}`));
|
|
225
|
+
|
|
226
|
+
// Show errors
|
|
227
|
+
if (result.errors.length > 0) {
|
|
228
|
+
for (const err of result.errors) {
|
|
229
|
+
lines.push(formatIssue(err));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Show warnings
|
|
234
|
+
if (result.warnings.length > 0) {
|
|
235
|
+
for (const warn of result.warnings) {
|
|
236
|
+
lines.push(formatIssue(warn));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return lines.join("\n");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Format validation summary for display
|
|
245
|
+
*/
|
|
246
|
+
function formatSummary(summary: ValidationSummary, strict: boolean): string {
|
|
247
|
+
const lines: string[] = [];
|
|
248
|
+
|
|
249
|
+
lines.push(chalk.bold("\nš Validation Summary"));
|
|
250
|
+
lines.push("");
|
|
251
|
+
|
|
252
|
+
for (const result of summary.results) {
|
|
253
|
+
lines.push(formatAgentResult(result));
|
|
254
|
+
lines.push("");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Summary stats
|
|
258
|
+
const validText = chalk.green(`${summary.valid} valid`);
|
|
259
|
+
const invalidText =
|
|
260
|
+
summary.invalid > 0
|
|
261
|
+
? chalk.red(`${summary.invalid} invalid`)
|
|
262
|
+
: chalk.gray(`${summary.invalid} invalid`);
|
|
263
|
+
const warningText =
|
|
264
|
+
summary.warnings > 0
|
|
265
|
+
? chalk.yellow(`${summary.warnings} warnings`)
|
|
266
|
+
: chalk.gray(`${summary.warnings} warnings`);
|
|
267
|
+
|
|
268
|
+
lines.push(`Total: ${summary.total} agents`);
|
|
269
|
+
lines.push(`${validText} | ${invalidText} | ${warningText}`);
|
|
270
|
+
|
|
271
|
+
// Overall status
|
|
272
|
+
const hasErrors = summary.invalid > 0;
|
|
273
|
+
const hasWarnings = summary.warnings > 0;
|
|
274
|
+
|
|
275
|
+
if (hasErrors) {
|
|
276
|
+
lines.push("");
|
|
277
|
+
lines.push(chalk.red("ā Validation failed - fix errors above"));
|
|
278
|
+
} else if (strict && hasWarnings) {
|
|
279
|
+
lines.push("");
|
|
280
|
+
lines.push(chalk.yellow("ā ļø Validation failed (strict mode) - fix warnings above"));
|
|
281
|
+
} else {
|
|
282
|
+
lines.push("");
|
|
283
|
+
lines.push(chalk.green("ā
All agents valid"));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return lines.join("\n");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// =============================================================================
|
|
290
|
+
// Command Handler
|
|
291
|
+
// =============================================================================
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Validate a single target (file path or slug)
|
|
295
|
+
*/
|
|
296
|
+
async function validateTarget(target: string): Promise<AgentValidationResult | null> {
|
|
297
|
+
// Check if it's a file path
|
|
298
|
+
const isFilePath =
|
|
299
|
+
target.includes(path.sep) ||
|
|
300
|
+
target.endsWith(".md") ||
|
|
301
|
+
target.endsWith(".yaml") ||
|
|
302
|
+
target.endsWith(".yml");
|
|
303
|
+
|
|
304
|
+
if (isFilePath) {
|
|
305
|
+
return validateAgentFile(target);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// It's a slug - find the agent
|
|
309
|
+
const discovery = new AgentDiscovery({ watchEnabled: false });
|
|
310
|
+
await discovery.discover();
|
|
311
|
+
const agent = discovery.get(target);
|
|
312
|
+
|
|
313
|
+
if (!agent) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return validateAgent(agent.definition, agent.sourcePath);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Validate all discovered agents
|
|
322
|
+
*/
|
|
323
|
+
async function validateAllAgents(): Promise<AgentValidationResult[]> {
|
|
324
|
+
const discovery = new AgentDiscovery({ watchEnabled: false });
|
|
325
|
+
await discovery.discover();
|
|
326
|
+
const allAgents = discovery.getAll();
|
|
327
|
+
|
|
328
|
+
const results: AgentValidationResult[] = [];
|
|
329
|
+
for (const [, discovered] of allAgents) {
|
|
330
|
+
results.push(validateAgent(discovered.definition, discovered.sourcePath));
|
|
331
|
+
}
|
|
332
|
+
return results;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Handle validate subcommand
|
|
337
|
+
*
|
|
338
|
+
* Validates all or specific custom agent definition files.
|
|
339
|
+
*
|
|
340
|
+
* @param options - Validate options
|
|
341
|
+
* @returns Command result with exit code 0 (success) or 1 (failure)
|
|
342
|
+
*/
|
|
343
|
+
export async function handleValidate(options: ValidateOptions = {}): Promise<CommandResult> {
|
|
344
|
+
try {
|
|
345
|
+
let results: AgentValidationResult[];
|
|
346
|
+
|
|
347
|
+
if (options.target) {
|
|
348
|
+
const result = await validateTarget(options.target);
|
|
349
|
+
if (!result) {
|
|
350
|
+
return error("RESOURCE_NOT_FOUND", `Agent not found: ${options.target}`, [
|
|
351
|
+
"Use /custom-agents list to see available agents",
|
|
352
|
+
]);
|
|
353
|
+
}
|
|
354
|
+
results = [result];
|
|
355
|
+
} else {
|
|
356
|
+
results = await validateAllAgents();
|
|
357
|
+
if (results.length === 0) {
|
|
358
|
+
return success(chalk.yellow("No custom agents found to validate"));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Build summary
|
|
363
|
+
const summary: ValidationSummary = {
|
|
364
|
+
total: results.length,
|
|
365
|
+
valid: results.filter((r) => r.valid).length,
|
|
366
|
+
invalid: results.filter((r) => !r.valid).length,
|
|
367
|
+
warnings: results.reduce((acc, r) => acc + r.warnings.length, 0),
|
|
368
|
+
results,
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// Determine success/failure
|
|
372
|
+
const hasErrors = summary.invalid > 0;
|
|
373
|
+
const hasWarnings = summary.warnings > 0;
|
|
374
|
+
const failed = hasErrors || (options.strict && hasWarnings);
|
|
375
|
+
|
|
376
|
+
// Return result
|
|
377
|
+
const output = formatSummary(summary, options.strict ?? false);
|
|
378
|
+
|
|
379
|
+
if (failed) {
|
|
380
|
+
return { kind: "error", code: "INTERNAL_ERROR", message: output };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return success(output);
|
|
384
|
+
} catch (err) {
|
|
385
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
386
|
+
return error("INTERNAL_ERROR", `Validation failed: ${message}`);
|
|
387
|
+
}
|
|
388
|
+
}
|