Skip to content

Commit 2e85fcc

Browse files
berkozeroclaude
andcommitted
Refactor to agent-first perceive-reason-act architecture
Replace describe/launch/wait-for commands with unified perceive command that returns screenshot paths + OCR elements + grid metadata. Remove tap --text in favor of coordinate-based tapping from perceive output. Agents now do all reasoning — CLI is a thin perceive-and-act bridge. - Add perceive command with --base64 flag for OpenClaw compatibility - Remove describe, launch, wait-for commands and tap --text option - Switch tap to CGEvent mouse input (iPhone Mirroring accepts it) - Add grid overlay generation to perceive output - Update WindowManager with ensureFocus and improved window detection - Update all docs, SKILL.md, and CHANGELOG Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fa407ff commit 2e85fcc

23 files changed

Lines changed: 1018 additions & 884 deletions

AGENTS.md

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# iphonebase
22

3-
Swift CLI to control iPhone via macOS iPhone Mirroring. Built for AI agents (OpenClaw, Claude Code, MCP) and standalone terminal use.
3+
Swift CLI to control iPhone via macOS iPhone Mirroring. Built for AI agents (OpenClaw, Claude Code) — the CLI is a thin perceive-and-act bridge, all reasoning lives in the agent.
44

55
## Tech Stack
66

