Appearance
Pipe: Script & Automate
Claude Code in interactive mode feels like pairing with another developer: you type, it responds, you iterate. But that model breaks down when you need Claude inside a script, a CI pipeline, or a cron job. You can't sit at the terminal and type /review for every commit when there are forty commits a day.
Print mode , claude -p "task" , is the bridge. It runs Claude headlessly, writes output to stdout, and exits. Pipe text into it, pipe its output into the next tool, wrap it in a shell script, call it from a Makefile. This is where Claude Code stops being a conversation and starts being infrastructure.

What you'll learn
claude -p "prompt"runs headlessly: reads stdin, writes stdout, exits when done, no interactive session--output-format jsonand--json-schemaextract structured data for programmatic consumption- Pipe workflows compose Claude with grep, jq, git, and any other Unix tool in a single pipeline
--resumepicks up an interrupted session, and--continuelets Claude keep working on the same task
The problem
You have a CI pipeline that builds, tests, and deploys. Every PR needs a code review before it merges. Right now, a human does those reviews, or they don't happen, and bugs ship. You could install a dedicated code review tool, configure it, maintain it, and pay for it. Or you could have Claude Code do the review from a one-liner in your CI config.
The same pattern applies to changelog generation, release note drafting, security advisory triage, log analysis, and a dozen other tasks that fit the shape "gather some text, ask Claude to reason about it, act on the result." The missing piece isn't Claude's ability, it's the plumbing that feeds it input and captures its output without you typing at a prompt.
Options & when to use each
Claude Code in headless mode gives you several output and execution patterns:
| Pattern | Command | Use when |
|---|---|---|
| Simple prompt | claude -p "summarize this" | One-shot reasoning task where you don't need structured output |
| Pipe input | `git diff HEAD~1 | claude -p "review this diff"` |
| Structured output | claude -p "..." --output-format json | You need machine-readable output to feed into the next step of a pipeline |
| Schema-constrained output | claude -p "..." --json-schema schema.json | You need output that conforms to a specific JSON shape for an API or database |
| Resume a session | claude --resume <session-id> -p "continue" | A task was interrupted or timed out, and you want to pick up where it left off |
| Continued work | claude --continue | Claude should keep working on the same task without a new prompt |
The --json-schema flag is worth special attention. Without it, even requesting JSON output gives you a model that might wrap the JSON in markdown fences, include explanatory text, or miss a field. With a JSON schema, Claude Code constrains the output to match your exact shape, no markdown, no commentary, no missing fields. This is the difference between "Claude, give me JSON" (which works most of the time) and "Claude, give me exactly this shape" (which works every time).
bash
# Without schema: Claude might return markdown-wrapped JSON
claude -p "List the top 3 functions in src/ by line count" --output-format json
# With schema: Claude returns exactly the shape you defined
claude -p "List the top 3 functions in src/ by line count" --output-format json --json-schema schema.jsonBuild it
Step 1: Pipe git diff into Claude for code review
The core pattern: a command produces text, you pipe it into claude -p, Claude reasons about it and produces a result.
bash
# Review the last commit
git diff HEAD~1 | claude -p "Review this diff for bugs, security issues, and style problems. Be concise."
# Review staged changes
git diff --staged | claude -p "Review these staged changes. Flag anything that should block the commit."
# Review all changes in a PR branch against main
git diff main...feature-branch | claude -p "Review this PR diff. Check for: missing tests, SQL injection, hardcoded secrets, inconsistent error handling."Each of these runs Claude headlessly and prints the review to stdout. No interactive session. No /review slash command. No terminal you have to keep open.
Step 2: Structured output for programmatic consumption
For CI pipelines, you want machine-readable output you can act on:
bash
# Generate a structured code review
git diff HEAD~1 | claude -p "Review this diff and output a JSON object with fields: summary (string), issues (array of {severity, file, line, description}), approved (boolean)" --output-format json > review.json
# Parse the result
jq '.approved' review.json # true or false
jq '.issues | length' review.json # how many issues
jq '.issues[] | select(.severity == "critical")' review.json # critical issues onlyNow your CI script can branch on the result:
bash
#!/bin/bash
git diff origin/main...HEAD | claude -p "..." --output-format json > review.json
if [ "$(jq -r '.approved' review.json)" != "true" ]; then
echo "Code review rejected. See review.json for details."
exit 1
fiStep 3: Schema-constrained output for API integration
Create a schema file review-schema.json:
json
{
"type": "object",
"properties": {
"summary": { "type": "string" },
"score": { "type": "integer", "minimum": 0, "maximum": 10 },
"issues": {
"type": "array",
"items": {
"type": "object",
"properties": {
"severity": { "enum": ["critical", "warning", "info"] },
"file": { "type": "string" },
"line": { "type": "integer" },
"message": { "type": "string" }
},
"required": ["severity", "file", "message"]
}
}
},
"required": ["summary", "score", "issues"]
}Execute the query using the schema definition. The agent will parse the input diff and enforce the JSON shape over the output.
bash
git diff HEAD~1 | claude -p "Review this diff" --output-format json --json-schema review-schema.json > review.jsonThe output is guaranteed to match the schema, no markdown wrapping, no extra fields, no missing required fields. You can POST it directly to an API, store it in a database, or parse it with jq without defensive error handling.
Step 4: Resume interrupted sessions
Print mode has a 5-minute timeout by default. For tasks that take longer, analyzing a large codebase, processing many files, use --resume:
bash
# Start a long-running task
claude -p "Analyze every Python file in src/ and list all functions with their docstrings" --output-format json > analysis.json
# ... times out after 5 minutes, session saved with ID shown in output
# Resume where it left off
claude --resume <session-id> -p "continue from where you left off" --output-format json >> analysis.jsonThe session ID is printed when the initial command times out or when you run with increased verbosity. You can also find it with claude --resume without arguments, which lists recent interrupted sessions.
Step 5: Compose Claude into a real pipeline
Here is a CI-ready script that reviews a PR, posts the result as a comment, and blocks the merge if critical issues are found:
bash
#!/bin/bash
set -euo pipefail
# Capture the diff
DIFF=$(git diff origin/main...HEAD)
# Run the review
REVIEW=$(echo "$DIFF" | claude -p "Review this PR diff for bugs, security issues, and style problems. Output JSON with fields: approved (boolean), issues (array of {severity, file, line, message})." --output-format json 2>/dev/null)
# Check if approved
APPROVED=$(echo "$REVIEW" | jq -r '.approved')
CRITICAL=$(echo "$REVIEW" | jq -r '[.issues[] | select(.severity == "critical")] | length')
echo "$REVIEW" | jq '.'
if [ "$APPROVED" != "true" ] || [ "$CRITICAL" -gt 0 ]; then
echo "::error::Claude Code review found $CRITICAL critical issues"
exit 1
fi
echo "::notice::Claude Code review passed"Drop that into .github/workflows/claude-review.sh or your CI runner's equivalent, and every PR gets an automated Claude Code review before it can merge.
What goes wrong
| Mistake | How you notice it | The fix |
|---|---|---|
| JSON output wrapped in markdown fences | jq fails to parse the output: "parse error: Invalid literal" | Use --json-schema with a schema file, which suppresses markdown wrapping. Alternatively, pipe through sed 's/^```json//; s/^```//' as a fallback |
| Task times out before completion | Claude exits with a timeout message and the output is incomplete | Use --resume <session-id> to continue. For tasks you know will be long, split them into smaller sub-tasks rather than one enormous prompt |
| Pipe input is too large for one turn | Claude truncates the input or the response | Filter the input before piping: `git diff HEAD~1 |
--json-schema is too strict | Claude returns valid JSON but omits data that didn't fit the schema | The schema is a constraint, not a suggestion. If the schema says issues is an array of exactly the listed fields, Claude won't include anything else. Add additionalProperties: true if you want flexibility |
| Shell quoting breaks the prompt | Command fails with a syntax error or Claude receives garbled input | Single-quote the prompt when it contains special characters: claude -p 'review: look for $PATH issues'. Double-quotes expand shell variables before Claude sees them |
Confirm it worked
Run a complete pipe-review-parse pipeline end to end:
bash
# 1. Create a test file with a known issue
mkdir -p /tmp/claude-pipe-test
cat > /tmp/claude-pipe-test/app.py << 'EOF'
def get_user(user_id):
query = f"SELECT * FROM users WHERE id = {user_id}"
return db.execute(query)
EOF
# 2. Initialize git, commit, make a change
cd /tmp/claude-pipe-test
git init && git add . && git commit -m "initial" --no-gpg-sign
cat > app.py << 'EOF'
def get_user(user_id):
query = f"SELECT * FROM users WHERE id = '{user_id}'"
return db.execute(query)
EOF
# 3. Pipe the diff into Claude for review
git diff | claude -p "Review this diff for security issues. Output JSON with fields: security_issues (array of strings), safe (boolean)" --output-format json > review.json
# 4. Inspect the result
cat review.json
# Expected: safe=false, security_issues mentions SQL injection
# 5. Clean up
cd /home/molsen/workspace
rm -rf /tmp/claude-pipe-testIf step 4 produces valid JSON that correctly identifies the SQL injection vulnerability, the pipe-and-script workflow is working. You've replaced manual code review with a one-liner you can drop into CI, cron, or a pre-commit hook.
Next: Settings and Configuration, the full settings hierarchy and permissions system.