Drive
Marionette sees and acts; it does not know your product. A prompt that names the screen, the precondition and the goal is most of a good run.
A good prompt
Give the agent what the screen cannot tell it: where to go, what to assume, what “works” means, and which keys to use. The report comes on its own when session reports are on.
Test the profile screen.
Connect to ws://127.0.0.1:9101/V3N5aBc=/ws. Assume the user is logged in.
On Profile, a malformed email must show an error and keep Submit
(key: submit_button) disabled; a valid one must submit.
Prompts for common jobs
Each prompt assumes a connected agent: add the ws:// URI from flutter run, or ask the agent to run the app first. Replace the screen names with yours.
I implemented the Forgot Password screen. Go to Login, tap "Forgot password", enter a valid email, submit, and check the logs that the reset request fired.
A verdict with the log line as its evidence.
Reproduce the ticket: on Settings, "Clear cache" does nothing. Follow the steps, save a screenshot, and read the logs. Do not change any code yet.
Evidence that the bug is real before anyone edits the code.
Fix the bug you just reproduced, hot reload, and replay the same steps. Tell me whether the symptom is gone, not just a nearby one.
The same steps before and after the fix, in one steps.md.
I refactored the routing. Open every bottom-nav tab, confirm each screen loads, and check the logs for exceptions after each one.
Each screen checked, each with its log check.
On Checkout, set text scale to 2.0, then 3.0 with bold text. After each, take a screenshot and check the logs for overflow errors.
Overflow errors per scale, with screenshots. Needs MarionetteDeviceConfig in the app.
Check the Profile form: a malformed email must show an error and keep Submit disabled, and a valid one must submit.
The run on the home page. Error text is read from a screenshot: the listing reports a field’s label, not its error.
Save screenshots of Settings in light and dark mode to the session for the pull request.
screenshots/01.png and 02.png, next to the session’s report.md.
List the custom extensions this app offers, use them to open each page once, and tell me what each page is for.
A map of the app, without reading its code.
How the agent should work
The drive Agent Skill teaches these; they are also the checklist for reading a run.
-
Look before acting:
get_interactive_elementson every new screen, not a guess from the source code. -
Target by
key, thenidentifier, thentext. Coordinates are a diagnostic: needing them means the widget needs a key. -
Confirm side effects in
get_logs. A tap that looks right is not proof that the request fired. -
Treat "element not found" as a setup gap first: an unconfigured widget, or painted content without
Semantics. -
Use
take_screenshots(inline: false)for evidence: it saves the image to the session instead of spending context on it. - A flow that is flaky every time needs a clearer key or a simpler hit target, not more retries.
Every tool
| MCP tool | CLI command | What it does |
|---|---|---|
connect |
--uri, or register + -i |
Attach to the app; opens a session when reports are on. |
disconnect |
— | Detach; names the session folder for report.md. |
get_interactive_elements |
get-interactive-elements |
List what is on screen; ancestor_keys, compaction. |
take_screenshots |
take-screenshots |
Capture every view; inline: false saves to the session. |
get_logs |
get-logs |
Logs since start or the last hot reload. |
tap |
tap |
By key, identifier, text, type or coordinates. |
double_tap, long_press |
double-tap, long-press |
With a delay or a duration. |
secondary_tap |
secondary-tap |
Right click, on desktop. |
swipe, pinch_zoom |
swipe, pinch-zoom |
Pages, dismissibles, drawers, maps, images. |
scroll_to |
scroll-to |
Scroll until a target is built and visible. |
press_back_button |
press-back-button |
System back, with Navigator or go_router. |
enter_text |
enter-text |
Set a field by key, identifier, or the focused field. |
press_key |
press-key |
A real key event: enter, tab, escape, shortcuts. |
set_device_config |
set-device-config |
Text scale, bold text, light or dark. |
list_custom_extensions, call_custom_extension |
— | The app's own actions, and named tools from schemas. |
hot_reload, hot_restart |
hot-reload, hot-restart |
Apply code changes, keeping or resetting state. |
| — | record-video |
A WebM clip of the app; needs ffmpeg. |
The CLI
marionette_cli installs the marionette command: the same actions as the MCP server, as shell commands. It needs the same setup in the app.
marionette help-ai prints a reference written for agents: have the agent read it once, or save it as a rule.
dart pub global activate marionette_cli
marionette help-ai
Pass --uri for a one-off command, or register the app under a name once and use
-i. list, doctor and unregister --stale manage the instances,
--timeout raises the 5 second default, and marionette mcp runs the MCP server itself.
$ marionette register shop ws://127.0.0.1:9101/V3N5aBc=/ws
$ marionette -i shop get-interactive-elements
$ marionette -i shop tap --key checkout_button
$ marionette -i shop enter-text --key promo_field --input "SPRING"
$ marionette -i shop get-logs
$ marionette -i shop record-video -o checkout.webm -d 10