Capítulo 2: Instalación y Configuración

Por: Artiko
claudeclaude-code-sdkinstalacionconfiguraciondockersetup

Capítulo 2: Instalación y Configuración


1. Requisitos del Sistema

Antes de instalar el SDK, verifica que tu sistema cumple con todos los requisitos. Una instalación incompleta es la causa más común de errores inesperados.

1.1 Tabla de compatibilidad por plataforma

Sistema OperativoPython SDKTypeScript SDKClaude Code CLINotas
macOS 12+ (Intel)Totalmente soportado
macOS 12+ (Apple Silicon)Nativo ARM, sin Rosetta
Ubuntu 20.04+Totalmente soportado
Debian 11+Totalmente soportado
Fedora 38+Totalmente soportado
WSL2 (Windows)Recomendado para Windows
Windows nativoParcialParcialLimitaciones en Bash tool
Alpine LinuxParcialParcialRequiere glibc compat
Docker LinuxVer sección Docker

1.2 Versiones de Python soportadas

Versión PythonSoportadaNotas
Python 3.9NoFalta asyncio.timeout y typing moderno
Python 3.10Sí (mínimo)match/case, asyncio.timeout disponibles
Python 3.11Mejor performance async, recomendado
Python 3.12Versión actual recomendada
Python 3.13Beta, funciona bien
PyPyNo probadoPueden haber problemas con subprocess async
# Verificar versión de Python
python --version
# o
python3 --version

# Verificar que es 3.10+
python -c "import sys; assert sys.version_info >= (3, 10), 'Se requiere Python 3.10+'; print('Python OK')"

1.3 Versiones de Node.js soportadas

Versión Node.jsSoportadaNotas
Node.js 16NoSin soporte de for await...of con streams modernos
Node.js 18 LTSSí (mínimo)Stable async iterators
Node.js 20 LTSRecomendado para producción
Node.js 22 LTSVersión más nueva, funciona bien
Node.js 23+Experimental, puede tener cambios
DenoNoNo soportado actualmente
BunParcialPuede funcionar pero no es oficial
# Verificar versión de Node.js
node --version

# Verificar que es 18+
node -e "const v = parseInt(process.version.slice(1)); if (v < 18) throw new Error('Node 18+ requerido'); console.log('Node.js OK: ' + process.version)"

1.4 Claude Code CLI: el prerequisito más importante

El SDK no es autónomo: necesita que Claude Code CLI esté instalado en el sistema. El SDK lanza Claude Code como subproceso y se comunica con él via stdin/stdout. Sin Claude Code CLI, el SDK no puede funcionar.

# Verificar que Claude Code está instalado
claude --version

# Si no está instalado, verás:
# zsh: command not found: claude

1.5 Espacio en disco y memoria


2. Instalación de Claude Code CLI

Claude Code CLI es la dependencia fundamental. Sin ella, el SDK no puede funcionar.

2.1 macOS con Homebrew (recomendado)

# Instalar via Homebrew
brew install --cask claude-code

# Verificar instalación
claude --version

# Output esperado:
# Claude Code 1.x.x

2.2 Todas las plataformas via npm

# Instalación global con npm
npm install -g @anthropic-ai/claude-code

# Con pnpm
pnpm add -g @anthropic-ai/claude-code

# Con yarn
yarn global add @anthropic-ai/claude-code

# Con bun (experimental)
bun add -g @anthropic-ai/claude-code

# Verificar
claude --version
which claude  # Debe mostrar la ruta del binario

2.3 macOS / Linux / WSL: script de instalación directa

# Script oficial de instalación
curl -fsSL https://claude.ai/install.sh | bash

# Si prefieres revisar el script antes de ejecutarlo:
curl -fsSL https://claude.ai/install.sh -o install_claude.sh
cat install_claude.sh  # revisa el contenido
bash install_claude.sh

# Agregar al PATH si no se agregó automáticamente
echo 'export PATH="$HOME/.claude/bin:$PATH"' >> ~/.bashrc
# o para zsh:
echo 'export PATH="$HOME/.claude/bin:$PATH"' >> ~/.zshrc
source ~/.bashrc  # o ~/.zshrc

2.4 Windows (PowerShell nativo)

En Windows nativo (sin WSL), la instalación via npm es la opción más confiable:

# Instalar Node.js desde https://nodejs.org si no lo tienes

# Instalar Claude Code
npm install -g @anthropic-ai/claude-code

# Verificar en PowerShell
claude --version

# Configurar la API Key en Windows
$env:ANTHROPIC_API_KEY = "sk-ant-xxxxxxxx"

# Para persistir la variable de entorno (permanente):
[System.Environment]::SetEnvironmentVariable("ANTHROPIC_API_KEY", "sk-ant-xxxxxxxx", "User")

Nota importante sobre Windows nativo: La herramienta Bash del agente tendrá limitaciones en Windows nativo. Para uso completo de herramientas de terminal, se recomienda WSL2.

2.5 Configurar la API Key en Claude Code

La primera vez que ejecutes Claude Code, te pedirá tu API Key de Anthropic:

# Primera ejecución - configuración interactiva
claude

# O configurar directamente via variable de entorno
export ANTHROPIC_API_KEY="sk-ant-xxxxxxxxxxxxxxxxxxxxxxxx"
claude --print "Di hola"

Para obtener tu API Key:

  1. Ve a https://console.anthropic.com/
  2. Inicia sesión o crea cuenta
  3. Ve a “API Keys” en el menú lateral
  4. Haz clic en “Create Key”
  5. Copia la key (solo se muestra una vez)

2.6 Verificar que Claude Code funciona

# Test básico: Claude debe responder sin errores
claude --print "Responde solo con la palabra: OK"

# Output esperado:
# OK

# Si ves errores de autenticación:
# Error: ANTHROPIC_API_KEY not set
# → Configura la variable de entorno

# Si ves errores de red:
# Error: Connection refused
# → Verifica tu conexión a internet o proxy

2.7 Problemas comunes en la instalación del CLI

Error: npm: permission denied al instalar globalmente

# Opción 1: Usar sudo (no recomendado por seguridad)
sudo npm install -g @anthropic-ai/claude-code

# Opción 2: Cambiar el directorio de npm (recomendado)
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
npm install -g @anthropic-ai/claude-code

Error: claude: command not found después de instalar

# Verificar dónde se instaló
npm list -g --depth=0 | grep claude-code
npm bin -g  # Muestra el directorio de binarios globales

# Agregar al PATH manualmente
export PATH="$(npm bin -g):$PATH"
# Para persistir, agregar al ~/.bashrc o ~/.zshrc

Error: GLIBC_2.xx not found en Linux

# Este error ocurre en distribuciones muy antiguas
# Solución 1: Actualizar el sistema
sudo apt update && sudo apt upgrade

