aider-ce 0.88.20__py3-none-any.whl → 0.88.38__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 (113) hide show
  1. aider/__init__.py +1 -1
  2. aider/_version.py +2 -2
  3. aider/args.py +63 -43
  4. aider/coders/agent_coder.py +331 -79
  5. aider/coders/agent_prompts.py +3 -15
  6. aider/coders/architect_coder.py +21 -5
  7. aider/coders/base_coder.py +661 -413
  8. aider/coders/base_prompts.py +6 -3
  9. aider/coders/chat_chunks.py +39 -17
  10. aider/commands.py +79 -15
  11. aider/diffs.py +10 -9
  12. aider/exceptions.py +1 -1
  13. aider/helpers/coroutines.py +8 -0
  14. aider/helpers/requests.py +45 -0
  15. aider/history.py +5 -0
  16. aider/io.py +179 -25
  17. aider/main.py +86 -35
  18. aider/models.py +16 -8
  19. aider/queries/tree-sitter-language-pack/c-tags.scm +3 -0
  20. aider/queries/tree-sitter-language-pack/clojure-tags.scm +5 -0
  21. aider/queries/tree-sitter-language-pack/commonlisp-tags.scm +5 -0
  22. aider/queries/tree-sitter-language-pack/cpp-tags.scm +3 -0
  23. aider/queries/tree-sitter-language-pack/csharp-tags.scm +6 -0
  24. aider/queries/tree-sitter-language-pack/dart-tags.scm +5 -0
  25. aider/queries/tree-sitter-language-pack/elixir-tags.scm +5 -0
  26. aider/queries/tree-sitter-language-pack/elm-tags.scm +3 -0
  27. aider/queries/tree-sitter-language-pack/go-tags.scm +7 -0
  28. aider/queries/tree-sitter-language-pack/java-tags.scm +6 -0
  29. aider/queries/tree-sitter-language-pack/javascript-tags.scm +8 -0
  30. aider/queries/tree-sitter-language-pack/lua-tags.scm +5 -0
  31. aider/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +3 -0
  32. aider/queries/tree-sitter-language-pack/python-tags.scm +10 -0
  33. aider/queries/tree-sitter-language-pack/r-tags.scm +6 -0
  34. aider/queries/tree-sitter-language-pack/ruby-tags.scm +5 -0
  35. aider/queries/tree-sitter-language-pack/rust-tags.scm +3 -0
  36. aider/queries/tree-sitter-language-pack/solidity-tags.scm +1 -1
  37. aider/queries/tree-sitter-language-pack/swift-tags.scm +4 -1
  38. aider/queries/tree-sitter-languages/c-tags.scm +3 -0
  39. aider/queries/tree-sitter-languages/c_sharp-tags.scm +6 -0
  40. aider/queries/tree-sitter-languages/cpp-tags.scm +3 -0
  41. aider/queries/tree-sitter-languages/dart-tags.scm +2 -1
  42. aider/queries/tree-sitter-languages/elixir-tags.scm +5 -0
  43. aider/queries/tree-sitter-languages/elm-tags.scm +3 -0
  44. aider/queries/tree-sitter-languages/fortran-tags.scm +3 -0
  45. aider/queries/tree-sitter-languages/go-tags.scm +6 -0
  46. aider/queries/tree-sitter-languages/haskell-tags.scm +2 -0
  47. aider/queries/tree-sitter-languages/java-tags.scm +6 -0
  48. aider/queries/tree-sitter-languages/javascript-tags.scm +8 -0
  49. aider/queries/tree-sitter-languages/julia-tags.scm +2 -2
  50. aider/queries/tree-sitter-languages/kotlin-tags.scm +3 -0
  51. aider/queries/tree-sitter-languages/ocaml_interface-tags.scm +6 -0
  52. aider/queries/tree-sitter-languages/php-tags.scm +6 -0
  53. aider/queries/tree-sitter-languages/python-tags.scm +10 -0
  54. aider/queries/tree-sitter-languages/ruby-tags.scm +5 -0
  55. aider/queries/tree-sitter-languages/rust-tags.scm +3 -0
  56. aider/queries/tree-sitter-languages/scala-tags.scm +2 -3
  57. aider/queries/tree-sitter-languages/typescript-tags.scm +3 -0
  58. aider/queries/tree-sitter-languages/zig-tags.scm +20 -3
  59. aider/repomap.py +71 -11
  60. aider/resources/model-metadata.json +27335 -635
  61. aider/resources/model-settings.yml +190 -0
  62. aider/scrape.py +2 -0
  63. aider/tools/__init__.py +2 -0
  64. aider/tools/command.py +84 -94
  65. aider/tools/command_interactive.py +95 -110
  66. aider/tools/delete_block.py +131 -159
  67. aider/tools/delete_line.py +97 -132
  68. aider/tools/delete_lines.py +120 -160
  69. aider/tools/extract_lines.py +288 -312
  70. aider/tools/finished.py +30 -43
  71. aider/tools/git_branch.py +107 -109
  72. aider/tools/git_diff.py +44 -56
  73. aider/tools/git_log.py +39 -53
  74. aider/tools/git_remote.py +37 -51
  75. aider/tools/git_show.py +33 -47
  76. aider/tools/git_status.py +30 -44
  77. aider/tools/grep.py +214 -242
  78. aider/tools/indent_lines.py +175 -201
  79. aider/tools/insert_block.py +220 -253
  80. aider/tools/list_changes.py +65 -80
  81. aider/tools/ls.py +64 -80
  82. aider/tools/make_editable.py +57 -73
  83. aider/tools/make_readonly.py +50 -66
  84. aider/tools/remove.py +64 -80
  85. aider/tools/replace_all.py +96 -109
  86. aider/tools/replace_line.py +118 -156
  87. aider/tools/replace_lines.py +160 -197
  88. aider/tools/replace_text.py +159 -160
  89. aider/tools/show_numbered_context.py +115 -141
  90. aider/tools/thinking.py +52 -0
  91. aider/tools/undo_change.py +78 -91
  92. aider/tools/update_todo_list.py +130 -138
  93. aider/tools/utils/base_tool.py +64 -0
  94. aider/tools/utils/output.py +118 -0
  95. aider/tools/view.py +38 -54
  96. aider/tools/view_files_matching.py +131 -134
  97. aider/tools/view_files_with_symbol.py +108 -120
  98. aider/urls.py +1 -1
  99. aider/versioncheck.py +4 -3
  100. aider/website/docs/config/adv-model-settings.md +237 -0
  101. aider/website/docs/config/agent-mode.md +36 -3
  102. aider/website/docs/config/model-aliases.md +2 -1
  103. aider/website/docs/faq.md +6 -11
  104. aider/website/docs/languages.md +2 -2
  105. aider/website/docs/more/infinite-output.md +27 -0
  106. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/METADATA +112 -70
  107. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/RECORD +112 -107
  108. aider_ce-0.88.38.dist-info/entry_points.txt +6 -0
  109. aider_ce-0.88.20.dist-info/entry_points.txt +0 -2
  110. /aider/tools/{tool_utils.py → utils/helpers.py} +0 -0
  111. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/WHEEL +0 -0
  112. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/licenses/LICENSE.txt +0 -0
  113. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/top_level.txt +0 -0
