> ## Documentation Index
> Fetch the complete documentation index at: https://visorai.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Command reference

> See every Visor command, the inputs it accepts, and the shape of the data it returns.

This page documents the current command surface exactly as implemented.

If you run `visor --help`, `visor -h`, or invoke the CLI with no command, Visor prints a usage summary instead of a JSON response envelope. `visor --version` and `visor -v` print the package version instead of a JSON response envelope.

## Global runtime inputs

These inputs are accepted globally and, where relevant, by individual commands.

| Input | Meaning |
| - | - |
| `device` | Device or simulator identifier |
| `timeout` | Per-step timeout budget in milliseconds for scenario runs |
| `output` | Base output directory for run artifacts and reports |
| `format` | `text` or `json` |
| `server-url` | Appium server URL |
| `app-id` | Android app package or iOS bundle identifier; on iOS it must match an installed app on the selected target |
| `map-dir` | Explicit directory for the runtime index, compact agent memory, and route checkpoints |
| `attach` | Attach to an already running app process when possible |
| `no-map` | Disable app-map reads and writes for this command |
| `repair` | Allow opt-in exploratory app-map repair after a missing or stale route |

Important notes:

* `format` is currently informational only for normal commands; the implementation still returns JSON envelopes
* `visor --help`, `visor -h`, and no-command invocation print plain-text usage instead of JSON
* `visor --version` and `visor -v` print the package version instead of JSON
* `platform` is inferred from the selected device; `--platform` is not accepted

## Mapped execution

Visor enables mapped execution by default for direct action commands and scenario runs. It observes lightweight UI source around target actions, stores a schema-versioned host-machine app map under `.visor/maps`, and can reuse confirmed navigation edges when a later command targets a known screen from a different current screen.

Agents should follow the repository-installed `visor-discovery` skill: read compact agent memory before visual evidence, use `route` for multi-step navigation, and use `discover` only to name or repair unknown live states. See [Agent memory and deterministic routes](/docs/features/app-map).

Use `--no-map` or `VISOR_NO_MAP=true` when you need raw Appium behavior. No-map mode disables both map reads and map writes.

Exploratory repair is off by default. Use `--repair` when you want Visor to try safe controls after a missing or stale route instead of failing fast.

Every mapped direct action returns `data.map`. Mapped target, coordinate, and scroll actions also return `data.observation` with before/after fingerprints, `screen_changed`, previous/current screen ids, and `visible_text_count`. Scenario runs return `data.run.map` and per-step map details under `data.run.steps[].details.map` when a step used routing, repair, or map observation.

## Direct action commands

### `tap`

Purpose: click an element or coordinate.

Inputs:

* target mode: `target`
* coordinate mode: `x`, `y`
* optional: `normalized`
* optional post-action wait: `--wait-for`, `--timeout`, and `--poll-ms` to wait for a selector after the tap

Returns action payload fields:

* `action`
* `platform`
* `args`

`args` contains either:

* `target`, or
* `x`, `y`, and `normalized`

Coordinate `x` and `y` values are screen points by default. With `normalized`, they are fractions of the current screen width and height.
Coordinate taps work as direct commands and as scenario steps. Target taps report `args.tap_method`; when Visor can resolve an element rectangle, it taps the center coordinate and returns the resolved `x` and `y`.

Examples:

```bash theme={null}
visor tap --target accessibility=Continue
visor tap --target text=Continue
visor tap --target text~=Settings
visor tap --target "first-in-section=Featured products"
visor tap --x 120 --y 640
visor tap --x 0.5 --y 0.92 --normalized
```

### `navigate`

Purpose: move to a destination.

Required input:

* `to`

Returns action payload fields:

* `action`
* `platform`
* `args.to`

Current behavior:

* the destination is passed to WebdriverIO through `url(to)`

### `act`

Purpose: perform a helper action.

Supported variants:

