How Ignore Pattern Matching Works in Claude Context for Codebases
Claude Context uses a layered ignore-pattern engine that evaluates glob patterns against file paths before any filesystem access, combining default patterns, custom configurations, environment variables, and standard ignore files into a single deduplicated list.
The ignore pattern matching algorithm in Claude Context determines which files enter the indexing pipeline by filtering a candidate set against multiple pattern sources. This process occurs entirely in memory before fs.stat or fs.readFile operations, ensuring ignored files never trigger disk I/O. The implementation spans packages/core/src/context.ts for pattern collection and packages/core/src/sync/synchronizer.ts for the matching engine itself.
Sources of Ignore Patterns in Claude Context
Claude Context aggregates ignore patterns from seven distinct sources, merging them into a unified array that the FileSynchronizer receives during initialization.
Default and Hard-Coded Patterns
The base pattern set originates from DEFAULT_IGNORE_PATTERNS in packages/core/src/context.ts. These cover common exclusions like version control, dependency directories, and build artifacts that should never be indexed regardless of project configuration.
Runtime Custom Patterns via MCP and API
When instantiating a Context directly or through the Model Context Protocol (MCP), you can supply additional patterns through the customIgnorePatterns field of the configuration object. The addCustomIgnorePatterns() method in context.ts appends these to the internal ignorePatterns array.
Environment Variable CUSTOM_IGNORE_PATTERNS
The Context.getCustomIgnorePatternsFromEnv() method reads comma-separated patterns from the CUSTOM_IGNORE_PATTERNS environment variable, enabling CI/CD pipelines and shell scripts to inject exclusions without modifying code:
export CUSTOM_IGNORE_PATTERNS="node_modules/**,dist/**,.env*"
Project-Level .gitignore
The Context.loadIgnoreFile() generic loader parses .gitignore from the project root, treating its patterns as additional exclusion rules. This ensures Claude Context respects the same boundaries as Git.
Custom .*ignore Files
The Context.findIgnoreFiles() discovery mechanism locates editor-specific ignore files like .cursorignore or .codeiumignore, merging their contents into the pattern set. This accommodates toolchain-specific exclusions without polluting .gitignore.
Global ~/.context/.contextignore
Finally, Context.loadGlobalIgnoreFile() loads patterns from the user's home directory, applying user-wide exclusions across all projects indexed by Claude Context.
All sources converge in Context.loadIgnorePatterns(), which deduplicates patterns before handing them to the FileSynchronizer constructor.
The Matching Algorithm in FileSynchronizer
The core matching logic resides in packages/core/src/sync/synchronizer.ts, specifically within FileSynchronizer.shouldIgnore(). This method evaluates every candidate path before any filesystem operation occurs.
Pre-Stat Evaluation Design
FileSynchronizer walks the directory tree recursively. For each entry, it calls shouldIgnore(relativePath, isDirectory) immediately. This design guarantees that ignored files never trigger fs.stat, fs.readFile, or similar operations, minimizing I/O overhead for large projects with extensive dependency trees.
Step-by-Step Matching Flow
The shouldIgnore implementation follows a strict evaluation order:
shouldIgnore(relativePath: string, isDirectory: boolean): boolean {
// 1. Hidden files and directories (prefix '.') are always excluded
if (pathParts.some(p => p.startsWith('.'))) {
return true;
}
// 2. Empty pattern set means no exclusions
if (this.ignorePatterns.length === 0) {
return false;
}
// 3. Normalize path to forward slashes, trim slashes
const normalized = relativePath
.replace(/\\/g, '/')
.replace(/^\/+|\/+$/g, '');
if (!normalized) {
return false; // never ignore the root itself
}
// 4. Direct pattern matching against full normalized path
for (const pattern of this.ignorePatterns) {
if (this.matchPattern(normalized, pattern, isDirectory)) {
return true;
}
}
// 5. Parent-directory cascade check
const parts = normalized.split('/');
for (let i = 0; i < parts.length; i++) {
const partial = parts.slice(0, i + 1).join('/');
for (const pattern of this.ignorePatterns) {
// Directory-only pattern (trailing slash)
if (pattern.endsWith('/') &&
this.simpleGlobMatch(partial, pattern.slice(0, -1))) {
return true;
}
// Full-path glob pattern
if (pattern.includes('/') &&
this.simpleGlobMatch(partial, pattern)) {
return true;
}
// Filename glob applied to each component
if (!pattern.includes('/') &&
this.simpleGlobMatch(parts[i], pattern)) {
return true;
}
}
}
return false;
}
Three-Mode Pattern Classification
The matchPattern helper in FileSynchronizer categorizes each pattern into one of three matching modes:
| Pattern Type | Identification | Matching Behavior |
|---|---|---|
| Directory-only | Ends with / |
Matches only directories; applied to full path or any parent segment |
| Full-path glob | Contains / but doesn't end with / |
Matches entire normalized path against the pattern |
| Filename glob | No / characters |
Matches only the final path component (basename) |
This classification enables precise control: node_modules/ ignores the entire directory tree, src/**/*.test.ts ignores test files in any src subdirectory, and *.log ignores log files regardless of location.
The simpleGlobMatch Implementation
The glob-to-regex conversion resides in simpleGlobMatch, which supports only the * wildcard:
simpleGlobMatch(text: string, pattern: string): boolean {
const regex = new RegExp('^' +
pattern
.replace(/[.+^${}()|[\]\\]/g, '\\$&') // escape regex metacharacters
.replace(/\*/g, '.*') // * matches any sequence
+ '$'
);
return regex.test(text);
}
This implementation intentionally excludes ? and ** to maintain predictable performance. Patterns like node_modules/** become node_modules/.* in regex form, which correctly matches any path starting with node_modules/.
Practical Configuration Examples
Instantiating Context with Custom Ignore Patterns
import { Context } from '@claude/context';
import { MilvusVectorDatabase } from '@claude/vectordb-milvus';
const ctx = new Context({
vectorDatabase: new MilvusVectorDatabase({ /* configuration */ }),
customIgnorePatterns: [
'temp/**', // temporary directories
'*.bak', // backup files
'private/**' // sensitive content
]
});
await ctx.indexCodebase('/path/to/project');
The customIgnorePatterns array passes through addCustomIgnorePatterns() in packages/core/src/context.ts before reaching the FileSynchronizer.
Environment-Based Configuration
# Multiple patterns separated by commas
export CUSTOM_IGNORE_PATTERNS="node_modules/**,dist/**,.env*,coverage/**"
# Optional: extend supported extensions
export CUSTOM_EXTENSIONS=".vue,.svelte,.astro"
node run-indexer.js
The Context.getCustomIgnorePatternsFromEnv() and Context.getCustomExtensionsFromEnv() methods in context.ts parse these variables automatically.
Debug Output for Pattern Matching
To observe which paths get excluded, enable debug logging during synchronization:
// Inside FileSynchronizer.generateFileHashes()
if (this.shouldIgnore(relativePath, entry.isDirectory())) {
console.log(`[Synchronizer] Ignored ${relativePath}`);
continue;
}
Typical output shows the algorithm in action:
[Synchronizer] Ignored node_modules/@types/react/index.d.ts
[Synchronizer] Ignored .git/config
[Synchronizer] Ignored logs/app.log
Performance Characteristics
The FileSynchronizer implementation prioritizes early termination and minimal overhead:
| Optimization | Implementation |
|---|---|
| Hidden file fast-path | Checked before pattern iteration—no regex compilation for .git, .env, etc. |
| Empty pattern short-circuit | Returns false immediately when no patterns configured |
| Single-pass normalization | Path cleaning happens once, reused for all pattern checks |
| Early return on match | return true immediately when any pattern matches |
| Compiled regex caching | simpleGlobMatch could cache regex objects (implementation-dependent) |
The parent-directory cascade check (step 5 in shouldIgnore) adds overhead proportional to path depth, but eliminates false positives where a directory pattern should exclude nested contents.
Summary
- Claude Context aggregates ignore patterns from seven sources: defaults, MCP parameters, environment variables,
.gitignore, custom.*ignorefiles, and a global~/.context/.contextignore - The
FileSynchronizerinpackages/core/src/sync/synchronizer.tsevaluates paths before any filesystem access viashouldIgnore() - Pattern matching uses three modes: directory-only (trailing
/), full-path glob (contains/), and filename glob (no/) - The
simpleGlobMatchhelper converts glob patterns to regexes, supporting only*wildcards for predictable performance - Hidden files and directories are always excluded regardless of pattern configuration
Frequently Asked Questions
What pattern syntax does Claude Context support?
Claude Context supports a subset of glob syntax limited to the * wildcard. Patterns may end with / to indicate directory-only matching, contain / for full-path matching, or omit slashes for filename matching. The simpleGlobMatch function in synchronizer.ts explicitly does not support ? or ** quantifiers to maintain deterministic performance characteristics.
How do I add custom ignore patterns without modifying code?
Set the CUSTOM_IGNORE_PATTERNS environment variable with comma-separated patterns before running the indexer. The Context.getCustomIgnorePatternsFromEnv() method in context.ts automatically parses and merges these patterns. Alternatively, create a .contextignore file in your project root or at ~/.context/.contextignore for user-wide exclusions.
Why are hidden files always ignored regardless of my patterns?
The FileSynchronizer.shouldIgnore() method implements a fast-path check that immediately returns true for any path component starting with . (dot). This hard-coded rule in synchronizer.ts precedes all pattern evaluation and cannot be overridden by configuration. The design prioritizes security and performance by excluding version control metadata, environment files, and editor state by default.
How does Claude Context handle nested directory patterns?
The matching algorithm implements parent-directory cascade checking in step 5 of shouldIgnore(). When evaluating a path like node_modules/lodash/index.js, the algorithm checks each parent segment (node_modules, node_modules/lodash) against directory-only patterns (those ending with /). If any parent matches, the entire subtree is excluded. This ensures patterns like node_modules/ correctly exclude all nested content without requiring explicit ** syntax.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →