fix(search): keep grep fallback root searchable
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user