Rust CLI Project AGENTS.md
AGENTS.md for a Rust command-line tool: clap derive conventions, anyhow vs thiserror boundaries, exit-code discipline, and integration tests with assert_cmd.
Use when: Use in a Rust CLI crate so agents put error types in the right layer, keep main thin, and verify behavior with end-to-end binary tests rather than only unit tests.
The skill file
Claude Code · AGENTS.mdAGENTS.md3252 chars
# AGENTS.md — Rust CLI
## Commands
```bash
cargo build
cargo test # unit + integration tests
cargo test --test cli # just the end-to-end binary tests
cargo clippy --all-targets -- -D warnings # warnings are errors; fix, don't allow
cargo fmt --check # must be clean before commit
cargo run -- <args> # try the tool locally
```
MSRV is pinned in `Cargo.toml` (`rust-version`). Do not use features newer than it. Add dependencies with `cargo add <crate>`; keep default features off (`--no-default-features`) unless needed and say why in the commit message.
## Structure
```
src/main.rs # thin: parse args, call run(), map error to exit code
src/cli.rs # clap derive structs — the entire CLI surface in one file
src/lib.rs # everything else lives behind the library crate
tests/cli.rs # integration tests running the real binary
```
- `main.rs` stays under ~40 lines. All logic is in the library so it can be unit-tested without spawning processes.
- The library never prints directly. It returns data; rendering to stdout/stderr happens in the binary layer, writing through a `&mut impl Write` so tests can capture output.
## CLI conventions (clap)
- Derive API only (`#[derive(Parser)]`), no builder calls. Subcommands are an enum with `#[derive(Subcommand)]`.
- Every flag has long form (`--output`), doc comment (becomes `--help` text), and where sensible a short form. Help text is a sentence fragment, no trailing period.
- Machine-readable output behind `--format json`; human output is the default. Never mix logs into stdout — diagnostics go to stderr.
- Respect conventions: `-q/--quiet`, `-v/--verbose` (counted), read from stdin when the input arg is `-`.
## Error handling
- **Library code (`src/lib.rs` and below): `thiserror`.** Each module defines a specific error enum; variants carry context (`#[error("config file {path} is not valid TOML")]`). No `anyhow` in the library — callers need to match on failures.
- **Binary code (`main.rs`): `anyhow`.** `run()` returns `anyhow::Result<ExitCode>`; use `.context("reading config")?` to add user-facing breadcrumbs at each step.
- Exit codes: 0 success, 1 expected failure (bad input, not found), 2 usage error (clap handles this). Map them in one place in `main`.
- Error messages: lowercase start, name the thing that failed and the path/value involved, suggest a fix when one is obvious. Never show a raw `Debug` print to users.
- No `.unwrap()`/`.expect()` outside tests and provably-infallible cases — and provably-infallible gets a comment proving it.
## Tests
- Integration tests in `tests/cli.rs` use `assert_cmd` + `predicates`: run the real binary, assert on exit code, stdout, stderr.
- Filesystem-touching tests use `tempfile::TempDir`; never write into the repo or rely on the user's home directory.
- Every bug fix adds a regression test that fails before the fix.
- Snapshot-test help output (`insta`) so CLI surface changes show up in review diffs.
## Style
- `cargo fmt` defaults — no rustfmt.toml debates.
- Prefer borrowing (`&str`, `&Path`) in signatures; take `impl AsRef<Path>` for path parameters.
- No `unsafe`. If you believe it is required, stop and ask first.
Install
drop into your repoSave this to your project or home directory so Claude Code can load it.
path./AGENTS.md
Discussion
What people are saying
Forks
0 forks of this skillLoading forks…