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,4 +1,5 @@
1
- from .tool_utils import (
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
- 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},
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
- # Normalized tool name for lookup
30
- NORM_NAME = "replaceall"
31
-
32
-
33
- def _execute_replace_all(coder, file_path, find_text, replace_text, change_id=None, dry_run=False):
34
- """
35
- Replace all occurrences of text in a file using utility functions.
36
- """
37
- # Get absolute file path
38
- abs_path = coder.abs_root_path(file_path)
39
- rel_path = coder.get_rel_fname(abs_path)
40
- tool_name = "ReplaceAll"
41
- try:
42
- # 1. Validate file and get content
43
- abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
44
-
45
- # 2. Count occurrences
46
- count = original_content.count(find_text)
47
- if count == 0:
48
- coder.io.tool_warning(f"Text '{find_text}' not found in file '{file_path}'")
49
- return "Warning: Text not found in file"
50
-
51
- # 3. Perform the replacement
52
- new_content = original_content.replace(find_text, replace_text)
53
-
54
- if original_content == new_content:
55
- coder.io.tool_warning("No changes made: replacement text is identical to original")
56
- return "Warning: No changes made (replacement identical to original)"
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
- # 4. Generate diff for feedback
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
- # 5. Handle dry run
62
- if dry_run:
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
- dry_run=True,
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
- # 6. Apply Change (Not dry run)
76
- metadata = {"find_text": find_text, "replace_text": replace_text, "occurrences": count}
77
- final_change_id = apply_change(
78
- coder,
79
- abs_path,
80
- rel_path,
81
- original_content,
82
- new_content,
83
- "replaceall",
84
- metadata,
85
- change_id,
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)
@@ -1,173 +1,135 @@
1
- import os
2
1
  import traceback
3
2
 
4
- 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},
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
- # Normalized tool name for lookup
24
- NORM_NAME = "replaceline"
25
-
26
-
27
- def _execute_replace_line(
28
- coder, file_path, line_number, new_content, change_id=None, dry_run=False
29
- ):
30
- """
31
- Replace a specific line identified by line number.
32
- Useful for fixing errors identified by error messages or linters.
33
-
34
- Parameters:
35
- - coder: The Coder instance
36
- - file_path: Path to the file to modify
37
- - line_number: The line number to replace (1-based)
38
- - new_content: New content for the line
39
- - change_id: Optional ID for tracking the change
40
- - dry_run: If True, simulate the change without modifying the file
41
-
42
- Returns a result message.
43
- """
44
- try:
45
- # Get absolute file path
46
- abs_path = coder.abs_root_path(file_path)
47
- rel_path = coder.get_rel_fname(abs_path)
48
-
49
- # Check if file exists
50
- if not os.path.isfile(abs_path):
51
- coder.io.tool_error(f"File '{file_path}' not found")
52
- return "Error: File not found"
53
-
54
- # Check if file is in editable context
55
- if abs_path not in coder.abs_fnames:
56
- if abs_path in coder.abs_read_only_fnames:
57
- coder.io.tool_error(f"File '{file_path}' is read-only. Use MakeEditable first.")
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"Invalid line_number value: '{line_number}'. Must be an integer."
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: Invalid line_number value '{line_number}'"
69
+ return f"Error: Line number {line_number} out of range"
82
70
 
83
- # Convert 1-based line number to 0-based index
84
- idx = line_number - 1
71
+ # Store original content for change tracking
72
+ original_line = lines[idx]
85
73
 
86
- if idx < 0 or idx >= len(lines):
87
- coder.io.tool_error(
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
- # Store original content for change tracking
94
- original_content = file_content
95
- original_line = lines[idx]
77
+ # Join lines back into a string
78
+ new_content_full = "\n".join(lines)
96
79
 
97
- # Replace the line
98
- lines[idx] = new_content
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
- # Join lines back into a string
101
- new_content_full = "\n".join(lines)
84
+ # Create a readable diff for the line replacement
85
+ diff = f"Line {line_number}:\n- {original_line}\n+ {new_content}"
102
86
 
103
- if original_content == new_content_full:
104
- coder.io.tool_warning("No changes made: new line content is identical to original")
105
- return "Warning: No changes made (new content identical to original)"
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
- # Create a readable diff for the line replacement
108
- diff = f"Line {line_number}:\n- {original_line}\n+ {new_content}"
92
+ # --- Apply Change (Not dry run) ---
93
+ coder.io.write_text(abs_path, new_content_full)
109
94
 
110
- # Handle dry run
111
- if dry_run:
112
- coder.io.tool_output(f"Dry run: Would replace line {line_number} in {file_path}")
113
- return f"Dry run: Would replace line {line_number}. Diff:\n{diff}"
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
- # --- Apply Change (Not dry run) ---
116
- coder.io.write_text(abs_path, new_content_full)
114
+ coder.files_edited_by_tools.add(rel_path)
117
115
 
118
- # Track the change
119
- try:
120
- metadata = {
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
- except Exception as track_e:
134
- coder.io.tool_error(f"Error tracking change for ReplaceLine: {track_e}")
135
- change_id = "TRACKING_FAILED"
136
-
137
- coder.files_edited_by_tools.add(rel_path)
138
-
139
- # Improve feedback
140
- coder.io.tool_output(
141
- f" Replaced line {line_number} in {file_path} (change_id: {change_id})"
142
- )
143
- return f"Successfully replaced line {line_number} (change_id: {change_id}). Diff:\n{diff}"
144
-
145
- except Exception as e:
146
- coder.io.tool_error(f"Error in ReplaceLine: {str(e)}\n{traceback.format_exc()}")
147
- return f"Error: {str(e)}"
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)