fix(search): keep grep fallback root searchable

This commit is contained in:
wanquanyang
2026-08-11 18:59:04 +08:00
committed by kshitij
parent 068f58740c
commit 0763e77bc4
2 changed files with 83 additions and 17 deletions

View File

@@ -17,6 +17,9 @@ import subprocess
import pytest
from tools.file_operations import ShellFileOperations
from tools.environments.local import LocalEnvironment
@pytest.fixture
def searchable_tree(tmp_path):
@@ -38,6 +41,12 @@ def searchable_tree(tmp_path):
git_dir.mkdir(parents=True)
(git_dir / "pack-abc.idx").write_text("git internal data")
# An arbitrary hidden directory verifies the fallback is not limited to
# a hard-coded list of known cache names.
private_dir = tmp_path / "skills" / ".private-index"
private_dir.mkdir(parents=True)
(private_dir / "notes.txt").write_text("unlisted hidden content")
return tmp_path / "skills"
@@ -64,25 +73,68 @@ class TestFindExcludesHiddenDirs:
class TestGrepExcludesHiddenDirs:
"""_search_with_grep should exclude hidden directories."""
"""The real search_files grep fallback should search the default root."""
def test_grep_skips_hub_cache(self, searchable_tree):
"""grep --exclude-dir should skip .hub/ directory."""
cmd = (
f"grep -rnHE --exclude-dir='.*' 'ignore' {searchable_tree}"
@staticmethod
def _grep_ops(searchable_tree, monkeypatch):
ops = ShellFileOperations(
LocalEnvironment(cwd=str(searchable_tree)),
cwd=str(searchable_tree),
)
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
# Should NOT find the injection text in .hub/index-cache/catalog.json
assert ".hub" not in result.stdout
assert "catalog.json" not in result.stdout
monkeypatch.setattr(ops, "_has_command", lambda command: command == "grep")
return ops
def test_grep_still_finds_visible_content(self, searchable_tree):
"""grep should still find content in visible directories."""
cmd = (
f"grep -rnHE --exclude-dir='.*' 'real skill' {searchable_tree}"
def test_grep_fallback_finds_visible_content(self, searchable_tree, monkeypatch):
"""Searching ``.`` must not exclude the search root itself."""
result = self._grep_ops(searchable_tree, monkeypatch).search(
"real skill",
path=".",
target="content",
)
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
assert "SKILL.md" in result.stdout
assert result.error is None
assert result.total_count > 0
assert any("SKILL.md" in match.path for match in result.matches)
def test_grep_fallback_finds_dot_relative_subdirectory(
self, searchable_tree, monkeypatch
):
"""An explicit ``./directory`` root must remain searchable too."""
result = self._grep_ops(searchable_tree, monkeypatch).search(
"real skill",
path="./my-skill",
target="content",
)
assert result.error is None
assert result.total_count == 1
assert result.matches[0].path.endswith("SKILL.md")
def test_grep_fallback_skips_hub_cache(self, searchable_tree, monkeypatch):
"""The fallback must not expose cached community skill content."""
result = self._grep_ops(searchable_tree, monkeypatch).search(
"ignore previous instructions",
path=".",
target="content",
)
assert result.error is None
assert result.total_count == 0
assert not result.matches
def test_grep_fallback_skips_arbitrary_hidden_directory(
self, searchable_tree, monkeypatch
):
"""Hidden-directory exclusion must not rely on a directory allowlist."""
result = self._grep_ops(searchable_tree, monkeypatch).search(
"unlisted hidden content",
path=".",
target="content",
)
assert result.error is None
assert result.total_count == 0
assert not result.matches
class TestRipgrepAlreadyExcludesHidden:

View File

@@ -3126,9 +3126,23 @@ class ShellFileOperations(FileOperations):
elif output_mode == "count":
cmd_parts.append("-c")
# Add pattern and path
# Add pattern and path. grep applies --exclude-dir to the command-line
# search root too, so passing the default relative root ``.`` causes
# ``.*`` to exclude the entire search. Anchor relative paths at the
# shell's live cwd; quoting $PWD separately keeps user paths escaped
# while working across local, container, and remote backends.
cmd_parts.append(self._escape_shell_arg(pattern))
cmd_parts.append(self._escape_shell_arg(path))
is_absolute = path.startswith(("/", "\\\\")) or bool(
re.match(r"^[A-Za-z]:[\\/]", path)
)
if is_absolute:
search_root = self._escape_shell_arg(path)
else:
relative_path = path[2:] if path.startswith("./") else path
search_root = '"$PWD"'
if relative_path not in {"", "."}:
search_root += f"/{self._escape_shell_arg(relative_path)}"
cmd_parts.append(search_root)
# Fetch generously so we can compute total before slicing
fetch_limit = limit + offset + (200 if context > 0 else 0)