Headless Mode

Run Command Code non-interactively in scripts, CI/CD pipelines, and automation workflows. Headless mode executes a single query, outputs the response to stdout, and exits.

stdin ──┐ ├──► cmd -p "query" ──┬──► stdout argv ──┘ │ │ text, or │ │ JSON frames │ └──► stderr --verbose ▼ progress exit code 0 ok · 8 max-turns hit

Use the -p (or --print) flag to run in headless mode:

cmd -p "explain this file"
Info

Always wrap your query in quotes. Unquoted multi-word queries cause argument parsing errors.

Direct Argument

Pass your query directly after the -p flag:

cmd -p "refactor the auth module"

Piped Stdin

Pipe input from another command or file:

echo "explain this error" | cmd -p
cat prompt.txt | cmd -p

Command Code auto-detects piped input when no query argument is provided. If stdin is a TTY and no query is given, it exits with an error.

By default, headless mode blocks tools that modify your system - file writes, file edits, and shell commands are denied. This keeps automated runs safe.

To enable all tools, pass --yolo:

cmd -p "fix the lint errors" --yolo

--dangerously-skip-permissions is also accepted as an alias.

ToolDefault (no flag)With --yolo
File reads, grep, globAllowedAllowed
File edits and writesBlockedAllowed
Shell commandsBlockedAllowed
Warning

Only use --yolo in trusted environments. It allows Command Code to modify files and run shell commands without confirmation.

Complete guidePermissions

Headless runs still go through the permission engine. The complete guide covers dont-ask — the fail-closed allowlist mode built for CI — plus rule syntax and the decision ladder.

Headless mode supports multi-turn tool execution. Command Code can read files, search code, and (with permissions) edit files and run commands, just like interactive mode.

The conversation loop runs for up to 100 turns by default. Raise or lower it with --max-turns. If the limit is reached, a warning is printed to stderr and the partial response is returned.

--output-format json turns print mode into a machine-readable stream. Use it when a script needs to react to the run, not just read the final answer.

cmd -p "summarize the auth module" --output-format json

The stream is newline-delimited JSON (NDJSON), one object per line, in two shapes.

Event frames, one per AgentEvent as the run progresses:

{"type": "event", "event": {"type": "tool_running", "toolCallId": "…", "toolName": "read_file", "description": "…"}}

One final result line, always last:

{"type": "result", "subtype": "success", "sessionId": "…", "stopReason": "end_turn", "usage": {}, "durationMs": 8421, "finalText": "…"}
FieldAlways present?Notes
subtypeyessuccess, error, or max_turns - comes first so a consumer can peek the outcome cheaply
usageyesToken usage totals for the run
durationMsyesWall-clock duration
finalTextyesThe assistant's final answer, same text --output-format text would print. Empty string on an error result
sessionIdoptionalOmitted when the run fails before a session is resolved (bad input, auth failure)
stopReasonoptionalWhy the loop ended (end_turn, max_turns, …). Omitted entirely on an error result
erroroptionalPresent only when subtype is error - stderr keeps the human-readable copy
Heads up

Treat sessionId and stopReason as optional. A run that fails early (auth, invalid input, session setup) emits subtype: "error" with neither field, so a script that indexes them unconditionally will break on exactly the cases it most needs to handle.

--output-format text (the default) keeps the classic behavior: just the final answer on stdout.

Note

Parse line by line rather than buffering the whole stream. The event list grows over time, so treat unknown event.type values as forward-compatible and ignore them.

Each headless run persists its transcript to disk, so you can chain follow-up queries that keep prior context. Headless sessions are tagged separately and stay hidden from the interactive /resume menu and from interactive --continue - automation never pollutes your interactive history.

Continue the most recent run

--continue (or -c) resumes the most recent headless session in the current directory:

cmd -p "find the slowest test" cmd -p --continue "now suggest a fix" # carries the previous turn's context

If no headless session exists yet, --continue starts a fresh one - so a -p --continue loop works from the first iteration.

Resume a specific session

