@@ -219,27 +219,18 @@ async def edit_file_contents(
219
219
Args:
220
220
file_path (str): Path to the file to edit
221
221
expected_hash (str): Expected hash of the file before editing
222
- patches (List[EditPatch]): List of patches to apply
223
- - start (int): Starting line number (1-based, optional, default: 1)
224
- - end (Optional[int]): Ending line number (inclusive)
225
- - contents (str): New content to insert
226
- Edit file contents with hash-based conflict detection and multiple patches (supporting new file creation).
227
-
228
- Args:
229
- file_path (str): Path to the file to edit (parent directories are created automatically)
230
- expected_hash (str): Expected hash of the file before editing (empty string for new files)
231
222
patches (List[Dict[str, Any]]): List of patches to apply, each containing:
232
223
- start (int): Starting line number (1-based)
233
224
- end (Optional[int]): Ending line number (inclusive)
234
- - contents (str): New content to insert
225
+ - contents (str): New content to insert (if empty string, consider using delete_text_file_contents instead)
235
226
- range_hash (str): Expected hash of the content being replaced
236
227
237
228
Returns:
238
229
Dict[str, Any]: Results of the operation containing:
239
230
- result: "ok" or "error"
240
231
- hash: New file hash if successful, None if error
241
232
- reason: Error message if result is "error"
242
- "content ": None,
233
+ "file_hash ": None,
243
234
}
244
235
245
236
# Read current file content and verify hash
@@ -251,7 +242,6 @@ async def edit_file_contents(
251
242
return {
252
243
"result" : "error" ,
253
244
"reason" : "File not found and non-empty hash provided" ,
254
- "content" : None ,
255
245
}
256
246
# Create parent directories if they don't exist
257
247
parent_dir = os .path .dirname (file_path )
@@ -262,7 +252,6 @@ async def edit_file_contents(
262
252
return {
263
253
"result" : "error" ,
264
254
"reason" : f"Failed to create directory: { str (e )} " ,
265
- "content" : None ,
266
255
}
267
256
# Initialize empty state for new file
268
257
current_content = ""
@@ -271,9 +260,14 @@ async def edit_file_contents(
271
260
encoding = "utf-8"
272
261
else :
273
262
# Read current file content and verify hash
274
- current_content , _ , _ , current_hash , total_lines , _ = (
275
- await self .read_file_contents (file_path , encoding = encoding )
276
- )
263
+ (
264
+ current_content ,
265
+ _ ,
266
+ _ ,
267
+ current_hash ,
268
+ total_lines ,
269
+ _ ,
270
+ ) = await self .read_file_contents (file_path , encoding = encoding )
277
271
278
272
# Treat empty file as new file
279
273
if not current_content :
@@ -284,14 +278,11 @@ async def edit_file_contents(
284
278
return {
285
279
"result" : "error" ,
286
280
"reason" : "Unexpected error - Cannot treat existing file as new" ,
287
- "file_hash" : None ,
288
- "content" : None ,
289
281
}
290
282
elif current_hash != expected_hash :
291
283
return {
292
284
"result" : "error" ,
293
285
"reason" : "FileHash mismatch - Please use get_text_file_contents tool to get current content and hashes, then retry with the updated hashes." ,
294
- "content" : None ,
295
286
}
296
287
else :
297
288
lines = current_content .splitlines (keepends = True )
@@ -324,8 +315,6 @@ async def edit_file_contents(
324
315
return {
325
316
"result" : "error" ,
326
317
"reason" : "Overlapping patches detected" ,
327
- "hash" : None ,
328
- "content" : None ,
329
318
}
330
319
331
320
# Apply patches
@@ -355,12 +344,7 @@ async def edit_file_contents(
355
344
and current_content
356
345
and expected_hash == ""
357
346
):
358
- return {
359
- "result" : "error" ,
360
- "reason" : "Unexpected error" ,
361
- "file_hash" : None ,
362
- "content" : None ,
363
- }
347
+ return {"result" : "error" , "reason" : "Unexpected error" }
364
348
365
349
# Calculate line ranges for zero-based indexing
366
350
start_zero = start - 1
@@ -400,7 +384,6 @@ async def edit_file_contents(
400
384
return {
401
385
"result" : "error" ,
402
386
"reason" : "Content range hash mismatch - Please use get_text_file_contents tool with the same start and end to get current content and hashes, then retry with the updated hashes." ,
403
- "content" : current_content ,
404
387
}
405
388
406
389
# Prepare new content
@@ -409,6 +392,30 @@ async def edit_file_contents(
409
392
else :
410
393
contents = patch ["contents" ]
411
394
395
+ # Check if this is a deletion (empty content)
396
+ if not contents .strip ():
397
+ return {
398
+ "result" : "ok" ,
399
+ "file_hash" : current_hash , # Return current hash since no changes made
400
+ "hint" : "For content deletion, please consider using delete_text_file_contents instead of patch with empty content" ,
401
+ "suggestion" : "delete" ,
402
+ }
403
+
404
+ # Set suggestions for alternative tools
405
+ suggestion = None
406
+ hint = None
407
+ if not os .path .exists (file_path ) or not current_content :
408
+ suggestion = "append"
409
+ hint = "For new or empty files, please consider using append_text_file_contents instead"
410
+ elif is_insertion :
411
+ if start_zero >= len (lines ):
412
+ suggestion = "append"
413
+ hint = "For adding content at the end of file, please consider using append_text_file_contents instead"
414
+ else :
415
+ suggestion = "insert"
416
+ hint = "For inserting content within file, please consider using insert_text_file_contents instead"
417
+
418
+ # Prepare the content
412
419
new_content = contents if contents .endswith ("\n " ) else contents + "\n "
413
420
new_lines = new_content .splitlines (keepends = True )
414
421
@@ -432,21 +439,14 @@ async def edit_file_contents(
432
439
"result" : "ok" ,
433
440
"file_hash" : new_hash ,
434
441
"reason" : None ,
442
+ "suggestion" : suggestion ,
443
+ "hint" : hint ,
435
444
}
436
445
437
446
except FileNotFoundError :
438
- return {
439
- "result" : "error" ,
440
- "reason" : f"File not found: { file_path } " ,
441
- "file_hash" : None ,
442
- "content" : None ,
443
- }
447
+ return {"result" : "error" , "reason" : f"File not found: { file_path } " }
444
448
except (IOError , UnicodeError , PermissionError ) as e :
445
- return {
446
- "result" : "error" ,
447
- "reason" : f"Error editing file: { str (e )} " ,
448
- "content" : None ,
449
- }
449
+ return {"result" : "error" , "reason" : f"Error editing file: { str (e )} " }
450
450
except Exception as e :
451
451
import traceback
452
452
@@ -455,7 +455,7 @@ async def edit_file_contents(
455
455
return {
456
456
"result" : "error" ,
457
457
"reason" : "Unexpected error occurred" ,
458
- "content " : None ,
458
+ "file_hash " : None ,
459
459
}
460
460
461
461
async def insert_text_file_contents (
@@ -493,11 +493,16 @@ async def insert_text_file_contents(
493
493
}
494
494
495
495
try :
496
- current_content , _ , _ , current_hash , total_lines , _ = (
497
- await self .read_file_contents (
498
- file_path ,
499
- encoding = encoding ,
500
- )
496
+ (
497
+ current_content ,
498
+ _ ,
499
+ _ ,
500
+ current_hash ,
501
+ total_lines ,
502
+ _ ,
503
+ ) = await self .read_file_contents (
504
+ file_path ,
505
+ encoding = encoding ,
501
506
)
502
507
503
508
if current_hash != file_hash :
@@ -585,11 +590,16 @@ async def delete_text_file_contents(
585
590
self ._validate_file_path (request .file_path )
586
591
587
592
try :
588
- current_content , _ , _ , current_hash , total_lines , _ = (
589
- await self .read_file_contents (
590
- request .file_path ,
591
- encoding = request .encoding or "utf-8" ,
592
- )
593
+ (
594
+ current_content ,
595
+ _ ,
596
+ _ ,
597
+ current_hash ,
598
+ total_lines ,
599
+ _ ,
600
+ ) = await self .read_file_contents (
601
+ request .file_path ,
602
+ encoding = request .encoding or "utf-8" ,
593
603
)
594
604
595
605
# Check for conflicts
0 commit comments