Communitygithub.com

show-n-tell

Crea videos demo narrados y con marca a partir de cualquier sitio web, manejando exploración, guion gráfico, narración TTS, grabación con Playwright, mezcla de audio/video, ajuste de velocidad y superposición de marca.

¿Qué es show-n-tell?

Show-n-tell es una habilidad para agentes de IA (como Claude Code o Cowork) que orquesta un pipeline completo para transformar un sitio web en un video demo narrado y con marca, de aproximadamente 5 minutos de duración. La habilidad guía al agente a través de fases secuenciales: desde la recopilación de requisitos y la captura de autenticación (para sitios con OAuth), hasta la exploración del sitio, la creación de un guion gráfico en YAML (con revisión del usuario en lenguaje natural), la síntesis de narración mediante OpenAI, la grabación con Playwright, la mezcla de audio y video, el ajuste de velocidad, y la superposición de logotipo y colores de marca. Está diseñada para entornos como Claude Code CLI y Cowork, adaptando su comportamiento en cada uno (por ejemplo, usando herramientas de Playwright MCP en CLI o Chrome MCP y 'computer-use' en Cowork). El resultado final es un archivo MP4 pulido, listo para compartir. La habilidad enfatiza la importancia de tener notas de intención claras y un logotipo, rechazando proceder con demos genéricos y sin marca.

Compatible con✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/carveragents/show-n-tell/tree/main/skills/show-n-tell

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿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 con marca 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 cargues por adelantado.

Cuándo usar esta habilidad

  • 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" / "demo del flujo del panel").
  • El usuario tiene acceso a un logotipo o marca denominativa.

Rechaza continuar si el usuario no te da ni notas de intención ni un logotipo. Los demos genéricos con marca predeterminada son malos demos. Pide orientación en lugar de producir silenciosamente un video débil.

Detección de entorno (leer antes de empezar)

Antes de hacer cualquier cosa, determina en qué entorno se está ejecutando Claude:

  • Cowork: mcp__cowork__list_artifacts y mcp__computer-use__screenshot está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:

FaseClaude Code CLICowork
Directorio de trabajo (Fase 5)~/demo-videos/<slug>/request_cowork_directory da la ruta
Exploración del sitio público (Fase 2b)Herramientas Playwright MCPHerramientas Chrome MCP o explore_page.py headless
Captura de autenticación OAuth (Fase 2a)Ejecutar capture_auth.py vía BashEl usuario ejecuta capture_auth.py localmente
Comandos largos (Fases 7–9)Ejecutar sincrónicamentenohup + polling (timeout de 45s de bash)
Entrega (Fase 11)Reportar ruta del mp4Ruta del mp4 + enlace computer://

Fase 1 — Entrevista (recopilar entradas)

Haz una o dos preguntas a la vez, no todas de golpe. Requerido:

  1. URL de destino — confirma que sea alcanzable.
  2. Notas de intención del demo — ¿qué historia cuenta? ¿Qué características importan?
  3. Audiencia + tono — interna/externa/inversores; explicativo/ventas/técnico/informal.
  4. Duración objetivo — por defecto 5 min.
  5. Logotipo — URL o ruta. Si solo hay texto, indica la limitación pero acéptalo.
  6. Colores de marca — primario (tinta) + acento. Si no se especifican, inferirlos del logotipo con PIL.
  7. ¿Autenticación? — ¿el sitio requiere inicio de sesión?
    • No → continuar.
    • Sí, basado en formulario (el sitio tiene sus propios campos de correo/contraseña que controlas) → usar session.pre_session en demo_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 por su cuenta, nunca en el chat. Ver examples/login-flow/.
    • Sí, OAuth / SSO / magic-link / passkey (Google, Microsoft, Okta, etc.) → usar session.storage_state. El usuario NO ejecuta ningún comando. TÚ lanzas helpers/capture_auth.py para ellos en la Fase 2a, y ellos solo inician sesión interactivamente en la ventana del navegador que se abre. El auth.json capturado se usa tanto para la exploración de la Fase 2b como para la grabación de la Fase 8. Ver examples/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 formulario". Para OAuth / SSO / magic-link / 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/<demo-slug>/
uv run helpers/capture_auth.py <login_or_start_url> \
  --out ~/demo-videos/<demo-slug>/auth.json \
  --viewport <recording.viewport de demo_config.yaml; por defecto 1440x900>

Ejecuta esto a través de la herramienta Bash con timeout: 600000 (10 minutos — el máximo). El script abre una ventana de Chromium con cabeza. Dile al usuario en palabras simples:

"Estoy abriendo una ventana del navegador para que inicies sesión. Completa el inicio de sesión (Google, Microsoft, etc., maneja 2FA), navega a 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 viewport; la discrepancia puede invalidar la sesión capturada en el momento de la grabación o exploración.

