@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,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PermissionDialog Component Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the PermissionDialog component which handles tool permission
|
|
5
|
+
* requests with approval/rejection flows and risk level display.
|
|
6
|
+
*
|
|
7
|
+
* Note: ink-testing-library's stdin.write() does not synchronously trigger
|
|
8
|
+
* useInput hooks. Tests focus on:
|
|
9
|
+
* - Rendering behavior (verifiable via lastFrame())
|
|
10
|
+
* - Props contract verification
|
|
11
|
+
* - Visual state assertions
|
|
12
|
+
*
|
|
13
|
+
* Behavioral tests (approval/rejection flows) are validated via:
|
|
14
|
+
* - Integration tests at the CLI level
|
|
15
|
+
* - Manual testing documentation
|
|
16
|
+
*
|
|
17
|
+
* @module tui/components/Tools/__tests__/PermissionDialog.test
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { render } from "ink-testing-library";
|
|
21
|
+
import type React from "react";
|
|
22
|
+
import { describe, expect, it, vi } from "vitest";
|
|
23
|
+
import type { ToolExecution } from "../../../context/ToolsContext.js";
|
|
24
|
+
import { ThemeProvider } from "../../../theme/index.js";
|
|
25
|
+
import { PermissionDialog, type RiskLevel } from "../PermissionDialog.js";
|
|
26
|
+
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// Test Helpers
|
|
29
|
+
// =============================================================================
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Wrap component with ThemeProvider for testing.
|
|
33
|
+
*/
|
|
34
|
+
function renderWithTheme(ui: React.ReactElement) {
|
|
35
|
+
return render(<ThemeProvider>{ui}</ThemeProvider>);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create a test tool execution with default values.
|
|
40
|
+
*/
|
|
41
|
+
function createTestExecution(overrides: Partial<ToolExecution> = {}): ToolExecution {
|
|
42
|
+
return {
|
|
43
|
+
id: "test-exec-1",
|
|
44
|
+
toolName: "test_tool",
|
|
45
|
+
params: {},
|
|
46
|
+
status: "pending",
|
|
47
|
+
...overrides,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Tests
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
describe("PermissionDialog", () => {
|
|
56
|
+
describe("risk level display", () => {
|
|
57
|
+
it("displays low risk badge with green styling", () => {
|
|
58
|
+
const execution = createTestExecution();
|
|
59
|
+
const onApprove = vi.fn();
|
|
60
|
+
const onReject = vi.fn();
|
|
61
|
+
|
|
62
|
+
const { lastFrame } = renderWithTheme(
|
|
63
|
+
<PermissionDialog
|
|
64
|
+
execution={execution}
|
|
65
|
+
riskLevel="low"
|
|
66
|
+
onApprove={onApprove}
|
|
67
|
+
onReject={onReject}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
expect(lastFrame()).toContain("Low Risk");
|
|
72
|
+
expect(lastFrame()).toContain("●");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("displays medium risk badge with yellow styling", () => {
|
|
76
|
+
const execution = createTestExecution();
|
|
77
|
+
const onApprove = vi.fn();
|
|
78
|
+
const onReject = vi.fn();
|
|
79
|
+
|
|
80
|
+
const { lastFrame } = renderWithTheme(
|
|
81
|
+
<PermissionDialog
|
|
82
|
+
execution={execution}
|
|
83
|
+
riskLevel="medium"
|
|
84
|
+
onApprove={onApprove}
|
|
85
|
+
onReject={onReject}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
expect(lastFrame()).toContain("Medium Risk");
|
|
90
|
+
expect(lastFrame()).toContain("▲");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("displays high risk badge with orange styling", () => {
|
|
94
|
+
const execution = createTestExecution();
|
|
95
|
+
const onApprove = vi.fn();
|
|
96
|
+
const onReject = vi.fn();
|
|
97
|
+
|
|
98
|
+
const { lastFrame } = renderWithTheme(
|
|
99
|
+
<PermissionDialog
|
|
100
|
+
execution={execution}
|
|
101
|
+
riskLevel="high"
|
|
102
|
+
onApprove={onApprove}
|
|
103
|
+
onReject={onReject}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
expect(lastFrame()).toContain("High Risk");
|
|
108
|
+
expect(lastFrame()).toContain("◆");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("displays critical risk badge with red styling", () => {
|
|
112
|
+
const execution = createTestExecution();
|
|
113
|
+
const onApprove = vi.fn();
|
|
114
|
+
const onReject = vi.fn();
|
|
115
|
+
|
|
116
|
+
const { lastFrame } = renderWithTheme(
|
|
117
|
+
<PermissionDialog
|
|
118
|
+
execution={execution}
|
|
119
|
+
riskLevel="critical"
|
|
120
|
+
onApprove={onApprove}
|
|
121
|
+
onReject={onReject}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
expect(lastFrame()).toContain("Critical Risk");
|
|
126
|
+
expect(lastFrame()).toContain("⬢");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("displays all risk levels correctly", () => {
|
|
130
|
+
const riskLevels: RiskLevel[] = ["low", "medium", "high", "critical"];
|
|
131
|
+
const expectedLabels = ["Low Risk", "Medium Risk", "High Risk", "Critical Risk"];
|
|
132
|
+
const expectedIcons = ["●", "▲", "◆", "⬢"];
|
|
133
|
+
|
|
134
|
+
riskLevels.forEach((level, index) => {
|
|
135
|
+
const execution = createTestExecution();
|
|
136
|
+
const { lastFrame } = renderWithTheme(
|
|
137
|
+
<PermissionDialog
|
|
138
|
+
execution={execution}
|
|
139
|
+
riskLevel={level}
|
|
140
|
+
onApprove={vi.fn()}
|
|
141
|
+
onReject={vi.fn()}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
expect(lastFrame()).toContain(expectedLabels[index]);
|
|
146
|
+
expect(lastFrame()).toContain(expectedIcons[index]);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("dialog content", () => {
|
|
152
|
+
it("displays tool permission request header", () => {
|
|
153
|
+
const execution = createTestExecution();
|
|
154
|
+
|
|
155
|
+
const { lastFrame } = renderWithTheme(
|
|
156
|
+
<PermissionDialog
|
|
157
|
+
execution={execution}
|
|
158
|
+
riskLevel="low"
|
|
159
|
+
onApprove={vi.fn()}
|
|
160
|
+
onReject={vi.fn()}
|
|
161
|
+
/>
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
// Translation key is used in tests
|
|
165
|
+
expect(lastFrame()).toContain("permission.requestTitle");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("displays tool name", () => {
|
|
169
|
+
const execution = createTestExecution({ toolName: "read_file" });
|
|
170
|
+
|
|
171
|
+
const { lastFrame } = renderWithTheme(
|
|
172
|
+
<PermissionDialog
|
|
173
|
+
execution={execution}
|
|
174
|
+
riskLevel="low"
|
|
175
|
+
onApprove={vi.fn()}
|
|
176
|
+
onReject={vi.fn()}
|
|
177
|
+
/>
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
expect(lastFrame()).toContain("read_file");
|
|
181
|
+
// Translation key is used in tests
|
|
182
|
+
expect(lastFrame()).toContain("permission.tool");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("displays tool parameters when present", () => {
|
|
186
|
+
const execution = createTestExecution({
|
|
187
|
+
toolName: "read_file",
|
|
188
|
+
params: { path: "/test/file.txt", encoding: "utf-8" },
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const { lastFrame } = renderWithTheme(
|
|
192
|
+
<PermissionDialog
|
|
193
|
+
execution={execution}
|
|
194
|
+
riskLevel="low"
|
|
195
|
+
onApprove={vi.fn()}
|
|
196
|
+
onReject={vi.fn()}
|
|
197
|
+
/>
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// Translation key is used in tests
|
|
201
|
+
expect(lastFrame()).toContain("permission.parameters");
|
|
202
|
+
expect(lastFrame()).toContain("path");
|
|
203
|
+
expect(lastFrame()).toContain("/test/file.txt");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("does not display parameters section when params are empty", () => {
|
|
207
|
+
const execution = createTestExecution({ params: {} });
|
|
208
|
+
|
|
209
|
+
const { lastFrame } = renderWithTheme(
|
|
210
|
+
<PermissionDialog
|
|
211
|
+
execution={execution}
|
|
212
|
+
riskLevel="low"
|
|
213
|
+
onApprove={vi.fn()}
|
|
214
|
+
onReject={vi.fn()}
|
|
215
|
+
/>
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// Translation key should not be present
|
|
219
|
+
expect(lastFrame()).not.toContain("permission.parameters");
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe("keybinding hints", () => {
|
|
224
|
+
it("displays approve keybinding hint", () => {
|
|
225
|
+
const execution = createTestExecution();
|
|
226
|
+
|
|
227
|
+
const { lastFrame } = renderWithTheme(
|
|
228
|
+
<PermissionDialog
|
|
229
|
+
execution={execution}
|
|
230
|
+
riskLevel="low"
|
|
231
|
+
onApprove={vi.fn()}
|
|
232
|
+
onReject={vi.fn()}
|
|
233
|
+
/>
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
expect(lastFrame()).toContain("[y/Enter]");
|
|
237
|
+
// Translation key is used in tests
|
|
238
|
+
expect(lastFrame()).toContain("permission.approve");
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("displays reject keybinding hint", () => {
|
|
242
|
+
const execution = createTestExecution();
|
|
243
|
+
|
|
244
|
+
const { lastFrame } = renderWithTheme(
|
|
245
|
+
<PermissionDialog
|
|
246
|
+
execution={execution}
|
|
247
|
+
riskLevel="low"
|
|
248
|
+
onApprove={vi.fn()}
|
|
249
|
+
onReject={vi.fn()}
|
|
250
|
+
/>
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
expect(lastFrame()).toContain("[n/Esc]");
|
|
254
|
+
// Translation key is used in tests
|
|
255
|
+
expect(lastFrame()).toContain("permission.reject");
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("displays always allow hint when onApproveAlways is provided", () => {
|
|
259
|
+
const execution = createTestExecution();
|
|
260
|
+
|
|
261
|
+
const { lastFrame } = renderWithTheme(
|
|
262
|
+
<PermissionDialog
|
|
263
|
+
execution={execution}
|
|
264
|
+
riskLevel="low"
|
|
265
|
+
onApprove={vi.fn()}
|
|
266
|
+
onReject={vi.fn()}
|
|
267
|
+
onApproveAlways={vi.fn()}
|
|
268
|
+
/>
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
expect(lastFrame()).toContain("[a]");
|
|
272
|
+
// Translation key is used in tests
|
|
273
|
+
expect(lastFrame()).toContain("permission.alwaysAllow");
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("does not display always allow hint when onApproveAlways is not provided", () => {
|
|
277
|
+
const execution = createTestExecution();
|
|
278
|
+
|
|
279
|
+
const { lastFrame } = renderWithTheme(
|
|
280
|
+
<PermissionDialog
|
|
281
|
+
execution={execution}
|
|
282
|
+
riskLevel="low"
|
|
283
|
+
onApprove={vi.fn()}
|
|
284
|
+
onReject={vi.fn()}
|
|
285
|
+
/>
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
// Translation key should not be present
|
|
289
|
+
expect(lastFrame()).not.toContain("permission.alwaysAllow");
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
describe("approval flow", () => {
|
|
294
|
+
/**
|
|
295
|
+
* Note: ink-testing-library's stdin.write() does not synchronously trigger useInput hooks.
|
|
296
|
+
* The approval flow (y, Y, Enter keys) is tested via integration tests.
|
|
297
|
+
*
|
|
298
|
+
* Expected behavior:
|
|
299
|
+
* - 'y' or 'Y': calls onApprove
|
|
300
|
+
* - Enter key: calls onApprove
|
|
301
|
+
* - 'a' or 'A': calls onApproveAlways (if provided)
|
|
302
|
+
*/
|
|
303
|
+
it("renders approve keybinding hint for user guidance", () => {
|
|
304
|
+
const execution = createTestExecution();
|
|
305
|
+
|
|
306
|
+
const { lastFrame } = renderWithTheme(
|
|
307
|
+
<PermissionDialog
|
|
308
|
+
execution={execution}
|
|
309
|
+
riskLevel="low"
|
|
310
|
+
onApprove={vi.fn()}
|
|
311
|
+
onReject={vi.fn()}
|
|
312
|
+
/>
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
expect(lastFrame()).toContain("[y/Enter]");
|
|
316
|
+
// Translation key is used in tests
|
|
317
|
+
expect(lastFrame()).toContain("permission.approve");
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("renders always allow option when onApproveAlways callback is provided", () => {
|
|
321
|
+
const execution = createTestExecution();
|
|
322
|
+
|
|
323
|
+
const { lastFrame } = renderWithTheme(
|
|
324
|
+
<PermissionDialog
|
|
325
|
+
execution={execution}
|
|
326
|
+
riskLevel="low"
|
|
327
|
+
onApprove={vi.fn()}
|
|
328
|
+
onReject={vi.fn()}
|
|
329
|
+
onApproveAlways={vi.fn()}
|
|
330
|
+
/>
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
expect(lastFrame()).toContain("[a]");
|
|
334
|
+
// Translation key is used in tests
|
|
335
|
+
expect(lastFrame()).toContain("permission.alwaysAllow");
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("does not render always allow option when callback is not provided", () => {
|
|
339
|
+
const execution = createTestExecution();
|
|
340
|
+
|
|
341
|
+
const { lastFrame } = renderWithTheme(
|
|
342
|
+
<PermissionDialog
|
|
343
|
+
execution={execution}
|
|
344
|
+
riskLevel="low"
|
|
345
|
+
onApprove={vi.fn()}
|
|
346
|
+
onReject={vi.fn()}
|
|
347
|
+
/>
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
// Translation key should not be present
|
|
351
|
+
expect(lastFrame()).not.toContain("permission.alwaysAllow");
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
describe("rejection flow", () => {
|
|
356
|
+
/**
|
|
357
|
+
* Note: ink-testing-library's stdin.write() does not synchronously trigger useInput hooks.
|
|
358
|
+
* The rejection flow (n, N, Escape keys) is tested via integration tests.
|
|
359
|
+
*
|
|
360
|
+
* Expected behavior:
|
|
361
|
+
* - 'n' or 'N': calls onReject
|
|
362
|
+
* - Escape key: calls onReject
|
|
363
|
+
*/
|
|
364
|
+
it("renders reject keybinding hint for user guidance", () => {
|
|
365
|
+
const execution = createTestExecution();
|
|
366
|
+
|
|
367
|
+
const { lastFrame } = renderWithTheme(
|
|
368
|
+
<PermissionDialog
|
|
369
|
+
execution={execution}
|
|
370
|
+
riskLevel="low"
|
|
371
|
+
onApprove={vi.fn()}
|
|
372
|
+
onReject={vi.fn()}
|
|
373
|
+
/>
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
expect(lastFrame()).toContain("[n/Esc]");
|
|
377
|
+
// Translation key is used in tests
|
|
378
|
+
expect(lastFrame()).toContain("permission.reject");
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
describe("focus behavior", () => {
|
|
383
|
+
/**
|
|
384
|
+
* Note: ink-testing-library's stdin.write() does not synchronously trigger useInput hooks.
|
|
385
|
+
* Focus behavior (isFocused prop) is tested via integration tests.
|
|
386
|
+
*
|
|
387
|
+
* Expected behavior:
|
|
388
|
+
* - When isFocused=false: keyboard input is ignored
|
|
389
|
+
* - When isFocused=true (default): keyboard input is processed
|
|
390
|
+
*/
|
|
391
|
+
it("accepts isFocused prop for controlling input responsiveness", () => {
|
|
392
|
+
const execution = createTestExecution();
|
|
393
|
+
|
|
394
|
+
// Should render without errors when isFocused is explicitly false
|
|
395
|
+
const { lastFrame: frame1 } = renderWithTheme(
|
|
396
|
+
<PermissionDialog
|
|
397
|
+
execution={execution}
|
|
398
|
+
riskLevel="low"
|
|
399
|
+
onApprove={vi.fn()}
|
|
400
|
+
onReject={vi.fn()}
|
|
401
|
+
isFocused={false}
|
|
402
|
+
/>
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
// Translation key is used in tests
|
|
406
|
+
expect(frame1()).toContain("permission.requestTitle");
|
|
407
|
+
|
|
408
|
+
// Should render without errors when isFocused is explicitly true
|
|
409
|
+
const { lastFrame: frame2 } = renderWithTheme(
|
|
410
|
+
<PermissionDialog
|
|
411
|
+
execution={execution}
|
|
412
|
+
riskLevel="low"
|
|
413
|
+
onApprove={vi.fn()}
|
|
414
|
+
onReject={vi.fn()}
|
|
415
|
+
isFocused={true}
|
|
416
|
+
/>
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
// Translation key is used in tests
|
|
420
|
+
expect(frame2()).toContain("permission.requestTitle");
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it("uses isFocused=true by default", () => {
|
|
424
|
+
const execution = createTestExecution();
|
|
425
|
+
|
|
426
|
+
// Should render without errors when isFocused is not provided
|
|
427
|
+
const { lastFrame } = renderWithTheme(
|
|
428
|
+
<PermissionDialog
|
|
429
|
+
execution={execution}
|
|
430
|
+
riskLevel="low"
|
|
431
|
+
onApprove={vi.fn()}
|
|
432
|
+
onReject={vi.fn()}
|
|
433
|
+
/>
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
// Translation key is used in tests
|
|
437
|
+
expect(lastFrame()).toContain("permission.requestTitle");
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
describe("double-action prevention", () => {
|
|
442
|
+
/**
|
|
443
|
+
* Note: ink-testing-library's stdin.write() does not synchronously trigger useInput hooks.
|
|
444
|
+
* Double-action prevention is tested via integration tests.
|
|
445
|
+
*
|
|
446
|
+
* Expected behavior:
|
|
447
|
+
* - Only the first valid keypress triggers a callback
|
|
448
|
+
* - Subsequent keypresses are ignored until execution changes
|
|
449
|
+
*/
|
|
450
|
+
it("tracks execution ID to reset handled state on execution change", () => {
|
|
451
|
+
const execution1 = createTestExecution({ id: "exec-1" });
|
|
452
|
+
const execution2 = createTestExecution({ id: "exec-2" });
|
|
453
|
+
|
|
454
|
+
// Both should render without errors
|
|
455
|
+
const { lastFrame: frame1 } = renderWithTheme(
|
|
456
|
+
<PermissionDialog
|
|
457
|
+
execution={execution1}
|
|
458
|
+
riskLevel="low"
|
|
459
|
+
onApprove={vi.fn()}
|
|
460
|
+
onReject={vi.fn()}
|
|
461
|
+
/>
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
// Translation key is used in tests
|
|
465
|
+
expect(frame1()).toContain("permission.requestTitle");
|
|
466
|
+
|
|
467
|
+
const { lastFrame: frame2 } = renderWithTheme(
|
|
468
|
+
<PermissionDialog
|
|
469
|
+
execution={execution2}
|
|
470
|
+
riskLevel="medium"
|
|
471
|
+
onApprove={vi.fn()}
|
|
472
|
+
onReject={vi.fn()}
|
|
473
|
+
/>
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
// Translation key is used in tests
|
|
477
|
+
expect(frame2()).toContain("permission.requestTitle");
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
describe("unknown keys", () => {
|
|
482
|
+
/**
|
|
483
|
+
* Note: ink-testing-library's stdin.write() does not synchronously trigger useInput hooks.
|
|
484
|
+
* Unknown key handling is tested via integration tests.
|
|
485
|
+
*
|
|
486
|
+
* Expected behavior:
|
|
487
|
+
* - Keys other than y, n, a, Enter, Escape are ignored
|
|
488
|
+
* - No callbacks are triggered for unknown keys
|
|
489
|
+
*/
|
|
490
|
+
it("only displays valid keybinding hints", () => {
|
|
491
|
+
const execution = createTestExecution();
|
|
492
|
+
|
|
493
|
+
const { lastFrame } = renderWithTheme(
|
|
494
|
+
<PermissionDialog
|
|
495
|
+
execution={execution}
|
|
496
|
+
riskLevel="low"
|
|
497
|
+
onApprove={vi.fn()}
|
|
498
|
+
onReject={vi.fn()}
|
|
499
|
+
/>
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
// Should only show documented keybindings
|
|
503
|
+
expect(lastFrame()).toContain("[y/Enter]");
|
|
504
|
+
expect(lastFrame()).toContain("[n/Esc]");
|
|
505
|
+
// Should not suggest random keys
|
|
506
|
+
expect(lastFrame()).not.toContain("[x]");
|
|
507
|
+
expect(lastFrame()).not.toContain("[z]");
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
});
|