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.
Files changed (113) hide show
  1. aider/__init__.py +1 -1
  2. aider/_version.py +2 -2
  3. aider/args.py +63 -43
  4. aider/coders/agent_coder.py +331 -79
  5. aider/coders/agent_prompts.py +3 -15
  6. aider/coders/architect_coder.py +21 -5
  7. aider/coders/base_coder.py +661 -413
  8. aider/coders/base_prompts.py +6 -3
  9. aider/coders/chat_chunks.py +39 -17
  10. aider/commands.py +79 -15
  11. aider/diffs.py +10 -9
  12. aider/exceptions.py +1 -1
  13. aider/helpers/coroutines.py +8 -0
  14. aider/helpers/requests.py +45 -0
  15. aider/history.py +5 -0
  16. aider/io.py +179 -25
  17. aider/main.py +86 -35
  18. aider/models.py +16 -8
  19. aider/queries/tree-sitter-language-pack/c-tags.scm +3 -0
  20. aider/queries/tree-sitter-language-pack/clojure-tags.scm +5 -0
  21. aider/queries/tree-sitter-language-pack/commonlisp-tags.scm +5 -0
  22. aider/queries/tree-sitter-language-pack/cpp-tags.scm +3 -0
  23. aider/queries/tree-sitter-language-pack/csharp-tags.scm +6 -0
  24. aider/queries/tree-sitter-language-pack/dart-tags.scm +5 -0
  25. aider/queries/tree-sitter-language-pack/elixir-tags.scm +5 -0
  26. aider/queries/tree-sitter-language-pack/elm-tags.scm +3 -0
  27. aider/queries/tree-sitter-language-pack/go-tags.scm +7 -0
  28. aider/queries/tree-sitter-language-pack/java-tags.scm +6 -0
  29. aider/queries/tree-sitter-language-pack/javascript-tags.scm +8 -0
  30. aider/queries/tree-sitter-language-pack/lua-tags.scm +5 -0
  31. aider/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +3 -0
  32. aider/queries/tree-sitter-language-pack/python-tags.scm +10 -0
  33. aider/queries/tree-sitter-language-pack/r-tags.scm +6 -0
  34. aider/queries/tree-sitter-language-pack/ruby-tags.scm +5 -0
  35. aider/queries/tree-sitter-language-pack/rust-tags.scm +3 -0
  36. aider/queries/tree-sitter-language-pack/solidity-tags.scm +1 -1
  37. aider/queries/tree-sitter-language-pack/swift-tags.scm +4 -1
  38. aider/queries/tree-sitter-languages/c-tags.scm +3 -0
  39. aider/queries/tree-sitter-languages/c_sharp-tags.scm +6 -0
  40. aider/queries/tree-sitter-languages/cpp-tags.scm +3 -0
  41. aider/queries/tree-sitter-languages/dart-tags.scm +2 -1
  42. aider/queries/tree-sitter-languages/elixir-tags.scm +5 -0
  43. aider/queries/tree-sitter-languages/elm-tags.scm +3 -0
  44. aider/queries/tree-sitter-languages/fortran-tags.scm +3 -0
  45. aider/queries/tree-sitter-languages/go-tags.scm +6 -0
  46. aider/queries/tree-sitter-languages/haskell-tags.scm +2 -0
  47. aider/queries/tree-sitter-languages/java-tags.scm +6 -0
  48. aider/queries/tree-sitter-languages/javascript-tags.scm +8 -0
  49. aider/queries/tree-sitter-languages/julia-tags.scm +2 -2
  50. aider/queries/tree-sitter-languages/kotlin-tags.scm +3 -0
  51. aider/queries/tree-sitter-languages/ocaml_interface-tags.scm +6 -0
  52. aider/queries/tree-sitter-languages/php-tags.scm +6 -0
  53. aider/queries/tree-sitter-languages/python-tags.scm +10 -0
  54. aider/queries/tree-sitter-languages/ruby-tags.scm +5 -0
  55. aider/queries/tree-sitter-languages/rust-tags.scm +3 -0
  56. aider/queries/tree-sitter-languages/scala-tags.scm +2 -3
  57. aider/queries/tree-sitter-languages/typescript-tags.scm +3 -0
  58. aider/queries/tree-sitter-languages/zig-tags.scm +20 -3
  59. aider/repomap.py +71 -11
  60. aider/resources/model-metadata.json +27335 -635
  61. aider/resources/model-settings.yml +190 -0
  62. aider/scrape.py +2 -0
  63. aider/tools/__init__.py +2 -0
  64. aider/tools/command.py +84 -94
  65. aider/tools/command_interactive.py +95 -110
  66. aider/tools/delete_block.py +131 -159
  67. aider/tools/delete_line.py +97 -132
  68. aider/tools/delete_lines.py +120 -160
  69. aider/tools/extract_lines.py +288 -312
  70. aider/tools/finished.py +30 -43
  71. aider/tools/git_branch.py +107 -109
  72. aider/tools/git_diff.py +44 -56
  73. aider/tools/git_log.py +39 -53
  74. aider/tools/git_remote.py +37 -51
  75. aider/tools/git_show.py +33 -47
  76. aider/tools/git_status.py +30 -44
  77. aider/tools/grep.py +214 -242
  78. aider/tools/indent_lines.py +175 -201
  79. aider/tools/insert_block.py +220 -253
  80. aider/tools/list_changes.py +65 -80
  81. aider/tools/ls.py +64 -80
  82. aider/tools/make_editable.py +57 -73
  83. aider/tools/make_readonly.py +50 -66
  84. aider/tools/remove.py +64 -80
  85. aider/tools/replace_all.py +96 -109
  86. aider/tools/replace_line.py +118 -156
  87. aider/tools/replace_lines.py +160 -197
  88. aider/tools/replace_text.py +159 -160
  89. aider/tools/show_numbered_context.py +115 -141
  90. aider/tools/thinking.py +52 -0
  91. aider/tools/undo_change.py +78 -91
  92. aider/tools/update_todo_list.py +130 -138
  93. aider/tools/utils/base_tool.py +64 -0
  94. aider/tools/utils/output.py +118 -0
  95. aider/tools/view.py +38 -54
  96. aider/tools/view_files_matching.py +131 -134
  97. aider/tools/view_files_with_symbol.py +108 -120
  98. aider/urls.py +1 -1
  99. aider/versioncheck.py +4 -3
  100. aider/website/docs/config/adv-model-settings.md +237 -0
  101. aider/website/docs/config/agent-mode.md +36 -3
  102. aider/website/docs/config/model-aliases.md +2 -1
  103. aider/website/docs/faq.md +6 -11
  104. aider/website/docs/languages.md +2 -2
  105. aider/website/docs/more/infinite-output.md +27 -0
  106. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/METADATA +112 -70
  107. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/RECORD +112 -107
  108. aider_ce-0.88.38.dist-info/entry_points.txt +6 -0
  109. aider_ce-0.88.20.dist-info/entry_points.txt +0 -2
  110. /aider/tools/{tool_utils.py → utils/helpers.py} +0 -0
  111. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/WHEEL +0 -0
  112. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/licenses/LICENSE.txt +0 -0
  113. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,8 @@