Si Chromium de Playwright no está instalado todavía (clon nuevo de la habilidad), 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 vía Bash como se describió anteriormente.

Si se ejecuta en Cowork: Claude maneja esto directamente usando herramientas de computer-use y Chrome — el usuario solo necesita iniciar sesión cuando se abre el navegador. Pasos:

  1. Asegúrate de que se conozca una ruta de directorio de trabajo. Si la Fase 5 aún no se ha ejecutado, llama a mcp__cowork__request_cowork_directory ahora para obtener la ruta de salidas y establecer <working_dir> en <outputs_path>/<demo-slug>/.

  2. Abre Chrome en el escritorio del usuario:

    mcp__computer-use__open_application  →  "Google Chrome"  (o "Chrome")
    
  3. 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__navigate a <login_or_start_url>
    • Extensión de Chrome no conectada: usa las herramientas mcp__computer-use__* para hacer clic en la barra de direcciones y escribir la URL
  4. Dile al usuario que inicie sesión — di en palabras simples:

    "Chrome está abierto en la página de inicio de sesión. Por favor, inicia sesión (maneja cualquier 2FA, SSO, etc.), luego navega a la página donde debe comenzar la grabación. Avísame cuando hayas terminado."

  5. Espera y observa. Haz polling con mcp__computer-use__screenshot cada ~15 segundos. Continúa una vez que la URL ya no sea una página de inicio de sesión/autenticación y un panel o página de aplicación sea visible.

  6. Extrae el estado de la sesión via 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);
    })()
    
  7. Escribe el resultado en <working_dir>/auth.json usando la herramienta de escritura. Este es un formato storage_state válido de Playwright — helpers/explore_page.py y record_demo.py lo consumen a través de browser.new_context(storage_state=...).

  8. Advertencia sobre cookies HttpOnly. JavaScript no puede leer cookies HttpOnly — son intencionalmente invisibles para document.cookie. Si el token de sesión del sitio es solo HttpOnly (común con cookies de sesión antiguas de Rails/Django), el estado extraído lo omitirá. Díselo 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 que se capturó — si encuentra un muro de inicio de sesión durante la Fase 8, podemos recurrir a usar capture_auth.py localmente." 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ó (existe auth.json), 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 demos 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/<demo-slug>/_explore/
uv run helpers/explore_page.py https://target.example.com/dashboard \
  --storage-state ~/demo-videos/<demo-slug>/auth.json \
  --out-dir ~/demo-videos/<demo-slug>/_explore/

Esto escribe tres archivos por página en _explore/: <slug>.png (captura de pantalla), <slug>.dom.html (DOM para selección de selectores), <slug>.meta.json ({url_solicitada, url_final, título, estado}). Lee el PNG con la herramienta de lectura para ver la página; busca en el DOM los selectores. Si final_url en meta.json apunta a una página de inicio de sesión, la sesión ha expirado — vuelve a ejecutar la Fase 2a.

Para páginas públicas (página de aterrizaje, páginas de marketing en cualquier demo; todo en un demo sin autenticación) — usa las herramientas Playwright MCP (mcp__plugin_playwright_playwright__*) como antes:

  • browser_navigate a la URL de destino, luego a 2–5 páginas que mencione la intención del usuario.
  • browser_snapshot o browser_take_screenshot para ver el diseño de cada página.
  • Usa browser_evaluate para leer el DOM en busca de selectores estables (prefiere semántico sobre nth-of-type cuando sea posible).

Si se ejecuta en Claude Code CLI: usa las herramientas mcp__plugin_playwright_playwright__* como se describió anteriormente.

Si se ejecuta en Cowork: Primero asegúrate de que Chromium esté disponible:

uv run --with playwright playwright install chromium

Luego elige el mejor método disponible:

  • Si mcp__Claude_in_Chrome__navigate está disponible (extensión de Chrome conectada), usa mcp__Claude_in_Chrome__navigate + mcp__Claude_in_Chrome__read_page para explorar el sitio interactivamente.
  • De lo contrario, recurre a la exploración headless:
    mkdir -p <working_dir>/_explore/
    uv run helpers/explore_page.py <url> --out-dir <working_dir>/_explore/
    
    (No se necesita --storage-state para páginas públicas — el argumento ahora es opcional.)

Anota para cada página significativa:

  • Momentos dignos del demo (desplazarse a la característica, clic para detalle, tooltip al pasar el ratón).
  • Adaptaciones del sitio necesarias: ¿truco de encabezado fijo? ¿Enlace profundo a PDF? ¿Modales bloqueadores? ¿Muro de autenticación?
  • Selectores que parezcan frágiles y que puedan necesitar defensas wait_for_selector.

No rastrees exhaustivamente. 2–5 acciones por página es suficiente.

