@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,621 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Validate Command
|
|
3
|
+
*
|
|
4
|
+
* Validates prompt files across all customization directories.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* - `vellum prompt validate` - Validate all prompt sources
|
|
8
|
+
* - `vellum prompt validate --fix` - Auto-fix simple issues
|
|
9
|
+
*
|
|
10
|
+
* @module cli/commands/prompt/validate
|
|
11
|
+
* @see REQ-016
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { extname, join, relative } from "node:path";
|
|
16
|
+
|
|
17
|
+
import chalk from "chalk";
|
|
18
|
+
|
|
19
|
+
import { ICONS } from "../../utils/icons.js";
|
|
20
|
+
import { EXIT_CODES } from "../exit-codes.js";
|
|
21
|
+
import type { CommandContext, CommandResult, SlashCommand } from "../types.js";
|
|
22
|
+
import { error, success } from "../types.js";
|
|
23
|
+
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// Constants
|
|
26
|
+
// =============================================================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Directories to scan for prompts
|
|
30
|
+
*/
|
|
31
|
+
const PROMPT_DIRECTORIES = [
|
|
32
|
+
{ name: "prompts", path: ".vellum/prompts" },
|
|
33
|
+
{ name: "commands", path: ".vellum/commands" },
|
|
34
|
+
{ name: "workflows", path: ".vellum/workflows" },
|
|
35
|
+
{ name: "skills", path: ".vellum/skills" },
|
|
36
|
+
{ name: "rules", path: ".vellum/rules" },
|
|
37
|
+
] as const;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Valid file extensions for prompt files
|
|
41
|
+
*/
|
|
42
|
+
const VALID_EXTENSIONS = new Set([".md", ".markdown"]);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* YAML frontmatter pattern
|
|
46
|
+
*/
|
|
47
|
+
const FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
48
|
+
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// Types
|
|
51
|
+
// =============================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Validation issue severity
|
|
55
|
+
*/
|
|
56
|
+
export type ValidationSeverity = "error" | "warning" | "info";
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Single validation issue
|
|
60
|
+
*/
|
|
61
|
+
export interface ValidationIssue {
|
|
62
|
+
/** Issue severity */
|
|
63
|
+
severity: ValidationSeverity;
|
|
64
|
+
/** File path relative to project root */
|
|
65
|
+
file: string;
|
|
66
|
+
/** Line number (1-based) */
|
|
67
|
+
line?: number;
|
|
68
|
+
/** Column number (1-based) */
|
|
69
|
+
column?: number;
|
|
70
|
+
/** Issue code for programmatic handling */
|
|
71
|
+
code: string;
|
|
72
|
+
/** Human-readable message */
|
|
73
|
+
message: string;
|
|
74
|
+
/** Whether this issue can be auto-fixed */
|
|
75
|
+
fixable: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Options for validate command
|
|
80
|
+
*/
|
|
81
|
+
export interface PromptValidateOptions {
|
|
82
|
+
/** Auto-fix simple issues */
|
|
83
|
+
fix?: boolean;
|
|
84
|
+
/** Show verbose output */
|
|
85
|
+
verbose?: boolean;
|
|
86
|
+
/** Output as JSON */
|
|
87
|
+
json?: boolean;
|
|
88
|
+
/** Working directory (defaults to process.cwd()) */
|
|
89
|
+
cwd?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Result of validation
|
|
94
|
+
*/
|
|
95
|
+
export interface PromptValidateResult {
|
|
96
|
+
/** Whether validation passed (no errors) */
|
|
97
|
+
valid: boolean;
|
|
98
|
+
/** Total files scanned */
|
|
99
|
+
filesScanned: number;
|
|
100
|
+
/** All issues found */
|
|
101
|
+
issues: ValidationIssue[];
|
|
102
|
+
/** Number of issues fixed (if --fix) */
|
|
103
|
+
issuesFixed: number;
|
|
104
|
+
/** Exit code */
|
|
105
|
+
exitCode: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// =============================================================================
|
|
109
|
+
// Validation Helpers
|
|
110
|
+
// =============================================================================
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check for trailing whitespace
|
|
114
|
+
*/
|
|
115
|
+
function checkTrailingWhitespace(content: string, file: string): ValidationIssue[] {
|
|
116
|
+
const issues: ValidationIssue[] = [];
|
|
117
|
+
const lines = content.split("\n");
|
|
118
|
+
|
|
119
|
+
for (let i = 0; i < lines.length; i++) {
|
|
120
|
+
const line = lines[i];
|
|
121
|
+
if (!line) continue;
|
|
122
|
+
if (line !== line.trimEnd()) {
|
|
123
|
+
issues.push({
|
|
124
|
+
severity: "warning",
|
|
125
|
+
file,
|
|
126
|
+
line: i + 1,
|
|
127
|
+
code: "trailing-whitespace",
|
|
128
|
+
message: "Trailing whitespace",
|
|
129
|
+
fixable: true,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return issues;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Check for missing newline at EOF
|
|
139
|
+
*/
|
|
140
|
+
function checkMissingNewlineAtEof(content: string, file: string): ValidationIssue | null {
|
|
141
|
+
if (content.length > 0 && !content.endsWith("\n")) {
|
|
142
|
+
return {
|
|
143
|
+
severity: "warning",
|
|
144
|
+
file,
|
|
145
|
+
line: content.split("\n").length,
|
|
146
|
+
code: "missing-newline-eof",
|
|
147
|
+
message: "Missing newline at end of file",
|
|
148
|
+
fixable: true,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Check for valid frontmatter
|
|
156
|
+
*/
|
|
157
|
+
function checkFrontmatter(content: string, file: string): ValidationIssue[] {
|
|
158
|
+
const issues: ValidationIssue[] = [];
|
|
159
|
+
|
|
160
|
+
// Check if file starts with frontmatter
|
|
161
|
+
if (!content.startsWith("---")) {
|
|
162
|
+
issues.push({
|
|
163
|
+
severity: "error",
|
|
164
|
+
file,
|
|
165
|
+
line: 1,
|
|
166
|
+
code: "missing-frontmatter",
|
|
167
|
+
message: "File must start with YAML frontmatter (---)",
|
|
168
|
+
fixable: false,
|
|
169
|
+
});
|
|
170
|
+
return issues;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Check if frontmatter is properly closed
|
|
174
|
+
const match = content.match(FRONTMATTER_PATTERN);
|
|
175
|
+
if (!match) {
|
|
176
|
+
issues.push({
|
|
177
|
+
severity: "error",
|
|
178
|
+
file,
|
|
179
|
+
line: 1,
|
|
180
|
+
code: "unclosed-frontmatter",
|
|
181
|
+
message: "YAML frontmatter is not properly closed",
|
|
182
|
+
fixable: false,
|
|
183
|
+
});
|
|
184
|
+
return issues;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Parse frontmatter for basic validation
|
|
188
|
+
const frontmatterContent = match[1];
|
|
189
|
+
if (!frontmatterContent) return issues;
|
|
190
|
+
const frontmatterLines = frontmatterContent.split("\n");
|
|
191
|
+
|
|
192
|
+
// Check for inconsistent indentation
|
|
193
|
+
let expectedIndent: string | null = null;
|
|
194
|
+
for (let i = 0; i < frontmatterLines.length; i++) {
|
|
195
|
+
const line = frontmatterLines[i];
|
|
196
|
+
if (!line || line.trim() === "") continue;
|
|
197
|
+
|
|
198
|
+
// Check for tabs vs spaces
|
|
199
|
+
const leadingWhitespace = line.match(/^(\s*)/)?.[1] ?? "";
|
|
200
|
+
if (leadingWhitespace.includes("\t") && leadingWhitespace.includes(" ")) {
|
|
201
|
+
issues.push({
|
|
202
|
+
severity: "warning",
|
|
203
|
+
file,
|
|
204
|
+
line: i + 2, // +2 because frontmatter starts at line 2
|
|
205
|
+
code: "mixed-indentation",
|
|
206
|
+
message: "Mixed tabs and spaces in indentation",
|
|
207
|
+
fixable: true,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Detect indent style
|
|
212
|
+
if (leadingWhitespace.length > 0 && expectedIndent === null) {
|
|
213
|
+
expectedIndent = leadingWhitespace.includes("\t") ? "\t" : " ";
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Check for required fields based on file location
|
|
218
|
+
const hasName = frontmatterContent.includes("name:");
|
|
219
|
+
const hasId = frontmatterContent.includes("id:");
|
|
220
|
+
|
|
221
|
+
if (!hasName && !hasId) {
|
|
222
|
+
issues.push({
|
|
223
|
+
severity: "warning",
|
|
224
|
+
file,
|
|
225
|
+
line: 1,
|
|
226
|
+
code: "missing-identifier",
|
|
227
|
+
message: "Frontmatter should have 'name' or 'id' field",
|
|
228
|
+
fixable: false,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return issues;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Check for duplicate keys in YAML frontmatter
|
|
237
|
+
*/
|
|
238
|
+
function checkDuplicateKeys(content: string, file: string): ValidationIssue[] {
|
|
239
|
+
const issues: ValidationIssue[] = [];
|
|
240
|
+
const match = content.match(FRONTMATTER_PATTERN);
|
|
241
|
+
if (!match || !match[1]) return issues;
|
|
242
|
+
|
|
243
|
+
const frontmatterContent = match[1];
|
|
244
|
+
const lines = frontmatterContent.split("\n");
|
|
245
|
+
const seenKeys = new Map<string, number>();
|
|
246
|
+
|
|
247
|
+
for (let i = 0; i < lines.length; i++) {
|
|
248
|
+
const line = lines[i];
|
|
249
|
+
if (!line) continue;
|
|
250
|
+
const keyMatch = line.match(/^(\w+):/);
|
|
251
|
+
if (keyMatch?.[1]) {
|
|
252
|
+
const key = keyMatch[1];
|
|
253
|
+
const prevLine = seenKeys.get(key);
|
|
254
|
+
if (prevLine !== undefined) {
|
|
255
|
+
issues.push({
|
|
256
|
+
severity: "error",
|
|
257
|
+
file,
|
|
258
|
+
line: i + 2,
|
|
259
|
+
code: "duplicate-key",
|
|
260
|
+
message: `Duplicate key '${key}' (first occurrence at line ${prevLine})`,
|
|
261
|
+
fixable: false,
|
|
262
|
+
});
|
|
263
|
+
} else {
|
|
264
|
+
seenKeys.set(key, i + 2);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return issues;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// =============================================================================
|
|
273
|
+
// Auto-Fix Functions
|
|
274
|
+
// =============================================================================
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Fix trailing whitespace in content
|
|
278
|
+
*/
|
|
279
|
+
function fixTrailingWhitespace(content: string): string {
|
|
280
|
+
return content
|
|
281
|
+
.split("\n")
|
|
282
|
+
.map((line) => line.trimEnd())
|
|
283
|
+
.join("\n");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Fix missing newline at EOF
|
|
288
|
+
*/
|
|
289
|
+
function fixMissingNewlineAtEof(content: string): string {
|
|
290
|
+
if (content.length > 0 && !content.endsWith("\n")) {
|
|
291
|
+
return `${content}\n`;
|
|
292
|
+
}
|
|
293
|
+
return content;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Fix mixed indentation (convert tabs to spaces)
|
|
298
|
+
*/
|
|
299
|
+
function fixMixedIndentation(content: string): string {
|
|
300
|
+
const match = content.match(FRONTMATTER_PATTERN);
|
|
301
|
+
if (!match || !match[1]) return content;
|
|
302
|
+
|
|
303
|
+
const frontmatterContent = match[1];
|
|
304
|
+
const fixedFrontmatter = frontmatterContent.replace(/\t/g, " ");
|
|
305
|
+
|
|
306
|
+
return content.replace(frontmatterContent, fixedFrontmatter);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Apply all auto-fixes to content
|
|
311
|
+
*/
|
|
312
|
+
function applyFixes(content: string, issues: ValidationIssue[]): string {
|
|
313
|
+
let fixed = content;
|
|
314
|
+
|
|
315
|
+
const hasMixedIndent = issues.some((i) => i.code === "mixed-indentation");
|
|
316
|
+
const hasTrailingWhitespace = issues.some((i) => i.code === "trailing-whitespace");
|
|
317
|
+
const hasMissingNewline = issues.some((i) => i.code === "missing-newline-eof");
|
|
318
|
+
|
|
319
|
+
if (hasMixedIndent) {
|
|
320
|
+
fixed = fixMixedIndentation(fixed);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (hasTrailingWhitespace) {
|
|
324
|
+
fixed = fixTrailingWhitespace(fixed);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (hasMissingNewline) {
|
|
328
|
+
fixed = fixMissingNewlineAtEof(fixed);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return fixed;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// =============================================================================
|
|
335
|
+
// File Scanning
|
|
336
|
+
// =============================================================================
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Get all markdown files in a directory recursively
|
|
340
|
+
*/
|
|
341
|
+
function getMarkdownFiles(dir: string, rootDir: string): string[] {
|
|
342
|
+
if (!existsSync(dir)) {
|
|
343
|
+
return [];
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const files: string[] = [];
|
|
347
|
+
const entries = readdirSync(dir);
|
|
348
|
+
|
|
349
|
+
for (const entry of entries) {
|
|
350
|
+
const fullPath = join(dir, entry);
|
|
351
|
+
const stat = statSync(fullPath);
|
|
352
|
+
|
|
353
|
+
if (stat.isDirectory()) {
|
|
354
|
+
files.push(...getMarkdownFiles(fullPath, rootDir));
|
|
355
|
+
} else if (stat.isFile() && VALID_EXTENSIONS.has(extname(entry))) {
|
|
356
|
+
files.push(fullPath);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return files;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Validate a single file
|
|
365
|
+
*/
|
|
366
|
+
function validateFile(filePath: string, rootDir: string): ValidationIssue[] {
|
|
367
|
+
const relativePath = relative(rootDir, filePath);
|
|
368
|
+
const content = readFileSync(filePath, "utf-8");
|
|
369
|
+
const issues: ValidationIssue[] = [];
|
|
370
|
+
|
|
371
|
+
// Run all checks
|
|
372
|
+
issues.push(...checkFrontmatter(content, relativePath));
|
|
373
|
+
issues.push(...checkTrailingWhitespace(content, relativePath));
|
|
374
|
+
issues.push(...checkDuplicateKeys(content, relativePath));
|
|
375
|
+
|
|
376
|
+
const newlineIssue = checkMissingNewlineAtEof(content, relativePath);
|
|
377
|
+
if (newlineIssue) {
|
|
378
|
+
issues.push(newlineIssue);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return issues;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// =============================================================================
|
|
385
|
+
// Command Execution
|
|
386
|
+
// =============================================================================
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Execute prompt validate command
|
|
390
|
+
*
|
|
391
|
+
* @param options - Command options
|
|
392
|
+
* @returns Validation result
|
|
393
|
+
*/
|
|
394
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Validation requires checking multiple rules and fixing issues
|
|
395
|
+
export async function executePromptValidate(
|
|
396
|
+
options: PromptValidateOptions = {}
|
|
397
|
+
): Promise<PromptValidateResult> {
|
|
398
|
+
const rootDir = options.cwd ?? process.cwd();
|
|
399
|
+
const allIssues: ValidationIssue[] = [];
|
|
400
|
+
const filesToFix = new Map<string, ValidationIssue[]>();
|
|
401
|
+
let filesScanned = 0;
|
|
402
|
+
let issuesFixed = 0;
|
|
403
|
+
|
|
404
|
+
// Scan all prompt directories
|
|
405
|
+
for (const { name, path } of PROMPT_DIRECTORIES) {
|
|
406
|
+
const dirPath = join(rootDir, path);
|
|
407
|
+
|
|
408
|
+
if (!existsSync(dirPath)) {
|
|
409
|
+
if (options.verbose) {
|
|
410
|
+
console.log(chalk.gray(`Skipping ${name}: ${path} not found`));
|
|
411
|
+
}
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const files = getMarkdownFiles(dirPath, rootDir);
|
|
416
|
+
|
|
417
|
+
if (options.verbose) {
|
|
418
|
+
console.log(chalk.blue(`\nScanning ${name}/ (${files.length} files)`));
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
for (const file of files) {
|
|
422
|
+
filesScanned++;
|
|
423
|
+
const issues = validateFile(file, rootDir);
|
|
424
|
+
|
|
425
|
+
if (issues.length > 0) {
|
|
426
|
+
allIssues.push(...issues);
|
|
427
|
+
|
|
428
|
+
// Collect fixable issues per file
|
|
429
|
+
const fixableIssues = issues.filter((i) => i.fixable);
|
|
430
|
+
if (fixableIssues.length > 0) {
|
|
431
|
+
filesToFix.set(file, fixableIssues);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Apply fixes if requested
|
|
438
|
+
if (options.fix && filesToFix.size > 0) {
|
|
439
|
+
for (const [filePath, issues] of filesToFix) {
|
|
440
|
+
const content = readFileSync(filePath, "utf-8");
|
|
441
|
+
const fixed = applyFixes(content, issues);
|
|
442
|
+
|
|
443
|
+
if (fixed !== content) {
|
|
444
|
+
writeFileSync(filePath, fixed, "utf-8");
|
|
445
|
+
issuesFixed += issues.length;
|
|
446
|
+
|
|
447
|
+
if (options.verbose) {
|
|
448
|
+
const relativePath = relative(rootDir, filePath);
|
|
449
|
+
console.log(chalk.green(` Fixed: ${relativePath}`));
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Calculate results
|
|
456
|
+
const errors = allIssues.filter((i) => i.severity === "error");
|
|
457
|
+
const warnings = allIssues.filter((i) => i.severity === "warning");
|
|
458
|
+
const unfixedIssues = allIssues.filter((i) => !i.fixable || !options.fix);
|
|
459
|
+
const hasErrors = errors.length > 0;
|
|
460
|
+
|
|
461
|
+
// Output results
|
|
462
|
+
if (options.json) {
|
|
463
|
+
console.log(
|
|
464
|
+
JSON.stringify(
|
|
465
|
+
{
|
|
466
|
+
valid: !hasErrors,
|
|
467
|
+
filesScanned,
|
|
468
|
+
errors: errors.length,
|
|
469
|
+
warnings: warnings.length,
|
|
470
|
+
issuesFixed,
|
|
471
|
+
issues: allIssues,
|
|
472
|
+
},
|
|
473
|
+
null,
|
|
474
|
+
2
|
|
475
|
+
)
|
|
476
|
+
);
|
|
477
|
+
} else {
|
|
478
|
+
// Display issues in file:line format
|
|
479
|
+
if (unfixedIssues.length > 0 || !options.fix) {
|
|
480
|
+
console.log(chalk.bold(`\n${ICONS.workflow} Validation Results\n`));
|
|
481
|
+
|
|
482
|
+
for (const issue of allIssues) {
|
|
483
|
+
if (options.fix && issue.fixable) continue;
|
|
484
|
+
|
|
485
|
+
const location = issue.line ? `:${issue.line}` : "";
|
|
486
|
+
const prefix =
|
|
487
|
+
issue.severity === "error"
|
|
488
|
+
? chalk.red("x")
|
|
489
|
+
: issue.severity === "warning"
|
|
490
|
+
? chalk.yellow(ICONS.warning)
|
|
491
|
+
: chalk.blue(ICONS.info);
|
|
492
|
+
|
|
493
|
+
console.log(`${prefix} ${issue.file}${location}: ${issue.message} (${issue.code})`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Summary
|
|
498
|
+
console.log(chalk.bold("\nSummary"));
|
|
499
|
+
console.log(chalk.gray(` Files scanned: ${filesScanned}`));
|
|
500
|
+
console.log(chalk.gray(` Errors: ${errors.length}`));
|
|
501
|
+
console.log(chalk.gray(` Warnings: ${warnings.length}`));
|
|
502
|
+
|
|
503
|
+
if (options.fix && issuesFixed > 0) {
|
|
504
|
+
console.log(chalk.green(` Issues fixed: ${issuesFixed}`));
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (hasErrors) {
|
|
508
|
+
console.log(chalk.red(`\n${ICONS.error} Validation failed`));
|
|
509
|
+
} else if (warnings.length > 0) {
|
|
510
|
+
console.log(chalk.yellow(`\n${ICONS.warning} Validation passed with warnings`));
|
|
511
|
+
} else {
|
|
512
|
+
console.log(chalk.green(`\n${ICONS.success} Validation passed`));
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return {
|
|
517
|
+
valid: !hasErrors,
|
|
518
|
+
filesScanned,
|
|
519
|
+
issues: allIssues,
|
|
520
|
+
issuesFixed,
|
|
521
|
+
exitCode: hasErrors ? EXIT_CODES.ERROR : EXIT_CODES.SUCCESS,
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// =============================================================================
|
|
526
|
+
// Slash Command Definition
|
|
527
|
+
// =============================================================================
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Prompt validate slash command for TUI
|
|
531
|
+
*/
|
|
532
|
+
export const promptValidateCommand: SlashCommand = {
|
|
533
|
+
name: "prompt-validate",
|
|
534
|
+
description: "Validate prompt files in .vellum/ directories",
|
|
535
|
+
kind: "builtin",
|
|
536
|
+
category: "config",
|
|
537
|
+
aliases: ["validate-prompts"],
|
|
538
|
+
namedArgs: [
|
|
539
|
+
{
|
|
540
|
+
name: "fix",
|
|
541
|
+
shorthand: "f",
|
|
542
|
+
type: "boolean",
|
|
543
|
+
description: "Auto-fix simple issues (trailing whitespace, etc.)",
|
|
544
|
+
required: false,
|
|
545
|
+
default: false,
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: "verbose",
|
|
549
|
+
shorthand: "v",
|
|
550
|
+
type: "boolean",
|
|
551
|
+
description: "Show verbose output",
|
|
552
|
+
required: false,
|
|
553
|
+
default: false,
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
name: "json",
|
|
557
|
+
shorthand: "j",
|
|
558
|
+
type: "boolean",
|
|
559
|
+
description: "Output as JSON",
|
|
560
|
+
required: false,
|
|
561
|
+
default: false,
|
|
562
|
+
},
|
|
563
|
+
],
|
|
564
|
+
examples: [
|
|
565
|
+
"/prompt-validate - Validate all prompts",
|
|
566
|
+
"/prompt-validate --fix - Auto-fix simple issues",
|
|
567
|
+
"/prompt-validate --json - Output as JSON",
|
|
568
|
+
],
|
|
569
|
+
|
|
570
|
+
execute: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
571
|
+
const fix = ctx.parsedArgs.named.fix as boolean | undefined;
|
|
572
|
+
const verbose = ctx.parsedArgs.named.verbose as boolean | undefined;
|
|
573
|
+
const json = ctx.parsedArgs.named.json as boolean | undefined;
|
|
574
|
+
|
|
575
|
+
const result = await executePromptValidate({
|
|
576
|
+
fix: fix ?? false,
|
|
577
|
+
verbose: verbose ?? false,
|
|
578
|
+
json: json ?? false,
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
if (result.valid) {
|
|
582
|
+
return success(
|
|
583
|
+
`Validated ${result.filesScanned} files. ${result.issuesFixed} issues fixed.`,
|
|
584
|
+
{
|
|
585
|
+
filesScanned: result.filesScanned,
|
|
586
|
+
issues: result.issues,
|
|
587
|
+
issuesFixed: result.issuesFixed,
|
|
588
|
+
}
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const errorCount = result.issues.filter((i) => i.severity === "error").length;
|
|
593
|
+
return error(
|
|
594
|
+
"INTERNAL_ERROR",
|
|
595
|
+
`Validation failed with ${errorCount} error(s). Run with --verbose for details.`
|
|
596
|
+
);
|
|
597
|
+
},
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
// =============================================================================
|
|
601
|
+
// CLI Entry Point
|
|
602
|
+
// =============================================================================
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Run prompt validate command from CLI
|
|
606
|
+
*
|
|
607
|
+
* @param options - CLI options
|
|
608
|
+
*/
|
|
609
|
+
export async function runPromptValidateCli(options: {
|
|
610
|
+
fix?: boolean;
|
|
611
|
+
verbose?: boolean;
|
|
612
|
+
json?: boolean;
|
|
613
|
+
}): Promise<void> {
|
|
614
|
+
const result = await executePromptValidate({
|
|
615
|
+
fix: options.fix ?? false,
|
|
616
|
+
verbose: options.verbose ?? false,
|
|
617
|
+
json: options.json ?? false,
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
process.exit(result.exitCode);
|
|
621
|
+
}
|