1
1
  import re
2
2
  import traceback
3
3
 
4
- from .tool_utils import (
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
- 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},
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
- # Normalized tool name for lookup
40
- NORM_NAME = "insertblock"
41
-
42
-
43
- def _execute_insert_block(
44
- coder,
45
- file_path,
46
- content,
47
- after_pattern=None,
48
- before_pattern=None,
49
- occurrence=1,
50
- change_id=None,
51
- dry_run=False,
52
- position=None,
53
- auto_indent=True,
54
- use_regex=False,
55
- ):
56
- """
57
- Insert a block of text after or before a specified pattern using utility functions.
58
-
59
- Args:
60
- coder: The coder instance
61
- file_path: Path to the file to modify
62
- content: The content to insert
63
- after_pattern: Pattern to insert after (mutually exclusive with before_pattern and position)
64
- before_pattern: Pattern to insert before (mutually exclusive with after_pattern and position)
65
- occurrence: Which occurrence of the pattern to use (1-based, or -1 for last)
66
- change_id: Optional ID for tracking changes
67
- dry_run: If True, only simulate the change
68
- position: Special position like "start_of_file" or "end_of_file"
69
- auto_indent: If True, automatically adjust indentation of inserted content
70
- use_regex: If True, treat patterns as regular expressions
71
- """
72
- tool_name = "InsertBlock"
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
- f"Invalid position: '{position}'. Valid values are 'start_of_file' or"
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
- # Find pattern matches
114
- pattern_line_indices = find_pattern_indices(lines, pattern, use_regex=use_regex)
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
- # Determine insertion point
122
- insertion_line_idx = target_line_idx
123
- if pattern_type == "after":
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
- # Format occurrence info for output
127
- num_occurrences = len(pattern_line_indices)
128
- occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""
91
+ # 3. Determine insertion point
92
+ insertion_line_idx = 0
93
+ pattern_type = ""
94
+ pattern_desc = ""
95
+ occurrence_str = ""
129
96
 
