aider-ce 0.88.20__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 +20 -0
- aider/__main__.py +4 -0
- aider/_version.py +34 -0
- aider/analytics.py +258 -0
- aider/args.py +1056 -0
- aider/args_formatter.py +228 -0
- aider/change_tracker.py +133 -0
- aider/coders/__init__.py +36 -0
- aider/coders/agent_coder.py +2166 -0
- aider/coders/agent_prompts.py +104 -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 +3613 -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 +175 -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/patch_coder.py +706 -0
- aider/coders/patch_prompts.py +159 -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 +115 -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 +65 -0
- aider/commands.py +2173 -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 +115 -0
- aider/format_settings.py +26 -0
- aider/gui.py +545 -0
- aider/help.py +163 -0
- aider/help_pats.py +19 -0
- aider/helpers/__init__.py +9 -0
- aider/helpers/similarity.py +98 -0
- aider/history.py +180 -0
- aider/io.py +1608 -0
- aider/linter.py +304 -0
- aider/llm.py +55 -0
- aider/main.py +1415 -0
- aider/mcp/__init__.py +174 -0
- aider/mcp/server.py +149 -0
- aider/mdstream.py +243 -0
- aider/models.py +1313 -0
- aider/onboarding.py +429 -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 +24 -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/fortran-tags.scm +15 -0
- aider/queries/tree-sitter-languages/go-tags.scm +30 -0
- aider/queries/tree-sitter-languages/haskell-tags.scm +3 -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/julia-tags.scm +60 -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/queries/tree-sitter-languages/zig-tags.scm +3 -0
- aider/reasoning_tags.py +82 -0
- aider/repo.py +621 -0
- aider/repomap.py +1174 -0
- aider/report.py +260 -0
- aider/resources/__init__.py +3 -0
- aider/resources/model-metadata.json +776 -0
- aider/resources/model-settings.yml +2068 -0
- aider/run_cmd.py +133 -0
- aider/scrape.py +293 -0
- aider/sendchat.py +242 -0
- aider/sessions.py +256 -0
- aider/special.py +203 -0
- aider/tools/__init__.py +72 -0
- aider/tools/command.py +105 -0
- aider/tools/command_interactive.py +122 -0
- aider/tools/delete_block.py +182 -0
- aider/tools/delete_line.py +155 -0
- aider/tools/delete_lines.py +184 -0
- aider/tools/extract_lines.py +341 -0
- aider/tools/finished.py +48 -0
- aider/tools/git_branch.py +129 -0
- aider/tools/git_diff.py +60 -0
- aider/tools/git_log.py +57 -0
- aider/tools/git_remote.py +53 -0
- aider/tools/git_show.py +51 -0
- aider/tools/git_status.py +46 -0
- aider/tools/grep.py +256 -0
- aider/tools/indent_lines.py +221 -0
- aider/tools/insert_block.py +288 -0
- aider/tools/list_changes.py +86 -0
- aider/tools/ls.py +93 -0
- aider/tools/make_editable.py +85 -0
- aider/tools/make_readonly.py +69 -0
- aider/tools/remove.py +91 -0
- aider/tools/replace_all.py +126 -0
- aider/tools/replace_line.py +173 -0
- aider/tools/replace_lines.py +217 -0
- aider/tools/replace_text.py +187 -0
- aider/tools/show_numbered_context.py +147 -0
- aider/tools/tool_utils.py +313 -0
- aider/tools/undo_change.py +95 -0
- aider/tools/update_todo_list.py +156 -0
- aider/tools/view.py +57 -0
- aider/tools/view_files_matching.py +141 -0
- aider/tools/view_files_with_symbol.py +129 -0
- aider/urls.py +17 -0
- aider/utils.py +456 -0
- aider/versioncheck.py +113 -0
- aider/voice.py +205 -0
- aider/waiting.py +38 -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 +2261 -0
- aider/website/docs/config/agent-mode.md +194 -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 +117 -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 +165 -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/sessions.md +203 -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.88.20.dist-info/METADATA +187 -0
- aider_ce-0.88.20.dist-info/RECORD +279 -0
- aider_ce-0.88.20.dist-info/WHEEL +5 -0
- aider_ce-0.88.20.dist-info/entry_points.txt +2 -0
- aider_ce-0.88.20.dist-info/licenses/LICENSE.txt +202 -0
- aider_ce-0.88.20.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
4
|
+
from .tool_utils import (
|
|
5
|
+
ToolError,
|
|
6
|
+
apply_change,
|
|
7
|
+
find_pattern_indices,
|
|
8
|
+
format_tool_result,
|
|
9
|
+
generate_unified_diff_snippet,
|
|
10
|
+
handle_tool_error,
|
|
11
|
+
select_occurrence_index,
|
|
12
|
+
validate_file_for_edit,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
schema = {
|
|
16
|
+
"type": "function",
|
|
17
|
+
"function": {
|
|
18
|
+
"name": "InsertBlock",
|
|
19
|
+
"description": "Insert a block of content into a file.",
|
|
20
|
+
"parameters": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"file_path": {"type": "string"},
|
|
24
|
+
"content": {"type": "string"},
|
|
25
|
+
"after_pattern": {"type": "string"},
|
|
26
|
+
"before_pattern": {"type": "string"},
|
|
27
|
+
"occurrence": {"type": "integer", "default": 1},
|
|
28
|
+
"change_id": {"type": "string"},
|
|
29
|
+
"dry_run": {"type": "boolean", "default": False},
|
|
30
|
+
"position": {"type": "string", "enum": ["top", "bottom"]},
|
|
31
|
+
"auto_indent": {"type": "boolean", "default": True},
|
|
32
|
+
"use_regex": {"type": "boolean", "default": False},
|
|
33
|
+
},
|
|
34
|
+
"required": ["file_path", "content"],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Normalized tool name for lookup
|
|
40
|
+
NORM_NAME = "insertblock"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _execute_insert_block(
|
|
44
|
+
coder,
|
|
45
|
+
file_path,
|
|
46
|
+
content,
|
|
47
|
+
after_pattern=None,
|
|
48
|
+
before_pattern=None,
|
|
49
|
+
occurrence=1,
|
|
50
|
+
change_id=None,
|
|
51
|
+
dry_run=False,
|
|
52
|
+
position=None,
|
|
53
|
+
auto_indent=True,
|
|
54
|
+
use_regex=False,
|
|
55
|
+
):
|
|
56
|
+
"""
|
|
57
|
+
Insert a block of text after or before a specified pattern using utility functions.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
coder: The coder instance
|
|
61
|
+
file_path: Path to the file to modify
|
|
62
|
+
content: The content to insert
|
|
63
|
+
after_pattern: Pattern to insert after (mutually exclusive with before_pattern and position)
|
|
64
|
+
before_pattern: Pattern to insert before (mutually exclusive with after_pattern and position)
|
|
65
|
+
occurrence: Which occurrence of the pattern to use (1-based, or -1 for last)
|
|
66
|
+
change_id: Optional ID for tracking changes
|
|
67
|
+
dry_run: If True, only simulate the change
|
|
68
|
+
position: Special position like "start_of_file" or "end_of_file"
|
|
69
|
+
auto_indent: If True, automatically adjust indentation of inserted content
|
|
70
|
+
use_regex: If True, treat patterns as regular expressions
|
|
71
|
+
"""
|
|
72
|
+
tool_name = "InsertBlock"
|
|
73
|
+
try:
|
|
74
|
+
# 1. Validate parameters
|
|
75
|
+
if sum(x is not None for x in [after_pattern, before_pattern, position]) != 1:
|
|
76
|
+
raise ToolError(
|
|
77
|
+
"Must specify exactly one of: after_pattern, before_pattern, or position"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# 2. Validate file and get content
|
|
81
|
+
abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
|
|
82
|
+
lines = original_content.splitlines()
|
|
83
|
+
|
|
84
|
+
# Handle empty files
|
|
85
|
+
if not lines:
|
|
86
|
+
lines = [""]
|
|
87
|
+
|
|
88
|
+
# 3. Determine insertion point
|
|
89
|
+
insertion_line_idx = 0
|
|
90
|
+
pattern_type = ""
|
|
91
|
+
pattern_desc = ""
|
|
92
|
+
occurrence_str = ""
|
|
93
|
+
|
|
94
|
+
if position:
|
|
95
|
+
# Handle special positions
|
|
96
|
+
if position == "start_of_file" or position == "top":
|
|
97
|
+
insertion_line_idx = 0
|
|
98
|
+
pattern_type = "at start of"
|
|
99
|
+
elif position == "end_of_file" or position == "bottom":
|
|
100
|
+
insertion_line_idx = len(lines)
|
|
101
|
+
pattern_type = "at end of"
|
|
102
|
+
else:
|
|
103
|
+
raise ToolError(
|
|
104
|
+
f"Invalid position: '{position}'. Valid values are 'start_of_file' or"
|
|
105
|
+
" 'end_of_file'"
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
# Handle pattern-based insertion
|
|
109
|
+
pattern = after_pattern if after_pattern else before_pattern
|
|
110
|
+
pattern_type = "after" if after_pattern else "before"
|
|
111
|
+
pattern_desc = f"Pattern '{pattern}'"
|
|
112
|
+
|
|
113
|
+
# Find pattern matches
|
|
114
|
+
pattern_line_indices = find_pattern_indices(lines, pattern, use_regex=use_regex)
|
|
115
|
+
|
|
116
|
+
# Select the target occurrence
|
|
117
|
+
target_line_idx = select_occurrence_index(
|
|
118
|
+
pattern_line_indices, occurrence, pattern_desc
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Determine insertion point
|
|
122
|
+
insertion_line_idx = target_line_idx
|
|
123
|
+
if pattern_type == "after":
|
|
124
|
+
insertion_line_idx += 1 # Insert on the line *after* the matched line
|
|
125
|
+
|
|
126
|
+
# Format occurrence info for output
|
|
127
|
+
num_occurrences = len(pattern_line_indices)
|
|
128
|
+
occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""
|
|
129
|
+
|
|
130
|
+
# 4. Handle indentation if requested
|
|
131
|
+
content_lines = content.splitlines()
|
|
132
|
+
|
|
133
|
+
if auto_indent and content_lines:
|
|
134
|
+
# Determine base indentation level
|
|
135
|
+
base_indent = ""
|
|
136
|
+
if insertion_line_idx > 0 and lines:
|
|
137
|
+
# Use indentation from the line before insertion point
|
|
138
|
+
reference_line_idx = min(insertion_line_idx - 1, len(lines) - 1)
|
|
139
|
+
reference_line = lines[reference_line_idx]
|
|
140
|
+
base_indent = re.match(r"^(\s*)", reference_line).group(1)
|
|
141
|
+
|
|
142
|
+
# Apply indentation to content lines, preserving relative indentation
|
|
143
|
+
if content_lines:
|
|
144
|
+
# Find minimum indentation in content to preserve relative indentation
|
|
145
|
+
content_indents = [
|
|
146
|
+
len(re.match(r"^(\s*)", line).group(1))
|
|
147
|
+
for line in content_lines
|
|
148
|
+
if line.strip()
|
|
149
|
+
]
|
|
150
|
+
min_content_indent = min(content_indents) if content_indents else 0
|
|
151
|
+
|
|
152
|
+
# Apply base indentation while preserving relative indentation
|
|
153
|
+
indented_content_lines = []
|
|
154
|
+
for line in content_lines:
|
|
155
|
+
if not line.strip(): # Empty or whitespace-only line
|
|
156
|
+
indented_content_lines.append("")
|
|
157
|
+
else:
|
|
158
|
+
# Remove existing indentation and add new base indentation
|
|
159
|
+
stripped_line = (
|
|
160
|
+
line[min_content_indent:] if min_content_indent <= len(line) else line
|
|
161
|
+
)
|
|
162
|
+
indented_content_lines.append(base_indent + stripped_line)
|
|
163
|
+
|
|
164
|
+
content_lines = indented_content_lines
|
|
165
|
+
|
|
166
|
+
# 5. Prepare the insertion
|
|
167
|
+
new_lines = lines[:insertion_line_idx] + content_lines + lines[insertion_line_idx:]
|
|
168
|
+
new_content = "\n".join(new_lines)
|
|
169
|
+
|
|
170
|
+
if original_content == new_content:
|
|
171
|
+
coder.io.tool_warning("No changes made: insertion would not change file")
|
|
172
|
+
return "Warning: No changes made (insertion would not change file)"
|
|
173
|
+
|
|
174
|
+
# 6. Generate diff for feedback
|
|
175
|
+
diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
|
|
176
|
+
|
|
177
|
+
# 7. Handle dry run
|
|
178
|
+
if dry_run:
|
|
179
|
+
if position:
|
|
180
|
+
dry_run_message = f"Dry run: Would insert block {pattern_type} {file_path}."
|
|
181
|
+
else:
|
|
182
|
+
dry_run_message = (
|
|
183
|
+
f"Dry run: Would insert block {pattern_type} {occurrence_str}pattern"
|
|
184
|
+
f" '{pattern}' in {file_path} at line {insertion_line_idx + 1}."
|
|
185
|
+
)
|
|
186
|
+
return format_tool_result(
|
|
187
|
+
coder,
|
|
188
|
+
tool_name,
|
|
189
|
+
"",
|
|
190
|
+
dry_run=True,
|
|
191
|
+
dry_run_message=dry_run_message,
|
|
192
|
+
diff_snippet=diff_snippet,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# 8. Apply Change (Not dry run)
|
|
196
|
+
metadata = {
|
|
197
|
+
"insertion_line_idx": insertion_line_idx,
|
|
198
|
+
"after_pattern": after_pattern,
|
|
199
|
+
"before_pattern": before_pattern,
|
|
200
|
+
"position": position,
|
|
201
|
+
"occurrence": occurrence,
|
|
202
|
+
"content": content,
|
|
203
|
+
"auto_indent": auto_indent,
|
|
204
|
+
"use_regex": use_regex,
|
|
205
|
+
}
|
|
206
|
+
final_change_id = apply_change(
|
|
207
|
+
coder,
|
|
208
|
+
abs_path,
|
|
209
|
+
rel_path,
|
|
210
|
+
original_content,
|
|
211
|
+
new_content,
|
|
212
|
+
"insertblock",
|
|
213
|
+
metadata,
|
|
214
|
+
change_id,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
coder.files_edited_by_tools.add(rel_path)
|
|
218
|
+
|
|
219
|
+
# 9. Format and return result
|
|
220
|
+
if position:
|
|
221
|
+
success_message = f"Inserted block {pattern_type} {file_path}"
|
|
222
|
+
else:
|
|
223
|
+
success_message = (
|
|
224
|
+
f"Inserted block {pattern_type} {occurrence_str}pattern in {file_path} at line"
|
|
225
|
+
f" {insertion_line_idx + 1}"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
return format_tool_result(
|
|
229
|
+
coder, tool_name, success_message, change_id=final_change_id, diff_snippet=diff_snippet
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
except ToolError as e:
|
|
233
|
+
# Handle errors raised by utility functions (expected errors)
|
|
234
|
+
return handle_tool_error(coder, tool_name, e, add_traceback=False)
|
|
235
|
+
|
|
236
|
+
except Exception as e:
|
|
237
|
+
coder.io.tool_error(
|
|
238
|
+
f"Error in InsertBlock: {str(e)}\n{traceback.format_exc()}"
|
|
239
|
+
) # Add traceback
|
|
240
|
+
return f"Error: {str(e)}"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def process_response(coder, params):
|
|
244
|
+
"""
|
|
245
|
+
Process the InsertBlock tool response.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
coder: The Coder instance
|
|
249
|
+
params: Dictionary of parameters
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
str: Result message
|
|
253
|
+
"""
|
|
254
|
+
file_path = params.get("file_path")
|
|
255
|
+
content = params.get("content")
|
|
256
|
+
after_pattern = params.get("after_pattern")
|
|
257
|
+
before_pattern = params.get("before_pattern")
|
|
258
|
+
occurrence = params.get("occurrence", 1)
|
|
259
|
+
change_id = params.get("change_id")
|
|
260
|
+
dry_run = params.get("dry_run", False)
|
|
261
|
+
position = params.get("position")
|
|
262
|
+
auto_indent = params.get("auto_indent", True)
|
|
263
|
+
use_regex = params.get("use_regex", False)
|
|
264
|
+
|
|
265
|
+
if (
|
|
266
|
+
file_path is not None
|
|
267
|
+
and content is not None
|
|
268
|
+
and (after_pattern is not None or before_pattern is not None or position is not None)
|
|
269
|
+
):
|
|
270
|
+
return _execute_insert_block(
|
|
271
|
+
coder,
|
|
272
|
+
file_path,
|
|
273
|
+
content,
|
|
274
|
+
after_pattern,
|
|
275
|
+
before_pattern,
|
|
276
|
+
occurrence,
|
|
277
|
+
change_id,
|
|
278
|
+
dry_run,
|
|
279
|
+
position,
|
|
280
|
+
auto_indent,
|
|
281
|
+
use_regex,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
else:
|
|
285
|
+
return (
|
|
286
|
+
"Error: Missing required parameters for InsertBlock (file_path,"
|
|
287
|
+
" content, and either after_pattern or before_pattern)"
|
|
288
|
+
)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import traceback
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
schema = {
|
|
5
|
+
"type": "function",
|
|
6
|
+
"function": {
|
|
7
|
+
"name": "ListChanges",
|
|
8
|
+
"description": "List recent changes made.",
|
|
9
|
+
"parameters": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"properties": {
|
|
12
|
+
"file_path": {"type": "string"},
|
|
13
|
+
"limit": {"type": "integer", "default": 10},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Normalized tool name for lookup
|
|
20
|
+
NORM_NAME = "listchanges"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _execute_list_changes(coder, file_path=None, limit=10):
|
|
24
|
+
"""
|
|
25
|
+
List recent changes made to files.
|
|
26
|
+
|
|
27
|
+
Parameters:
|
|
28
|
+
- coder: The Coder instance
|
|
29
|
+
- file_path: Optional path to filter changes by file
|
|
30
|
+
- limit: Maximum number of changes to list
|
|
31
|
+
|
|
32
|
+
Returns a formatted list of changes.
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
# If file_path is specified, get the absolute path
|
|
36
|
+
rel_file_path = None
|
|
37
|
+
if file_path:
|
|
38
|
+
abs_path = coder.abs_root_path(file_path)
|
|
39
|
+
rel_file_path = coder.get_rel_fname(abs_path)
|
|
40
|
+
|
|
41
|
+
# Get the list of changes
|
|
42
|
+
changes = coder.change_tracker.list_changes(rel_file_path, limit)
|
|
43
|
+
|
|
44
|
+
if not changes:
|
|
45
|
+
if file_path:
|
|
46
|
+
return f"No changes found for file '{file_path}'"
|
|
47
|
+
else:
|
|
48
|
+
return "No changes have been made yet"
|
|
49
|
+
|
|
50
|
+
# Format the changes into a readable list
|
|
51
|
+
result = "Recent changes:\n"
|
|
52
|
+
for i, change in enumerate(changes):
|
|
53
|
+
change_time = datetime.fromtimestamp(change["timestamp"]).strftime("%H:%M:%S")
|
|
54
|
+
change_type = change["type"]
|
|
55
|
+
file_path = change["file_path"]
|
|
56
|
+
change_id = change["id"]
|
|
57
|
+
|
|
58
|
+
result += (
|
|
59
|
+
f"{i + 1}. [{change_id}] {change_time} - {change_type.upper()} on {file_path}\n"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
coder.io.tool_output(result) # Also print to console for user
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
coder.io.tool_error(
|
|
67
|
+
f"Error in ListChanges: {str(e)}\n{traceback.format_exc()}"
|
|
68
|
+
) # Add traceback
|
|
69
|
+
return f"Error: {str(e)}"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def process_response(coder, params):
|
|
73
|
+
"""
|
|
74
|
+
Process the ListChanges tool response.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
coder: The Coder instance
|
|
78
|
+
params: Dictionary of parameters
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
str: Result message
|
|
82
|
+
"""
|
|
83
|
+
file_path = params.get("file_path")
|
|
84
|
+
limit = params.get("limit", 10)
|
|
85
|
+
|
|
86
|
+
return _execute_list_changes(coder, file_path, limit)
|
aider/tools/ls.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
schema = {
|
|
4
|
+
"type": "function",
|
|
5
|
+
"function": {
|
|
6
|
+
"name": "Ls",
|
|
7
|
+
"description": "List files in a directory.",
|
|
8
|
+
"parameters": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"properties": {
|
|
11
|
+
"directory": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "The directory to list.",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
"required": ["directory"],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Normalized tool name for lookup
|
|
22
|
+
NORM_NAME = "ls"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def execute_ls(coder, dir_path=None, directory=None):
|
|
26
|
+
# Handle both positional and keyword arguments for backward compatibility
|
|
27
|
+
if dir_path is None and directory is not None:
|
|
28
|
+
dir_path = directory
|
|
29
|
+
elif dir_path is None:
|
|
30
|
+
return "Error: Missing directory parameter"
|
|
31
|
+
"""
|
|
32
|
+
List files in directory and optionally add some to context.
|
|
33
|
+
|
|
34
|
+
This provides information about the structure of the codebase,
|
|
35
|
+
similar to how a developer would explore directories.
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
# Make the path relative to root if it's absolute
|
|
39
|
+
if dir_path.startswith("/"):
|
|
40
|
+
rel_dir = os.path.relpath(dir_path, coder.root)
|
|
41
|
+
else:
|
|
42
|
+
rel_dir = dir_path
|
|
43
|
+
|
|
44
|
+
# Get absolute path
|
|
45
|
+
abs_dir = coder.abs_root_path(rel_dir)
|
|
46
|
+
|
|
47
|
+
# Check if path exists
|
|
48
|
+
if not os.path.exists(abs_dir):
|
|
49
|
+
coder.io.tool_output(f"⚠️ Directory '{dir_path}' not found")
|
|
50
|
+
return "Directory not found"
|
|
51
|
+
|
|
52
|
+
# Get directory contents
|
|
53
|
+
contents = []
|
|
54
|
+
try:
|
|
55
|
+
with os.scandir(abs_dir) as entries:
|
|
56
|
+
for entry in entries:
|
|
57
|
+
if entry.is_file() and not entry.name.startswith("."):
|
|
58
|
+
rel_path = os.path.join(rel_dir, entry.name)
|
|
59
|
+
contents.append(rel_path)
|
|
60
|
+
except NotADirectoryError:
|
|
61
|
+
# If it's a file, just return the file
|
|
62
|
+
contents = [rel_dir]
|
|
63
|
+
|
|
64
|
+
if contents:
|
|
65
|
+
coder.io.tool_output(f"📋 Listed {len(contents)} file(s) in '{dir_path}'")
|
|
66
|
+
if len(contents) > 10:
|
|
67
|
+
return f"Found {len(contents)} files: {', '.join(contents[:10])}..."
|
|
68
|
+
else:
|
|
69
|
+
return f"Found {len(contents)} files: {', '.join(contents)}"
|
|
70
|
+
else:
|
|
71
|
+
coder.io.tool_output(f"📋 No files found in '{dir_path}'")
|
|
72
|
+
return "No files found in directory"
|
|
73
|
+
except Exception as e:
|
|
74
|
+
coder.io.tool_error(f"Error in ls: {str(e)}")
|
|
75
|
+
return f"Error: {str(e)}"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def process_response(coder, params):
|
|
79
|
+
"""
|
|
80
|
+
Process the Ls tool response.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
coder: The Coder instance
|
|
84
|
+
params: Dictionary of parameters
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
str: Result message
|
|
88
|
+
"""
|
|
89
|
+
directory = params.get("directory")
|
|
90
|
+
if directory is not None:
|
|
91
|
+
return execute_ls(coder, directory)
|
|
92
|
+
else:
|
|
93
|
+
return "Error: Missing 'directory' parameter for Ls"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
schema = {
|
|
4
|
+
"type": "function",
|
|
5
|
+
"function": {
|
|
6
|
+
"name": "MakeEditable",
|
|
7
|
+
"description": "Make a read-only file editable.",
|
|
8
|
+
"parameters": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"properties": {
|
|
11
|
+
"file_path": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "The path to the file to make editable.",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
"required": ["file_path"],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Normalized tool name for lookup
|
|
22
|
+
NORM_NAME = "makeeditable"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Keep the underscore prefix as this function is primarily for internal coder use
|
|
26
|
+
def _execute_make_editable(coder, file_path):
|
|
27
|
+
"""
|
|
28
|
+
Convert a read-only file to an editable file.
|
|
29
|
+
|
|
30
|
+
This allows the LLM to upgrade a file from read-only to editable
|
|
31
|
+
when it determines it needs to make changes to that file.
|
|
32
|
+
"""
|
|
33
|
+
try:
|
|
34
|
+
# Get absolute path
|
|
35
|
+
abs_path = coder.abs_root_path(file_path)
|
|
36
|
+
|
|
37
|
+
# Check if file is already editable
|
|
38
|
+
if abs_path in coder.abs_fnames:
|
|
39
|
+
coder.io.tool_output(f"📝 File '{file_path}' is already editable")
|
|
40
|
+
return "File is already editable"
|
|
41
|
+
|
|
42
|
+
# Check if file exists on disk
|
|
43
|
+
if not os.path.isfile(abs_path):
|
|
44
|
+
coder.io.tool_output(f"⚠️ File '{file_path}' not found")
|
|
45
|
+
return "Error: File not found"
|
|
46
|
+
|
|
47
|
+
# File exists, is not editable, might be read-only or not in context yet
|
|
48
|
+
was_read_only = False
|
|
49
|
+
if abs_path in coder.abs_read_only_fnames:
|
|
50
|
+
coder.abs_read_only_fnames.remove(abs_path)
|
|
51
|
+
was_read_only = True
|
|
52
|
+
|
|
53
|
+
# Add to editable files
|
|
54
|
+
coder.abs_fnames.add(abs_path)
|
|
55
|
+
|
|
56
|
+
if was_read_only:
|
|
57
|
+
coder.io.tool_output(f"📝 Moved '{file_path}' from read-only to editable")
|
|
58
|
+
return "File is now editable (moved from read-only)"
|
|
59
|
+
else:
|
|
60
|
+
# File was not previously in context at all
|
|
61
|
+
coder.io.tool_output(f"📝 Added '{file_path}' directly to editable context")
|
|
62
|
+
# Track if added during exploration? Maybe not needed for direct MakeEditable.
|
|
63
|
+
# coder.files_added_in_exploration.add(rel_path) # Consider if needed
|
|
64
|
+
return "File is now editable (added directly)"
|
|
65
|
+
except Exception as e:
|
|
66
|
+
coder.io.tool_error(f"Error in MakeEditable for '{file_path}': {str(e)}")
|
|
67
|
+
return f"Error: {str(e)}"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def process_response(coder, params):
|
|
71
|
+
"""
|
|
72
|
+
Process the MakeEditable tool response.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
coder: The Coder instance
|
|
76
|
+
params: Dictionary of parameters
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
str: Result message
|
|
80
|
+
"""
|
|
81
|
+
file_path = params.get("file_path")
|
|
82
|
+
if file_path is not None:
|
|
83
|
+
return _execute_make_editable(coder, file_path)
|
|
84
|
+
else:
|
|
85
|
+
return "Error: Missing 'file_path' parameter for MakeEditable"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
schema = {
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "MakeReadonly",
|
|
5
|
+
"description": "Make an editable file read-only.",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"file_path": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "The path to the file to make read-only.",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
"required": ["file_path"],
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Normalized tool name for lookup
|
|
20
|
+
NORM_NAME = "makereadonly"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _execute_make_readonly(coder, file_path):
|
|
24
|
+
"""
|
|
25
|
+
Convert an editable file to a read-only file.
|
|
26
|
+
|
|
27
|
+
This allows the LLM to downgrade a file from editable to read-only
|
|
28
|
+
when it determines it no longer needs to make changes to that file.
|
|
29
|
+
"""
|
|
30
|
+
try:
|
|
31
|
+
# Get absolute path
|
|
32
|
+
abs_path = coder.abs_root_path(file_path)
|
|
33
|
+
|
|
34
|
+
# Check if file is in editable context
|
|
35
|
+
if abs_path not in coder.abs_fnames:
|
|
36
|
+
if abs_path in coder.abs_read_only_fnames:
|
|
37
|
+
coder.io.tool_output(f"📚 File '{file_path}' is already read-only")
|
|
38
|
+
return "File is already read-only"
|
|
39
|
+
else:
|
|
40
|
+
coder.io.tool_output(f"⚠️ File '{file_path}' not in context")
|
|
41
|
+
return "File not in context"
|
|
42
|
+
|
|
43
|
+
# Move from editable to read-only
|
|
44
|
+
coder.abs_fnames.remove(abs_path)
|
|
45
|
+
coder.abs_read_only_fnames.add(abs_path)
|
|
46
|
+
|
|
47
|
+
coder.io.tool_output(f"📚 Made '{file_path}' read-only")
|
|
48
|
+
return "File is now read-only"
|
|
49
|
+
except Exception as e:
|
|
50
|
+
coder.io.tool_error(f"Error making file read-only: {str(e)}")
|
|
51
|
+
return f"Error: {str(e)}"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def process_response(coder, params):
|
|
55
|
+
"""
|
|
56
|
+
Process the MakeReadonly tool response.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
coder: The Coder instance
|
|
60
|
+
params: Dictionary of parameters
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
str: Result message
|
|
64
|
+
"""
|
|
65
|
+
file_path = params.get("file_path")
|
|
66
|
+
if file_path is not None:
|
|
67
|
+
return _execute_make_readonly(coder, file_path)
|
|
68
|
+
else:
|
|
69
|
+
return "Error: Missing 'file_path' parameter for MakeReadonly"
|