File Ignore Patterns in Codebase Memory MCP: How .cbmignore, .gitignore, and Hardcoded Rules Work

Codebase Memory MCP (CBM) implements a deterministic seven-layer ignore system where hardcoded safety rules, fast-mode filters, hierarchical .gitignore files, and project-specific .cbmignore patterns are evaluated in sequence, with later layers overriding earlier matches except for non-negatable safety-core directories.

Codebase Memory MCP is a repository indexing engine that relies on sophisticated file ignore patterns to exclude irrelevant artifacts from memory. Understanding how .cbmignore, .gitignore, and hardcoded rules interact is critical for configuring optimal indexing coverage. The system evaluates exclusion criteria in strict priority order, ensuring that safety-critical directories remain protected while allowing fine-grained project customization.

The Seven-Layer Ignore Architecture

CBM processes file ignore patterns through seven distinct layers defined in src/discover/discover.c. The evaluation order guarantees deterministic behavior:

  1. Hardcoded skip directories – Always excluded (e.g., .git, node_modules)
  2. Hardcoded ignored suffixes – File extensions blocked in all modes (e.g., .pyc, .exe)
  3. Fast-mode skip filenames – Specific filenames excluded in MODERATE and FAST modes
  4. Fast-mode substring patterns – Generic patterns excluded in non-FULL modes
  5. .gitignore hierarchy – Repository root, nested, global, and per-clone exclusions
  6. .cbmignore overrides – Project-specific ignores with negation support
  7. Safety-core enforcement – Non-negatable protection for critical directories

Layer 1 – Hardcoded Safety Directories and Suffixes

The base layer consists of static arrays compiled into the binary. These rules apply universally unless specifically bypassed by the indexing mode.

Non-Negatable Directories (ALWAYS_SKIP_DIRS)

The cbm_should_skip_dir() function checks directories against ALWAYS_SKIP_DIRS at lines 31-51 of src/discover/discover.c. This list includes .git, node_modules, venv, and other build artifacts.

bool cbm_should_skip_dir(const char *dirname, cbm_index_mode_t mode) {
    if (str_in_list(dirname, ALWAYS_SKIP_DIRS)) return true;
    if (mode != CBM_MODE_FULL && str_in_list(dirname, FAST_SKIP_DIRS)) return true;
    return false;
}

File Extension Filters (ALWAYS_IGNORED_SUFFIXES)

Binary and generated file extensions are filtered by cbm_has_ignored_suffix() at lines 64-71. This checks ALWAYS_IGNORED_SUFFIXES (e.g., .png, .exe) and conditionally FAST_IGNORED_SUFFIXES based on mode.

bool cbm_has_ignored_suffix(const char *filename, cbm_index_mode_t mode) {
    for (int i = 0; ALWAYS_IGNORED_SUFFIXES[i]; i++)
        if (ends_with(filename, ALWAYS_IGNORED_SUFFIXES[i])) return true;
    if (mode != CBM_MODE_FULL)
        for (int i = 0; FAST_IGNORED_SUFFIXES[i]; i++)
            if (ends_with(filename, FAST_IGNORED_SUFFIXES[i])) return true;
    return false;
}

Layer 2 – Fast-Mode Optimizations (MODERATE and FAST)

When operating in CBM_MODE_MODERATE or CBM_MODE_FAST, CBM applies additional exclusions to improve performance. These are bypassed only in CBM_MODE_FULL.

Additional Directories and Filenames

The FAST_SKIP_DIRS array expands directory exclusions, while FAST_SKIP_FILENAMES (checked at lines 80-87) filters specific files like LICENSE, CHANGELOG, and lock files.

Substring Pattern Matching

At lines 91-99, cbm_matches_fast_pattern() searches for substrings indicating generated code (e.g., .d.ts, .generated., .test.). These patterns are only active when mode != CBM_MODE_FULL.

Layer 3 – Gitignore Hierarchy

CBM integrates deeply with Git's ignore semantics, supporting the complete .gitignore specification including negation and directory-specific rules.

Repository Root and Nested Files

The discovery routine loads the repository-root .gitignore at lines 989-1016, then dynamically loads nested .gitignore files during directory traversal via try_load_nested_gitignore(). Later patterns in the hierarchy override earlier ones.

Global Excludes and Per-Clone Exclusions

Global excludes are resolved from core.excludesfile (typically configured in ~/.gitconfig) through resolve_global_excludes_path(). Per-clone exclusions are loaded from the Git common directory's info/exclude file.

/* Load repo-root .gitignore */
char gi_path[CBM_SZ_4K];
snprintf(gi_path, sizeof(gi_path), "%s/.gitignore", repo_path);
cbm_gitignore_t *gitignore = cbm_gitignore_load(gi_path);

