@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,562 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resume Command
|
|
3
|
+
*
|
|
4
|
+
* Resumes a previous session by ID or most recent session.
|
|
5
|
+
* Supports interactive session selection with pagination.
|
|
6
|
+
*
|
|
7
|
+
* @module cli/commands/session/resume
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { select } from "@inquirer/prompts";
|
|
11
|
+
import type { Session, SessionListService, SessionMetadata, StorageManager } from "@vellum/core";
|
|
12
|
+
|
|
13
|
+
import type { CommandContext, CommandResult, SlashCommand } from "../types.js";
|
|
14
|
+
import { error, pending, success } from "../types.js";
|
|
15
|
+
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Short ID Constants
|
|
18
|
+
// =============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Number of characters in a short ID.
|
|
22
|
+
*/
|
|
23
|
+
export const SHORT_ID_LENGTH = 8;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Number of sessions to show per page in interactive selector.
|
|
27
|
+
*/
|
|
28
|
+
export const SESSIONS_PER_PAGE = 10;
|
|
29
|
+
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// Session Selection Utilities
|
|
32
|
+
// =============================================================================
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Options for session selection.
|
|
36
|
+
*/
|
|
37
|
+
export interface SessionSelectOptions {
|
|
38
|
+
/** Storage manager for session data */
|
|
39
|
+
storage: StorageManager;
|
|
40
|
+
/** Session list service for queries */
|
|
41
|
+
listService: SessionListService;
|
|
42
|
+
/** Whether to show sessions from all directories */
|
|
43
|
+
showAllDirs?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Format a date as "MM/dd HH:mm".
|
|
48
|
+
*
|
|
49
|
+
* @param date - Date to format
|
|
50
|
+
* @returns Formatted string like "12/30 14:30"
|
|
51
|
+
*/
|
|
52
|
+
function formatSessionDate(date: Date): string {
|
|
53
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
54
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
55
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
56
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
57
|
+
return `${month}/${day} ${hours}:${minutes}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Format a session metadata for display in the selector.
|
|
62
|
+
*
|
|
63
|
+
* @param session - Session metadata
|
|
64
|
+
* @param index - Display index (1-based)
|
|
65
|
+
* @returns Formatted string like "1. 调试API错误 (12/30 14:30) - 15条消息"
|
|
66
|
+
*/
|
|
67
|
+
export function formatSessionChoice(session: SessionMetadata, index: number): string {
|
|
68
|
+
const date = formatSessionDate(session.lastActive);
|
|
69
|
+
return `${index}. ${session.title} (${date}) - ${session.messageCount}条消息`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Group sessions by working directory.
|
|
74
|
+
*
|
|
75
|
+
* @param sessions - Array of session metadata
|
|
76
|
+
* @returns Map of working directory to sessions
|
|
77
|
+
*/
|
|
78
|
+
export function groupSessionsByDirectory(
|
|
79
|
+
sessions: SessionMetadata[]
|
|
80
|
+
): Map<string, SessionMetadata[]> {
|
|
81
|
+
const grouped = new Map<string, SessionMetadata[]>();
|
|
82
|
+
|
|
83
|
+
for (const session of sessions) {
|
|
84
|
+
const dir = session.workingDirectory;
|
|
85
|
+
const existing = grouped.get(dir);
|
|
86
|
+
if (existing) {
|
|
87
|
+
existing.push(session);
|
|
88
|
+
} else {
|
|
89
|
+
grouped.set(dir, [session]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return grouped;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Interactively select a session from the list.
|
|
98
|
+
*
|
|
99
|
+
* Shows a paginated list of recent sessions for user selection.
|
|
100
|
+
* Returns null if the user cancels the selection.
|
|
101
|
+
*
|
|
102
|
+
* @param options - Selection options
|
|
103
|
+
* @returns Selected session or null if cancelled
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* const session = await selectSession({ storage, listService });
|
|
108
|
+
* if (session) {
|
|
109
|
+
* console.log("Selected:", session.metadata.title);
|
|
110
|
+
* }
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export async function selectSession(options: SessionSelectOptions): Promise<Session | null> {
|
|
114
|
+
const { storage, listService, showAllDirs } = options;
|
|
115
|
+
|
|
116
|
+
// Get recent sessions
|
|
117
|
+
const sessions = await listService.getRecentSessions(SESSIONS_PER_PAGE * 5); // Get more for pagination
|
|
118
|
+
|
|
119
|
+
if (sessions.length === 0) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Build choices for the selector
|
|
124
|
+
interface SessionChoice {
|
|
125
|
+
name: string;
|
|
126
|
+
value: string;
|
|
127
|
+
description?: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const choices: SessionChoice[] = [];
|
|
131
|
+
|
|
132
|
+
if (showAllDirs) {
|
|
133
|
+
// Group by directory
|
|
134
|
+
const grouped = groupSessionsByDirectory(sessions);
|
|
135
|
+
let globalIndex = 1;
|
|
136
|
+
|
|
137
|
+
for (const [dir, dirSessions] of grouped) {
|
|
138
|
+
// Add separator for each directory
|
|
139
|
+
choices.push({
|
|
140
|
+
name: `── ${dir} ──`,
|
|
141
|
+
value: "__separator__",
|
|
142
|
+
description: `${dirSessions.length} 个会话`,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Add sessions in this directory (up to page size)
|
|
146
|
+
const displaySessions = dirSessions.slice(0, SESSIONS_PER_PAGE);
|
|
147
|
+
for (const session of displaySessions) {
|
|
148
|
+
choices.push({
|
|
149
|
+
name: formatSessionChoice(session, globalIndex),
|
|
150
|
+
value: session.id,
|
|
151
|
+
description: session.summary,
|
|
152
|
+
});
|
|
153
|
+
globalIndex++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
// Flat list with pagination
|
|
158
|
+
const displaySessions = sessions.slice(0, SESSIONS_PER_PAGE);
|
|
159
|
+
displaySessions.forEach((session, idx) => {
|
|
160
|
+
choices.push({
|
|
161
|
+
name: formatSessionChoice(session, idx + 1),
|
|
162
|
+
value: session.id,
|
|
163
|
+
description: session.summary,
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Filter out separators for actual selection
|
|
169
|
+
const selectableChoices = choices.filter((c) => c.value !== "__separator__");
|
|
170
|
+
|
|
171
|
+
if (selectableChoices.length === 0) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
const selectedId = await select({
|
|
177
|
+
message: "选择要恢复的会话:",
|
|
178
|
+
choices: selectableChoices,
|
|
179
|
+
pageSize: SESSIONS_PER_PAGE,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Load the selected session
|
|
183
|
+
const session = await storage.load(selectedId);
|
|
184
|
+
return session;
|
|
185
|
+
} catch {
|
|
186
|
+
// User cancelled (Ctrl+C)
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// =============================================================================
|
|
192
|
+
// Session Lookup Utilities
|
|
193
|
+
// =============================================================================
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Result of a session lookup operation.
|
|
197
|
+
*/
|
|
198
|
+
export interface SessionLookupResult {
|
|
199
|
+
/** Whether the lookup was successful */
|
|
200
|
+
ok: boolean;
|
|
201
|
+
/** The found session (if ok is true) */
|
|
202
|
+
session?: Session;
|
|
203
|
+
/** Error message (if ok is false) */
|
|
204
|
+
error?: string;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Options for session lookup operations.
|
|
209
|
+
*/
|
|
210
|
+
export interface SessionLookupOptions {
|
|
211
|
+
/** Storage manager for session data */
|
|
212
|
+
storage: StorageManager;
|
|
213
|
+
/** Session list service for queries */
|
|
214
|
+
listService: SessionListService;
|
|
215
|
+
/** Whether to search all directories */
|
|
216
|
+
searchAllDirs?: boolean;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Finds a session by full ID or short ID (first 8 characters).
|
|
221
|
+
*
|
|
222
|
+
* Short IDs are matched against the prefix of full session IDs.
|
|
223
|
+
* If multiple sessions match a short ID, returns an error.
|
|
224
|
+
*
|
|
225
|
+
* @param id - Full session ID or short ID prefix
|
|
226
|
+
* @param options - Lookup options
|
|
227
|
+
* @returns Session lookup result
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* const result = await findSessionById("abc12345", { storage, listService });
|
|
232
|
+
* if (result.ok && result.session) {
|
|
233
|
+
* console.log(result.session.metadata.title);
|
|
234
|
+
* }
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
export async function findSessionById(
|
|
238
|
+
id: string,
|
|
239
|
+
options: SessionLookupOptions
|
|
240
|
+
): Promise<SessionLookupResult> {
|
|
241
|
+
const { storage } = options;
|
|
242
|
+
|
|
243
|
+
// Try exact match first
|
|
244
|
+
try {
|
|
245
|
+
const session = await storage.load(id);
|
|
246
|
+
return { ok: true, session };
|
|
247
|
+
} catch {
|
|
248
|
+
// Not found by exact ID, continue to short ID search
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// If ID is short, search by prefix
|
|
252
|
+
if (id.length <= SHORT_ID_LENGTH) {
|
|
253
|
+
const index = await storage.getIndex();
|
|
254
|
+
const matches: SessionMetadata[] = [];
|
|
255
|
+
|
|
256
|
+
for (const [sessionId, metadata] of index) {
|
|
257
|
+
if (sessionId.toLowerCase().startsWith(id.toLowerCase())) {
|
|
258
|
+
matches.push(metadata);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (matches.length === 0) {
|
|
263
|
+
return { ok: false, error: `未找到会话: ${id}` };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (matches.length > 1) {
|
|
267
|
+
const matchList = matches.slice(0, 5).map((m) => ` ${m.id.slice(0, 8)} - ${m.title}`);
|
|
268
|
+
return {
|
|
269
|
+
ok: false,
|
|
270
|
+
error: `多个会话匹配 "${id}":\n${matchList.join("\n")}\n请使用更长的ID以区分`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Single match - load the full session
|
|
275
|
+
const firstMatch = matches[0];
|
|
276
|
+
if (!firstMatch) {
|
|
277
|
+
return { ok: false, error: `未找到会话: ${id}` };
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
const session = await storage.load(firstMatch.id);
|
|
281
|
+
return { ok: true, session };
|
|
282
|
+
} catch {
|
|
283
|
+
return { ok: false, error: "会话已损坏,无法恢复" };
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return { ok: false, error: `未找到会话: ${id}` };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Gets the most recent session.
|
|
292
|
+
*
|
|
293
|
+
* @param options - Lookup options
|
|
294
|
+
* @returns Session lookup result
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* ```typescript
|
|
298
|
+
* const result = await getMostRecentSession({ storage, listService });
|
|
299
|
+
* if (result.ok && result.session) {
|
|
300
|
+
* console.log("Most recent:", result.session.metadata.title);
|
|
301
|
+
* }
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
export async function getMostRecentSession(
|
|
305
|
+
options: SessionLookupOptions
|
|
306
|
+
): Promise<SessionLookupResult> {
|
|
307
|
+
const { storage, listService } = options;
|
|
308
|
+
|
|
309
|
+
const recent = await listService.getRecentSessions(1);
|
|
310
|
+
|
|
311
|
+
if (recent.length === 0) {
|
|
312
|
+
return { ok: false, error: "没有可恢复的会话" };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const firstRecent = recent[0];
|
|
316
|
+
if (!firstRecent) {
|
|
317
|
+
return { ok: false, error: "没有可恢复的会话" };
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
const session = await storage.load(firstRecent.id);
|
|
321
|
+
return { ok: true, session };
|
|
322
|
+
} catch {
|
|
323
|
+
return { ok: false, error: "会话已损坏,无法恢复" };
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// =============================================================================
|
|
328
|
+
// Resume Session Event Data
|
|
329
|
+
// =============================================================================
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Event data emitted when a session is resumed.
|
|
333
|
+
*/
|
|
334
|
+
export interface ResumeSessionEventData {
|
|
335
|
+
/** The session being resumed */
|
|
336
|
+
session: Session;
|
|
337
|
+
/** Whether --last flag was used */
|
|
338
|
+
usedLastFlag: boolean;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// =============================================================================
|
|
342
|
+
// T032: ResumeCommand Definition
|
|
343
|
+
// =============================================================================
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Creates the resume command with injected dependencies.
|
|
347
|
+
*
|
|
348
|
+
* This factory allows the command to be created with specific
|
|
349
|
+
* storage and list service instances, enabling testing and
|
|
350
|
+
* different storage backends.
|
|
351
|
+
*
|
|
352
|
+
* @param storage - Storage manager for session data
|
|
353
|
+
* @param listService - Session list service for queries
|
|
354
|
+
* @returns SlashCommand instance
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```typescript
|
|
358
|
+
* const storage = await StorageManager.create();
|
|
359
|
+
* const listService = new SessionListService(storage);
|
|
360
|
+
* const resumeCmd = createResumeCommand(storage, listService);
|
|
361
|
+
* registry.register(resumeCmd);
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
export function createResumeCommand(
|
|
365
|
+
storage: StorageManager,
|
|
366
|
+
listService: SessionListService
|
|
367
|
+
): SlashCommand {
|
|
368
|
+
return {
|
|
369
|
+
name: "resume",
|
|
370
|
+
description: "Resume a previous session",
|
|
371
|
+
kind: "builtin",
|
|
372
|
+
category: "session",
|
|
373
|
+
aliases: ["r", "restore"],
|
|
374
|
+
positionalArgs: [
|
|
375
|
+
{
|
|
376
|
+
name: "session-id",
|
|
377
|
+
type: "string",
|
|
378
|
+
description: "Session ID or short ID (first 8 characters)",
|
|
379
|
+
required: false,
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
namedArgs: [
|
|
383
|
+
{
|
|
384
|
+
name: "last",
|
|
385
|
+
shorthand: "l",
|
|
386
|
+
type: "boolean",
|
|
387
|
+
description: "Resume most recent session",
|
|
388
|
+
required: false,
|
|
389
|
+
default: false,
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: "all",
|
|
393
|
+
shorthand: "a",
|
|
394
|
+
type: "boolean",
|
|
395
|
+
description: "Show sessions from all directories",
|
|
396
|
+
required: false,
|
|
397
|
+
default: false,
|
|
398
|
+
},
|
|
399
|
+
],
|
|
400
|
+
examples: [
|
|
401
|
+
"/resume - Interactive session selector",
|
|
402
|
+
"/resume abc12345 - Resume session by short ID",
|
|
403
|
+
"/resume --last - Resume most recent session",
|
|
404
|
+
"/resume -l - Resume most recent session (short form)",
|
|
405
|
+
"/resume --all - Interactive selector with all directories",
|
|
406
|
+
],
|
|
407
|
+
|
|
408
|
+
execute: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
409
|
+
const sessionId = ctx.parsedArgs.positional[0] as string | undefined;
|
|
410
|
+
const useLast = ctx.parsedArgs.named.last as boolean | undefined;
|
|
411
|
+
const searchAllDirs = ctx.parsedArgs.named.all as boolean | undefined;
|
|
412
|
+
|
|
413
|
+
// Cannot use both session ID and --last
|
|
414
|
+
if (sessionId && useLast) {
|
|
415
|
+
return error("INVALID_ARGUMENT", "不能同时指定会话ID和 --last 标志", [
|
|
416
|
+
"/resume <session-id>",
|
|
417
|
+
"/resume --last",
|
|
418
|
+
]);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const lookupOptions: SessionLookupOptions = {
|
|
422
|
+
storage,
|
|
423
|
+
listService,
|
|
424
|
+
searchAllDirs,
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// Interactive selection: when no session ID and no --last flag
|
|
428
|
+
if (!sessionId && !useLast) {
|
|
429
|
+
return pending({
|
|
430
|
+
message: "正在加载会话列表...",
|
|
431
|
+
showProgress: true,
|
|
432
|
+
promise: (async (): Promise<CommandResult> => {
|
|
433
|
+
const session = await selectSession({
|
|
434
|
+
storage,
|
|
435
|
+
listService,
|
|
436
|
+
showAllDirs: searchAllDirs,
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
if (!session) {
|
|
440
|
+
return error("COMMAND_ABORTED", "已取消选择");
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Emit resume event
|
|
444
|
+
const eventData: ResumeSessionEventData = {
|
|
445
|
+
session,
|
|
446
|
+
usedLastFlag: false,
|
|
447
|
+
};
|
|
448
|
+
ctx.emit("session:resume", eventData);
|
|
449
|
+
|
|
450
|
+
return success(`正在恢复会话: ${session.metadata.title}`, {
|
|
451
|
+
session,
|
|
452
|
+
sessionId: session.metadata.id,
|
|
453
|
+
title: session.metadata.title,
|
|
454
|
+
});
|
|
455
|
+
})(),
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Use pending result for async operation
|
|
460
|
+
return pending({
|
|
461
|
+
message: useLast ? "正在查找最近的会话..." : `正在查找会话: ${sessionId}...`,
|
|
462
|
+
showProgress: true,
|
|
463
|
+
promise: (async (): Promise<CommandResult> => {
|
|
464
|
+
let result: SessionLookupResult;
|
|
465
|
+
|
|
466
|
+
if (useLast) {
|
|
467
|
+
result = await getMostRecentSession(lookupOptions);
|
|
468
|
+
} else if (sessionId) {
|
|
469
|
+
result = await findSessionById(sessionId, lookupOptions);
|
|
470
|
+
} else {
|
|
471
|
+
// This should never happen due to validation above
|
|
472
|
+
return error("INTERNAL_ERROR", "无效的命令状态");
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (!result.ok || !result.session) {
|
|
476
|
+
return error("RESOURCE_NOT_FOUND", result.error ?? "未找到会话");
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const session = result.session;
|
|
480
|
+
|
|
481
|
+
// Emit resume event
|
|
482
|
+
const eventData: ResumeSessionEventData = {
|
|
483
|
+
session,
|
|
484
|
+
usedLastFlag: !!useLast,
|
|
485
|
+
};
|
|
486
|
+
ctx.emit("session:resume", eventData);
|
|
487
|
+
|
|
488
|
+
// Display appropriate message
|
|
489
|
+
const message = useLast
|
|
490
|
+
? `正在恢复最近的会话: ${session.metadata.title}`
|
|
491
|
+
: `正在恢复会话: ${session.metadata.title}`;
|
|
492
|
+
|
|
493
|
+
return success(message, {
|
|
494
|
+
session,
|
|
495
|
+
sessionId: session.metadata.id,
|
|
496
|
+
title: session.metadata.title,
|
|
497
|
+
});
|
|
498
|
+
})(),
|
|
499
|
+
});
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Default resume command (requires initialization with storage).
|
|
506
|
+
*
|
|
507
|
+
* This is a placeholder that throws if executed without proper initialization.
|
|
508
|
+
* Use `createResumeCommand` factory for production use.
|
|
509
|
+
*
|
|
510
|
+
* @example
|
|
511
|
+
* ```typescript
|
|
512
|
+
* // For testing only - use createResumeCommand in production
|
|
513
|
+
* registry.register(resumeCommand);
|
|
514
|
+
* ```
|
|
515
|
+
*/
|
|
516
|
+
export const resumeCommand: SlashCommand = {
|
|
517
|
+
name: "resume",
|
|
518
|
+
description: "Resume a previous session",
|
|
519
|
+
kind: "builtin",
|
|
520
|
+
category: "session",
|
|
521
|
+
aliases: ["r", "restore"],
|
|
522
|
+
positionalArgs: [
|
|
523
|
+
{
|
|
524
|
+
name: "session-id",
|
|
525
|
+
type: "string",
|
|
526
|
+
description: "Session ID or short ID (first 8 characters)",
|
|
527
|
+
required: false,
|
|
528
|
+
},
|
|
529
|
+
],
|
|
530
|
+
namedArgs: [
|
|
531
|
+
{
|
|
532
|
+
name: "last",
|
|
533
|
+
shorthand: "l",
|
|
534
|
+
type: "boolean",
|
|
535
|
+
description: "Resume most recent session",
|
|
536
|
+
required: false,
|
|
537
|
+
default: false,
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
name: "all",
|
|
541
|
+
shorthand: "a",
|
|
542
|
+
type: "boolean",
|
|
543
|
+
description: "Show sessions from all directories",
|
|
544
|
+
required: false,
|
|
545
|
+
default: false,
|
|
546
|
+
},
|
|
547
|
+
],
|
|
548
|
+
examples: [
|
|
549
|
+
"/resume - Interactive session selector",
|
|
550
|
+
"/resume abc12345 - Resume session by short ID",
|
|
551
|
+
"/resume --last - Resume most recent session",
|
|
552
|
+
"/resume -l - Resume most recent session (short form)",
|
|
553
|
+
"/resume --all - Interactive selector with all directories",
|
|
554
|
+
],
|
|
555
|
+
|
|
556
|
+
execute: async (_ctx: CommandContext): Promise<CommandResult> => {
|
|
557
|
+
return error(
|
|
558
|
+
"INTERNAL_ERROR",
|
|
559
|
+
"Resume command not initialized. Use createResumeCommand with storage dependencies."
|
|
560
|
+
);
|
|
561
|
+
},
|
|
562
|
+
};
|