aider-ce 0.87.13.dev3__py3-none-any.whl → 0.88.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of aider-ce might be problematic. Click here for more details.

Files changed (61) hide show
  1. aider/__init__.py +1 -1
  2. aider/_version.py +2 -2
  3. aider/args.py +6 -0
  4. aider/coders/architect_coder.py +3 -3
  5. aider/coders/base_coder.py +511 -190
  6. aider/coders/context_coder.py +1 -1
  7. aider/coders/editblock_func_coder.py +2 -2
  8. aider/coders/navigator_coder.py +451 -649
  9. aider/coders/navigator_legacy_prompts.py +49 -284
  10. aider/coders/navigator_prompts.py +46 -473
  11. aider/coders/search_replace.py +0 -0
  12. aider/coders/wholefile_func_coder.py +2 -2
  13. aider/commands.py +56 -44
  14. aider/exceptions.py +1 -0
  15. aider/history.py +14 -12
  16. aider/io.py +354 -117
  17. aider/llm.py +12 -4
  18. aider/main.py +32 -29
  19. aider/mcp/__init__.py +65 -2
  20. aider/mcp/server.py +37 -11
  21. aider/models.py +45 -20
  22. aider/onboarding.py +5 -5
  23. aider/repo.py +7 -7
  24. aider/resources/model-metadata.json +8 -8
  25. aider/scrape.py +2 -2
  26. aider/sendchat.py +185 -15
  27. aider/tools/__init__.py +44 -23
  28. aider/tools/command.py +18 -0
  29. aider/tools/command_interactive.py +18 -0
  30. aider/tools/delete_block.py +23 -0
  31. aider/tools/delete_line.py +19 -1
  32. aider/tools/delete_lines.py +20 -1
  33. aider/tools/extract_lines.py +25 -2
  34. aider/tools/git.py +142 -0
  35. aider/tools/grep.py +47 -2
  36. aider/tools/indent_lines.py +25 -0
  37. aider/tools/insert_block.py +26 -0
  38. aider/tools/list_changes.py +15 -0
  39. aider/tools/ls.py +24 -1
  40. aider/tools/make_editable.py +18 -0
  41. aider/tools/make_readonly.py +19 -0
  42. aider/tools/remove.py +22 -0
  43. aider/tools/replace_all.py +21 -0
  44. aider/tools/replace_line.py +20 -1
  45. aider/tools/replace_lines.py +21 -1
  46. aider/tools/replace_text.py +22 -0
  47. aider/tools/show_numbered_context.py +18 -0
  48. aider/tools/undo_change.py +15 -0
  49. aider/tools/update_todo_list.py +131 -0
  50. aider/tools/view.py +23 -0
  51. aider/tools/view_files_at_glob.py +32 -27
  52. aider/tools/view_files_matching.py +51 -37
  53. aider/tools/view_files_with_symbol.py +41 -54
  54. aider/tools/view_todo_list.py +57 -0
  55. aider/waiting.py +20 -203
  56. {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.1.dist-info}/METADATA +21 -5
  57. {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.1.dist-info}/RECORD +60 -57
  58. {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.1.dist-info}/WHEEL +0 -0
  59. {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.1.dist-info}/entry_points.txt +0 -0
  60. {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.1.dist-info}/licenses/LICENSE.txt +0 -0
  61. {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.1.dist-info}/top_level.txt +0 -0
