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/replace_all.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from .
|
|
1
|
+
from aider.tools.utils.base_tool import BaseTool
|
|
2
|
+
from aider.tools.utils.helpers import (
|
|
2
3
|
ToolError,
|
|
3
4
|
apply_change,
|
|
4
5
|
format_tool_result,
|
|
@@ -6,121 +7,107 @@ from .tool_utils import (
|
|
|
6
7
|
handle_tool_error,
|
|
7
8
|
validate_file_for_edit,
|
|
8
9
|
)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
10
|
+
from aider.tools.utils.output import tool_body_unwrapped, tool_footer, tool_header
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Tool(BaseTool):
|
|
14
|
+
NORM_NAME = "replaceall"
|
|
15
|
+
SCHEMA = {
|
|
16
|
+
"type": "function",
|
|
17
|
+
"function": {
|
|
18
|
+
"name": "ReplaceAll",
|
|
19
|
+
"description": "Replace all occurrences of text in a file.",
|
|
20
|
+
"parameters": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"file_path": {"type": "string"},
|
|
24
|
+
"find_text": {"type": "string"},
|
|
25
|
+
"replace_text": {"type": "string"},
|
|
26
|
+
"change_id": {"type": "string"},
|
|
27
|
+
"dry_run": {"type": "boolean", "default": False},
|
|
28
|
+
},
|
|
29
|
+
"required": ["file_path", "find_text", "replace_text"],
|
|
23
30
|
},
|
|
24
|
-
"required": ["file_path", "find_text", "replace_text"],
|
|
25
31
|
},
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def execute(cls, coder, file_path, find_text, replace_text, change_id=None, dry_run=False):
|
|
36
|
+
"""
|
|
37
|
+
Replace all occurrences of text in a file using utility functions.
|
|
38
|
+
"""
|
|
39
|
+
# Get absolute file path
|
|
40
|
+
abs_path = coder.abs_root_path(file_path)
|
|
41
|
+
rel_path = coder.get_rel_fname(abs_path)
|
|
42
|
+
tool_name = "ReplaceAll"
|
|
43
|
+
try:
|
|
44
|
+
# 1. Validate file and get content
|
|
45
|
+
abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
|
|
46
|
+
|
|
47
|
+
# 2. Count occurrences
|
|
48
|
+
count = original_content.count(find_text)
|
|
49
|
+
if count == 0:
|
|
50
|
+
coder.io.tool_warning(f"Text '{find_text}' not found in file '{file_path}'")
|
|
51
|
+
return "Warning: Text not found in file"
|
|
52
|
+
|
|
53
|
+
# 3. Perform the replacement
|
|
54
|
+
new_content = original_content.replace(find_text, replace_text)
|
|
55
|
+
|
|
56
|
+
if original_content == new_content:
|
|
57
|
+
coder.io.tool_warning("No changes made: replacement text is identical to original")
|
|
58
|
+
return "Warning: No changes made (replacement identical to original)"
|
|
59
|
+
|
|
60
|
+
# 4. Generate diff for feedback
|
|
61
|
+
diff_examples = generate_unified_diff_snippet(original_content, new_content, rel_path)
|
|
62
|
+
|
|
63
|
+
# 5. Handle dry run
|
|
64
|
+
if dry_run:
|
|
65
|
+
dry_run_message = (
|
|
66
|
+
f"Dry run: Would replace {count} occurrences of '{find_text}' in {file_path}."
|
|
67
|
+
)
|
|
68
|
+
return format_tool_result(
|
|
69
|
+
coder,
|
|
70
|
+
tool_name,
|
|
71
|
+
"",
|
|
72
|
+
dry_run=True,
|
|
73
|
+
dry_run_message=dry_run_message,
|
|
74
|
+
diff_snippet=diff_examples,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# 6. Apply Change (Not dry run)
|
|
78
|
+
metadata = {"find_text": find_text, "replace_text": replace_text, "occurrences": count}
|
|
79
|
+
final_change_id = apply_change(
|
|
80
|
+
coder,
|
|
81
|
+
abs_path,
|
|
82
|
+
rel_path,
|
|
83
|
+
original_content,
|
|
84
|
+
new_content,
|
|
85
|
+
"replaceall",
|
|
86
|
+
metadata,
|
|
87
|
+
change_id,
|
|
88
|
+
)
|
|
57
89
|
|
|
58
|
-
|
|
59
|
-
diff_examples = generate_unified_diff_snippet(original_content, new_content, rel_path)
|
|
90
|
+
coder.files_edited_by_tools.add(rel_path)
|
|
60
91
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
dry_run_message = (
|
|
64
|
-
f"Dry run: Would replace {count} occurrences of '{find_text}' in {file_path}."
|
|
65
|
-
)
|
|
92
|
+
# 7. Format and return result
|
|
93
|
+
success_message = f"Replaced {count} occurrences in {file_path}"
|
|
66
94
|
return format_tool_result(
|
|
67
95
|
coder,
|
|
68
96
|
tool_name,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
dry_run_message=dry_run_message,
|
|
97
|
+
success_message,
|
|
98
|
+
change_id=final_change_id,
|
|
72
99
|
diff_snippet=diff_examples,
|
|
73
100
|
)
|
|
74
101
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
coder.files_edited_by_tools.add(rel_path)
|
|
89
|
-
|
|
90
|
-
# 7. Format and return result
|
|
91
|
-
success_message = f"Replaced {count} occurrences in {file_path}"
|
|
92
|
-
return format_tool_result(
|
|
93
|
-
coder, tool_name, success_message, change_id=final_change_id, diff_snippet=diff_examples
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
except ToolError as e:
|
|
97
|
-
# Handle errors raised by utility functions
|
|
98
|
-
return handle_tool_error(coder, tool_name, e, add_traceback=False)
|
|
99
|
-
except Exception as e:
|
|
100
|
-
# Handle unexpected errors
|
|
101
|
-
return handle_tool_error(coder, tool_name, e)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def process_response(coder, params):
|
|
105
|
-
"""
|
|
106
|
-
Process the ReplaceAll tool response.
|
|
107
|
-
|
|
108
|
-
Args:
|
|
109
|
-
coder: The Coder instance
|
|
110
|
-
params: Dictionary of parameters
|
|
111
|
-
|
|
112
|
-
Returns:
|
|
113
|
-
str: Result message
|
|
114
|
-
"""
|
|
115
|
-
file_path = params.get("file_path")
|
|
116
|
-
find_text = params.get("find_text")
|
|
117
|
-
replace_text = params.get("replace_text")
|
|
118
|
-
change_id = params.get("change_id")
|
|
119
|
-
dry_run = params.get("dry_run", False)
|
|
120
|
-
|
|
121
|
-
if file_path is not None and find_text is not None and replace_text is not None:
|
|
122
|
-
return _execute_replace_all(coder, file_path, find_text, replace_text, change_id, dry_run)
|
|
123
|
-
else:
|
|
124
|
-
return (
|
|
125
|
-
"Error: Missing required parameters for ReplaceAll (file_path, find_text, replace_text)"
|
|
126
|
-
)
|
|
102
|
+
except ToolError as e:
|
|
103
|
+
# Handle errors raised by utility functions
|
|
104
|
+
return handle_tool_error(coder, tool_name, e, add_traceback=False)
|
|
105
|
+
except Exception as e:
|
|
106
|
+
# Handle unexpected errors
|
|
107
|
+
return handle_tool_error(coder, tool_name, e)
|
|
108
|
+
|
|
109
|
+
@classmethod
|
|
110
|
+
def format_output(cls, coder, mcp_server, tool_response):
|
|
111
|
+
tool_header(coder=coder, mcp_server=mcp_server, tool_response=tool_response)
|
|
112
|
+
tool_body_unwrapped(coder=coder, tool_response=tool_response)
|
|
113
|
+
tool_footer(coder=coder, tool_response=tool_response)
|
aider/tools/replace_line.py
CHANGED
|
@@ -1,173 +1,135 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import traceback
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
from aider.tools.utils.base_tool import BaseTool
|
|
4
|
+
from aider.tools.utils.helpers import ToolError, validate_file_for_edit
|
|
5
|
+
from aider.tools.utils.output import tool_body_unwrapped, tool_footer, tool_header
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Tool(BaseTool):
|
|
9
|
+
NORM_NAME = "replaceline"
|
|
10
|
+
SCHEMA = {
|
|
11
|
+
"type": "function",
|
|
12
|
+
"function": {
|
|
13
|
+
"name": "ReplaceLine",
|
|
14
|
+
"description": "Replace a single line in a file.",
|
|
15
|
+
"parameters": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"file_path": {"type": "string"},
|
|
19
|
+
"line_number": {"type": "integer"},
|
|
20
|
+
"new_content": {"type": "string"},
|
|
21
|
+
"change_id": {"type": "string"},
|
|
22
|
+
"dry_run": {"type": "boolean", "default": False},
|
|
23
|
+
},
|
|
24
|
+
"required": ["file_path", "line_number", "new_content"],
|
|
17
25
|
},
|
|
18
|
-
"required": ["file_path", "line_number", "new_content"],
|
|
19
26
|
},
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return "Error: File is read-only. Use MakeEditable first."
|
|
59
|
-
else:
|
|
60
|
-
coder.io.tool_error(f"File '{file_path}' not in context")
|
|
61
|
-
return "Error: File not in context"
|
|
62
|
-
|
|
63
|
-
# Reread file content immediately before modification
|
|
64
|
-
file_content = coder.io.read_text(abs_path)
|
|
65
|
-
if file_content is None:
|
|
66
|
-
coder.io.tool_error(f"Could not read file '{file_path}' before ReplaceLine operation.")
|
|
67
|
-
return f"Error: Could not read file '{file_path}'"
|
|
68
|
-
|
|
69
|
-
# Split into lines
|
|
70
|
-
lines = file_content.splitlines()
|
|
71
|
-
|
|
72
|
-
# Validate line number
|
|
73
|
-
if not isinstance(line_number, int):
|
|
74
|
-
try:
|
|
75
|
-
line_number = int(line_number)
|
|
76
|
-
except ValueError:
|
|
77
|
-
coder.io.tool_error(f"Line number must be an integer, got '{line_number}'")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def execute(cls, coder, file_path, line_number, new_content, change_id=None, dry_run=False):
|
|
31
|
+
"""
|
|
32
|
+
Replace a specific line identified by line number.
|
|
33
|
+
Useful for fixing errors identified by error messages or linters.
|
|
34
|
+
|
|
35
|
+
Parameters:
|
|
36
|
+
- coder: The Coder instance
|
|
37
|
+
- file_path: Path to the file to modify
|
|
38
|
+
- line_number: The line number to replace (1-based)
|
|
39
|
+
- new_content: New content for the line
|
|
40
|
+
- change_id: Optional ID for tracking the change
|
|
41
|
+
- dry_run: If True, simulate the change without modifying the file
|
|
42
|
+
|
|
43
|
+
Returns a result message.
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
# 1. Validate file and get content
|
|
47
|
+
abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
|
|
48
|
+
lines = original_content.splitlines()
|
|
49
|
+
|
|
50
|
+
# Validate line number
|
|
51
|
+
if not isinstance(line_number, int):
|
|
52
|
+
try:
|
|
53
|
+
line_number = int(line_number)
|
|
54
|
+
except ValueError:
|
|
55
|
+
coder.io.tool_error(f"Line number must be an integer, got '{line_number}'")
|
|
56
|
+
coder.io.tool_error(
|
|
57
|
+
f"Invalid line_number value: '{line_number}'. Must be an integer."
|
|
58
|
+
)
|
|
59
|
+
return f"Error: Invalid line_number value '{line_number}'"
|
|
60
|
+
|
|
61
|
+
# Convert 1-based line number to 0-based index
|
|
62
|
+
idx = line_number - 1
|
|
63
|
+
|
|
64
|
+
if idx < 0 or idx >= len(lines):
|
|
78
65
|
coder.io.tool_error(
|
|
79
|
-
f"
|
|
66
|
+
f"Line number {line_number} is out of range for file '{file_path}' (has"
|
|
67
|
+
f" {len(lines)} lines)."
|
|
80
68
|
)
|
|
81
|
-
return f"Error:
|
|
69
|
+
return f"Error: Line number {line_number} out of range"
|
|
82
70
|
|
|
83
|
-
|
|
84
|
-
|
|
71
|
+
# Store original content for change tracking
|
|
72
|
+
original_line = lines[idx]
|
|
85
73
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
f"Line number {line_number} is out of range for file '{file_path}' (has"
|
|
89
|
-
f" {len(lines)} lines)."
|
|
90
|
-
)
|
|
91
|
-
return f"Error: Line number {line_number} out of range"
|
|
74
|
+
# Replace the line
|
|
75
|
+
lines[idx] = new_content
|
|
92
76
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
original_line = lines[idx]
|
|
77
|
+
# Join lines back into a string
|
|
78
|
+
new_content_full = "\n".join(lines)
|
|
96
79
|
|
|
97
|
-
|
|
98
|
-
|
|
80
|
+
if original_content == new_content_full:
|
|
81
|
+
coder.io.tool_warning("No changes made: new line content is identical to original")
|
|
82
|
+
return "Warning: No changes made (new content identical to original)"
|
|
99
83
|
|
|
100
|
-
|
|
101
|
-
|
|
84
|
+
# Create a readable diff for the line replacement
|
|
85
|
+
diff = f"Line {line_number}:\n- {original_line}\n+ {new_content}"
|
|
102
86
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
87
|
+
# Handle dry run
|
|
88
|
+
if dry_run:
|
|
89
|
+
coder.io.tool_output(f"Dry run: Would replace line {line_number} in {file_path}")
|
|
90
|
+
return f"Dry run: Would replace line {line_number}. Diff:\n{diff}"
|
|
106
91
|
|
|
107
|
-
|
|
108
|
-
|
|
92
|
+
# --- Apply Change (Not dry run) ---
|
|
93
|
+
coder.io.write_text(abs_path, new_content_full)
|
|
109
94
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
95
|
+
# Track the change
|
|
96
|
+
try:
|
|
97
|
+
metadata = {
|
|
98
|
+
"line_number": line_number,
|
|
99
|
+
"original_line": original_line,
|
|
100
|
+
"new_line": new_content,
|
|
101
|
+
}
|
|
102
|
+
change_id = coder.change_tracker.track_change(
|
|
103
|
+
file_path=rel_path,
|
|
104
|
+
change_type="replaceline",
|
|
105
|
+
original_content=original_content,
|
|
106
|
+
new_content=new_content_full,
|
|
107
|
+
metadata=metadata,
|
|
108
|
+
change_id=change_id,
|
|
109
|
+
)
|
|
110
|
+
except Exception as track_e:
|
|
111
|
+
coder.io.tool_error(f"Error tracking change for ReplaceLine: {track_e}")
|
|
112
|
+
change_id = "TRACKING_FAILED"
|
|
114
113
|
|
|
115
|
-
|
|
116
|
-
coder.io.write_text(abs_path, new_content_full)
|
|
114
|
+
coder.files_edited_by_tools.add(rel_path)
|
|
117
115
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"line_number": line_number,
|
|
122
|
-
"original_line": original_line,
|
|
123
|
-
"new_line": new_content,
|
|
124
|
-
}
|
|
125
|
-
change_id = coder.change_tracker.track_change(
|
|
126
|
-
file_path=rel_path,
|
|
127
|
-
change_type="replaceline",
|
|
128
|
-
original_content=original_content,
|
|
129
|
-
new_content=new_content_full,
|
|
130
|
-
metadata=metadata,
|
|
131
|
-
change_id=change_id,
|
|
116
|
+
# Improve feedback
|
|
117
|
+
coder.io.tool_output(
|
|
118
|
+
f"✅ Replaced line {line_number} in {file_path} (change_id: {change_id})"
|
|
132
119
|
)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
f"
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
coder
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def process_response(coder, params):
|
|
151
|
-
"""
|
|
152
|
-
Process the ReplaceLine tool response.
|
|
153
|
-
|
|
154
|
-
Args:
|
|
155
|
-
coder: The Coder instance
|
|
156
|
-
params: Dictionary of parameters
|
|
157
|
-
|
|
158
|
-
Returns:
|
|
159
|
-
str: Result message
|
|
160
|
-
"""
|
|
161
|
-
file_path = params.get("file_path")
|
|
162
|
-
line_number = params.get("line_number")
|
|
163
|
-
new_content = params.get("new_content")
|
|
164
|
-
change_id = params.get("change_id")
|
|
165
|
-
dry_run = params.get("dry_run", False)
|
|
166
|
-
|
|
167
|
-
if file_path is not None and line_number is not None and new_content is not None:
|
|
168
|
-
return _execute_replace_line(coder, file_path, line_number, new_content, change_id, dry_run)
|
|
169
|
-
else:
|
|
170
|
-
return (
|
|
171
|
-
"Error: Missing required parameters for ReplaceLine (file_path,"
|
|
172
|
-
" line_number, new_content)"
|
|
173
|
-
)
|
|
120
|
+
return (
|
|
121
|
+
f"Successfully replaced line {line_number} (change_id: {change_id}). Diff:\n{diff}"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
except ToolError as e:
|
|
125
|
+
coder.io.tool_error(f"Error in ReplaceLine: {str(e)}")
|
|
126
|
+
return f"Error: {str(e)}"
|
|
127
|
+
except Exception as e:
|
|
128
|
+
coder.io.tool_error(f"Error in ReplaceLine: {str(e)}\n{traceback.format_exc()}")
|
|
129
|
+
return f"Error: {str(e)}"
|
|
130
|
+
|
|
131
|
+
@classmethod
|
|
132
|
+
def format_output(cls, coder, mcp_server, tool_response):
|
|
133
|
+
tool_header(coder=coder, mcp_server=mcp_server, tool_response=tool_response)
|
|
134
|
+
tool_body_unwrapped(coder=coder, tool_response=tool_response)
|
|
135
|
+
tool_footer(coder=coder, tool_response=tool_response)
|