C2 · lesson 7 · 10 min read
Headless mode and PR review in CI
Run Claude Code non-interactively in pipelines, keep it locked down, and review pull requests with the official GitHub Action.
Հայերեն
In CI there is no human to click “allow”. Headless mode is the same Claude Code
running with -p. Your job is to decide in advance exactly what it may do, how long
it may run, and what shape the output takes.
The building blocks
claude -p "Explain the failing test in test-output.txt" \
--permission-mode dontAsk \
--allowedTools "Read" "Grep" \
--output-format json \
--max-turns 5 \
--append-system-prompt "Answer in at most 5 bullet points."
| Flag | Why it matters in CI |
|---|---|
-p / --print |
Non-interactive; exits 0 on success, non-zero on failure |
--permission-mode dontAsk |
Anything not pre-approved is denied, not left waiting for a prompt |
--allowedTools |
The exact tools this job may use |
--max-turns |
Caps the agent loop (print mode only; there is no limit by default) |
--max-budget-usd |
Caps estimated spend |
--output-format json |
One JSON object you can parse |
--append-system-prompt |
Adds job-specific instructions without replacing the defaults |
--bare |
Skips hooks, skills, plugins, MCP servers and CLAUDE.md, for reproducible runs |
The JSON output includes result (the answer), is_error, num_turns,
total_cost_usd, session_id and permission_denials, which is useful for seeing
what the job tried to do but wasn’t allowed to. If you pass --json-schema, the
validated result arrives in structured_output.
Credentials
The usual options in CI are ANTHROPIC_API_KEY, or a subscription token made with
claude setup-token. That command prints a one-year OAuth token and doesn’t save
it anywhere, and you provide it as CLAUDE_CODE_OAUTH_TOKEN. Either way the value
lives in your CI secret store, never in the repository.
PR review with the GitHub Action
The official action is anthropics/claude-code-action@v1. Run
/install-github-app in Claude Code to set it up on github.com, or add a workflow
by hand. The docs’ review workflow looks like this:
name: Code Review
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: "https://github.com/anthropics/claude-plugins-official.git"
plugins: "code-review@claude-plugins-official"
prompt: "/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment"'
Things to notice:
- Two modes. With a
promptthe action runs once as automation. Without one it waits for someone to write@claudein a comment (trigger_phrase). - Least privilege. The review job only reads the repo and is allowed exactly one tool, posting inline comments.
- CLI flags go in
claude_args.--max-turns,--modeland--append-system-promptall go there. Older@betainputs such asdirect_promptandmax_turnswere replaced inv1.
Recap
claude -pis headless Claude Code. Decide its permissions up front, withdontAskand an explicit--allowedToolslist.- Cap runs with
--max-turnsand--max-budget-usd, and parse--output-format json. - Keep credentials in CI secrets.
claude setup-tokengives a one-year token. anthropics/claude-code-action@v1runs PR reviews with a prompt, or on@claudementions.
See it in action
transcript
$ claude -p "Summarise why the build failed" --permission-mode dontAsk --output-format json --max-turns 3 < build.log | jq '.is_error, .result, .total_cost_usd' false "Integration test test_payments_refund fails: the mock gateway returns 503 because PAYMENTS_URL is unset in the test job." 0.0123 $ echo $? 0
$ ./try-it.sh
Try it: summarise a failed build in CI
- In a CI job, run
claude -p "Summarise why this build failed: $(tail -200 build.log)" --permission-mode dontAsk --output-format json --max-turns 3. - Parse
.resultand.total_cost_usdfrom the JSON with jq and post the result as a job summary. - Store the credential as a repository secret (
ANTHROPIC_API_KEY, or a token fromclaude setup-tokenasCLAUDE_CODE_OAUTH_TOKEN). Never put it in the workflow file. - Add the PR review workflow from this lesson and open a test PR.
Check yourself
4 questions. Original practice questions, not taken from the real exam.