aider/tools/git.py ADDED
@@ -0,0 +1,142 @@
1
+ from aider.repo import ANY_GIT_ERROR
2
+
3
+ git_diff_schema = {
4
+ "type": "function",
5
+ "function": {
6
+ "name": "git_diff",
7
+ "description": (
8
+ "Show the diff between the current working directory and a git branch or commit."
9
+ ),
10
+ "parameters": {
11
+ "type": "object",
12
+ "properties": {
13
+ "branch": {
14
+ "type": "string",
15
+ "description": "The branch or commit hash to diff against. Defaults to HEAD.",
16
+ },
17
+ },
18
+ "required": [],
19
+ },
20
+ },
21
+ }
22
+
23
+
24
+ def _execute_git_diff(coder, branch=None):
25
+ """
26
+ Show the diff between the current working directory and a git branch or commit.
27
+ """
28
+ if not coder.repo:
29
+ return "Not in a git repository."
30
+
31
+ try:
32
+ if branch:
33
+ diff = coder.repo.diff_commits(False, branch, "HEAD")
34
+ else:
35
+ diff = coder.repo.diff_commits(False, "HEAD", None)
36
+
37
+ if not diff:
38
+ return "No differences found."
39
+ return diff
40
+ except ANY_GIT_ERROR as e:
41
+ coder.io.tool_error(f"Error running git diff: {e}")
42
+ return f"Error running git diff: {e}"
43
+
44
+
45
+ git_log_schema = {
46
+ "type": "function",
47
+ "function": {
48
+ "name": "git_log",
49
+ "description": "Show the git log.",
50
+ "parameters": {
51
+ "type": "object",
52
+ "properties": {
53
+ "limit": {
54
+ "type": "integer",
55
+ "description": "The maximum number of commits to show. Defaults to 10.",
56
+ },
57
+ },
58
+ "required": [],
59
+ },
60
+ },
61
+ }
62
+
63
+
64
+ def _execute_git_log(coder, limit=10):
65
+ """
66
+ Show the git log.
67
+ """
68
+ if not coder.repo:
69
+ return "Not in a git repository."
70
+
71
+ try:
72
+ commits = list(coder.repo.repo.iter_commits(max_count=limit))
73
+ log_output = []
74
+ for commit in commits:
75
+ short_hash = commit.hexsha[:8]
76
+ message = commit.message.strip().split("\n")[0]
77
+ log_output.append(f"{short_hash} {message}")
78
+ return "\n".join(log_output)
79
+ except ANY_GIT_ERROR as e:
80
+ coder.io.tool_error(f"Error running git log: {e}")
81
+ return f"Error running git log: {e}"
82
+
83
+
84
+ git_show_schema = {
85
+ "type": "function",
86
+ "function": {
87
+ "name": "git_show",
88
+ "description": "Show various types of objects (blobs, trees, tags, and commits).",
89
+ "parameters": {
90
+ "type": "object",
91
+ "properties": {
92
+ "object": {
93
+ "type": "string",
94
+ "description": "The object to show. Defaults to HEAD.",
95
+ },
96
+ },
97
+ "required": [],
98
+ },
99
+ },
100
+ }
101
+
102
+
103
+ def _execute_git_show(coder, object="HEAD"):
104
+ """
105
+ Show various types of objects (blobs, trees, tags, and commits).
106
+ """
107
+ if not coder.repo:
108
+ return "Not in a git repository."
109
+
110
+ try:
111
+ return coder.repo.repo.git.show(object)
112
+ except ANY_GIT_ERROR as e:
113
+ coder.io.tool_error(f"Error running git show: {e}")
114
+ return f"Error running git show: {e}"
115
+
116
+
117
+ git_status_schema = {
118
+ "type": "function",
119
+ "function": {
120
+ "name": "git_status",
121
+ "description": "Show the working tree status.",
122
+ "parameters": {
123
+ "type": "object",
124
+ "properties": {},
125
+ "required": [],
126
+ },
127
+ },
128
+ }
129
+
130
+
131
+ def _execute_git_status(coder):
132
+ """
133
+ Show the working tree status.
134
+ """
135
+ if not coder.repo:
136
+ return "Not in a git repository."
137
+
138
+ try:
139
+ return coder.repo.repo.git.status()
140
+ except ANY_GIT_ERROR as e:
141
+ coder.io.tool_error(f"Error running git status: {e}")
142
+ return f"Error running git status: {e}"
aider/tools/grep.py CHANGED
@@ -1,9 +1,54 @@
1
- import shlex
2
1
  import shutil
