#!/usr/bin/env bash # create-full.sh — pipeline completo para crear un agente o robot funcional # # Pipeline de 7+1 pasos: # 1. SCAFFOLD → crear archivos base desde template # 2. BUILD → go build -tags goolm ./... # 3. REGISTER → crear usuario Matrix + token # 4. VERIFY E2EE → cross-signing + recovery key # 5. CONVERT (robot) → eliminar LLM/prompts si type=robot # 6. AUTO-AVATAR → generar y aplicar foto de perfil # 7. DISPLAY NAME → configurar nombre visible en Matrix # 8. PERSONALIZE → (automatico si se pasan --description / --system-prompt) # # Pasos posteriores: # 9. REBUILD → recompilar tras personalizacion # 10. START/RESTART → arrancar el launcher con el bot # 11. HEALTH CHECK → ./dev-scripts/agent/health-check.sh # 12. SELF-INTRODUCE → ./dev-scripts/agent/notify-developer.sh # # Uso básico: # ./dev-scripts/agent/create-full.sh "Display Name" # ./dev-scripts/agent/create-full.sh "Display Name" --type robot # # Uso con personalización automática (Paso 8 incluido): # ./dev-scripts/agent/create-full.sh weather-bot "Weather Bot" \ # --description "Consulta el tiempo actual y predicciones" \ # --provider anthropic \ # --system-prompt "Eres Weather Bot, especialista en meteorología." # # Flags de personalización (opcionales, activan el Paso 8 automático): # --description "" descripcion del agente # --provider proveedor LLM (default: claude-code) # REGLA PROYECTO: usar claude-code SIEMPRE salvo razon explicita # --model modelo LLM (default: segun provider) # --tone tono (default: friendly) # --prefix "" emoji prefix (default: 🤖) # --system-prompt "" system prompt inline # --system-prompt-file system prompt desde archivo # --tool-use habilitar tool_use en config # --language idioma (default: es) # --avatar imagen para el avatar (default: generador random) # ej: https://example/pikachu.png o ./avatars/poke.png # # Requisitos en .env: # MATRIX_ADMIN_TOKEN, MATRIX_HOMESERVER, MATRIX_SERVER_NAME # DEVELOPER_MATRIX_USERS (opcional, para notificación al developer) source "$(dirname "$0")/../_common.sh" load_env need_arg "${1:-}" ID="$1" DISPLAYNAME="${2:-$ID}" TYPE="agent" NORM="$(normalize_id "$ID")" SCRIPT_DIR="$(dirname "$0")" # Flags de personalización (Paso 8) PERSONALIZE_DESCRIPTION="" PERSONALIZE_PROVIDER="" PERSONALIZE_MODEL="" PERSONALIZE_TONE="friendly" PERSONALIZE_PREFIX="🤖" PERSONALIZE_SYSTEM_PROMPT="" PERSONALIZE_SYSTEM_PROMPT_FILE="" PERSONALIZE_TOOL_USE=false PERSONALIZE_LANGUAGE="es" DO_PERSONALIZE=false # Parse flags shift 2 2>/dev/null || shift 1 2>/dev/null || true while [[ $# -gt 0 ]]; do case "$1" in --type) TYPE="${2:-agent}"; shift 2 ;; --type=*) TYPE="${1#--type=}"; shift ;; --description) PERSONALIZE_DESCRIPTION="${2:-}"; DO_PERSONALIZE=true; shift 2 ;; --description=*) PERSONALIZE_DESCRIPTION="${1#--description=}"; DO_PERSONALIZE=true; shift ;; --provider) PERSONALIZE_PROVIDER="${2:-}"; DO_PERSONALIZE=true; shift 2 ;; --provider=*) PERSONALIZE_PROVIDER="${1#--provider=}"; DO_PERSONALIZE=true; shift ;; --model) PERSONALIZE_MODEL="${2:-}"; DO_PERSONALIZE=true; shift 2 ;; --model=*) PERSONALIZE_MODEL="${1#--model=}"; DO_PERSONALIZE=true; shift ;; --tone) PERSONALIZE_TONE="${2:-friendly}"; DO_PERSONALIZE=true; shift 2 ;; --tone=*) PERSONALIZE_TONE="${1#--tone=}"; DO_PERSONALIZE=true; shift ;; --prefix) PERSONALIZE_PREFIX="${2:-🤖}"; DO_PERSONALIZE=true; shift 2 ;; --prefix=*) PERSONALIZE_PREFIX="${1#--prefix=}"; DO_PERSONALIZE=true; shift ;; --system-prompt) PERSONALIZE_SYSTEM_PROMPT="${2:-}"; DO_PERSONALIZE=true; shift 2 ;; --system-prompt=*) PERSONALIZE_SYSTEM_PROMPT="${1#--system-prompt=}"; DO_PERSONALIZE=true; shift ;; --system-prompt-file) PERSONALIZE_SYSTEM_PROMPT_FILE="${2:-}"; DO_PERSONALIZE=true; shift 2 ;; --system-prompt-file=*) PERSONALIZE_SYSTEM_PROMPT_FILE="${1#--system-prompt-file=}"; DO_PERSONALIZE=true; shift ;; --tool-use) PERSONALIZE_TOOL_USE=true; DO_PERSONALIZE=true; shift ;; --language) PERSONALIZE_LANGUAGE="${2:-es}"; DO_PERSONALIZE=true; shift 2 ;; --language=*) PERSONALIZE_LANGUAGE="${1#--language=}"; DO_PERSONALIZE=true; shift ;; --avatar) AVATAR_SOURCE="${2:-}"; shift 2 ;; --avatar=*) AVATAR_SOURCE="${1#--avatar=}"; shift ;; *) shift ;; esac done # AVATAR_SOURCE puede ser URL (http/https) o ruta local. Vacio = generador random. : "${AVATAR_SOURCE:=}" if [[ "$TYPE" == "robot" ]]; then TYPE_LABEL="robot" TYPE_EMOJI="🤖" else TYPE_LABEL="agente" TYPE_EMOJI="🧠" fi echo "" echo -e "${BLU}═══════════════════════════════════════════════════════${RST}" echo -e "${BLU} Creando ${TYPE_LABEL}: ${GRN}$ID${BLU} ($DISPLAYNAME) ${TYPE_EMOJI}${RST}" echo -e "${BLU}═══════════════════════════════════════════════════════${RST}" echo "" # ── Paso 1: Scaffold ───────────────────────────────────────────────────── TOTAL_STEPS=7 [[ "$TYPE" == "robot" ]] && TOTAL_STEPS=8 info "Paso 1/${TOTAL_STEPS} — Scaffold (agent.go, config.yaml, prompts, launcher)" echo "" "$SCRIPT_DIR/new-agent.sh" "$ID" "$DISPLAYNAME" echo "" # ── Paso 2: Verificar compilación ───────────────────────────────────────── info "Paso 2/${TOTAL_STEPS} — Verificando compilación..." if "$GO" build -tags goolm ./... 2>&1; then ok "Compilación exitosa" else fail "Error de compilación — revisa agents/$ID/agent.go y cmd/launcher/main.go" fi echo "" # ── Paso 3: Registrar en Matrix ────────────────────────────────────────── info "Paso 3/${TOTAL_STEPS} — Registrando en Matrix..." echo "" # Reload .env in case new-agent.sh or previous steps changed it load_env "$SCRIPT_DIR/register.sh" "$ID" "$DISPLAYNAME" echo "" # ── Paso 4: Verificar E2EE ─────────────────────────────────────────────── info "Paso 4/${TOTAL_STEPS} — Verificación E2EE (cross-signing + recovery key)..." echo "" # Reload .env to pick up token, password, pickle key from register.sh load_env "$SCRIPT_DIR/verify.sh" "$ID" echo "" # ── Paso 5 (robots): Convertir a robot ─────────────────────────────────── if [[ "$TYPE" == "robot" ]]; then info "Paso 5/${TOTAL_STEPS} — Convirtiendo a robot..." echo "" "$SCRIPT_DIR/convert-to-robot.sh" "$ID" "$DISPLAYNAME" # Rebuild after conversion info "Recompilando tras conversión..." "$GO" build -tags goolm ./... 2>&1 || fail "Error de compilación tras conversión a robot" ok "Recompilación exitosa" echo "" fi # ── Paso auto-avatar: Generar/aplicar avatar ──────────────────────────── AVATAR_STEP=$((TOTAL_STEPS - 2)) info "Paso ${AVATAR_STEP}/${TOTAL_STEPS} — Configurando avatar del bot..." echo "" # Resuelve el binario de agentctl como array (preserva split por espacios) if [[ -f "$REPO_ROOT/bin/agentctl" ]]; then CTL_ARR=("$REPO_ROOT/bin/agentctl") else CTL_ARR=("$GO" run -tags goolm ./cmd/agentctl) fi # Si el usuario pasa --avatar, usa la URL/ruta indicada en vez del generador random. AVATAR_CMD=("${CTL_ARR[@]}" auto-avatar "$ID") if [[ -n "$AVATAR_SOURCE" ]]; then if [[ "$AVATAR_SOURCE" =~ ^https?:// ]]; then AVATAR_CMD+=(--from-url "$AVATAR_SOURCE") info "Usando avatar personalizado desde URL: $AVATAR_SOURCE" else AVATAR_CMD+=(--from-file "$AVATAR_SOURCE") info "Usando avatar personalizado desde archivo: $AVATAR_SOURCE" fi fi if "${AVATAR_CMD[@]}" 2>&1; then ok "Avatar configurado y aplicado" else warn "No se pudo configurar avatar (se puede hacer despues con: agentctl auto-avatar $ID [--from-url | --from-file ])" fi echo "" # ── Paso display name: Configurar nombre visible en Matrix ────────────── DISPLAYNAME_STEP=$((TOTAL_STEPS - 1)) info "Paso ${DISPLAYNAME_STEP}/${TOTAL_STEPS} — Configurando display name en Matrix..." echo "" # Reload .env to pick up token from register.sh load_env TOKEN_VAR="MATRIX_TOKEN_${NORM}" BOT_TOKEN="${!TOKEN_VAR:-}" if [[ -n "$BOT_TOKEN" ]]; then USER_ID="@${ID}:${MATRIX_SERVER_NAME}" if curl -sf -X PUT \ "${MATRIX_HOMESERVER}/_matrix/client/v3/profile/${USER_ID}/displayname" \ -H "Authorization: Bearer $BOT_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"displayname\": \"${DISPLAYNAME}\"}" >/dev/null 2>&1; then ok "Display name configurado: $DISPLAYNAME" else warn "No se pudo configurar display name (se puede hacer despues manualmente)" fi else warn "Token del bot no encontrado — display name no configurado" fi echo "" # ── Paso 8a (robots): aplicar --description al config.yaml ────────────── # Los robots no tienen prompts/system.md ni agent.go (no LLM), pero su # config.yaml SI tiene un campo `description:` que personalize.sh ignora. # Para evitar que el robot quede con la descripcion del template literal, # parcheamos la linea aqui. if [[ "$TYPE" == "robot" ]] && [[ -n "$PERSONALIZE_DESCRIPTION" ]]; then CFG_FILE="agents/$ID/config.yaml" if [[ -f "$CFG_FILE" ]]; then # Escapar caracteres especiales del valor para sed ESCAPED_DESC="$(printf '%s' "$PERSONALIZE_DESCRIPTION" | sed -e 's/[\/&|]/\\&/g')" sed -i "0,/^ description:.*/s|| description: \"$ESCAPED_DESC\"|" "$CFG_FILE" ok "Descripcion del robot aplicada al config.yaml" fi fi # ── Paso 8 (automático, solo agents): Personalizar archivos ───────────── PERSONALIZE_DONE=false if $DO_PERSONALIZE && [[ "$TYPE" != "robot" ]]; then PERSONALIZE_EXTRA_STEP=$((TOTAL_STEPS + 1)) info "Paso ${PERSONALIZE_EXTRA_STEP} — Personalizando archivos del agente (automático)..." echo "" # Construir args para personalize.sh PERSONALIZE_ARGS=() [[ -n "$PERSONALIZE_DESCRIPTION" ]] && PERSONALIZE_ARGS+=(--description "$PERSONALIZE_DESCRIPTION") [[ -n "$PERSONALIZE_PROVIDER" ]] && PERSONALIZE_ARGS+=(--provider "$PERSONALIZE_PROVIDER") [[ -n "$PERSONALIZE_MODEL" ]] && PERSONALIZE_ARGS+=(--model "$PERSONALIZE_MODEL") [[ "$PERSONALIZE_TONE" != "friendly" ]] && PERSONALIZE_ARGS+=(--tone "$PERSONALIZE_TONE") [[ "$PERSONALIZE_PREFIX" != "🤖" ]] && PERSONALIZE_ARGS+=(--prefix "$PERSONALIZE_PREFIX") [[ -n "$PERSONALIZE_SYSTEM_PROMPT" ]] && PERSONALIZE_ARGS+=(--system-prompt "$PERSONALIZE_SYSTEM_PROMPT") [[ -n "$PERSONALIZE_SYSTEM_PROMPT_FILE" ]] && PERSONALIZE_ARGS+=(--system-prompt-file "$PERSONALIZE_SYSTEM_PROMPT_FILE") $PERSONALIZE_TOOL_USE && PERSONALIZE_ARGS+=(--tool-use) [[ "$PERSONALIZE_LANGUAGE" != "es" ]] && PERSONALIZE_ARGS+=(--language "$PERSONALIZE_LANGUAGE") if "$SCRIPT_DIR/personalize.sh" "$ID" "${PERSONALIZE_ARGS[@]}"; then ok "Personalización completada" PERSONALIZE_DONE=true # Recompilar tras personalización info "Recompilando tras personalización..." if "$GO" build -tags goolm ./... 2>&1; then ok "Recompilación exitosa" else fail "Error de compilación tras personalización — revisa agents/$ID/agent.go" fi else warn "Personalización falló — revisa los flags o edita los archivos manualmente" fi echo "" fi # ── Paso final: Notificar al developer ─────────────────────────────────── NOTIFY_STEP=$TOTAL_STEPS info "Paso ${NOTIFY_STEP}+ — Notificando a desarrolladores..." echo "" "$SCRIPT_DIR/notify-developer.sh" "$ID" "$TYPE" "$DISPLAYNAME" || true echo "" # ── Resumen ────────────────────────────────────────────────────────────── echo -e "${GRN}═══════════════════════════════════════════════════════${RST}" echo -e "${GRN} ✓ ${TYPE_LABEL^} $ID creado exitosamente ${TYPE_EMOJI}${RST}" echo -e "${GRN}═══════════════════════════════════════════════════════${RST}" echo "" echo -e " ${BLU}Archivos creados:${RST}" echo -e " agents/$ID/agent.go" echo -e " agents/$ID/config.yaml" if [[ "$TYPE" != "robot" ]]; then echo -e " agents/$ID/prompts/system.md" fi echo "" echo -e " ${BLU}Variables en .env:${RST}" echo -e " MATRIX_TOKEN_${NORM}" echo -e " MATRIX_PASSWORD_${NORM}" echo -e " PICKLE_KEY_${NORM}" echo -e " SSSS_RECOVERY_KEY_${NORM}" echo "" echo -e " ${BLU}Launcher actualizado:${RST}" echo -e " cmd/launcher/main.go (import)" echo "" if $PERSONALIZE_DONE; then echo -e "${GRN}Paso 8 completado automáticamente ✓${RST}" echo -e " ${DIM}config.yaml, agent.go y prompts/system.md personalizados${RST}" echo "" echo -e "${YLW}Siguientes pasos (9-12 del pipeline):${RST}" echo "" echo -e " ${BLU}9. REBUILD${RST}:" echo -e " ${DIM}go build -tags goolm ./...${RST}" echo "" echo -e " ${BLU}10. START${RST}:" echo -e " ${DIM}./dev-scripts/server/start.sh${RST}" echo "" echo -e " ${BLU}11. HEALTH CHECK${RST}:" echo -e " ${DIM}./dev-scripts/agent/health-check.sh $ID${RST}" echo "" echo -e " ${BLU}12. SELF-INTRODUCE${RST} (tras health check ok):" echo -e " ${DIM}./dev-scripts/agent/notify-developer.sh $ID $TYPE \"$DISPLAYNAME\"${RST}" else echo -e "${YLW}Siguientes pasos (8-12 del pipeline):${RST}" echo "" if [[ "$TYPE" == "robot" ]]; then echo -e " ${BLU}8. PERSONALIZE${RST} — añadir comandos custom:" echo -e " ${DIM}agents/$ID/commands.go${RST}" echo -e " ${DIM}cmd/launcher/main.go${RST} (registrar comandos)" else echo -e " ${BLU}8. PERSONALIZE${RST} — personalizar los archivos del agente:" echo -e " ${DIM}agents/$ID/agent.go${RST} — reglas de decisión" echo -e " ${DIM}agents/$ID/config.yaml${RST} — LLM, tools, personalidad" echo -e " ${DIM}agents/$ID/prompts/system.md${RST} — system prompt" fi echo "" echo -e " ${BLU}9. REBUILD${RST}:" echo -e " ${DIM}go build -tags goolm ./...${RST}" echo "" echo -e " ${BLU}10. START${RST}:" echo -e " ${DIM}./dev-scripts/server/start.sh${RST}" echo "" echo -e " ${BLU}11. HEALTH CHECK${RST}:" echo -e " ${DIM}./dev-scripts/agent/health-check.sh $ID${RST}" echo "" echo -e " ${BLU}12. SELF-INTRODUCE${RST} (tras health check ok):" echo -e " ${DIM}./dev-scripts/agent/notify-developer.sh $ID $TYPE \"$DISPLAYNAME\"${RST}" echo "" fi # end $PERSONALIZE_DONE else