aider-ce 0.88.20__py3-none-any.whl → 0.88.38__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.
- aider/__init__.py +1 -1
- aider/_version.py +2 -2
- aider/args.py +63 -43
- aider/coders/agent_coder.py +331 -79
- aider/coders/agent_prompts.py +3 -15
- aider/coders/architect_coder.py +21 -5
- aider/coders/base_coder.py +661 -413
- aider/coders/base_prompts.py +6 -3
- aider/coders/chat_chunks.py +39 -17
- aider/commands.py +79 -15
- aider/diffs.py +10 -9
- aider/exceptions.py +1 -1
- aider/helpers/coroutines.py +8 -0
- aider/helpers/requests.py +45 -0
- aider/history.py +5 -0
- aider/io.py +179 -25
- aider/main.py +86 -35
- aider/models.py +16 -8
- aider/queries/tree-sitter-language-pack/c-tags.scm +3 -0
- aider/queries/tree-sitter-language-pack/clojure-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/commonlisp-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/cpp-tags.scm +3 -0
- aider/queries/tree-sitter-language-pack/csharp-tags.scm +6 -0
- aider/queries/tree-sitter-language-pack/dart-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/elixir-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/elm-tags.scm +3 -0
- aider/queries/tree-sitter-language-pack/go-tags.scm +7 -0
- aider/queries/tree-sitter-language-pack/java-tags.scm +6 -0
- aider/queries/tree-sitter-language-pack/javascript-tags.scm +8 -0
- aider/queries/tree-sitter-language-pack/lua-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +3 -0
- aider/queries/tree-sitter-language-pack/python-tags.scm +10 -0
- aider/queries/tree-sitter-language-pack/r-tags.scm +6 -0
- aider/queries/tree-sitter-language-pack/ruby-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/rust-tags.scm +3 -0
- aider/queries/tree-sitter-language-pack/solidity-tags.scm +1 -1
- aider/queries/tree-sitter-language-pack/swift-tags.scm +4 -1
- aider/queries/tree-sitter-languages/c-tags.scm +3 -0
- aider/queries/tree-sitter-languages/c_sharp-tags.scm +6 -0
- aider/queries/tree-sitter-languages/cpp-tags.scm +3 -0
- aider/queries/tree-sitter-languages/dart-tags.scm +2 -1
- aider/queries/tree-sitter-languages/elixir-tags.scm +5 -0
- aider/queries/tree-sitter-languages/elm-tags.scm +3 -0
- aider/queries/tree-sitter-languages/fortran-tags.scm +3 -0
- aider/queries/tree-sitter-languages/go-tags.scm +6 -0
- aider/queries/tree-sitter-languages/haskell-tags.scm +2 -0
- aider/queries/tree-sitter-languages/java-tags.scm +6 -0
- aider/queries/tree-sitter-languages/javascript-tags.scm +8 -0
- aider/queries/tree-sitter-languages/julia-tags.scm +2 -2
- aider/queries/tree-sitter-languages/kotlin-tags.scm +3 -0
- aider/queries/tree-sitter-languages/ocaml_interface-tags.scm +6 -0
- aider/queries/tree-sitter-languages/php-tags.scm +6 -0
- aider/queries/tree-sitter-languages/python-tags.scm +10 -0
- aider/queries/tree-sitter-languages/ruby-tags.scm +5 -0
- aider/queries/tree-sitter-languages/rust-tags.scm +3 -0
- aider/queries/tree-sitter-languages/scala-tags.scm +2 -3
- aider/queries/tree-sitter-languages/typescript-tags.scm +3 -0
- aider/queries/tree-sitter-languages/zig-tags.scm +20 -3
- aider/repomap.py +71 -11
- aider/resources/model-metadata.json +27335 -635
- aider/resources/model-settings.yml +190 -0
- aider/scrape.py +2 -0
- aider/tools/__init__.py +2 -0
- aider/tools/command.py +84 -94
- aider/tools/command_interactive.py +95 -110
- aider/tools/delete_block.py +131 -159
- aider/tools/delete_line.py +97 -132
- aider/tools/delete_lines.py +120 -160
- aider/tools/extract_lines.py +288 -312
- aider/tools/finished.py +30 -43
- aider/tools/git_branch.py +107 -109
- aider/tools/git_diff.py +44 -56
- aider/tools/git_log.py +39 -53
- aider/tools/git_remote.py +37 -51
- aider/tools/git_show.py +33 -47
- aider/tools/git_status.py +30 -44
- aider/tools/grep.py +214 -242
- aider/tools/indent_lines.py +175 -201
- aider/tools/insert_block.py +220 -253
- aider/tools/list_changes.py +65 -80
- aider/tools/ls.py +64 -80
- aider/tools/make_editable.py +57 -73
- aider/tools/make_readonly.py +50 -66
- aider/tools/remove.py +64 -80
- aider/tools/replace_all.py +96 -109
- aider/tools/replace_line.py +118 -156
- aider/tools/replace_lines.py +160 -197
- aider/tools/replace_text.py +159 -160
- aider/tools/show_numbered_context.py +115 -141
- aider/tools/thinking.py +52 -0
- aider/tools/undo_change.py +78 -91
- aider/tools/update_todo_list.py +130 -138
- aider/tools/utils/base_tool.py +64 -0
- aider/tools/utils/output.py +118 -0
- aider/tools/view.py +38 -54
- aider/tools/view_files_matching.py +131 -134
- aider/tools/view_files_with_symbol.py +108 -120
- aider/urls.py +1 -1
- aider/versioncheck.py +4 -3
- aider/website/docs/config/adv-model-settings.md +237 -0
- aider/website/docs/config/agent-mode.md +36 -3
- aider/website/docs/config/model-aliases.md +2 -1
- aider/website/docs/faq.md +6 -11
- aider/website/docs/languages.md +2 -2
- aider/website/docs/more/infinite-output.md +27 -0
- {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/METADATA +112 -70
- {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/RECORD +112 -107
- aider_ce-0.88.38.dist-info/entry_points.txt +6 -0
- aider_ce-0.88.20.dist-info/entry_points.txt +0 -2
- /aider/tools/{tool_utils.py → utils/helpers.py} +0 -0
- {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/WHEEL +0 -0
- {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/licenses/LICENSE.txt +0 -0
- {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/top_level.txt +0 -0
aider/tools/insert_block.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import traceback
|
|
3
3
|
|
|
4
|
-
from .
|
|
4
|
+
from aider.tools.utils.base_tool import BaseTool
|
|
5
|
+
from aider.tools.utils.helpers import (
|
|
5
6
|
ToolError,
|
|
6
7
|
apply_change,
|
|
7
8
|
find_pattern_indices,
|
|
@@ -11,278 +12,244 @@ from .tool_utils import (
|
|
|
11
12
|
select_occurrence_index,
|
|
12
13
|
validate_file_for_edit,
|
|
13
14
|
)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
from aider.tools.utils.output import tool_body_unwrapped, tool_footer, tool_header
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Tool(BaseTool):
|
|
19
|
+
NORM_NAME = "insertblock"
|
|
20
|
+
SCHEMA = {
|
|
21
|
+
"type": "function",
|
|
22
|
+
"function": {
|
|
23
|
+
"name": "InsertBlock",
|
|
24
|
+
"description": "Insert a block of content into a file.",
|
|
25
|
+
"parameters": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"properties": {
|
|
28
|
+
"file_path": {"type": "string"},
|
|
29
|
+
"content": {"type": "string"},
|
|
30
|
+
"after_pattern": {"type": "string"},
|
|
31
|
+
"before_pattern": {"type": "string"},
|
|
32
|
+
"occurrence": {"type": "integer", "default": 1},
|
|
33
|
+
"change_id": {"type": "string"},
|
|
34
|
+
"dry_run": {"type": "boolean", "default": False},
|
|
35
|
+
"position": {"type": "string", "enum": ["top", "bottom"]},
|
|
36
|
+
"auto_indent": {"type": "boolean", "default": True},
|
|
37
|
+
"use_regex": {"type": "boolean", "default": False},
|
|
38
|
+
},
|
|
39
|
+
"required": ["file_path", "content"],
|
|
33
40
|
},
|
|
34
|
-
"required": ["file_path", "content"],
|
|
35
41
|
},
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
try:
|
|
74
|
-
# 1. Validate parameters
|
|
75
|
-
if sum(x is not None for x in [after_pattern, before_pattern, position]) != 1:
|
|
76
|
-
raise ToolError(
|
|
77
|
-
"Must specify exactly one of: after_pattern, before_pattern, or position"
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
# 2. Validate file and get content
|
|
81
|
-
abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
|
|
82
|
-
lines = original_content.splitlines()
|
|
83
|
-
|
|
84
|
-
# Handle empty files
|
|
85
|
-
if not lines:
|
|
86
|
-
lines = [""]
|
|
87
|
-
|
|
88
|
-
# 3. Determine insertion point
|
|
89
|
-
insertion_line_idx = 0
|
|
90
|
-
pattern_type = ""
|
|
91
|
-
pattern_desc = ""
|
|
92
|
-
occurrence_str = ""
|
|
93
|
-
|
|
94
|
-
if position:
|
|
95
|
-
# Handle special positions
|
|
96
|
-
if position == "start_of_file" or position == "top":
|
|
97
|
-
insertion_line_idx = 0
|
|
98
|
-
pattern_type = "at start of"
|
|
99
|
-
elif position == "end_of_file" or position == "bottom":
|
|
100
|
-
insertion_line_idx = len(lines)
|
|
101
|
-
pattern_type = "at end of"
|
|
102
|
-
else:
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def execute(
|
|
46
|
+
cls,
|
|
47
|
+
coder,
|
|
48
|
+
file_path,
|
|
49
|
+
content,
|
|
50
|
+
after_pattern=None,
|
|
51
|
+
before_pattern=None,
|
|
52
|
+
occurrence=1,
|
|
53
|
+
change_id=None,
|
|
54
|
+
dry_run=False,
|
|
55
|
+
position=None,
|
|
56
|
+
auto_indent=True,
|
|
57
|
+
use_regex=False,
|
|
58
|
+
):
|
|
59
|
+
"""
|
|
60
|
+
Insert a block of text after or before a specified pattern using utility functions.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
coder: The coder instance
|
|
64
|
+
file_path: Path to the file to modify
|
|
65
|
+
content: The content to insert
|
|
66
|
+
after_pattern: Pattern to insert after (mutually exclusive with before_pattern and position)
|
|
67
|
+
before_pattern: Pattern to insert before (mutually exclusive with after_pattern and position)
|
|
68
|
+
occurrence: Which occurrence of the pattern to use (1-based, or -1 for last)
|
|
69
|
+
change_id: Optional ID for tracking changes
|
|
70
|
+
dry_run: If True, only simulate the change
|
|
71
|
+
position: Special position like "start_of_file" or "end_of_file"
|
|
72
|
+
auto_indent: If True, automatically adjust indentation of inserted content
|
|
73
|
+
use_regex: If True, treat patterns as regular expressions
|
|
74
|
+
"""
|
|
75
|
+
tool_name = "InsertBlock"
|
|
76
|
+
try:
|
|
77
|
+
# 1. Validate parameters
|
|
78
|
+
if sum(x is not None for x in [after_pattern, before_pattern, position]) != 1:
|
|
103
79
|
raise ToolError(
|
|
104
|
-
|
|
105
|
-
" 'end_of_file'"
|
|
80
|
+
"Must specify exactly one of: after_pattern, before_pattern, or position"
|
|
106
81
|
)
|
|
107
|
-
else:
|
|
108
|
-
# Handle pattern-based insertion
|
|
109
|
-
pattern = after_pattern if after_pattern else before_pattern
|
|
110
|
-
pattern_type = "after" if after_pattern else "before"
|
|
111
|
-
pattern_desc = f"Pattern '{pattern}'"
|
|
112
82
|
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
# Select the target occurrence
|
|
117
|
-
target_line_idx = select_occurrence_index(
|
|
118
|
-
pattern_line_indices, occurrence, pattern_desc
|
|
119
|
-
)
|
|
83
|
+
# 2. Validate file and get content
|
|
84
|
+
abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
|
|
85
|
+
lines = original_content.splitlines()
|
|
120
86
|
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
insertion_line_idx += 1 # Insert on the line *after* the matched line
|
|
87
|
+
# Handle empty files
|
|
88
|
+
if not lines:
|
|
89
|
+
lines = [""]
|
|
125
90
|
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
|
|
91
|
+
# 3. Determine insertion point
|
|
92
|
+
insertion_line_idx = 0
|
|
93
|
+
pattern_type = ""
|
|
94
|
+
pattern_desc = ""
|
|
95
|
+
occurrence_str = ""
|
|
129
96
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
#
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if line.strip()
|
|
149
|
-
]
|
|
150
|
-
min_content_indent = min(content_indents) if content_indents else 0
|
|
97
|
+
if position:
|
|
98
|
+
# Handle special positions
|
|
99
|
+
if position == "start_of_file" or position == "top":
|
|
100
|
+
insertion_line_idx = 0
|
|
101
|
+
pattern_type = "at start of"
|
|
102
|
+
elif position == "end_of_file" or position == "bottom":
|
|
103
|
+
insertion_line_idx = len(lines)
|
|
104
|
+
pattern_type = "at end of"
|
|
105
|
+
else:
|
|
106
|
+
raise ToolError(
|
|
107
|
+
f"Invalid position: '{position}'. Valid values are 'start_of_file' or"
|
|
108
|
+
" 'end_of_file'"
|
|
109
|
+
)
|
|
110
|
+
else:
|
|
111
|
+
# Handle pattern-based insertion
|
|
112
|
+
pattern = after_pattern if after_pattern else before_pattern
|
|
113
|
+
pattern_type = "after" if after_pattern else "before"
|
|
114
|
+
pattern_desc = f"Pattern '{pattern}'"
|
|
151
115
|
|
|
152
|
-
#
|
|
153
|
-
|
|
154
|
-
for line in content_lines:
|
|
155
|
-
if not line.strip(): # Empty or whitespace-only line
|
|
156
|
-
indented_content_lines.append("")
|
|
157
|
-
else:
|
|
158
|
-
# Remove existing indentation and add new base indentation
|
|
159
|
-
stripped_line = (
|
|
160
|
-
line[min_content_indent:] if min_content_indent <= len(line) else line
|
|
161
|
-
)
|
|
162
|
-
indented_content_lines.append(base_indent + stripped_line)
|
|
116
|
+
# Find pattern matches
|
|
117
|
+
pattern_line_indices = find_pattern_indices(lines, pattern, use_regex=use_regex)
|
|
163
118
|
|
|
164
|
-
|
|
119
|
+
# Select the target occurrence
|
|
120
|
+
target_line_idx = select_occurrence_index(
|
|
121
|
+
pattern_line_indices, occurrence, pattern_desc
|
|
122
|
+
)
|
|
165
123
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
124
|
+
# Determine insertion point
|
|
125
|
+
insertion_line_idx = target_line_idx
|
|
126
|
+
if pattern_type == "after":
|
|
127
|
+
insertion_line_idx += 1 # Insert on the line *after* the matched line
|
|
128
|
+
|
|
129
|
+
# Format occurrence info for output
|
|
130
|
+
num_occurrences = len(pattern_line_indices)
|
|
131
|
+
occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""
|
|
132
|
+
|
|
133
|
+
# 4. Handle indentation if requested
|
|
134
|
+
content_lines = content.splitlines()
|
|
135
|
+
|
|
136
|
+
if auto_indent and content_lines:
|
|
137
|
+
# Determine base indentation level
|
|
138
|
+
base_indent = ""
|
|
139
|
+
if insertion_line_idx > 0 and lines:
|
|
140
|
+
# Use indentation from the line before insertion point
|
|
141
|
+
reference_line_idx = min(insertion_line_idx - 1, len(lines) - 1)
|
|
142
|
+
reference_line = lines[reference_line_idx]
|
|
143
|
+
base_indent = re.match(r"^(\s*)", reference_line).group(1)
|
|
144
|
+
|
|
145
|
+
# Apply indentation to content lines, preserving relative indentation
|
|
146
|
+
if content_lines:
|
|
147
|
+
# Find minimum indentation in content to preserve relative indentation
|
|
148
|
+
content_indents = [
|
|
149
|
+
len(re.match(r"^(\s*)", line).group(1))
|
|
150
|
+
for line in content_lines
|
|
151
|
+
if line.strip()
|
|
152
|
+
]
|
|
153
|
+
min_content_indent = min(content_indents) if content_indents else 0
|
|
154
|
+
|
|
155
|
+
# Apply base indentation while preserving relative indentation
|
|
156
|
+
indented_content_lines = []
|
|
157
|
+
for line in content_lines:
|
|
158
|
+
if not line.strip(): # Empty or whitespace-only line
|
|
159
|
+
indented_content_lines.append("")
|
|
160
|
+
else:
|
|
161
|
+
# Remove existing indentation and add new base indentation
|
|
162
|
+
stripped_line = (
|
|
163
|
+
line[min_content_indent:]
|
|
164
|
+
if min_content_indent <= len(line)
|
|
165
|
+
else line
|
|
166
|
+
)
|
|
167
|
+
indented_content_lines.append(base_indent + stripped_line)
|
|
168
|
+
|
|
169
|
+
content_lines = indented_content_lines
|
|
170
|
+
|
|
171
|
+
# 5. Prepare the insertion
|
|
172
|
+
new_lines = lines[:insertion_line_idx] + content_lines + lines[insertion_line_idx:]
|
|
173
|
+
new_content = "\n".join(new_lines)
|
|
174
|
+
|
|
175
|
+
if original_content == new_content:
|
|
176
|
+
coder.io.tool_warning("No changes made: insertion would not change file")
|
|
177
|
+
return "Warning: No changes made (insertion would not change file)"
|
|
178
|
+
|
|
179
|
+
# 6. Generate diff for feedback
|
|
180
|
+
diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
|
|
181
|
+
|
|
182
|
+
# 7. Handle dry run
|
|
183
|
+
if dry_run:
|
|
184
|
+
if position:
|
|
185
|
+
dry_run_message = f"Dry run: Would insert block {pattern_type} {file_path}."
|
|
186
|
+
else:
|
|
187
|
+
dry_run_message = (
|
|
188
|
+
f"Dry run: Would insert block {pattern_type} {occurrence_str}pattern"
|
|
189
|
+
f" '{pattern}' in {file_path} at line {insertion_line_idx + 1}."
|
|
190
|
+
)
|
|
191
|
+
return format_tool_result(
|
|
192
|
+
coder,
|
|
193
|
+
tool_name,
|
|
194
|
+
"",
|
|
195
|
+
dry_run=True,
|
|
196
|
+
dry_run_message=dry_run_message,
|
|
197
|
+
diff_snippet=diff_snippet,
|
|
198
|
+
)
|
|
169
199
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
200
|
+
# 8. Apply Change (Not dry run)
|
|
201
|
+
metadata = {
|
|
202
|
+
"insertion_line_idx": insertion_line_idx,
|
|
203
|
+
"after_pattern": after_pattern,
|
|
204
|
+
"before_pattern": before_pattern,
|
|
205
|
+
"position": position,
|
|
206
|
+
"occurrence": occurrence,
|
|
207
|
+
"content": content,
|
|
208
|
+
"auto_indent": auto_indent,
|
|
209
|
+
"use_regex": use_regex,
|
|
210
|
+
}
|
|
211
|
+
final_change_id = apply_change(
|
|
212
|
+
coder,
|
|
213
|
+
abs_path,
|
|
214
|
+
rel_path,
|
|
215
|
+
original_content,
|
|
216
|
+
new_content,
|
|
217
|
+
"insertblock",
|
|
218
|
+
metadata,
|
|
219
|
+
change_id,
|
|
220
|
+
)
|
|
173
221
|
|
|
174
|
-
|
|
175
|
-
diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
|
|
222
|
+
coder.files_edited_by_tools.add(rel_path)
|
|
176
223
|
|
|
177
|
-
|
|
178
|
-
if dry_run:
|
|
224
|
+
# 9. Format and return result
|
|
179
225
|
if position:
|
|
180
|
-
|
|
226
|
+
success_message = f"Inserted block {pattern_type} {file_path}"
|
|
181
227
|
else:
|
|
182
|
-
|
|
183
|
-
f"
|
|
184
|
-
f"
|
|
228
|
+
success_message = (
|
|
229
|
+
f"Inserted block {pattern_type} {occurrence_str}pattern in {file_path} at line"
|
|
230
|
+
f" {insertion_line_idx + 1}"
|
|
185
231
|
)
|
|
232
|
+
|
|
186
233
|
return format_tool_result(
|
|
187
234
|
coder,
|
|
188
235
|
tool_name,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
dry_run_message=dry_run_message,
|
|
236
|
+
success_message,
|
|
237
|
+
change_id=final_change_id,
|
|
192
238
|
diff_snippet=diff_snippet,
|
|
193
239
|
)
|
|
194
240
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
original_content,
|
|
211
|
-
new_content,
|
|
212
|
-
"insertblock",
|
|
213
|
-
metadata,
|
|
214
|
-
change_id,
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
coder.files_edited_by_tools.add(rel_path)
|
|
218
|
-
|
|
219
|
-
# 9. Format and return result
|
|
220
|
-
if position:
|
|
221
|
-
success_message = f"Inserted block {pattern_type} {file_path}"
|
|
222
|
-
else:
|
|
223
|
-
success_message = (
|
|
224
|
-
f"Inserted block {pattern_type} {occurrence_str}pattern in {file_path} at line"
|
|
225
|
-
f" {insertion_line_idx + 1}"
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
return format_tool_result(
|
|
229
|
-
coder, tool_name, success_message, change_id=final_change_id, diff_snippet=diff_snippet
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
except ToolError as e:
|
|
233
|
-
# Handle errors raised by utility functions (expected errors)
|
|
234
|
-
return handle_tool_error(coder, tool_name, e, add_traceback=False)
|
|
235
|
-
|
|
236
|
-
except Exception as e:
|
|
237
|
-
coder.io.tool_error(
|
|
238
|
-
f"Error in InsertBlock: {str(e)}\n{traceback.format_exc()}"
|
|
239
|
-
) # Add traceback
|
|
240
|
-
return f"Error: {str(e)}"
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
def process_response(coder, params):
|
|
244
|
-
"""
|
|
245
|
-
Process the InsertBlock tool response.
|
|
246
|
-
|
|
247
|
-
Args:
|
|
248
|
-
coder: The Coder instance
|
|
249
|
-
params: Dictionary of parameters
|
|
250
|
-
|
|
251
|
-
Returns:
|
|
252
|
-
str: Result message
|
|
253
|
-
"""
|
|
254
|
-
file_path = params.get("file_path")
|
|
255
|
-
content = params.get("content")
|
|
256
|
-
after_pattern = params.get("after_pattern")
|
|
257
|
-
before_pattern = params.get("before_pattern")
|
|
258
|
-
occurrence = params.get("occurrence", 1)
|
|
259
|
-
change_id = params.get("change_id")
|
|
260
|
-
dry_run = params.get("dry_run", False)
|
|
261
|
-
position = params.get("position")
|
|
262
|
-
auto_indent = params.get("auto_indent", True)
|
|
263
|
-
use_regex = params.get("use_regex", False)
|
|
264
|
-
|
|
265
|
-
if (
|
|
266
|
-
file_path is not None
|
|
267
|
-
and content is not None
|
|
268
|
-
and (after_pattern is not None or before_pattern is not None or position is not None)
|
|
269
|
-
):
|
|
270
|
-
return _execute_insert_block(
|
|
271
|
-
coder,
|
|
272
|
-
file_path,
|
|
273
|
-
content,
|
|
274
|
-
after_pattern,
|
|
275
|
-
before_pattern,
|
|
276
|
-
occurrence,
|
|
277
|
-
change_id,
|
|
278
|
-
dry_run,
|
|
279
|
-
position,
|
|
280
|
-
auto_indent,
|
|
281
|
-
use_regex,
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
else:
|
|
285
|
-
return (
|
|
286
|
-
"Error: Missing required parameters for InsertBlock (file_path,"
|
|
287
|
-
" content, and either after_pattern or before_pattern)"
|
|
288
|
-
)
|
|
241
|
+
except ToolError as e:
|
|
242
|
+
# Handle errors raised by utility functions (expected errors)
|
|
243
|
+
return handle_tool_error(coder, tool_name, e, add_traceback=False)
|
|
244
|
+
|
|
245
|
+
except Exception as e:
|
|
246
|
+
coder.io.tool_error(
|
|
247
|
+
f"Error in InsertBlock: {str(e)}\n{traceback.format_exc()}"
|
|
248
|
+
) # Add traceback
|
|
249
|
+
return f"Error: {str(e)}"
|
|
250
|
+
|
|
251
|
+
@classmethod
|
|
252
|
+
def format_output(cls, coder, mcp_server, tool_response):
|
|
253
|
+
tool_header(coder=coder, mcp_server=mcp_server, tool_response=tool_response)
|
|
254
|
+
tool_body_unwrapped(coder=coder, tool_response=tool_response)
|
|
255
|
+
tool_footer(coder=coder, tool_response=tool_response)
|
aider/tools/list_changes.py
CHANGED
|
@@ -1,86 +1,71 @@
|
|
|
1
1
|
import traceback
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
from aider.tools.utils.base_tool import BaseTool
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Tool(BaseTool):
|
|
8
|
+
NORM_NAME = "listchanges"
|
|
9
|
+
SCHEMA = {
|
|
10
|
+
"type": "function",
|
|
11
|
+
"function": {
|
|
12
|
+
"name": "ListChanges",
|
|
13
|
+
"description": "List recent changes made.",
|
|
14
|
+
"parameters": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"file_path": {"type": "string"},
|
|
18
|
+
"limit": {"type": "integer", "default": 10},
|
|
19
|
+
},
|
|
14
20
|
},
|
|
15
21
|
},
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"""
|
|
34
|
-
try:
|
|
35
|
-
# If file_path is specified, get the absolute path
|
|
36
|
-
rel_file_path = None
|
|
37
|
-
if file_path:
|
|
38
|
-
abs_path = coder.abs_root_path(file_path)
|
|
39
|
-
rel_file_path = coder.get_rel_fname(abs_path)
|
|
40
|
-
|
|
41
|
-
# Get the list of changes
|
|
42
|
-
changes = coder.change_tracker.list_changes(rel_file_path, limit)
|
|
43
|
-
|
|
44
|
-
if not changes:
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def execute(cls, coder, file_path=None, limit=10):
|
|
26
|
+
"""
|
|
27
|
+
List recent changes made to files.
|
|
28
|
+
|
|
29
|
+
Parameters:
|
|
30
|
+
- coder: The Coder instance
|
|
31
|
+
- file_path: Optional path to filter changes by file
|
|
32
|
+
- limit: Maximum number of changes to list
|
|
33
|
+
|
|
34
|
+
Returns a formatted list of changes.
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
# If file_path is specified, get the absolute path
|
|
38
|
+
rel_file_path = None
|
|
45
39
|
if file_path:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
params: Dictionary of parameters
|
|
79
|
-
|
|
80
|
-
Returns:
|
|
81
|
-
str: Result message
|
|
82
|
-
"""
|
|
83
|
-
file_path = params.get("file_path")
|
|
84
|
-
limit = params.get("limit", 10)
|
|
85
|
-
|
|
86
|
-
return _execute_list_changes(coder, file_path, limit)
|
|
40
|
+
abs_path = coder.abs_root_path(file_path)
|
|
41
|
+
rel_file_path = coder.get_rel_fname(abs_path)
|
|
42
|
+
|
|
43
|
+
# Get the list of changes
|
|
44
|
+
changes = coder.change_tracker.list_changes(rel_file_path, limit)
|
|
45
|
+
|
|
46
|
+
if not changes:
|
|
47
|
+
if file_path:
|
|
48
|
+
return f"No changes found for file '{file_path}'"
|
|
49
|
+
else:
|
|
50
|
+
return "No changes have been made yet"
|
|
51
|
+
|
|
52
|
+
# Format the changes into a readable list
|
|
53
|
+
result = "Recent changes:\n"
|
|
54
|
+
for i, change in enumerate(changes):
|
|
55
|
+
change_time = datetime.fromtimestamp(change["timestamp"]).strftime("%H:%M:%S")
|
|
56
|
+
change_type = change["type"]
|
|
57
|
+
file_path = change["file_path"]
|
|
58
|
+
change_id = change["id"]
|
|
59
|
+
|
|
60
|
+
result += (
|
|
61
|
+
f"{i + 1}. [{change_id}] {change_time} - {change_type.upper()} on {file_path}\n"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
coder.io.tool_output(result) # Also print to console for user
|
|
65
|
+
return result
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
coder.io.tool_error(
|
|
69
|
+
f"Error in ListChanges: {str(e)}\n{traceback.format_exc()}"
|
|
70
|
+
) # Add traceback
|
|
71
|
+
return f"Error: {str(e)}"
|