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/extract_lines.py
CHANGED
|
@@ -1,341 +1,317 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import traceback
|
|
3
3
|
|
|
4
|
-
from .
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
4
|
+
from aider.tools.utils.base_tool import BaseTool
|
|
5
|
+
from aider.tools.utils.helpers import generate_unified_diff_snippet
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Tool(BaseTool):
|
|
9
|
+
NORM_NAME = "extractlines"
|
|
10
|
+
SCHEMA = {
|
|
11
|
+
"type": "function",
|
|
12
|
+
"function": {
|
|
13
|
+
"name": "ExtractLines",
|
|
14
|
+
"description": "Extract lines from a source file and append them to a target file.",
|
|
15
|
+
"parameters": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"source_file_path": {"type": "string"},
|
|
19
|
+
"target_file_path": {"type": "string"},
|
|
20
|
+
"start_pattern": {"type": "string"},
|
|
21
|
+
"end_pattern": {"type": "string"},
|
|
22
|
+
"line_count": {"type": "integer"},
|
|
23
|
+
"near_context": {"type": "string"},
|
|
24
|
+
"occurrence": {"type": "integer", "default": 1},
|
|
25
|
+
"dry_run": {"type": "boolean", "default": False},
|
|
26
|
+
},
|
|
27
|
+
"required": ["source_file_path", "target_file_path", "start_pattern"],
|
|
22
28
|
},
|
|
23
|
-
"required": ["source_file_path", "target_file_path", "start_pattern"],
|
|
24
29
|
},
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def execute(
|
|
34
|
+
cls,
|
|
35
|
+
coder,
|
|
36
|
+
source_file_path,
|
|
37
|
+
target_file_path,
|
|
38
|
+
start_pattern,
|
|
39
|
+
end_pattern=None,
|
|
40
|
+
line_count=None,
|
|
41
|
+
near_context=None,
|
|
42
|
+
occurrence=1,
|
|
43
|
+
dry_run=False,
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
Extract a range of lines from a source file and move them to a target file.
|
|
47
|
+
|
|
48
|
+
Parameters:
|
|
49
|
+
- coder: The Coder instance
|
|
50
|
+
- source_file_path: Path to the file to extract lines from
|
|
51
|
+
- target_file_path: Path to the file to append extracted lines to (will be created if needed)
|
|
52
|
+
- start_pattern: Pattern marking the start of the block to extract
|
|
53
|
+
- end_pattern: Optional pattern marking the end of the block
|
|
54
|
+
- line_count: Optional number of lines to extract (alternative to end_pattern)
|
|
55
|
+
- near_context: Optional text nearby to help locate the correct instance of the start_pattern
|
|
56
|
+
- occurrence: Which occurrence of the start_pattern to use (1-based index, or -1 for last)
|
|
57
|
+
- dry_run: If True, simulate the change without modifying files
|
|
58
|
+
|
|
59
|
+
Returns a result message.
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
# --- Validate Source File ---
|
|
63
|
+
abs_source_path = coder.abs_root_path(source_file_path)
|
|
64
|
+
rel_source_path = coder.get_rel_fname(abs_source_path)
|
|
65
|
+
|
|
66
|
+
if not os.path.isfile(abs_source_path):
|
|
67
|
+
coder.io.tool_error(f"Source file '{source_file_path}' not found")
|
|
68
|
+
return "Error: Source file not found"
|
|
69
|
+
|
|
70
|
+
if abs_source_path not in coder.abs_fnames:
|
|
71
|
+
if abs_source_path in coder.abs_read_only_fnames:
|
|
72
|
+
coder.io.tool_error(
|
|
73
|
+
f"Source file '{source_file_path}' is read-only. Use MakeEditable first."
|
|
74
|
+
)
|
|
75
|
+
return "Error: Source file is read-only. Use MakeEditable first."
|
|
76
|
+
else:
|
|
77
|
+
coder.io.tool_error(f"Source file '{source_file_path}' not in context")
|
|
78
|
+
return "Error: Source file not in context"
|
|
79
|
+
|
|
80
|
+
# --- Validate Target File ---
|
|
81
|
+
abs_target_path = coder.abs_root_path(target_file_path)
|
|
82
|
+
rel_target_path = coder.get_rel_fname(abs_target_path)
|
|
83
|
+
target_exists = os.path.isfile(abs_target_path)
|
|
84
|
+
target_is_editable = abs_target_path in coder.abs_fnames
|
|
85
|
+
target_is_readonly = abs_target_path in coder.abs_read_only_fnames
|
|
86
|
+
|
|
87
|
+
if target_exists and not target_is_editable:
|
|
88
|
+
if target_is_readonly:
|
|
89
|
+
coder.io.tool_error(
|
|
90
|
+
f"Target file '{target_file_path}' exists but is read-only. Use"
|
|
91
|
+
" MakeEditable first."
|
|
92
|
+
)
|
|
93
|
+
return "Error: Target file exists but is read-only. Use MakeEditable first."
|
|
94
|
+
else:
|
|
95
|
+
# This case shouldn't happen if file exists, but handle defensively
|
|
96
|
+
coder.io.tool_error(
|
|
97
|
+
f"Target file '{target_file_path}' exists but is not in context. Add it"
|
|
98
|
+
" first."
|
|
99
|
+
)
|
|
100
|
+
return "Error: Target file exists but is not in context."
|
|
101
|
+
|
|
102
|
+
# --- Read Source Content ---
|
|
103
|
+
source_content = coder.io.read_text(abs_source_path)
|
|
104
|
+
if source_content is None:
|
|
94
105
|
coder.io.tool_error(
|
|
95
|
-
f"
|
|
106
|
+
f"Could not read source file '{source_file_path}' before ExtractLines"
|
|
107
|
+
" operation."
|
|
96
108
|
)
|
|
97
|
-
return "Error:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
context_window_start = max(0, i - 5)
|
|
120
|
-
context_window_end = min(len(source_lines), i + 6)
|
|
121
|
-
context_block = "\n".join(source_lines[context_window_start:context_window_end])
|
|
122
|
-
if near_context in context_block:
|
|
109
|
+
return f"Error: Could not read source file '{source_file_path}'"
|
|
110
|
+
|
|
111
|
+
# --- Find Extraction Range ---
|
|
112
|
+
if end_pattern and line_count:
|
|
113
|
+
coder.io.tool_error("Cannot specify both end_pattern and line_count")
|
|
114
|
+
return "Error: Cannot specify both end_pattern and line_count"
|
|
115
|
+
|
|
116
|
+
source_lines = source_content.splitlines()
|
|
117
|
+
original_source_content = source_content
|
|
118
|
+
|
|
119
|
+
start_pattern_line_indices = []
|
|
120
|
+
for i, line in enumerate(source_lines):
|
|
121
|
+
if start_pattern in line:
|
|
122
|
+
if near_context:
|
|
123
|
+
context_window_start = max(0, i - 5)
|
|
124
|
+
context_window_end = min(len(source_lines), i + 6)
|
|
125
|
+
context_block = "\n".join(
|
|
126
|
+
source_lines[context_window_start:context_window_end]
|
|
127
|
+
)
|
|
128
|
+
if near_context in context_block:
|
|
129
|
+
start_pattern_line_indices.append(i)
|
|
130
|
+
else:
|
|
123
131
|
start_pattern_line_indices.append(i)
|
|
124
|
-
else:
|
|
125
|
-
start_pattern_line_indices.append(i)
|
|
126
|
-
|
|
127
|
-
if not start_pattern_line_indices:
|
|
128
|
-
err_msg = f"Start pattern '{start_pattern}' not found"
|
|
129
|
-
if near_context:
|
|
130
|
-
err_msg += f" near context '{near_context}'"
|
|
131
|
-
err_msg += f" in source file '{source_file_path}'."
|
|
132
|
-
coder.io.tool_error(err_msg)
|
|
133
|
-
return f"Error: {err_msg}"
|
|
134
132
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
occurrence = int(occurrence)
|
|
138
|
-
if occurrence == -1:
|
|
139
|
-
target_idx = num_occurrences - 1
|
|
140
|
-
elif occurrence > 0 and occurrence <= num_occurrences:
|
|
141
|
-
target_idx = occurrence - 1
|
|
142
|
-
else:
|
|
143
|
-
err_msg = (
|
|
144
|
-
f"Occurrence number {occurrence} is out of range for start pattern"
|
|
145
|
-
f" '{start_pattern}'. Found {num_occurrences} occurrences"
|
|
146
|
-
)
|
|
133
|
+
if not start_pattern_line_indices:
|
|
134
|
+
err_msg = f"Start pattern '{start_pattern}' not found"
|
|
147
135
|
if near_context:
|
|
148
|
-
err_msg += f" near '{near_context}'"
|
|
149
|
-
err_msg += f" in '{source_file_path}'."
|
|
136
|
+
err_msg += f" near context '{near_context}'"
|
|
137
|
+
err_msg += f" in source file '{source_file_path}'."
|
|
150
138
|
coder.io.tool_error(err_msg)
|
|
151
139
|
return f"Error: {err_msg}"
|
|
152
|
-
except ValueError:
|
|
153
|
-
coder.io.tool_error(f"Invalid occurrence value: '{occurrence}'. Must be an integer.")
|
|
154
|
-
return f"Error: Invalid occurrence value '{occurrence}'"
|
|
155
140
|
|
|
156
|
-
|
|
157
|
-
occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""
|
|
158
|
-
|
|
159
|
-
end_line = -1
|
|
160
|
-
if end_pattern:
|
|
161
|
-
for i in range(start_line, len(source_lines)):
|
|
162
|
-
if end_pattern in source_lines[i]:
|
|
163
|
-
end_line = i
|
|
164
|
-
break
|
|
165
|
-
if end_line == -1:
|
|
166
|
-
err_msg = (
|
|
167
|
-
f"End pattern '{end_pattern}' not found after {occurrence_str}start pattern"
|
|
168
|
-
f" '{start_pattern}' (line {start_line + 1}) in '{source_file_path}'."
|
|
169
|
-
)
|
|
170
|
-
coder.io.tool_error(err_msg)
|
|
171
|
-
return f"Error: {err_msg}"
|
|
172
|
-
elif line_count:
|
|
141
|
+
num_occurrences = len(start_pattern_line_indices)
|
|
173
142
|
try:
|
|
174
|
-
|
|
175
|
-
if
|
|
176
|
-
|
|
177
|
-
|
|
143
|
+
occurrence = int(occurrence)
|
|
144
|
+
if occurrence == -1:
|
|
145
|
+
target_idx = num_occurrences - 1
|
|
146
|
+
elif occurrence > 0 and occurrence <= num_occurrences:
|
|
147
|
+
target_idx = occurrence - 1
|
|
148
|
+
else:
|
|
149
|
+
err_msg = (
|
|
150
|
+
f"Occurrence number {occurrence} is out of range for start pattern"
|
|
151
|
+
f" '{start_pattern}'. Found {num_occurrences} occurrences"
|
|
152
|
+
)
|
|
153
|
+
if near_context:
|
|
154
|
+
err_msg += f" near '{near_context}'"
|
|
155
|
+
err_msg += f" in '{source_file_path}'."
|
|
156
|
+
coder.io.tool_error(err_msg)
|
|
157
|
+
return f"Error: {err_msg}"
|
|
178
158
|
except ValueError:
|
|
179
159
|
coder.io.tool_error(
|
|
180
|
-
f"Invalid
|
|
160
|
+
f"Invalid occurrence value: '{occurrence}'. Must be an integer."
|
|
181
161
|
)
|
|
182
|
-
return f"Error: Invalid
|
|
183
|
-
|
|
184
|
-
|
|
162
|
+
return f"Error: Invalid occurrence value '{occurrence}'"
|
|
163
|
+
|
|
164
|
+
start_line = start_pattern_line_indices[target_idx]
|
|
165
|
+
occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""
|
|
166
|
+
|
|
167
|
+
end_line = -1
|
|
168
|
+
if end_pattern:
|
|
169
|
+
for i in range(start_line, len(source_lines)):
|
|
170
|
+
if end_pattern in source_lines[i]:
|
|
171
|
+
end_line = i
|
|
172
|
+
break
|
|
173
|
+
if end_line == -1:
|
|
174
|
+
err_msg = (
|
|
175
|
+
f"End pattern '{end_pattern}' not found after {occurrence_str}start"
|
|
176
|
+
f" pattern '{start_pattern}' (line {start_line + 1}) in"
|
|
177
|
+
f" '{source_file_path}'."
|
|
178
|
+
)
|
|
179
|
+
coder.io.tool_error(err_msg)
|
|
180
|
+
return f"Error: {err_msg}"
|
|
181
|
+
elif line_count:
|
|
182
|
+
try:
|
|
183
|
+
line_count = int(line_count)
|
|
184
|
+
if line_count <= 0:
|
|
185
|
+
raise ValueError("Line count must be positive")
|
|
186
|
+
end_line = min(start_line + line_count - 1, len(source_lines) - 1)
|
|
187
|
+
except ValueError:
|
|
188
|
+
coder.io.tool_error(
|
|
189
|
+
f"Invalid line_count value: '{line_count}'. Must be a positive integer."
|
|
190
|
+
)
|
|
191
|
+
return f"Error: Invalid line_count value '{line_count}'"
|
|
192
|
+
else:
|
|
193
|
+
end_line = start_line # Extract just the start line if no end specified
|
|
194
|
+
|
|
195
|
+
# --- Prepare Content Changes ---
|
|
196
|
+
extracted_lines = source_lines[start_line : end_line + 1]
|
|
197
|
+
new_source_lines = source_lines[:start_line] + source_lines[end_line + 1 :]
|
|
198
|
+
new_source_content = "\n".join(new_source_lines)
|
|
199
|
+
|
|
200
|
+
target_content = ""
|
|
201
|
+
if target_exists:
|
|
202
|
+
target_content = coder.io.read_text(abs_target_path)
|
|
203
|
+
if target_content is None:
|
|
204
|
+
coder.io.tool_error(
|
|
205
|
+
f"Could not read existing target file '{target_file_path}'."
|
|
206
|
+
)
|
|
207
|
+
return f"Error: Could not read target file '{target_file_path}'"
|
|
208
|
+
original_target_content = target_content # For tracking
|
|
209
|
+
|
|
210
|
+
# Append extracted lines to target content, ensuring a newline if target wasn't empty
|
|
211
|
+
extracted_block = "\n".join(extracted_lines)
|
|
212
|
+
if target_content and not target_content.endswith("\n"):
|
|
213
|
+
target_content += "\n" # Add newline before appending if needed
|
|
214
|
+
new_target_content = target_content + extracted_block
|
|
215
|
+
|
|
216
|
+
# --- Generate Diffs ---
|
|
217
|
+
source_diff_snippet = generate_unified_diff_snippet(
|
|
218
|
+
original_source_content, new_source_content, rel_source_path
|
|
219
|
+
)
|
|
220
|
+
target_insertion_line = len(target_content.splitlines()) if target_content else 0
|
|
221
|
+
target_diff_snippet = generate_unified_diff_snippet(
|
|
222
|
+
original_target_content, new_target_content, rel_target_path
|
|
223
|
+
)
|
|
185
224
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
225
|
+
# --- Handle Dry Run ---
|
|
226
|
+
if dry_run:
|
|
227
|
+
num_extracted = end_line - start_line + 1
|
|
228
|
+
target_action = "append to" if target_exists else "create"
|
|
229
|
+
coder.io.tool_output(
|
|
230
|
+
f"Dry run: Would extract {num_extracted} lines (from {occurrence_str}start"
|
|
231
|
+
f" pattern '{start_pattern}') in {source_file_path} and {target_action}"
|
|
232
|
+
f" {target_file_path}"
|
|
233
|
+
)
|
|
234
|
+
# Provide more informative dry run response with diffs
|
|
235
|
+
return (
|
|
236
|
+
f"Dry run: Would extract {num_extracted} lines from {rel_source_path} and"
|
|
237
|
+
f" {target_action} {rel_target_path}.\nSource Diff"
|
|
238
|
+
f" (Deletion):\n{source_diff_snippet}\nTarget Diff"
|
|
239
|
+
f" (Insertion):\n{target_diff_snippet}"
|
|
240
|
+
)
|
|
190
241
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if target_content is None:
|
|
195
|
-
coder.io.tool_error(f"Could not read existing target file '{target_file_path}'.")
|
|
196
|
-
return f"Error: Could not read target file '{target_file_path}'"
|
|
197
|
-
original_target_content = target_content # For tracking
|
|
242
|
+
# --- Apply Changes (Not Dry Run) ---
|
|
243
|
+
coder.io.write_text(abs_source_path, new_source_content)
|
|
244
|
+
coder.io.write_text(abs_target_path, new_target_content)
|
|
198
245
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
246
|
+
# --- Track Changes ---
|
|
247
|
+
source_change_id = "TRACKING_FAILED"
|
|
248
|
+
target_change_id = "TRACKING_FAILED"
|
|
249
|
+
try:
|
|
250
|
+
source_metadata = {
|
|
251
|
+
"start_line": start_line + 1,
|
|
252
|
+
"end_line": end_line + 1,
|
|
253
|
+
"start_pattern": start_pattern,
|
|
254
|
+
"end_pattern": end_pattern,
|
|
255
|
+
"line_count": line_count,
|
|
256
|
+
"near_context": near_context,
|
|
257
|
+
"occurrence": occurrence,
|
|
258
|
+
"extracted_content": extracted_block,
|
|
259
|
+
"target_file": rel_target_path,
|
|
260
|
+
}
|
|
261
|
+
source_change_id = coder.change_tracker.track_change(
|
|
262
|
+
file_path=rel_source_path,
|
|
263
|
+
change_type="extractlines_source",
|
|
264
|
+
original_content=original_source_content,
|
|
265
|
+
new_content=new_source_content,
|
|
266
|
+
metadata=source_metadata,
|
|
267
|
+
)
|
|
268
|
+
except Exception as track_e:
|
|
269
|
+
coder.io.tool_error(f"Error tracking source change for ExtractLines: {track_e}")
|
|
204
270
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
271
|
+
try:
|
|
272
|
+
target_metadata = {
|
|
273
|
+
"insertion_line": target_insertion_line + 1,
|
|
274
|
+
"inserted_content": extracted_block,
|
|
275
|
+
"source_file": rel_source_path,
|
|
276
|
+
}
|
|
277
|
+
target_change_id = coder.change_tracker.track_change(
|
|
278
|
+
file_path=rel_target_path,
|
|
279
|
+
change_type="extractlines_target",
|
|
280
|
+
original_content=original_target_content,
|
|
281
|
+
new_content=new_target_content,
|
|
282
|
+
metadata=target_metadata,
|
|
283
|
+
)
|
|
284
|
+
except Exception as track_e:
|
|
285
|
+
coder.io.tool_error(f"Error tracking target change for ExtractLines: {track_e}")
|
|
286
|
+
|
|
287
|
+
# --- Update Context ---
|
|
288
|
+
coder.files_edited_by_tools.add(rel_source_path)
|
|
289
|
+
coder.files_edited_by_tools.add(rel_target_path)
|
|
290
|
+
|
|
291
|
+
if not target_exists:
|
|
292
|
+
# Add the newly created file to editable context
|
|
293
|
+
coder.abs_fnames.add(abs_target_path)
|
|
294
|
+
coder.io.tool_output(
|
|
295
|
+
f"✨ Created and added '{target_file_path}' to editable context."
|
|
296
|
+
)
|
|
213
297
|
|
|
214
|
-
|
|
215
|
-
if dry_run:
|
|
298
|
+
# --- Return Result ---
|
|
216
299
|
num_extracted = end_line - start_line + 1
|
|
217
|
-
target_action = "
|
|
300
|
+
target_action = "appended to" if target_exists else "created"
|
|
218
301
|
coder.io.tool_output(
|
|
219
|
-
f"
|
|
220
|
-
f"
|
|
302
|
+
f"✅ Extracted {num_extracted} lines from {rel_source_path} (change_id:"
|
|
303
|
+
f" {source_change_id}) and {target_action} {rel_target_path} (change_id:"
|
|
304
|
+
f" {target_change_id})"
|
|
221
305
|
)
|
|
222
|
-
# Provide more informative
|
|
306
|
+
# Provide more informative success response with change IDs and diffs
|
|
223
307
|
return (
|
|
224
|
-
f"
|
|
225
|
-
f" {target_action} {rel_target_path}.\nSource
|
|
226
|
-
f" (Deletion):\n{source_diff_snippet}\nTarget
|
|
308
|
+
f"Successfully extracted {num_extracted} lines from {rel_source_path} and"
|
|
309
|
+
f" {target_action} {rel_target_path}.\nSource Change ID:"
|
|
310
|
+
f" {source_change_id}\nSource Diff (Deletion):\n{source_diff_snippet}\nTarget"
|
|
311
|
+
f" Change ID: {target_change_id}\nTarget Diff"
|
|
227
312
|
f" (Insertion):\n{target_diff_snippet}"
|
|
228
313
|
)
|
|
229
314
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
# --- Track Changes ---
|
|
235
|
-
source_change_id = "TRACKING_FAILED"
|
|
236
|
-
target_change_id = "TRACKING_FAILED"
|
|
237
|
-
try:
|
|
238
|
-
source_metadata = {
|
|
239
|
-
"start_line": start_line + 1,
|
|
240
|
-
"end_line": end_line + 1,
|
|
241
|
-
"start_pattern": start_pattern,
|
|
242
|
-
"end_pattern": end_pattern,
|
|
243
|
-
"line_count": line_count,
|
|
244
|
-
"near_context": near_context,
|
|
245
|
-
"occurrence": occurrence,
|
|
246
|
-
"extracted_content": extracted_block,
|
|
247
|
-
"target_file": rel_target_path,
|
|
248
|
-
}
|
|
249
|
-
source_change_id = coder.change_tracker.track_change(
|
|
250
|
-
file_path=rel_source_path,
|
|
251
|
-
change_type="extractlines_source",
|
|
252
|
-
original_content=original_source_content,
|
|
253
|
-
new_content=new_source_content,
|
|
254
|
-
metadata=source_metadata,
|
|
255
|
-
)
|
|
256
|
-
except Exception as track_e:
|
|
257
|
-
coder.io.tool_error(f"Error tracking source change for ExtractLines: {track_e}")
|
|
258
|
-
|
|
259
|
-
try:
|
|
260
|
-
target_metadata = {
|
|
261
|
-
"insertion_line": target_insertion_line + 1,
|
|
262
|
-
"inserted_content": extracted_block,
|
|
263
|
-
"source_file": rel_source_path,
|
|
264
|
-
}
|
|
265
|
-
target_change_id = coder.change_tracker.track_change(
|
|
266
|
-
file_path=rel_target_path,
|
|
267
|
-
change_type="extractlines_target",
|
|
268
|
-
original_content=original_target_content,
|
|
269
|
-
new_content=new_target_content,
|
|
270
|
-
metadata=target_metadata,
|
|
271
|
-
)
|
|
272
|
-
except Exception as track_e:
|
|
273
|
-
coder.io.tool_error(f"Error tracking target change for ExtractLines: {track_e}")
|
|
274
|
-
|
|
275
|
-
# --- Update Context ---
|
|
276
|
-
coder.files_edited_by_tools.add(rel_source_path)
|
|
277
|
-
coder.files_edited_by_tools.add(rel_target_path)
|
|
278
|
-
|
|
279
|
-
if not target_exists:
|
|
280
|
-
# Add the newly created file to editable context
|
|
281
|
-
coder.abs_fnames.add(abs_target_path)
|
|
282
|
-
coder.io.tool_output(f"✨ Created and added '{target_file_path}' to editable context.")
|
|
283
|
-
|
|
284
|
-
# --- Return Result ---
|
|
285
|
-
num_extracted = end_line - start_line + 1
|
|
286
|
-
target_action = "appended to" if target_exists else "created"
|
|
287
|
-
coder.io.tool_output(
|
|
288
|
-
f"✅ Extracted {num_extracted} lines from {rel_source_path} (change_id:"
|
|
289
|
-
f" {source_change_id}) and {target_action} {rel_target_path} (change_id:"
|
|
290
|
-
f" {target_change_id})"
|
|
291
|
-
)
|
|
292
|
-
# Provide more informative success response with change IDs and diffs
|
|
293
|
-
return (
|
|
294
|
-
f"Successfully extracted {num_extracted} lines from {rel_source_path} and"
|
|
295
|
-
f" {target_action} {rel_target_path}.\nSource Change ID: {source_change_id}\nSource"
|
|
296
|
-
f" Diff (Deletion):\n{source_diff_snippet}\nTarget Change ID:"
|
|
297
|
-
f" {target_change_id}\nTarget Diff (Insertion):\n{target_diff_snippet}"
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
except Exception as e:
|
|
301
|
-
coder.io.tool_error(f"Error in ExtractLines: {str(e)}\n{traceback.format_exc()}")
|
|
302
|
-
return f"Error: {str(e)}"
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
def process_response(coder, params):
|
|
306
|
-
"""
|
|
307
|
-
Process the ExtractLines tool response.
|
|
308
|
-
|
|
309
|
-
Args:
|
|
310
|
-
coder: The Coder instance
|
|
311
|
-
params: Dictionary of parameters
|
|
312
|
-
|
|
313
|
-
Returns:
|
|
314
|
-
str: Result message
|
|
315
|
-
"""
|
|
316
|
-
source_file_path = params.get("source_file_path")
|
|
317
|
-
target_file_path = params.get("target_file_path")
|
|
318
|
-
start_pattern = params.get("start_pattern")
|
|
319
|
-
end_pattern = params.get("end_pattern")
|
|
320
|
-
line_count = params.get("line_count")
|
|
321
|
-
near_context = params.get("near_context")
|
|
322
|
-
occurrence = params.get("occurrence", 1)
|
|
323
|
-
dry_run = params.get("dry_run", False)
|
|
324
|
-
|
|
325
|
-
if source_file_path and target_file_path and start_pattern:
|
|
326
|
-
return _execute_extract_lines(
|
|
327
|
-
coder,
|
|
328
|
-
source_file_path,
|
|
329
|
-
target_file_path,
|
|
330
|
-
start_pattern,
|
|
331
|
-
end_pattern,
|
|
332
|
-
line_count,
|
|
333
|
-
near_context,
|
|
334
|
-
occurrence,
|
|
335
|
-
dry_run,
|
|
336
|
-
)
|
|
337
|
-
else:
|
|
338
|
-
return (
|
|
339
|
-
"Error: Missing required parameters for ExtractLines (source_file_path,"
|
|
340
|
-
" target_file_path, start_pattern)"
|
|
341
|
-
)
|
|
315
|
+
except Exception as e:
|
|
316
|
+
coder.io.tool_error(f"Error in ExtractLines: {str(e)}\n{traceback.format_exc()}")
|
|
317
|
+
return f"Error: {str(e)}"
|