130
- # 4. Handle indentation if requested
131
- content_lines = content.splitlines()
132
-
133
- if auto_indent and content_lines:
134
- # Determine base indentation level
135
- base_indent = ""
136
- if insertion_line_idx > 0 and lines:
137
- # Use indentation from the line before insertion point
138
- reference_line_idx = min(insertion_line_idx - 1, len(lines) - 1)
139
- reference_line = lines[reference_line_idx]
140
- base_indent = re.match(r"^(\s*)", reference_line).group(1)
141
-
142
- # Apply indentation to content lines, preserving relative indentation
143
- if content_lines:
144
- # Find minimum indentation in content to preserve relative indentation
145
- content_indents = [
146
- len(re.match(r"^(\s*)", line).group(1))
147
- for line in content_lines
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
- # Apply base indentation while preserving relative indentation
153
- indented_content_lines = []
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
- content_lines = indented_content_lines
119
+ # Select the target occurrence
120
+ target_line_idx = select_occurrence_index(
121
+ pattern_line_indices, occurrence, pattern_desc
122
+ )
165
123
 
166
- # 5. Prepare the insertion
167
- new_lines = lines[:insertion_line_idx] + content_lines + lines[insertion_line_idx:]
168
- new_content = "\n".join(new_lines)
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
- if original_content == new_content:
171
- coder.io.tool_warning("No changes made: insertion would not change file")
172
- return "Warning: No changes made (insertion would not change file)"
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
- # 6. Generate diff for feedback
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
- # 7. Handle dry run
178
- if dry_run:
224
+ # 9. Format and return result
179
225
  if position:
180
- dry_run_message = f"Dry run: Would insert block {pattern_type} {file_path}."
226
+ success_message = f"Inserted block {pattern_type} {file_path}"
181
227
  else:
182
- dry_run_message = (
183
- f"Dry run: Would insert block {pattern_type} {occurrence_str}pattern"
184
- f" '{pattern}' in {file_path} at line {insertion_line_idx + 1}."
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
- dry_run=True,
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
- # 8. Apply Change (Not dry run)
196
- metadata = {
197
- "insertion_line_idx": insertion_line_idx,
198
- "after_pattern": after_pattern,
199
- "before_pattern": before_pattern,
200
- "position": position,
201
- "occurrence": occurrence,
202
- "content": content,
203
- "auto_indent": auto_indent,
204
- "use_regex": use_regex,
205
- }
206
- final_change_id = apply_change(
207
- coder,
208
- abs_path,
209
- rel_path,
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)
@@ -1,86 +1,71 @@
1
1
  import traceback
2
2
  from datetime import datetime
3
3
 
4
- 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},
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
- # Normalized tool name for lookup
20
- NORM_NAME = "listchanges"
21
-
22
-
23
- def _execute_list_changes(coder, file_path=None, limit=10):
24
- """
25
- List recent changes made to files.
26
-
27
- Parameters:
28
- - coder: The Coder instance
29
- - file_path: Optional path to filter changes by file
30
- - limit: Maximum number of changes to list
31
-
32
- Returns a formatted list of changes.
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
- return f"No changes found for file '{file_path}'"
47
- else:
48
- return "No changes have been made yet"
49
-
50
- # Format the changes into a readable list
51
- result = "Recent changes:\n"
52
- for i, change in enumerate(changes):
53
- change_time = datetime.fromtimestamp(change["timestamp"]).strftime("%H:%M:%S")
54
- change_type = change["type"]
55
- file_path = change["file_path"]
56
- change_id = change["id"]
57
-
58
- result += (
59
- f"{i + 1}. [{change_id}] {change_time} - {change_type.upper()} on {file_path}\n"
60
- )
61
-
62
- coder.io.tool_output(result) # Also print to console for user
63
- return result
64
-
65
- except Exception as e:
66
- coder.io.tool_error(
67
- f"Error in ListChanges: {str(e)}\n{traceback.format_exc()}"
68
- ) # Add traceback
69
- return f"Error: {str(e)}"
70
-
71
-
72
- def process_response(coder, params):
73
- """
74
- Process the ListChanges tool response.
75
-
76
- Args:
77
- coder: The Coder instance
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)}"