Get started

Claim a simulator, let an agent drive your app on it and verify a screen, then release it.

Set up with your agent

Paste this into your coding agent. It installs warden if needed, then claims a simulator, drives your app, verifies a screen and hands the device back.

prompt

Use warden to drive this app on an iOS simulator and verify a screen. 1. If warden is missing: install it with bunx @delacour/warden install if bun is installed (check with command -v bun), otherwise npx @delacour/warden install. Show me the diff before you approve it. If warden is not found afterwards, add ~/.local/bin to PATH. Then run warden doctor and fix what it reports. 2. Run warden skill show, read it, and follow its rules: never touch a device you did not claim. 3. Run warden dev ios --json in the background and take the udid from its first line. Pass it on every call. 4. Run warden ui snapshot -i and find the elements you need. Prefer id= selectors over @e refs. 5. Act with warden ui press and warden ui fill, then verify with warden ui wait, is or get. A screenshot is not verification. 6. If an element has no testID, add testID and accessible in the component instead of retrying. 7. Stop warden dev when done; it releases the simulator and the Metro port. Report what you verified and the selectors you used.

The agent claims, looks, acts, verifies and releases. If a step fails it reads the exit code and hint, snapshots again and retries with a selector. Details of the loop: Agents drive devices. If the agent does not know warden yet: Set up your agent.

Or set up manually

By the end of this page your agent has its own simulator, has opened your app on it, read the screen as text, tapped and typed, checked the result and handed the device back. Nothing else on the Mac could take that simulator in the meantime, so several agents can do the same thing at once.

You need an app to drive and one of:

  • macOS with Xcode and an iOS runtime, for simulators.
  • The Android SDK, for emulators.

Install warden and the agent skill

npm i -g @delacour/warden && warden install

warden install links the binary, installs the skill that teaches your agent the rules and wires the SessionEnd hook for Claude Code and Codex. It shows a diff and asks first. Other agents and options: Set up your agent.

Claim a device

warden claim ios --json

warden reuses a free warden simulator, boots a stopped one or clones a new one, then waits until it is ready. If the host is full the claim waits in the boot queue (default --wait 30m). --json prints one document:

{
  "leases": [{ "leaseId": "…", "udid": "…", "name": "warden-iphone-17-1", "action": "reuse", "label": "…" }],
  "udids": ["…"],
  "reclaimed": []
}

Keep the udid (Android: the serial) and pass it on every later call. Do not pick a device from simctl list or adb devices: you may take another agent's.

A bare claim by an agent lasts 5 minutes and nothing refreshes it. Every warden ui call heartbeats the lease, so a device you keep driving stays yours. See Claim & release devices.

Run the app

In your app's directory:

warden dev ios --json

warden installs the dev build cached for your native fingerprint, starts Metro on a leased port and opens the dev client. The first stdout line is { udid, port, metroUrl, leaseIds, source }; read the udid from it instead of running step 2. Ctrl-C releases the device and the port. See warden dev.

To skip Metro and use the build with this worktree's JS repacked in, open the app on a claimed device:

warden ui open com.example.app --kind simulator --udid $UDID

Open the app and read the screen

warden ui open com.example.app --udid $UDID
warden ui snapshot -i --udid $UDID

Under warden dev, open attaches to the running dev client. snapshot -i prints the interactive elements and anything with a testID:

generation 4 · axbridge · 393×852@3x · 5 of 41 elements
@e1 [textbox] "Add a todo" id=todo-input value="" enabled hittable (24,118 345×48)
@e2 [button] "Add" id=todo-add enabled hittable (24,176 345×44)

Each line is @eN [role] "name" id=<testID> value="…" <states> (x,y w×h). A ref is valid until the next snapshot; a selector such as id=todo-add outlives screen changes. Add testID and accessible in the component for anything the agent must touch.

Act

warden ui fill id=todo-input "Buy milk" --udid $UDID
warden ui press id=todo-add --settle --udid $UDID

fill types per key and reads the text back. --settle waits for a quiet screen and prints what changed as - / + lines. scroll, gesture, alert and key cover the rest: warden ui.

Verify

warden ui wait text "Buy milk" --udid $UDID
warden ui is id=todo-add enabled --udid $UDID
warden ui get id=todo-input value --udid $UDID

wait polls until the element or text appears (default --timeout 5000). is exits 0 for true and 1 for false. get reads text, value, label, id, role, frame or count. A screenshot is not verification.

Release

warden ui close --release --udid $UDID

This stops the device's helper and frees the lease. Without it the bare claim expires after 5 minutes. For a claim you made without warden ui: warden release <leaseId> [--shutdown]. A device held by your running warden dev stays leased until you stop warden dev.

Control the device

Change what the device sees: network, location, clock, biometrics, permissions, camera feed, locale and timezone, accessibility settings, battery, and one-off push or memory-warning events. Each is recorded on your lease and undone on release.

warden ui network set 3g --udid $UDID
warden ui location set 37.3349,-122.009 --udid $UDID

Biometrics run the real Face ID, Touch ID or fingerprint prompt, with no app-side bypass:

warden ui biometrics enroll --udid $UDID
warden ui biometrics match --udid $UDID

Every condition, its platform support and how to set it from a spec, a suite or warden ui: Device conditions. One page each: Network, Location, Clock, Biometrics, Permissions, Camera, Locale and timezone, Accessibility settings, Battery, Push and memory warnings.

Feed a picture or clip into the app's real camera, or check what a screen reader would say:

warden ui camera set fixtures/qr-hello.png --udid $UDID
warden ui a11y audit --udid $UDID

Audits, reading order, announcements and TalkBack: Accessibility testing.

Run many in parallel

Each agent claims its own device, so five agents get five simulators. For your own script, warden run claims devices and ports, runs a command and releases everything on exit:

warden run ios --count 2 --port 8091:20 -- bun run e2e

warden batch hands a list of independent jobs to N devices, next job to whichever frees up first. How many boot at once depends on the host's RAM and CPU: warden capacity shows the budget and warden queue your place in line. See Scripts and Capacity & queue.

Keep it as a test

A check that must keep passing is a spec run by warden e2e. It uses the same driver, so a selector that works in warden ui works in a spec.

What else is here

Agent control

Device control

Parallel & capacity

On this page