#!/usr/bin/env bash # init_jupyter_analysis # ---------------------- # Inicializa un analisis Jupyter completo. # # Dos modos de uso: # Suelto: analysis/{nombre}/ # Proyecto: projects/{proyecto}/analysis/{nombre}/ (con --project ) # # En modo proyecto, tambien genera analysis.md con frontmatter correcto # y ejecuta `fn index` al final para que el analisis quede registrado. # # Compone: assert_command_exists + find_free_port + init_uv_venv + # uv_add_packages + write_jupyter_launcher + # write_mcp_jupyter_config + write_claude_jupyter_rules + # write_jupyter_registry_kernel + write_analysis_md # # USO: # ./init_jupyter_analysis.sh [paquetes_extra...] # ./init_jupyter_analysis.sh --project [paquetes_extra...] # ./init_jupyter_analysis.sh --project [paquetes_extra...] # ./init_jupyter_analysis.sh [paquetes...] --desc "descripcion" # # EJEMPLOS: # ./init_jupyter_analysis.sh finanzas # ./init_jupyter_analysis.sh --project aurgi sale_prices --desc "Comprobar precios" # ./init_jupyter_analysis.sh ml scikit-learn torch set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REGISTRY_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" # Source funciones atomicas source "$REGISTRY_ROOT/bash/functions/shell/assert_command_exists.sh" source "$REGISTRY_ROOT/bash/functions/shell/find_free_port.sh" source "$REGISTRY_ROOT/bash/functions/infra/init_uv_venv.sh" source "$REGISTRY_ROOT/bash/functions/infra/uv_add_packages.sh" source "$REGISTRY_ROOT/bash/functions/infra/write_jupyter_launcher.sh" source "$REGISTRY_ROOT/bash/functions/infra/write_mcp_jupyter_config.sh" source "$REGISTRY_ROOT/bash/functions/infra/write_claude_jupyter_rules.sh" source "$REGISTRY_ROOT/bash/functions/infra/write_jupyter_registry_kernel.sh" source "$REGISTRY_ROOT/bash/functions/infra/write_analysis_md.sh" # ── Parsing de argumentos (flags mezclados con posicionales) ─ PROJECT="" NOMBRE="" DESC="" TAGS="" EXTRA_PACKAGES=() while [ $# -gt 0 ]; do case "$1" in --project) PROJECT="$2"; shift 2 ;; --desc|--description) DESC="$2"; shift 2 ;; --tags) TAGS="$2"; shift 2 ;; -h|--help) grep "^#" "$0" | sed 's/^# \?//' ; exit 0 ;; -*) echo "Flag desconocido: $1" >&2 ; exit 1 ;; *) if [ -z "$NOMBRE" ]; then NOMBRE="$1" else EXTRA_PACKAGES+=("$1") fi shift ;; esac done if [ -z "$NOMBRE" ]; then echo "Uso: $0 [--project ] [paquetes_extra...]" >&2 echo " Ejemplo: $0 --project aurgi sale_prices --desc 'Comprobar precios'" >&2 exit 1 fi # ── Resolver directorio destino ────────────────────────────── if [ -n "$PROJECT" ]; then PROJECT_DIR="${REGISTRY_ROOT}/projects/${PROJECT}" if [ ! -f "${PROJECT_DIR}/project.md" ]; then echo "ERROR: El proyecto '${PROJECT}' no existe en projects/${PROJECT}/" >&2 echo " Creao primero con 'fn add -k project' o manualmente." >&2 exit 1 fi ANALYSIS_DIR="${PROJECT_DIR}/analysis/${NOMBRE}" else ANALYSIS_DIR="${REGISTRY_ROOT}/analysis/${NOMBRE}" fi if [ -z "$DESC" ]; then DESC="Analisis ${NOMBRE}" fi echo "" echo "════════════════════════════════════════════════════════════" echo " INIT JUPYTER ANALYSIS: ${NOMBRE}" if [ -n "$PROJECT" ]; then echo " Proyecto: ${PROJECT}" fi echo " Directorio: ${ANALYSIS_DIR}" echo "════════════════════════════════════════════════════════════" echo "" # ── 1. Verificar herramientas ─────────────────────────────── echo "[1/9] Verificando herramientas..." assert_command_exists uv || assert_command_exists python3 echo " OK" # ── 2. Crear estructura de carpetas ───────────────────────── echo "[2/9] Creando estructura..." mkdir -p "$ANALYSIS_DIR/notebooks" "$ANALYSIS_DIR/data" echo " ${ANALYSIS_DIR}/notebooks/" echo " ${ANALYSIS_DIR}/data/" # ── 3. Crear venv ─────────────────────────────────────────── echo "[3/9] Inicializando venv..." venv_path=$(init_uv_venv "$ANALYSIS_DIR") echo " $venv_path" # ── 4. Instalar paquetes ──────────────────────────────────── echo "[4/9] Instalando paquetes..." BASE_PACKAGES=(jupyter jupyterlab jupyter-collaboration jupyter-mcp-server pandas numpy matplotlib) ALL_PACKAGES=("${BASE_PACKAGES[@]}" "${EXTRA_PACKAGES[@]}") uv_add_packages "$ANALYSIS_DIR" "${ALL_PACKAGES[@]}" echo " Instalados: ${ALL_PACKAGES[*]}" # ── 5. Generar launcher ───────────────────────────────────── echo "[5/9] Generando launcher..." launcher=$(write_jupyter_launcher "$ANALYSIS_DIR") echo " $launcher" # ── 6. Configurar MCP ─────────────────────────────────────── echo "[6/9] Configurando MCP..." port=$(find_free_port 8888 8899) mcp_config=$(write_mcp_jupyter_config "$ANALYSIS_DIR" "$port") echo " $mcp_config (puerto: $port)" # ── 7. Reglas para agentes ────────────────────────────────── echo "[7/9] Escribiendo reglas Claude..." rules=$(write_claude_jupyter_rules "$ANALYSIS_DIR") echo " $rules" # ── 8. Kernel startup con acceso al registry ──────────────── echo "[8/9] Configurando kernel con acceso al registry..." kernel_startup=$(write_jupyter_registry_kernel "$ANALYSIS_DIR") echo " $kernel_startup" # ── 9. analysis.md (+ fn index si es proyecto) ────────────── echo "[9/9] Escribiendo analysis.md..." export FN_REGISTRY_ROOT="$REGISTRY_ROOT" md_path=$(write_analysis_md "$ANALYSIS_DIR" "$NOMBRE" "$DESC" "$TAGS") echo " $md_path" if [ -n "$PROJECT" ]; then echo "" echo " Indexando registry..." if [ -x "${REGISTRY_ROOT}/fn" ]; then ( cd "$REGISTRY_ROOT" && ./fn index 2>&1 | tail -3 ) else echo " WARN: binario 'fn' no encontrado en ${REGISTRY_ROOT}. Ejecuta 'fn index' manualmente." fi fi # ── Resumen ───────────────────────────────────────────────── echo "" echo "════════════════════════════════════════════════════════════" echo " ANALISIS '${NOMBRE}' LISTO" echo "════════════════════════════════════════════════════════════" echo "" echo " Pasos siguientes:" echo "" echo " 1. En otra terminal:" echo " cd ${ANALYSIS_DIR} && ./run-jupyter-lab.sh" echo "" echo " 2. Abrir Claude en el analisis:" echo " cd ${ANALYSIS_DIR} && claude" echo "" echo " 3. Abrir en navegador: http://localhost:${port}" echo "" echo " FN_REGISTRY_ROOT=${REGISTRY_ROOT}" echo ""