diff --git a/tools/github_tool.py b/tools/github_tool.py index 67d89c6..192d832 100644 --- a/tools/github_tool.py +++ b/tools/github_tool.py @@ -54,10 +54,7 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "path": { - "type": "string", - "description": "Path to the file in the repository" - } + "path": {"type": "string", "description": "Path to the file in the repository"} }, "required": ["path"] } @@ -71,10 +68,7 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "path": { - "type": "string", - "description": "Path to the directory in the repository" - } + "path": {"type": "string", "description": "Path to the directory in the repository"} }, "required": ["path"] } @@ -88,10 +82,7 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "query": { - "type": "string", - "description": "Search query" - } + "query": {"type": "string", "description": "Search query"} }, "required": ["query"] } @@ -105,15 +96,8 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "branch_name": { - "type": "string", - "description": "Name of the new branch" - }, - "base_branch": { - "type": "string", - "description": "Name of the base branch", - "default": "main" - } + "branch_name": {"type": "string", "description": "Name of the new branch"}, + "base_branch": {"type": "string", "description": "Name of the base branch", "default": "main"} }, "required": ["branch_name"] } @@ -127,18 +111,9 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "file_path": { - "type": "string", - "description": "Path to the file in the repository" - }, - "commit_message": { - "type": "string", - "description": "Commit message" - }, - "content": { - "type": "string", - "description": "Content of the file" - } + "file_path": {"type": "string", "description": "Path to the file in the repository"}, + "commit_message": {"type": "string", "description": "Commit message"}, + "content": {"type": "string", "description": "Content of the file"} }, "required": ["file_path", "commit_message", "content"] } @@ -152,19 +127,9 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "title": { - "type": "string", - "description": "Title of the pull request" - }, - "body": { - "type": "string", - "description": "Body of the pull request" - }, - "base": { - "type": "string", - "description": "The name of the branch you want the changes pulled into", - "default": "main" - } + "title": {"type": "string", "description": "Title of the pull request"}, + "body": {"type": "string", "description": "Body of the pull request"}, + "base": {"type": "string", "description": "The name of the branch you want the changes pulled into", "default": "main"} }, "required": ["title", "body"] } @@ -178,15 +143,8 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "file_path": { - "type": "string", - "description": "Path to the file in the repository" - }, - "num_commits": { - "type": "integer", - "description": "Number of commits to retrieve", - "default": 10 - } + "file_path": {"type": "string", "description": "Path to the file in the repository"}, + "num_commits": {"type": "integer", "description": "Number of commits to retrieve", "default": 10} }, "required": ["file_path"] } @@ -200,10 +158,7 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "branch": { - "type": "string", - "description": "Name of the branch" - } + "branch": {"type": "string", "description": "Name of the branch"} }, "required": ["branch"] } @@ -225,10 +180,7 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "branch_name": { - "type": "string", - "description": "Name of the branch to set as current" - } + "branch_name": {"type": "string", "description": "Name of the branch to set as current"} }, "required": ["branch_name"] } @@ -242,14 +194,8 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "file_path": { - "type": "string", - "description": "Path to the file in the repository" - }, - "commit_sha": { - "type": "string", - "description": "SHA of the commit to retrieve the file from" - } + "file_path": {"type": "string", "description": "Path to the file in the repository"}, + "commit_sha": {"type": "string", "description": "SHA of the commit to retrieve the file from"} }, "required": ["file_path", "commit_sha"] } @@ -263,16 +209,8 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "per_page": { - "type": "integer", - "description": "Number of branches to return per page (max 100)", - "default": 100 - }, - "all_pages": { - "type": "boolean", - "description": "Whether to fetch all pages of results", - "default": True - } + "per_page": {"type": "integer", "description": "Number of branches to return per page (max 100)", "default": 100}, + "all_pages": {"type": "boolean", "description": "Whether to fetch all pages of results", "default": True} } } } @@ -285,10 +223,7 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "pull_number": { - "type": "integer", - "description": "The number of the pull request" - } + "pull_number": {"type": "integer", "description": "The number of the pull request"} }, "required": ["pull_number"] } @@ -302,10 +237,7 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "pull_number": { - "type": "integer", - "description": "The number of the pull request" - } + "pull_number": {"type": "integer", "description": "The number of the pull request"} }, "required": ["pull_number"] } @@ -319,20 +251,9 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "pull_number": { - "type": "integer", - "description": "The number of the pull request" - }, - "commit_title": { - "type": "string", - "description": "Title for the automatic commit message", - "default": "Merge pull request" - }, - "commit_message": { - "type": "string", - "description": "Extra detail to append to automatic commit message", - "default": "" - }, + "pull_number": {"type": "integer", "description": "The number of the pull request"}, + "commit_title": {"type": "string", "description": "Title for the automatic commit message", "default": "Merge pull request"}, + "commit_message": {"type": "string", "description": "Extra detail to append to automatic commit message", "default": ""}, "merge_method": { "type": "string", "description": "Merge method to use", @@ -352,10 +273,7 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "branch_name": { - "type": "string", - "description": "Name of the branch to delete" - } + "branch_name": {"type": "string", "description": "Name of the branch to delete"} }, "required": ["branch_name"] } @@ -369,10 +287,7 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "issue_number": { - "type": "integer", - "description": "The number of the issue" - } + "issue_number": {"type": "integer", "description": "The number of the issue"} }, "required": ["issue_number"] } @@ -386,19 +301,11 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "title": { - "type": "string", - "description": "Title of the issue" - }, - "body": { - "type": "string", - "description": "Body of the issue" - }, + "title": {"type": "string", "description": "Title of the issue"}, + "body": {"type": "string", "description": "Body of the issue"}, "labels": { "type": "array", - "items": { - "type": "string" - }, + "items": {"type": "string"}, "description": "Labels to apply to the issue" } }, @@ -414,22 +321,9 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "state": { - "type": "string", - "enum": ["open", "closed", "all"], - "default": "open", - "description": "State of the issues to retrieve" - }, - "per_page": { - "type": "integer", - "default": 30, - "description": "Number of issues to return per page" - }, - "page": { - "type": "integer", - "default": 1, - "description": "Page number of the results to fetch" - } + "state": {"type": "string", "enum": ["open", "closed", "all"], "default": "open", "description": "State of the issues to retrieve"}, + "per_page": {"type": "integer", "default": 30, "description": "Number of issues to return per page"}, + "page": {"type": "integer", "default": 1, "description": "Page number of the results to fetch"} } } } @@ -442,14 +336,8 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "issue_number": { - "type": "integer", - "description": "The number of the issue" - }, - "comment": { - "type": "string", - "description": "The comment to add to the issue" - } + "issue_number": {"type": "integer", "description": "The number of the issue"}, + "comment": {"type": "string", "description": "The comment to add to the issue"} }, "required": ["issue_number", "comment"] } @@ -463,10 +351,7 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "issue_number": { - "type": "integer", - "description": "The number of the issue" - } + "issue_number": {"type": "integer", "description": "The number of the issue"} }, "required": ["issue_number"] } @@ -480,14 +365,8 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "name": { - "type": "string", - "description": "Name of the project board" - }, - "body": { - "type": "string", - "description": "Body of the project board" - } + "name": {"type": "string", "description": "Name of the project board"}, + "body": {"type": "string", "description": "Body of the project board"} }, "required": ["name"] } @@ -501,14 +380,8 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "project_id": { - "type": "integer", - "description": "ID of the project board" - }, - "column_name": { - "type": "string", - "description": "Name of the column" - } + "project_id": {"type": "integer", "description": "ID of the project board"}, + "column_name": {"type": "string", "description": "Name of the column"} }, "required": ["project_id", "column_name"] } @@ -522,14 +395,8 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "column_id": { - "type": "integer", - "description": "ID of the project column" - }, - "note": { - "type": "string", - "description": "Note for the project card" - } + "column_id": {"type": "integer", "description": "ID of the project column"}, + "note": {"type": "string", "description": "Note for the project card"} }, "required": ["column_id", "note"] } @@ -543,18 +410,9 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "card_id": { - "type": "integer", - "description": "ID of the project card" - }, - "position": { - "type": "string", - "description": "New position of the card" - }, - "column_id": { - "type": "integer", - "description": "ID of the target column" - } + "card_id": {"type": "integer", "description": "ID of the project card"}, + "position": {"type": "string", "description": "New position of the card"}, + "column_id": {"type": "integer", "description": "ID of the target column"} }, "required": ["card_id", "position", "column_id"] } @@ -568,18 +426,9 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "card_id": { - "type": "integer", - "description": "ID of the project card" - }, - "content_id": { - "type": "integer", - "description": "ID of the issue or pull request" - }, - "content_type": { - "type": "string", - "description": "Type of the content (Issue or PullRequest)" - } + "card_id": {"type": "integer", "description": "ID of the project card"}, + "content_id": {"type": "integer", "description": "ID of the issue or pull request"}, + "content_type": {"type": "string", "description": "Type of the content (Issue or PullRequest)"} }, "required": ["card_id", "content_id", "content_type"] } @@ -601,24 +450,111 @@ class GitHubTool(BaseTool): "parameters": { "type": "object", "properties": { - "project_id": { - "type": "integer", - "description": "ID of the project board" - } + "project_id": {"type": "integer", "description": "ID of the project board"} }, "required": ["project_id"] } } + }, + # New functions for PR review + { + "type": "function", + "function": { + "name": "get_pull_request_details", + "description": "Get detailed information about a pull request", + "parameters": { + "type": "object", + "properties": { + "pull_number": {"type": "integer", "description": "The number of the pull request"} + }, + "required": ["pull_number"] + } + } + }, + { + "type": "function", + "function": { + "name": "get_pull_request_diff", + "description": "Get the diff of a pull request", + "parameters": { + "type": "object", + "properties": { + "pull_number": {"type": "integer", "description": "The number of the pull request"} + }, + "required": ["pull_number"] + } + } + }, + { + "type": "function", + "function": { + "name": "get_pull_request_files", + "description": "Get a list of files changed in a pull request, with their patch details", + "parameters": { + "type": "object", + "properties": { + "pull_number": {"type": "integer", "description": "The number of the pull request"} + }, + "required": ["pull_number"] + } + } + }, + { + "type": "function", + "function": { + "name": "create_pull_request_review_comment", + "description": "Add a comment to a specific line of a file in a pull request review", + "parameters": { + "type": "object", + "properties": { + "pull_number": {"type": "integer", "description": "The number of the pull request"}, + "body": {"type": "string", "description": "The text of the comment"}, + "commit_id": {"type": "string", "description": "The SHA of the commit to comment on"}, + "path": {"type": "string", "description": "The path to the file being commented on"}, + "position": {"type": "integer", "description": "The line index in the diff to comment on (starting at 1)"}, + "side": {"type": "string", "enum": ["LEFT", "RIGHT"], "description": "The side of the diff to comment on (LEFT for old file, RIGHT for new file)", "default": "RIGHT"}, + "start_line": {"type": "integer", "description": "The start line of the diff if commenting on a range"}, + "start_side": {"type": "string", "enum": ["LEFT", "RIGHT"], "description": "The side of the diff for the start line"} + }, + "required": ["pull_number", "body", "commit_id", "path", "position"] + } + } + }, + { + "type": "function", + "function": { + "name": "list_pull_request_review_comments", + "description": "List comments on a pull request review", + "parameters": { + "type": "object", + "properties": { + "pull_number": {"type": "integer", "description": "The number of the pull request"} + }, + "required": ["pull_number"] + } + } + }, + { + "type": "function", + "function": { + "name": "submit_pull_request_review", + "description": "Submit a formal pull request review (APPROVE, REQUEST_CHANGES, COMMENT)", + "parameters": { + "type": "object", + "properties": { + "pull_number": {"type": "integer", "description": "The number of the pull request"}, + "event": {"type": "string", "enum": ["APPROVE", "REQUEST_CHANGES", "COMMENT"], "description": "The type of review event"}, + "body": {"type": "string", "description": "The body of the review (required for REQUEST_CHANGES, optional for others)"} + }, + "required": ["pull_number", "event"] + } + } } ] - - - - @metrics.measure - def execute(self, function_name, **kwargs): + def execute(self, function_name, **kwargs):\ self.logger.info(f"Executing: {function_name}") if function_name == "read_file": @@ -678,6 +614,21 @@ class GitHubTool(BaseTool): return self._list_project_boards() elif function_name == "view_project_board_items": return self._view_project_board_items(kwargs["project_id"]) + # New function dispatching + elif function_name == "get_pull_request_details": + return self._get_pull_request_details(kwargs["pull_number"]) + elif function_name == "get_pull_request_diff": + return self._get_pull_request_diff(kwargs["pull_number"]) + elif function_name == "get_pull_request_files": + return self._get_pull_request_files(kwargs["pull_number"]) + elif function_name == "create_pull_request_review_comment": + return self._create_pull_request_review_comment(kwargs["pull_number"], kwargs["body"], kwargs["commit_id"], + kwargs["path"], kwargs["position"], kwargs.get("side", "RIGHT"), + kwargs.get("start_line"), kwargs.get("start_side")) + elif function_name == "list_pull_request_review_comments": + return self._list_pull_request_review_comments(kwargs["pull_number"]) + elif function_name == "submit_pull_request_review": + return self._submit_pull_request_review(kwargs["pull_number"], kwargs["event"], kwargs.get("body")) else: error_message = f"Unknown function: {function_name}" self.logger.error(error_message) @@ -757,7 +708,7 @@ class GitHubTool(BaseTool): self.logger.info(success_message) return success_message else: - error_message = f"Error committing file: {response.status_code}\nResponse: {response.text}" + error_message = f"Error committing file: {response.status_code}\\nResponse: {response.text}" self.logger.error(error_message) return error_message @@ -777,7 +728,7 @@ class GitHubTool(BaseTool): self.logger.info(success_message) return success_message else: - error_message = f"Error creating pull request: {response.status_code}\nResponse: {response.text}" + error_message = f"Error creating pull request: {response.status_code}\\nResponse: {response.text}" self.logger.error(error_message) return error_message @@ -909,7 +860,7 @@ class GitHubTool(BaseTool): self.logger.info(success_message) return success_message else: - error_message = f"Error approving pull request: {response.status_code}\nResponse: {response.text}" + error_message = f"Error approving pull request: {response.status_code}\\nResponse: {response.text}" self.logger.error(error_message) return error_message @@ -924,7 +875,7 @@ class GitHubTool(BaseTool): self.logger.info(success_message) return success_message else: - error_message = f"Error closing pull request: {response.status_code}\nResponse: {response.text}" + error_message = f"Error closing pull request: {response.status_code}\\nResponse: {response.text}" self.logger.error(error_message) return error_message @@ -939,7 +890,7 @@ class GitHubTool(BaseTool): self.logger.info(success_message) return success_message else: - error_message = f"Error merging pull request: {response.status_code}\nResponse: {response.text}" + error_message = f"Error merging pull request: {response.status_code}\\nResponse: {response.text}" self.logger.error(error_message) return error_message @@ -953,7 +904,7 @@ class GitHubTool(BaseTool): self.logger.info(success_message) return success_message else: - error_message = f"Error deleting branch: {response.status_code}\nResponse: {response.text}" + error_message = f"Error deleting branch: {response.status_code}\\nResponse: {response.text}" self.logger.error(error_message) return error_message @metrics.measure @@ -977,7 +928,7 @@ class GitHubTool(BaseTool): self.logger.info(f"Successfully retrieved details for issue {issue_number}") return issue_details else: - error_message = f"Error getting issue details: {response.status_code}\nResponse: {response.text}" + error_message = f"Error getting issue details: {response.status_code}\\nResponse: {response.text}" self.logger.error(error_message) return error_message @@ -998,7 +949,7 @@ class GitHubTool(BaseTool): self.logger.info(success_message) return success_message else: - error_message = f"Error creating issue: {response.status_code}\nResponse: {response.text}" + error_message = f"Error creating issue: {response.status_code}\\nResponse: {response.text}" self.logger.error(error_message) return error_message @@ -1023,7 +974,7 @@ class GitHubTool(BaseTool): self.logger.info(f"Successfully listed issues. Found {len(issues)} issues.") return issues else: - error_message = f"Error listing issues: {response.status_code}\nResponse: {response.text}" + error_message = f"Error listing issues: {response.status_code}\\nResponse: {response.text}" self.logger.error(error_message) return error_message @@ -1039,7 +990,7 @@ class GitHubTool(BaseTool): self.logger.info(success_message) return success_message else: - error_message = f"Error adding comment to issue: {response.status_code}\nResponse: {response.text}" + error_message = f"Error adding comment to issue: {response.status_code}\\nResponse: {response.text}" self.logger.error(error_message) return error_message @@ -1059,7 +1010,7 @@ class GitHubTool(BaseTool): self.logger.info(f"Successfully retrieved comments for issue {issue_number}. Found {len(comments)} comments.") return comments else: - error_message = f"Error getting issue comments: {response.status_code}\nResponse: {response.text}" + error_message = f"Error getting issue comments: {response.status_code}\\nResponse: {response.text}" self.logger.error(error_message) return error_message @@ -1185,7 +1136,7 @@ class GitHubTool(BaseTool): } @metrics.measure - def _list_project_boards(self): + def _list_project_boards(self):\ self.logger.info("Fetching project boards...") url = f"{self.base_url}/repos/{self.repo}/projects" response = requests.get(url, headers=self.headers) @@ -1222,4 +1173,102 @@ class GitHubTool(BaseTool): else: error_message = f"Error fetching columns for project board: {columns_response.status_code}" self.logger.error(error_message) - return error_message \ No newline at end of file + return error_message + + # New functions for PR review capabilities + @metrics.measure + def _get_pull_request_details(self, pull_number): + self.logger.info(f"Getting details for pull request: {pull_number}") + url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}" + response = requests.get(url, headers=self.headers) + if response.status_code == 200: + self.logger.info(f"Successfully retrieved details for PR {pull_number}") + return response.json() + else: + error_message = f"Error getting pull request details: {response.status_code}\\nResponse: {response.text}" + self.logger.error(error_message) + return error_message + + @metrics.measure + def _get_pull_request_diff(self, pull_number): + self.logger.info(f"Getting diff for pull request: {pull_number}") + url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}" + headers = self.headers.copy() + headers["Accept"] = "application/vnd.github.v3.diff" + response = requests.get(url, headers=headers) + if response.status_code == 200: + self.logger.info(f"Successfully retrieved diff for PR {pull_number}") + return response.text + else: + error_message = f"Error getting pull request diff: {response.status_code}\\nResponse: {response.text}" + self.logger.error(error_message) + return error_message + + @metrics.measure + def _get_pull_request_files(self, pull_number): + self.logger.info(f"Getting files for pull request: {pull_number}") + url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}/files" + response = requests.get(url, headers=self.headers) + if response.status_code == 200: + self.logger.info(f"Successfully retrieved files for PR {pull_number}") + return response.json() + else: + error_message = f"Error getting pull request files: {response.status_code}\\nResponse: {response.text}" + self.logger.error(error_message) + return error_message + + @metrics.measure + def _create_pull_request_review_comment(self, pull_number, body, commit_id, path, position, side="RIGHT", start_line=None, start_side=None): + self.logger.info(f"Adding review comment to PR {pull_number} on file {path} at position {position}") + url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}/comments" + data = { + "body": body, + "commit_id": commit_id, + "path": path, + "position": position, + "side": side + } + if start_line is not None: + data["start_line"] = start_line + if start_side is not None: + data["start_side"] = start_side + + response = requests.post(url, headers=self.headers, json=data) + if response.status_code == 201: + success_message = f"Comment added to PR {pull_number} successfully: {response.json()['html_url']}" + self.logger.info(success_message) + return success_message + else: + error_message = f"Error creating pull request review comment: {response.status_code}\\nResponse: {response.text}" + self.logger.error(error_message) + return error_message + + @metrics.measure + def _list_pull_request_review_comments(self, pull_number): + self.logger.info(f"Listing review comments for pull request: {pull_number}") + url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}/comments" + response = requests.get(url, headers=self.headers) + if response.status_code == 200: + self.logger.info(f"Successfully retrieved review comments for PR {pull_number}") + return response.json() + else: + error_message = f"Error listing pull request review comments: {response.status_code}\\nResponse: {response.text}" + self.logger.error(error_message) + return error_message + + @metrics.measure + def _submit_pull_request_review(self, pull_number, event, body=None): + self.logger.info(f"Submitting review for pull request {pull_number} with event: {event}") + url = f"{self.base_url}/repos/{self.repo}/pulls/{pull_number}/reviews" + data = {"event": event} + if body: + data["body"] = body + response = requests.post(url, headers=self.headers, json=data) + if response.status_code == 200: + success_message = f"Review submitted for PR {pull_number} successfully." + self.logger.info(success_message) + return success_message + else: + error_message = f"Error submitting pull request review: {response.status_code}\\nResponse: {response.text}" + self.logger.error(error_message) + return error_message