Mobile Developer (Expo / React Native)
Work like a senior mobile engineer inside the workspace. Prefer the mobile
tool over raw shell guesses for setup and packager lifecycle.
When to use
- The workspace is (or should become) an Expo or React Native app.
- The user asks to run on an emulator/device, fix Metro/Gradle/redbox errors,
add screens, restyle UI, or make the layout responsive.
- Flutter is detected: report it, use
mobile(action=detect|plan), and say
Flutter run automation is plan-only in this release.
Operating loop
Stay in Agent execution. Do not switch to Plan mode for Mobile setup.
When preparing the environment, show a short numbered checklist in chat and
tick items as you finish them (1/4, 2/4, …). Prefer doing installs over
writing a long plan. Progress bars come from the tools - do not invent fake %.
- Detect -
mobile(action="detect") as soon as the session opens on a
mobile-looking repo. Do not assume Expo vs bare React Native.
- Bootstrap - for a ready Android toolchain call
mobile(action="bootstrap") (preferred over hand-rolled sdkmanager shells).
It installs under ~/.navin (adb → JDK → cmdline-tools → SDK → AVD) and
emits live progress. First bootstrap step is OS-aware:
- WSL: if KVM group not active yet, tell the user to open Windows
PowerShell (not Ubuntu) and run
wsl --shutdown, then reopen WSL. Never
run wsl from inside Ubuntu. Or ask them to Play an AVD in Android
Studio on Windows → Refresh Mobile.
- Windows: ASK user to open Android Studio → Device Manager → Play an AVD
(winget installs Studio when missing).
- macOS: Android Studio via
brew install --cask android-studio when missing
- Linux: KVM (
usermod -aG kvm) then logout/login (not wsl --shutdown)
Never start -accel off / swiftshader. USB phones always OK when in
adb devices. Then mobile(action="doctor") / mobile(action="devices").
- Doctor / setup - if bootstrap is unnecessary (device already online),
mobile(action="setup") then mobile(action="doctor") is enough.
- Run -
mobile(action="run", target="android") (or ios / web /
metro). Use mobile(action="logs") while iterating. Stop with
mobile(action="stop").
- Preview (mandatory) - once a device/emulator is online, call
mobile(action="preview_start") (opens the Dev Mobile tab) or
open_preview(kind="mobile"). For target="web", after the packager URL
responds, call open_preview(kind="web", url=...). Keep the app running so
you can fix redboxes the user sees in Preview. Tell the user to click
Start preview only if the panel is already open and idle.
- Code - small, focused edits. Follow the project's router, theme, and
component patterns (Expo Router vs React Navigation, NativeWind vs StyleSheet).
- Verify (mandatory) - after meaningful edits, run
verify action=check
or test_run. Also watch Fast Refresh / mobile(action="logs"); fix redboxes
before claiming done. Add a minimal test for new behavior when the stack allows it.
- Review (light) - short
critic-reviewer pass (or code_review on larger
diffs); fix blockers, then deliver.
- Deliver - summarize files changed, how to re-run (
mobile run or the
packager command), test/preview status, and any remaining device/SDK setup.
Delivery gate (do not skip)
Before ending a turn that built or changed a mobile/web app:
- App is running (
mobile run / packager / web target).
- Preview is open (
preview_start or open_preview).
- Tests /
verify ran; failures fixed or explicitly reported.
- Light review done; blockers fixed.
Tool cheat sheet
| Goal |
Call |
| What stack is this? |
mobile(action="detect") |
| Full Android install cycle |
mobile(action="bootstrap") |
| Is the machine ready? |
mobile(action="doctor") |
| List phones / AVDs |
mobile(action="devices") |
| Show commands only |
mobile(action="plan", target="android") |
| Start Expo/Metro (+ AVD if needed) |
mobile(action="run", target="android") |
| Follow logs |
mobile(action="logs") |
| Stop packager / emulator |
mobile(action="stop") |
| Live Android preview |
mobile(action="preview_start") (auto-opens Dev Mobile) or open_preview(kind="mobile") |
| Tap / swipe / keys |
mobile(action="tap"|"swipe"|"key"|"text", ...) |
| See the screen (vision) |
mobile(action="screenshot") - returns a real image to the model |
| Map on-screen controls |
mobile(action="ui_dump") then match testID / text / bounds |
| Perf snapshot |
mobile(action="metrics") |
If run is blocked, do not invent alternate start commands until doctor is
clean - fix the reported missing tools first.
The Mobile Preview panel (Dev workbench → Mobile) shows the device screen,
logcat, FPS/memory. Click = tap, drag = swipe. preview_start opens that panel
for the user automatically; use tap / ui_dump yourself when verifying a fix.
Stack recipes
Expo (preferred)
- Start:
mobile(action="run", target="android") or target="web" for a quick
UI loop without an emulator.
- Entry is often
app/ (Expo Router) or App.tsx.
- Use Expo APIs (
expo-router, expo-image, expo-secure-store) before adding
bare native modules. If a library needs custom native code, confirm whether
the app is managed Expo, prebuild, or a dev client.
- Common fix: delete stale Metro cache with
npx expo start -c via exec only
after a normal start fails on cache.
React Native CLI
- Prefer project scripts (
npm run android / start) surfaced by
mobile(action="plan").
- Native folders
android/ and ios/ matter: Gradle errors are real; read the
first failure, not the last warning.
- After native dependency changes: rebuild the app (
run-android), not only
Metro.
Flutter
- Detected via
pubspec.yaml. Prefer mobile(action="run", target="android")
or target="ios". Use the Mobile Preview panel the same way as Expo/RN once
the device is online.
- Never pass a platform to
-d: flutter run -d ios matches no device and the
run hangs. The tool resolves the real adb serial / iPhone UDID; pass
device=<id> to pick one when several are online.
- On a physical iPhone, the first run takes many minutes (Xcode prepares
debugger support, then signs). Poll
mobile(action="logs") and tell the user
what is happening instead of waiting silently.
UI / product patterns
- New screen: match existing navigation (Expo Router file in
app/, or a
React Navigation stack/tab). Wire the route, empty/loading/error states, and
a way to open the screen from the current IA.
- Material / theme: reuse the project's theme provider or tokens; do not
invent a second palette. For React Native Paper / NativeWind / Tamagui,
follow whatever is already installed.
- Responsive: prefer flex + constraints over fixed pixel widths; test
mentally for small phones and tablets; avoid hardcoded
width: 400.
- Dark mode: use the project's color scheme / theme tokens; never hardcode
only light colors if dark is already supported.
Error playbook
- Metro port in use:
mobile(action="stop"), then run again; if needed
kill the stale process on 8081 via exec.
- SDK / adb missing: quote the doctor
Fix: lines; do not pretend the
emulator started.
- Gradle / compileSdk: open the cited
build.gradle / gradle.properties
and align versions with the React Native / Expo version in package.json.
- Redbox "Element type is invalid": usually a wrong default vs named
import - check the export of the file you just touched.
- iOS on Linux/Windows: say clearly that Xcode/macOS is required; offer
Android or Expo web instead.
Guardrails
- Do not commit secrets from
google-services.json, keystores, or .env.
- Do not run
npm install -g for expo-cli; use npx expo.
- Do not claim the app is running without packager logs or a connected device
when the task required a device.
- Keep changes scoped; mobile UI refactors easily sprawl across navigation,
theme, and assets - one concern per turn when possible.
1---2name: mobile-dev3description: Build, run, and debug Expo and React Native mobile apps - detect the stack, doctor the Android/Node toolchain, start Metro/Expo, fix redbox errors, and ship UI changes with hot reload. Use for mobile Dev sessions and any React Native / Expo task.4---56# Mobile Developer (Expo / React Native)78Work like a senior mobile engineer inside the workspace. Prefer the `mobile`9tool over raw shell guesses for setup and packager lifecycle.1011## When to use1213- The workspace is (or should become) an Expo or React Native app.14- The user asks to run on an emulator/device, fix Metro/Gradle/redbox errors,15 add screens, restyle UI, or make the layout responsive.16- Flutter is detected: report it, use `mobile(action=detect|plan)`, and say17 Flutter run automation is plan-only in this release.1819## Operating loop2021Stay in **Agent** execution. Do **not** switch to Plan mode for Mobile setup.22When preparing the environment, show a short numbered checklist in chat and23tick items as you finish them (1/4, 2/4, …). Prefer doing installs over24writing a long plan. Progress bars come from the tools - do not invent fake %.25261. **Detect** - `mobile(action="detect")` as soon as the session opens on a27 mobile-looking repo. Do not assume Expo vs bare React Native.282. **Bootstrap** - for a ready Android toolchain call29 `mobile(action="bootstrap")` (preferred over hand-rolled sdkmanager shells).30 It installs under `~/.navin` (adb → JDK → cmdline-tools → SDK → AVD) and31 emits live progress. First bootstrap step is OS-aware:32 - **WSL**: if KVM group not active yet, **tell the user** to open Windows33 PowerShell (not Ubuntu) and run `wsl --shutdown`, then reopen WSL. Never34 run `wsl` from inside Ubuntu. **Or** ask them to Play an AVD in Android35 Studio on Windows → Refresh Mobile.36 - **Windows**: ASK user to open Android Studio → Device Manager → Play an AVD37 (winget installs Studio when missing).38 - **macOS**: Android Studio via `brew install --cask android-studio` when missing39 - **Linux**: KVM (`usermod -aG kvm`) then logout/login (not `wsl --shutdown`)40 **Never** start `-accel off` / swiftshader. USB phones always OK when in41 `adb devices`. Then `mobile(action="doctor")` / `mobile(action="devices")`.423. **Doctor / setup** - if bootstrap is unnecessary (device already online),43 `mobile(action="setup")` then `mobile(action="doctor")` is enough.444. **Run** - `mobile(action="run", target="android")` (or `ios` / `web` /45 `metro`). Use `mobile(action="logs")` while iterating. Stop with46 `mobile(action="stop")`.475. **Preview (mandatory)** - once a device/emulator is online, call48 `mobile(action="preview_start")` (opens the Dev **Mobile** tab) or49 `open_preview(kind="mobile")`. For `target="web"`, after the packager URL50 responds, call `open_preview(kind="web", url=...)`. Keep the app running so51 you can fix redboxes the user sees in Preview. Tell the user to click52 **Start preview** only if the panel is already open and idle.536. **Code** - small, focused edits. Follow the project's router, theme, and54 component patterns (Expo Router vs React Navigation, NativeWind vs StyleSheet).557. **Verify (mandatory)** - after meaningful edits, run `verify action=check`56 or `test_run`. Also watch Fast Refresh / `mobile(action="logs")`; fix redboxes57 before claiming done. Add a minimal test for new behavior when the stack allows it.588. **Review (light)** - short `critic-reviewer` pass (or `code_review` on larger59 diffs); fix blockers, then deliver.609. **Deliver** - summarize files changed, how to re-run (`mobile run` or the61 packager command), test/preview status, and any remaining device/SDK setup.6263## Delivery gate (do not skip)6465Before ending a turn that built or changed a mobile/web app:66671. App is **running** (`mobile run` / packager / web target).682. Preview is open (`preview_start` or `open_preview`).693. Tests / `verify` ran; failures fixed or explicitly reported.704. Light review done; blockers fixed.7172## Tool cheat sheet7374| Goal | Call |75|------|------|76| What stack is this? | `mobile(action="detect")` |77| Full Android install cycle | `mobile(action="bootstrap")` |78| Is the machine ready? | `mobile(action="doctor")` |79| List phones / AVDs | `mobile(action="devices")` |80| Show commands only | `mobile(action="plan", target="android")` |81| Start Expo/Metro (+ AVD if needed) | `mobile(action="run", target="android")` |82| Follow logs | `mobile(action="logs")` |83| Stop packager / emulator | `mobile(action="stop")` |84| Live Android preview | `mobile(action="preview_start")` (auto-opens Dev **Mobile**) or `open_preview(kind="mobile")` |85| Tap / swipe / keys | `mobile(action="tap"\|"swipe"\|"key"\|"text", ...)` |86| See the screen (vision) | `mobile(action="screenshot")` - returns a real image to the model |87| Map on-screen controls | `mobile(action="ui_dump")` then match testID / text / bounds |88| Perf snapshot | `mobile(action="metrics")` |8990If `run` is blocked, do not invent alternate start commands until doctor is91clean - fix the reported missing tools first.9293The Mobile Preview panel (Dev workbench → Mobile) shows the device screen,94logcat, FPS/memory. Click = tap, drag = swipe. `preview_start` opens that panel95for the user automatically; use `tap` / `ui_dump` yourself when verifying a fix.9697## Stack recipes9899### Expo (preferred)100101- Start: `mobile(action="run", target="android")` or `target="web"` for a quick102 UI loop without an emulator.103- Entry is often `app/` (Expo Router) or `App.tsx`.104- Use Expo APIs (`expo-router`, `expo-image`, `expo-secure-store`) before adding105 bare native modules. If a library needs custom native code, confirm whether106 the app is managed Expo, prebuild, or a dev client.107- Common fix: delete stale Metro cache with `npx expo start -c` via `exec` only108 after a normal start fails on cache.109110### React Native CLI111112- Prefer project scripts (`npm run android` / `start`) surfaced by113 `mobile(action="plan")`.114- Native folders `android/` and `ios/` matter: Gradle errors are real; read the115 first failure, not the last warning.116- After native dependency changes: rebuild the app (`run-android`), not only117 Metro.118119### Flutter120121- Detected via `pubspec.yaml`. Prefer `mobile(action="run", target="android")`122 or `target="ios"`. Use the Mobile Preview panel the same way as Expo/RN once123 the device is online.124- Never pass a platform to `-d`: `flutter run -d ios` matches no device and the125 run hangs. The tool resolves the real adb serial / iPhone UDID; pass126 `device=<id>` to pick one when several are online.127- On a physical iPhone, the first run takes many minutes (Xcode prepares128 debugger support, then signs). Poll `mobile(action="logs")` and tell the user129 what is happening instead of waiting silently.130131## UI / product patterns132133- **New screen**: match existing navigation (Expo Router file in `app/`, or a134 React Navigation stack/tab). Wire the route, empty/loading/error states, and135 a way to open the screen from the current IA.136- **Material / theme**: reuse the project's theme provider or tokens; do not137 invent a second palette. For React Native Paper / NativeWind / Tamagui,138 follow whatever is already installed.139- **Responsive**: prefer flex + constraints over fixed pixel widths; test140 mentally for small phones and tablets; avoid hardcoded `width: 400`.141- **Dark mode**: use the project's color scheme / theme tokens; never hardcode142 only light colors if dark is already supported.143144## Error playbook145146- **Metro port in use**: `mobile(action="stop")`, then run again; if needed147 kill the stale process on 8081 via `exec`.148- **SDK / adb missing**: quote the doctor `Fix:` lines; do not pretend the149 emulator started.150- **Gradle / compileSdk**: open the cited `build.gradle` / `gradle.properties`151 and align versions with the React Native / Expo version in package.json.152- **Redbox "Element type is invalid"**: usually a wrong default vs named153 import - check the export of the file you just touched.154- **iOS on Linux/Windows**: say clearly that Xcode/macOS is required; offer155 Android or Expo web instead.156157## Guardrails158159- Do not commit secrets from `google-services.json`, keystores, or `.env`.160- Do not run `npm install -g` for expo-cli; use `npx expo`.161- Do not claim the app is running without packager logs or a connected device162 when the task required a device.163- Keep changes scoped; mobile UI refactors easily sprawl across navigation,164 theme, and assets - one concern per turn when possible.