Execute read file.
(self, path: str, offset: int | None = None, limit: int | None = None)
| 106 | } |
| 107 | |
| 108 | async def execute(self, path: str, offset: int | None = None, limit: int | None = None) -> ToolResult: |
| 109 | """Execute read file.""" |
| 110 | try: |
| 111 | file_path = Path(path) |
| 112 | # Resolve relative paths relative to workspace_dir |
| 113 | if not file_path.is_absolute(): |
| 114 | file_path = self.workspace_dir / file_path |
| 115 | |
| 116 | if not file_path.exists(): |
| 117 | return ToolResult( |
| 118 | success=False, |
| 119 | content="", |
| 120 | error=f"File not found: {path}", |
| 121 | ) |
| 122 | |
| 123 | # Read file content with line numbers |
| 124 | with open(file_path, encoding="utf-8") as f: |
| 125 | lines = f.readlines() |
| 126 | |
| 127 | # Apply offset and limit |
| 128 | start = (offset - 1) if offset else 0 |
| 129 | end = (start + limit) if limit else len(lines) |
| 130 | if start < 0: |
| 131 | start = 0 |
| 132 | if end > len(lines): |
| 133 | end = len(lines) |
| 134 | |
| 135 | selected_lines = lines[start:end] |
| 136 | |
| 137 | # Format with line numbers (1-indexed) |
| 138 | numbered_lines = [] |
| 139 | for i, line in enumerate(selected_lines, start=start + 1): |
| 140 | # Remove trailing newline for formatting |
| 141 | line_content = line.rstrip("\n") |
| 142 | numbered_lines.append(f"{i:6d}|{line_content}") |
| 143 | |
| 144 | content = "\n".join(numbered_lines) |
| 145 | |
| 146 | # Apply token truncation if needed |
| 147 | max_tokens = 32000 |
| 148 | content = truncate_text_by_tokens(content, max_tokens) |
| 149 | |
| 150 | return ToolResult(success=True, content=content) |
| 151 | except Exception as e: |
| 152 | return ToolResult(success=False, content="", error=str(e)) |
| 153 | |
| 154 | |
| 155 | class WriteTool(Tool): |