yc1838 Claude Opus 4.7 commited on
Commit
0dc5c75
·
1 Parent(s): 96fa5bb

fix: add default excludes and truncation sentinel to find_files

Browse files

Addresses code-review findings I2 and I3:
- I2: skip node_modules/.git/.venv/__pycache__/etc. so find_files
returns useful results on real codebases
- I3: signal truncation when result count exceeds max_results
- M3: drop unused os import in tests

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

src/lilith_agent/tools/files.py CHANGED
@@ -15,6 +15,21 @@ from pypdf import PdfReader
15
  _WRITE_ROOT: Path = Path(".lilith/scratch").resolve()
16
 
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  def set_write_root(root: str | Path) -> None:
19
  """Set the sandboxed root directory for write_file. Call once at startup."""
20
  global _WRITE_ROOT
@@ -166,9 +181,12 @@ def find_files(name: str, root: str = ".", max_results: int = 200) -> str:
166
  root = os.path.expanduser(root)
167
  if not Path(root).exists():
168
  return f"ERROR: Path {root} does not exist."
 
 
 
169
  try:
170
  result = subprocess.run(
171
- ["find", str(root), "-name", name],
172
  capture_output=True,
173
  text=True,
174
  timeout=30,
@@ -176,7 +194,13 @@ def find_files(name: str, root: str = ".", max_results: int = 200) -> str:
176
  lines = [ln for ln in result.stdout.splitlines() if ln]
177
  if not lines:
178
  return "No files found."
179
- return "\n".join(lines[:max_results])
 
 
 
 
 
 
180
  except subprocess.TimeoutExpired:
181
  return "ERROR: find timed out (>30s)."
182
  except Exception as e:
 
15
  _WRITE_ROOT: Path = Path(".lilith/scratch").resolve()
16
 
17
 
18
+ # Default directories pruned by find_files. Keeps results useful on real
19
+ # codebases by skipping vendored/build/cache trees.
20
+ _DEFAULT_EXCLUDES: tuple[str, ...] = (
21
+ "node_modules",
22
+ ".git",
23
+ ".venv",
24
+ "__pycache__",
25
+ ".pytest_cache",
26
+ ".mypy_cache",
27
+ "dist",
28
+ "build",
29
+ ".tox",
30
+ )
31
+
32
+
33
  def set_write_root(root: str | Path) -> None:
34
  """Set the sandboxed root directory for write_file. Call once at startup."""
35
  global _WRITE_ROOT
 
181
  root = os.path.expanduser(root)
182
  if not Path(root).exists():
183
  return f"ERROR: Path {root} does not exist."
184
+ argv = ["find", str(root), "-name", name]
185
+ for excl in _DEFAULT_EXCLUDES:
186
+ argv.extend(["-not", "-path", f"*/{excl}/*"])
187
  try:
188
  result = subprocess.run(
189
+ argv,
190
  capture_output=True,
191
  text=True,
192
  timeout=30,
 
194
  lines = [ln for ln in result.stdout.splitlines() if ln]
195
  if not lines:
196
  return "No files found."
197
+ if len(lines) > max_results:
198
+ shown = lines[:max_results]
199
+ shown.append(
200
+ f"[truncated: showing first {max_results} of {len(lines)} matches]"
201
+ )
202
+ return "\n".join(shown)
203
+ return "\n".join(lines)
204
  except subprocess.TimeoutExpired:
205
  return "ERROR: find timed out (>30s)."
206
  except Exception as e:
tests/test_files.py CHANGED
@@ -1,6 +1,5 @@
1
  from __future__ import annotations
2
 
3
- import os
4
  from pathlib import Path
5
 
6
  import pytest
@@ -122,3 +121,31 @@ def test_find_files_expands_tilde(monkeypatch, tmp_path: Path):
122
  def test_find_files_missing_root_errors(tmp_path: Path):
123
  out = find_files("anything", str(tmp_path / "does_not_exist"))
124
  assert out.startswith("ERROR")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from __future__ import annotations
2
 
 
3
  from pathlib import Path
4
 
5
  import pytest
 
121
  def test_find_files_missing_root_errors(tmp_path: Path):
122
  out = find_files("anything", str(tmp_path / "does_not_exist"))
123
  assert out.startswith("ERROR")
124
+
125
+
126
+ def test_find_files_excludes_node_modules(tmp_path: Path):
127
+ (tmp_path / "node_modules").mkdir()
128
+ (tmp_path / "node_modules" / "skip.txt").write_text("")
129
+ (tmp_path / "src").mkdir()
130
+ (tmp_path / "src" / "keep.txt").write_text("")
131
+ out = find_files("*.txt", str(tmp_path))
132
+ assert "keep.txt" in out
133
+ assert "skip.txt" not in out
134
+
135
+
136
+ def test_find_files_excludes_dot_git(tmp_path: Path):
137
+ (tmp_path / ".git").mkdir()
138
+ (tmp_path / ".git" / "config").write_text("")
139
+ (tmp_path / "src").mkdir()
140
+ (tmp_path / "src" / "config").write_text("")
141
+ out = find_files("config", str(tmp_path))
142
+ assert str(tmp_path / "src" / "config") in out
143
+ assert str(tmp_path / ".git" / "config") not in out
144
+
145
+
146
+ def test_find_files_truncation_sentinel(tmp_path: Path):
147
+ for i in range(4):
148
+ (tmp_path / f"file{i}.txt").write_text("")
149
+ out = find_files("*.txt", str(tmp_path), max_results=3)
150
+ assert "[truncated:" in out
151
+ assert "of 4" in out