Files
agent 697c523604 feat: scaffold claude_extract — captura headless de TUI via PTY
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.
2026-06-03 22:28:06 +02:00

140 lines
6.8 KiB
Markdown

---
name: claude_extract
lang: go
domain: infra
version: 0.1.0
description: "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."
tags: [cli, terminal, pty, tui, automation, capture]
uses_functions:
- pty_capture_idle_go_infra
- vt_render_go_tui
- strip_ansi_go_core
uses_types: []
framework: ""
entry_point: "main.go"
dir_path: "apps/claude_extract"
icon:
phosphor: "terminal-window"
accent: "#7c3aed"
e2e_checks:
- id: build
cmd: "CGO_ENABLED=1 go build -tags fts5 -o claude_extract ."
timeout_s: 120
- id: smoke_capture
cmd: "./claude_extract --cmd bash --arg -lc --arg 'echo CAPTURA_OK' --warmup 200ms --idle 400ms --max 5s"
expect_stdout_contains: "CAPTURA_OK"
timeout_s: 15
- id: smoke_screen_layout
cmd: "./claude_extract --cmd bash --arg -lc --arg $'printf \"foo\\033[10Gbar\\n\"' --mode screen --warmup 200ms --idle 400ms --max 5s"
expect_stdout_contains: "foo"
timeout_s: 15
- id: smoke_stream_strip
cmd: "./claude_extract --cmd bash --arg -lc --arg $'printf \"\\033[31mROJO\\033[0m\\n\"' --mode stream --warmup 200ms --idle 400ms --max 5s"
expect_stdout_contains: "ROJO"
timeout_s: 15
- id: smoke_exec_pipe
cmd: "./claude_extract --cmd bash --arg -lc --arg 'echo pipe ok' --warmup 200ms --idle 400ms --max 5s --exec 'tr a-z A-Z'"
expect_stdout_contains: "PIPE OK"
timeout_s: 15
---
# claude_extract
## Que hace
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
```bash
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.