/**
 * TUI tool renderers for Hub tools.
 *
 * Each renderer provides a compact renderCall (one-line summary of the invocation)
 * and a renderResult (collapsed / expanded views of the result).
 * Renderers are looked up by tool name and spread into the registerTool() call.
 */
import { Box, Text, Container } from '@mariozechner/pi-tui';
import { formatToolDisplay } from "./agentuity-cli.js";
// ──────────────────────────────────────────────
// Line-safety helper — must be declared before SimpleText so
// render() can reference it without temporal-dead-zone issues.
// ──────────────────────────────────────────────
/** Pre-render safety net — truncate lines before they reach render(). Reduced from 200 to 160. */
const SAFE_LINE_WIDTH = 160;
function safeLine(line) {
    return line.length > SAFE_LINE_WIDTH ? line.slice(0, SAFE_LINE_WIDTH - 3) + '...' : line;
}
/**
 * Truncate a string (possibly containing ANSI escape codes) to fit within
 * a given visible width. Handles escape sequences properly so colours are
 * never broken — a RESET is appended when truncation occurs.
 */
export function truncateToWidth(line, maxWidth) {
    if (maxWidth <= 0)
        return '';
    const normalized = line.replace(/\t/g, '    ');
    // Fast path: no ANSI codes and short enough
    if (normalized.length <= maxWidth && !normalized.includes('\x1b'))
        return normalized;
    // Strip ANSI to measure visible length
    // eslint-disable-next-line no-control-regex
    const visible = normalized.replace(/\x1b\[[0-9;]*m/g, '');
    if (visible.length <= maxWidth)
        return normalized;
    // Need to truncate — walk through respecting ANSI escape sequences
    let vis = 0;
    let i = 0;
    const target = Math.max(0, maxWidth - 3); // room for "..."
    while (i < normalized.length && vis < target) {
        if (normalized[i] === '\x1b') {
            // Skip entire ANSI escape sequence
            const end = normalized.indexOf('m', i);
            if (end !== -1) {
                i = end + 1;
            }
            else {
                i++;
            }
        }
        else {
            vis++;
            i++;
        }
    }
    return normalized.slice(0, i) + '\x1b[0m...';
}
// ──────────────────────────────────────────────
// Lightweight text component used by simpler renderers.
// Complex renderers (task, parallel_tasks) use Box/Text/Container from pi-tui.
// ──────────────────────────────────────────────
export class SimpleText {
    text;
    constructor(text) {
        this.text = text;
    }
    render(width) {
        return this.text.split('\n').map((line) => truncateToWidth(line, width));
    }
    invalidate() {
        // no-op — we don't cache
    }
}
// ──────────────────────────────────────────────
// Helpers
// ──────────────────────────────────────────────
/** Extract plain-text from a tool result's content array. */
function resultText(result) {
    return result.content
        .filter((c) => 'text' in c && typeof c.text === 'string')
        .map((c) => ('text' in c ? c.text : ''))
        .join('\n');
}
/** Attempt to parse result text as JSON, returning undefined on failure. */
function tryParseJson(text) {
    try {
        return JSON.parse(text);
    }
    catch {
        return undefined;
    }
}
/** Truncate a string to a max length, appending '\u2026' when truncated. */
function truncate(str, max) {
    if (str.length <= max)
        return str;
    return str.slice(0, max - 1) + '\u2026';
}
function toSingleLinePreview(value) {
    return value.replace(/\s+/g, ' ').trim();
}
function isCommandToolName(toolName) {
    const normalized = toolName.trim().toLowerCase();
    return normalized === 'bash' || normalized === 'execute_command' || normalized.includes('shell');
}
function getCommandArg(args) {
    const value = args['command'] ?? args['cmd'];
    return typeof value === 'string' && value.trim() ? value.trim() : undefined;
}
function getCommandTimeoutLabel(args) {
    const timeout = args['timeout'];
    if (typeof timeout === 'number' && Number.isFinite(timeout) && timeout > 0) {
        return `${timeout}s`;
    }
    if (typeof timeout === 'string' && timeout.trim()) {
        const value = timeout.trim();
        return /s$/i.test(value) ? value : `${value}s`;
    }
    return undefined;
}
// ──────────────────────────────────────────────
// Individual tool renderers
// ──────────────────────────────────────────────
function memorySearchRenderers() {
    return {
        renderCall(args, theme) {
            const query = String(args['query'] ?? '');
            const limit = args['limit'];
            let text = theme.fg('toolTitle', theme.bold('memory search '));
            text += theme.fg('accent', truncate(query, 60));
            if (limit)
                text += theme.fg('muted', ` (limit ${limit})`);
            return new SimpleText(text);
        },
        renderResult(result, { expanded, isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Searching\u2026'));
            const raw = resultText(result);
            const parsed = tryParseJson(raw);
            const items = Array.isArray(parsed) ? parsed : [];
            let text = theme.fg('success', `${items.length} result${items.length !== 1 ? 's' : ''}`);
            if (expanded && items.length > 0) {
                const lines = items.slice(0, 10).map((item) => {
                    const key = truncate(String(item['key'] ?? item['id'] ?? '?'), 120);
                    const score = typeof item['score'] === 'number'
                        ? ` (${item['score'].toFixed(2)})`
                        : '';
                    return `  ${theme.fg('accent', key)}${theme.fg('muted', score)}`;
                });
                text += '\n' + lines.join('\n');
                if (items.length > 10)
                    text += theme.fg('muted', `\n  \u2026and ${items.length - 10} more`);
            }
            return new SimpleText(text);
        },
    };
}
function memoryStoreRenderers() {
    return {
        renderCall(args, theme) {
            const key = String(args['key'] ?? '');
            let text = theme.fg('toolTitle', theme.bold('memory store '));
            text += theme.fg('accent', truncate(key, 60));
            return new SimpleText(text);
        },
        renderResult(_result, { isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Storing\u2026'));
            return new SimpleText(theme.fg('success', 'Stored'));
        },
    };
}
function memoryGetRenderers() {
    return {
        renderCall(args, theme) {
            const key = String(args['key'] ?? '');
            let text = theme.fg('toolTitle', theme.bold('memory get '));
            text += theme.fg('accent', truncate(key, 60));
            return new SimpleText(text);
        },
        renderResult(result, { expanded, isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Loading\u2026'));
            const raw = resultText(result);
            const parsed = tryParseJson(raw);
            if (!parsed) {
                return new SimpleText(theme.fg('muted', raw ? 'Retrieved' : 'Not found'));
            }
            let text = theme.fg('success', 'Retrieved');
            if (expanded) {
                const preview = typeof parsed === 'object'
                    ? JSON.stringify(parsed, null, 2)
                        .split('\n')
                        .slice(0, 10)
                        .map(safeLine)
                        .join('\n')
                    : String(parsed);
                text += '\n' + theme.fg('toolOutput', truncate(preview, 500));
            }
            return new SimpleText(text);
        },
    };
}
function memoryUpdateRenderers() {
    return {
        renderCall(args, theme) {
            const key = String(args['key'] ?? '');
            let text = theme.fg('toolTitle', theme.bold('memory update '));
            text += theme.fg('accent', truncate(key, 60));
            return new SimpleText(text);
        },
        renderResult(_result, { isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Updating\u2026'));
            return new SimpleText(theme.fg('success', 'Updated'));
        },
    };
}
function memoryDeleteRenderers() {
    return {
        renderCall(args, theme) {
            const key = String(args['key'] ?? '');
            let text = theme.fg('toolTitle', theme.bold('memory delete '));
            text += theme.fg('accent', truncate(key, 60));
            return new SimpleText(text);
        },
        renderResult(_result, { isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Deleting\u2026'));
            return new SimpleText(theme.fg('muted', 'Deleted'));
        },
    };
}
function memoryListRenderers() {
    return {
        renderCall(args, theme) {
            const namespace = String(args['namespace'] ?? '');
            const prefix = args['prefix'];
            let text = theme.fg('toolTitle', theme.bold('memory list'));
            if (namespace)
                text += theme.fg('accent', ` ${truncate(namespace, 30)}`);
            if (prefix)
                text += theme.fg('accent', ` ${truncate(prefix, 40)}`);
            return new SimpleText(text);
        },
        renderResult(result, { expanded, isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Listing\u2026'));
            const raw = resultText(result);
            const parsed = tryParseJson(raw);
            const keys = Array.isArray(parsed)
                ? parsed
                : parsed &&
                    typeof parsed === 'object' &&
                    Array.isArray(parsed['keys'])
                    ? parsed['keys']
                    : [];
            let text = theme.fg('success', `${keys.length} key${keys.length !== 1 ? 's' : ''}`);
            if (expanded && keys.length > 0) {
                const lines = keys
                    .slice(0, 15)
                    .map((k) => `  ${theme.fg('accent', truncate(String(k), 120))}`);
                text += '\n' + lines.join('\n');
                if (keys.length > 15)
                    text += theme.fg('muted', `\n  \u2026and ${keys.length - 15} more`);
            }
            return new SimpleText(text);
        },
    };
}
function context7SearchRenderers() {
    return {
        renderCall(args, theme) {
            const library = String(args['libraryId'] ?? args['library'] ?? '');
            const query = String(args['query'] ?? '');
            let text = theme.fg('toolTitle', theme.bold('context7 '));
            if (library)
                text += theme.fg('accent', truncate(library, 30) + ' \u2014 ');
            text += theme.fg('text', truncate(query, 50));
            return new SimpleText(text);
        },
        renderResult(result, { expanded, isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Searching docs\u2026'));
            const raw = resultText(result);
            const parsed = tryParseJson(raw);
            const snippets = Array.isArray(parsed)
                ? parsed
                : parsed &&
                    typeof parsed === 'object' &&
                    Array.isArray(parsed['snippets'])
                    ? parsed['snippets']
                    : [];
            const count = snippets.length || (raw.length > 0 ? '?' : '0');
            let text = theme.fg('success', `${count} snippet${count !== 1 ? 's' : ''}`);
            if (expanded && snippets.length > 0) {
                const lines = snippets.slice(0, 5).map((s) => {
                    const snip = s;
                    const title = String(snip['title'] ?? snip['name'] ?? '');
                    return `  ${theme.fg('accent', truncate(title, 80))}`;
                });
                text += '\n' + lines.join('\n');
                if (snippets.length > 5)
                    text += theme.fg('muted', `\n  \u2026and ${snippets.length - 5} more`);
            }
            return new SimpleText(text);
        },
    };
}
function grepAppSearchRenderers() {
    return {
        renderCall(args, theme) {
            const query = String(args['query'] ?? '');
            const lang = args['language'];
            let text = theme.fg('toolTitle', theme.bold('grep.app '));
            text += theme.fg('accent', truncate(query, 50));
            if (lang) {
                const langStr = Array.isArray(lang) ? lang.join(', ') : String(lang);
                text += theme.fg('muted', ` [${langStr}]`);
            }
            return new SimpleText(text);
        },
        renderResult(result, { expanded, isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Searching GitHub\u2026'));
            const raw = resultText(result);
            const parsed = tryParseJson(raw);
            const matches = Array.isArray(parsed)
                ? parsed
                : parsed &&
                    typeof parsed === 'object' &&
                    Array.isArray(parsed['results'])
                    ? parsed['results']
                    : [];
            const count = matches.length || (raw.length > 0 ? '?' : '0');
            let text = theme.fg('success', `${count} match${count !== 1 ? 'es' : ''}`);
            if (expanded && matches.length > 0) {
                const lines = matches.slice(0, 8).map((m) => {
                    const match = m;
                    const path = String(match['path'] ?? match['file'] ?? match['repo'] ?? '');
                    return `  ${theme.fg('accent', truncate(path, 80))}`;
                });
                text += '\n' + lines.join('\n');
                if (matches.length > 8)
                    text += theme.fg('muted', `\n  \u2026and ${matches.length - 8} more`);
            }
            return new SimpleText(text);
        },
    };
}
function sessionDashboardRenderers() {
    return {
        renderCall(_args, theme) {
            return new SimpleText(theme.fg('toolTitle', theme.bold('session dashboard')));
        },
        renderResult(result, { isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Loading dashboard\u2026'));
            const raw = resultText(result);
            const summary = truncate(raw.replace(/\n/g, ' '), 80);
            return new SimpleText(theme.fg('toolOutput', summary || 'OK'));
        },
    };
}
function sessionTodoCreateRenderers() {
    return {
        renderCall(args, theme) {
            const title = String(args['title'] ?? '');
            let text = theme.fg('toolTitle', theme.bold('session todo create '));
            text += theme.fg('accent', truncate(title, 60));
            return new SimpleText(text);
        },
        renderResult(result, { expanded, isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Creating todo\u2026'));
            const raw = resultText(result);
            const parsed = tryParseJson(raw);
            const task = parsed && typeof parsed === 'object'
                ? parsed['task']
                : undefined;
            if (!task)
                return new SimpleText(theme.fg('toolOutput', truncate(raw.replace(/\n/g, ' '), 100)));
            const id = String(task['id'] ?? '');
            const status = String(task['status'] ?? 'open');
            const priority = String(task['priority'] ?? 'none');
            const title = String(task['title'] ?? '').trim();
            const display = title.length > 0 ? truncate(title, 72) : truncate(id, 28);
            let text = theme.fg('success', `${status} ${display}`);
            text += theme.fg('dim', ` (${truncate(id, 20)} prio:${priority})`);
            if (expanded && title.length > 0) {
                text += '\n' + theme.fg('accent', truncate(title, 120));
            }
            return new SimpleText(text);
        },
    };
}
function sessionTodoUpdateRenderers() {
    return {
        renderCall(args, theme) {
            const id = String(args['id'] ?? '');
            const nextStatus = typeof args['status'] === 'string' ? ` -> ${String(args['status'])}` : '';
            return new SimpleText(theme.fg('toolTitle', theme.bold('session todo update ')) +
                theme.fg('accent', truncate(id, 24)) +
                theme.fg('dim', nextStatus));
        },
        renderResult(result, { isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Updating todo\u2026'));
            const raw = resultText(result);
            const parsed = tryParseJson(raw);
            const task = parsed && typeof parsed === 'object'
                ? parsed['task']
                : undefined;
            if (!task)
                return new SimpleText(theme.fg('toolOutput', truncate(raw.replace(/\n/g, ' '), 100)));
            const id = String(task['id'] ?? '');
            const status = String(task['status'] ?? 'open');
            const title = String(task['title'] ?? '').trim();
            const display = title.length > 0 ? truncate(title, 72) : truncate(id, 28);
            return new SimpleText(theme.fg('success', `${status} ${display}`) + theme.fg('dim', ` (${truncate(id, 20)})`));
        },
    };
}
function sessionTodoListRenderers() {
    return {
        renderCall(args, theme) {
            const status = args['status'] ? ` status:${String(args['status'])}` : '';
            const assignee = args['assignee'] ? ` owner:${String(args['assignee'])}` : '';
            const scope = args['scope'] ? ` scope:${String(args['scope'])}` : '';
            return new SimpleText(theme.fg('toolTitle', theme.bold('session todos')) +
                theme.fg('dim', `${scope}${status}${assignee}`));
        },
        renderResult(result, { expanded, isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Loading todos\u2026'));
            const raw = resultText(result);
            const parsed = tryParseJson(raw);
            if (!parsed || typeof parsed !== 'object') {
                return new SimpleText(theme.fg('toolOutput', truncate(raw.replace(/\n/g, ' '), 120)));
            }
            const count = typeof parsed['count'] === 'number' ? parsed['count'] : 0;
            const summary = parsed['summary'] && typeof parsed['summary'] === 'object'
                ? parsed['summary']
                : {};
            const todos = Array.isArray(parsed['todos'])
                ? parsed['todos']
                : [];
            let text = theme.fg('success', `${count} todo${count === 1 ? '' : 's'}`);
            text += theme.fg('dim', `  o:${Number(summary['open'] ?? 0)} ip:${Number(summary['in_progress'] ?? 0)} d:${Number(summary['done'] ?? 0) + Number(summary['closed'] ?? 0)} x:${Number(summary['cancelled'] ?? 0)}`);
            if (expanded && todos.length > 0) {
                const lines = todos.slice(0, 20).map((todo) => {
                    const status = String(todo['status'] ?? 'open');
                    const marker = status === 'done' || status === 'closed'
                        ? theme.fg('success', '✓')
                        : status === 'in_progress'
                            ? theme.fg('accent', '●')
                            : status === 'cancelled'
                                ? theme.fg('error', 'x')
                                : theme.fg('warning', '○');
                    const id = truncate(String(todo['id'] ?? ''), 18);
                    const title = truncate(String(todo['title'] ?? ''), 72);
                    const owner = typeof todo['assignee'] === 'string' && todo['assignee'].length > 0
                        ? ` @${todo['assignee']}`
                        : '';
                    return `  ${marker} ${theme.fg('dim', id)} ${title}${theme.fg('muted', owner)}`;
                });
                text += '\n' + lines.join('\n');
                if (todos.length > 20)
                    text += theme.fg('muted', `\n  \u2026and ${todos.length - 20} more`);
            }
            return new SimpleText(text);
        },
    };
}
function sessionTodoCommentRenderers() {
    return {
        renderCall(args, theme) {
            const id = String(args['id'] ?? '');
            return new SimpleText(theme.fg('toolTitle', theme.bold('session todo comment ')) +
                theme.fg('accent', truncate(id, 32)));
        },
        renderResult(result, { isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Adding comment\u2026'));
            const raw = resultText(result);
            const parsed = tryParseJson(raw);
            if (parsed && parsed['commented'] === true) {
                const taskId = typeof parsed['taskId'] === 'string' ? truncate(parsed['taskId'], 20) : '';
                return new SimpleText(theme.fg('success', `Comment saved${taskId ? ` (${taskId})` : ''}`));
            }
            return new SimpleText(theme.fg('toolOutput', truncate(raw.replace(/\n/g, ' '), 100)));
        },
    };
}
function sessionTodoAttachRenderers() {
    return {
        renderCall(args, theme) {
            const id = String(args['id'] ?? '');
            const name = String(args['name'] ?? '');
            let text = theme.fg('toolTitle', theme.bold('session todo attach '));
            text += theme.fg('accent', truncate(id, 24));
            if (name)
                text += theme.fg('dim', ` ${truncate(name, 40)}`);
            return new SimpleText(text);
        },
        renderResult(result, { isPartial }, theme) {
            if (isPartial)
                return new SimpleText(theme.fg('warning', 'Attaching\u2026'));
            const raw = resultText(result);
            const parsed = tryParseJson(raw);
            if (!parsed || typeof parsed !== 'object') {
                return new SimpleText(theme.fg('toolOutput', truncate(raw.replace(/\n/g, ' '), 100)));
            }
            const count = typeof parsed['attachmentCount'] === 'number' ? parsed['attachmentCount'] : undefined;
            const task = parsed['task'] && typeof parsed['task'] === 'object'
                ? parsed['task']
                : undefined;
            const title = typeof task?.['title'] === 'string' ? truncate(task['title'], 56) : '';
            const id = typeof task?.['id'] === 'string' ? truncate(task['id'], 20) : '';
            const text = count !== undefined ? `Attachment saved (${count} total)` : 'Attachment saved';
            const suffix = title ? ` ${title}${id ? ` (${id})` : ''}` : id ? ` (${id})` : '';
            return new SimpleText(theme.fg('success', `${text}${suffix}`));
        },
    };
}
// ──────────────────────────────────────────────
// Registry
function taskRenderers() {
    return {
        renderCall(args, theme) {
            const agent = String(args['subagent_type'] ?? '?');
            const desc = String(args['description'] ?? '');
            let text = theme.fg('accent', safeLine(agent));
            if (desc)
                text += theme.fg('dim', ` — ${truncate(desc, 60)}`);
            return new Text(text, 0, 0);
        },
        renderResult(result, { expanded, isPartial }, theme) {
            if (isPartial)
                return new Text(theme.fg('warning', 'running...'), 0, 0);
            const raw = resultText(result);
            const lineCount = raw.split('\n').length;
            // Detect agent failure — result starts with "Agent X failed:"
            const isError = raw.startsWith('Agent ') && raw.includes('failed:');
            if (isError) {
                const bgFn = (t) => theme.bg('toolErrorBg', t);
                const box = new Box(1, 0, bgFn);
                let errorContent = theme.fg('error', 'failed');
                if (expanded) {
                    errorContent +=
                        '\n' + theme.fg('error', raw.split('\n').slice(0, 10).map(safeLine).join('\n'));
                }
                else {
                    // Show first line of error in collapsed view
                    const firstLine = raw.split('\n')[0] || '';
                    errorContent += theme.fg('dim', '  ' + firstLine.slice(0, 80));
                    errorContent += theme.fg('muted', '  ctrl+h  ctrl+shift+v|alt+shift+v');
                }
                box.addChild(new Text(errorContent, 0, 0));
                return box;
            }
            // Try to extract token stats from the appended footer
            // Pattern: _agent: Xms | Y in Z out tokens | $cost_
            const statsMatch = raw.match(/_(\w+): (\d+)ms \| (\d+) in (\d+) out tokens \| \$([0-9.]+)_/);
            let text = theme.fg('success', 'done');
            if (statsMatch) {
                const [, , durationMs, tokIn, tokOut, cost] = statsMatch;
                const duration = Number(durationMs) >= 1000
                    ? `${(Number(durationMs) / 1000).toFixed(1)}s`
                    : `${durationMs}ms`;
                text += theme.fg('dim', `  ${duration}  \u2191${tokIn} \u2193${tokOut} $${cost}`);
            }
            else {
                text += theme.fg('dim', ` (${lineCount} lines)`);
            }
            if (!expanded) {
                text += theme.fg('muted', '  ctrl+h  ctrl+shift+v|alt+shift+v');
            }
            if (expanded) {
                const preview = raw.split('\n').slice(0, 20).map(safeLine).join('\n');
                text += '\n' + theme.fg('dim', preview);
                if (lineCount > 20)
                    text += theme.fg('muted', '\n...more  ctrl+shift+v|alt+shift+v');
            }
            return new Text(text, 0, 0);
        },
    };
}
function parallelTasksRenderers() {
    return {
        renderCall(args, theme) {
            const tasks = args['tasks'] ?? [];
            const agents = tasks.map((t) => String(t['subagent_type'] ?? '?'));
            const text = theme.fg('accent', agents.join(' + '));
            return new Text(safeLine(text), 0, 0);
        },
        renderResult(result, { expanded, isPartial }, theme) {
            if (isPartial)
                return new Text(theme.fg('warning', 'running...'), 0, 0);
            const raw = resultText(result);
            // Parse agent names and statuses from ### headers in the raw output
            // Format: "### agent_name (Xms)" for success, "### agent_name (FAILED)" for failure
            const agentEntries = [];
            const headerPattern = /^### (\S+) \((?:FAILED|(\d+)ms)\)/gm;
            let match = headerPattern.exec(raw);
            while (match !== null) {
                agentEntries.push({ name: match[1] ?? '?', failed: match[2] === undefined });
                match = headerPattern.exec(raw);
            }
            // Build chain visualization with status icons
            // ✓ = success (green), ✗ = failed (red)
            const chain = agentEntries
                .map(({ name, failed }) => {
                const icon = failed
                    ? theme.fg('error', '\u2717') // ✗
                    : theme.fg('success', '\u2713'); // ✓
                const nameColor = failed ? 'error' : 'dim';
                return `${icon} ${theme.fg(nameColor, name)}`;
            })
                .join('  ');
            const lineCount = raw.split('\n').length;
            const hasFailures = agentEntries.some((e) => e.failed);
            // Build summary header
            let summaryText = chain;
            summaryText +=
                '\n' +
                    theme.fg(hasFailures ? 'error' : 'success', hasFailures ? 'done (with failures)' : 'done');
            summaryText += theme.fg('dim', ` (${lineCount} lines)`);
            if (!expanded) {
                summaryText += theme.fg('muted', '  ctrl+h  ctrl+shift+v|alt+shift+v');
                return new Text(summaryText, 0, 0);
            }
            // Expanded view: use Container to group summary + agent sections
            const container = new Container();
            container.addChild(new Text(summaryText, 0, 0));
            // Split by ### headers to show each agent section separately
            const sections = raw.split(/(?=^### )/m);
            for (const section of sections) {
                const trimmed = section.trim();
                if (!trimmed)
                    continue;
                const isFailed = trimmed.includes('(FAILED)');
                const lines = trimmed.split('\n');
                const preview = lines.slice(0, 15).map(safeLine).join('\n');
                let sectionContent = preview;
                if (lines.length > 15) {
                    sectionContent +=
                        '\n' +
                            theme.fg('muted', `  ...${lines.length - 15} more lines  ctrl+shift+v|alt+shift+v`);
                }
                if (isFailed) {
                    // Failed sections get error background via Box
                    const box = new Box(1, 0, (t) => theme.bg('toolErrorBg', t));
                    box.addChild(new Text(theme.fg('error', sectionContent), 0, 0));
                    container.addChild(box);
                }
                else {
                    container.addChild(new Text(theme.fg('dim', sectionContent), 0, 0));
                }
            }
            return container;
        },
    };
}
function commandToolRenderers(toolName) {
    return {
        renderCall(args, theme) {
            const display = formatToolDisplay(toolName, args);
            const timeout = getCommandTimeoutLabel(args);
            if (display.branded) {
                let text = theme.fg('toolTitle', theme.bold(display.toolName));
                if (display.toolArgs) {
                    text += theme.fg('accent', ` ${display.toolArgs}`);
                }
                if (timeout) {
                    text += theme.fg('dim', ` (timeout ${timeout})`);
                }
                return new SimpleText(text);
            }
            let text = theme.fg('toolTitle', theme.bold('$ '));
            const commandPreview = getCommandArg(args);
            text += theme.fg('accent', truncate(commandPreview ? toSingleLinePreview(commandPreview) : display.toolName, 80));
            if (timeout) {
                text += theme.fg('dim', ` (timeout ${timeout})`);
            }
            return new SimpleText(text);
        },
    };
}
// ──────────────────────────────────────────────
const RENDERERS = {
    memory_service_search: memorySearchRenderers,
    memory_service_store: memoryStoreRenderers,
    memory_service_get: memoryGetRenderers,
    memory_service_update: memoryUpdateRenderers,
    memory_service_delete: memoryDeleteRenderers,
    memory_service_list: memoryListRenderers,
    context7_search: context7SearchRenderers,
    grep_app_search: grepAppSearchRenderers,
    session_dashboard: sessionDashboardRenderers,
    session_todo_create: sessionTodoCreateRenderers,
    session_todo_update: sessionTodoUpdateRenderers,
    session_todo_list: sessionTodoListRenderers,
    session_todo_comment: sessionTodoCommentRenderers,
    session_todo_attach: sessionTodoAttachRenderers,
    task: taskRenderers,
    parallel_tasks: parallelTasksRenderers,
};
/**
 * Look up renderCall / renderResult functions for a Hub tool.
 * Returns undefined for tools without custom rendering.
 */
export function getToolRenderers(toolName) {
    const factory = RENDERERS[toolName];
    if (factory)
        return factory();
    if (isCommandToolName(toolName))
        return commandToolRenderers(toolName);
    return undefined;
}
//# sourceMappingURL=renderers.js.map