headroom.walls.sh · ci
Claude Code in CI
Run Claude Code in GitHub Actions and other CI pipelines with --print for non-interactive output — automated PR reviews, code analysis, and documentation generation.
The key flag: --print
By default, Claude Code runs in interactive mode — it opens a session where you type prompts and get responses. In CI, you need it to run once, print output, and exit. That's --print:
claude --print "review this diff for security issues"
With --print, Claude Code reads stdin (if piped) or uses only the flags you provide, outputs its response to stdout, then exits with code 0 on success or non-zero on error. No terminal UI, no session state, fully scriptable.
GitHub Actions: basic setup
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Review PR diff
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
run: |
git diff origin/${{ github.base_ref }}...HEAD > /tmp/pr.diff
claude --print "Review this pull request diff for bugs, security issues, and style problems. Be concise." < /tmp/pr.diff
Setting the secret
In your GitHub repo: Settings → Secrets and variables → Actions → New repository secret. Name: ANTHROPIC_API_KEY, value: your key from console.anthropic.com.
Common CI use cases
PR review with GitHub comment
- name: Review and comment
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git diff origin/${{ github.base_ref }}...HEAD > /tmp/pr.diff
REVIEW=$(claude --print "Review this diff. Flag bugs and security issues only — skip style nitpicks. Use markdown." < /tmp/pr.diff)
gh pr comment ${{ github.event.pull_request.number }} --body "$REVIEW"
Check for TODO comments before merge
- name: Flag TODOs
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
run: |
TODOS=$(grep -rn "TODO\|FIXME\|HACK\|XXX" src/ || true)
if [ -n "$TODOS" ]; then
echo "$TODOS" | claude --print "Categorize these TODO comments by severity: blocker (must fix before merge), warning (should fix soon), info (nice to have)."
fi
Generate release notes from commits
- name: Generate release notes
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
run: |
git log --oneline v${{ github.event.release.tag_name }}^..HEAD > /tmp/commits.txt
claude --print "Write user-facing release notes from these git commits. Group by: new features, bug fixes, breaking changes. Skip chores and ci commits." < /tmp/commits.txt > RELEASE_NOTES.md
Important flags for CI
| Flag / env var | What it does in CI |
|---|---|
| Non-interactive: single prompt → stdout → exit. Essential for CI. | |
| --output-format json | Structured JSON output — easier to parse in scripts than raw text. |
| --max-turns N | Limit how many tool-use turns Claude Code can take (cost control). |
| CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 | Disables telemetry and update checks — faster CI, no noise. |
| --allowedTools "Read,Bash" | Restrict which tools Claude Code can use in this run. |
Piping files and stdin
Claude Code with --print reads stdin when you pipe to it. This is the cleanest way to pass large inputs like diffs, log files, or generated content:
# Pipe a file cat error.log | claude --print "summarize the errors in this log" # Pipe git diff git diff HEAD~1 | claude --print "what changed and why does it matter" # Pipe and capture output SUMMARY=$(cat report.json | claude --print "extract the key metrics as a bullet list")
Exit codes and error handling
claude --print "check this code" < src/main.py EXIT=$? if [ $EXIT -ne 0 ]; then echo "Claude Code failed with exit code $EXIT" exit 1 fi
Exit 0 = success. Non-zero = API error, auth failure, or --max-turns exceeded. Always check the exit code in scripts that gate a deploy or merge.
Cost control in CI
Each CI run makes real API calls — they count against your 5-hour session and 7-day weekly limits, and cost money. A few practices that help:
Use --max-turns 5 to cap agentic tool use. Scope your prompts tightly (diff only, not full repo). Cache the Claude Code install step. Run only on PRs that touch relevant paths using paths: in your workflow trigger.
Headroom shows your Claude Code session (5h) and weekly (7d) utilization in the macOS menu bar — useful when CI pipelines are eating into your local coding quota. The numbers update live from the same data Claude Code tracks itself.
brew install patwalls/tap/headroom
→ Environment variables
→ Agent mode and subagents
→ Permissions and allow rules
→ Rate limits and windows