@@ -31,20 +31,36 @@ Sources/
3131
ActionResult.swift # Shared JSON response envelope
3232
iphonebase/ # CLI executable
3333
IPhoneBase.swift # Entry point, command registration
34-
Commands/ # One file per command (13 commands)
34+
Commands/ # One file per command (11 commands)
3535
skills/
3636
iphonebase/SKILL.md # OpenClaw skill definition
3737
Tests/
38-
IPhoneBaseCoreTests/ # Unit tests (HIDKeyMap)
38+
IPhoneBaseCoreTests/ # Unit tests
3939
```
4040

41+
## Design Philosophy
42+
43+
**Perceive → Reason → Act.** The CLI provides two things:
44+
1. **Perception:** `perceive --json` returns screenshot + OCR + grid metadata
45+
2. **Actions:** `tap`, `swipe`, `scroll`, `drag`, `type`, `key`, `home`
46+
47+
The agent (LLM) does all reasoning. No OCR-based decision-making in the CLI.
48+
49+
## Commands (11)
50+
51+
| Category | Commands |
52+
|----------|----------|
53+
| **Perceive** | `perceive`, `screenshot`, `status`, `doctor` |
54+
| **Act** | `tap`, `swipe`, `scroll`, `drag`, `type`, `key`, `home` |
55+
4156
## Adding a New Command
4257

4358
1. Create `Sources/iphonebase/Commands/XxxCommand.swift`
4459
2. Implement `AsyncParsableCommand` (or `ParsableCommand` for sync-only)
4560
3. Add `XxxCommand.self` to the `subcommands` array in `IPhoneBase.swift`
4661
4. Include `--json` flag using the shared `ActionResult<T>` envelope
4762
5. Update `skills/iphonebase/SKILL.md` with the new command docs
63+
6. Action commands must be "dumb executors" — no embedded OCR or reasoning
4864

4965
## Code Conventions
5066

@@ -58,15 +74,17 @@ Tests/
5874

5975
## Coordinate System (critical)
6076

61-
- ScreenCapture captures at 2x retina resolution
62-
- OCR (Vision) returns normalized coords with bottom-left origin — must invert Y
63-
- `tap --text` handles conversion automatically; raw `tap x y` is relative to window (screen points)
77+
- ScreenCapture captures at 2x retina resolution (image pixels)
78+
- OCR (Vision) returns normalized coords with bottom-left origin — `OCREngine` inverts Y
79+
- `perceive` scales all coordinates (OCR elements + grid cells) to **window-relative screen points**
80+
- `tap x y` expects window-relative screen points — coordinates from `perceive` flow directly
6481
- All InputInjector operations use absolute screen coordinates (`window.bounds.origin + offset`)
6582

6683
## Input Injection Gotchas
6784

68-
- iPhone Mirroring blocks CGEvent clicks — only Karabiner virtual HID works
69-
- Tap sequence: `CGWarpMouseCursorPosition` → nudge-sync virtual pointer (3x 1px/-1px) → click via HID
85+
- iPhone Mirroring accepts CGEvent mouse input for taps
86+
- Swipe/drag uses Karabiner HID pointing click-drag (CGEvent scroll wheel is ignored)
87+
- Tap sequence: `CGWarpMouseCursorPosition` → CGEvent mouseDown/mouseUp
7088
- Timing delays (`usleep`) throughout InputInjector are tuned values, not arbitrary
7189
- Karabiner daemon must be running (not just installed)
7290

CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
77
## [Unreleased]
88

99
### Added
10+
- `perceive` now saves screenshots to `/tmp/iphonebase/` as files and returns paths in JSON (much smaller output)
11+
- `perceive --base64` flag for inline base64 image data (OpenClaw compatibility)
12+
- `perceive` now generates grid-overlay screenshot (`screen-grid.png`) alongside raw screenshot
1013
- `doctor` command — diagnostic check of all prerequisites (8 checks)
11-
- `wait-for` command — poll screen via OCR until text appears or timeout
1214
- `scroll` command — scroll up/down via mouse wheel with configurable clicks
1315
- `drag` command — smooth point-to-point drag with configurable steps
1416
- `--json` flag on all commands via shared `ActionResult<T>` envelope
@@ -18,10 +20,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
1820
- PR template
1921

2022
### Changed
23+
- Agent-first refactor: CLI is now a thin perceive-and-act bridge, all reasoning lives in the agent
2124
- All commands now return structured `ActionResult` JSON with `success`, `action`, `data`, `error`, `durationMs`
25+
- `perceive` grid cell coordinates now use window-relative screen points (matching OCR elements)
2226
- Improved input injection reliability with tuned timing and nudge-sync sequence
2327
- README rewritten with badges, agent quick start, comparison table
2428

29+
### Removed
30+
- `describe` command — redundant with `perceive --json` which includes OCR elements
31+
- `wait-for` command — agents should use perceive polling loops for full screen visibility
32+
- `launch` command — agents orchestrate app launching via Spotlight (`key 3 --modifier cmd` + `type` + `key enter`)
33+
- `tap --text` option — agents use coordinates from `perceive` with `tap x y`
34+
2535
## [0.1.0] - 2025-01-20
2636

2737
### Added

CLAUDE.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,16 @@ swift test # tests
2929
- Call `wm.bringToFront()` before any input injection
3030
- Errors: typed enums with `CustomStringConvertible`; throw `ExitCode.failure` for user errors
3131

32+
## Design Philosophy
33+
- **Perceive → Reason → Act:** `perceive` gives AI eyes, action commands are hands, AI does all reasoning
34+
- No OCR-based decision-making in action commands — agent provides coordinates from `perceive`
35+
- Action commands are "dumb executors": `tap x y`, `swipe up`, `type "text"`
36+
3237
## Coordinate System (critical)
3338
- ScreenCapture captures at 2x retina resolution
3439
- OCR (Vision) returns normalized coords with bottom-left origin — must invert Y
35-
- `tap --text` handles conversion automatically; raw `tap x y` is relative to window (screen points)
40+
- `perceive` scales all coordinates to window-relative screen points
41+
- `tap x y` expects window-relative screen points — coordinates from `perceive` flow directly
3642
- All InputInjector operations use absolute screen coordinates (`window.bounds.origin + offset`)
3743

3844
## Input Injection Gotchas

README.md

Lines changed: 73 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -57,47 +57,64 @@ Run `iphonebase doctor` to check all prerequisites at once.
5757

5858
## Real-World Examples
5959

60+
All examples follow the **perceive → reason → act** loop. The AI agent reads the `perceive` output, reasons about what to do, then acts.
61+
6062
### Send an iMessage
6163

6264
```bash
63-
iphonebase launch "Messages"
64-
iphonebase wait-for "Messages" --timeout 5
65-
iphonebase tap --text "Mom"
66-
iphonebase wait-for "iMessage" --timeout 5
65+
iphonebase key 3 --modifier cmd # open Spotlight
66+
iphonebase type "Messages" # search for Messages
67+
iphonebase key enter # open it
68+
sleep 1
69+
iphonebase perceive --json # see Messages screen
70+
# Agent reads image, finds "Mom" at (200, 340)
71+
iphonebase tap 200 340 # tap on Mom's conversation
72+
sleep 1
73+
iphonebase perceive --json # see conversation
6774
iphonebase type "Running 10 min late!"
68-
iphonebase tap --text "Send"
69-
iphonebase wait-for "Delivered" --timeout 10
75+
iphonebase perceive --json # find Send button at (350, 680)
76+
iphonebase tap 350 680 # tap Send
7077
```
7178

72-
### Check the weather
79+
### Navigate Settings
7380

7481
```bash
75-
iphonebase launch "Weather"
76-
iphonebase wait-for "Weather" --timeout 5
77-
iphonebase describe --json | jq '.data.elements[].text'
82+
iphonebase key 3 --modifier cmd # open Spotlight
83+
iphonebase type "Settings"
84+
iphonebase key enter
85+
sleep 1
86+
iphonebase perceive --json # see Settings screen
87+
# Agent finds "General" at (200, 340)
88+
iphonebase tap 200 340
89+
sleep 1
90+
iphonebase perceive --json # see General screen
91+
# Agent finds "About" at (200, 280)
92+
iphonebase tap 200 280
93+
sleep 1
94+
iphonebase perceive --json # read iOS version from screen
7895
```
7996

80-
### Navigate Settings
97+
### Scroll through a feed
8198

8299
```bash
83-
iphonebase launch "Settings"
84-
iphonebase wait-for "Settings" --timeout 5
85-
iphonebase tap --text "General"
86-
iphonebase wait-for "About" --timeout 5
87-
iphonebase tap --text "About"
88-
iphonebase wait-for "iOS Version" --timeout 5
89-
iphonebase describe --json # read the full screen
100+
iphonebase perceive --json # see current screen
101+
iphonebase scroll down --clicks 5 # scroll content
102+
sleep 0.5
103+
iphonebase perceive --json # see new content
90104
```
91105

92-
### Scroll through a feed
106+
### Tap an app icon (grid cell)
93107

94108
```bash
95-
iphonebase launch "Instagram"
96-
iphonebase wait-for "Instagram" --timeout 5
97-
iphonebase scroll down --clicks 5
98-
iphonebase screenshot --output feed.png
109+
iphonebase perceive --json # get screen state
110+
# Agent reads grid image, sees Gmail icon in cell B12
111+
iphonebase tap --cell B12 # tap the icon by grid cell
112+
sleep 1
113+
iphonebase perceive --json # verify app opened
99114
```
100115

116+
Use grid cells for icons, toggles, and non-text elements. Use OCR coordinates for text labels and menu items. See [Grid Mode](#grid-mode-for-vision-model-agents) below.
117+
101118
## Grid Mode for Vision-Model Agents
102119

103120
OCR misses icons, images, and non-text UI elements. Grid mode lets vision-capable LLMs (Claude, GPT-4o) see the full screen with coordinate references:
@@ -134,43 +151,32 @@ No jailbreak. No developer account. No app installation on the phone. Your iPhon
134151

135152
| Command | Description |
136153
|---|---|
137-
| `doctor` | Run diagnostics on all prerequisites |
138-
| `status` | Check if iPhone Mirroring is available |
139-
| `screenshot` | Capture the iPhone screen as PNG (supports `--grid`) |
140-
| `describe` | OCR — detect all text on screen with coordinates |
141-
| `wait-for` | Poll until specific text appears (or timeout) |
142-
| `tap` | Tap by coordinates, text, or grid cell |
154+
| `perceive` | Screenshot + OCR + grid metadata — the agent's primary input |
155+
| `tap` | Tap by coordinates or grid cell |
143156
| `swipe` | Swipe up/down/left/right |
144-
| `scroll` | Scroll via mouse wheel |
157+
| `scroll` | Scroll up/down |
145158
| `drag` | Point-to-point drag |
146159
| `type` | Type text character by character |
147160
| `key` | Press a key with optional modifiers |
148161
| `home` | Go to iPhone home screen |
149-
| `launch` | Open an app by name via Spotlight |
162+
| `screenshot` | Capture the iPhone screen as PNG (supports `--grid`) |
163+
| `status` | Check if iPhone Mirroring is available |
164+
| `doctor` | Run diagnostics on all prerequisites |
150165

151166
Every command supports `--json` for structured machine-readable output.
152167

153168
### Command Examples
154169

155170
```bash
156-
# Screenshot (with optional grid overlay for vision-model agents)
157-
iphonebase screenshot --output screen.png
158-
iphonebase screenshot --grid --output grid.png
159-
160-
# Read the screen via OCR
161-
iphonebase describe
162-
iphonebase describe --json
163-
164-
# Tap by text (recommended — handles coordinate conversion)
165-
iphonebase tap --text "Settings"
166-
iphonebase tap --text "Send" --double
167-
iphonebase tap --text "Photos" --long
171+
# Perceive: screenshot + OCR + grid metadata (agent's primary input)
172+
iphonebase perceive --json
173+
iphonebase perceive --json --base64 # inline image for OpenClaw
168174

