One command to copy your entire codebase to clipboard for AI assistants.
catclip src # That's it.Don't worry about accidentally copying that package-lock.json or creating a .gitignore before first run.
- ⚡ Instant - Zero setup, smart defaults, copies 5000+ files in seconds
- 🔍 Fuzzy when needed -
catclip componentsresolves directly when unique or with bundledfzfwhen multiple matches remain - 📄 Near-instant filename lookup -
catclip Footer.tsxor shorthands likeFooresolve exact file names across the repo almost instantly - 🧩 Multiple targets -
catclip README.md src docsin one run - 🧾 File headers in output - each file is wrapped in
<file path="path/to/file">tags - 🌳 Visual preview - Tree view with file count, size, and token estimate before copying
- 🙈 Git-aware - Respects safe discovery rules from
.gitignoreand.hiss, filters by staged/unstaged/untracked in git repos, and can output diffs instead of full files - 🎛️ Flexible ignores -
--exclude "*.css"to skip,--includeto authorize blocked files or directories from.gitignoreor.hiss,--onlyto narrow authorized targets safely - 🛡️ Secret protection - Blocks
.env, keys, credentials
brew tap tigreau/catclip && brew install catclipPackaged installs are expected to include catclip plus private bundled rg and fzf helpers. Runtime does not fall back to arbitrary user PATH copies.
curl -fsSL https://raw.githubusercontent.com/tigreau/catclip/main/install.sh | bashIf curl is not available:
wget -qO- https://raw.githubusercontent.com/tigreau/catclip/main/install.sh | bashThis installer downloads the latest prebuilt release bundle. It does not require Go.
Requirements: Clipboard tool (auto-detected)
- macOS: Built-in ✓
- Linux:
xcliporwl-clipboard
Bundled with catclip:
ripgrepfor Git-visible file discovery and--containsfzffor fuzzy target resolution
Packaged installs always carry private bundled rg and fzf binaries. If one is missing, the install is incomplete and should be reinstalled instead of relying on a system fallback.
Manual install (Linux binary, no script)
Download the release archive that matches your architecture:
catclip_linux_amd64.tar.gzforx86_64catclip_linux_arm64.tar.gzforaarch64/arm64
PREFIX="${PREFIX:-$HOME/.local}"
BIN_DIR="$PREFIX/bin"
SHARE_DIR="$PREFIX/share/catclip"
mkdir -p "$BIN_DIR" "$SHARE_DIR"
tar -xzf catclip_linux_amd64.tar.gz
install -m 755 catclip "$BIN_DIR/catclip"
install -m 755 catclip-tree "$BIN_DIR/catclip-tree"
install -m 644 VERSION "$SHARE_DIR/VERSION"
install -d "$SHARE_DIR/bin"
install -m 755 bin/rg "$SHARE_DIR/bin/rg"
install -m 755 bin/fzf "$SHARE_DIR/bin/fzf"If ~/.local/bin is not already on PATH, add it in your shell profile.
The global config (~/.config/catclip/.hiss) is created automatically on first run.
Build from source
git clone https://github.com/tigreau/catclip.git
cd catclip
./install.shWhen run from a cloned checkout, ./install.sh builds the checked-out source instead of downloading a release bundle.
Go is only required for this source-install path.
The source install also copies your current rg and fzf into the installed package under share/catclip/bin/.
Manual local build (developer-only, not a full packaged install):
go build ./cmd/catclipThis raw binary does not include the private bundled rg/fzf tools. For a normal local install, use ./install.sh.
Updating & Uninstalling
Use the section that matches how you installed catclip.# Homebrew
brew upgrade catclip
brew uninstall catclip
# Direct script / local install
./uninstall.sh
# Manual binary install
rm -f "$HOME/.local/bin/catclip" \
"$HOME/.local/bin/catclip-tree" \
"$HOME/.local/share/catclip/VERSION" \
"$HOME/.local/share/catclip/bin/rg" \
"$HOME/.local/share/catclip/bin/fzf"# Open the safe picker (interactive TTY):
catclip
# Open the modifier picker (interactive TTY):
catclip --
# Copy source directory:
catclip src
# Fuzzy search:
catclip components # Resolves a unique 'components' dir directly; if multiple exist, fzf lets you choose
# Direct file target:
catclip Button.tsx # Near-instant exact basename lookup anywhere
catclip Sidebar.tsx # Another exact basename lookup
# Direct scoped shorthand:
catclip layout/Footer.tsx # Resolves directly when unique
# File shorthand:
catclip btn.tsx # Resolves directly when unique, otherwise uses bundled fzf
# Exact nested path:
catclip src/components/ui/Button.tsx
# Multiple targets at once:
catclip README.md src docs Button.tsxPlain targets stay independent: catclip src Button.tsx docs searches Button.tsx across the whole repo. Exact paths, exact basenames, and deterministic shorthand resolve directly; bundled fzf is only used when shorthand still has multiple viable matches.
More Examples
# Authorize a blocked directory for this run:
catclip --include tests
# Authorize a blocked file for this run:
catclip --include .env.production
# Authorize and narrow a blocked target safely:
catclip --include coverage --only "*.json"
# Output to screen (stdout) instead of clipboard:
catclip src --print
# Preview what would be copied (fast dry-run):
catclip src --preview
# Skip files (this run only):
catclip src --exclude "LoginForm.tsx"
# Only files containing a pattern (regex):
catclip src --contains "TODO"
# Only blocks around TODO matches (not full files):
catclip src --contains "TODO" --snippet
# Staged changes as unified diff (great for commit review):
catclip --staged --diff
# All changes as patches + architecture reference:
catclip --changed --diff --then src/api/reference.tscatclip --changed # All modified: staged + unstaged + untracked
catclip src --changed # Scoped to srcUse specific filters instead of --changed to narrow what you grab:
catclip --staged # Files in the git index (staged for commit)
catclip --unstaged # Tracked files with uncommitted modifications
catclip --untracked # New files not yet tracked by git
catclip --staged --untracked # Combine: staged + new, skip WIP edits--changed is shorthand for all three. Each flag implies --changed automatically.
Replace full file content with unified git patches:
catclip --changed --diff # All modified files as patches
catclip --staged --diff # Staged changes only — ideal for commit review
catclip --unstaged --diff # WIP edits — what you're actively changingUntracked files have no diff and are included with their full content.
The tree preview shows [diff only] or [snippet only] on files with partial output.
With --contains, extract only the blank-line-bounded blocks around each match instead of the full file:
catclip src --contains "TODO" --snippet # Blocks around each TODO
catclip . --contains "useState" --snippet # React hook call-sites onlyIf a match's blank-line-bounded block spans the whole file, --snippet can
look identical to full-file output for that file even though snippet mode is
active.
Use --then to apply different modifiers to different targets:
catclip src --only "*.ts" --then tests --only "*.test.ts"
# Scope 1: src — only TypeScript source files
# Scope 2: tests — only test filesOverlapping scopes are allowed; final copied files are deduplicated by path.
A bare --then starts a normal new scope from .; it is not a "remaining
files only" operator. Think of --then as the one-command equivalent of
running multiple catclip scope commands and unioning their results.
Without --then, all targets share the same modifiers:
catclip src lib --only "*.ts" # Both filtered to .ts filesWithin one scope, modifier stages are sequential.
That means:
- each
--only ...occurrence is one narrowing stage - each
--exclude ...occurrence is one removing stage - values inside one occurrence OR together
- later stages run after earlier ones
Example:
catclip src \
--only "*.ts" "*.tsx" \
--exclude "*.test.tsx" "*.stories.tsx" \
--only "*Auth*" "*Login*"Meaning:
- start from
src - keep TypeScript files
- remove test and story files
- narrow again to files whose names match
*Auth*or*Login*
--include is intentionally different: it is an additive authorization stage,
not a normal sequential filter stage like --only / --exclude.
catclip uses ~/.config/catclip/.hiss (gitignore-inspired syntax, created on first run) plus the local project .gitignore as its safe discovery baseline.
On first run, catclip creates the default .hiss and applies it immediately, so discovery is still safe from the start.
That means you usually do not need to explain ignores every run: .hiss plus local .gitignore already block things like .env.*, credentials.json, .idea/, .vscode/, tests/, fixtures/, coverage/, and lockfiles such as package-lock.json, Cargo.lock, and go.sum.
# Example .hiss file (trailing / = directory)
node_modules/
*.log
.env
Edit config:
catclip --hiss # open ignore config in editor
catclip --hiss-reset # restore defaultsFor this run only:
catclip src --exclude "*.test.*" # skip test files
catclip --include tests # authorize ignored tests/
catclip --include .env.production # authorize a blocked file
catclip --include coverage --only "*.json" # authorize ignored coverage/, then narrow| Flag | Description |
|---|---|
-h, --help |
Show help |
-y, --yes |
Skip confirmation |
-q, --quiet |
Suppress normal stderr output; in non-preview runs this also skips tree rendering and confirmation |
-v, --verbose |
Show phase timings and debug info |
--exclude VALUE... |
Add one exclude stage (values OR together; trailing / = directory) |
-p, --print |
Output to screen (stdout) instead of clipboard; output spinner stays off so stdout remains clean, and interactive TTY runs add a stderr separator line before payload output |
--hiss |
Open ignore config in editor |
-t, --no-tree |
Skip tree rendering |
--hiss-reset |
Restore default ignore config |
--only VALUE... |
Add one only stage (values OR together within that stage) |
--changed |
All modified files: staged + unstaged + untracked (requires Git) |
--staged |
Only staged files (git index) |
--unstaged |
Only unstaged tracked modifications |
--untracked |
Only new untracked files |
--diff |
Emit unified diff instead of full file (requires a change-selection flag) |
--contains PATTERN |
Only files whose contents match regex pattern |
--snippet |
With --contains: emit only matched blocks (blank-line bounded) |
--then |
Start a new scope (separate targets with different modifiers) |
--preview |
Show file tree and token count without copying |
--include VALUE... |
Authorize one or more ignored targets for this scope |
Full docs: catclip --help
Stage semantics:
--onlyand--excludeare sequential stages within a scope- repeating them later starts another stage, not a global merged set
--includeis additive and special: it authorizes ignored targets into the current scope before later stages run
No clipboard tool found
Install for your platform:
# Ubuntu/Debian
sudo apt install xclip # or wl-clipboard for Wayland
# Fedora
sudo dnf install xclip # or wl-clipboard for Wayland
# Arch
sudo pacman -S xclip # or wl-clipboard for WaylandOr output to screen (stdout): catclip src --print > code.txt
Directory ignored
Check: catclip --hiss
Include this run: use --include, optionally with --only to narrow
Remove permanently: catclip --hiss (delete the line from the config)
PRs welcome! Keep changes portable across macOS and Linux, and preserve CLI/output parity.
- Fork & clone
- Create branch:
git checkout -b feature/name - Submit PR