({
input,
result,
isError = false,
isRunning = false,
startedAt,
completedAt,
}: ToolBashProps)
| 55 | const MAX_OUTPUT_LINES = 200; |
| 56 | |
| 57 | export function ToolBash({ |
| 58 | input, |
| 59 | result, |
| 60 | isError = false, |
| 61 | isRunning = false, |
| 62 | startedAt, |
| 63 | completedAt, |
| 64 | }: ToolBashProps) { |
| 65 | const [showAll, setShowAll] = useState(false); |
| 66 | |
| 67 | const exitCode = result ? parseExitCode(result) : null; |
| 68 | const outputText = result ? stripExitCode(result) : ""; |
| 69 | const outputLines = outputText.split("\n"); |
| 70 | const isTruncated = !showAll && outputLines.length > MAX_OUTPUT_LINES; |
| 71 | const displayOutput = isTruncated |
| 72 | ? outputLines.slice(0, MAX_OUTPUT_LINES).join("\n") |
| 73 | : outputText; |
| 74 | |
| 75 | // Determine if it's an error (non-zero exit code or isError prop) |
| 76 | const hasError = isError || (exitCode !== null && exitCode !== 0); |
| 77 | |
| 78 | return ( |
| 79 | <ToolUseBlock |
| 80 | toolName="bash" |
| 81 | toolInput={input} |
| 82 | toolResult={result} |
| 83 | isError={hasError} |
| 84 | isRunning={isRunning} |
| 85 | startedAt={startedAt} |
| 86 | completedAt={completedAt} |
| 87 | > |
| 88 | {/* Command display */} |
| 89 | <div className="flex items-center gap-2 px-3 py-2 bg-surface-850 border-b border-surface-700/50"> |
| 90 | <span className="text-brand-400 font-mono text-xs select-none">$</span> |
| 91 | <code className="font-mono text-xs text-surface-100 flex-1 break-all"> |
| 92 | {input.command} |
| 93 | </code> |
| 94 | <div className="flex items-center gap-1 flex-shrink-0"> |
| 95 | {input.timeout && ( |
| 96 | <span |
| 97 | className="flex items-center gap-1 text-xs text-surface-500" |
| 98 | title={`Timeout: ${input.timeout}ms`} |
| 99 | > |
| 100 | <Clock className="w-3 h-3" /> |
| 101 | {(input.timeout / 1000).toFixed(0)}s |
| 102 | </span> |
| 103 | )} |
| 104 | <CopyButton text={input.command} /> |
| 105 | </div> |
| 106 | </div> |
| 107 | |
| 108 | {/* Output */} |
| 109 | {isRunning ? ( |
| 110 | <div className="px-3 py-3 font-mono text-xs text-brand-400 animate-pulse-soft"> |
| 111 | Running… |
| 112 | </div> |
| 113 | ) : outputText ? ( |
| 114 | <div> |
nothing calls this directly
no test coverage detected