UNPKG

capsule-ai-cli

Version:

The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing

328 lines 15.5 kB
import React from 'react'; import { Box, Text } from 'ink'; import chalk from 'chalk'; import { getProviderColor } from '../utils/provider-colors.js'; import { ToolResultDisplay } from './ToolResultDisplay.js'; import { MarkdownRenderer } from '../utils/markdown-renderer.js'; import { OpenRouterSetupBox } from './OpenRouterSetupBox.js'; import { ActivationSetupBox } from './ActivationSetupBox.js'; function truncateString(str, maxLength = 50) { const text = typeof str === 'string' ? str : String(str || ''); if (text.length <= maxLength) return text; return text.substring(0, maxLength - 3) + '...'; } function formatEditResult(output) { if (typeof output === 'string') return output; if (output.message) { let result = output.message; if (output.diff) { result += '\n' + output.diff; } return result; } return JSON.stringify(output, null, 2); } function formatSearchResult(output) { if (typeof output === 'string') return output; if (output.files) { return `Found ${output.files.length} files`; } if (output.matches) { return `Found ${output.matches} matches`; } return JSON.stringify(output, null, 2); } export const ChatMessage = ({ type, content, metadata, provider = 'openai' }) => { const providerColor = getProviderColor(provider); const needsExtraSpacing = type === 'tool-call' && metadata?.toolName === 'Sub-Agents'; if (type === 'user') { return (React.createElement(Box, { marginBottom: 0.25 }, React.createElement(Text, { color: providerColor }, chalk.hex(providerColor)('> ')), React.createElement(Box, { flexGrow: 1 }, React.createElement(MarkdownRenderer, { content: content, provider: provider })))); } if (type === 'tool-call') { let params = {}; try { params = JSON.parse(content); } catch { params = {}; } let argDisplay = ''; if (params.path || params.file_path) { argDisplay = truncateString(params.path || params.file_path, 50); } else if (params.command) { argDisplay = truncateString(params.command, 50); } else if (params.url) { argDisplay = truncateString(params.url, 50); } else if (params.query || params.pattern) { argDisplay = truncateString(params.query || params.pattern, 50); } const icon = metadata?.success === true ? chalk.hex('#00FF00')('⏺') : metadata?.success === false ? chalk.hex('#FF0000')('⏺') : chalk.hex('#FFFF00')('⏺'); return (React.createElement(Box, { flexDirection: "column", marginBottom: needsExtraSpacing ? 0.5 : 0.25, marginTop: needsExtraSpacing ? 0.5 : 0, flexGrow: 1 }, React.createElement(Box, { flexWrap: "wrap" }, React.createElement(Text, { wrap: "wrap" }, icon, " ", chalk.bold(metadata?.toolName || 'Tool'), argDisplay && `(${argDisplay})`)))); } if (type === 'tool-result') { let parsedContent = ''; const toolMetadata = { success: metadata?.success === true, toolName: metadata?.toolName, error: metadata?.error }; try { let result; if (typeof content === 'string') { try { result = JSON.parse(content); } catch { result = { output: content }; } } else { result = content; } if (result.error) { toolMetadata.error = result.error; toolMetadata.success = false; } else if (!toolMetadata.success && typeof content === 'string' && content.trim()) { toolMetadata.error = content; } if (!toolMetadata.error && toolMetadata.success) { const originalParams = metadata?.originalParams; if (originalParams) { if (originalParams.path || originalParams.file_path) { toolMetadata.filePath = originalParams.path || originalParams.file_path; } if (originalParams.command) { toolMetadata.command = originalParams.command; } if (originalParams.url) { toolMetadata.url = originalParams.url; } if (originalParams.query) { toolMetadata.query = originalParams.query; } } if (result.path) toolMetadata.filePath = result.path; if (result.filePath) toolMetadata.filePath = result.filePath; if (result.file_path) toolMetadata.filePath = result.file_path; if (result.command) toolMetadata.command = result.command; if (result.url) toolMetadata.url = result.url; if (metadata?.toolName === 'Read') { const lines = result.output?.split('\n') || []; toolMetadata.linesRead = lines.length; } else if (metadata?.toolName === 'Write') { toolMetadata.filePath = result.path || result.filePath; if (result.lines !== undefined) { toolMetadata.linesRead = result.lines; } else if (result.output) { const lines = result.output.split('\n') || []; toolMetadata.linesRead = lines.length; } } else if (metadata?.toolName === 'Edit' || metadata?.toolName === 'MultiEdit') { const output = result.output || ''; const additions = (output.match(/\+/g) || []).length; const deletions = (output.match(/-/g) || []).length; toolMetadata.additions = additions; toolMetadata.deletions = deletions; } else if (metadata?.toolName === 'Grep') { const output = result.output || ''; const lines = output.split('\n').filter((l) => l.trim()); toolMetadata.matchCount = lines.length; toolMetadata.fileCount = new Set(lines.map((l) => l.split(':')[0])).size; } else if (metadata?.toolName === 'Glob') { const files = result.output?.split('\n').filter((f) => f.trim()) || []; toolMetadata.fileCount = files.length; } else if (metadata?.toolName === 'Bash') { toolMetadata.command = result.command || 'command'; } else if (metadata?.toolName === 'LS' || metadata?.toolName === 'List') { const files = result.output?.split('\n').filter((f) => f.trim()) || []; toolMetadata.fileCount = files.length; } else if (metadata?.toolName === 'Todo' || metadata?.toolName === 'TodoWrite') { if (result.display) { toolMetadata.todoDisplay = result.display; } else if (result.todos) { toolMetadata.todoCount = result.todos.length; } } else if (metadata?.toolName === 'Search') { if (result.resultCount !== undefined) { toolMetadata.resultCount = result.resultCount; } if (result.type) { toolMetadata.searchType = result.type; } } else if (metadata?.toolName === 'Git') { if (result.command) { const match = result.command.match(/git\s+(\w+)/); if (match) { toolMetadata.gitCommand = match[1]; } } if (result.summary) { toolMetadata.gitSummary = result.summary; } } else if (metadata?.toolName === 'Web Fetch') { if (result.url) { toolMetadata.url = result.url; } if (result.status) { toolMetadata.status = result.status; } } } if (result.output) { if (metadata?.toolName === 'Edit' || metadata?.toolName === 'Update') { parsedContent = formatEditResult(result.output); } else if (metadata?.toolName === 'Search' || metadata?.toolName === 'Grep') { parsedContent = formatSearchResult(result.output); } else { parsedContent = typeof result.output === 'string' ? result.output : JSON.stringify(result.output, null, 2); } } else if (result.display && (metadata?.toolName === 'Todo' || metadata?.toolName === 'TodoWrite')) { parsedContent = result.display; } else { parsedContent = typeof content === 'string' ? content : JSON.stringify(content, null, 2); } if ((metadata?.toolName === 'Todo' || metadata?.toolName === 'TodoWrite') && typeof parsedContent === 'string' && parsedContent.includes('Update Todos')) { toolMetadata.todoDisplay = parsedContent; } } catch (error) { parsedContent = typeof content === 'string' ? content : JSON.stringify(content, null, 2); if (!toolMetadata.error) { toolMetadata.error = 'Failed to parse tool result'; } } return (React.createElement(Box, { marginBottom: 0.25 }, React.createElement(ToolResultDisplay, { toolName: metadata?.toolName || 'Unknown', content: parsedContent, metadata: toolMetadata }))); } if (type === 'assistant') { const contentStr = typeof content === 'string' ? content : JSON.stringify(content, null, 2); if (!contentStr.trim()) { return null; } const finalContent = contentStr.startsWith('⏺') ? contentStr : '⏺ ' + contentStr; return (React.createElement(Box, { marginBottom: 0.25 }, React.createElement(MarkdownRenderer, { content: finalContent, provider: provider }))); } if (type === 'system') { const contentStr = typeof content === 'string' ? content : JSON.stringify(content, null, 2); if (contentStr === 'openrouter-setup') { return (React.createElement(Box, { marginBottom: 1 }, React.createElement(OpenRouterSetupBox, null))); } if (contentStr === 'activation-setup') { return (React.createElement(Box, { marginBottom: 1 }, React.createElement(ActivationSetupBox, null))); } const formatSystemMessage = (text) => { return text; }; if (contentStr.includes('██████╗')) { const lines = contentStr.split('\n'); const logoLines = lines.slice(0, 7); const messageLines = lines.slice(7).join('\n').trim(); return (React.createElement(Box, { flexDirection: "column", marginBottom: 0.25 }, React.createElement(Box, { flexDirection: "column" }, logoLines.map((line, i) => (React.createElement(Text, { key: i, color: providerColor }, line)))), messageLines && (React.createElement(Box, { marginTop: 1 }, React.createElement(Text, { color: providerColor }, formatSystemMessage(messageLines)))))); } if (contentStr.includes('Welcome to Capsule CLI') || contentStr.includes('Mode switched') || contentStr.includes('Model changed') || contentStr.includes('Provider changed')) { return (React.createElement(Box, { marginBottom: 1 }, React.createElement(Text, { color: providerColor }, formatSystemMessage(contentStr)))); } return (React.createElement(Box, { marginBottom: 0.25 }, React.createElement(Text, { color: "yellow" }, contentStr))); } if (type === 'error') { return (React.createElement(Box, { marginBottom: 0.25 }, React.createElement(Text, { color: "red" }, "\u26A0\uFE0F ", content))); } if (type === 'sub-agent-start') { const statusIcon = metadata?.agentStatus === 'completed' ? '✓' : metadata?.agentStatus === 'failed' ? '✗' : metadata?.agentStatus === 'running' ? '◐' : '○'; const statusColor = metadata?.agentStatus === 'completed' ? 'green' : metadata?.agentStatus === 'failed' ? 'red' : metadata?.agentStatus === 'running' ? 'yellow' : 'gray'; return (React.createElement(Box, { marginLeft: 2 }, React.createElement(Box, null, React.createElement(Text, { color: "gray" }, "\u23BF "), React.createElement(Text, { color: statusColor }, statusIcon, " "), React.createElement(Text, null, truncateString(content, 80))))); } if (type === 'sub-agent-result') { const agents = metadata?.agentTasks || []; const allSuccessful = agents.every((a) => a.status === 'completed'); const summaryColor = allSuccessful ? 'green' : 'yellow'; return (React.createElement(Box, { flexDirection: "column", marginTop: 0.25 }, React.createElement(Box, { marginLeft: 4 }, React.createElement(Text, { color: "gray" }, "\u23BF "), React.createElement(Text, { bold: true, color: summaryColor }, "\u2728 ", agents.length, " task", agents.length > 1 ? 's' : '', " completed")), React.createElement(Box, { flexDirection: "column", marginLeft: 6 }, agents.map((agent) => { const statusIcon = agent.status === 'completed' ? '✓' : '✗'; const statusColor = agent.status === 'completed' ? 'green' : 'red'; return (React.createElement(Box, { key: agent.id }, React.createElement(Text, { color: statusColor }, statusIcon, " "), React.createElement(Text, null, truncateString(agent.task, 60)), agent.toolsUsed && agent.toolsUsed.length > 0 && (React.createElement(Text, { dimColor: true }, " \u2022 ", agent.toolsUsed.join(', '))))); })))); } return null; }; //# sourceMappingURL=ChatMessage.js.map