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.

Files changed (264) hide show
  1. aider/__init__.py +20 -0
  2. aider/__main__.py +4 -0
  3. aider/_version.py +34 -0
  4. aider/analytics.py +258 -0
  5. aider/args.py +1014 -0
  6. aider/args_formatter.py +228 -0
  7. aider/change_tracker.py +133 -0
  8. aider/coders/__init__.py +36 -0
  9. aider/coders/architect_coder.py +48 -0
  10. aider/coders/architect_prompts.py +40 -0
  11. aider/coders/ask_coder.py +9 -0
  12. aider/coders/ask_prompts.py +35 -0
  13. aider/coders/base_coder.py +3013 -0
  14. aider/coders/base_prompts.py +87 -0
  15. aider/coders/chat_chunks.py +64 -0
  16. aider/coders/context_coder.py +53 -0
  17. aider/coders/context_prompts.py +75 -0
  18. aider/coders/editblock_coder.py +657 -0
  19. aider/coders/editblock_fenced_coder.py +10 -0
  20. aider/coders/editblock_fenced_prompts.py +143 -0
  21. aider/coders/editblock_func_coder.py +141 -0
  22. aider/coders/editblock_func_prompts.py +27 -0
  23. aider/coders/editblock_prompts.py +177 -0
  24. aider/coders/editor_diff_fenced_coder.py +9 -0
  25. aider/coders/editor_diff_fenced_prompts.py +11 -0
  26. aider/coders/editor_editblock_coder.py +9 -0
  27. aider/coders/editor_editblock_prompts.py +21 -0
  28. aider/coders/editor_whole_coder.py +9 -0
  29. aider/coders/editor_whole_prompts.py +12 -0
  30. aider/coders/help_coder.py +16 -0
  31. aider/coders/help_prompts.py +46 -0
  32. aider/coders/navigator_coder.py +2711 -0
  33. aider/coders/navigator_legacy_prompts.py +338 -0
  34. aider/coders/navigator_prompts.py +530 -0
  35. aider/coders/patch_coder.py +706 -0
  36. aider/coders/patch_prompts.py +161 -0
  37. aider/coders/search_replace.py +757 -0
  38. aider/coders/shell.py +37 -0
  39. aider/coders/single_wholefile_func_coder.py +102 -0
  40. aider/coders/single_wholefile_func_prompts.py +27 -0
  41. aider/coders/udiff_coder.py +429 -0
  42. aider/coders/udiff_prompts.py +117 -0
  43. aider/coders/udiff_simple.py +14 -0
  44. aider/coders/udiff_simple_prompts.py +25 -0
  45. aider/coders/wholefile_coder.py +144 -0
  46. aider/coders/wholefile_func_coder.py +134 -0
  47. aider/coders/wholefile_func_prompts.py +27 -0
  48. aider/coders/wholefile_prompts.py +70 -0
  49. aider/commands.py +1946 -0
  50. aider/copypaste.py +72 -0
  51. aider/deprecated.py +126 -0
  52. aider/diffs.py +128 -0
  53. aider/dump.py +29 -0
  54. aider/editor.py +147 -0
  55. aider/exceptions.py +107 -0
  56. aider/format_settings.py +26 -0
  57. aider/gui.py +545 -0
  58. aider/help.py +163 -0
  59. aider/help_pats.py +19 -0
  60. aider/history.py +178 -0
  61. aider/io.py +1257 -0
  62. aider/linter.py +304 -0
  63. aider/llm.py +47 -0
  64. aider/main.py +1297 -0
  65. aider/mcp/__init__.py +94 -0
  66. aider/mcp/server.py +119 -0
  67. aider/mdstream.py +243 -0
  68. aider/models.py +1344 -0
  69. aider/onboarding.py +428 -0
  70. aider/openrouter.py +129 -0
  71. aider/prompts.py +56 -0
  72. aider/queries/tree-sitter-language-pack/README.md +7 -0
  73. aider/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
  74. aider/queries/tree-sitter-language-pack/c-tags.scm +9 -0
  75. aider/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
  76. aider/queries/tree-sitter-language-pack/clojure-tags.scm +7 -0
  77. aider/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
  78. aider/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
  79. aider/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
  80. aider/queries/tree-sitter-language-pack/d-tags.scm +26 -0
  81. aider/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
  82. aider/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
  83. aider/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
  84. aider/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
  85. aider/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
  86. aider/queries/tree-sitter-language-pack/go-tags.scm +42 -0
  87. aider/queries/tree-sitter-language-pack/java-tags.scm +20 -0
  88. aider/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
  89. aider/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
  90. aider/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
  91. aider/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
  92. aider/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
  93. aider/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
  94. aider/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
  95. aider/queries/tree-sitter-language-pack/python-tags.scm +14 -0
  96. aider/queries/tree-sitter-language-pack/r-tags.scm +21 -0
  97. aider/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
  98. aider/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
  99. aider/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
  100. aider/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
  101. aider/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
  102. aider/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
  103. aider/queries/tree-sitter-languages/README.md +23 -0
  104. aider/queries/tree-sitter-languages/c-tags.scm +9 -0
  105. aider/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
  106. aider/queries/tree-sitter-languages/cpp-tags.scm +15 -0
  107. aider/queries/tree-sitter-languages/dart-tags.scm +91 -0
  108. aider/queries/tree-sitter-languages/elisp-tags.scm +8 -0
  109. aider/queries/tree-sitter-languages/elixir-tags.scm +54 -0
  110. aider/queries/tree-sitter-languages/elm-tags.scm +19 -0
  111. aider/queries/tree-sitter-languages/go-tags.scm +30 -0
  112. aider/queries/tree-sitter-languages/hcl-tags.scm +77 -0
  113. aider/queries/tree-sitter-languages/java-tags.scm +20 -0
  114. aider/queries/tree-sitter-languages/javascript-tags.scm +88 -0
  115. aider/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
  116. aider/queries/tree-sitter-languages/matlab-tags.scm +10 -0
  117. aider/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
  118. aider/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
  119. aider/queries/tree-sitter-languages/php-tags.scm +26 -0
  120. aider/queries/tree-sitter-languages/python-tags.scm +12 -0
  121. aider/queries/tree-sitter-languages/ql-tags.scm +26 -0
  122. aider/queries/tree-sitter-languages/ruby-tags.scm +64 -0
  123. aider/queries/tree-sitter-languages/rust-tags.scm +60 -0
  124. aider/queries/tree-sitter-languages/scala-tags.scm +65 -0
  125. aider/queries/tree-sitter-languages/typescript-tags.scm +41 -0
  126. aider/reasoning_tags.py +82 -0
  127. aider/repo.py +621 -0
  128. aider/repomap.py +988 -0
  129. aider/report.py +200 -0
  130. aider/resources/__init__.py +3 -0
  131. aider/resources/model-metadata.json +699 -0
  132. aider/resources/model-settings.yml +2046 -0
  133. aider/run_cmd.py +132 -0
  134. aider/scrape.py +284 -0
  135. aider/sendchat.py +61 -0
  136. aider/special.py +203 -0
  137. aider/tools/__init__.py +26 -0
  138. aider/tools/command.py +58 -0
  139. aider/tools/command_interactive.py +53 -0
  140. aider/tools/delete_block.py +120 -0
  141. aider/tools/delete_line.py +112 -0
  142. aider/tools/delete_lines.py +137 -0
  143. aider/tools/extract_lines.py +276 -0
  144. aider/tools/grep.py +171 -0
  145. aider/tools/indent_lines.py +155 -0
  146. aider/tools/insert_block.py +211 -0
  147. aider/tools/list_changes.py +51 -0
  148. aider/tools/ls.py +49 -0
  149. aider/tools/make_editable.py +46 -0
  150. aider/tools/make_readonly.py +29 -0
  151. aider/tools/remove.py +48 -0
  152. aider/tools/replace_all.py +77 -0
  153. aider/tools/replace_line.py +125 -0
  154. aider/tools/replace_lines.py +160 -0
  155. aider/tools/replace_text.py +125 -0
  156. aider/tools/show_numbered_context.py +101 -0
  157. aider/tools/tool_utils.py +313 -0
  158. aider/tools/undo_change.py +60 -0
  159. aider/tools/view.py +13 -0
  160. aider/tools/view_files_at_glob.py +65 -0
  161. aider/tools/view_files_matching.py +103 -0
  162. aider/tools/view_files_with_symbol.py +121 -0
  163. aider/urls.py +17 -0
  164. aider/utils.py +454 -0
  165. aider/versioncheck.py +113 -0
  166. aider/voice.py +187 -0
  167. aider/waiting.py +221 -0
  168. aider/watch.py +318 -0
  169. aider/watch_prompts.py +12 -0
  170. aider/website/Gemfile +8 -0
  171. aider/website/_includes/blame.md +162 -0
  172. aider/website/_includes/get-started.md +22 -0
  173. aider/website/_includes/help-tip.md +5 -0
  174. aider/website/_includes/help.md +24 -0
  175. aider/website/_includes/install.md +5 -0
  176. aider/website/_includes/keys.md +4 -0
  177. aider/website/_includes/model-warnings.md +67 -0
  178. aider/website/_includes/multi-line.md +22 -0
  179. aider/website/_includes/python-m-aider.md +5 -0
  180. aider/website/_includes/recording.css +228 -0
  181. aider/website/_includes/recording.md +34 -0
  182. aider/website/_includes/replit-pipx.md +9 -0
  183. aider/website/_includes/works-best.md +1 -0
  184. aider/website/_sass/custom/custom.scss +103 -0
  185. aider/website/docs/config/adv-model-settings.md +2260 -0
  186. aider/website/docs/config/aider_conf.md +548 -0
  187. aider/website/docs/config/api-keys.md +90 -0
  188. aider/website/docs/config/dotenv.md +493 -0
  189. aider/website/docs/config/editor.md +127 -0
  190. aider/website/docs/config/mcp.md +95 -0
  191. aider/website/docs/config/model-aliases.md +104 -0
  192. aider/website/docs/config/options.md +890 -0
  193. aider/website/docs/config/reasoning.md +210 -0
  194. aider/website/docs/config.md +44 -0
  195. aider/website/docs/faq.md +384 -0
  196. aider/website/docs/git.md +76 -0
  197. aider/website/docs/index.md +47 -0
  198. aider/website/docs/install/codespaces.md +39 -0
  199. aider/website/docs/install/docker.md +57 -0
  200. aider/website/docs/install/optional.md +100 -0
  201. aider/website/docs/install/replit.md +8 -0
  202. aider/website/docs/install.md +115 -0
  203. aider/website/docs/languages.md +264 -0
  204. aider/website/docs/legal/contributor-agreement.md +111 -0
  205. aider/website/docs/legal/privacy.md +104 -0
  206. aider/website/docs/llms/anthropic.md +77 -0
  207. aider/website/docs/llms/azure.md +48 -0
  208. aider/website/docs/llms/bedrock.md +132 -0
  209. aider/website/docs/llms/cohere.md +34 -0
  210. aider/website/docs/llms/deepseek.md +32 -0
  211. aider/website/docs/llms/gemini.md +49 -0
  212. aider/website/docs/llms/github.md +111 -0
  213. aider/website/docs/llms/groq.md +36 -0
  214. aider/website/docs/llms/lm-studio.md +39 -0
  215. aider/website/docs/llms/ollama.md +75 -0
  216. aider/website/docs/llms/openai-compat.md +39 -0
  217. aider/website/docs/llms/openai.md +58 -0
  218. aider/website/docs/llms/openrouter.md +78 -0
  219. aider/website/docs/llms/other.md +111 -0
  220. aider/website/docs/llms/vertex.md +50 -0
  221. aider/website/docs/llms/warnings.md +10 -0
  222. aider/website/docs/llms/xai.md +53 -0
  223. aider/website/docs/llms.md +54 -0
  224. aider/website/docs/more/analytics.md +127 -0
  225. aider/website/docs/more/edit-formats.md +116 -0
  226. aider/website/docs/more/infinite-output.md +159 -0
  227. aider/website/docs/more-info.md +8 -0
  228. aider/website/docs/recordings/auto-accept-architect.md +31 -0
  229. aider/website/docs/recordings/dont-drop-original-read-files.md +35 -0
  230. aider/website/docs/recordings/index.md +21 -0
  231. aider/website/docs/recordings/model-accepts-settings.md +69 -0
  232. aider/website/docs/recordings/tree-sitter-language-pack.md +80 -0
  233. aider/website/docs/repomap.md +112 -0
  234. aider/website/docs/scripting.md +100 -0
  235. aider/website/docs/troubleshooting/aider-not-found.md +24 -0
  236. aider/website/docs/troubleshooting/edit-errors.md +76 -0
  237. aider/website/docs/troubleshooting/imports.md +62 -0
  238. aider/website/docs/troubleshooting/models-and-keys.md +54 -0
  239. aider/website/docs/troubleshooting/support.md +79 -0
  240. aider/website/docs/troubleshooting/token-limits.md +96 -0
  241. aider/website/docs/troubleshooting/warnings.md +12 -0
  242. aider/website/docs/troubleshooting.md +11 -0
  243. aider/website/docs/usage/browser.md +57 -0
  244. aider/website/docs/usage/caching.md +49 -0
  245. aider/website/docs/usage/commands.md +133 -0
  246. aider/website/docs/usage/conventions.md +119 -0
  247. aider/website/docs/usage/copypaste.md +121 -0
  248. aider/website/docs/usage/images-urls.md +48 -0
  249. aider/website/docs/usage/lint-test.md +118 -0
  250. aider/website/docs/usage/modes.md +211 -0
  251. aider/website/docs/usage/not-code.md +179 -0
  252. aider/website/docs/usage/notifications.md +87 -0
  253. aider/website/docs/usage/tips.md +79 -0
  254. aider/website/docs/usage/tutorials.md +30 -0
  255. aider/website/docs/usage/voice.md +121 -0
  256. aider/website/docs/usage/watch.md +294 -0
  257. aider/website/docs/usage.md +102 -0
  258. aider/website/share/index.md +101 -0
  259. aider_ce-0.87.2.dev9.dist-info/METADATA +543 -0
  260. aider_ce-0.87.2.dev9.dist-info/RECORD +264 -0
  261. aider_ce-0.87.2.dev9.dist-info/WHEEL +5 -0
  262. aider_ce-0.87.2.dev9.dist-info/entry_points.txt +3 -0
  263. aider_ce-0.87.2.dev9.dist-info/licenses/LICENSE.txt +202 -0
  264. aider_ce-0.87.2.dev9.dist-info/top_level.txt +1 -0
