Módulo 7: Integraciones: Git, SDK, y Remote Control

Headless Mode CLI: automatización sin interacción

Headless Mode CLI: automatización sin interacción

Descripción

El SDK te da acceso programático a Claude Code desde Python o TypeScript. Pero a veces no necesitas toda esa maquinaria. A veces lo que quieres es un comando bash que envíe un prompt a Claude Code, reciba la respuesta, y listo. Sin scripts, sin async/await, sin imports — un one-liner en la terminal.

Para eso existe el headless mode: el flag -p que convierte a Claude Code en una herramienta de línea de comandos no interactiva. Le pasas un prompt, te devuelve una respuesta, y el proceso termina. Sin conversación, sin aprobaciones, sin interacción. Es Claude Code como un pipe más en tu pipeline de Unix.

Esta cápsula cubre cómo usar el headless mode, qué formatos de salida están disponibles, cómo combinarlo con otros flags, y cómo construir scripts prácticos de automatización que se ejecutan sin supervisión humana.


Qué es el headless mode

El modo headless es la ejecución no interactiva de Claude Code. En lugar del flujo normal (prompt → respuesta → prompt → respuesta), el headless mode es:

Input (prompt) → Procesamiento → Output (respuesta) → Fin del proceso

El flag -p

claude -p "tu prompt aquí"

Eso es todo. Claude Code recibe el prompt, ejecuta las acciones necesarias (leer archivos, analizar código, etc.), devuelve la respuesta en stdout, y el proceso termina.

Ejemplo básico

claude -p "¿Qué framework usa este proyecto?"

Output:

Este proyecto usa FastAPI (Python) con SQLAlchemy como ORM 
y Pydantic para validación de datos.

Ejemplo intermedio

claude -p "Lista todos los endpoints de la API con sus métodos HTTP y paths"

Output:

Endpoints encontrados:

GET  /api/health          - Health check
POST /api/auth/login      - User login
POST /api/auth/register   - User registration
GET  /api/users           - List users
GET  /api/users/:id       - Get user by ID
PUT  /api/users/:id       - Update user
DELETE /api/users/:id     - Delete user
GET  /api/products        - List products
POST /api/products        - Create product

Formatos de salida

El headless mode soporta tres formatos de salida que controlas con --output-format:

text (default)

claude -p "Explica la función main" --output-format text

Devuelve texto plano. Es el formato por defecto — no necesitas especificarlo.

Cuándo usar: scripts simples, output legible por humanos, piping a otros comandos de texto.

json

claude -p "Explica la función main" --output-format json

Devuelve un objeto JSON con la respuesta completa:

{
  "type": "result",
  "subtype": "success",
  "cost_usd": 0.003,
  "is_error": false,
  "duration_ms": 2340,
  "duration_api_ms": 1850,
  "num_turns": 1,
  "result": "La función main() es el entry point de la aplicación...",
  "session_id": "abc123"
}

Cuándo usar: cuando necesitas parsear la respuesta programáticamente, verificar costos, o integrar con otros sistemas que esperan JSON.

stream-json

claude -p "Explica la función main" --output-format stream-json

Devuelve mensajes JSON line-by-line a medida que Claude Code produce output:

{"type":"system","subtype":"init","session_id":"abc123"}
{"type":"assistant","message":{"type":"text","text":"La función main()"}}
{"type":"assistant","message":{"type":"text","text":" es el entry point"}}
{"type":"result","subtype":"success","cost_usd":0.003,"result":"..."}

Cuándo usar: cuando necesitas streaming en tiempo real, mostrar progreso, o procesar la respuesta mientras se genera.

Comparación de formatos

FormatoProgreso en tiempo realParseableMetadataCaso de uso
text❌❌❌Scripts simples, lectura humana
json❌✅✅Integración con sistemas, CI/CD
stream-json✅✅✅Streaming, UIs, procesamiento parcial

Flags complementarios

⚠️ Flags en evolución: Claude Code se actualiza frecuentemente. Verifica los flags disponibles con claude --help o claude -p --help antes de usarlos en scripts de producción.

El headless mode se combina con otros flags para control fino:

--model: seleccionar modelo

claude -p "Review this code" --model opus
claude -p "Quick fix" --model sonnet

