This document outlines the characteristics that make CLIs easier and more reliable for AI systems to use. These principles apply to any CLI, not just this one.
Why it matters: AI systems need to parse output reliably. Free-form text is ambiguous.
# ❌ Hard for AI to parse
$ git status
On branch main
Changes not staged for commit:
modified: file.txt
# ✅ Easy for AI to parse
$ git status --porcelain
M file.txt
# ✅ Even better
$ kubectl get pods -o json
{
"items": [
{"name": "pod-1", "status": "Running"}
]
}Best practices:
- Provide
--json,--yaml, or--format=jsonflags - Use consistent structure across all commands
- Include metadata (timestamps, versions, etc.)
Why it matters: AI can infer command structure and reduce errors.
# ✅ Consistent pattern
$ app create <resource> <name>
$ app get <resource> <id>
$ app list <resource>
$ app update <resource> <id> <data>
$ app delete <resource> <id>
# ❌ Inconsistent
$ app new-user <name> # Why "new-user" not "create user"?
$ app show-user <id> # Why "show" not "get"?
$ app rm-user <id> # Why "rm" not "delete"?Best practices:
- Use consistent verbs:
create,get,list,update,delete - Use consistent resource naming
- Follow REST-like patterns when possible
Why it matters: AI needs to understand failures programmatically.
# ❌ Vague error
$ app deploy
Error: Something went wrong
# ✅ Structured error
$ app deploy
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid configuration",
"fields": {
"region": ["Region is required"],
"instance_type": ["Invalid instance type"]
}
}
}Best practices:
- Use error codes (not just messages)
- Include field-level validation errors
- Provide actionable error messages
- Use consistent error structure
Why it matters: AI can't interact with prompts. Everything must be command-line flags.
# ❌ Interactive (hard for AI)
$ app create
Enter name: [prompt]
Enter region: [prompt]
Confirm? [y/n]
# ✅ Non-interactive (easy for AI)
$ app create --name=myapp --region=us-east-1 --yesBest practices:
- Provide flags for all inputs
- Use
--yesor--forceto skip confirmations - Support environment variables
- Support config files for complex inputs
Why it matters: AI may retry operations. Commands should be safe to run multiple times.
# ✅ Idempotent
$ app set-config --key=value
# Running multiple times has same effect
# ❌ Not idempotent
$ app increment-counter
# Each run changes state differentlyBest practices:
- Make operations idempotent when possible
- Use "upsert" semantics (create or update)
- Document idempotency in help text
Why it matters: AI can validate operations before executing them.
# ✅ Dry-run mode
$ app deploy --dry-run
Would deploy:
- Application: myapp
- Region: us-east-1
- Instances: 2
# ✅ Preview changes
$ app update-config --preview
Changes:
- region: us-east-1 → us-east-2
- instances: 1 → 2Best practices:
- Provide
--dry-runor--previewflags - Show what would happen without executing
- Make output format identical to real execution
Why it matters: AI needs to discover available commands and options.
# ✅ Structured help
$ app --help-json
{
"commands": [
{
"name": "deploy",
"description": "Deploy an application",
"options": [
{
"name": "--region",
"type": "string",
"required": true,
"description": "AWS region"
}
]
}
]
}
# ✅ At minimum, consistent help format
$ app deploy --help
Usage: app deploy [OPTIONS]
Options:
--region TEXT AWS region (required)
--instances INT Number of instances [default: 1]Best practices:
- Provide
--help-jsonor structured help - Include type information for options
- Mark required vs optional clearly
- Include examples in help
Why it matters: AI needs to know if a command succeeded or failed.
# ✅ Standard exit codes
0 - Success
1 - General error
2 - Misuse of shell command
64 - User input error
65 - Data format error
66 - Cannot open input
67 - Addressee unknown
68 - Host name unknown
69 - Service unavailable
70 - Internal software error
71 - System error
72 - Critical OS file missing
73 - Can't create output file
74 - I/O error
75 - Temp failure
76 - Remote error in protocol
77 - Permission denied
78 - Configuration errorBest practices:
- Use standard exit codes
- Document exit codes in help
- Be consistent across commands
- Use non-zero for any failure
Why it matters: AI needs to check current state before making changes.
# ✅ Check state
$ app status
{
"status": "running",
"version": "1.2.3",
"uptime": "2h 15m"
}
# ✅ List resources
$ app list
[
{"id": "1", "name": "app1", "status": "running"},
{"id": "2", "name": "app2", "status": "stopped"}
]Best practices:
- Provide status/state commands
- Make state queryable (not just visible in UI)
- Include timestamps in state
- Show relationships between resources
Why it matters: AI needs to know what format inputs should be.
# ✅ Type information
$ app create --name=STRING --count=INT --enabled=BOOL --tags=ARRAY
# ✅ Validation errors
$ app create --count=abc
Error: Invalid value for --count: 'abc' is not an integerBest practices:
- Validate inputs early
- Provide clear type information
- Show validation errors before execution
- Support type coercion where safe
Why it matters: AI needs predictable outcomes. Partial failures are confusing.
# ✅ Atomic
$ app deploy
# Either fully succeeds or fully fails (rolls back)
# ❌ Non-atomic
$ app deploy
# Partially succeeds, leaves system in inconsistent stateBest practices:
- Make operations atomic when possible
- Use transactions for multi-step operations
- Provide rollback mechanisms
- Document partial failure scenarios
Why it matters: AI needs to know if operations are still running.
# ✅ Structured progress
$ app deploy --json
{"status": "building", "progress": 45, "stage": "compile"}
{"status": "deploying", "progress": 80, "stage": "deploy"}
{"status": "complete", "progress": 100}
# ✅ At minimum, non-blocking
$ app deploy --background
Job ID: 12345
$ app status --job=12345Best practices:
- Provide structured progress output
- Support background/async operations
- Allow querying job status
- Include time estimates when possible
Why it matters: AI needs to understand what happened.
# ✅ Structured logs
$ app deploy --log-format=json
{"timestamp": "2024-01-01T12:00:00Z", "level": "info", "message": "Starting deployment"}
{"timestamp": "2024-01-01T12:00:05Z", "level": "info", "message": "Build complete"}
# ✅ Log levels
$ app deploy --log-level=debugBest practices:
- Support structured logging (JSON)
- Provide log levels
- Include timestamps
- Make logs queryable
Why it matters: AI needs flexible ways to provide configuration.
# ✅ Support multiple sources (in priority order)
1. Command-line flags
2. Environment variables
3. Config files
4. Defaults
$ APP_REGION=us-east-1 app deploy --instances=2
# Uses env var for region, flag for instancesBest practices:
- Support config files
- Support environment variables
- Document precedence
- Provide config validation
Why it matters: AI needs to know what features are available.
# ✅ Version info
$ app --version
app version 1.2.3
API version: v2
# ✅ Feature flags
$ app --features
{
"features": {
"deploy": true,
"rollback": true,
"scale": false # Not available in this version
}
}Best practices:
- Provide version information
- Support feature detection
- Document breaking changes
- Provide migration paths
Why it matters: AI often needs to process multiple items efficiently.
# ✅ Batch operations
$ app deploy --apps=app1,app2,app3
# Or
$ app deploy --apps-file=apps.txt
# ✅ Parallel execution
$ app deploy --parallel --max-concurrent=5Best practices:
- Support batch operations
- Allow parallel execution with limits
- Provide progress for batches
- Handle partial batch failures gracefully
Why it matters: AI needs to find specific resources efficiently.
# ✅ Filtering
$ app list --filter="status=running®ion=us-east-1"
# ✅ Querying
$ app list --query=".items[?status=='running']"
# ✅ Sorting
$ app list --sort="name" --order="desc"Best practices:
- Support filtering
- Support sorting
- Support pagination
- Use standard query syntax when possible
Why it matters: AI should validate before executing expensive operations.
# ✅ Validate config
$ app deploy --validate-only
✓ Configuration valid
✓ Dependencies available
✓ Permissions sufficient
# ✅ Check prerequisites
$ app deploy --check
Checking prerequisites...
✓ API key configured
✓ Network connectivity
✗ Insufficient quota (need 2, have 1)Best practices:
- Provide validation commands
- Check prerequisites
- Validate early
- Provide actionable feedback
A CLI is AI-friendly when it has:
- Structured output (JSON/YAML flags)
- Consistent command patterns (predictable naming)
- Structured errors (codes, field-level errors)
- Non-interactive mode (all inputs via flags)
- Idempotent operations (safe to retry)
- Dry-run mode (preview before execute)
- Machine-readable help (JSON help format)
- Standard exit codes (0 = success, non-zero = failure)
- State queries (check current state)
- Type validation (clear input types)
- Atomic operations (all-or-nothing)
- Structured progress (JSON progress updates)
- Structured logging (JSON logs)
- Config flexibility (files, env vars, flags)
- Version info (feature detection)
- Batch operations (process multiple items)
- Query/filter (find specific resources)
- Validation commands (check before execute)
kubectl:
-o jsonfor structured output- Consistent resource/verb pattern
- Dry-run with
--dry-run=client - Clear exit codes
terraform:
- JSON output mode
- Plan before apply (dry-run)
- State management
- Structured errors
aws-cli:
--output json- Consistent service/operation pattern
- Comprehensive help
- Structured errors
git (without flags):
- Free-form text output
- Inconsistent command patterns
- Interactive prompts
- Hard to parse status
docker (basic usage):
- Text-based output
- Inconsistent formatting
- Limited structured output
The most important principle: Provide machine-readable alternatives for everything. If a human can read it, an AI should be able to parse it programmatically. This means:
- JSON/YAML output flags
- Structured errors with codes
- Non-interactive modes
- Consistent patterns
- Comprehensive validation
When in doubt, ask: "Can an AI system reliably parse and act on this output without ambiguity?"