Skip to content

fix(cli): stop glob and grep include patterns from bypassing .gitignore - #15017

Merged
WebReflection merged 5 commits into
mainfrom
fix/glob-respect-gitignore
Oct 13, 2026
Merged

WebReflection merged 5 commits into
mainfrom
fix/glob-respect-gitignore

Conversation

@WebReflection

@WebReflection WebReflection commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Closes #15003.

What

Glob searches, the grep tool's include filter and Core find stop returning gitignored files and stop walking ignored folders such as node_modules, dist or .venv, whatever the shape of the pattern.

Cause: the Core ripgrep adapter passed the agent's pattern as --glob=<pattern>. That makes it a ripgrep override, and overrides win over ignore rules. Any pattern that also matches folder names (**/*, **, *) whitelisted ignored folders, and any gitignored file that matched was returned. In this repo, **/* matched 139,817 files, and 128,897 of them were ignored; the glob tool hid most of that only because it stops at 100 results.

Fix: ripgrep now lists only the files its ignore rules allow, and the pattern is matched in packages/core/src/kilocode/ripgrep-glob.ts, using the same gitignore-style rules ripgrep uses for overrides.

  • Shared code: packages/core/src/ripgrep.ts gets only marked hooks (the pattern arguments and a per-line filter).
  • Who benefits: every caller of Core glob, find and grep (with include): the glob and grep tools, their Core v2 versions, filesystem search, the file API and the marketplace scan.
  • Grep: the include glob is applied to each match's path. Grep always searched hidden files, so only ignored files change. A search of an explicit file still ignores include, as before.

Behavior kept on purpose

  • Hidden paths: a hidden file is still returned when it matches (e.g. **/.gitignore, .env.example), and a hidden folder is still searched only when the pattern matches the folder itself.
    • Upstream opencode made hidden paths opt-in in 4ea368e; that will come up at the next upstream merge as a separate decision.
  • Other unchanged cases: negated patterns (!*.ts) are still passed to ripgrep, since they never bypass ignore rules. Invalid patterns are still reported by ripgrep itself: --pre-glob=<pattern> makes ripgrep parse the pattern without using it, because no --pre command is set. .git is never searched.
  • The experimental.glob_include_ignored allowlist from feat(cli): let glob search an opt-in allowlist of ignored paths #14988 keeps its semantics. Its extra ripgrep run still uses override globs, so that run now keeps only paths matching a listed entry, and the filter runs before the result limit, so ignored files elsewhere can't take up the result slots. The normal search runs alongside it.

How it's verified

  • Differential test against ripgrep (packages/core/test/kilocode/ripgrep-glob.test.ts): for 41 patterns × hidden on/off, on a fixture with nested .gitignore files, .ignore, negations (!out/keep.txt), hidden files and folders, and folders named like patterns, results equal exactly what rg --glob=<pattern> returned before, minus ignored files.
    • The same check on this repo's 139k files with 107 patterns × hidden on/off: 0 differences.
  • Same check for find and grep include: for every pattern above, find (hidden on/off) and grep include return exactly what ripgrep returned before, minus ignored files.
  • Mutation check: reverting the hooks makes the matching Core tests and 3 of the glob tool tests fail.
  • Tests added for: the limit applying after ignored files are dropped, a caller filter applying before the limit, invalid patterns, negated patterns, and the allowlist with folder-matching patterns.
  • Suites run: the full CLI suite (287 files) and the Core suite (1,369 pass). The one failure is a PTY shell test that fails the same way on clean main on this machine.

Performance

Measured in-process with the real glob tool, alternating main and this branch, 20 runs per round, 3 rounds. The workspace was this repo: 10.6k searchable files, 139k with node_modules.

Pattern main medians Branch medians
**/*.ts (stops after 100 results) 8.5 / 9.6 / 8.4 ms 8.5 / 8.7 / 8.3 ms
**/*.nomatch (walks every non-ignored file) 23.1 / 25.4 / 22.5 ms 23.9 / 24.0 / 23.7 ms
**/package.json 23.9 / 22.5 / 22.1 ms 23.6 / 25.1 / 24.7 ms
packages/*/src/**/*.ts 7.0 / 7.5 / 7.1 ms 8.2 / 8.0 / 8.3 ms
**/* 7.3 / 6.9 / 6.9 ms 7.5 / 7.8 / 9.0 ms
  • File-name patterns (**/*.ts, *.md) are also passed to ripgrep as a file type, so it still prints only matching files.
  • Patterns with folders are matched in JS on every listed path, which costs about 1 ms here.
  • Allowlist users: with experimental.glob_include_ignored set, every search now runs both ripgrep passes, and the allowlist pass no longer stops early on results it would throw away. That adds about 15 ms on this repo (e.g. **/*.ts with packages/*/dist: 9.7 → 29.4 ms).

