diff --git a/src/auth/setup-ui.tsx b/src/auth/setup-ui.tsx
index 15555e9..cfbab34 100644
--- a/src/auth/setup-ui.tsx
+++ b/src/auth/setup-ui.tsx
@@ -3,16 +3,14 @@
*/
import { hostname } from "node:os";
-import { Box, Text, useApp, useInput } from "ink";
+import { Box, useApp, useInput } from "ink";
import { useState } from "react";
import { AnimatedLogo } from "../cli/components/AnimatedLogo";
import { colors } from "../cli/components/colors";
+import { Text } from "../cli/components/Text";
import { settingsManager } from "../settings-manager";
import { pollForToken, requestDeviceCode } from "./oauth";
-const upArrow = String.fromCharCode(0x2191);
-const downArrow = String.fromCharCode(0x2193);
-
type SetupMode = "menu" | "device-code" | "auth-code" | "self-host" | "done";
interface SetupUIProps {
@@ -189,9 +187,7 @@ export function SetupUI({ onComplete }: SetupUIProps) {
-
- Use {upArrow}/{downArrow} to navigate, Enter to select
-
+ Use ↑/↓ to navigate, Enter to select
);
}
diff --git a/src/cli/App.tsx b/src/cli/App.tsx
index 2ef2c49..ff97b10 100644
--- a/src/cli/App.tsx
+++ b/src/cli/App.tsx
@@ -14,7 +14,7 @@ import type {
} from "@letta-ai/letta-client/resources/agents/messages";
import type { LlmConfig } from "@letta-ai/letta-client/resources/models/models";
import type { StopReasonType } from "@letta-ai/letta-client/resources/runs/runs";
-import { Box, Static, Text } from "ink";
+import { Box, Static } from "ink";
import {
useCallback,
useEffect,
@@ -134,7 +134,6 @@ import { PendingApprovalStub } from "./components/PendingApprovalStub";
import { PinDialog, validateAgentName } from "./components/PinDialog";
import { ProviderSelector } from "./components/ProviderSelector";
import { ReasoningMessage } from "./components/ReasoningMessageRich";
-
import { formatUsageStats } from "./components/SessionStats";
// InlinePlanApproval kept for easy rollback if needed
// import { InlinePlanApproval } from "./components/InlinePlanApproval";
@@ -143,6 +142,7 @@ import { SubagentGroupDisplay } from "./components/SubagentGroupDisplay";
import { SubagentGroupStatic } from "./components/SubagentGroupStatic";
import { SubagentManager } from "./components/SubagentManager";
import { SystemPromptSelector } from "./components/SystemPromptSelector";
+import { Text } from "./components/Text";
import { ToolCallMessage } from "./components/ToolCallMessageRich";
import { ToolsetSelector } from "./components/ToolsetSelector";
import { UserMessage } from "./components/UserMessageRich";
diff --git a/src/cli/components/AdvancedDiffRenderer.tsx b/src/cli/components/AdvancedDiffRenderer.tsx
index c29b1bc..68ada1b 100644
--- a/src/cli/components/AdvancedDiffRenderer.tsx
+++ b/src/cli/components/AdvancedDiffRenderer.tsx
@@ -1,6 +1,6 @@
import { relative } from "node:path";
import * as Diff from "diff";
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { useMemo } from "react";
import {
ADV_DIFF_CONTEXT_LINES,
@@ -10,6 +10,7 @@ import {
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { EditRenderer, MultiEditRenderer, WriteRenderer } from "./DiffRenderer";
+import { Text } from "./Text";
type EditItem = {
old_string: string;
diff --git a/src/cli/components/AgentInfoBar.tsx b/src/cli/components/AgentInfoBar.tsx
index 0373115..77fdac0 100644
--- a/src/cli/components/AgentInfoBar.tsx
+++ b/src/cli/components/AgentInfoBar.tsx
@@ -1,10 +1,11 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import Link from "ink-link";
import { memo, useMemo } from "react";
import { DEFAULT_AGENT_NAME } from "../../constants";
import { settingsManager } from "../../settings-manager";
import { getVersion } from "../../version";
import { colors } from "./colors";
+import { Text } from "./Text";
interface AgentInfoBarProps {
agentId?: string;
diff --git a/src/cli/components/AgentSelector.tsx b/src/cli/components/AgentSelector.tsx
index 7b17b89..90b9f3c 100644
--- a/src/cli/components/AgentSelector.tsx
+++ b/src/cli/components/AgentSelector.tsx
@@ -1,6 +1,6 @@
import type { Letta } from "@letta-ai/letta-client";
import type { AgentState } from "@letta-ai/letta-client/resources/agents/agents";
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useCallback, useEffect, useRef, useState } from "react";
import { getClient } from "../../agent/client";
import { getModelDisplayName } from "../../agent/model";
@@ -8,6 +8,7 @@ import { settingsManager } from "../../settings-manager";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { MarkdownDisplay } from "./MarkdownDisplay";
+import { Text } from "./Text";
// Horizontal line character (matches approval dialogs)
const SOLID_LINE = "─";
diff --git a/src/cli/components/AnimatedLogo.tsx b/src/cli/components/AnimatedLogo.tsx
index 6e423ab..58137fb 100644
--- a/src/cli/components/AnimatedLogo.tsx
+++ b/src/cli/components/AnimatedLogo.tsx
@@ -1,106 +1,95 @@
-import { Text } from "ink";
import { useEffect, useState } from "react";
import { colors } from "./colors";
-
-function fixBunEncoding(text: string): string {
- if (typeof Bun === "undefined") return text;
-
- // Replace literal characters with Unicode codepoints
- return text
- .replace(/█/g, String.fromCharCode(0x2588)) // Full block
- .replace(/▓/g, String.fromCharCode(0x2593)) // Dark shade
- .replace(/▒/g, String.fromCharCode(0x2592)) // Medium shade
- .replace(/░/g, String.fromCharCode(0x2591)); // Light shade
-}
+import { Text } from "./Text";
// Define animation frames - 3D rotation effect with gradient (█ → ▓ → ▒ → ░)
// Each frame is ~10 chars wide, 5 lines tall - matches login dialog asciiLogo size
const logoFrames = [
// 1. Front view (fully facing)
- ` ██████
+ ` ██████
██ ██
██ ██ ██
██ ██
██████ `,
// 2. Just starting to turn right
- ` ▓█████
+ ` ▓█████
▓█ ▓█
▓█ ▓█ ▓█
▓█ ▓█
▓█████ `,
// 3. Slight right turn
- ` ▓▓████
+ ` ▓▓████
▓▓ ▓▓
▓▓ ▓▓ ▓▓
▓▓ ▓▓
▓▓████ `,
// 4. More right (gradient deepening)
- ` ░▓▓███
+ ` ░▓▓███
░▓▓ ░▓▓
░▓▓ ░▓ ░▓▓
░▓▓ ░▓▓
░▓▓███ `,
// 5. Even more right
- ` ░░▓▓██
- ░▓▓ ░▓▓
- ░▓▓░▓░▓▓
- ░▓▓ ░▓▓
+ ` ░░▓▓██
+ ░▓▓ ░▓▓
+ ░▓▓░▓░▓▓
+ ░▓▓ ░▓▓
░░▓▓██ `,
// 6. Approaching side
- ` ░▓▓█
- ░░▓░░▓
- ░░▓▓░▓
- ░░▓░░▓
+ ` ░▓▓█
+ ░░▓░░▓
+ ░░▓▓░▓
+ ░░▓░░▓
░▓▓█ `,
// 7. Almost side
- ` ░▓▓▓
- ░▓░▓
- ░▓▓▓
- ░▓░▓
+ ` ░▓▓▓
+ ░▓░▓
+ ░▓▓▓
+ ░▓░▓
░▓▓▓ `,
// 8. Side view
- ` ▓▓▓▓
- ▓▓▓▓
- ▓▓▓▓
- ▓▓▓▓
+ ` ▓▓▓▓
+ ▓▓▓▓
+ ▓▓▓▓
+ ▓▓▓▓
▓▓▓▓ `,
// 9. Leaving side (mirror of 7)
- ` ▓▓▓░
- ▓░▓░
- ▓▓▓░
- ▓░▓░
+ ` ▓▓▓░
+ ▓░▓░
+ ▓▓▓░
+ ▓░▓░
▓▓▓░ `,
// 10. Past side (mirror of 6)
- ` █▓▓░
- ▓░░▓░░
- ▓░▓▓░░
- ▓░░▓░░
+ ` █▓▓░
+ ▓░░▓░░
+ ▓░▓▓░░
+ ▓░░▓░░
█▓▓░ `,
// 11. More past side (mirror of 5)
- ` ██▓▓░░
- ▓▓░ ▓▓░
- ▓▓░▓░▓▓░
- ▓▓░ ▓▓░
+ ` ██▓▓░░
+ ▓▓░ ▓▓░
+ ▓▓░▓░▓▓░
+ ▓▓░ ▓▓░
██▓▓░░ `,
// 12. Returning (mirror of 4)
- ` ███▓▓░
+ ` ███▓▓░
▓▓░ ▓▓░
▓▓░ ▓░ ▓▓░
▓▓░ ▓▓░
███▓▓░ `,
// 13. Almost front (mirror of 3)
- ` ████▓▓
+ ` ████▓▓
▓▓ ▓▓
▓▓ ▓▓ ▓▓
▓▓ ▓▓
████▓▓ `,
// 14. Nearly front (mirror of 2)
- ` █████▓
+ ` █████▓
█▓ █▓
█▓ █▓ █▓
█▓ █▓
█████▓ `,
-].map(fixBunEncoding);
+];
interface AnimatedLogoProps {
color?: string;
diff --git a/src/cli/components/ApprovalDialog.tsx b/src/cli/components/ApprovalDialog.tsx
index 27d03a9..43b2584 100644
--- a/src/cli/components/ApprovalDialog.tsx
+++ b/src/cli/components/ApprovalDialog.tsx
@@ -1,10 +1,11 @@
// Import useInput from vendored Ink for bracketed paste support
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import RawTextInput from "ink-text-input";
import { type ComponentType, useMemo, useState } from "react";
import { type AdvancedDiffSuccess, computeAdvancedDiff } from "../helpers/diff";
import type { ApprovalRequest } from "../helpers/stream";
import { AdvancedDiffRenderer } from "./AdvancedDiffRenderer";
+import { Text } from "./Text";
type Props = {
approvalRequest: ApprovalRequest;
diff --git a/src/cli/components/ApprovalDialogRich.tsx b/src/cli/components/ApprovalDialogRich.tsx
index 4e1ac53..5ca9718 100644
--- a/src/cli/components/ApprovalDialogRich.tsx
+++ b/src/cli/components/ApprovalDialogRich.tsx
@@ -1,5 +1,5 @@
// Import useInput from vendored Ink for bracketed paste support
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import type React from "react";
import { memo, useEffect, useMemo, useState } from "react";
import type { ApprovalContext } from "../../permissions/analyzer";
@@ -14,6 +14,7 @@ import type { ApprovalRequest } from "../helpers/stream";
import { AdvancedDiffRenderer } from "./AdvancedDiffRenderer";
import { colors } from "./colors";
import { PasteAwareTextInput } from "./PasteAwareTextInput";
+import { Text } from "./Text";
type Props = {
approvals: ApprovalRequest[];
diff --git a/src/cli/components/ApprovalPreview.tsx b/src/cli/components/ApprovalPreview.tsx
index 8ffe90d..2b67906 100644
--- a/src/cli/components/ApprovalPreview.tsx
+++ b/src/cli/components/ApprovalPreview.tsx
@@ -1,4 +1,4 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import type { AdvancedDiffSuccess } from "../helpers/diff";
import { parsePatchOperations } from "../helpers/formatArgsDisplay";
@@ -7,6 +7,7 @@ import { AdvancedDiffRenderer } from "./AdvancedDiffRenderer";
import { colors } from "./colors";
import { BashPreview } from "./previews/BashPreview";
import { PlanPreview } from "./previews/PlanPreview";
+import { Text } from "./Text";
const SOLID_LINE = "─";
const DOTTED_LINE = "╌";
diff --git a/src/cli/components/AssistantMessage.tsx b/src/cli/components/AssistantMessage.tsx
index fcc8b25..933a8db 100644
--- a/src/cli/components/AssistantMessage.tsx
+++ b/src/cli/components/AssistantMessage.tsx
@@ -1,5 +1,5 @@
-import { Text } from "ink";
import { memo } from "react";
+import { Text } from "./Text";
type AssistantLine = {
kind: "assistant";
diff --git a/src/cli/components/AssistantMessageRich.tsx b/src/cli/components/AssistantMessageRich.tsx
index b4f50d3..0715b60 100644
--- a/src/cli/components/AssistantMessageRich.tsx
+++ b/src/cli/components/AssistantMessageRich.tsx
@@ -1,7 +1,8 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { MarkdownDisplay } from "./MarkdownDisplay.js";
+import { Text } from "./Text";
// Helper function to normalize text - copied from old codebase
const normalize = (s: string) =>
diff --git a/src/cli/components/Autocomplete.tsx b/src/cli/components/Autocomplete.tsx
index 99311de..b35d309 100644
--- a/src/cli/components/Autocomplete.tsx
+++ b/src/cli/components/Autocomplete.tsx
@@ -1,6 +1,7 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import type { ReactNode } from "react";
import { colors } from "./colors";
+import { Text } from "./Text";
interface AutocompleteBoxProps {
/** Optional header text shown at top of autocomplete */
diff --git a/src/cli/components/BashCommandMessage.tsx b/src/cli/components/BashCommandMessage.tsx
index 93092ce..b6b3d66 100644
--- a/src/cli/components/BashCommandMessage.tsx
+++ b/src/cli/components/BashCommandMessage.tsx
@@ -1,4 +1,4 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { INTERRUPTED_BY_USER } from "../../constants";
import type { StreamingState } from "../helpers/accumulator";
@@ -8,6 +8,7 @@ import { CollapsedOutputDisplay } from "./CollapsedOutputDisplay";
import { colors } from "./colors.js";
import { MarkdownDisplay } from "./MarkdownDisplay.js";
import { StreamingOutputDisplay } from "./StreamingOutputDisplay";
+import { Text } from "./Text";
type BashCommandLine = {
kind: "bash_command";
diff --git a/src/cli/components/BlinkDot.tsx b/src/cli/components/BlinkDot.tsx
index b04e2c9..6ec52c8 100644
--- a/src/cli/components/BlinkDot.tsx
+++ b/src/cli/components/BlinkDot.tsx
@@ -1,7 +1,7 @@
-import { Text } from "ink";
import { memo, useEffect, useState } from "react";
import { useAnimation } from "../contexts/AnimationContext.js";
import { colors } from "./colors.js";
+import { Text } from "./Text";
/**
* A blinking dot indicator for running/pending states.
diff --git a/src/cli/components/CollapsedOutputDisplay.tsx b/src/cli/components/CollapsedOutputDisplay.tsx
index c649ad7..558a2e3 100644
--- a/src/cli/components/CollapsedOutputDisplay.tsx
+++ b/src/cli/components/CollapsedOutputDisplay.tsx
@@ -1,7 +1,8 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { MarkdownDisplay } from "./MarkdownDisplay";
+import { Text } from "./Text";
const DEFAULT_COLLAPSED_LINES = 3;
const PREFIX_WIDTH = 5; // " ⎿ " or " "
diff --git a/src/cli/components/CommandMessage.tsx b/src/cli/components/CommandMessage.tsx
index f0b5625..809512f 100644
--- a/src/cli/components/CommandMessage.tsx
+++ b/src/cli/components/CommandMessage.tsx
@@ -1,9 +1,10 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { BlinkDot } from "./BlinkDot.js";
import { colors } from "./colors.js";
import { MarkdownDisplay } from "./MarkdownDisplay.js";
+import { Text } from "./Text";
type CommandLine = {
kind: "command";
diff --git a/src/cli/components/CompactingAnimation.tsx b/src/cli/components/CompactingAnimation.tsx
index df7af0e..b09fbd9 100644
--- a/src/cli/components/CompactingAnimation.tsx
+++ b/src/cli/components/CompactingAnimation.tsx
@@ -1,5 +1,5 @@
-import { Text } from "ink";
import { memo, useEffect, useState } from "react";
+import { Text } from "./Text";
// Default configuration
const DEFAULT_GARBAGE_CHARS = "._";
diff --git a/src/cli/components/ConversationSelector.tsx b/src/cli/components/ConversationSelector.tsx
index 8528b99..957055f 100644
--- a/src/cli/components/ConversationSelector.tsx
+++ b/src/cli/components/ConversationSelector.tsx
@@ -1,13 +1,14 @@
import type { Letta } from "@letta-ai/letta-client";
import type { Message } from "@letta-ai/letta-client/resources/agents/messages";
import type { Conversation } from "@letta-ai/letta-client/resources/conversations/conversations";
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useCallback, useEffect, useRef, useState } from "react";
import { getClient } from "../../agent/client";
import { SYSTEM_REMINDER_OPEN } from "../../constants";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { MarkdownDisplay } from "./MarkdownDisplay";
+import { Text } from "./Text";
// Horizontal line character (matches approval dialogs)
const SOLID_LINE = "─";
diff --git a/src/cli/components/DiffRenderer.tsx b/src/cli/components/DiffRenderer.tsx
index fecd184..9507b57 100644
--- a/src/cli/components/DiffRenderer.tsx
+++ b/src/cli/components/DiffRenderer.tsx
@@ -1,8 +1,9 @@
import { relative } from "node:path";
import * as Diff from "diff";
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
+import { Text } from "./Text";
// Helper to format path as relative with ../
/**
diff --git a/src/cli/components/EnterPlanModeDialog.tsx b/src/cli/components/EnterPlanModeDialog.tsx
index bc970bf..5503e7a 100644
--- a/src/cli/components/EnterPlanModeDialog.tsx
+++ b/src/cli/components/EnterPlanModeDialog.tsx
@@ -1,7 +1,8 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useState } from "react";
import { useProgressIndicator } from "../hooks/useProgressIndicator";
import { colors } from "./colors";
+import { Text } from "./Text";
type Props = {
onApprove: () => void;
diff --git a/src/cli/components/ErrorMessage.tsx b/src/cli/components/ErrorMessage.tsx
index 46cdf8f..4e2c323 100644
--- a/src/cli/components/ErrorMessage.tsx
+++ b/src/cli/components/ErrorMessage.tsx
@@ -1,5 +1,5 @@
-import { Text } from "ink";
import { memo } from "react";
+import { Text } from "./Text";
type ErrorLine = {
kind: "error";
diff --git a/src/cli/components/ErrorMessageRich.tsx b/src/cli/components/ErrorMessageRich.tsx
index da01e7f..07d5cbf 100644
--- a/src/cli/components/ErrorMessageRich.tsx
+++ b/src/cli/components/ErrorMessageRich.tsx
@@ -1,6 +1,7 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
+import { Text } from "./Text";
type ErrorLine = {
kind: "error";
diff --git a/src/cli/components/EventMessage.tsx b/src/cli/components/EventMessage.tsx
index d677f42..d6e8e0b 100644
--- a/src/cli/components/EventMessage.tsx
+++ b/src/cli/components/EventMessage.tsx
@@ -1,10 +1,11 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { COMPACTION_SUMMARY_HEADER } from "../../constants";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { BlinkDot } from "./BlinkDot.js";
import { CompactingAnimation } from "./CompactingAnimation";
import { colors } from "./colors.js";
+import { Text } from "./Text";
type EventLine = {
kind: "event";
diff --git a/src/cli/components/FeedbackDialog.tsx b/src/cli/components/FeedbackDialog.tsx
index fc6d9d2..09e4699 100644
--- a/src/cli/components/FeedbackDialog.tsx
+++ b/src/cli/components/FeedbackDialog.tsx
@@ -1,8 +1,9 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useState } from "react";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { PasteAwareTextInput } from "./PasteAwareTextInput";
+import { Text } from "./Text";
const SOLID_LINE = "─";
diff --git a/src/cli/components/FileAutocomplete.tsx b/src/cli/components/FileAutocomplete.tsx
index e713f77..041ce11 100644
--- a/src/cli/components/FileAutocomplete.tsx
+++ b/src/cli/components/FileAutocomplete.tsx
@@ -1,9 +1,9 @@
-import { Text } from "ink";
import { useEffect, useRef, useState } from "react";
import { searchFiles } from "../helpers/fileSearch";
import { useAutocompleteNavigation } from "../hooks/useAutocompleteNavigation";
import { AutocompleteBox, AutocompleteItem } from "./Autocomplete";
import { colors } from "./colors";
+import { Text } from "./Text";
import type { AutocompleteProps, FileMatch } from "./types/autocomplete";
// Extract the text after the "@" symbol where the cursor is positioned
diff --git a/src/cli/components/HelpDialog.tsx b/src/cli/components/HelpDialog.tsx
index d00b7c5..c85d6ec 100644
--- a/src/cli/components/HelpDialog.tsx
+++ b/src/cli/components/HelpDialog.tsx
@@ -1,8 +1,9 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useCallback, useEffect, useMemo, useState } from "react";
import { getVersion } from "../../version";
import { commands } from "../commands/registry";
import { colors } from "./colors";
+import { Text } from "./Text";
const PAGE_SIZE = 10;
diff --git a/src/cli/components/HooksManager.tsx b/src/cli/components/HooksManager.tsx
index 032d2f0..9b16b27 100644
--- a/src/cli/components/HooksManager.tsx
+++ b/src/cli/components/HooksManager.tsx
@@ -1,7 +1,7 @@
// src/cli/components/HooksManager.tsx
// Interactive TUI for managing hooks configuration
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useCallback, useEffect, useRef, useState } from "react";
import {
type HookEvent,
@@ -29,6 +29,7 @@ import { settingsManager } from "../../settings-manager";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { PasteAwareTextInput } from "./PasteAwareTextInput";
+import { Text } from "./Text";
// Box drawing characters
const BOX_TOP_LEFT = "╭";
diff --git a/src/cli/components/InlineBashApproval.tsx b/src/cli/components/InlineBashApproval.tsx
index a1b5b2b..3d6c59b 100644
--- a/src/cli/components/InlineBashApproval.tsx
+++ b/src/cli/components/InlineBashApproval.tsx
@@ -1,9 +1,10 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useMemo, useState } from "react";
import { useProgressIndicator } from "../hooks/useProgressIndicator";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { useTextInputCursor } from "../hooks/useTextInputCursor";
import { colors } from "./colors";
+import { Text } from "./Text";
type BashInfo = {
toolName: string;
diff --git a/src/cli/components/InlineEnterPlanModeApproval.tsx b/src/cli/components/InlineEnterPlanModeApproval.tsx
index 1d7f21f..b0645a8 100644
--- a/src/cli/components/InlineEnterPlanModeApproval.tsx
+++ b/src/cli/components/InlineEnterPlanModeApproval.tsx
@@ -1,8 +1,9 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useState } from "react";
import { useProgressIndicator } from "../hooks/useProgressIndicator";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
+import { Text } from "./Text";
type Props = {
onApprove: () => void;
diff --git a/src/cli/components/InlineFileEditApproval.tsx b/src/cli/components/InlineFileEditApproval.tsx
index 79e0005..62c19c6 100644
--- a/src/cli/components/InlineFileEditApproval.tsx
+++ b/src/cli/components/InlineFileEditApproval.tsx
@@ -1,4 +1,4 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useMemo, useState } from "react";
import type { AdvancedDiffSuccess } from "../helpers/diff";
import { parsePatchToAdvancedDiff } from "../helpers/diff";
@@ -8,6 +8,7 @@ import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { useTextInputCursor } from "../hooks/useTextInputCursor";
import { AdvancedDiffRenderer } from "./AdvancedDiffRenderer";
import { colors } from "./colors";
+import { Text } from "./Text";
type FileEditInfo = {
toolName: string;
diff --git a/src/cli/components/InlineGenericApproval.tsx b/src/cli/components/InlineGenericApproval.tsx
index 6393315..68072d5 100644
--- a/src/cli/components/InlineGenericApproval.tsx
+++ b/src/cli/components/InlineGenericApproval.tsx
@@ -1,9 +1,10 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useMemo, useState } from "react";
import { useProgressIndicator } from "../hooks/useProgressIndicator";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { useTextInputCursor } from "../hooks/useTextInputCursor";
import { colors } from "./colors";
+import { Text } from "./Text";
type Props = {
toolName: string;
diff --git a/src/cli/components/InlineMarkdownRenderer.tsx b/src/cli/components/InlineMarkdownRenderer.tsx
index 7afee01..2c2b62d 100644
--- a/src/cli/components/InlineMarkdownRenderer.tsx
+++ b/src/cli/components/InlineMarkdownRenderer.tsx
@@ -1,6 +1,6 @@
-import { Text } from "ink";
import type React from "react";
import { colors } from "./colors.js";
+import { Text } from "./Text";
interface InlineMarkdownProps {
text: string;
diff --git a/src/cli/components/InlinePlanApproval.tsx b/src/cli/components/InlinePlanApproval.tsx
index 7c9cb51..1c056a4 100644
--- a/src/cli/components/InlinePlanApproval.tsx
+++ b/src/cli/components/InlinePlanApproval.tsx
@@ -1,10 +1,11 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useMemo, useState } from "react";
import { useProgressIndicator } from "../hooks/useProgressIndicator";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { useTextInputCursor } from "../hooks/useTextInputCursor";
import { colors } from "./colors";
import { MarkdownDisplay } from "./MarkdownDisplay";
+import { Text } from "./Text";
type Props = {
plan: string;
diff --git a/src/cli/components/InlineQuestionApproval.tsx b/src/cli/components/InlineQuestionApproval.tsx
index fb4047c..c5492ee 100644
--- a/src/cli/components/InlineQuestionApproval.tsx
+++ b/src/cli/components/InlineQuestionApproval.tsx
@@ -1,9 +1,10 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { Fragment, memo, useMemo, useState } from "react";
import { useProgressIndicator } from "../hooks/useProgressIndicator";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { useTextInputCursor } from "../hooks/useTextInputCursor";
import { colors } from "./colors";
+import { Text } from "./Text";
interface QuestionOption {
label: string;
diff --git a/src/cli/components/InlineTaskApproval.tsx b/src/cli/components/InlineTaskApproval.tsx
index a1af3d7..6397262 100644
--- a/src/cli/components/InlineTaskApproval.tsx
+++ b/src/cli/components/InlineTaskApproval.tsx
@@ -1,9 +1,10 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useMemo, useState } from "react";
import { useProgressIndicator } from "../hooks/useProgressIndicator";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { useTextInputCursor } from "../hooks/useTextInputCursor";
import { colors } from "./colors";
+import { Text } from "./Text";
type Props = {
taskInfo: {
diff --git a/src/cli/components/InputRich.tsx b/src/cli/components/InputRich.tsx
index 70bf028..3858961 100644
--- a/src/cli/components/InputRich.tsx
+++ b/src/cli/components/InputRich.tsx
@@ -3,7 +3,7 @@
import { EventEmitter } from "node:events";
import { stdin } from "node:process";
import chalk from "chalk";
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import SpinnerLib from "ink-spinner";
import {
type ComponentType,
@@ -30,6 +30,7 @@ import { InputAssist } from "./InputAssist";
import { PasteAwareTextInput } from "./PasteAwareTextInput";
import { QueuedMessages } from "./QueuedMessages";
import { ShimmerText } from "./ShimmerText";
+import { Text } from "./Text";
// Type assertion for ink-spinner compatibility
const Spinner = SpinnerLib as ComponentType<{ type?: string }>;
diff --git a/src/cli/components/MarkdownDisplay.tsx b/src/cli/components/MarkdownDisplay.tsx
index 7b44edf..4e84044 100644
--- a/src/cli/components/MarkdownDisplay.tsx
+++ b/src/cli/components/MarkdownDisplay.tsx
@@ -1,8 +1,9 @@
-import { Box, Text, Transform } from "ink";
+import { Box, Transform } from "ink";
import type React from "react";
import stringWidth from "string-width";
import { colors, hexToBgAnsi } from "./colors.js";
import { InlineMarkdown } from "./InlineMarkdownRenderer.js";
+import { Text } from "./Text";
interface MarkdownDisplayProps {
text: string;
diff --git a/src/cli/components/McpConnectFlow.tsx b/src/cli/components/McpConnectFlow.tsx
index 02983a6..e171f8e 100644
--- a/src/cli/components/McpConnectFlow.tsx
+++ b/src/cli/components/McpConnectFlow.tsx
@@ -3,9 +3,8 @@
* Flow: Select transport → Enter URL → Connect (OAuth if needed) → Enter name → Create
*/
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useCallback, useState } from "react";
-
import { getClient } from "../../agent/client";
import {
connectMcpServer,
@@ -16,6 +15,7 @@ import {
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { PasteAwareTextInput } from "./PasteAwareTextInput";
+import { Text } from "./Text";
const SOLID_LINE = "─";
diff --git a/src/cli/components/McpSelector.tsx b/src/cli/components/McpSelector.tsx
index c2e0c33..8855190 100644
--- a/src/cli/components/McpSelector.tsx
+++ b/src/cli/components/McpSelector.tsx
@@ -4,11 +4,12 @@ import type {
StreamableHTTPMcpServer,
} from "@letta-ai/letta-client/resources/mcp-servers/mcp-servers";
import type { Tool } from "@letta-ai/letta-client/resources/tools";
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useCallback, useEffect, useState } from "react";
import { getClient } from "../../agent/client";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
+import { Text } from "./Text";
// Horizontal line character (matches approval dialogs)
const SOLID_LINE = "─";
diff --git a/src/cli/components/MemfsTreeViewer.tsx b/src/cli/components/MemfsTreeViewer.tsx
index 9dae8d5..da1feab 100644
--- a/src/cli/components/MemfsTreeViewer.tsx
+++ b/src/cli/components/MemfsTreeViewer.tsx
@@ -1,6 +1,6 @@
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
import { join, relative } from "node:path";
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import Link from "ink-link";
import { useMemo, useState } from "react";
import {
@@ -9,6 +9,7 @@ import {
} from "../../agent/memoryFilesystem";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
+import { Text } from "./Text";
// Line characters
const SOLID_LINE = "─";
diff --git a/src/cli/components/MemoryDiffRenderer.tsx b/src/cli/components/MemoryDiffRenderer.tsx
index 9aa264f..c6dff88 100644
--- a/src/cli/components/MemoryDiffRenderer.tsx
+++ b/src/cli/components/MemoryDiffRenderer.tsx
@@ -1,7 +1,8 @@
import * as Diff from "diff";
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
+import { Text } from "./Text";
interface MemoryDiffRendererProps {
argsText: string;
diff --git a/src/cli/components/MemoryTabViewer.tsx b/src/cli/components/MemoryTabViewer.tsx
index fdcbc0d..94c61e1 100644
--- a/src/cli/components/MemoryTabViewer.tsx
+++ b/src/cli/components/MemoryTabViewer.tsx
@@ -1,11 +1,12 @@
import type { Block } from "@letta-ai/letta-client/resources/agents/blocks";
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import Link from "ink-link";
import { useEffect, useState } from "react";
import { getClient } from "../../agent/client";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { MarkdownDisplay } from "./MarkdownDisplay";
+import { Text } from "./Text";
// Horizontal line character (matches approval dialogs)
const SOLID_LINE = "─";
diff --git a/src/cli/components/MessageSearch.tsx b/src/cli/components/MessageSearch.tsx
index 5148a6b..cc1b828 100644
--- a/src/cli/components/MessageSearch.tsx
+++ b/src/cli/components/MessageSearch.tsx
@@ -1,10 +1,11 @@
import type { Letta } from "@letta-ai/letta-client";
import type { MessageSearchResponse } from "@letta-ai/letta-client/resources/messages";
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useCallback, useEffect, useRef, useState } from "react";
import { getClient } from "../../agent/client";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
+import { Text } from "./Text";
// Horizontal line character (matches approval dialogs)
const SOLID_LINE = "─";
diff --git a/src/cli/components/ModelSelector.tsx b/src/cli/components/ModelSelector.tsx
index c6029f7..0a2520b 100644
--- a/src/cli/components/ModelSelector.tsx
+++ b/src/cli/components/ModelSelector.tsx
@@ -1,5 +1,5 @@
// Import useInput from vendored Ink for bracketed paste support
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
clearAvailableModelsCache,
@@ -9,6 +9,7 @@ import {
import { models } from "../../agent/model";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
+import { Text } from "./Text";
// Horizontal line character (matches approval dialogs)
const SOLID_LINE = "─";
diff --git a/src/cli/components/NewAgentDialog.tsx b/src/cli/components/NewAgentDialog.tsx
index 4fb27ce..b4dcadb 100644
--- a/src/cli/components/NewAgentDialog.tsx
+++ b/src/cli/components/NewAgentDialog.tsx
@@ -1,10 +1,11 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useState } from "react";
import { DEFAULT_AGENT_NAME } from "../../constants";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { PasteAwareTextInput } from "./PasteAwareTextInput";
import { validateAgentName } from "./PinDialog";
+import { Text } from "./Text";
// Horizontal line character (matches other selectors)
const SOLID_LINE = "─";
diff --git a/src/cli/components/PendingApprovalStub.tsx b/src/cli/components/PendingApprovalStub.tsx
index 6622409..cd66e59 100644
--- a/src/cli/components/PendingApprovalStub.tsx
+++ b/src/cli/components/PendingApprovalStub.tsx
@@ -1,5 +1,6 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
+import { Text } from "./Text";
type Props = {
toolName: string;
diff --git a/src/cli/components/PinDialog.tsx b/src/cli/components/PinDialog.tsx
index 421f1cb..1ac82d2 100644
--- a/src/cli/components/PinDialog.tsx
+++ b/src/cli/components/PinDialog.tsx
@@ -1,8 +1,9 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useState } from "react";
import { DEFAULT_AGENT_NAME } from "../../constants";
import { colors } from "./colors";
import { PasteAwareTextInput } from "./PasteAwareTextInput";
+import { Text } from "./Text";
interface PinDialogProps {
currentName: string;
diff --git a/src/cli/components/PlanModeDialog.tsx b/src/cli/components/PlanModeDialog.tsx
index d1334b2..3291655 100644
--- a/src/cli/components/PlanModeDialog.tsx
+++ b/src/cli/components/PlanModeDialog.tsx
@@ -1,10 +1,11 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useState } from "react";
import { resolvePlaceholders } from "../helpers/pasteRegistry";
import { useProgressIndicator } from "../hooks/useProgressIndicator";
import { colors } from "./colors";
import { MarkdownDisplay } from "./MarkdownDisplay";
import { PasteAwareTextInput } from "./PasteAwareTextInput";
+import { Text } from "./Text";
type Props = {
plan: string;
diff --git a/src/cli/components/PlanRenderer.tsx b/src/cli/components/PlanRenderer.tsx
index 17e5201..0d53cb1 100644
--- a/src/cli/components/PlanRenderer.tsx
+++ b/src/cli/components/PlanRenderer.tsx
@@ -1,7 +1,8 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import type React from "react";
import { useTerminalWidth } from "../hooks/useTerminalWidth.js";
import { colors } from "./colors.js";
+import { Text } from "./Text";
interface PlanItem {
step: string;
diff --git a/src/cli/components/ProviderSelector.tsx b/src/cli/components/ProviderSelector.tsx
index 609b5c0..e2c29ca 100644
--- a/src/cli/components/ProviderSelector.tsx
+++ b/src/cli/components/ProviderSelector.tsx
@@ -1,4 +1,4 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useCallback, useEffect, useRef, useState } from "react";
import {
type AuthMethod,
@@ -17,6 +17,7 @@ import {
} from "../../utils/aws-credentials";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
+import { Text } from "./Text";
const SOLID_LINE = "─";
diff --git a/src/cli/components/QueuedMessages.tsx b/src/cli/components/QueuedMessages.tsx
index bbe9cc1..286afee 100644
--- a/src/cli/components/QueuedMessages.tsx
+++ b/src/cli/components/QueuedMessages.tsx
@@ -1,5 +1,6 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
+import { Text } from "./Text";
interface QueuedMessagesProps {
messages: string[];
diff --git a/src/cli/components/ReasoningMessage.tsx b/src/cli/components/ReasoningMessage.tsx
index 4f9fc31..6016583 100644
--- a/src/cli/components/ReasoningMessage.tsx
+++ b/src/cli/components/ReasoningMessage.tsx
@@ -1,5 +1,5 @@
-import { Text } from "ink";
import { memo } from "react";
+import { Text } from "./Text";
type ReasoningLine = {
kind: "reasoning";
diff --git a/src/cli/components/ReasoningMessageRich.tsx b/src/cli/components/ReasoningMessageRich.tsx
index 258134f..1c9d78f 100644
--- a/src/cli/components/ReasoningMessageRich.tsx
+++ b/src/cli/components/ReasoningMessageRich.tsx
@@ -1,7 +1,8 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { MarkdownDisplay } from "./MarkdownDisplay.js";
+import { Text } from "./Text";
// Helper function to normalize text - copied from old codebase
const normalize = (s: string) =>
diff --git a/src/cli/components/ShimmerText.tsx b/src/cli/components/ShimmerText.tsx
index 6d043ff..e1d1148 100644
--- a/src/cli/components/ShimmerText.tsx
+++ b/src/cli/components/ShimmerText.tsx
@@ -1,7 +1,7 @@
import chalk from "chalk";
-import { Text } from "ink";
import { memo } from "react";
import { colors } from "./colors.js";
+import { Text } from "./Text";
interface ShimmerTextProps {
color?: string;
diff --git a/src/cli/components/SlashCommandAutocomplete.tsx b/src/cli/components/SlashCommandAutocomplete.tsx
index 66d97da..c36a354 100644
--- a/src/cli/components/SlashCommandAutocomplete.tsx
+++ b/src/cli/components/SlashCommandAutocomplete.tsx
@@ -1,9 +1,9 @@
-import { Text } from "ink";
import { useEffect, useMemo, useState } from "react";
import { settingsManager } from "../../settings-manager";
import { commands } from "../commands/registry";
import { useAutocompleteNavigation } from "../hooks/useAutocompleteNavigation";
import { AutocompleteBox, AutocompleteItem } from "./Autocomplete";
+import { Text } from "./Text";
import type { AutocompleteProps, CommandMatch } from "./types/autocomplete";
const VISIBLE_COMMANDS = 8; // Number of commands visible at once
diff --git a/src/cli/components/StaticPlanApproval.tsx b/src/cli/components/StaticPlanApproval.tsx
index 5806116..5ccf766 100644
--- a/src/cli/components/StaticPlanApproval.tsx
+++ b/src/cli/components/StaticPlanApproval.tsx
@@ -1,9 +1,10 @@
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useState } from "react";
import { useProgressIndicator } from "../hooks/useProgressIndicator";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { useTextInputCursor } from "../hooks/useTextInputCursor";
import { colors } from "./colors";
+import { Text } from "./Text";
type Props = {
onApprove: () => void;
diff --git a/src/cli/components/StatusMessage.tsx b/src/cli/components/StatusMessage.tsx
index dce4c80..07019fd 100644
--- a/src/cli/components/StatusMessage.tsx
+++ b/src/cli/components/StatusMessage.tsx
@@ -1,7 +1,8 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
+import { Text } from "./Text";
type StatusLine = {
kind: "status";
diff --git a/src/cli/components/StreamingOutputDisplay.tsx b/src/cli/components/StreamingOutputDisplay.tsx
index 27f408b..ec6fb63 100644
--- a/src/cli/components/StreamingOutputDisplay.tsx
+++ b/src/cli/components/StreamingOutputDisplay.tsx
@@ -1,6 +1,7 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo, useEffect, useState } from "react";
import type { StreamingState } from "../helpers/accumulator";
+import { Text } from "./Text";
interface StreamingOutputDisplayProps {
streaming: StreamingState;
diff --git a/src/cli/components/SubagentGroupDisplay.tsx b/src/cli/components/SubagentGroupDisplay.tsx
index 598eeea..8306905 100644
--- a/src/cli/components/SubagentGroupDisplay.tsx
+++ b/src/cli/components/SubagentGroupDisplay.tsx
@@ -15,7 +15,7 @@
* SubagentGroupStatic instead (a pure props-based snapshot with no hooks).
*/
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { memo, useSyncExternalStore } from "react";
import { useAnimation } from "../contexts/AnimationContext.js";
import { formatStats, getTreeChars } from "../helpers/subagentDisplay.js";
@@ -28,6 +28,7 @@ import {
import { useTerminalWidth } from "../hooks/useTerminalWidth.js";
import { BlinkDot } from "./BlinkDot.js";
import { colors } from "./colors.js";
+import { Text } from "./Text";
function formatToolArgs(argsStr: string): string {
try {
diff --git a/src/cli/components/SubagentGroupStatic.tsx b/src/cli/components/SubagentGroupStatic.tsx
index a074b31..8624ea9 100644
--- a/src/cli/components/SubagentGroupStatic.tsx
+++ b/src/cli/components/SubagentGroupStatic.tsx
@@ -13,11 +13,12 @@
* Shows: "Ran N subagents" with final stats (tool count, tokens).
*/
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { formatStats, getTreeChars } from "../helpers/subagentDisplay.js";
import { useTerminalWidth } from "../hooks/useTerminalWidth.js";
import { colors } from "./colors.js";
+import { Text } from "./Text";
// ============================================================================
// Types
diff --git a/src/cli/components/SubagentManager.tsx b/src/cli/components/SubagentManager.tsx
index f4f855c..98d4b58 100644
--- a/src/cli/components/SubagentManager.tsx
+++ b/src/cli/components/SubagentManager.tsx
@@ -2,7 +2,7 @@
* SubagentManager component - displays available subagents
*/
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useEffect, useState } from "react";
import {
AGENTS_DIR,
@@ -13,6 +13,7 @@ import {
type SubagentConfig,
} from "../../agent/subagents";
import { colors } from "./colors";
+import { Text } from "./Text";
interface SubagentManagerProps {
onClose: () => void;
diff --git a/src/cli/components/SystemPromptSelector.tsx b/src/cli/components/SystemPromptSelector.tsx
index 627f399..58da2eb 100644
--- a/src/cli/components/SystemPromptSelector.tsx
+++ b/src/cli/components/SystemPromptSelector.tsx
@@ -1,9 +1,10 @@
// Import useInput from vendored Ink for bracketed paste support
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useMemo, useState } from "react";
import { SYSTEM_PROMPTS } from "../../agent/promptAssets";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
+import { Text } from "./Text";
// Horizontal line character (matches approval dialogs)
const SOLID_LINE = "─";
diff --git a/src/cli/components/Text.tsx b/src/cli/components/Text.tsx
new file mode 100644
index 0000000..aa125d2
--- /dev/null
+++ b/src/cli/components/Text.tsx
@@ -0,0 +1,45 @@
+import { Text as InkText, type TextProps } from "ink";
+import type { ReactNode } from "react";
+
+const isBun = typeof Bun !== "undefined";
+const decoder = new TextDecoder("utf-8", { fatal: false });
+
+function fixBunEncoding(value: ReactNode): ReactNode {
+ if (!isBun) return value;
+
+ if (typeof value === "string") {
+ // Quick check: if no non-ASCII characters, return as-is
+ if (!/[\x80-\xFF]/.test(value)) return value;
+
+ const bytes: number[] = [];
+
+ for (let i = 0; i < value.length; i++) {
+ const code = value.charCodeAt(i);
+
+ // Check for 2-byte UTF-8 sequence: 0xC2 followed by 0x80-0xBF
+ if (code === 0xc2 && i + 1 < value.length) {
+ const nextCode = value.charCodeAt(i + 1);
+ if (nextCode >= 0x80 && nextCode <= 0xbf) {
+ bytes.push(0xc2, nextCode);
+ i++;
+ continue;
+ }
+ }
+
+ bytes.push(code);
+ }
+
+ return decoder.decode(new Uint8Array(bytes));
+ }
+
+ // Handle arrays of children
+ if (Array.isArray(value)) {
+ return value.map(fixBunEncoding);
+ }
+
+ return value;
+}
+
+export function Text({ children, ...props }: TextProps) {
+ return {fixBunEncoding(children)};
+}
diff --git a/src/cli/components/TodoRenderer.tsx b/src/cli/components/TodoRenderer.tsx
index a1fd0e0..d93d453 100644
--- a/src/cli/components/TodoRenderer.tsx
+++ b/src/cli/components/TodoRenderer.tsx
@@ -1,7 +1,8 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import type React from "react";
import { useTerminalWidth } from "../hooks/useTerminalWidth.js";
import { colors } from "./colors.js";
+import { Text } from "./Text";
interface TodoItem {
content: string;
diff --git a/src/cli/components/ToolCallMessageRich.tsx b/src/cli/components/ToolCallMessageRich.tsx
index acdf6db..69e8912 100644
--- a/src/cli/components/ToolCallMessageRich.tsx
+++ b/src/cli/components/ToolCallMessageRich.tsx
@@ -1,6 +1,6 @@
// existsSync, readFileSync removed - no longer needed since plan content
// is shown via StaticPlanApproval during approval, not in tool result
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { INTERRUPTED_BY_USER } from "../../constants";
import { clipToolReturn } from "../../tools/manager.js";
@@ -23,6 +23,7 @@ import {
isTaskTool,
isTodoTool,
} from "../helpers/toolNameMapping.js";
+import { Text } from "./Text";
/**
* Check if tool is AskUserQuestion
diff --git a/src/cli/components/ToolsetSelector.tsx b/src/cli/components/ToolsetSelector.tsx
index 16fef93..5cca7ff 100644
--- a/src/cli/components/ToolsetSelector.tsx
+++ b/src/cli/components/ToolsetSelector.tsx
@@ -1,8 +1,9 @@
// Import useInput from vendored Ink for bracketed paste support
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import { useMemo, useState } from "react";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
+import { Text } from "./Text";
// Horizontal line character (matches approval dialogs)
const SOLID_LINE = "─";
diff --git a/src/cli/components/Transcript.tsx b/src/cli/components/Transcript.tsx
index 01c8057..78c1eaa 100644
--- a/src/cli/components/Transcript.tsx
+++ b/src/cli/components/Transcript.tsx
@@ -1,4 +1,5 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
+import { Text } from "./Text";
export type Row =
| { kind: "user"; text: string; id?: string }
diff --git a/src/cli/components/UserMessage.tsx b/src/cli/components/UserMessage.tsx
index 5cf5403..9c9101a 100644
--- a/src/cli/components/UserMessage.tsx
+++ b/src/cli/components/UserMessage.tsx
@@ -1,5 +1,5 @@
-import { Text } from "ink";
import { memo } from "react";
+import { Text } from "./Text";
type UserLine = {
kind: "user";
diff --git a/src/cli/components/UserMessageRich.tsx b/src/cli/components/UserMessageRich.tsx
index de7418b..0c8d120 100644
--- a/src/cli/components/UserMessageRich.tsx
+++ b/src/cli/components/UserMessageRich.tsx
@@ -1,9 +1,9 @@
-import { Text } from "ink";
import { memo } from "react";
import stringWidth from "string-width";
import { SYSTEM_REMINDER_CLOSE, SYSTEM_REMINDER_OPEN } from "../../constants";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors, hexToBgAnsi, hexToFgAnsi } from "./colors";
+import { Text } from "./Text";
type UserLine = {
kind: "user";
diff --git a/src/cli/components/WelcomeScreen.tsx b/src/cli/components/WelcomeScreen.tsx
index 82e9158..03666b4 100644
--- a/src/cli/components/WelcomeScreen.tsx
+++ b/src/cli/components/WelcomeScreen.tsx
@@ -1,8 +1,7 @@
import { homedir } from "node:os";
import type { Letta } from "@letta-ai/letta-client";
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { useEffect, useState } from "react";
-
import type { AgentProvenance } from "../../agent/create";
import { getModelDisplayName } from "../../agent/model";
import { settingsManager } from "../../settings-manager";
@@ -10,6 +9,7 @@ import { getVersion } from "../../version";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { AnimatedLogo } from "./AnimatedLogo";
import { colors } from "./colors";
+import { Text } from "./Text";
/**
* Convert absolute path to use ~ for home directory
diff --git a/src/cli/components/previews/BashPreview.tsx b/src/cli/components/previews/BashPreview.tsx
index 06e8da1..a47ff40 100644
--- a/src/cli/components/previews/BashPreview.tsx
+++ b/src/cli/components/previews/BashPreview.tsx
@@ -1,7 +1,8 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { useTerminalWidth } from "../../hooks/useTerminalWidth";
import { colors } from "../colors";
+import { Text } from "../Text";
const SOLID_LINE = "─";
diff --git a/src/cli/components/previews/PlanPreview.tsx b/src/cli/components/previews/PlanPreview.tsx
index 2fa770e..161cbac 100644
--- a/src/cli/components/previews/PlanPreview.tsx
+++ b/src/cli/components/previews/PlanPreview.tsx
@@ -1,8 +1,9 @@
-import { Box, Text } from "ink";
+import { Box } from "ink";
import { memo } from "react";
import { useTerminalWidth } from "../../hooks/useTerminalWidth";
import { colors } from "../colors";
import { MarkdownDisplay } from "../MarkdownDisplay";
+import { Text } from "../Text";
const SOLID_LINE = "─";
const DOTTED_LINE = "╌";
diff --git a/src/cli/profile-selection.tsx b/src/cli/profile-selection.tsx
index 41ea9b7..f5ef1b5 100644
--- a/src/cli/profile-selection.tsx
+++ b/src/cli/profile-selection.tsx
@@ -4,11 +4,12 @@
*/
import type { AgentState } from "@letta-ai/letta-client/resources/agents/agents";
-import { Box, Text, useInput } from "ink";
+import { Box, useInput } from "ink";
import React, { useCallback, useEffect, useState } from "react";
import { getClient } from "../agent/client";
import { settingsManager } from "../settings-manager";
import { colors } from "./components/colors";
+import { Text } from "./components/Text";
import { WelcomeScreen } from "./components/WelcomeScreen";
interface ProfileOption {