--- name: list_resumable_claudes kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func ListResumableClaudesFrom(claudeDir string) ([]ResumableClaude, error) | func ListResumableClaudes() ([]ResumableClaude, error)" description: "Lista las sesiones de Claude Code CERRADAS que se pueden reabrir con `claude --resume ` (Linux). Escanea ~/.claude/sessions/*.json para construir el conjunto de sessionIds VIVOS (mismo criterio anti-PID-reciclado que list_claude_fleet: procStart == campo 22 de /proc//stat), luego recorre ~/.claude/goals/*.json y devuelve cada sesion cuyo proceso NO esta vivo y que tiene un goal no vacio. Cada entrada lleva session_id, goal, emojis y name (rename) del goal.json, y last_active = mtime del goal.json. Ordenadas por last_active desc y limitadas a 40. Pieza de datos del picker de resume de la app TUI fleetview." tags: [claude-fleet, infra, claude, session, resume, proc, tui] uses_functions: [list_claude_fleet_go_infra] uses_types: [resumable_claude_go_infra] returns: [resumable_claude_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). ListResumableClaudesFrom lo recibe explicito (testeable con t.TempDir()); ListResumableClaudes lo resuelve via os.UserHomeDir() + .claude." output: "Slice de ResumableClaude (resumable_claude_go_infra), una entrada por sesion CERRADA con goal en goals/.json. Cada entrada lleva SessionID (basename del goal.json sin .json), Goal, Emojis, Name (rename) y LastActive (mtime del goal.json en epoch segundos). Excluye las sesiones cuyo proceso sigue vivo (ya en la flota) y las que no tienen goal. Ordenado por LastActive descendente y capado a 40 resultados. Devuelve slice vacio (sin error) si la carpeta goals/ no existe; error si no se puede leer por otra causa." tested: true tests: ["TestListResumableClaudesFrom"] test_file_path: "functions/infra/resumable_claude_test.go" file_path: "functions/infra/resumable_claude.go" notes: "Complementaria de list_claude_fleet_go_infra: aquella lista las sesiones VIVAS, esta las CERRADAS-pero-resumibles. Reutiliza los helpers procIsAlive/procStartTime del mismo paquete infra (definidos en functions/infra/list_claude_fleet.go) — no los redefine. El conjunto de vivos se construye desde sessions/*.json; el catalogo de candidatas desde goals/*.json. El sessionId de una candidata es el basename del goal.json (no hay sessions/.json para ella porque su proceso ya murio). LastActive es el mtime del archivo, no la actividad real de la conversacion. Build tag //go:build !windows (depende de /proc, no portable a Windows)." --- ## Ejemplo ```go package main import ( "fmt" "fn-registry/functions/infra" ) func main() { resumables, err := infra.ListResumableClaudes() // escanea ~/.claude if err != nil { panic(err) } for _, r := range resumables { fmt.Printf("%s %-40s claude --resume %s\n", r.Emojis, r.Goal, r.SessionID) } } ``` ```go // Variante testeable: escanea un directorio arbitrario (fixtures en tests). resumables, _ := infra.ListResumableClaudesFrom("/home/enmanuel/.claude") fmt.Println(len(resumables), "sesiones reabribles") ``` ## Cuando usarla Cuando necesites poblar un picker de "reanudar" en la TUI fleetview (o cualquier UI/automatizacion equivalente): te da las sesiones de Claude Code que ya cerraste pero que tenian un objetivo guardado, listas para `claude --resume `. Excluye las que siguen vivas (esas ya estan en la flota, las lista `list_claude_fleet_go_infra`). Usa `ListResumableClaudesFrom` en tests (inyectando un directorio con fixtures) y `ListResumableClaudes` en runtime real. ## Gotchas - **Impura: lee el filesystem y /proc.** No es determinista entre llamadas (las sesiones nacen y mueren). Solo lectura — nunca relanza ni mata nada. - **El statusline purga goals viejos.** Las sesiones de mas de ~7 dias suelen tener su `goals/.json` purgado por el statusline, asi que dejan de aparecer aqui aunque `claude --resume` siga pudiendo reabrirlas. Esta funcion solo ve lo que queda en `goals/`. - **PID reciclado.** El conjunto de "vivos" usa el mismo guardado anti-PID-reciclado que `list_claude_fleet`: un PID reasignado a otro proceso NO marca la sesion como viva (procStart != campo 22 de /proc//stat), por lo que su goal seguira saliendo como resumible correctamente. - **Orden por mtime, no por actividad real.** `LastActive` es el `mtime` del `goal.json`, que se toca cuando el statusline reescribe el objetivo/fase — no es el instante exacto del ultimo mensaje de la conversacion. Es una aproximacion "lo mas reciente arriba", no un timestamp exacto de actividad. - **Cap a 40.** Solo se devuelven las 40 mas recientes; si hay mas goals cerrados, los antiguos se omiten. - **Goals sin goal o ilegibles se omiten** silenciosamente. Un `goal.json` con `goal` vacio (o solo espacios) no es resumible (no hay trabajo que reanudar). Archivos no-`.json` y JSON corrupto se ignoran. - **/proc no es portable.** Build tag `//go:build !windows`; depende de `/proc//stat` (Linux) para decidir que sesiones estan vivas.