3
2
  from pathlib import Path
4
3
 
4
+ import oslex
5
+
5
6
  from aider.run_cmd import run_cmd_subprocess
6
7
 
8
+ grep_schema = {
9
+ "type": "function",
10
+ "function": {
11
+ "name": "Grep",
12
+ "description": "Search for a pattern in files.",
13
+ "parameters": {
14
+ "type": "object",
15
+ "properties": {
16
+ "pattern": {
17
+ "type": "string",
18
+ "description": "The pattern to search for.",
19
+ },
20
+ "file_pattern": {
21
+ "type": "string",
22
+ "description": "Glob pattern for files to search. Defaults to '*'.",
23
+ },
24
+ "directory": {
25
+ "type": "string",
26
+ "description": "Directory to search in. Defaults to '.'.",
27
+ },
28
+ "use_regex": {
29
+ "type": "boolean",
30
+ "description": "Whether to use regex. Defaults to False.",
31
+ },
32
+ "case_insensitive": {
33
+ "type": "boolean",
34
+ "description": (
35
+ "Whether to perform a case-insensitive search. Defaults to False."
36
+ ),
37
+ },
38
+ "context_before": {
39
+ "type": "integer",
40
+ "description": "Number of lines to show before a match. Defaults to 5.",
41
+ },
42
+ "context_after": {
43
+ "type": "integer",
44
+ "description": "Number of lines to show after a match. Defaults to 5.",
45
+ },
46
+ },
47
+ "required": ["pattern"],
48
+ },
49
+ },
50
+ }
51
+
7
52
 
8
53
  def _find_search_tool():
9
54
  """Find the best available command-line search tool (rg, ag, grep)."""
