Claude Code in CI/CD Pipelines: Automating Development with Headless Mode

Shunku

Claude Code isn't just an interactive development tool—it can run programmatically in CI/CD pipelines, enabling AI-powered automation at scale. Using headless mode, you can automate code reviews, fix lint errors, generate documentation, triage issues, and more.

Understanding Headless Mode

Headless mode runs Claude Code non-interactively using the -p (or --print) flag. Instead of an interactive session, you pass a prompt directly:

claude -p "Summarize the changes in this PR"

Claude processes the prompt, executes any necessary tools, and returns the result—all without human intervention.

Basic Usage

# Simple query
claude -p "What does the auth module do?"

# With tool permissions
claude -p "Fix the lint errors in src/" --allowedTools "Read,Edit,Bash(npm run lint)"

# With output format
claude -p "List all exported functions" --output-format json

Output Formats

Headless mode supports multiple output formats for different automation needs:

Plain Text (Default)

claude -p "Summarize the README" --output-format text

Returns human-readable text, ideal for display in PR comments.

JSON with Metadata

claude -p "Analyze the code" --output-format json

Returns structured JSON with the result, session ID, and usage metrics:

{
  "result": "The authentication module uses JWT tokens...",
  "session_id": "abc123",
  "usage": {
    "input_tokens": 1500,
    "output_tokens": 450
  }
}

Structured JSON with Schema

claude -p "Extract function names from auth.ts" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}}}'

Forces output to conform to a specified JSON schema—perfect for parsing in automation scripts.

Stream JSON

claude -p "Generate a report" --output-format stream-json

Returns newline-delimited JSON for real-time processing.

Tool Permissions

By default, Claude Code cannot use tools that modify files or run commands. Use --allowedTools to grant specific permissions:

claude -p "Fix TypeScript errors" \
  --allowedTools "Read,Edit,Bash(npm run typecheck)"

Permission Patterns

# Allow specific tools
--allowedTools "Read,Edit,Write"

# Allow specific bash commands
--allowedTools "Bash(npm run lint),Bash(npm run test)"

# Allow bash with glob patterns
--allowedTools "Bash(git diff:*),Bash(git log:*)"

Safety Considerations

Be conservative with permissions. In CI/CD environments:

  • Only allow tools absolutely necessary for the task
  • Prefer read-only operations when possible
  • Avoid unrestricted Bash access
  • Use specific command patterns, not wildcards

GitHub Actions Integration

Automated Code Review

name: AI 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: Run AI Review
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          # Get the diff
          git diff origin/main...HEAD > changes.diff

          # Run Claude review
          claude -p "Review this diff for:
          1. Potential bugs
          2. Security vulnerabilities
          3. Performance issues
          4. Code style violations

          Be specific with line numbers and suggestions.

          $(cat changes.diff)" \
            --output-format json > review.json

      - name: Post Review Comment
        uses: actions/github-script@v7
        with:
          script: |
            const review = require('./review.json');
            github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body: `## AI Code Review\n\n${review.result}`
            });

Automated Lint Fixing

name: Auto-fix Lint Errors

on:
  push:
    branches: [main]

jobs:
  lint-fix:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install Dependencies
        run: npm ci

      - name: Install Claude Code
        run: npm install -g @anthropic-ai/claude-code

      - name: Fix Lint Errors
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          claude -p "Run npm run lint, then fix any errors found.
          After fixing, run lint again to verify all errors are resolved." \
            --allowedTools "Read,Edit,Bash(npm run lint)"

      - name: Commit Fixes
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add -A
          git diff --staged --quiet || git commit -m "fix: auto-fix lint errors"
          git push

Issue Triage

name: Issue Triage

on:
  issues:
    types: [opened]

jobs:
  triage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Claude Code
        run: npm install -g @anthropic-ai/claude-code

      - name: Analyze Issue
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        id: analyze
        run: |
          claude -p "Analyze this GitHub issue and suggest:
          1. Appropriate labels (bug, feature, documentation, etc.)
          2. Priority (high, medium, low)
          3. Which files/areas of the codebase are likely involved

          Issue title: ${{ github.event.issue.title }}
          Issue body: ${{ github.event.issue.body }}" \
            --output-format json \
            --json-schema '{
              "type": "object",
              "properties": {
                "labels": {"type": "array", "items": {"type": "string"}},
                "priority": {"type": "string"},
                "files": {"type": "array", "items": {"type": "string"}}
              }
            }' > analysis.json

      - name: Apply Labels
        uses: actions/github-script@v7
        with:
          script: |
            const analysis = require('./analysis.json');
            await github.rest.issues.addLabels({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              labels: analysis.structured_output.labels
            });

CI/CD Architecture Patterns

The Review Pipeline

