"use client"; import { memo } from "react"; import { IconCheck, IconX, IconTerminal } from "@tabler/icons-react"; import { GridSpinner } from "@/components/grid-spinner"; import { transformPathsInText } from "@/lib/utils"; import type { Message } from "@/lib/types/http"; import { ExpandableRow } from "./expandable-row"; import { useExpandState } from "./use-expand-state"; type ShellExecOutput = { exit_code?: number; stdout?: string; stderr?: string; }; type ShellExecPayload = { command?: string; work_dir?: string; output?: ShellExecOutput; }; type ToolExecuteMetadata = { tool_call_id?: string; status?: "pending" | "running" | "complete" | "error"; normalized?: { shell_exec?: ShellExecPayload }; }; type ToolExecuteMessageProps = { comment: Message; worktreePath?: string; }; function ExecuteStatusIcon({ status, exitCode, }: { status: string | undefined; exitCode: number | undefined; }) { if (status === "complete") { return exitCode === 0 ? ( ) : ( ); } if (status === "error") return ; if (status === "running") return ; return null; } type ExecuteOutputProps = { displayWorkDir: string | null; workDir: string | undefined; hasOutput: string | undefined | false; output: ShellExecOutput | undefined; }; function ExecuteOutputContent({ displayWorkDir, workDir, hasOutput, output }: ExecuteOutputProps) { return (
{displayWorkDir && !hasOutput && (
cwd:{" "} {displayWorkDir}
)} {output?.stdout && (
          {output.stdout}
        
)} {output?.stderr && (
          {output.stderr}
        
)}
); } function parseExecuteMetadata(comment: Message) { const metadata = comment.metadata as ToolExecuteMetadata | undefined; const status = metadata?.status; const shellExec = metadata?.normalized?.shell_exec; const output = shellExec?.output; const workDir = shellExec?.work_dir; const hasOutput = output?.stdout || output?.stderr; const hasExpandableContent = hasOutput || workDir; const isSuccess = status === "complete" && (output?.exit_code === 0 || output?.exit_code === undefined); return { status, output, workDir, hasOutput, hasExpandableContent, isSuccess }; } export const ToolExecuteMessage = memo(function ToolExecuteMessage({ comment, worktreePath, }: ToolExecuteMessageProps) { const { status, output, workDir, hasOutput, hasExpandableContent, isSuccess } = parseExecuteMetadata(comment); const autoExpanded = status === "running"; const { isExpanded, handleToggle } = useExpandState(status, autoExpanded); const displayWorkDir = workDir ? transformPathsInText(workDir, worktreePath) : null; return ( } header={
{transformPathsInText(comment.content, worktreePath)} {!isSuccess && }
} hasExpandableContent={!!hasExpandableContent} isExpanded={isExpanded} onToggle={handleToggle} >
); });