The only formatter your AI needs to know.
One command. Every file type. All formatters run in parallel.
Stop telling your AI agents how to auto-format your code. Give them one command, ffx, that auto-formats all changed files using the right tool for each file type. Written in Rust for speed.
Install the latest release with a single command:
curl -LsSf https://ffx.bfoos.net/install.sh | bash
# Then initialize in your repo to install the pre-commit hook
ffx initThis downloads the prebuilt binary for your platform (macOS Apple Silicon or Intel).
Binaries available on GitHub Releases.
# Format changed files (default)
ffx
# Format staged files only
ffx --staged
# Format files changed vs a base branch (great for PRs)
ffx --base origin/main
# Format all matching files
ffx --all
# Check mode for CI (uses check_args, exits non-zero if issues found)
ffx --check --base origin/main
# Use custom config
ffx --config path/to/.fast-format-x.yaml
# Limit parallel jobs
ffx --jobs 4
# Stop on first failure
ffx --fail-fast
# Verbose output
ffx --verboseRun ffx automatically before every commit and scaffold a starter config if you don't have one yet:
ffx initThis installs a pre-commit hook that:
- Runs
ffxon staged files - Re-stages any files modified by formatters
If .fast-format-x.yaml doesn't exist, ffx init also creates a template with common formatters and a reminder to customize the tools for your repository.
Replace multiple formatting instructions in your AGENTS.md with one line:
## Formatting
Run `ffx` to auto-format every changed file (it runs the correct formatter for each file)Instead of teaching your AI agent about prettier, standard, rubocop, gofmt, and rustfmt, just tell it to run ffx. One command. No wasted tokens.
Create .fast-format-x.yaml in your repo root:
version: 1
tools:
- name: rubocop
include:
- "**/*.rb"
- "**/*.rake"
exclude:
- "vendor/**"
cmd: bundle
args: [exec, rubocop, -A] # format mode (default)
check_args: [exec, rubocop] # check mode (--check flag)
- name: prettier
include: ["**/*.md", "**/*.yml", "**/*.yaml", "**/*.js", "**/*.ts"]
cmd: npx
args: [prettier, --write]
check_args: [prettier, --check]
- name: ktlint
include: ["**/*.kt", "**/*.kts"]
cmd: ktlint
args: [-F]
check_args: [] # ktlint checks by default
- name: gofmt
include: ["**/*.go"]
cmd: gofmt
args: [-w]
check_args: [-l] # list files that differ
- name: rustfmt
include: ["**/*.rs"]
cmd: cargo
args: [fmt, --]
check_args: [fmt, --, --check]Use --check to verify files are formatted without modifying them.
For pull requests, use --base to only check files changed in the PR. This is much faster than checking all files:
# Check only files changed vs the base branch
ffx --check --base origin/mainExample GitHub Actions workflow:
- name: Check formatting
run: |
git fetch origin ${{ github.base_ref }} --depth=1
ffx --check --base origin/${{ github.base_ref }}The --base flag uses git diff <base>...HEAD to find files changed since branching, so it catches all commits in the PR.
For main branch commits or scheduled checks, verify all files:
ffx --all --checkWhen --check is passed, ffx uses check_args instead of args. If check_args is not defined for a tool, it falls back to args.
If any checks fail, ffx shows a "Details" section after the summary with the full output from each failed tool, making it easy to see exactly what needs fixing.
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Formatter failure |
| 2 | Config error |
| 3 | Missing executable |
Contributions are welcome.
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Verify
rustc --version
cargo --version# Build debug version
cargo build
# Run directly
cargo run
cargo run -- --help
cargo run -- --all
# Build optimized release
cargo build --release# Check code compiles
cargo check
# Run tests
cargo test
# Format code
cargo fmt
# Lint code
cargo clippy
# Watch for changes
cargo install cargo-watch # one-time
cargo watch -x checkReleases are managed with cargo-release. This ensures Cargo.toml version stays in sync with git tags.
# Install cargo-release (one-time)
cargo install cargo-release
# Release a new version (updates Cargo.toml, commits, tags, and pushes)
cargo release patch # 0.1.3 → 0.1.4
cargo release minor # 0.1.3 → 0.2.0
cargo release major # 0.1.3 → 1.0.0
# Dry run to see what will happen
cargo release patch --dry-runThe push triggers the GitHub Actions release workflow, which builds binaries for all platforms and creates a GitHub Release.