aider/tools/ls.py CHANGED
@@ -1,93 +1,77 @@
1
1
  import os
2
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.",
3
+ from aider.tools.utils.base_tool import BaseTool
4
+
5
+
6
+ class Tool(BaseTool):
7
+ NORM_NAME = "ls"
8
+ SCHEMA = {
9
+ "type": "function",
10
+ "function": {
11
+ "name": "Ls",
12
+ "description": "List files in a directory.",
13
+ "parameters": {
14
+ "type": "object",
15
+ "properties": {
16
+ "directory": {
17
+ "type": "string",
18
+ "description": "The directory to list.",
19
+ },
14
20
  },
21
+ "required": ["directory"],
15
22
  },
16
- "required": ["directory"],
17
23
  },
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.
24
+ }
33
25
 
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
26
+ @classmethod
27
+ def execute(cls, coder, dir_path=None, directory=None):
28
+ # Handle both positional and keyword arguments for backward compatibility
29
+ if dir_path is None and directory is not None:
30
+ dir_path = directory
31
+ elif dir_path is None:
32
+ return "Error: Missing directory parameter"
33
+ """
34
+ List files in directory and optionally add some to context.
43
35
 
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 = []
36
+ This provides information about the structure of the codebase,
37
+ similar to how a developer would explore directories.
38
+ """
54
39
  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])}..."