--max-turns: limitar iteraciones

claude -p "Analyze this project" --max-turns 3

Limita cuántas veces Claude Code puede iterar (leer archivos, ejecutar tools, etc.) antes de dar la respuesta final. Útil para controlar costos y tiempo.

--dangerously-skip-permissions

claude -p "Fix the linting errors" --dangerously-skip-permissions

Salta todas las verificaciones de permisos. Claude Code puede leer, escribir, y ejecutar sin pedir aprobación.

⚠️ PRECAUCION EXTREMA: Este flag debe usarse SOLO en entornos controlados y aislados (containers, CI/CD sandboxes, entornos efimeros). Nunca en tu máquina de desarrollo con código de producción. Con --no-permissions, Claude Code puede ejecutar cualquier comando sin supervision, incluyendo operaciones destructivas como borrar archivos o modificar configuraciones criticas. Si necesitas automatizacion, prefiere --allowedTools para pre-aprobar solo las herramientas especificas que necesitas.

--output-file: guardar output en archivo

claude -p "Generate API documentation" --output-file docs/api.md

--system-prompt: prompt de sistema personalizado

claude -p "Review auth.py" --system-prompt "You are a security expert. Focus only on security vulnerabilities."

--append-system-prompt: agregar al system prompt existente

claude -p "Review auth.py" --append-system-prompt "Focus on SQL injection and XSS."

La diferencia con --system-prompt: --append-system-prompt mantiene el system prompt default de Claude Code (incluyendo CLAUDE.md) y agrega tu texto. --system-prompt lo reemplaza completamente.

--allowedTools: restringir herramientas

claude -p "Analyze this code" --allowedTools Read Grep Glob

Limita qué herramientas puede usar Claude Code. Para review, solo necesita leer.

Combinación de flags

claude -p "Review the latest changes for security issues" \
  --model opus \
  --max-turns 5 \
  --output-format json \
  --allowedTools Read Grep Glob \
  --append-system-prompt "Focus on OWASP Top 10 vulnerabilities"

Piping: Claude Code en pipelines Unix

El headless mode sigue la filosofía Unix: puede recibir input via stdin y enviar output a stdout. Esto lo hace composable con otras herramientas.

Pipe de entrada

cat src/auth.py | claude -p "Review this code for security issues"
git diff | claude -p "Explain these changes"
git log --oneline -10 | claude -p "Summarize the recent development activity"

Pipe de salida

claude -p "Generate a .gitignore for a Python FastAPI project" > .gitignore
claude -p "List all TODO comments in the codebase" | grep "HIGH"

Composición completa

git diff --staged | claude -p "Write a conventional commit message for these changes" | git commit -F -

Este one-liner:

  1. Obtiene los cambios staged en Git
  2. Los pasa a Claude Code para generar un commit message
  3. Usa ese mensaje para hacer el commit

Otro ejemplo de composición

find src -name "*.py" -newer last_review.txt | \
  while read f; do
    echo "=== $f ===" >> review.md
    claude -p "Brief review of $f: bugs, issues, score 1-10" >> review.md
  done

Scripts prácticos

Script 1: Code review automatizado en git diff

#!/bin/bash
# review-changes.sh - Review uncommitted changes

DIFF=$(git diff)

if [ -z "$DIFF" ]; then
    echo "No changes to review."
    exit 0
fi

echo "Reviewing changes..."
echo "$DIFF" | claude -p "Review this git diff. For each file changed:
1. What changed and why (your best guess)
2. Any bugs or issues
3. Suggestions

Format as markdown." --output-format text > review-output.md

echo "Review saved to review-output.md"
cat review-output.md

Script 2: Generador de PR description

#!/bin/bash
# pr-description.sh - Generate PR description from branch commits

BASE_BRANCH=${1:-main}
BRANCH=$(git branch --show-current)

echo "Generating PR description for $BRANCH (base: $BASE_BRANCH)..."

COMMITS=$(git log $BASE_BRANCH..$BRANCH --oneline)
DIFF=$(git diff $BASE_BRANCH...$BRANCH --stat)
FULL_DIFF=$(git diff $BASE_BRANCH...$BRANCH)

