From 7c6f9affd76d074eeedf6ad40f9409cf101c2080 Mon Sep 17 00:00:00 2001 From: Kevin Yin <182213728+yinkev@users.noreply.github.com> Date: Fri, 5 Jun 2026 08:44:03 -0700 Subject: [PATCH] fix(file): align grep fallback regex behavior --- .../tools/test_file_operations_edge_cases.py | 26 +++++++++++++++++++ tools/file_operations.py | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/tools/test_file_operations_edge_cases.py b/tests/tools/test_file_operations_edge_cases.py index 6a1898cc8254e..ca5fa188f63c9 100644 --- a/tests/tools/test_file_operations_edge_cases.py +++ b/tests/tools/test_file_operations_edge_cases.py @@ -253,6 +253,32 @@ class TestPaginationBounds: class TestSearchContextParsing: + def test_search_with_grep_uses_extended_regex(self): + env = MagicMock() + env.cwd = "/tmp" + ops = ShellFileOperations(env) + + with patch.object(ops, "_exec") as mock_exec: + mock_exec.return_value = MagicMock( + exit_code=0, + stdout="./first.txt:1:foo\n./second.txt:1:bar\n", + ) + result = ops._search_with_grep( + "foo|bar", + path=".", + file_glob=None, + limit=10, + offset=0, + output_mode="content", + context=0, + ) + + cmd_arg = mock_exec.call_args[0][0] + assert cmd_arg.startswith("set -o pipefail; grep -rnHE ") + assert result.error is None + assert result.total_count == 2 + assert [match.content for match in result.matches] == ["foo", "bar"] + def test_parse_search_context_line_prefers_rightmost_numeric_separator(self): parsed = _parse_search_context_line("dir/file-12-name.py-8-context here") diff --git a/tools/file_operations.py b/tools/file_operations.py index ab4965ea14d83..2c1d1a62c9675 100644 --- a/tools/file_operations.py +++ b/tools/file_operations.py @@ -2684,7 +2684,7 @@ class ShellFileOperations(FileOperations): def _search_with_grep(self, pattern: str, path: str, file_glob: Optional[str], limit: int, offset: int, output_mode: str, context: int) -> SearchResult: """Fallback search using grep.""" - cmd_parts = ["grep", "-rnH"] # -H forces filename even for single-file searches + cmd_parts = ["grep", "-rnHE"] # -H forces filenames; -E matches rg regex behavior # Exclude hidden directories (matching ripgrep's default behavior). # This prevents searching inside .hub/index-cache/, .git/, etc.