fix: FileTools uses local workspace before API for file content (#6.2)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from gitea.client import GiteaClient
|
||||
|
||||
@@ -5,8 +7,9 @@ from gitea.client import GiteaClient
|
||||
class FileTools:
|
||||
"""Tools for Gitea file/content operations."""
|
||||
|
||||
def __init__(self, client: GiteaClient) -> None:
|
||||
def __init__(self, client: GiteaClient, repo_path: str | None = None) -> None:
|
||||
self._client = client
|
||||
self._repo_path: str | None = repo_path
|
||||
|
||||
def _paginate_lines(
|
||||
self,
|
||||
@@ -35,6 +38,15 @@ class FileTools:
|
||||
)
|
||||
return result
|
||||
|
||||
def _resolve_local_path(self, owner: str, repo: str, path: str) -> str | None:
|
||||
"""Resolve owner/repo/path to a local filesystem path if the repo is cloned."""
|
||||
if not self._repo_path:
|
||||
return None
|
||||
local_repo: str = os.path.join(self._repo_path, owner, repo)
|
||||
if os.path.isdir(local_repo):
|
||||
return os.path.join(local_repo, path)
|
||||
return None
|
||||
|
||||
def get_file_content(
|
||||
self,
|
||||
owner: str,
|
||||
@@ -45,10 +57,22 @@ class FileTools:
|
||||
) -> str:
|
||||
"""Get the content of a file from a Gitea repository with line paging.
|
||||
|
||||
Checks the local workspace first if repo_path is configured, falling
|
||||
back to the remote API when the file is not available locally.
|
||||
|
||||
Args:
|
||||
offset: 1-indexed line to start from (default 1).
|
||||
limit: Maximum number of lines to return (default 250).
|
||||
"""
|
||||
local_path: str | None = self._resolve_local_path(owner, repo, path)
|
||||
if local_path and os.path.isfile(local_path):
|
||||
try:
|
||||
with open(local_path, 'r', encoding='utf-8', errors='replace') as f:
|
||||
raw: str = f.read()
|
||||
return self._paginate_lines(raw, offset, limit)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
content = self._client.files.get_file_content(owner, repo, path)
|
||||
raw: str = "\n".join(content) if isinstance(content, list) else content
|
||||
@@ -67,11 +91,28 @@ class FileTools:
|
||||
) -> str:
|
||||
"""Get file content at a specific git ref with line paging.
|
||||
|
||||
Checks the local workspace first using ``git show`` if the repo is
|
||||
cloned locally, falling back to the remote API.
|
||||
|
||||
Args:
|
||||
ref: Branch, tag, or commit SHA (default 'master').
|
||||
offset: 1-indexed line to start from (default 1).
|
||||
limit: Maximum number of lines to return (default 250).
|
||||
"""
|
||||
if self._repo_path:
|
||||
local_repo: str = os.path.join(self._repo_path, owner, repo)
|
||||
if os.path.isdir(local_repo):
|
||||
try:
|
||||
import subprocess
|
||||
result = subprocess.run(
|
||||
["git", "-C", local_repo, "show", f"{ref}:{path}"],
|
||||
capture_output=True, text=True, timeout=15,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
return self._paginate_lines(result.stdout, offset, limit)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
content = self._client.files.get_file_content(owner, repo, path, ref)
|
||||
raw: str = "\n".join(content) if isinstance(content, list) else content
|
||||
|
||||
Reference in New Issue
Block a user