aider-ce 0.87.13.dev3__py3-none-any.whl → 0.88.0__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.
- aider/__init__.py +1 -1
- aider/_version.py +2 -2
- aider/args.py +6 -0
- aider/coders/architect_coder.py +3 -3
- aider/coders/base_coder.py +505 -184
- aider/coders/context_coder.py +1 -1
- aider/coders/editblock_func_coder.py +2 -2
- aider/coders/navigator_coder.py +451 -649
- aider/coders/navigator_legacy_prompts.py +49 -284
- aider/coders/navigator_prompts.py +46 -473
- aider/coders/search_replace.py +0 -0
- aider/coders/wholefile_func_coder.py +2 -2
- aider/commands.py +56 -44
- aider/history.py +14 -12
- aider/io.py +354 -117
- aider/llm.py +12 -4
- aider/main.py +22 -19
- aider/mcp/__init__.py +65 -2
- aider/mcp/server.py +37 -11
- aider/models.py +45 -20
- aider/onboarding.py +4 -4
- aider/repo.py +7 -7
- aider/resources/model-metadata.json +8 -8
- aider/scrape.py +2 -2
- aider/sendchat.py +185 -15
- aider/tools/__init__.py +44 -23
- aider/tools/command.py +18 -0
- aider/tools/command_interactive.py +18 -0
- aider/tools/delete_block.py +23 -0
- aider/tools/delete_line.py +19 -1
- aider/tools/delete_lines.py +20 -1
- aider/tools/extract_lines.py +25 -2
- aider/tools/git.py +142 -0
- aider/tools/grep.py +47 -2
- aider/tools/indent_lines.py +25 -0
- aider/tools/insert_block.py +26 -0
- aider/tools/list_changes.py +15 -0
- aider/tools/ls.py +24 -1
- aider/tools/make_editable.py +18 -0
- aider/tools/make_readonly.py +19 -0
- aider/tools/remove.py +22 -0
- aider/tools/replace_all.py +21 -0
- aider/tools/replace_line.py +20 -1
- aider/tools/replace_lines.py +21 -1
- aider/tools/replace_text.py +22 -0
- aider/tools/show_numbered_context.py +18 -0
- aider/tools/undo_change.py +15 -0
- aider/tools/update_todo_list.py +131 -0
- aider/tools/view.py +23 -0
- aider/tools/view_files_at_glob.py +32 -27
- aider/tools/view_files_matching.py +51 -37
- aider/tools/view_files_with_symbol.py +41 -54
- aider/tools/view_todo_list.py +57 -0
- aider/waiting.py +20 -203
- {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.0.dist-info}/METADATA +21 -5
- {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.0.dist-info}/RECORD +59 -56
- {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.0.dist-info}/WHEEL +0 -0
- {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.0.dist-info}/entry_points.txt +0 -0
- {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.0.dist-info}/licenses/LICENSE.txt +0 -0
- {aider_ce-0.87.13.dev3.dist-info → aider_ce-0.88.0.dist-info}/top_level.txt +0 -0
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 =
|
|
165
|
+
command_string = oslex.join(cmd_args)
|
|
121
166
|
|
|
122
167
|
coder.io.tool_output(f"⚙️ Executing {tool_name}: {command_string}")
|
|
123
168
|
|
aider/tools/indent_lines.py
CHANGED
|
@@ -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 = (
|
aider/tools/insert_block.py
CHANGED
|
@@ -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}"
|
aider/tools/list_changes.py
CHANGED
|
@@ -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
|
-
|
|
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
|
|
aider/tools/make_editable.py
CHANGED
|
@@ -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):
|
aider/tools/make_readonly.py
CHANGED
|
@@ -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
|
"""
|
aider/tools/replace_all.py
CHANGED
|
@@ -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(
|
aider/tools/replace_line.py
CHANGED
|
@@ -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.
|
|
134
|
+
coder.files_edited_by_tools.add(rel_path)
|
|
116
135
|
|
|
117
136
|
# Improve feedback
|
|
118
137
|
coder.io.tool_output(
|
aider/tools/replace_lines.py
CHANGED
|
@@ -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.
|
|
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
|
|
aider/tools/replace_text.py
CHANGED
|
@@ -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
|
aider/tools/undo_change.py
CHANGED
|
@@ -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
|
"""
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from .tool_utils import (
|
|
2
|
+
ToolError,
|
|
3
|
+
format_tool_result,
|
|
4
|
+
generate_unified_diff_snippet,
|
|
5
|
+
handle_tool_error,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
update_todo_list_schema = {
|
|
9
|
+
"type": "function",
|
|
10
|
+
"function": {
|
|
11
|
+
"name": "UpdateTodoList",
|
|
12
|
+
"description": "Update the todo list with new items or modify existing ones.",
|
|
13
|
+
"parameters": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {
|
|
16
|
+
"content": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "The new content for the todo list.",
|
|
19
|
+
},
|
|
20
|
+
"append": {
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
"description": (
|
|
23
|
+
"Whether to append to existing content instead of replacing it. Defaults to"
|
|
24
|
+
" False."
|
|
25
|
+
),
|
|
26
|
+
},
|
|
27
|
+
"change_id": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Optional change ID for tracking.",
|
|
30
|
+
},
|
|
31
|
+
"dry_run": {
|
|
32
|
+
"type": "boolean",
|
|
33
|
+
"description": (
|
|
34
|
+
"Whether to perform a dry run without actually updating the file. Defaults"
|
|
35
|
+
" to False."
|
|
36
|
+
),
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
"required": ["content"],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _execute_update_todo_list(coder, content, append=False, change_id=None, dry_run=False):
|
|
46
|
+
"""
|
|
47
|
+
Update the todo list file (.aider.todo.txt) with new content.
|
|
48
|
+
Can either replace the entire content or append to it.
|
|
49
|
+
"""
|
|
50
|
+
tool_name = "UpdateTodoList"
|
|
51
|
+
try:
|
|
52
|
+
# Define the todo file path
|
|
53
|
+
todo_file_path = ".aider.todo.txt"
|
|
54
|
+
abs_path = coder.abs_root_path(todo_file_path)
|
|
55
|
+
|
|
56
|
+
# Get existing content if appending
|
|
57
|
+
existing_content = ""
|
|
58
|
+
import os
|
|
59
|
+
|
|
60
|
+
if os.path.isfile(abs_path):
|
|
61
|
+
existing_content = coder.io.read_text(abs_path) or ""
|
|
62
|
+
|
|
63
|
+
# Prepare new content
|
|
64
|
+
if append:
|
|
65
|
+
if existing_content and not existing_content.endswith("\n"):
|
|
66
|
+
existing_content += "\n"
|
|
67
|
+
new_content = existing_content + content
|
|
68
|
+
else:
|
|
69
|
+
new_content = content
|
|
70
|
+
|
|
71
|
+
# Check if content exceeds 4096 characters and warn
|
|
72
|
+
if len(new_content) > 4096:
|
|
73
|
+
coder.io.tool_warning(
|
|
74
|
+
"⚠️ Todo list content exceeds 4096 characters. Consider summarizing the plan before"
|
|
75
|
+
" proceeding."
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Check if content actually changed
|
|
79
|
+
if existing_content == new_content:
|
|
80
|
+
coder.io.tool_warning("No changes made: new content is identical to existing")
|
|
81
|
+
return "Warning: No changes made (content identical to existing)"
|
|
82
|
+
|
|
83
|
+
# Generate diff for feedback
|
|
84
|
+
diff_snippet = generate_unified_diff_snippet(existing_content, new_content, todo_file_path)
|
|
85
|
+
|
|
86
|
+
# Handle dry run
|
|
87
|
+
if dry_run:
|
|
88
|
+
action = "append to" if append else "replace"
|
|
89
|
+
dry_run_message = f"Dry run: Would {action} todo list in {todo_file_path}."
|
|
90
|
+
return format_tool_result(
|
|
91
|
+
coder,
|
|
92
|
+
tool_name,
|
|
93
|
+
"",
|
|
94
|
+
dry_run=True,
|
|
95
|
+
dry_run_message=dry_run_message,
|
|
96
|
+
diff_snippet=diff_snippet,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Apply change
|
|
100
|
+
metadata = {
|
|
101
|
+
"append": append,
|
|
102
|
+
"existing_length": len(existing_content),
|
|
103
|
+
"new_length": len(new_content),
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# Write the file directly since it's a special file
|
|
107
|
+
coder.io.write_text(abs_path, new_content)
|
|
108
|
+
|
|
109
|
+
# Track the change
|
|
110
|
+
final_change_id = coder.change_tracker.track_change(
|
|
111
|
+
file_path=todo_file_path,
|
|
112
|
+
change_type="updatetodolist",
|
|
113
|
+
original_content=existing_content,
|
|
114
|
+
new_content=new_content,
|
|
115
|
+
metadata=metadata,
|
|
116
|
+
change_id=change_id,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
coder.aider_edited_files.add(todo_file_path)
|
|
120
|
+
|
|
121
|
+
# Format and return result
|
|
122
|
+
action = "appended to" if append else "updated"
|
|
123
|
+
success_message = f"Successfully {action} todo list in {todo_file_path}"
|
|
124
|
+
return format_tool_result(
|
|
125
|
+
coder, tool_name, success_message, change_id=final_change_id, diff_snippet=diff_snippet
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
except ToolError as e:
|
|
129
|
+
return handle_tool_error(coder, tool_name, e, add_traceback=False)
|
|
130
|
+
except Exception as e:
|
|
131
|
+
return handle_tool_error(coder, tool_name, e)
|
aider/tools/view.py
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
view_schema = {
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "View",
|
|
5
|
+
"description": (
|
|
6
|
+
"View a specific file and add it to context."
|
|
7
|
+
"Only use this when the file is not already in the context "
|
|
8
|
+
"and when editing the file is necessary to accomplish the goal."
|
|
9
|
+
),
|
|
10
|
+
"parameters": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"file_path": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "The path to the file to view.",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
"required": ["file_path"],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
1
24
|
def execute_view(coder, file_path):
|
|
2
25
|
"""
|
|
3
26
|
Explicitly add a file to context as read-only.
|