Tomacco

Docs Recipes

Recipes#

Short sequences to copy into a coding agent's session or a terminal. Every line is a real tomaccoctl call, checked against the schema like the other guides. Over MCP, the same operation names are the tool names.

On this page (4)

Three rules hold for all of them:

The agent guide has the full contract, and Examples has a project to try these on.

Build and run an Android app#

Needs the Android SDK, a JDK and Gradle configured, and an emulator or phone; set them up with tomaccoctl setup or route C of getting started.

tomaccoctl call project.inspect '{"path":"/absolute/MyApp"}'
tomaccoctl call project.execute '{"path":"/absolute/MyApp","platform":"android","action":"build","format":"apk"}'
tomaccoctl call job.wait '{"job_id":"JOB_ID","seconds":60}'
tomaccoctl call device.list '{"platform":"android"}'
tomaccoctl call project.run '{"path":"/absolute/MyApp","platform":"android","device":"emulator-5554"}'

No emulator yet? android.avds lists them and android.avd_start starts one:

tomaccoctl call android.avds '{}'
tomaccoctl call android.avd_start '{"name":"Tomacco"}'

For a new app instead of an existing one, start with project.create:

tomaccoctl call project.create '{"path":"/absolute/MyApp","kind":"android","name":"MyApp","organization":"dev.example"}'

Take a screenshot and test the UI#

With the app running on emulator-5554. device.capture waits up to 30 seconds for the picture, and over MCP returns the PNG inline. android.hierarchy gives the UI tree with each element's text, id and bounds.

tomaccoctl call device.capture '{"platform":"android","device":"emulator-5554","path":"/absolute/screen.png"}'
tomaccoctl call android.hierarchy '{"device":"emulator-5554"}'

Find and press controls by what they say rather than by coordinates, then check the result:

tomaccoctl call testing.find '{"device":"emulator-5554","selector":{"text":"Add one"}}'
tomaccoctl call testing.action '{"device":"emulator-5554","selector":{"text":"Add one"},"action":"click"}'
tomaccoctl call testing.find '{"device":"emulator-5554","selector":{"text":"1"},"timeout":5}'

To repeat a whole flow, write it as a scenario and run it with testing.run. It writes a JSON report, JUnit results and screenshots of failures into directory. The format is in examples/hello-android/ui-test.json.

tomaccoctl call testing.run '{"path":"/absolute/ui-test.json","directory":"/absolute/ui-results","devices":["emulator-5554"]}'

To catch visual changes, compare a fresh capture with a baseline you approved. The first time, look at the picture and keep it with update_baseline; later calls report the changed fraction and write a diff image with the changes in red:

tomaccoctl call testing.visual_diff '{"actual":"/absolute/screen.png","baseline":"/absolute/baselines/home.png","update_baseline":true}'
tomaccoctl call testing.visual_diff '{"actual":"/absolute/screen.png","baseline":"/absolute/baselines/home.png","diff":"/absolute/home-diff.png","threshold":0.001}'

android.input types ASCII text directly and pastes other text, such as Grüße or emoji, through the device clipboard; that needs scrcpy configured and Android 7 or newer. To replace a whole field's text, install the companion once with testing.setup, then use testing.action with fill.

Build an iOS app on Linux with xtool#

Needs Swift and xtool, and Apple's SDK imported from an Xcode.xip you downloaded from Apple yourself. Tomacco does not ship or download that SDK. ios.doctor says what is missing. This builds an .app on Linux; it does not need a phone.

tomaccoctl call ios.sdk_install '{"path":"/absolute/Xcode.xip","slim":true}'
tomaccoctl call ios.doctor '{}'
tomaccoctl call project.create '{"path":"/absolute/MyApp","kind":"xtool","name":"MyApp","organization":"dev.example","build":true}'
tomaccoctl call job.wait '{"job_id":"JOB_ID","seconds":60}'
tomaccoctl call project.app '{"path":"/absolute/MyApp"}'

project.app names the built .app, its bundle_id, and whether the virtual iOS device accepts it. After changing the sources, rebuild:

tomaccoctl call project.execute '{"path":"/absolute/MyApp","platform":"ios"}'

A signed IPA and running on a real iPhone need an Apple account and a paired phone; follow the iPhone guide. Execution on a physical iPhone has not been qualified.

Put an app on the virtual iPhone#

Needs a configured and set-up virtual iOS device, which is experimental and takes a separate setup with your own Apple images. Start it if it is not running. The start is one durable job of about 9 minutes; follow it with ios.virtual.status and job.logs until the phase is launch-accepted.

tomaccoctl call ios.virtual.status '{}'
tomaccoctl call ios.virtual.start '{}'
tomaccoctl call job.logs '{"job_id":"JOB_ID","offset":0}'

Accepted is not the same as visible: display_visible:false in the status means every frame stayed black. On the running phone, one call installs and opens an arm64 .app without a reboot, in about 25 seconds:

tomaccoctl call ios.virtual.deploy '{"bundle":"/absolute/MyApp/xtool/MyApp.app"}'
tomaccoctl call job.logs '{"job_id":"JOB_ID","offset":0}'

For an xtool project, one job builds it, deploys it and checks for a crash after the launch; project.watch with the same arguments does that again on every save. The agent guide describes the run's report. Not run on a live phone yet.

tomaccoctl call project.run '{"path":"/absolute/MyApp","platform":"ios","device":"virtual-iphone"}'

The bundle rules (thin arm64, size limits, allowed entitlements) are in usage; a refusal names the file and the reason. To keep the app across restarts and open it at each start:

tomaccoctl call ios.virtual.configure '{"add_application":"/absolute/MyApp/xtool/MyApp.app"}'
tomaccoctl call ios.virtual.configure '{"application":"dev.example.MyApp"}'

View this page as Markdown on GitHub