Communitygithub.com

ssunduko/product-video

Resolva seletores de linguagem natural (intenções `ask "..."`) em um script `.demo` contra o aplicativo ativo, usando a extensão Claude no Chrome, e escreva o arquivo `scripts/<nome>.selectors.json`. Use quando um script tiver intenções não resolvidas, quando a compilação ou gravação falhar com o erro "selector(s) still state an intent", ou quando um redesign quebrar seletores que funcionavam antes.

O que é product-video?

Esta habilidade permite que um agente de IA resolva selettores de linguagem natural em scripts de demonstração (`.demo`), transformando intenções como `ask "a caixa de mensagem na parte inferior do chat"` em seletores DOM reais que podem ser compilados e executados de forma determinística. Ela resolve o problema de seletores frágeis que dependem de texto gerado por IA, que muda a cada execução, e elimina a necessidade de modelos de linguagem durante a gravação, evitando tempo de espera ("dead air") em vídeos. O processo envolve carregar o script, navegar pelo fluxo do aplicativo em ordem de batidas, inspecionar o DOM com ferramentas de desenvolvedor, e registrar a resolução em um arquivo auxiliar (`selectors.json`). A habilidade suporta plataformas que usam a extensão Chrome do Claude e prioriza seletores estáveis como `data-testid`, `aria-label` ou `btn:Name`. Ela também inclui verificações de unicidade e visibilidade, tratamento de erros e instruções para evitar seletores frágeis.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ssunduko/product-video/tree/HEAD/.claude/skills/resolve-selectors

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Resolvendo seletores de linguagem natural

Um script .demo diz o que significa:

selectors:
  composer = ask "a caixa de mensagem na parte inferior do chat"

Seu trabalho é encontrar o que realmente está no DOM ativo e escrever em scripts/<nome>.selectors.json, para que script.mjs possa compilá-lo em um seletor comum e driver.mjs possa gravar uma tomada determinística.

Você é o resolvedor. Nada no pipeline chama um modelo — script.mjs lê o arquivo auxiliar e se recusa a compilar sem ele. A resolução acontece aqui, uma vez, antes da gravação, porque o tempo da tomada é a linha do tempo do vídeo e uma ida e volta do modelo durante uma execução cairia no vídeo final como tempo morto.

Antes de começar

  • O aplicativo deve estar acessível e, se precisar de login, já autenticado no Chrome do usuário. Esse é o propósito de usar a extensão: ela acessa a sessão real, então não há ritual de SSO nem código de e-mail para preencher.
  • Leia o script primeiro. Você precisa do cabeçalho app:/target: para a URL e as batidas, porque os seletores devem ser resolvidos na ordem do fluxo (veja abaixo).