Grep and find (Core ripgrep.grep / find on this repo, 2 rounds, medians):

Search main Branch
grep Effect.gen, include: "*.ts" (stops after 100 hits) 9.8 / 11.4 ms 10.0 / 11.0 ms
grep no match, include: "*.ts" 73.7 / 74.4 ms 76.3 / 75.6 ms
grep no match, include: "packages/**/*.ts" 88.6 / 75.4 ms 76.4 / 75.7 ms
grep no match, include: "**/*" 2,837 / 2,899 ms 142 / 142 ms
grep Effect.gen, include: "packages/core/**/*.ts" (stops after 100 hits) 21.2 / 20.4 ms 37.6 / 42.5 ms
find **/* / **/*.nomatch 8.7 / 23.2 ms 8.2 / 23.8 ms
  • One case is slower: a grep whose include names a folder path and that stops early. Ripgrep now searches every file of that type (here every .ts) and the folder part is checked afterwards, so it finds the 100 hits later. It can never cost more than a search through every file of that type (~75 ms here).
  • Narrowing the search: limiting it to the literal folder (packages/core) would be faster, but ripgrep always searches an explicitly named folder even if it's ignored, so I left it out.

Follow-ups

@kilo-code-bot

kilo-code-bot Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Incremental review at f8ebadd8af covers only the test-robustness commit "test(core): compare glob tests with the installed ripgrep on every platform". The added rejects helper mirrors the production isInvalidPattern check (error parsing glob), reads stderr to completion before awaiting exited (no deadlock or process leak), and the malformed-pattern test now asserts the tool's invalidPattern flag against the installed ripgrep's real behavior instead of assuming all four patterns are rejected. The list normalization change (./-prefix stripping plus backslash folding) is platform-neutral on Linux CI. No new bugs, memory-leak, or test-quality regressions introduced. Previous review carried no open findings.

Files Reviewed (1 file)
  • packages/core/test/kilocode/ripgrep-glob.test.ts
Previous Review Summary (commit e48c978)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit e48c978)

Status: No Issues Found | Recommendation: Merge

The change stops glob patterns from bypassing ignore rules by matching the agent's pattern in packages/core/src/kilocode/ripgrep-glob.ts after ripgrep lists ignore-allowed files, instead of passing it as an overriding --glob. I traced the matcher (compile/walk/keep), the per-line filter in the shared ripgrep.ts adapter (marked kilocode_change), and the reworked glob_include_ignored search path. The hidden-path handling, the pre-limit filtering, and the allowlist/wide-search behavior all line up with the differential tests and the changeset. Fork hygiene is good: shared ripgrep.ts changes are minimal and marked, and the new logic lives under kilocode/ paths. No memory-leak or resource-lifetime issues found (the WeakMap plan cache and per-search Map are transient and scoped to the input object).

Files Reviewed (6 files)
  • .changeset/glob-respect-gitignore.md
  • packages/core/src/kilocode/ripgrep-glob.ts
  • packages/core/src/ripgrep.ts
  • packages/core/test/kilocode/ripgrep-glob.test.ts
  • packages/opencode/src/kilocode/tool/glob-ignored.ts
  • packages/opencode/test/kilocode/tool/glob-ignored.test.ts

Reviewed by deepseek-v4.1-flash · Input: 44K · Output: 7.7K · Cached: 269.2K

Review guidance: REVIEW.md from base branch main

@WebReflection WebReflection changed the title fix(cli): stop glob patterns from bypassing .gitignore fix(cli): stop glob and grep include patterns from bypassing .gitignore Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:cli CLI, TUI and core engine

Projects

None yet

Development

Successfully merging this pull request may close these issues.

glob tool: patterns that match folder names bypass .gitignore (**/* lists node_modules)

2 participants