Introduction
pctx generates LLM-ready context from your codebase.
It scans files, applies sensible filters, truncates oversized content, and formats the result as Markdown, XML, plain text, or structured JSON. The output is designed to be pasted into, copied to, or consumed by AI coding assistants.
Why pctx?
AI coding agents often spend many tool calls discovering a project:
- list files
- inspect directories
- read files
- skip build artifacts
- recover from oversized or binary files
- repeat
That exploration costs time and tokens. pctx packages the useful parts of a repository into one controlled context snapshot.
Use it to:
- Reduce latency by avoiding repeated filesystem exploration.
- Improve accuracy by giving an assistant a broad project view up front.
- Save tokens with default exclusions and truncation.
- Control scope with include/exclude patterns and
.pctx.toml. - Automate workflows with JSON output and stable exit codes.
What pctx outputs
By default, pctx writes Markdown to stdout:
`src/lib.rs`:
```rust
pub mod cli;
pub mod config;
```
`README.md`:
```markdown
# My Project
```
You can also include a file tree:
pctx --tree
Or produce structured output for scripts:
pctx --json
Features
- Recursive file discovery
.gitignoresupport- Built-in exclusions for common noisy paths
- Hidden-path filtering with explicit
--hiddenopt-in - Binary-file detection
- File size limits
- Include/exclude patterns using gitignore-style syntax
- Markdown, XML, and plain text output
- Clipboard and file destinations
- JSON output for automation
- Stdin mode for integrating with
find,fd,git diff, and other tools - Truncation for long files and long lines
- Approximate token estimation
Safety note
pctx reads local files and emits their contents. Review the generated context before sharing it with external services.
Default exclusions skip many common secrets and environment files, such as .env, .env.*, *.pem, and *.key, but no automatic filter can guarantee that sensitive information is excluded.
Installation
From crates.io
Install the latest published version with Cargo:
cargo install pctx
Verify the installation:
pctx --version
pctx --help
Update an existing installation:
cargo install pctx --force
Uninstall:
cargo uninstall pctx
Build from source
git clone https://github.com/mc-marcocheng/pctx
cd pctx
cargo build --release
The compiled binary will be available at:
target/release/pctx
You can run it directly:
./target/release/pctx --help
Or install it locally from the checkout:
cargo install --path .
Shell completions
Generate completions with:
pctx completions bash
pctx completions zsh
pctx completions fish
pctx completions powershell
pctx completions elvish
Example for Bash:
pctx completions bash > pctx.bash
source pctx.bash
Example for Zsh:
pctx completions zsh > _pctx
Then move _pctx into a directory listed in your $fpath.
Build the documentation locally
If you have mdbook installed:
cd docs
mdbook serve
Then open the local URL printed by mdbook.
Usage
Quick start
# Generate context for the current directory
pctx
# Include a file tree
pctx --tree
# Copy generated context to the clipboard
pctx --clipboard
# Write generated context to a file
pctx --output context.md
# Overwrite an existing output file
pctx --output context.md --force
# Generate structured JSON
pctx --json
# Preview files without writing the final context
pctx --dry-run
Command overview
# Generate context
pctx [OPTIONS] [PATHS...]
# List files that would be included
pctx files list [OPTIONS]
# Display the included file tree
pctx files tree [OPTIONS]
# Configuration management
pctx config show
pctx config init
pctx config defaults
# Shell completions
pctx completions bash
pctx completions zsh
pctx completions fish
pctx completions powershell
pctx completions elvish
If no path is supplied, pctx scans the current directory:
pctx
You can pass files or directories explicitly:
pctx src README.md Cargo.toml
Output options
| Flag | Description |
|---|---|
--clipboard, -c | Copy output to the system clipboard |
--output FILE, -o | Write output to a file |
--force | Overwrite the output file if it already exists |
--format FORMAT, -f | Output format: markdown, xml, or plain |
--tree, -t | Include a file tree at the beginning of the output |
--stats, -s | Print a statistics summary |
--json | Emit structured JSON |
--absolute-paths | Display absolute paths instead of relative paths |
--stdin | Read file paths from stdin, one per line |
--stdin0 | Read NUL-delimited file paths from stdin |
--paths-file0 FILE | Read NUL-delimited file paths from a file |
--path-alias ALIAS=PATH | Display files under PATH using ALIAS instead of a relative or absolute path; repeatable |
Examples:
pctx --format markdown
pctx --format xml
pctx --format plain
pctx --tree --stats
pctx --absolute-paths
Filtering options
| Flag | Description |
|---|---|
--exclude PATTERN, -e | Exclude files matching a pattern; repeatable |
--include PATTERN, -i | Include only files matching a pattern; repeatable |
--hidden | Include dot-prefixed files and directories |
--no-default-excludes | Disable built-in exclusion patterns |
--no-gitignore | Ignore .gitignore rules |
--max-size KB | Maximum file size to include, in KiB. Default: 1024 |
--max-depth N, -d | Maximum traversal depth. 0 means unlimited |
Examples:
# Include only Rust and TOML files
pctx --include "*.rs" --include "*.toml"
# Exclude test files
pctx --exclude "*.test.ts" --exclude "__tests__"
# Disable built-in exclusions
pctx --no-default-excludes
# Ignore .gitignore files
pctx --no-gitignore
# Include files up to 2 MiB
pctx --max-size 2048
# Only scan immediate children
pctx --max-depth 1
# Scan children and grandchildren
pctx --max-depth 2
Hidden files and directories
Dot-prefixed paths are hidden by default. Examples include:
.github.vscode.envproject/.config/file.toml
Use --hidden to include them:
pctx --hidden .github
pctx --hidden .github/workflows
Hidden-path filtering is separate from default exclusions and .gitignore rules.
This does not include .github:
pctx --no-default-excludes .github
Use:
pctx --hidden --no-default-excludes .github
The filtering layers are independent:
| Mechanism | Controlled by |
|---|---|
| Dot-prefixed paths | --hidden |
Built-in exclusions such as node_modules and target | --no-default-excludes |
.gitignore and related git ignore rules | --no-gitignore |
Truncation options
| Flag | Description |
|---|---|
--no-truncation | Disable file and line truncation |
--max-lines N | Maximum lines per file before truncation. 0 means unlimited |
--head-lines N | Lines to keep at the start of a truncated file |
--tail-lines N | Lines to keep at the end of a truncated file |
--max-line-length N | Maximum characters per line before truncation. 0 means unlimited |
--head-chars N | Characters to keep at the start of a truncated line |
--tail-chars N | Characters to keep at the end of a truncated line |
Defaults:
| Setting | Default |
|---|---|
max_lines | 500 |
head_lines | 20 |
tail_lines | 10 |
max_line_length | 500 |
head_chars | 200 |
tail_chars | 100 |
Examples:
# Keep more of long files
pctx --max-lines 1000 --head-lines 50 --tail-lines 25
# Disable only line-count truncation
pctx --max-lines 0
# Disable only long-line truncation
pctx --max-line-length 0
# Disable all truncation
pctx --no-truncation
When a file is truncated, pctx preserves the beginning and end of the file and inserts an omission marker between them.
Stdin mode
Use --stdin to read paths from standard input:
find . -name "*.rs" -mtime -1 | pctx --stdin
This is useful for composing with other tools:
# Recently changed Rust files
find . -name "*.rs" -mtime -7 | pctx --stdin
# Files from a saved list
cat files_to_review.txt | pctx --stdin
# Files selected by pctx itself
pctx files list --quiet | grep -v test | pctx --stdin
# Changed files in git
git diff --name-only HEAD~5 | pctx --stdin
# Files found by fd
fd -e rs -e toml --changed-within 2weeks | pctx --stdin
Behavior in stdin mode:
- Empty lines are ignored.
- Whitespace around each line is trimmed.
- File paths are processed directly.
- Directory paths are expanded recursively.
- Positional paths are ignored when
--stdinis used. - Non-existent paths are reported as file errors; if some files succeed, the command exits with partial success.
NUL-delimited path input
--stdin splits on newlines, which breaks for paths that contain them. Use --stdin0 or --paths-file0 for NUL-delimited input instead:
# NUL-delimited stdin (pairs naturally with `find -print0`)
find . -type f -print0 | pctx --stdin0
# NUL-delimited path file — avoids command-line length limits and does not
# require keeping an interactive stdin pipe open
pctx --paths-file0 selected-paths.bin
--stdin, --stdin0, and --paths-file0 are mutually exclusive. Positional paths are ignored whenever any of them is used, and an empty input is treated as no matching files (exit code 6).
Path aliases
--path-alias ALIAS=PATH maps files under an absolute directory to a short display name, which is useful for combining unrelated roots (e.g. multiple repositories) without leaking absolute paths into the generated output:
pctx \
/home/me/api/src/main.rs \
/home/me/shared/src/lib.rs \
--path-alias api=/home/me/api \
--path-alias shared=/home/me/shared
This renders paths as api/src/main.rs and shared/src/lib.rs. Notes:
PATHmust exist and resolve to a directory.- Aliases must be unique, and each
PATHmay only be aliased once. - When aliased roots are nested, the most specific (deepest) root wins.
--absolute-pathsoverrides aliases and always shows the full path.
Configuration file selection
By default, pctx searches the current directory and its parents for .pctx.toml. Override this with:
| Flag | Description |
|---|---|
--config FILE | Load exactly this config file; errors (missing or malformed) are fatal |
--no-config | Disable automatic and explicit .pctx.toml loading entirely |
# Explicit configuration
pctx --config /workspace/.pctx.toml
# Ignore all config files
pctx --no-config
--config and --no-config are mutually exclusive. Only an automatically discovered malformed config warns and falls back to defaults; an explicitly supplied --config file that is missing or malformed is a hard error.
Capabilities
pctx capabilities reports which machine-readable features this build supports, which is useful for integrations that need to detect support before using a flag:
pctx --json capabilities
Listing files
# Human-readable file list
pctx files list
# Bare paths only, one per line
pctx files list --quiet
# JSON output
pctx files list --json
--quiet is designed for pipelines:
pctx files list --quiet | xargs wc -l
File tree
pctx files tree
pctx files tree --json
To include the same tree in generated context:
pctx --tree
Dry run
Use --dry-run to inspect what would be included:
pctx --dry-run
pctx --dry-run --json
Dry run still scans and processes files so it can report truncation and approximate token counts, but it does not write the final context document.
Global options
| Flag | Description |
|---|---|
--json | Use structured JSON output |
--verbose, -v | Print additional diagnostics to stderr |
--quiet, -q | Suppress non-essential output |
--no-color | Disable colored output |
Practical recipes
# Generate compact context for a Rust crate
pctx --include "*.rs" --include "*.toml" --tree
# Prepare context for a code review from changed files
git diff --name-only main...HEAD | pctx --stdin --tree
# Generate XML for a downstream parser
pctx --format xml --output context.xml
# Copy only source-like files to clipboard
pctx src Cargo.toml README.md --clipboard
# Show what would be included after filters
pctx --include "*.ts" --exclude "*.test.ts" --dry-run
# Find files pctx would include, then post-filter with grep
pctx files list --quiet | grep -E '\.(rs|toml)$' | pctx --stdin
Configuration
pctx can load project defaults from a .pctx.toml file.
Create one with:
pctx config init
By default, pctx searches for .pctx.toml in the current directory and then in parent directories. This can be overridden:
| Flag | Description |
|---|---|
--config FILE | Load exactly this config file instead of searching. A missing or malformed file is a fatal error |
--no-config | Disable automatic and explicit .pctx.toml loading; only built-in defaults and CLI arguments apply |
pctx --config /workspace/.pctx.toml
pctx --no-config
--config and --no-config are mutually exclusive.
Supported keys
The configuration file currently supports:
| Key | Type | Description |
|---|---|---|
exclude | array of strings | Additional gitignore-style exclusion patterns |
include | array of strings | If non-empty, only matching files are included |
max_lines | integer | Maximum lines per file before truncation. 0 means unlimited |
head_lines | integer | Lines to keep at the start of a truncated file |
tail_lines | integer | Lines to keep at the end of a truncated file |
max_line_length | integer | Maximum characters per line before truncation. 0 means unlimited |
head_chars | integer | Characters to keep at the start of a truncated line |
tail_chars | integer | Characters to keep at the end of a truncated line |
Example:
# Patterns to exclude in addition to built-in defaults
exclude = [
"*.generated.ts",
"vendor/",
"__snapshots__",
]
# If specified, only matching files are included
include = [
"*.rs",
"*.toml",
"*.md",
]
# File truncation
max_lines = 500
head_lines = 20
tail_lines = 10
# Long-line truncation
max_line_length = 500
head_chars = 200
tail_chars = 100
Precedence
Settings are applied in this order, highest priority first:
- Command-line arguments
.pctx.toml- Built-in defaults
For example:
max_lines = 500
can be overridden with:
pctx --max-lines 1000
Include and exclude patterns are additive:
- Built-in excludes are added first, unless
--no-default-excludesis used. - Config-file excludes are added.
- CLI excludes are added.
- Config-file includes are added.
- CLI includes are added.
Config commands
# Show the resolved config file contents or defaults
pctx config show
# Create .pctx.toml in the current directory
pctx config init
# Overwrite an existing .pctx.toml
pctx config init --force
# Print built-in exclusion patterns
pctx config defaults
JSON is also supported:
pctx config show --json
pctx config defaults --json
Syntax errors
If a config file is automatically discovered (no --config given) but cannot be parsed, pctx prints a warning and continues without that config file.
If a config file is loaded via an explicit --config FILE, a missing file or parse error is a fatal error instead — the same is true for --config with pctx config show.
Default exclusions
pctx excludes many common noisy or unsafe files by default.
Examples include:
- Version control:
.git,.svn,.hg - Dependencies:
node_modules,vendor,bower_components - Rust:
target,Cargo.lock - Python:
__pycache__,.pytest_cache,.mypy_cache,.venv - Build outputs:
dist,build,out,bin,obj - Editor files:
.idea,.vscode,*.swp - Caches:
.cache,.parcel-cache,.turbo,.next - Environment/secrets:
.env,.env.*,*.pem,*.key - Logs:
*.log,logs - Media and binaries:
*.png,*.jpg,*.pdf,*.zip,*.dll,*.so - Generated/minified files:
*.map,*.min.js,*.min.css
View the exact list:
pctx config defaults
Disable built-in exclusions:
pctx --no-default-excludes
Hidden paths are separate
Hidden-path filtering is not part of the default exclusion pattern list.
Dot-prefixed paths such as .github require --hidden, even when:
--no-default-excludesis used--no-gitignoreis used.gitignoredoes not exclude them
Example:
# Usually no files matched, because .github is hidden
pctx --no-default-excludes .github
# Include it explicitly
pctx --hidden --no-default-excludes .github
Pattern syntax
Patterns use gitignore-style matching.
| Pattern | Matches |
|---|---|
*.log | .log files at any level |
test_* | Files or path components starting with test_ |
**/tests/** | Any tests directory at any level |
/src/generated | src/generated at the scan root |
docs/ | A directory named docs |
src/config | src/config and files below it |
src/config/ | Files below a directory named src/config |
Examples:
exclude = [
"*.log",
"node_modules",
"dist/",
"**/*.generated.ts",
]
include = [
"*.rs",
"src/**/*.toml",
]
Pattern limitations
- Negation patterns such as
!important.logare not supported. - Unsupported negation patterns are ignored with a warning.
- Character-class behavior such as
[abc]depends on the underlying glob implementation. - Some
**/edge cases may differ from exact git behavior. - Leading
./or.\is stripped with a warning because include/exclude values are patterns, not positional paths.
Prefer positional paths when selecting a concrete directory:
# Good: scan this path
pctx src/config
# Also valid: pattern filtering
pctx --include "src/config"
Troubleshooting
No files matched
Try:
pctx --dry-run --verbose
Common causes:
- The path is dot-prefixed and needs
--hidden. - Include patterns are too restrictive.
- A custom exclude pattern matched more than expected.
- Built-in exclusions filtered the files.
.gitignorefiltered the files.- Files exceeded
--max-size. - Files are binary.
A hidden directory is still skipped
Use --hidden:
pctx --hidden .github
--no-default-excludes does not affect hidden-path filtering.
A config pattern did not behave like git
pctx supports gitignore-style patterns, but not every gitignore feature. Avoid negation patterns and test with:
pctx files list --dry-run
For file listing, use:
pctx files list --verbose
JSON & Exit Codes
Use --json for machine-readable output:
pctx --json
In JSON mode:
- The structured response is written to stdout.
- Progress and diagnostic messages are written to stderr.
- Errors are also written to stdout as JSON.
- The process exit code still indicates success or failure.
- Requested side effects (
--output FILE,--clipboard) are completed before the JSON response is printed, so a side-effect failure produces a single JSON error response rather than a success response followed by an error.
Capabilities
Integrations that need to detect which features a given pctx build supports can query them in a stable, machine-readable form:
pctx --json capabilities
{
"schema_version": 1,
"name": "pctx",
"version": "1.1.0",
"clipboard": true,
"tokens": true,
"json_output": true,
"stdin": true,
"stdin0": true,
"paths_file0": true,
"path_aliases": true,
"formats": ["markdown", "xml", "plain"]
}
Response statuses
Every JSON response has a top-level status.
Possible values:
| Status | Meaning |
|---|---|
success | Operation completed successfully |
partial | Some files were processed, but some failed or were skipped with errors |
error | The operation failed |
Successful context response
pctx --json
Example shape:
{
"status": "success",
"data": {
"content": "`src/lib.rs`:\n```rust\npub mod cli;\n```\n",
"format": "markdown",
"files": [
{
"path": "src/lib.rs",
"extension": "rs",
"size_bytes": 128,
"line_count": 8,
"truncated": false
}
]
},
"stats": {
"file_count": 1,
"total_lines": 8,
"total_bytes": 128,
"truncated_count": 0,
"skipped_count": 0,
"token_estimate": 42,
"duration_ms": 3
}
}
Partial response
If some files fail but others succeed, pctx returns partial and exits with code 7.
Example:
{
"status": "partial",
"data": {
"content": "...",
"format": "markdown",
"files": [
{
"path": "src/lib.rs",
"extension": "rs",
"size_bytes": 128,
"line_count": 8,
"truncated": false
}
]
},
"stats": {
"file_count": 1,
"total_lines": 8,
"total_bytes": 128,
"truncated_count": 0,
"skipped_count": 0,
"token_estimate": 42,
"duration_ms": 3
},
"errors": [
{
"path": "large.log",
"code": "file_too_large",
"message": "File too large (2000000 bytes, max 1048576): large.log",
"transient": false
}
]
}
Error response
Example:
{
"status": "error",
"code": "no_files_matched",
"message": "No files matched the specified filters",
"input": {
"paths": [],
"exclude": [],
"include": ["*.rs"],
"hidden": false,
"no_default_excludes": false,
"no_gitignore": false,
"max_size_kb": 1024,
"max_depth": 0,
"stdin": false
},
"suggestion": "Include patterns are active; check whether the files match `--include` or the `include` entries in `.pctx.toml`.",
"transient": false,
"exit_code": 6
}
File list JSON
pctx files list --json
Example shape:
{
"status": "success",
"data": [
{
"path": "src/lib.rs",
"extension": "rs",
"size_bytes": 128,
"truncated": false
}
],
"stats": {
"file_count": 1,
"total_lines": 0,
"total_bytes": 0,
"truncated_count": 0,
"skipped_count": 0,
"duration_ms": 0
}
}
files list does not read file contents, so line_count is omitted.
Tree JSON
pctx files tree --json
Example shape:
{
"status": "success",
"data": {
"tree": "src\n└── lib.rs\n"
},
"stats": {
"file_count": 1,
"total_lines": 0,
"total_bytes": 0,
"truncated_count": 0,
"skipped_count": 0,
"duration_ms": 0
}
}
Error codes
Common machine-readable error codes include:
| Code | Meaning |
|---|---|
file_not_found | File or directory does not exist |
permission_denied | File or directory could not be read |
binary_file | File appears to be binary |
file_too_large | File exceeds --max-size |
encoding_error | File encoding could not be handled |
invalid_pattern | Include/exclude pattern is invalid |
no_files_matched | Filters matched no files |
output_exists | Output file exists and --force was not used |
git_error | Git command failed |
config_error | Config file could not be parsed or used |
clipboard_error | Clipboard write failed |
io_error | Generic I/O error |
json_error | JSON serialization failed |
walk_error | Directory traversal failed |
ignore_error | Ignore-pattern handling failed |
Exit codes
Exit codes are part of the CLI contract.
| Exit code | Name | Meaning |
|---|---|---|
0 | Success | Operation completed successfully |
1 | Failure | General or unspecified failure |
2 | Usage error | Invalid arguments or bad flag combinations |
3 | Not found | File, directory, or config file not found |
4 | Permission denied | Cannot read a file or directory |
5 | Conflict | Output file exists without --force |
6 | No match | No files matched filters |
7 | Partial | Some files succeeded and some failed |
Scripting examples
# Extract generated context
pctx --json | jq -r '.data.content'
# Get included file paths
pctx --json | jq -r '.data.files[].path'
# Fail on partial success
response="$(pctx --json)"
status="$(printf '%s' "$response" | jq -r '.status')"
test "$status" = "success"
# List large files pctx would include
pctx files list --json \
| jq -r '.data[] | select(.size_bytes > 10000) | .path'
# Use exit codes
if pctx --json > context.json; then
echo "success"
else
case "$?" in
6) echo "no files matched" ;;
7) echo "partial success" ;;
*) echo "failed" ;;
esac
fi
Architecture & Developer Guide
pctx is implemented in Rust as both a CLI application and a library crate.
The CLI entry point lives in src/main.rs. Most reusable functionality is exposed through modules under src/lib.rs.
High-level pipeline
A normal pctx run follows this pipeline:
CLI args
│
▼
Config resolution
│
▼
File discovery
│
▼
Filtering
│
▼
Content reading
│
▼
Truncation
│
▼
Formatting
│
▼
Destination output
In JSON mode, the same pipeline is used, but responses are wrapped in structured JSON.
Module overview
| Module | Purpose |
|---|---|
cli | Defines command-line arguments and subcommands with clap |
config | Resolves defaults, .pctx.toml, and CLI overrides |
scanner | Discovers candidate files from paths, git, stdin, or directory walking |
filter | Handles binary detection and gitignore-style include/exclude patterns |
content | Reads files and builds FileEntry values |
content::truncator | Applies file and long-line truncation |
output | Formats content and writes to stdout, files, or clipboard |
output::json_types | Defines the structured JSON API |
output::tree | Builds and renders file trees |
stats | Tracks file counts, sizes, truncation counts, and token estimates |
error | Defines typed errors, suggestions, and error codes |
exit_codes | Defines stable process exit codes |
CLI layer
src/cli.rs defines:
- global options such as
--json,--verbose,--quiet, and--no-color - generate options, used when no subcommand is supplied
files listfiles treeconfig showconfig initconfig defaultscompletions
The CLI is intentionally designed for both humans and automation:
- Human-readable output goes to stdout or stderr depending on purpose.
- JSON result payloads go to stdout.
- Diagnostic messages go to stderr.
- Exit codes communicate command outcome.
Configuration
Configuration is represented by Config in src/config/mod.rs.
Sources are merged in this order:
- CLI arguments
.pctx.toml- built-in defaults
The config file type is FileConfig in src/config/file.rs.
Currently, .pctx.toml supports:
excludeinclude- truncation settings
Built-in exclusions are defined in src/config/defaults.rs.
Scanning
File discovery is handled by Scanner in src/scanner/mod.rs.
There are two primary scanning paths:
-
Configured paths
- Used by normal
pctxgeneration. - Defaults to
.. - Accepts files and directories.
- Used by normal
-
Explicit stdin paths
- Used with
--stdin. - Reads one path per line.
- Expands directories recursively.
- Used with
Directory traversal is implemented in src/scanner/walker.rs using the ignore crate.
When gitignore support is enabled and the target is inside a git repository, src/scanner/git.rs can use:
git ls-files -z --cached --others --exclude-standard
This gives git-aware file discovery for tracked and untracked files while respecting standard git exclusions.
Filtering model
Filtering happens in several layers:
-
Traversal-level filtering
- Hidden paths may be skipped during walking.
- Gitignore rules may be applied during walking or git scanning.
- Maximum depth may limit recursion.
-
Pattern filtering
- Built-in excludes
- Config-file excludes
- CLI excludes
- Config-file includes
- CLI includes
-
File validation
- Maximum file size
- Binary detection
Hidden-path filtering is independent from default exclusions and gitignore rules.
Pattern matching
src/filter/patterns.rs implements gitignore-style matching using the glob crate.
Important behavior:
- Simple patterns can match path components anywhere.
- Multi-component patterns can match nested paths.
- Trailing slash patterns match directories.
- Leading
/anchors a pattern to the scan root. - Negation patterns are not supported.
Binary detection
src/filter/binary.rs detects binary files using:
- known binary extensions
- common magic-byte signatures
- null-byte checks
- non-printable byte ratio
Binary files are skipped before content processing.
Content processing
ContentProcessor in src/content/mod.rs turns paths into FileEntry values.
A FileEntry contains:
- absolute path
- relative display path
- extension
- original byte count
- original line count
- processed line count
- truncation metadata
- processed content
Files are read by src/content/reader.rs.
Invalid UTF-8 is converted lossily rather than failing immediately, which makes pctx tolerant of mixed or imperfect text encodings.
Truncation
src/content/truncator.rs handles two truncation modes:
-
File-level truncation
- Triggered when a file exceeds
max_lines. - Preserves the configured head and tail line counts.
- Inserts an omission marker.
- Triggered when a file exceeds
-
Line-level truncation
- Triggered when a line exceeds
max_line_length. - Preserves the configured head and tail character counts.
- Inserts a character omission marker.
- Triggered when a line exceeds
0 disables the corresponding truncation limit.
Formatting
Formatting lives in src/output/formatter.rs.
Supported formats:
| Format | Description |
|---|---|
markdown | File labels and fenced code blocks |
xml | XML document with file contents in CDATA |
plain | Simple text separators |
Markdown formatting automatically grows code fences if file content already contains triple backticks.
XML formatting escapes attributes and protects against CDATA termination sequences.
Output destinations
Output destination handling is split across:
src/output/stdout.rssrc/output/file.rssrc/output/clipboard.rs
File output uses an atomic create-new behavior by default, so existing files are not overwritten unless --force is supplied.
JSON API
Structured JSON types are defined in src/output/json_types.rs.
Top-level responses are:
successpartialerror
Errors include:
- machine-readable code
- message
- optional input context
- optional suggestion
- transient flag
- exit code
The JSON API is intended for scripts, CI jobs, and agent harnesses.
Errors and exit codes
src/error.rs defines PctxError.
Each error can provide:
- display message
- machine-readable code
- suggested fix
- transient/non-transient classification
- structured input context
- exit code
Stable exit code constants are defined in src/exit_codes.rs.
When changing exit codes, update:
src/exit_codes.rs- CLI help text in
src/cli.rs - user documentation
- scripts or integrations that depend on the code
Statistics
src/stats.rs tracks:
- file count
- total original lines
- total original bytes
- truncated file count
- skipped count
- approximate token estimate
- duration
Token estimation uses a tokenizer when compiled with token support; otherwise it falls back to an approximate character-based estimate.
Tests
The project includes unit tests for:
- config loading and merging
- default exclusions
- content processing
- truncation
- binary detection
- pattern matching
- output formatting
- tree rendering
- stats formatting
- error metadata
Snapshot tests are used for formatter and tree output.
Run tests with:
cargo test
If an intentional formatting change affects snapshots, review/update snapshots with the normal insta workflow.
Developer workflow
Useful commands:
# Format code
cargo fmt
# Lint
cargo clippy --all-targets --all-features
# Run tests
cargo test
# Build release binary
cargo build --release
# Try the CLI locally
cargo run -- --help
cargo run -- --dry-run --tree
cargo run -- files list --quiet
Build and serve documentation:
cd docs
mdbook serve
Extension points
Common areas to extend:
| Goal | Likely files |
|---|---|
| Add a CLI flag | src/cli.rs, src/config/mod.rs, docs |
| Add a config key | src/config/file.rs, src/config/mod.rs, docs |
| Add an output format | src/cli.rs, src/output/formatter.rs, snapshot tests |
| Change default excludes | src/config/defaults.rs, docs |
| Improve pattern behavior | src/filter/patterns.rs |
| Change JSON response shape | src/output/json_types.rs, docs |
| Add a new subcommand | src/cli.rs, src/main.rs, docs |
Notes for maintainers
- Keep CLI help and documentation in sync.
- Keep JSON response shapes stable where possible.
- Treat exit codes as public API.
- Prefer adding tests for filtering and formatting changes.
- Be careful when changing hidden-file behavior; it is intentionally independent from default exclusions and gitignore rules.