40
+ # Make the path relative to root if it's absolute
41
+ if dir_path.startswith("/"):
42
+ rel_dir = os.path.relpath(dir_path, coder.root)
68
43
  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)}"
44
+ rel_dir = dir_path
76
45
 
46
+ # Get absolute path
47
+ abs_dir = coder.abs_root_path(rel_dir)
77
48
 
78
- def process_response(coder, params):
79
- """
80
- Process the Ls tool response.
49
+ # Check if path exists
50
+ if not os.path.exists(abs_dir):
51
+ coder.io.tool_output(f"⚠️ Directory '{dir_path}' not found")
52
+ return "Directory not found"
81
53
 
82
- Args:
83
- coder: The Coder instance
84
- params: Dictionary of parameters
54
+ # Get directory contents
55
+ contents = []
56
+ try:
57
+ with os.scandir(abs_dir) as entries:
58
+ for entry in entries:
59
+ if entry.is_file() and not entry.name.startswith("."):
60
+ rel_path = os.path.join(rel_dir, entry.name)
61
+ contents.append(rel_path)
62
+ except NotADirectoryError:
63
+ # If it's a file, just return the file
64
+ contents = [rel_dir]
85
65
 
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"
66
+ if contents:
67
+ coder.io.tool_output(f"📋 Listed {len(contents)} file(s) in '{dir_path}'")
68
+ if len(contents) > 10:
69
+ return f"Found {len(contents)} files: {', '.join(contents[:10])}..."
70
+ else:
71
+ return f"Found {len(contents)} files: {', '.join(contents)}"
72
+ else:
73
+ coder.io.tool_output(f"📋 No files found in '{dir_path}'")
74
+ return "No files found in directory"
75
+ except Exception as e:
76
+ coder.io.tool_error(f"Error in ls: {str(e)}")
77
+ return f"Error: {str(e)}"
@@ -1,85 +1,69 @@
1
1
  import os
2
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.",
3
+ from aider.tools.utils.base_tool import BaseTool
4
+
5
+
6
+ class Tool(BaseTool):
7
+ NORM_NAME = "makeeditable"
8
+ SCHEMA = {
9
+ "type": "function",
10
+ "function": {
11
+ "name": "MakeEditable",
12
+ "description": "Make a read-only file editable.",
13
+ "parameters": {
14
+ "type": "object",
15
+ "properties": {
16
+ "file_path": {
17
+ "type": "string",
18
+ "description": "The path to the file to make editable.",
19
+ },
14
20
  },
21
+ "required": ["file_path"],
15
22
  },
16
- "required": ["file_path"],
17
23
  },
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"
24
+ }
46
25
 
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
26
+ # Keep the underscore prefix as this function is primarily for internal coder use
27
+ @classmethod
28
+ def execute(cls, coder, file_path):
29
+ """
30
+ Convert a read-only file to an editable file.
52
31
 
53
- # Add to editable files
54
- coder.abs_fnames.add(abs_path)
32
+ This allows the LLM to upgrade a file from read-only to editable
33
+ when it determines it needs to make changes to that file.
34
+ """
35
+ try:
36
+ # Get absolute path
37
+ abs_path = coder.abs_root_path(file_path)
55
38
 
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)}"
39
+ # Check if file is already editable
40
+ if abs_path in coder.abs_fnames:
41
+ coder.io.tool_output(f"📝 File '{file_path}' is already editable")
42
+ return "File is already editable"
68
43
 
44
+ # Check if file exists on disk
45
+ if not os.path.isfile(abs_path):
46
+ coder.io.tool_output(f"⚠️ File '{file_path}' not found")
47
+ return "Error: File not found"
69
48
 
70
- def process_response(coder, params):
71
- """
72
- Process the MakeEditable tool response.
49
+ # File exists, is not editable, might be read-only or not in context yet
50
+ was_read_only = False
51
+ if abs_path in coder.abs_read_only_fnames:
52
+ coder.abs_read_only_fnames.remove(abs_path)
53
+ was_read_only = True
73
54
 
