--- name: list_claude_fleet kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func ListClaudeFleetFrom(claudeDir string) ([]ClaudeFleet, error) | func ListClaudeFleet() ([]ClaudeFleet, error)" description: "Lista la flota de procesos Claude Code de la maquina local (Linux). Escanea ~/.claude/sessions/*.json, cruza cada PID vivo contra /proc para validar liveness (anti-PID-reciclado via procStart == campo 22 de /proc//stat), une el goal/phase de ~/.claude/goals/.json, extrae KITTY_PID del environ y deriva los campos de display (Target, Rename). Devuelve todas las sesiones ordenadas por status (idle, waiting, busy, otro) y por updatedAt desc; el caller filtra por Alive. Pieza de datos de la app TUI fleetview." tags: [claude-fleet, infra, claude, session, proc, fleet, tui] uses_functions: [] uses_types: [claude_fleet_go_infra] returns: [claude_fleet_go_infra] returns_optional: false error_type: "error_go_core" imports: [] params: - name: "claudeDir" desc: "Directorio raiz de Claude Code a escanear (ej. /home/enmanuel/.claude). ListClaudeFleetFrom lo recibe explicito (testeable con t.TempDir()); ListClaudeFleet lo resuelve via os.UserHomeDir() + .claude." output: "Slice de ClaudeFleet (claude_fleet_go_infra), una entrada por sesion con JSON parseable en sessions/. Cada entrada lleva PID, KittyPID, SessionID, Rename, Target, Goal, Phase, Status, Cwd, TmuxWindow (\"\"), Alive y UpdatedAt. Ordenado por rango de status y luego por UpdatedAt descendente. Devuelve slice vacio (sin error) si la carpeta sessions/ no existe; error si no se puede leer la carpeta por otra causa." tested: true tests: ["TestListClaudeFleetFrom", "TestListClaudeFleetFromMissingDir"] test_file_path: "functions/infra/list_claude_fleet_test.go" file_path: "functions/infra/list_claude_fleet.go" notes: "Misma fuente de verdad que reboot_all_claudes_bash_infra (~/.claude/sessions/.json de Claude Code 2.1.x: pid, sessionId, cwd, procStart, status, updatedAt). Solo LEE y valida — no relanza ni mata nada. La validacion anti-PID-reciclado replica la del bash (procStart del JSON vs campo 22 de /proc//stat) pero parseando de forma robusta el comm (campo 2 entre parentesis, que puede contener espacios y ')'): se toma lo que hay tras el ULTIMO ')' y starttime es el indice 19 de ese resto. TmuxWindow queda \"\" (se rellena en una fase posterior). Build tag //go:build !windows (depende de /proc, no portable a Windows)." --- ## Ejemplo ```go package main import ( "fmt" "fn-registry/functions/infra" ) func main() { fleet, err := infra.ListClaudeFleet() // escanea ~/.claude if err != nil { panic(err) } for _, c := range fleet { if !c.Alive { continue // el caller filtra las sesiones muertas } fmt.Printf("[%s] %-20s pid=%d kitty=%d %s\n", c.Status, c.Rename, c.PID, c.KittyPID, c.Target) } } ``` ```go // Variante testeable: escanea un directorio arbitrario (fixtures en tests). fleet, _ := infra.ListClaudeFleetFrom("/home/enmanuel/.claude") fmt.Println(len(fleet), "sesiones conocidas") ``` ## Cuando usarla Cuando necesites enumerar las sesiones de Claude Code vivas en la maquina local para mostrarlas, monitorizarlas o actuar sobre ellas (TUI fleetview, dashboards, automatizaciones). Da el join PID -> sessionId -> cwd -> goal/phase ya resuelto y validado contra /proc, en lugar de reimplementarlo a mano cada vez. Usa `ListClaudeFleetFrom` en tests (inyectando un directorio con fixtures) y `ListClaudeFleet` en runtime real. ## Gotchas - **Impura: lee el filesystem y /proc.** No es determinista entre llamadas (las sesiones nacen y mueren). Solo lectura — nunca mata ni relanza procesos. - **Anti-PID-reciclado.** `Alive` solo es true si el proceso existe Y su starttime (campo 22 de `/proc//stat`) coincide con el `procStart` del JSON. Un JSON huerfano cuyo PID fue reasignado a otro proceso se marca `Alive=false` aunque ese PID este vivo. Si el JSON no trae `procStart`, basta con que el proceso exista. - **Parseo del `comm` en /proc//stat.** El campo 2 (comm) va entre parentesis y puede contener espacios y el caracter ')'. La funcion parsea tomando lo que hay tras el ULTIMO ')'; un split ingenuo por espacios daria un starttime equivocado. - **/proc no es portable.** Build tag `//go:build !windows`; depende de `/proc//stat` y `/proc//environ` (Linux). En macOS/BSD no funciona tal cual. - **environ ilegible -> KittyPID=0.** Si `/proc//environ` no es legible (permisos, proceso de otro usuario, o el proceso ya murio entre el ReadDir y el ReadFile) `KittyPID` cae a 0 sin error. Tambien es 0 legitimamente cuando claude no corre bajo kitty (ej. tmux remoto). - **Devuelve TODAS las sesiones con JSON parseable**, vivas o muertas. El caller decide filtrar por `Alive`. Archivos no-`.json` y JSON corrupto se ignoran silenciosamente. - **TmuxWindow siempre "".** Reservado para una fase posterior; hoy no se rellena.