# Solución 2: Usar la versión npm que incluye binarios precompilados
# (normalmente resuelto en versiones más nuevas de Claude Code)

3. Instalación del SDK Python

3.1 Instalación básica con pip

# Método más simple
pip install claude-code-sdk

# Verificar la versión instalada
pip show claude-code-sdk
# Output:
# Name: claude-code-sdk
# Version: 0.0.x
# Location: /usr/local/lib/python3.12/site-packages

# Verificar que se puede importar
python -c "from claude_code_sdk import query, ClaudeCodeOptions; print('Importación exitosa')"

3.2 Instalación con uv (recomendado para proyectos nuevos)

uv es un gestor de paquetes Python muy rápido escrito en Rust. Es la opción moderna y recomendada.

# Instalar uv si no lo tienes
curl -LsSf https://astral.sh/uv/install.sh | sh
# o en macOS:
brew install uv

# Crear un nuevo proyecto
uv init mi-agente
cd mi-agente

# Agregar claude-code-sdk al proyecto
uv add claude-code-sdk

# Agregar dependencias de desarrollo
uv add --dev pytest pytest-asyncio ruff mypy

# Ejecutar con uv (maneja automáticamente el entorno virtual)
uv run python src/agente.py

Con uv, el pyproject.toml se actualiza automáticamente:

[project]
name = "mi-agente"
version = "0.1.0"
dependencies = [
    "claude-code-sdk>=0.0.9",
]

3.3 Instalación con Poetry

# Crear nuevo proyecto con Poetry
poetry new mi-agente
cd mi-agente

# Agregar la dependencia
poetry add claude-code-sdk

# Agregar dependencias de desarrollo
poetry add --group dev pytest pytest-asyncio

# Activar el entorno
poetry shell

# O ejecutar directamente
poetry run python src/agente.py

pyproject.toml con Poetry:

