diff --git a/.claude/skills/init-jupyter/setup-jupyter.sh b/.claude/skills/init-jupyter/setup-jupyter.sh new file mode 100755 index 0000000..ff43600 --- /dev/null +++ b/.claude/skills/init-jupyter/setup-jupyter.sh @@ -0,0 +1,397 @@ +#!/bin/bash +# setup-jupyter.sh - Automatiza la configuración de Jupyter + MCP para Claude +# Uso: ./setup-jupyter.sh [ruta-proyecto] +# Generado para el skill /init-jupyter + +set -e + +# Colores para output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# ═══════════════════════════════════════════════════════════════ +# FUNCIONES AUXILIARES +# ═══════════════════════════════════════════════════════════════ + +log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } +log_ok() { echo -e "${GREEN}[OK]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +find_free_port() { + for port in 8888 8889 8890 8891 8892 8893 8894 8895 8896 8897 8898 8899; do + if ! lsof -i:$port >/dev/null 2>&1; then + echo $port + return + fi + done + echo 8888 +} + +# ═══════════════════════════════════════════════════════════════ +# PASO 0: CAMBIAR AL DIRECTORIO DEL PROYECTO +# ═══════════════════════════════════════════════════════════════ + +RUTA="${1:-.}" +cd "$RUTA" || { log_error "No se pudo acceder a: $RUTA"; exit 1; } +PROJECT_PATH=$(pwd) + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " SETUP JUPYTER + MCP para Claude" +echo " Proyecto: $PROJECT_PATH" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +# ═══════════════════════════════════════════════════════════════ +# PASO 1: VERIFICACIONES DE ESTADO +# ═══════════════════════════════════════════════════════════════ + +log_info "Verificando estado actual..." + +# Variables de estado +MCP_LOCAL=false +REPO_CONFIG=false +JUPYTER_RUNNING=false +RULES_OK=false +GLOBAL_MCP=false +CURRENT_PORT="" + +# 1. MCP instalado localmente +if [ -f .venv/bin/jupyter-mcp-server ]; then + MCP_LOCAL=true + log_ok "MCP instalado en .venv/bin/" +else + log_warn "MCP no instalado localmente" +fi + +# 2. Repo configurado (.mcp.json con jupyter) +if [ -f .mcp.json ] && grep -q "jupyter" .mcp.json 2>/dev/null; then + REPO_CONFIG=true + log_ok "Repo configurado (.mcp.json tiene jupyter)" +else + log_warn "Repo no configurado para Jupyter" +fi + +# 3. Jupyter corriendo en este repo +if pgrep -af "jupyter" 2>/dev/null | grep -q "$PROJECT_PATH"; then + JUPYTER_RUNNING=true + CURRENT_PORT=$(pgrep -af "jupyter" | grep "$PROJECT_PATH" | grep -oP 'port[= ]\K\d+' | head -1) + [ -z "$CURRENT_PORT" ] && CURRENT_PORT=$(cat .jupyter-port 2>/dev/null || echo "desconocido") + log_ok "Jupyter corriendo en puerto $CURRENT_PORT" +else + log_warn "Jupyter no está corriendo" +fi + +# 4. Reglas en CLAUDE.md +if [ -f .claude/CLAUDE.md ] && grep -q "JUPYTER HABILITADO" .claude/CLAUDE.md 2>/dev/null; then + RULES_OK=true + log_ok "Reglas Jupyter en CLAUDE.md" +else + log_warn "Reglas Jupyter no configuradas" +fi + +# 5. Instalación global (problemática) +if [ -f ~/.local/bin/jupyter-mcp-server ]; then + GLOBAL_MCP=true + log_warn "Existe instalación GLOBAL de jupyter-mcp-server (se eliminará)" +fi + +echo "" + +# ═══════════════════════════════════════════════════════════════ +# DECISIÓN: ¿Ya está todo configurado? +# ═══════════════════════════════════════════════════════════════ + +if $MCP_LOCAL && $REPO_CONFIG && $RULES_OK; then + if $JUPYTER_RUNNING; then + echo "═══════════════════════════════════════════════════════════════" + log_ok "TODO LISTO - Jupyter configurado y corriendo en puerto $CURRENT_PORT" + echo "═══════════════════════════════════════════════════════════════" + echo "" + echo "STATUS: READY" + echo "PORT: $CURRENT_PORT" + exit 0 + else + echo "═══════════════════════════════════════════════════════════════" + log_ok "CONFIGURADO - Solo falta iniciar Jupyter" + echo "═══════════════════════════════════════════════════════════════" + echo "" + echo "Ejecuta en otra terminal:" + echo " cd $PROJECT_PATH && ./run-jupyter-lab.sh" + echo "" + echo "STATUS: CONFIGURED_NOT_RUNNING" + exit 0 + fi +fi + +# ═══════════════════════════════════════════════════════════════ +# PASO 2: ELIMINAR INSTALACIÓN GLOBAL SI EXISTE +# ═══════════════════════════════════════════════════════════════ + +if $GLOBAL_MCP; then + log_info "Eliminando instalación global de jupyter-mcp-server..." + uv tool uninstall jupyter-mcp-server 2>/dev/null || true + pip uninstall -y jupyter-mcp-server 2>/dev/null || true + rm -f ~/.local/bin/jupyter-mcp-server + + if [ -f ~/.local/bin/jupyter-mcp-server ]; then + log_error "No se pudo eliminar instalación global" + else + log_ok "Instalación global eliminada" + fi +fi + +# ═══════════════════════════════════════════════════════════════ +# PASO 3: DETECTAR PUERTO LIBRE +# ═══════════════════════════════════════════════════════════════ + +PUERTO=$(find_free_port) +log_info "Puerto asignado: $PUERTO" + +# ═══════════════════════════════════════════════════════════════ +# PASO 4: CREAR ESTRUCTURA DE CARPETAS +# ═══════════════════════════════════════════════════════════════ + +log_info "Creando estructura de carpetas..." +mkdir -p .claude notebooks +log_ok "Carpetas .claude/ y notebooks/ creadas" + +# ═══════════════════════════════════════════════════════════════ +# PASO 5: CREAR run-jupyter-lab.sh +# ═══════════════════════════════════════════════════════════════ + +log_info "Creando run-jupyter-lab.sh..." + +cat > run-jupyter-lab.sh << 'SCRIPT' +#!/bin/bash +# Jupyter Lab para colaboración Claude + Usuario +# Generado por /init-jupyter +# REQUIERE: jupyter-collaboration instalado para que funcione el MCP + +# Autodetectar puerto libre +find_free_port() { + for port in 8888 8889 8890 8891 8892 8893 8894 8895 8896 8897 8898 8899; do + if ! lsof -i:$port >/dev/null 2>&1; then + echo $port + return + fi + done + echo 8888 +} + +PORT=${1:-$(find_free_port)} + +# Guardar puerto para que Claude lo lea +echo $PORT > .jupyter-port + +# Activar entorno si existe +source .venv/bin/activate 2>/dev/null || true + +# Verificar que jupyter-collaboration está instalado +if ! python -c "import jupyter_collaboration" 2>/dev/null; then + echo "ERROR: jupyter-collaboration no está instalado" + echo "Instala con: uv add jupyter-collaboration" + exit 1 +fi + +echo "══════════════════════════════════════════════════════" +echo " Jupyter Lab + Colaboración iniciando en puerto $PORT" +echo "══════════════════════════════════════════════════════" +echo "" +echo " Abre: http://localhost:$PORT" +echo "" +echo " Claude se conectará al kernel cuando abras un notebook" +echo " Colaboración en tiempo real ACTIVADA" +echo " Usa Ctrl+C para detener" +echo "" +echo "══════════════════════════════════════════════════════" + +jupyter lab \ + --port=$PORT \ + --no-browser \ + --ServerApp.token='' \ + --ServerApp.password='' \ + --ServerApp.disable_check_xsrf=True \ + --ServerApp.allow_origin='*' \ + --ServerApp.root_dir="$(pwd)" \ + --YDocExtension.ystore_class='ypy_websocket.ystore.TempFileYStore' \ + --collaborative +SCRIPT + +chmod +x run-jupyter-lab.sh +log_ok "run-jupyter-lab.sh creado" + +# ═══════════════════════════════════════════════════════════════ +# PASO 6: CREAR/ACTUALIZAR .mcp.json +# ═══════════════════════════════════════════════════════════════ + +log_info "Configurando .mcp.json..." + +# Crear config temporal +cat > /tmp/jupyter-mcp.json << EOF +{ + "mcpServers": { + "jupyter": { + "command": "${PROJECT_PATH}/.venv/bin/jupyter-mcp-server", + "args": [ + "--runtime-url", "http://localhost:${PUERTO}", + "--start-new-runtime", "false" + ] + } + } +} +EOF + +# Merge o crear +if [ -f .mcp.json ]; then + if command -v jq >/dev/null 2>&1; then + jq -s '.[0] * .[1] | .mcpServers = (.[0].mcpServers // {}) * (.[1].mcpServers // {})' \ + .mcp.json /tmp/jupyter-mcp.json > .mcp.json.tmp + mv .mcp.json.tmp .mcp.json + log_ok ".mcp.json actualizado (merge)" + else + log_warn "jq no disponible, sobrescribiendo .mcp.json" + mv /tmp/jupyter-mcp.json .mcp.json + fi +else + mv /tmp/jupyter-mcp.json .mcp.json + log_ok ".mcp.json creado" +fi + +rm -f /tmp/jupyter-mcp.json + +# ═══════════════════════════════════════════════════════════════ +# PASO 7: ACTUALIZAR .claude/CLAUDE.md +# ═══════════════════════════════════════════════════════════════ + +log_info "Configurando reglas en .claude/CLAUDE.md..." + +JUPYTER_RULES='# JUPYTER HABILITADO EN ESTE REPO + +Este repositorio está configurado para exploración de datos con Jupyter + Claude. + +## Estado del entorno +- **Lanzador**: `./run-jupyter-lab.sh` +- **Puerto config**: ver `.jupyter-port` o `.mcp.json` +- **MCP**: jupyter-mcp-server configurado + +## Reglas OBLIGATORIAS para Claude + +### 1. SIEMPRE usar MCP jupyter para ejecutar código Python +- Las ejecuciones se ven en tiempo real en Jupyter Lab del usuario +- Compartimos variables y estado del kernel +- **NUNCA usar bash para ejecutar Python en este repo** + +### 2. Verificar Jupyter activo ANTES de ejecutar +```bash +pgrep -af "jupyter" | grep "$(pwd)" || cat .jupyter-port 2>/dev/null +``` +- Si no está activo → pedir al usuario: "Ejecuta `./run-jupyter-lab.sh` en otra terminal" + +### 3. Gestión de notebooks +- Si un notebook tiene >50 celdas → crear uno nuevo +- Nombrar descriptivamente: `01_exploracion.ipynb`, `02_limpieza.ipynb` +- Mantener notebooks enfocados en una tarea + +### 4. Antes de código pesado +- Avisar al usuario +- Usar `%%time` o `tqdm` para progreso + +--- + +' + +if [ -f .claude/CLAUDE.md ]; then + if ! grep -q "JUPYTER HABILITADO" .claude/CLAUDE.md; then + # Prepend + echo "$JUPYTER_RULES" > .claude/CLAUDE.md.tmp + cat .claude/CLAUDE.md >> .claude/CLAUDE.md.tmp + mv .claude/CLAUDE.md.tmp .claude/CLAUDE.md + log_ok "Reglas añadidas al principio de CLAUDE.md" + else + log_ok "Reglas ya existían en CLAUDE.md" + fi +else + echo "$JUPYTER_RULES" > .claude/CLAUDE.md + log_ok "CLAUDE.md creado con reglas" +fi + +# ═══════════════════════════════════════════════════════════════ +# PASO 8: INICIALIZAR ENTORNO PYTHON E INSTALAR DEPENDENCIAS +# ═══════════════════════════════════════════════════════════════ + +log_info "Configurando entorno Python..." + +# Crear venv si no existe +if [ ! -d .venv ]; then + log_info "Creando venv..." + uv venv 2>/dev/null || python -m venv .venv +fi + +# Activar venv +source .venv/bin/activate 2>/dev/null || true + +# Instalar dependencias +log_info "Instalando dependencias (jupyter, mcp, colaboración)..." +if command -v uv >/dev/null 2>&1; then + # Inicializar pyproject.toml si no existe + [ ! -f pyproject.toml ] && uv init 2>/dev/null || true + uv add jupyter jupyterlab jupyter-collaboration jupyter-mcp-server pandas numpy matplotlib 2>/dev/null || \ + pip install jupyter jupyterlab jupyter-collaboration jupyter-mcp-server pandas numpy matplotlib +else + pip install jupyter jupyterlab jupyter-collaboration jupyter-mcp-server pandas numpy matplotlib +fi + +# Verificar instalación +echo "" +if [ -f .venv/bin/jupyter-mcp-server ]; then + log_ok "jupyter-mcp-server instalado en venv local" +else + log_error "jupyter-mcp-server NO instalado" +fi + +if python -c "import jupyter_collaboration" 2>/dev/null; then + log_ok "jupyter-collaboration instalado" +else + log_warn "jupyter-collaboration no instalado" +fi + +# ═══════════════════════════════════════════════════════════════ +# RESUMEN FINAL +# ═══════════════════════════════════════════════════════════════ + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " CONFIGURACIÓN COMPLETADA" +echo "═══════════════════════════════════════════════════════════════" +echo "" +echo " Proyecto: $PROJECT_PATH" +echo " Puerto: $PUERTO" +echo "" +echo " Archivos creados/actualizados:" +echo " ✓ run-jupyter-lab.sh" +echo " ✓ .mcp.json" +echo " ✓ .claude/CLAUDE.md" +echo " ✓ .venv/bin/jupyter-mcp-server" +echo "" +echo " ┌─────────────────────────────────────────────────────────┐" +echo " │ PASOS SIGUIENTES: │" +echo " │ │" +echo " │ 1. SALIR de Claude Code: /exit o Ctrl+C │" +echo " │ (necesario para cargar el nuevo MCP) │" +echo " │ │" +echo " │ 2. En OTRA terminal: │" +echo " │ cd $PROJECT_PATH && ./run-jupyter-lab.sh │" +echo " │ │" +echo " │ 3. Volver a entrar a Claude: │" +echo " │ cd $PROJECT_PATH && claude │" +echo " │ │" +echo " │ 4. Abrir en navegador: http://localhost:$PUERTO │" +echo " └─────────────────────────────────────────────────────────┘" +echo "" +echo "STATUS: CONFIGURED" +echo "PORT: $PUERTO"