from pathlib import Path from typing import Any from duck_core.tools.base import ToolResult from duck_core.tools.paths import WorkspacePathError, resolve_workspace_path class FileReadTool: name = "file_read" risk_level = "low" def __init__(self, workspace: str, max_bytes: int = 1_000_000): self.workspace = workspace self.max_bytes = max_bytes async def run(self, args: dict[str, Any]) -> ToolResult: raw_path = str(args.get("path", "")) try: path = resolve_workspace_path(self.workspace, raw_path) except WorkspacePathError as exc: return ToolResult(ok=False, error=str(exc)) if self._requires_approval(path): return ToolResult(ok=False, error=f"Reading {raw_path} requires explicit approval") if not path.is_file(): return ToolResult(ok=False, error=f"File not found: {raw_path}") if path.stat().st_size > self.max_bytes: return ToolResult(ok=False, error=f"File exceeds max size: {self.max_bytes}") return ToolResult( ok=True, output=path.read_text(errors="replace"), metadata={"path": str(path), "bytes_read": path.stat().st_size}, ) def _requires_approval(self, path: Path) -> bool: parts = set(path.parts) return path.name == ".env" or ".ssh" in parts or str(path) == "/etc/shadow"