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