Skip to main content

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

  1. Installation
  2. Quick Start
  3. CLI Reference
  4. Workflows & Examples
  5. Output Format
  6. Ignore Semantics (Git-style)
  7. Python API
  8. Configuration & State
  9. Troubleshooting
  10. Security & Privacy
  11. Development
  12. FAQ
  13. 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 as xclip or xsel:

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.json for tracking watched patterns
  • Creates .contextr/.ignore with default *.lock rule
  • 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 .gitignore to 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 *.lock rule 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

  1. Use more specific watch patterns
  2. Add ignore rules for generated/vendor code
  3. Use --no-contents for just the tree
  4. 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*.lock files are always ignored
  • Transparent State — All config is in readable JSON/text files

Best Practices

  1. Run ctxr gis to import .gitignore rules (which typically exclude secrets)
  2. Add sensitive patterns to ignore: ctxr ignore ".env" "*.pem" "**/secrets/**"
  3. Review the file tree before sharing: ctxr --no-contents
  4. 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:

  1. Fork the repository
  2. Create a feature branch
  3. Run uv run pre-commit run --all-files before committing
  4. 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

contextr-2.0.0.tar.gz (32.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

contextr-2.0.0-py3-none-any.whl (29.9 kB view details)

Uploaded Python 3

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

Hashes for contextr-2.0.0.tar.gz
Algorithm Hash digest
SHA256 9f24f5d7b8b238abdcb77a881c78edf3cdb5a54ee35be0b030e550293c85ce28
MD5 94cd11f9cb6e90c092f85987ebc4c5b1
BLAKE2b-256 9231baea4aabd9093fe14f6dbb3f8c7180941a49af8711206e786db9e8a2deb1

See more details on using hashes here.

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

Hashes for contextr-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c674d1010ff0c59f2978e2c72eb9b59e3512ca4856b37987ac2ceccf1082a65b
MD5 06be7640087c55cbba0575366b6bb777
BLAKE2b-256 65f1c350bd72be5d7906ada40f71d18749e1e8652a12b90416fb4e01c8a84295

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page