agent-friendly-cli
Agent-Friendly CLI Skill
Two modes: Create (new CLI) and Audit (existing CLI).
Mode: Audit
When given an existing CLI, evaluate it against each requirement below. For each item report: ✅ pass, ❌ fail, or ⚠️ partial — and give a concrete fix for every non-pass.
Present findings as a prioritized list (blockers first, then improvements).
Mode: Create
When asked to build a new CLI, apply all requirements below from the start. Remind the user of the CLI vs. MCP decision before writing code.
CLI vs. MCP Decision
Choose CLI when:
- Fewer than ~15 commands
- Stateless operations
- Agent has shell access
- Token budget matters (MCP adds ambient cost in system prompt)
Choose MCP when:
- 50+ tools behind one server
- Stateful sessions needed
- No shell access for agent
- Multi-agent systems
Requirements
1. Structured Output
--jsonflag outputs machine-readable JSON to stdout- In
--jsonmode: all warnings, progress, and human text go to stderr so stdout stays parseable - In normal (human) mode: output goes to stdout as usual
- Keep output flat over nested — easier to parse reliably
- Consistent field types across all commands: timestamps in ISO 8601, durations in seconds
# Human mode — rich output to stdout
$ mytool list
┌─────┬──────┐
│ ID │ Name │
└─────┴──────┘
# Machine mode — clean JSON to stdout, any warnings to stderr
$ mytool list --json
[{"id":"abc","name":"foo","created_at":"2025-01-01T00:00:00Z"}]
2. Exit Codes
Agents use $? for control flow. Use meaningful codes:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General failure |
| 2 | Usage error (bad arguments) |
| 3 | Resource not found |
| 4 | Permission denied |
| 5 | Conflict (resource already exists) |
3. Idempotency
- Design commands safe to retry: prefer
ensure/upsertsemantics overcreate - Or support
--if-not-existsto turn conflict into a no-op - If idempotency isn't possible, return exit code
5on conflict so agents can handle it
4. Self-Documenting Help
--helpincludes realistic examples for every command- Required vs. optional flags are clearly marked
--jsonis documented prominently- Use hierarchical
noun verbpattern:tool resource action(e.g.docker container ls, notdocker-container-ls)
5. Composability & Piping
--quiet/-qfor bare output (one item per line, no decoration) — pipe-friendly--output jsonwith optional--fields id,nameto limit response size- Batch operations via
--selectoror repeated args instead of requiring 50 individual calls
6. Dry-Run & No-Prompt Modes
--dry-runproduces structured output showing what would change — nothing is mutated--yes/--forcebypasses all interactive prompts (agents cannot type "y")- Detect non-TTY (
!isatty(stdin)) and either skip prompts automatically or fail fast with a clear message pointing to--yes
7. Actionable Error Messages
Include in structured error output:
error_code/error_type(not just"Error: deployment failed")- The failing input echoed back, so agents can construct a fix
- Suggested next step where applicable
- Whether the error is transient (safe to retry) or permanent (give up)
{
"error": "resource_not_found",
"message": "Project 'staging' does not exist",
"input": {"project": "staging"},
"suggestion": "Run 'mytool project list' to see available projects",
"retryable": false
}
8. Input Hardening
Agents hallucinate in ways humans don't:
- Validate file paths — reject traversals (
../../.ssh) - Reject control characters and shell-special characters in IDs/names
- Validate resource IDs — reject
?,#,%, URL-encoded sequences - Guard against double-encoding (
%2520→%20→)
Checklist (quick reference)
[ ] --json flag outputs to stdout; warnings/progress to stderr only in --json mode
[ ] Meaningful exit codes (0–5 minimum)
[ ] Idempotent operations or clear conflict handling (exit 5)
[ ] --help with realistic examples per command
[ ] --dry-run for destructive/mutating commands
[ ] --yes/--force to bypass all prompts
[ ] Non-TTY detection (auto-skip prompts or fail + hint)
[ ] --quiet/-q for bare pipe-friendly output
[ ] Consistent field names and types across commands
[ ] Noun-verb command hierarchy
[ ] Structured error with error_code, input, suggestion, retryable
[ ] Batch operations for bulk work
[ ] Input validation (paths, IDs, encoding)
Note on Design Philosophy
Most CLIs are subtly hostile to agents. The goal isn't a rewrite — it's layering agent-friendly patterns on top of human-friendly ones. Support both paths in the same binary: rich tables for humans when stdout is a TTY, clean JSON when it isn't.