""" Edit file tool (find and replace) """ import os from typing import Dict, Any from .registry import Tool class EditTool(Tool): """Tool for editing files using find and replace""" def __init__(self): super().__init__( name="edit", description="Edit a file by finding and replacing exact text. The old_string must match exactly (including whitespace).", parameters={ "type": "object", "properties": { "file_path": { "type": "string", "description": "Path to the file to edit (relative or absolute)" }, "old_string": { "type": "string", "description": "Exact text to find and replace (must match exactly)" }, "new_string": { "type": "string", "description": "New text to replace the old text with" } }, "required": ["file_path", "old_string", "new_string"] } ) async def execute(self, args: Dict[str, Any]) -> str: """Execute the edit tool""" file_path = args.get("file_path") old_string = args.get("old_string") new_string = args.get("new_string") if not file_path or old_string is None or new_string is None: return "Error: file_path, old_string, and new_string are required" try: # Convert to absolute path if relative if not os.path.isabs(file_path): file_path = os.path.abspath(file_path) if not os.path.exists(file_path): return f"Error: File '{file_path}' does not exist" if not os.path.isfile(file_path): return f"Error: '{file_path}' is not a file" # Read the current content with open(file_path, 'r', encoding='utf-8') as f: content = f.read() # Check if old_string exists in the content if old_string not in content: return f"Error: The specified text was not found in '{file_path}'" # Count occurrences occurrence_count = content.count(old_string) # Perform the replacement new_content = content.replace(old_string, new_string) # Write the modified content back with open(file_path, 'w', encoding='utf-8') as f: f.write(new_content) # Calculate changes old_lines = len(content.splitlines()) new_lines = len(new_content.splitlines()) line_diff = new_lines - old_lines result_parts = [ f"Successfully edited '{file_path}':", f"- Replaced {occurrence_count} occurrence(s) of the specified text", f"- File now has {new_lines} lines ({line_diff:+d} lines)" ] return "\n".join(result_parts) except UnicodeDecodeError: return f"Error: Could not read file '{file_path}' - file appears to be binary" except Exception as e: return f"Error: Could not edit file '{file_path}': {e}"