Macros
Tokens are expensive. A macro batches many gestures into a single model call, so the agent invokes a sequence you authored and rehearsed instead of driving the phone step by step. Macros save tokens and time.
Create a macro
Section titled “Create a macro”Scaffold one, then edit the macro file:
physiclaw macros init notify-user-whatsappDirectory~/.physiclaw/macros/
- README.md
- notify-user-whatsapp.yml
notify-user-whatsapp.yml
Section titled “notify-user-whatsapp.yml”# What this file is: a recorded hand, so it lives in macros/.kind: macro# The pack grammar this file is written in.schema: 1name: notify-user-whatsapp# The agent picks a macro by this line alone, so make clear when to use it and what it does.description: Use when messaging your user on WhatsApp. Opens the chat and stages the text, stopping before Send
# false means the agent can't see or call it.enabled: true
# The args needed for this macro.inputs: message: description: The message text to send example: "Order placed, $4.77"
# Run top to bottom, one action per step: a verb and its object,# then anything the step needs beside it.steps: - home_screen
# A press names what it taps; `at` is the box, [left, top, right, # bottom] as 0–1 screen fractions. On-screen text as the object lets # the tap follow that text if it moves; icons get a plain description. - tap: "the WhatsApp icon" at: [0.31, 0.893, 0.491, 0.974]
# Sleep, then confirm what arrived. WhatsApp reopens where it was # left, so accept either title. - wait: 2 expect: {text: ["Chats", "your-user-name"], within: top} hint: "WhatsApp did not open — log in, then send manually"
# `skip_when` skips the step when the chat is already open; `require` # checks the right row sits where the tap lands. - tap: "your-user-name" at: [0.03, 0.32, 0.60, 0.42] skip_when: {text: "your-user-name", within: [0.20, 0.05, 0.60, 0.11]} require: {text: "your-user-name", within: [0.03, 0.30, 0.95, 0.44]}
- send_to_clipboard: "{message}"
# Skip if the keyboard is up. Its space bar has no label, so use "123". - tap: "the message input box" at: [0.2, 0.928, 0.6, 0.962] skip_when: {text: "123", within: [0.05, 0.86, 0.30, 0.93]}
# The input box sits higher once the keyboard is up. - long_press: "the message input box (keyboard up)" at: [0.2, 0.59, 0.6, 0.62]
- tap: "Paste" at: [0.10, 0.545, 0.21, 0.566] require: {text: "Paste", within: [0.03, 0.52, 0.40, 0.59]}
# No send step: the agent checks the text and decides whether to send.Rehearse
Section titled “Rehearse”Start the MCP server:
physiclaw mcpThen lint every macro file and replay this one on the live rig:
physiclaw macros checkphysiclaw macros run notify-user-whatsapp -i message=hiBest practices
Section titled “Best practices”Keep macros short and single-purpose. Anything that needs a decision mid-way, like confirmations, payments, or choosing among results, belongs to the agent, not a macro.
Let an agent draft it. Connect claude or codex to your MCP server, then ask it to write the macro.
Watch the stats. physiclaw macros stats shows per-macro counters. A rising consecutive_aborts streak means the app layout changed under the macro; re-rehearse and update the bboxes.