74
- Args:
75
- coder: The Coder instance
76
- params: Dictionary of parameters
55
+ # Add to editable files
56
+ coder.abs_fnames.add(abs_path)
77
57
 
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"
58
+ if was_read_only:
59
+ coder.io.tool_output(f"📝 Moved '{file_path}' from read-only to editable")
60
+ return "File is now editable (moved from read-only)"
61
+ else:
62
+ # File was not previously in context at all
63
+ coder.io.tool_output(f"📝 Added '{file_path}' directly to editable context")
64
+ # Track if added during exploration? Maybe not needed for direct MakeEditable.
65
+ # coder.files_added_in_exploration.add(rel_path) # Consider if needed
66
+ return "File is now editable (added directly)"
67
+ except Exception as e:
68
+ coder.io.tool_error(f"Error in MakeEditable for '{file_path}': {str(e)}")
69
+ return f"Error: {str(e)}"
@@ -1,69 +1,53 @@
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.",
1
+ from aider.tools.utils.base_tool import BaseTool
2
+
3
+
4
+ class Tool(BaseTool):
5
+ NORM_NAME = "makereadonly"
6
+ SCHEMA = {
7
+ "type": "function",
8
+ "function": {
9
+ "name": "MakeReadonly",
10
+ "description": "Make an editable file read-only.",
11
+ "parameters": {
12
+ "type": "object",
13
+ "properties": {
14
+ "file_path": {
15
+ "type": "string",
16
+ "description": "The path to the file to make read-only.",
17
+ },
12
18
  },
19
+ "required": ["file_path"],
13
20
  },
14
- "required": ["file_path"],
15
21
  },
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"
22
+ }
23
+
24
+ @classmethod
25
+ def execute(cls, coder, file_path):
26
+ """
27
+ Convert an editable file to a read-only file.
28
+
29
+ This allows the LLM to downgrade a file from editable to read-only
30
+ when it determines it no longer needs to make changes to that file.
31
+ """
32
+ try:
33
+ # Get absolute path
34
+ abs_path = coder.abs_root_path(file_path)
35
+
36
+ # Check if file is in editable context
37
+ if abs_path not in coder.abs_fnames:
38
+ if abs_path in coder.abs_read_only_fnames:
39
+ coder.io.tool_output(f"📚 File '{file_path}' is already read-only")
40
+ return "File is already read-only"
41
+ else:
42
+ coder.io.tool_output(f"⚠️ File '{file_path}' not in context")
43
+ return "File not in context"
44
+
45
+ # Move from editable to read-only
46
+ coder.abs_fnames.remove(abs_path)
47
+ coder.abs_read_only_fnames.add(abs_path)
48
+
49
+ coder.io.tool_output(f"📚 Made '{file_path}' read-only")
50
+ return "File is now read-only"
51
+ except Exception as e:
52
+ coder.io.tool_error(f"Error making file read-only: {str(e)}")
53
+ return f"Error: {str(e)}"
aider/tools/remove.py CHANGED
@@ -1,91 +1,75 @@
1
1
  import time
2
2
 
