@@ -5,6 +5,7 @@ import { createTool } from '../../core/createTool.js';
55import type { ExecutionContext , ToolResult } from '../../types/index.js' ;
66import { ToolErrorType , ToolKind } from '../../types/index.js' ;
77import { ToolSchemas } from '../../validation/zodSchemas.js' ;
8+ import { generateDiffSnippet } from './diffUtils.js' ;
89import { FileAccessTracker } from './FileAccessTracker.js' ;
910import { SnapshotManager } from './SnapshotManager.js' ;
1011
@@ -101,9 +102,18 @@ export const writeTool = createTool({
101102
102103 // 检查文件是否存在(用于后续验证和快照)
103104 let fileExists = false ;
105+ let oldContent : string | null = null ;
104106 try {
105107 await fs . access ( file_path ) ;
106108 fileExists = true ;
109+ // 如果文件存在且是文本文件,读取旧内容用于生成 diff
110+ if ( encoding === 'utf8' ) {
111+ try {
112+ oldContent = await fs . readFile ( file_path , 'utf8' ) ;
113+ } catch ( error ) {
114+ console . warn ( '[WriteTool] 读取旧文件内容失败:' , error ) ;
115+ }
116+ }
107117 } catch {
108118 // 文件不存在
109119 }
@@ -170,6 +180,16 @@ export const writeTool = createTool({
170180 const lineCount = encoding === 'utf8' ? content . split ( '\n' ) . length : 0 ;
171181 const fileName = file_path . split ( '/' ) . pop ( ) || file_path ;
172182
183+ // 生成 diff(如果是覆盖现有文本文件)
184+ let diffSnippet : string | null = null ;
185+ if ( oldContent && encoding === 'utf8' && oldContent !== content ) {
186+ // 文件大小限制:超过 1MB 跳过 diff 生成(避免性能问题)
187+ const MAX_DIFF_SIZE = 1024 * 1024 ; // 1MB
188+ if ( oldContent . length < MAX_DIFF_SIZE && content . length < MAX_DIFF_SIZE ) {
189+ diffSnippet = generateDiffSnippet ( oldContent , content , 4 ) ;
190+ }
191+ }
192+
173193 const metadata : Record < string , any > = {
174194 file_path,
175195 content_size : content . length ,
@@ -180,13 +200,19 @@ export const writeTool = createTool({
180200 session_id : sessionId ,
181201 message_id : messageId ,
182202 last_modified : stats . mtime . toISOString ( ) ,
203+ has_diff : ! ! diffSnippet , // 是否生成了 diff
183204 summary :
184205 encoding === 'utf8'
185206 ? `写入 ${ lineCount } 行到 ${ fileName } `
186207 : `写入 ${ formatFileSize ( stats . size ) } 到 ${ fileName } ` ,
187208 } ;
188209
189- const displayMessage = formatDisplayMessage ( file_path , metadata , content ) ;
210+ const displayMessage = formatDisplayMessage (
211+ file_path ,
212+ metadata ,
213+ content ,
214+ diffSnippet
215+ ) ;
190216
191217 return {
192218 success : true ,
@@ -248,7 +274,8 @@ export const writeTool = createTool({
248274function formatDisplayMessage (
249275 filePath : string ,
250276 metadata : Record < string , any > ,
251- content ?: string
277+ content ?: string ,
278+ diffSnippet ?: string | null
252279) : string {
253280 let message = `✅ 成功写入文件: ${ filePath } ` ;
254281
@@ -264,8 +291,13 @@ function formatDisplayMessage(
264291 message += `\n🔐 使用编码: ${ metadata . encoding } ` ;
265292 }
266293
267- // 添加内容预览(仅对文本文件)
268- if ( content && metadata . encoding === 'utf8' ) {
294+ // 优先显示 diff(如果有)
295+ if ( diffSnippet ) {
296+ message += diffSnippet ;
297+ }
298+
299+ // 添加内容预览(仅对文本文件且没有 diff 时才显示完整预览)
300+ if ( content && metadata . encoding === 'utf8' && ! diffSnippet ) {
269301 const preview = generateContentPreview ( filePath , content ) ;
270302 if ( preview ) {
271303 message += '\n\n' + preview ;
0 commit comments