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