diff --git a/packages/builtin-tools/src/tools/FileEditTool/UI.tsx b/packages/builtin-tools/src/tools/FileEditTool/UI.tsx index 417ffa3f5..8b77ad550 100644 --- a/packages/builtin-tools/src/tools/FileEditTool/UI.tsx +++ b/packages/builtin-tools/src/tools/FileEditTool/UI.tsx @@ -1,7 +1,5 @@ import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' -import type { StructuredPatchHunk } from 'diff' import * as React from 'react' -import { Suspense, use, useState } from 'react' import { FileEditToolUseRejectedMessage } from 'src/components/FileEditToolUseRejectedMessage.js' import { MessageResponse } from 'src/components/MessageResponse.js' import { extractTag } from 'src/utils/messages.js' @@ -12,19 +10,10 @@ import { Text } from '@anthropic/ink' import { FilePathLink } from 'src/components/FilePathLink.js' import type { Tools } from 'src/Tool.js' import type { Message, ProgressMessage } from 'src/types/message.js' -import { adjustHunkLineNumbers, CONTEXT_LINES } from 'src/utils/diff.js' import { FILE_NOT_FOUND_CWD_NOTE, getDisplayPath } from 'src/utils/file.js' -import { logError } from 'src/utils/log.js' import { getPlansDirectory } from 'src/utils/plans.js' -import { readEditContext } from 'src/utils/readEditContext.js' -import { firstLineOf } from 'src/utils/stringUtils.js' import type { ThemeName } from 'src/utils/theme.js' import type { FileEditOutput } from './types.js' -import { - findActualString, - getPatchForEdit, - preserveQuoteStyle, -} from './utils.js' export function userFacingName( input: @@ -99,8 +88,6 @@ export function renderToolResultMessage( - ) - } - - const isNewFile = oldString === '' - - // For new file creation, show content preview instead of diff - if (isNewFile) { - return ( - - ) - } + const isNewFile = input.old_string === '' return ( - @@ -201,115 +157,3 @@ export function renderToolUseErrorMessage( } return } - -type RejectionDiffData = { - patch: StructuredPatchHunk[] - firstLine: string | null - fileContent: string | undefined -} - -function EditRejectionDiff({ - filePath, - oldString, - newString, - replaceAll, - style, - verbose, -}: { - filePath: string - oldString: string - newString: string - replaceAll: boolean - style?: 'condensed' - verbose: boolean -}): React.ReactNode { - const [dataPromise] = useState(() => - loadRejectionDiff(filePath, oldString, newString, replaceAll), - ) - return ( - - } - > - - - ) -} - -function EditRejectionBody({ - promise, - filePath, - style, - verbose, -}: { - promise: Promise - filePath: string - style?: 'condensed' - verbose: boolean -}): React.ReactNode { - const { patch, firstLine, fileContent } = use(promise) - return ( - - ) -} - -async function loadRejectionDiff( - filePath: string, - oldString: string, - newString: string, - replaceAll: boolean, -): Promise { - try { - // Chunked read — context window around the first occurrence. replaceAll - // still shows matches *within* the window via getPatchForEdit; we accept - // losing the all-occurrences view to keep the read bounded. - const ctx = await readEditContext(filePath, oldString, CONTEXT_LINES) - if (ctx === null || ctx.truncated || ctx.content === '') { - // ENOENT / not found / truncated — diff just the tool inputs. - const { patch } = getPatchForEdit({ - filePath, - fileContents: oldString, - oldString, - newString, - }) - return { patch, firstLine: null, fileContent: undefined } - } - const actualOld = findActualString(ctx.content, oldString) || oldString - const actualNew = preserveQuoteStyle(oldString, actualOld, newString) - const { patch } = getPatchForEdit({ - filePath, - fileContents: ctx.content, - oldString: actualOld, - newString: actualNew, - replaceAll, - }) - return { - patch: adjustHunkLineNumbers(patch, ctx.lineOffset - 1), - firstLine: ctx.lineOffset === 1 ? firstLineOf(ctx.content) : null, - fileContent: ctx.content, - } - } catch (e) { - // User may have manually applied the change while the diff was shown. - logError(e as Error) - return { patch: [], firstLine: null, fileContent: undefined } - } -} diff --git a/packages/builtin-tools/src/tools/FileWriteTool/UI.tsx b/packages/builtin-tools/src/tools/FileWriteTool/UI.tsx index 5b96cf4f8..3c868c84c 100644 --- a/packages/builtin-tools/src/tools/FileWriteTool/UI.tsx +++ b/packages/builtin-tools/src/tools/FileWriteTool/UI.tsx @@ -1,8 +1,6 @@ import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs' -import type { StructuredPatchHunk } from 'diff' -import { isAbsolute, relative, resolve } from 'path' +import { relative } from 'path' import * as React from 'react' -import { Suspense, use, useState } from 'react' import { MessageResponse } from 'src/components/MessageResponse.js' import { extractTag } from 'src/utils/messages.js' import { CtrlOToExpand } from 'src/components/CtrlOToExpand.js' @@ -17,11 +15,8 @@ import { FilePathLink } from 'src/components/FilePathLink.js' import type { ToolProgressData } from 'src/Tool.js' import type { ProgressMessage } from 'src/types/message.js' import { getCwd } from 'src/utils/cwd.js' -import { getPatchForDisplay } from 'src/utils/diff.js' import { getDisplayPath } from 'src/utils/file.js' -import { logError } from 'src/utils/log.js' import { getPlansDirectory } from 'src/utils/plans.js' -import { openForScan, readCapped } from 'src/utils/readEditContext.js' import type { Output } from './FileWriteTool.js' const MAX_LINES_TO_RENDER = 10 @@ -137,131 +132,19 @@ export function renderToolUseMessage( } export function renderToolUseRejectedMessage( - { file_path, content }: { file_path: string; content: string }, + { file_path }: { file_path: string; content: string }, { style, verbose }: { style?: 'condensed'; verbose: boolean }, ): React.ReactNode { return ( - - ) -} - -type RejectionDiffData = - | { type: 'create' } - | { type: 'update'; patch: StructuredPatchHunk[]; oldContent: string } - | { type: 'error' } - -function WriteRejectionDiff({ - filePath, - content, - style, - verbose, -}: { - filePath: string - content: string - style?: 'condensed' - verbose: boolean -}): React.ReactNode { - const [dataPromise] = useState(() => loadRejectionDiff(filePath, content)) - const firstLine = content.split('\n')[0] ?? null - const createFallback = ( - ) - return ( - - - - ) -} - -function WriteRejectionBody({ - promise, - filePath, - firstLine, - createFallback, - style, - verbose, -}: { - promise: Promise - filePath: string - firstLine: string | null - createFallback: React.ReactNode - style?: 'condensed' - verbose: boolean -}): React.ReactNode { - const data = use(promise) - if (data.type === 'create') return createFallback - if (data.type === 'error') { - return ( - - (No changes) - - ) - } - return ( - ) } -async function loadRejectionDiff( - filePath: string, - content: string, -): Promise { - try { - const fullFilePath = isAbsolute(filePath) - ? filePath - : resolve(getCwd(), filePath) - const handle = await openForScan(fullFilePath) - if (handle === null) return { type: 'create' } - let oldContent: string | null - try { - oldContent = await readCapped(handle) - } finally { - await handle.close() - } - // File exceeds MAX_SCAN_BYTES — fall back to the create view rather than - // OOMing on a diff of a multi-GB file. - if (oldContent === null) return { type: 'create' } - const patch = getPatchForDisplay({ - filePath, - fileContents: oldContent, - edits: [ - { old_string: oldContent, new_string: content, replace_all: false }, - ], - }) - return { type: 'update', patch, oldContent } - } catch (e) { - // User may have manually applied the change while the diff was shown. - logError(e as Error) - return { type: 'error' } - } -} - export function renderToolUseErrorMessage( result: ToolResultBlockParam['content'], { verbose }: { verbose: boolean }, @@ -324,8 +207,6 @@ export function renderToolResultMessage( acc + count(hunk.lines, _ => _.startsWith('+')), 0, @@ -55,7 +47,7 @@ export function FileEditToolUpdatedMessage({ // Plan files: invert condensed behavior // - Regular mode: just show the hint (user can type /plan to see full content) - // - Condensed mode (subagent view): show the diff + // - Condensed mode (subagent view): show the text if (previewHint) { if (style !== 'condensed' && !verbose) { return ( @@ -69,18 +61,6 @@ export function FileEditToolUpdatedMessage({ } return ( - - - {text} - - - + {text} ) } diff --git a/src/components/FileEditToolUseRejectedMessage.tsx b/src/components/FileEditToolUseRejectedMessage.tsx index 2d53117d2..87b85d9f6 100644 --- a/src/components/FileEditToolUseRejectedMessage.tsx +++ b/src/components/FileEditToolUseRejectedMessage.tsx @@ -1,24 +1,12 @@ -import type { StructuredPatchHunk } from 'diff' import { relative } from 'path' import * as React from 'react' -import { useTerminalSize } from 'src/hooks/useTerminalSize.js' import { getCwd } from 'src/utils/cwd.js' import { Box, Text } from '@anthropic/ink' -import { HighlightedCode } from './HighlightedCode.js' import { MessageResponse } from './MessageResponse.js' -import { StructuredDiffList } from './StructuredDiffList.js' - -const MAX_LINES_TO_RENDER = 10 type Props = { file_path: string operation: 'write' | 'update' - // For updates - show diff - patch?: StructuredPatchHunk[] - firstLine: string | null - fileContent?: string - // For new file creation - show content preview - content?: string style?: 'condensed' verbose: boolean } @@ -26,14 +14,9 @@ type Props = { export function FileEditToolUseRejectedMessage({ file_path, operation, - patch, - firstLine, - fileContent, - content, style, verbose, }: Props): React.ReactNode { - const { columns } = useTerminalSize() const text = ( User rejected {operation} to @@ -48,51 +31,5 @@ export function FileEditToolUseRejectedMessage({ return {text} } - // For new file creation, show content preview (dimmed) - if (operation === 'write' && content !== undefined) { - const lines = content.split('\n') - const numLines = lines.length - const plusLines = numLines - MAX_LINES_TO_RENDER - const truncatedContent = verbose - ? content - : lines.slice(0, MAX_LINES_TO_RENDER).join('\n') - - return ( - - - {text} - - {!verbose && plusLines > 0 && ( - … +{plusLines} lines - )} - - - ) - } - - // For updates, show diff - if (!patch || patch.length === 0) { - return {text} - } - - return ( - - - {text} - - - - ) + return {text} }