Files
fn_registry/bash/functions/infra/launch_claude_agent_kitty.sh
T
egutierrez 5c253a26e2 feat(infra): grupo orchestration — launch_claude_agent_kitty + list_claude_agents
Dos funciones bash para la mecanica del modo orquestador (Claudes secundarios
interactivos en kitty):

- launch_claude_agent_kitty(title, directory, prompt_file): lanza un Claude Code
  secundario en su propia terminal kitty con un prompt autonomo inyectado y
  --dangerously-skip-permissions, detached (setsid nohup ... disown) para
  sobrevivir al cierre de la terminal padre.
- list_claude_agents([--json] [--exclude-current]): lista la flota de Claudes
  vivos cruzando pgrep -x claude con ~/.claude/sessions/<PID>.json (con
  validacion anti-PID-reciclado por procStart), reportando PID, sessionId, cwd,
  status, etime y KITTY_PID. Reusa la logica de descubrimiento de
  reboot_all_claudes_bash_infra.

Tag de grupo de capacidad: orchestration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:32:33 +02:00

136 lines
5.6 KiB
Bash
Executable File

#!/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 <prompt_file>)" para no expandir nada en
# el shell del orquestador.
# - Log de arranque en /tmp/orq_<slug>_kitty.log, donde <slug> 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 <title> <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