mobile-emulator-start — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited mobile-emulator-start (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Deliver a working dev loop (emulator online → tunnel → bundler ready → app intent) without burning time on duplicate Metros, stale caches, or racing the dev client ahead of /status.
Pair with `mobile-emulator-test` for full QA; this skill focuses on bring-up + terminal hygiene.
adb reverse (or reverse again when the serial appears).am start / dev-client deeplink (avoids “Cannot connect to Expo CLI” / 127.0.0.1 refused).--launch / reverse if the emulator booted late — re-run reverse + intent after adb devices shows device.Before spawning duplicate processes, inspect what is already running.
If the workspace exposes a terminals/ folder (often under the project’s .cursor/projects/.../terminals/ or similar), read *`.txt` headers**:
expo start, metro, emulator, gradle?ENOENT, SyntaxError in the app (fix code before restarting blindly).Prefer reusing a healthy Metro session when the user wants speed; only kill + restart when stuck, on wrong port, or after selecting the fresh path (Phase C).
# Metro default
curl -sSf "http://127.0.0.1:8081/status" | head -c 120 || true
adb devicesIf Metro responds and an *`emulator- device** exists, you may only need **adb reverse`** + reload — not a full reboot.
| Goal | Do this | Avoid |
|---|---|---|
| Fast / Hot Reload | Start Metro without wiping node_modules/.cache / .expo every time; reuse Metro if healthy | Chaining clean + full cache wipe before every edit |
| Fresh / “weird Metro” | One explicit cold wipe (repo script or expo start --clear, delete .expo, etc.), then start Metro | Assuming “fresh” without killing the old Metro on the same port |
Heuristic: if the user says “stale bundle / nothing changed / white screen after hours of dev” → lean fresh once, confirm Phase 1.5-style bundle checks from mobile-emulator-test. If they say “just start my session” → prefer fast.
Common: `8081` (Metro), `19000`–`19002` (Expo). Discover from package.json scripts or prior terminal output.
netstat -ano | findstr :8081 → taskkill /PID <pid> /Flsof -i :8081 → kill -9 <pid> (or fuser -k 8081/tcp)Kill orphan `adb.exe` only when transport is stuck (symptom: adb devices hangs / offline spam). Pattern that works but is heavy-handed on Windows: taskkill /IM adb.exe /F /T then adb start-server.
Starting Metro from the wrong `cwd` serves the wrong graph — always cd into the mobile app root the project uses (apps/mobile, packages/app-mobile, etc.) before npx expo start / react-native start.
| Goal | Resolution | Why |
|---|---|---|
| Full-screen screenshot QA (default) | `1080×4000` | Tall virtual display — the user can scroll the emulator window and take a single long screenshot that captures more UI in one shot, making it easier to review without asking for multiple scrolled captures. Use this for all sessions by default. |
| Standard tap / interaction QA | 1080×2400 | Standard phone size. Slightly easier coordinate math for adb shell input tap. Switch to this only if the user explicitly requests it or if the emulator window is too tall for their monitor. |
Default: always start at `1080×4000` for maximum screenshot coverage.
To switch resolution without rebooting the AVD:
adb shell wm size 1080x4000 # default (tall screenshots)
adb shell wm size 1080x2400 # switch to standard if needed
adb shell wm size reset # restore to AVD default
adb shell wm size # verifyScale factor for coordinate math when screenshots are displayed at ~294px wide in chat:
device_x = display_x × (1080 / display_image_width_px)
device_y = display_y × (4000 / display_image_height_px)Example — button at display position (147, 1200) in a 294×1568-px chat image:
device_x = 147 × (1080/294) ≈ 540device_y = 1200 × (4000/1568) ≈ 3061emulator -list-avds
# Then:
emulator -avd "<AVD_NAME>" -netdelay none -netspeed fullUse %LOCALAPPDATA%\Android\Sdk\emulator\emulator.exe on Windows if emulator is not on PATH.
After boot, immediately set the target resolution:
adb shell wm size 1080x2400 # default for tap QAAfter boot:
adb shell wm size
adb shell wm densityExpect `Physical size: 1080x2400` for the default tap-QA preset (or 1080x4000 if scroll QA was requested). If you see 720×1280 or another unintended size, the AVD skin.path is overriding hw.lcd.* — use adb shell wm size 1080x2400 to override at runtime, or apply the minimal custom skin workflow in `mobile-emulator-test` Phase 0.5.
_no_skin failureIf the emulator exits immediately with `unknown skin name '_no_skin'`, remove/replace `skin.name=_no_skin` in the AVD `config.ini` — see `mobile-emulator-test` Phase 0.5b–c.
adb attach + reverseadb start-server
adb wait-for-device
# Wait until boot_completed if scripts are flaky:
adb shell getprop sys.boot_completed # expect 1
METRO_PORT=8081 # or repo-specific
adb reverse tcp:${METRO_PORT} tcp:${METRO_PORT}Re-apply `adb reverse` after new emulator session, `adb kill-server`, or USB reconnect.
From the correct app cwd:
# Expo dev client (common)
npx expo start --dev-client --port 8081
# Bare React Native (example)
npx react-native start --port 8081Run long-lived processes in the background when the agent must continue other work; otherwise the user’s terminal owns the foreground process.
Fresh-start variant (slow, clears Metro’s world — use sparingly):
npx expo start --dev-client --clear --port 8081, or repo script equivalent (dev-fresh, start:clean, etc.).METRO_PORT=8081
# Bash-style loop; cap total wait ~120–180s on cold cache wipes
while ! curl -sSf "http://127.0.0.1:${METRO_PORT}/status" | grep -q "running"; do sleep 3; doneOnly then trigger the app (Phase H). This removes the classic race where the emulator shows Cannot connect / `ECONNREFUSED` even though Metro appears a few seconds later.
Discover `scheme` from app.json / app.config.* (and Android intent-filter if native-linked). Pattern:
<scheme>://expo-development-client/?url=http%3A%2F%2Flocalhost%3A<PORT>adb shell am start -W -a android.intent.action.VIEW \
-d "<scheme>://expo-development-client/?url=http%3A%2F%2Flocalhost%3A8081"adb shell am start -W -n <applicationId>/.<MainActivityName>adb shell input keyevent 82 (menu) and enable Fast Refresh, orcurl -X POST http://localhost:8081/reload when supported.| Symptom | Likely cause | First move |
|---|---|---|
Metro terminal shows EADDRINUSE | Old Metro on same port | Phase C kill port owner; restart one Metro |
Error loading app · unexpected end of stream | Missing adb reverse or Metro not up yet | Phase E + G, then relaunch |
Emulator never in adb devices | AVD skin crash / HW issue | Check emulator stdout; `mobile-emulator-test` 0.5 |
Physical size wrong | Stock skin clobbering hw.lcd | adb shell wm size 1080x2400 overrides at runtime; persistent fix → `mobile-emulator-test` 0.5 |
Cannot connect to Expo CLI / 127.0.0.1:8081 | Deeplink before /status ready | Always Phase G → H |
| Two Metro instances | Two terminals / two repos | Read Phase A headers; kill stray PID |
| Stale JS after fix | Cache / wrong cwd | Fresh path once + expo start --clear |
fs.inotify.max_user_watches (rare; only when Metro logs watcher errors).Many monorepos expose:
node scripts/dev-android.mjs --launch --port 8081If such a script polls `/status` and sets `adb reverse`, prefer it after the emulator is already listed in adb devices. If the script bails early with no device, start the emulator first or manually run Phase E–H once online.
curl /status = runningadb devices shows `device`, not offlineadb reverse tcp:<PORT> tcp:<PORT> executed for that Metro portadb shell wm size matches expected 1080×4000 (default) or 1080×2400 (if user requested standard)/status OK — logcat has no immediate `ECONNREFUSED` to Metro~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.