diff --git a/cli/src/components/ChatMessage.test.tsx b/cli/src/components/ChatMessage.test.tsx
index 73dd68445f..639e145845 100644
--- a/cli/src/components/ChatMessage.test.tsx
+++ b/cli/src/components/ChatMessage.test.tsx
@@ -31,9 +31,9 @@ describe("ChatMessage subagent rendering", () => {
const frame = lastFrame() || ""
expect(frame).toContain("Cline wants to run subagents")
- expect(frame).toContain("├─ Find codebase stats and size")
- expect(frame).toContain("├─ Find funny comments and easter eggs")
- expect(frame).toContain("└─ Find unusual patterns and history")
+ expect(frame).toContain("├─ Find codebase stats and size")
+ expect(frame).toContain("├─ Find funny comments and easter eggs")
+ expect(frame).toContain("└─ Find unusual patterns and history")
})
it("renders subagent progress rows with compact token stats and completion checks", () => {
diff --git a/cli/src/components/SubagentMessage.tsx b/cli/src/components/SubagentMessage.tsx
index 1dab6a6696..46b0158f12 100644
--- a/cli/src/components/SubagentMessage.tsx
+++ b/cli/src/components/SubagentMessage.tsx
@@ -3,6 +3,7 @@ import { Box, Text } from "ink"
import Spinner from "ink-spinner"
import React from "react"
import { COLORS } from "../constants/colors"
+import { useTerminalSize } from "../hooks/useTerminalSize"
import { jsonParseSafe } from "../utils/parser"
interface SubagentMessageProps {
@@ -11,6 +12,9 @@ interface SubagentMessageProps {
mode?: "act" | "plan"
}
+const TREE_PREFIX_WIDTH = 5
+const MIN_PROMPT_WIDTH = 20
+
const DotRow: React.FC<{ children: React.ReactNode; color?: string; flashing?: boolean }> = ({
children,
color,
@@ -58,9 +62,108 @@ function formatSubagentStats(entry: SubagentStatusItem): string {
return `${entry.toolCalls} ${toolUses} · ${tokensUsed} tokens · ${totalCost}`
}
+function wrapPrompt(text: string, width: number): string[] {
+ if (!text) {
+ return [""]
+ }
+
+ const normalizedWidth = Math.max(1, width)
+ const wrappedLines: string[] = []
+ const paragraphs = text.split("\n")
+
+ for (const paragraph of paragraphs) {
+ const words = paragraph.trim().split(/\s+/).filter(Boolean)
+ if (words.length === 0) {
+ wrappedLines.push("")
+ continue
+ }
+
+ let line = ""
+ for (const word of words) {
+ if (!line) {
+ if (word.length <= normalizedWidth) {
+ line = word
+ continue
+ }
+
+ let remaining = word
+ while (remaining.length > normalizedWidth) {
+ wrappedLines.push(remaining.slice(0, normalizedWidth))
+ remaining = remaining.slice(normalizedWidth)
+ }
+ line = remaining
+ continue
+ }
+
+ if (line.length + 1 + word.length <= normalizedWidth) {
+ line = `${line} ${word}`
+ continue
+ }
+
+ wrappedLines.push(line)
+
+ if (word.length <= normalizedWidth) {
+ line = word
+ continue
+ }
+
+ let remaining = word
+ while (remaining.length > normalizedWidth) {
+ wrappedLines.push(remaining.slice(0, normalizedWidth))
+ remaining = remaining.slice(normalizedWidth)
+ }
+ line = remaining
+ }
+
+ if (line) {
+ wrappedLines.push(line)
+ }
+ }
+
+ return wrappedLines.length > 0 ? wrappedLines : [text]
+}
+
+const TreePromptRow: React.FC<{
+ prefix: React.ReactNode
+ continuationPrefix: string
+ prompt: string
+ promptWidth: number
+ color?: string
+}> = ({ prefix, continuationPrefix, prompt, promptWidth, color }) => {
+ const lines = wrapPrompt(prompt, promptWidth)
+
+ return (
+
+ {lines.map((line, index) => (
+
+
+ {index === 0 ? prefix : {continuationPrefix}}
+
+
+ {line}
+
+
+ ))}
+
+ )
+}
+
+const TreeStatsRow: React.FC<{ prefix: string; stats: string }> = ({ prefix, stats }) => (
+
+
+ {prefix}
+
+
+ ⎿ {stats}
+
+
+)
+
export const SubagentMessage: React.FC = ({ message, mode, isStreaming }) => {
const { type, ask, say, text, partial } = message
const toolColor = mode === "plan" ? "yellow" : COLORS.primaryBlue
+ const { columns } = useTerminalSize()
+ const promptWidth = Math.max(MIN_PROMPT_WIDTH, columns - 2 - TREE_PREFIX_WIDTH)
if ((type === "ask" && ask === "use_subagents") || say === "use_subagents") {
const parsed = text
@@ -90,10 +193,16 @@ export const SubagentMessage: React.FC = ({ message, mode,
{prompts.map((prompt, index) => {
const isLastPrompt = index === prompts.length - 1
const branch = isLastPrompt ? "└─" : "├─"
+ const continuationPrefix = isLastPrompt ? " " : "│ "
return (
-
- {branch} {prompt}
-
+ {`${branch} `}}
+ prompt={prompt}
+ promptWidth={promptWidth}
+ />
)
})}
@@ -132,19 +241,26 @@ export const SubagentMessage: React.FC = ({ message, mode,
{items.map((entry, index) => {
const isLastEntry = index === items.length - 1
- const branch = isLastEntry ? "└─ " : "├─ "
- const connector = isLastEntry ? " " : "│ "
+ const branch = isLastEntry ? "└─" : "├─"
+ const continuationPrefix = isLastEntry ? " " : "│ "
const key = `${entry.index}-${index}`
if (entry.status === "completed") {
return (
-
- {branch}✓ {entry.prompt}
-
-
- {connector}⎿ {formatSubagentStats(entry)}
-
+
+ {`${branch} `}
+ ✓
+
+ }
+ prompt={entry.prompt}
+ promptWidth={promptWidth}
+ />
+
)
}
@@ -152,32 +268,44 @@ export const SubagentMessage: React.FC = ({ message, mode,
if (entry.status === "failed") {
return (
-
- {branch}✗ {entry.prompt}
-
-
- {connector}⎿ {formatSubagentStats(entry)}
-
+
+ {`${branch} `}
+ ✗
+
+ }
+ prompt={entry.prompt}
+ promptWidth={promptWidth}
+ />
+
)
}
return (
-
- {branch}
- {entry.status === "running" ? (
-
-
-
- ) : (
- •
- )}
- {entry.prompt}
-
-
- {connector}⎿ {formatSubagentStats(entry)}
-
+
+ {branch}
+ {entry.status === "running" ? (
+
+
+
+ ) : (
+ •
+ )}
+
+ }
+ prompt={entry.prompt}
+ promptWidth={promptWidth}
+ />
+
)
})}