Files
claude-code/src/tools/BashTool/BashToolResultMessage.tsx
2026-04-07 16:17:48 +08:00

130 lines
4.0 KiB
TypeScript

import React from 'react'
import { removeSandboxViolationTags } from 'src/utils/sandbox/sandbox-ui-utils.js'
import { KeyboardShortcutHint } from '@anthropic/ink'
import { MessageResponse } from '../../components/MessageResponse.js'
import { OutputLine } from '../../components/shell/OutputLine.js'
import { ShellTimeDisplay } from '../../components/shell/ShellTimeDisplay.js'
import { Box, Text } from '@anthropic/ink'
import type { Out as BashOut } from './BashTool.js'
type Props = {
content: Omit<BashOut, 'interrupted'>
verbose: boolean
timeoutMs?: number
}
// Pattern to match "Shell cwd was reset to <path>" message
// Use (?:^|\n) to match either start of string or after a newline
const SHELL_CWD_RESET_PATTERN = /(?:^|\n)(Shell cwd was reset to .+)$/
/**
* Extracts sandbox violations from stderr if present
* Returns both the cleaned stderr and the violations content
*/
function extractSandboxViolations(stderr: string): {
cleanedStderr: string
} {
const violationsMatch = stderr.match(
/<sandbox_violations>([\s\S]*?)<\/sandbox_violations>/,
)
if (!violationsMatch) {
return { cleanedStderr: stderr }
}
// Remove the sandbox violations section from stderr
const cleanedStderr = removeSandboxViolationTags(stderr).trim()
return {
cleanedStderr,
}
}
/**
* Extracts the "Shell cwd was reset" warning message from stderr
* Returns the cleaned stderr and the warning message separately
*/
function extractCwdResetWarning(stderr: string): {
cleanedStderr: string
cwdResetWarning: string | null
} {
const match = stderr.match(SHELL_CWD_RESET_PATTERN)
if (!match) {
return { cleanedStderr: stderr, cwdResetWarning: null }
}
// Extract the warning message from capture group 1
const cwdResetWarning = match[1] ?? null
// Remove the warning from stderr (replace the full match)
const cleanedStderr = stderr.replace(SHELL_CWD_RESET_PATTERN, '').trim()
return { cleanedStderr, cwdResetWarning }
}
export default function BashToolResultMessage({
content: {
stdout = '',
stderr: stdErrWithViolations = '',
isImage,
returnCodeInterpretation,
noOutputExpected,
backgroundTaskId,
},
verbose,
timeoutMs,
}: Props): React.ReactNode {
// Extract sandbox violations from stderr as it feels cleaner on the UI
// We want the model to see the violations, so it can explain what went wrong, and the
// user can access them in the violation logs
const { cleanedStderr: stderrWithoutViolations } =
extractSandboxViolations(stdErrWithViolations)
// Extract "Shell cwd was reset" warning to render it with warning color instead of error
const { cleanedStderr: stderr, cwdResetWarning } = extractCwdResetWarning(
stderrWithoutViolations,
)
// If this is an image, we don't want to truncate it in the UI
if (isImage) {
return (
<MessageResponse height={1}>
<Text dimColor>[Image data detected and sent to Claude]</Text>
</MessageResponse>
)
}
return (
<Box flexDirection="column">
{stdout !== '' ? <OutputLine content={stdout} verbose={verbose} /> : null}
{stderr.trim() !== '' ? (
<OutputLine content={stderr} verbose={verbose} isError />
) : null}
{cwdResetWarning ? (
<MessageResponse>
<Text dimColor>{cwdResetWarning}</Text>
</MessageResponse>
) : null}
{stdout === '' && stderr.trim() === '' && !cwdResetWarning ? (
<MessageResponse height={1}>
<Text dimColor>
{backgroundTaskId ? (
<>
Running in the background{' '}
<KeyboardShortcutHint shortcut="↓" action="manage" parens />
</>
) : (
returnCodeInterpretation ||
(noOutputExpected ? 'Done' : '(No output)')
)}
</Text>
</MessageResponse>
) : null}
{timeoutMs && (
<MessageResponse>
<ShellTimeDisplay timeoutMs={timeoutMs} />
</MessageResponse>
)}
</Box>
)
}