aider/tools/command.py ADDED
@@ -0,0 +1,58 @@
1
+ # Import necessary functions
2
+ from aider.run_cmd import run_cmd_subprocess
3
+
4
+
5
+ def _execute_command(coder, command_string):
6
+ """
7
+ Execute a non-interactive shell command after user confirmation.
8
+ """
9
+ try:
10
+ # Ask for confirmation before executing.
11
+ # allow_never=True enables the 'Always' option.
12
+ # confirm_ask handles remembering the 'Always' choice based on the subject.
13
+ confirmed = coder.io.confirm_ask(
14
+ "Allow execution of this command?",
15
+ subject=command_string,
16
+ explicit_yes_required=True, # Require explicit 'yes' or 'always'
17
+ allow_never=True, # Enable the 'Always' option
18
+ )
19
+
20
+ if not confirmed:
21
+ # This happens if the user explicitly says 'no' this time.
22
+ # If 'Always' was chosen previously, confirm_ask returns True directly.
23
+ coder.io.tool_output(f"Skipped execution of shell command: {command_string}")
24
+ return "Shell command execution skipped by user."
25
+
26
+ # Proceed with execution if confirmed is True
27
+ coder.io.tool_output(f"⚙️ Executing non-interactive shell command: {command_string}")
28
+
29
+ # Use run_cmd_subprocess for non-interactive execution
30
+ exit_status, combined_output = run_cmd_subprocess(
31
+ command_string, verbose=coder.verbose, cwd=coder.root # Execute in the project root
32
+ )
33
+
34
+ # Format the output for the result message, include more content
35
+ output_content = combined_output or ""
36
+ # Use the existing token threshold constant as the character limit for truncation
37
+ output_limit = coder.large_file_token_threshold
38
+ if len(output_content) > output_limit:
39
+ # Truncate and add a clear message using the constant value
40
+ output_content = (
41
+ output_content[:output_limit]
42
+ + f"\n... (output truncated at {output_limit} characters, based on"
43
+ " large_file_token_threshold)"
44
+ )
45
+
46
+ if exit_status == 0:
47
+ return f"Shell command executed successfully (exit code 0). Output:\n{output_content}"
48
+ else:
49
+ return f"Shell command failed with exit code {exit_status}. Output:\n{output_content}"
50
+
51
+ except Exception as e:
52
+ coder.io.tool_error(
53
+ f"Error executing non-interactive shell command '{command_string}': {str(e)}"
54
+ )
55
+ # Optionally include traceback for debugging if verbose
56
+ # if coder.verbose:
57
+ # coder.io.tool_error(traceback.format_exc())
58
+ return f"Error executing command: {str(e)}"
@@ -0,0 +1,53 @@
1
+ # Import necessary functions
2
+ from aider.run_cmd import run_cmd
3
+
4
+
5
+ def _execute_command_interactive(coder, command_string):
6
+ """
7
+ Execute an interactive shell command using run_cmd (which uses pexpect/PTY).
8
+ """
9
+ try:
10
+ coder.io.tool_output(f"⚙️ Starting interactive shell command: {command_string}")
11
+ coder.io.tool_output(">>> You may need to interact with the command below <<<")
12
+
13
+ # Use run_cmd which handles PTY logic
14
+ exit_status, combined_output = run_cmd(
15
+ command_string,
16
+ verbose=coder.verbose, # Pass verbose flag
17
+ error_print=coder.io.tool_error, # Use io for error printing
18
+ cwd=coder.root, # Execute in the project root
19
+ )
20
+
21
+ coder.io.tool_output(">>> Interactive command finished <<<")
22
+
23
+ # Format the output for the result message, include more content
24
+ output_content = combined_output or ""
25
+ # Use the existing token threshold constant as the character limit for truncation
26
+ output_limit = coder.large_file_token_threshold
27
+ if len(output_content) > output_limit:
28
+ # Truncate and add a clear message using the constant value
29
+ output_content = (
30
+ output_content[:output_limit]
31
+ + f"\n... (output truncated at {output_limit} characters, based on"
32
+ " large_file_token_threshold)"
33
+ )
34
+
35
+ if exit_status == 0:
36
+ return (
37
+ "Interactive command finished successfully (exit code 0)."
38
+ f" Output:\n{output_content}"
39
+ )
40
+ else:
41
+ return (
42
+ f"Interactive command finished with exit code {exit_status}."
43
+ f" Output:\n{output_content}"
44
+ )
45
+
46
+ except Exception as e:
47
+ coder.io.tool_error(
48
+ f"Error executing interactive shell command '{command_string}': {str(e)}"
49
+ )
50
+ # Optionally include traceback for debugging if verbose
51
+ # if coder.verbose:
52
+ # coder.io.tool_error(traceback.format_exc())
53
+ return f"Error executing interactive command: {str(e)}"
@@ -0,0 +1,120 @@
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_delete_block(
15
+ coder,
16
+ file_path,
17
+ start_pattern,
18
+ end_pattern=None,
19
+ line_count=None,
20
+ near_context=None,
21
+ occurrence=1,
22
+ change_id=None,
23
+ dry_run=False,
24
+ ):
25
+ """
26
+ Delete a block of text between start_pattern and end_pattern (inclusive).
27
+ Uses utility functions for validation, finding lines, and applying changes.
28
+ """
29
+ tool_name = "DeleteBlock"
30
+ try:
31
+ # 1. Validate file and get content
32
+ abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
33
+ lines = original_content.splitlines()
34
+
35
+ # 2. Find the start line
36
+ pattern_desc = f"Start pattern '{start_pattern}'"
37
+ if near_context:
38
+ pattern_desc += f" near context '{near_context}'"
39
+ start_pattern_indices = find_pattern_indices(lines, start_pattern, near_context)
40
+ start_line_idx = select_occurrence_index(start_pattern_indices, occurrence, pattern_desc)
41
+
42
+ # 3. Determine the end line, passing pattern_desc for better error messages
43
+ start_line, end_line = determine_line_range(
44
+ coder=coder,
45
+ file_path=rel_path,
46
+ lines=lines,
47
+ start_pattern_line_index=start_line_idx,
48
+ end_pattern=end_pattern,
49
+ line_count=line_count,
50
+ target_symbol=None, # DeleteBlock uses patterns, not symbols
51
+ pattern_desc=pattern_desc,
52
+ )
53
+
54
+ # 4. Prepare the deletion
55
+ deleted_lines = lines[start_line : end_line + 1]
56
+ new_lines = lines[:start_line] + lines[end_line + 1 :]
57
+ new_content = "\n".join(new_lines)
58
+
59
+ if original_content == new_content:
60
+ coder.io.tool_warning("No changes made: deletion would not change file")
61
+ return "Warning: No changes made (deletion would not change file)"
62
+
63
+ # 5. Generate diff for feedback
64
+ diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
65
+ num_deleted = end_line - start_line + 1
66
+ num_occurrences = len(start_pattern_indices)
67
+ occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""
68
+
69
+ # 6. Handle dry run
70
+ if dry_run:
71
+ dry_run_message = (
72
+ f"Dry run: Would delete {num_deleted} lines ({start_line + 1}-{end_line + 1}) based"
73
+ f" on {occurrence_str}start pattern '{start_pattern}' in {file_path}."
74
+ )
75
+ return format_tool_result(
76
+ coder,
77
+ tool_name,
78
+ "",
79
+ dry_run=True,
80
+ dry_run_message=dry_run_message,
81
+ diff_snippet=diff_snippet,
82
+ )
83
+
84
+ # 7. Apply Change (Not dry run)
85
+ metadata = {
86
+ "start_line": start_line + 1,
87
+ "end_line": end_line + 1,
88
+ "start_pattern": start_pattern,
89
+ "end_pattern": end_pattern,
90
+ "line_count": line_count,
91
+ "near_context": near_context,
92
+ "occurrence": occurrence,
93
+ "deleted_content": "\n".join(deleted_lines),
94
+ }
95
+ final_change_id = apply_change(
96
+ coder,
97
+ abs_path,
98
+ rel_path,
99
+ original_content,
100
+ new_content,
101
+ "deleteblock",
102
+ metadata,
103
+ change_id,
104
+ )
105
+
106
+ # 8. Format and return result, adding line range to success message
107
+ success_message = (
108
+ f"Deleted {num_deleted} lines ({start_line + 1}-{end_line + 1}) (from"
109
+ f" {occurrence_str}start pattern) in {file_path}"
110
+ )
111
+ return format_tool_result(
112
+ coder, tool_name, success_message, change_id=final_change_id, diff_snippet=diff_snippet
113
+ )
114
+
115
+ except ToolError as e:
116
+ # Handle errors raised by utility functions (expected errors)
117
+ return handle_tool_error(coder, tool_name, e, add_traceback=False)
118
+ except Exception as e:
119
+ # Handle unexpected errors
120
+ return handle_tool_error(coder, tool_name, e)
@@ -0,0 +1,112 @@
1
+ import os
2
+
3
+ from .tool_utils import (
4
+ ToolError,
5
+ apply_change,
6
+ format_tool_result,
7
+ generate_unified_diff_snippet,
8
+ handle_tool_error,
9
+ )
10
+
11
+
12
+ def _execute_delete_line(coder, file_path, line_number, change_id=None, dry_run=False):
13
+ """
14
+ Delete a specific line number (1-based).
15
+
16
+ Parameters:
17
+ - coder: The Coder instance
18
+ - file_path: Path to the file to modify
19
+ - line_number: The 1-based line number to delete
20
+ - change_id: Optional ID for tracking the change
21
+ - dry_run: If True, simulate the change without modifying the file
22
+
23
+ Returns a result message.
24
+ """
25
+
26
+ tool_name = "DeleteLine"
27
+ try:
28
+ # Get absolute file path
29
+ abs_path = coder.abs_root_path(file_path)
30
+ rel_path = coder.get_rel_fname(abs_path)
31
+
32
+ # Check if file exists
33
+ if not os.path.isfile(abs_path):
34
+ raise ToolError(f"File '{file_path}' not found")
35
+
36
+ # Check if file is in editable context
37
+ if abs_path not in coder.abs_fnames:
38
+ if abs_path in coder.abs_read_only_fnames:
39
+ raise ToolError(f"File '{file_path}' is read-only. Use MakeEditable first.")
40
+ else:
41
+ raise ToolError(f"File '{file_path}' not in context")
42
+
43
+ # Reread file content immediately before modification
44
+ file_content = coder.io.read_text(abs_path)
45
+ if file_content is None:
46
+ raise ToolError(f"Could not read file '{file_path}'")
47
+
48
+ lines = file_content.splitlines()
49
+ original_content = file_content
50
+
51
+ # Validate line number
52
+ try:
53
+ line_num_int = int(line_number)
54
+ if line_num_int < 1 or line_num_int > len(lines):
55
+ raise ToolError(f"Line number {line_num_int} is out of range (1-{len(lines)})")
56
+ line_idx = line_num_int - 1 # Convert to 0-based index
57
+ except ValueError:
58
+ raise ToolError(f"Invalid line_number value: '{line_number}'. Must be an integer.")
59
+
60
+ # Prepare the deletion
61
+ deleted_line = lines[line_idx]
62
+ new_lines = lines[:line_idx] + lines[line_idx + 1 :]
63
+ new_content = "\n".join(new_lines)
64
+
65
+ if original_content == new_content:
66
+ coder.io.tool_warning(
67
+ f"No changes made: deleting line {line_num_int} would not change file"
68
+ )
69
+ return f"Warning: No changes made (deleting line {line_num_int} would not change file)"
70
+
71
+ # Generate diff snippet
72
+ diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
73
+
74
+ # Handle dry run
75
+ if dry_run:
76
+ dry_run_message = f"Dry run: Would delete line {line_num_int} in {file_path}"
77
+ return format_tool_result(
78
+ coder,
79
+ tool_name,
80
+ "",
81
+ dry_run=True,
82
+ dry_run_message=dry_run_message,
83
+ diff_snippet=diff_snippet,
84
+ )
85
+
86
+ # --- Apply Change (Not dry run) ---
87
+ metadata = {"line_number": line_num_int, "deleted_content": deleted_line}
88
+ final_change_id = apply_change(
89
+ coder,
90
+ abs_path,
91
+ rel_path,
92
+ original_content,
93
+ new_content,
94
+ "deleteline",
95
+ metadata,
96
+ change_id,
97
+ )
98
+
99
+ coder.aider_edited_files.add(rel_path)
100
+
101
+ # Format and return result
102
+ success_message = f"Deleted line {line_num_int} in {file_path}"
103
+ return format_tool_result(
104
+ coder, tool_name, success_message, change_id=final_change_id, diff_snippet=diff_snippet
105
+ )
106
+
107
+ except ToolError as e:
108
+ # Handle errors raised by utility functions (expected errors)
109
+ return handle_tool_error(coder, tool_name, e, add_traceback=False)
110
+ except Exception as e:
111
+ # Handle unexpected errors
112
+ return handle_tool_error(coder, tool_name, e)
@@ -0,0 +1,137 @@
1
+ import os
2
+
3
+ from .tool_utils import (
4
+ ToolError,
5
+ apply_change,
6
+ format_tool_result,
7
+ generate_unified_diff_snippet,
8
+ handle_tool_error,
9
+ )
10
+
11
+
12
+ def _execute_delete_lines(coder, file_path, start_line, end_line, change_id=None, dry_run=False):
13
+ """
14
+ Delete a range of lines (1-based, inclusive).
15
+
16
+ Parameters:
17
+ - coder: The Coder instance
18
+ - file_path: Path to the file to modify
19
+ - start_line: The 1-based starting line number to delete
20
+ - end_line: The 1-based ending line number to delete
21
+ - change_id: Optional ID for tracking the change
22
+ - dry_run: If True, simulate the change without modifying the file
23
+
24
+ Returns a result message.
25
+ """
26
+ tool_name = "DeleteLines"
27
+ try:
28
+ # Get absolute file path
29
+ abs_path = coder.abs_root_path(file_path)
30
+ rel_path = coder.get_rel_fname(abs_path)
31
+
32
+ # Check if file exists
33
+ if not os.path.isfile(abs_path):
34
+ raise ToolError(f"File '{file_path}' not found")
35
+
36
+ # Check if file is in editable context
37
+ if abs_path not in coder.abs_fnames:
38
+ if abs_path in coder.abs_read_only_fnames:
39
+ raise ToolError(f"File '{file_path}' is read-only. Use MakeEditable first.")
40
+ else:
41
+ raise ToolError(f"File '{file_path}' not in context")
42
+
43
+ # Reread file content immediately before modification
44
+ file_content = coder.io.read_text(abs_path)
45
+ if file_content is None:
46
+ raise ToolError(f"Could not read file '{file_path}'")
47
+
48
+ lines = file_content.splitlines()
49
+ original_content = file_content
50
+
51
+ # Validate line numbers
52
+ try:
53
+ start_line_int = int(start_line)
54
+ end_line_int = int(end_line)
55
+
56
+ if start_line_int < 1 or start_line_int > len(lines):
57
+ raise ToolError(f"Start line {start_line_int} is out of range (1-{len(lines)})")
58
+ if end_line_int < 1 or end_line_int > len(lines):
59
+ raise ToolError(f"End line {end_line_int} is out of range (1-{len(lines)})")
60
+ if start_line_int > end_line_int:
61
+ raise ToolError(
62
+ f"Start line {start_line_int} cannot be after end line {end_line_int}"
63
+ )
64
+
65
+ start_idx = start_line_int - 1 # Convert to 0-based index
66
+ end_idx = end_line_int - 1 # Convert to 0-based index
67
+ except ValueError:
68
+ raise ToolError(
69
+ f"Invalid line numbers: '{start_line}', '{end_line}'. Must be integers."
70
+ )
71
+
72
+ # Prepare the deletion
73
+ deleted_lines = lines[start_idx : end_idx + 1]
74
+ new_lines = lines[:start_idx] + lines[end_idx + 1 :]
75
+ new_content = "\n".join(new_lines)
76
+
77
+ if original_content == new_content:
78
+ coder.io.tool_warning(
79
+ f"No changes made: deleting lines {start_line_int}-{end_line_int} would not change"
80
+ " file"
81
+ )
82
+ return (
83
+ f"Warning: No changes made (deleting lines {start_line_int}-{end_line_int} would"
84
+ " not change file)"
85
+ )
86
+
87
+ # Generate diff snippet
88
+ diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
89
+
90
+ # Handle dry run
91
+ if dry_run:
92
+ dry_run_message = (
93
+ f"Dry run: Would delete lines {start_line_int}-{end_line_int} in {file_path}"
94
+ )
95
+ return format_tool_result(
96
+ coder,
97
+ tool_name,
98
+ "",
99
+ dry_run=True,
100
+ dry_run_message=dry_run_message,
101
+ diff_snippet=diff_snippet,
102
+ )
103
+
104
+ # --- Apply Change (Not dry run) ---
105
+ metadata = {
106
+ "start_line": start_line_int,
107
+ "end_line": end_line_int,
108
+ "deleted_content": "\n".join(deleted_lines),
109
+ }
110
+
111
+ final_change_id = apply_change(
112
+ coder,
113
+ abs_path,
114
+ rel_path,
115
+ original_content,
116
+ new_content,
117
+ "deletelines",
118
+ metadata,
119
+ change_id,
120
+ )
121
+
122
+ coder.aider_edited_files.add(rel_path)
123
+ num_deleted = end_idx - start_idx + 1
124
+ # Format and return result
125
+ success_message = (
126
+ f"Deleted {num_deleted} lines ({start_line_int}-{end_line_int}) in {file_path}"
127
+ )
128
+ return format_tool_result(
129
+ coder, tool_name, success_message, change_id=final_change_id, diff_snippet=diff_snippet
130
+ )
131
+
132
+ except ToolError as e:
133
+ # Handle errors raised by utility functions (expected errors)
134
+ return handle_tool_error(coder, tool_name, e, add_traceback=False)
135
+ except Exception as e:
136
+ # Handle unexpected errors
137
+ return handle_tool_error(coder, tool_name, e)