"""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}" )