aider-ce 0.87.2.dev9__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.
Potentially problematic release.
This version of aider-ce might be problematic. Click here for more details.
- aider/__init__.py +20 -0
- aider/__main__.py +4 -0
- aider/_version.py +34 -0
- aider/analytics.py +258 -0
- aider/args.py +1014 -0
- aider/args_formatter.py +228 -0
- aider/change_tracker.py +133 -0
- aider/coders/__init__.py +36 -0
- aider/coders/architect_coder.py +48 -0
- aider/coders/architect_prompts.py +40 -0
- aider/coders/ask_coder.py +9 -0
- aider/coders/ask_prompts.py +35 -0
- aider/coders/base_coder.py +3013 -0
- aider/coders/base_prompts.py +87 -0
- aider/coders/chat_chunks.py +64 -0
- aider/coders/context_coder.py +53 -0
- aider/coders/context_prompts.py +75 -0
- aider/coders/editblock_coder.py +657 -0
- aider/coders/editblock_fenced_coder.py +10 -0
- aider/coders/editblock_fenced_prompts.py +143 -0
- aider/coders/editblock_func_coder.py +141 -0
- aider/coders/editblock_func_prompts.py +27 -0
- aider/coders/editblock_prompts.py +177 -0
- aider/coders/editor_diff_fenced_coder.py +9 -0
- aider/coders/editor_diff_fenced_prompts.py +11 -0
- aider/coders/editor_editblock_coder.py +9 -0
- aider/coders/editor_editblock_prompts.py +21 -0
- aider/coders/editor_whole_coder.py +9 -0
- aider/coders/editor_whole_prompts.py +12 -0
- aider/coders/help_coder.py +16 -0
- aider/coders/help_prompts.py +46 -0
- aider/coders/navigator_coder.py +2711 -0
- aider/coders/navigator_legacy_prompts.py +338 -0
- aider/coders/navigator_prompts.py +530 -0
- aider/coders/patch_coder.py +706 -0
- aider/coders/patch_prompts.py +161 -0
- aider/coders/search_replace.py +757 -0
- aider/coders/shell.py +37 -0
- aider/coders/single_wholefile_func_coder.py +102 -0
- aider/coders/single_wholefile_func_prompts.py +27 -0
- aider/coders/udiff_coder.py +429 -0
- aider/coders/udiff_prompts.py +117 -0
- aider/coders/udiff_simple.py +14 -0
- aider/coders/udiff_simple_prompts.py +25 -0
- aider/coders/wholefile_coder.py +144 -0
- aider/coders/wholefile_func_coder.py +134 -0
- aider/coders/wholefile_func_prompts.py +27 -0
- aider/coders/wholefile_prompts.py +70 -0
- aider/commands.py +1946 -0
- aider/copypaste.py +72 -0
- aider/deprecated.py +126 -0
- aider/diffs.py +128 -0
- aider/dump.py +29 -0
- aider/editor.py +147 -0
- aider/exceptions.py +107 -0
- aider/format_settings.py +26 -0
- aider/gui.py +545 -0
- aider/help.py +163 -0
- aider/help_pats.py +19 -0
- aider/history.py +178 -0
- aider/io.py +1257 -0
- aider/linter.py +304 -0
- aider/llm.py +47 -0
- aider/main.py +1297 -0
- aider/mcp/__init__.py +94 -0
- aider/mcp/server.py +119 -0
- aider/mdstream.py +243 -0
- aider/models.py +1344 -0
- aider/onboarding.py +428 -0
- aider/openrouter.py +129 -0
- aider/prompts.py +56 -0
- aider/queries/tree-sitter-language-pack/README.md +7 -0
- aider/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/c-tags.scm +9 -0
- aider/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
- aider/queries/tree-sitter-language-pack/clojure-tags.scm +7 -0
- aider/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
- aider/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
- aider/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
- aider/queries/tree-sitter-language-pack/d-tags.scm +26 -0
- aider/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
- aider/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
- aider/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
- aider/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
- aider/queries/tree-sitter-language-pack/go-tags.scm +42 -0
- aider/queries/tree-sitter-language-pack/java-tags.scm +20 -0
- aider/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
- aider/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
- aider/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
- aider/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
- aider/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
- aider/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
- aider/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/python-tags.scm +14 -0
- aider/queries/tree-sitter-language-pack/r-tags.scm +21 -0
- aider/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
- aider/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
- aider/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
- aider/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
- aider/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
- aider/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
- aider/queries/tree-sitter-languages/README.md +23 -0
- aider/queries/tree-sitter-languages/c-tags.scm +9 -0
- aider/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
- aider/queries/tree-sitter-languages/cpp-tags.scm +15 -0
- aider/queries/tree-sitter-languages/dart-tags.scm +91 -0
- aider/queries/tree-sitter-languages/elisp-tags.scm +8 -0
- aider/queries/tree-sitter-languages/elixir-tags.scm +54 -0
- aider/queries/tree-sitter-languages/elm-tags.scm +19 -0
- aider/queries/tree-sitter-languages/go-tags.scm +30 -0
- aider/queries/tree-sitter-languages/hcl-tags.scm +77 -0
- aider/queries/tree-sitter-languages/java-tags.scm +20 -0
- aider/queries/tree-sitter-languages/javascript-tags.scm +88 -0
- aider/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
- aider/queries/tree-sitter-languages/matlab-tags.scm +10 -0
- aider/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
- aider/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
- aider/queries/tree-sitter-languages/php-tags.scm +26 -0
- aider/queries/tree-sitter-languages/python-tags.scm +12 -0
- aider/queries/tree-sitter-languages/ql-tags.scm +26 -0
- aider/queries/tree-sitter-languages/ruby-tags.scm +64 -0
- aider/queries/tree-sitter-languages/rust-tags.scm +60 -0
- aider/queries/tree-sitter-languages/scala-tags.scm +65 -0
- aider/queries/tree-sitter-languages/typescript-tags.scm +41 -0
- aider/reasoning_tags.py +82 -0
- aider/repo.py +621 -0
- aider/repomap.py +988 -0
- aider/report.py +200 -0
- aider/resources/__init__.py +3 -0
- aider/resources/model-metadata.json +699 -0
- aider/resources/model-settings.yml +2046 -0
- aider/run_cmd.py +132 -0
- aider/scrape.py +284 -0
- aider/sendchat.py +61 -0
- aider/special.py +203 -0
- aider/tools/__init__.py +26 -0
- aider/tools/command.py +58 -0
- aider/tools/command_interactive.py +53 -0
- aider/tools/delete_block.py +120 -0
- aider/tools/delete_line.py +112 -0
- aider/tools/delete_lines.py +137 -0
- aider/tools/extract_lines.py +276 -0
- aider/tools/grep.py +171 -0
- aider/tools/indent_lines.py +155 -0
- aider/tools/insert_block.py +211 -0
- aider/tools/list_changes.py +51 -0
- aider/tools/ls.py +49 -0
- aider/tools/make_editable.py +46 -0
- aider/tools/make_readonly.py +29 -0
- aider/tools/remove.py +48 -0
- aider/tools/replace_all.py +77 -0
- aider/tools/replace_line.py +125 -0
- aider/tools/replace_lines.py +160 -0
- aider/tools/replace_text.py +125 -0
- aider/tools/show_numbered_context.py +101 -0
- aider/tools/tool_utils.py +313 -0
- aider/tools/undo_change.py +60 -0
- aider/tools/view.py +13 -0
- aider/tools/view_files_at_glob.py +65 -0
- aider/tools/view_files_matching.py +103 -0
- aider/tools/view_files_with_symbol.py +121 -0
- aider/urls.py +17 -0
- aider/utils.py +454 -0
- aider/versioncheck.py +113 -0
- aider/voice.py +187 -0
- aider/waiting.py +221 -0
- aider/watch.py +318 -0
- aider/watch_prompts.py +12 -0
- aider/website/Gemfile +8 -0
- aider/website/_includes/blame.md +162 -0
- aider/website/_includes/get-started.md +22 -0
- aider/website/_includes/help-tip.md +5 -0
- aider/website/_includes/help.md +24 -0
- aider/website/_includes/install.md +5 -0
- aider/website/_includes/keys.md +4 -0
- aider/website/_includes/model-warnings.md +67 -0
- aider/website/_includes/multi-line.md +22 -0
- aider/website/_includes/python-m-aider.md +5 -0
- aider/website/_includes/recording.css +228 -0
- aider/website/_includes/recording.md +34 -0
- aider/website/_includes/replit-pipx.md +9 -0
- aider/website/_includes/works-best.md +1 -0
- aider/website/_sass/custom/custom.scss +103 -0
- aider/website/docs/config/adv-model-settings.md +2260 -0
- aider/website/docs/config/aider_conf.md +548 -0
- aider/website/docs/config/api-keys.md +90 -0
- aider/website/docs/config/dotenv.md +493 -0
- aider/website/docs/config/editor.md +127 -0
- aider/website/docs/config/mcp.md +95 -0
- aider/website/docs/config/model-aliases.md +104 -0
- aider/website/docs/config/options.md +890 -0
- aider/website/docs/config/reasoning.md +210 -0
- aider/website/docs/config.md +44 -0
- aider/website/docs/faq.md +384 -0
- aider/website/docs/git.md +76 -0
- aider/website/docs/index.md +47 -0
- aider/website/docs/install/codespaces.md +39 -0
- aider/website/docs/install/docker.md +57 -0
- aider/website/docs/install/optional.md +100 -0
- aider/website/docs/install/replit.md +8 -0
- aider/website/docs/install.md +115 -0
- aider/website/docs/languages.md +264 -0
- aider/website/docs/legal/contributor-agreement.md +111 -0
- aider/website/docs/legal/privacy.md +104 -0
- aider/website/docs/llms/anthropic.md +77 -0
- aider/website/docs/llms/azure.md +48 -0
- aider/website/docs/llms/bedrock.md +132 -0
- aider/website/docs/llms/cohere.md +34 -0
- aider/website/docs/llms/deepseek.md +32 -0
- aider/website/docs/llms/gemini.md +49 -0
- aider/website/docs/llms/github.md +111 -0
- aider/website/docs/llms/groq.md +36 -0
- aider/website/docs/llms/lm-studio.md +39 -0
- aider/website/docs/llms/ollama.md +75 -0
- aider/website/docs/llms/openai-compat.md +39 -0
- aider/website/docs/llms/openai.md +58 -0
- aider/website/docs/llms/openrouter.md +78 -0
- aider/website/docs/llms/other.md +111 -0
- aider/website/docs/llms/vertex.md +50 -0
- aider/website/docs/llms/warnings.md +10 -0
- aider/website/docs/llms/xai.md +53 -0
- aider/website/docs/llms.md +54 -0
- aider/website/docs/more/analytics.md +127 -0
- aider/website/docs/more/edit-formats.md +116 -0
- aider/website/docs/more/infinite-output.md +159 -0
- aider/website/docs/more-info.md +8 -0
- aider/website/docs/recordings/auto-accept-architect.md +31 -0
- aider/website/docs/recordings/dont-drop-original-read-files.md +35 -0
- aider/website/docs/recordings/index.md +21 -0
- aider/website/docs/recordings/model-accepts-settings.md +69 -0
- aider/website/docs/recordings/tree-sitter-language-pack.md +80 -0
- aider/website/docs/repomap.md +112 -0
- aider/website/docs/scripting.md +100 -0
- aider/website/docs/troubleshooting/aider-not-found.md +24 -0
- aider/website/docs/troubleshooting/edit-errors.md +76 -0
- aider/website/docs/troubleshooting/imports.md +62 -0
- aider/website/docs/troubleshooting/models-and-keys.md +54 -0
- aider/website/docs/troubleshooting/support.md +79 -0
- aider/website/docs/troubleshooting/token-limits.md +96 -0
- aider/website/docs/troubleshooting/warnings.md +12 -0
- aider/website/docs/troubleshooting.md +11 -0
- aider/website/docs/usage/browser.md +57 -0
- aider/website/docs/usage/caching.md +49 -0
- aider/website/docs/usage/commands.md +133 -0
- aider/website/docs/usage/conventions.md +119 -0
- aider/website/docs/usage/copypaste.md +121 -0
- aider/website/docs/usage/images-urls.md +48 -0
- aider/website/docs/usage/lint-test.md +118 -0
- aider/website/docs/usage/modes.md +211 -0
- aider/website/docs/usage/not-code.md +179 -0
- aider/website/docs/usage/notifications.md +87 -0
- aider/website/docs/usage/tips.md +79 -0
- aider/website/docs/usage/tutorials.md +30 -0
- aider/website/docs/usage/voice.md +121 -0
- aider/website/docs/usage/watch.md +294 -0
- aider/website/docs/usage.md +102 -0
- aider/website/share/index.md +101 -0
- aider_ce-0.87.2.dev9.dist-info/METADATA +543 -0
- aider_ce-0.87.2.dev9.dist-info/RECORD +264 -0
- aider_ce-0.87.2.dev9.dist-info/WHEEL +5 -0
- aider_ce-0.87.2.dev9.dist-info/entry_points.txt +3 -0
- aider_ce-0.87.2.dev9.dist-info/licenses/LICENSE.txt +202 -0
- aider_ce-0.87.2.dev9.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
4
|
+
from .tool_utils import generate_unified_diff_snippet
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _execute_extract_lines(
|
|
8
|
+
coder,
|
|
9
|
+
source_file_path,
|
|
10
|
+
target_file_path,
|
|
11
|
+
start_pattern,
|
|
12
|
+
end_pattern=None,
|
|
13
|
+
line_count=None,
|
|
14
|
+
near_context=None,
|
|
15
|
+
occurrence=1,
|
|
16
|
+
dry_run=False,
|
|
17
|
+
):
|
|
18
|
+
"""
|
|
19
|
+
Extract a range of lines from a source file and move them to a target file.
|
|
20
|
+
|
|
21
|
+
Parameters:
|
|
22
|
+
- coder: The Coder instance
|
|
23
|
+
- source_file_path: Path to the file to extract lines from
|
|
24
|
+
- target_file_path: Path to the file to append extracted lines to (will be created if needed)
|
|
25
|
+
- start_pattern: Pattern marking the start of the block to extract
|
|
26
|
+
- end_pattern: Optional pattern marking the end of the block
|
|
27
|
+
- line_count: Optional number of lines to extract (alternative to end_pattern)
|
|
28
|
+
- near_context: Optional text nearby to help locate the correct instance of the start_pattern
|
|
29
|
+
- occurrence: Which occurrence of the start_pattern to use (1-based index, or -1 for last)
|
|
30
|
+
- dry_run: If True, simulate the change without modifying files
|
|
31
|
+
|
|
32
|
+
Returns a result message.
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
# --- Validate Source File ---
|
|
36
|
+
abs_source_path = coder.abs_root_path(source_file_path)
|
|
37
|
+
rel_source_path = coder.get_rel_fname(abs_source_path)
|
|
38
|
+
|
|
39
|
+
if not os.path.isfile(abs_source_path):
|
|
40
|
+
coder.io.tool_error(f"Source file '{source_file_path}' not found")
|
|
41
|
+
return "Error: Source file not found"
|
|
42
|
+
|
|
43
|
+
if abs_source_path not in coder.abs_fnames:
|
|
44
|
+
if abs_source_path in coder.abs_read_only_fnames:
|
|
45
|
+
coder.io.tool_error(
|
|
46
|
+
f"Source file '{source_file_path}' is read-only. Use MakeEditable first."
|
|
47
|
+
)
|
|
48
|
+
return "Error: Source file is read-only. Use MakeEditable first."
|
|
49
|
+
else:
|
|
50
|
+
coder.io.tool_error(f"Source file '{source_file_path}' not in context")
|
|
51
|
+
return "Error: Source file not in context"
|
|
52
|
+
|
|
53
|
+
# --- Validate Target File ---
|
|
54
|
+
abs_target_path = coder.abs_root_path(target_file_path)
|
|
55
|
+
rel_target_path = coder.get_rel_fname(abs_target_path)
|
|
56
|
+
target_exists = os.path.isfile(abs_target_path)
|
|
57
|
+
target_is_editable = abs_target_path in coder.abs_fnames
|
|
58
|
+
target_is_readonly = abs_target_path in coder.abs_read_only_fnames
|
|
59
|
+
|
|
60
|
+
if target_exists and not target_is_editable:
|
|
61
|
+
if target_is_readonly:
|
|
62
|
+
coder.io.tool_error(
|
|
63
|
+
f"Target file '{target_file_path}' exists but is read-only. Use MakeEditable"
|
|
64
|
+
" first."
|
|
65
|
+
)
|
|
66
|
+
return "Error: Target file exists but is read-only. Use MakeEditable first."
|
|
67
|
+
else:
|
|
68
|
+
# This case shouldn't happen if file exists, but handle defensively
|
|
69
|
+
coder.io.tool_error(
|
|
70
|
+
f"Target file '{target_file_path}' exists but is not in context. Add it first."
|
|
71
|
+
)
|
|
72
|
+
return "Error: Target file exists but is not in context."
|
|
73
|
+
|
|
74
|
+
# --- Read Source Content ---
|
|
75
|
+
source_content = coder.io.read_text(abs_source_path)
|
|
76
|
+
if source_content is None:
|
|
77
|
+
coder.io.tool_error(
|
|
78
|
+
f"Could not read source file '{source_file_path}' before ExtractLines operation."
|
|
79
|
+
)
|
|
80
|
+
return f"Error: Could not read source file '{source_file_path}'"
|
|
81
|
+
|
|
82
|
+
# --- Find Extraction Range ---
|
|
83
|
+
if end_pattern and line_count:
|
|
84
|
+
coder.io.tool_error("Cannot specify both end_pattern and line_count")
|
|
85
|
+
return "Error: Cannot specify both end_pattern and line_count"
|
|
86
|
+
|
|
87
|
+
source_lines = source_content.splitlines()
|
|
88
|
+
original_source_content = source_content
|
|
89
|
+
|
|
90
|
+
start_pattern_line_indices = []
|
|
91
|
+
for i, line in enumerate(source_lines):
|
|
92
|
+
if start_pattern in line:
|
|
93
|
+
if near_context:
|
|
94
|
+
context_window_start = max(0, i - 5)
|
|
95
|
+
context_window_end = min(len(source_lines), i + 6)
|
|
96
|
+
context_block = "\n".join(source_lines[context_window_start:context_window_end])
|
|
97
|
+
if near_context in context_block:
|
|
98
|
+
start_pattern_line_indices.append(i)
|
|
99
|
+
else:
|
|
100
|
+
start_pattern_line_indices.append(i)
|
|
101
|
+
|
|
102
|
+
if not start_pattern_line_indices:
|
|
103
|
+
err_msg = f"Start pattern '{start_pattern}' not found"
|
|
104
|
+
if near_context:
|
|
105
|
+
err_msg += f" near context '{near_context}'"
|
|
106
|
+
err_msg += f" in source file '{source_file_path}'."
|
|
107
|
+
coder.io.tool_error(err_msg)
|
|
108
|
+
return f"Error: {err_msg}"
|
|
109
|
+
|
|
110
|
+
num_occurrences = len(start_pattern_line_indices)
|
|
111
|
+
try:
|
|
112
|
+
occurrence = int(occurrence)
|
|
113
|
+
if occurrence == -1:
|
|
114
|
+
target_idx = num_occurrences - 1
|
|
115
|
+
elif occurrence > 0 and occurrence <= num_occurrences:
|
|
116
|
+
target_idx = occurrence - 1
|
|
117
|
+
else:
|
|
118
|
+
err_msg = (
|
|
119
|
+
f"Occurrence number {occurrence} is out of range for start pattern"
|
|
120
|
+
f" '{start_pattern}'. Found {num_occurrences} occurrences"
|
|
121
|
+
)
|
|
122
|
+
if near_context:
|
|
123
|
+
err_msg += f" near '{near_context}'"
|
|
124
|
+
err_msg += f" in '{source_file_path}'."
|
|
125
|
+
coder.io.tool_error(err_msg)
|
|
126
|
+
return f"Error: {err_msg}"
|
|
127
|
+
except ValueError:
|
|
128
|
+
coder.io.tool_error(f"Invalid occurrence value: '{occurrence}'. Must be an integer.")
|
|
129
|
+
return f"Error: Invalid occurrence value '{occurrence}'"
|
|
130
|
+
|
|
131
|
+
start_line = start_pattern_line_indices[target_idx]
|
|
132
|
+
occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""
|
|
133
|
+
|
|
134
|
+
end_line = -1
|
|
135
|
+
if end_pattern:
|
|
136
|
+
for i in range(start_line, len(source_lines)):
|
|
137
|
+
if end_pattern in source_lines[i]:
|
|
138
|
+
end_line = i
|
|
139
|
+
break
|
|
140
|
+
if end_line == -1:
|
|
141
|
+
err_msg = (
|
|
142
|
+
f"End pattern '{end_pattern}' not found after {occurrence_str}start pattern"
|
|
143
|
+
f" '{start_pattern}' (line {start_line + 1}) in '{source_file_path}'."
|
|
144
|
+
)
|
|
145
|
+
coder.io.tool_error(err_msg)
|
|
146
|
+
return f"Error: {err_msg}"
|
|
147
|
+
elif line_count:
|
|
148
|
+
try:
|
|
149
|
+
line_count = int(line_count)
|
|
150
|
+
if line_count <= 0:
|
|
151
|
+
raise ValueError("Line count must be positive")
|
|
152
|
+
end_line = min(start_line + line_count - 1, len(source_lines) - 1)
|
|
153
|
+
except ValueError:
|
|
154
|
+
coder.io.tool_error(
|
|
155
|
+
f"Invalid line_count value: '{line_count}'. Must be a positive integer."
|
|
156
|
+
)
|
|
157
|
+
return f"Error: Invalid line_count value '{line_count}'"
|
|
158
|
+
else:
|
|
159
|
+
end_line = start_line # Extract just the start line if no end specified
|
|
160
|
+
|
|
161
|
+
# --- Prepare Content Changes ---
|
|
162
|
+
extracted_lines = source_lines[start_line : end_line + 1]
|
|
163
|
+
new_source_lines = source_lines[:start_line] + source_lines[end_line + 1 :]
|
|
164
|
+
new_source_content = "\n".join(new_source_lines)
|
|
165
|
+
|
|
166
|
+
target_content = ""
|
|
167
|
+
if target_exists:
|
|
168
|
+
target_content = coder.io.read_text(abs_target_path)
|
|
169
|
+
if target_content is None:
|
|
170
|
+
coder.io.tool_error(f"Could not read existing target file '{target_file_path}'.")
|
|
171
|
+
return f"Error: Could not read target file '{target_file_path}'"
|
|
172
|
+
original_target_content = target_content # For tracking
|
|
173
|
+
|
|
174
|
+
# Append extracted lines to target content, ensuring a newline if target wasn't empty
|
|
175
|
+
extracted_block = "\n".join(extracted_lines)
|
|
176
|
+
if target_content and not target_content.endswith("\n"):
|
|
177
|
+
target_content += "\n" # Add newline before appending if needed
|
|
178
|
+
new_target_content = target_content + extracted_block
|
|
179
|
+
|
|
180
|
+
# --- Generate Diffs ---
|
|
181
|
+
source_diff_snippet = generate_unified_diff_snippet(
|
|
182
|
+
original_source_content, new_source_content, rel_source_path
|
|
183
|
+
)
|
|
184
|
+
target_insertion_line = len(target_content.splitlines()) if target_content else 0
|
|
185
|
+
target_diff_snippet = generate_unified_diff_snippet(
|
|
186
|
+
original_target_content, new_target_content, rel_target_path
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# --- Handle Dry Run ---
|
|
190
|
+
if dry_run:
|
|
191
|
+
num_extracted = end_line - start_line + 1
|
|
192
|
+
target_action = "append to" if target_exists else "create"
|
|
193
|
+
coder.io.tool_output(
|
|
194
|
+
f"Dry run: Would extract {num_extracted} lines (from {occurrence_str}start pattern"
|
|
195
|
+
f" '{start_pattern}') in {source_file_path} and {target_action} {target_file_path}"
|
|
196
|
+
)
|
|
197
|
+
# Provide more informative dry run response with diffs
|
|
198
|
+
return (
|
|
199
|
+
f"Dry run: Would extract {num_extracted} lines from {rel_source_path} and"
|
|
200
|
+
f" {target_action} {rel_target_path}.\nSource Diff"
|
|
201
|
+
f" (Deletion):\n{source_diff_snippet}\nTarget Diff"
|
|
202
|
+
f" (Insertion):\n{target_diff_snippet}"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# --- Apply Changes (Not Dry Run) ---
|
|
206
|
+
coder.io.write_text(abs_source_path, new_source_content)
|
|
207
|
+
coder.io.write_text(abs_target_path, new_target_content)
|
|
208
|
+
|
|
209
|
+
# --- Track Changes ---
|
|
210
|
+
source_change_id = "TRACKING_FAILED"
|
|
211
|
+
target_change_id = "TRACKING_FAILED"
|
|
212
|
+
try:
|
|
213
|
+
source_metadata = {
|
|
214
|
+
"start_line": start_line + 1,
|
|
215
|
+
"end_line": end_line + 1,
|
|
216
|
+
"start_pattern": start_pattern,
|
|
217
|
+
"end_pattern": end_pattern,
|
|
218
|
+
"line_count": line_count,
|
|
219
|
+
"near_context": near_context,
|
|
220
|
+
"occurrence": occurrence,
|
|
221
|
+
"extracted_content": extracted_block,
|
|
222
|
+
"target_file": rel_target_path,
|
|
223
|
+
}
|
|
224
|
+
source_change_id = coder.change_tracker.track_change(
|
|
225
|
+
file_path=rel_source_path,
|
|
226
|
+
change_type="extractlines_source",
|
|
227
|
+
original_content=original_source_content,
|
|
228
|
+
new_content=new_source_content,
|
|
229
|
+
metadata=source_metadata,
|
|
230
|
+
)
|
|
231
|
+
except Exception as track_e:
|
|
232
|
+
coder.io.tool_error(f"Error tracking source change for ExtractLines: {track_e}")
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
target_metadata = {
|
|
236
|
+
"insertion_line": target_insertion_line + 1,
|
|
237
|
+
"inserted_content": extracted_block,
|
|
238
|
+
"source_file": rel_source_path,
|
|
239
|
+
}
|
|
240
|
+
target_change_id = coder.change_tracker.track_change(
|
|
241
|
+
file_path=rel_target_path,
|
|
242
|
+
change_type="extractlines_target",
|
|
243
|
+
original_content=original_target_content,
|
|
244
|
+
new_content=new_target_content,
|
|
245
|
+
metadata=target_metadata,
|
|
246
|
+
)
|
|
247
|
+
except Exception as track_e:
|
|
248
|
+
coder.io.tool_error(f"Error tracking target change for ExtractLines: {track_e}")
|
|
249
|
+
|
|
250
|
+
# --- Update Context ---
|
|
251
|
+
coder.aider_edited_files.add(rel_source_path)
|
|
252
|
+
coder.aider_edited_files.add(rel_target_path)
|
|
253
|
+
if not target_exists:
|
|
254
|
+
# Add the newly created file to editable context
|
|
255
|
+
coder.abs_fnames.add(abs_target_path)
|
|
256
|
+
coder.io.tool_output(f"✨ Created and added '{target_file_path}' to editable context.")
|
|
257
|
+
|
|
258
|
+
# --- Return Result ---
|
|
259
|
+
num_extracted = end_line - start_line + 1
|
|
260
|
+
target_action = "appended to" if target_exists else "created"
|
|
261
|
+
coder.io.tool_output(
|
|
262
|
+
f"✅ Extracted {num_extracted} lines from {rel_source_path} (change_id:"
|
|
263
|
+
f" {source_change_id}) and {target_action} {rel_target_path} (change_id:"
|
|
264
|
+
f" {target_change_id})"
|
|
265
|
+
)
|
|
266
|
+
# Provide more informative success response with change IDs and diffs
|
|
267
|
+
return (
|
|
268
|
+
f"Successfully extracted {num_extracted} lines from {rel_source_path} and"
|
|
269
|
+
f" {target_action} {rel_target_path}.\nSource Change ID: {source_change_id}\nSource"
|
|
270
|
+
f" Diff (Deletion):\n{source_diff_snippet}\nTarget Change ID:"
|
|
271
|
+
f" {target_change_id}\nTarget Diff (Insertion):\n{target_diff_snippet}"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
except Exception as e:
|
|
275
|
+
coder.io.tool_error(f"Error in ExtractLines: {str(e)}\n{traceback.format_exc()}")
|
|
276
|
+
return f"Error: {str(e)}"
|
aider/tools/grep.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import shlex
|
|
2
|
+
import shutil
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from aider.run_cmd import run_cmd_subprocess
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _find_search_tool():
|
|
9
|
+
"""Find the best available command-line search tool (rg, ag, grep)."""
|
|
10
|
+
if shutil.which("rg"):
|
|
11
|
+
return "rg", shutil.which("rg")
|
|
12
|
+
elif shutil.which("ag"):
|
|
13
|
+
return "ag", shutil.which("ag")
|
|
14
|
+
elif shutil.which("grep"):
|
|
15
|
+
return "grep", shutil.which("grep")
|
|
16
|
+
else:
|
|
17
|
+
return None, None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _execute_grep(
|
|
21
|
+
coder,
|
|
22
|
+
pattern,
|
|
23
|
+
file_pattern="*",
|
|
24
|
+
directory=".",
|
|
25
|
+
use_regex=False,
|
|
26
|
+
case_insensitive=False,
|
|
27
|
+
context_before=5,
|
|
28
|
+
context_after=5,
|
|
29
|
+
):
|
|
30
|
+
"""
|
|
31
|
+
Search for lines matching a pattern in files within the project repository.
|
|
32
|
+
Uses rg (ripgrep), ag (the silver searcher), or grep, whichever is available.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
coder: The Coder instance.
|
|
36
|
+
pattern (str): The pattern to search for.
|
|
37
|
+
file_pattern (str, optional): Glob pattern to filter files. Defaults to "*".
|
|
38
|
+
directory (str, optional): Directory to search within relative to repo root. Defaults to ".".
|
|
39
|
+
use_regex (bool, optional): Whether the pattern is a regular expression. Defaults to False.
|
|
40
|
+
case_insensitive (bool, optional): Whether the search should be case-insensitive. Defaults to False.
|
|
41
|
+
context_before (int, optional): Number of context lines to show before matches. Defaults to 5.
|
|
42
|
+
context_after (int, optional): Number of context lines to show after matches. Defaults to 5.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
str: Formatted result indicating success or failure, including matching lines or error message.
|
|
46
|
+
"""
|
|
47
|
+
repo = coder.repo
|
|
48
|
+
if not repo:
|
|
49
|
+
coder.io.tool_error("Not in a git repository.")
|
|
50
|
+
return "Error: Not in a git repository."
|
|
51
|
+
|
|
52
|
+
tool_name, tool_path = _find_search_tool()
|
|
53
|
+
if not tool_path:
|
|
54
|
+
coder.io.tool_error("No search tool (rg, ag, grep) found in PATH.")
|
|
55
|
+
return "Error: No search tool (rg, ag, grep) found."
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
search_dir_path = Path(repo.root) / directory
|
|
59
|
+
if not search_dir_path.is_dir():
|
|
60
|
+
coder.io.tool_error(f"Directory not found: {directory}")
|
|
61
|
+
return f"Error: Directory not found: {directory}"
|
|
62
|
+
|
|
63
|
+
# Build the command arguments based on the available tool
|
|
64
|
+
cmd_args = [tool_path]
|
|
65
|
+
|
|
66
|
+
# Common options or tool-specific equivalents
|
|
67
|
+
if tool_name in ["rg", "grep"]:
|
|
68
|
+
cmd_args.append("-n") # Line numbers for rg and grep
|
|
69
|
+
# ag includes line numbers by default
|
|
70
|
+
|
|
71
|
+
# Context lines (Before and After)
|
|
72
|
+
if context_before > 0:
|
|
73
|
+
# All tools use -B for lines before
|
|
74
|
+
cmd_args.extend(["-B", str(context_before)])
|
|
75
|
+
if context_after > 0:
|
|
76
|
+
# All tools use -A for lines after
|
|
77
|
+
cmd_args.extend(["-A", str(context_after)])
|
|
78
|
+
|
|
79
|
+
# Case sensitivity
|
|
80
|
+
if case_insensitive:
|
|
81
|
+
cmd_args.append("-i") # Add case-insensitivity flag for all tools
|
|
82
|
+
|
|
83
|
+
# Pattern type (regex vs fixed string)
|
|
84
|
+
if use_regex:
|
|
85
|
+
if tool_name == "grep":
|
|
86
|
+
cmd_args.append("-E") # Use extended regex for grep
|
|
87
|
+
# rg and ag use regex by default, no flag needed for basic ERE
|
|
88
|
+
else:
|
|
89
|
+
if tool_name == "rg":
|
|
90
|
+
cmd_args.append("-F") # Fixed strings for rg
|
|
91
|
+
elif tool_name == "ag":
|
|
92
|
+
cmd_args.append("-Q") # Literal/fixed strings for ag
|
|
93
|
+
elif tool_name == "grep":
|
|
94
|
+
cmd_args.append("-F") # Fixed strings for grep
|
|
95
|
+
|
|
96
|
+
# File filtering
|
|
97
|
+
if (
|
|
98
|
+
file_pattern != "*"
|
|
99
|
+
): # Avoid adding glob if it's the default '*' which might behave differently
|
|
100
|
+
if tool_name == "rg":
|
|
101
|
+
cmd_args.extend(["-g", file_pattern])
|
|
102
|
+
elif tool_name == "ag":
|
|
103
|
+
cmd_args.extend(["-G", file_pattern])
|
|
104
|
+
elif tool_name == "grep":
|
|
105
|
+
# grep needs recursive flag when filtering
|
|
106
|
+
cmd_args.append("-r")
|
|
107
|
+
cmd_args.append(f"--include={file_pattern}")
|
|
108
|
+
elif tool_name == "grep":
|
|
109
|
+
# grep needs recursive flag even without include filter
|
|
110
|
+
cmd_args.append("-r")
|
|
111
|
+
|
|
112
|
+
# Directory exclusion (rg and ag respect .gitignore/.git by default)
|
|
113
|
+
if tool_name == "grep":
|
|
114
|
+
cmd_args.append("--exclude-dir=.git")
|
|
115
|
+
|
|
116
|
+
# Add pattern and directory path
|
|
117
|
+
cmd_args.extend([pattern, str(search_dir_path)])
|
|
118
|
+
|
|
119
|
+
# Convert list to command string for run_cmd_subprocess
|
|
120
|
+
command_string = shlex.join(cmd_args)
|
|
121
|
+
|
|
122
|
+
coder.io.tool_output(f"⚙️ Executing {tool_name}: {command_string}")
|
|
123
|
+
|
|
124
|
+
# Use run_cmd_subprocess for execution
|
|
125
|
+
# Note: rg, ag, and grep return 1 if no matches are found, which is not an error for this tool.
|
|
126
|
+
exit_status, combined_output = run_cmd_subprocess(
|
|
127
|
+
command_string, verbose=coder.verbose, cwd=coder.root # Execute in the project root
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Format the output for the result message
|
|
131
|
+
output_content = combined_output or ""
|
|
132
|
+
|
|
133
|
+
# Handle exit codes (consistent across rg, ag, grep)
|
|
134
|
+
if exit_status == 0:
|
|
135
|
+
# Limit output size if necessary
|
|
136
|
+
max_output_lines = 50 # Consider making this configurable
|
|
137
|
+
output_lines = output_content.splitlines()
|
|
138
|
+
if len(output_lines) > max_output_lines:
|
|
139
|
+
truncated_output = "\n".join(output_lines[:max_output_lines])
|
|
140
|
+
result_message = (
|
|
141
|
+
f"Found matches (truncated):\n```text\n{truncated_output}\n..."
|
|
142
|
+
f" ({len(output_lines) - max_output_lines} more lines)\n```"
|
|
143
|
+
)
|
|
144
|
+
elif not output_content:
|
|
145
|
+
# Should not happen if return code is 0, but handle defensively
|
|
146
|
+
coder.io.tool_warning(f"{tool_name} returned 0 but produced no output.")
|
|
147
|
+
result_message = "No matches found (unexpected)."
|
|
148
|
+
else:
|
|
149
|
+
result_message = f"Found matches:\n```text\n{output_content}\n```"
|
|
150
|
+
return result_message
|
|
151
|
+
|
|
152
|
+
elif exit_status == 1:
|
|
153
|
+
# Exit code 1 means no matches found - this is expected behavior, not an error.
|
|
154
|
+
return "No matches found."
|
|
155
|
+
else:
|
|
156
|
+
# Exit code > 1 indicates an actual error
|
|
157
|
+
error_message = f"{tool_name.capitalize()} command failed with exit code {exit_status}."
|
|
158
|
+
if output_content:
|
|
159
|
+
# Truncate error output as well if it's too long
|
|
160
|
+
error_limit = 1000 # Example limit for error output
|
|
161
|
+
if len(output_content) > error_limit:
|
|
162
|
+
output_content = output_content[:error_limit] + "\n... (error output truncated)"
|
|
163
|
+
error_message += f" Output:\n{output_content}"
|
|
164
|
+
coder.io.tool_error(error_message)
|
|
165
|
+
return f"Error: {error_message}"
|
|
166
|
+
|
|
167
|
+
except Exception as e:
|
|
168
|
+
# Add command_string to the error message if it's defined
|
|
169
|
+
cmd_str_info = f"'{command_string}' " if "command_string" in locals() else ""
|
|
170
|
+
coder.io.tool_error(f"Error executing {tool_name} command {cmd_str_info}: {str(e)}")
|
|
171
|
+
return f"Error executing {tool_name}: {str(e)}"
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from .tool_utils import (
|
|
2
|
+
ToolError,
|
|
3
|
+
apply_change,
|
|
4
|
+
determine_line_range,
|
|
5
|
+
find_pattern_indices,
|
|
6
|
+
format_tool_result,
|
|
7
|
+
generate_unified_diff_snippet,
|
|
8
|
+
handle_tool_error,
|
|
9
|
+
select_occurrence_index,
|
|
10
|
+
validate_file_for_edit,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _execute_indent_lines(
|
|
15
|
+
coder,
|
|
16
|
+
file_path,
|
|
17
|
+
start_pattern,
|
|
18
|
+
end_pattern=None,
|
|
19
|
+
line_count=None,
|
|
20
|
+
indent_levels=1,
|
|
21
|
+
near_context=None,
|
|
22
|
+
occurrence=1,
|
|
23
|
+
change_id=None,
|
|
24
|
+
dry_run=False,
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Indent or unindent a block of lines in a file using utility functions.
|
|
28
|
+
|
|
29
|
+
Parameters:
|
|
30
|
+
- coder: The Coder instance
|
|
31
|
+
- file_path: Path to the file to modify
|
|
32
|
+
- start_pattern: Pattern marking the start of the block to indent (line containing this pattern)
|
|
33
|
+
- end_pattern: Optional pattern marking the end of the block (line containing this pattern)
|
|
34
|
+
- line_count: Optional number of lines to indent (alternative to end_pattern)
|
|
35
|
+
- indent_levels: Number of levels to indent (positive) or unindent (negative)
|
|
36
|
+
- near_context: Optional text nearby to help locate the correct instance of the start_pattern
|
|
37
|
+
- occurrence: Which occurrence of the start_pattern to use (1-based index, or -1 for last)
|
|
38
|
+
- change_id: Optional ID for tracking the change
|
|
39
|
+
- dry_run: If True, simulate the change without modifying the file
|
|
40
|
+
|
|
41
|
+
Returns a result message.
|
|
42
|
+
"""
|
|
43
|
+
tool_name = "IndentLines"
|
|
44
|
+
try:
|
|
45
|
+
# 1. Validate file and get content
|
|
46
|
+
abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
|
|
47
|
+
lines = original_content.splitlines()
|
|
48
|
+
|
|
49
|
+
# 2. Find the start line
|
|
50
|
+
pattern_desc = f"Start pattern '{start_pattern}'"
|
|
51
|
+
if near_context:
|
|
52
|
+
pattern_desc += f" near context '{near_context}'"
|
|
53
|
+
start_pattern_indices = find_pattern_indices(lines, start_pattern, near_context)
|
|
54
|
+
start_line_idx = select_occurrence_index(start_pattern_indices, occurrence, pattern_desc)
|
|
55
|
+
|
|
56
|
+
# 3. Determine the end line
|
|
57
|
+
start_line, end_line = determine_line_range(
|
|
58
|
+
coder=coder,
|
|
59
|
+
file_path=rel_path,
|
|
60
|
+
lines=lines,
|
|
61
|
+
start_pattern_line_index=start_line_idx,
|
|
62
|
+
end_pattern=end_pattern,
|
|
63
|
+
line_count=line_count,
|
|
64
|
+
target_symbol=None, # IndentLines uses patterns, not symbols
|
|
65
|
+
pattern_desc=pattern_desc,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# 4. Validate and prepare indentation
|
|
69
|
+
try:
|
|
70
|
+
indent_levels = int(indent_levels)
|
|
71
|
+
except ValueError:
|
|
72
|
+
raise ToolError(f"Invalid indent_levels value: '{indent_levels}'. Must be an integer.")
|
|
73
|
+
|
|
74
|
+
indent_str = " " * 4 # Assume 4 spaces per level
|
|
75
|
+
modified_lines = list(lines)
|
|
76
|
+
|
|
77
|
+
# Apply indentation logic (core logic remains)
|
|
78
|
+
for i in range(start_line, end_line + 1):
|
|
79
|
+
if indent_levels > 0:
|
|
80
|
+
modified_lines[i] = (indent_str * indent_levels) + modified_lines[i]
|
|
81
|
+
elif indent_levels < 0:
|
|
82
|
+
spaces_to_remove = abs(indent_levels) * len(indent_str)
|
|
83
|
+
current_leading_spaces = len(modified_lines[i]) - len(modified_lines[i].lstrip(" "))
|
|
84
|
+
actual_remove = min(spaces_to_remove, current_leading_spaces)
|
|
85
|
+
if actual_remove > 0:
|
|
86
|
+
modified_lines[i] = modified_lines[i][actual_remove:]
|
|
87
|
+
|
|
88
|
+
new_content = "\n".join(modified_lines)
|
|
89
|
+
|
|
90
|
+
if original_content == new_content:
|
|
91
|
+
coder.io.tool_warning("No changes made: indentation would not change file")
|
|
92
|
+
return "Warning: No changes made (indentation would not change file)"
|
|
93
|
+
|
|
94
|
+
# 5. Generate diff for feedback
|
|
95
|
+
diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
|
|
96
|
+
num_occurrences = len(start_pattern_indices)
|
|
97
|
+
occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""
|
|
98
|
+
action = "indent" if indent_levels > 0 else "unindent"
|
|
99
|
+
levels = abs(indent_levels)
|
|
100
|
+
level_text = "level" if levels == 1 else "levels"
|
|
101
|
+
num_lines = end_line - start_line + 1
|
|
102
|
+
|
|
103
|
+
# 6. Handle dry run
|
|
104
|
+
if dry_run:
|
|
105
|
+
dry_run_message = (
|
|
106
|
+
f"Dry run: Would {action} {num_lines} lines ({start_line + 1}-{end_line + 1}) by"
|
|
107
|
+
f" {levels} {level_text} (based on {occurrence_str}start pattern '{start_pattern}')"
|
|
108
|
+
f" in {file_path}."
|
|
109
|
+
)
|
|
110
|
+
return format_tool_result(
|
|
111
|
+
coder,
|
|
112
|
+
tool_name,
|
|
113
|
+
"",
|
|
114
|
+
dry_run=True,
|
|
115
|
+
dry_run_message=dry_run_message,
|
|
116
|
+
diff_snippet=diff_snippet,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# 7. Apply Change (Not dry run)
|
|
120
|
+
metadata = {
|
|
121
|
+
"start_line": start_line + 1,
|
|
122
|
+
"end_line": end_line + 1,
|
|
123
|
+
"start_pattern": start_pattern,
|
|
124
|
+
"end_pattern": end_pattern,
|
|
125
|
+
"line_count": line_count,
|
|
126
|
+
"indent_levels": indent_levels,
|
|
127
|
+
"near_context": near_context,
|
|
128
|
+
"occurrence": occurrence,
|
|
129
|
+
}
|
|
130
|
+
final_change_id = apply_change(
|
|
131
|
+
coder,
|
|
132
|
+
abs_path,
|
|
133
|
+
rel_path,
|
|
134
|
+
original_content,
|
|
135
|
+
new_content,
|
|
136
|
+
"indentlines",
|
|
137
|
+
metadata,
|
|
138
|
+
change_id,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# 8. Format and return result
|
|
142
|
+
action_past = "Indented" if indent_levels > 0 else "Unindented"
|
|
143
|
+
success_message = (
|
|
144
|
+
f"{action_past} {num_lines} lines by {levels} {level_text} (from {occurrence_str}start"
|
|
145
|
+
f" pattern) in {file_path}"
|
|
146
|
+
)
|
|
147
|
+
return format_tool_result(
|
|
148
|
+
coder, tool_name, success_message, change_id=final_change_id, diff_snippet=diff_snippet
|
|
149
|
+
)
|
|
150
|
+
except ToolError as e:
|
|
151
|
+
# Handle errors raised by utility functions (expected errors)
|
|
152
|
+
return handle_tool_error(coder, tool_name, e, add_traceback=False)
|
|
153
|
+
except Exception as e:
|
|
154
|
+
# Handle unexpected errors
|
|
155
|
+
return handle_tool_error(coder, tool_name, e)
|