@@ -268,23 +268,28 @@ async def edit_file_contents(
268
268
await self .read_file_contents (file_path , encoding = encoding )
269
269
)
270
270
271
- if current_hash != expected_hash :
271
+ # Treat empty file as new file
272
+ if not current_content :
273
+ current_content = ""
274
+ current_hash = ""
275
+ lines = []
276
+ elif current_hash != expected_hash :
272
277
return {
273
278
"result" : "error" ,
274
279
"reason" : "Hash mismatch - file has been modified" ,
275
280
"hash" : None ,
276
281
"content" : current_content ,
277
282
}
278
-
279
- # Convert content to lines for easier manipulation
280
- lines = current_content .splitlines (keepends = True )
283
+ else :
284
+ # Convert content to lines for easier manipulation
285
+ lines = current_content .splitlines (keepends = True )
281
286
282
287
# Sort patches from bottom to top to avoid line number shifts
283
288
sorted_patches = sorted (
284
289
patches ,
285
290
key = lambda x : (
286
291
- (x .get ("line_start" , 1 )),
287
- - (x .get ("line_end" , x .get ("line_start" , 1 ))),
292
+ - (x .get ("line_end" , x .get ("line_start" , 1 )) or float ( "inf" ) ),
288
293
),
289
294
)
290
295
@@ -314,12 +319,12 @@ async def edit_file_contents(
314
319
line_start = patch .get ("line_start" , 1 )
315
320
line_end = patch .get ("line_end" , line_start )
316
321
expected_range_hash = patch .get ("range_hash" )
317
- is_insertion = line_end < line_start
322
+ is_insertion = False if line_end is None else line_end < line_start
318
323
319
- # Skip range_hash for new files and insertions
320
- if not os .path .exists (file_path ) or is_insertion :
324
+ # Skip range_hash for new files, empty files and insertions
325
+ if not os .path .exists (file_path ) or not current_content or is_insertion :
321
326
expected_range_hash = self .calculate_hash ("" )
322
- # For existing files and non-insertions, range_hash is required
327
+ # For existing, non-empty files and non-insertions, range_hash is required
323
328
elif expected_range_hash is None :
324
329
return {
325
330
"result" : "error" ,
@@ -331,43 +336,58 @@ async def edit_file_contents(
331
336
# Handle insertion or replacement
332
337
if is_insertion :
333
338
target_content = "" # For insertion, we verify empty content
334
-
335
- # Convert to 0-based indexing
336
- line_start -= 1
337
- if not is_insertion :
338
- if line_end is not None :
339
- line_end -= 1
340
- else :
341
- line_end = len (lines ) - 1
342
-
343
- # Ensure we don't exceed file bounds for replacements
344
- line_end = min (line_end , len (lines ) - 1 )
339
+ else :
340
+ # Convert to 0-based indexing for existing content
341
+ line_start_zero = line_start - 1
345
342
346
343
# Calculate target content for hash verification
347
- target_lines = lines [line_start : line_end + 1 ]
348
- target_content = "" .join (target_lines )
344
+ if line_start_zero >= len (lines ):
345
+ target_content = ""
346
+ else :
347
+ # If line_end is None, we read until the end of the file
348
+ if line_end is None :
349
+ target_lines = lines [line_start_zero :]
350
+ else :
351
+ # Adjust to 0-based indexing and make inclusive
352
+ line_end_zero = min (line_end - 1 , len (lines ) - 1 )
353
+ target_lines = lines [line_start_zero : line_end_zero + 1 ]
354
+ target_content = "" .join (target_lines )
349
355
350
356
# Calculate actual range hash and verify only for non-insertions
351
357
actual_range_hash = self .calculate_hash (target_content )
352
358
if not is_insertion and actual_range_hash != expected_range_hash :
353
359
return {
354
360
"result" : "error" ,
355
- "reason" : f"Range hash mismatch for lines { line_start + 1 } -{ line_end + 1 } " ,
361
+ "reason" : f"Range hash mismatch for lines { line_start } -{ line_end if line_end else len ( lines ) } ( { actual_range_hash } != { expected_range_hash } ) " ,
356
362
"hash" : None ,
357
363
"content" : current_content ,
358
364
}
359
365
360
- # Replace lines or insert content
366
+ # Convert to 0-based indexing for modification
367
+ line_start -= 1
368
+ if not is_insertion :
369
+ # Handle line_end consistently with hash verification
370
+ if line_end is None :
371
+ # When line_end is None, replace until the end
372
+ line_end = len (lines )
373
+ else :
374
+ line_end = min (line_end , len (lines ))
375
+
376
+ # Apply the changes
361
377
new_content = patch ["contents" ]
362
378
if not new_content .endswith ("\n " ):
363
379
new_content += "\n "
364
380
new_lines = new_content .splitlines (keepends = True )
381
+
365
382
if is_insertion :
366
- # For insertion, we insert at line_start
367
383
lines [line_start :line_start ] = new_lines
368
384
else :
369
385
# For replacement, we replace the range
370
- lines [line_start : line_end + 1 ] = new_lines
386
+ lines [line_start :line_end ] = new_lines
387
+
388
+ print (f"patch: { patch } " )
389
+ print (f"line_end: { line_end } " )
390
+ print (f"is_insertion: { is_insertion } " )
371
391
372
392
# Write the final content back to file
373
393
final_content = "" .join (lines )
@@ -398,6 +418,10 @@ async def edit_file_contents(
398
418
"content" : None ,
399
419
}
400
420
except Exception as e :
421
+ import traceback
422
+
423
+ print (f"Error: { str (e )} " )
424
+ print (f"Traceback:\n { traceback .format_exc ()} " )
401
425
return {
402
426
"result" : "error" ,
403
427
"reason" : str (e ),
0 commit comments