Use CLAUDE.md Files
Configure project-specific guidelines for consistent CI behavior
Your team merges 30 PRs a week and every one waits hours for a human first-pass review. Flaky tests get rubber-stamped, a security regression slips through, and the nightly docs drift further from the code. You don’t need more reviewers - you need an automated first pass that runs on every PR and a way to drive Claude Code from inside your pipeline.
This is where Claude Code stops being an interactive tool and becomes part of your CI/CD. With the anthropics/claude-code-action@v1 GitHub Action and headless mode (claude -p), you can wire AI into the events you already react to: comments, labels, failed builds, and cron schedules.
@claude workflow that responds to PR and issue comments/review commandThe fastest way to enable GitHub integration:
claude to open the interactive REPL, then enter the slash command /install-github-app. It walks you through installing the GitHub app and creating the required secrets.@claude implement this feature based on the issue descriptionFor custom configurations or cloud providers:
name: Claude Code Actions
on: issue_comment: types: [created] pull_request_review_comment: types: [created] issues: types: [opened]
permissions: contents: write pull-requests: write issues: write
jobs: claude-pr: if: contains(github.event.comment.body, '@claude') runs-on: ubuntu-latest timeout-minutes: 60 # Job-level clock, not an action input steps: - uses: actions/checkout@v4
- uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} trigger_phrase: '@claude' claude_args: '--max-turns 30'name: Claude via Bedrock
permissions: contents: write pull-requests: write issues: write id-token: write # For OIDC
jobs: claude-pr: if: contains(github.event.comment.body, '@claude') runs-on: ubuntu-latest env: AWS_REGION: us-west-2 steps: - uses: actions/checkout@v4
- name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} aws-region: us-west-2
- uses: anthropics/claude-code-action@v1 with: use_bedrock: 'true' github_token: ${{ secrets.GITHUB_TOKEN }} trigger_phrase: '@claude' # Bedrock IDs need the region prefix AND a dated -v1:0 suffix. # Verify the exact date against the Bedrock model catalog. claude_args: '--model us.anthropic.claude-sonnet-4-5-20250929-v1:0 --max-turns 10'name: Claude via Vertex AI
permissions: contents: write pull-requests: write issues: write id-token: write # For workload identity
jobs: claude-pr: if: contains(github.event.comment.body, '@claude') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Authenticate to Google Cloud id: auth uses: google-github-actions/auth@v2 with: workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
- uses: anthropics/claude-code-action@v1 with: use_vertex: 'true' github_token: ${{ secrets.GITHUB_TOKEN }} trigger_phrase: '@claude' # Vertex IDs use an @version suffix. Verify against the Vertex catalog. claude_args: '--model claude-sonnet-4@20250514 --max-turns 10' env: ANTHROPIC_VERTEX_PROJECT_ID: ${{ steps.auth.outputs.project_id }} CLOUD_ML_REGION: us-east5Transform issues directly into pull requests:
name: Issue to PR
on: issues: types: [labeled]
jobs: implement-feature: if: github.event.label.name == 'implement-with-claude' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- uses: anthropics/claude-code-action@v1 with: prompt: | Implement the feature described in issue #${{ github.event.issue.number }}:
${{ github.event.issue.title }}
${{ github.event.issue.body }}
Follow our coding standards in CLAUDE.md. Create comprehensive tests. Update documentation as needed.
When the change is complete, open a pull request titled "feat: ${{ github.event.issue.title }}" and reference this issue in the body. anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} claude_args: '--max-turns 30'Enhance PR reviews with AI analysis:
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 # Full history for better analysis
- uses: anthropics/claude-code-action@v1 with: # /review is a built-in command; the action posts review comments to the PR. prompt: '/review' anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} claude_args: '--max-turns 5'The built-in /review command handles the common cases. When you want a security-focused pass with a fixed output format, inline your own instructions in the prompt field instead (there is no prompt_file input):
Automatically attempt to fix CI failures:
name: Auto-fix CI Failures
on: workflow_run: workflows: ['CI'] types: [completed]
jobs: fix-failures: if: ${{ github.event.workflow_run.conclusion == 'failure' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.workflow_run.head_branch }}
- name: Get failure logs uses: actions/github-script@v7 id: logs with: script: | // workflow_run.id is a RUN id, so use downloadWorkflowRunLogs (run_id), // not downloadJobLogsForWorkflowRun (which expects a job_id). const logs = await github.rest.actions.downloadWorkflowRunLogs({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{ github.event.workflow_run.id }} }); return logs.data;
- uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} claude_args: '--max-turns 20' prompt: | The CI build failed with these errors:
${{ steps.logs.outputs.result }}
Fix the issues causing the build to fail. Focus on test failures, linting errors, and type errors. Commit the fix to the current branch with the message "fix: resolve CI failures".Run Claude Code programmatically:
# Simple one-shot commandclaude -p "Update all copyright headers to 2026" --output-format json
# With specific permissionsclaude -p "Fix the failing test in auth.test.js" \ --allowedTools "Edit" "Read" "Bash" \ --output-format json
# Pipe data for processingcat error.log | claude -p "Analyze these errors and suggest fixes"Handle large-scale migrations:
#!/bin/bash# Generate task listclaude -p "List all React class components that need hooks migration" \ --output-format json > tasks.json
# Process each componentjq -r '.files[]' tasks.json | while read file; do echo "Migrating $file..." claude -p "Convert $file from class component to hooks. Preserve all functionality." \ --allowedTools "Edit"doneIntegrate with existing tools:
# Code quality pipelinenpm run lint 2>&1 | \ claude -p "Fix all linting errors" --allowedTools "Edit" | \ claude -p "Now run tests and fix any failures" --allowedTools "Bash" "Edit" | \ claude -p "Generate a summary of changes" > changes.md#!/bin/bash# Check for TODO commentsif git diff --cached --name-only | xargs grep -l "TODO" > /dev/null; then echo "Found TODO comments. Asking Claude to address them..."
git diff --cached --name-only | xargs grep -l "TODO" | while read file; do claude -p "In $file, implement any TODO comments or convert them to proper issues" \ --allowedTools "Edit" done
# Re-stage changes git add -ufiname: Update Documentation
on: schedule: - cron: '0 2 * * *' # 2 AM daily workflow_dispatch:
jobs: update-docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Update API Documentation run: | claude -p "Update API documentation in docs/api.md based on current code in src/api/" \ --allowedTools "Edit" "Read" \ --output-format json > result.json
- name: Update README run: | claude -p "Update README.md badges, dependencies list, and examples based on package.json and recent changes" \ --allowedTools "Edit" "Read"
- name: Create PR if changes uses: peter-evans/create-pull-request@v7 with: title: 'docs: automated documentation updates' commit-message: 'docs: update API docs and README' branch: auto-update-docsOrchestrate changes across microservices:
name: Coordinated Service Update
on: workflow_dispatch: inputs: change_description: description: 'Describe the change to implement' required: true
jobs: plan: runs-on: ubuntu-latest outputs: plan: ${{ steps.create-plan.outputs.plan }} steps: - uses: actions/checkout@v4
- id: create-plan run: | PLAN=$(claude -p "Create an implementation plan for: ${{ github.event.inputs.change_description }}. List affected services and order of updates." --output-format json) echo "plan=$PLAN" >> $GITHUB_OUTPUT
update-services: needs: plan strategy: matrix: service: ${{ fromJson(needs.plan.outputs.plan).services }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: repository: myorg/${{ matrix.service }}
- uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} claude_args: '--max-turns 30' prompt: | Implement this change: ${{ github.event.inputs.change_description }} This is service: ${{ matrix.service }} Full plan: ${{ needs.plan.outputs.plan }}
Ensure backward compatibility, then open a pull request against this service's default branch describing the change and its place in the overall plan.name: Security Analysis
on: pull_request: branches: [main]
jobs: security-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Run Security Scan run: | npm audit --json > audit.json bandit -r . -f json -o bandit.json || true
- name: Analyze and Fix run: | claude -p "Analyze security reports and fix critical issues: NPM Audit: $(cat audit.json) Bandit: $(cat bandit.json)
Fix only CRITICAL and HIGH severity issues. Document any issues that require manual review." \ --allowedTools "Edit" "Read"
- name: Generate Security Report run: | claude -p "Generate a security assessment report based on the changes made" \ > security-report.md
- name: Comment on PR uses: actions/github-script@v7 with: script: | const report = require('fs').readFileSync('security-report.md', 'utf8'); github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: report });Only run Claude on relevant changes:
name: Smart Claude Trigger
on: pull_request: paths: - '**.ts' - '**.tsx' - '**.js' - '**.jsx'
jobs: analyze-complexity: runs-on: ubuntu-latest outputs: should-run-claude: ${{ steps.check.outputs.result }} steps: - uses: actions/checkout@v4
- id: check run: | # Only run Claude for substantial changes LINES_CHANGED=$(git diff --numstat origin/main..HEAD | awk '{sum+=$1+$2} END {print sum}') if [ $LINES_CHANGED -gt 50 ]; then echo "result=true" >> $GITHUB_OUTPUT else echo "result=false" >> $GITHUB_OUTPUT fi
claude-review: needs: analyze-complexity if: needs.analyze-complexity.outputs.should-run-claude == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} prompt: '/review' claude_args: '--max-turns 5'Reduce redundant API calls:
- name: Cache Claude Analysis uses: actions/cache@v4 with: path: .claude-cache key: claude-${{ hashFiles('**/*.ts', '**/*.tsx') }}
- name: Run Claude Analysis run: | if [ -f .claude-cache/analysis.json ]; then echo "Using cached analysis" else claude -p "Analyze codebase for potential improvements" \ --output-format json > .claude-cache/analysis.json fi- name: Report Usage Metrics if: always() run: | claude -p "Summarize the work done in this CI run" --output-format json > usage.json
# Send to monitoring service curl -X POST https://metrics.company.com/claude-usage \ -H "Content-Type: application/json" \ -d @usage.jsonconst { execSync } = require('child_process');
function trackClaudePerformance(command, context) { const start = Date.now(); try { const result = execSync(`claude -p "${command}" --output-format json`, { encoding: 'utf8', maxBuffer: 10 * 1024 * 1024, });
const duration = Date.now() - start; const parsed = JSON.parse(result);
// Send to monitoring sendMetrics({ command, context, duration, tokensUsed: parsed.usage?.total_tokens, success: true, });
return parsed; } catch (error) { sendMetrics({ command, context, duration: Date.now() - start, success: false, error: error.message, }); throw error; }}Use CLAUDE.md Files
Configure project-specific guidelines for consistent CI behavior
Cap Turns and Time
Set --max-turns in claude_args and a job-level timeout-minutes: to prevent runaway costs
Review Before Merge
Always have human review for Claude-generated changes
Monitor Costs
Track API usage and optimize triggers
Symptoms: @claude mentions ignored
Solutions:
Symptoms: 401/403 errors in logs
Solutions:
Symptoms: Jobs cancelled after timeout
Solutions:
timeout-minutes: (there is no timeout_minutes action input)--max-turns in claude_args so a runaway job ends soonerFor custom integrations beyond GitHub Actions:
import { exec } from 'child_process';import { promisify } from 'util';import * as fs from 'fs/promises';
const execAsync = promisify(exec);
async function automateCodeReview(prNumber: number) { // Prepare the review prompt const prompt = `Review pull request #${prNumber} for: - Security vulnerabilities - Performance issues - Code style violations - Missing tests
Use the CLAUDE.md file for project context and .github/review-guide.md for review guidelines.`;
// Save prompt to a file for complex multi-line prompts await fs.writeFile('/tmp/review-prompt.txt', prompt);
try { // With `cat file | claude -p`, stdin becomes the prompt, so no positional // argument is needed. This mirrors the official idiom: // gh pr diff "$1" | claude -p --append-system-prompt "..." --output-format json const { stdout } = await execAsync( `cat /tmp/review-prompt.txt | claude -p --output-format json`, { cwd: process.cwd(), env: { ...process.env } } );
return JSON.parse(stdout); } catch (error) { console.error('Claude Code review failed:', error); throw error; }}
// Alternative: Stream output for real-time processingimport { spawn } from 'child_process';
function streamCodeReview(prNumber: number): Promise<string[]> { return new Promise((resolve, reject) => { const messages: string[] = [];
const claude = spawn('claude', [ '-p', `Review PR #${prNumber} for security and performance`, '--output-format', 'json' ]);
claude.stdout.on('data', (data) => { messages.push(data.toString()); });
claude.on('close', (code) => { if (code === 0) { resolve(messages); } else { reject(new Error(`Claude Code exited with code ${code}`)); } }); });}Team Workflows
Scale CI/CD patterns across your organization
Cost Management
Optimize CI/CD costs and usage
Security Patterns
Implement secure CI/CD practices