How the .cbmignore File Functions in DeusData Codebase Memory MCP Projects

The .cbmignore file is a project-specific ignore file used by the Codebase Memory MCP indexer that follows .gitignore syntax but operates at layer 4 of a five-layer filter stack, meaning it cannot override repository .gitignore rules and can only rescue files from global Git excludes.

The DeusData/codebase-memory-mcp repository introduces a custom ignore mechanism for its intelligent code indexer. The .cbmignore file allows developers to control exactly which files enter the indexer's discovery walk, but its behavior differs significantly from standard Git ignore rules. Understanding the precedence hierarchy defined in src/mcp/mcp.c is essential for optimizing what gets indexed in your DeusData projects.

What Is the .cbmignore File?

The .cbmignore file is a repository-specific configuration that controls the Codebase Memory MCP indexer's file discovery process. According to the specification in docs/cbmignore.md, it uses the same pattern syntax as .gitignore—including wildcards, negation (!), and anchored paths—but only affects what the indexer discovers when walking the repository tree. Unlike .gitignore, which Git uses for version control, .cbmignore specifically targets the indexing pipeline and ultimately influences how data is stored in components like internal/cbm/zstd_store.c.

The Five-Layer Filter Precedence Hierarchy

When the indexer initiates a discovery walk, it applies filters in a strict order. The first layer that rejects a path wins, preventing subsequent layers from evaluating that path. As implemented in src/mcp/mcp.c, the hierarchy is:

  1. Built-in skip list
  2. Repository .gitignore
  3. Nested .gitignore files
  4. .cbmignore
  5. Git global excludes

Layer 1: Built-in Skip List

The indexer begins with a hardcoded list of directories and patterns to skip, including .git, node_modules, dist, vendor, and various tool caches. These skips are non-negotiable—.cbmignore cannot override this layer.

Layer 2: Repository .gitignore

The indexer merges the repository's .gitignore patterns with Git's info/exclude file. In this layer, later patterns win on conflict. Because this layer executes before .cbmignore, any file excluded here is permanently removed from consideration before the indexer reaches layer 4.

Layer 3: Nested .gitignore Files

Gitignore files in subdirectories are evaluated relative to their own directories. These nested configurations apply before .cbmignore, further restricting the file set that reaches the custom ignore layer.

Layer 4: The .cbmignore File

At this stage, the indexer consults the .cbmignore file. A positive match excludes the path from indexing. However, a negated match (!) has limited power: it can only rescue paths from layer 5 (global Git excludes). It cannot re-include files excluded by the repository .gitignore or nested .gitignore files.

Layer 5: Global Git Excludes

Finally, the indexer checks Git's global configuration, specifically core.excludesFile or $XDG_CONFIG_HOME/git/ignore. This is the only layer that .cbmignore negations can override.

Practical Syntax and Implementation Examples

The .cbmignore file resides in your repository root and supports standard Git ignore patterns. Here are practical examples from the documentation:


# .cbmignore – exclude all generated protobuf files

*.pb.go

# Exclude a top-level directory (anchor to repo root)

/third_party/

# Exclude any directory named "snapshots" anywhere

snapshots/

# Re-include a file that the global Git exclude would skip

!*.sql   # works only for layer 5, not for .gitignore or built-in skips

For comparison, the repository .gitignore (layer 2) might contain:


# .gitignore – typical repository-wide ignores

node_modules/
dist/
*.log

Adding *.pb.go to .cbmignore stops the indexer from seeing protobuf source files even if .gitignore does not mention them. However, if you add !*.sql to rescue SQL files, this only works if they were excluded by your global Git configuration—not if they were excluded by the repository's .gitignore.

Critical Limitations and Directory Exclusions

Understanding scope limitations prevents indexing surprises. Directory-level exclusions are terminal: if a directory is excluded by .cbmignore, the walk never descends into it, meaning subsequent negations cannot re-include files within that directory. You must re-include the directory itself before you can re-include its contents.

Additionally, negations inside .cbmignore follow the "last matching pattern wins" rule, consistent with Git's behavior. However, remember that this rule only applies within the constrained scope of layer 5 overrides.

Summary

  • The .cbmignore file controls what the Codebase Memory MCP indexer discovers, using standard Git ignore syntax.
  • It operates at layer 4 of a five-layer filter system, positioned after built-in skips, repository .gitignore, and nested .gitignore files.
  • .cbmignore cannot override repository .gitignore or built-in skip lists.
  • Negation patterns (!) in .cbmignore can only rescue files excluded by global Git excludes (layer 5).
  • Excluding a directory in .cbmignore prevents the indexer from traversing it, making file-level negations within that directory impossible.
  • Key implementation files include docs/cbmignore.md for the specification and src/mcp/mcp.c for the filter application logic.

Frequently Asked Questions

Can .cbmignore override my repository's .gitignore rules?

No. According to the filter hierarchy in src/mcp/mcp.c, the repository .gitignore (layer 2) is evaluated before .cbmignore (layer 4). Once a path is rejected by .gitignore, it never reaches the .cbmignore evaluation stage. The .cbmignore file can only influence files that pass through the first three layers.

Why are my negation patterns in .cbmignore not working?

Negation patterns using ! in .cbmignore have limited scope. They can only rescue files that were excluded by global Git excludes (layer 5), such as those defined in core.excludesFile or $XDG_CONFIG_HOME/git/ignore. They cannot re-include files excluded by the repository .gitignore, nested .gitignore files, or the built-in skip list. Check which layer is blocking your files first.

Does .cbmignore support the same syntax as .gitignore?

Yes. As documented in docs/cbmignore.md, .cbmignore supports wildcards (*), negation (!), anchored patterns (/pattern), and directory-specific patterns (dir/). It follows the same "last matching pattern wins" resolution rules as Git. However, the key difference is its position in the precedence stack and its limitation to affecting only the MCP indexer, not Git itself.

What happens if I exclude a directory in .cbmignore but try to re-include a file inside it?

If you exclude a directory in .cbmignore, the indexer terminates the walk at that directory and never examines its contents. Consequently, you cannot use a subsequent negation pattern to re-include individual files within that excluded directory. You must either exclude the directory using a more specific pattern that allows the parent to be traversed, or re-include the directory itself before attempting to re-include specific files.

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 →