Filter precedence
FileConcat decides which files end up in your output through a four-layer pipeline. Each layer runs in order and the next one only sees what survived the previous. Knowing the order makes it easier to predict why a file appears or disappears when you type a pattern.
The four layers
- Drop-time prune runs at ingestion.
- Validation cache runs once per file at ingestion.
- Pattern layer runs live every time you edit a textarea.
- Manual overrides are sticky and applied last.
The pipeline is deterministic. Same inputs produce the same included
decision for every file.
Layer 1: drop-time prune
Only two directory names are excluded before files even enter memory:
.git and node_modules. They live in a hardcoded set named
HARDCODED_PRUNE_DIRS inside apps/web/src/hooks/use-file-ingestion.ts.
The walker that traverses your dropped folder skips any directory whose
name matches.
These two names are special because they are large enough to crash the
browser tab on real projects. Every other ignore pattern, including
dist, build, vendor, and lockfiles, runs at filter time. Those
files are read into memory first and hidden from view by a later layer.
For typical projects this is invisible. If a drop feels slow, look for oversized auto-generated folders that the patterns will catch later but that the walker still pulled in.
Layer 2: validation cache
When a file is ingested, FileConcat checks two things and records the
result in a sticky validations map:
- Is the content binary? Binary files are excluded permanently.
- Does the file exceed the configured max size (default 32 MB)? Oversize files are excluded permanently.
This check runs once per file. Pattern edits do not retrigger it, which is why pattern changes feel instantaneous even on large drops.
Layer 3: pattern layer
Two textareas in the FilterRail drive this layer:
- Include is a comma-separated list of glob patterns. Empty means every surviving file passes.
- Ignore is a comma-separated list of glob patterns. Files matching any of them are excluded.
There is one rule that surprises people: if the Include textarea has any non-empty content, the Ignore textarea is skipped entirely. The include list is treated as an exclusive whitelist. To go back to "every file except these", you have to clear the Include textarea.
Pattern matching uses the pathMatches helper in
packages/core/src/path-utils/glob-match.ts. Two semantics matter:
- Bare directory or filename patterns match anywhere in the path.
Typing
node_modulesmatchesapps/web/node_modules/react/index.js, not just a top-levelnode_modules. The same goes for*.log, matching every.logfile at any depth. - Slashed patterns match the full path or any path suffix obtained by
stripping leading directories. Typing
src/**/*.tsmatchessrc/app.tsxand alsomyrepo/src/app.tsx. This lets patterns work when you drop a folder whose name you do not want to type.
See File filtering for the pattern syntax in more detail.
Layer 4: manual overrides
Every checkbox in the file tree writes to a sticky map called
userToggled. An entry for a path can be "include" or "exclude",
and either value wins over whatever the pattern layer decided.
The map only stores divergences. If you tick a checkbox so that the file's state matches the pattern decision anyway, the entry is removed instead of recorded. The result is that the override map stays small and only tracks the deliberate exceptions.
The counter line shows how many overrides are active and offers a
clear link that empties the map. Pattern edits never touch this map,
so a preset chip click or an Include rewrite cannot accidentally lose
your individual decisions.
Worked example
You drop a Node project into the workbench:
- Drop-time prune removes
.git/andnode_modules/. Everything else, includingdist/, lands in memory. - Validation marks any binaries (PNGs, fonts) as excluded.
- You type
distinto the Ignore textarea. The file tree updates immediately. Every file underdist/disappears. - You manually re-check one file at
dist/special.jsbecause you want it in the context.userToggled["dist/special.js"] = "include". - You edit the Ignore textarea to add
vendor. Pattern layer hidesvendor/*but the override fordist/special.jsis untouched.
Every other interaction in the workbench is a variation on this loop.

