feat(infra): grupo claude-fleet — FleetView TUI + orquestacion de Claudes
Sistema FleetView para centralizar la flota de procesos Claude Code vivos en una sola ventana kitty + tmux (socket aislado -L fleet) con un panel TUI: - list_claude_fleet (+ tipo claude_fleet): escanea ~/.claude/sessions + goals + runtime, valida procesos vivos (anti-PID-reciclado), join por sessionId. - list_resumable_claudes (+ tipo resumable_claude): sesiones cerradas reanudables. - wrappers tmux: tmux_new_claude_window (con --resume), tmux_swap_window_into_console (preserva ancho del sidebar), tmux_map_claude_panes. - launch_kittyclaude: comando entrypoint; instala atajos alt+flechas/enter/n/0/k/r, mouse on, remain-on-exit off; fija el ancho del sidebar con hooks. - docs/capabilities/claude-fleet.md + entrada en el INDEX. Incluye ademas funciones datascience en progreso (excel/duckdb/postgres) y ajustes varios de docs e infra de otra sesion, agrupados aqui para no perderlos. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
//go:build !windows && linux
|
||||
|
||||
package infra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// tmuxAvailable reports whether the tmux binary is present. Tests skip when it
|
||||
// is not (CI without tmux).
|
||||
func tmuxAvailable(t *testing.T) {
|
||||
t.Helper()
|
||||
if _, err := exec.LookPath("tmux"); err != nil {
|
||||
t.Skipf("tmux no disponible en PATH: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// isolatedSocket returns a per-test isolated tmux socket name and registers a
|
||||
// cleanup that kills its server. All commands in a test run against
|
||||
// `tmux -L <socket> ...`, never the user's default server.
|
||||
func isolatedSocket(t *testing.T) string {
|
||||
t.Helper()
|
||||
socket := fmt.Sprintf("fleettest_%d_%d", os.Getpid(), time.Now().UnixNano())
|
||||
t.Cleanup(func() {
|
||||
// best-effort: el server puede no existir si el test fallo antes de crearlo
|
||||
_ = exec.Command("tmux", "-L", socket, "kill-server").Run()
|
||||
})
|
||||
return socket
|
||||
}
|
||||
|
||||
// startConsoleSession crea una sesion <session> con una window "console" cuyo
|
||||
// pane 0 corre `cat` (simula la TUI fleetview, un proceso que no termina).
|
||||
func startConsoleSession(t *testing.T, socket, session string) {
|
||||
t.Helper()
|
||||
cmd := exec.Command("tmux", "-L", socket,
|
||||
"new-session", "-d", "-s", session, "-n", "console", "cat")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("new-session: %v (%s)", err, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTmuxNewClaudeWindow(t *testing.T) {
|
||||
tmuxAvailable(t)
|
||||
socket := isolatedSocket(t)
|
||||
session := "fleet"
|
||||
startConsoleSession(t, socket, session)
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("getwd: %v", err)
|
||||
}
|
||||
|
||||
// El comando real ("claude ...") no esta disponible en el test, pero
|
||||
// new-window devuelve el window_id ANTES de que el comando pueda fallar:
|
||||
// tmux crea la window y reporta su id sincronamente. Validamos que el id
|
||||
// venga con la forma esperada (@N) y no vacio.
|
||||
windowID, err := TmuxNewClaudeWindow(socket, session, cwd)
|
||||
if err != nil {
|
||||
t.Fatalf("TmuxNewClaudeWindow: %v", err)
|
||||
}
|
||||
if windowID == "" {
|
||||
t.Fatal("window_id vacio")
|
||||
}
|
||||
if !strings.HasPrefix(windowID, "@") {
|
||||
t.Errorf("window_id %q no tiene la forma esperada @N", windowID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTmuxNewClaudeWindowEmptyArgs(t *testing.T) {
|
||||
if _, err := TmuxNewClaudeWindow("", "fleet", "/tmp"); err == nil {
|
||||
t.Error("socket vacio deberia dar error")
|
||||
}
|
||||
if _, err := TmuxNewClaudeWindow("sock", "", "/tmp"); err == nil {
|
||||
t.Error("session vacia deberia dar error")
|
||||
}
|
||||
if _, err := TmuxNewClaudeWindow("sock", "fleet", ""); err == nil {
|
||||
t.Error("cwd vacio deberia dar error")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user