Files
coding-agent-gitea/gitea/tools/issue_tools.py
T
meeks dfdd8c0931 fix: improve prompts, error messages, and workspace concurrency
- Externalize coordinator, notification, and planning prompts to separate files
- Add workspace mutex for concurrent file operations
- Improve error messages across file_tools, issue_tools, and pr_tools
- Add logging to tool modules for better debugging
- Update tests to match new error message strings
2026-08-01 20:01:46 +02:00

171 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Tools for Gitea issue operations."""
import json
import logging
from typing import Any
from gitea.client import GiteaClient
from gitea.models import IssueModel, CommentModel, LabelModel
logger: logging.Logger = logging.getLogger("gitea.tools.issue_tools")
class IssueTools:
"""Tools for Gitea issue operations."""
def __init__(self, client: GiteaClient) -> None:
self._client = client
def get_issue(self, owner: str, repo: str, issue_number: int) -> IssueModel:
try:
return self._client.issues.get_issue(owner, repo, issue_number)
except Exception as e:
logger.error(f"Error getting issue #{issue_number}: {e}", exc_info=True)
raise
def close_issue(self, owner: str, repo: str, issue_number: int) -> str:
try:
self._client.issues.close_issue(owner, repo, issue_number)
return f"Issue #{issue_number} closed successfully."
except Exception as e:
logger.error(
f"Failed to close issue #{issue_number} in {owner}/{repo}: {e}",
exc_info=True,
)
return (
f"Error: Could not close issue #{issue_number} in {owner}/{repo}. "
f"Check permissions or if the issue is already closed. Details: {e}"
)
def get_issue_comments(
self,
owner: str,
repo: str,
issue_number: int,
limit: int = 20,
offset: int = 0,
) -> str:
"""Get comments on an issue 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.issues.get_issue_comments(
owner, repo, issue_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:
logger.error(
f"Failed to get comments for issue #{issue_number} in {owner}/{repo}: {e}",
exc_info=True,
)
return (
f"Error: Could not retrieve comments for issue #{issue_number} in {owner}/{repo}. "
f"Verify the issue exists and you have access. Details: {e}"
)
def list_assigned_issues(self) -> list[dict[str, Any]]:
try:
repos = self._client.repos.list_all_user_repos()
all_issues: list[dict[str, Any]] = []
for repo in repos:
owner = repo.owner
repo_name = repo.name
issues = self._client.issues.list_assigned_issues(owner, repo_name)
if issues:
all_issues.extend(
[
issue.model_dump()
if hasattr(issue, "model_dump")
else issue
for issue in issues
]
)
return all_issues
except Exception as e:
logger.error(f"Error listing assigned issues: {e}", exc_info=True)
return []
def list_issues(self, owner: str, repo: str, state: str = "open") -> str:
try:
issues = self._client.issues.list_repo_issues(owner, repo, state)
if not issues:
return f"No {state} issues in {owner}/{repo}."
summary = [f"#{issue.number}: {issue.title}" for issue in issues]
return "\n".join(summary)
except Exception as e:
logger.error(
f"Failed to list {state} issues in {owner}/{repo}: {e}",
exc_info=True,
)
return (
f"Error: Could not list issues for {owner}/{repo}. "
f"Verify the repository exists and you have access. Details: {e}"
)
def create_issue(
self,
owner: str,
repo: str,
title: str,
body: str,
labels: list[str] | None = None,
assignees: list[str] | None = None,
) -> str:
try:
issue = self._client.issues.create_issue(
owner, repo, title, body, labels, assignees
)
return f"Issue #{issue.number} created successfully in {owner}/{repo}."
except Exception as e:
logger.error(
f"Failed to create issue '{title}' in {owner}/{repo}: {e}",
exc_info=True,
)
return (
f"Error: Could not create issue '{title}' in {owner}/{repo}. "
f"Check repository permissions and label/assignee names. Details: {e}"
)
def add_label_to_issue(
self, owner: str, repo: str, issue_number: int, label: str
) -> str:
try:
self._client.issues.add_label(owner, repo, issue_number, label)
return f"Label '{label}' added to issue #{issue_number}."
except Exception as e:
logger.error(
f"Failed to add label '{label}' to issue #{issue_number} in {owner}/{repo}: {e}",
exc_info=True,
)
return (
f"Error: Could not add label '{label}' to issue #{issue_number} in {owner}/{repo}. "
f"Verify the label exists in the repository. Details: {e}"
)
def add_comment_to_issue(
self, owner: str, repo: str, issue_number: int, body: str
) -> str:
try:
self._client.issues.add_comment(owner, repo, issue_number, body)
return f"Comment added to issue #{issue_number}."
except Exception as e:
logger.error(
f"Failed to add comment to issue #{issue_number} in {owner}/{repo}: {e}",
exc_info=True,
)
return (
f"Error: Could not add comment to issue #{issue_number} in {owner}/{repo}. "
f"Verify the issue exists and you have write access. Details: {e}"
)