rust-cli
Skill4 starsChanged 7 months agoMITView on GitHub
Design and implementation guidance for maintainable Rust CLIs with strong ergonomics, machine-friendly behavior, testable structure, and release automation. Use when creating or refactoring a Rust command-line tool, choosing crates like clap/anyhow/tracing/serde_json, improving command UX, adding JSON output, tightening test/release workflows, or making an existing CLI safer for agents and automation.
- Installs packages
We split this skill into the rules it gives an agent
10 rules. 0 are common, 6 are distinctive, and 4 are very specific to this project.
0
Common
Most of the 3 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 13
Use this skill when you need to design or implement a Rust CLI with production-grade ergonomics and automation.
- Distinctive: line 26
When the user asks you to create common project scaffolding, start from the copy/paste templates and adapt them.
- Very specific: line 34
If this CLI will be operated by AI agents and/or automation, also consult the agentic-cli-design skill.
- Very specific: line 102
If you store timestamps as Unix seconds (i64) and compute a cleanup cutoff using SystemTime::now(), tests can become flaky across platforms.
- Very specific: line 113
Tests: avoid boundary conditions.
- Distinctive: line 154
Use predictable exit codes and integration tests that execute the compiled binary.
- Very specific: line 159
Use cargo-release to bump versions, create git tags, and (optionally) publish to crates.io.
- Distinctive: line 187
After bumping/tagging, publish explicitly:
- Distinctive: line 195
Before publishing (especially in automation), prefer these checks:
- Distinctive: line 208
If you need to publish an already-created tag:
“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
rust-cli - Build Maintainable Rust CLIs
Use this skill when you need to design or implement a Rust CLI with production-grade ergonomics and automation.
For language-agnostic OSS publication/release hygiene (LICENSE/SECURITY.md, release notes, CI policy, repo bootstrap conventions), consult the oss-publish skill.
Defaults (Agent-Friendly)
- CLI parsing:
clapderive. - Error handling:
anyhow(orthiserrorfor library-style errors). - Logging/diagnostics:
tracing+tracing-subscriber(write logs to stderr). - Structured output:
serde+serde_json.
Quick Recipes (Start Here)
When the user asks you to create common project scaffolding, start from the copy/paste templates and adapt them.
- Makefile: use the template in
rust-cli/references/templates.md(section: "Makefile (common Rust CLI targets)"). - CI (GitHub Actions): use the template in
rust-cli/references/templates.md. - Integration tests: use the
assert_cmdexample inrust-cli/references/templates.md.
Companion Skill: agentic-cli-design
If this CLI will be operated by AI agents and/or automation, also consult the agentic-cli-design skill.
Borrow these concepts: machine-readable output, non-interactive operation, idempotent commands, safe-by-default behavior, observability, and introspection.
Cross-platform testing pitfalls
Home directory override (Windows limitation)
On Windows, directories::BaseDirs calls Windows API (SHGetKnownFolderPath) directly; setting HOME or USERPROFILE environment variables does not override the returned path. This differs from Unix/Linux/macOS, where directories typically reads $HOME.
| Platform | Behavior | Override via env? |
| Unix/Linux/macOS | Reads $HOME environment variable | ✅ Yes |
| Windows | Calls Win32 API (SHGetKnownFolderPath) | ❌ No |
Test design patterns:
// Pattern 1: Unix-only test (recommended for HOME override)
#[test]
#[cfg(not(target_os = "windows"))]
fn test_home_override() {
let temp = TempDir::new().unwrap();
env::set_var("HOME", temp.path());
// Test code that uses directories::BaseDirs
}
// Pattern 2: Platform-specific logic
#[test]
fn test_cross_platform_home() {
#[cfg(unix)]
{
env::set_var("HOME", "/tmp/test");
// Unix-specific test
}
#[cfg(windows)]
{
// Windows: use explicit paths or dependency injection
let test_dir = PathBuf::from("C:\\temp\\test");
// Windows-specific test
}
}
// Pattern 3: Dependency injection (best for portability)
fn install_skill_to_path(base_dir: &Path, skill_name: &str) {
// Accept path directly, avoid BaseDirs in implementation
}
#[test]
fn test_install_with_explicit_path() {
let temp = TempDir::new().unwrap();
install_skill_to_path(temp.path(), "my-skill");
// Portable across all platforms
}Other common cases with similar issues:
| Function | Platform behavior | Override via env? |
std::env::temp_dir() | Calls OS API | ❌ No |
std::env::current_exe() | Calls OS API | ❌ No |
directories::ProjectDirs (config/cache) | Windows: API, Unix: XDG vars | Partial (Unix only) |
Recommendation: Design functions to accept explicit paths where testability matters; use BaseDirs / ProjectDirs only in top-level CLI entrypoint or well-isolated modules.
Flaky tests: time-based cutoffs (SystemTime + second precision)
If you store timestamps as Unix seconds (i64) and compute a cleanup cutoff using SystemTime::now(), tests can become flaky across platforms.
This often passes on Linux/macOS and fails on Windows due to timing/resolution differences.
Typical failure mode (SQLite example):
record()insertscreated_at = now_secs()(e.g.1707408142).- a moment later,
cleanup_old_entries(0)computescutoff = now_secs()(e.g.1707408143). - SQL uses
WHERE created_at < cutoffwhich matches the freshly inserted row (1707408142 < 1707408143).
Recommended fixes (pick one that matches intended semantics):
- Tests: avoid boundary conditions; use a large margin (e.g.
cleanup_old_entries(1)instead of0), and optionally insert a small sleep between operations to avoid same-second edges. - Implementation: define
days <= 0semantics explicitly (often a no-op), or inject a clock so tests can be deterministic. If you keep second-precision storage, be careful with<vs<=and how you define "older than N days".
Example test adjustment (stable across platforms):
#[test]
fn test_cleanup_old_entries() {
let (ledger, _temp) = create_test_ledger();
ledger
.record("test-1", "hash-1", "tweet-1", "success")
.unwrap();
// Ensure the cutoff is not computed in the exact same instant.
std::thread::sleep(std::time::Duration::from_millis(10));
// Use a safe margin: a fresh entry should not be deleted.
let deleted = ledger.cleanup_old_entries(1).unwrap();
assert_eq!(deleted, 0);
let entry = ledger.lookup("test-1").unwrap();
assert!(entry.is_some());
}Other common cross-platform differences
| Feature | Unix/macOS | Windows | Testing approach |
| Path separator | / | \ | Always use std::path::Path |
| Line endings | \n | \r\n | Explicitly specify in tests or use .replace() |
| Executable extension | none | .exe | Use env!("CARGO_BIN_EXE_<name>") |
| Case sensitivity | Yes | No | Test with varied cases on Windows |
| Temp directory | /tmp or /var/tmp | %TEMP% | Use std::env::temp_dir() or tempfile crate |
Rust-specific workflow (minimal)
- Implement a JSON output mode (e.g.
--json) with stdout reserved for the result. - Send logs/diagnostics to stderr (e.g. via
tracing). - Use predictable exit codes and integration tests that execute the compiled binary.
- For repo-wide release/quality gate conventions, see
oss-publish.
Release workflow (cargo-release)
Use cargo-release to bump versions, create git tags, and (optionally) publish to crates.io.
Install:
cargo install cargo-releaseBump version + tag (no publish)
This is a safe default when you want to control publishing manually:
# Patch: 0.1.0 -> 0.1.1
cargo release patch --execute --no-confirm --no-publish
# Minor: 0.1.0 -> 0.2.0
cargo release minor --execute --no-confirm --no-publish
# Major: 0.1.0 -> 1.0.0
cargo release major --execute --no-confirm --no-publishNotes:
--no-confirmmakes the command non-interactive (agent/CI friendly). Use without it for a safety prompt.--no-publishkeeps crates.io publishing as an explicit step.
After bumping/tagging, publish explicitly:
cargo publishPublish preflight checks
Before publishing (especially in automation), prefer these checks:
cargo fmt
cargo clippy -- -D warnings
cargo test
# Ensures the package can be built as it will be uploaded
cargo publish --dry-runOptional: publish from a tag
If you need to publish an already-created tag:
git checkout <tag>
cargo publishRecommendation: keep the release workflow simple. In most repos, publishing from a clean working tree on the release commit (the one that was tagged) is sufficient.
Templates
- Crate selection:
rust-cli/references/crates.md - Copy/paste scaffolding (Cargo.toml, main.rs, tests, prek config):
rust-cli/references/templates.md
Other versions4 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 4
- full-stack-skills/rust-skills★ 8same name
- po4yka/rust-skills★ 2same name
- adxptived/Rust-Skills★ 1same name
- Microbiosis/rust-claude-skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository25 other files it gives its agents.
- AGENTS.mdAGENTS.md
- greats-soul-archive-contributor/AGENTS.mdAGENTS.md
- agentic-cli-designSkill
- ai-chat-frontendSkill
- autoresearch-agentSkill
- brainstorm-frameworksSkill
- business-planningSkill
- clawdbot-configSkill
- dashboard-design-guidebookSkill
- fermi-estimationSkill
- firecrawlSkill
- gogcliSkill
- greats-soul-archive-contributorSkill
- jp-grantsSkill
- openclaw-agent-creatorSkill
- opencode-agent-creatorSkill
- opencode-command-creatorSkill
- openspec-brownfield-baselineSkill
- oss-publishSkill
- product-improvement-proposalSkill
- python-uv-projectSkill
- slack-rsSkill
- unit-test-hygieneSkill
- wt-setupSkill
- youtube-summarizerSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

