import React, { useState } from 'react'; import { Terminal, CheckCircle, AlertTriangle, CircleDashed, Code, Clock, ArrowRight, TerminalIcon, } from 'lucide-react'; import { ToolViewProps } from '../types'; import { formatTimestamp, getToolTitle } from '../utils'; import { cn } from '@/lib/utils'; import { useTheme } from 'next-themes'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from "@/components/ui/scroll-area"; import { LoadingState } from '../shared/LoadingState'; import { extractCommandData } from './_utils'; export function CommandToolView({ name = 'execute-command', assistantContent, toolContent, assistantTimestamp, toolTimestamp, isSuccess = true, isStreaming = false, }: ToolViewProps) { const { resolvedTheme } = useTheme(); const isDarkTheme = resolvedTheme === 'dark'; const [showFullOutput, setShowFullOutput] = useState(true); const { command, output, exitCode, sessionName, cwd, completed, actualIsSuccess, actualToolTimestamp, actualAssistantTimestamp } = extractCommandData( assistantContent, toolContent, isSuccess, toolTimestamp, assistantTimestamp ); const displayText = name === 'check-command-output' ? sessionName : command; const displayLabel = name === 'check-command-output' ? 'Session' : 'Command'; const displayPrefix = name === 'check-command-output' ? 'tmux:' : '$'; const toolTitle = getToolTitle(name); // Check if this is a non-blocking command with just a status message const isNonBlockingCommand = React.useMemo(() => { if (!output) return false; // Check if output contains typical non-blocking command messages const nonBlockingPatterns = [ 'Command sent to tmux session', 'Use check_command_output to view results', 'Session still running', 'completed: false' ]; return nonBlockingPatterns.some(pattern => output.toLowerCase().includes(pattern.toLowerCase()) ); }, [output]); // Check if there's actual command output to display const hasActualOutput = React.useMemo(() => { if (!output) return false; // If it's a non-blocking command, don't show output section if (isNonBlockingCommand) return false; // Check if output contains actual command results (not just status messages) const actualOutputPatterns = [ 'root@', 'COMMAND_DONE_', 'Count:', 'date:', 'ls:', 'pwd:' ]; return actualOutputPatterns.some(pattern => output.includes(pattern) ) || output.trim().length > 50; // Arbitrary threshold for "substantial" output }, [output, isNonBlockingCommand]); const formattedOutput = React.useMemo(() => { if (!output || !hasActualOutput) return []; let processedOutput = output; // Handle case where output is already an object if (typeof output === 'object' && output !== null) { try { processedOutput = JSON.stringify(output, null, 2); } catch (e) { processedOutput = String(output); } } else if (typeof output === 'string') { // Try to parse as JSON first try { if (output.trim().startsWith('{') || output.trim().startsWith('[')) { const parsed = JSON.parse(output); if (parsed && typeof parsed === 'object') { // If it's a complex object, stringify it nicely processedOutput = JSON.stringify(parsed, null, 2); } else { processedOutput = String(parsed); } } else { processedOutput = output; } } catch (e) { // If parsing fails, use as plain text processedOutput = output; } } else { processedOutput = String(output); } processedOutput = processedOutput.replace(/\\\\/g, '\\'); processedOutput = processedOutput .replace(/\\n/g, '\n') .replace(/\\t/g, '\t') .replace(/\\"/g, '"') .replace(/\\'/g, "'"); processedOutput = processedOutput.replace(/\\u([0-9a-fA-F]{4})/g, (_match, group) => { return String.fromCharCode(parseInt(group, 16)); }); return processedOutput.split('\n'); }, [output, hasActualOutput]); const hasMoreLines = formattedOutput.length > 10; const previewLines = formattedOutput.slice(0, 10); const linesToShow = showFullOutput ? formattedOutput : previewLines; return (
{toolTitle}
{!isStreaming && ( {actualIsSuccess ? ( ) : ( )} {actualIsSuccess ? (name === 'check-command-output' ? 'Output retrieved successfully' : 'Command executed successfully') : (name === 'check-command-output' ? 'Failed to retrieve output' : 'Command failed') } )}
{isStreaming ? ( ) : displayText ? (
Terminal
{exitCode !== null && exitCode !== 0 && ( Error )}
{/* Command line */}
{command && ( <> {displayPrefix} {command} )}
{/* Terminal output (render as real terminal text, not JSON) */} {formattedOutput.length > 0 && (
                        {linesToShow.map((line, idx) => (
                          
                            {line}
                            {'\n'}
                          
                        ))}
                      
)} {!showFullOutput && hasMoreLines && (
+ {formattedOutput.length - 10} more lines
)}
{/* Show status message for non-blocking commands */} {isNonBlockingCommand && output && (
Command Status

{output}

)} {!output && !isStreaming && !isNonBlockingCommand && (

No output received

)}
) : (

{name === 'check-command-output' ? 'No Session Found' : 'No Command Found'}

{name === 'check-command-output' ? 'No session name was detected. Please provide a valid session name to check.' : 'No command was detected. Please provide a valid command to execute.' }

)}
{!isStreaming && displayText && ( {displayLabel} )}
{actualToolTimestamp && !isStreaming ? formatTimestamp(actualToolTimestamp) : actualAssistantTimestamp ? formatTimestamp(actualAssistantTimestamp) : ''}
); }