A command-line tool that exports codebases into single Markdown artifacts optimized for LLM context and code review workflows.
fuori --staged -o review.mdTurns your messy working tree into clean, auditable context. Zero copy-paste, zero noise.
- Pack codebase context for an LLM without pasting files by hand
- Review staged changes as one artifact before a commit or PR
- Snapshot a subtree for debugging, discussion, or handoff
- Select files from Git, the filesystem, or stdin depending on the mode
- Strip out anything non-exportable such as binaries, empty files, oversized files, symlinks
- Render one Markdown artifact with a project tree, per-file headings, and fenced code blocks
- LLM context packing: export a codebase into one Markdown artifact, ready for any AI assistant
- Auto-detects Git worktrees by default and falls back to recursive filesystem scanning elsewhere
- Git-aware file selection via the default worktree mode,
--staged,--unstaged, or--diff <range> - Caller-supplied file selection via
--from-stdin, with optional NUL-delimited parsing via-0/--null - Respects
.gitignorerules in Git-backed modes and local ignore rules in filesystem mode - Includes a project tree that matches the exported artifact
- Binary file auto-detection and exclusion
- Configurable file size cap (default: 100 KB)
- Formats code in Markdown with appropriate language identifiers
- Estimates final artifact token usage with a chars/token heuristic
- Warns or hard-fails when estimated tokens exceed a configurable threshold
- Outputs to
_export.mdby default (configurable, including stdout) - Simple C implementation that is easy to inspect and review
- Zero dependencies, plain C99 + POSIX, compiles in seconds on almost anything
Design notes live in docs/design.md.
- A C compiler supporting C99
- POSIX.1-2008 environment (Linux, macOS, WSL, etc.)
brew tap hyle/tap
brew install fuoricodeInstalls fuori, with fuoricode available as an alias.
# System-wide
make install PREFIX=/usr/local
# User-local
make install PREFIX="$HOME/.local"Run fuori in any directory:
fuoriBy default it writes _export.md to the current directory. Inside a Git repo, it uses Git's view of the working tree (tracked + untracked non-ignored files). Outside a repo, or with --no-git, it falls back to the recursive filesystem walking.
fuori [OPTIONS]Options:
| Flag | Description |
|---|---|
-h, --help |
Display help |
-V, --version |
Show version |
-v, --verbose |
Show progress during export |
-o, --output <path> |
Output path (- for stdout) |
--staged |
Export staged files |
--unstaged |
Export unstaged tracked files |
--diff <range> |
Export files changed in a diff range |
--from-stdin |
Read paths from stdin |
-0, --null |
Use NUL as the stdin delimiter (requires --from-stdin) |
--line-numbers |
Prefix exported code lines with line numbers |
--hunks [<n>] |
In Git delta modes, export only changed hunks plus context lines |
--tree / --no-tree |
Include/omit project tree (default: on) |
--tree-depth <n> |
Limit tree render depth |
-s <size_kb> |
Max file size in KB (default: 100) |
--warn-tokens <n> |
Warn above token threshold (default: 200k) |
--max-tokens <n> |
Hard-fail above token threshold |
--no-clobber |
Fail if output already exists |
--no-git |
Force filesystem selection |
--no-default-ignore |
Disable built-in default ignore patterns in filesystem mode |
--allow-sensitive |
Export files even if they match sensitive-file protection rules |
Git selection flags (--staged, --unstaged, --diff) and --from-stdin are mutually exclusive; --no-git cannot be combined with them.
--no-default-ignore only applies to filesystem selection.
--hunks only applies to --staged, --unstaged, and --diff.
Examples:
fuori # Export current working tree
fuori --unstaged # Export unstaged changes
fuori --staged -o review.md # Staged changes to a named file
fuori --diff HEAD~3..HEAD # Files changed in the last 3 commits
fuori --diff main...HEAD # Changes since branching from main
fuori --staged --hunks # Only changed hunks with default context
fuori --diff main...HEAD --hunks=8 # Wider hunk context for review
fuori -o - > codebase.md # Pipe to stdout
fuori --no-tree # Skip the project tree section
fuori --tree-depth 2 # Shallow tree
fuori --line-numbers --staged # Add line numbers for review-oriented exports
fuori -s 50 # 50 KB file size cap
fuori --warn-tokens 100000 # Earlier token warning
fuori --max-tokens 270000 # Hard token budget
fuori -o out.md --no-clobber # Refuse to overwrite
fuori --no-git --no-default-ignore # Disable built-in filesystem ignore defaults
fuori --allow-sensitive # Export files that secret protection would skipFilesystem mode always honors a local .gitignore file when present.
These rules apply in --no-git mode and during automatic fallback outside Git repositories.
Supported syntax:
- Comments (
#) - Negation (
!pattern) - Directory trailing slash (
dir/) - Root-anchored patterns (
/pattern) - Recursive globs (
**/node_modules/,**/*.pyc)
In filesystem mode, fuori also applies a built-in default ignore list unless --no-default-ignore is set:
| Category | Patterns |
|---|---|
| VCS | .git/ |
| Dependencies | node_modules/, .venv/, __pycache__/ |
| Build output | build/, dist/, bin/ |
| Compiled artifacts | *.o, *.a, *.so, *.exe, *.dll |
| Environment / OS | .env, .DS_Store, *.log |
Use --no-default-ignore to disable only the built-in defaults. Local .gitignore rules still apply.
To bypass ignore-based selection entirely, use --from-stdin.
Files larger than the specified size limit (default 100KB) are automatically excluded from the export to prevent including large binary or data files. You can change this limit using the -s option.
By default, fuori skips files that look obviously sensitive and prints a warning to stderr.
This protection applies in every selection mode, including Git-backed modes and --from-stdin.
v1 checks:
- high-risk filenames such as
.env*,credentials*,secret*,id_rsa*,*.pem, and*.key - obvious content patterns such as private key blocks and a small set of API key prefixes
Sensitive files are omitted entirely from the Markdown output. In --staged, --unstaged, and --diff mode they are also omitted from Change Context.
Use --allow-sensitive to disable this protection for a run.
By default, fuori asks Git for tracked files plus untracked non-ignored files in the current subtree.
If Git is unavailable or the directory is not a repository, it silently falls back to the filesystem walker.
Use --no-git to force the filesystem walker explicitly.
| Mode | Git command |
|---|---|
| Default | git ls-files -z --cached --others --exclude-standard |
--staged |
git diff --cached --name-status --diff-filter=AMR |
--unstaged |
git diff --name-status --diff-filter=AMR |
--diff <range> |
git diff --name-status --diff-filter=AMR <range> (two-dot and three-dot ranges both work) |
Additional semantics:
- The default Git-backed mode and explicit Git file-selection modes are scoped to the current working directory subtree when run from a Git subdirectory
- Git-selected files bypass ignore rules at selection time
- Git-selected files still go through normal export-time checks such as regular-file validation, symlink skipping, binary detection, size limits, sensitive-file protection, and output-file self-exclusion
--unstageddoes not include untracked files- Renamed files are exported under the current path reported by Git
--staged,--unstaged, and--diffinclude aChange Contextsection with change status summaries--hunks[=N]narrows Git delta exports to changed hunks plusNlines of surrounding context (3by default)- Added files still export as full files under
--hunks - Delta entries with no renderable changed-line ranges stay in
Change Contextbut omit file bodies and tree entries under--hunks - If Git selects no files,
fuoristill succeeds and writes an empty export
Use --from-stdin when another tool should decide which paths to export.
# newline-delimited (default)
fd -e c -e h | fuori --from-stdin
# NUL-delimited (safe for arbitrary filenames)
fd -e c --print0 | fuori --from-stdin -0
git ls-files -z -- src/ | fuori --from-stdin -0Semantics:
- stdin-selected paths bypass selection-time ignore rules
- they still go through export-time checks such as regular-file validation, symlink skipping, binary detection, size limits, sensitive-file protection, and output-file self-exclusion
--from-stdinis mutually exclusive with--staged,--unstaged,--diff, and--no-git-0/--nullrequires--from-stdin- empty stdin is a successful no-op export that still emits the export preamble
- stdin input order is not preserved; paths are sorted and deduplicated before export
- display paths are caller-supplied, so absolute stdin paths render as absolute headings and relative stdin paths render as relative headings
After a successful export, fuori prints a compact summary to stderr:
Files exported: 42
Bytes written: 183,421
Est. tokens: ~52,400 (approx, assuming BPE ~3.5 chars/token)
The estimate is based on the final Markdown artifact, not just the raw source files, so headings, code fences, and the optional tree section are included in the byte count before estimating tokens.
By default, fuori warns when the estimate exceeds 200,000 tokens:
Warning: output may exceed 200,000 token context window. Consider using --staged or --diff to narrow scope.
Use --warn-tokens <n> to change the warning threshold, or --max-tokens <n> to fail before writing any output when the estimated size would exceed a hard limit.
fuori exports UTF-8 text files and skips inputs that do not pass its text/binary detection path.
In practice, that means files with NUL bytes, invalid UTF-8, or too many control characters are excluded from the export.
Empty files are skipped.
Symbolic links are skipped to avoid recursion cycles.
UTF-16 and other non-UTF-8 text encodings are currently treated as non-exportable and skipped.
Sensitive files are skipped by default unless --allow-sensitive is set.
The output markdown file will contain:
- A preamble with repository, mode, and generation timestamp metadata, plus
Line numbers: onandHunks: on (context: N)when enabled, and a short mode description - A
Change Contextsection for--staged,--unstaged, and--diffexports - A project tree section that reflects the exported artifact (enabled by default)
- A header with the file path
- Either a full-file code block or one or more hunk slices separated by omission markers such as
... 84 unchanged lines omitted ... - Optional line-number prefixes inside code blocks when
--line-numbersis set; hunk exports keep original file line numbers - Appropriate language identifiers for syntax highlighting
- A
stderrsummary of files, bytes, and estimated tokens after successful completion
Example file contents excerpt (the Makefile section is omitted for brevity):
# Codebase Export
Repository: my-project
Mode: recursive
Generated: 2026-03-16T12:34:56Z
Line numbers: on
This document contains all the source code files from the current directory subtree.
## Project Tree
```text
├── src
│ └── main.c
└── Makefile
```
## src/main.c
```
1 | #include <stdio.h>
2 |
3 | int main() {
4 | printf("Hello, World!\n");
5 | return 0;
6 | }
```