Skip to content
Skill · Claude CodeAGENTS.mdCoding

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.

@libraryNewcomer
0 upvotes0 forks0 comments

The skill file

Claude Code · AGENTS.md
AGENTS.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 repo

Save 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 skill
Loading forks…