Chapter 9: Automated Workflows
Claude Code is not only an interactive tool. It is a programmable component that you can embed in CI/CD pipelines, scheduled jobs, GitHub Actions, and custom automation scripts. This chapter covers everything you need to run Claude Code headlessly and build automated workflows.
The --print Flag: Headless Mode
The --print flag (shorthand -p) is the foundation of all automation. It takes a query, runs Claude Code non-interactively, prints the response, and exits.
claude -p "What does this codebase do?"# Process piped content
cat app.log | claude -p "Summarize errors and categorize by severity"# Use in scripts
RESULT=$(claude -p "List all TODO comments in the codebase")
echo "$RESULT" > todo-report.txtIn --print mode, Claude cannot ask you questions or request permissions interactively. You must either pre-approve the necessary tools with --allowedTools, or use --dangerously-skip-permissions for trusted automation environments.
CLI Flags for Automation
These flags are especially relevant for automated use:
--max-turns N — Limits the number of agentic turns. Prevents runaway execution in scripts:
claude -p "Analyze security vulnerabilities" --max-turns 10--max-budget-usd N — Hard cap on API cost. The task fails if this budget is exceeded:
claude -p "Refactor the entire codebase" --max-budget-usd 2.50--output-format [text|json|stream-json] — Controls the output format:
# JSON output for programmatic parsing
claude -p "List all functions in auth.py" --output-format json
# Streaming JSON for real-time processing
claude -p "Run tests and report results" --output-format stream-json--allowedTools — Pre-approves specific tools for non-interactive use:
claude -p "Run the test suite" --allowedTools "Bash(npm test)"--no-session-persistence — Prevents the session from being saved to disk:
claude -p "Analyze this PR" --no-session-persistence--append-system-prompt — Adds context to the system prompt without replacing it:
claude -p "Review this code" --append-system-prompt "You are reviewing for PCI-DSS compliance. Flag any credit card data handling."--json-schema — Constrains the output to match a JSON Schema. The response is validated against the schema and retried if it does not conform:
claude -p "Extract all function names from this file" \
--output-format json \
--json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}'--bare — Minimal headless mode. Skips hooks, LSP integration, plugins, MCP servers, auto-memory, and CLAUDE.md loading. Fastest startup; use when you want pure model inference without any project configuration:
claude -p "What is the time complexity of binary search?" --bare--bare is useful for quick queries, testing, and cases where you explicitly do not want project-specific configuration to influence the response.
Output Formats
Text Output (default)
Plain text response, suitable for human-readable reports or simple piping:
claude -p "Generate release notes since the last tag" > RELEASE_NOTES.mdJSON Output
Structured JSON with metadata about the response:
claude -p "List all exported functions" --output-format json | jq '.result'The JSON includes:
{
"result": "The response text",
"session_id": "abc123",
"total_cost_usd": 0.012,
"usage": {
"input_tokens": 4523,
"output_tokens": 892
}
}Stream JSON Output
For long-running tasks, stream the response as it is generated. Each line is a JSON event:
claude -p "Analyze all 50 modules" --output-format stream-json | while read line; do
EVENT_TYPE=$(echo "$line" | jq -r '.type')
if [ "$EVENT_TYPE" = "result" ]; then
echo "$(echo "$line" | jq -r '.result')"
fi
doneGitHub Actions Integration
Claude Code's official GitHub Action (anthropics/claude-code-action@v1) integrates AI directly into your GitHub workflows.
Setup
The fastest setup uses the built-in command:
/install-github-appThis installs the Claude GitHub App, adds the required secrets, and creates the workflow file. For manual setup:
- Install the Claude GitHub App
- Add
ANTHROPIC_API_KEYto your repository secrets - Create
.github/workflows/claude.yml
Responding to @claude Mentions
The most common use case: respond when someone mentions @claude in a PR or issue comment.
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
if: contains(github.event.comment.body, '@claude')
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}When a team member comments @claude fix the failing tests, Claude analyzes the PR, finds the failures, implements fixes, and pushes commits to the branch.
Automatic PR Review
Run a review on every pull request without requiring a @claude trigger:
name: Automated PR Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Review this pull request for:
1. Security vulnerabilities (Critical/High/Medium/Low)
2. Logic errors or edge cases
3. Missing tests for new code
4. Performance concerns
Post findings as a PR review comment. For each issue, include
the file path, line number, description, and suggested fix.
claude_args: "--max-turns 15"Scheduled Automation
Run Claude on a schedule for recurring maintenance tasks:
name: Weekly Dependency Audit
on:
schedule:
- cron: "0 9 * * 1" # Every Monday at 9 AM
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Audit project dependencies:
1. Run npm audit and report HIGH/CRITICAL vulnerabilities
2. Check for packages more than 2 major versions behind
3. Create a GitHub issue titled "Weekly Dependency Audit [date]" with findings
4. If there are CRITICAL vulnerabilities, assign the issue to @maintainer-account
claude_args: "--max-turns 10 --allowedTools Bash,Read,Write"Issue Triage Automation
Automatically categorize and label new issues:
name: Issue Triage
on:
issues:
types: [opened]
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Triage this new issue:
1. Read the issue title and body
2. Categorize as: bug, enhancement, question, documentation, or security
3. Estimate complexity: trivial, small, medium, large
4. Apply appropriate GitHub labels using the gh CLI
5. If it is a security issue, add the "security" label and ping @security-team
6. Post a friendly acknowledgment comment
claude_args: "--allowedTools Bash(gh *)"Advanced: Using Skills in GitHub Actions
Skills can be invoked from GitHub Actions workflows:
name: Pre-merge Quality Check
on:
pull_request:
types: [ready_for_review]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: "/security-review"
claude_args: "--max-turns 20"This invokes your project's security-review skill on the PR diff, using the same skill definition your team uses locally.
Pipe-Based Automation
Claude Code follows Unix conventions. Pipe data in and out like any other tool:
# Analyze logs
tail -f production.log | claude -p "Alert me to anomalies. Format: [SEVERITY] timestamp: message"
# Review changed files
git diff main --name-only | claude -p "Review these changed files for security vulnerabilities"
# Translate strings
cat locales/en.json | claude -p "Translate all values to French, preserve keys, output valid JSON" > locales/fr.json
# Generate documentation
find src -name "*.ts" -not -name "*.test.ts" | xargs cat | claude -p "Generate API documentation in OpenAPI 3.0 YAML format"Scripted Multi-Step Workflows
For complex automation, chain multiple Claude invocations in a bash script:
#!/bin/bash
# release-prep.sh - Automated release preparation
set -e
VERSION=${1:-$(git describe --tags --abbrev=0 | sed 's/v//')}
NEXT_VERSION=$2
echo "Preparing release $VERSION -> $NEXT_VERSION"
# Step 1: Verify clean state
echo "Step 1: Verifying test suite..."
claude -p "Run the full test suite. If any tests fail, output 'FAIL:' followed by a summary. If all pass, output 'PASS'." \
--allowedTools "Bash(npm test)" --max-turns 5 | grep -q "^PASS" || {
echo "Tests failing. Aborting release prep."
exit 1
}
# Step 2: Generate changelog
echo "Step 2: Generating changelog..."
CHANGELOG=$(claude -p "Generate a Keep a Changelog entry for version $VERSION based on git commits since the last tag. Output only the markdown, no explanation." \
--allowedTools "Bash(git *)" --max-turns 3)
# Step 3: Update version files
echo "Step 3: Updating version..."
claude -p "Update the version to $NEXT_VERSION in package.json and any other version files you find." \
--allowedTools "Read,Edit,Bash(find *)" --max-turns 5 --dangerously-skip-permissions
# Step 4: Prepend to CHANGELOG
echo "$CHANGELOG" | cat - CHANGELOG.md > /tmp/changelog.tmp && mv /tmp/changelog.tmp CHANGELOG.md
# Step 5: Create commit
git add -A
git commit -m "chore: release $VERSION"
git tag "v$VERSION"
echo "Release $VERSION prepared. Review the changes and run: git push && git push --tags"Continuous Integration Patterns
Per-Commit Security Scan
name: Security Scan
on: [push]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # Need two commits for diff
- name: Run security scan
run: |
RESULT=$(claude -p "Review the changes in this commit for security vulnerabilities. Output JSON with fields: vulnerabilities (array of {severity, file, line, description}), summary (string)." \
--allowedTools "Bash(git diff HEAD~1)" --output-format text --max-turns 5)
CRITICAL=$(echo "$RESULT" | python3 -c "import sys,json; data=json.load(sys.stdin) if sys.stdin.readable() else {}; print(sum(1 for v in data.get('vulnerabilities',[]) if v.get('severity')=='Critical'))" 2>/dev/null || echo "0")
if [ "$CRITICAL" -gt 0 ]; then
echo "::error::$CRITICAL critical security vulnerabilities found"
echo "$RESULT"
exit 1
fi
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}Documentation Freshness Check
name: Documentation Check
on:
pull_request:
paths:
- 'src/**'
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Check if the documentation needs updating for the changes in this PR:
1. Run `git diff origin/main --name-only` to see changed files
2. For each changed file, check if there is corresponding documentation
3. If source code changed but documentation was not updated, list the gaps
4. If everything looks up to date, say "Documentation is current."
5. Post your findings as a PR comment
claude_args: "--max-turns 10"Cost Management in Automation
Automated workflows can accumulate costs quickly. Key controls:
Always set --max-turns in automated contexts. Default is unlimited.
Always set --max-budget-usd for expensive tasks. The task fails cleanly when budget is exceeded rather than running indefinitely.
Use Haiku for simple tasks. Changelog generation, label application, and basic summaries do not need Opus or Sonnet:
claude -p "Apply the correct label to this issue" --model haiku --max-turns 3Scope tools tightly. An agent that can only run git commands will not accidentally do expensive file-wide operations.
Monitor costs. The JSON output format includes total_cost_usd for each run. Log this in your CI system to track trends.
Next up: Chapter 10 — Custom Tool Creation — Building MCP tools that Claude can use, defining tool schemas, and deploying tools for team use.