@@ -117,7 +162,7 @@ def _execute_grep(
117
162
  cmd_args.extend([pattern, str(search_dir_path)])
118
163
 
119
164
  # Convert list to command string for run_cmd_subprocess
120
- command_string = shlex.join(cmd_args)
165
+ command_string = oslex.join(cmd_args)
121
166
 
122
167
  coder.io.tool_output(f"⚙️ Executing {tool_name}: {command_string}")
123
168
 
@@ -10,6 +10,29 @@ from .tool_utils import (
10
10
  validate_file_for_edit,
11
11
  )
12
12
 
13
+ indent_lines_schema = {
14
+ "type": "function",
15
+ "function": {
16
+ "name": "IndentLines",
17
+ "description": "Indent a block of lines in a file.",
18
+ "parameters": {
19
+ "type": "object",
20
+ "properties": {
21
+ "file_path": {"type": "string"},
22
+ "start_pattern": {"type": "string"},
23
+ "end_pattern": {"type": "string"},
24
+ "line_count": {"type": "integer"},
25
+ "indent_levels": {"type": "integer", "default": 1},
26
+ "near_context": {"type": "string"},
27
+ "occurrence": {"type": "integer", "default": 1},
28
+ "change_id": {"type": "string"},
29
+ "dry_run": {"type": "boolean", "default": False},
30
+ },
31
+ "required": ["file_path", "start_pattern"],
32
+ },
33
+ },
34
+ }
35
+
13
36
 
14
37
  def _execute_indent_lines(
15
38
  coder,
@@ -138,6 +161,8 @@ def _execute_indent_lines(
138
161
  change_id,
139
162
  )
140
163
 
164
+ coder.files_edited_by_tools.add(rel_path)
165
+
141
166
  # 8. Format and return result
142
167
  action_past = "Indented" if indent_levels > 0 else "Unindented"
143
168
  success_message = (
@@ -12,6 +12,30 @@ from .tool_utils import (
12
12
  validate_file_for_edit,
13
13
  )
14
14
 
15
+ insert_block_schema = {
16
+ "type": "function",
17
+ "function": {
18
+ "name": "InsertBlock",
19
+ "description": "Insert a block of content into a file.",
20
+ "parameters": {
21
+ "type": "object",
22
+ "properties": {
23
+ "file_path": {"type": "string"},
24
+ "content": {"type": "string"},
25
+ "after_pattern": {"type": "string"},
26
+ "before_pattern": {"type": "string"},
27
+ "occurrence": {"type": "integer", "default": 1},
28
+ "change_id": {"type": "string"},
29
+ "dry_run": {"type": "boolean", "default": False},
30
+ "position": {"type": "string", "enum": ["top", "bottom"]},
31
+ "auto_indent": {"type": "boolean", "default": True},
32
+ "use_regex": {"type": "boolean", "default": False},
33
+ },
34
+ "required": ["file_path", "content"],
35
+ },
36
+ },
37
+ }
38
+
15
39
 
16
40
  def _execute_insert_block(
17
41
  coder,
@@ -187,6 +211,8 @@ def _execute_insert_block(
187
211
  change_id,
188
212
  )
189
213
 
214
+ coder.files_edited_by_tools.add(rel_path)
215
+
190
216
  # 9. Format and return result
191
217
  if position:
192
218
  success_message = f"Inserted block {pattern_type} {file_path}"
@@ -1,6 +1,21 @@
1
1
  import traceback
2
2
  from datetime import datetime
3
3
 
4
+ list_changes_schema = {
5
+ "type": "function",
6
+ "function": {
7
+ "name": "ListChanges",
8
+ "description": "List recent changes made.",
9
+ "parameters": {
10
+ "type": "object",
11
+ "properties": {
12
+ "file_path": {"type": "string"},
13
+ "limit": {"type": "integer", "default": 10},
14
+ },
15
+ },
16
+ },
17
+ }
18
+
4
19
 
5
20
  def _execute_list_changes(coder, file_path=None, limit=10):
6
21
  """
aider/tools/ls.py CHANGED
@@ -1,7 +1,30 @@
1
1
  import os
2
2
 
3
+ ls_schema = {
4
+ "type": "function",
5
+ "function": {
6
+ "name": "Ls",
7
+ "description": "List files in a directory.",
8
+ "parameters": {
9
+ "type": "object",
10
+ "properties": {
11
+ "directory": {
12
+ "type": "string",
13
+ "description": "The directory to list.",
14
+ },
15
+ },
16
+ "required": ["directory"],
17
+ },
18
+ },
19
+ }
3
20
 
4
- def execute_ls(coder, dir_path):
21
+
22
+ def execute_ls(coder, dir_path=None, directory=None):
23
+ # Handle both positional and keyword arguments for backward compatibility
24
+ if dir_path is None and directory is not None:
25
+ dir_path = directory
26
+ elif dir_path is None:
27
+ return "Error: Missing directory parameter"
5
28
  """
6
29
  List files in directory and optionally add some to context.
7
30
 
@@ -1,5 +1,23 @@
1
1
  import os
2
2
 
3
+ make_editable_schema = {
4
+ "type": "function",
5
+ "function": {
6
+ "name": "MakeEditable",
7
+ "description": "Make a read-only file editable.",
8
+ "parameters": {
9
+ "type": "object",
10
+ "properties": {
11
+ "file_path": {
12
+ "type": "string",
13
+ "description": "The path to the file to make editable.",
14
+ },
15
+ },
16
+ "required": ["file_path"],
17
+ },
18
+ },
19
+ }
20
+
3
21
 
4
22
  # Keep the underscore prefix as this function is primarily for internal coder use
5
23
  def _execute_make_editable(coder, file_path):
@@ -1,3 +1,22 @@
1
+ make_readonly_schema = {
2
+ "type": "function",
3
+ "function": {
4
+ "name": "MakeReadonly",
5
+ "description": "Make an editable file read-only.",
6
+ "parameters": {
7
+ "type": "object",
8
+ "properties": {
9
+ "file_path": {
10
+ "type": "string",
11
+ "description": "The path to the file to make read-only.",
12
+ },
13
+ },
14
+ "required": ["file_path"],
15
+ },
16
+ },
17
+ }
18
+
19
+
1
20
  def _execute_make_readonly(coder, file_path):
2
21
  """
3
22
  Convert an editable file to a read-only file.
aider/tools/remove.py CHANGED
@@ -1,5 +1,27 @@
1
1
  import time
2
2
 
3
+ remove_schema = {
4
+ "type": "function",
5
+ "function": {
6
+ "name": "Remove",
7
+ "description": (
8
+ "Remove a file from the chat context. Should be used proactively to keep con"
9
+ "Should be used after editing a file when all edits are done "
10
+ "and the file is no longer necessary in context."
11
+ ),
12
+ "parameters": {
13
+ "type": "object",
14
+ "properties": {
15
+ "file_path": {
16
+ "type": "string",
17
+ "description": "The path to the file to remove.",
18
+ },
19
+ },
20
+ "required": ["file_path"],
21
+ },
22
+ },
23
+ }
24
+
3
25
 
4
26
  def _execute_remove(coder, file_path):
5
27
  """
@@ -7,6 +7,25 @@ from .tool_utils import (
7
7
  validate_file_for_edit,
8
8
  )
9
9
 
10
+ replace_all_schema = {
11
+ "type": "function",
12
+ "function": {
13
+ "name": "ReplaceAll",
14
+ "description": "Replace all occurrences of text in a file.",
15
+ "parameters": {
16
+ "type": "object",
17
+ "properties": {
18
+ "file_path": {"type": "string"},
19
+ "find_text": {"type": "string"},
20
+ "replace_text": {"type": "string"},
21
+ "change_id": {"type": "string"},
22
+ "dry_run": {"type": "boolean", "default": False},
23
+ },
24
+ "required": ["file_path", "find_text", "replace_text"],
25
+ },
26
+ },
27
+ }
28
+
10
29
 
11
30
  def _execute_replace_all(coder, file_path, find_text, replace_text, change_id=None, dry_run=False):
12
31
  """
@@ -63,6 +82,8 @@ def _execute_replace_all(coder, file_path, find_text, replace_text, change_id=No
63
82
  change_id,
64
83
  )
65
84
 
85
+ coder.files_edited_by_tools.add(rel_path)
86
+
66
87
  # 7. Format and return result
67
88
  success_message = f"Replaced {count} occurrences in {file_path}"
68
89
  return format_tool_result(
@@ -1,6 +1,25 @@
1
1
  import os
2
2
  import traceback
3
3
 
4
+ replace_line_schema = {
5
+ "type": "function",
6
+ "function": {
7
+ "name": "ReplaceLine",
8
+ "description": "Replace a single line in a file.",
9
+ "parameters": {
10
+ "type": "object",
11
+ "properties": {
12
+ "file_path": {"type": "string"},
13
+ "line_number": {"type": "integer"},
14
+ "new_content": {"type": "string"},
15
+ "change_id": {"type": "string"},
16
+ "dry_run": {"type": "boolean", "default": False},
17
+ },
18
+ "required": ["file_path", "line_number", "new_content"],
19
+ },
20
+ },
21
+ }
22
+
4
23
 
5
24
  def _execute_replace_line(
6
25
  coder, file_path, line_number, new_content, change_id=None, dry_run=False
@@ -112,7 +131,7 @@ def _execute_replace_line(
112
131
  coder.io.tool_error(f"Error tracking change for ReplaceLine: {track_e}")
113
132
  change_id = "TRACKING_FAILED"
114
133
 
115
- coder.aider_edited_files.add(rel_path)
134
+ coder.files_edited_by_tools.add(rel_path)
116
135
 
117
136
  # Improve feedback
118
137
  coder.io.tool_output(
@@ -8,6 +8,26 @@ from .tool_utils import (
8
8
  handle_tool_error,
9
9
  )
10
10
 
11
+ replace_lines_schema = {
12
+ "type": "function",
13
+ "function": {
14
+ "name": "ReplaceLines",
15
+ "description": "Replace a range of lines in a file.",
16
+ "parameters": {
17
+ "type": "object",
18
+ "properties": {
19
+ "file_path": {"type": "string"},
20
+ "start_line": {"type": "integer"},
21
+ "end_line": {"type": "integer"},
22
+ "new_content": {"type": "string"},
23
+ "change_id": {"type": "string"},
24
+ "dry_run": {"type": "boolean", "default": False},
25
+ },
26
+ "required": ["file_path", "start_line", "end_line", "new_content"],
27
+ },
28
+ },
29
+ }
30
+
11
31
 
12
32
  def _execute_replace_lines(
13
33
  coder, file_path, start_line, end_line, new_content, change_id=None, dry_run=False
@@ -139,7 +159,7 @@ def _execute_replace_lines(
139
159
  change_id,
140
160
  )
141
161
 
142
- coder.aider_edited_files.add(rel_path)
162
+ coder.files_edited_by_tools.add(rel_path)
143
163
  replaced_count = end_line - start_line + 1
144
164
  new_count = len(new_lines)
145
165
 
@@ -7,6 +7,27 @@ from .tool_utils import (
7
7
  validate_file_for_edit,
8
8
  )
9
9
 
10
+ replace_text_schema = {
11
+ "type": "function",
12
+ "function": {
13
+ "name": "ReplaceText",
14
+ "description": "Replace text in a file.",
15
+ "parameters": {
16
+ "type": "object",
17
+ "properties": {
18
+ "file_path": {"type": "string"},
19
+ "find_text": {"type": "string"},
20
+ "replace_text": {"type": "string"},
21
+ "near_context": {"type": "string"},
22
+ "occurrence": {"type": "integer", "default": 1},
23
+ "change_id": {"type": "string"},
24
+ "dry_run": {"type": "boolean", "default": False},
25
+ },
26
+ "required": ["file_path", "find_text", "replace_text"],
27
+ },
28
+ },
29
+ }
30
+
10
31
 
11
32
  def _execute_replace_text(
12
33
  coder,
@@ -111,6 +132,7 @@ def _execute_replace_text(
111
132
  change_id,
112
133
  )
113
134
 
135
+ coder.files_edited_by_tools.add(rel_path)
114
136
  # 8. Format and return result
115
137
  success_message = f"Replaced {occurrence_str} in {file_path}"
116
138
  return format_tool_result(
@@ -2,6 +2,24 @@ import os
2
2
 
3
3
  from .tool_utils import ToolError, handle_tool_error, resolve_paths
4
4
 
5
+ show_numbered_context_schema = {
6
+ "type": "function",
7
+ "function": {
8
+ "name": "ShowNumberedContext",
9
+ "description": "Show numbered lines of context around a pattern or line number.",
10
+ "parameters": {
11
+ "type": "object",
12
+ "properties": {
13
+ "file_path": {"type": "string"},
14
+ "pattern": {"type": "string"},
15
+ "line_number": {"type": "integer"},
16
+ "context_lines": {"type": "integer", "default": 3},
17
+ },
18
+ "required": ["file_path"],
19
+ },
20
+ },
21
+ }
22
+
5
23
 
6
24
  def execute_show_numbered_context(
7
25
  coder, file_path, pattern=None, line_number=None, context_lines=3
@@ -1,5 +1,20 @@
1
1
  import traceback
2
2
 
3
+ undo_change_schema = {
4
+ "type": "function",
5
+ "function": {
6
+ "name": "UndoChange",
7
+ "description": "Undo a previously applied change.",
8
+ "parameters": {
9
+ "type": "object",
10
+ "properties": {
11
+ "change_id": {"type": "string"},
12
+ "file_path": {"type": "string"},
13
+ },
14
+ },
15
+ },
16
+ }
17
+
3
18
 
4
19
  def _execute_undo_change(coder, change_id=None, file_path=None):
5
20
  """