cmc collects the contents of multiple files and outputs them to stdout, a file, or the system clipboard — in one command. It is purpose-built for preparing source code and project context for Large Language Models (LLMs).
Instead of manually opening five files, selecting text, copying, switching tabs, pasting, and repeating — cmc does it all in a single command.
# Before: 15 seconds of tedious clicking
# After:
cmc -R src/ -p -c # whole project → clipboard in 0.1s- Installation
- Quick start
- Real-world workflows
- Full CLI reference
- Selection vs exclusion
- Binary files
- Clipboard backends
- Output format
- Output destinations
- Exit codes
- Building
- Testing
- Packaging
- Editor integration
- FAQ / Troubleshooting
- Contributing
- License
gcc,makelibmagic-dev(for MIME-type binary detection)
git clone https://github.com/ctrichet/cmc.git
cd cmc
make
sudo make installIf libmagic-dev headers/libraries are not installed system-wide:
apt-get download libmagic-dev
dpkg-deb -x libmagic-dev_*.deb /tmp/libmagic-dev
make MAGIC_DIR=/tmp/libmagic-dev/usr
sudo make install# Import the GPG key
curl -fsSL https://ctrichet.github.io/cmc/cmc.gpg.asc | sudo gpg --dearmor -o /usr/share/keyrings/cmc.gpg
# Add the repository
echo "deb [signed-by=/usr/share/keyrings/cmc.gpg] https://ctrichet.github.io/cmc/ stable main" | sudo tee /etc/apt/sources.list.d/cmc.list
# Install
sudo apt update && sudo apt install cmcsudo dpkg -i cmc_*.deb
sudo apt install -fcmc --versioncmc main.cPrints the contents of main.c to the terminal.
cmc main.c helper.c config.hConcatenates the three files in lexicographic order.
cmc -R src/ -p -cRecursively collects all files under src/, prepends each with its path header (with a leading ./ stripped), and copies everything to the clipboard.
cmc -R . -e "*.o" "build/*" "__pycache__/*"Once -e is encountered, all subsequent positional arguments become exclusion patterns. Scans the current directory recursively, skipping object files, the build directory, and Python cache.
cmc -R . -e ".git/*" "node_modules/*" -p -o ctx.txtAll files under ., excluding .git and node_modules, with path labels, written to ctx.txt.
# With Simon Willison's 'llm' CLI
cmc -R src/ -p | llm -s "Explain the architecture of this project"
# With 'sgpt' (ShellGPT)
cmc src/*.py -p | sgpt "Find bugs in these files"
# With a plain curl prompt to an API
cmc -R . -e "*.md" -p | jq -Rs '{contents: ., question: "Review this code"}' | \
curl -s https://api.openai.com/v1/chat/completions -H "Authorization: Bearer $OPENAI_KEY" -d @-cmc src/CHANGELOG.md src/MIGRATION.md -ccmc -R /var/log/myapp/ -e "*.gz" -p -o debug_context.txt# Find files mentioning "auth" then feed them to cmc
grep -rl "auth" --include="*.rs" src/ | xargs cmc -p -ccmc -R . -E "*.md" -p -o snapshot.mdcmc [OPTIONS] [PATHS...]
| Short | Long | Description |
|---|---|---|
-R |
--recursive |
Recursively scan directories |
-e |
--exclude PATTERN |
Exclude files matching a POSIX glob |
-E |
--excludes PATTERN |
Exclude files matching a glob (also loads $XDG_CONFIG_HOME/cmc/.cmc_excludes) |
-o |
--output FILE |
Write output to FILE |
-c |
--clipboard |
Copy output to system clipboard |
-s |
--symlinks |
Follow symbolic links |
-p |
--paths |
Prepend each file with its path (with a leading ./ stripped) |
-b |
--binary |
Include binary files |
-h |
--help |
Display help and exit |
-v |
--version |
Show version and exit |
-- |
End of option parsing |
-cand-ocannot be used together (exit code 2).-senables following symbolic links in all scan modes (without it, symlinks are skipped).-bis rarely needed —libmagicdetection is reliable for most binary formats.
Arguments are parsed left to right.
- Before the first
-e/-E: all positional arguments are selection patterns (POSIX globs or literal paths). Only files matching at least one pattern are included. - After
-e/-E: all positional arguments are exclusion patterns (POSIX globs). Matching files are skipped. - If no selection pattern is given, all discovered files are included (subject to exclusions).
selection patterns exclusion patterns
┌───────────────┐ ┌──────────────────────────┐
cmc *.c *.h -e *_test.* *_mock.*
# Select .c and .h files, but exclude test/mock files
cmc *.c *.h -e *_test.* *_mock.*
# Select everything under src/, exclude what's in $XDG_CONFIG_HOME/cmc/.cmc_excludes
cmc -R src/ -E "*.o"
# Only exclude, no selection: grab everything except .o files
cmc -R . -e "*.o"
# Multiple exclusion patterns with a single -e
cmc -R src/ -e "*_test.go" "*_mock.go" "*.pb.go"A file is copied only if all three conditions are met:
(matches a selection pattern OR no selection patterns exist)
AND
(does not match any exclusion pattern)
AND
(not a binary file OR --binary is set)
When -E / --excludes is used, cmc automatically loads exclusion patterns from a fixed user-config location:
If $XDG_CONFIG_HOME is set |
$XDG_CONFIG_HOME/cmc/.cmc_excludes |
|---|---|
| Default | ~/.config/cmc/.cmc_excludes |
The file format is one glob pattern per line, with # for comments (same as the patterns passed via -e / -E on the command line).
Example .cmc_excludes:
# Build artifacts
build/*
target/*
dist/*
# Dependencies
node_modules/*
vendor/*
# Cache / metadata
.git/*
__pycache__/*
*.pyc
.eggs/*
# IDE files
*.swp
*.swo
.vscode/*
.idea/*
# Media (usually binary)
*.png
*.jpg
*.pdf
*.zip
Usage — patterns from command line AND from file are combined:
cmc -R . -E "*.txt" -p -cIf .cmc_excludes does not exist, a warning is printed to stderr and cmc continues with only the command-line patterns. A sample file is provided as .cmc_excludes.example in the project repository.
This replaces the old --exclude-file behavior (which required an explicit file path argument).
By default, binary files are silently excluded. Detection uses libmagic to inspect MIME types (ELF executables, images, PDFs, archives, etc.).
To include binaries:
cmc -b image.png data.dbUse with caution — binary content will produce garbled text in your terminal or clipboard.
| Environment | Tool | Install |
|---|---|---|
| Wayland | wl-copy |
sudo apt install wl-clipboard |
| X11 | xclip |
sudo apt install xclip |
Detection is automatic. If neither tool is found, cmc -c exits with code 4.
Without -p, cmc outputs raw file contents concatenated in order.
### FILE: src/main.c ###
int main(void) {
return 0;
}
### FILE: src/util.c ###
int add(int a, int b) {
return a + b;
}
The ### FILE: <path> ### header makes it easy for LLMs to associate code with filenames, and for users to navigate long concatenated outputs.
Files are emitted in lexicographical ascending order of their paths as given by the user or scan result.
a/file.txt
b/file.txt
c/file.txt
| Flags | Destination |
|---|---|
| (none) | stdout |
-o FILE |
FILE (overwrites) |
-c |
System clipboard |
-c and -o are mutually exclusive — using both exits with code 2.
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error (I/O, memory) |
| 2 | Invalid command line arguments, or -c and -o used together |
| 3 | Output file error |
| 4 | Clipboard tool not found |
| 5 | libmagic initialization error |
Scripts can check $? to distinguish failure modes:
cmc -R . -c || case $? in
4) echo "Install wl-clipboard or xclip" ;;
2) echo "Bad arguments" ;;
esacmakeBuild with a custom libmagic path:
make MAGIC_DIR=/path/to/libmagic-dev/usrClean build artifacts:
make cleanmake checkOr run individual test suites:
./tests/test_basic.sh
./tests/test_recursive.sh
./tests/test_exclude.sh
./tests/test_exclude_file.sh
./tests/test_exclusion_selection.sh
./tests/test_error.sh
./tests/test_paths.sh
./tests/test_file_output.sh
./tests/test_mutex.sh
./tests/test_separator.sh
./tests/test_symlink.sh
./tests/test_symlink_flat.sh
./tests/test_trailing_slash.sh
./tests/test_binary.sh
./tests/test_clipboard.sh
./tests/test_symlink_explicit.sh
./tests/test_exclude_absolute_root.sh
./tests/test_long_opts.sh
./tests/test_version.shTests are shell scripts that exercise cmc end-to-end and validate exit codes, output content, and edge cases (empty directories, missing files, recursive symlinks, binary detection, clipboard integration, exclusion config).
sudo apt install devscripts libmagic-dev
dpkg-buildpackage -b -us -ucThe .deb is generated in the parent directory.
Releases are automated via GitHub Actions. To create a new release:
# 1. Merge develop into main
git checkout main
git merge develop
# 2. Tag the release
git tag v1.0.0
# 3. Push — the CI builds .deb packages, creates a GitHub Release,
# and publishes the APT repository to GitHub Pages
git push origin main --tagsThe CI workflow builds .deb packages for amd64 and arm64, attaches them to the GitHub Release, generates checksums, and updates the APT repository on gh-pages.
Map a key to dump the current project into a new buffer:
vim.keymap.set("n", "<leader>cc", function()
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(buf)
vim.fn.systemlist("cmc -R . -p", {}, "silent")
end, { desc = "cmc: load project context into buffer" })Add a task in .vscode/tasks.json:
{
"version": "2.0.0",
"tasks": [{
"label": "cmc: copy project to clipboard",
"type": "shell",
"command": "cmc -R . -p -c",
"problemMatcher": []
}]
}alias ctx='cmc -R . -p -c'
alias ctxo='cmc -R . -p -o context.txt'Install the appropriate clipboard tool:
# Wayland
sudo apt install wl-clipboard
# X11
sudo apt install xclipPass -b / --binary. Note that binary content will produce unreadable output.
cmc writes to stdout by default, so it composes naturally:
cmc src/*.py -p | wc -l
cmc -R . -p | head -100
cmc -R . -p | grep -n "TODO"You can, but it requires a chain of tools with careful handling of delimiters, recursion, and binary detection. cmc packages all of that into a single predictable command with a consistent output format designed for LLM consumption.
Only with -s / --symlinks. Without it, symlinks are skipped in all scan modes (recursive, flat, and explicit paths).
cmc treats all files as raw bytes. It does not perform encoding conversion — files are copied verbatim.
cmc copies whatever file contents it is asked to copy. Do not run it on directories that contain secrets, .env files, API keys, or private keys unless you intend to expose them. A .cmc_excludes file can help exclude sensitive files.
cmc buffers the entire output in memory before writing. Very large files (hundreds of MB) will consume proportional memory. For typical source code projects this is not an issue.
Check that:
- Your selection patterns actually match files
- Your exclusion patterns aren't accidentally excluding everything
- Files are not all binary (use
-bto test) - The paths exist and are readable
Currently Linux only. The code uses POSIX APIs (nftw, getopt_long, fork/exec) that would need adaptation for macOS/BSD (different nftw signature) and Windows (no POSIX subsystem).
Contributions are welcome!
- Open an issue for bugs or feature requests
- Submit a pull request with a clear description
- Ensure all tests pass:
make check - Follow existing code style:
- C17, POSIX APIs
- No trailing whitespace
gcc -Wall -Wextra -Werrorclean
- If adding a feature, include a test
Copyright (C) 2026 cmc contributors
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
See LICENSE for details.
GPLv3 was chosen over AGPLv3 (which adds a network-server source requirement unnecessary for a CLI tool) and MIT (which would allow proprietary redistribution without source). GPLv3 ensures that all modifications remain free and open source, matching the project's goal of serving the open-source LLM tooling ecosystem.