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/git_show.py CHANGED
@@ -1,51 +1,37 @@
1
1
  from aider.repo import ANY_GIT_ERROR
2
-
3
- schema = {
4
- "type": "function",
5
- "function": {
6
- "name": "GitShow",
7
- "description": "Show various types of objects (blobs, trees, tags, and commits).",
8
- "parameters": {
9
- "type": "object",
10
- "properties": {
11
- "object": {
12
- "type": "string",
13
- "description": "The object to show. Defaults to HEAD.",
2
+ from aider.tools.utils.base_tool import BaseTool
3
+
4
+
5
+ class Tool(BaseTool):
6
+ NORM_NAME = "gitshow"
7
+ SCHEMA = {
8
+ "type": "function",
9
+ "function": {
10
+ "name": "GitShow",
11
+ "description": "Show various types of objects (blobs, trees, tags, and commits).",
12
+ "parameters": {
13
+ "type": "object",
14
+ "properties": {
15
+ "object": {
16
+ "type": "string",
17
+ "description": "The object to show. Defaults to HEAD.",
18
+ },
14
19
  },
20
+ "required": [],
15
21
  },
16
- "required": [],
17
22
  },
18
- },
19
- }
20
-
21
- # Normalized tool name for lookup
22
- NORM_NAME = "gitshow"
23
-
24
-
25
- def _execute_git_show(coder, object="HEAD"):
26
- """
27
- Show various types of objects (blobs, trees, tags, and commits).
28
- """
29
- if not coder.repo:
30
- return "Not in a git repository."
31
-
32
- try:
33
- return coder.repo.repo.git.show(object)
34
- except ANY_GIT_ERROR as e:
35
- coder.io.tool_error(f"Error running git show: {e}")
36
- return f"Error running git show: {e}"
37
-
38
-
39
- def process_response(coder, params):
40
- """
41
- Process the GitShow tool response.
42
-
43
- Args:
44
- coder: The Coder instance
45
- params: Dictionary of parameters
46
-
47
- Returns:
48
- str: Result message
49
- """
50
- object = params.get("object", "HEAD")
51
- return _execute_git_show(coder, object)
23
+ }
24
+
25
+ @classmethod
26
+ def execute(cls, coder, object="HEAD"):
27
+ """
28
+ Show various types of objects (blobs, trees, tags, and commits).
29
+ """
30
+ if not coder.repo:
31
+ return "Not in a git repository."
32
+
33
+ try:
34
+ return coder.repo.repo.git.show(object)
35
+ except ANY_GIT_ERROR as e:
36
+ coder.io.tool_error(f"Error running git show: {e}")
37
+ return f"Error running git show: {e}"
aider/tools/git_status.py CHANGED
@@ -1,46 +1,32 @@
1
1
  from aider.repo import ANY_GIT_ERROR
2
-
3
- schema = {
4
- "type": "function",
5
- "function": {
6
- "name": "GitStatus",
7
- "description": "Show the working tree status.",
8
- "parameters": {
9
- "type": "object",
10
- "properties": {},
11
- "required": [],
2
+ from aider.tools.utils.base_tool import BaseTool
3
+
4
+
5
+ class Tool(BaseTool):
6
+ NORM_NAME = "gitstatus"
7
+ SCHEMA = {
8
+ "type": "function",
9
+ "function": {
10
+ "name": "GitStatus",
11
+ "description": "Show the working tree status.",
12
+ "parameters": {
13
+ "type": "object",
14
+ "properties": {},
15
+ "required": [],
16
+ },
12
17
  },
13
- },
14
- }
15
-
16
- # Normalized tool name for lookup
17
- NORM_NAME = "gitstatus"
18
-
19
-
20
- def _execute_git_status(coder):
21
- """
22
- Show the working tree status.
23
- """
24
- if not coder.repo:
25
- return "Not in a git repository."
26
-
27
- try:
28
- return coder.repo.repo.git.status()
29
- except ANY_GIT_ERROR as e:
30
- coder.io.tool_error(f"Error running git status: {e}")
31
- return f"Error running git status: {e}"
32
-
33
-
34
- def process_response(coder, params):
35
- """
36
- Process the GitStatus tool response.
37
-
38
- Args:
39
- coder: The Coder instance
40
- params: Dictionary of parameters (should be empty for GitStatus)
41
-
42
- Returns:
43
- str: Result message
44
- """
45
- # GitStatus tool has no parameters to validate
46
- return _execute_git_status(coder)
18
+ }
19
+
20
+ @classmethod
21
+ def execute(cls, coder):
22
+ """
23
+ Show the working tree status.
24
+ """
25
+ if not coder.repo:
26
+ return "Not in a git repository."
27
+
28
+ try:
29
+ return coder.repo.repo.git.status()
30
+ except ANY_GIT_ERROR as e:
31
+ coder.io.tool_error(f"Error running git status: {e}")
32
+ return f"Error running git status: {e}"
aider/tools/grep.py CHANGED
@@ -4,253 +4,225 @@ from pathlib import Path
4
4
  import oslex
5
5
 
6
6
  from aider.run_cmd import run_cmd_subprocess
7
-
8
- schema = {
9
- "type": "function",
10
- "function": {
11
- "name": "Grep",
12
- "description": "Search for a pattern in files.",
13
- "parameters": {
14
- "type": "object",
15
- "properties": {
16
- "pattern": {
17
- "type": "string",
18
- "description": "The pattern to search for.",
19
- },
20
- "file_pattern": {
21
- "type": "string",
22
- "description": "Glob pattern for files to search. Defaults to '*'.",
23
- },
24
- "directory": {
25
- "type": "string",
26
- "description": "Directory to search in. Defaults to '.'.",
27
- },
28
- "use_regex": {
29
- "type": "boolean",
30
- "description": "Whether to use regex. Defaults to False.",
31
- },
32
- "case_insensitive": {
33
- "type": "boolean",
34
- "description": (
35
- "Whether to perform a case-insensitive search. Defaults to False."
36
- ),
37
- },
38
- "context_before": {
39
- "type": "integer",
40
- "description": "Number of lines to show before a match. Defaults to 5.",
41
- },
42
- "context_after": {
43
- "type": "integer",
44
- "description": "Number of lines to show after a match. Defaults to 5.",
7
+ from aider.tools.utils.base_tool import BaseTool
8
+
9
+
10
+ class Tool(BaseTool):
11
+ NORM_NAME = "grep"
12
+ SCHEMA = {
13
+ "type": "function",
14
+ "function": {
15
+ "name": "Grep",
16
+ "description": "Search for a pattern in files.",
17
+ "parameters": {
18
+ "type": "object",
19
+ "properties": {
20
+ "pattern": {
21
+ "type": "string",
22
+ "description": "The pattern to search for.",
23
+ },
24
+ "file_pattern": {
25
+ "type": "string",
26
+ "description": "Glob pattern for files to search. Defaults to '*'.",
27
+ },
28
+ "directory": {
29
+ "type": "string",
30
+ "description": "Directory to search in. Defaults to '.'.",
31
+ },
32
+ "use_regex": {
33
+ "type": "boolean",
34
+ "description": "Whether to use regex. Defaults to False.",
35
+ },
36
+ "case_insensitive": {
37
+ "type": "boolean",
38
+ "description": (
39
+ "Whether to perform a case-insensitive search. Defaults to False."
40
+ ),
41
+ },
42
+ "context_before": {
43
+ "type": "integer",
44
+ "description": "Number of lines to show before a match. Defaults to 5.",
45
+ },
46
+ "context_after": {
47
+ "type": "integer",
48
+ "description": "Number of lines to show after a match. Defaults to 5.",
49
+ },
45
50
  },
51
+ "required": ["pattern"],
46
52
  },
47
- "required": ["pattern"],
48
53
  },
49
- },
50
- }
51
-
52
- # Normalized tool name for lookup
53
- NORM_NAME = "grep"
54
-
55
-
56
- def _find_search_tool():
57
- """Find the best available command-line search tool (rg, ag, grep)."""
58
- if shutil.which("rg"):
59
- return "rg", shutil.which("rg")
60
- elif shutil.which("ag"):
61
- return "ag", shutil.which("ag")
62
- elif shutil.which("grep"):
63
- return "grep", shutil.which("grep")
64
- else:
65
- return None, None
66
-
67
-
68
- def _execute_grep(
69
- coder,
70
- pattern,
71
- file_pattern="*",
72
- directory=".",
73
- use_regex=False,
74
- case_insensitive=False,
75
- context_before=5,
76
- context_after=5,
77
- ):
78
- """
79
- Search for lines matching a pattern in files within the project repository.
80
- Uses rg (ripgrep), ag (the silver searcher), or grep, whichever is available.
81
-
82
- Args:
83
- coder: The Coder instance.
84
- pattern (str): The pattern to search for.
85
- file_pattern (str, optional): Glob pattern to filter files. Defaults to "*".
86
- directory (str, optional): Directory to search within relative to repo root. Defaults to ".".
87
- use_regex (bool, optional): Whether the pattern is a regular expression. Defaults to False.
88
- case_insensitive (bool, optional): Whether the search should be case-insensitive. Defaults to False.
89
- context_before (int, optional): Number of context lines to show before matches. Defaults to 5.
90
- context_after (int, optional): Number of context lines to show after matches. Defaults to 5.
91
-
92
- Returns:
93
- str: Formatted result indicating success or failure, including matching lines or error message.
94
- """
95
- repo = coder.repo
96
- if not repo:
97
- coder.io.tool_error("Not in a git repository.")
98
- return "Error: Not in a git repository."
99
-
100
- tool_name, tool_path = _find_search_tool()
101
- if not tool_path:
102
- coder.io.tool_error("No search tool (rg, ag, grep) found in PATH.")
103
- return "Error: No search tool (rg, ag, grep) found."
104
-
105
- try:
106
- search_dir_path = Path(repo.root) / directory
107
- if not search_dir_path.is_dir():
108
- coder.io.tool_error(f"Directory not found: {directory}")
109
- return f"Error: Directory not found: {directory}"
110
-
111
- # Build the command arguments based on the available tool
112
- cmd_args = [tool_path]
113
-
114
- # Common options or tool-specific equivalents
115
- if tool_name in ["rg", "grep"]:
116
- cmd_args.append("-n") # Line numbers for rg and grep
117
- # ag includes line numbers by default
118
-
119
- if tool_name in ["rg"]:
120
- cmd_args.append("--heading") # Filename above output for ripgrep
121
-
122
- # Context lines (Before and After)
123
- if context_before > 0:
124
- # All tools use -B for lines before
125
- cmd_args.extend(["-B", str(context_before)])
126
- if context_after > 0:
127
- # All tools use -A for lines after
128
- cmd_args.extend(["-A", str(context_after)])
129
-
130
- # Case sensitivity
131
- if case_insensitive:
132
- cmd_args.append("-i") # Add case-insensitivity flag for all tools
133
-
134
- # Pattern type (regex vs fixed string)
135
- if use_regex:
136
- if tool_name == "grep":
137
- cmd_args.append("-E") # Use extended regex for grep
138
- # rg and ag use regex by default, no flag needed for basic ERE
54
+ }
55
+
56
+ @classmethod
57
+ def _find_search_tool(self):
58
+ """Find the best available command-line search tool (rg, ag, grep)."""
59
+ if shutil.which("rg"):
60
+ return "rg", shutil.which("rg")
61
+ elif shutil.which("ag"):
62
+ return "ag", shutil.which("ag")
63
+ elif shutil.which("grep"):
64
+ return "grep", shutil.which("grep")
139
65
  else:
140
- if tool_name == "rg":
141
- cmd_args.append("-F") # Fixed strings for rg
142
- elif tool_name == "ag":
143
- cmd_args.append("-Q") # Literal/fixed strings for ag
144
- elif tool_name == "grep":
145
- cmd_args.append("-F") # Fixed strings for grep
146
-
147
- # File filtering
148
- if (
149
- file_pattern != "*"
150
- ): # Avoid adding glob if it's the default '*' which might behave differently
151
- if tool_name == "rg":
152
- cmd_args.extend(["-g", file_pattern])
153
- elif tool_name == "ag":
154
- cmd_args.extend(["-G", file_pattern])
66
+ return None, None
67
+
68
+ @classmethod
69
+ def execute(
70
+ cls,
71
+ coder,
72
+ pattern,
73
+ file_pattern="*",
74
+ directory=".",
75
+ use_regex=False,
76
+ case_insensitive=False,
77
+ context_before=5,
78
+ context_after=5,
79
+ ):
80
+ """
81
+ Search for lines matching a pattern in files within the project repository.
82
+ Uses rg (ripgrep), ag (the silver searcher), or grep, whichever is available.
83
+
84
+ Args:
85
+ coder: The Coder instance.
86
+ pattern (str): The pattern to search for.
87
+ file_pattern (str, optional): Glob pattern to filter files. Defaults to "*".
88
+ directory (str, optional): Directory to search within relative to repo root. Defaults to ".".
89
+ use_regex (bool, optional): Whether the pattern is a regular expression. Defaults to False.
90
+ case_insensitive (bool, optional): Whether the search should be case-insensitive. Defaults to False.
91
+ context_before (int, optional): Number of context lines to show before matches. Defaults to 5.
92
+ context_after (int, optional): Number of context lines to show after matches. Defaults to 5.
93
+
94
+ Returns:
95
+ str: Formatted result indicating success or failure, including matching lines or error message.
96
+ """
97
+ repo = coder.repo
98
+ if not repo:
99
+ coder.io.tool_error("Not in a git repository.")
100
+ return "Error: Not in a git repository."
101
+
102
+ tool_name, tool_path = cls._find_search_tool()
103
+ if not tool_path:
104
+ coder.io.tool_error("No search tool (rg, ag, grep) found in PATH.")
105
+ return "Error: No search tool (rg, ag, grep) found."
106
+
107
+ try:
108
+ search_dir_path = Path(repo.root) / directory
109
+ if not search_dir_path.is_dir():
110
+ coder.io.tool_error(f"Directory not found: {directory}")
111
+ return f"Error: Directory not found: {directory}"
112
+
113
+ # Build the command arguments based on the available tool
114
+ cmd_args = [tool_path]
115
+
116
+ # Common options or tool-specific equivalents
117
+ if tool_name in ["rg", "grep"]:
118
+ cmd_args.append("-n") # Line numbers for rg and grep
119
+ # ag includes line numbers by default
120
+
121
+ if tool_name in ["rg"]:
122
+ cmd_args.append("--heading") # Filename above output for ripgrep
123
+
124
+ # Context lines (Before and After)
125
+ if context_before > 0:
126
+ # All tools use -B for lines before
127
+ cmd_args.extend(["-B", str(context_before)])
128
+ if context_after > 0:
129
+ # All tools use -A for lines after
130
+ cmd_args.extend(["-A", str(context_after)])
131
+
132
+ # Case sensitivity
133
+ if case_insensitive:
134
+ cmd_args.append("-i") # Add case-insensitivity flag for all tools
135
+
136
+ # Pattern type (regex vs fixed string)
137
+ if use_regex:
138
+ if tool_name == "grep":
139
+ cmd_args.append("-E") # Use extended regex for grep
140
+ # rg and ag use regex by default, no flag needed for basic ERE
141
+ else:
142
+ if tool_name == "rg":
143
+ cmd_args.append("-F") # Fixed strings for rg
144
+ elif tool_name == "ag":
145
+ cmd_args.append("-Q") # Literal/fixed strings for ag
146
+ elif tool_name == "grep":
147
+ cmd_args.append("-F") # Fixed strings for grep
148
+
149
+ # File filtering
150
+ if (
151
+ file_pattern != "*"
152
+ ): # Avoid adding glob if it's the default '*' which might behave differently
153
+ if tool_name == "rg":
154
+ cmd_args.extend(["-g", file_pattern])
155
+ elif tool_name == "ag":
156
+ cmd_args.extend(["-G", file_pattern])
157
+ elif tool_name == "grep":
158
+ # grep needs recursive flag when filtering
159
+ cmd_args.append("-r")
160
+ cmd_args.append(f"--include={file_pattern}")
155
161
  elif tool_name == "grep":
156
- # grep needs recursive flag when filtering
162
+ # grep needs recursive flag even without include filter
157
163
  cmd_args.append("-r")
158
- cmd_args.append(f"--include={file_pattern}")
159
- elif tool_name == "grep":
160
- # grep needs recursive flag even without include filter
161
- cmd_args.append("-r")
162
-
163
- # Directory exclusion (rg and ag respect .gitignore/.git by default)
164
- if tool_name == "grep":
165
- cmd_args.append("--exclude-dir=.git")
166
-
167
- # Add pattern and directory path
168
- cmd_args.extend([pattern, str(search_dir_path)])
169
164
 
170
- # Convert list to command string for run_cmd_subprocess
171
- command_string = oslex.join(cmd_args)
172
-
173
- coder.io.tool_output(f"⚙️ Executing {tool_name}: {command_string}")
174
-
175
- # Use run_cmd_subprocess for execution
176
- # Note: rg, ag, and grep return 1 if no matches are found, which is not an error for this tool.
177
- exit_status, combined_output = run_cmd_subprocess(
178
- command_string, verbose=coder.verbose, cwd=coder.root # Execute in the project root
179
- )
180
-
181
- # Format the output for the result message
182
- output_content = combined_output or ""
183
-
184
- # Handle exit codes (consistent across rg, ag, grep)
185
- if exit_status == 0:
186
- # Limit output size if necessary
187
- max_output_lines = 50 # Consider making this configurable
188
- output_lines = output_content.splitlines()
189
- if len(output_lines) > max_output_lines:
190
- truncated_output = "\n".join(output_lines[:max_output_lines])
191
- result_message = (
192
- f"Found matches (truncated):\n```text\n{truncated_output}\n..."
193
- f" ({len(output_lines) - max_output_lines} more lines)\n```"
194
- )
195
- elif not output_content:
196
- # Should not happen if return code is 0, but handle defensively
197
- coder.io.tool_warning(f"{tool_name} returned 0 but produced no output.")
198
- result_message = "No matches found (unexpected)."
165
+ # Directory exclusion (rg and ag respect .gitignore/.git by default)
166
+ if tool_name == "grep":
167
+ cmd_args.append("--exclude-dir=.git")
168
+
169
+ # Add pattern and directory path
170
+ cmd_args.extend([pattern, str(search_dir_path)])
171
+
172
+ # Convert list to command string for run_cmd_subprocess
173
+ command_string = oslex.join(cmd_args)
174
+
175
+ coder.io.tool_output(f"⚙️ Executing {tool_name}: {command_string}")
176
+
177
+ # Use run_cmd_subprocess for execution
178
+ # Note: rg, ag, and grep return 1 if no matches are found, which is not an error for this tool.
179
+ exit_status, combined_output = run_cmd_subprocess(
180
+ command_string, verbose=coder.verbose, cwd=coder.root # Execute in the project root
181
+ )
182
+
183
+ # Format the output for the result message
184
+ output_content = combined_output or ""
185
+
186
+ # Handle exit codes (consistent across rg, ag, grep)
187
+ if exit_status == 0:
188
+ # Limit output size if necessary
189
+ max_output_lines = 50 # Consider making this configurable
190
+ output_lines = output_content.splitlines()
191
+ if len(output_lines) > max_output_lines:
192
+ truncated_output = "\n".join(output_lines[:max_output_lines])
193
+ result_message = (
194
+ f"Found matches (truncated):\n```text\n{truncated_output}\n..."
195
+ f" ({len(output_lines) - max_output_lines} more lines)\n```"
196
+ )
197
+ elif not output_content:
198
+ # Should not happen if return code is 0, but handle defensively
199
+ coder.io.tool_warning(f"{tool_name} returned 0 but produced no output.")
200
+ result_message = "No matches found (unexpected)."
201
+ else:
202
+ result_message = f"Found matches:\n```text\n{output_content}\n```"
203
+ return result_message
204
+
205
+ elif exit_status == 1:
206
+ # Exit code 1 means no matches found - this is expected behavior, not an error.
207
+ return "No matches found."
199
208
  else:
200
- result_message = f"Found matches:\n```text\n{output_content}\n```"
201
- return result_message
202
-
203
- elif exit_status == 1:
204
- # Exit code 1 means no matches found - this is expected behavior, not an error.
205
- return "No matches found."
206
- else:
207
- # Exit code > 1 indicates an actual error
208
- error_message = f"{tool_name.capitalize()} command failed with exit code {exit_status}."
209
- if output_content:
210
- # Truncate error output as well if it's too long
211
- error_limit = 1000 # Example limit for error output
212
- if len(output_content) > error_limit:
213
- output_content = output_content[:error_limit] + "\n... (error output truncated)"
214
- error_message += f" Output:\n{output_content}"
215
- coder.io.tool_error(error_message)
216
- return f"Error: {error_message}"
217
-
218
- except Exception as e:
219
- # Add command_string to the error message if it's defined
220
- cmd_str_info = f"'{command_string}' " if "command_string" in locals() else ""
221
- coder.io.tool_error(f"Error executing {tool_name} command {cmd_str_info}: {str(e)}")
222
- return f"Error executing {tool_name}: {str(e)}"
223
-
224
-
225
- def process_response(coder, params):
226
- """
227
- Process the Grep tool response.
228
-
229
- Args:
230
- coder: The Coder instance
231
- params: Dictionary of parameters
232
-
233
- Returns:
234
- str: Result message
235
- """
236
- pattern = params.get("pattern")
237
- file_pattern = params.get("file_pattern", "*") # Default to all files
238
- directory = params.get("directory", ".") # Default to current directory
239
- use_regex = params.get("use_regex", False) # Default to literal search
240
- case_insensitive = params.get("case_insensitive", False) # Default to case-sensitive
241
- context_before = params.get("context_before", 5)
242
- context_after = params.get("context_after", 5)
243
-
244
- if pattern is not None:
245
- return _execute_grep(
246
- coder,
247
- pattern,
248
- file_pattern,
249
- directory,
250
- use_regex,
251
- case_insensitive,
252
- context_before,
253
- context_after,
254
- )
255
- else:
256
- return "Error: Missing required 'pattern' parameter for Grep"
209
+ # Exit code > 1 indicates an actual error
210
+ error_message = (
211
+ f"{tool_name.capitalize()} command failed with exit code {exit_status}."
212
+ )
213
+ if output_content:
214
+ # Truncate error output as well if it's too long
215
+ error_limit = 1000 # Example limit for error output
216
+ if len(output_content) > error_limit:
217
+ output_content = (
218
+ output_content[:error_limit] + "\n... (error output truncated)"
219
+ )
220
+ error_message += f" Output:\n{output_content}"
221
+ coder.io.tool_error(error_message)
222
+ return f"Error: {error_message}"
223
+
224
+ except Exception as e:
225
+ # Add command_string to the error message if it's defined
226
+ cmd_str_info = f"'{command_string}' " if "command_string" in locals() else ""
227
+ coder.io.tool_error(f"Error executing {tool_name} command {cmd_str_info}: {str(e)}")
228
+ return f"Error executing {tool_name}: {str(e)}"