claude -p "Generate a GitHub PR description based on these commits and changes.

Branch: $BRANCH
Base: $BASE_BRANCH

Commits:
$COMMITS

Files changed:
$DIFF

Full diff (first 5000 chars):
${FULL_DIFF:0:5000}

Format:
## Summary
[2-3 sentences]

## Changes
[bullet points]

## Testing
[how to test]

## Notes
[any caveats or follow-ups]" --output-format text

Uso:

chmod +x pr-description.sh
./pr-description.sh main

Script 3: Generador de tests para funciones nuevas

#!/bin/bash
# generate-tests.sh - Generate tests for new/modified functions

FILES=$(git diff --name-only --diff-filter=AM | grep "\.py$")

if [ -z "$FILES" ]; then
    echo "No new or modified Python files."
    exit 0
fi

for FILE in $FILES; do
    TEST_FILE="tests/test_$(basename $FILE)"
    echo "Generating tests for $FILE..."

    claude -p "Read $FILE and generate pytest tests for any new or modified 
    functions. Include edge cases and error handling. Output ONLY the test 
    code, no explanations." \
    --allowedTools Read Grep \
    --max-turns 3 \
    --output-format text > "$TEST_FILE"

    echo "  Created: $TEST_FILE"
done

Script 4: Documentación de cambios diarios

#!/bin/bash
# daily-changelog.sh - Generate daily changelog

YESTERDAY=$(date -v-1d +%Y-%m-%d 2>/dev/null || date -d "yesterday" +%Y-%m-%d)
TODAY=$(date +%Y-%m-%d)

COMMITS=$(git log --after="$YESTERDAY" --before="$TODAY 23:59:59" --oneline)

if [ -z "$COMMITS" ]; then
    echo "No commits since yesterday."
    exit 0
fi

claude -p "Generate a changelog entry for today ($TODAY) based on these commits:

$COMMITS

Format:
## $TODAY

### Added
- [new features]

### Changed  
- [modifications]

### Fixed
- [bug fixes]

Skip sections with no entries." --output-format text >> CHANGELOG.md

echo "Changelog updated."

Script 5: Pre-commit hook con Claude Code

#!/bin/bash
# .git/hooks/pre-commit - Claude Code pre-commit review

STAGED=$(git diff --staged --name-only)

if [ -z "$STAGED" ]; then
    exit 0
fi

DIFF=$(git diff --staged)

RESULT=$(echo "$DIFF" | claude -p "Quick review of these staged changes. 
If there are critical bugs or security issues, respond with BLOCK and explain why. 
If changes look OK, respond with PASS.
Only respond BLOCK for actual bugs, not style issues." \
--max-turns 2 --output-format text)

if echo "$RESULT" | grep -q "BLOCK"; then
    echo "❌ Pre-commit review found issues:"
    echo "$RESULT"
    echo ""
    echo "Fix the issues or use 'git commit --no-verify' to skip."
    exit 1
fi

echo "✅ Pre-commit review passed."
exit 0

Instalar:

cp pre-commit.sh .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

Comparaciones y decisiones

Headless CLI vs SDK: cuándo usar cada uno

AspectoHeadless CLI (-p)SDK (Python/TS)
ComplejidadUna línea bashScript completo
SetupNinguno (ya tienes Claude Code)Instalar librería
Error handlingExit codesTry/catch, tipos
Streamingstream-jsonNative async iterators
ComposabilidadPipes UnixFunciones programáticas
ConcurrenciaManual (background jobs)asyncio.gather() / Promise.all()
Structured output--output-format jsonParsing en código
Ideal paraScripts simples, hooks, one-linersPipelines complejos, apps

Regla de decisión

¿Puedes resolverlo con un pipe de bash?
├── Sí → Headless CLI
└── No → ¿Necesitas lógica condicional, loops, o error handling?
    ├── Sí → SDK
    └── No → Headless CLI con script bash

Headless CLI vs Claude Code interactivo

SituaciónInteractivoHeadless
Desarrollo activo✅❌
Code review en CI/CD❌✅
Pre-commit hook❌✅
Explorar un codebase nuevo✅❌
Generar docs automáticamente❌✅
Debugging✅❌
Cron job diario❌✅
Pair programming✅❌

Patterns comunes

Pattern: template de script reutilizable

#!/bin/bash
# claude-task.sh - Template for headless Claude Code tasks

set -euo pipefail

PROMPT="$1"
FORMAT="${2:-text}"
MAX_TURNS="${3:-5}"

if [ -z "$PROMPT" ]; then
    echo "Usage: ./claude-task.sh \"prompt\" [format] [max_turns]"
    exit 1
fi

claude -p "$PROMPT" \
    --output-format "$FORMAT" \
    --max-turns "$MAX_TURNS" \
    --allowedTools Read Grep Glob

Pattern: batch processing con xargs

find src -name "*.py" | \
    xargs -I {} -P 3 bash -c \
    'claude -p "Brief quality score (1-10) for {}: " --max-turns 2'

Procesa archivos en paralelo (3 a la vez) con xargs -P.

Pattern: guardar resultados con timestamp

OUTPUT_DIR="reports/$(date +%Y-%m-%d)"
mkdir -p "$OUTPUT_DIR"

claude -p "Full project analysis" \
    --output-format json > "$OUTPUT_DIR/analysis.json"

Pattern: condicional basado en output

RESULT=$(claude -p "Are there any security vulnerabilities in src/auth.py? Answer YES or NO only." --max-turns 3)

if echo "$RESULT" | grep -qi "YES"; then
    echo "⚠️ Security issues detected!"
    claude -p "Detail the security vulnerabilities in src/auth.py" > security-report.md
    exit 1
fi

echo "✅ No security issues found."

Pitfalls y edge cases

Pitfall 1: prompts largos en la línea de comandos

Bash tiene un límite en la longitud de argumentos. Para prompts largos, usa heredoc o un archivo:

claude -p "$(cat <<'EOF'
Analyze this project with the following criteria:
1. Code quality and readability
2. Test coverage
3. Security vulnerabilities
4. Performance bottlenecks
5. Documentation completeness

For each criterion, provide a score from 1-10 and specific examples.
EOF
)"

O desde un archivo:

claude -p "$(cat prompts/review-template.txt)"

Pitfall 2: caracteres especiales en prompts

Comillas, backticks, y $ dentro del prompt pueden causar problemas:

# MAL: el $ se interpreta como variable
claude -p "Explain the $HOME variable in bash"

# BIEN: usar comillas simples
claude -p 'Explain the $HOME variable in bash'

Pitfall 3: timeout en scripts largos

El headless mode no tiene timeout por defecto. Un prompt complejo puede tardar minutos:

timeout 120 claude -p "Analyze this large codebase" --max-turns 3

Usa timeout para limitar la ejecución.

Pitfall 4: costos acumulados en loops

Un loop que ejecuta Claude Code 100 veces puede ser costoso:

# ⚠️ Esto ejecuta Claude Code 100 veces
for f in $(find src -name "*.py"); do
    claude -p "Review $f" >> reviews.md
done

Considera agrupar archivos en un solo prompt o usar el SDK con batch processing.

Pitfall 5: no usar --allowedTools en automatización

Sin restricción de herramientas, Claude Code puede ejecutar comandos arbitrarios en headless mode. Siempre limita las herramientas:

# ⚠️ Claude puede ejecutar cualquier comando
claude -p "Fix the code" --no-permissions

# ✅ Solo puede leer archivos
claude -p "Review the code" --allowedTools Read Grep Glob

Pitfall 6: output mixto en json

Cuando usas --output-format json, toda la salida es JSON. Si tu script espera texto plano, se rompe:

# Extraer solo el resultado de la respuesta JSON
claude -p "Hello" --output-format json | jq -r '.result'

Usa jq para extraer campos específicos del JSON.


Ejemplo completo integrado

Un pipeline de CI/CD que usa headless mode para múltiples validaciones:

#!/bin/bash
# ci-claude-pipeline.sh - Claude Code CI pipeline

set -euo pipefail

echo "🔍 Claude Code CI Pipeline"
echo "=========================="

REPORT_DIR="ci-reports"
mkdir -p "$REPORT_DIR"
EXIT_CODE=0

# Step 1: Security review
echo ""
echo "Step 1/4: Security review..."
SECURITY=$(git diff main...HEAD | claude -p "Review this diff for security 
vulnerabilities. Respond with JSON: 
{\"critical\": 0, \"high\": 0, \"medium\": 0, \"issues\": [...]}" \
    --output-format json \
    --max-turns 3 \
    --allowedTools Read Grep Glob)

echo "$SECURITY" | jq -r '.result' > "$REPORT_DIR/security.json"

CRITICAL=$(echo "$SECURITY" | jq -r '.result' | jq -r '.critical // 0')
if [ "$CRITICAL" -gt 0 ] 2>/dev/null; then
    echo "  ❌ Critical security issues found!"
    EXIT_CODE=1
else
    echo "  ✅ No critical security issues"
fi

# Step 2: Code quality
echo ""
echo "Step 2/4: Code quality review..."
claude -p "Rate the code quality of the files changed in this branch 
(git diff main...HEAD --name-only). Score each file 1-10.
Format as markdown table: | File | Score | Key Issue |" \
    --max-turns 5 \
    --allowedTools Read Grep Glob \
    --output-format text > "$REPORT_DIR/quality.md"
echo "  ✅ Quality report generated"

# Step 3: Documentation check
echo ""
echo "Step 3/4: Documentation check..."
claude -p "Check if the changed files (git diff main...HEAD --name-only) 
have adequate documentation: docstrings, comments for complex logic, 
README updates if needed. Answer PASS or FAIL with details." \
    --max-turns 3 \
    --allowedTools Read Grep Glob \
    --output-format text > "$REPORT_DIR/docs.md"
echo "  ✅ Documentation check complete"

# Step 4: Generate PR description
echo ""
echo "Step 4/4: Generate PR description..."
COMMITS=$(git log main..HEAD --oneline)
DIFF_STAT=$(git diff main...HEAD --stat)

claude -p "Generate a PR description based on:
Commits: $COMMITS
Files changed: $DIFF_STAT
Format: ## Summary, ## Changes, ## Testing" \
    --max-turns 3 \
    --output-format text > "$REPORT_DIR/pr-description.md"
echo "  ✅ PR description generated"

# Summary
echo ""
echo "=========================="
echo "Reports saved to $REPORT_DIR/"
ls -la "$REPORT_DIR/"

exit $EXIT_CODE

Ejercicios prácticos

Ejercicio 1: Básico — Tu primer headless prompt

Ejecuta Claude Code en headless mode para obtener información sobre tu proyecto.

Requisitos:

  • Usa claude -p con un prompt que pregunte por la estructura del proyecto
  • Prueba los tres formatos de salida (text, json, stream-json)
  • Redirige el output de json a un archivo y extrae el resultado con jq
Solución
# Texto plano
claude -p "What framework does this project use?"

# JSON
claude -p "What framework does this project use?" --output-format json > analysis.json
cat analysis.json | jq -r '.result'

# Stream JSON
claude -p "What framework does this project use?" --output-format stream-json

Ejercicio 2: Intermedio — Script de review

Crea un script bash que revise los cambios sin commitear y genere un reporte.

Requisitos:

  • Lee git diff de los cambios actuales
  • Pasa el diff a Claude Code con -p
  • Genera un archivo review.md con el resultado
  • Si no hay cambios, muestra un mensaje y sale con exit code 0
Solución
#!/bin/bash
# review.sh

DIFF=$(git diff)

if [ -z "$DIFF" ]; then
    echo "No changes to review."
    exit 0
fi

echo "$DIFF" | claude -p "Review these code changes. Format as markdown:
## Summary
## Issues Found (if any)
## Suggestions" \
    --max-turns 3 \
    --allowedTools Read Grep Glob \
    --output-format text > review.md

echo "Review saved to review.md"
chmod +x review.sh
./review.sh

Ejercicio 3: Intermedio — Generador de commit messages

Crea un script que genere un commit message basado en los cambios staged.

Requisitos:

  • Lee git diff --staged
  • Genera un conventional commit message
  • Muestra el message y pregunta si quieres usarlo
  • Si aceptas, ejecuta el commit
Solución
#!/bin/bash
# smart-commit.sh

DIFF=$(git diff --staged)

if [ -z "$DIFF" ]; then
    echo "No staged changes. Run 'git add' first."
    exit 1
fi

MESSAGE=$(echo "$DIFF" | claude -p "Generate a conventional commit message 
for these changes. Format: type(scope): description. 
Add a body if the changes are complex. Output ONLY the commit message." \
    --max-turns 2 \
    --output-format text)

echo "Proposed commit message:"
echo "---"
echo "$MESSAGE"
echo "---"
echo ""
read -p "Use this message? (y/n) " CONFIRM

if [ "$CONFIRM" = "y" ]; then
    git commit -m "$MESSAGE"
    echo "✅ Committed!"
else
    echo "Cancelled."
fi

Ejercicio 4: Avanzado — Pre-commit hook

Implementa un pre-commit hook que usa Claude Code para validar cambios.

Requisitos:

  • Se instala en .git/hooks/pre-commit
  • Revisa cambios staged para bugs críticos
  • Si encuentra bugs, bloquea el commit con un mensaje claro
  • Timeout de 60 segundos
  • Solo usa herramientas de lectura
Solución
#!/bin/bash
# .git/hooks/pre-commit

DIFF=$(git diff --staged)

if [ -z "$DIFF" ]; then
    exit 0
fi

RESULT=$(timeout 60 bash -c "echo '$DIFF' | claude -p 'Quick security and bug check. 
If critical issues found, say BLOCK: [reason]. 
If OK, say PASS. Be concise.' \
    --max-turns 2 \
    --allowedTools Read Grep" 2>/dev/null)

if [ $? -ne 0 ]; then
    echo "⚠️ Claude Code review timed out. Proceeding with commit."
    exit 0
fi

if echo "$RESULT" | grep -q "BLOCK"; then
    echo "❌ Pre-commit review blocked the commit:"
    echo "$RESULT"
    echo ""
    echo "Use 'git commit --no-verify' to skip this check."
    exit 1
fi

echo "✅ Pre-commit check passed."
exit 0
cp pre-commit.sh .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

Ejercicio 5: Challenge — CI pipeline completo

Crea un script de CI que ejecute múltiples validaciones con Claude Code.

Requisitos:

  • Step 1: Security review del diff
  • Step 2: Quality score de archivos cambiados
  • Step 3: Verificar documentación
  • Step 4: Generar PR description
  • Guardar todos los reportes en un directorio
  • Exit code 1 si hay problemas críticos de seguridad
Guía

Usa el ejemplo completo integrado de esta cápsula como base. Adapta los prompts a tu proyecto. Puntos clave:

  1. set -euo pipefail para manejo de errores
  2. --output-format json para el security review (parseable)
  3. --output-format text para reportes legibles
  4. --allowedTools Read Grep Glob en todos los pasos (solo lectura)
  5. --max-turns 3-5 para controlar costos
  6. jq para extraer datos del JSON

Verifica que el script funciona con bash -x ci-pipeline.sh para ver cada comando ejecutado.


Resumen

Lo que aprendiste en esta cápsula:

  • El headless mode (-p) ejecuta Claude Code sin interacción: un prompt, una respuesta, fin del proceso
  • Tres formatos de salida: text (default, legible), json (parseable, con metadata), stream-json (streaming en tiempo real)
  • Flags complementarios: --model, --max-turns, --no-permissions, --output-file, --system-prompt, --allowedTools
  • Piping Unix: Claude Code puede recibir input via stdin y enviar output a stdout, haciéndolo composable con pipes
  • Scripts prácticos: code review automatizado, PR descriptions, test generation, pre-commit hooks, daily changelogs
  • Headless vs SDK: headless para scripts simples y one-liners; SDK para lógica compleja, error handling, y streaming
  • Pitfalls: prompts largos, caracteres especiales, timeouts, costos acumulados, permisos sin restricción

Siguiente cápsula: 05 - MCP y Remote Control — cómo conectar Claude Code con fuentes de datos externas y controlarlo remotamente.


Recursos adicionales

Documentación oficial

Automatización

  • Hooks — Hooks como complemento de headless mode
  • Settings — Configurar permisos para automatización
  • Best Practices — Buenas prácticas para scripts automatizados

Complementarios