#!/usr/bin/env bash # launch_claude_agent_kitty — Lanza un Claude Code secundario interactivo y # persistente en su propia terminal kitty, con un prompt autonomo inyectado # desde un archivo. Es la mecanica de lanzamiento del "modo orquestador": un # Claude principal descompone una tarea y lanza N secundarios, cada uno en su # kitty, que el humano ve y puede retomar. # # Mecanismo: # - setsid nohup kitty ... & disown -> la ventana sobrevive al cierre de la # terminal padre (igual que reboot_all_claudes con setsid). # - zsh -ic 'claude ...; exec zsh' -> al terminar el claude queda una shell # interactiva viva para que el humano siga en esa terminal. # - --dangerously-skip-permissions -> agente autonomo desatendido (sin # confirmaciones). Riesgo asumido a proposito. # - El prompt se inyecta con "$(cat )" para no expandir nada en # el shell del orquestador. # - Log de arranque en /tmp/orq__kitty.log, donde deriva del # title (minusculas, no-alfanumerico -> '_'). set -euo pipefail IFS=$' \t\n' launch_claude_agent_kitty() { # ----------------------------------------------------------------------- # Ayuda / sin argumentos. # ----------------------------------------------------------------------- if [[ $# -eq 0 || "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then cat <<'USAGE' Uso: launch_claude_agent_kitty <directory> <prompt_file> Lanza un Claude Code secundario interactivo y persistente en su propia terminal kitty, con el prompt del archivo <prompt_file> inyectado y --dangerously-skip-permissions (agente autonomo desatendido). Argumentos (los 3 obligatorios): title Titulo de la ventana kitty. Ej: "fn_registry · subtarea X". directory Directorio de trabajo AISLADO donde arranca el claude secundario (worktree git, sub-repo, o dir cualquiera). Debe existir. Usa un dir aislado: dos claudes en el mismo working tree comparten HEAD y dispersan commits. prompt_file Ruta a un archivo .md con el prompt autonomo a inyectar. Debe existir y ser legible. Ejemplo: launch_claude_agent_kitty "fn_registry · docs" /tmp/orq_docs_wt /tmp/orq_docs.md El log de arranque va a /tmp/orq_<slug>_kitty.log (slug derivado del title). USAGE return 0 fi # ----------------------------------------------------------------------- # Validacion de argumentos. # ----------------------------------------------------------------------- if [[ $# -ne 3 ]]; then echo "launch_claude_agent_kitty: se requieren 3 argumentos <title> <directory> <prompt_file> (recibidos: $#). Usa -h." >&2 return 2 fi local title="$1" local directory="$2" local prompt_file="$3" if [[ -z "$title" ]]; then echo "launch_claude_agent_kitty: <title> no puede estar vacio." >&2 return 2 fi if [[ ! -d "$directory" ]]; then echo "launch_claude_agent_kitty: el directorio de trabajo no existe: '$directory'." >&2 return 2 fi if [[ ! -f "$prompt_file" ]]; then echo "launch_claude_agent_kitty: el prompt_file no existe: '$prompt_file'." >&2 return 2 fi if [[ ! -r "$prompt_file" ]]; then echo "launch_claude_agent_kitty: el prompt_file no es legible: '$prompt_file'." >&2 return 2 fi # ----------------------------------------------------------------------- # Comprobar que kitty esta instalado. # ----------------------------------------------------------------------- if ! command -v kitty >/dev/null 2>&1; then echo "launch_claude_agent_kitty: 'kitty' no esta instalado o no esta en el PATH." >&2 return 1 fi # ----------------------------------------------------------------------- # Derivar el slug del title para el nombre del log. # minusculas, todo no-alfanumerico -> '_', colapsar/recortar '_'. # ----------------------------------------------------------------------- local slug slug="$(printf '%s' "$title" \ | tr '[:upper:]' '[:lower:]' \ | tr -c 'a-z0-9' '_' \ | sed -E 's/_+/_/g; s/^_//; s/_$//')" [[ -z "$slug" ]] && slug="agent" local log="/tmp/orq_${slug}_kitty.log" # ----------------------------------------------------------------------- # Lanzar la kitty detached. El prompt se inyecta con "$(cat <prompt_file>)" # ya escapado para que se evalue DENTRO de la kitty, no aqui. # exec zsh deja una shell viva cuando el claude termina. # ----------------------------------------------------------------------- local inner inner="claude --dangerously-skip-permissions \"\$(cat $(printf '%q' "$prompt_file"))\"; exec zsh" setsid nohup kitty \ --title "$title" \ --directory "$directory" \ zsh -ic "$inner" \ >"$log" 2>&1 & disown 2>/dev/null || true # ----------------------------------------------------------------------- # Reportar. Con setsid el $! es el PID de setsid, no el de kitty; basta # con confirmar el lanzamiento y apuntar al log donde se ve el arranque. # ----------------------------------------------------------------------- echo "launch_claude_agent_kitty: claude secundario lanzado." echo " title: $title" echo " directory: $directory" echo " prompt_file: $prompt_file" echo " log: $log" echo " (sigue el arranque con: tail -f $(printf '%q' "$log"))" return 0 } # Permitir ejecutar el archivo directamente (no solo como funcion sourced). if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then launch_claude_agent_kitty "$@" fi