diff --git a/.claude/skills/init-jupyter/SKILL.md b/.claude/skills/init-jupyter/SKILL.md index 128685e..d5fd749 100644 --- a/.claude/skills/init-jupyter/SKILL.md +++ b/.claude/skills/init-jupyter/SKILL.md @@ -4,431 +4,55 @@ description: Inicializa entorno Jupyter para exploración de datos con MCP compa argument-hint: [ruta-proyecto] disable-model-invocation: true user-invocable: true -allowed-tools: Bash, Read, Write, Edit, Glob +allowed-tools: Bash, Read --- # Inicializar Entorno Jupyter para Data Science Skill para preparar cualquier repo para exploración de datos con Jupyter + Claude colaborando en kernel compartido. -## ⚠️ REGLA CRÍTICA: MCP SIEMPRE LOCAL +## Instrucciones para el agente -**NUNCA instalar jupyter-mcp-server globalmente.** Siempre: -- Instalar en `.venv/bin/` del proyecto con `uv add jupyter-mcp-server` -- Usar ruta ABSOLUTA en `.claude/settings.local.json` -- Si existe instalación global (`~/.local/bin/jupyter-mcp-server`), eliminarla con `uv tool uninstall jupyter-mcp-server` +**Todo el trabajo está automatizado en `setup-jupyter.sh`.** Solo ejecuta el script y comunica el resultado. -## PASO 0: Verificar estado actual del repo - -**Ejecutar estas comprobaciones primero:** +### Paso único: Ejecutar el script ```bash -# 1. ¿MCP instalado en el venv LOCAL del proyecto? -[ -f .venv/bin/jupyter-mcp-server ] && echo "MCP_OK" || echo "MCP_NO" +# Obtener ruta del script (está junto a este SKILL.md) +SKILL_DIR="$HOME/DataProyects/repo_Claude/.claude/skills/init-jupyter" -# 2. ¿Repo ya configurado para Jupyter? -[ -f .claude/settings.local.json ] && grep -q "jupyter" .claude/settings.local.json && echo "REPO_CONFIG_OK" || echo "REPO_NO_CONFIG" - -# 3. ¿Jupyter corriendo en este repo? -pgrep -af "jupyter" | grep "$(pwd)" && echo "JUPYTER_RUNNING" || echo "JUPYTER_STOPPED" - -# 4. ¿CLAUDE.md tiene reglas Jupyter? -[ -f .claude/CLAUDE.md ] && grep -q "JUPYTER HABILITADO" .claude/CLAUDE.md && echo "RULES_OK" || echo "RULES_NO" - -# 5. ¿Puerto guardado está sincronizado con Jupyter real? -if [ -f .jupyter-port ]; then - PORT=$(cat .jupyter-port) - if lsof -i:$PORT 2>/dev/null | grep -q jupyter; then - echo "PORT_SYNC_OK" - else - echo "PORT_DESYNC (archivo dice $PORT pero no hay Jupyter ahí)" - fi -fi - -# 6. ¿Existe instalación GLOBAL de jupyter-mcp-server? (problemática) -[ -f ~/.local/bin/jupyter-mcp-server ] && echo "GLOBAL_MCP_EXISTS (eliminar)" || echo "NO_GLOBAL_MCP" +# Ejecutar con la ruta del proyecto (argumento del skill o directorio actual) +bash "$SKILL_DIR/setup-jupyter.sh" "${1:-.}" ``` -### Resultado: Instalación GLOBAL detectada (GLOBAL_MCP_EXISTS) +### Interpretar el resultado -**Eliminar inmediatamente para evitar conflictos:** +El script imprime una línea `STATUS:` al final: -```bash -# Eliminar instalación global -uv tool uninstall jupyter-mcp-server 2>/dev/null || pip uninstall -y jupyter-mcp-server 2>/dev/null -rm -f ~/.local/bin/jupyter-mcp-server +| STATUS | Significado | Qué decir al usuario | +|--------|-------------|---------------------| +| `READY` | Todo configurado y Jupyter corriendo | "Jupyter ya está listo. Puedes empezar a trabajar con notebooks via MCP." | +| `CONFIGURED_NOT_RUNNING` | Configurado pero Jupyter no corre | "Todo configurado. Ejecuta `./run-jupyter-lab.sh` en otra terminal para iniciar Jupyter." | +| `CONFIGURED` | Recién configurado | Mostrar los pasos siguientes que imprime el script | -# Verificar eliminación -[ -f ~/.local/bin/jupyter-mcp-server ] && echo "ERROR: No se pudo eliminar" || echo "Instalación global eliminada OK" -``` +### Si hay errores -Continuar con instalación local. +- Si el script falla, leer el error y ayudar al usuario +- Problemas comunes: + - `jq` no instalado → el merge de `.mcp.json` puede fallar + - `uv` no disponible → usa pip como fallback + - Puerto ocupado → el script autodetecta otro -### Resultado: MCP NO instalado +## Archivos que crea el script -**IMPORTANTE: NUNCA instalar jupyter-mcp-server globalmente. Siempre en el venv local.** +| Archivo | Propósito | +|---------|-----------| +| `run-jupyter-lab.sh` | Lanzador de Jupyter con autodetección de puerto | +| `.mcp.json` | Config MCP con ruta absoluta al ejecutable local | +| `.claude/CLAUDE.md` | Reglas para que Claude use MCP en lugar de bash | +| `.jupyter-port` | Puerto actual (creado al ejecutar Jupyter) | -```bash -# Crear venv si no existe -[ -d .venv ] || uv venv +## Regla crítica: MCP siempre local -# Instalar MCP en el venv local del proyecto -source .venv/bin/activate -uv add jupyter-mcp-server || pip install jupyter-mcp-server - -# Verificar instalación local exitosa -[ -f .venv/bin/jupyter-mcp-server ] && echo "MCP instalado localmente OK" || echo "ERROR: MCP no instalado" -``` - -Continuar con los siguientes pasos para configurar el MCP local. - -### Resultado: Repo YA configurado + Jupyter corriendo - -Informar: -``` -Este repo ya tiene Jupyter configurado y está corriendo. -Puedes empezar a trabajar directamente con notebooks via MCP. -``` -**DETENER AQUÍ** (no hace falta reconfigurar) - -### Resultado: Repo YA configurado + Jupyter NO corriendo - -Informar: -``` -Este repo tiene Jupyter configurado pero no está corriendo. -Ejecuta ./run-jupyter-lab.sh en otra terminal para iniciarlo. -``` -**DETENER AQUÍ** - -### Resultado: Puerto DESINCRONIZADO (PORT_DESYNC) - -El archivo `.jupyter-port` dice un puerto pero Jupyter no está corriendo ahí. - -**Opciones:** -1. Si Jupyter está corriendo en otro puerto → actualizar `.jupyter-port` y `.claude/settings.local.json` -2. Si Jupyter no está corriendo → ejecutar `./run-jupyter-lab.sh` (actualizará el puerto automáticamente) - -```bash -# Buscar en qué puerto está Jupyter realmente -pgrep -af "jupyter" | grep -oP 'port[= ]\K\d+' || echo "Jupyter no está corriendo" -``` - -### Resultado: Repo NO configurado → Continuar con PASO 1 - ---- - -## PASO 1: Detectar ruta de trabajo - -```bash -RUTA="${1:-.}" -cd "$RUTA" -pwd -``` - ---- - -## PASO 2: Verificar si Jupyter ya corre en ESTE repo - -```bash -# Buscar proceso jupyter con el directorio actual -REPO_PATH=$(pwd) -pgrep -af "jupyter" | grep -E "(lab|notebook)" | grep "$REPO_PATH" || echo "NO_JUPYTER" -``` - -- Si encuentra proceso → Informar "Jupyter ya está corriendo en este repo" y saltar a Paso 6 -- Si NO encuentra → Continuar - ---- - -## PASO 3: Detectar puerto libre (autodetección) - -```bash -# Encontrar primer puerto libre entre 8888-8899 -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 - break - fi -done -``` - -Guardar el puerto detectado para usarlo en los siguientes pasos. - ---- - -## PASO 4: Crear/Actualizar archivos del proyecto - -### 4a. Crear estructura si no existe - -```bash -mkdir -p .claude notebooks -``` - -### 4b. Crear `run-jupyter-lab.sh` con puerto autodetectado - -Crear el archivo con el PUERTO detectado en Paso 3: - -```bash -cat > run-jupyter-lab.sh << 'SCRIPT' -#!/bin/bash -# Jupyter Lab para colaboración Claude + Usuario -# Generado por /init-jupyter - -# 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 - -echo "══════════════════════════════════════════════════════" -echo " Jupyter Lab iniciando en puerto $PORT" -echo "══════════════════════════════════════════════════════" -echo "" -echo " Abre: http://localhost:$PORT" -echo "" -echo " Claude se conectará al kernel cuando abras un notebook" -echo " Usa Ctrl+C para detener" -echo "" -echo "══════════════════════════════════════════════════════" - -jupyter lab \ - --port=$PORT \ - --no-browser \ - --NotebookApp.token='' \ - --NotebookApp.password='' \ - --NotebookApp.disable_check_xsrf=True \ - --ServerApp.allow_origin='*' \ - --ServerApp.root_dir="$(pwd)" -SCRIPT -chmod +x run-jupyter-lab.sh -``` - -### 4c. Crear/Actualizar `.claude/settings.local.json` (con MERGE) - -**IMPORTANTE: Usar ruta ABSOLUTA al ejecutable del venv local.** -**IMPORTANTE: Hacer MERGE para preservar otros MCPs existentes.** - -Usar el PUERTO detectado y la ruta absoluta: - -```bash -# Obtener ruta absoluta del proyecto -PROJECT_PATH=$(pwd) -PUERTO=8888 # Reemplazar con el puerto detectado en Paso 3 - -# Crear config de jupyter como JSON temporal -cat > /tmp/jupyter-mcp.json << EOF -{ - "mcpServers": { - "jupyter": { - "command": "${PROJECT_PATH}/.venv/bin/jupyter-mcp-server", - "args": [ - "--jupyter-url", "http://localhost:${PUERTO}", - "--start-new-runtime", "false" - ] - } - } -} -EOF - -# Merge: si existe settings.local.json, combinar; si no, crear nuevo -if [ -f .claude/settings.local.json ]; then - # Merge profundo: preserva otros MCPs, actualiza/añade jupyter - jq -s '.[0] * .[1] | .mcpServers = (.[0].mcpServers // {}) * (.[1].mcpServers // {})' \ - .claude/settings.local.json /tmp/jupyter-mcp.json > .claude/settings.local.json.tmp - mv .claude/settings.local.json.tmp .claude/settings.local.json - echo "settings.local.json actualizado (merge con config existente)" -else - # No existe, crear nuevo - mv /tmp/jupyter-mcp.json .claude/settings.local.json - echo "settings.local.json creado" -fi - -rm -f /tmp/jupyter-mcp.json -``` - -**NOTA:** La ruta absoluta es crítica. Si usas solo `jupyter-mcp-server`, Claude buscará en PATH global y fallará. - -### 4d. Añadir/Actualizar instrucciones en `.claude/CLAUDE.md` - -**IMPORTANTE**: Las reglas de Jupyter deben ir AL PRINCIPIO del archivo para que nuevas conversaciones las vean inmediatamente. - -**Lógica de inserción:** - -1. Verificar si existe `.claude/CLAUDE.md` -2. Si existe: - - Leer contenido actual - - Si NO contiene "JUPYTER HABILITADO" → prepend (añadir al principio) - - Si YA contiene → no modificar -3. Si no existe → crearlo con las reglas - -**Contenido a insertar AL PRINCIPIO:** - -```markdown -# 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 `.claude/settings.local.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 - ---- - -``` - -**Comando bash para prepend:** - -```bash -# Si CLAUDE.md existe y NO tiene las reglas -if [ -f .claude/CLAUDE.md ]; then - if ! grep -q "JUPYTER HABILITADO" .claude/CLAUDE.md; then - # Prepend: crear temp con reglas + contenido original - cat > .claude/CLAUDE.md.tmp << 'JUPYTER_RULES' -[CONTENIDO DE REGLAS AQUÍ] -JUPYTER_RULES - cat .claude/CLAUDE.md >> .claude/CLAUDE.md.tmp - mv .claude/CLAUDE.md.tmp .claude/CLAUDE.md - fi -else - # Crear nuevo - cat > .claude/CLAUDE.md << 'JUPYTER_RULES' -[CONTENIDO DE REGLAS AQUÍ] -JUPYTER_RULES -fi -``` - ---- - -## PASO 5: Inicializar entorno Python si no existe - -### 5a. Si no existe `pyproject.toml`: - -```bash -uv init 2>/dev/null || true -uv venv 2>/dev/null || true -``` - -### 5b. Instalar dependencias de Jupyter (incluyendo MCP): - -**IMPORTANTE: jupyter-mcp-server SIEMPRE se instala en el venv local, NUNCA global.** - -```bash -source .venv/bin/activate 2>/dev/null || true -uv add jupyter jupyterlab jupyter-mcp-server pandas numpy matplotlib 2>/dev/null || pip install jupyter jupyterlab jupyter-mcp-server pandas numpy matplotlib - -# Verificar que MCP quedó instalado localmente -[ -f .venv/bin/jupyter-mcp-server ] && echo "MCP instalado en venv local OK" || echo "ERROR: MCP no instalado" -``` - ---- - -## PASO 6: Resumen final al usuario - -Mostrar: - -``` -═══════════════════════════════════════════════════════════════ - Entorno Jupyter configurado en: [RUTA] -═══════════════════════════════════════════════════════════════ - - Puerto asignado: [PUERTO] - MCP: .venv/bin/jupyter-mcp-server (LOCAL al proyecto) - - ┌─────────────────────────────────────────────────────────┐ - │ PASOS PARA ACTIVAR (orden importante): │ - │ │ - │ 1. SALIR de Claude Code: /exit o Ctrl+C │ - │ (necesario para que cargue el nuevo MCP) │ - │ │ - │ 2. En OTRA terminal, ejecuta: │ - │ cd [RUTA] && ./run-jupyter-lab.sh │ - │ │ - │ 3. Volver a entrar a Claude: │ - │ cd [RUTA] && claude │ - │ (ahora detectará el MCP de Jupyter) │ - │ │ - │ 4. Abre en navegador: http://localhost:[PUERTO] │ - │ │ - │ 5. Crea o abre un notebook y empieza a trabajar │ - └─────────────────────────────────────────────────────────┘ - - Archivos creados/actualizados: - ✓ run-jupyter-lab.sh (lanzador con autodetección de puerto) - ✓ .claude/settings.local.json (config MCP - merge con existente) - ✓ .claude/CLAUDE.md (reglas de trabajo al principio) - ✓ .venv/bin/jupyter-mcp-server (ejecutable MCP local) - - Verificación rápida del MCP: - $ ls -la .venv/bin/jupyter-mcp-server - - Si el MCP no funciona después de reiniciar Claude: - 1. Verifica que Jupyter está corriendo: pgrep -af jupyter - 2. Verifica puerto: cat .jupyter-port - 3. Verifica MCP existe: ls .venv/bin/jupyter-mcp-server - 4. Revisa config: cat .claude/settings.local.json - -═══════════════════════════════════════════════════════════════ -``` - ---- - -## Comandos útiles post-inicialización - -```bash -# Ver puerto actual -cat .jupyter-port - -# Verificar Jupyter corriendo -pgrep -af jupyter - -# Listar kernels activos -curl -s http://localhost:$(cat .jupyter-port)/api/kernels | jq - -# Listar notebooks abiertos -curl -s http://localhost:$(cat .jupyter-port)/api/sessions | jq -``` - ---- - -## Notas para uso frecuente - -- El script `run-jupyter-lab.sh` autodetecta puerto cada vez que se ejecuta -- El archivo `.jupyter-port` siempre tiene el puerto actual -- Si cambias de puerto, actualiza `.claude/settings.local.json` o recarga Claude -- Las reglas en `CLAUDE.md` se mantienen entre sesiones +El script instala `jupyter-mcp-server` en `.venv/bin/` del proyecto, NUNCA globalmente. +Si detecta instalación global, la elimina automáticamente. 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"