Journal indexDockup / field note
Note / cli-design-for-ai-agents

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:

EventWhat it provesWhat it does not prove
Request acceptedThe platform understood the requestThe code built
Build completedAn image or artifact was createdThe app started
Health gate passedThe new instance responded as requiredBusiness flows work
Traffic switchedThe release became activeIt will stay healthy
Uptime observationThe service remains reachableEvery 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 codeMeaningSafe agent response
not_logged_inNo usable tokenStop and request authentication
not_linkedNo .dockup target for pushResolve or pass the target
no_targetService could not be identifiedRun services --json
needs_confirmDestructive action lacks approvalAsk a human
deploy_trigger_failedDeployment could not startReport the API error
deploy_failedBuild or deploy reached failureRead build logs
deploy_timeoutStill running after the wait limitReport 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:

  1. Every read and write operation has machine-readable output.
  2. Failure produces a non-zero process exit.
  3. Asynchronous mutations can wait for a documented terminal state.
  4. Secret values are never returned by read commands.
  5. Destructive actions require explicit confirmation.
  6. Errors have stable codes suitable for branching.
  7. The CLI package and agent instructions remain version-aligned.
  8. 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:

TestExpected behavior
Valid requestJSON result and exit 0
Invalid tokenStable auth code and non-zero exit
Unknown targetStable target code and no mutation
Long-running deployWaits until terminal state or timeout
Failed deployNon-zero exit plus diagnosable deployment ID
Missing destructive approvalneeds_confirm, no deletion
Secret readKey metadata visible, value masked
Warning during JSON outputWarning 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.