Fase 3 — Borrador de storyboard.yaml

Genera 15–30 beats. Cada beat = una acción de cámara + 1–3 oraciones de narración. Consulta docs/SCHEMAS.md para la gramática de acciones (subconjunto de Fase A: goto, goto_and_scroll, scroll_into_view, scroll_y, hover, click).

Objetivo para un video de 5 minutos a velocidad 1.2x:

  • Tiempo total post-aceleración: ~5:00
  • Tiempo total de ejecución bruta: ~6:00
  • Promedio por beat: ~12s, de los cuales ~9–10s es narración

Reglas estrictas al redactar la narración:

  1. Cada afirmación factual debe ser visible en la pantalla en el fotograma de este beat. Sin números inventados ("el agente marcó 199 cambios"). Si no lo ves en la captura de pantalla, no lo digas.
  2. Las siglas que se leen letra por letra deben escribirse con guiones: S-P-M-E, K-Y-B, A-P-I. El prompt de TTS aplica esta convención.
  3. La longitud coincide con el contenido visual: 8–18s para beats de desplazamiento, 6–12s para beats de clic.
  4. El tono coincide con el campo voice.tone de branding.yaml.

Agrupa mentalmente los beats en segmentos (Resumen / Característica A / Característica B / Cierre) pero escribe una lista plana en YAML — sin marcadores de sección.

Fase 4 — Revisión en lenguaje natural

El usuario no edita YAML directamente. Presentas el borrador como una lista numerada y aceptas comentarios en lenguaje natural. Usa este formato exactamente:

Aquí está el flujo del demo que he redactado. Tiempo estimado: 5:08 (después de aceleración 1.2x).
Dime qué te gustaría cambiar.

RESUMEN (60s)
1. Aterrizar en la página de inicio (10s) — "Bienvenido a Acme Cloud, una plataforma serverless para equipos pequeños..."
2. Desplazarse a las características (15s) — "Tres capacidades principales impulsan la plataforma..."
3. Desplazarse a los resultados (8s) — "Estas diez áreas de política..."

CARACTERÍSTICA 1 — Panel (45s)
4. Navegar al panel (8s) — "Haciendo clic en el panel..."
5. Pasar el ratón sobre el feed de actividad en vivo (12s) — "Cada proyecto muestra un feed de actividad en vivo..."
...

CIERRE (20s)
27. Volver a las estadísticas de la página de inicio (12s) — "Tres razones por las que los equipos eligen Acme..."
28. Outro (8s)

Luego: "¿Algo que añadir, eliminar o reordenar? ¿Algún ajuste en el tono de la narración? ¿Algo que omitir?"

Traduce los comentarios en lenguaje natural a ediciones en YAML:

  • "Saltar beat 7" → eliminar del YAML
  • "Hacer el beat 3 más largo" → extender la narración con más detalle en pantalla (dividir en dos beats si es mucho más largo)
  • "Mencionar precios en algún lado" → identificar el beat correcto y añadir una oración (verificar que los precios sean realmente visibles en el fotograma de ese beat)
  • "Tono demasiado formal" → reescribir la narración con un lenguaje más suelto
  • "Mostrar la página del equipo en lugar de acerca de" → intercambiar URL + ajustar narración

Vuelve a presentar el flujo actualizado. Itera hasta que el usuario apruebe explícitamente.

Verificación de desviación de tiempo antes de TTS: suma len(beat.narration) / 16 (caracteres/segundo a 140 ppm) en todos los beats. Si estimado * (1 / speed_multiplier) se desvía más de 30s del objetivo, indícalo: "Esto actualmente llega a ~6:20 post-aceleración frente a tu objetivo de 5 min. ¿Quieres que recorte?"

Fase 5 — Configuración del directorio de trabajo

mkdir -p ~/demo-videos/<demo-slug>/_assets
cp <ruta_del_logotipo>             ~/demo-videos/<demo-slug>/_assets/<logo>.png
# escribe storyboard.yaml, branding.yaml, demo_config.yaml de tus borradores

Si se ejecuta en Claude Code CLI: usa ~/demo-videos/<demo-slug>/ como se muestra arriba.

Si se ejecuta en Cowork: llama a mcp__cowork__request_cowork_directory para obtener la ruta de salidas del usuario. Establece el directorio de trabajo en <outputs_path>/<demo-slug>/. Usa esta ruta para TODAS las escrituras de archivos posteriores en las Fases 6–11 — guárdala ahora y mantenla consistente durante toda la sesión. Crea el subdirectorio _assets/ bajo esta ruta, no bajo ~/demo-videos/.

Si el usuario no tiene un archivo de logotipo en el disco y solo tiene una URL, descárgalo con curl y colócalo en _assets/. Renómbralo para que coincida con el nombre de archivo que usarás en branding.yaml.

Skills relacionados