agentleFS
mobiumdevmobiumdev/mobium/skills/mobile-check/SKILL.md

mobile-check

Skill2 starsChanged 34 days agoApache-2.0View on GitHub

Automate mobile apps — native, hybrid, cross-platform, and pages in a mobile browser or installed as a PWA — with the Mobium CLI, on Android emulators and phones and iOS simulators and iPhones. Use to launch and drive an app, fill forms, read the screen, answer system dialogs, reach into WebViews, capture screenshots and recordings, or confirm that a mobile change actually works.

2 other projects use a version of this.

Adopted by

Most recently taken up by

  • Other versions2 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 2

    1. duncan-buildroom/freeskills★ 65same name
    2. nobufumi-tego/gcp-agile-template★ 0same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • In this repositoryOne other file it gives its agents.

The file

Show its rules and text

We split this skill into the rules it gives an agent

14 rules. About 0 are common, 6 are distinctive, and 8 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈6

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 8

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If you need to test a *website* — tabs, browser sessions, a desktop browser — use vibium instead.

    line 27
  2. Distinctive:

    Every automation follows the same loop:

    line 33
  3. Very specific:

    Re-map: after anything that changes the screen, run mobium map again

    line 37
  4. Very specific:

    Run mobium --help to confirm, then use the resolved path throughout.

    line 51
  5. Very specific:

    If nothing is listed, an emulator or simulator can be started with mobium boot <avd-or-simulator-name>, which answers once it has booted.

    line 60
  6. Very specific:

    Use mobium current to check a tap went where you expected:

    line 98
  7. Distinctive:

    Never sleep. Every action already waits for its target — up to two seconds to be there and enabled, up to five to hold still — so a tap …

    line 118
  8. Distinctive:

    Every action waits until its target is on screen and enabled (up to two seconds) and holding still (up to five), and refuses — with a …

    line 255
  9. Distinctive:

    Every step is checked before the first runs, and the batch stops at the first failure with that step's own error and exit status.

    line 279
  10. Very specific:

    Add ,role=button to narrow an ambiguous locator: text=Continue,role=button.

    line 299
  11. Distinctive:

    Every one of these can move the screen, so map again before acting.

    line 382
  12. Distinctive:

    Do not chain when you need to read the output to decide what to do next — run those separately so you can look at the map before choosing a …

    line 457
  13. Very specific:

    Every failure also carries a code, so you can act on the kind without parsing the sentence: --json prints it ("code": "no_such_element", …

    line 464
  14. Very specific:

    Ask the user to start one.

    line 503

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Mobium Mobile Automation — CLI Reference

The mobium CLI automates mobile apps on Android emulators and phones, and iOS simulators and iPhones. A background daemon keeps the device session alive between commands, so each command is fast after the first.

mobium launch com.example.shop && mobium map && mobium tap @e5 && mobium map

What the app is built with decides how to reach it:

App typeHow
Native (Android SDK, Jetpack Compose, UIKit, SwiftUI)map and act — the default
Cross-platform that renders native views (React Native)the same as native: it renders real native views. Use the @ref from map, not a hand-written label= or testid=: one testID repeats across several views
Hybrid (a WebView inside the app)switch context into the WebView — Hybrid apps
A page in Chrome or Safari, or a PWA — including one from the Play Store (a Trusted Web Activity)the same contexts: the page is WEBVIEW_… — pick it by the URL contexts prints, not by its name. launch of an installed PWA's package says what it is; Chrome draws it, so current names Chrome. On iOS a tap in Safari's or a home-screen web app's page is placed from text the page and the native tree both report, and lands; where it cannot be placed it is refused, and NATIVE_APP reaches the same elements. On Android, a refusal saying Chrome stopped reporting its WebView means no tap can be placed in that page: read it, and say so
Cross-platform that paints (Flutter)the same as native: Flutter publishes a semantics tree, and map reads it. Its text is the label, not the text — use label=; a Semantics(identifier:) is a testid=. A widget with no semantics (a custom painting, an icon with no tooltip) cannot be named: say so rather than retrying

If you need to test a website — tabs, browser sessions, a desktop browser — use vibium instead. Mobium reaches a page already open on the device; it does not manage the browser.

Core Workflow

Every automation follows the same loop:

  1. Map: mobium map — lists what is on screen as @e1, @e2, …
  2. Act: mobium tap @e5, mobium type @e2 "text", mobium swipe up
  3. Re-map: after anything that changes the screen, run mobium map again

Refs are only valid for the screen they were taken from. A ref is re-resolved against a fresh snapshot before every action, so a stale one fails loudly rather than tapping the wrong thing — but that means you must re-map after navigating.

Binary Resolution

Resolve the mobium binary path once before running commands:

  1. Try mobium directly (works if it is on PATH)
  2. Fall back to ./bin/mobium (dev environment, in the project root)

Run mobium --help to confirm, then use the resolved path throughout.

First: check there is a device

mobium devices
# emulator-5554   device   (android emulator, model: sdk_gphone64_arm64, Android 15 (API 35), navigation: gestures)

If nothing is listed, an emulator or simulator can be started with mobium boot <avd-or-simulator-name>, which answers once it has booted; a phone has to be plugged in by the user (Android with USB debugging on; an iPhone trusted and unlocked). Say so rather than retrying.

Commands

Getting to the screen you want

  • mobium current — which app is in the foreground
  • mobium state <package|bundle-id> — any app: not installed, not running, background or foreground
  • mobium shake — shake an emulator or simulator (shake-to-undo, a debug menu); a real phone refuses. Check the screen after: whether the app reacts is its own detector's business
  • mobium biometric — whether a face or finger is enrolled; enroll / unenroll set it, and match / nomatch present one to the prompt that is up and say what the device made of it (accepted, not recognized, failed, locked out). Emulators and simulators only; an emulator gets PIN 1111 to enroll, which unenroll removes. With no prompt up it refuses — start the app's sign-in first
  • mobium hit-test <target> — on an iOS simulator, whether UIKit would give a tap there to the target, and if not, to what; the one way to see an overlay hidden from accessibility. Takes about three seconds: use it when a tap "worked" and the app disagrees
  • mobium background 5 — send the app in front away for 5 seconds and bring it back where it was: how a resume path is tested
  • mobium launch <package|bundle-id> — open an app
  • mobium terminate <package|bundle-id> — stop it, to start a flow clean
  • mobium open <url> — a URL or deep link, the quickest route to a screen
  • mobium install <path> — install a local .apk or .app
  • mobium apps — what is installed; --system includes the platform's own
  • mobium uninstall <package|bundle-id> — remove one, verified by listing afterwards, because adb uninstall reports success when it has only removed the updates to a system app

Refs do not survive these. Launching, terminating or opening a link discards the previous screen's refs; run mobium map afterwards.

Use mobium current to check a tap went where you expected:

mobium tap @e5 && mobium current

When something fails for no good reason

mobium doctor checks the environment and needs no device — that is the point. Several of the toolchain's own errors name the wrong cause, so run it before believing one of them. It exits non-zero if it found anything.

Discovery

  • mobium map — actionable elements with @refs (run this before interacting)
  • mobium find <locator> — matching elements, without tapping
  • mobium text — everything readable on screen
  • mobium text @e3 — the text of one element

Waiting

Never sleep. Every action already waits for its target — up to two seconds to be there and enabled, up to five to hold still — so a tap straight after a launch or another tap is fine. When something takes longer than that — a login, a network fetch — wait for the thing itself rather than for a duration:

  • mobium wait "text=Welcome back" — until it is on screen
  • mobium wait 'text="Wi-Fi"' — quoted: the whole text, so a part of it on the screen you are leaving cannot satisfy the wait
  • mobium tap 'label=More within text=Card 8' — the More in Card 8's row; a row without one finds nothing, never its neighbor's
  • mobium tap 'label=Delete,nth=3' — the third Delete shown, in reading order; brittle, so prefer within
  • mobium wait role=progressbar --for hidden — until a spinner goes
  • mobium wait @e4 --for text --text "Sent" — until its text says so
  • mobium wait testid=submit --for enabled — until a control can be used (--for disabled for the other way)
  • mobium wait testid=terms --for checked — a checkbox, radio or switch (--for unchecked for the other way)
  • mobium wait testid=search --for value --text "" — until a field holds exactly that (here: nothing); --for text matches part of what it says
  • mobium wait testid=email --for focused — until the field has the cursor
  • mobium wait "text=Done" --timeout 30s — default is 10s

A wait that succeeds remaps the screen, so the element it found already has a ref and can be tapped without a mobium map in between. A wait that fails says what was on screen instead, which is usually the answer.

Reaching things below the fold

mobium map only sees what is currently on screen. Tap, type and long-press already scroll to a target that is not, so most of the time nothing extra is needed — reach for scroll-to to look without acting, or to go back up:

  • mobium scroll-to "text=Sign out" — bring it into view and get a ref
  • mobium scroll-to "text=Network & internet" --direction up

For a horizontal pager, carousel or row of chips, give --direction left or right. If a scroll reports that the swipe changed the screen instead of scrolling it, the thing being swiped was not a list.

Permissions

A first-launch permission dialog will stop a flow dead. Grant before you start rather than trying to tap through it:

  • mobium grant com.example.shop all — everything the app declares
  • mobium grant com.example.shop camera location — just these
  • mobium revoke com.example.shop location — test the app without it
  • mobium reset-permissions com.example.shop — that app back to prompting, "don't ask again" included; with no app, every app on the device

Names are the same on both platforms where both have the thing. Where they do not — camera and notifications have no iOS simulator equivalent, reminders and siri have no Android one — you get an error saying so rather than a silent no-op. On Android the grant is verified by reading the state back, and anything the app never declared is reported as skipped.

Battery and clock

  • mobium battery — level, charging state, and on Android what powers it; an iOS simulator has no battery and says so
  • mobium time — the device's clock in its own zone; a simulator's is the Mac's, and the answer says so

Tests

mobium test runs *.test.json files: each test is a list of the same steps app_batch takes — {"name": "app_tap", "arguments": {...}} — and its assertions are app_wait_for steps (not, exact and count included) or {"expect": {"tool": ..., "field": ..., "equals": ...}} on a tool that only reads. Projects in mobium.config.json are devices — or, with MOBIUM_GRID set, a platform each, and every project leases its own device from the grid for the run. -g, --project, --retries, --last-failed, --reporter list,junit,html, then mobium show-report. A failure keeps the step, its error code, a screenshot and the map. tests/ in the repository is a worked example.

Files

mobium upload invoice.pdf                         # where a file picker looks
mobium download report.csv                        # what the app saved there
mobium upload ./seed --device-path /data/local/tmp/seed            # Android, any shell path
mobium download --app com.example.shop --device-path databases/shop.db   # its private data
mobium download --app com.example.Shop --device-path Library/Caches -o caches   # iOS container
  • Without --device-path a file goes where the device keeps downloads, and on Android it is indexed so a file picker finds it. With it, a file or a whole folder goes to that exact path.
  • An Android app's private data needs a debuggable build. A release build is refused, saying so — Android lets only the app, or run-as on a debuggable build, read it. Do not retry; use shared storage or a debug build.
  • A folder downloads to a new folder, never into one that exists. Every file's size is read back at the far end; trust the answer, not the exit code alone.

Network

  • mobium network — what is in place: online or offline, and any shaping
  • mobium network --offline / --online — airplane mode, waited for until the network is really gone or back; an emulator or a real Android phone
  • mobium network --latency 300 --download 1600 --upload 750 — shape the traffic, replacing any shaping before; an emulator only (it needs root)
  • mobium network --reset — no shaping, airplane mode off. The end of the session does this for you, back to how it found the device
  • iOS refuses all of it: nothing outside it controls its network

Light and dark

Dark mode is a different rendering of every screen and is where contrast and hard-coded colors break, so a flow is worth running in both:

  • mobium appearance — what is it now?
  • mobium appearance dark / light — switch, confirmed by reading it back
  • mobium appearance auto — Android only; iOS says it has no such thing

Switching discards the refs from the last map, so map again afterwards.

Accessibility settings are the same kind of rendering change, and each lasts only for the session — the device is put back exactly as it was when it ends:

  • mobium accessibility — every setting the device has, as it is now
  • mobium accessibility bold_text on — reduce_motion, bold_text, increase_contrast, invert_colors, grayscale and the rest take on or off
  • mobium accessibility text_size accessibility-large (iOS) / text_scale 1.3 (Android) — text size is a category on one and a scale on the other, and each says so if given the other's

On a real iPhone, where nothing outside can change them, Mobium goes through the Settings app and comes back to the app that was in front — about ten seconds a change. The six switches work there; invert_colors, grayscale and text size are refused, naming the Settings screen. On Android a text-size change restarts the running app's screen, so map again afterwards.

Acting

Every action waits until its target is on screen and enabled (up to two seconds) and holding still (up to five), and refuses — with a reason and a remedy — rather than tapping something a user could not: a target under a system dialog or under the keyboard, a control that stays disabled, or a button passed to type.

  • mobium tap @e5 — tap a ref
  • mobium tap "text=Sign In" — tap by locator, no map needed
  • mobium tap 540 1200 — tap raw device coordinates
  • mobium type @e2 "hello@example.com" — type into an element, after what it holds
  • mobium type @e2 "" — clear a field
  • mobium fill @e2 "new" — replace the contents (Vibium's fill; type adds to them)
  • mobium swipe up — scroll down the page (the finger moves up)
  • mobium swipe left — next pager screen
  • mobium swipe 540 1800 540 600 — exact drag
  • mobium long-press @e4 — open a context menu
  • mobium screenshot -o shot.png — capture the screen

Several steps in one call

  • mobium batch steps.json (or - for stdin) — run a known sequence in one call: [{"name": "app_tap", "arguments": {"target": "text=Sign in"}}, ...], each step a tool and the arguments it takes on its own

Every step is checked before the first runs, and the batch stops at the first failure with that step's own error and exit status. Use it for steps whose outcome you do not need to read before choosing the next; after an app_map step, prefer locators to refs, since the map replaces them.

Locators

Locators work the same on both platforms, so one script targets either:

LocatorAndroidiOS
text=Sign Inelement textelement value
label=Emailcontent-descaccessibility label
testid=submitresource-idaccessibility identifier
role=buttonwidget classXCUIElementType
class=RecyclerViewexact classexact type

Roles: button, input, password, checkbox, switch, radio, link, image, list, tab, text.

Add ,role=button to narrow an ambiguous locator: text=Continue,role=button.

role=password is how you address a password field. Mobium never prints its contents — map shows the field's resource id and reads come back as •••••••••••• (12 characters, hidden: …) — so there is no visible text to match on. password is a narrower input: role=input matches these too.

Hybrid apps (WebViews)

Web content inside an app is a separate context:

mobium contexts                      # NATIVE_APP, WEBVIEW_com.example
mobium context WEBVIEW_com.example   # map and tap now work on the page
mobium context NATIVE_APP            # back to the native shell

This works on both platforms. Android WebViews must be debuggable (setWebContentsDebuggingEnabled(true)) and iOS WKWebViews must be inspectable (webView.isInspectable = true on iOS 16.4+). If a WebView does not appear in contexts, that is a property of the app — do not keep retrying.

mobium logs                  # what has the page logged since the last read?
mobium logs --level error    # uncaught errors and rejections
mobium eval "document.title" # ask the page directly

logs drains what it returns, so it reports what happened since the last call — which is how you assert "nothing was logged during this step". Capture starts when you enter the context, so a page's initial load is already over.

Two things that look like bugs and are not:

  • map may say "these elements cannot be tapped". The page's position inside its host element is unknowable there — mobile Safari is the usual cause. The refs, labels and mobium text are still correct; only acting on a coordinate is refused. Read the page, act natively, or use a deep link.
  • "the page never announced a target" means another debugger already holds that page. Ask the user to close Safari's Web Inspector, or run mobium daemon stop if a previous session is still switched into it.

Switch back to NATIVE_APP when finished with a page. Leaving a session inside one keeps it locked away from everything else.

Device state

mobium orientation                  # which way, and is it pinned?
mobium orientation landscape        # turn it and pin it
mobium orientation auto             # follow the sensor again
mobium locale org.wikipedia ja-JP   # run one app in Japanese
mobium locale org.wikipedia device  # back to the device language

Both invalidate the refs from the last map — a rotation re-lays out every screen and a language change re-renders it, so map again before acting.

Two honest limits worth knowing, because neither platform will tell you:

  • An activity that pins its own orientation cannot be turned from outside. Mobium reports that rather than claiming success.
  • Setting a language confirms the device stored the tag, not that the app speaks it. Android accepts a tag the app has no translation for and the app then renders in its default language. Look at the screen.

Relaunch an app after changing its language, or it keeps the old one.

Interruptions and hardware buttons

mobium press back                  # primary navigation on Android; says where it went
mobium press back --gesture        # the swipe in from the edge; the only back on iOS
mobium press home                  # says the launcher came forward
mobium lock lock                   # a state, not a toggle
mobium call ring                   # then accept, or hang
mobium sms "your code is 123456"
mobium timezone Asia/Tokyo
mobium notifications               # what is in the shade?
mobium notifications --post "your code is 123456"
mobium notifications --shade open  # now the notification can be tapped

Every one of these can move the screen, so map again before acting.

  • iOS has no back button. press back is refused; tap the app's own back chevron, or ask for the swipe with press back --gesture, which starts in the navigation bar because a list row's swipe actions would take it anywhere else. An app with no navigation stack has no swipe back at all.
  • On Android --gesture is refused with three-button navigation — an edge swipe is not back there. mobium devices names the mode.
  • Calls and messages are emulator-only. A real phone cannot be made to ring from outside. Mobium says which it is rather than failing obscurely.
  • A notification cannot be tapped until the shade is open. Until then it is not on screen and map cannot see it: notifications --shade open, then map, then tap it. Close the shade when finished, or every later locator fails against a panel covering the app.
  • Back says what it did. "it left com.example.shop" means back closed the app — usually a bug when the app had a screen to go back to; "still in the foreground" means it went somewhere inside it, and on iOS the navigation bar's new title is given. Map again to see which screen.

Dialogs

  • mobium alert — is a system or app dialog up, what does it say, and what are its buttons
  • mobium alert accept / dismiss — yes and no, on a dialog of one or two buttons: a two-button permission prompt is granted by accept and denied by dismiss, in any language. A dialog of three or more is refused, naming its buttons — the location prompt has two that grant — and every answer says which button it pressed.
  • mobium alert --button "Allow While Using App" — press one by its caption (any case, straight or curly quotes) or its position from 1, on any dialog.
  • mobium dialogs --when "Save Password" --press "Not Now" — declare an answer for a dialog that turns up on its own schedule. An action that meets it presses that button, confirms the dialog went, carries on, and says so in its result. mobium dialogs lists the rules, --clear removes them.

Choosing a device or backend

  • --device <serial> — when more than one device is running
  • --driver uiautomator — Android without installing anything (slower, and it cannot type)
  • --driver wda — iOS simulators and iPhones
  • --driver <anything else> — a third-party driver the user installed, as an executable named mobium-driver-<name>. mobium doctor lists the ones it can find. Such a driver may not support everything: if it refuses something, it never advertised that capability and retrying will not help.

Reading map output

@e1 Search (input)
@e2 Sign In (button)
@e3 Remember me (checkbox)

Each line is @ref label (role). Pick by the label, act with the ref.

Prefer the @ref to retyping the label. Many labels are composed from several elements — a search result's title plus its description, say — so no single element carries that text and text=<the whole label> will not match it. A label ending in … has been shortened for readability and will not match either. The ref always works; it is printed for that reason.

map lists only what is actionable and on screen. Something a locator can resolve may still be absent from the map — scrolled out of its container, or reported by the platform as not visible. That is why an element you can see in a screenshot but not in map is usually a scroll away, not a bug.

Command Chaining

Chain with && when the steps are independent of each other's output:

mobium map && mobium tap @e2 && mobium map

Do not chain when you need to read the output to decide what to do next — run those separately so you can look at the map before choosing a ref.

When things fail

Mobium's errors say what to do; read them rather than retrying blindly.

Every failure also carries a code, so you can act on the kind without parsing the sentence: --json prints it ("code": "no_such_element", with a remedy and details), and the exit status groups it:

ExitCodesWhat to do
2invalid_argumentfix the command; retrying it unchanged cannot help
3no_device, device_not_ready, toolchain_missingask the user: start, unlock or restart a device, install a tool
4no_such_element, ambiguous_locator, element_not_reachable, no_such_context, no_such_alertre-map, narrow the locator, or scroll; with check: receivesEvents, deal with what details names over the target — answer the dialog, hide the keyboard — never restart the device for it
5unsupportedthis device or backend cannot; do not retry
6timeoutthe one kind worth retrying as it is
7not_confirmedthe command claimed success and the device disagrees — report it, it is a finding
1anything elseread the message
  • no element matches … — the screen may have changed, run map again The screen moved. Re-map and pick a fresh ref.
  • … matches N elements — narrow it by appending ",role=button" … Ambiguous locator. Add a role or map and use a ref.
  • the uiautomator backend cannot type into an element Drop --driver uiautomator; the default backend can type.
  • the <name> backend cannot … That backend never advertised the capability. Switch backends; do not retry.
  • a dialog is over the app — "…" — and nothing on it matches … Answer the dialog first (mobium alert, or tap its button), or declare a rule with mobium dialogs.
  • … failed check <check>: <reason> — <what to do> The target is there and an action refused to touch it. The check is in the error's details too; decide by it, not by the wording:
    • visible — off screen: mobium scroll-to it.
    • enabled — disabled: do what enables it, or mobium wait <target> --for enabled.
    • stable — still moving: wait, or tap by coordinates if it animates on purpose.
    • receivesEvents — something is over it: a dialog (answer it), the keyboard (mobium keyboard --hide, or on an iPhone mobium keyboard --key enter), or the app's own control (dismiss it, wait for it to go, or tap it if it is what you meant).
    • editable — not a text field: use tap for it; type is for fields.
  • no Android device or emulator is running Ask the user to start one. Mobium cannot.
  • … matches N elements of the page / no element of the page … matches Inside a WebView a locator matches what map reads of the page: text and label are an element's accessible name, testid its data-testid or id, role its role. Narrow it with ,role=, quotes, ,nth=, or a CSS selector: mobium tap 'css=button.save', with a frame's path before it as map prints one (iframe#f >> button).

Verifying your work

A command reporting success is not evidence the app did anything. After an action that should change the screen, confirm it:

mobium tap @e5 && mobium map        # did the screen change as expected?
mobium screenshot -o after.png      # look at it when the map is ambiguous

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.