Skip to content

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.

Ընտրեք տեքստը՝ հայերեն թարգմանությունը տեսնելու համար։Highlight text to see Armenian. Machine-assisted translation; English is the source of truth.

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 prompt the action runs once as automation. Without one it waits for someone to write @claude in 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, --model and --append-system-prompt all go there. Older @beta inputs such as direct_prompt and max_turns were replaced in v1.

Recap

  • claude -p is headless Claude Code. Decide its permissions up front, with dontAsk and an explicit --allowedTools list.
  • Cap runs with --max-turns and --max-budget-usd, and parse --output-format json.
  • Keep credentials in CI secrets. claude setup-token gives a one-year token.
  • anthropics/claude-code-action@v1 runs PR reviews with a prompt, or on @claude mentions.

See it in action

headless run in CI · illustrative session
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

  1. 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.
  2. Parse .result and .total_cost_usd from the JSON with jq and post the result as a job summary.
  3. Store the credential as a repository secret (ANTHROPIC_API_KEY, or a token from claude setup-token as CLAUDE_CODE_OAUTH_TOKEN). Never put it in the workflow file.
  4. 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.

  1. A CI step runs claude -p to summarise build logs. You need the job to fail if the summary step errors, and you want to record how much each run cost.

    1. Which flag gives you a machine-readable result with that information?

  2. A scheduled job asks Claude Code to investigate failing integration tests. Once, it looped for an hour and ran up a large bill.

    2. Which two flags bound the run? (Choose two.)

    Choose 2.

  3. You add anthropics/claude-code-action@v1 to review every pull request automatically, with no human typing @claude.

    3. What makes the action run as automation on each PR instead of waiting for a mention?

  4. You want CI to use your Claude subscription instead of an API key.

    4. What's the supported way to do that?

Answer every question to check.