AI Agent CLI Design: JSON, Exit Codes, and Wait
AI agent CLI design requires structured JSON, real exit codes, terminal-state waiting, stable errors, and safe confirmation for production automation.
An AI agent CLI is not merely a human command-line tool that happens to be callable from a model. It is an operational protocol. The agent needs deterministic inputs, structured outputs, meaningful exit codes, stable error categories, and a way to wait until asynchronous infrastructure reaches a final state.
Without that contract, an agent is forced to infer success from prose such as “deployment started.” That inference is dangerous because an accepted request may later fail during build, health checks, container startup, or traffic cutover.
Why is guessed deployment success dangerous?
Most infrastructure operations are asynchronous. An API can accept a deployment and return an ID in milliseconds, while the actual build takes several minutes. If an agent reports success at the acceptance boundary, every later step is built on a false premise.
Consider the difference:
| Event | What it proves | What it does not prove |
|---|---|---|
| Request accepted | The platform understood the request | The code built |
| Build completed | An image or artifact was created | The app started |
| Health gate passed | The new instance responded as required | Business flows work |
| Traffic switched | The release became active | It will stay healthy |
| Uptime observation | The service remains reachable | Every feature is correct |
A human may notice the distinction in a dashboard. An agent operating through text needs it encoded in the interface.
Dockup’s command contract separates queueing from completion. A deploy without --wait returns immediately with waited:false; a deploy with --wait blocks until success, failure, or timeout:
dockup deploy production/api --wait --json
The default timeout is 900 seconds. The command exits 0 only after a successful terminal state. It exits non-zero with deploy_failed or deploy_timeout when the result is not success.
What does a structured JSON CLI give an AI agent?
Structured JSON replaces prose interpretation with named fields. The agent can locate status, deploymentId, target, or code directly rather than depending on punctuation, color, column width, or wording.
A successful result can be consumed as data:
{
"ok": true,
"target": "production/api",
"deploymentId": "dep_123",
"waited": true,
"status": "success",
"durationMs": 142381,
"url": "https://api.dockup.tech"
}
A failure uses the same transport shape:
{
"ok": false,
"error": "Deployment failed",
"code": "deploy_failed"
}
The important design rule is that JSON is written to stdout while warnings that must not corrupt parsing go to stderr. Follow-mode logs use NDJSON—one JSON object per line—so a caller can process a stream incrementally without waiting for one giant array.
Dockup applies --json across its command surface. With 135 commands, requiring an agent to infer flags from memory would be brittle. The CLI reference and packaged skill provide the version-aligned command instructions the agent should follow.
The important design property is not clever discovery. It is that the agent receives current, structured operating guidance and does not invent a flag from an old prompt.
How should real exit codes control deployment automation?
The operating system exit code is the most portable success signal available to shell scripts, CI runners, and coding agents. Exit 0 means the command achieved its defined outcome. A non-zero code means the caller must branch into recovery, escalation, or termination.
This shell fragment is intentionally boring:
if dockup deploy production/api --wait --json > result.json; then
echo "deployment reached success"
else
dockup logs production/api --build --json
exit 1
fi
It does not search stdout for the word “success.” It does not assume that an HTTP 202 response means production is ready. It delegates the definition of success to the CLI and propagates failure to the parent process.
Real exit codes are equally important for one-shot commands inside a container. Dockup’s PRO exec command returns stdout, stderr, and the actual command exit code:
dockup exec "npm run migrate" \
-s production/api \
--json
An agent can therefore distinguish a completed migration from a command that merely launched. This is a foundational principle of AI agent production guardrails.
How does terminal-state waiting replace fragile polling?
Hand-written polling loops introduce hidden policy decisions: how often to poll, which states are terminal, how long to wait, whether a transient network error should reset the timer, and what to do when a container restarts.
An agent is especially likely to get those decisions wrong because it may not know the platform’s complete state machine. The platform should own the wait semantics.
Dockup provides two useful patterns:
dockup deploy production/api --wait --timeout 1800 --json
dockup push --json
deploy --wait waits explicitly. push waits by default after pushing and triggering the release; --no-wait is the opt-out. Both return an exit code that reflects the terminal result.
Log following follows the same idea:
dockup logs production/api --build -f --json
The stream ends when the build reaches success or failure. A final NDJSON object marks done:true, and a failed build exits non-zero. The caller does not need a second polling implementation.
For application availability after deployment, Dockup’s uptime command returns minute-level checks, average response time, and p95:
dockup uptime production/api --hours 24 --json
Waiting and monitoring are separate concepts. --wait answers whether this deployment reached a terminal result; uptime answers how the running service behaved over time.
Which error codes should an agent understand?
Stable error categories let an agent take a bounded action without interpreting every possible message. Dockup exposes codes such as:
| Error code | Meaning | Safe agent response |
|---|---|---|
not_logged_in | No usable token | Stop and request authentication |
not_linked | No .dockup target for push | Resolve or pass the target |
no_target | Service could not be identified | Run services --json |
needs_confirm | Destructive action lacks approval | Ask a human |
deploy_trigger_failed | Deployment could not start | Report the API error |
deploy_failed | Build or deploy reached failure | Read build logs |
deploy_timeout | Still running after the wait limit | Report uncertainty or extend deliberately |
The error message remains useful context, but the code drives the first branch. That makes automation resilient to clearer wording or localization.
Confirmation is also part of the protocol. A destructive command should not silently proceed because the caller is non-interactive. Dockup refuses such operations without --yes and returns needs_confirm. An autonomous agent sees a question, not a barrier to bypass.
The safety model is explored further in security best practices.
What is the minimum contract for a production-ready CLI?
A production-ready AI agent CLI should meet a small but strict contract:
- Every read and write operation has machine-readable output.
- Failure produces a non-zero process exit.
- Asynchronous mutations can wait for a documented terminal state.
- Secret values are never returned by read commands.
- Destructive actions require explicit confirmation.
- Errors have stable codes suitable for branching.
- The CLI package and agent instructions remain version-aligned.
- Mutations are captured in an audit trail.
Dockup’s skill turns these rules into default behavior for Claude Code and Codex. It instructs the agent to use JSON, authenticate with DOCKUP_TOKEN, discover exact targets, deploy with --wait, protect credentials, and stop on needs_confirm.
Compare this model with the broader concepts in agent skills vs MCP. A skill supplies operating knowledge; the CLI remains the executable interface whose exit status and output define truth.
A test matrix for an agent-facing command
Before exposing any infrastructure command to an agent, test more than the happy path:
| Test | Expected behavior |
|---|---|
| Valid request | JSON result and exit 0 |
| Invalid token | Stable auth code and non-zero exit |
| Unknown target | Stable target code and no mutation |
| Long-running deploy | Waits until terminal state or timeout |
| Failed deploy | Non-zero exit plus diagnosable deployment ID |
| Missing destructive approval | needs_confirm, no deletion |
| Secret read | Key metadata visible, value masked |
| Warning during JSON output | Warning on stderr, valid stdout JSON |
This matrix is more valuable than a polished progress spinner. Human formatting can be layered on top; a deterministic machine contract cannot be reconstructed after the fact.
The Dockup CLI documentation shows the concrete commands behind this model, while AI-powered development explains the larger shift from manual tool use to agent-directed workflows.
Treat observability as part of the command contract
An agent-facing mutation should return identifiers that make later investigation possible. A deployment response needs the target and deployment ID; a created database needs a stable slug; a volume snapshot needs its snapshot ID. Without those references, the agent can describe an event but cannot reliably inspect, retry, or reverse it.
The audit trail completes the contract. Structured output explains one invocation, while audit records connect multiple invocations over time. Together they let operators answer whether the agent acted on the intended resource and whether a later recovery command referred to the same production event.
Keep the interface boring
A dependable AI agent CLI should be unsurprising under success, failure, timeout, and retry.
Final interface test
The AI agent CLI must fail truthfully.
Put the workflow into production
Test the contract from a shell first: verify JSON parsing, a successful exit, a forced failure, a timeout, and a blocked destructive operation before delegating production access.
npm install -g dockup-cli
dockup skill install
The first command installs the CLI. The second installs the matching Dockup skill for Claude Code and Codex. Start free at app.dockup.ai.
FAQ
What makes a CLI suitable for AI agents?
It needs structured output, real exit codes, terminal-state waiting, stable error codes, secret masking, and explicit confirmation for destructive operations.
Why is JSON better than human-formatted CLI output for agents?
JSON provides stable field names and types. The agent does not have to infer meaning from colors, tables, punctuation, or changing prose.
Why is an accepted deployment request not a success?
Acceptance only proves that the platform queued the operation. The later build, startup, health gate, and traffic cutover can still fail.
What is the default Dockup deployment wait timeout?
The default timeout for dockup deploy --wait is 900 seconds, and it can be changed with the documented --timeout option.
How should an agent react to needs_confirm?
It should stop and ask for explicit approval. The code means the requested action is destructive and was intentionally not executed.