/* Merge global excludes if available */
if (has_git_config && resolve_global_excludes_path(gi_path, sizeof(gi_path)))
    global_gi = cbm_gitignore_load(gi_path);

Layer 4 – Project-Specific .cbmignore

The .cbmignore file provides project-level control, loaded from <repo>/.cbmignore or a custom path specified in opts->ignore_file at lines 1037-1044.

Loading and Syntax

.cbmignore uses standard gitignore syntax (globs, wildcards, directory anchors). The implementation in src/discover/gitignore.c handles pattern matching through cbm_gitignore_match_result().

Negation Rules and Safety Restrictions

Negation patterns (e.g., !obj/) can un-skip paths excluded by .gitignore or earlier layers. However, safety-core directories.git, node_modules, .worktrees, and .claude-worktrees—are immune to negation as enforced by is_safety_core_dir() at lines 22-25.

Lines 514-525 implement the negation logic:

/* Directory-skip with possible negation */
bool unskipped = cbmignore && !is_safety_core_dir(entry_name) &&
                 cbm_gitignore_match_result(cbmignore, rel_path, true) < 0;
if (!unskipped) return true;       // keep the hard-coded skip

How Mode Selection Affects Ignore Rules

The indexing mode controls whether fast-mode exclusions are active:

  • CBM_MODE_FULL: Ignores only ALWAYS_SKIP_DIRS and ALWAYS_IGNORED_SUFFIXES, providing raw repository visibility
  • CBM_MODE_MODERATE and CBM_MODE_FAST: Enable all fast-mode filters (FAST_SKIP_DIRS, FAST_SKIP_FILENAMES, FAST_PATTERNS)

This behavior is implemented in cbm_should_skip_dir() at lines 53-60 and cbm_has_ignored_suffix() at lines 64-71.

Skip Reason Tracking

The file_skip_reason() function (lines referenced in the skip logic) returns a string identifier for why a file was excluded. Possible values include:

  • "gitignore" – Matched a .gitignore pattern
  • "cbmignore" – Matched a .cbmignore pattern
  • "ignored-suffix" – Matched hardcoded extension filters
  • "skip-list" – Matched fast-mode filename filters
  • "fast-pattern" – Matched fast-mode substring patterns
  • "size-cap" – Exceeded opts->max_file_size

Summary

  • CBM applies seven layers of file ignore patterns in strict priority order, from hardcoded safety rules to project-specific .cbmignore files.
  • Hardcoded directories (.git, node_modules) and suffixes (.pyc, .exe) are filtered first and cannot be bypassed by negation patterns.
  • Fast-mode filters (filenames, substring patterns) are only active outside CBM_MODE_FULL, allowing performance tuning.
  • Gitignore hierarchy supports repository root, nested, global (core.excludesfile), and per-clone (info/exclude) exclusion files.
  • .cbmignore supports standard gitignore syntax including negations (!pattern), but cannot un-skip safety-core directories (.git, node_modules, .worktrees, .claude-worktrees).

Frequently Asked Questions

What is the difference between .cbmignore and .gitignore?

.cbmignore is specific to Codebase Memory MCP and can override .gitignore exclusions through negation patterns (e.g., !build/ to index a directory ignored by Git). However, unlike .gitignore, .cbmignore cannot override hardcoded safety-core directories like .git or node_modules. Both files use identical syntax rules as implemented in src/discover/gitignore.c.

Can I override hardcoded directory exclusions like node_modules?

No. The directories .git, node_modules, .worktrees, and .claude-worktrees are designated as safety-core and are non-negatable. The is_safety_core_dir() function at lines 22-25 of src/discover/discover.c prevents any pattern (including .cbmignore negations) from including these directories, protecting against OOM errors and duplicate indexing.

How does the indexing mode affect which files are ignored?

The indexing mode determines whether fast-mode exclusions apply. In CBM_MODE_FULL, only ALWAYS_SKIP_DIRS and ALWAYS_IGNORED_SUFFIXES are active. In CBM_MODE_MODERATE or CBM_MODE_FAST, additional filters from FAST_SKIP_DIRS, FAST_SKIP_FILENAMES, and FAST_PATTERNS are evaluated, excluding files like LICENSE, CHANGELOG, and generated artifacts (.d.ts, .test.).

Where should I place my .cbmignore file?

Place .cbmignore in your repository root to apply project-wide rules. Alternatively, specify a custom path via the ignore_file option in cbm_discover_opts_t. The file is loaded at lines 1037-1044 of src/discover/discover.c, defaulting to <repo_path>/.cbmignore if no custom path is provided. Nested .cbmignore files are not supported; use a single root file with path-specific patterns instead.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →