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
| Formato | Progreso en tiempo real | Parseable | Metadata | Caso 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 --helpoclaude -p --helpantes 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--allowedToolspara 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:
- Obtiene los cambios staged en Git
- Los pasa a Claude Code para generar un commit message
- 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
| Aspecto | Headless CLI (-p) | SDK (Python/TS) |
|---|---|---|
| Complejidad | Una línea bash | Script completo |
| Setup | Ninguno (ya tienes Claude Code) | Instalar librería |
| Error handling | Exit codes | Try/catch, tipos |
| Streaming | stream-json | Native async iterators |
| Composabilidad | Pipes Unix | Funciones programáticas |
| Concurrencia | Manual (background jobs) | asyncio.gather() / Promise.all() |
| Structured output | --output-format json | Parsing en código |
| Ideal para | Scripts simples, hooks, one-liners | Pipelines 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ón | Interactivo | Headless |
|---|---|---|
| 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 -pcon un prompt que pregunte por la estructura del proyecto - Prueba los tres formatos de salida (
text,json,stream-json) - Redirige el output de
jsona un archivo y extrae el resultado conjq
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 diffde los cambios actuales - Pasa el diff a Claude Code con
-p - Genera un archivo
review.mdcon 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:
set -euo pipefailpara manejo de errores--output-format jsonpara el security review (parseable)--output-format textpara reportes legibles--allowedTools Read Grep Globen todos los pasos (solo lectura)--max-turns 3-5para controlar costosjqpara 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
- Headless Mode — Documentación completa del modo headless
- CLI Reference — Todos los flags de CLI
- GitHub Actions — Headless mode en CI/CD
Automatización
- Hooks — Hooks como complemento de headless mode
- Settings — Configurar permisos para automatización
- Best Practices — Buenas prácticas para scripts automatizados
Complementarios
- SDK Documentation — Para cuando headless no es suficiente
- jq Manual — Procesamiento de JSON en la línea de comandos