@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,596 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal Detection Utilities (T060)
|
|
3
|
+
*
|
|
4
|
+
* Detects terminal capabilities for optimal TUI rendering.
|
|
5
|
+
* Supports various terminal emulators and gracefully degrades
|
|
6
|
+
* for unsupported features.
|
|
7
|
+
*
|
|
8
|
+
* @module tui/utils/detectTerminal
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Types
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Terminal type enumeration.
|
|
17
|
+
*/
|
|
18
|
+
export type TerminalType =
|
|
19
|
+
| "iterm2"
|
|
20
|
+
| "kitty"
|
|
21
|
+
| "wezterm"
|
|
22
|
+
| "windows-terminal"
|
|
23
|
+
| "vscode"
|
|
24
|
+
| "alacritty"
|
|
25
|
+
| "hyper"
|
|
26
|
+
| "terminal-app" // macOS Terminal.app
|
|
27
|
+
| "gnome-terminal"
|
|
28
|
+
| "konsole"
|
|
29
|
+
| "xterm"
|
|
30
|
+
| "mintty" // Git Bash, Cygwin
|
|
31
|
+
| "conemu"
|
|
32
|
+
| "cmd"
|
|
33
|
+
| "powershell"
|
|
34
|
+
| "unknown";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Terminal capabilities detected from the environment.
|
|
38
|
+
*/
|
|
39
|
+
export interface TerminalCapabilities {
|
|
40
|
+
/** Terminal type/emulator name */
|
|
41
|
+
readonly terminalType: TerminalType;
|
|
42
|
+
/** Supports true color (24-bit) */
|
|
43
|
+
readonly trueColor: boolean;
|
|
44
|
+
/** Supports 256 colors */
|
|
45
|
+
readonly color256: boolean;
|
|
46
|
+
/** Supports Unicode characters */
|
|
47
|
+
readonly unicode: boolean;
|
|
48
|
+
/** Supports Sixel graphics */
|
|
49
|
+
readonly sixel: boolean;
|
|
50
|
+
/** Supports Kitty graphics protocol */
|
|
51
|
+
readonly kittyGraphics: boolean;
|
|
52
|
+
/** Supports iTerm2 inline images */
|
|
53
|
+
readonly iterm2Images: boolean;
|
|
54
|
+
/** Supports alternate screen buffer */
|
|
55
|
+
readonly alternateBuffer: boolean;
|
|
56
|
+
/** Supports mouse events */
|
|
57
|
+
readonly mouseSupport: boolean;
|
|
58
|
+
/** Supports bracketed paste mode */
|
|
59
|
+
readonly bracketedPaste: boolean;
|
|
60
|
+
/** Supports hyperlinks (OSC 8) */
|
|
61
|
+
readonly hyperlinks: boolean;
|
|
62
|
+
/** Supports styled underlines */
|
|
63
|
+
readonly styledUnderlines: boolean;
|
|
64
|
+
/** Terminal width in columns */
|
|
65
|
+
readonly columns: number;
|
|
66
|
+
/** Terminal height in rows */
|
|
67
|
+
readonly rows: number;
|
|
68
|
+
/** Whether running in CI environment */
|
|
69
|
+
readonly isCI: boolean;
|
|
70
|
+
/** Whether running in a TTY */
|
|
71
|
+
readonly isTTY: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Options for terminal detection.
|
|
76
|
+
*/
|
|
77
|
+
export interface DetectTerminalOptions {
|
|
78
|
+
/** Force specific terminal type (for testing) */
|
|
79
|
+
readonly forceTerminal?: TerminalType;
|
|
80
|
+
/** Force specific color level (for testing) */
|
|
81
|
+
readonly forceColorLevel?: 0 | 1 | 2 | 3;
|
|
82
|
+
/** Override environment variables */
|
|
83
|
+
readonly env?: Record<string, string | undefined>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// =============================================================================
|
|
87
|
+
// Constants
|
|
88
|
+
// =============================================================================
|
|
89
|
+
|
|
90
|
+
/** Default capabilities for unknown terminals */
|
|
91
|
+
const DEFAULT_CAPABILITIES: TerminalCapabilities = {
|
|
92
|
+
terminalType: "unknown",
|
|
93
|
+
trueColor: false,
|
|
94
|
+
color256: false,
|
|
95
|
+
unicode: false,
|
|
96
|
+
sixel: false,
|
|
97
|
+
kittyGraphics: false,
|
|
98
|
+
iterm2Images: false,
|
|
99
|
+
alternateBuffer: true,
|
|
100
|
+
mouseSupport: false,
|
|
101
|
+
bracketedPaste: false,
|
|
102
|
+
hyperlinks: false,
|
|
103
|
+
styledUnderlines: false,
|
|
104
|
+
columns: 80,
|
|
105
|
+
rows: 24,
|
|
106
|
+
isCI: false,
|
|
107
|
+
isTTY: false,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/** CI environment variable names */
|
|
111
|
+
const CI_ENV_VARS = [
|
|
112
|
+
"CI",
|
|
113
|
+
"CONTINUOUS_INTEGRATION",
|
|
114
|
+
"BUILD_NUMBER",
|
|
115
|
+
"GITHUB_ACTIONS",
|
|
116
|
+
"GITLAB_CI",
|
|
117
|
+
"CIRCLECI",
|
|
118
|
+
"TRAVIS",
|
|
119
|
+
"JENKINS_URL",
|
|
120
|
+
"TEAMCITY_VERSION",
|
|
121
|
+
"BUILDKITE",
|
|
122
|
+
"TF_BUILD", // Azure Pipelines
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// Terminal Type Detection
|
|
127
|
+
// =============================================================================
|
|
128
|
+
|
|
129
|
+
interface TerminalEnvVars {
|
|
130
|
+
termProgram: string;
|
|
131
|
+
term: string;
|
|
132
|
+
lcTerminal: string;
|
|
133
|
+
wtSession: string | undefined;
|
|
134
|
+
kitty: string | undefined;
|
|
135
|
+
wezterm: string | undefined;
|
|
136
|
+
alacritty: string | undefined;
|
|
137
|
+
conemu: string | undefined;
|
|
138
|
+
vscodeTerminal: boolean;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Extract terminal-related environment variables */
|
|
142
|
+
function extractTerminalEnvVars(env: Record<string, string | undefined>): TerminalEnvVars {
|
|
143
|
+
return {
|
|
144
|
+
termProgram: env.TERM_PROGRAM?.toLowerCase() ?? "",
|
|
145
|
+
term: env.TERM?.toLowerCase() ?? "",
|
|
146
|
+
lcTerminal: env.LC_TERMINAL?.toLowerCase() ?? "",
|
|
147
|
+
wtSession: env.WT_SESSION,
|
|
148
|
+
kitty: env.KITTY_WINDOW_ID,
|
|
149
|
+
wezterm: env.WEZTERM_PANE,
|
|
150
|
+
alacritty: env.ALACRITTY_WINDOW_ID,
|
|
151
|
+
conemu: env.ConEmuANSI,
|
|
152
|
+
vscodeTerminal: Boolean(env.VSCODE_INJECTION || env.TERM_PROGRAM === "vscode"),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Detect modern GUI terminal emulators */
|
|
157
|
+
function detectModernTerminal(vars: TerminalEnvVars): TerminalType | null {
|
|
158
|
+
if (vars.termProgram === "iterm.app" || vars.lcTerminal === "iterm2") return "iterm2";
|
|
159
|
+
if (vars.kitty || vars.termProgram === "kitty" || vars.term === "xterm-kitty") return "kitty";
|
|
160
|
+
if (vars.wezterm || vars.termProgram === "wezterm") return "wezterm";
|
|
161
|
+
if (vars.wtSession) return "windows-terminal";
|
|
162
|
+
if (vars.vscodeTerminal || vars.termProgram === "vscode") return "vscode";
|
|
163
|
+
if (vars.alacritty || vars.termProgram === "alacritty") return "alacritty";
|
|
164
|
+
if (vars.termProgram === "hyper") return "hyper";
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Detect platform-specific terminals */
|
|
169
|
+
function detectPlatformTerminal(
|
|
170
|
+
vars: TerminalEnvVars,
|
|
171
|
+
env: Record<string, string | undefined>
|
|
172
|
+
): TerminalType | null {
|
|
173
|
+
if (vars.termProgram === "apple_terminal") return "terminal-app";
|
|
174
|
+
if (env.GNOME_TERMINAL_SCREEN || env.VTE_VERSION) return "gnome-terminal";
|
|
175
|
+
if (env.KONSOLE_VERSION) return "konsole";
|
|
176
|
+
if (vars.conemu) return "conemu";
|
|
177
|
+
if (vars.term.includes("mintty") || env.MSYSTEM) return "mintty";
|
|
178
|
+
if (vars.term.includes("xterm")) return "xterm";
|
|
179
|
+
if (env.PROMPT && !env.SHELL) return "cmd";
|
|
180
|
+
if (env.PSModulePath && !vars.wtSession) return "powershell";
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Detect the terminal type from environment variables.
|
|
186
|
+
*/
|
|
187
|
+
function detectTerminalType(env: Record<string, string | undefined>): TerminalType {
|
|
188
|
+
const vars = extractTerminalEnvVars(env);
|
|
189
|
+
|
|
190
|
+
const modernTerminal = detectModernTerminal(vars);
|
|
191
|
+
if (modernTerminal) return modernTerminal;
|
|
192
|
+
|
|
193
|
+
const platformTerminal = detectPlatformTerminal(vars, env);
|
|
194
|
+
if (platformTerminal) return platformTerminal;
|
|
195
|
+
|
|
196
|
+
return "unknown";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// =============================================================================
|
|
200
|
+
// Color Support Detection
|
|
201
|
+
// =============================================================================
|
|
202
|
+
|
|
203
|
+
/** Check forced color settings from environment */
|
|
204
|
+
function checkForcedColorLevel(env: Record<string, string | undefined>): 0 | 1 | 2 | 3 | null {
|
|
205
|
+
if ("NO_COLOR" in env) return 0;
|
|
206
|
+
|
|
207
|
+
const forceColor = env.FORCE_COLOR;
|
|
208
|
+
if (forceColor === "0" || forceColor === "false") return 0;
|
|
209
|
+
if (forceColor === "1" || forceColor === "true") return 1;
|
|
210
|
+
if (forceColor === "2") return 2;
|
|
211
|
+
if (forceColor === "3") return 3;
|
|
212
|
+
|
|
213
|
+
const colorTerm = env.COLORTERM?.toLowerCase() ?? "";
|
|
214
|
+
if (colorTerm === "truecolor" || colorTerm === "24bit") return 3;
|
|
215
|
+
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Get color level for specific terminal type */
|
|
220
|
+
function getTerminalColorLevel(
|
|
221
|
+
terminalType: TerminalType,
|
|
222
|
+
env: Record<string, string | undefined>
|
|
223
|
+
): 0 | 1 | 2 | 3 {
|
|
224
|
+
// Modern terminals with true color
|
|
225
|
+
const trueColorTerminals: TerminalType[] = [
|
|
226
|
+
"iterm2",
|
|
227
|
+
"kitty",
|
|
228
|
+
"wezterm",
|
|
229
|
+
"windows-terminal",
|
|
230
|
+
"vscode",
|
|
231
|
+
"alacritty",
|
|
232
|
+
"hyper",
|
|
233
|
+
"mintty",
|
|
234
|
+
"conemu",
|
|
235
|
+
];
|
|
236
|
+
if (trueColorTerminals.includes(terminalType)) return 3;
|
|
237
|
+
|
|
238
|
+
// VTE-based terminals
|
|
239
|
+
if (terminalType === "gnome-terminal" || terminalType === "konsole") {
|
|
240
|
+
const vteVersion = parseInt(env.VTE_VERSION ?? "0", 10);
|
|
241
|
+
return vteVersion >= 3600 ? 3 : 2;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (terminalType === "terminal-app") return 2;
|
|
245
|
+
|
|
246
|
+
if (terminalType === "xterm") {
|
|
247
|
+
const term = env.TERM ?? "";
|
|
248
|
+
if (term.includes("256color")) return 2;
|
|
249
|
+
if (term.includes("truecolor") || term.includes("24bit")) return 3;
|
|
250
|
+
return 1;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (terminalType === "cmd" || terminalType === "powershell") {
|
|
254
|
+
return process.platform === "win32" ? 2 : 1;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return 0;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Fallback color detection from TERM variable */
|
|
261
|
+
function detectColorFromTerm(env: Record<string, string | undefined>): 0 | 1 | 2 | 3 {
|
|
262
|
+
const term = env.TERM ?? "";
|
|
263
|
+
if (term.includes("256color")) return 2;
|
|
264
|
+
if (term.includes("color")) return 1;
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Detect color support level.
|
|
270
|
+
* Returns: 0 = no color, 1 = basic (16), 2 = 256, 3 = truecolor (16M)
|
|
271
|
+
*/
|
|
272
|
+
function detectColorLevel(
|
|
273
|
+
env: Record<string, string | undefined>,
|
|
274
|
+
terminalType: TerminalType
|
|
275
|
+
): 0 | 1 | 2 | 3 {
|
|
276
|
+
const forcedLevel = checkForcedColorLevel(env);
|
|
277
|
+
if (forcedLevel !== null) return forcedLevel;
|
|
278
|
+
|
|
279
|
+
if (terminalType !== "unknown") {
|
|
280
|
+
return getTerminalColorLevel(terminalType, env);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return detectColorFromTerm(env);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// =============================================================================
|
|
287
|
+
// Feature Detection
|
|
288
|
+
// =============================================================================
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Detect Unicode support.
|
|
292
|
+
*/
|
|
293
|
+
function detectUnicodeSupport(
|
|
294
|
+
env: Record<string, string | undefined>,
|
|
295
|
+
terminalType: TerminalType
|
|
296
|
+
): boolean {
|
|
297
|
+
// Check locale settings
|
|
298
|
+
const lang = env.LANG ?? "";
|
|
299
|
+
const lcAll = env.LC_ALL ?? "";
|
|
300
|
+
const lcCtype = env.LC_CTYPE ?? "";
|
|
301
|
+
|
|
302
|
+
const hasUtf8Locale =
|
|
303
|
+
lang.toLowerCase().includes("utf") ||
|
|
304
|
+
lcAll.toLowerCase().includes("utf") ||
|
|
305
|
+
lcCtype.toLowerCase().includes("utf");
|
|
306
|
+
|
|
307
|
+
if (hasUtf8Locale) {
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Modern terminals generally support Unicode
|
|
312
|
+
const modernTerminals: TerminalType[] = [
|
|
313
|
+
"iterm2",
|
|
314
|
+
"kitty",
|
|
315
|
+
"wezterm",
|
|
316
|
+
"windows-terminal",
|
|
317
|
+
"vscode",
|
|
318
|
+
"alacritty",
|
|
319
|
+
"hyper",
|
|
320
|
+
"gnome-terminal",
|
|
321
|
+
"konsole",
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
return modernTerminals.includes(terminalType);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Detect Sixel graphics support.
|
|
329
|
+
*/
|
|
330
|
+
function detectSixelSupport(terminalType: TerminalType): boolean {
|
|
331
|
+
// Terminals known to support Sixel
|
|
332
|
+
const sixelTerminals: TerminalType[] = [
|
|
333
|
+
"kitty", // Optional
|
|
334
|
+
"wezterm",
|
|
335
|
+
"mintty",
|
|
336
|
+
];
|
|
337
|
+
|
|
338
|
+
return sixelTerminals.includes(terminalType);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Detect Kitty graphics protocol support.
|
|
343
|
+
*/
|
|
344
|
+
function detectKittyGraphicsSupport(terminalType: TerminalType): boolean {
|
|
345
|
+
return terminalType === "kitty" || terminalType === "wezterm";
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Detect iTerm2 inline images support.
|
|
350
|
+
*/
|
|
351
|
+
function detectITerm2ImagesSupport(terminalType: TerminalType): boolean {
|
|
352
|
+
return terminalType === "iterm2" || terminalType === "wezterm";
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Detect hyperlink (OSC 8) support.
|
|
357
|
+
*/
|
|
358
|
+
function detectHyperlinkSupport(terminalType: TerminalType): boolean {
|
|
359
|
+
const hyperlinkTerminals: TerminalType[] = [
|
|
360
|
+
"iterm2",
|
|
361
|
+
"kitty",
|
|
362
|
+
"wezterm",
|
|
363
|
+
"windows-terminal",
|
|
364
|
+
"vscode",
|
|
365
|
+
"gnome-terminal",
|
|
366
|
+
"konsole",
|
|
367
|
+
"alacritty",
|
|
368
|
+
"hyper",
|
|
369
|
+
];
|
|
370
|
+
|
|
371
|
+
return hyperlinkTerminals.includes(terminalType);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Detect styled underlines support.
|
|
376
|
+
*/
|
|
377
|
+
function detectStyledUnderlines(terminalType: TerminalType): boolean {
|
|
378
|
+
const styledUnderlineTerminals: TerminalType[] = [
|
|
379
|
+
"iterm2",
|
|
380
|
+
"kitty",
|
|
381
|
+
"wezterm",
|
|
382
|
+
"vscode",
|
|
383
|
+
"gnome-terminal", // VTE-based
|
|
384
|
+
"konsole",
|
|
385
|
+
];
|
|
386
|
+
|
|
387
|
+
return styledUnderlineTerminals.includes(terminalType);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Detect if running in CI environment.
|
|
392
|
+
*/
|
|
393
|
+
function detectCI(env: Record<string, string | undefined>): boolean {
|
|
394
|
+
return CI_ENV_VARS.some((varName) => varName in env);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Detect PowerShell running on ConPTY-backed terminals.
|
|
399
|
+
* Used to disable alternate buffer by default due to clear/scroll issues.
|
|
400
|
+
*/
|
|
401
|
+
export function isConptyTerminal(
|
|
402
|
+
env: Record<string, string | undefined> = process.env as Record<string, string | undefined>
|
|
403
|
+
): boolean {
|
|
404
|
+
if (process.platform !== "win32") {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const termProgram = env.TERM_PROGRAM?.toLowerCase() ?? "";
|
|
409
|
+
return Boolean(
|
|
410
|
+
env.WT_SESSION || env.VSCODE_INJECTION || env.VSCODE_GIT_IPC_HANDLE || termProgram === "vscode"
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function isPowerShellConptyTerminal(
|
|
415
|
+
env: Record<string, string | undefined> = process.env as Record<string, string | undefined>
|
|
416
|
+
): boolean {
|
|
417
|
+
if (!isConptyTerminal(env)) {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const isPowerShell = Boolean(env.PSModulePath);
|
|
422
|
+
return isPowerShell;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// =============================================================================
|
|
426
|
+
// Main Detection Function
|
|
427
|
+
// =============================================================================
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Detect terminal capabilities.
|
|
431
|
+
*
|
|
432
|
+
* Analyzes the environment to determine what features the current
|
|
433
|
+
* terminal supports. Use this to conditionally enable/disable
|
|
434
|
+
* TUI features based on terminal capabilities.
|
|
435
|
+
*
|
|
436
|
+
* @example
|
|
437
|
+
* ```tsx
|
|
438
|
+
* const capabilities = detectTerminal();
|
|
439
|
+
*
|
|
440
|
+
* // Use true color if available
|
|
441
|
+
* const color = capabilities.trueColor
|
|
442
|
+
* ? '#ff6b6b'
|
|
443
|
+
* : 'red';
|
|
444
|
+
*
|
|
445
|
+
* // Show fancy Unicode if supported
|
|
446
|
+
* const bullet = capabilities.unicode ? '●' : '*';
|
|
447
|
+
*
|
|
448
|
+
* // Disable animations in CI
|
|
449
|
+
* const animate = !capabilities.isCI;
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
452
|
+
export function detectTerminal(options: DetectTerminalOptions = {}): TerminalCapabilities {
|
|
453
|
+
const { forceTerminal, forceColorLevel, env: envOverride } = options;
|
|
454
|
+
|
|
455
|
+
// Use overrides or process.env
|
|
456
|
+
const env = envOverride ?? (process.env as Record<string, string | undefined>);
|
|
457
|
+
|
|
458
|
+
// Get terminal dimensions
|
|
459
|
+
const columns = process.stdout.columns || DEFAULT_CAPABILITIES.columns;
|
|
460
|
+
const rows = process.stdout.rows || DEFAULT_CAPABILITIES.rows;
|
|
461
|
+
|
|
462
|
+
// Check if running in TTY
|
|
463
|
+
const isTTY = process.stdout.isTTY ?? false;
|
|
464
|
+
|
|
465
|
+
// Detect terminal type
|
|
466
|
+
const terminalType = forceTerminal ?? detectTerminalType(env);
|
|
467
|
+
|
|
468
|
+
// Detect color level
|
|
469
|
+
const colorLevel = forceColorLevel ?? detectColorLevel(env, terminalType);
|
|
470
|
+
|
|
471
|
+
// Detect other capabilities
|
|
472
|
+
const isCI = detectCI(env);
|
|
473
|
+
const unicode = detectUnicodeSupport(env, terminalType);
|
|
474
|
+
const sixel = detectSixelSupport(terminalType);
|
|
475
|
+
const kittyGraphics = detectKittyGraphicsSupport(terminalType);
|
|
476
|
+
const iterm2Images = detectITerm2ImagesSupport(terminalType);
|
|
477
|
+
const hyperlinks = detectHyperlinkSupport(terminalType);
|
|
478
|
+
const styledUnderlines = detectStyledUnderlines(terminalType);
|
|
479
|
+
|
|
480
|
+
return {
|
|
481
|
+
terminalType,
|
|
482
|
+
trueColor: colorLevel >= 3,
|
|
483
|
+
color256: colorLevel >= 2,
|
|
484
|
+
unicode,
|
|
485
|
+
sixel,
|
|
486
|
+
kittyGraphics,
|
|
487
|
+
iterm2Images,
|
|
488
|
+
alternateBuffer: isTTY && !isCI && !isConptyTerminal(env),
|
|
489
|
+
mouseSupport: isTTY && !isCI,
|
|
490
|
+
bracketedPaste: isTTY,
|
|
491
|
+
hyperlinks,
|
|
492
|
+
styledUnderlines,
|
|
493
|
+
columns,
|
|
494
|
+
rows,
|
|
495
|
+
isCI,
|
|
496
|
+
isTTY,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// =============================================================================
|
|
501
|
+
// Utility Functions
|
|
502
|
+
// =============================================================================
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Get a human-readable name for the terminal type.
|
|
506
|
+
*/
|
|
507
|
+
export function getTerminalName(type: TerminalType): string {
|
|
508
|
+
const names: Record<TerminalType, string> = {
|
|
509
|
+
iterm2: "iTerm2",
|
|
510
|
+
kitty: "Kitty",
|
|
511
|
+
wezterm: "WezTerm",
|
|
512
|
+
"windows-terminal": "Windows Terminal",
|
|
513
|
+
vscode: "VS Code",
|
|
514
|
+
alacritty: "Alacritty",
|
|
515
|
+
hyper: "Hyper",
|
|
516
|
+
"terminal-app": "Terminal.app",
|
|
517
|
+
"gnome-terminal": "GNOME Terminal",
|
|
518
|
+
konsole: "Konsole",
|
|
519
|
+
xterm: "XTerm",
|
|
520
|
+
mintty: "MinTTY",
|
|
521
|
+
conemu: "ConEmu",
|
|
522
|
+
cmd: "Command Prompt",
|
|
523
|
+
powershell: "PowerShell",
|
|
524
|
+
unknown: "Unknown Terminal",
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
return names[type];
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Create a degraded color based on capabilities.
|
|
532
|
+
*/
|
|
533
|
+
export function degradeColor(
|
|
534
|
+
capabilities: TerminalCapabilities,
|
|
535
|
+
trueColor: string,
|
|
536
|
+
color256: number,
|
|
537
|
+
basicColor: string
|
|
538
|
+
): string {
|
|
539
|
+
if (capabilities.trueColor) {
|
|
540
|
+
return trueColor;
|
|
541
|
+
}
|
|
542
|
+
if (capabilities.color256) {
|
|
543
|
+
return `\x1b[38;5;${color256}m`;
|
|
544
|
+
}
|
|
545
|
+
return basicColor;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Get appropriate symbol based on Unicode support.
|
|
550
|
+
*/
|
|
551
|
+
export function getSymbol(
|
|
552
|
+
capabilities: TerminalCapabilities,
|
|
553
|
+
unicode: string,
|
|
554
|
+
ascii: string
|
|
555
|
+
): string {
|
|
556
|
+
return capabilities.unicode ? unicode : ascii;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Create a hyperlink if supported, otherwise return plain text.
|
|
561
|
+
*/
|
|
562
|
+
export function createHyperlink(
|
|
563
|
+
capabilities: TerminalCapabilities,
|
|
564
|
+
url: string,
|
|
565
|
+
text: string
|
|
566
|
+
): string {
|
|
567
|
+
if (capabilities.hyperlinks) {
|
|
568
|
+
return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
|
|
569
|
+
}
|
|
570
|
+
return text;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// =============================================================================
|
|
574
|
+
// Singleton Instance
|
|
575
|
+
// =============================================================================
|
|
576
|
+
|
|
577
|
+
let cachedCapabilities: TerminalCapabilities | null = null;
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Get cached terminal capabilities (singleton).
|
|
581
|
+
* Use this for performance when you don't need to re-detect.
|
|
582
|
+
*/
|
|
583
|
+
export function getTerminalCapabilities(): TerminalCapabilities {
|
|
584
|
+
if (!cachedCapabilities) {
|
|
585
|
+
cachedCapabilities = detectTerminal();
|
|
586
|
+
}
|
|
587
|
+
return cachedCapabilities;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Clear the cached terminal capabilities.
|
|
592
|
+
* Useful for testing or when terminal conditions change.
|
|
593
|
+
*/
|
|
594
|
+
export function clearTerminalCapabilitiesCache(): void {
|
|
595
|
+
cachedCapabilities = null;
|
|
596
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe Split Point Detection for Markdown Text
|
|
3
|
+
*
|
|
4
|
+
* Finds safe points to split streaming markdown text without
|
|
5
|
+
* breaking code blocks or creating awkward visual splits.
|
|
6
|
+
*
|
|
7
|
+
* Pattern adapted from Gemini CLI: useGeminiStream.ts
|
|
8
|
+
*
|
|
9
|
+
* @module tui/utils/findLastSafeSplitPoint
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Detect if an index is inside a code block.
|
|
14
|
+
*
|
|
15
|
+
* Searches backwards from the given index to find any opening ```.
|
|
16
|
+
* If found, checks if there's a closing ``` after it before our index.
|
|
17
|
+
*
|
|
18
|
+
* @param content - The text to analyze
|
|
19
|
+
* @param index - The index to check
|
|
20
|
+
* @returns true if inside a code block
|
|
21
|
+
*/
|
|
22
|
+
function isIndexInsideCodeBlock(content: string, index: number): boolean {
|
|
23
|
+
// Find all code block markers before the index
|
|
24
|
+
const textBefore = content.slice(0, index);
|
|
25
|
+
const markers = [...textBefore.matchAll(/```/g)];
|
|
26
|
+
|
|
27
|
+
// If odd number of markers, we're inside a code block
|
|
28
|
+
return markers.length % 2 !== 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Find the start of the enclosing code block (if any).
|
|
33
|
+
*
|
|
34
|
+
* @param content - The text to analyze
|
|
35
|
+
* @param index - The index to check
|
|
36
|
+
* @returns Start index of enclosing code block, or -1 if not inside one
|
|
37
|
+
*/
|
|
38
|
+
function findEnclosingCodeBlockStart(content: string, index: number): number {
|
|
39
|
+
if (!isIndexInsideCodeBlock(content, index)) {
|
|
40
|
+
return -1;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Find the last opening ``` before the index
|
|
44
|
+
const textBefore = content.slice(0, index);
|
|
45
|
+
const lastOpening = textBefore.lastIndexOf("```");
|
|
46
|
+
return lastOpening;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Find the last safe point to split markdown text.
|
|
51
|
+
*
|
|
52
|
+
* Newline-gated strategy (like Codex/Gemini CLI):
|
|
53
|
+
* - Only splits at double newlines (\n\n) - paragraph boundaries
|
|
54
|
+
* - Never splits inside code blocks
|
|
55
|
+
* - No arbitrary character limit - waits for natural break points
|
|
56
|
+
*
|
|
57
|
+
* This prevents visual jitter from mid-sentence or mid-paragraph splits.
|
|
58
|
+
*
|
|
59
|
+
* @param text - The text to analyze
|
|
60
|
+
* @param _minLength - Deprecated, kept for API compatibility (ignored)
|
|
61
|
+
* @returns Split index after \n\n, or -1 if no safe split found
|
|
62
|
+
*/
|
|
63
|
+
export function findLastSafeSplitPoint(text: string, _minLength = 0): number {
|
|
64
|
+
// Check if we're currently inside a code block
|
|
65
|
+
const enclosingBlockStart = findEnclosingCodeBlockStart(text, text.length);
|
|
66
|
+
if (enclosingBlockStart !== -1) {
|
|
67
|
+
// End of content is inside a code block - split right before it
|
|
68
|
+
return enclosingBlockStart;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Search for the last double newline (\n\n) not inside a code block
|
|
72
|
+
let searchStartIndex = text.length;
|
|
73
|
+
while (searchStartIndex >= 0) {
|
|
74
|
+
const dnlIndex = text.lastIndexOf("\n\n", searchStartIndex);
|
|
75
|
+
if (dnlIndex === -1) {
|
|
76
|
+
// No more double newlines found
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const potentialSplitPoint = dnlIndex + 2;
|
|
81
|
+
if (!isIndexInsideCodeBlock(text, potentialSplitPoint)) {
|
|
82
|
+
return potentialSplitPoint;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If potentialSplitPoint was inside a code block,
|
|
86
|
+
// search before the \n\n we just found
|
|
87
|
+
searchStartIndex = dnlIndex - 1;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// No safe split point found - don't split
|
|
91
|
+
return -1;
|
|
92
|
+
}
|