project-demo-video
Construa o vídeo de demonstração como código para que possa ser re-renderizado em minutos após qualquer alteração. Tudo na tela é real: o aplicativo em execução, a saída real dos comandos, os arquivos fonte reais. Nada é uma maquete.
Este skill foi escrito para que um agente de codificação menos capaz possa segui-lo passo a passo. Siga as fases em ordem. Cada fase tem uma linha Concluído quando: não prossiga até que seja verdade. Copie os arquivos de templates/ em vez de escrevê-los novamente.
Instalação: copie toda esta pasta (SKILL.md e templates/) para a pasta de skills do agente, por exemplo ~/.claude/skills/project-demo-video/, ou apenas aponte o agente para SKILL.md.
O que você produz
| Saída | Notas |
|---|---|
<nome>-voiced.mp4 | 1920x1080, 30 fps, H.264 + AAC, legendas incorporadas, com voz |
<nome>-captions-only.mp4 | mesma imagem, faixa de áudio silenciosa (o usuário pode escolher esta ou gravar sua própria voz) |
<nome>.srt | legendas como arquivo, mesmo texto da narração |
thumbnail.png | 1280x720 |
narration.md, shotlist.md | roteiro com timestamps reais; cada cena com sua fonte |
frames/ | os quadros de controle de qualidade que você realmente examinou |
repo/ | arquivos do projeto alterados ou novos, espelhados em seus caminhos de repositório |
COPY_LIST.md, README-snippet.md, REPORT.md | o que copiar para onde, uma seção README "Veja funcionar", o que está feito e o que é arriscado |
0. Regras que você não deve quebrar
- Fonte somente leitura. Nunca escreva, faça commit ou envie push nos repositórios reais do usuário. Trabalhe em uma cópia (
robocopy/cp, excluindonode_modules,.git, saída de build), depois executenpm cilá. Mantenha as alterações que valem a pena na pasta de saída, espelhando os caminhos do repositório, maisCOPY_LIST.md. Outra ferramenta ou pessoa faz o trabalho com git. - Seja honesto na tela. Qualquer coisa simulada (um dispositivo, um conector, um pagamento, um modelo) é rotulada como simulada no vídeo. Nunca insinue hardware, implantações ou usuários que não existem. Copie a própria tabela de "real vs simulado" do projeto; se o README estiver errado, diga isso no relatório e não repita a afirmação.
- Sem segredos na tela ou em arquivos que você enviar: tokens, chaves de API, conteúdo de
.env, chaves privadas, e-mails reais. Tokens de desenvolvimento que o projeto documenta (por exemploBearer dev-alice) são aceitáveis. Ao mostrar código, escolha intervalos de linhas que excluam segredos. - Nenhuma menção à ferramenta de IA que escreveu o código, ou seu fornecedor, em quadros, narração, legendas ou documentos enviados, a menos que o usuário diga o contrário. Um ID de modelo em tempo de execução que faça parte da configuração do produto pode aparecer como um nome de modelo. Verifique com
grep -riantes de terminar. - Respeite as regras da competição. Leia o limite de duração (geralmente "menos de 3:00"; mire em 2:50) e a regra de tecnologia exigida. O vídeo deve mostrar a tecnologia exigida realmente funcionando, não apenas mencioná-la.
- Camada gratuita, sem implantações, sem compras. Não implante, crie recursos em nuvem ou gaste dinheiro. Se uma etapa precisar de uma credencial ou gravação do usuário, pergunte uma vez, claramente, e continue com todo o resto.
- Peça ao usuário apenas credenciais, suas próprias gravações, ou uma escolha que seja realmente dele (por exemplo qual voz). Decida todo o resto sozinho e diga o que decidiu.
1. Pré-requisitos (verifique, não presuma)
node --version # 22+ (24 recomendado; arquivos .mjs devem ser JavaScript puro, sem casts do TypeScript)
- Na cópia do projeto:
@playwright/test(com Chromium instalado) effmpeg-staticdevem existir. Verifique:node -e "import('@playwright/test').then(async m=>{const b=await m.chromium.launch();console.log(b.version());await b.close()})"enode -e "import('ffmpeg-static').then(m=>console.log(m.default))". Se estiver faltando e o projeto proibir novas dependências, coloque-os em uma pasta de ferramentas separada e aponte para ela. - Fala: Windows PowerShell com
System.Speech(nativo). No macOS usesay, no Linuxespeak-ng; então você deve adaptartemplates/lib/tts.mjs(apenassynthAlletts.ps1são específicos do SO). - Nenhum
ffprobevem com ffmpeg-static: meça durações a partir dos cabeçalhos WAV (wavSecondsemtemplates/lib/tts.mjs) e da própria temporização do gravador. - Notas sobre shell do Windows: prefira as ferramentas Write e Edit para qualquer arquivo que contenha barras invertidas, regex ou
$. A reescrita inlinepython -c/heredoc de JS corrompe silenciosamente escapes. Osleepem primeiro plano é bloqueado em alguns agentes: aguarde comuntil grep -q "done" log; do sleep 10; done.
2. Fases
Fase A: Requisitos e mapa de provas
Leia a página de regras, o README, os próprios documentos do projeto e quaisquer notas de história. Escreva uma tabela em suas anotações:
| O juiz deve estar convencido de que | Onde o vídeo prova isso | Cena |
|---|---|---|
| a tecnologia exigida é chamada no código | chamada real visível: log de requisição, terminal, trecho de código | ... |
| a experiência funciona | aplicativo rodando em um navegador | ... |
| o que é real vs simulado | a própria tabela do projeto | ... |
Concluído quando toda linha tem uma cena.
Fase B: Espaço de trabalho
- Copie o repositório para uma pasta temporária e instale:
npm ci. - Copie
templates/para dentro da cópia comovideo/(mantenha a estrutura:video/lib,video/pages,video/scenes,video/final.mjs,video/narration.mjs, ...). Adicione aopackage.jsonscripts:"video:final": "node video/final.mjs". Não adicione dependências. - Adicione
video/out/ao.gitignorena cópia.
Fase C: Teste o aplicativo real e caça de bugs (faça isso antes de gravar qualquer coisa)
Inicie o aplicativo da forma que um juiz iniciaria (npm run demo ou o comando do README) em sua porta padrão documentada, para que todo comando mostrado na tela seja literal. Conduza-o com um pequeno script Playwright, tire screenshots em 1920x1080 após cada etapa da história e as examine. Verifique:
- login, painéis vazios, banners de erro vermelhos, erros no console;
- se cada estado que a história necessita realmente renderiza (a primeira execução do projeto de referência mostrou um login falho, uma caixa de entrada vazia e uma visão chave ausente, tudo escondido por um rascunho antigo de vídeo);
- texto duplicado ou cortado.
Corrija bugs reais na cópia, minimamente, execute as próprias verificações do projeto (npm run verify, testes, typecheck) e registre cada correção para COPY_LIST.md. Se o aplicativo não tem como mostrar o tráfego de rede, adicione um pequeno hook opcional (um log de requisição) em vez de falsificar um.
Concluído quando toda a história roda limpa duas vezes seguidas e as verificações do projeto passam.
Fase D: Storyboard e narração
- Escolha cenas. História de referência (cerca de 2:50):
| Cena | Duração | Mostra |
|---|---|---|
| gancho | 12 s | uma mudança reverbera em vários compromissos (cartão animado) |
| inicialização | 6 s | o único comando que inicia tudo, saída real |
| sim (o app) | 60 s | 8-10 batidas; log de requisição aberto em 2 batidas |
| terminal de prova | 17-30 s | cliente oficial ou CLI contra o mesmo servidor; verificações de autenticação |
| tour de código | 16 s | 3-4 trechos reais que provam a tecnologia exigida |
| arquitetura, stack cloud, real vs simulado | 30 s | diagrama, stack fonte, tabela de honestidade, cartão open-source |
| encerramento | 12 s | slogan, links do repositório, créditos de build, rodapé "X é simulado" |
- Escreva
video/narration.mjs:scenes = [{ id, title, segs: [{ id, text }] }]. Cadasegé uma linha falada e uma legenda (texté mostrado literalmente). Regras: cerca de 2.4 palavras por segundo; total de palavras faladas 330-380; uma ideia por linha; não mais que cerca de 12 palavras em qualquer cartão na tela; letra inicial maiúscula; sem exageros. - Adicione dicas de pronúncia na tabela
sayapenas para o mecanismo de fala (siglas soletradas, "Alexa+" como "Alexa plus"); nunca altere o texto da legenda. Concluído quando a narração é lida como uma história que um estranho entende e cada linha do mapa de provas é falada ou mostrada.
Fase E: Adapte os templates
Edite apenas o que é específico do projeto:
| Arquivo | Edição |
|---|---|
video/narration.mjs | suas cenas e linhas |
video/scenes/sim.mjs | as etapas do seu app: o que digitar ou clicar, seletores, os zooms da câmera (lens(...), zoomWire) |
video/scenes/static.mjs | comandos de terminal (CMD), codeBlocks (arquivo + regex da primeira linha + contagem de linhas + regex de destaque), chamadas de página de hook/fechamento |
video/proofs.mjs, video/proof-auth.mjs | os comandos reais que provam suas afirmações; eles imprimem apenas códigos de status e nomes de erro |
video/pages/*.html | texto, cores, blocos, caixas de diagrama, linhas de tabela, links |
video/lib/overlay.mjs | seletores SIM_CSS para o layout do seu app (veja regras de Layout), fonte de dados do ticker |
video/final.mjs | a constante LIMIT e a ordem das cenas (a ordem vem de narration.mjs) |
video/thumbnail.mjs, video/docs.mjs | títulos e a tabela SHOT |
Não edite lib/recorder.mjs, lib/director.mjs, lib/tts.* a menos que algo esteja quebrado.
Fase F: Capture a saída da prova real
node video/proofs.mjs inicia um servidor novo, executa os comandos reais (inspetor oficial ou CLI cliente, verificações de autenticação, saída de instalação/inicialização) e salva o que imprimiram em video/out/proofs.json. As cenas de terminal reproduzem esta saída capturada em um terminal HTML em um tamanho legível. Regras: mostre o texto do comando real (encurte apenas o caminho do pacote); trunque saída muito longa visivelmente (uma linha "... N linhas ..." escurecida) e coloque "Execução gravada" no chip; nunca imprima tokens; formate JSON de forma legível e diga se o fizer.
Fase G: Áudio da narração
Padrão (sem necessidade de conta): node video/final.mjs --rate 2 faz um WAV por linha com a voz nativa do Windows (Microsoft Zira) e mede cada duração. A taxa 1 é cerca de 150 s de fala para o roteiro de referência, taxa 2 cerca de 139 s, taxa 3 é muito rápida. Escolha a taxa mais lenta que mantenha o vídeo inteiro abaixo do limite.
Voz opcional com som humano: veja Apêndice A. Tudo abaixo funciona da mesma forma com qualquer uma.
Fase H: Grave e monte
node video/final.mjs --rate 2 # tudo: tts, provas, gravar todas as cenas, montar ambos os cortes
node video/final.mjs --rate 2 --reuse-proofs # pular reexecução de provas
node video/final.mjs --scenes sim,close --reuse-tts --reuse-proofs # regravar apenas algumas cenas
node video/final.mjs --assemble-only # re-mux (legendas, áudio) sem regravar
node video/final.mjs --tts-dir video/out/tts-human --name my_video --single # construir a partir de outra pasta de áudio
Uma execução completa leva de 6 a 10 minutos; execute-a em segundo plano e monitore o log. O script falha se o total for 180 s ou mais. Mire em 170 s.
Importante: se você alterar o texto da narração ou a voz, as durações mudam, então cada cena deve ser regravada (as marcas vêm das durações). Alterar apenas os visuais de uma cena significa --scenes <id> --reuse-tts.
Fase I: Controle de qualidade (nunca pule, nunca confie no log)
- Extraia quadros no final de cada linha de narração do MP4 final:
ffmpeg -ss <markEnd-0.3> -i final.mp4 -frames:v 1 frame.png(a linha do tempo está emvideo/out/timeline.json). Visualize-os. Verifique legibilidade em 1080p, espaço vazio, texto cortado, posição e tamanho da legenda, sincronia (o evento da legenda está na tela), nenhum segredo, nenhuma menção a fornecedor, rótulos de honestidade presentes. ffmpeg -i final.mp4 -af volumedetect -f null -: média cerca de -22 dB, máxima abaixo de 0 dB.- Duração abaixo do limite em ambos os cortes; ambos os arquivos têm um fluxo de vídeo e um de áudio.
grep -ril "<palavras do fornecedor>" video/pages video/narration.mjs <documentos de saída>não retorna nada, exceto um ID de modelo permitido.- Execute as verificações do projeto novamente (
npm run verify; e2e se a UI mudou). - Corrija e re-renderize até que todos passem.
Fase J: Entregáveis
Gere narration.md e shotlist.md com node video/docs.mjs <outdir> (timestamps reais). Copie os MP4s, SRT, miniatura (node video/thumbnail.mjs <png>), os quadros de controle de qualidade e os arquivos do repositório alterados (todos os arquivos diferentes entre o original e sua cópia, em seus caminhos de repositório). Escreva COPY_LIST.md (correções de bugs primeiro, depois novos arquivos, mensagens de commit sugeridas, "execute build e verifique após copiar"), README-snippet.md (comando de execução, arquivo ponto de entrada:linha, arquivo cliente:linha, arquivo skill, link de placeholder do vídeo) e REPORT.md (concluído, o que o usuário deve fazer, riscos). Diga claramente o que você não pôde verificar (por exemplo, você não pôde ouvir a voz).
3. Como o pipeline funciona (leia antes de alterá-lo)
Gravador (lib/recorder.mjs). O recordVideo próprio do Playwright limita a taxa de bits e desfoca texto pequeno, então este usa screencast do Chromium: quadros JPEG (qualidade 92) são carimbados quando chegam, escritos em disco e codificados a 30 fps constantes com libx264 crf 17. Telas estáticas produzem poucos quadros; o último quadro é mantido. Os timestamps vêm de um único relógio (performance.timeOrigin + performance.now()), o mesmo que o diretor usa.
Diretor (lib/director.mjs). Cada linha de narração é um segmento. d.seg(id, actions, {tail}) registra o tempo de início, executa as ações na tela, então espera até que a linha tenha terminado de tocar mais tail * 0.45 segundos (as caudas são aparadas globalmente para caber no limite). As marcas (id, início, fim, texto) são salvas em video/out/<scene>.marks.json. Como o áudio é posteriormente colocado nas marcas registradas, imagem e voz não podem dessincronizar. Regra para uma batida: execute a ação dentro do segmento e certifique-se de que a ação termina antes da narração; se a linha for mais curta que a ação, divida-a em duas linhas (é por isso que uma batida de história se tornou sim9 e sim10).
Áudio (final.mjs). Cada WAV é atrasado para seu início global com adelay, mixado com amix=normalize=0, limitado com alimiter, e preenchido com apad=whole_dur=<total> (sem o preenchimento, -shortest corta o final do vídeo).
Legendas. As pistas são o texto da narração dividido em finais de frase (decimais como "2.0" são protegidos) e em vírgulas quando acima de 82 caracteres, com o tempo dividido proporcionalmente ao comprimento. O .srt é enviado. A versão incorporada usa um arquivo ASS gerado com PlayResX: 1920, PlayResY: 1080 e tamanho de fonte 36. Não incorporar um SRT com force_style: o libass o dimensiona contra uma tela de 288 pixels de altura e o texto fica cerca de quatro vezes maior.
Overlay (lib/overlay.mjs), injetado na página do app gravado apenas (o próprio app não é editado). Manipula:
- O ticker (barra de status de rolagem com o nome do projeto, timestamp, cabeçalho de "gravação");
- O selo "real" no canto;
- O botão de log de requisições (um toggle que mostra uma janela de console overlay);
- O indicador de beat (um pequeno ponto que muda de cor quando um segmento começa).
Layout. O app é gravado em uma viewport de 1920x1080, mas marginado em 2.5% em cada borda (então a área ativa é cerca de 1824x1026) para deixar espaço para legendas e sobreposições. Use o SIM_CSS em overlay.mjs para ajustar seletores se o app tiver padding ou margens fixas.
Temporização. Porque os segmentos são temporizados pelo diretor e o áudio é colocado nas marcas, a montagem é determinística: a mesma marca de entrada produz a mesma saída. Se uma cena precisar de uma pausa, adicione uma linha de narração vazia ou use d.wait(seconds) dentro de um segmento (mas lembre-se de que isso conta para o limite de tempo).
Manipulação de erros. Se um segmento falhar (um seletor não encontrado, um timeout), o diretor registra o erro, tira um screenshot, e continua com o próximo segmento. O vídeo final tem um card de erro no lugar da cena que falhou. Verifique video/out/errors.log após cada execução.
4. Apêndices
Apêndice A: Vozes humanas (opcional, ElevenLabs)
Para usar uma voz ElevenLabs em vez da voz nativa:
- Obtenha uma chave de API em https://elevenlabs.io (não armazene no repositório; use variável de ambiente
ELEVENLABS_API_KEY). - Em
video/final.mjs, definaconst TTS_ENGINE = 'elevenlabs';econst ELEVENLABS_VOICE = '<voice_id>';. - Execute
node video/final.mjs --rate 2 --tts-engine elevenlabs.
Aviso: Isso incorre em custos (o nível gratuito tem um limite de caracteres). O padrão é a voz nativa do SO.
Apêndice B: Solução de problemas comuns
| Problema | Causa provável | Solução |
|---|---|---|
| O vídeo está sem áudio | Faixa de áudio faltando ou não mixada | Verifique final.mjs; execute ffprobe para confirmar streams de áudio; certifique-se de que amix não está em modo silencioso |
| Legendas cortadas ou muito grandes | Arquivo SRT renderizado com force_style | Use o gerador ASS; verifique PlayResX/PlayResY |
| O app não inicia | Dependências faltando ou comando errado | Verifique o comando real no README; teste manualmente antes de gravar |
| A saída do terminal está vazia | proofs.mjs falhou em capturar | Execute node video/proofs.mjs manualmente; verifique se o servidor está rodando |
Apêndice C: Referência rápida de comandos
# Renderização completa
node video/final.mjs --rate 2
# Regravar apenas cenas modificadas (economiza tempo)
node video/final.mjs --scenes sim,boot --reuse-tts --reuse-proofs
# Montar novamente sem regravar
node video/final.mjs --assemble-only
# Extrair quadros para QA
ffmpeg -ss 1:23 -i video/out/final.mp4 -frames:v 1 qa_frame.png