---
name: pantoqa-agent
version: 0.0.1
description: Run automated QA/testing tasks on Android and iOS apps by sending natural-language prompts to the PantoQA CLI (pantoqacli), which drives a real or emulated device through a local bridge. Use this skill whenever the user mentions PantoQA, pantoqacli, or "PantoQA Skills" by name, or more generally whenever they want to test a mobile app, automate a mobile UI flow, run a QA scenario (e.g. "open Settings and enable Wi-Fi"), check connected device/bridge status, or resume/replay a QA session — even if they don't use the word "test" explicitly (e.g. "walk through the onboarding flow on the emulator", "check if Wi-Fi toggles correctly").
---

# PantoQA Skills

PantoQA drives an Android or iOS device (emulator or real hardware) by turning natural-language prompts into UI actions. It has two moving parts:

- **`pantoqacli run`** — sends prompts over a websocket to the QA server, which returns actions to execute.
- **Bridge CLI** — a local HTTP service (default `http://0.0.0.0:6565`) that actually performs actions (taps, swipes, etc.) on your locally connected device.

It also supports remote-device session management through `remote device start`, `remote device status`, `remote device preview`, and `remote device stop`, plus remote app build management through `remote app upload` and `remote app list`.


## Local bridge vs remote device

- **Bridge mode (local device)**: used by `run` for `bridge_v2_action` / `bridge_action` execution. This is for a locally attached emulator/device and requires the bridge service.
- **Remote device mode**: used by `remote device start`, `remote device status`, `remote device preview`, and `remote device stop` commands.
- **Important**: `remote device` commands do not replace `run`; they manage/check a remote device session. `run` still performs QA prompt execution over websocket.

## Setup

Before using the skill, check whether both package commands are available and install only the missing packages. With `pipx`:

```bash
command -v pantoqa_bridge >/dev/null 2>&1 || pipx install pantoqa_bridge
command -v pantoqacli >/dev/null 2>&1 || pipx install pantoqacli
```

If `pipx` is not available, use `pip` and check each package before installing:

```bash
python -m pip show pantoqa_bridge >/dev/null 2>&1 || python -m pip install pantoqa_bridge
python -m pip show pantoqacli >/dev/null 2>&1 || python -m pip install pantoqacli
```

To upgrade both packages:

```bash
pipx upgrade pantoqa_bridge
pipx upgrade pantoqacli
```

When using `pip`, upgrade both packages with:

```bash
pip install --upgrade pantoqa_bridge pantoqacli
```

After installing and checking the packages, export the installed skill definition and replace the current file:

```bash
skill_file="skills/pantoqa-agent/SKILL.md"
pantoqacli export-skill > "$skill_file"
grep -q '^name: pantoqa-agent$' "$skill_file" || exit 1
```

Keep the file at `skills/pantoqa-agent/SKILL.md` with the `name: pantoqa-agent` frontmatter so the skill remains enabled and loads in future sessions.

## Login (authentication)

`run` and the `remote device` commands need a token. Token resolution order is: `--token` flag, then `$PANTOQA_TOKEN`, then `~/pantoqa-agent/.token`. If none of these are set, log in first:

```bash
pantoqacli login
```

After the browser callback, `login` verifies the API key with the API server before saving it to `~/pantoqa-agent/.token`. To verify the currently resolved key later:

```bash
pantoqacli test-auth
```

The `auth` alias is also supported:

```bash
pantoqacli auth
```

To check the installed CLI version:

```bash
pantoqacli --version
```

This opens the browser to the PantoQA token-generation page. Once the user completes login there, the token is captured automatically and saved to `~/pantoqa-agent/.token` — no manual copy/paste needed. Subsequent `run` / `remote device` commands then pick it up automatically without needing `--token`.

Optional `--timeout` (default `300` sec) controls how long the CLI waits for the browser callback before giving up.

### QA Bridge CLI
Bridge only for local device testing. It's the driver for the local devices.

Start the bridge service with:

```bash
pantoqa_bridge # To start the bridge service.
```

#### Before running a local test: check the bridge

The bridge must be up and a device must be attached before `run` will work. Always check first rather than assuming:

```bash
pantoqacli bridge status
```

Then confirm a device is available and grab its serial number:

```bash
pantoqacli bridge devices
```

The `serial_no` from this output is what you pass to `--device-serial-no` in `run`. If the bridge isn't reachable or no device is listed, stop and tell the user — don't attempt `run` against a dead bridge.

## Running a test

One prompt = one task. Use `--prompt` once per task; repeat the flag for multiple sequential tasks.

```bash
pantoqacli run \
  --prompt "Open Settings app"
```

Multiple tasks in one session:

```bash
pantoqacli run \
  --prompt "Open Settings" \
  --prompt "Enable Wi-Fi"
```

