capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
247 lines • 9.99 kB
JavaScript
import React from 'react';
import { Box, Text } from 'ink';
import chalk from 'chalk';
function getContentPreview(content, toolName, metadata) {
const lines = [];
try {
if (content.startsWith('{') && content.includes('"')) {
return [];
}
switch (toolName) {
case 'Read':
case 'Write': {
const fileLines = content.split('\n').filter(l => l.trim());
if (fileLines.length > 0) {
lines.push(fileLines[0]);
if (fileLines.length > 1) {
lines.push(fileLines[1]);
}
}
break;
}
case 'List': {
const items = content.split('\n').filter(l => l.trim()).slice(0, 3);
items.forEach(item => {
lines.push(item);
});
break;
}
case 'Search':
case 'Grep': {
const matches = content.split('\n').filter(l => l.trim()).slice(0, 2);
matches.forEach(match => {
const cleaned = match.replace(/^[^:]+:/, '');
lines.push(cleaned);
});
break;
}
case 'Bash': {
const outputLines = content.split('\n').filter(l => l.trim()).slice(0, 2);
outputLines.forEach(line => {
lines.push(line);
});
break;
}
case 'Git': {
const gitLines = content.split('\n').filter(l => l.trim()).slice(0, 3);
gitLines.forEach(line => {
lines.push(line);
});
break;
}
case 'Todo':
case 'TodoWrite':
if (metadata?.todoDisplay) {
const todoLines = metadata.todoDisplay.split('\n')
.filter((l) => l.includes('[') && (l.includes('✓') || l.includes('○') || l.includes('→')))
.slice(0, 2);
todoLines.forEach((line) => {
const cleaned = line.replace(/^\s*\d+\.\s*/, '').trim();
lines.push(cleaned);
});
}
break;
default: {
const previewLines = content.split('\n').filter(l => l.trim()).slice(0, 2);
previewLines.forEach(line => {
lines.push(line);
});
}
}
}
catch {
const fallbackLines = content.split('\n').filter(l => l.trim()).slice(0, 1);
if (fallbackLines.length > 0) {
lines.push(fallbackLines[0]);
}
}
return lines;
}
const TOOL_ICONS = {
'Read': '⏺',
'Write': '⏺',
'Edit': '⏺',
'MultiEdit': '⏺',
'Bash': '⏺',
'Grep': '⏺',
'Glob': '⏺',
'Search': '⏺',
'Sub-Agents': '⏺',
'Todo': '⏺',
'TodoWrite': '⏺',
'LS': '⏺',
'List': '⏺',
'WebFetch': '⏺',
'WebSearch': '⏺',
'Git': '⏺',
'Web Fetch': '⏺',
'Sub-Agent Results': '⏺',
'ExitPlanMode': '⏺',
};
const formatCompactSummary = (toolName, metadata) => {
const { filePath, additions, deletions, linesRead, matchCount, fileCount, command, error, taskCount, success, todoDisplay, todoCount } = metadata || {};
if (error || success === false) {
return chalk.hex('#FF0000')(`Error: ${error || 'Operation failed'}`);
}
switch (toolName) {
case 'Read':
return `Read ${linesRead || 0} lines ${filePath ? `from ${filePath}` : ''}`;
case 'Write':
return `Created ${filePath || 'file'} with ${linesRead || 0} lines`;
case 'Edit':
case 'MultiEdit':
return `Updated ${filePath || 'file'}: +${additions || 0} -${deletions || 0}`;
case 'Bash':
return `$ ${command || 'command'}`;
case 'Grep':
return `Found ${matchCount || 0} matches in ${fileCount || 0} files`;
case 'Glob':
return `Found ${fileCount || 0} files`;
case 'Search':
if (metadata?.resultCount !== undefined) {
return `Found ${metadata.resultCount} results`;
}
return `Search completed`;
case 'Sub-Agents':
if (metadata?.agentStatus === 'completed') {
return `Sub-agent completed`;
}
else if (metadata?.agentStatus === 'failed') {
return chalk.hex('#FF0000')(`Sub-agent failed`);
}
else if (metadata?.agentStatus === 'running') {
return `Sub-agent running...`;
}
else if (taskCount && taskCount > 1) {
return `Spawned ${taskCount} sub-agents`;
}
return `Spawned sub-agent`;
case 'Todo':
case 'TodoWrite':
if (todoDisplay) {
const lines = todoDisplay.split('\n').filter((line) => line.trim());
const tasks = lines.slice(0, 3).map((line) => {
const match = line.match(/[⎿\s]*(☐|☑|◐|☒|⊗)\s*(.+)/);
if (match) {
const status = match[1];
const description = match[2].trim();
return `${status} ${description}`;
}
return line.trim();
}).filter((t) => t && !t.startsWith('⎿'));
const uniqueTasks = Array.from(new Set(tasks));
if (uniqueTasks.length === 0) {
return `No tasks`;
}
if (uniqueTasks.length === 1) {
return uniqueTasks[0];
}
else {
return uniqueTasks.map((task, index) => {
if (index === 0)
return task;
return '\n' + task;
}).join('') + (uniqueTasks.length < tasks.length ? chalk.hex('#808080')(' (deduped)') : '');
}
}
else if (todoCount !== undefined) {
return `${todoCount} task${todoCount !== 1 ? 's' : ''}`;
}
return `Updated todo list`;
case 'LS':
case 'List':
if (fileCount !== undefined) {
return `Listed ${fileCount} items`;
}
return `Listed directory`;
case 'WebFetch':
case 'Web Fetch':
if (metadata?.status) {
return `${metadata.url || 'Web page'} - ${metadata.status}`;
}
return `Fetched web content`;
case 'WebSearch':
return `Searched the web`;
case 'Git':
if (metadata?.gitCommand) {
return `git ${metadata.gitCommand}${metadata.gitSummary ? ` - ${metadata.gitSummary}` : ''}`;
}
return `Git command executed`;
case 'ExitPlanMode':
return `Exited plan mode`;
case 'Sub-Agent Results':
if (metadata?.agentCount) {
return `${metadata.agentCount} sub-agent${metadata.agentCount > 1 ? 's' : ''} completed`;
}
return `Sub-agents completed`;
default:
return `Completed`;
}
};
export const ToolResultDisplay = ({ toolName, content, metadata }) => {
const icon = TOOL_ICONS[toolName] || '⏺';
const isError = metadata?.error || metadata?.success === false;
const summaryColor = isError ? '#FF0000' : '#00FF00';
return (React.createElement(Box, { flexDirection: "column", marginTop: 0.25, flexGrow: 1 },
React.createElement(Box, { flexWrap: "wrap" },
React.createElement(Text, { color: summaryColor, wrap: "wrap" },
icon,
" ",
chalk.bold(toolName),
(() => {
if (metadata?.filePath) {
return `(${metadata.filePath})`;
}
else if (metadata?.command) {
return `(${metadata.command})`;
}
else if (metadata?.url) {
return `(${metadata.url})`;
}
else if (metadata?.query) {
return `(${metadata.query})`;
}
else if (metadata?.args) {
return `(${metadata.args})`;
}
return '';
})())),
(() => {
const preview = getContentPreview(content, toolName, metadata);
if (preview.length > 0) {
return (React.createElement(Box, { marginLeft: 4, flexDirection: "column", flexWrap: "wrap" }, preview.map((line, index) => (React.createElement(Box, { key: `preview-${index}`, flexWrap: "wrap" },
React.createElement(Text, { color: "#808080" }, "\u23BF "),
React.createElement(Text, { dimColor: true, wrap: "wrap" }, line))))));
}
return null;
})(),
React.createElement(Box, { marginLeft: 4, flexDirection: "column", flexWrap: "wrap" }, (() => {
const summary = formatCompactSummary(toolName, metadata);
const lines = summary.split('\n');
return lines.map((line, index) => (React.createElement(Box, { key: index, flexWrap: "wrap" },
index === 0 && React.createElement(Text, { color: "#808080" }, "\u23BF "),
index > 0 && React.createElement(Text, null, " "),
React.createElement(Text, { color: isError ? '#FF0000' : undefined, wrap: "wrap" }, line))));
})())));
};
//# sourceMappingURL=ToolResultDisplay.js.map