* `name=type` with `target` and `value`
* `name=type` with `value` only, which types into the active field
* `name=back`
* `name=home`
* `name=reset`
* `name=drag` with `start-x`, `start-y`, `end-x`, and `end-y`
* `name=slider` with `target`, `value` from `0` to `1`, and optional `start-value`

`drag` also accepts `normalized` so coordinates can be fractions of the viewport. `reset` requires `app-id` because Visor restarts the target app.

Optional post-action wait inputs:

* `--wait-for`
* `--timeout`
* `--poll-ms`

`--wait-for` polls for a selector after the helper action completes. `--timeout` and `--poll-ms` control that post-action wait.

Returns action payload fields:

* `action`
* `platform`
* `args`

### `scroll`

Purpose: perform a page-style vertical scroll.

Required input:

* `direction` with value `up` or `down`

Optional input:

* `percent`, default `70`
* post-action wait: `--wait-for`, `--timeout`, and `--poll-ms`

`--wait-for` polls for a selector after the scroll completes. `--timeout` and `--poll-ms` control that post-action wait.

Returns action payload fields:

* `action`
* `platform`
* `args.direction`
* `args.percent`

Current behavior:

* Android uses Appium scroll gestures within the current viewport
* iOS attempts the same gesture path and falls back to a touch swipe when needed

### `screenshot`

Purpose: capture a PNG image.

Optional input:

* `label`
* `path`
* `settle` or `settle-ms`

Returns action payload fields:

* `action`
* `platform`
* `args.label`
* `args.file`
* `args.path`
* `args.width`
* `args.height`

If `label` is omitted, the runtime defaults it to `capture`.
If `path` is provided, Visor writes the PNG to that path. Otherwise it writes `<label>.png` in the current working directory for direct Appium actions, or under `--output` when that directory is supplied. Direct command responses include the screenshot path in both `data.args.path` and the top-level `artifacts` array. `settle` waits briefly before capture; `settle-ms` controls the wait duration.

Examples:

```bash theme={null}
visor screenshot --label checkout
visor screenshot --path /tmp/checkout.png
```

### `wait`

Purpose: pause, wait for a selector, or wait for source stability.

Inputs:

* `ms`
* `for` with optional `timeout` and `poll-ms`
* `stable` with optional `timeout` and `poll-ms`

Returns action payload fields:

* `action`
* `platform`
* `args.ms`, or selector/stability wait result fields

`wait --for <selector>` polls until the selector exists. `wait --stable` polls UI source until two consecutive captures match. If either predicate times out, the command or scenario step fails and preserves the wait payload with `matched: false`.

### `source`

Purpose: capture current UI source as XML.

Optional inputs:

* `label`
* `path`
* `settle` or `settle-ms`

Returns action payload fields:

* `action`
* `platform`
* `args.label`
* `args.file`
* `args.path`
* `args.format`
* `args.bytes`

For direct action commands, run `visor start` first.
For direct `screenshot` and `source`, a returned `args.path` is also surfaced as a top-level response artifact. `--output <dir>` writes the capture under that directory when `path` is not provided.

### `discover`

Purpose: observe the current screen and update the shared app map without running a scenario action.

Optional runtime inputs:

* `device`
* `server-url`
* `app-id`
* `map-dir`
* `attach`
* `no-map`
* `crawl`
* `crawl-depth`
* `crawl-limit`
* `crawl-include`
* `crawl-allow-risky`
* `annotate-current`
* `observation-token`

Use `--crawl` only when you explicitly want diagnostic programmatic exploration. Crawling is bounded by `--crawl-depth` and `--crawl-limit`; the agent discovery workflow does not run it by default.
Use `--crawl-include <text>` and `--crawl-allow-risky` on sandbox/dev builds when you intentionally want the crawler to include otherwise risky controls such as delete, logout, purchase, or confirm surfaces.

