From b0f04b056fe3492a0112426453c6c7e79ca0968c Mon Sep 17 00:00:00 2001 From: tomjuggler Date: Thu, 6 Nov 2025 20:11:34 +0200 Subject: [PATCH 1/3] feat: add git_branch and git_remote tools Co-authored-by: aider (deepseek/deepseek-reasoner) --- aider/coders/navigator_coder.py | 8 ++ aider/tools/__init__.py | 4 + aider/tools/git.py | 127 ++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) diff --git a/aider/coders/navigator_coder.py b/aider/coders/navigator_coder.py index df7e4159ccd..50fec9cb7bf 100644 --- a/aider/coders/navigator_coder.py +++ b/aider/coders/navigator_coder.py @@ -61,10 +61,14 @@ _execute_git_log, _execute_git_show, _execute_git_status, + _execute_git_branch, + _execute_git_remote, git_diff_schema, git_log_schema, git_show_schema, git_status_schema, + git_branch_schema, + git_remote_schema, ) from aider.tools.grep import _execute_grep from aider.tools.indent_lines import _execute_indent_lines @@ -217,6 +221,8 @@ def get_local_tool_schemas(self): git_log_schema, git_show_schema, git_status_schema, + git_branch_schema, + git_remote_schema, ] async def initialize_mcp_tools(self): @@ -297,6 +303,8 @@ async def _execute_local_tool_calls(self, tool_calls_list): "git_log": _execute_git_log, "git_show": _execute_git_show, "git_status": _execute_git_status, + "git_branch": _execute_git_branch, + "git_remote": _execute_git_remote, } func = tool_functions.get(norm_tool_name) diff --git a/aider/tools/__init__.py b/aider/tools/__init__.py index 3de1c4945fc..d3ea822f940 100644 --- a/aider/tools/__init__.py +++ b/aider/tools/__init__.py @@ -15,10 +15,14 @@ _execute_git_log, _execute_git_show, _execute_git_status, + _execute_git_branch, + _execute_git_remote, git_diff_schema, git_log_schema, git_show_schema, git_status_schema, + git_branch_schema, + git_remote_schema, ) from .grep import _execute_grep, grep_schema from .indent_lines import _execute_indent_lines, indent_lines_schema diff --git a/aider/tools/git.py b/aider/tools/git.py index f9fefb7f507..a6afeb2256c 100644 --- a/aider/tools/git.py +++ b/aider/tools/git.py @@ -140,3 +140,130 @@ def _execute_git_status(coder): except ANY_GIT_ERROR as e: coder.io.tool_error(f"Error running git status: {e}") return f"Error running git status: {e}" + + +git_branch_schema = { + "type": "function", + "function": { + "name": "git_branch", + "description": "List, create, or delete branches.", + "parameters": { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": ["list", "create", "delete"], + "description": "The branch operation to perform.", + }, + "name": { + "type": "string", + "description": "Branch name for create/delete operations.", + }, + }, + "required": ["action"], + }, + }, +} + + +def _execute_git_branch(coder, action, name=None): + """ + List, create, or delete branches. + """ + if not coder.repo: + return "Not in a git repository." + + try: + if action == "list": + # Get all branches and current branch + branches = [] + current_branch = coder.repo.repo.active_branch.name + for branch in coder.repo.repo.heads: + prefix = "* " if branch.name == current_branch else " " + branches.append(f"{prefix}{branch.name}") + return "\n".join(branches) + + elif action == "create": + if not name: + return "Branch name required for create operation." + coder.repo.repo.create_head(name) + return f"Created branch: {name}" + + elif action == "delete": + if not name: + return "Branch name required for delete operation." + coder.repo.repo.delete_head(name) + return f"Deleted branch: {name}" + + else: + return f"Unknown branch action: {action}" + + except ANY_GIT_ERROR as e: + coder.io.tool_error(f"Error running git branch: {e}") + return f"Error running git branch: {e}" + + +git_remote_schema = { + "type": "function", + "function": { + "name": "git_remote", + "description": "Manage set of tracked repositories.", + "parameters": { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": ["list", "add", "remove"], + "description": "The remote operation to perform.", + }, + "name": { + "type": "string", + "description": "Remote name for add/remove operations.", + }, + "url": { + "type": "string", + "description": "Remote URL for add operation.", + }, + }, + "required": ["action"], + }, + }, +} + + +def _execute_git_remote(coder, action, name=None, url=None): + """ + Manage set of tracked repositories. + """ + if not coder.repo: + return "Not in a git repository." + + try: + if action == "list": + remotes = coder.repo.repo.remotes + if not remotes: + return "No remotes configured." + + result = [] + for remote in remotes: + result.append(f"{remote.name}\t{remote.url}") + return "\n".join(result) + + elif action == "add": + if not name or not url: + return "Remote name and URL required for add operation." + coder.repo.repo.create_remote(name, url) + return f"Added remote: {name} -> {url}" + + elif action == "remove": + if not name: + return "Remote name required for remove operation." + coder.repo.repo.delete_remote(name) + return f"Removed remote: {name}" + + else: + return f"Unknown remote action: {action}" + + except ANY_GIT_ERROR as e: + coder.io.tool_error(f"Error running git remote: {e}") + return f"Error running git remote: {e}" From 8b44bb9c9ed9a8f1501ee2373ba8a3892e184f34 Mon Sep 17 00:00:00 2001 From: tomjuggler Date: Thu, 6 Nov 2025 20:29:01 +0200 Subject: [PATCH 2/3] refactor: make git branch and remote tools read-only Co-authored-by: aider (deepseek/deepseek-reasoner) --- aider/tools/git.py | 106 ++++++++++----------------------------------- 1 file changed, 24 insertions(+), 82 deletions(-) diff --git a/aider/tools/git.py b/aider/tools/git.py index a6afeb2256c..407758c12ed 100644 --- a/aider/tools/git.py +++ b/aider/tools/git.py @@ -146,58 +146,31 @@ def _execute_git_status(coder): "type": "function", "function": { "name": "git_branch", - "description": "List, create, or delete branches.", + "description": "List branches in the repository.", "parameters": { "type": "object", - "properties": { - "action": { - "type": "string", - "enum": ["list", "create", "delete"], - "description": "The branch operation to perform.", - }, - "name": { - "type": "string", - "description": "Branch name for create/delete operations.", - }, - }, - "required": ["action"], + "properties": {}, + "required": [], }, }, } -def _execute_git_branch(coder, action, name=None): +def _execute_git_branch(coder): """ - List, create, or delete branches. + List branches in the repository. """ if not coder.repo: return "Not in a git repository." try: - if action == "list": - # Get all branches and current branch - branches = [] - current_branch = coder.repo.repo.active_branch.name - for branch in coder.repo.repo.heads: - prefix = "* " if branch.name == current_branch else " " - branches.append(f"{prefix}{branch.name}") - return "\n".join(branches) - - elif action == "create": - if not name: - return "Branch name required for create operation." - coder.repo.repo.create_head(name) - return f"Created branch: {name}" - - elif action == "delete": - if not name: - return "Branch name required for delete operation." - coder.repo.repo.delete_head(name) - return f"Deleted branch: {name}" - - else: - return f"Unknown branch action: {action}" - + # Get all branches and current branch + branches = [] + current_branch = coder.repo.repo.active_branch.name + for branch in coder.repo.repo.heads: + prefix = "* " if branch.name == current_branch else " " + branches.append(f"{prefix}{branch.name}") + return "\n".join(branches) except ANY_GIT_ERROR as e: coder.io.tool_error(f"Error running git branch: {e}") return f"Error running git branch: {e}" @@ -207,63 +180,32 @@ def _execute_git_branch(coder, action, name=None): "type": "function", "function": { "name": "git_remote", - "description": "Manage set of tracked repositories.", + "description": "List remote repositories.", "parameters": { "type": "object", - "properties": { - "action": { - "type": "string", - "enum": ["list", "add", "remove"], - "description": "The remote operation to perform.", - }, - "name": { - "type": "string", - "description": "Remote name for add/remove operations.", - }, - "url": { - "type": "string", - "description": "Remote URL for add operation.", - }, - }, - "required": ["action"], + "properties": {}, + "required": [], }, }, } -def _execute_git_remote(coder, action, name=None, url=None): +def _execute_git_remote(coder): """ - Manage set of tracked repositories. + List remote repositories. """ if not coder.repo: return "Not in a git repository." try: - if action == "list": - remotes = coder.repo.repo.remotes - if not remotes: - return "No remotes configured." - - result = [] - for remote in remotes: - result.append(f"{remote.name}\t{remote.url}") - return "\n".join(result) + remotes = coder.repo.repo.remotes + if not remotes: + return "No remotes configured." - elif action == "add": - if not name or not url: - return "Remote name and URL required for add operation." - coder.repo.repo.create_remote(name, url) - return f"Added remote: {name} -> {url}" - - elif action == "remove": - if not name: - return "Remote name required for remove operation." - coder.repo.repo.delete_remote(name) - return f"Removed remote: {name}" - - else: - return f"Unknown remote action: {action}" - + result = [] + for remote in remotes: + result.append(f"{remote.name}\t{remote.url}") + return "\n".join(result) except ANY_GIT_ERROR as e: coder.io.tool_error(f"Error running git remote: {e}") return f"Error running git remote: {e}" From b1c1d82a8d41d2ec6f76f4350a9853912fdbd71b Mon Sep 17 00:00:00 2001 From: tomjuggler Date: Thu, 6 Nov 2025 20:46:57 +0200 Subject: [PATCH 3/3] reformatted with black --- aider/coders/navigator_coder.py | 8 ++++---- aider/tools/__init__.py | 8 ++++---- aider/tools/git.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/aider/coders/navigator_coder.py b/aider/coders/navigator_coder.py index 50fec9cb7bf..efb07362242 100644 --- a/aider/coders/navigator_coder.py +++ b/aider/coders/navigator_coder.py @@ -57,18 +57,18 @@ from aider.tools.delete_lines import _execute_delete_lines from aider.tools.extract_lines import _execute_extract_lines from aider.tools.git import ( + _execute_git_branch, _execute_git_diff, _execute_git_log, + _execute_git_remote, _execute_git_show, _execute_git_status, - _execute_git_branch, - _execute_git_remote, + git_branch_schema, git_diff_schema, git_log_schema, + git_remote_schema, git_show_schema, git_status_schema, - git_branch_schema, - git_remote_schema, ) from aider.tools.grep import _execute_grep from aider.tools.indent_lines import _execute_indent_lines diff --git a/aider/tools/__init__.py b/aider/tools/__init__.py index d3ea822f940..ed1fc712236 100644 --- a/aider/tools/__init__.py +++ b/aider/tools/__init__.py @@ -11,18 +11,18 @@ from .delete_lines import _execute_delete_lines, delete_lines_schema from .extract_lines import _execute_extract_lines, extract_lines_schema from .git import ( + _execute_git_branch, _execute_git_diff, _execute_git_log, + _execute_git_remote, _execute_git_show, _execute_git_status, - _execute_git_branch, - _execute_git_remote, + git_branch_schema, git_diff_schema, git_log_schema, + git_remote_schema, git_show_schema, git_status_schema, - git_branch_schema, - git_remote_schema, ) from .grep import _execute_grep, grep_schema from .indent_lines import _execute_indent_lines, indent_lines_schema diff --git a/aider/tools/git.py b/aider/tools/git.py index 407758c12ed..3ca75abb1ae 100644 --- a/aider/tools/git.py +++ b/aider/tools/git.py @@ -201,7 +201,7 @@ def _execute_git_remote(coder): remotes = coder.repo.repo.remotes if not remotes: return "No remotes configured." - + result = [] for remote in remotes: result.append(f"{remote.name}\t{remote.url}")