flowchart LR
    subgraph Trigger["Trigger"]
        PR["Pull Request"]
    end

    subgraph Analysis["Claude Analysis"]
        Review["Code Review"]
        Security["Security Scan"]
        Perf["Performance Check"]
    end

    subgraph Action["Action"]
        Comment["PR Comment"]
        Label["Add Labels"]
        Block["Block if Critical"]
    end

    PR --> Review
    PR --> Security
    PR --> Perf

    Review --> Comment
    Security --> Label
    Security --> Block
    Perf --> Comment

    style Trigger fill:#3b82f6,color:#fff
    style Analysis fill:#8b5cf6,color:#fff
    style Action fill:#22c55e,color:#fff

The Fix Pipeline

flowchart LR
    subgraph Trigger["Trigger"]
        Push["Push to Main"]
    end

    subgraph Check["Standard Checks"]
        Lint["Lint"]
        Type["TypeCheck"]
        Test["Test"]
    end

    subgraph Fix["Claude Fix"]
        AutoFix["Auto-fix Errors"]
    end

    subgraph Verify["Verify"]
        Recheck["Re-run Checks"]
        Commit["Commit Fixes"]
    end

    Push --> Check
    Check -->|"Errors"| Fix
    Check -->|"Pass"| Done["Done"]
    Fix --> Verify
    Verify --> Done

    style Trigger fill:#3b82f6,color:#fff
    style Check fill:#f59e0b,color:#000
    style Fix fill:#8b5cf6,color:#fff
    style Verify fill:#22c55e,color:#fff

Session Management

For multi-step workflows, maintain conversation context across commands:

# First step: analyze
result=$(claude -p "Analyze the test failures in this output: $(npm run test 2>&1)" \
  --output-format json)

session_id=$(echo $result | jq -r '.session_id')

# Second step: fix (continuing conversation)
claude -p "Now fix the issues you identified" \
  --resume "$session_id" \
  --allowedTools "Read,Edit"

# Third step: verify
claude -p "Run the tests again to verify fixes" \
  --resume "$session_id" \
  --allowedTools "Bash(npm run test)"

Custom System Prompts

Customize Claude's behavior for specific CI/CD tasks:

Append to Default Prompt

claude -p "Review this code" \
  --append-system-prompt "You are a security-focused code reviewer.
  Prioritize identifying potential security vulnerabilities."

Replace Default Prompt

claude -p "Analyze this code" \
  --system-prompt "You are a performance optimization expert.
  Focus only on performance-related issues. Ignore style and formatting."

Best Practices

Keep Jobs Fast

Target 3-5 minute maximum for review jobs:

- name: Run AI Review
  timeout-minutes: 5
  run: |
    claude -p "Quick review of critical issues only" \
      --max-output-tokens 1000

Use Quality Gates

Require standard checks after AI modifications:

- name: AI Fix
  run: claude -p "Fix errors" --allowedTools "Read,Edit,Bash(npm run fix)"

- name: Verify Build
  run: npm run build

- name: Verify Tests
  run: npm run test

- name: Verify Lint
  run: npm run lint

Handle Failures Gracefully

- name: AI Review
  id: review
  continue-on-error: true
  run: claude -p "Review code" --output-format json > review.json

- name: Fallback Comment
  if: steps.review.outcome == 'failure'
  run: echo "AI review unavailable" > review.json

Limit Scope

Don't ask Claude to do too much in one step:

# Bad: Too broad
claude -p "Review everything and fix all issues"

# Good: Focused
claude -p "Check for SQL injection vulnerabilities in src/api/"

Secure API Keys

Never hardcode API keys. Use secrets management:

env:
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

Common Use Cases

Use Case Trigger Permissions Needed
Code review PR opened Read only
Lint fixing Push to main Read, Edit, Bash(lint)
Test analysis Test failure Read only
Documentation Release tag Read, Write
Issue triage Issue opened Read only
Dependency updates Schedule Read, Edit, Bash

Limitations and Considerations

Authentication

Some environments require interactive login. Ensure your CI environment has proper API key configuration.

Cost Management

Claude API calls have costs. Implement:

  • Token limits per job
  • Job frequency limits
  • Cost monitoring and alerts

Determinism

AI outputs can vary between runs. For critical pipelines:

  • Use structured output schemas
  • Implement validation checks
  • Have fallback behavior

Summary

Headless mode transforms Claude Code into a CI/CD automation tool:

Component Purpose Key Flag
-p flag Non-interactive execution Required
--allowedTools Permission control Safety
--output-format Structured output Integration
--resume Multi-step workflows Context
--json-schema Predictable output Parsing

Key principles:

  • Be conservative with permissions: Only grant what's necessary
  • Keep jobs focused: One task per Claude invocation
  • Verify after changes: Always run standard checks
  • Handle failures gracefully: Have fallback behavior
  • Monitor costs: Track API usage in CI

Claude Code in CI/CD enables intelligent automation that goes beyond static analysis—reviewing code semantically, fixing issues contextually, and triaging with understanding.

References