Interceptor iOS
Agent-operator skill for the iOS surface of Interceptor. Use the interceptor ios * CLI to drive any installed app on an owned, unlocked, Developer-Mode iPhone: ref-tagged accessibility trees, deterministic coordinate taps, reliable text entry, scroll/drag, hardware buttons, screenshots, and app lifecycle.
The phone runs Interceptor's own on-device XCUITest runner (InterceptorRunner — not WebDriverAgent). The runner dials into the daemon over a WebSocket, exactly like the browser extension; the daemon drives it from there. Once the phone is paired over WiFi, no cable is needed. For content inside a browser tab load interceptor-browser; for a macOS app load interceptor-macos.
This installed skill is self-contained. Source checkouts also have AGENTS.md, but packaged users may only have the skill directory below /Library/Application Support/Interceptor/skills.
Fast Path
interceptor ios devices # 1. Phones with the agent installed (+ aliases)
interceptor ios status # 2. Per-phone connection state
interceptor ios tree --on phone # 3. Auto-connects, returns the ref-tagged element tree
interceptor ios find --label "Slack" --on phone # 4. Locate an element by label/role
interceptor ios click e29 --on phone # 5. Deterministic coordinate tap at the ref's frame center
interceptor ios type e243 "hello" --on phone # 6. Focus the field, then type
interceptor ios screenshot --on phone # 7. Capture the screen (VLM-budget resized)
Omit --on <name> when only one phone is set up — it's used by default. Set an alias once with interceptor ios name <udid> phone, then always use --on phone.
Treat eN refs as short-lived. The UI changes between calls; re-read with interceptor ios tree before acting. Refs carry frames and resolve to coordinates, so a tap is deterministic even if the underlying element handle went stale.
Apps with no usable tree (games, canvases): click, drag, and scroll also take screen points, drag X,Y X,Y --duration 2 is a long press, and interceptor macos vision text --image <screenshot> reads the screen. For a loop that needs a fresh look every few hundred milliseconds, ios stream start --out <path> has the runner push JPEG frames over Wi-Fi and rewrite that one file, and ios gesture presses with several fingers at once (a held pedal plus a tapped arrow). The recipes and the box-to-point conversion are in the command catalog.
The Model
- The device dials in. Ordinary runner verbs auto-launch on an unlocked phone and connect back over the network. The daemon hands the runner a VPN (Tailscale) address when the Mac has one, because iOS silently denies a backgrounded runner's LAN connection until the user has granted it Local Network access (Settings › Privacy & Security › Local Network), and a VPN address is exempt from that check;
interceptor ios status shows the address as dialBack / dialBackVia. Put the phone on the same VPN, or grant that switch once and LAN dial-back works too. Unlock and its probe require an already connected resident runner and do not auto-launch.
- Unlocked + foreground matters. A locked phone refuses app launches. While the runner is connected and resident,
interceptor ios unlock --secret ios-passcode attempts vault-backed passcode entry; success requires an observed unlocked state. ios unlock --probe checks without typing and can succeed while reporting locked: true. Both commands fail immediately when disconnected, without trying to launch XCTest on a locked phone. Unlock once and run ios tree to connect the runner first. Keep Auto-Lock off while working.
- Passcodes come from the vault, never from chat. Nothing can fake Face ID or Apple Pay. On a Face ID sheet tap "Enter Passcode", then
interceptor ios type <ref> --secret ios-passcode (or ios keys --secret ios-passcode); the runner types into SpringBoard when the sheet owns the keyboard. Register the passcode once with interceptor macos secret register ios-passcode --target ios.
- The XCTest authorization sheet is a human gate. Stop and ask. The first XCUITest launch after a reboot, and any later re-authorization, shows "Enter iPhone Passcode for XCTest — Enable UI Automation" on the phone; a runner verb then fails with
Not authorized for performing UI testing actions or times out on XCTestManager_IDEInterface. Nothing on the Mac can enter it: the runner is the process it blocks, so ios unlock and keys --secret cannot help; AccessibilityAudit and Accessibility Inspector can read the sheet but every action on it reports unsupported and leaves the field at 0 of 6; Switch Control cannot target a digit; iPhone Mirroring does not forward keystrokes to it; re-signing Apple's tools strips the private entitlements they need. When you see the sheet, report it and ask for the passcode to be tapped on the phone (or typed by a paired hardware keyboard), then restart the daemon and retry. Do not spend time on accessibility, Switch Control, Mirroring, or re-signing routes.
- Setup uses Xcode.
interceptor ios setup builds, signs, installs, and launches a team-scoped runner using the Apple Developer team configured in Xcode. interceptor ios login is unavailable and fails before password input. A background timer reruns Xcode setup before the certificate expires. Once registered, the runner stays resident for the XCUITest session; later verbs reuse it without a 30-second lease or relaunch.
Workflows
References
When To Switch Surfaces
- Target is content inside a browser tab (DOM, network, SPA state) → load
interceptor-browser.
- Target is a native macOS app (or the browser chrome / OS dialogs on the Mac) → load
interceptor-macos.
- Target is an app on the physical iPhone → this skill.
Do Not Default To Troubleshooting
- User wants an iPhone task completed → run
interceptor ios * commands.
- User wants Interceptor's iOS support fixed, installed, or explained → that's a separate task; ask before diving into repo state.
- Inside the Interceptor repo, use this skill for live device validation, not as the primary source of repo-development instructions.