VibeView
Dirija dispositivos iOS, Android, Apple TV e Android TV ao vivo na nuvem para verificar alterações de apps: leia a árvore de UI, toque, digite, pressione o controle remoto, faça asserções e leia logs de apps de qualquer cliente MCP.
Documentação
Deixe seu agente de IA de codificação verificar uma alteração dirigindo uma cópia real e em execução do seu aplicativo em um dispositivo de nuvem VibeView — instalando o build, tocando pela interface, lendo o que está na tela e relatando se realmente funcionou. Este é o mesmo loop de desenvolvimento ao vivo descrito em Live Development, estendido para que um agente (não apenas você) possa dirigir a sessão pela linha de comando.
Instalar e autenticar
npm install -g vibeview
vibeview login
vibeview login abre seu navegador para concluir a autenticação e salva um token localmente. Em CI ou qualquer ambiente não interativo, defina VIBEVIEW_API_TOKEN — todo comando aceita isso como alternativa a estar logado.
Disponibilizando para seu agente
Instalar a CLI não diz por si só ao seu agente de codificação que o VibeView existe. Um comando configura isso:
vibeview agent-setup
Ele faz três coisas:
- Instala a habilidade do agente em
~/.claude/skills/vibeview-agent/, onde o Claude Code a descobre automaticamente em todos os projetos. A habilidade descreve todo o fluxo de trabalho — requisitos de build, o loop de verificação, limpeza — para que o agente saiba como usar o VibeView, não apenas que ele existe. Passe--projectpara instalar no.claude/skills/do projeto atual (útil quando colegas de equipe também devem recebê-la, já que esse diretório pode ser commitado). - Oferece registrar o servidor MCP com o Claude Code (
claude mcp add vibeview -- vibeview mcp), para que as ferramentas fiquem visíveis em todas as sessões. Passe--mcppara registrar sem perguntar, ou--no-mcppara pular. Outros clientes MCP são configurados por cliente — veja Usando com um cliente MCP abaixo. - Sugere uma linha para o
CLAUDE.mddo seu projeto (ouAGENTS.mdse você usa outros agentes) dizendo ao agente para verificar alterações de UI em um dispositivo ao vivo. Este é o empurrão mais forte por projeto: a habilidade ensina ao agente como, a linha de instrução diz quando.
Você precisa tanto da habilidade quanto do MCP? Geralmente não. Se seu agente pode executar comandos de shell (Claude Code em um terminal, por exemplo), a habilidade sozinha é suficiente — ela dirige a CLI diretamente, e pular o MCP mantém definições de ferramentas fora do contexto do seu agente. Registre o servidor MCP quando o agente não puder executar shell: Claude Desktop, modo agente do Copilot, ambientes restritos onde ferramentas nomeadas são permitidas, mas comandos arbitrários não são. Para esses clientes, MCP não é um extra — é a única forma de acesso.
Agentes além do Claude Code podem ser apontados diretamente para o arquivo de habilidade — são instruções simples que qualquer agente de codificação pode seguir — ou receber o servidor MCP via configuração do próprio cliente.
Outras formas de instalar a habilidade
A mesma habilidade está publicada em github.com/vibeview/skills, para que agentes que leem habilidades do GitHub possam instalá-la sem o comando de configuração da CLI:
- Qualquer agente que use a CLI
skills(Claude Code, Cursor, Codex, Gemini CLI, GitHub Copilot e outros):npx skills add vibeview/skills - Claude Code, como plugin — instala a habilidade e registra o servidor MCP em um passo:
/plugin marketplace add vibeview/skills /plugin install vibeview@vibeview
Qualquer caminho que você use, o agente ainda precisa da CLI vibeview instalada e logada (vibeview login), porque a habilidade dirige a CLI e o servidor MCP do plugin executa vibeview mcp.
Uma nota honesta: mesmo totalmente configurado, um agente solicitado apenas a "implementar X" nem sempre verificará em um dispositivo sem ser solicitado. A configuração acima aumenta consideravelmente as chances, mas dizer "implemente X e verifique no dispositivo" é o que torna isso confiável.
O loop de desenvolvimento
- Envie um build de debug do seu aplicativo (necessário apenas novamente após uma alteração nativa — veja Preparando Seu Build):
vibeview upload-app ./path/to/app-debug.apk - Inicie o Metro no seu projeto React Native (
yarn startou equivalente). - Inicie uma sessão em segundo plano:
Isso imprime um eventovibeview dev --detach --jsonsession_readycom umsession_idque seu agente usa para direcionar todos os comandos seguintes (--session <id>).session_readydispara assim que a sessão realmente aceita comandos — o dispositivo está ativo e o aplicativo está instalado — então o primeiroui-treepode segui-lo imediatamente. O evento também inclui a URL de uma página onde você pode assistir e interagir com a sessão — sessões destacadas nunca abrem um navegador por conta própria, então abra essa URL você mesmo se quiser acompanhar. Seu campobuilddiz qual build foi instalado e por quê, e lista os outros builds de debug do aplicativo (veja abaixo). A página mostra o dispositivo ao vivo apenas para a conta que iniciou a sessão; um colega que a abrir é informado de quem é a sessão. Para permitir que outra pessoa assista ou dirija, ative colaboração e envie o link. - Dirija a sessão com os verbos de comando abaixo — sempre comece com
ui-treepara ver o que está na tela e obter referências de elementos (@e5, etc.) para agir. Se o aplicativo ainda estiver na tela de splash ou carregando, a árvore pode voltar vazia ou quase vazia — isso é o aplicativo inicializando, não uma sessão quebrada. Aguarde alguns segundos e busque novamente. - Quando terminar, pare a sessão:
vibeview dev-stop
Todo comando também funciona contra uma sessão que você já iniciou no painel do VibeView — passe o ID da sessão com --session <id> em vez de iniciar uma nova com dev --detach. Pare essa sessão com vibeview stop <session-id>.
Para executar um upload específico em vez do build de debug mais recente do aplicativo, liste os builds do aplicativo e fixe um — deve ser um build de debug (KIND debug):
vibeview list-builds <app-id>
vibeview dev --detach --json --app <app-id> --build <build-id>
Sem --build <id>, vibeview dev executa o build de debug mais recente do aplicativo, a menos que a versão do próprio aplicativo do projeto possa ser lida (da configuração do Expo, incluindo um app.config.ts dinâmico quando o Expo está instalado no projeto, ou do projeto nativo) e um build de debug mais antigo tenha essa versão enquanto o mais recente não; então ele executa esse e diz o porquê. Se a versão não puder ser lida, ele diz isso. Quando a sessão inicia, ela imprime o build que instalou (id, versão, tempo de upload e nota), o build de debug mais recente de cada outra versão e a versão do React Native do projeto. Se o aplicativo mostrar uma tela de erro vermelha logo após iniciar, o build pode ter sido feito de outro código nativo que não o do seu projeto: execute um build da versão do seu projeto com --build <id>.
Assistindo junto — e assumindo o controle
Toda sessão tem uma página ao vivo e totalmente interativa (o url em session_ready, ou page_url da ferramenta MCP dev_start). A habilidade incluída instrui os agentes a compartilhá-la com você no momento em que uma sessão inicia, para que você possa assistir o agente trabalhar em tempo real — e dirigir o dispositivo você mesmo sempre que quiser. Os comandos do agente e sua entrada coexistem; mantenha uma única aba do navegador por sessão.
Esse controle também é como os agentes se desbloqueiam. A habilidade diz a eles para parar e perguntar a você sempre que uma tela precisar de algo que apenas um humano deve fornecer — entrar com credenciais reais, um código 2FA, um CAPTCHA ou uma confirmação arriscada — em vez de adivinhar. Faça sua parte na página da sessão, diga ao agente que terminou, e ele relê a tela (verificando que o bloqueio realmente sumiu) antes de continuar.
Uma sessão destacada registra seu estado em um diretório .vibeview/ na raiz do seu projeto (é assim que comandos posteriores a encontram sem --session). É estado local da máquina, não algo para commitar: quando seu projeto tem um .gitignore, a CLI adiciona .vibeview/* a ele, além de !.vibeview/test-spec.json para que uma especificação de teste nessa pasta ainda seja commitada. Não ignore a pasta inteira com .vibeview/: o Git não pode re-incluir um arquivo dentro de uma pasta ignorada.
Comandos
Todo comando abaixo aceita --session <id> (direciona uma sessão específica; recai na sessão dev --detach do projeto atual se omitido) e --json (imprime o resultado bruto como uma linha JSON em vez de texto legível).
| Comando | Propósito |
|---|---|
ui-tree | Busca os elementos de UI da tela atual, cada um com uma referência (@e5) para agir. |
logs | Lê logs recentes do aplicativo do dispositivo — saída do console JS, erros nativos e mensagens de crash. |
screenshot | Captura uma captura de tela da tela atual e salva em disco. Seus pixels são as coordenadas que tap e drag usam (veja Coordenadas). |
tap <target> | Toca em um elemento por referência (ex.: @e5) ou por coordenadas (ex.: 100,200), no espaço descrito em Coordenadas. |
long-press <ref> | Toque longo (tocar e segurar) em um elemento. |
swipe <direction> | Desliza para cima, baixo, esquerda ou direita. |
scroll <direction> | Rola a visualização atual para cima, baixo, esquerda ou direita. |
scroll-to <text> | Rola até que um elemento correspondente àquele texto esteja visível, parando no final da lista. |
drag <from> <to> | Arrasta entre dois pontos, cada um uma referência de elemento ou coordenadas brutas. |
alert <get|accept|dismiss> | Inspeciona ou responde a um alerta do sistema no iOS ou Apple TV. |
type <text> | Digita texto no campo de entrada atualmente focado. |
clear-text | Limpa o campo de entrada atualmente focado. |
press <button> | Pressiona um botão do dispositivo ou gesto do sistema. iPhone/iPad: home, back, lock, siri, enter. Android phone/tablet: home, back, lock, enter. Apple TV: dpad_up, dpad_down, dpad_left, dpad_right, dpad_center, back, home, menu. Android TV: dpad_up, dpad_down, dpad_left, dpad_right, dpad_center, back, home, enter. Roku: up, down, left, right, select, back, enter, backspace, play, rewind, forward, replay, info (os nomes dpad_* também funcionam). vibeview press --help os lista, e um pressionamento recusado imprime a lista. |
open-url <url> | Abre um deep link dentro do aplicativo. No iOS e Apple TV, apenas o esquema de URL próprio do aplicativo é aceito (veja abaixo). |
relaunch-app | Fecha o aplicativo em teste e o inicia novamente. É um início a frio que mantém os dados do aplicativo, então é a forma de verificar se algo sobrevive a um reinício; o log do aplicativo continua transmitindo durante isso. Não disponível no Roku. |
set-posture <closed|partial|open> ou set-posture --angle <0-180> | Dobra ou desdobra um dispositivo dobrável (fator de forma foldable em list-devices --models — o iPhone Duo, dobráveis Android como o Pixel 9 Pro Fold) — para um preset, ou para um ângulo exato de dobradiça em graus (passe um ou outro, não ambos). Erro em um dispositivo sem dobradiça. |
rotate [--degrees <90|180|270>] | Rotaciona o dispositivo. Um telefone ou tablet, e um dobrável Android, alterna entre retrato e paisagem (apenas 90 é aceito); o iPhone Duo gira um quarto de volta no sentido horário, 270 gira de volta um quarto e 180 vira de cabeça para baixo. Não disponível em dispositivos TV. |
set-location --lat <n> --lon <n> | Define a posição GPS simulada do dispositivo, em graus decimais (oeste e sul são negativos), para testar fluxos baseados em localização. O aplicativo lê como uma correção de GPS. Telefones, tablets e dobráveis; não disponível em dispositivos TV. |
wait | Aguarda um momento antes da próxima ação. |
find <text> | Encontra um elemento pelo seu texto, opcionalmente restrito a estar perto/acima/abaixo de outro elemento. |
tap-focused <ref> | Apenas TV: move o foco para um elemento e o seleciona em um passo. |
focus <ref> | Apenas TV: move o foco para um elemento sem selecioná-lo. |
Após cada ação, a resposta informa o que mudou na tela — uma lista completa de elementos, as diferenças específicas ou uma nota de que nada mudou — para que um agente sempre saiba o estado atual antes de decidir o que fazer a seguir. Isso significa que um loop normal é ui-tree uma vez no início, depois agir, agir, agir: você não precisa rebuscar a árvore entre os passos.
Alguns detalhes dessa saída:
- A lista de diferenças começa com os valores de campos alterados (o que você acabou de digitar ou definir) e termina com uma contagem de quaisquer diferenças que foram omitidas.
- Enquanto o teclado do iOS está visível, ele aparece como uma única linha no final da árvore (
keyboard: visible, com uma referência à tecla de retorno) em vez de uma linha por tecla; os elementos do próprio aplicativo permanecem no topo. - Na Apple TV, Android TV e Roku,
focus,tap-focusedepresscomeçam com o elemento que está com foco depois, por exemplofocused: @e112 button "Camping". - Quando vários elementos correspondem,
findprefere aquele cujo rótulo visível contém o texto, e informa quando correspondeu ao id de um elemento. - Se o dispositivo não retornar nenhuma árvore de elementos, mesmo ao ler novamente um momento depois,
ui-treeimprimeerrore sai com o código1em vez de imprimir uma árvore vazia; execute novamente.
Um comando que foi executado mas não fez o que foi solicitado imprime error em vez de ok e sai com o código 1, como um comando recusado. Isso inclui um scroll-to que nunca encontrou seu texto, e um type ou clear-text cujo campo não contém o texto esperado depois; o motivo é impresso abaixo da linha de status. Um campo de senha não pode ser lido de volta, então digitar nele não é verificado.
Coordenadas
tap e drag usam coordenadas no mesmo espaço dos retângulos de elementos em ui-tree e dos resultados de find. Em um telefone, tablet ou Android TV, screenshot salva sua imagem nesse tamanho, então um ponto que você lê de uma captura de tela pode ser tocado como está. Não escale coordenadas por conta própria. No Android, esse espaço pode ser menor que a resolução da tela do dispositivo (as capturas de tela de um Pixel 7 Pro são 1152x2496, não 1440x3120), e sempre cobre a tela inteira, incluindo barras de status e navegação, mesmo quando a janela do próprio aplicativo não cobre. Coordenadas fora dele são recusadas com um erro que informa o intervalo válido.
No Roku, os retângulos em ui-tree estão nas coordenadas de interface do próprio canal (1920x1080 para um canal full-HD), enquanto uma captura de tela é a imagem que o Roku emite, que pode ser menor (1280x720 em 720p). Um Roku é controlado pelo controle remoto, não por coordenadas, então compare os dois por proporção: em um canal full-HD em uma captura de tela de 1280x720, divida um retângulo por 1,5. Retângulos também podem ultrapassar a borda da tela, para as partes de uma linha que estão roladas para fora da vista.
Botões, Enter e links profundos
press aceita os botões do dispositivo em que você está. Um nome que o dispositivo não possui é recusado, e o erro lista os válidos para aquele dispositivo.
press enter pressiona Enter (Return no iOS), o que envia o campo de texto focado, como uma caixa de busca ou o último campo de um formulário. Funciona em telefones, tablets, Android TV e Roku; na Apple TV, use dpad_center. Em telefones e tablets, uma quebra de linha em type faz o mesmo, então vibeview type $'hello\n' digita hello e depois envia.
open-url entrega o link apenas ao aplicativo em teste. No Android, um link para o qual o aplicativo não tem tela (uma página web, ou um link de outro aplicativo) falha com um erro que informa isso; nunca abre um navegador ou outro aplicativo. No iOS e na Apple TV, o sistema decide qual aplicativo abre um link, então apenas links no esquema de URL do próprio aplicativo (por exemplo myapp://settings) são aceitos: um link web (https://...) ou um link de sistema como tel: é recusado com um erro em vez de abrir o Safari ou outro aplicativo.
Um press back que deixa a tela exatamente como estava ainda imprime ok, com uma nota de que nada mudou: a tela pode não responder ao gesto de voltar do sistema (um cabeçalho personalizado, por exemplo), então toque no botão de voltar do próprio aplicativo.
Dispositivos dobráveis
Qualquer dispositivo com dobradiça é um dobrável — o iPhone Duo (iOS) e dobráveis Android como o Pixel 9 Pro Fold. Para executar em um, inicie a sessão com o nome do modelo. Uma sessão que pede apenas um telefone ou tablet (o padrão) nunca recebe um dobrável, mesmo quando um dobrável é o único dispositivo livre — ela espera por um telefone como qualquer outra inicialização ocupada. vibeview list-devices --models lista os modelos que você pode iniciar e marca cada dobrável como foldable:
vibeview list-devices --models
vibeview dev --detach --json --model "iPhone Duo"
vibeview dev --platform android --detach --json --model "Pixel 9 Pro Fold"
Todos funcionam da mesma maneira: a sessão começa fechada, na tela de capa menor, e você dobra com um preset ou um ângulo exato, e gira em qualquer postura:
vibeview set-posture partial --session <id>
vibeview set-posture --angle 75 --session <id>
vibeview rotate --session <id>
Cada resposta informa onde o dispositivo terminou. Após set-posture, isso é a postura em que se estabilizou, o ângulo da dobradiça e o tamanho da tela agora visível, como é exibida (largura e altura trocam enquanto a imagem está girada de lado, então o iPhone Duo aberto é 2853 x 2007), e a tela acesa, cover ou inner. O dispositivo decide qual tela acender, como um real faria, e a resposta espera até que ele se estabilize.
Os presets movem a dobradiça para os mesmos ângulos em todos os dobráveis — closed 0, partial 120, open 180 graus — mas cada dispositivo nomeia um ângulo exato por faixas que o próprio dispositivo define, então o mesmo ângulo pode ser lido de forma diferente. Confie no posture na resposta em vez do ângulo que você pediu. O iPhone Duo e, como exemplo Android, o Pixel 9 Pro Fold:
| Ângulo da dobradiça | iPhone Duo | Pixel 9 Pro Fold |
|---|---|---|
| 0 | closed | closed |
| 1-30 | geralmente closed; pode permanecer partial | closed |
| 31-84 | geralmente closed; pode permanecer partial | partial |
| 85-149 | geralmente partial; pode permanecer closed | partial |
| 150-169 | geralmente partial; pode permanecer closed | open |
| 170-180 | open | open |
No iPhone Duo, qual tela está acesa entre 1 e 169 graus depende de como a dobradiça chegou lá. Movida de uma vez a partir de fechada ou totalmente aberta, ela alterna em cerca de 85 graus em ambas as direções — a coluna "geralmente". Movida em pequenos passos, ela mantém a tela que já tinha: abrir um pouco de cada vez pode manter a tela de capa acesa até cerca de 160 graus, e fechar um pouco de cada vez pode manter a tela interna acesa até o dispositivo estar fechado. Apenas 0 (a capa) e 170 ou mais (a tela interna) são os mesmos independentemente do que veio antes; as marcas posture_ranges do dispositivo indicam os outros ângulos com a postura que também podem ser lidos como. Um dobrável Android nomeia um ângulo da mesma maneira, independentemente da direção de onde veio, e usa sua tela interna sempre que não está fechado — meio aberto incluído. Após rotate, é a nova orientação (portrait, landscape_left, landscape_right ou portrait_upside_down) e, no Duo, como o conteúdo da tela está girado: um aplicativo que suporta a nova orientação gira seu layout, enquanto a tela inicial e aplicativos apenas retrato giram com o dispositivo.
A resposta de rotate de um Duo carrega dois campos de orientação, e eles respondem perguntas diferentes:
device_orientationé como o próprio dispositivo é segurado. Ele se move um quarto de volta a cadarotatee começa emportrait.screen_orientationé como a imagem está girada na tela acesa, medida contra a borda vertical própria dessa tela. Não é uma segunda leitura de como o dispositivo é segurado, então os dois campos podem nomear orientações diferentes mesmo quando o aplicativo girou com o dispositivo.
Para verificar se seu aplicativo se organizou para a nova orientação, compare a largura e a altura da raiz da nova árvore de interface em vez de qualquer um dos campos. A resposta de rotate de um dobrável Android também carrega ambos os campos, com a postura em que foi girado. Telefones e tablets relatam device_orientation e, como screen_orientation, como a tela realmente ficou: um aplicativo que suporta apenas retrato permanece retrato quando o dispositivo gira, e a resposta informa isso (device held landscape_left (screen stayed portrait)) em vez de sugerir que a tela girou.
Dobrar alterna entre duas telas de tamanhos diferentes e girar vira a imagem, então toda referência de elemento anterior está desatualizada — a resposta carrega a nova tela, e ui-tree a busca novamente. Um dispositivo que ainda está dobrando recusa um giro; tente novamente uma vez que a dobra tenha terminado.
Use a árvore e a captura de tela para coisas diferentes
A árvore de interface descreve estrutura: quais elementos existem, que texto carregam e onde estão. Ela não carrega informações de cor, contraste ou empilhamento, então uma tela pode passar em todas as verificações que a árvore pode expressar e ainda estar visivelmente quebrada — texto renderizado em uma cor que desaparece no fundo, ou um elemento desenhado sob outro.
Use a árvore para encontrar elementos e confirmar estrutura. Tire uma captura de tela sempre que uma mudança afetar como algo parece, e inspecione a imagem em si.
A árvore descreve seu aplicativo, não as barras de status e navegação do dispositivo. No Android, quando a sombra de notificações é puxada para baixo sobre seu aplicativo, a árvore descreve a sombra, então seus botões podem ser encontrados e tocados.
Duas lacunas para conhecer no iOS. A árvore não mostra se uma caixa de seleção está marcada: a mudança aparece no diff depois que você a toca (por exemplo value "checkbox, unchecked" → "checkbox, checked"), e uma captura de tela mostra. E as barras de rolagem de uma visualização rolável são listadas como elementos slider ("Vertical scroll bar, 1 page"); não são controles para agir, então role com swipe.
Pelo MCP, screenshot escreve o arquivo e retorna seu caminho; passe inline: true para receber a imagem em si. Um agente que não pode abrir arquivos na máquina que executa o servidor precisa de inline: true para ver qualquer coisa — custa contexto, então vale reservar para as verificações onde a aparência realmente importa. A imagem inline é mantida abaixo de 1 MB para que clientes de modelo a aceitem: uma captura de tela grande volta como JPEG, no mesmo tamanho do PNG salvo, então um ponto lido dela ainda é a coordenada que tap e drag usam. Com --json, a CLI imprime onde escreveu o arquivo e seu tamanho, não os dados da imagem.
Um exemplo prático
Adicionar um título a uma tela e confirmar que ele renderiza, com recarga automática já em execução:
vibeview ui-tree # what's on screen now?
# ...edit your component in your editor; Fast Refresh pushes it in ~1s...
vibeview ui-tree # the new heading appears in the tree
vibeview screenshot --out ./check.png # ...and actually looks right
vibeview tap @e7 # keep going: tap through the flow
Sem reconstrução e sem reenvio — uma mudança de JavaScript ou asset chega ao dispositivo pelo Metro. Você só reconstrói quando dependências nativas mudam.
Referências de elementos ficam desatualizadas assim que a tela muda. Usar uma desatualizada é recusado e a ação não é executada; como nada aconteceu, a resposta relata a tela como inalterada em vez de retornar uma lista de elementos fresca, então execute ui-tree novamente para obter referências ao vivo antes de tentar de novo.
Alguns comandos valem a pena conhecer especificamente:
scroll-toé a forma correta de alcançar algo fora da tela — ele continua rolando até que um elemento cujo texto ou rótulo de acessibilidade corresponda fique visível, e desiste quando o conteúdo para de se mover, em vez de você adivinhar quantas chamadas descrollsão necessárias. Ele descobre sozinho para qual direção rolar em listas verticais comuns; passe--directionpara linhas horizontais de itens. Em um iPhone Duo aberto ou parcialmente aberto,scroll-tonão está disponível na tela interna e retorna um erro informando isso; usescrollouswipee verifique a árvore novamente.dragé o único comando para um gesto preciso de dois pontos — arrastar um controle deslizante para um valor, reordenar uma lista arrastando um item ou mover um mapa.swipeescrollaceitam apenas uma direção e não conseguem expressar isso. Cada extremidade pode ser uma referência de elemento ou coordenadas, então você pode misturá-las. As opções permitem desacelerar o arrasto para precisão, pressionar e segurar antes do arrasto começar (para gestos de reordenação) e segurar no destino antes de soltar.alertlida com alertas do sistema, como solicitações de permissão, que ficam acima do seu aplicativo e o bloqueiam. Usealert getpara ler a mensagem e os rótulos exatos dos botões, depoisalert acceptoualert dismiss— opcionalmente com--buttonpara escolher um rótulo específico. Na Apple TV, os mesmos comandos cobrem os avisos do tvOS, incluindo a confirmação "Abrir em …?" que um deep link levanta; o botão escolhido é respondido com o controle remoto, então nada no seu aplicativo é tocado.logsé como um agente descobre por que algo quebrou. Ele retorna a saída do próprio aplicativo — linhas do console JS, erros nativos, mensagens de falha — limitada ao aplicativo em teste, não ao dispositivo inteiro. Quando o aplicativo trava ou congela, a tela sozinha não consegue explicar; o log geralmente consegue. Cada resposta termina com um valorcursor: passe-o de volta como--sincepara receber apenas linhas que chegaram após sua leitura anterior, então agir e depois verificarlogs --since <cursor>mostra exatamente o que aquela ação registrou.--taillimita quantas linhas retornam (padrão 100, máximo 500, as mais recentes são mantidas). No Roku, o log é o console do canal: sua saídaprint, o motivo da saída e o erro com backtrace quando ele para em um erro de execução.
Para aplicativos Apple TV e Android TV, inicie a sessão com --platform tvos ou --platform androidtv e navegue com o d-pad via press (dpad_up, dpad_down, dpad_left, dpad_right, dpad_center). press home sai do aplicativo em ambos; o próximo ui-tree descreve o que está na tela (a tela inicial da TV ou um aviso sobre ela), não uma árvore vazia.
Sessões Roku começam de vibeview dev --platform roku (com --detach se o agente executar o loop sozinho) e usam os mesmos verbos, com alguns detalhes específicos do Roku. Os nomes próprios do controle remoto são press up, down, left, right, select e back (os nomes dpad_* funcionam como aliases), além de enter, backspace e as teclas de mídia play, rewind, forward, replay e info, e press home é recusado, porque a sessão está confinada ao seu canal. Qualquer outro nome também é recusado, com a lista acima. type envia o texto diretamente para um teclado na tela focado, e o texto digitado é lido de volta do campo, então o resultado informa se ele foi aplicado. open-url não está disponível. Um canal se move por foco, não por toque, então tap, long-press, swipe, scroll, drag e scroll-to são recusados no Roku com uma mensagem indicando o que usar: press para mover, focus para caminhar até um elemento, type para texto. logs retorna a saída do console do canal — tudo o que ele imprime, por que saiu e o erro com backtrace após um erro de execução — então um canal que falha ao iniciar é diagnosticado a partir de logs junto com screenshot e ui-tree.
Usando com um cliente MCP
Os mesmos comandos também estão disponíveis como ferramentas MCP, então qualquer agente compatível com MCP pode chamá-los diretamente em vez de usar o CLI. MCP é um padrão aberto, então isso funciona com qualquer modelo e qualquer cliente que o suporte — não apenas um fornecedor.
O servidor roda via stdio: o comando é vibeview e o argumento é mcp. Cada cliente expressa isso de forma ligeiramente diferente.
Claude Code (linha de comando):
claude mcp add vibeview -- vibeview mcp
Cursor — .cursor/mcp.json no seu projeto (ou o global em ~/.cursor/):
{
"mcpServers": {
"vibeview": {
"command": "vibeview",
"args": ["mcp"]
}
}
}
Claude Desktop — claude_desktop_config.json, mesma forma de mcpServers acima.
VS Code (modo agente do GitHub Copilot) — .vscode/mcp.json:
{
"servers": {
"vibeview": {
"command": "vibeview",
"args": ["mcp"]
}
}
}
Qualquer outra coisa — aponte o cliente para o comando vibeview com args ["mcp"]. Se o cliente não encontrar o binário, use o caminho absoluto de which vibeview (uma instalação npm global geralmente resolve sem ele).
A autenticação vem do mesmo lugar que o CLI: execute vibeview login uma vez ou defina VIBEVIEW_API_TOKEN no ambiente em que seu cliente inicia o servidor. Se seu cliente suportar variáveis de ambiente por servidor, adicione lá:
{
"mcpServers": {
"vibeview": {
"command": "vibeview",
"args": ["mcp"],
"env": { "VIBEVIEW_API_TOKEN": "your-token" }
}
}
}
Os nomes das ferramentas são os nomes dos comandos acima com hífens substituídos por sublinhados (ui_tree, tap, scroll_to, long_press e assim por diante), e cada uma aceita seus argumentos por nome mais um session_id opcional.
Oito ferramentas existem apenas via MCP, cobrindo as etapas que um usuário do CLI faria com comandos de shell comuns, para que um agente sem acesso ao shell ainda possa executar o loop completo:
| Ferramenta | Propósito |
|---|---|
upload_app | Envie um build por caminho (um .app de simulador iOS ou tvOS zipado, um .apk Android ou Android TV, ou um .zip de canal Roku) e receba o ID do aplicativo para iniciar uma sessão — o equivalente a vibeview upload-app. |
list_apps | Liste os aplicativos da sua organização com IDs e plataformas, opcionalmente para uma plataforma — o equivalente a vibeview list-apps. |
dev_start | Inicie uma sessão que permanece aberta pela vida útil da conexão — o equivalente a vibeview dev --detach. Ele retorna assim que o aplicativo está no dispositivo e informa qual dispositivo recebeu: modelo, versão do SO, categoria e, para dobráveis, a postura e a tela acesa. Também informa qual build instalou (id, versão, hora do upload e nota), lista os outros builds de depuração do aplicativo e nomeia a versão do React Native que o projeto local usa, para que uma tela de erro vermelha logo após o início (como "React Native version mismatch") possa ser rastreada até um build feito de outro código nativo. Sem build_id, ele escolhe o build da mesma forma que vibeview dev: o build de depuração mais recente, a menos que a versão do aplicativo do projeto possa ser lida e um build de depuração mais antigo tenha essa versão enquanto o mais recente não; ele então executa esse e informa o motivo, e informa quando a versão não pode ser lida. Se todos os dispositivos do modelo estiverem ocupados, ele espera na fila e relata sua posição e progresso enquanto espera (para clientes que pedem progresso) e quanto tempo esperou. Passe model (por exemplo, "iPhone Duo") para executar em um modelo exato de dispositivo e build_id para executar um build específico. Passe standalone: true para executar um build como está, sem Metro: um build de release ou um build de CI ou nuvem que você só quer abrir. Um build de depuração não precisa disso, mesmo com JavaScript empacotado. Uma sessão autônoma sem build_id executa o build de release mais recente do aplicativo. Um aplicativo para outra plataforma é recusado antes de qualquer dispositivo ser usado. |
list_builds | Liste os builds enviados de um aplicativo com seu tipo (debug, release ou unknown) — o equivalente a vibeview list-builds. |
list_device_models | Liste os modelos de dispositivo que você pode iniciar, com quantos de cada estão livres e ocupados agora, e um marcador foldable para cada dispositivo com dobradiça (o iPhone Duo, dobráveis Android como o Pixel 9 Pro Fold) — o equivalente a vibeview list-devices --models. Um dev_start em um modelo sem nenhum livre espera na fila por um. |
dev_stop | Pare a sessão que dev_start iniciou. Se nenhuma estiver em execução, ele informa. |
stop_session | Pare uma sessão pelo ID — o equivalente a vibeview stop <session-id>. Uma sessão que já terminou é relatada como tal. A sessão de um colega é recusada a menos que você seja administrador da organização. |
dev_reload | Apenas Roku: reempacote o canal com o comando de build em vibeview.json, envie-o e reinicie-o na sessão que dev_start mantém (ou, sem sessão mantida, a sessão vibeview dev --detach deste projeto) — o equivalente a pressionar r em vibeview dev. Um session_id nomeando qualquer outra sessão é recusado. Outras plataformas recarregam a quente via Metro sozinhas. |
Observe que dev_start tem estado: uma vez que ele é bem-sucedido, toda chamada de ferramenta posterior que não nomeie um session_id tem como alvo essa sessão. Apenas uma pode ser executada por vez, e ela mantém minutos de streaming faturáveis até dev_stop ser chamado ou a conexão terminar. Quando o cliente sai, fecha a conexão ou para o servidor, o servidor para a sessão que dev_start iniciou — incluindo uma ainda esperando na fila — antes de sair. Uma sessão que ele apenas teve como alvo por session_id, ou uma sessão vibeview dev --detach, continua em execução. Se o servidor desaparecer sem isso (for morto, travar ou o computador dormir), sua sessão termina em cerca de 90 segundos, a menos que uma aba do navegador ainda a esteja assistindo. O tempo limite de inatividade também se aplica a uma sessão dev_start, porque manter a conexão aberta não é uso. Ações do dispositivo (toque, digitação, deslize, pressionar e as outras ferramentas de ação, e dev_reload no Roku) mantêm a sessão ativa. Ler com ui_tree, screenshot, logs ou find, ou esperar com wait, não. Quando a sessão está a cerca de um minuto de terminar por inatividade, o próximo resultado da ferramenta termina com uma linha como Session … ends in 40 s for inactivity, para que o agente possa agir ou parar. Se a sessão terminar de outra forma (parada no painel, tempo limite de inatividade ou uma falha), a próxima chamada de ferramenta informa que a sessão terminou e por quê, e dev_start inicia uma nova. Se outra conexão de desenvolvimento VibeView assumir a sessão, o servidor a libera sem pará-la, e a próxima chamada de ferramenta informa isso. Após o computador acordar do sono, essa mensagem informa que a sessão terminou porque o computador parou de fazer check-in.
Quando um cliente se conecta, o servidor também envia um breve guia (o campo MCP instructions) descrevendo este loop, que clientes como Claude Desktop mostram ao modelo.
Canais Roku executam o mesmo loop via MCP: dev_start com plataforma roku inicia a sessão, e dev_reload após cada edição coloca o novo pacote no dispositivo. De um shell, vibeview dev --platform roku --detach e vibeview dev-reload são os mesmos dois passos.
Dirigindo uma sessão de embed
Uma sessão que alguém iniciou a partir de uma demo incorporada pode ser dirigida com os mesmos comandos. Você tem como alvo a sessão que esse visitante já está usando: passe o ID com --session <id> (ou session_id via MCP) e todo comando chega ao dispositivo na frente deles. O Agent Control nunca inicia uma segunda sessão para isso, e nenhum dispositivo adicional é usado.
Obtendo o ID da sessão
A página incorporada o anuncia à sua página. Ouça o evento session:started — ele carrega o ID — e entregue-o ao seu agente:
window.addEventListener('message', (event) => {
if (event.data?.source !== 'vibeview-embed') return;
if (event.data.type === 'session:started') {
const sessionId = event.data.sessionId; // pass this to Agent Control
}
});
Se a página identificar seus visitantes — consulte Identificando seus visitantes — o id que você recebe é a sessão daquele visitante e, com o Máximo de sessões por visitante no padrão de 1, ele permanece o mesmo entre recarregamentos: eles voltam para a sessão já em execução em vez de iniciar uma nova, então um agente que você apontou para ela antes ainda está apontado para o dispositivo à frente deles. Aumente esse limite e um recarregamento inicia uma sessão com um NOVO id até o visitante atingir o limite, então releia o id em cada session:started em vez de presumir que é o que você já tem. Cada carregamento anuncia a sessão para sua página novamente, então você verá session:started carregando o mesmo id de antes — após uma curta espera, se essa sessão ainda estiver sendo configurada, ou imediatamente se estiver pronta. Em um embed anônimo, cada carregamento é uma sessão diferente com um id diferente.
Se o visitante cair em uma fila, você receberá session:queued primeiro — esse evento carrega apenas a posição na fila, não um id, porque nenhum dispositivo foi entregue ainda. Aguarde o session:started. Consulte Eventos de página para a lista completa.
Mantenha sua credencial no seu próprio servidor. A página do embed nunca recebe uma, e sua página também não deveria guardar uma — ela só precisa do id da sessão.
Duas coisas precisam ser verdadeiras primeiro:
- O Controle de Agente está permitido naquela chave de embed. É uma configuração por chave em Configurações → Embeds, desativada por padrão, e apenas um administrador da organização pode ativá-la — consulte Permitir controle de agente.
- Você está chamando como Desenvolvedor ou Administrador na organização que possui a chave. Credenciais de Visualizador são recusadas, e um token nunca carrega mais autoridade do que o membro ao qual pertence.
Pessoas visualizando seu embed em um navegador nunca ganham nada disso. Não há superfície de comandos na página incorporada — conduzir a sessão é algo que sua organização faz com suas próprias credenciais, a partir de suas próprias ferramentas — e ativar a configuração não muda nada sobre o que um visitante pode fazer.
Se qualquer uma das condições não for atendida, o comando é recusado imediatamente e nada é executado no dispositivo. O mesmo se aplica no momento em que a chave deixa de estar ativa — desativar a configuração, desabilitar a chave ou revogá-la encerra o acesso do agente no próximo comando, mesmo no meio de uma sessão.
O que está disponível em uma sessão de embed
Um embed mantém deliberadamente os visitantes dentro do seu aplicativo, e o Controle de Agente mantém essa mesma linha. Tudo o que você precisa para ler e conduzir uma tela está lá; as coisas que tirariam um visitante do seu aplicativo, ou o reiniciariam sob ele, não estão.
| Comando | Em uma sessão de embed |
|---|---|
ui-tree, screenshot, logs | Leia a tela e a saída própria do aplicativo, exatamente como em qualquer outra sessão. |
tap, long-press, drag, swipe, scroll, scroll-to | Disponível, por referência de elemento ou por coordenadas. |
type, clear-text | Disponível. |
alert | Disponível — responda a um alerta do sistema sobreposto ao seu aplicativo. |
find, wait | Disponível. |
set-posture, rotate | Disponível — dobrar ou girar o dispositivo muda como o visitante o segura, não o aplicativo em que está. |
set-location | Disponível — muda onde o aplicativo acha que o dispositivo está, não o aplicativo em que o visitante está. |
tap-focused, focus | Disponível em sessões de TV. |
press <button> | Limitado a back e ao d-pad (dpad_up, dpad_down, dpad_left, dpad_right, dpad_center). home, lock e siri são recusados: eles tirariam o visitante do seu aplicativo, apagariam a tela dele ou o entregariam a um assistente do sistema. |
open-url | Não disponível. Abrir um deep link ou URL é o único comando cujo propósito inteiro é sair do aplicativo. |
relaunch-app | Não disponível. O embed mantém seu aplicativo em execução para o visitante e o reinicia sozinho quando necessário. |
Um comando recusado diz o que não é permitido e o que é, para que um agente possa se corrigir em vez de tentar novamente às cegas — e a recusa acontece antes que qualquer coisa chegue ao dispositivo.
O visitante também está usando a tela
Este é o único lugar onde você não está sozinho no dispositivo. Um visitante pode tocar entre dois dos seus comandos, então a tela que você leu há um momento pode não ser a tela na qual você está agindo, e uma referência de elemento que você estava segurando pode estar desatualizada quando você a usar.
Quando isso acontece, o comando retorna sem sucesso sem agir, e a resposta traz uma leitura atualizada da tela atual — incluindo o que o visitante acabou de mudar. Trate isso como algo normal, não como uma falha para tentar novamente: releia a tela e escolha o elemento novamente a partir do que está lá agora. Repetir a mesma referência continuará falhando, porque ela não aponta mais para nada.
Compartilhar a tela é o objetivo disso, não uma falha a ser contornada. O visitante continua tocando enquanto você trabalha, e eles veem suas ações acontecerem ao vivo no próprio dispositivo deles.
A habilidade
O pacote npm também inclui uma habilidade pronta em skills/vibeview-agent/SKILL.md descrevendo todo esse fluxo de trabalho (requisitos de build, o loop, verificação, limpeza e a referência completa de comandos) em uma forma que agentes de codificação com acesso ao sistema de arquivos podem ler e seguir diretamente. Ela cobre o mesmo conteúdo desta página.
Para ver o que um agente faz com ela, leia Um agente de IA enviou um aplicativo Expo para o TestFlight em 43 minutos: um agente de codificação construiu o aplicativo de exemplo Kitlist, verificou-o em dispositivos iOS e Android na nuvem com esses comandos e o enviou para ambas as lojas, com o log de execução citado ao longo do texto.
Cobrança
Uma sessão iniciada dessa forma é uma sessão comum do VibeView — ela cobra minutos de streaming da mesma forma que qualquer sessão iniciada pelo painel. Lembre-se de executar vibeview dev-stop (ou parar a sessão que seu agente iniciou) quando terminar, para que ela não continue em execução sem supervisão.