Share your codebase with LLMs - codebase export tool for AI conversations
Project description
contextr (ctxr)
"git add for AI conversations." Select the files you want an LLM to "see," format them beautifully, and get them onto your clipboard—or into a file—in one command.
contextr is a command-line tool for preparing and sharing code context with Large Language Models (LLMs) such as ChatGPT, Claude, Gemini, etc. It watches the files you care about, respects git-style ignore rules, and exports a clean, LLM-friendly Markdown bundle with a file tree and (optionally) file contents.
Why contextr?
When working with LLMs on coding tasks, you often need to share relevant parts of your codebase. Manually copying files is tedious and error-prone. contextr solves this by letting you:
- Curate once, export anytime — Define what files matter, then get fresh snapshots instantly
- Stay focused — Only share what's relevant, not your entire repo
- Keep secrets safe — Ignore sensitive files with familiar gitignore patterns
- Work naturally — Use glob patterns you already know from git
Features
- Focused Context — Watch only what matters using familiar glob patterns
- Git-style Ignores — Last-match-wins semantics,
!negations,**globs, bare directory names - One-shot Export — Copy straight to clipboard or write to a file
- Smart Formatting — Auto-detected language hints, dynamic fence handling, file tree visualization
- Cross-platform — Works on macOS, Linux, and Windows
- Typed & Tested — Strict typing with Pyright
Table of Contents
- Installation
- Quick Start
- CLI Reference
- Workflows & Examples
- Output Format
- Ignore Semantics (Git-style)
- Python API
- Configuration & State
- Troubleshooting
- Security & Privacy
- Development
- FAQ
- License
Installation
Requires Python 3.12+
Option A — PyPI (recommended)
pip install contextr
This installs both commands: ctxr (short) and contextr (full).
Option B — pipx (isolated environment)
pipx install contextr
Option C — From source
git clone https://github.com/nathan-luo/contextr.git
cd contextr
# For development (uses uv)
uv sync --extra dev
# Or editable install with pip
pip install -e .
Note (Linux): Clipboard features rely on
pyperclip. On some systems you may need a clipboard helper such asxcliporxsel:sudo apt install xclip # Debian/Ubuntu
Quick Start
# 1. Initialize (once per repo)
ctxr init
# 2. Watch files you care about
ctxr watch "src/**/*.py" "pyproject.toml" "README.md"
# 3. Optionally import your .gitignore rules
ctxr gis
# 4. Export context (copies to clipboard)
ctxr
# 5. Paste into your LLM conversation!
That's it! Your watched files are now formatted as Markdown and ready to paste.
Quick Command Reference
| Command | Shortcut | Description |
|---|---|---|
ctxr |
- | Export context to clipboard |
ctxr init |
- | Initialize .contextr directory |
ctxr watch <patterns> |
- | Add glob patterns to watch |
ctxr ignore <patterns> |
- | Add ignore rules |
ctxr unignore <patterns> |
- | Remove ignore rules |
ctxr gis |
- | Import .gitignore patterns |
ctxr watchclear |
ctxr wc |
Clear watched patterns/files |
ctxr ignoreclear |
ctxr ic |
Clear ignore rules (keeps .gitignore imports) |
CLI Reference
Use ctxr (or the full alias contextr) for all commands.
ctxr — Export Context (default)
ctxr [OPTIONS]
With no subcommand, ctxr refreshes your watched files and exports the context.
| Option | Description |
|---|---|
--to-file PATH, -o PATH |
Write output to a file instead of clipboard |
--no-clipboard |
Don't copy to clipboard (print to stdout) |
--absolute |
Use absolute paths in output |
--no-contents |
Export only the file tree, no file contents |
Examples:
ctxr # Export to clipboard
ctxr -o context.md # Write to file
ctxr --no-contents # Just the file tree
ctxr --absolute -o full.md # Absolute paths to file
ctxr init — Initialize
ctxr init
Creates the .contextr/ directory and default .ignore file. Run this once per project.
- Creates
.contextr/state.jsonfor tracking watched patterns - Creates
.contextr/.ignorewith default*.lockrule - Safe to run multiple times (won't overwrite existing config)
ctxr watch — Add Watch Patterns
ctxr watch <PATTERN...>
Add glob patterns to your watch list. Files matching these patterns will be included in exports.
Examples:
# Watch Python source files
ctxr watch "src/**/*.py"
# Watch multiple patterns at once
ctxr watch "src/**/*.py" "tests/**/*.py" "pyproject.toml"
# Watch a specific file
ctxr watch "README.md"
# Watch entire directories
ctxr watch "src" "docs"
Important: Always quote glob patterns to prevent shell expansion!
ctxr ignore — Add Ignore Rules
ctxr ignore <PATTERN...>
Add patterns to the ignore list. Matching files are immediately removed from context.
Examples:
# Ignore Python cache
ctxr ignore "**/__pycache__/**" "*.pyc"
# Ignore build artifacts
ctxr ignore "build/" "dist/" "*.egg-info"
# Ignore test files
ctxr ignore "**/test_*.py"
ctxr unignore — Remove Ignore Rules
ctxr unignore <PATTERN...>
Remove specific patterns from the ignore list. Use this to undo previous ignore commands.
Examples:
# Stop ignoring log files
ctxr unignore "*.log"
# Remove multiple patterns
ctxr unignore "*.pyc" "**/__pycache__/**"
ctxr gis — Git Ignore Sync
ctxr gis
Import patterns from your .gitignore into .contextr/.ignore. This is a one-way sync that adds patterns without removing existing ones.
- Skips patterns already present
- Handles inline comments (
pattern # comment) - Run again after updating
.gitignoreto sync new patterns
ctxr watchclear / ctxr wc — Clear Watched Patterns
ctxr watchclear
ctxr wc # shortcut
Clear all watched patterns and files from context. Ignore rules are preserved.
Use this to start fresh with a new set of files to watch.
ctxr ignoreclear / ctxr ic — Clear Ignore Rules
ctxr ignoreclear [OPTIONS]
ctxr ic [OPTIONS] # shortcut
Clear user-defined ignore rules. By default, patterns imported from .gitignore are preserved (re-imported after clearing).
| Option | Description |
|---|---|
--clear-gis |
Also clear patterns imported from .gitignore |
Examples:
ctxr ic # Clear user ignores, keep .gitignore patterns
ctxr ic --clear-gis # Clear everything (except built-in *.lock)
Note: The built-in
*.lockrule is always preserved to prevent accidentally sharing lockfiles.
Workflows & Examples
Workflow 1: Python Project
# Setup (once)
ctxr init
ctxr gis # Import .gitignore
ctxr watch "src/**/*.py" "pyproject.toml" # Watch source + config
# Daily use
ctxr # Export and paste to LLM
Workflow 2: Full-stack Web App
ctxr init
ctxr gis
ctxr watch "src/**/*.ts" "src/**/*.tsx" # Frontend
ctxr watch "api/**/*.py" # Backend
ctxr watch "*.json" "*.yaml" # Configs
ctxr ignore "**/node_modules/**" "**/.next/**"
Workflow 3: Documentation Focus
ctxr init
ctxr watch "docs/**/*.md" "README.md" "CHANGELOG.md"
ctxr --no-contents -o tree.md # Just structure
ctxr -o full-docs.md # Full content
Workflow 4: Debugging a Specific Issue
# Temporarily focus on problem area
ctxr wc # Clear current watch
ctxr watch "src/auth/**/*.py" "tests/test_auth.py"
ctxr # Share focused context
# Later, restore broader context
ctxr wc
ctxr watch "src/**/*.py"
Workflow 5: Sharing with Different LLMs
# Claude/ChatGPT (clipboard)
ctxr
# Local LLM / file-based workflow
ctxr -o context.md
# Just structure for initial overview
ctxr --no-contents
Output Format
Running ctxr produces a Markdown document optimized for LLMs:
# Project Context: your-project
Files selected: 5
## File Structure
```
📁 Context
├── src
│ ├── main.py
│ └── utils
│ └── helpers.py
├── tests
│ └── test_main.py
└── pyproject.toml
```
## File Contents
### src/main.py
```python
def main():
print("Hello, World!")
```
### src/utils/helpers.py
```python
def helper():
pass
```
Format Features
- Smart Language Detection — File extensions map to syntax highlighting hints
- Dynamic Fences — Automatically uses longer fence sequences if your code contains backticks
- Truncation — Large files are truncated with
[... truncated ...]marker - Clean Tree — Visual hierarchy with folder/file icons
Ignore Semantics (Git-style)
contextr implements git-style ignore semantics with ordered rules and "last match wins":
| Pattern | Meaning |
|---|---|
*.log |
Ignore all .log files anywhere |
build/ |
Ignore build directory |
build |
Same as above (bare name = directory) |
/build |
Only ignore build at repo root |
**/test_*.py |
Ignore test files anywhere |
!important.log |
UN-ignore important.log (negation) |
a/**/b |
Match a/b, a/x/b, a/x/y/b, etc. |
Platform Behavior
- macOS/Windows: Case-insensitive matching
- Linux: Case-sensitive matching
Example .contextr/.ignore
# Build artifacts
build/
dist/
*.egg-info
# Cache
**/__pycache__/**
*.pyc
.pytest_cache/
# IDE
.idea/
.vscode/
# But keep VS Code settings
!.vscode/settings.json
# Environment
.env
.env.*
!.env.example
# Always ignored (built-in)
*.lock
Python API
Use contextr programmatically in your own tools:
from pathlib import Path
from contextr import ContextManager, ProfileManager, format_export_content
# Initialize
cm = ContextManager()
# Watch and ignore
cm.watch_paths(["src/**/*.py", "*.md"])
cm.add_ignore_patterns(["**/__pycache__/**", "*.pyc"])
# Refresh and export
cm.refresh_watched()
output = format_export_content(
files=cm.files,
base_dir=cm.base_dir,
include_contents=True, # False for tree only
absolute_paths=False, # True for absolute paths
max_bytes=512_000, # Per-file size limit
)
# Save to file
Path("context.md").write_text(output, encoding="utf-8")
# Or work with files directly
print(f"Watching {len(cm.watched_patterns)} patterns")
print(f"Context has {len(cm.files)} files")
for f in cm.get_file_paths(relative=True):
print(f" - {f}")
Using Profiles (Advanced)
from contextr import ContextManager, ProfileManager
cm = ContextManager()
pm = ProfileManager(cm.storage, cm.base_dir)
# Save current watch patterns as a profile
cm.watch_paths(["src/**/*.py"])
pm.save_profile("python-only",
watched_patterns=list(cm.watched_patterns),
description="Just Python source files"
)
# Later, load it back
profile = pm.load_profile("python-only")
cm.apply_profile(profile, "python-only")
Configuration & State
All state is stored in the .contextr/ directory:
.contextr/
├── state.json # Current watched patterns and file list
├── .ignore # Ignore rules (git-style)
└── profiles/ # Saved profiles (if using Python API)
└── my-profile.json
state.json Structure
{
"current_profile": null,
"files": [
"src/main.py",
"src/utils.py"
],
"watched_patterns": [
"src/**/*.py"
]
}
Adding to .gitignore
You may want to add .contextr/ to your .gitignore if you don't want to share your context configuration with collaborators:
# contextr state (optional)
.contextr/
Or commit it to share your team's default context setup.
Troubleshooting
Clipboard doesn't work
Linux: Install a clipboard helper:
sudo apt install xclip # Debian/Ubuntu
sudo dnf install xclip # Fedora
sudo pacman -S xclip # Arch
Workaround: Use --to-file or --no-clipboard:
ctxr -o context.md
ctxr --no-clipboard # Prints to stdout
Patterns not matching
Always quote glob patterns to prevent shell expansion:
# Correct
ctxr watch "src/**/*.py"
# Wrong - shell expands before ctxr sees it
ctxr watch src/**/*.py
Files still appearing after ignore
Remember: last match wins. Check your .contextr/.ignore for conflicting rules:
*.log # Ignores all .log files
!debug.log # But this un-ignores debug.log
Export is too large
- Use more specific watch patterns
- Add ignore rules for generated/vendor code
- Use
--no-contentsfor just the tree - Large files are auto-truncated at 512KB
State seems corrupted
Reset and start fresh:
rm -rf .contextr
ctxr init
ctxr gis
ctxr watch "your/patterns/**"
Security & Privacy
contextr is designed with privacy in mind:
- 100% Local — No network calls, no telemetry, no cloud
- You Control What's Shared — Only watched, non-ignored files are exported
- No Secrets by Default —
*.lockfiles are always ignored - Transparent State — All config is in readable JSON/text files
Best Practices
- Run
ctxr gisto import.gitignorerules (which typically exclude secrets) - Add sensitive patterns to ignore:
ctxr ignore ".env" "*.pem" "**/secrets/**" - Review the file tree before sharing:
ctxr --no-contents - Don't commit
.contextr/if it might contain sensitive file paths
Development
Setup
git clone https://github.com/nathan-luo/contextr.git
cd contextr
uv sync --extra dev
uv run pre-commit install
Commands
# Lint & format
uv run ruff check .
uv run ruff format .
# Type check
uv run pyright
# Run tests
uv run pytest
# Run all checks
uv run pre-commit run --all-files
Project Structure
src/contextr/
├── __init__.py # Public API exports
├── cli.py # Typer CLI commands
├── manager.py # Core ContextManager class
├── profile.py # Profile management
├── formatters.py # Markdown output formatting
├── storage/ # Storage abstraction
│ ├── base.py # StorageBackend ABC
│ └── json_storage.py # JSON file implementation
└── utils/
├── ignore_utils.py # Git-style ignore matching
└── path_utils.py # Cross-platform path handling
FAQ
Q: Can I export only the file tree?
Yes: ctxr --no-contents
Q: How do I share context via file instead of clipboard?
Use ctxr -o context.md or ctxr --to-file context.md
Q: Can I watch directories recursively?
Yes, use ** glob: ctxr watch "src/**/*.py" or just ctxr watch "src" for all files.
Q: How do I undo an ignore rule?
Use ctxr unignore "pattern" to remove it from the ignore list.
Q: Will long files break the Markdown output?
No — files over 512KB are truncated, and fence sequences are dynamically chosen to avoid conflicts with backticks in your code.
Q: How do I start over completely?
rm -rf .contextr && ctxr init
Q: Can multiple people share the same context config?
Yes, commit .contextr/ to your repo. Everyone will get the same watch/ignore patterns.
Q: Does this work with monorepos?
Yes! Use specific patterns: ctxr watch "packages/my-pkg/src/**"
License
MIT — see LICENSE for details.
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Run
uv run pre-commit run --all-filesbefore committing - Open a pull request
For bugs and feature requests, please open an issue.
Project details
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 contextr-2.0.0.tar.gz.
File metadata
- Download URL: contextr-2.0.0.tar.gz
- Upload date:
- Size: 32.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f24f5d7b8b238abdcb77a881c78edf3cdb5a54ee35be0b030e550293c85ce28
|
|
| MD5 |
94cd11f9cb6e90c092f85987ebc4c5b1
|
|
| BLAKE2b-256 |
9231baea4aabd9093fe14f6dbb3f8c7180941a49af8711206e786db9e8a2deb1
|
File details
Details for the file contextr-2.0.0-py3-none-any.whl.
File metadata
- Download URL: contextr-2.0.0-py3-none-any.whl
- Upload date:
- Size: 29.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c674d1010ff0c59f2978e2c72eb9b59e3512ca4856b37987ac2ceccf1082a65b
|
|
| MD5 |
06be7640087c55cbba0575366b6bb777
|
|
| BLAKE2b-256 |
65f1c350bd72be5d7906ada40f71d18749e1e8652a12b90416fb4e01c8a84295
|