3
- schema = {
4
- "type": "function",
5
- "function": {
6
- "name": "Remove",
7
- "description": (
8
- "Remove a file from the chat context. Should be used proactively to keep con"
9
- "Should be used after editing a file when all edits are done "
10
- "and the file is no longer necessary in context."
11
- ),
12
- "parameters": {
13
- "type": "object",
14
- "properties": {
15
- "file_path": {
16
- "type": "string",
17
- "description": "The path to the file to remove.",
3
+ from aider.tools.utils.base_tool import BaseTool
4
+
5
+
6
+ class Tool(BaseTool):
7
+ NORM_NAME = "remove"
8
+ SCHEMA = {
9
+ "type": "function",
10
+ "function": {
11
+ "name": "Remove",
12
+ "description": (
13
+ "Remove a file from the chat context. Should be used proactively to keep con"
14
+ "Should be used after editing a file when all edits are done "
15
+ "and the file is no longer necessary in context."
16
+ ),
17
+ "parameters": {
18
+ "type": "object",
19
+ "properties": {
20
+ "file_path": {
21
+ "type": "string",
22
+ "description": "The path to the file to remove.",
23
+ },
18
24
  },
25
+ "required": ["file_path"],
19
26
  },
20
- "required": ["file_path"],
21
27
  },
22
- },
23
- }
24
-
25
- # Normalized tool name for lookup
26
- NORM_NAME = "remove"
27
-
28
-
29
- def _execute_remove(coder, file_path):
30
- """
31
- Explicitly remove a file from context.
32
-
33
- This allows the LLM to clean up its context when files are no
34
- longer needed, keeping the context focused and efficient.
35
- """
36
- try:
37
- # Get absolute path
38
- abs_path = coder.abs_root_path(file_path)
39
- rel_path = coder.get_rel_fname(abs_path)
40
-
41
- # Check if file is in context (either editable or read-only)
42
- removed = False
43
- if abs_path in coder.abs_fnames:
44
- # Don't remove if it's the last editable file and there are no read-only files
45
- if len(coder.abs_fnames) <= 1 and not coder.abs_read_only_fnames:
46
- coder.io.tool_output(
47
- f"⚠️ Cannot remove '{file_path}' - it's the only file in context"
48
- )
49
- return "Cannot remove - last file in context"
50
- coder.abs_fnames.remove(abs_path)
51
- removed = True
52
- elif abs_path in coder.abs_read_only_fnames:
53
- # Don't remove if it's the last read-only file and there are no editable files
54
- if len(coder.abs_read_only_fnames) <= 1 and not coder.abs_fnames:
55
- coder.io.tool_output(
56
- f"⚠️ Cannot remove '{file_path}' - it's the only file in context"
57
- )
58
- return "Cannot remove - last file in context"
59
- coder.abs_read_only_fnames.remove(abs_path)
60
- removed = True
61
-
62
- if not removed:
63
- coder.io.tool_output(f"⚠️ File '{file_path}' not in context")
64
- return "File not in context"
28
+ }
65
29
 
66
- # Track in recently removed
67
- coder.recently_removed[rel_path] = {"removed_at": time.time()}
30
+ @classmethod
31
+ def execute(cls, coder, file_path):
32
+ """
33
+ Explicitly remove a file from context.
68
34
 
69
- coder.io.tool_output(f"🗑️ Explicitly removed '{file_path}' from context")
70
- return "Removed file from context"
71
- except Exception as e:
72
- coder.io.tool_error(f"Error removing file: {str(e)}")
73
- return f"Error: {str(e)}"
35
+ This allows the LLM to clean up its context when files are no
36
+ longer needed, keeping the context focused and efficient.
37
+ """
38
+ try:
39
+ # Get absolute path
40
+ abs_path = coder.abs_root_path(file_path)
41
+ rel_path = coder.get_rel_fname(abs_path)
74
42
 
43
+ # Check if file is in context (either editable or read-only)
44
+ removed = False
45
+ if abs_path in coder.abs_fnames:
46
+ # Don't remove if it's the last editable file and there are no read-only files
47
+ if len(coder.abs_fnames) <= 1 and not coder.abs_read_only_fnames:
48
+ coder.io.tool_output(
49
+ f"⚠️ Cannot remove '{file_path}' - it's the only file in context"
50
+ )
51
+ return "Cannot remove - last file in context"
52
+ coder.abs_fnames.remove(abs_path)
53
+ removed = True
54
+ elif abs_path in coder.abs_read_only_fnames:
55
+ # Don't remove if it's the last read-only file and there are no editable files
56
+ if len(coder.abs_read_only_fnames) <= 1 and not coder.abs_fnames:
57
+ coder.io.tool_output(
58
+ f"⚠️ Cannot remove '{file_path}' - it's the only file in context"
59
+ )
60
+ return "Cannot remove - last file in context"
61
+ coder.abs_read_only_fnames.remove(abs_path)
62
+ removed = True
75
63
 
76
- def process_response(coder, params):
77
- """
78
- Process the Remove tool response.
64
+ if not removed:
65
+ coder.io.tool_output(f"⚠️ File '{file_path}' not in context")
66
+ return "File not in context"
79
67
 
80
- Args:
81
- coder: The Coder instance
82
- params: Dictionary of parameters
68
+ # Track in recently removed
69
+ coder.recently_removed[rel_path] = {"removed_at": time.time()}
83
70
 
84
- Returns:
85
- str: Result message
86
- """
87
- file_path = params.get("file_path")
88
- if file_path is not None:
89
- return _execute_remove(coder, file_path)
90
- else:
91
- return "Error: Missing 'file_path' parameter for Remove"
71
+ coder.io.tool_output(f"🗑️ Explicitly removed '{file_path}' from context")
72
+ return "Removed file from context"
73
+ except Exception as e:
74
+ coder.io.tool_error(f"Error removing file: {str(e)}")
75
+ return f"Error: {str(e)}"