Prompts run **sequentially**. A run is complete once the websocket reports one of these terminal states: `completed`, `qa_stopped`, `replay_stopped`. Incoming `bridge_v2_action` / `bridge_action` messages are executed via `POST /v2/perform-action` against the bridge and the result is sent back over the websocket as `driver_action_response` — this happens automatically, no manual intervention needed.

## Mandatory session-ID rules

There are two different IDs:

- `remote_session_id`: returned by `remote device start`; pass it to `run` as `--lt-session-id`.
- `qa_session_id`: returned by the first `run`; pass it to later `run` commands as `--session-id`.

After the first successful `run` for a remote device:

1. Save both IDs:
   - `remote_session_id = <remote device session ID>`
   - `qa_session_id = <run result session_id>`
2. Every subsequent prompt on that same remote device must include both:
   ```bash
   --lt-session-id "$remote_session_id" \
   --session-id "$qa_session_id"
   ```
3. After the first successful run, always send the same `qa_session_id` with every subsequent run for that remote device, unless the user explicitly asks not to reuse the session.
4. If the previous `run` did not return a `qa_session_id`, stop and report the missing ID instead of silently starting a new session.
5. If the remote device session is stopped, do not reuse its IDs for a new device session.

### Required command pattern

First prompt:

```bash
pantoqacli run \
  --driver-type REMOTE \
  --lt-session-id "$remote_session_id" \
  --platform android \
  --prompt "<prompt>"
```

Capture the returned `session_id` as `qa_session_id`.

Later prompts:

```bash
pantoqacli run \
  --driver-type REMOTE \
  --lt-session-id "$remote_session_id" \
  --session-id "$qa_session_id" \
  --platform android \
  --prompt "<prompt>"
```

Before executing any later prompt, verify that `--session-id` is present and equals the previously returned QA session ID.

### Example

```bash
# First run returns session_id=qa-123
pantoqacli run --driver-type REMOTE \
  --lt-session-id remote-456 \
  --prompt "Open Wikipedia"

# Continue the same QA session
pantoqacli run --driver-type REMOTE \
  --lt-session-id remote-456 \
  --session-id qa-123 \
  --prompt "Close Chrome"
```

### Required parameters

| Flag | Description |
|---|---|

| `--prompt` | One task per flag; repeat for multiple tasks. |

### Optional parameters

**Connection / session**
| Flag | Default | Description |
|---|---|---|
| `--token` | `--token` arg, then `$PANTOQA_TOKEN`, then `~/pantoqa-agent/.token` | Auth/API token used by both `run` and remote device commands. Run `pantoqacli login` to populate `~/pantoqa-agent/.token` if none of these are set. |
| `--base-url` | `$PANTOQA_BASE_URL` or `https://qa-app.getpanto.ai` | QA server base URL. |
| `--ws-path` | `/api/v1/qa/ws` | Websocket path (used when base URL has no path). |
| `--session-id` | none | Resume an existing websocket QA session. |
`--session-exe-type` is sent as `EXECUTE` automatically. |
| `--lt-session-id` | none | Include a remote device session id in the websocket query (`lt_session_id`) for `run`. This is for remote-device session mapping, not for `remote device start/status` commands. |

**Target / driver**
| Flag | Default | Description |
|---|---|---|
| `--platform` | `android` | `android` or `ios`. |
| `--driver-type` | `BRIDGE_V2` | `BRIDGE_V2` or `REMOTE` |
| `--session-exe-type` | `EXECUTE` | `TESTRUN`, `EXECUTE`, or `LOCALRUN`. |

**Execution behavior**
| Flag | Default | Description |
|---|---|---|
| `--blind-mode` | off | Send `blind_mode=true` (skip visual confirmation). |
| `--continue-on-failure` | off | Keep running remaining prompts even if one fails. |

**Bridge**
| Flag | Default | Description |
|---|---|---|
| `--bridge-base-url` | `$PANTOQA_BRIDGE_BASE_URL` or `http://0.0.0.0:6565` | Bridge base URL. |
| `--repeat-times` | `1` | Repeat count sent to the bridge per action. |
| `--device-serial-no` | none | Target device serial (get it from `bridge devices`). |
| `--xml-mode` | `adb` | XML mode sent to the bridge. |
| `--no-use-app` | off (i.e. `use_app=true` by default) | Send `use_app=false` to the bridge. |

**Other**
| Flag | Default | Description |
|---|---|---|
| `--verbose` | off | Enable debug logging. |

### Full example with optional params