169-
# Tap by grid cell (use with screenshot --grid)
170-
iphonebase tap --cell B3
171-
172-
# Tap by raw coordinates (relative to mirroring window)
175+
# Tap by coordinates (from perceive output) or grid cell
173176
iphonebase tap 200 400
177+
iphonebase tap --cell B3
178+
iphonebase tap 200 400 --double
179+
iphonebase tap 200 400 --long
174180

175181
# Swipe and scroll
176182
iphonebase swipe up
@@ -184,13 +190,14 @@ iphonebase drag 100 200 300 400 --steps 30
184190
iphonebase type "hello world"
185191
iphonebase key enter
186192
iphonebase key a --modifier cmd
193+
iphonebase key 3 --modifier cmd # open Spotlight on iPhone
187194

188195
# Navigate
189196
iphonebase home
190-
iphonebase launch "Messages"
191197

192-
# Wait for screen transition
193-
iphonebase wait-for "General" --timeout 10
198+
# Screenshot (with optional grid overlay)
199+
iphonebase screenshot --output screen.png
200+
iphonebase screenshot --grid --output grid.png
194201
```
195202

196203
### JSON Output
@@ -222,12 +229,12 @@ Then ask your agent to interact with your phone:
222229
> "Open Settings on my iPhone and check the iOS version"
223230
224231
OpenClaw will automatically use iphonebase to:
225-
1. `launch "Settings"`open the Settings app
226-
2. `wait-for "Settings"` — confirm it loaded
227-
3. `tap --text "General"` — navigate to General
228-
4. `wait-for "About"`wait for the screen
229-
5. `tap --text "About"`open the About page
230-
6. `describe --json` — read the iOS version and report back
232+
1. `perceive --json --base64`capture current screen state (image + OCR + grid)
233+
2. Decide next action (LLM) based on what it sees
234+
3. `tap 200 340` — tap "General" using coordinates from perceive
235+
4. `perceive --json --base64`verify navigation
236+
5. `tap 200 280`tap "About"
237+
6. `perceive --json --base64` — read the iOS version and report back
231238

232239
The skill definition lives in `skills/iphonebase/SKILL.md` — it teaches agents the full command set, recommended workflow, and coordinate system.
233240

@@ -247,7 +254,7 @@ iphonebase is a plain CLI with `--json` output. Any agent that can execute shell
247254
import subprocess, json
248255

249256
result = subprocess.run(
250-
["iphonebase", "describe", "--json"],
257+
["iphonebase", "perceive", "--json"],
251258
capture_output=True, text=True
252259
)
253260
screen = json.loads(result.stdout)
@@ -257,20 +264,20 @@ for element in screen["data"]["elements"]:
257264

258265
## Agent Workflow
259266

260-
The recommended automation loop for AI agents:
267+
The recommended **perceive → reason → act** loop:
261268

262269
```
263-
1. iphonebase doctor # Verify prerequisites (first run)
264-
2. iphonebase status --json # Confirm mirroring is active
265-
3. iphonebase describe --json # Read current screen (or screenshot --grid --json)
270+
1. iphonebase doctor # Verify prerequisites (first run only)
271+
2. iphonebase perceive --json # Read current screen state
272+
3. Read the gridImagePath file # See the screen (Claude Code)
266273
4. Reason about which element to interact with
267-
5. Act: tap / type / swipe / scroll / drag / key / launch / home
268-
6. iphonebase wait-for "expected text" --timeout 5
269-
7. iphonebase describe --json # Verify action had expected effect
270-
↳ Repeat 3–7 for multi-step tasks
274+
5. Act: tap / type / swipe / scroll / drag / key / home
275+
6. sleep 0.5-1 # Let UI settle
276+
7. iphonebase perceive --json # Verify action had expected effect
277+
↳ Repeat 2–7 for multi-step tasks
271278
```
272279

273-
Use `wait-for` between actions instead of hardcoded delays.
280+
All coordinates from `perceive` flow directly into `tap x y` — no conversion needed.
274281

275282
## Architecture
276283

0 commit comments

Comments
 (0)