App CLI que automatiza una TUI interactiva a traves de un pseudo-terminal y
captura su texto. Pensada para la CLI claude (solo interactiva con TTY real),
generica para cualquier TUI.
- Modo screen: reconstruye layout 2D con vt_render_go_tui (emulador VT100).
- Modo stream: limpia ANSI de output secuencial con strip_ansi_go_core.
- Modo raw: bytes del PTY intactos.
- --exec pipea el texto a otro proceso; --cwd salta el dialogo MCP de claude.
Captura via pty_capture_idle_go_infra. Validada end-to-end contra claude
(prompt enviado, respuesta capturada) y con 5 e2e_checks POSIX deterministas.
CLI que automatiza una TUI interactiva a traves de un pseudo-terminal (PTY) headless y captura su texto. Pensada para la CLI 'claude' (solo entra en modo interactivo con un TTY real) pero sirve para cualquier TUI. Reconstruye el layout 2D con un emulador VT, o limpia ANSI de output secuencial, y permite pipear el resultado a otro proceso.
cli
terminal
pty
tui
automation
capture
pty_capture_idle_go_infra
vt_render_go_tui
strip_ansi_go_core
main.go
apps/claude_extract
phosphor
accent
terminal-window
#7c3aed
id
cmd
timeout_s
build
CGO_ENABLED=1 go build -tags fts5 -o claude_extract .
Automatiza una CLI interactiva (TUI) y captura su texto, de forma headless, a traves de un
pseudo-terminal (PTY). Nunca abre una ventana de terminal: el PTY es virtual, en memoria.
Existe porque algunas CLIs — sobre todo la CLI claude — solo entran en su modo interactivo
rico cuando detectan un TTY real. Un pipe normal las degrada a modo "print". claude_extract
le da al proceso hijo un PTY real, lo dirige con input scripteado (teclea el prompt, espera, y
pulsa Enter como pasos separados), espera a que el render se estabilice, y devuelve el texto.
Por defecto el texto se imprime limpio a stdout. Con --exec se pipea a otro proceso por
stdin. Con --mode raw se obtienen los bytes del terminal sin tocar.
Arquitectura
La app es solo orquestacion + superficie de linea de comandos + defaults amables con claude.
Toda la logica reutilizable vive en el registry:
Pieza
Funcion del registry
Rol
Captura PTY
pty_capture_idle_go_infra
Lanza el comando en un PTY, inyecta input, corta por inactividad o timeout, devuelve bytes crudos.
Render de pantalla
vt_render_go_tui
Emula un terminal VT100 y reconstruye el layout 2D (espacios entre columnas que en el stream eran movimientos de cursor). Modo screen.
Limpieza de stream
strip_ansi_go_core
Quita secuencias ANSI de output secuencial tipo log. Modo stream.
Modos de salida (--mode)
Modo
Que hace
Cuando
screen (default)
Reconstruye el layout 2D con vt_render.
TUIs que posicionan texto con cursor absoluto: claude, htop, dialog. Sin esto las palabras quedan pegadas ("2newMCPservers").
stream
Quita ANSI del stream con strip_ansi.
Output secuencial: logs, builds, comandos que imprimen linea a linea.
raw
Bytes del PTY intactos (ANSI incluido).
Cuando quieres procesar los escape codes tu mismo. Atajo: --raw.
Ejemplo
cd apps/claude_extract
CGO_ENABLED=1 go build -tags fts5 -o claude_extract .
# Preguntar a claude y obtener su respuesta como texto con layout (modo screen).# --cwd apunta a un repo donde los MCP de claude ya estan aprobados, para saltar# el dialogo de arranque "new MCP servers found".
./claude_extract \
--prompt "responde unicamente con la palabra PONG"\
--cwd /home/enmanuel/fn_registry \
--warmup 4s --step-delay 600ms --idle 4s --max 60s
# Capturar una TUI cualquiera (sin prompt), output secuencial limpio.
./claude_extract --cmd bash --arg -lc --arg 'echo hola' --mode stream
# Pipear el texto capturado a otro proceso por stdin.
./claude_extract --prompt "lista 5 ideas" --cwd /home/enmanuel/fn_registry --exec "tee ideas.txt"# Leer el prompt de un pipe.echo"explica este error"| ./claude_extract --cwd /home/enmanuel/fn_registry
Flags
Flag
Default
Que hace
--cmd
claude
Comando a lanzar dentro del PTY.
--arg
—
Argumento extra para --cmd (repetible).
--prompt
—
Texto que se teclea primero, seguido de Enter. Si vacio y stdin es un pipe, se lee de stdin.
--send
—
Input crudo extra tras el prompt (repetible). Incluye \r para Enter, ej. --send $'\r'.
--mode
screen
screen | stream | raw. Ver tabla de modos.
--raw
false
Atajo de --mode raw.
--warmup
2.5s
Espera antes de enviar input, para que la TUI cargue.
--step-delay
300ms
Espera entre inputs sucesivos (entre teclear y Enter).
--idle
2.5s
Corta la captura tras este silencio (sin bytes nuevos de la TUI).
--max
120s
Timeout duro de toda la captura.
--exec
—
Pipea el texto capturado al stdin de este comando (via sh -c).
--out
—
Tambien escribe el texto capturado a este archivo.
--cwd
—
Ejecuta el hijo en este directorio (util para saltar el dialogo MCP de claude).
Cuando usarla
Cuando necesites el render real de una CLI interactiva como texto, para auditar, scriptear o
alimentar otro proceso.
Cuando claude -p (modo print) no te sirva porque quieres exactamente lo que muestra la TUI.
Gotchas
Linux/Unix only: el PTY es POSIX (heredado de pty_capture_idle_go_infra).
Enter separado: el prompt y el Enter se envian como pasos distintos a proposito; un \r
pegado al texto lo trata claude como newline literal en el input, no como submit.
Layout pegado en modo stream: para TUIs con posicionamiento absoluto usa --mode screen.
--mode stream (strip_ansi) pega las palabras porque los espacios entre columnas eran
movimientos de cursor.
Spinners y el corte por idle: si la TUI hace render periodico (spinner, reloj), el --idle
no se dispara y la captura cae al --max. Para claude, el spinner se detiene al terminar la
respuesta, asi que --idle corta poco despues; sube --max si la respuesta es larga.
Dialogo de arranque de claude: en un cwd cuyos MCP no estan aprobados, claude muestra
"new MCP servers found" y bloquea. Usa --cwd <repo-raiz-aprobado> o despacha el dialogo con
--send.
Dimensiones fijas 40x120: el PTY y el render usan 40 filas x 120 columnas. Una respuesta
mas ancha se envuelve a 120 columnas.
Sin color: el modo screen reconstruye texto y layout, no color.