[tool.poetry]
name = "mi-agente"
version = "0.1.0"
description = "Agente con Claude Code SDK"
authors = ["Tu Nombre <[email protected]>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
claude-code-sdk = ">=0.0.9"
python-dotenv = ">=1.0.0"

[tool.poetry.group.dev.dependencies]
pytest = ">=7.0"
pytest-asyncio = ">=0.23"
ruff = ">=0.3"
mypy = ">=1.8"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

3.4 Crear entorno virtual manualmente

Para quienes prefieren el control total con venv estándar:

# Crear entorno virtual en el directorio del proyecto
python -m venv .venv

# Activar en macOS/Linux
source .venv/bin/activate

# Activar en Windows PowerShell
.\.venv\Scripts\Activate.ps1

# Activar en Windows CMD
.\.venv\Scripts\activate.bat

# Verificar que estás en el entorno virtual
which python  # debe mostrar .venv/bin/python

# Instalar dependencias
pip install claude-code-sdk python-dotenv

# Guardar dependencias con versiones exactas
pip freeze > requirements.txt

requirements.txt con versiones pinned:

# requirements.txt - versiones pinned para reproducibilidad
claude-code-sdk==0.0.9
anyio==4.3.0
httpx==0.27.0
pydantic==2.6.3
python-dotenv==1.0.1

# Dev dependencies (en requirements-dev.txt separado)
# pytest==8.1.1
# pytest-asyncio==0.23.5
# ruff==0.3.4
# mypy==1.9.0

3.5 Instalación en Docker (Python)

El siguiente Dockerfile crea una imagen con todo lo necesario para ejecutar un agente Python:

# Dockerfile.python
FROM python:3.12-slim

# Instalar Node.js (necesario para Claude Code CLI)
RUN apt-get update && apt-get install -y \
    curl \
    nodejs \
    npm \
    && rm -rf /var/lib/apt/lists/*

# Instalar Claude Code CLI
RUN npm install -g @anthropic-ai/claude-code

# Configurar directorio de trabajo
WORKDIR /app

# Instalar dependencias Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copiar código del agente
COPY src/ ./src/
COPY CLAUDE.md ./

# Variables de entorno (se proveerán en runtime, no en build)
ENV ANTHROPIC_API_KEY=""
ENV PYTHONUNBUFFERED=1

# Ejecutar el agente
CMD ["python", "src/agente.py"]
# Construir la imagen
docker build -f Dockerfile.python -t mi-agente-python .

# Ejecutar con la API key
docker run -e ANTHROPIC_API_KEY=sk-ant-xxx mi-agente-python

# Montar el proyecto para desarrollo
docker run \
  -e ANTHROPIC_API_KEY=sk-ant-xxx \
  -v $(pwd)/src:/app/src \
  mi-agente-python

3.6 Verificar la instalación Python

Script completo de verificación:

#!/usr/bin/env python3
"""verificar_instalacion.py - Verifica que el SDK está correctamente instalado."""

import sys
import os
import asyncio
import subprocess


def verificar_python():
    """Verifica que la versión de Python es compatible."""
    version = sys.version_info
    if version < (3, 10):
        print(f"ERROR: Python {version.major}.{version.minor} no soportado. Se requiere 3.10+")
        return False
    print(f"OK: Python {version.major}.{version.minor}.{version.micro}")
    return True


def verificar_claude_cli():
    """Verifica que Claude Code CLI está instalado y accesible."""
    try:
        result = subprocess.run(
            ["claude", "--version"],
            capture_output=True,
            text=True,
            timeout=10
        )
        if result.returncode == 0:
            print(f"OK: Claude Code CLI instalado: {result.stdout.strip()}")
            return True
        else:
            print(f"ERROR: Claude Code CLI retornó código {result.returncode}")
            return False
    except FileNotFoundError:
        print("ERROR: Claude Code CLI no encontrado. Instala con: npm install -g @anthropic-ai/claude-code")
        return False
    except subprocess.TimeoutExpired:
        print("ERROR: Claude Code CLI tardó demasiado en responder")
        return False


def verificar_api_key():
    """Verifica que la API key está configurada."""
    api_key = os.environ.get("ANTHROPIC_API_KEY", "")
    if not api_key:
        print("ERROR: ANTHROPIC_API_KEY no configurada")
        return False
    if not api_key.startswith("sk-ant-"):
        print(f"ADVERTENCIA: La API key no tiene el formato esperado (sk-ant-...)")
        print(f"  Valor: {api_key[:10]}...")
    else:
        print(f"OK: ANTHROPIC_API_KEY configurada: {api_key[:15]}...")
    return bool(api_key)


def verificar_sdk_import():
    """Verifica que el SDK se puede importar correctamente."""
    try:
        from claude_code_sdk import query, ClaudeCodeOptions
        print("OK: claude_code_sdk importado correctamente")
        return True
    except ImportError as e:
        print(f"ERROR: No se puede importar claude_code_sdk: {e}")
        print("  Instala con: pip install claude-code-sdk")
        return False


async def verificar_funcionamiento():
    """Hace una llamada real al SDK para verificar que funciona end-to-end."""
    try:
        from claude_code_sdk import query, ClaudeCodeOptions

        print("Haciendo llamada de prueba al agente...")
        resultado = None

        async for message in query(
            prompt="Responde únicamente con la frase: VERIFICACION_EXITOSA",
            options=ClaudeCodeOptions(max_turns=3)
        ):
            if hasattr(message, 'result') and message.result:
                resultado = message.result

        if resultado and "VERIFICACION_EXITOSA" in resultado:
            print(f"OK: Agente respondió correctamente: '{resultado.strip()}'")
            return True
        else:
            print(f"ADVERTENCIA: Agente respondió pero no con el texto esperado: '{resultado}'")
            return True  # El agente funcionó aunque no siguió el formato exacto
    except Exception as e:
        print(f"ERROR en verificación de funcionamiento: {type(e).__name__}: {e}")
        return False


async def main():
    print("=" * 50)
    print("VERIFICACIÓN DE INSTALACIÓN - Claude Code SDK")
    print("=" * 50)
    print()

    checks = [
        ("Python Version", verificar_python()),
        ("Claude Code CLI", verificar_claude_cli()),
        ("API Key", verificar_api_key()),
        ("SDK Import", verificar_sdk_import()),
    ]

    todos_ok = all(resultado for _, resultado in checks)

    if todos_ok:
        print()
        funcionamiento = await verificar_funcionamiento()
        checks.append(("Funcionamiento", funcionamiento))

    print()
    print("=" * 50)
    print("RESUMEN")
    print("=" * 50)
    for nombre, resultado in checks:
        estado = "✓" if resultado else "✗"
        print(f"  {estado} {nombre}")

    todos_ok = all(r for _, r in checks)
    print()
    if todos_ok:
        print("La instalación está completa y funcionando correctamente.")
    else:
        print("Hay problemas en la instalación. Revisa los errores arriba.")
        sys.exit(1)


if __name__ == "__main__":
    asyncio.run(main())

4. Instalación del SDK TypeScript/Node.js

4.1 Instalación básica con npm

# Crear directorio del proyecto
mkdir mi-agente-ts && cd mi-agente-ts
npm init -y

# Instalar el SDK
npm install @anthropic-ai/claude-code-sdk

# Verificar instalación
node -e "const sdk = require('@anthropic-ai/claude-code-sdk'); console.log('SDK cargado:', Object.keys(sdk))"

# Verificar versión
node -e "console.log(require('@anthropic-ai/claude-code-sdk/package.json').version)"

4.2 Instalación con pnpm (recomendado para monorepos)

# Instalar pnpm si no lo tienes
npm install -g pnpm

# Crear proyecto
mkdir mi-agente-ts && cd mi-agente-ts
pnpm init

# Instalar SDK
pnpm add @anthropic-ai/claude-code-sdk

# Instalar dependencias de desarrollo
pnpm add -D typescript @types/node tsx vitest

# Verificar
pnpm list @anthropic-ai/claude-code-sdk

4.3 Instalación con Bun

# Instalar Bun si no lo tienes
curl -fsSL https://bun.sh/install | bash

# Crear proyecto
mkdir mi-agente-bun && cd mi-agente-bun
bun init

# Instalar SDK
bun add @anthropic-ai/claude-code-sdk

# Ejecutar directamente con Bun
bun run src/agente.ts

Nota sobre Bun: El SDK no está oficialmente soportado con Bun como runtime. Puede funcionar para casos simples pero puede tener problemas con el manejo de subprocesos.

4.4 ESM vs CommonJS

El SDK usa ES Modules (ESM) de forma nativa. Para TypeScript moderno:

Para proyectos ESM (recomendado):

// package.json
{
  "type": "module",
  "dependencies": {
    "@anthropic-ai/claude-code-sdk": "^0.2.0"
  }
}
// ✓ Importación ESM
import { query, ClaudeCodeOptions } from "@anthropic-ai/claude-code-sdk";

Para proyectos CommonJS (compatibilidad legacy):

// package.json
{
  "type": "commonjs"
}
// El SDK tiene exports para CJS también
const { query } = require("@anthropic-ai/claude-code-sdk");

// Para TypeScript con CJS:
import { query } from "@anthropic-ai/claude-code-sdk";
// (funciona con moduleResolution: "bundler" o "node16")

4.5 Tipos TypeScript disponibles

El SDK exporta los siguientes tipos principales:

import {
  // Función principal
  query,

  // Opciones de configuración
  ClaudeCodeOptions,

  // Tipos de mensajes del stream
  AssistantMessage,
  ResultMessage,
  SystemMessage,

  // Tipos de contenido en mensajes
  TextBlock,
  ToolUseBlock,
  ToolResultBlock,

  // Tipos de error
  CLINotFoundError,
  CLIConnectionError,
  ProcessError,

  // Tipos de permisos
  PermissionMode,

  // Tipo para hooks
  HookDefinition,
} from "@anthropic-ai/claude-code-sdk";

// Ejemplo de uso con tipos estrictos
const opciones: ClaudeCodeOptions = {
  cwd: "/mi/proyecto",
  maxTurns: 20,
  permissionMode: "acceptEdits" as PermissionMode,
};

4.6 Verificar la instalación TypeScript

// verificar-instalacion.ts
import { query, ClaudeCodeOptions } from "@anthropic-ai/claude-code-sdk";
import { execSync } from "child_process";

function verificarCLI(): boolean {
  try {
    const version = execSync("claude --version", { encoding: "utf-8" }).trim();
    console.log(`OK: Claude Code CLI: ${version}`);
    return true;
  } catch {
    console.error("ERROR: Claude Code CLI no encontrado");
    console.error("  Instala con: npm install -g @anthropic-ai/claude-code");
    return false;
  }
}

function verificarApiKey(): boolean {
  const apiKey = process.env.ANTHROPIC_API_KEY;
  if (!apiKey) {
    console.error("ERROR: ANTHROPIC_API_KEY no configurada");
    return false;
  }
  console.log(`OK: ANTHROPIC_API_KEY: ${apiKey.slice(0, 15)}...`);
  return true;
}

async function verificarFuncionamiento(): Promise<boolean> {
  console.log("Haciendo llamada de prueba...");
  try {
    for await (const message of query({
      prompt: "Responde únicamente con: VERIFICACION_EXITOSA",
      options: { maxTurns: 3 } as ClaudeCodeOptions,
    })) {
      if (message.type === "result") {
        if (message.result?.includes("VERIFICACION_EXITOSA")) {
          console.log(`OK: Respuesta correcta: '${message.result.trim()}'`);
        } else {
          console.log(`OK: Agente respondió (sin formato exacto): '${message.result?.slice(0, 50)}'`);
        }
        return true;
      }
    }
    return false;
  } catch (error) {
    console.error(`ERROR: ${error instanceof Error ? error.message : String(error)}`);
    return false;
  }
}

async function main() {
  console.log("=".repeat(50));
  console.log("VERIFICACIÓN - Claude Code SDK (TypeScript)");
  console.log("=".repeat(50));
  console.log();

  const checks: Array<[string, boolean]> = [
    ["Node.js Version", parseInt(process.version.slice(1)) >= 18],
    ["Claude Code CLI", verificarCLI()],
    ["API Key", verificarApiKey()],
  ];

  console.log(`OK: Node.js ${process.version}`);

  const todosOk = checks.every(([, r]) => r);

  if (todosOk) {
    console.log();
    const funciona = await verificarFuncionamiento();
    checks.push(["Funcionamiento", funciona]);
  }

  console.log();
  console.log("=".repeat(50));
  for (const [nombre, resultado] of checks) {
    console.log(`  ${resultado ? "✓" : "✗"} ${nombre}`);
  }

  if (!checks.every(([, r]) => r)) {
    process.exit(1);
  }
}

main().catch(console.error);

5. Configuración de API Key y Secrets

5.1 Métodos de configuración

flowchart TD
    A["¿Cómo configurar ANTHROPIC_API_KEY?"] --> B{"¿Entorno de producción?"}
    B -->|Sí| C["Usar secrets manager<br/>(AWS Secrets, Vault, etc.)"]
    B -->|No| D{"¿Desarrollo local?"}
    D -->|Sí| E["Archivo .env con dotenv"]
    D -->|No| F{"¿CI/CD?"}
    F -->|Sí| G["Variables de entorno del CI<br/>(GitHub Actions, etc.)"]
    F -->|No| H["Variable de entorno del shell"]

    C --> I["NUNCA hardcodear en código"]
    E --> I
    G --> I
    H --> I

5.2 Configuración con archivo .env

Python con python-dotenv:

# Instalar
pip install python-dotenv
# src/agente.py - al inicio del archivo
import os
from dotenv import load_dotenv

# Cargar el .env ANTES de importar el SDK
load_dotenv()

# Ahora el SDK puede acceder a ANTHROPIC_API_KEY
from claude_code_sdk import query, ClaudeCodeOptions

# Verificar que la key está disponible
api_key = os.getenv("ANTHROPIC_API_KEY")
if not api_key:
    raise ValueError("ANTHROPIC_API_KEY no configurada. Copia .env.example a .env y agrega tu key.")

TypeScript con dotenv:

// src/agente.ts - al inicio del archivo
import "dotenv/config"; // Carga .env automáticamente

// Alternativa más explícita:
import { config } from "dotenv";
config(); // Carga .env

// Ahora el SDK puede acceder a ANTHROPIC_API_KEY
import { query, ClaudeCodeOptions } from "@anthropic-ai/claude-code-sdk";

const apiKey = process.env.ANTHROPIC_API_KEY;
if (!apiKey) {
  throw new Error("ANTHROPIC_API_KEY no configurada");
}

Archivo .env:

# .env - NUNCA committear este archivo al repositorio
ANTHROPIC_API_KEY=sk-ant-api03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# Opcionales
CLAUDE_MODEL=claude-sonnet-4-5
MAX_TURNS=20
LOG_LEVEL=info

Archivo .env.example (sí committear):

# .env.example - plantilla para configurar el entorno
# Copia este archivo a .env y completa los valores

# REQUERIDO: Tu API Key de Anthropic
# Obtén una en: https://console.anthropic.com/
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxx

# OPCIONAL: Modelo a usar (default: claude-sonnet-4-5)
# Opciones: claude-opus-4-5, claude-sonnet-4-5, claude-haiku-3-5
CLAUDE_MODEL=claude-sonnet-4-5

# OPCIONAL: Máximo de turnos del agente (default: 20)
MAX_TURNS=20

5.3 Configuración en CI/CD (GitHub Actions)

# .github/workflows/agente.yml
name: Ejecutar Agente de Análisis

on:
  push:
    branches: [main]
  pull_request:

jobs:
  analisis:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js (para Claude Code CLI)
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Instalar Claude Code CLI
        run: npm install -g @anthropic-ai/claude-code

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Instalar dependencias Python
        run: pip install claude-code-sdk python-dotenv

      - name: Ejecutar agente de análisis
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}  # Configurado en GitHub Secrets
        run: python src/agente.py .

5.4 Anti-patrón: hardcodear la API key

# MAL: NUNCA hagas esto
from claude_code_sdk import query, ClaudeCodeOptions

# La API key está expuesta en el código fuente
options = ClaudeCodeOptions(
    # ERROR: Si subes esto a GitHub, tu key queda expuesta públicamente
    api_key="sk-ant-api03-xxxxxxx"  # NUNCA hagas esto
)

# BIEN: Siempre usa variables de entorno
import os
from dotenv import load_dotenv
load_dotenv()

# El SDK lee ANTHROPIC_API_KEY del entorno automáticamente
options = ClaudeCodeOptions(cwd="/mi/proyecto")

6. Configuración Avanzada con ClaudeCodeOptions

ClaudeCodeOptions es la clase principal de configuración del SDK. Aquí están todas las opciones disponibles con explicación detallada.

6.1 Tabla completa de opciones

OpciónTipoDefaultDescripción
cwdstr | PathProcess cwdDirectorio de trabajo del agente
max_turns (Python) / maxTurns (TS)intIlimitadoMáximo de iteraciones del loop
permission_mode (Python) / permissionMode (TS)str"default"Control de permisos de herramientas
system_prompt (Python) / systemPrompt (TS)strNonePrompt de sistema adicional
modelstr"claude-sonnet-4-5"Modelo de Claude a usar
cli_path (Python) / cliPath (TS)str"claude"Ruta al binario de Claude Code
api_key (Python) / apiKey (TS)strEnv varAPI key (mejor usar env var)
mcp_servers (Python) / mcpServers (TS)list[dict][]Servidores MCP a conectar

6.2 permission_mode: explicación detallada

El modo de permisos controla qué acciones puede tomar el agente sin pedir confirmación:

from claude_code_sdk import query, ClaudeCodeOptions

# MODO "default": Claude pide permiso antes de acciones potencialmente peligrosas
# Úsalo cuando: el agente puede interactuar con un humano
options_interactivo = ClaudeCodeOptions(
    permission_mode="default"
    # El agente pausará y pedirá confirmación antes de:
    # - Ejecutar comandos bash que modifican el sistema
    # - Escribir en archivos fuera del cwd
    # - Acciones destructivas
)

# MODO "acceptEdits": acepta automáticamente cambios en archivos
# Úsalo cuando: quieres que el agente modifique código automáticamente
# pero no ejecute comandos arbitrarios
options_edicion = ClaudeCodeOptions(
    permission_mode="acceptEdits"
    # El agente puede: Read, Write, Edit sin confirmación
    # El agente NO puede: Bash (o pedirá permiso)
)

# MODO "bypassPermissions": acepta todo sin preguntar
# Úsalo cuando: automatización de confianza total, scripts internos
# ADVERTENCIA: permite ejecución de comandos bash, borrar archivos, etc.
options_automatico = ClaudeCodeOptions(
    permission_mode="bypassPermissions"
    # El agente puede hacer TODO sin preguntar
    # Usa con extremo cuidado y solo con prompts confiables
)
// Lo mismo en TypeScript
const opcionesDefault: ClaudeCodeOptions = {
  permissionMode: "default",
};

const opcionesEdicion: ClaudeCodeOptions = {
  permissionMode: "acceptEdits",
};

const opcionesAutomatico: ClaudeCodeOptions = {
  permissionMode: "bypassPermissions",
};

6.3 Configuración del modelo

from claude_code_sdk import query, ClaudeCodeOptions

# Modelos disponibles y sus casos de uso:
# claude-opus-4-5     → Tareas complejas, razonamiento profundo, más caro
# claude-sonnet-4-5   → Balance costo/capacidad, uso general (DEFAULT)
# claude-haiku-3-5    → Tareas simples, muy económico, más rápido

# Para análisis de código complejo:
opciones_opus = ClaudeCodeOptions(
    model="claude-opus-4-5",
    max_turns=30
)

# Para automatización de volumen alto:
opciones_haiku = ClaudeCodeOptions(
    model="claude-haiku-3-5",
    max_turns=10
)

# Para uso general (recomendado):
opciones_sonnet = ClaudeCodeOptions(
    model="claude-sonnet-4-5",
    max_turns=20
)

6.4 system_prompt: dar instrucciones permanentes al agente

from claude_code_sdk import query, ClaudeCodeOptions

# El system_prompt se agrega ANTES de cada conversación
# Es ideal para definir el rol, restricciones y convenciones
options = ClaudeCodeOptions(
    cwd="/mi/proyecto",
    system_prompt="""Eres un experto en Python senior trabajando en este proyecto.

RESTRICCIONES:
- Solo trabaja en archivos dentro de src/ y tests/
- NUNCA modifiques pyproject.toml, .env, o archivos de configuración
- NUNCA ejecutes comandos con sudo
- NUNCA instales paquetes nuevos sin pedirme confirmación

CONVENCIONES DEL PROYECTO:
- Usamos Black para formateo (88 caracteres por línea)
- Docstrings en formato Google
- Type hints obligatorios en todas las funciones públicas
- Principios SOLID: funciones de máximo 30 líneas

ANTES DE HACER CAMBIOS:
1. Lee el archivo completo para entender el contexto
2. Busca tests existentes relacionados
3. Verifica que el cambio no rompe la interfaz pública"""
)

async for message in query(
    prompt="Refactoriza src/auth/login.py para mejorar el manejo de errores",
    options=options
):
    # ...
    pass

6.5 Configuración de proxies y entornos corporativos

import os
from claude_code_sdk import query, ClaudeCodeOptions

# Si estás detrás de un proxy corporativo:
os.environ["HTTPS_PROXY"] = "https://proxy.empresa.com:8080"
os.environ["HTTP_PROXY"] = "http://proxy.empresa.com:8080"
os.environ["NO_PROXY"] = "localhost,127.0.0.1"

# El SDK heredará estas variables de entorno al spawnar Claude Code

# Para certificados SSL corporativos:
os.environ["NODE_EXTRA_CA_CERTS"] = "/path/to/corporate-ca.crt"

# Claude Code respeta las variables de entorno estándar de proxy
options = ClaudeCodeOptions(cwd="/mi/proyecto")

6.6 Configuración completa: ejemplo real de producción

"""
configuracion_produccion.py

Configuraciones listas para usar en diferentes contextos de producción.
"""

import os
from pathlib import Path
from claude_code_sdk import ClaudeCodeOptions


MODELO_DEFAULT = os.getenv("CLAUDE_MODEL", "claude-sonnet-4-5")
MAX_TURNS_DEFAULT = int(os.getenv("MAX_TURNS", "20"))


def config_code_review(repo_path: str) -> ClaudeCodeOptions:
    """Para revisiones de código automatizadas en CI/CD."""
    return ClaudeCodeOptions(
        cwd=repo_path,
        max_turns=15,
        permission_mode="bypassPermissions",  # Solo lectura en práctica
        model=MODELO_DEFAULT,
        system_prompt="""Eres un revisor de código experto.
IMPORTANTE: Este agente SOLO debe LEER archivos, NO escribir ni modificar nada.
Analiza el código y genera un reporte sin hacer ningún cambio.""",
    )


def config_refactor_automatico(repo_path: str) -> ClaudeCodeOptions:
    """Para refactoring automático con verificación de tests."""
    return ClaudeCodeOptions(
        cwd=repo_path,
        max_turns=MAX_TURNS_DEFAULT,
        permission_mode="acceptEdits",
        model="claude-opus-4-5",  # Más capaz para refactoring complejo
        system_prompt="""Eres un experto en refactoring de código.
Antes de cualquier cambio: lee el archivo completo y sus tests.
Después de cada cambio: verifica que los tests aún pasan.
Si un cambio puede romper la API pública, documéntalo en un comentario.""",
    )


def config_generacion_documentacion(repo_path: str) -> ClaudeCodeOptions:
    """Para generar documentación automáticamente."""
    return ClaudeCodeOptions(
        cwd=repo_path,
        max_turns=30,
        permission_mode="acceptEdits",
        model=MODELO_DEFAULT,
        system_prompt="""Genera documentación técnica clara y precisa.
Usa Markdown con formato excelente.
Incluye ejemplos de código para cada función pública.
NO expliques el código en voz pasiva; usa voz activa y lenguaje directo.""",
    )
// configuracion-produccion.ts
import { ClaudeCodeOptions } from "@anthropic-ai/claude-code-sdk";

const MODELO_DEFAULT = process.env.CLAUDE_MODEL || "claude-sonnet-4-5";
const MAX_TURNS_DEFAULT = parseInt(process.env.MAX_TURNS || "20");

export const configCodeReview = (repoPath: string): ClaudeCodeOptions => ({
  cwd: repoPath,
  maxTurns: 15,
  permissionMode: "bypassPermissions",
  model: MODELO_DEFAULT,
  systemPrompt: `Eres un revisor de código experto.
IMPORTANTE: Solo LEE archivos, NO escribas ni modifiques nada.
Genera un reporte de code review sin hacer cambios.`,
});

export const configRefactor = (repoPath: string): ClaudeCodeOptions => ({
  cwd: repoPath,
  maxTurns: MAX_TURNS_DEFAULT,
  permissionMode: "acceptEdits",
  model: "claude-opus-4-5",
  systemPrompt: `Eres un experto en refactoring.
Lee el archivo completo antes de cualquier cambio.
Verifica la consistencia con el resto del codebase.`,
});

7. Estructura de Proyecto Recomendada

7.1 Estructura Python completa

mi-agente-python/

├── .env                        # API keys (NO en git)
├── .env.example                # Plantilla (SÍ en git)
├── .gitignore
├── pyproject.toml              # Configuración del proyecto
├── CLAUDE.md                   # Instrucciones para el agente

├── src/
│   └── mi_agente/
│       ├── __init__.py
│       ├── agente.py           # Función principal del agente
│       ├── config.py           # ClaudeCodeOptions reutilizables
│       ├── cli.py              # Interfaz de línea de comandos
│       │
│       ├── hooks/              # Hooks pre/post herramienta
│       │   ├── __init__.py
│       │   ├── auditoria.py    # Logging de todas las acciones
│       │   └── seguridad.py    # Rechazar acciones peligrosas
│       │
│       └── utils/
│           ├── __init__.py
│           └── reportes.py     # Formatear y guardar reportes

└── tests/
    ├── __init__.py
    ├── conftest.py             # Fixtures de pytest
    ├── test_agente.py          # Tests del agente
    └── test_hooks.py           # Tests de hooks

src/mi_agente/__init__.py:

"""Mi Agente - Automatización con Claude Code SDK."""

from .agente import ejecutar_agente
from .config import config_lectura, config_escritura

__all__ = ["ejecutar_agente", "config_lectura", "config_escritura"]
__version__ = "0.1.0"

src/mi_agente/agente.py:

"""Función principal del agente."""

import asyncio
from claude_code_sdk import query, ClaudeCodeOptions
from .config import config_lectura


async def ejecutar_agente(
    prompt: str,
    proyecto_path: str,
    opciones: ClaudeCodeOptions | None = None
) -> str:
    """
    Ejecuta el agente con el prompt dado y retorna el resultado.

    Args:
        prompt: Instrucción para el agente
        proyecto_path: Ruta al proyecto donde trabaja el agente
        opciones: ClaudeCodeOptions personalizado (opcional)

    Returns:
        str: Resultado final del agente
    """
    if opciones is None:
        opciones = config_lectura(proyecto_path)

    resultado_final = ""

    async for message in query(prompt=prompt, options=opciones):
        if hasattr(message, 'result') and message.result:
            resultado_final = message.result

    return resultado_final

7.2 Estructura TypeScript completa

mi-agente-ts/

├── .env
├── .env.example
├── .gitignore
├── package.json
├── tsconfig.json
├── CLAUDE.md

├── src/
│   ├── index.ts               # Entry point / CLI
│   ├── agente.ts              # Función principal
│   ├── config.ts              # ClaudeCodeOptions reutilizables
│   │
│   ├── hooks/
│   │   ├── auditoria.ts       # Logging de acciones
│   │   └── seguridad.ts       # Bloquear acciones peligrosas
│   │
│   └── utils/
│       └── reportes.ts        # Formatear resultados

└── tests/
    ├── agente.test.ts
    └── hooks.test.ts

7.3 .gitignore adecuado

# Secrets y configuración local
.env
.env.local
.env.*.local

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
.venv/
venv/
ENV/
.eggs/
*.egg-info/
dist/
build/

# Node.js
node_modules/
dist/
.next/
.nuxt/

# Claude Code
.claude/
.claude-checkpoints/

# Logs del agente
logs/
*.log
agent-output/

# IDEs
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

8. Primer Agente Verificable

8.1 Python: agente hello world completo

#!/usr/bin/env python3
"""
hello_agent.py

El agente más simple posible: analiza el directorio actual y genera un resumen.
"""

import asyncio
import os
from pathlib import Path
from dotenv import load_dotenv

# PASO 1: Cargar variables de entorno
load_dotenv()

# PASO 2: Verificar prerequisitos
if not os.getenv("ANTHROPIC_API_KEY"):
    print("ERROR: Configura ANTHROPIC_API_KEY en tu .env o variable de entorno")
    exit(1)

from claude_code_sdk import query, ClaudeCodeOptions


async def main():
    # PASO 3: Configurar el agente
    directorio = Path.cwd()
    options = ClaudeCodeOptions(
        cwd=str(directorio),
        max_turns=5,            # Límite corto para el hello world
        permission_mode="bypassPermissions",  # Solo lectura
    )

    print(f"Analizando directorio: {directorio}")
    print("-" * 40)

    # PASO 4: Ejecutar el agente
    mensajes_recibidos = 0
    costo = 0.0

    async for message in query(
        prompt="Mira qué hay en el directorio actual (usa ls o Glob) y dame un resumen de 3 líneas de qué proyecto es este.",
        options=options
    ):
        mensajes_recibidos += 1

        # Ver el "pensamiento" del agente en tiempo real
        if hasattr(message, 'content'):
            for block in message.content:
                if hasattr(block, 'text') and block.text.strip():
                    print(f"Claude: {block.text.strip()[:200]}")

        # El resultado final
        if hasattr(message, 'result'):
            print()
            print("=" * 40)
            print("RESULTADO FINAL:")
            print(message.result)
            costo = getattr(message, 'cost_usd', 0) or 0
            turnos = getattr(message, 'num_turns', 0)
            print()
            print(f"Turnos: {turnos} | Costo: ${costo:.6f} USD")

    print(f"Mensajes totales recibidos: {mensajes_recibidos}")


if __name__ == "__main__":
    asyncio.run(main())

Output esperado al ejecutar:

Analizando directorio: /home/dev/mi-proyecto
----------------------------------------
Claude: Voy a explorar el directorio actual para entender qué proyecto es este.
Claude: He visto los archivos. Es un proyecto TypeScript con Express...

========================================
RESULTADO FINAL:
Este es un proyecto de API REST en TypeScript usando Express.js con PostgreSQL.
Tiene estructura de Clean Architecture con controllers, use cases y repositories.
Incluye tests con Jest y tiene configuración de Docker para desarrollo.

Turnos: 3 | Costo: $0.000234 USD
Mensajes totales recibidos: 7

8.2 TypeScript: agente hello world completo

// hello-agent.ts
import "dotenv/config";
import { query, ClaudeCodeOptions } from "@anthropic-ai/claude-code-sdk";
import { resolve } from "path";

// Verificar prerequisitos
if (!process.env.ANTHROPIC_API_KEY) {
  console.error("ERROR: Configura ANTHROPIC_API_KEY en tu .env");
  process.exit(1);
}

async function main() {
  const directorio = resolve(process.cwd());

  const options: ClaudeCodeOptions = {
    cwd: directorio,
    maxTurns: 5,
    permissionMode: "bypassPermissions",
  };

  console.log(`Analizando directorio: ${directorio}`);
  console.log("-".repeat(40));

  let costo = 0;
  let turnos = 0;

  for await (const message of query({
    prompt: "Mira qué hay en el directorio actual y dame un resumen de 3 líneas de qué proyecto es.",
    options,
  })) {
    // Ver el pensamiento del agente
    if (message.type === "assistant") {
      for (const block of message.message.content) {
        if (block.type === "text" && block.text.trim()) {
          console.log(`Claude: ${block.text.slice(0, 200)}`);
        }
      }
    }

    // Resultado final
    if (message.type === "result") {
      console.log();
      console.log("=".repeat(40));
      console.log("RESULTADO FINAL:");
      console.log(message.result);
      costo = message.cost_usd || 0;
      turnos = message.num_turns;
      console.log();
      console.log(`Turnos: ${turnos} | Costo: $${costo.toFixed(6)} USD`);
    }
  }
}

main().catch(console.error);

9. Configuración de IDE

9.1 Visual Studio Code

Extensiones recomendadas para desarrollo con el SDK:

// .vscode/extensions.json
{
  "recommendations": [
    "ms-python.python",           // Python Language Server
    "ms-python.mypy-type-checker", // Type checking
    "charliermarsh.ruff",          // Linting y formateo Python
    "dbaeumer.vscode-eslint",      // ESLint para TypeScript
    "esbenp.prettier-vscode",      // Formateo TypeScript
    "ms-vscode.vscode-typescript-next", // TypeScript moderno
    "bradlc.vscode-tailwindcss",   // Si usas Tailwind
    "usernamehw.errorlens"         // Errores inline en el código
  ]
}

Configuración de debugging para el agente Python:

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Agente Python",
      "type": "debugpy",
      "request": "launch",
      "program": "${workspaceFolder}/src/agente.py",
      "console": "integratedTerminal",
      "envFile": "${workspaceFolder}/.env",
      "args": ["${workspaceFolder}"],  // Directorio a analizar
      "justMyCode": false  // Para ver el código del SDK también
    },
    {
      "name": "Debug Agente TypeScript",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/src/agente.ts",
      "runtimeExecutable": "tsx",
      "envFile": "${workspaceFolder}/.env",
      "console": "integratedTerminal"
    }
  ]
}

Configuración de settings para el proyecto:

// .vscode/settings.json
{
  "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
  "editor.formatOnSave": true,
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff"
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "editor.codeActionsOnSave": {
    "source.fixAll.ruff": "explicit"
  }
}

9.2 Punto de breakpoint útiles para debugging

# Para debuggear el stream de mensajes del agente:
import asyncio
from claude_code_sdk import query, ClaudeCodeOptions

async def debug_agente():
    async for message in query(prompt="Lista los archivos"):
        # PON UN BREAKPOINT EN ESTA LÍNEA para inspeccionar cada mensaje
        tipo = type(message).__name__
        print(f"Tipo de mensaje: {tipo}")
        print(f"Atributos: {vars(message)}")
        # El debugger se detendrá aquí para cada mensaje del stream

asyncio.run(debug_agente())

10. Troubleshooting de Instalación

10.1 Tabla de errores comunes y soluciones

ErrorCausaSolución
CLINotFoundErrorClaude Code CLI no instaladonpm install -g @anthropic-ai/claude-code
ANTHROPIC_API_KEY not setVariable de entorno no configuradaExportar la variable o crear .env
Invalid API keyKey incorrecta o expiradaGenerar nueva key en console.anthropic.com
Connection refusedSin conexión a internet o proxyVerificar red, configurar proxy
ModuleNotFoundError: claude_code_sdkSDK Python no instaladopip install claude-code-sdk
Cannot find module '@anthropic-ai/claude-code-sdk'SDK TS no instaladonpm install @anthropic-ai/claude-code-sdk
Permission denied executing claudePermisos del binariochmod +x $(which claude)
TimeoutErrorTarea muy largaAumentar timeout o dividir en subtareas
ProcessError: non-zero exitError interno del agenteVer mensaje de error, puede ser problema de API key
Python 3.9 not supportedVersión antiguaActualizar a Python 3.10+

10.2 Error: CLINotFoundError

import asyncio
from claude_code_sdk import query, ClaudeCodeOptions
from claude_code_sdk.errors import CLINotFoundError  # Importar el error específico

async def main():
    try:
        async for message in query(prompt="Hola"):
            pass
    except CLINotFoundError as e:
        print(f"Claude Code CLI no encontrado: {e}")
        print()
        print("Soluciones:")
        print("  1. npm install -g @anthropic-ai/claude-code")
        print("  2. brew install --cask claude-code  (macOS)")
        print("  3. Verifica que 'claude' está en el PATH: which claude")
        print()
        print("Si está instalado pero no se encuentra:")
        print("  export PATH=$(npm bin -g):$PATH")

Para un cli_path personalizado:

import shutil
from claude_code_sdk import ClaudeCodeOptions

# Si tienes Claude Code en una ruta no estándar
ruta_claude = shutil.which("claude") or "/ruta/alternativa/claude"

options = ClaudeCodeOptions(
    cli_path=ruta_claude
)

10.3 Error: API Key inválida

# Verificar que la variable está configurada
echo $ANTHROPIC_API_KEY

# Verificar el formato correcto (debe empezar con sk-ant-)
echo $ANTHROPIC_API_KEY | grep -q "^sk-ant-" && echo "Formato OK" || echo "Formato incorrecto"

# Probar la API key directamente con curl
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{"model": "claude-haiku-3-5", "max_tokens": 10, "messages": [{"role": "user", "content": "Hi"}]}'

# Si la key es válida, verás una respuesta JSON con el mensaje
# Si es inválida, verás: {"type":"error","error":{"type":"authentication_error",...}}

10.4 Error: Connection / Timeout

import asyncio
import os
from claude_code_sdk import query, ClaudeCodeOptions

async def query_con_retry(prompt: str, max_intentos: int = 3) -> str:
    """Wrapper con retry automático para errores de conexión."""
    from claude_code_sdk.errors import CLIConnectionError

    for intento in range(1, max_intentos + 1):
        try:
            resultado = ""
            async for message in query(prompt=prompt):
                if hasattr(message, 'result'):
                    resultado = message.result or ""
            return resultado

        except CLIConnectionError as e:
            if intento == max_intentos:
                raise
            print(f"Error de conexión (intento {intento}/{max_intentos}): {e}")
            print(f"Reintentando en {intento * 2} segundos...")
            await asyncio.sleep(intento * 2)

    return ""

10.5 Error: ProcessError

import asyncio
from claude_code_sdk import query
from claude_code_sdk.errors import ProcessError

async def main():
    try:
        async for message in query(prompt="Hola"):
            pass
    except ProcessError as e:
        print(f"Error del proceso Claude: {e}")
        print(f"Código de salida: {e.returncode}")
        print(f"Stderr: {e.stderr}")

        # Causas comunes:
        if "ANTHROPIC_API_KEY" in str(e):
            print("→ Configura tu API key")
        elif "rate limit" in str(e).lower():
            print("→ Excediste el rate limit. Espera unos segundos.")
        elif "context length" in str(e).lower():
            print("→ El contexto es muy grande. Reduce el scope del agente.")

10.6 Diagnóstico completo del entorno

#!/bin/bash
# diagnostico.sh - Script de diagnóstico completo

echo "=== DIAGNÓSTICO CLAUDE CODE SDK ==="
echo

echo "1. Sistema operativo:"
uname -a

echo
echo "2. Python:"
python --version 2>/dev/null || python3 --version 2>/dev/null || echo "  Python no encontrado"

echo
echo "3. Node.js:"
node --version 2>/dev/null || echo "  Node.js no encontrado"

echo
echo "4. npm:"
npm --version 2>/dev/null || echo "  npm no encontrado"

echo
echo "5. Claude Code CLI:"
claude --version 2>/dev/null || echo "  Claude Code CLI no encontrado (ejecuta: npm install -g @anthropic-ai/claude-code)"
which claude 2>/dev/null || echo "  claude no está en PATH"

echo
echo "6. ANTHROPIC_API_KEY:"
if [ -n "$ANTHROPIC_API_KEY" ]; then
    echo "  Configurada: ${ANTHROPIC_API_KEY:0:15}..."
else
    echo "  NO configurada"
fi

echo
echo "7. claude-code-sdk Python:"
pip show claude-code-sdk 2>/dev/null | grep "Version" || echo "  No instalado (ejecuta: pip install claude-code-sdk)"

echo
echo "8. @anthropic-ai/claude-code-sdk:"
npm list @anthropic-ai/claude-code-sdk 2>/dev/null | grep "claude-code-sdk" || echo "  No instalado en este directorio"

echo
echo "=== FIN DIAGNÓSTICO ==="
# Ejecutar el script de diagnóstico
chmod +x diagnostico.sh
./diagnostico.sh

11. Uso con Docker y Contenedores

11.1 Dockerfile Python de producción

# Dockerfile.python-produccion
# Imagen optimizada para producción

# Etapa 1: Builder
FROM python:3.12-slim AS builder

WORKDIR /app

# Instalar dependencias de build
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt


# Etapa 2: Runtime
FROM python:3.12-slim AS runtime

# Instalar Node.js y npm (necesario para Claude Code CLI)
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    ca-certificates \
    && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
    && apt-get install -y --no-install-recommends nodejs \
    && rm -rf /var/lib/apt/lists/*

# Instalar Claude Code CLI
RUN npm install -g @anthropic-ai/claude-code

# Copiar paquetes Python instalados desde builder
COPY --from=builder /root/.local /root/.local

WORKDIR /app

# Copiar código de la aplicación
COPY src/ ./src/
COPY CLAUDE.md ./

# Usuario no-root por seguridad
RUN useradd -m -u 1000 agente && chown -R agente:agente /app
USER agente

# Variables de entorno
ENV PATH=/root/.local/bin:$PATH
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

# La API key se provee en runtime, no en build
ENV ANTHROPIC_API_KEY=""

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD python -c "import claude_code_sdk; print('OK')" || exit 1

CMD ["python", "src/agente.py"]

11.2 Dockerfile Node.js de producción

# Dockerfile.nodejs-produccion
FROM node:20-slim AS builder

WORKDIR /app

# Instalar dependencias
COPY package*.json tsconfig.json ./
RUN npm ci

# Compilar TypeScript
COPY src/ ./src/
RUN npm run build


FROM node:20-slim AS runtime

WORKDIR /app

# Instalar Claude Code CLI globalmente
RUN npm install -g @anthropic-ai/claude-code

# Copiar solo los archivos necesarios para producción
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./
COPY CLAUDE.md ./

# Usuario no-root
RUN useradd -m -u 1000 agente && chown -R agente:agente /app
USER agente

ENV NODE_ENV=production
ENV ANTHROPIC_API_KEY=""

HEALTHCHECK --interval=30s --timeout=10s \
    CMD node -e "require('@anthropic-ai/claude-code-sdk'); process.exit(0)" || exit 1

CMD ["node", "dist/index.js"]

11.3 docker-compose.yml para desarrollo

# docker-compose.yml
version: "3.9"

services:
  agente-python:
    build:
      context: .
      dockerfile: Dockerfile.python-produccion
    environment:
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
      - CLAUDE_MODEL=${CLAUDE_MODEL:-claude-sonnet-4-5}
      - MAX_TURNS=${MAX_TURNS:-20}
    volumes:
      # Montar el proyecto a analizar (solo lectura)
      - ${PROJECT_PATH:-./proyecto}:/proyecto:ro
      # Montar código del agente para desarrollo (lectura-escritura)
      - ./src:/app/src
      # Guardar outputs del agente
      - ./output:/app/output
    working_dir: /app
    restart: unless-stopped

  agente-typescript:
    build:
      context: .
      dockerfile: Dockerfile.nodejs-produccion
    environment:
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
      - NODE_ENV=production
    volumes:
      - ${PROJECT_PATH:-./proyecto}:/proyecto:ro
      - ./output:/app/output
    restart: unless-stopped
# Uso del docker-compose
export ANTHROPIC_API_KEY=sk-ant-xxxxxx
export PROJECT_PATH=/ruta/a/mi/proyecto

# Iniciar los servicios
docker-compose up -d

# Ver logs del agente
docker-compose logs -f agente-python

# Detener
docker-compose down

11.4 Consideraciones de seguridad en Docker

Cuando ejecutas el SDK en Docker, el agente tiene acceso al filesystem del contenedor. Para limitar el alcance:

# docker-compose.yml con restricciones de seguridad
services:
  agente-seguro:
    build: .
    environment:
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
    volumes:
      # SOLO montar el directorio específico que el agente necesita
      - ${PROJECT_PATH}:/proyecto:ro  # Solo lectura
    # Limitar recursos
    deploy:
      resources:
        limits:
          cpus: "2"
          memory: 2G
    # Security options
    security_opt:
      - no-new-privileges:true
    read_only: false  # El agente puede necesitar escribir en /tmp
    tmpfs:
      - /tmp  # Solo escritura en memoria temporal

Anterior: Capítulo 1: Introducción al Claude Code SDK Siguiente: Capítulo 3: Query Básico y Tipos de Mensajes