¿Qué hace show-n-tell?
Orquestas un pipeline completo que convierte cualquier sitio web + un logotipo + un poco de intención en un video demo narrado y personalizado de 5 minutos. Los scripts en scripts/ son tuberías simples; tú eres el director.
Documentos de referencia en esta carpeta: docs/SCHEMAS.md (gramática YAML + acciones), docs/GOTCHAS.md (problemas conocidos + soluciones), docs/CONTEXT.md ("cómo se ve un buen resultado"), examples/halyard-spme/ (ejemplo completo canónico). Léelos según sea necesario, no los precargues.
Cuándo usar este skill
- El usuario quiere un video demo narrado de un sitio web (en vivo, desarrollo local o estático).
- El usuario tiene al menos una guía de intención ligera ("muestra X, Y, Z" / "demuestra el flujo del panel").
- El usuario tiene acceso a un logotipo o marca textual.
Rechaza continuar si el usuario no te da ni notas de intención ni un logotipo. Las demostraciones genéricas con marca predeterminada son malas demostraciones. Pide orientación en lugar de producir silenciosamente un video deficiente.
Detección del entorno (leer antes de comenzar)
Antes de hacer cualquier cosa, determina en qué entorno se está ejecutando Claude:
- Cowork:
mcp__cowork__list_artifactsymcp__computer-use__screenshotestán disponibles en la lista de herramientas de Claude. - Claude Code CLI: esas herramientas están ausentes.
Esto es importante porque varias fases se comportan de manera diferente entre entornos. Referencia rápida:
| Fase | Claude Code CLI | Cowork |
|---|---|---|
| Directorio de trabajo (Fase 5) | ~/demo-videos/<slug>/ | request_cowork_directory muestra la ruta |
| Exploración del sitio pública (Fase 2b) | Herramientas MCP de Playwright | Herramientas MCP de Chrome o explore_page.py sin cabeza |
| Captura de autenticación OAuth (Fase 2a) | Ejecuta capture_auth.py mediante Bash | El usuario ejecuta capture_auth.py localmente |
| Comandos largos (Fases 7–9) | Ejecutar sincrónicamente | nohup + sondeo (timeout de bash de 45s) |
| Entrega (Fase 11) | Reportar ruta del mp4 | Ruta del mp4 + enlace computer:// |
Fase 1 — Entrevista (recopilar entradas)
Haz una o dos preguntas a la vez, no todas. Obligatorio:
- URL objetivo — confirma que sea accesible.
- Notas de intención de la demo — ¿qué historia cuenta? ¿Qué características importan?
- Audiencia + tono — interna/externa/inversores; explicativo/ventas/técnico/informal.
- Duración objetivo — predeterminado 5 min.
- Logotipo — URL o ruta. Si solo hay texto disponible, señala la limitación pero acéptalo.
- Colores de marca — primario (tinta) + acento. Si no se especifican, inferir del logotipo con PIL.
- ¿Autenticación? — ¿el sitio requiere inicio de sesión?
- No → continuar.
- Sí, basado en formularios (el sitio tiene sus propios campos de correo/contraseña que controlas) → usa
session.pre_sessionendemo_config.yaml. Recopila los nombres de las variables de entorno de las credenciales (NO los valores reales) y la URL de inicio de sesión/los selectores. Dile al usuario que ponga las credenciales reales en<working_dir>/.envél mismo antes de ejecutar, nunca en el chat. Verexamples/login-flow/. - Sí, OAuth / SSO / enlace mágico / passkey (Google, Microsoft, Okta, etc.) → usa
session.storage_state. El usuario NO ejecuta ningún comando. TÚ lanzashelpers/capture_auth.pypara ellos en la Fase 2a, y ellos solo inician sesión interactivamente en la ventana del navegador que se abre. Elauth.jsoncapturado se usa tanto para la exploración de la Fase 2b como para la grabación de la Fase 8. Verexamples/oauth-storage-state/.
Fase 2a — Captura de autenticación (solo OAuth)
Salta esta fase por completo si la pregunta 7 de la Fase 1 determinó "sin autenticación" o "basado en formularios". Para OAuth / SSO / enlace mágico / passkey, captura una sesión autenticada AHORA, antes de la exploración del sitio — la Fase 2b la necesita para ver páginas autenticadas.
mkdir -p ~/demo-videos/<slug-de-demo>/
uv run helpers/capture_auth.py <url_de_inicio_o_login> \
--out ~/demo-videos/<slug-de-demo>/auth.json \
--viewport <recording.viewport de demo_config.yaml; predeterminado 1440x900>
Ejecuta esto mediante la herramienta Bash con timeout: 600000 (10 minutos — el máximo). El script abre una ventana de Chromium con interfaz gráfica. Dile al usuario en palabras claras:
"Estoy abriendo una ventana del navegador para que inicies sesión. Completa el inicio de sesión (Google, Microsoft, etc., maneja 2FA), navega hasta la página desde la que quieres que comience la grabación, luego cierra la ventana del navegador. Guardaré la sesión y continuaré automáticamente."
El script escribe auth.json con modo 0600 de forma atómica. Si el usuario tarda más de 10 minutos (raro, pero posible con 2FA basada en teléfono en otra habitación), el timeout de Bash se activa — solo vuelve a ejecutar el mismo comando.
--viewport DEBE coincidir con recording.viewport en demo_config.yaml. Algunos sitios toman la huella del tamaño de la ventana; la discrepancia puede invalidar la sesión capturada al grabar o explorar.
Si Chromium de Playwright aún no está instalado (clon del skill reciente), el script muestra un error procesable. Ejecuta uv run --with playwright playwright install chromium una vez.
Si se ejecuta en Claude Code CLI: ejecuta capture_auth.py mediante Bash como se describe arriba.
Si se ejecuta en Cowork: Claude maneja esto directamente usando herramientas de uso de computadora y Chrome — el usuario solo necesita iniciar sesión cuando se abre el navegador. Pasos:
-
Asegúrate de que se conozca la ruta del directorio de trabajo. Si la Fase 5 aún no se ha ejecutado, llama a
mcp__cowork__request_cowork_directoryahora para obtener la ruta de salidas y establece<working_dir>en<outputs_path>/<slug-de-demo>/. -
Abre Chrome en el escritorio del usuario:
mcp__computer-use__open_application → "Google Chrome" (o "Chrome") -
Navega a la URL de inicio de sesión. Prefiere el MCP de Chrome si está conectado:
- Extensión de Chrome disponible:
mcp__Claude_in_Chrome__navigatea<url_de_inicio_o_login> - Extensión de Chrome no conectada: usa las herramientas
mcp__computer-use__*para hacer clic en la barra de direcciones y escribir la URL
- Extensión de Chrome disponible:
-
Dile al usuario que inicie sesión — di en palabras claras:
"Chrome está abierto en la página de inicio de sesión. Por favor, inicia sesión (maneja cualquier 2FA, SSO, etc.), luego navega hasta la página donde debe comenzar la grabación. Avísame cuando hayas terminado."
-
Espera y observa. Sondea con
mcp__computer-use__screenshotcada ~15 segundos. Continúa una vez que la URL ya no sea una página de inicio de sesión/auth y se vea un panel o una página de aplicación. -
Extrae el estado de la sesión mediante JavaScript. Ejecuta este fragmento con
mcp__Claude_in_Chrome__javascript_tool:(() => { const cookies = document.cookie.split(';').map(c => { const [name, ...rest] = c.trim().split('='); return { name: name.trim(), value: rest.join('='), domain: location.hostname, path: '/', expires: -1, httpOnly: false, secure: location.protocol === 'https:', sameSite: 'Lax' }; }).filter(c => c.name); const ls = {}; for (let i = 0; i < localStorage.length; i++) { const k = localStorage.key(i); ls[k] = localStorage.getItem(k); } const ss = {}; for (let i = 0; i < sessionStorage.length; i++) { const k = sessionStorage.key(i); ss[k] = sessionStorage.getItem(k); } return JSON.stringify({ cookies, origins: [{ origin: location.origin, localStorage: Object.entries(ls).map(([n,v]) => ({name:n,value:v})), sessionStorage: Object.entries(ss).map(([n,v]) => ({name:n,value:v})) }] }, null, 2); })() -
Escribe el resultado en
<working_dir>/auth.jsonusando la herramienta Write. Este es un formatostorage_stateválido de Playwright —helpers/explore_page.pyyrecord_demo.pylo consumen mediantebrowser.new_context(storage_state=...). -
Advertencia sobre cookies HttpOnly. JavaScript no puede leer cookies HttpOnly — son intencionalmente invisibles para
document.cookie. Si la cookie de sesión del sitio es solo HttpOnly (común en sesiones antiguas de Rails/Django), el estado extraído no la incluirá. Dile al usuario:"Nota: las cookies de sesión marcadas como HttpOnly no son accesibles desde JavaScript, por lo que es posible que no haya capturado todos los tokens de autenticación. La grabación intentará usar lo capturado — si encuentra un muro de inicio de sesión durante la Fase 8, podemos recurrir a usar
capture_auth.pylocalmente." Luego continúa — muchos sitios modernos (Google, Microsoft, Okta) mantienen suficiente estado en cookies no HttpOnly o localStorage para que Playwright funcione.
Fase 2b — Exploración del sitio
Si la Fase 2a se ejecutó (auth.json existe), usa helpers/explore_page.py para ver páginas autenticadas — el MCP de Playwright no puede cargar storage_state y solo vería muros de inicio de sesión. Para demostraciones sin autenticación, el MCP de Playwright está bien y es más interactivo; úsalo como antes.
Para páginas autenticadas:
mkdir -p ~/demo-videos/<slug-de-demo>/_explore/
uv run helpers/explore_page.py https://ejemplo.com/panel \
--storage-state ~/demo-videos/<slug-de-demo>/auth.json \
--out-dir ~/demo-videos/<slug-de-demo>/_explore/
Esto escribe tres archivos por página en _explore/: <slug>.png (captura de pantalla), <slug>.dom.html (DOM para seleccionar selectores), <slug>.meta.json ({url_solicitada, url_final, título, estado}). Lee el PNG con la herramienta Read para ver la página; busca en el DOM selectores. Si final_url en meta.json apunta a una página de inicio de sesión, la sesión expiró — vuelve a ejecutar la Fase 2a.
Para páginas públicas (página de aterrizaje, páginas de marketing en cualquier demo; todo en una demo sin autenticación) — usa las herramientas MCP de Playwright (mcp__plugin_playwright_playwright__*) como antes:
browser_navigatea la URL objetivo, luego a 2–5 páginas que mencione la intención del usuario.browser_snapshotobrowser_take_screenshotpara ver el diseño de cada página.- Usa
browser_evaluatepara leer el DOM y obtener selectores estables (prefiere semánticos sobrenth-of-typecuando sea posible).
Si se ejecuta en Claude Code CLI: usa las herramientas mcp__plugin_playwright_playwright__* como se describe arriba.
Si se ejecuta en Cowork: Primero asegúrate de que Chromium esté