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.
Important notes:
formatis currently informational only for normal commands; the implementation still returns JSON envelopesvisor --help,visor -h, and no-command invocation print plain-text usage instead of JSONvisor --versionandvisor -vprint the package version instead of JSONplatformis inferred from the selected device;--platformis 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.
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-msto wait for a selector after the tap
actionplatformargs
args contains either:
target, orx,y, andnormalized
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:
navigate
Purpose: move to a destination.
Required input:
to
actionplatformargs.to
- the destination is passed to WebdriverIO through
url(to)
act
Purpose: perform a helper action.
Supported variants:
name=typewithtargetandvaluename=typewithvalueonly, which types into the active fieldname=backname=homename=resetname=dragwithstart-x,start-y,end-x, andend-yname=sliderwithtarget,valuefrom0to1, and optionalstart-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:
actionplatformargs
scroll
Purpose: perform a page-style vertical scroll.
Required input:
directionwith valueupordown
percent, default70- 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:
actionplatformargs.directionargs.percent
- 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:
labelpathsettleorsettle-ms
actionplatformargs.labelargs.fileargs.pathargs.widthargs.height
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:
wait
Purpose: pause, wait for a selector, or wait for source stability.
Inputs:
msforwith optionaltimeoutandpoll-msstablewith optionaltimeoutandpoll-ms
actionplatformargs.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:
labelpathsettleorsettle-ms
actionplatformargs.labelargs.fileargs.pathargs.formatargs.bytes
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:
deviceserver-urlapp-idmap-dirattachno-mapcrawlcrawl-depthcrawl-limitcrawl-includecrawl-allow-riskyannotate-currentobservation-token
--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.
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:
actionmapmap.summary, when the persisted map can be readscreenobservation_tokenmemoryannotation, when--annotate-currentis usedcrawl, when--crawlis 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
deviceserver-urlapp-idattachmap-dir
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.
record
Purpose: start or stop a named replay flow.
Required input:
name
stopforcerecord-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
devicetimeoutoutputserver-urlapp-idattachno-maprepairparamaskey=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
validissues
- returns success when the scenario is valid
- returns a non-zero exit code with
status: okwhen 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
devicetimeoutoutputserver-urlapp-idattachruntime, with valuesappiumorlocalno-map
runwarnings
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
runswith default20thresholdwith default95.0devicetimeoutoutputserver-urlapp-idattachcompare-map
runsthresholddeterminismScorepassfailuresrunIdswarningsvariantswhencompare-mapis enabled
--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 toartifacts
messagepathformat
Runtime lifecycle commands
start
Purpose: start the Visor daemon and ensure Appium is reachable.
Optional inputs:
server-urlappium-cmd
serverUrldaemonappium
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
serverUrldaemonappium
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-urlforce
serverUrlstoppeddaemonappium
Response envelope behavior
Every command returns the same top-level envelope fields:statuscommand_idstarted_atended_atartifactsnext_actionerrordata
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.