@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,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch Executor (T-048)
|
|
3
|
+
*
|
|
4
|
+
* Executes multiple commands in sequence from a batch script.
|
|
5
|
+
* Supports continue-on-error option for resilient batch processing.
|
|
6
|
+
*
|
|
7
|
+
* @module cli/commands/batch/executor
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { CommandExecutor } from "../executor.js";
|
|
11
|
+
import type { CommandResult } from "../types.js";
|
|
12
|
+
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Interfaces
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Configuration for batch execution
|
|
19
|
+
*/
|
|
20
|
+
export interface BatchConfig {
|
|
21
|
+
/** Continue executing remaining commands if one fails */
|
|
22
|
+
continueOnError?: boolean;
|
|
23
|
+
/** Abort signal for cancellation */
|
|
24
|
+
signal?: AbortSignal;
|
|
25
|
+
/** Callback invoked before each command */
|
|
26
|
+
onBeforeCommand?: (command: string, index: number) => void;
|
|
27
|
+
/** Callback invoked after each command */
|
|
28
|
+
onAfterCommand?: (command: string, index: number, result: CommandResult) => void;
|
|
29
|
+
/** Skip empty lines and comments (lines starting with #) */
|
|
30
|
+
skipComments?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Result of a single command in batch execution
|
|
35
|
+
*/
|
|
36
|
+
export interface BatchCommandResult {
|
|
37
|
+
/** Original command string */
|
|
38
|
+
command: string;
|
|
39
|
+
/** Command index in batch (0-based) */
|
|
40
|
+
index: number;
|
|
41
|
+
/** Execution result */
|
|
42
|
+
result: CommandResult;
|
|
43
|
+
/** Whether command was skipped (comment or empty) */
|
|
44
|
+
skipped: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Result of batch execution
|
|
49
|
+
*/
|
|
50
|
+
export interface BatchResult {
|
|
51
|
+
/** Results for each command */
|
|
52
|
+
commands: BatchCommandResult[];
|
|
53
|
+
/** Total commands processed */
|
|
54
|
+
total: number;
|
|
55
|
+
/** Number of successful commands */
|
|
56
|
+
succeeded: number;
|
|
57
|
+
/** Number of failed commands */
|
|
58
|
+
failed: number;
|
|
59
|
+
/** Number of skipped commands */
|
|
60
|
+
skipped: number;
|
|
61
|
+
/** Whether batch completed (not aborted early) */
|
|
62
|
+
completed: boolean;
|
|
63
|
+
/** Error that caused early abort (if any) */
|
|
64
|
+
abortError?: Error;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Result of batch script validation
|
|
69
|
+
*/
|
|
70
|
+
export interface BatchValidationResult {
|
|
71
|
+
/** Whether script is valid */
|
|
72
|
+
valid: boolean;
|
|
73
|
+
/** Number of commands found */
|
|
74
|
+
commandCount: number;
|
|
75
|
+
/** Validation warnings */
|
|
76
|
+
warnings: string[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// Batch Script Parser
|
|
81
|
+
// =============================================================================
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Parses batch scripts into individual commands
|
|
85
|
+
*
|
|
86
|
+
* Handles:
|
|
87
|
+
* - Newline-separated commands
|
|
88
|
+
* - Comment lines (starting with #)
|
|
89
|
+
* - Empty lines
|
|
90
|
+
* - Leading/trailing whitespace
|
|
91
|
+
*/
|
|
92
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: Parser provides a logical grouping for batch parsing functionality
|
|
93
|
+
export class BatchScriptParser {
|
|
94
|
+
/**
|
|
95
|
+
* Parse a batch script into command lines
|
|
96
|
+
*
|
|
97
|
+
* @param script - Batch script content
|
|
98
|
+
* @param skipComments - Whether to filter out comments and empty lines
|
|
99
|
+
* @returns Array of command strings
|
|
100
|
+
*/
|
|
101
|
+
static parse(script: string, skipComments = true): string[] {
|
|
102
|
+
const lines = script.split(/\r?\n/);
|
|
103
|
+
|
|
104
|
+
if (!skipComments) {
|
|
105
|
+
return lines;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const result: string[] = [];
|
|
109
|
+
for (const line of lines) {
|
|
110
|
+
const trimmed = line.trim();
|
|
111
|
+
// Skip empty lines
|
|
112
|
+
if (trimmed === "") continue;
|
|
113
|
+
// Skip comment lines
|
|
114
|
+
if (trimmed.startsWith("#")) continue;
|
|
115
|
+
result.push(trimmed);
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Check if a line is a comment
|
|
122
|
+
*
|
|
123
|
+
* @param line - Line to check
|
|
124
|
+
* @returns true if line is a comment
|
|
125
|
+
*/
|
|
126
|
+
static isComment(line: string): boolean {
|
|
127
|
+
return line.trim().startsWith("#");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Check if a line is empty (whitespace only)
|
|
132
|
+
*
|
|
133
|
+
* @param line - Line to check
|
|
134
|
+
* @returns true if line is empty
|
|
135
|
+
*/
|
|
136
|
+
static isEmpty(line: string): boolean {
|
|
137
|
+
return line.trim() === "";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Validate batch script for common issues
|
|
142
|
+
*
|
|
143
|
+
* @param script - Batch script content
|
|
144
|
+
* @returns Validation result with any warnings
|
|
145
|
+
*/
|
|
146
|
+
static validate(script: string): BatchValidationResult {
|
|
147
|
+
const lines = script.split(/\r?\n/);
|
|
148
|
+
const warnings: string[] = [];
|
|
149
|
+
let commandCount = 0;
|
|
150
|
+
|
|
151
|
+
for (let i = 0; i < lines.length; i++) {
|
|
152
|
+
const rawLine = lines[i];
|
|
153
|
+
if (rawLine === undefined) continue;
|
|
154
|
+
const line = rawLine.trim();
|
|
155
|
+
|
|
156
|
+
if (BatchScriptParser.isEmpty(line) || BatchScriptParser.isComment(line)) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
commandCount++;
|
|
161
|
+
|
|
162
|
+
// Check for common issues
|
|
163
|
+
if (!line.startsWith("/")) {
|
|
164
|
+
warnings.push(`Line ${i + 1}: Command should start with /`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
valid: commandCount > 0,
|
|
170
|
+
commandCount,
|
|
171
|
+
warnings,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// =============================================================================
|
|
177
|
+
// Batch Executor
|
|
178
|
+
// =============================================================================
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Executes batch scripts with error handling
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* const batch = new BatchExecutor(executor);
|
|
186
|
+
*
|
|
187
|
+
* const script = `
|
|
188
|
+
* # Setup commands
|
|
189
|
+
* /login anthropic
|
|
190
|
+
* /config set theme dark
|
|
191
|
+
*
|
|
192
|
+
* # Main operation
|
|
193
|
+
* /help
|
|
194
|
+
* `;
|
|
195
|
+
*
|
|
196
|
+
* const result = await batch.execute(script, {
|
|
197
|
+
* continueOnError: true,
|
|
198
|
+
* onBeforeCommand: (cmd, i) => console.log(`Running ${i + 1}: ${cmd}`),
|
|
199
|
+
* });
|
|
200
|
+
*
|
|
201
|
+
* console.log(`Completed: ${result.succeeded}/${result.total}`);
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export class BatchExecutor {
|
|
205
|
+
constructor(private readonly executor: CommandExecutor) {}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Execute a batch script
|
|
209
|
+
*
|
|
210
|
+
* @param script - Batch script content (newline-separated commands)
|
|
211
|
+
* @param config - Batch execution configuration
|
|
212
|
+
* @returns Batch execution result
|
|
213
|
+
*/
|
|
214
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Core batch execution logic requires sequential processing
|
|
215
|
+
async execute(script: string, config: BatchConfig = {}): Promise<BatchResult> {
|
|
216
|
+
const {
|
|
217
|
+
continueOnError = false,
|
|
218
|
+
signal,
|
|
219
|
+
onBeforeCommand,
|
|
220
|
+
onAfterCommand,
|
|
221
|
+
skipComments = true,
|
|
222
|
+
} = config;
|
|
223
|
+
|
|
224
|
+
const lines = script.split(/\r?\n/);
|
|
225
|
+
const commands: BatchCommandResult[] = [];
|
|
226
|
+
let completed = true;
|
|
227
|
+
let abortError: Error | undefined;
|
|
228
|
+
|
|
229
|
+
for (let i = 0; i < lines.length; i++) {
|
|
230
|
+
// Check for abort signal
|
|
231
|
+
if (signal?.aborted) {
|
|
232
|
+
completed = false;
|
|
233
|
+
abortError = new Error("Batch execution aborted");
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const line = lines[i];
|
|
238
|
+
if (line === undefined) continue;
|
|
239
|
+
const trimmedLine = line.trim();
|
|
240
|
+
|
|
241
|
+
// Handle comments and empty lines
|
|
242
|
+
if (
|
|
243
|
+
skipComments &&
|
|
244
|
+
(BatchScriptParser.isEmpty(trimmedLine) || BatchScriptParser.isComment(trimmedLine))
|
|
245
|
+
) {
|
|
246
|
+
commands.push({
|
|
247
|
+
command: line,
|
|
248
|
+
index: i,
|
|
249
|
+
result: { kind: "success" },
|
|
250
|
+
skipped: true,
|
|
251
|
+
});
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Execute command
|
|
256
|
+
onBeforeCommand?.(trimmedLine, i);
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
const result = await this.executor.execute(trimmedLine, signal);
|
|
260
|
+
|
|
261
|
+
commands.push({
|
|
262
|
+
command: trimmedLine,
|
|
263
|
+
index: i,
|
|
264
|
+
result,
|
|
265
|
+
skipped: false,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
onAfterCommand?.(trimmedLine, i, result);
|
|
269
|
+
|
|
270
|
+
// Check for failure
|
|
271
|
+
if (result.kind === "error" && !continueOnError) {
|
|
272
|
+
completed = false;
|
|
273
|
+
abortError = new Error(`Command failed: ${result.message}`);
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
} catch (error) {
|
|
277
|
+
const errorResult: CommandResult = {
|
|
278
|
+
kind: "error",
|
|
279
|
+
code: "INTERNAL_ERROR",
|
|
280
|
+
message: error instanceof Error ? error.message : String(error),
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
commands.push({
|
|
284
|
+
command: trimmedLine,
|
|
285
|
+
index: i,
|
|
286
|
+
result: errorResult,
|
|
287
|
+
skipped: false,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
onAfterCommand?.(trimmedLine, i, errorResult);
|
|
291
|
+
|
|
292
|
+
if (!continueOnError) {
|
|
293
|
+
completed = false;
|
|
294
|
+
abortError = error instanceof Error ? error : new Error(String(error));
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Calculate summary
|
|
301
|
+
const executed = commands.filter((c) => !c.skipped);
|
|
302
|
+
const succeeded = executed.filter((c) => c.result.kind === "success").length;
|
|
303
|
+
const failed = executed.filter((c) => c.result.kind === "error").length;
|
|
304
|
+
const skipped = commands.filter((c) => c.skipped).length;
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
commands,
|
|
308
|
+
total: executed.length,
|
|
309
|
+
succeeded,
|
|
310
|
+
failed,
|
|
311
|
+
skipped,
|
|
312
|
+
completed,
|
|
313
|
+
abortError,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Execute commands from an array
|
|
319
|
+
*
|
|
320
|
+
* @param commands - Array of command strings
|
|
321
|
+
* @param config - Batch execution configuration
|
|
322
|
+
* @returns Batch execution result
|
|
323
|
+
*/
|
|
324
|
+
async executeCommands(commands: string[], config: BatchConfig = {}): Promise<BatchResult> {
|
|
325
|
+
const script = commands.join("\n");
|
|
326
|
+
return this.execute(script, { ...config, skipComments: false });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Create a batch script from an array of commands
|
|
332
|
+
*
|
|
333
|
+
* @param commands - Array of command strings
|
|
334
|
+
* @param options - Script creation options
|
|
335
|
+
* @returns Formatted batch script
|
|
336
|
+
*/
|
|
337
|
+
export function createBatchScript(
|
|
338
|
+
commands: string[],
|
|
339
|
+
options: { header?: string; comments?: Record<number, string> } = {}
|
|
340
|
+
): string {
|
|
341
|
+
const { header, comments = {} } = options;
|
|
342
|
+
const lines: string[] = [];
|
|
343
|
+
|
|
344
|
+
if (header) {
|
|
345
|
+
lines.push(`# ${header}`);
|
|
346
|
+
lines.push("");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
for (let i = 0; i < commands.length; i++) {
|
|
350
|
+
const comment = comments[i];
|
|
351
|
+
const cmd = commands[i];
|
|
352
|
+
if (comment) {
|
|
353
|
+
lines.push(`# ${comment}`);
|
|
354
|
+
}
|
|
355
|
+
if (cmd !== undefined) {
|
|
356
|
+
lines.push(cmd);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return lines.join("\n");
|
|
361
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commit Command
|
|
3
|
+
*
|
|
4
|
+
* Quick Git commit command with optional auto-generated message.
|
|
5
|
+
* Inspired by Aider's /commit command pattern.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* - /commit - Stage all changes and commit with auto-generated message
|
|
9
|
+
* - /commit "message" - Stage all changes and commit with specified message
|
|
10
|
+
* - /commit --amend - Amend the last commit
|
|
11
|
+
*
|
|
12
|
+
* @module cli/commands/commit
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { execSync } from "node:child_process";
|
|
16
|
+
import type { CommandContext, CommandResult, SlashCommand } from "./types.js";
|
|
17
|
+
import { error, success } from "./types.js";
|
|
18
|
+
|
|
19
|
+
// =============================================================================
|
|
20
|
+
// Git Utilities
|
|
21
|
+
// =============================================================================
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Execute a git command and return the result.
|
|
25
|
+
*/
|
|
26
|
+
function runGitCommand(command: string, cwd?: string): { success: boolean; output: string } {
|
|
27
|
+
try {
|
|
28
|
+
const output = execSync(command, {
|
|
29
|
+
encoding: "utf-8",
|
|
30
|
+
cwd: cwd ?? process.cwd(),
|
|
31
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
32
|
+
});
|
|
33
|
+
return { success: true, output: output.trim() };
|
|
34
|
+
} catch (err) {
|
|
35
|
+
const error = err as { stderr?: string; message?: string };
|
|
36
|
+
return { success: false, output: error.stderr?.trim() ?? error.message ?? "Unknown error" };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if we're in a git repository.
|
|
42
|
+
*/
|
|
43
|
+
function isGitRepo(): boolean {
|
|
44
|
+
const result = runGitCommand("git rev-parse --is-inside-work-tree");
|
|
45
|
+
return result.success && result.output === "true";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get list of staged files.
|
|
50
|
+
*/
|
|
51
|
+
function getStagedFiles(): string[] {
|
|
52
|
+
const result = runGitCommand("git diff --cached --name-only");
|
|
53
|
+
if (!result.success || !result.output) return [];
|
|
54
|
+
return result.output.split("\n").filter((f) => f.trim().length > 0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get list of unstaged/untracked changes.
|
|
59
|
+
*/
|
|
60
|
+
function getUnstagedChanges(): { modified: string[]; untracked: string[] } {
|
|
61
|
+
const statusResult = runGitCommand("git status --porcelain");
|
|
62
|
+
if (!statusResult.success) return { modified: [], untracked: [] };
|
|
63
|
+
|
|
64
|
+
const modified: string[] = [];
|
|
65
|
+
const untracked: string[] = [];
|
|
66
|
+
|
|
67
|
+
for (const line of statusResult.output.split("\n")) {
|
|
68
|
+
if (!line.trim()) continue;
|
|
69
|
+
const status = line.slice(0, 2);
|
|
70
|
+
const file = line.slice(3);
|
|
71
|
+
|
|
72
|
+
if (status.includes("?")) {
|
|
73
|
+
untracked.push(file);
|
|
74
|
+
} else if (status[1] !== " " && status[1] !== "?") {
|
|
75
|
+
modified.push(file);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { modified, untracked };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Generate a simple commit message from staged changes.
|
|
84
|
+
*/
|
|
85
|
+
function generateCommitMessage(stagedFiles: string[]): string {
|
|
86
|
+
if (stagedFiles.length === 0) return "Update files";
|
|
87
|
+
if (stagedFiles.length === 1) {
|
|
88
|
+
const file = stagedFiles[0];
|
|
89
|
+
return `Update ${file}`;
|
|
90
|
+
}
|
|
91
|
+
// Group by directory or type
|
|
92
|
+
const extensions = new Set(stagedFiles.map((f) => f.split(".").pop() ?? "file"));
|
|
93
|
+
if (extensions.size === 1) {
|
|
94
|
+
const ext = [...extensions][0];
|
|
95
|
+
return `Update ${stagedFiles.length} ${ext} files`;
|
|
96
|
+
}
|
|
97
|
+
return `Update ${stagedFiles.length} files`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// =============================================================================
|
|
101
|
+
// Command Definition
|
|
102
|
+
// =============================================================================
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* /commit command - Quick git commit with optional message.
|
|
106
|
+
*
|
|
107
|
+
* Stages all changes and commits. If no message is provided,
|
|
108
|
+
* generates a simple descriptive message based on changed files.
|
|
109
|
+
*/
|
|
110
|
+
export const commitCommand: SlashCommand = {
|
|
111
|
+
name: "commit",
|
|
112
|
+
description: "Stage and commit all changes with optional message",
|
|
113
|
+
kind: "builtin",
|
|
114
|
+
category: "tools",
|
|
115
|
+
aliases: ["ci"],
|
|
116
|
+
positionalArgs: [
|
|
117
|
+
{
|
|
118
|
+
name: "message",
|
|
119
|
+
type: "string",
|
|
120
|
+
description: "Commit message (auto-generated if omitted)",
|
|
121
|
+
required: false,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
namedArgs: [
|
|
125
|
+
{
|
|
126
|
+
name: "amend",
|
|
127
|
+
shorthand: "a",
|
|
128
|
+
type: "boolean",
|
|
129
|
+
description: "Amend the last commit",
|
|
130
|
+
required: false,
|
|
131
|
+
default: false,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "no-stage",
|
|
135
|
+
shorthand: "n",
|
|
136
|
+
type: "boolean",
|
|
137
|
+
description: "Skip staging (commit only already staged files)",
|
|
138
|
+
required: false,
|
|
139
|
+
default: false,
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
examples: [
|
|
143
|
+
"/commit - Auto-generate message and commit all",
|
|
144
|
+
'/commit "Fix login bug" - Commit with specific message',
|
|
145
|
+
"/commit --amend - Amend last commit",
|
|
146
|
+
'/commit --amend "New msg" - Amend with new message',
|
|
147
|
+
],
|
|
148
|
+
|
|
149
|
+
execute: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
150
|
+
// Check if we're in a git repository
|
|
151
|
+
if (!isGitRepo()) {
|
|
152
|
+
return error("OPERATION_NOT_ALLOWED", "Not in a git repository", [
|
|
153
|
+
"Navigate to a git repository first",
|
|
154
|
+
"Use `git init` to initialize a new repository",
|
|
155
|
+
]);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const message = ctx.parsedArgs.positional[0] as string | undefined;
|
|
159
|
+
const amend = ctx.parsedArgs.named["amend"] as boolean | undefined;
|
|
160
|
+
const noStage = ctx.parsedArgs.named["no-stage"] as boolean | undefined;
|
|
161
|
+
|
|
162
|
+
// Stage all changes unless --no-stage is specified
|
|
163
|
+
if (!noStage) {
|
|
164
|
+
const stageResult = runGitCommand("git add -A");
|
|
165
|
+
if (!stageResult.success) {
|
|
166
|
+
return error("INTERNAL_ERROR", `Failed to stage changes: ${stageResult.output}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Check for staged changes
|
|
171
|
+
const stagedFiles = getStagedFiles();
|
|
172
|
+
if (stagedFiles.length === 0 && !amend) {
|
|
173
|
+
const unstaged = getUnstagedChanges();
|
|
174
|
+
if (unstaged.modified.length === 0 && unstaged.untracked.length === 0) {
|
|
175
|
+
return error("OPERATION_NOT_ALLOWED", "Nothing to commit - working tree is clean");
|
|
176
|
+
}
|
|
177
|
+
return error("OPERATION_NOT_ALLOWED", "No staged changes to commit", [
|
|
178
|
+
"Use /commit without --no-stage to auto-stage all changes",
|
|
179
|
+
"Stage files manually with `git add <file>`",
|
|
180
|
+
]);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Generate or use provided message
|
|
184
|
+
const commitMessage = message ?? generateCommitMessage(stagedFiles);
|
|
185
|
+
|
|
186
|
+
// Build commit command
|
|
187
|
+
let gitCmd = `git commit -m "${commitMessage.replace(/"/g, '\\"')}"`;
|
|
188
|
+
if (amend) {
|
|
189
|
+
gitCmd = message
|
|
190
|
+
? `git commit --amend -m "${commitMessage.replace(/"/g, '\\"')}"`
|
|
191
|
+
: "git commit --amend --no-edit";
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Execute commit
|
|
195
|
+
const commitResult = runGitCommand(gitCmd);
|
|
196
|
+
if (!commitResult.success) {
|
|
197
|
+
return error("INTERNAL_ERROR", `Commit failed: ${commitResult.output}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Get the commit hash
|
|
201
|
+
const hashResult = runGitCommand("git rev-parse --short HEAD");
|
|
202
|
+
const commitHash = hashResult.success ? hashResult.output : "unknown";
|
|
203
|
+
|
|
204
|
+
// Build success message
|
|
205
|
+
const lines = [
|
|
206
|
+
amend ? "📝 Amended commit" : "✅ Committed successfully",
|
|
207
|
+
"",
|
|
208
|
+
` Commit: ${commitHash}`,
|
|
209
|
+
` Message: ${commitMessage}`,
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
if (!amend && stagedFiles.length > 0) {
|
|
213
|
+
lines.push(` Files: ${stagedFiles.length} changed`);
|
|
214
|
+
if (stagedFiles.length <= 5) {
|
|
215
|
+
for (const file of stagedFiles) {
|
|
216
|
+
lines.push(` • ${file}`);
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
for (const file of stagedFiles.slice(0, 3)) {
|
|
220
|
+
lines.push(` • ${file}`);
|
|
221
|
+
}
|
|
222
|
+
lines.push(` • ... and ${stagedFiles.length - 3} more`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return success(lines.join("\n"), {
|
|
227
|
+
hash: commitHash,
|
|
228
|
+
message: commitMessage,
|
|
229
|
+
files: stagedFiles,
|
|
230
|
+
amended: amend ?? false,
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export default commitCommand;
|