diff --git a/aider/coders/navigator_coder.py b/aider/coders/navigator_coder.py index f871962b104..df7e4159ccd 100644 --- a/aider/coders/navigator_coder.py +++ b/aider/coders/navigator_coder.py @@ -56,6 +56,16 @@ from aider.tools.delete_line import _execute_delete_line 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_diff, + _execute_git_log, + _execute_git_show, + _execute_git_status, + git_diff_schema, + git_log_schema, + git_show_schema, + git_status_schema, +) from aider.tools.grep import _execute_grep from aider.tools.indent_lines import _execute_indent_lines from aider.tools.insert_block import _execute_insert_block @@ -203,6 +213,10 @@ def get_local_tool_schemas(self): extract_lines_schema, show_numbered_context_schema, update_todo_list_schema, + git_diff_schema, + git_log_schema, + git_show_schema, + git_status_schema, ] async def initialize_mcp_tools(self): @@ -279,6 +293,10 @@ async def _execute_local_tool_calls(self, tool_calls_list): "extractlines": _execute_extract_lines, "shownumberedcontext": execute_show_numbered_context, "updatetodolist": _execute_update_todo_list, + "git_diff": _execute_git_diff, + "git_log": _execute_git_log, + "git_show": _execute_git_show, + "git_status": _execute_git_status, } func = tool_functions.get(norm_tool_name) diff --git a/aider/tools/__init__.py b/aider/tools/__init__.py index 6d280977276..3de1c4945fc 100644 --- a/aider/tools/__init__.py +++ b/aider/tools/__init__.py @@ -10,6 +10,16 @@ from .delete_line import _execute_delete_line, delete_line_schema 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_diff, + _execute_git_log, + _execute_git_show, + _execute_git_status, + git_diff_schema, + git_log_schema, + git_show_schema, + git_status_schema, +) from .grep import _execute_grep, grep_schema from .indent_lines import _execute_indent_lines, indent_lines_schema from .insert_block import _execute_insert_block, insert_block_schema diff --git a/aider/tools/git.py b/aider/tools/git.py new file mode 100644 index 00000000000..f9fefb7f507 --- /dev/null +++ b/aider/tools/git.py @@ -0,0 +1,142 @@ +from aider.repo import ANY_GIT_ERROR + +git_diff_schema = { + "type": "function", + "function": { + "name": "git_diff", + "description": ( + "Show the diff between the current working directory and a git branch or commit." + ), + "parameters": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "The branch or commit hash to diff against. Defaults to HEAD.", + }, + }, + "required": [], + }, + }, +} + + +def _execute_git_diff(coder, branch=None): + """ + Show the diff between the current working directory and a git branch or commit. + """ + if not coder.repo: + return "Not in a git repository." + + try: + if branch: + diff = coder.repo.diff_commits(False, branch, "HEAD") + else: + diff = coder.repo.diff_commits(False, "HEAD", None) + + if not diff: + return "No differences found." + return diff + except ANY_GIT_ERROR as e: + coder.io.tool_error(f"Error running git diff: {e}") + return f"Error running git diff: {e}" + + +git_log_schema = { + "type": "function", + "function": { + "name": "git_log", + "description": "Show the git log.", + "parameters": { + "type": "object", + "properties": { + "limit": { + "type": "integer", + "description": "The maximum number of commits to show. Defaults to 10.", + }, + }, + "required": [], + }, + }, +} + + +def _execute_git_log(coder, limit=10): + """ + Show the git log. + """ + if not coder.repo: + return "Not in a git repository." + + try: + commits = list(coder.repo.repo.iter_commits(max_count=limit)) + log_output = [] + for commit in commits: + short_hash = commit.hexsha[:8] + message = commit.message.strip().split("\n")[0] + log_output.append(f"{short_hash} {message}") + return "\n".join(log_output) + except ANY_GIT_ERROR as e: + coder.io.tool_error(f"Error running git log: {e}") + return f"Error running git log: {e}" + + +git_show_schema = { + "type": "function", + "function": { + "name": "git_show", + "description": "Show various types of objects (blobs, trees, tags, and commits).", + "parameters": { + "type": "object", + "properties": { + "object": { + "type": "string", + "description": "The object to show. Defaults to HEAD.", + }, + }, + "required": [], + }, + }, +} + + +def _execute_git_show(coder, object="HEAD"): + """ + Show various types of objects (blobs, trees, tags, and commits). + """ + if not coder.repo: + return "Not in a git repository." + + try: + return coder.repo.repo.git.show(object) + except ANY_GIT_ERROR as e: + coder.io.tool_error(f"Error running git show: {e}") + return f"Error running git show: {e}" + + +git_status_schema = { + "type": "function", + "function": { + "name": "git_status", + "description": "Show the working tree status.", + "parameters": { + "type": "object", + "properties": {}, + "required": [], + }, + }, +} + + +def _execute_git_status(coder): + """ + Show the working tree status. + """ + if not coder.repo: + return "Not in a git repository." + + try: + return coder.repo.repo.git.status() + except ANY_GIT_ERROR as e: + coder.io.tool_error(f"Error running git status: {e}") + return f"Error running git status: {e}" diff --git a/pyproject.toml b/pyproject.toml index 0858c9d8331..c39e82e813d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ playwright = { file = "requirements/requirements-playwright.in" } include-package-data = true [tool.setuptools.packages.find] -include = ["aider"] +include = ["aider*"] [build-system] requires = ["setuptools>=68", "setuptools_scm[toml]>=8"]