Vibeview
Pricing

VibeView CLI: Run Mobile Tests from a Terminal

Run VibeView tests from the command line. Integrate with CI/CD pipelines for automated mobile testing.

Quick Start

# 1. Install
npm install -g vibeview

# 2. Log in (opens browser)
vibeview login --base-url https://vibeview.io

# 3. Run a test suite
vibeview run-suite <suite-id> --output human

Installation

The CLI is a public npm package:

npm install -g vibeview

Requirements: Node.js 20 or later.

After installation, the vibeview command is available in your terminal.

Authentication

The CLI resolves your API token in this order:

  1. --token flag — pass a token directly on each command.
  2. VIBEVIEW_API_TOKEN environment variable — best for CI environments.
  3. Config file — saved automatically by vibeview login at ~/.vibeview/config.json.

If no token is found, the CLI exits with an error and suggests using the --token flag, setting VIBEVIEW_API_TOKEN, or running vibeview login.

The server URL defaults to https://vibeview.io and is saved when you log in, so commands don’t ask for it. To use a self-hosted or staging instance, pass --base-url to vibeview login (it is stored for later commands) or set VIBEVIEW_BASE_URL.

Commands

login

Log in to VibeView interactively. Opens your browser to complete authentication, then saves the token locally.

vibeview login --base-url https://vibeview.io
OptionDescription
--base-url <url>VibeView server URL (defaults to https://vibeview.io)

The command:

  1. Opens your browser to the VibeView login page (you are taken straight to the authorization screen if you’re already signed in).
  2. Asks you to confirm with an Authorize button — only approve requests you started from your own terminal.
  3. Waits up to 2 minutes for you to complete the authorization.
  4. Saves the token to ~/.vibeview/config.json (file permissions 0600).

logout

Log out of VibeView: revokes the saved login token server-side and removes it from ~/.vibeview/config.json.

vibeview logout

Each vibeview login creates an API token that stays active until revoked, so use vibeview logout (rather than just deleting the config file) when you’re done on a machine. If the server can’t be reached, the token is still removed locally and a warning tells you to revoke it in the dashboard (Settings → API Tokens). Tokens supplied via VIBEVIEW_API_TOKEN or --token are not affected.

agent-setup

Make VibeView discoverable to your coding agent: installs the vibeview-agent skill where Claude Code auto-discovers it, optionally registers the MCP server, and suggests a CLAUDE.md line for your project.

vibeview agent-setup
OptionDescription
--projectInstall the skill into this project’s .claude/skills/ instead of ~/.claude/skills/
--mcpRegister the MCP server with Claude Code without asking
--no-mcpSkip MCP registration

See AI Agent Control for the full agent workflow.

dev

Stream your app from a live device with Metro hot-reload, for local React Native development. See the Live Development guide for the full workflow.

The first time you run vibeview dev in a project, it walks you through setup: which platform to run, an optional build command (with a detected default offered for Expo and bare React Native projects — you can accept it, enter your own, or skip it), and an optional command that starts Metro (same detected-default-or-skip choice). Your answers are saved to a vibeview.json file in the project so later runs start immediately with no prompts. Any prompt can be skipped — vibeview dev still works without a build or Metro command configured, and skipped prompts print a hint showing the exact keys to add to vibeview.json later if you change your mind.

After setup, if you don’t have an uploaded app yet for the chosen platform, vibeview dev builds and uploads one automatically (when you configured a build command) or sends you to the app picker (when you skipped it). If you already have an app, it asks whether to build a fresh one now or reuse the existing upload.

Once a build is selected, vibeview dev starts a device session, connects your local Metro bundler to the app running on the device, and opens the session in your browser. Press Ctrl-C to end the session.

vibeview dev runs the latest debug build of the app you pick (Android debug .apk or iOS simulator build). If the app has no debug build, or the build can’t be installed on the device (for example an Android build without an x86_64 architecture), it stops with a clear message instead of opening a session you can’t use — upload one with vibeview build or upload-app first, or rebuild with x86_64 included.

If the app configured for a platform was deleted, vibeview dev warns and asks you to pick again; a stale --app <id> exits with an error telling you to rerun without --app.

vibeview dev
OptionDescriptionDefault
--token <token>API token(from auth resolution)
--platform <platform>ios, android, tvos, androidtv, or roku(asked interactively on first run)
--app <app_id>Uploaded debug build to run (see vibeview list-apps)(asked interactively on first run)
--buildRun your configured build command and upload the result before starting the session(off)
--metro-port <port>Local Metro port8081
--device-category <category>phone or tablet (TV platforms always use a TV device)phone
--no-openDon’t open the browser automatically(opens by default)

Example: pin the platform and app to skip the interactive prompt:

vibeview dev --platform ios --app abc123

Example: rebuild and upload after a native change, then start the session:

vibeview dev --build

Your choices are remembered per platform in the project’s vibeview.json, so one project can hold a phone app and a TV app side by side. A bare vibeview dev reruns whatever platform you used last; --platform <name> switches (and remembers the app you pick for it):

{
  "defaultPlatform": "android",
  "metroPort": 8081,
  "metroCommand": "yarn start",
  "platforms": {
    "android": {
      "appId": "myapp-a1b2c3",
      "deviceCategory": "phone",
      "build": {
        "command": "cd android && ./gradlew assembleDebug",
        "artifact": "android/app/build/outputs/apk/debug/*.apk"
      }
    },
    "androidtv": { "appId": "myapp-tv-d4e5f6" }
  }
}

metroCommand (and metroPort) can also be set per platform — inside a platform’s entry in platforms — to override the top-level value, for projects that start Metro differently per target:

{
  "defaultPlatform": "ios",
  "metroCommand": "yarn start",
  "platforms": {
    "ios": { "appId": "myapp-a1b2c3" },
    "tvos": { "appId": "myapp-tv-d4e5f6", "metroCommand": "yarn start:tv", "metroPort": 8082 }
  }
}

Metro auto-start

vibeview dev checks whether Metro is already answering on the configured port before starting a session:

  • If a Metro (or Metro-compatible packager) is already running there, vibeview dev uses it as-is — it never touches a Metro it didn’t start.
  • If nothing is running on the port and metroCommand is configured (top-level or per-platform), vibeview dev starts it for you, prefixing its output with [metro], and stops it automatically when the session ends.
  • If vibeview dev started Metro for one platform and you switch to a platform whose effective Metro command is different, it restarts Metro with the new platform’s command.
  • If nothing is running and no metroCommand is configured, vibeview dev stops with a clear error telling you to start Metro yourself or add metroCommand to vibeview.json.

Roku (beta)

vibeview dev --platform roku streams a Roku channel the same way, with one difference: Roku has no JS bundle to hot-reload, so instead of a Metro connection the session gives you a manual reload loop.

In a Roku channel project, the first run asks for two values instead of a build command and a Metro command, saved to vibeview.json under platforms.roku.build:

  • command — the shell command that packages your channel into a zip.
  • artifact — the path to the zip that command produces.
{
  "defaultPlatform": "roku",
  "platforms": {
    "roku": {
      "appId": "mychannel-a1b2c3",
      "build": {
        "command": "npm run package",
        "artifact": "out/channel.zip"
      },
      "watchIgnore": ["docs", "tests/fixtures"]
    }
  }
}

Once the session is running, the terminal accepts these keys — just press the key, no Enter needed:

KeyAction
rRe-package the channel, upload it, and reload it into the session you’re already watching — the channel restarts on the device a few seconds later.
q (or Ctrl-C)End the session.

A reload already in progress ignores a repeat r instead of queuing another one.

--watch [seconds] turns on automatic reload: after a file in the project changes, vibeview dev waits for the given quiet period with no further changes, then reloads on its own — no need to press r. A bare --watch uses a 2-second quiet period; --watch 5 waits 5 seconds instead.

vibeview dev --platform roku --watch
vibeview dev --platform roku --watch 5

A file you save while a reload is running is not lost: one more reload runs once the current one finishes, however many times you saved.

--watch already skips the zip your packaging command produces. Add watchIgnore (optional, shown above) to skip more: a list of paths relative to your project root, each covering everything inside it, for the folders you edit without wanting a reload — docs, test fixtures, planning notes. It adds to the paths already skipped rather than replacing them. If your packaging command writes working files into a folder that is watched, the channel keeps reloading itself — add that folder to watchIgnore to stop it.

If your packaging command only deploys straight to a device instead of producing a zip, the reload fails with a message telling you to use the package/zip step instead. A failed reload leaves the session running, so you can fix the problem and press r again.

If your channel fails to install when the session starts up — for example, the device rejects the package — vibeview dev reports the reason and ends the session instead of leaving you staring at a stream with nothing installed. This can be reported either while the session is still starting, or shortly after, once it’s already live.

If the session itself ends while you are watching it — it expires, or someone stops it from the dashboard — vibeview dev says so and exits instead of leaving you pressing r against a session that is gone.

build

Run your configured build command and upload the result — the same build-and-upload step vibeview dev --build runs before starting a session, available standalone for CI or a manual rebuild.

vibeview build

vibeview build reads platforms.<platform>.build from vibeview.json (a command to run from the project root, and an artifact path the build produces), runs the command with its output streamed to your terminal, then locates and uploads the artifact. The artifact path can end in a * glob in its filename (for example android/app/build/outputs/apk/debug/*.apk) — when more than one file matches, the most recently modified one is uploaded. A matched .app directory (iOS simulator builds) is zipped automatically before upload. On success, the uploaded app’s id is written back into vibeview.json as that platform’s appId, so the next vibeview dev run picks it up.

If no build command is configured for the target platform, vibeview build prompts for one interactively — offering a detected default command for Expo and bare React Native projects where one is recognized — and saves your answer to vibeview.json. Under --json or when not running in an interactive terminal, a missing build command is an error instead of a prompt.

OptionDescriptionDefault
--token <token>API token(from auth resolution)
--platform <platform>ios, android, tvos, androidtv, or roku; with --cloud, all targets every configured platform with a build blockvibeview.json’s defaultPlatform
--jsonPrint the result as one JSON line (app_id, name, platform) instead of human-readable output(human output)
--cloudBuild in the cloud instead of locally — see Cloud Builds(local build)
--production--cloud only: build a signed release artifact instead of a simulator/emulator build — see App Signing & Production Builds(simulator/emulator build)
--no-wait--cloud only: print build ids and exit without waiting(wait)
--download--cloud only: download each succeeded build’s artifact (implies wait)(no download)

Example: build and upload for a specific platform, capturing the app id in CI:

APP_ID=$(vibeview build --platform android --json | jq -r .app_id)

builds

Manage cloud builds: vibeview builds list | logs <id> [--follow] | download <id> [--out <dir>] | cancel <id> | delete-artifact <id>. delete-artifact removes a production build’s stored artifact file (irreversible; the build record, logs, and cost stay intact — only the downloadable file is removed). See Cloud Builds for configuration, billing, and limits.

credentials

Manage signing credentials used by vibeview build --cloud --production:

vibeview credentials add --platform ios --file cert.p12 --profile app.mobileprovision [--app <id>]
vibeview credentials add --platform android --file release.jks [--app <id>]
vibeview credentials list [--app <id>]
vibeview credentials rm <name> [--app <id>]

Every password is prompted for interactively with hidden input — there is no password flag. See App Signing & Production Builds for what each platform needs, scoping, and expiry.

env

Manage build variables and secrets used by cloud builds: vibeview env list [--app <id>] [--json] | env set NAME [VALUE] [--secret|--sensitive|--plain] [--file <path>] [--app <id>] | env rm NAME [--app <id>] | env pull [--app <id>] [--out .env]. See Build Variables & Secrets for visibility levels, scopes, file variables, limits, and log masking.

list-suites

List all test suites available in your organization.

vibeview list-suites --output human
OptionDescriptionDefault
--token <token>API token(from auth resolution)
--output <format>Output format: human, jsonhuman
--tag <tag>Filter by tag (repeatable)(all suites)
--match <mode>any or all — how multiple --tag values are combinedany

Example output (human):

  3 suite(s)

  abc123  Login Flow [ios] [smoke, auth]  (4 cases)
  def456  Checkout  [android]  (6 cases)
  ghi789  Onboarding [ios] [smoke]  (3 cases)

Tags (when present) are shown in brackets after the platform. Descriptions are not rendered in human output — use --output json to retrieve them.

Example output (JSON):

[
  {
    "id": "abc123",
    "name": "Login Flow",
    "platform": "ios",
    "case_count": 4,
    "description": "Covers email/password and SSO sign-in.",
    "tags": [
      { "name": "smoke", "display_name": "Smoke" },
      { "name": "auth", "display_name": "Auth" }
    ]
  }
]

Every suite includes description (string or null) and tags (array, possibly empty). Use JSON output when you need either field — for example, to drive downstream tooling that picks suites by description text or tag membership.

Tag filtering example:

vibeview list-suites --tag profiles --tag smoke --match any

run-suite

Run all test cases in a suite and wait for results.

vibeview run-suite <suite-id> [options]
OptionDescriptionDefault
--token <token>API token(from auth resolution)
--output <format>Output format: human, json, junithuman
--verboseShow per-case breakdownfalse
--timeout <seconds>Max wait time in seconds300
--commit-sha <sha>Git commit SHA for GitHub status checks(none)
--model <model>AI model to use(server default)
--provider <provider>AI provider(server default)
--mode <mode>Execution mode: hybrid, ai, replay(server default)
--device-category <category>Device category to allocate: phone, tablet, tvphone
--device <device_id>Pin run to a specific device (see list-devices). Conflicts with --device-category.(none)
--build <id>Specific AppBuild id to run against (integer). Defaults to the suite app’s latest build when omitted.(latest)
--metadata <json>JSON object passed through unchanged to webhook payloads (e.g. PR context)(none)
--metadata-file <path>Read the metadata JSON object from a file — use for nested JSON in CI(none)

The command triggers the suite run, then polls every 3 seconds until the run completes or the timeout is reached. The polled JSON result includes per-step/per-case error_kind (failure classification) and screenshot_signed_url (signed, no-auth image URL) for programmatic consumers — see Reading run results in the CI testing guide.

Example:

vibeview run-suite abc123 \
  --commit-sha $(git rev-parse HEAD) \
  --output junit \
  --timeout 300

Pin a specific build:

vibeview run-suite abc123 --build 142

The id is the numeric AppBuild id, visible on the app detail page. If the suite has no app attached, --build returns an error.

run-suites

Run all suites matching a tag filter, sequentially. Each matched suite gets its own device session and SuiteRun. Useful when CI doesn’t know specific suite IDs but knows what areas changed in a PR.

vibeview run-suites --tag profiles --commit-sha $GIT_SHA
OptionDescriptionDefault
--tag <tag>Tag to match (repeatable)required
--match <mode>any matches if a suite has any of the tags; all requires every tagany
--platform <platform>Restrict the tag-matched set to one platform: ios, android, tvos, androidtv. Suites of other platforms are skipped (and reported).(no filter)
--output <format>Output format: human, json, junithuman
--verbosePrint per-suite progress as they completefalse
--timeout <seconds>Per-suite timeout. Total wait is capped at 2 hours regardless of N.300
--build <id>AppBuild id, validated against every matched suitelatest
--commit-sha <sha>Git SHA propagated to every SuiteRun-
--model <model>AI modelinferred
--provider <provider>AI providerinferred
--mode <mode>Execution mode: hybrid, ai, replayhybrid
--device-category <category>Device category to allocate: phone, tablet, tvphone
--device <device_id>Pin run to a specific device (see list-devices). Conflicts with --device-category.(none)
--metadata <json>JSON object passed through unchanged to webhook payloads (e.g. PR context)(none)
--metadata-file <path>Read the metadata JSON object from a file — use for nested JSON in CI(none)
--token <token>API token (or set VIBEVIEW_API_TOKEN)-

Exit codes: 0 aggregate passed; 1 any suite failed; 2 no matches / timeout / error / cancelled.

Example: run all suites tagged auth with the GitHub commit SHA propagated:

vibeview run-suites --tag auth --commit-sha $(git rev-parse HEAD) --output junit > results.xml

Scoping to one platform with --platform: A tag like prod may be shared by suites across iOS, Android, and TV. In a CI matrix where each job builds a single platform, add --platform so the job only runs the suites it actually built an app for — the rest are skipped and listed as excluded:

# A job that built the Android APK only runs the Android suites tagged prod
vibeview run-suites --tag prod --platform android

A suite’s platform comes from the suite itself, or its linked app. Suites with no resolvable platform are treated as a mismatch and excluded under a --platform filter. Omitting --platform keeps the previous behavior — every tag-matched suite runs. --platform combines with --match: --match all --platform android runs suites that have all the given tags and target Android.

run-test

Run a single test case and wait for results.

vibeview run-test <test-id> [options]
OptionDescriptionDefault
--token <token>API token(from auth resolution)
--output <format>Output format: human, json, junithuman
--verboseShow step detailsfalse
--timeout <seconds>Max wait time in seconds300
--commit-sha <sha>Git commit SHA for GitHub status checks(none)
--model <model>AI model to use(server default)
--provider <provider>AI provider(server default)
--mode <mode>Execution mode: hybrid, ai, replay(server default)
--device-category <category>Device category to allocate: phone, tablet, tvphone
--device <device_id>Pin run to a specific device (see list-devices). Conflicts with --device-category.(none)
--build <id>Specific AppBuild id to run against (integer). Defaults to the test case app’s latest build when omitted.(latest)
--metadata <json>JSON object passed through unchanged to webhook payloads (e.g. PR context)(none)
--metadata-file <path>Read the metadata JSON object from a file — use for nested JSON in CI(none)

Example:

vibeview run-test test-789 --mode ai --output json

Pin a specific build:

vibeview run-test test-789 --build 142

The id is the numeric AppBuild id, visible on the app detail page. If the test case has no app attached, --build returns an error.

When the device pool is busy

If no matching device is available when you submit a run, VibeView queues you instead of failing. The CLI’s spinner shows your live position:

Queued — position 3 in line for iPhone 15 Pro (tap-pool-01)

Once a device frees up, the run automatically transitions through pending and into running without any intervention on your side.

--timeout covers the entire journey — queue wait plus execution — so set it generously when the pool is contended. On Ctrl+C or --timeout, the CLI cancels its queue entry server-side so the slot doesn’t sit unused.

If you target a specific device with --device <id>, the queue only counts users waiting for that device. Use vibeview list-devices to see what’s currently available or busy.

upload-app

Upload an app build file. If an app with the same bundle/package ID and platform already exists, the build is added to the existing app instead of creating a new one.

vibeview upload-app <file> [options]

Supported file types: a compressed .app simulator bundle (.zip, .tar.gz, or .tgz archive) for iOS, and .apk for Android. .ipa files are rejected: iOS simulators require simulator .app builds, not device .ipa archives. See Preparing Your App Build for how to produce these files.

OptionDescriptionDefault
--token <token>API token(from auth resolution)
--output <format>Output format: human, jsonhuman
--name <name>App name (auto-detected from binary if not provided)(auto-detected)

Example:

vibeview upload-app ./build/MyApp.app --output json

Returns the app ID (public_id) which can be used with create-test --app.

create-test

Create a standalone AI test case. The test name can be passed as a positional argument; steps can be provided inline, from a file, or piped via stdin.

vibeview create-test [name] [options]
OptionDescriptionDefault
[name]Test case name (positional argument)CLI Test
--token <token>API token(from auth resolution)
--output <format>Output format: human, jsonhuman
--app <id>App ID (public_id) or package name (requires --platform)(required)
--platform <platform>Platform (ios, android) when using package name for --app(optional)
--steps <json>Test steps as inline JSON array(one of steps/steps-file/stdin required)
--steps-file <path>Path to JSON file containing test steps(optional)
--context <text>Additional context for AI test execution(optional)
--test-name <name>Test case name (alternative to the positional argument; the positional wins if both are given)CLI Test

Steps are an array of objects with a description field:

[{"description": "Open the app"}, {"description": "Tap Login"}, {"description": "Verify home screen"}]

Examples:

# Inline steps
vibeview create-test --app abc123 --steps '[{"description":"Open app"},{"description":"Tap Login"}]'

# From a file, with the name as a positional argument
vibeview create-test "Login Flow" --app abc123 --steps-file ./test-steps.json

# Piped from another tool
echo '[{"description":"Verify onboarding"}]' | vibeview create-test --app abc123

list-apps

List all apps in your organization.

vibeview list-apps [options]
OptionDescriptionDefault
--token <token>API token(from auth resolution)
--output <format>Output format: human, jsonhuman

Example output (human):

  3 app(s)

  abc123  MyApp [ios]  com.example.myapp
  def456  MyApp [android]  com.example.myapp
  ghi789  TestApp [ios]  com.test.app

list-devices

List devices registered with the platform — useful to find a device_id to pass to --device.

vibeview list-devices [options]
OptionDescriptionDefault
--type <type>Filter by device type: ios, android(all)
--category <category>Filter by category: phone, tablet, tv(all)
--status <status>Filter by status: available, busy, offline(all)
--output <format>human or jsonhuman
--token <token>API token(from auth resolution)

Example: find an available Android TV:

vibeview list-devices --type android --category tv

Sample output:

DEVICE_ID         NAME              TYPE     CATEGORY  OS    STATUS
atv-01            Apple TV 4K       ios      tv        17.5  available
emu-tv-01         Android TV        android  tv        34    busy

Output Formats

human

Readable terminal output with colors and a spinner during execution. Best for local development.

json

Structured JSON for programmatic parsing. Includes full run result with per-case status, duration, and error messages.

junit

JUnit XML format for CI/CD integration. Compatible with GitHub Actions, Jenkins, GitLab CI, and other CI systems that consume JUnit reports.

Example: upload JUnit results as a GitHub Actions artifact

- name: Run VibeView suite
  run: vibeview run-suite ${{ secrets.SUITE_ID }} --output junit > results.xml

- name: Upload test results
  uses: actions/upload-artifact@v4
  with:
    name: vibeview-results
    path: results.xml

Exit Codes

CodeMeaning
0All tests passed
1One or more tests failed
2Execution error (network failure, authentication error, timeout, or a cancelled run)

Use exit codes in CI scripts to gate deployments:

vibeview run-suite $SUITE_ID --commit-sha $COMMIT_SHA || exit 1

CI/CD Integration

For the simplest GitHub Actions setup, use the VibeView workflow template which wraps these CLI commands automatically.

test-spec.json Convention

CI pipelines can store test configuration in .vibeview/test-spec.json at the repository root. This file defines the test steps as code, allowing AI agents and automation tools to describe tests declaratively. The app build path comes from the workflow’s APP_PATH env var (not the spec file), and platform is auto-detected from the binary at upload time.

{
  "version": 1,
  "steps": ["Launch the app", "Verify the home screen loads"],
  "context": "Optional context for the AI agent",
  "test_name": "My Test"
}

The workflow template reads this file automatically. For the full schema reference and examples, see the CI Testing guide.

GitHub Actions

name: VibeView Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install VibeView CLI
        run: npm install -g vibeview

      - name: Run test suite
        env:
          VIBEVIEW_API_TOKEN: ${{ secrets.VIBEVIEW_API_TOKEN }}
          VIBEVIEW_BASE_URL: ${{ secrets.VIBEVIEW_BASE_URL }}
        run: |
          vibeview run-suite ${{ secrets.SUITE_ID }} \
            --commit-sha ${{ github.sha }} \
            --output junit > results.xml

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: vibeview-results
          path: results.xml

Tip: replace run-suite ${{ secrets.SUITE_ID }} with run-suites --tag <area> to run a tag-filtered set instead of a single suite.

Generic CI

For any CI system, set the VIBEVIEW_API_TOKEN and VIBEVIEW_BASE_URL environment variables, then call the CLI:

export VIBEVIEW_API_TOKEN="your-token-here"
export VIBEVIEW_BASE_URL="https://vibeview.io"

vibeview run-suite <suite-id> --commit-sha $(git rev-parse HEAD) --output human

The CLI exits with code 0 on success and 1 on failure, so your CI pipeline can use the exit code to pass or fail the build.