picogent-py/picogent/tools/read.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

94 lines
3.3 KiB
Python

"""
Read file tool
"""
import os
from typing import Dict, Any
from .registry import Tool
class ReadTool(Tool):
"""Tool for reading files with optional offset/limit"""
def __init__(self):
super().__init__(
name="read",
description="Read the contents of a file. Supports optional offset/limit for reading specific lines.",
parameters={
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Path to the file to read (relative or absolute)"
},
"offset": {
"type": "integer",
"description": "Line number to start reading from (1-indexed, optional)",
"minimum": 1
},
"limit": {
"type": "integer",
"description": "Maximum number of lines to read (optional)",
"minimum": 1
}
},
"required": ["file_path"]
}
)
async def execute(self, args: Dict[str, Any]) -> str:
"""Execute the read tool"""
file_path = args.get("file_path")
offset = args.get("offset", 1) # 1-indexed
limit = args.get("limit")
if not file_path:
return "Error: file_path is 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"
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
# Apply offset (convert from 1-indexed to 0-indexed)
start_idx = max(0, offset - 1)
# Apply limit
if limit is not None:
end_idx = start_idx + limit
selected_lines = lines[start_idx:end_idx]
else:
selected_lines = lines[start_idx:]
content = ''.join(selected_lines)
# Add metadata
total_lines = len(lines)
shown_lines = len(selected_lines)
if offset > 1 or limit is not None:
metadata = f"# File: {file_path} (showing lines {offset}-{offset + shown_lines - 1} of {total_lines})\n\n"
else:
metadata = f"# File: {file_path} ({total_lines} lines)\n\n"
return metadata + content
except UnicodeDecodeError:
try:
# Try with different encoding
with open(file_path, 'r', encoding='latin1') as f:
content = f.read()
return f"# File: {file_path} (read with latin1 encoding)\n\n{content}"
except Exception as e:
return f"Error: Could not read file '{file_path}': {e}"
except Exception as e:
return f"Error: Could not read file '{file_path}': {e}"