Use `--annotate-current <file|->` to merge semantic screen/action descriptions into an observed variant. Pass a UTF-8 JSON file or `-` for one JSON value from standard input. Add `--observation-token <token>` to annotate the exact prior discovery without another source read. Visor validates annotation input before device selection or daemon work.

```json theme={null}
{
  "screen": {
    "label": "Checkout shipping address",
    "purpose": "Collect delivery details before payment"
  },
  "actions": [
    {
      "command": "tap",
      "args": { "target": "text=Continue" },
      "label": "Continue to payment",
      "intent": "advance_checkout",
      "safety": "safe"
    }
  ]
}
```

`screen` and `actions` are optional individually, but the document must contain at least one screen or action. Screen annotations require `label` and `purpose`; they also accept `description` and `notes`. Actions require `command`, `args`, `label`, `intent`, and `safety`, where safety is `safe`, `needs-input`, `risky`, or `unknown`; they also accept `description` and `notes`. Unknown fields and unsupported commands fail with `INPUT_ERROR`.

Response data fields:

* `action`
* `map`
* `map.summary`, when the persisted map can be read
* `screen`
* `observation_token`
* `memory`
* `annotation`, when `--annotate-current` is used
* `crawl`, when `--crawl` is used

`annotation` reports `screen_applied` plus action counts for `actions_inserted`, `actions_updated`, and `actions_merged`.

`map.summary` is a path-free inventory of the persisted app map for review: schema version, identity, app id, platform, screen count, variant count, edge count, authentication-required variant count, and last update time.

`map.agent_path` points to the compact AI-only memory. `map.path` points to Visor's internal runtime index.

### `route`

Purpose: execute one deterministic, agent-authored multi-path route through a single daemon request.

Required input:

* plan file path, or `-` to read JSON from standard input

Runtime inputs:

* `device`
* `server-url`
* `app-id`
* `attach`
* `map-dir`

The plan requires a semantic `goal` and at least one ordered path. Every step requires `safety: "safe"`, a supported action command, arguments, and an expected screen plus executable selector. Visor rejects unsafe plans before device work.

Visor tries eligible paths in document order. It checkpoints each step and returns after the first completed path. When no path can recover an unexpected state, it returns `status: "needs_discovery"` with compact rediscovery context.

```bash theme={null}
visor route /tmp/settings-route.json \
  --device <id> --app-id com.example.app --attach --map-dir /tmp/visor-map
```

### `record`

Purpose: start or stop a named replay flow.

Required input:

* `name`

Optional input:

* `stop`
* `force`
* `record-values`

`visor record <name>` creates `.visor/flows/<name>.json` and marks it active. Successful direct action commands append ordered steps to every active flow. `visor record <name> --stop` closes the flow so it can be replayed.

Recording is privacy-preserving by default: typed `act --name type` values are redacted from the flow, and map, observation, and screen metadata are not persisted into `.visor/flows/*.json`. Use `--record-values` only when you intentionally want typed values saved for replay. Starting a recording fails if a flow with the same name already exists; pass `--force` to overwrite it.

### `replay`

Purpose: run a recorded flow through the normal scenario runner.

Required input:

* `name`

Optional runtime inputs:

* `device`
* `timeout`
* `output`
* `server-url`
* `app-id`
* `attach`
* `no-map`
* `repair`
* `param` as `key=value`

`replay` converts recorded steps into a generated scenario and runs it through the Appium-backed daemon path. `--param query=shoes` substitutes `{{query}}` placeholders in recorded args before execution. Replay responses list substituted names in `data.param_keys` and do not echo parameter values.

## Scenario commands

### `validate`

Purpose: parse a scenario and return validation issues.

Required input:

* `scenario`

Response data fields:

* `valid`
* `issues`

Exit behavior:

* returns success when the scenario is valid
* returns a non-zero exit code with `status: ok` when the scenario is syntactically valid JSON but fails schema validation
* returns a failure envelope when parsing fails or the file cannot be read

