CI/CD Integration
Automate Claude Code in pipelines: run headless with -p, emit machine-parseable JSON, and separate generation from review.
Claude Code defaults to an interactive, conversational interface — and a CI pipeline has no keyboard. Turning it into a reliable automation engine is mostly about running it non-interactively and getting machine-readable results back. This lesson is one of the most heavily tested in Domain 3.
The -p (print) flag
The single most important element is the -p (print) flag. Without it, a CI job hangs forever waiting for input. With it, Claude runs the prompt once, prints the result, and exits.
# Non-interactive, single-shot execution
claude -p "Analyse this pull request for security issues"Try to force headless mode with an environment variable or by redirecting stdin. Claude still waits for interactive input and the job hangs forever.
CLAUDE_HEADLESS=true claude "Review this PR" # still hangsPass the `-p` (print) flag. Claude runs the prompt once, prints the result, and exits.
claude -p "Review this PR for security issues"A CI job that hangs is fixed by the -p flag, not by an environment variable like CLAUDE_HEADLESS=true or by redirecting stdin. This is a frequently tested detail.
Machine-parseable output
For a pipeline to act on results, ask for structured output. --output-format json wraps the response in an envelope with the result text, session id, and cost metadata. --json-schema validates the final output against a schema, landing the conforming data in the envelope's structured_output field where jq can extract it.
# .github/workflows/review.yml (excerpt)
jobs:
claude-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Security review
run: |
claude -p \
--output-format json \
--json-schema '{"type":"object","properties":{"findings":{"type":"array"}}}' \
"Review this PR for security issues" \
| jq '.structured_output'Separate generation from review
The same session that wrote code is weaker at reviewing it, because it carries the reasoning that justified those choices and won't question them. Run independent invocations: one session to generate, a separate one to review. To avoid repeating the same comments on every push, feed prior findings into the review and ask only for new or unaddressed issues — this preserves the signal-to-noise ratio.
# Session A: generate
claude -p "Implement the authentication middleware"
# Session B: independent review
claude -p "Review the authentication middleware for security issues"CLAUDE.md and key CLI flags
Claude Code reads project CLAUDE.md in CI exactly as it does interactively, so document testing standards, fixtures, and review severity there — without it, CI-generated tests tend toward low-value boilerplate. Useful headless flags include --append-system-prompt (adds to the default, keeping tool guidance and safety, versus --system-prompt which replaces everything), --max-turns to cap agentic turns, and --permission-mode / --allowedTools to control tool access non-interactively.
| Flag | Purpose |
|---|---|
| -p | Non-interactive single-shot run (headless) |
| --output-format json | Wrap result in a parseable envelope |
| --json-schema | Validate output against a schema |
| --append-system-prompt | Add to default prompt (keeps safety/tools) |
| --max-turns | Cap the number of agentic turns |
Batch API vs real-time
The Message Batches API is about 50% cheaper but can take up to 24 hours with no latency guarantee. So use the real-time API for blocking pre-merge checks where developers wait on the result, and reserve the Batch API for non-time-sensitive work like overnight reports or weekly audits. Using Batch for a blocking gate is a classic wrong answer.
Use the Batch API for a blocking pre-merge check to save ~50%. Developers may wait up to 24 hours for a result — the gate stalls every PR.
Use the real-time API for blocking checks where developers wait; save the Batch API for non-urgent overnight reports or weekly audits.
Run with -p, emit --output-format json (optionally --json-schema), review in a session separate from generation, and choose real-time vs Batch API by whether developers are waiting.
--append-system-prompt keeps Claude's default tool guidance and safety instructions; --system-prompt drops all of it. Prefer append unless you truly need a clean slate.
How the exam will try to trick you
The distractors below look right under time pressure — learn the tell.
- The trap
Fix a hanging CI job by setting
CLAUDE_HEADLESS=true, adding--batch, or redirecting stdin from/dev/null.Correct answerPass the
-p(--print) flag so Claude runs the prompt once, prints to stdout, and exits.Why: None of those toggles exist; without
-pClaude still waits for interactive input that never arrives. - The trap
Generate the code and review it in the same Claude Code session to keep context.
Correct answerRun generation and review as separate, independent invocations.
Why: The generating session carries the reasoning that justified its choices, so it won't challenge them; a fresh reviewer judges the code on its merits.
- The trap
Use the Message Batches API for a blocking pre-merge check to save ~50%.
Correct answerUse the real-time API for blocking pre-merge checks; reserve Batch for overnight or weekly analysis.
Why: Batch offers no latency SLA (up to 24 hours), so a blocking gate would stall every PR.
- The trap
Run each push's review from scratch with no prior context.
Correct answerFeed previous findings into the review and ask only for new or unaddressed issues.
Why: Reviewing from scratch reproduces the same comments every run, eroding trust and signal-to-noise.
Key takeaways
- The -p flag makes Claude Code run non-interactively; without it CI hangs.
- --output-format json plus --json-schema yield machine-parseable, validated results.
- Review in a session independent from the one that generated the code.
- Feed prior findings back in to avoid duplicate review comments.
- CLAUDE.md is read in CI, so document standards and fixtures there.
- Use real-time API for blocking checks; Batch API for non-urgent reports.
Frequently asked questions
How do I run Claude Code in a CI/CD pipeline?+
Invoke it with the -p (print) flag so it runs the prompt once and exits instead of waiting for keyboard input. Add --output-format json and optionally --json-schema so the pipeline can parse and validate the result programmatically.
Why should code review use a different Claude session than generation?+
The session that generated code carries the reasoning that justified its choices, so it is less likely to challenge them. An independent review session evaluates the changes without that bias and catches issues the original session would rationalize.
When should I use the Batch API instead of the real-time API?+
Use the Batch API for non-time-sensitive work like overnight reports or weekly audits, where its roughly 50% cost saving is worth up to 24 hours of latency. Use the real-time API for blocking pre-merge checks where developers are waiting on the result.