Skip to content

Run Claude Code Headless: -p Mode, CI Pipelines, and Scripts That Don't Hang

Headless mode is how you put an agent inside a shell script or a CI pipeline. It is also where the sharp edges live, because everything the interactive UI does for you — permission prompts, visible progress, a human noticing the agent is stuck — is gone.

August 20, 20268 min readShift The Culture

Claude Code has a headless mode: claude -p "your prompt" runs one agentic job with no interactive UI, prints the result, and exits. It is how you put an agent inside a shell script, a CI pipeline, or another program. It is also where most of the sharp edges live, because everything the interactive UI does for you — permission prompts, visible progress, a human noticing the agent is stuck — is gone. Here is how to run it so the failure modes are handled instead of invisible.

The basics: -p and output formats

-p (long form --print) is print mode: no TTY needed, response goes to stdout, exit code tells you whether the run errored. Three output formats:

the three shapes of headless output
claude -p "summarize the failing tests"                      # plain text
claude -p "..." --output-format json                         # one JSON object at the end
claude -p "..." --output-format stream-json --verbose        # event stream as it runs
  • text is for humans reading a terminal. Do not parse it in scripts.
  • json is the one you want for automation: a single result object with the final response, an error flag, the session id, and usage/cost fields. Pipe it through jq and branch on the error flag — not on grepping the prose.
  • stream-json emits every event as it happens. Use it when a run is long and you need liveness — a wrapper that watches the stream can kill a run that has gone quiet or is looping, instead of waiting on a result that never comes.

The permission problem — the part that actually bites

In an interactive session, a tool call the rules don't cover pops a prompt and you click allow. Headless, there is no one to ask. A call that would have prompted is simply not approved — and the agent either works around it, half-finishes, or reports failure. The symptom is a headless run that “works but does nothing”: it planned the edits and never got to write them. You have four levers, in order of preference:

  1. Grant exactly what the job needs with --allowedTools: --allowedTools "Read" "Grep" "Bash(git diff *)". Rules use the same syntax as settings.json permission rules — and the same traps, like rules that look right but never match; we covered those in permission rules that do nothing.
  2. Or set a permission mode: --permission-mode acceptEdits auto-accepts file edits but still gates the dangerous stuff. plan mode is the read-only variant — useful for headless analysis jobs that should never write anything.
  3. Put the standing policy in the repo: .claude/settings.json allow rules apply to headless runs too. For a job you run daily, checked-in rules beat a growing command line.
  4. --dangerously-skip-permissions is the blunt instrument. It does what it says — every tool call, no gate. We only consider it inside a throwaway container with nothing worth stealing: no credentials, no production access, no personal files. On a real workstation an agent with every permission and a scraped-in malicious instruction is your problem, not a hypothetical one.

Give every headless run a budget

An interactive agent that spirals gets interrupted by a human sigh. A headless one keeps going. --max-turns 20caps the number of agentic turns and exits with an error when the cap is hit — which turns “the nightly job burned tokens for four hours” into a visible failure you can alert on. Wrap the whole thing in a timeout as well (timeout 15m claude -p …); turns and wall-clock measure different runaways, and we have been saved by each when the other did not trigger.

Sessions and state between runs

Each -p invocation is a fresh session, but the transcript persists. Capture the session id from the JSON result and you can continue the same conversation later with claude --resume <id> -p "now do step two"; claude --continue picks up the most recent session in the directory. That makes multi-stage pipelines possible — plan in one run, review the plan with a human or a script, execute in a second run — without re-explaining the world each time. For everything else the agent should remember across runs, the answer is files, not sessions: CLAUDE.md and the persistence mechanisms apply to headless exactly as they do interactively.

In CI

  • Authenticate with an API key in the environment — CI has no browser to run the OAuth login.
  • Non-TTY is fine: -p exists for exactly this. If a run hangs in CI but works locally, suspect a tool call waiting on permission (see above) or an MCP server that needs environment variables your runner does not have — the diagnosis path is the same as an MCP server not loading.
  • Treat cost as a first-class output. The JSON result includes usage — log it per run. A pipeline that runs on every push can quietly become your biggest API line item; context is where the money goes.
  • Clean up after runs. Headless sessions that die mid-flight can leave MCP server processes behind, and on a busy runner or dev box they accumulate — orphaned processes is the write-up.

The headless pre-flight, on one screen

before you ship a headless job
1. --output-format json      → parse the result object, branch on the error flag
2. permissions decided       → --allowedTools / --permission-mode / repo settings
   (skip-permissions only inside a disposable container)
3. --max-turns N             → plus a wall-clock timeout around the whole command
4. required output defined   → wrapper validates a file/JSON/test, not prose
5. usage logged per run      → cost is an output, watch it
6. session id captured       → --resume makes multi-stage pipelines cheap
7. after: no leaked processes on the runner

Flag reference: Anthropic's CLI reference. For orchestrating more than one of these at once — parallel headless workers with a coordinator — start from subagents, which solve the same problem inside a single session.

SharePost on X
Free · 13 pages · no upsell inside

Get the Operator Field Kit — free

Six production prompts, the five-step operator setup, and nine rules from our own failure log.

  • 6 complete prompts — printed in full, not previews
  • The five-step setup, each step with a pass/fail test
  • 9 rules from the failure log that produced them

The kit, then the occasional operator note. One click unsubscribes and we never sell the address.

Keep reading