### `run`

Purpose: execute one scenario, evaluate assertions, and write reports.

Required input:

* `scenario`

Optional runtime inputs:

* `device`
* `timeout`
* `output`
* `server-url`
* `app-id`
* `attach`
* `runtime`, with values `appium` or `local`
* `no-map`

Response data fields:

* `run`
* `warnings`

The `run` object contains the full run result, including step results, assertion results, artifacts, determinism signature, and run-level error data.

For Appium-backed scenario runs, run `visor start` first.

For deterministic local development checks, pass `--runtime local`. This runs the scenario against Visor's local fixture runtime, writes normal reports and artifacts, and does not require Appium, a daemon, an emulator, a simulator, or a real app.

When Visor enables mapped execution, the run also includes map metadata and any hidden route or repair decisions in step details.

### `benchmark`

Purpose: run one scenario repeatedly and compute a determinism score.

Required input:

* `scenario`

Optional inputs:

* `runs` with default `20`
* `threshold` with default `95.0`
* `device`
* `timeout`
* `output`
* `server-url`
* `app-id`
* `attach`
* `compare-map`

Response data fields:

* `runs`
* `threshold`
* `determinismScore`
* `pass`
* `failures`
* `runIds`
* `warnings`
* `variants` when `compare-map` is enabled

Benchmark runs are Appium-backed. They do not accept `--runtime local`; use `run` with `--runtime local` for deterministic local harness checks.

Use `--compare-map` to run the same scenario twice per iteration group: once with app-map reads and writes disabled, and once with app-map enabled. Each entry in `variants` includes `name`, `mapEnabled`, `runs`, `threshold`, `determinismScore`, `pass`, `failures`, and `runIds`.

For benchmark runs, run `visor start` first.

### `report`

Purpose: return guidance about where run reports are stored.

Optional input:

* `path`, which defaults to `artifacts`

Response data fields:

* `message`
* `path`
* `format`

## Runtime lifecycle commands

### `start`

Purpose: start the Visor daemon and ensure Appium is reachable.

Optional inputs:

* `server-url`
* `appium-cmd`

Response data fields:

* `serverUrl`
* `daemon`
* `appium`

`daemon` includes process and diagnostic metadata such as `running`, `pid`, `socketPath`, `metadataPath`, `logPath`, `packageVersion`, `runtimeVersion`, `currentPackageVersion`, and `stale`. When `visor start` finds an already-running daemon whose package version differs from the current CLI package version, or whose package version is `unknown`, `daemon.stale` is `true` and the response includes `daemon.warning` and `daemon.nextAction`.

### `status`

Purpose: report Visor daemon state, Appium reachability, and warm sessions.

Optional inputs:

* `server-url`

Response data fields:

* `serverUrl`
* `daemon`
* `appium`

`daemon.packageVersion` and `daemon.runtimeVersion` describe the running daemon process. `daemon.currentPackageVersion` describes the CLI process that produced the status response. If those package versions differ, or the daemon does not report a package version and is shown as `unknown`, `daemon.stale` is `true`, `daemon.warning` explains the mismatch, and `daemon.nextAction` is `restart`. Visor reports this state but does not automatically restart the daemon.

### `stop`

Purpose: close warm sessions and stop the Visor daemon.

Optional inputs:

* `server-url`
* `force`

Response data fields:

* `serverUrl`
* `stopped`
* `daemon`
* `appium`

## Response envelope behavior

Every command returns the same top-level envelope fields:

* `status`
* `command_id`
* `started_at`
* `ended_at`
* `artifacts`
* `next_action`
* `error`
* `data`

`next_action` is a hint, not an execution chain. Examples include `run`, `report`, and `none`.

Help and version output are the exceptions: `visor --help`, `visor -h`, and no-command invocation print usage text instead of an envelope, while `visor --version` and `visor -v` print the package version.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.