| name | add-agent-friendly-output |
| description | Make a CLI's output agent-friendly — stdout=data only, stderr=context only, semantic exit codes (80-119), typed errors with suggestions, help-json introspection, no internal retries, non-interactive by default. Use when asked to "make this CLI agent-friendly", "add agent-friendly output", "add semantic exit codes", or audit a tool's output contract. |
Make a CLI agent-friendly (cli-output-spec convention)
Apply the six rules from PROTOCOL.md to a CLI so its output is safe for
programmatic consumption by AI agents. Full how-to in RECIPE.md. This skill
is the checklist.
The six rules
-
Stream separation: stdout = data only (JSON or line-based, parseable, no ANSI when
piped). stderr = context only (progress, logs, warnings — never data). Never mix.
-
Semantic exit codes: 0 success. 80-89 input/validation. 90-99
precondition/resource. 100-109 external/integration. 110-119 internal/bug. Agents
branch on $?.
-
Typed errors: {"ok":false,"error":{"code":90,"type":"not_authenticated","message":"...","recoverable":false,"suggestions":["myapp signup"]}}.
Code matches exit code. recoverable tells agent whether to retry. suggestions gives
the agent the exact fix command.
-
No internal retries: report the error once, exit. The agent decides whether to
retry, back off, or escalate. Internal retry loops hide failures and eat timeout budget.
-
help-json introspection: machine-readable command catalog (commands, args, flags,
auth requirements, exit codes, env vars). Agents query capabilities; they don't read docs.
-
Non-interactive by default: --no-interactive / --yes / --json flags disable
prompts. Credentials from flags/env/config, never stdin on the happy path.
The four edits
-
Stream separation + JSON output: add log() → stderr, relay() → stdout. Add
--json flag. Check [ -t 1 ] for ANSI colors. Copy from RECIPE.md step 1.
-
Semantic exit codes + typed errors: add fail(code, type, msg, ...suggestions)
function that prints typed JSON to stderr and exits with the code. Map HTTP status to
exit code. Copy from RECIPE.md step 2.
-
help-json: add a help_json() function that outputs the command catalog as JSON.
Add help-json / --help-json to the dispatch. Copy from RECIPE.md step 3.
-
Non-interactive flags + no retries: accept and ignore --json/--no-interactive/
--yes/--no-color global flags. Remove any internal retry loops. Copy from RECIPE.md
step 4.
Verify before you're done
myapp leads --json 2>/dev/null | jq '.leads | length'
myapp leads --json 2>&1 1>/dev/null | head -1
myapp leads --json; echo $?
myapp leads --json
myapp leads --json 2>&1 | jq '.error | {code,type,recoverable}'
myapp help-json | jq '.commands | keys'
myapp leads --no-interactive --yes
Gotchas
- Don't print data to stderr "for context". An agent that ignores stderr loses the
data. stdout is the single source of truth.
- Don't use exit code 1 for everything. Agents can't distinguish "wrong args" from
"server down" from "bug". Use the 80-119 ranges.
- Don't retry internally. Report once, exit, let the agent decide.
- Don't print ANSI when piped. Check
[ -t 1 ] (bash) or term.IsTerminal() (Go).
- Bash quoting in
case patterns: parentheses inside double-quoted strings in a
case pattern are parsed as pattern separators. Avoid ( in usage strings.