Print the session id with --verbose (written to stderr, so stdout stays clean for piping):

cmd -p --verbose "start a code review" # stderr → session: 9f4e1c0a-...

Then resume that exact session by id:

cmd -p --resume 9f4e1c0a-... "continue the review"
Info

A bare --resume with no id errors in headless mode - there is no interactive picker. Use --continue to pick up the latest run instead.

Open a headless session in interactive mode

Pass a headless session id to plain cmd --resume <id> (without -p) to load that transcript into a full interactive session:

cmd --resume 9f4e1c0a-...

Use exit codes to handle results in scripts and CI/CD:

CodeMeaningConstant
0SuccessEXIT_SUCCESS
1General errorEXIT_ERROR
3Not authenticatedEXIT_AUTH_ERROR
4Permission deniedEXIT_PERMISSION_DENIED
5Rate limit exceededEXIT_RATE_LIMITED
6Network failureEXIT_CONNECTION_ERROR
7API server error (5xx)EXIT_SERVER_ERROR
8Max turns reached before final answerEXIT_MAX_TURNS_REACHED
9Model produced no responseEXIT_NO_RESPONSE
10Insufficient creditsEXIT_INSUFFICIENT_CREDITS
130Interrupted (SIGINT/SIGTERM)EXIT_INTERRUPTED

Example usage in a script:

cmd -p "check for security issues" --yolo if [ $? -ne 0 ]; then echo "Command Code failed" exit 1 fi

#!/bin/bash set -e # Generate docs for changed files CHANGED=$(git diff --name-only HEAD~1) echo "Document these changed files: $CHANGED" | cmd -p --skip-onboarding

Headless mode is pipe-friendly. Output goes to stdout, errors and warnings go to stderr:

# Save response to a file cmd -p "generate a README for this project" > README.md # Pipe to another command cmd -p "list all TODO comments" | grep "critical" # Use in command substitution SUMMARY=$(cmd -p "summarize this codebase in one paragraph")

Flags useful for headless and automated workflows:

FlagDescription
-p, --print [query]Run in headless mode
--output-format <format>Print-mode output: text (default) or json - see JSON output
-c, --continueResume the most recent headless session in this directory
-r, --resume <id>Resume a specific headless session by id (no bare picker in print mode)
--verbosePrint the resolved session id to stderr (for chaining --resume)
--max-turns <number>Maximum conversation turns in print mode (default: 100, no upper bound)
-m, --model <model>Run on a specific model this session
--effort <level>Set reasoning effort (low, medium, high, …)
--theme <theme>Set the color theme (dark or light)
--config <key=value>Set any setting headlessly (repeatable) - see below
--yoloAllow file writes and shell commands
--auto-acceptStart in auto-accept mode (alias for --permission-mode auto-accept)
--skip-onboardingSkip taste onboarding (for CI/automated runs)
-t, --trustAuto-trust project (skip initial permission prompt)
--planStart in plan mode (read-only exploration)
--permission-mode <mode>Set permission mode: standard, plan, auto-accept

Slash commands are interactive-only, but the settings behind them are available as flags - never as a /slash string on the command line. The common ones have dedicated flags (--model, --effort, --theme), and --config key=value reaches any setting the /config UI can change:

# Set your model + reasoning effort for this run cmd -p "review this diff" --model claude-sonnet-4-6 --effort high # Set any setting; repeatable. Invalid keys/values exit non-zero. cmd --config theme=dark --config compact-mode=fast

--config is the headless equivalent of the interactive /config UI, so a new setting becomes scriptable automatically. Confirmations print to stderr, keeping -p stdout clean.

LimitationDetails
No interactive promptsNo keyboard shortcuts or interactive UI. Slash commands aren't typed here - use flags instead (see Configuring settings); session-lifecycle commands like /clear and /reload have no meaning in one-shot mode
No resume pickerA bare --resume errors in print mode; resume by explicit id or use --continue
Stdin timeoutPiped stdin times out after 30 seconds if no data is received