Headless Claude Code Automation from Scripts and CI
A practical walkthrough of running Claude Code non-interactively with claude -p: bare mode, output formats (text, json, stream-json), JSON schema output, piping stdin, auto-approving tools, and authentication for scripts and CI.
Open the source and read safety notes before installing.
Safety notes
- Headless runs act without a human present; scope tools tightly with --allowedTools or a locked-down permission mode (dontAsk) so the job cannot run more than intended.
- Bare mode (--bare) skips auto-discovery of hooks, skills, plugins, MCP servers, and CLAUDE.md, giving reproducible runs; pass only the context you need with explicit flags.
- Avoid broad Bash permissions in CI; prefer prefix-scoped rules like Bash(git diff *) and pipe data in so the agent does not need read permissions.
- Constrain which branches and paths the automation can touch, and review its output before it gates merges or deploys.
Privacy notes
- Headless runs send the prompt, piped stdin, and repository context to the model provider; review what a job exposes before running it on private code.
- Bare mode skips OAuth and keychain reads, so authentication must come from ANTHROPIC_API_KEY or an apiKeyHelper; store these as CI secrets, never inline.
- CI logs can capture command output and the JSON result (including cost metadata); avoid printing secrets and mask sensitive variables.
Prerequisites
- Claude Code installed in the environment where the script or CI job runs.
- An authentication method that works headlessly: ANTHROPIC_API_KEY, a long-lived OAuth token, an apiKeyHelper, or a cloud provider's credentials.
- jq or similar if you plan to parse JSON output in shell scripts.
Schema details
- Install type
- cli
- Troubleshooting
- No
Full copyable content
Use this guide to run Claude Code non-interactively from scripts and CI with claude -p, bare mode, structured JSON output, and pre-approved tools.About this resource
Overview
Claude Code runs non-interactively when you pass -p (or --print) with a
prompt. This headless mode is how you use Claude Code in scripts, build steps,
and CI pipelines: pipe data in, get structured output back, and pre-approve the
tools the job needs. It uses the same agent loop and tools as an interactive
session.
Basic usage
claude -p "What does the auth module do?"
All CLI options work with -p, including --continue, --allowedTools, and
--output-format.
Bare mode for reproducible runs
--bare skips auto-discovery of hooks, skills, plugins, MCP servers, auto
memory, and CLAUDE.md, so a job behaves the same on every machine. Pass only the
context you need:
claude --bare -p "Summarize this file" --allowedTools "Read"
In bare mode, authentication must come from ANTHROPIC_API_KEY or an
apiKeyHelper in --settings; it skips OAuth and keychain reads. Load extra
context with flags like --append-system-prompt, --settings, --mcp-config,
--agents, or --plugin-dir. Bare mode is the recommended mode for scripted
calls.
Pipe data through Claude
cat build-error.txt | claude -p 'concisely explain the root cause of this build error' > output.txt
Piped stdin is capped at 10MB; for larger inputs, write to a file and reference its path in the prompt.
Structured output
# JSON with result, session id, and metadata (including total_cost_usd)
claude -p "Summarize this project" --output-format json | jq -r '.result'
# Conform to a schema
claude -p "Extract the main function names from auth.py" \
--output-format json \
--json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' \
| jq '.structured_output'
Use --output-format stream-json --verbose --include-partial-messages for
newline-delimited streaming events.
Auto-approve tools safely
claude --bare -p "Run the test suite and summarize any failures" \
--permission-mode dontAsk \
--allowedTools "Bash(npm test),Bash(git diff *)"
For a locked-down CI baseline, use --bare so repository-local instructions,
hooks, skills, plugins, MCP servers, and CLAUDE.md cannot change the run. Pass a
permission mode: --permission-mode dontAsk denies anything not in your allow
rules or the read-only command set. --allowedTools uses permission-rule syntax,
so Bash(git diff *) allows a prefix (note the space before *). Avoid broad
rules such as Bash,Read,Edit in CI because they allow arbitrary shell commands
and broad repository reads or edits.
Continue conversations
session_id=$(claude -p "Start a review" --output-format json | jq -r '.session_id')
claude -p "Continue that review" --resume "$session_id"
Note: user-invoked skills like /code-review are interactive-only; in -p mode,
describe the task directly.
Source
- Run Claude Code programmatically: https://code.claude.com/docs/en/headless
Source citations
Signals
Loading live community signals…
A short, calm digest of reviewed Claude resources. Unsubscribe any time.