Files
fn_registry/bash/functions/infra/launch_fleetclaude.sh
T
egutierrez 111ee17bcc feat(infra): auto-commit con 1 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-17 09:21:15 +02:00

217 lines
9.4 KiB
Bash

#!/usr/bin/env bash
# launch_fleetclaude — Entrypoint MVP de FleetView.
#
# Abre UNA ventana kitty corriendo una sesion tmux de dos panes:
# - pane izquierdo: la TUI 'fleetview' (la flota de Claudes centralizada).
# - pane derecho: 'claude --dangerously-skip-permissions'.
#
# Objetivo: dejar de tener N ventanas kitty dispersas y centralizar el control
# de los Claudes en una sola ventana.
#
# Funcion IMPURA: lanza procesos (tmux + kitty) con efectos secundarios.
# - Crea/reusa una sesion tmux detached llamada <session> (idempotente).
# - Lanza una ventana kitty desacoplada del shell padre (setsid) para que
# sobreviva al cierre de la terminal que la invoco.
# - No toca atajos de teclado ni kitty.conf.
set -euo pipefail
IFS=$' \t\n'
launch_fleetclaude() {
local cwd=""
local bin=""
local session="fleet"
local cols=52
local T="tmux -L fleet" # socket tmux aislado: no toca el tmux normal del usuario
# -----------------------------------------------------------------------
# Parseo de argumentos
# -----------------------------------------------------------------------
while [[ $# -gt 0 ]]; do
case "$1" in
--cwd)
shift
cwd="${1:-}"
;;
--bin)
shift
bin="${1:-}"
;;
--session)
shift
session="${1:-}"
;;
--cols)
shift
cols="${1:-40}"
;;
-h|--help)
cat <<'USAGE'
Uso: launch_fleetclaude [opciones]
Abre una ventana kitty con una sesion tmux de dos panes: la TUI fleetview a la
izquierda y 'claude --dangerously-skip-permissions' a la derecha.
Opciones:
--cwd <dir> Directorio de trabajo de los panes.
Default: raiz del repo fn_registry (derivada dinamicamente).
--bin <path> Ruta al binario de la TUI fleetview.
Default: <repo>/apps/fleetview/fleetview
--session <name> Nombre de la sesion tmux. Default: fleet.
--cols <n> Ancho (columnas) del pane izquierdo. Default: 40.
-h, --help Muestra esta ayuda.
Ejemplos:
launch_fleetclaude
launch_fleetclaude --cwd ~/fn_registry
launch_fleetclaude --session fleet --cols 50
USAGE
return 0
;;
*)
echo "launch_fleetclaude: opcion desconocida: '$1' (usa -h)" >&2
return 2
;;
esac
shift
done
# -----------------------------------------------------------------------
# Derivar la raiz del repo fn_registry dinamicamente (NO hardcodear paths
# de usuario). Estrategia: subir desde la ubicacion del script con
# 'git rev-parse --show-toplevel'; fallbacks razonables si no aplica.
# -----------------------------------------------------------------------
local script_dir repo_root=""
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# El script vive en <repo>/bash/functions/infra/, asi que la raiz son 3
# niveles arriba; pero preferimos git para robustez.
repo_root="$(git -C "$script_dir" rev-parse --show-toplevel 2>/dev/null || true)"
if [[ -z "$repo_root" ]]; then
# Fallback 1: navegacion relativa desde la ubicacion del script.
repo_root="$(cd "$script_dir/../../.." 2>/dev/null && pwd || true)"
fi
if [[ -z "$repo_root" ]]; then
# Fallback 2: variable de entorno del registry o el cwd actual.
repo_root="${FN_REGISTRY_ROOT:-$PWD}"
fi
# Defaults derivados de la raiz del repo.
[[ -z "$cwd" ]] && cwd="$repo_root"
[[ -z "$bin" ]] && bin="$repo_root/apps/fleetview/fleetview"
# Validar cwd: si no existe, caer al repo_root.
if [[ ! -d "$cwd" ]]; then
echo "launch_fleetclaude: --cwd '$cwd' no existe; usando '$repo_root'." >&2
cwd="$repo_root"
fi
# -----------------------------------------------------------------------
# Comprobar herramientas necesarias.
# -----------------------------------------------------------------------
if ! command -v tmux >/dev/null 2>&1; then
echo "launch_fleetclaude: tmux no esta instalado." >&2
return 1
fi
if ! command -v kitty >/dev/null 2>&1; then
echo "launch_fleetclaude: kitty no esta instalado." >&2
return 1
fi
# -----------------------------------------------------------------------
# Comando para el pane izquierdo:
# - Si el binario fleetview existe -> ejecutarlo (exec, sin shell colgado).
# - Si NO existe -> mensaje claro + shell interactiva (no falla en silencio).
# -----------------------------------------------------------------------
local left_cmd
if [[ -x "$bin" ]]; then
left_cmd="exec $(printf '%q' "$bin")"
else
# Fallback claro: instruye como compilar la TUI y deja una shell viva.
left_cmd="echo 'fleetview no compilado: cd apps/fleetview && go build -o fleetview .'; exec \"\$SHELL\""
fi
# -----------------------------------------------------------------------
# Montar la sesion tmux SOLO si no existe (idempotencia). Socket aislado $T.
# -----------------------------------------------------------------------
if $T has-session -t "$session" 2>/dev/null; then
echo "launch_fleetclaude: la sesion tmux '$session' ya existe; reutilizandola."
else
echo "launch_fleetclaude: creando sesion tmux '$session' en '$cwd'."
# Sesion detached con ventana 'console', pane 0 en el cwd objetivo.
$T new-session -d -s "$session" -n console -c "$cwd"
# pane 0 (izquierda) = la TUI fleetview (o el fallback claro).
$T send-keys -t "$session":console.0 "$left_cmd" C-m
# pane 1 (derecha) = claude, dividiendo horizontalmente (split lado a lado).
$T split-window -h -t "$session":console -c "$cwd"
$T send-keys -t "$session":console.1 "exec claude --dangerously-skip-permissions" C-m
# Fijar el ancho del pane izquierdo en columnas.
$T resize-pane -t "$session":console.0 -x "$cols"
# Foco inicial en el pane de claude (derecha).
$T select-pane -t "$session":console.1
fi
# -----------------------------------------------------------------------
# Atajos globales (alt+*) en el socket aislado: redirigen la tecla al pane
# de la TUI (console.0) ESTES DONDE ESTES, para controlar la flota sin salir
# del pane de Claude. La TUI (fleetview) es quien interpreta Up/Down/Enter/n.
# `bind -n` = tabla root (sin prefijo). Idempotente: re-set en cada lanzamiento.
# -----------------------------------------------------------------------
$T bind -n M-Up send-keys -t "$session":console.0 Up
$T bind -n M-Down send-keys -t "$session":console.0 Down
$T bind -n M-Enter send-keys -t "$session":console.0 Enter
$T bind -n M-n send-keys -t "$session":console.0 n
$T bind -n M-0 send-keys -t "$session":console.0 n
$T bind -n M-k send-keys -t "$session":console.0 k
$T bind -n M-r send-keys -t "$session":console.0 r
$T bind -n M-u send-keys -t "$session":console.0 u
$T bind -n M-h send-keys -t "$session":console.0 h
$T bind -n M-Left send-keys -t "$session":console.0 Escape
$T bind -n M-q send-keys -t "$session":console.0 Q
# Raton: enruta clicks/rueda al pane bajo el cursor; la TUI los interpreta.
$T set -g mouse on
# Al salir un Claude (exit / Ctrl-D / kill), cerrar su window en vez de
# dejarla muerta ("dead" pane) en la sesion.
$T set -g remain-on-exit off
# Estetica neutra: sin el verde fosforo por defecto de tmux. Status bar gris y
# bordes de pane gris tenue, iguales en activo e inactivo (separacion simple,
# sin resaltado de enfoque).
$T set -g status-style "bg=colour236,fg=colour250"
$T set -g pane-border-style "fg=colour238"
$T set -g pane-active-border-style "fg=colour240"
# Mantener el ancho del sidebar (pane 0) cuando kitty redimensiona la ventana
# tras el attach, o cuando se conmuta de Claude (window-linked / layout change).
$T set-hook -g client-resized "resize-pane -t $session:console.0 -x $cols"
$T set-hook -g window-layout-changed "resize-pane -t $session:console.0 -x $cols"
# -----------------------------------------------------------------------
# Lanzar kitty adjuntando la sesion, DESACOPLADA del shell padre con
# setsid, para que no muera al cerrar la terminal invocadora.
# (Mismo patron que reboot_all_claudes para relanzar terminales.)
# -----------------------------------------------------------------------
# Adjuntar la sesion:
# - Si se invoca desde una terminal interactiva, convertir ESA terminal en
# el panel FleetView (exec reemplaza el proceso; al hacer detach vuelve la
# shell). Asi `fleetclaude` no abre otra ventana: usa la actual.
# - Si NO hay TTY (atajo de escritorio, cron, script), abrir una ventana
# kitty nueva desacoplada (setsid) como antes.
if [ -t 0 ] && [ -t 1 ]; then
exec tmux -L fleet attach -t "$session"
fi
setsid kitty --title "FleetView" -e tmux -L fleet attach -t "$session" </dev/null >/dev/null 2>&1 &
disown 2>/dev/null || true
echo "launch_fleetclaude: ventana kitty 'FleetView' adjunta a la sesion tmux '$session'."
return 0
}
# Permitir ejecutar el archivo directamente (no solo como funcion sourced).
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
launch_fleetclaude "$@"
fi