picogent-py/picogent/tools/edit.py
Markov 5417980b76 Initial implementation of PicoGent - minimal AI coding agent
- Implemented ReAct loop in agent.py
- Added Anthropic provider using httpx (no SDK)
- Created tools: read, write, edit, bash
- Added session management with JSONL format
- Included configuration system with env var support
- Added CLI interface and example usage
- Minimal dependencies: only httpx
2026-02-22 23:18:02 +01:00

91 lines
3.4 KiB
Python

"""
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}"