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