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