VibeView
Maneja dispositivos iOS, Android, Apple TV y Android TV en vivo en la nube para verificar cambios en la app: lee el árbol de interfaz, toca, escribe, presiona el control remoto, verifica y lee los registros de la app desde cualquier cliente MCP.
Documentación
Deja que tu agente de IA de codificación verifique un cambio manejando una copia real y en ejecución de tu aplicación en un dispositivo en la nube de VibeView: instalando la compilación, tocando la interfaz, leyendo lo que aparece en pantalla e informando si realmente funcionó. Este es el mismo ciclo de desarrollo en vivo descrito en Live Development, extendido para que un agente (no solo tú) pueda manejar la sesión desde la línea de comandos.
Instalación y autenticación
npm install -g vibeview
vibeview login
vibeview login abre tu navegador para completar la autenticación y guarda un token localmente. En CI o en cualquier entorno no interactivo, configura VIBEVIEW_API_TOKEN en su lugar: cada comando lo acepta como alternativa a estar autenticado.
Cómo ponerlo a disposición de tu agente
Instalar la CLI no le dice por sí solo a tu agente de codificación que VibeView existe. Un comando lo configura:
vibeview agent-setup
Hace tres cosas:
- Instala la habilidad del agente en
~/.claude/skills/vibeview-agent/, donde Claude Code la descubre automáticamente en cada proyecto. La habilidad describe todo el flujo de trabajo (requisitos de compilación, el ciclo de verificación, la limpieza), para que el agente sepa cómo usar VibeView, no solo que existe. Pasa--projectpara instalarla en el.claude/skills/del proyecto actual en su lugar (útil cuando los compañeros de equipo también deberían tenerla, ya que ese directorio se puede confirmar). - Ofrece registrar el servidor MCP con Claude Code (
claude mcp add vibeview -- vibeview mcp), para que las herramientas sean visibles en cada sesión. Pasa--mcppara registrarlo sin preguntar, o--no-mcppara omitirlo. Otros clientes MCP se configuran por cliente: consulta Using it with an MCP client a continuación. - Sugiere una línea para el
CLAUDE.mdde tu proyecto (oAGENTS.mdsi usas otros agentes) que le indique al agente verificar los cambios de interfaz en un dispositivo en vivo. Este es el empujón más fuerte por proyecto: la habilidad le enseña al agente cómo, la línea de instrucción le dice cuándo.
¿Necesitas tanto la habilidad como MCP? Generalmente no. Si tu agente puede ejecutar comandos de shell (Claude Code en una terminal, por ejemplo), la habilidad por sí sola es suficiente: maneja la CLI directamente, y omitir MCP mantiene las definiciones de herramientas fuera del contexto de tu agente. Registra el servidor MCP cuando el agente no pueda ejecutar comandos de shell: Claude Desktop, modo agente de Copilot, entornos restringidos donde se permiten herramientas con nombre pero no comandos arbitrarios. Para esos clientes, MCP no es un extra: es la única vía de acceso.
Los agentes que no sean Claude Code pueden apuntar directamente al archivo de la habilidad: son instrucciones simples que cualquier agente de codificación puede seguir, o se les puede dar el servidor MCP a través de su propia configuración de cliente.
Otras formas de instalar la habilidad
La misma habilidad está publicada en github.com/vibeview/skills, por lo que los agentes que leen habilidades desde GitHub pueden instalarla sin el comando de configuración de la CLI:
- Cualquier agente que use la CLI de
skills(Claude Code, Cursor, Codex, Gemini CLI, GitHub Copilot y otros):npx skills add vibeview/skills - Claude Code, como plugin: instala la habilidad y registra el servidor MCP en un solo paso:
/plugin marketplace add vibeview/skills /plugin install vibeview@vibeview
Sea cual sea la ruta que uses, el agente aún necesita la CLI de vibeview instalada y autenticada (vibeview login), porque la habilidad maneja la CLI y el servidor MCP del plugin ejecuta vibeview mcp.
Una nota honesta: incluso con todo configurado, un agente al que solo se le pide "implementar X" no siempre verificará en un dispositivo sin que se lo pidan. La configuración anterior aumenta considerablemente las probabilidades, pero decir "implementa X y verifícalo en el dispositivo" es lo que lo hace confiable.
El ciclo de desarrollo
- Sube una compilación de depuración de tu aplicación (solo se necesita de nuevo después de un cambio nativo: consulta Preparing Your Build):
vibeview upload-app ./path/to/app-debug.apk - Inicia Metro en tu proyecto de React Native (
yarn starto equivalente). - Inicia una sesión en segundo plano:
Esto imprime un evento devibeview dev --detach --jsonsession_readycon unsession_idque tu agente usa para apuntar a cada comando que sigue (--session <id>).session_readyse dispara una vez que la sesión realmente acepta comandos (el dispositivo está activo y la aplicación está instalada), por lo que el primerui-treepuede seguirlo inmediatamente. El evento también incluye la URL de una página donde puedes ver e interactuar con la sesión: las sesiones desacopladas nunca abren un navegador por sí mismas, así que abre esa URL tú mismo si quieres ver lo que sucede. Su campobuildindica qué compilación se instaló y por qué, y enumera las otras compilaciones de depuración de la aplicación (ver más abajo). La página muestra el dispositivo en vivo solo a la cuenta que inició la sesión; un compañero que la abra verá de quién es la sesión. Para permitir que otra persona la vea o la maneje, activa collaboration y envíale su enlace. - Maneja la sesión con los verbos de comando a continuación: siempre comienza con
ui-treepara ver lo que hay en pantalla y obtener referencias de elementos (@e5, etc.) para actuar. Si la aplicación aún está en su pantalla de inicio o de carga, el árbol puede volver vacío o casi vacío: eso es la aplicación iniciándose, no una sesión rota. Espera unos segundos y vuelve a obtenerlo. - Cuando termines, detén la sesión:
vibeview dev-stop
Cada comando también funciona contra una sesión que ya iniciaste desde el panel de VibeView: pasa su ID de sesión con --session <id> en lugar de iniciar una nueva con dev --detach. Detén dicha sesión con vibeview stop <session-id>.
Para ejecutar una subida específica en lugar de la compilación de depuración más reciente de la aplicación, enumera las compilaciones de la aplicación y fija una: debe ser una compilación de depuración (KIND debug):
vibeview list-builds <app-id>
vibeview dev --detach --json --app <app-id> --build <build-id>
Sin --build <id>, vibeview dev ejecuta la compilación de depuración más reciente de la aplicación, a menos que se pueda leer la versión de la aplicación del proyecto (desde la configuración de Expo, incluido un app.config.ts dinámico cuando Expo está instalado en el proyecto, o desde el proyecto nativo) y una compilación de depuración más antigua tenga esa versión mientras que la más reciente no; entonces ejecuta esa y explica por qué. Si la versión no se puede leer, lo dice. Cuando la sesión comienza, imprime la compilación que instaló (id, versión, tiempo de subida y nota), la compilación de depuración más reciente de cada otra versión y la versión de React Native del proyecto. Si la aplicación muestra una pantalla de error roja justo después de iniciarse, la compilación puede haberse hecho desde otro código nativo que no sea el de tu proyecto: ejecuta una compilación de la versión de tu proyecto con --build <id>.
Ver en paralelo y tomar el control
Cada sesión tiene una página en vivo y totalmente interactiva (el url en session_ready, o page_url desde la herramienta MCP dev_start). La habilidad incluida instruye a los agentes a compartirla contigo en el momento en que comienza una sesión, para que puedas ver al agente trabajar en tiempo real y manejar el dispositivo tú mismo cuando quieras. Los comandos del agente y tu entrada coexisten; mantén una sola pestaña del navegador por sesión.
Ese control también es cómo los agentes se desbloquean. La habilidad les dice que se detengan y te pregunten cada vez que una pantalla necesite algo que solo un humano debería proporcionar (iniciar sesión con credenciales reales, un código 2FA, un CAPTCHA o una confirmación arriesgada) en lugar de adivinar. Haz tu parte en la página de la sesión, dile al agente que terminaste, y este vuelve a leer la pantalla (verificando que el bloqueo realmente desapareció) antes de continuar.
Una sesión desacoplada registra su estado en un directorio .vibeview/ en la raíz de tu proyecto (así es como los comandos posteriores lo encuentran sin --session). Es estado de la máquina local, no algo para confirmar: cuando tu proyecto tiene un .gitignore, la CLI agrega .vibeview/* a él, además de !.vibeview/test-spec.json para que una test spec en esa carpeta aún se confirme. No ignores toda la carpeta con .vibeview/: Git no puede volver a incluir un archivo dentro de una carpeta ignorada.
Comandos
Cada comando a continuación acepta --session <id> (apunta a una sesión específica; recurre a la sesión dev --detach del proyecto actual si se omite) y --json (imprime el resultado sin procesar como una línea JSON en lugar de texto legible).
| Comando | Propósito |
|---|---|
ui-tree | Obtiene los elementos de la interfaz de la pantalla actual, cada uno con una referencia (@e5) para actuar. |
logs | Lee los registros recientes de la aplicación desde el dispositivo: salida de consola JS, errores nativos y mensajes de fallos. |
screenshot | Captura una captura de pantalla de la pantalla actual y la guarda en disco. Sus píxeles son las coordenadas que tap y drag toman (consulta Coordinates). |
tap <target> | Toca un elemento por referencia (p. ej., @e5) o por coordenadas (p. ej., 100,200), en el espacio descrito en Coordinates. |
long-press <ref> | Mantiene presionado (tocar y mantener) un elemento. |
swipe <direction> | Desliza hacia arriba, abajo, izquierda o derecha. |
scroll <direction> | Desplaza la vista actual hacia arriba, abajo, izquierda o derecha. |
scroll-to <text> | Desplaza hasta que un elemento que coincida con ese texto sea visible, deteniéndose al final de la lista. |
drag <from> <to> | Arrastra entre dos puntos, cada uno una referencia de elemento o coordenadas sin procesar. |
alert <get|accept|dismiss> | Inspecciona o responde a una alerta del sistema en iOS o Apple TV. |
type <text> | Escribe texto en el campo de entrada enfocado actualmente. |
clear-text | Borra el campo de entrada enfocado actualmente. |
press <button> | Presiona un botón del dispositivo o un gesto del sistema. iPhone/iPad: home, back, lock, siri, enter. Teléfono/tableta Android: 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 (los nombres dpad_* también funcionan). vibeview press --help los enumera, y una pulsación rechazada imprime la lista. |
open-url <url> | Abre un enlace profundo dentro de la aplicación. En iOS y Apple TV solo se acepta el esquema de URL propio de la aplicación (ver más abajo). |
relaunch-app | Cierra la aplicación bajo prueba y la inicia de nuevo. Es un inicio en frío que conserva los datos de la aplicación, por lo que es la forma de verificar que algo sobrevive a un reinicio; el registro de la aplicación sigue transmitiéndose a través de él. No disponible en Roku. |
set-posture <closed|partial|open> o set-posture --angle <0-180> | Dobla o despliega un dispositivo plegable (factor de forma foldable en list-devices --models: el iPhone Duo, plegables Android como el Pixel 9 Pro Fold), a un valor preestablecido o a un ángulo de bisagra exacto en grados (pasa uno u otro, no ambos). Da error en un dispositivo sin bisagra. |
rotate [--degrees <90|180|270>] | Rota el dispositivo. Un teléfono o tableta, y un plegable Android, cambia entre vertical y horizontal (solo se acepta 90); el iPhone Duo gira un cuarto de vuelta en sentido horario, 270 lo gira un cuarto hacia atrás y 180 lo pone boca abajo. No disponible en dispositivos TV. |
set-location --lat <n> --lon <n> | Establece la posición GPS simulada del dispositivo, en grados decimales (oeste y sur son negativos), para probar flujos basados en ubicación. La aplicación lo lee como una fijación GPS. Teléfonos, tabletas y plegables; no disponible en dispositivos TV. |
wait | Espera un momento antes de la siguiente acción. |
find <text> | Encuentra un elemento por su texto, opcionalmente restringido a estar cerca/arriba/abajo de otro elemento. |
tap-focused <ref> | Solo TV: mueve el foco a un elemento y selecciónalo en un solo paso. |
focus <ref> | Solo TV: mueve el foco a un elemento sin seleccionarlo. |
Después de cada acción, la respuesta te dice qué cambió en pantalla: una lista completa de elementos, las diferencias específicas o una nota de que nada cambió, para que un agente siempre sepa el estado actual antes de decidir qué hacer a continuación. Eso significa que un bucle normal es ui-tree una vez al inicio, luego actúa, actúa, actúa: no necesitas volver a obtener el árbol entre pasos.
Algunos detalles de esa salida:
- La lista de diferencias comienza con los valores de campos modificados (lo que acabas de escribir o establecer) y termina con un recuento de cualquier diferencia que haya omitido.
- Mientras el teclado de iOS está visible, aparece como una sola línea al final del árbol (
keyboard: visible, con una referencia a su tecla de retorno) en lugar de una línea por tecla; los elementos propios de la aplicación permanecen en la parte superior. - En Apple TV, Android TV y Roku,
focus,tap-focusedypresscomienzan con el elemento que tiene el foco después, por ejemplofocused: @e112 button "Camping". - Cuando varios elementos coinciden,
findprefiere uno cuya etiqueta visible contenga el texto, y lo indica cuando en su lugar coincidió con el id de un elemento. - Si el dispositivo no devuelve ningún árbol de elementos, incluso al leerlo de nuevo un momento después,
ui-treeimprimeerrory sale con el código1en lugar de imprimir un árbol vacío; ejecútalo de nuevo.
Un comando que se ejecutó pero no hizo lo que se le pidió imprime error en lugar de ok y sale con el código 1, como uno rechazado. Eso incluye un scroll-to que nunca encontró su texto, y un type o clear-text cuyo campo no contiene el texto esperado después; el motivo se imprime debajo de la línea de estado. Un campo de contraseña no se puede leer de vuelta, por lo que escribir en uno no se verifica.
Coordenadas
tap y drag toman coordenadas en el mismo espacio que los rectángulos de elementos en ui-tree y los resultados de find. En un teléfono, tableta o Android TV, screenshot guarda su imagen en ese tamaño, por lo que un punto que leas de una captura de pantalla se puede tocar tal cual. No escales las coordenadas tú mismo. En Android, este espacio puede ser más pequeño que la resolución de pantalla del dispositivo (las capturas de un Pixel 7 Pro son 1152x2496, no 1440x3120), y siempre cubre toda la pantalla, incluidas las barras de estado y navegación, incluso cuando la ventana propia de la aplicación no lo hace. Las coordenadas fuera de él se rechazan con un error que indica el rango válido.
En Roku, los rectángulos en ui-tree están en las coordenadas de UI propias del canal (1920x1080 para un canal full-HD), mientras que una captura de pantalla es la imagen que el Roku emite, que puede ser más pequeña (1280x720 a 720p). Un Roku se controla con el mando, no por coordenadas, así que compara ambos por proporción: en un canal full-HD en una captura de 1280x720, divide un rectángulo por 1.5. Los rectángulos también pueden extenderse más allá del borde de la pantalla, para las partes de una fila que están desplazadas fuera de vista.
Botones, Enter y enlaces profundos
press toma los botones del dispositivo en el que estás. Un nombre que el dispositivo no tiene se rechaza, y el error enumera los válidos para ese dispositivo.
press enter presiona Enter (Retorno en iOS), que envía el campo de texto enfocado, como un cuadro de búsqueda o el último campo de un formulario. Funciona en teléfonos, tabletas, Android TV y Roku; en Apple TV, usa dpad_center. En teléfonos y tabletas, un salto de línea en type hace lo mismo, por lo que vibeview type $'hello\n' escribe hello y luego envía.
open-url entrega el enlace solo a la aplicación bajo prueba. En Android, un enlace para el que la aplicación no tiene pantalla (una página web, o un enlace de otra aplicación) falla con un error que lo indica; nunca abre un navegador u otra aplicación. En iOS y Apple TV, el sistema decide qué aplicación abre un enlace, por lo que solo se aceptan enlaces en el esquema de URL propio de la aplicación (por ejemplo myapp://settings): un enlace web (https://...) o un enlace del sistema como tel: se rechaza con un error en lugar de abrir Safari u otra aplicación.
Un press back que deja la pantalla exactamente como estaba aún imprime ok, con una nota de que nada cambió: la pantalla puede no responder al gesto de retroceso del sistema (un encabezado personalizado, por ejemplo), así que toca el botón de retroceso propio de la aplicación en su lugar.
Dispositivos plegables
Cualquier dispositivo con bisagra es un plegable: el iPhone Duo (iOS) y los plegables Android como el Pixel 9 Pro Fold. Para ejecutar en uno, inicia la sesión con su nombre de modelo. Una sesión que solo pide un teléfono o tableta (el valor predeterminado) nunca recibe un plegable, incluso cuando un plegable es el único dispositivo libre: espera un teléfono como cualquier otro inicio ocupado. vibeview list-devices --models enumera los modelos que puedes iniciar y marca cada plegable 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 funcionan igual: la sesión comienza cerrada, en la pantalla de cubierta más pequeña, y lo pliegas con un ajuste preestablecido o un ángulo exacto, y lo rotas en cualquier postura:
vibeview set-posture partial --session <id>
vibeview set-posture --angle 75 --session <id>
vibeview rotate --session <id>
Cada respuesta indica dónde terminó el dispositivo. Después de set-posture, eso es la postura en la que se asentó, el ángulo de la bisagra y el tamaño de la pantalla que se muestra ahora, tal como se muestra (el ancho y el alto se intercambian mientras la imagen está girada de lado, por lo que el iPhone Duo abierto es 2853 x 2007), y la pantalla iluminada, cover o inner. El dispositivo decide qué pantalla iluminar, como lo haría uno real, y la respuesta espera hasta que se haya asentado.
Los ajustes preestablecidos mueven la bisagra a los mismos ángulos en cada plegable: closed 0, partial 120, open 180 grados, pero cada dispositivo nombra un ángulo exacto mediante rangos que el propio dispositivo define, por lo que el mismo ángulo puede leerse de manera diferente. Confía en el posture en la respuesta sobre el ángulo que pediste. El iPhone Duo y, como ejemplo de Android, el Pixel 9 Pro Fold:
| Ángulo de bisagra | iPhone Duo | Pixel 9 Pro Fold |
|---|---|---|
| 0 | closed | closed |
| 1-30 | generalmente closed; puede permanecer partial | closed |
| 31-84 | generalmente closed; puede permanecer partial | partial |
| 85-149 | generalmente partial; puede permanecer closed | partial |
| 150-169 | generalmente partial; puede permanecer closed | open |
| 170-180 | open | open |
En el iPhone Duo, qué pantalla se ilumina entre 1 y 169 grados depende de cómo llegó la bisagra allí. Movido de una sola vez desde cerrado o desde completamente abierto, cambia a aproximadamente 85 grados en ambas direcciones: la columna "generalmente". Movido en pequeños pasos, mantiene la pantalla que ya tenía: abrir poco a poco puede mantener la pantalla de cubierta encendida hasta aproximadamente 160 grados, y cerrar poco a poco puede mantener la pantalla interior encendida hasta que el dispositivo esté cerrado. Solo 0 (la cubierta) y 170 y más (la pantalla interior) son iguales sin importar lo que vino antes; los posture_ranges del dispositivo marcan los otros ángulos con la postura que también pueden leer. Un plegable Android nombra un ángulo de la misma manera sin importar la dirección de la que vino, y usa su pantalla interior siempre que no esté cerrado, incluido el semiabierto. Después de rotate, es la nueva orientación (portrait, landscape_left, landscape_right o portrait_upside_down) y, en el Duo, cómo se giran los contenidos de la pantalla: una aplicación que admite la nueva orientación gira su diseño, mientras que la pantalla de inicio y las aplicaciones solo verticales giran con el dispositivo.
La respuesta de rotate de un Duo lleva dos campos de orientación, y responden preguntas diferentes:
device_orientationes cómo se sostiene el propio dispositivo. Se mueve un cuarto de vuelta con cadarotatey comienza enportrait.screen_orientationes cómo está girada la imagen en la pantalla iluminada, medida contra el borde vertical propio de esa pantalla. No es una segunda lectura de cómo se sostiene el dispositivo, por lo que los dos campos pueden nombrar orientaciones diferentes incluso cuando la aplicación giró con el dispositivo.
Para verificar si tu aplicación se diseñó para la nueva orientación, compara el ancho y el alto de la raíz del nuevo árbol de UI en lugar de cualquiera de los campos. La respuesta de rotate de un plegable Android también lleva ambos campos, con la postura en la que se rotó. Los teléfonos y tabletas informan device_orientation y, como screen_orientation, cómo salió realmente la pantalla: una aplicación que solo admite vertical permanece vertical cuando el dispositivo gira, y la respuesta lo indica (device held landscape_left (screen stayed portrait)) en lugar de sugerir que la pantalla giró.
Plegar cambia entre dos pantallas de diferentes tamaños y rotar gira la imagen, por lo que cada referencia de elemento anterior está obsoleta: la respuesta lleva la nueva pantalla, y ui-tree la obtiene de nuevo. Un dispositivo que aún se está plegando rechaza una rotación; inténtalo de nuevo una vez que el pliegue haya terminado.
Usa el árbol y la captura de pantalla para cosas diferentes
El árbol de UI describe la estructura: qué elementos existen, qué texto llevan y dónde están. No lleva información de color, contraste o apilamiento, por lo que una pantalla puede pasar todas las verificaciones que el árbol puede expresar y aún así estar visiblemente rota: texto renderizado en un color que desaparece en el fondo, o un elemento dibujado debajo de otro.
Usa el árbol para encontrar elementos y confirmar la estructura. Toma una captura de pantalla siempre que un cambio afecte cómo se ve algo, e inspecciona la imagen misma.
El árbol describe tu aplicación, no las barras de estado y navegación del dispositivo. En Android, cuando la sombra de notificaciones se despliega sobre tu aplicación, el árbol describe la sombra en su lugar, por lo que sus botones se pueden encontrar y tocar.
Dos vacíos a conocer en iOS. El árbol no muestra si una casilla de verificación está marcada: el cambio sí aparece en el diff después de tocarla (por ejemplo value "checkbox, unchecked" → "checkbox, checked"), y una captura de pantalla lo muestra. Y las barras de desplazamiento de una vista desplazable se enumeran como elementos slider ("Vertical scroll bar, 1 page"); no son controles para actuar, así que desplázate con swipe en su lugar.
A través de MCP, screenshot escribe el archivo y devuelve su ruta; pasa inline: true para recibir la imagen misma. Un agente que no puede abrir archivos en la máquina que ejecuta el servidor necesita inline: true para ver algo en absoluto: cuesta contexto, por lo que vale la pena reservarlo para las verificaciones donde la apariencia realmente importa. La imagen en línea se mantiene por debajo de 1 MB para que los clientes de modelos la acepten: una captura de pantalla grande vuelve como JPEG, al mismo tamaño que el PNG guardado, por lo que un punto leído de ella sigue siendo la coordenada que tap y drag toman. Con --json, la CLI imprime dónde escribió el archivo y su tamaño, no los datos de la imagen.
Un ejemplo trabajado
Agregar un encabezado a una pantalla y confirmar que se renderiza, con recarga en caliente ya ejecutándose:
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
Sin reconstrucción y sin re-subida: un cambio de JavaScript o de activos llega al dispositivo a través de Metro. Solo reconstruyes cuando cambian las dependencias nativas.
Las referencias de elementos quedan obsoletas tan pronto como la pantalla cambia. Usar una obsoleta se rechaza y la acción no se ejecuta; como no pasó nada, la respuesta informa la pantalla como sin cambios en lugar de devolver una lista de elementos fresca, así que ejecuta ui-tree de nuevo para obtener referencias en vivo antes de reintentar.
Algunos comandos vale la pena conocer específicamente:
scroll-toes la forma correcta de alcanzar algo fuera de pantalla: sigue desplazándose hasta que un elemento cuyo texto o etiqueta de accesibilidad coincida sea visible, y se rinde cuando el contenido deja de moverse, en lugar de que adivines cuántas llamadas ascrollse necesitan. Determina por sí solo en qué dirección desplazarse en listas verticales ordinarias; pasa--directionpara filas horizontales de elementos. En un iPhone Duo abierto o parcialmente abierto,scroll-tono está disponible en la pantalla interior y devuelve un error indicándolo; usascrolloswipey revisa el árbol nuevamente.drages el único comando para un gesto preciso de dos puntos: arrastrar un control deslizante a un valor, reordenar una lista arrastrando un elemento o desplazar un mapa.swipeyscrollsolo aceptan una dirección y no pueden expresar eso. Cada extremo puede ser una referencia a un elemento o coordenadas, por lo que puedes combinarlos. Las opciones te permiten ralentizar el arrastre para mayor precisión, mantener presionado antes de que comience el arrastre (para gestos de reordenamiento) y mantener en el destino antes de soltar.alertmaneja alertas del sistema, como solicitudes de permisos, que se superponen a tu aplicación y la bloquean. Usaalert getpara leer el mensaje y las etiquetas exactas de los botones, luegoalert acceptoalert dismiss— opcionalmente con--buttonpara elegir una etiqueta específica. En Apple TV, los mismos comandos cubren los avisos de tvOS, incluida la confirmación "¿Abrir en …?" que genera un enlace profundo; el botón elegido se responde con el control remoto, por lo que nada en tu aplicación se toca.logses cómo un agente descubre por qué algo falló. Devuelve la salida propia de la aplicación — líneas de consola de JS, errores nativos, mensajes de bloqueo — limitada a la aplicación bajo prueba, no a todo el dispositivo. Cuando la aplicación se bloquea o se congela, la pantalla por sí sola no puede explicarlo; el registro generalmente sí. Cada respuesta termina con un valor decursor: pásalo de vuelta como--sincepara recibir solo las líneas que llegaron después de tu lectura anterior, de modo que actuar y luego verificarlogs --since <cursor>muestre exactamente lo que esa acción registró.--taillimita cuántas líneas se devuelven (predeterminado 100, máximo 500, se conservan las más recientes). En Roku, el registro es la consola del canal: su salida deprint, la razón por la que salió y el error con el rastreo de pila cuando se detiene por un error en tiempo de ejecución.
Para aplicaciones de Apple TV y Android TV, inicia la sesión con --platform tvos o --platform androidtv y navega con el pad direccional mediante press (dpad_up, dpad_down, dpad_left, dpad_right, dpad_center). press home sale de la aplicación en ambos; el siguiente ui-tree describe lo que está en pantalla (la pantalla de inicio de TV o un aviso superpuesto), no un árbol vacío.
Las sesiones de Roku comienzan desde vibeview dev --platform roku (con --detach si el agente ejecuta el bucle por sí mismo) y aceptan los mismos verbos, con algunos detalles específicos de Roku. Los nombres propios del control remoto son press up, down, left, right, select y back (los nombres de dpad_* funcionan como alias), además de enter, backspace y las teclas multimedia play, rewind, forward, replay y info, y press home se rechaza, porque la sesión está confinada a tu canal. Cualquier otro nombre también se rechaza, con la lista anterior. type envía el texto directamente a un teclado en pantalla enfocado, y el texto escrito se lee de vuelta desde el campo, por lo que el resultado te indica si se registró. open-url no está disponible. Un canal se mueve por enfoque, no por toque, por lo que tap, long-press, swipe, scroll, drag y scroll-to se rechazan en Roku con un mensaje que indica qué usar en su lugar: press para moverse, focus para caminar hasta un elemento, type para texto. logs devuelve la salida de consola del canal — todo lo que imprime, por qué salió y el error con el rastreo de pila después de un error en tiempo de ejecución — por lo que un canal que no se inicia se diagnostica desde logs junto con screenshot y ui-tree.
Uso con un cliente MCP
Los mismos comandos también están disponibles como herramientas MCP, por lo que cualquier agente compatible con MCP puede llamarlos directamente en lugar de usar la CLI. MCP es un estándar abierto, por lo que funciona con cualquier modelo y cualquier cliente que lo hable, no solo con un proveedor.
El servidor se ejecuta sobre stdio: el comando es vibeview y el argumento es mcp. Cada cliente lo expresa de manera ligeramente diferente.
Claude Code (línea de comandos):
claude mcp add vibeview -- vibeview mcp
Cursor — .cursor/mcp.json en tu proyecto (o el global en ~/.cursor/):
{
"mcpServers": {
"vibeview": {
"command": "vibeview",
"args": ["mcp"]
}
}
}
Claude Desktop — claude_desktop_config.json, misma forma de mcpServers que arriba.
VS Code (modo agente de GitHub Copilot) — .vscode/mcp.json:
{
"servers": {
"vibeview": {
"command": "vibeview",
"args": ["mcp"]
}
}
}
Cualquier otra cosa — apunta el cliente al comando vibeview con argumentos ["mcp"]. Si el cliente no puede encontrar el binario, usa la ruta absoluta de which vibeview (una instalación global de npm generalmente se resuelve sin ella).
La autenticación proviene del mismo lugar que la CLI: ejecuta vibeview login una vez, o establece VIBEVIEW_API_TOKEN en el entorno con el que tu cliente inicia el servidor. Si tu cliente admite variables de entorno por servidor, agrégalas allí:
{
"mcpServers": {
"vibeview": {
"command": "vibeview",
"args": ["mcp"],
"env": { "VIBEVIEW_API_TOKEN": "your-token" }
}
}
}
Los nombres de las herramientas son los nombres de los comandos anteriores con guiones reemplazados por guiones bajos (ui_tree, tap, scroll_to, long_press, y así sucesivamente), y cada una acepta sus argumentos por nombre más un session_id opcional.
Ocho herramientas existen solo a través de MCP, cubriendo los pasos que un usuario de CLI haría con comandos de shell ordinarios, para que un agente sin acceso a shell pueda ejecutar todo el bucle:
| Herramienta | Propósito |
|---|---|
upload_app | Sube una compilación por ruta (un .app de simulador de iOS o tvOS comprimido, un .apk de Android o Android TV, o un .zip de canal de Roku) y devuelve el ID de la aplicación para iniciar una sesión — el equivalente de vibeview upload-app. |
list_apps | Lista las aplicaciones de tu organización con sus IDs y plataformas, opcionalmente para una plataforma — el equivalente de vibeview list-apps. |
dev_start | Inicia una sesión que permanece abierta durante toda la vida de la conexión — el equivalente de vibeview dev --detach. Devuelve una vez que la aplicación está activa en el dispositivo y dice qué dispositivo obtuvo: modelo, versión del sistema operativo, categoría y, para un plegable, la postura y la pantalla iluminada. También dice qué compilación instaló (id, versión, tiempo de carga y nota), lista las otras compilaciones de depuración de la aplicación y nombra la versión de React Native que usa el proyecto local, para que una pantalla de error roja justo después del inicio (como "React Native version mismatch") pueda rastrearse hasta una compilación hecha desde otro código nativo. Sin build_id, elige la compilación de la misma manera que vibeview dev: la compilación de depuración más reciente, a menos que la versión de la aplicación del proyecto pueda leerse y una compilación de depuración más antigua tenga esa versión mientras que la más reciente no; entonces ejecuta esa y dice por qué, y dice cuándo no se puede leer la versión. Si todos los dispositivos del modelo están ocupados, espera en la cola e informa su lugar y progreso mientras espera (a clientes que soliciten progreso), y cuánto tiempo esperó. Pasa model (por ejemplo, "iPhone Duo") para ejecutar en un modelo de dispositivo exacto, y build_id para ejecutar una compilación específica en su lugar. Pasa standalone: true para ejecutar una compilación tal como está, sin Metro: una compilación de lanzamiento, o una compilación de CI o nube que solo quieras abrir. Una compilación de depuración no lo necesita, incluso una con su JavaScript incluido. Una sesión independiente sin build_id ejecuta la compilación de lanzamiento más reciente de la aplicación. Una aplicación para otra plataforma se rechaza antes de usar cualquier dispositivo. |
list_builds | Lista las compilaciones subidas de una aplicación con su tipo (debug, release o unknown) — el equivalente de vibeview list-builds. |
list_device_models | Lista los modelos de dispositivos que puedes iniciar, con cuántos de cada uno están libres y ocupados en este momento, y un marcador de foldable para cada dispositivo con bisagra (el iPhone Duo, plegables de Android como el Pixel 9 Pro Fold) — el equivalente de vibeview list-devices --models. Un dev_start en un modelo sin ninguno libre espera en la cola por uno. |
dev_stop | Detiene la sesión que dev_start inició. Si no hay ninguna en ejecución, lo dice. |
stop_session | Detiene una sesión por su ID — el equivalente de vibeview stop <session-id>. Una sesión que ya había terminado se informa como tal. La sesión de un compañero de equipo se rechaza a menos que seas administrador de la organización. |
dev_reload | Solo Roku: reempaqueta el canal con el comando de compilación en vibeview.json, súbelo y reinícialo en la sesión que dev_start mantiene (o, sin sesión mantenida, la sesión de vibeview dev --detach de este proyecto) — el equivalente de presionar r en vibeview dev. Un session_id que nombre cualquier otra sesión se rechaza. Otras plataformas recargan en caliente a través de Metro por sí solas. |
Ten en cuenta que dev_start tiene estado: una vez que tiene éxito, cada llamada de herramienta posterior que no nombre un session_id apunta a esa sesión. Solo una puede ejecutarse a la vez, y sigue facturando minutos de transmisión hasta que se llame a dev_stop o la conexión termine. Cuando el cliente sale, cierra la conexión o detiene el servidor, el servidor detiene la sesión que dev_start inició — incluida una que aún espera en la cola — antes de salir. Una sesión a la que solo apuntó session_id, o una sesión de vibeview dev --detach, sigue ejecutándose. Si el servidor desaparece sin eso (se mata o se bloquea, o la computadora se suspende), su sesión termina en unos 90 segundos, a menos que una pestaña del navegador aún la esté viendo. El tiempo de espera de inactividad también se aplica a una sesión de dev_start, porque mantener la conexión abierta no es uso. Las acciones del dispositivo (tocar, escribir, deslizar, presionar y las otras herramientas de acción, y dev_reload en Roku) mantienen la sesión activa. Leerla con ui_tree, screenshot, logs o find, o esperar con wait, no lo hace. Cuando la sesión está a punto de terminar por inactividad, el siguiente resultado de herramienta termina con una línea como Session … ends in 40 s for inactivity, para que el agente pueda actuar o detenerse. Si la sesión termina de otra manera (detenida desde el panel, el tiempo de espera de inactividad o una falla), la siguiente llamada de herramienta dice que la sesión ha terminado y por qué, y dev_start inicia una nueva. Si otra conexión de desarrollo de VibeView toma la sesión, el servidor la deja ir sin detenerla, y la siguiente llamada de herramienta lo dice. Después de que la computadora se despierte del sueño, ese mensaje dice que la sesión terminó porque la computadora dejó de registrarse.
Cuando un cliente se conecta, el servidor también envía una breve introducción (el campo MCP instructions) que describe este bucle, que clientes como Claude Desktop muestran al modelo.
Los canales de Roku ejecutan el mismo bucle a través de MCP: dev_start con plataforma roku inicia la sesión, y dev_reload después de cada edición coloca el nuevo paquete en el dispositivo. Desde un shell, vibeview dev --platform roku --detach y vibeview dev-reload son los mismos dos pasos.
Conducción de una sesión de inserción
Una sesión que alguien inició desde una demostración insertada puede conducirse con los mismos comandos. Apuntas a la sesión que ese visitante ya está usando: pasa su id con --session <id> (o session_id a través de MCP) y cada comando aterriza en el dispositivo frente a ellos. Agent Control nunca inicia una segunda sesión para esto, y no se usa ningún dispositivo adicional.
Obtención del id de sesión
La página insertada lo anuncia a tu página. Escucha el evento session:started — lleva el id — y pásalo a tu 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
}
});
Si la página identifica a sus visitantes — consulte Identificando a sus visitantes — el id que obtiene es la sesión propia de ese visitante y, con Máximo de sesiones por visitante en su valor predeterminado de 1, permanece igual en sus recargas: vuelven a la sesión ya en ejecución en lugar de iniciar una nueva, por lo que un agente al que apuntó antes sigue apuntando al dispositivo frente a ellos. Si aumenta ese límite, una recarga inicia una sesión con un id NUEVO hasta que el visitante alcance su límite, así que vuelva a leer el id en cada session:started en lugar de asumir que es el que ya tiene. Cada carga anuncia la sesión a su página nuevamente, por lo que verá session:started con el mismo id de antes — después de una breve espera, si esa sesión aún se está configurando, o de inmediato si está lista. En un embed anónimo, cada carga es una sesión diferente con un id diferente.
Si el visitante llega a una cola, recibirá session:queued primero — ese solo lleva su lugar en la fila, no un id, porque aún no se ha asignado ningún dispositivo. Espere por session:started. Consulte Eventos de página para la lista completa.
Mantenga su credencial en su propio servidor. La página del embed nunca recibe una, y su página tampoco debería tener una — solo necesita el id de sesión.
Dos cosas deben ser ciertas primero:
- El Control de Agente está permitido en esa clave de embed. Es una configuración por clave en Configuración → Embeds, desactivada por defecto, y solo un administrador de la organización puede activarla — consulte Permitir control de agente.
- Está llamando como Desarrollador o Administrador en la organización propietaria de la clave. Las credenciales de Visor son rechazadas, y un token nunca tiene más autoridad que el miembro al que pertenece.
Las personas que ven su embed en un navegador nunca obtienen nada de esto. No hay superficie de comandos en la página embebida — conducir la sesión es algo que su organización hace con sus propias credenciales, desde sus propias herramientas — y activar la configuración no cambia nada sobre lo que un visitante puede hacer.
Si alguna de las condiciones no se cumple, el comando se rechaza por completo y nada se ejecuta en el dispositivo. Lo mismo aplica en el momento en que la clave deja de estar activa — desactivar la configuración, deshabilitar la clave o revocarla termina el acceso del agente en el siguiente comando, incluso a mitad de sesión.
Qué está disponible en una sesión de embed
Un embed mantiene deliberadamente a los visitantes dentro de su aplicación, y el Control de Agente mantiene esa misma línea. Todo lo que necesita para leer y conducir una pantalla está ahí; las cosas que sacarían a un visitante de su aplicación, o la reiniciarían bajo ellos, no están.
| Comando | En una sesión de embed |
|---|---|
ui-tree, screenshot, logs | Lea la pantalla y la salida propia de la aplicación, exactamente como en cualquier otra sesión. |
tap, long-press, drag, swipe, scroll, scroll-to | Disponibles, por referencia de elemento o por coordenadas. |
type, clear-text | Disponibles. |
alert | Disponible — responda a una alerta del sistema que se superpone a su aplicación. |
find, wait | Disponibles. |
set-posture, rotate | Disponibles — plegar o girar el dispositivo cambia cómo lo sostiene el visitante, no la aplicación en la que está. |
set-location | Disponible — cambia dónde la aplicación cree que está el dispositivo, no la aplicación en la que está el visitante. |
tap-focused, focus | Disponibles en sesiones de TV. |
press <button> | Limitado a back y el pad direccional (dpad_up, dpad_down, dpad_left, dpad_right, dpad_center). home, lock y siri son rechazados: sacarían al visitante de su aplicación, dejarían su pantalla en blanco o lo entregarían a un asistente del sistema. |
open-url | No disponible. Abrir un enlace profundo o una URL es el único comando cuyo propósito completo es salir de la aplicación. |
relaunch-app | No disponible. El embed mantiene su aplicación en ejecución para el visitante y la reinicia por sí mismo cuando es necesario. |
Un comando rechazado indica qué no está permitido y qué sí, para que un agente pueda corregirse en lugar de reintentar a ciegas — y el rechazo ocurre antes de que algo llegue al dispositivo.
El visitante también está usando la pantalla
Este es el único lugar donde no está solo en el dispositivo. Un visitante puede tocar entre dos de sus comandos, por lo que la pantalla que leyó hace un momento puede no ser la pantalla sobre la que está actuando, y una referencia de elemento que tenía puede estar obsoleta para cuando la use.
Cuando eso ocurre, el comando regresa sin éxito sin actuar, y la respuesta incluye una lectura fresca de la pantalla actual — incluido lo que el visitante acaba de cambiar. Trátelo como algo normal en lugar de como un fallo para reintentar: vuelva a leer la pantalla y elija el elemento nuevamente desde lo que hay ahora. Repetir la misma referencia seguirá fallando, porque ya no apunta a nada.
Compartir la pantalla es el propósito de esto, no un defecto que sortear. El visitante sigue tocando mientras usted trabaja, y observan sus acciones en vivo en su propio dispositivo.
La habilidad
El paquete npm también incluye una habilidad lista para usar en skills/vibeview-agent/SKILL.md que describe todo este flujo de trabajo (requisitos de compilación, el bucle, la verificación, la limpieza y la referencia completa de comandos) en una forma que los agentes de codificación con acceso al sistema de archivos pueden leer y seguir directamente. Cubre el mismo terreno que esta página.
Para ver qué hace un agente con ella, lea Un agente de IA envió una aplicación Expo a TestFlight en 43 minutos: un agente de codificación construyó la aplicación de muestra Kitlist, la verificó en dispositivos iOS y Android en la nube con estos comandos y la envió a ambas tiendas, con el registro de ejecución citado en todo momento.
Facturación
Una sesión iniciada de esta manera es una sesión ordinaria de VibeView — factura minutos de transmisión igual que cualquier sesión iniciada desde el panel. Recuerde ejecutar vibeview dev-stop (o detener la sesión que su agente inició) cuando termine para que no siga ejecutándose sin supervisión.