"""Issue Handler - manages Gitea issues, labels, milestones, and comments""" import json from typing import Any from plugins.gitea.client import GiteaClient def get_tool_specifications() -> list[dict[str, Any]]: """Return tool specifications for ToolGenerator""" return [ # === ISSUES === { "name": "list_issues", "method_name": "list_issues", "description": "List issues in a Gitea repository with filters. Returns paginated list of issues.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, "state": { "type": "string", "description": "Filter by state", "enum": ["open", "closed", "all"], "default": "open", }, "labels": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Comma-separated label IDs", }, "q": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Search query", }, "page": { "type": "integer", "description": "Page number", "default": 1, "minimum": 1, }, "limit": { "type": "integer", "description": "Items per page (1-100)", "default": 30, "minimum": 1, "maximum": 100, }, }, "required": ["owner", "repo"], }, "scope": "read", }, { "name": "get_issue", "method_name": "get_issue", "description": "Get details of a specific issue by number.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, "issue_number": { "type": "integer", "description": "Issue number", "minimum": 1, }, }, "required": ["owner", "repo", "issue_number"], }, "scope": "read", }, { "name": "create_issue", "method_name": "create_issue", "description": "Create a new issue in a Gitea repository with optional labels, assignees, and milestone.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, "title": { "type": "string", "description": "Issue title", "minLength": 1, "maxLength": 255, }, "body": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Issue description (supports Markdown)", }, "assignee": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Assignee username", }, "assignees": { "anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}], "description": "List of assignee usernames", }, "labels": { "anyOf": [ {"type": "array", "items": {"type": "integer"}}, {"type": "null"}, ], "description": "List of label IDs", }, "milestone": { "anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Milestone ID", }, "closed": { "type": "boolean", "description": "Create as closed", "default": False, }, }, "required": ["owner", "repo", "title"], }, "scope": "write", }, { "name": "update_issue", "method_name": "update_issue", "description": "Update an existing issue. Can modify title, body, state, assignees, labels, and milestone.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, "issue_number": { "type": "integer", "description": "Issue number", "minimum": 1, }, "title": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Issue title", }, "body": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Issue description", }, "state": { "anyOf": [{"type": "string", "enum": ["open", "closed"]}, {"type": "null"}], "description": "Issue state", }, "assignee": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Assignee username", }, "assignees": { "anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}], "description": "List of assignee usernames", }, "labels": { "anyOf": [ {"type": "array", "items": {"type": "integer"}}, {"type": "null"}, ], "description": "List of label IDs", }, "milestone": { "anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Milestone ID", }, }, "required": ["owner", "repo", "issue_number"], }, "scope": "write", }, { "name": "close_issue", "method_name": "close_issue", "description": "Close an issue.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, "issue_number": { "type": "integer", "description": "Issue number", "minimum": 1, }, }, "required": ["owner", "repo", "issue_number"], }, "scope": "write", }, { "name": "reopen_issue", "method_name": "reopen_issue", "description": "Reopen a closed issue.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, "issue_number": { "type": "integer", "description": "Issue number", "minimum": 1, }, }, "required": ["owner", "repo", "issue_number"], }, "scope": "write", }, # === COMMENTS === { "name": "list_issue_comments", "method_name": "list_issue_comments", "description": "List all comments on an issue.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, "issue_number": { "type": "integer", "description": "Issue number", "minimum": 1, }, }, "required": ["owner", "repo", "issue_number"], }, "scope": "read", }, { "name": "create_issue_comment", "method_name": "create_issue_comment", "description": "Add a comment to an issue.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, "issue_number": { "type": "integer", "description": "Issue number", "minimum": 1, }, "body": { "type": "string", "description": "Comment body (supports Markdown)", "minLength": 1, }, }, "required": ["owner", "repo", "issue_number", "body"], }, "scope": "write", }, # === LABELS === { "name": "list_labels", "method_name": "list_labels", "description": "List all labels in a repository.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, }, "required": ["owner", "repo"], }, "scope": "read", }, { "name": "create_label", "method_name": "create_label", "description": "Create a new label in a repository.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, "name": { "type": "string", "description": "Label name", "minLength": 1, "maxLength": 50, }, "color": { "type": "string", "description": "Label color (hex without #, e.g., 'ff0000')", "pattern": "^[0-9A-Fa-f]{6}$", }, "description": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Label description", "maxLength": 200, }, }, "required": ["owner", "repo", "name", "color"], }, "scope": "write", }, # === MILESTONES === { "name": "list_milestones", "method_name": "list_milestones", "description": "List all milestones in a repository.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, "state": { "anyOf": [ {"type": "string", "enum": ["open", "closed", "all"]}, {"type": "null"}, ], "description": "Filter by state", "default": "open", }, }, "required": ["owner", "repo"], }, "scope": "read", }, { "name": "create_milestone", "method_name": "create_milestone", "description": "Create a new milestone in a repository.", "schema": { "type": "object", "properties": { "owner": {"type": "string", "description": "Repository owner", "minLength": 1}, "repo": {"type": "string", "description": "Repository name", "minLength": 1}, "title": { "type": "string", "description": "Milestone title", "minLength": 1, "maxLength": 255, }, "description": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Milestone description", }, "due_on": { "anyOf": [{"type": "string", "format": "date-time"}, {"type": "null"}], "description": "Due date (ISO 8601 format)", }, "state": { "type": "string", "description": "State", "enum": ["open", "closed"], "default": "open", }, }, "required": ["owner", "repo", "title"], }, "scope": "write", }, ] async def list_issues( client: GiteaClient, owner: str, repo: str, state: str = "open", labels: str | None = None, q: str | None = None, page: int = 1, limit: int = 30, ) -> str: """List repository issues""" params = {"state": state, "labels": labels, "q": q, "page": page, "limit": limit} issues = await client.list_issues(owner, repo, params) result = {"success": True, "count": len(issues), "issues": issues} return json.dumps(result, indent=2) async def get_issue(client: GiteaClient, owner: str, repo: str, issue_number: int) -> str: """Get issue details""" issue = await client.get_issue(owner, repo, issue_number) result = {"success": True, "issue": issue} return json.dumps(result, indent=2) async def create_issue( client: GiteaClient, owner: str, repo: str, title: str, body: str | None = None, assignee: str | None = None, assignees: list[str] | None = None, labels: list[int] | None = None, milestone: int | None = None, closed: bool = False, ) -> str: """Create a new issue""" data = { "title": title, "body": body, "assignee": assignee, "assignees": assignees, "labels": labels, "milestone": milestone, "closed": closed, } issue = await client.create_issue(owner, repo, data) result = { "success": True, "message": f"Issue #{issue['number']} created successfully", "issue": issue, } return json.dumps(result, indent=2) async def update_issue( client: GiteaClient, owner: str, repo: str, issue_number: int, **kwargs ) -> str: """Update an issue""" # Build update data from kwargs data = { k: v for k, v in kwargs.items() if v is not None and k not in ["owner", "repo", "issue_number"] } issue = await client.update_issue(owner, repo, issue_number, data) result = { "success": True, "message": f"Issue #{issue_number} updated successfully", "issue": issue, } return json.dumps(result, indent=2) async def close_issue(client: GiteaClient, owner: str, repo: str, issue_number: int) -> str: """Close an issue""" data = {"state": "closed"} issue = await client.update_issue(owner, repo, issue_number, data) result = { "success": True, "message": f"Issue #{issue_number} closed successfully", "issue": issue, } return json.dumps(result, indent=2) async def reopen_issue(client: GiteaClient, owner: str, repo: str, issue_number: int) -> str: """Reopen an issue""" data = {"state": "open"} issue = await client.update_issue(owner, repo, issue_number, data) result = { "success": True, "message": f"Issue #{issue_number} reopened successfully", "issue": issue, } return json.dumps(result, indent=2) # Comment operations async def list_issue_comments(client: GiteaClient, owner: str, repo: str, issue_number: int) -> str: """List issue comments""" comments = await client.list_issue_comments(owner, repo, issue_number) result = {"success": True, "count": len(comments), "comments": comments} return json.dumps(result, indent=2) async def create_issue_comment( client: GiteaClient, owner: str, repo: str, issue_number: int, body: str ) -> str: """Create issue comment""" data = {"body": body} comment = await client.create_issue_comment(owner, repo, issue_number, data) result = { "success": True, "message": f"Comment added to issue #{issue_number}", "comment": comment, } return json.dumps(result, indent=2) # Label operations async def list_labels(client: GiteaClient, owner: str, repo: str) -> str: """List repository labels""" labels = await client.list_labels(owner, repo) result = {"success": True, "count": len(labels), "labels": labels} return json.dumps(result, indent=2) async def create_label( client: GiteaClient, owner: str, repo: str, name: str, color: str, description: str | None = None, ) -> str: """Create a label""" data = {"name": name, "color": color, "description": description} label = await client.create_label(owner, repo, data) result = {"success": True, "message": f"Label '{name}' created successfully", "label": label} return json.dumps(result, indent=2) # Milestone operations async def list_milestones( client: GiteaClient, owner: str, repo: str, state: str | None = None ) -> str: """List repository milestones""" milestones = await client.list_milestones(owner, repo, state=state) result = {"success": True, "count": len(milestones), "milestones": milestones} return json.dumps(result, indent=2) async def create_milestone( client: GiteaClient, owner: str, repo: str, title: str, description: str | None = None, due_on: str | None = None, state: str = "open", ) -> str: """Create a milestone""" data = {"title": title, "description": description, "due_on": due_on, "state": state} milestone = await client.create_milestone(owner, repo, data) result = { "success": True, "message": f"Milestone '{title}' created successfully", "milestone": milestone, } return json.dumps(result, indent=2)