Procedimento

  1. Obtenha a lista de trabalho.

    cd pipeline
    node script.mjs ../scripts/<nome>.demo --intents
    

    JSON na saída padrão: cada seletor ask, sua intenção e sua resolução atual (null se não resolvido). Resolva os nulos. Re-resolva o restante apenas se o usuário disser que o fluxo está quebrado.

  2. Carregue as ferramentas do navegador em UMA chamada ToolSearch, depois chame tabs_context_mcp antes de qualquer outra coisa. Crie uma nova aba com tabs_create_mcp a menos que o usuário tenha apontado para uma já existente.

  3. Percorra o fluxo na ordem das batidas. Esta é a parte fácil de errar: muitos seletores não existem na página inicial. Uma tabela de resultados aparece apenas após o envio de uma pergunta; um controle "Parar" existe apenas enquanto algo está sendo gerado. Então, percorra as batidas do script, resolvendo cada ask no momento em que seu elemento está na tela.

    Dados reais. Antes de qualquer etapa que crie, modifique, envie ou exclua algo, pare e pergunte ao usuário. feature.demo envia quatro mensagens para uma conta real do Salesmessage. Passe o mouse em vez de clicar quando você só precisa localizar algo, e nunca clique em um controle destrutivo para "ver o que é".

  4. Encontre o elemento e escolha o identificador mais durável. Em ordem de preferência:

    PrefiraPor quê
    [data-testid=...], data-test, data-qacolocados lá para serem selecionados; sobrevivem a redesigns
    um id estávelbom, a menos que pareça gerado (:r3a:, id-1a2b)
    botão/link pelo nome acessível exato → btn:Nomeo btn: do driver corresponde ao texto, aria-label e img[alt] interno
    [aria-label=...], [role=...]estáveis e geralmente semânticos
    estrutura semântica (main table, nav a[href="/teams"])sobrevive a mudanças de estilo

    Nunca use: cadeias nth-child, nomes de classe gerados (css-1a2b3c, longas pilhas Tailwind), ou qualquer coisa baseada em conteúdo gerado. Esse último é a armadilha que todo este recurso existe para evitar — a resposta de um modelo é redigida de forma diferente a cada execução, então um seletor que corresponde ao seu texto é um cara ou coroa. Ancore no contêiner ou em um sinal de conclusão.

  5. Verifique antes de escrever. Para cada candidato, confirme com javascript_tool que corresponde a exatamente um elemento e que o elemento está visível:

    const q = 'div[data-testid="chat-input"]';
    const n = document.querySelectorAll(q);
    console.log(JSON.stringify({
      q, count: n.length,
      visible: n[0] ? !!(n[0].offsetWidth || n[0].offsetHeight) : false,
      tag: n[0]?.tagName, editable: n[0]?.isContentEditable,
    }));
    

    Leia de volta com read_console_messages. Mais de uma correspondência, ou zero, significa continuar procurando — não escreva um seletor que você não viu corresponder.

  6. Escreva o arquivo auxiliar, scripts/<nome>.selectors.json:

    {
      "resolvedAgainst": "https://claude.ai/new",
      "resolvedAt": "2026-08-10T09:00:00Z",
      "resolvedBy": "claude-in-chrome",
      "selectors": {
        "a caixa de mensagem na parte inferior do chat": {
          "spec": "div[data-testid=\"chat-input\"]",
          "evidence": "contenteditable tiptap div, aria-label 'Write a message', 1 correspondência, visível"
        }
      }
    }
    

    Indexado pelo texto da intenção, literalmente do script — renomear um seletor ou reordenar o bloco não deve órfão de uma resolução. spec é o que quer que o driver aceite: um seletor puppeteer bruto, btn:Texto|Alt, ou ::-p-text(substring). evidence é o que você realmente observou; escreva para que a próxima pessoa possa dizer se ainda é válido.

  7. Confirme e devolva.

    node script.mjs ../scripts/<nome>.demo --intents   # espera 0 não resolvidos
    node script.mjs ../scripts/<nome>.demo --print     # compila
    

    Relate o que você resolveu, entre o que teve que adivinhar e qualquer coisa que você pulou porque precisava de uma etapa destrutiva.

Quando você não puder resolver um

Diga e pare; não invente um seletor. Uma intenção não resolvida é um erro de compilação limpo antes mesmo do Chrome ser iniciado. Um seletor errado falha no meio da tomada, depois que a gravação começou — o lugar caro para falhar. Se dois elementos corresponderem igualmente bem à descrição, pergunte ao usuário qual eles queriam em vez de escolher.

Armadilhas

SintomaCausa / correção
O elemento não está na páginaAparece mais tarde no fluxo. Percorra as batidas em ordem; resolva cada intenção quando seu elemento estiver realmente na tela
Dois elementos correspondemA descrição é ambígua. Especifique com o usuário e depois reformule o ask no script para que a próxima resolução seja inequívoca
Seletor funciona agora, quebra na próxima execuçãoEstá baseado em conteúdo gerado ou em uma classe gerada. Reancore em um testid, um role ou um contêiner
Um âncora wait ... goneSão transitórios por natureza (um botão Parar enquanto gera). Resolva enquanto está na tela e prefira aria-label — muda menos que o texto
Resolução desviou após um redesignApague apenas aquela chave do arquivo auxiliar e execute novamente. O .demo não muda; esse é o objetivo
Diálogo bloqueia a extensãoNão clique em coisas que abrem confirm/alert. Um diálogo modal congela a extensão até ser descartado manualmente

Habilidades Relacionadas