Repository navigation
fix(tools): prune _SKIP_DIRS below the glob base, not the whole path - #256
Merged
Zongwei9888 merged 1 commit intoOct 12, 2026
Merged
Conversation
GlobTool skipped every file when a component of the workspace path collided with _SKIP_DIRS (build/dist/venv/node_modules/...), because p.parts carries the workspace's own ancestors and not just the search tree. It silently returned "No files matched." for a project that has files, while grep -- sharing the same constant -- returned results. Prune relative to the search base instead, matching GrepTool._walk.
Collaborator
|
Merged in 9550d53. Thank you for the fix and the side-by-side comparison with Checked before merging:
One consequence, which I think is the intended behavior: an explicit On the symlink issue you mentioned, I can reproduce it too: a workspace passed as |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
globsilently reports"No files matched."for a workspace whose path contains adirectory component named
build,dist,venv,.venv,node_modules,__pycache__or.git— even though the project has matching files.grep, whichshares the same
_SKIP_DIRSconstant in the same module, returns results for the verysame workspace.
Root cause
GlobTool.executeprunes ignored directories againstp.partsof an absolute path:base.glob()yields absolute paths, sop.partsalso carries every component abovethe search base — e.g.
('/', 'home', 'me', 'build', 'app', 'src', 'm.py'). When any ofthose ancestors collides with
_SKIP_DIRS, every file matches and the loop skips all ofthem. The prune is meant to skip noise directories inside the search tree, not the
workspace's own ancestors.
The sibling in the same module gets this right.
GrepTool._walkprunes during the walk,so only directories below
rootare ever considered:rootis never tested against_SKIP_DIRS. That is why the two siblings disagree.Reproduction (real code, workspace named
build)Why it matters
The failure is silent, not an error. The agent asks "which Python files exist?", gets
"No files matched.", and concludes the project is empty — a wrong premise that thendrives planning, edits and review. Nothing in the output hints that a directory-name
collision caused it.
Fix
Prune relative to the search base instead of the whole absolute path:
matches = [] for p in sorted(base.glob(pattern)): - if any(part in _SKIP_DIRS for part in p.parts): + # Prune relative to the base only. ``p.parts`` also carries the + # workspace's own ancestors, so a workspace rooted under a + # directory named e.g. "build" or "venv" skipped every file. + rel = os.path.relpath(p, base) + if any(part in _SKIP_DIRS for part in Path(rel).parts): continue if p.is_file(): matches.append(os.path.relpath(p, self._workspace))Two changed lines; the output path (workspace-relative) is untouched.
Verification
Test effectiveness. Reverting only the source change while keeping the new test:
Restoring the fix:
28 passed.Differential fuzzing, 500 randomized workspace trees (ancestor chains mixing
_SKIP_DIRSnames with ordinary names, nested ignored dirs, 8 patterns,pathargument sometimes set):
Every changed case has an ignored-name ancestor and a strict superset of the previous
result, i.e. the fix only adds files that were wrongly suppressed; it never removed one.
Existing tests. Related subset (search / file tools / harness / spawn_agent /
code_mode / apply_patch) on unmodified
main:162 passed, 3 skipped. With the fix:166 passed, 3 skipped— exactly +4, the new parametrised cases.Full suite:
2266 passed, 11 skippedin 2m25s, no failures.Scope / no data impact
The change only affects which paths are included in
globoutput. It touches nopersisted key, file path, cache key or serialization format, so there is no existing-data
migration concern.
Not addressed here
While reproducing this I noticed a separate, pre-existing issue I deliberately left out
of this PR:
os.path.relpath(p, self._workspace)relativises against the unresolvedworkspace string while
baseis.resolve()d, so when the workspace path contains asymlink (macOS
/tmp→/private/tmp,/var→/private/var) both tools emit../../../../private/var/...rather than a workspace-relative path, despiteGlobTool.descriptionpromising "workspace-relative". It is independent of the bugfixed here and changing it alters existing output formatting, so it seems better as its
own PR — happy to open one if you agree.
Assisted by AI (CodeBuddy/Claude). I verified every claim above by running the real
GlobTool/GrepToolcode, not a reimplementation. Happy to convert this to an issuefirst, or to drop the comment lines if you prefer the change to be purely mechanical.