```bash
pantoqacli run \
  --base-url $PANTOQA_BASE_URL \
  --token $PANTOQA_TOKEN \
  --prompt "Open Settings" \
  --prompt "Enable Wi-Fi" \
  --driver-type BRIDGE_V2 \
  --session-exe-type EXECUTE \
  --platform android \
  --ws-path /api/v1/qa/ws \
  --session-id qa-session-123 \
  --lt-session-id lt-789 \
  --blind-mode \
  --continue-on-failure \
  --bridge-base-url ${PANTOQA_BRIDGE_BASE_URL:-http://0.0.0.0:6565} \
  --repeat-times 1 \
  --device-serial-no emulator-5554 \
  --xml-mode adb \
  --verbose
```

## Bridge utility commands

Use these to inspect the bridge independently of a test run — always run `bridge status` (and `bridge devices` if the task involves a specific device) before `run`.

```bash
# Full bridge health JSON
pantoqacli bridge status

# Device-focused output — serial_no here is what --device-serial-no expects
pantoqacli bridge devices
```

Both accept an optional `--bridge-base-url` (default `$PANTOQA_BRIDGE_BASE_URL` or `http://0.0.0.0:6565`).

## Uploaded app builds (for remote device only)

Use `remote app upload` to upload one Android APK or iOS IPA for the authenticated
organization. Provide a display name and exactly one build file; the CLI picks
the required API form field from the `.apk` or `.ipa` extension. The response
contains the build `id` used by downstream test execution APIs.

```bash
pantoqacli remote app upload \
  --app-name "Example Android" \
  --file ./app-release.apk
```

List builds uploaded by the current organization, newest first:

```bash
pantoqacli remote app list
```

Use the desired build record's `id` as the required `--app-id` when starting a
remote device session. Do not infer the build from its display name. If more
than one record could be the intended build, ask the user to choose and show
each candidate's build `id`, display name, and upload time.

Both commands accept `--base-url` and `--token`, with the standard token
resolution order described above.

## Remote device commands

Use these when you need a managed remote device session (instead of only local bridge execution).

```bash
# List uploaded builds and copy the `id` of the app to run. If the intended
# build is unclear, ask the user to choose by build id, display name, and upload time.
pantoqacli remote app list

# Start a remote device session (prints remote session_id when ready).
pantoqacli remote device start --app-id <uploaded-app-id>

# Optional explicit params
pantoqacli remote device start \
  --app-id <uploaded-app-id> \
  --base-url $PANTOQA_BASE_URL \
  --token $PANTOQA_TOKEN \
  --device-name "pixel *" \
  --platform-version "14" \
  --startup-timeout 120

# Check remote device status
pantoqacli remote device status

# Open the remote-device preview website for the active session
pantoqacli remote device preview

# Open a specific session's preview website using the inline form
pantoqacli remote device preview --session-id={session_id}

# Open a specific session's preview website using a custom base URL
pantoqacli remote device preview \
  --session-id <remote-session-id> \
  --website-url $PANTOQA_WEBSITE_URL

# Check a specific session id
pantoqacli remote device status \
  --session-id <remote-session-id>

# Stop remote device background worker
pantoqacli remote device stop

# Optional timeout before force kill
pantoqacli remote device stop \
  --timeout 10
```

Remote command notes:
- `remote device start` requires `--app-id`, which is the `id` from `remote app list`; it creates and holds a remote device session, then prints the session id. If there is any doubt about which build to use, prompt the user to choose after showing the build id, display name, and upload time for each candidate.
- `remote device status` checks whether a remote session is active/inactive.
- `remote device preview` opens the preview page for the active session. Always open the preview page after doing `remote device start`` 
- `remote device stop` stops the background worker process started by `remote device start`.
- Both use the API key token.
- If `--session-id` is omitted for `status`, it falls back to `~/pantoqa-agent/.remote_device`.
- `~/pantoqa-agent/.remote_device` is a JSON state file with this structure:
  - `{"pid": "<worker-pid>", "session_id": "<remote-session-id>"}`
- `_remote_device_worker` is an internal command used by `start`; do not invoke it directly unless debugging internals.

## Troubleshooting

- **`bridge status` unreachable** — the local bridge process isn't running; tell the user to start it before retrying.
- **`bridge devices` returns empty** — no emulator/device is attached; nothing to run tests against yet.
- **Remote device start/status fails with auth error** — verify token resolution order: `--token`, then `$PANTOQA_TOKEN`, then `~/pantoqa-agent/.token`. If none are set, run `pantoqacli login`.
- **A prompt fails mid-run** — by default the whole run stops; pass `--continue-on-failure` if the user wants remaining prompts to execute regardless.
- **Resuming a websocket QA session** — reuse `--session-id` from a prior `run`.
- **Mapping to remote device session** — provide `--lt-session-id` when you need QA events associated with a specific remote-device session and driver_type=REMOTE.