FileMerger
A developer-focused CLI that consolidates project files into a single AI-ready context document for LLMs.
Maintained by DjangoPlay.
FileMerger is a developer-focused CLI tool that consolidates project files into a single, AI-ready text output. It's built to be read by a model, not a human — the goal is to give an AI an accurate, low-noise, structurally-aware picture of "the underlying implementation" of a codebase or a slice of one.
It helps you:
- Feed complete, structured code context to AI tools (ChatGPT, Gemini, Grok, Claude, etc.)
- Target files/directories by name across multiple source roots, without hand-writing paths
- Keep secrets out of what you paste into a chat window
- Produce deterministic, git-ignore-aware snapshots for review or audit
Installation
pip install filemerger-cli
Basic Usage
Merge a directory:
filemerger src/
Merge from multiple named sources, targeting by directory/file name, skipping others:
filemerger --source app api --target views/ models.py --skip migrations/ __pycache__/
Stream Markdown straight to stdout (e.g. to pipe into a clipboard tool):
filemerger src/ --format markdown --stdout | pbcopy
Dry run (no file written):
filemerger . --dry-run
Advanced Usage
Default Output
Merge the current directory using the default output format with common filtering, limits, and statistics.
filemerger . --source . --skip __pycache__/ node_modules/ .git/ --output merged --no-tree --separator 80 --include Dockerfile "*.env.example" --exclude "*.min.js" "*.map" --allow-dir migrations,tests --allow-file .DS_Store --allow-ext .yaml,.yml,.toml --max-size 25 --follow-symlinks --sort path --max-lines-per-file 1000 --max-tokens 200000 --stats --verbose
Markdown Output
Merge the current directory and generate Markdown output.
filemerger . --source . --skip __pycache__/ node_modules/ .git/ --output merged --format markdown --no-tree --include Dockerfile "*.env.example" --exclude "*.min.js" "*.map" --allow-dir migrations,tests --allow-file .DS_Store --allow-ext .yaml,.yml,.toml --max-size 25 --follow-symlinks --sort path --max-lines-per-file 1000 --max-tokens 200000 --stats --verbose
LLM Output
Merge the current directory using the LLM-optimized output format.
filemerger . --source . --skip __pycache__/ node_modules/ .git/ --output merged --llm --no-tree --include Dockerfile "*.env.example" --exclude "*.min.js" "*.map" --allow-dir migrations,tests --allow-file .DS_Store --allow-ext .yaml,.yml,.toml --max-size 25 --follow-symlinks --sort path --max-lines-per-file 1000 --max-tokens 200000 --stats --verbose
Compact LLM Output
Merge the current directory using the compact LLM output format.
filemerger . --source . --skip __pycache__/ node_modules/ .git/ --output merged --llm-compact --no-tree --include Dockerfile "*.env.example" --exclude "*.min.js" "*.map" --allow-dir migrations,tests --allow-file .DS_Store --allow-ext .yaml,.yml,.toml --max-size 25 --follow-symlinks --sort path --max-lines-per-file 1000 --max-tokens 200000 --stats --verbose
AI Markers Output
Merge the current directory using AI-friendly file boundary markers.
filemerger . --source . --skip __pycache__/ node_modules/ .git/ --output merged --ai-markers --no-tree --include Dockerfile "*.env.example" --exclude "*.min.js" "*.map" --allow-dir migrations,tests --allow-file .DS_Store --allow-ext .yaml,.yml,.toml --max-size 25 --follow-symlinks --sort path --max-lines-per-file 1000 --max-tokens 200000 --stats --verbose
JSON Output
Merge the current directory and generate JSON output.
filemerger . --source . --skip __pycache__/ node_modules/ .git/ --output merged --format json --no-tree --include Dockerfile "*.env.example" --exclude "*.min.js" "*.map" --allow-dir migrations,tests --allow-file .DS_Store --allow-ext .yaml,.yml,.toml --max-size 25 --follow-symlinks --sort path --max-lines-per-file 1000 --max-tokens 200000 --stats --verbose
Print to Standard Output
Merge the current directory and print Markdown output directly to the terminal.
filemerger . --source . --skip __pycache__/ node_modules/ .git/ --stdout --format markdown --no-tree --include Dockerfile "*.env.example" --exclude "*.min.js" "*.map" --allow-dir migrations,tests --allow-file .DS_Store --allow-ext .yaml,.yml,.toml --max-size 25 --follow-symlinks --sort path --max-lines-per-file 1000 --max-tokens 200000 --stats --verbose
Dry Run
Preview which files would be merged without generating any output.
filemerger . --source . --skip __pycache__/ node_modules/ .git/ --format markdown --no-tree --include Dockerfile "*.env.example" --exclude "*.min.js" "*.map" --allow-dir migrations,tests --allow-file .DS_Store --allow-ext .yaml,.yml,.toml --max-size 25 --follow-symlinks --sort path --max-lines-per-file 1000 --max-tokens 200000 --dry-run --stats --verbose
Without Secret Redaction
Merge the current directory while disabling automatic secret redaction.
filemerger . --source . --skip __pycache__/ node_modules/ .git/ --output merged --format markdown --no-tree --include Dockerfile "*.env.example" --exclude "*.min.js" "*.map" --allow-dir migrations,tests --allow-file .DS_Store --allow-ext .yaml,.yml,.toml --max-size 25 --follow-symlinks --sort path --no-redact-secrets --max-lines-per-file 1000 --max-tokens 200000 --stats --verbose
Selecting Files
There are two ways to choose input, and they can be combined in one run.
1. Explicit paths (original behavior)
filemerger src/ utils/helpers.py
Any mix of files and directories.
2. Name/path targeting — --source / --target / --skip
For codebases where you want "every views/ folder and every models.py
file under these roots, except migrations":
filemerger --source users apidocs/views help/forms \
--target views/ models.py \
--skip migrations/ __pycache__/
Matching rules (identical for --target and --skip):
| Entry form | Meaning |
|---|---|
views/ (trailing slash) |
Directory name, matched anywhere under a --source root |
views/login.py (contains /, no trailing slash) |
An exact relative path from a --source root |
models.py (no slash) |
A bare filename, matched anywhere under a --source root |
If --target is omitted, every file under --source is a candidate
(subject to the usual filtering below).
This replaces the standalone get_filemerger_paths.py helper script —
its intent is now built directly into the main tool, with the directory
matching bug fixed (it previously never actually included directory
targets) and dependable pruning during the directory walk instead of
fragile path-prefix comparisons.
Filtering
Applied to every candidate file, from either selection method:
- Extensions — allow-listed (
.py,.js,.ts,.json,.md, …). Extend with--allow-ext, or force-include a specific file regardless of extension with--include(glob, e.g.--include Dockerfile). .gitignore— honored per-directory, the same waygitdoes (a nested.gitignoreonly applies to its own subtree), plus an optional.filemergerignorewith identical syntax.- Excluded directories/files —
node_modules,__pycache__,.git,migrations,tests, etc. by default. Restore one with--allow-dir/--allow-file; drop more with--exclude(glob). - Symlinks — not followed by default. Opt in with
--follow-symlinks. - Binary files — detected via a null-byte heuristic (not just a failed extension check) and skipped, reported as such.
- Max file size — 2 MB default, override with
--max-size.
Safety
Secret redaction (on by default)
Before anything is written, file content is scanned for common secret
shapes (AWS keys, generic api_key=..., bearer tokens, private key
blocks, Slack/GitHub tokens) and matches are replaced with
[REDACTED:SECRET]. Disable with --no-redact-secrets if you specifically
need raw content. The number of redactions appears in --stats.
Context budgeting
filemerger . --stats --max-tokens 100000
--stats reports an approximate token count (character-based heuristic,
no extra dependency). --max-tokens warns and exits non-zero if the
merged output would exceed that budget — useful in scripts/CI before
piping into a model with a fixed context window.
Per-file truncation
filemerger . --max-lines-per-file 400
Caps any single file's contributed lines, appending a [truncated after N lines] marker, so one huge generated file can't dominate the output.
Output Formats
Select with --format, or the older shorthand flags (kept for backward compatibility).
--format |
Shorthand flag | Best for |
|---|---|---|
default |
(none) | Human review, audits |
llm |
--llm / --llm-compact |
Legacy structured plain-text for AI ingestion |
ai-markers |
--ai-markers |
Explicit <<<FILE>>> boundary markers |
markdown |
— | Recommended default for AI chat tools — fenced code blocks, per-language syntax highlighting, renders cleanly |
json |
— | Programmatic consumption — a list of {path, language, content, skipped, redactions, ...} |
All formats (except json) are preceded by a directory-tree summary so the
model sees project shape before content. Disable with --no-tree.
filemerger src/ --format markdown
Output Destination
By default, output is written to filemerger-output.txt (or -o <name>)
in the current directory. Use --stdout to stream to stdout instead
(status/stat lines go to stderr so stdout stays clean for piping):
filemerger src/ --format json --stdout > context.json
Statistics
filemerger src/ --stats
Reports: files, lines, bytes, estimated tokens, skipped files (binary / non-UTF8 / unreadable), and redacted-secret count.
Ordering
filemerger src/ --sort {path,name,size,mtime}
Default is path (deterministic, matches prior versions).
Configuration
Optional .filemerger.toml, discovered by searching upward from the
current directory to the filesystem root (git-style), so it works the same
whether you run filemerger from the project root or a subdirectory:
[filters]
max_file_size_mb = 1
exclude_dirs = ["tests"]
[output]
separator_length = 60
Runtime Overrides
CLI flags always win over .filemerger.toml, which always wins over
built-in defaults:
CLI arguments → .filemerger.toml → config.py defaults
filemerger . \
--allow-dir migrations,tests \
--allow-ext .yaml \
--allow-file .DS_Store \
--max-size 5 \
--separator 40
Design Goals
- Deterministic output
- Minimal configuration
- AI-first formatting (Markdown/JSON as first-class formats, not afterthoughts)
- Safe by default (secrets redacted, binaries skipped, symlinks not followed)
- Predictable file ordering
- Zero project mutation
License
This project is licensed under the MIT License. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file filemerger_cli-0.4.0.tar.gz.
File metadata
- Download URL: filemerger_cli-0.4.0.tar.gz
- Upload date:
- Size: 24.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bbe415587f9ab063ea176279ffae2f0344a5e6f3e096d8a7fb5b447ca4df66f9
|
|
| MD5 |
bebd9634d957b72de2cef7d0358f7372
|
|
| BLAKE2b-256 |
a9289fd4467c7bde3430ff3dac887760afbb29571967a53d2fa888cf5bf9940e
|
File details
Details for the file filemerger_cli-0.4.0-py3-none-any.whl.
File metadata
- Download URL: filemerger_cli-0.4.0-py3-none-any.whl
- Upload date:
- Size: 25.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
290570e72a08aa8376c01c74d62c761a2e82f6a10d483d58412ae906b8170dea
|
|
| MD5 |
45f580b92759850852ebeabc8a3fd4be
|
|
| BLAKE2b-256 |
b38922fd997dfeeff2bda887a005c205216d3dea1e1da68c37a4a2d6702c5686
|