| 64 | return ToolResult(output=f"Error running rg: {exc}", is_error=True) |
| 65 | |
| 66 | def _python_search(self, pattern: str, path: Path, include: str | None) -> ToolResult: |
| 67 | try: |
| 68 | regex = re.compile(pattern) |
| 69 | except re.error as exc: |
| 70 | return ToolResult(output=f"Invalid regex: {exc}", is_error=True) |
| 71 | |
| 72 | matches: list[str] = [] |
| 73 | files = [path] if path.is_file() else sorted(path.rglob(include or "*")) |
| 74 | |
| 75 | for fp in files: |
| 76 | if not fp.is_file(): |
| 77 | continue |
| 78 | try: |
| 79 | for i, line in enumerate(fp.read_text(errors="replace").splitlines(), 1): |
| 80 | if regex.search(line): |
| 81 | matches.append(f"{fp}:{i}:{line.rstrip()}") |
| 82 | if len(matches) >= MAX_MATCHES: |
| 83 | matches.append(f"... (truncated at {MAX_MATCHES} matches)") |
| 84 | return ToolResult(output="\n".join(matches)) |
| 85 | except Exception: |
| 86 | continue |
| 87 | |
| 88 | if not matches: |
| 89 | return ToolResult(output="No matches found.") |
| 90 | return ToolResult(output="\n".join(matches)) |