feat: add output truncation and offset paging to all large-output tools

- coding_tools: list_files (max_entries=200, sorted), run_command
  (max_chars=8000, output_offset), grep_search (max_lines=100, offset);
  added _truncate_output helper for run_command paging
- pr_tools: get_pull_request_diff / get_pull_request_patch now truncate
  at 15k chars with hunk-boundary awareness and char_offset paging;
  get_pull_request_comments gains limit/offset paging
- issue_tools: get_issue_comments gains limit/offset paging
- file_tools: get_file_content / get_file_content_with_ref now paginate
  by line (offset=1, limit=250), matching existing read_file convention;
  added _paginate_lines helper
- research_tools: fetch_url gains char_offset parameter; _smart_truncate
  now slices from an offset and embeds next char_offset in the footer
- gitea_tools facade: all new params threaded through
This commit is contained in:
Michael
2026-06-28 19:57:14 +02:00
parent c0c9278d75
commit bf13a979c2
6 changed files with 383 additions and 69 deletions
+85 -7
View File
@@ -5,6 +5,30 @@ from typing import Any
from gitea.client import GiteaClient
from gitea.models import PullRequestModel, CommentModel
_MAX_DIFF_CHARS: int = 15_000
def _truncate_diff(
text: str,
max_chars: int = _MAX_DIFF_CHARS,
char_offset: int = 0,
) -> str:
"""Slice [char_offset : char_offset+max_chars] from text, cutting at a hunk boundary."""
total: int = len(text)
chunk: str = text[char_offset : char_offset + max_chars]
if char_offset > 0 or (char_offset + max_chars) < total:
# Try to cut at a diff hunk boundary (@@) for coherence
hunk_boundary: int = chunk.rfind("\n@@")
if hunk_boundary > int(len(chunk) * 0.6):
chunk = chunk[:hunk_boundary]
next_offset: int = char_offset + len(chunk)
chunk += (
f"\n\n[Diff truncated — {total} chars total. "
f"Showing chars {char_offset}{next_offset}. "
f"Re-call with char_offset={next_offset} to read more.]"
)
return chunk
class PRTools:
"""Tools for Gitea pull request operations."""
@@ -26,10 +50,34 @@ class PRTools:
except Exception as e:
return f"Error closing pull request: {str(e)}"
def get_pull_request_comments(self, owner: str, repo: str, pull_number: int) -> str:
def get_pull_request_comments(
self,
owner: str,
repo: str,
pull_number: int,
limit: int = 20,
offset: int = 0,
) -> str:
"""Get comments on a pull request with optional paging.
Args:
limit: Maximum number of comments to return (default 20).
offset: Zero-based comment index to start from (default 0).
"""
try:
comments: list[CommentModel] = self._client.get_pull_request_comments(owner, repo, pull_number)
return json.dumps([c.model_dump() for c in comments], indent=2)
comments: list[CommentModel] = self._client.get_pull_request_comments(
owner, repo, pull_number
)
total: int = len(comments)
page: list[CommentModel] = comments[offset : offset + limit]
result: str = json.dumps([c.model_dump() for c in page], indent=2)
if total > offset + limit:
next_offset: int = offset + limit
result += (
f"\n\n[{total} comments total — showing {offset}{offset + len(page)}. "
f"Re-call with offset={next_offset} to see more.]"
)
return result
except Exception as e:
return f"Error getting PR comments: {str(e)}"
@@ -72,15 +120,45 @@ class PRTools:
except Exception as e:
return f"Error adding label to PR #{pr_number}: {e}"
def get_pull_request_diff(self, owner: str, repo: str, pull_number: int) -> str:
def get_pull_request_diff(
self,
owner: str,
repo: str,
pull_number: int,
max_chars: int = _MAX_DIFF_CHARS,
char_offset: int = 0,
) -> str:
"""Get the diff of a pull request, with truncation and offset paging.
Args:
max_chars: Maximum characters to return (default 15 000).
char_offset: Character offset to start reading from (default 0).
Increment by max_chars to page through a large diff.
"""
try:
return self._client.get_pull_request_diff(owner, repo, pull_number)
diff: str = self._client.get_pull_request_diff(owner, repo, pull_number)
return _truncate_diff(diff, max_chars, char_offset)
except Exception as e:
return f"Error getting PR diff: {str(e)}"
def get_pull_request_patch(self, owner: str, repo: str, pull_number: int) -> str:
def get_pull_request_patch(
self,
owner: str,
repo: str,
pull_number: int,
max_chars: int = _MAX_DIFF_CHARS,
char_offset: int = 0,
) -> str:
"""Get the patch of a pull request, with truncation and offset paging.
Args:
max_chars: Maximum characters to return (default 15 000).
char_offset: Character offset to start reading from (default 0).
Increment by max_chars to page through a large patch.
"""
try:
return self._client.get_pull_request_patch(owner, repo, pull_number)
patch: str = self._client.get_pull_request_patch(owner, repo, pull_number)
return _truncate_diff(patch, max_chars, char_offset)
except Exception as e:
return f"Error getting PR patch: {str(e)}"