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_text.py
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import difflib
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from aider.tools.utils.base_tool import BaseTool
|
|
5
|
+
from aider.tools.utils.helpers import (
|
|
2
6
|
ToolError,
|
|
3
7
|
apply_change,
|
|
4
8
|
format_tool_result,
|
|
@@ -6,182 +10,177 @@ from .tool_utils import (
|
|
|
6
10
|
handle_tool_error,
|
|
7
11
|
validate_file_for_edit,
|
|
8
12
|
)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
from aider.tools.utils.output import color_markers, tool_footer, tool_header
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Tool(BaseTool):
|
|
17
|
+
NORM_NAME = "replacetext"
|
|
18
|
+
SCHEMA = {
|
|
19
|
+
"type": "function",
|
|
20
|
+
"function": {
|
|
21
|
+
"name": "ReplaceText",
|
|
22
|
+
"description": "Replace text in a file.",
|
|
23
|
+
"parameters": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"file_path": {"type": "string"},
|
|
27
|
+
"find_text": {"type": "string"},
|
|
28
|
+
"replace_text": {"type": "string"},
|
|
29
|
+
"near_context": {"type": "string"},
|
|
30
|
+
"occurrence": {"type": "integer", "default": 1},
|
|
31
|
+
"change_id": {"type": "string"},
|
|
32
|
+
"dry_run": {"type": "boolean", "default": False},
|
|
33
|
+
},
|
|
34
|
+
"required": ["file_path", "find_text", "replace_text"],
|
|
25
35
|
},
|
|
26
|
-
"required": ["file_path", "find_text", "replace_text"],
|
|
27
36
|
},
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Uses utility functions for validation, finding occurrences, and applying changes.
|
|
48
|
-
"""
|
|
49
|
-
tool_name = "ReplaceText"
|
|
50
|
-
try:
|
|
51
|
-
# 1. Validate file and get content
|
|
52
|
-
abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
|
|
53
|
-
|
|
54
|
-
# 2. Find occurrences using helper function
|
|
55
|
-
# Note: _find_occurrences is currently on the Coder class, not in tool_utils
|
|
56
|
-
occurrences = coder._find_occurrences(original_content, find_text, near_context)
|
|
57
|
-
|
|
58
|
-
if not occurrences:
|
|
59
|
-
err_msg = f"Text '{find_text}' not found"
|
|
60
|
-
if near_context:
|
|
61
|
-
err_msg += f" near context '{near_context}'"
|
|
62
|
-
err_msg += f" in file '{file_path}'."
|
|
63
|
-
raise ToolError(err_msg)
|
|
64
|
-
|
|
65
|
-
# 3. Select the occurrence index
|
|
66
|
-
num_occurrences = len(occurrences)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def execute(
|
|
41
|
+
cls,
|
|
42
|
+
coder,
|
|
43
|
+
file_path,
|
|
44
|
+
find_text,
|
|
45
|
+
replace_text,
|
|
46
|
+
near_context=None,
|
|
47
|
+
occurrence=1,
|
|
48
|
+
change_id=None,
|
|
49
|
+
dry_run=False,
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Replace specific text with new text, optionally using nearby context for disambiguation.
|
|
53
|
+
Uses utility functions for validation, finding occurrences, and applying changes.
|
|
54
|
+
"""
|
|
55
|
+
tool_name = "ReplaceText"
|
|
67
56
|
try:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
err_msg =
|
|
77
|
-
f"Occurrence number {occurrence} is out of range. Found"
|
|
78
|
-
f" {num_occurrences} occurrences of '{find_text}'"
|
|
79
|
-
)
|
|
57
|
+
# 1. Validate file and get content
|
|
58
|
+
abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
|
|
59
|
+
|
|
60
|
+
# 2. Find occurrences using helper function
|
|
61
|
+
# Note: _find_occurrences is currently on the Coder class, not in tool_utils
|
|
62
|
+
occurrences = coder._find_occurrences(original_content, find_text, near_context)
|
|
63
|
+
|
|
64
|
+
if not occurrences:
|
|
65
|
+
err_msg = f"Text '{find_text}' not found"
|
|
80
66
|
if near_context:
|
|
81
|
-
err_msg += f" near '{near_context}'"
|
|
82
|
-
err_msg += f" in '{file_path}'."
|
|
67
|
+
err_msg += f" near context '{near_context}'"
|
|
68
|
+
err_msg += f" in file '{file_path}'."
|
|
83
69
|
raise ToolError(err_msg)
|
|
84
|
-
except ValueError:
|
|
85
|
-
raise ToolError(f"Invalid occurrence value: '{occurrence}'. Must be an integer.")
|
|
86
70
|
|
|
87
|
-
|
|
71
|
+
# 3. Select the occurrence index
|
|
72
|
+
num_occurrences = len(occurrences)
|
|
73
|
+
try:
|
|
74
|
+
occurrence = int(occurrence)
|
|
75
|
+
if occurrence == -1:
|
|
76
|
+
if num_occurrences == 0:
|
|
77
|
+
raise ToolError(
|
|
78
|
+
f"Text '{find_text}' not found, cannot select last occurrence."
|
|
79
|
+
)
|
|
80
|
+
target_idx = num_occurrences - 1
|
|
81
|
+
elif 1 <= occurrence <= num_occurrences:
|
|
82
|
+
target_idx = occurrence - 1 # Convert 1-based to 0-based
|
|
83
|
+
else:
|
|
84
|
+
err_msg = (
|
|
85
|
+
f"Occurrence number {occurrence} is out of range. Found"
|
|
86
|
+
f" {num_occurrences} occurrences of '{find_text}'"
|
|
87
|
+
)
|
|
88
|
+
if near_context:
|
|
89
|
+
err_msg += f" near '{near_context}'"
|
|
90
|
+
err_msg += f" in '{file_path}'."
|
|
91
|
+
raise ToolError(err_msg)
|
|
92
|
+
except ValueError:
|
|
93
|
+
raise ToolError(f"Invalid occurrence value: '{occurrence}'. Must be an integer.")
|
|
94
|
+
|
|
95
|
+
start_index = occurrences[target_idx]
|
|
96
|
+
|
|
97
|
+
# 4. Perform the replacement
|
|
98
|
+
new_content = (
|
|
99
|
+
original_content[:start_index]
|
|
100
|
+
+ replace_text
|
|
101
|
+
+ original_content[start_index + len(find_text) :]
|
|
102
|
+
)
|
|
88
103
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
+ replace_text
|
|
93
|
-
+ original_content[start_index + len(find_text) :]
|
|
94
|
-
)
|
|
104
|
+
if original_content == new_content:
|
|
105
|
+
coder.io.tool_warning("No changes made: replacement text is identical to original")
|
|
106
|
+
return "Warning: No changes made (replacement identical to original)"
|
|
95
107
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
108
|
+
# 5. Generate diff for feedback
|
|
109
|
+
# Note: _generate_diff_snippet is currently on the Coder class
|
|
110
|
+
diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
|
|
111
|
+
occurrence_str = f"occurrence {occurrence}" if num_occurrences > 1 else "text"
|
|
99
112
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
113
|
+
# 6. Handle dry run
|
|
114
|
+
if dry_run:
|
|
115
|
+
dry_run_message = (
|
|
116
|
+
f"Dry run: Would replace {occurrence_str} of '{find_text}' in {file_path}."
|
|
117
|
+
)
|
|
118
|
+
return format_tool_result(
|
|
119
|
+
coder,
|
|
120
|
+
tool_name,
|
|
121
|
+
"",
|
|
122
|
+
dry_run=True,
|
|
123
|
+
dry_run_message=dry_run_message,
|
|
124
|
+
diff_snippet=diff_snippet,
|
|
125
|
+
)
|
|
104
126
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
127
|
+
# 7. Apply Change (Not dry run)
|
|
128
|
+
metadata = {
|
|
129
|
+
"start_index": start_index,
|
|
130
|
+
"find_text": find_text,
|
|
131
|
+
"replace_text": replace_text,
|
|
132
|
+
"near_context": near_context,
|
|
133
|
+
"occurrence": occurrence,
|
|
134
|
+
}
|
|
135
|
+
final_change_id = apply_change(
|
|
136
|
+
coder,
|
|
137
|
+
abs_path,
|
|
138
|
+
rel_path,
|
|
139
|
+
original_content,
|
|
140
|
+
new_content,
|
|
141
|
+
"replacetext",
|
|
142
|
+
metadata,
|
|
143
|
+
change_id,
|
|
109
144
|
)
|
|
145
|
+
|
|
146
|
+
coder.files_edited_by_tools.add(rel_path)
|
|
147
|
+
# 8. Format and return result
|
|
148
|
+
success_message = f"Replaced {occurrence_str} in {file_path}"
|
|
110
149
|
return format_tool_result(
|
|
111
150
|
coder,
|
|
112
151
|
tool_name,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
dry_run_message=dry_run_message,
|
|
152
|
+
success_message,
|
|
153
|
+
change_id=final_change_id,
|
|
116
154
|
diff_snippet=diff_snippet,
|
|
117
155
|
)
|
|
118
156
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"
|
|
134
|
-
|
|
135
|
-
change_id,
|
|
157
|
+
except ToolError as e:
|
|
158
|
+
# Handle errors raised by utility functions or explicitly raised here
|
|
159
|
+
return handle_tool_error(coder, tool_name, e, add_traceback=False)
|
|
160
|
+
except Exception as e:
|
|
161
|
+
# Handle unexpected errors
|
|
162
|
+
return handle_tool_error(coder, tool_name, e)
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def format_output(cls, coder, mcp_server, tool_response):
|
|
166
|
+
color_start, color_end = color_markers(coder)
|
|
167
|
+
params = json.loads(tool_response.function.arguments)
|
|
168
|
+
diff = difflib.unified_diff(
|
|
169
|
+
params["find_text"].splitlines(),
|
|
170
|
+
params["replace_text"].splitlines(),
|
|
171
|
+
lineterm="",
|
|
172
|
+
n=float("inf"),
|
|
136
173
|
)
|
|
137
174
|
|
|
138
|
-
coder
|
|
139
|
-
# 8. Format and return result
|
|
140
|
-
success_message = f"Replaced {occurrence_str} in {file_path}"
|
|
141
|
-
return format_tool_result(
|
|
142
|
-
coder, tool_name, success_message, change_id=final_change_id, diff_snippet=diff_snippet
|
|
143
|
-
)
|
|
175
|
+
tool_header(coder=coder, mcp_server=mcp_server, tool_response=tool_response)
|
|
144
176
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
Process the ReplaceText tool response.
|
|
156
|
-
|
|
157
|
-
Args:
|
|
158
|
-
coder: The Coder instance
|
|
159
|
-
params: Dictionary of parameters
|
|
160
|
-
|
|
161
|
-
Returns:
|
|
162
|
-
str: Result message
|
|
163
|
-
"""
|
|
164
|
-
file_path = params.get("file_path")
|
|
165
|
-
find_text = params.get("find_text")
|
|
166
|
-
replace_text = params.get("replace_text")
|
|
167
|
-
near_context = params.get("near_context")
|
|
168
|
-
occurrence = params.get("occurrence", 1)
|
|
169
|
-
change_id = params.get("change_id")
|
|
170
|
-
dry_run = params.get("dry_run", False)
|
|
171
|
-
|
|
172
|
-
if file_path is not None and find_text is not None and replace_text is not None:
|
|
173
|
-
return _execute_replace_text(
|
|
174
|
-
coder,
|
|
175
|
-
file_path,
|
|
176
|
-
find_text,
|
|
177
|
-
replace_text,
|
|
178
|
-
near_context,
|
|
179
|
-
occurrence,
|
|
180
|
-
change_id,
|
|
181
|
-
dry_run,
|
|
182
|
-
)
|
|
183
|
-
else:
|
|
184
|
-
return (
|
|
185
|
-
"Error: Missing required parameters for ReplaceText (file_path,"
|
|
186
|
-
" find_text, replace_text)"
|
|
187
|
-
)
|
|
177
|
+
coder.io.tool_output("")
|
|
178
|
+
coder.io.tool_output(f"{color_start}file_path:{color_end}")
|
|
179
|
+
coder.io.tool_output(params["file_path"])
|
|
180
|
+
coder.io.tool_output("")
|
|
181
|
+
|
|
182
|
+
coder.io.tool_output(f"{color_start}diff:{color_end}")
|
|
183
|
+
coder.io.tool_output("\n".join(list(diff)[2:]))
|
|
184
|
+
coder.io.tool_output("")
|
|
185
|
+
|
|
186
|
+
tool_footer(coder=coder, tool_response=tool_response)
|
|
@@ -1,147 +1,121 @@
|
|
|
1
1
|
import os
|
|
2
2
|
|
|
3
|
-
from .
|
|
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, handle_tool_error, resolve_paths
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Tool(BaseTool):
|
|
8
|
+
NORM_NAME = "shownumberedcontext"
|
|
9
|
+
SCHEMA = {
|
|
10
|
+
"type": "function",
|
|
11
|
+
"function": {
|
|
12
|
+
"name": "ShowNumberedContext",
|
|
13
|
+
"description": "Show numbered lines of context around a pattern or line number.",
|
|
14
|
+
"parameters": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"file_path": {"type": "string"},
|
|
18
|
+
"pattern": {"type": "string"},
|
|
19
|
+
"line_number": {"type": "integer"},
|
|
20
|
+
"context_lines": {"type": "integer", "default": 3},
|
|
21
|
+
},
|
|
22
|
+
"required": ["file_path"],
|
|
17
23
|
},
|
|
18
|
-
"required": ["file_path"],
|
|
19
24
|
},
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def execute(cls, coder, file_path, pattern=None, line_number=None, context_lines=3):
|
|
29
|
+
"""
|
|
30
|
+
Displays numbered lines from file_path centered around a target location
|
|
31
|
+
(pattern or line_number), without adding the file to context.
|
|
32
|
+
Uses utility functions for path resolution and error handling.
|
|
33
|
+
"""
|
|
34
|
+
tool_name = "ShowNumberedContext"
|
|
35
|
+
try:
|
|
36
|
+
# 1. Validate arguments
|
|
37
|
+
if not (pattern is None) ^ (line_number is None):
|
|
38
|
+
raise ToolError("Provide exactly one of 'pattern' or 'line_number'.")
|
|
39
|
+
|
|
40
|
+
# 2. Resolve path
|
|
41
|
+
abs_path, rel_path = resolve_paths(coder, file_path)
|
|
42
|
+
if not os.path.exists(abs_path):
|
|
43
|
+
# Check existence after resolving, as resolve_paths doesn't guarantee existence
|
|
44
|
+
raise ToolError(f"File not found: {file_path}")
|
|
45
|
+
|
|
46
|
+
# 3. Read file content
|
|
47
|
+
content = coder.io.read_text(abs_path)
|
|
48
|
+
if content is None:
|
|
49
|
+
raise ToolError(f"Could not read file: {file_path}")
|
|
50
|
+
lines = content.splitlines()
|
|
51
|
+
num_lines = len(lines)
|
|
52
|
+
|
|
53
|
+
# 4. Determine center line index
|
|
54
|
+
center_line_idx = -1
|
|
55
|
+
found_by = ""
|
|
56
|
+
|
|
57
|
+
if line_number is not None:
|
|
58
|
+
try:
|
|
59
|
+
line_number_int = int(line_number)
|
|
60
|
+
if 1 <= line_number_int <= num_lines:
|
|
61
|
+
center_line_idx = line_number_int - 1 # Convert to 0-based index
|
|
62
|
+
found_by = f"line {line_number_int}"
|
|
63
|
+
else:
|
|
64
|
+
raise ToolError(
|
|
65
|
+
f"Line number {line_number_int} is out of range (1-{num_lines}) for"
|
|
66
|
+
f" {file_path}."
|
|
67
|
+
)
|
|
68
|
+
except ValueError:
|
|
69
|
+
raise ToolError(f"Invalid line number '{line_number}'. Must be an integer.")
|
|
70
|
+
|
|
71
|
+
elif pattern is not None:
|
|
72
|
+
# TODO: Update this section for multiline pattern support later
|
|
73
|
+
first_match_line_idx = -1
|
|
74
|
+
for i, line in enumerate(lines):
|
|
75
|
+
if pattern in line:
|
|
76
|
+
first_match_line_idx = i
|
|
77
|
+
break
|
|
78
|
+
|
|
79
|
+
if first_match_line_idx != -1:
|
|
80
|
+
center_line_idx = first_match_line_idx
|
|
81
|
+
found_by = f"pattern '{pattern}' on line {center_line_idx + 1}"
|
|
64
82
|
else:
|
|
65
|
-
raise ToolError(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
raise ToolError(f"Pattern '{pattern}' not found in {file_path}.")
|
|
84
|
+
|
|
85
|
+
if center_line_idx == -1:
|
|
86
|
+
# Should not happen if logic above is correct, but as a safeguard
|
|
87
|
+
raise ToolError("Internal error: Could not determine center line.")
|
|
88
|
+
|
|
89
|
+
# 5. Calculate context window
|
|
90
|
+
try:
|
|
91
|
+
context_lines_int = int(context_lines)
|
|
92
|
+
if context_lines_int < 0:
|
|
93
|
+
raise ValueError("Context lines must be non-negative")
|
|
69
94
|
except ValueError:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
coder
|
|
97
|
-
f"Invalid context_lines value '{context_lines}', using default 3."
|
|
98
|
-
)
|
|
99
|
-
context_lines_int = 3
|
|
100
|
-
|
|
101
|
-
start_line_idx = max(0, center_line_idx - context_lines_int)
|
|
102
|
-
end_line_idx = min(num_lines - 1, center_line_idx + context_lines_int)
|
|
103
|
-
|
|
104
|
-
# 6. Format output
|
|
105
|
-
# Use rel_path for user-facing messages
|
|
106
|
-
output_lines = [f"Displaying context around {found_by} in {rel_path}:"]
|
|
107
|
-
max_line_num_width = len(str(end_line_idx + 1)) # Width for padding
|
|
108
|
-
|
|
109
|
-
for i in range(start_line_idx, end_line_idx + 1):
|
|
110
|
-
line_num_str = str(i + 1).rjust(max_line_num_width)
|
|
111
|
-
output_lines.append(f"{line_num_str} | {lines[i]}")
|
|
112
|
-
|
|
113
|
-
# Log success and return the formatted context directly
|
|
114
|
-
coder.io.tool_output(f"Successfully retrieved context for {rel_path}")
|
|
115
|
-
return "\n".join(output_lines)
|
|
116
|
-
|
|
117
|
-
except ToolError as e:
|
|
118
|
-
# Handle expected errors raised by utility functions or validation
|
|
119
|
-
return handle_tool_error(coder, tool_name, e, add_traceback=False)
|
|
120
|
-
except Exception as e:
|
|
121
|
-
# Handle unexpected errors during processing
|
|
122
|
-
return handle_tool_error(coder, tool_name, e)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def process_response(coder, params):
|
|
126
|
-
"""
|
|
127
|
-
Process the ShowNumberedContext tool response.
|
|
128
|
-
|
|
129
|
-
Args:
|
|
130
|
-
coder: The Coder instance
|
|
131
|
-
params: Dictionary of parameters
|
|
132
|
-
|
|
133
|
-
Returns:
|
|
134
|
-
str: Result message
|
|
135
|
-
"""
|
|
136
|
-
file_path = params.get("file_path")
|
|
137
|
-
pattern = params.get("pattern")
|
|
138
|
-
line_number = params.get("line_number")
|
|
139
|
-
context_lines = params.get("context_lines", 3)
|
|
140
|
-
|
|
141
|
-
if file_path is not None and (pattern is not None or line_number is not None):
|
|
142
|
-
return execute_show_numbered_context(coder, file_path, pattern, line_number, context_lines)
|
|
143
|
-
else:
|
|
144
|
-
return (
|
|
145
|
-
"Error: Missing required parameters for ViewNumberedContext (file_path"
|
|
146
|
-
" and either pattern or line_number)"
|
|
147
|
-
)
|
|
95
|
+
coder.io.tool_warning(
|
|
96
|
+
f"Invalid context_lines value '{context_lines}', using default 3."
|
|
97
|
+
)
|
|
98
|
+
context_lines_int = 3
|
|
99
|
+
|
|
100
|
+
start_line_idx = max(0, center_line_idx - context_lines_int)
|
|
101
|
+
end_line_idx = min(num_lines - 1, center_line_idx + context_lines_int)
|
|
102
|
+
|
|
103
|
+
# 6. Format output
|
|
104
|
+
# Use rel_path for user-facing messages
|
|
105
|
+
output_lines = [f"Displaying context around {found_by} in {rel_path}:"]
|
|
106
|
+
max_line_num_width = len(str(end_line_idx + 1)) # Width for padding
|
|
107
|
+
|
|
108
|
+
for i in range(start_line_idx, end_line_idx + 1):
|
|
109
|
+
line_num_str = str(i + 1).rjust(max_line_num_width)
|
|
110
|
+
output_lines.append(f"{line_num_str} | {lines[i]}")
|
|
111
|
+
|
|
112
|
+
# Log success and return the formatted context directly
|
|
113
|
+
coder.io.tool_output(f"Successfully retrieved context for {rel_path}")
|
|
114
|
+
return "\n".join(output_lines)
|
|
115
|
+
|
|
116
|
+
except ToolError as e:
|
|
117
|
+
# Handle expected errors raised by utility functions or validation
|
|
118
|
+
return handle_tool_error(coder, tool_name, e, add_traceback=False)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
# Handle unexpected errors during processing
|
|
121
|
+
return handle_tool_error(coder, tool_name, e)
|