device-loop
Skill4 starsChanged 17 days agoMITView on GitHub
Bring up an Android emulator without Android Studio and without sudo, and validate a React Native or Expo app on it, seeing the real screen with screencap and iterating with Expo Go or a development build. Use when the native app has to be genuinely tested, when a new machine needs the emulator set up, to validate surfaces the browser does not reproduce (notifications, widgets, GL maps, dark mode) or to reproduce a bug that only happens in the built app.
In this repository46 other files it gives its agents.
- AGENTS.mdAGENTS.md
- add-repo-to-docsSkill
- adrSkill
- auth-bypass-localSkill
- before-afterSkill
- bug-huntSkill
- carry-envSkill
- code-healthSkill
- crux-doctorSkill
- crux-initSkill
- css-to-componentsSkill
- deploy-fallbackSkill
- deploy-watchSkill
- drive-browserSkill
- e2eSkill
- end-sessionSkill
- eval-fixturesSkill
- explain-planSkill
- flowSkill
- grillSkill
- merge-conflictsSkill
- motion-videoSkill
- oauth-reauthorizeSkill
- prod-signalsSkill
- promote-to-docsSkill
- prose-checkSkill
- read-documentSkill
- render-loopSkill
- repo-mapSkill
- review-prSkill
- root-causeSkill
- runbookSkill
- security-auditSkill
- share-localSkill
- shipSkill
- skill-auditSkill
- skill-creatorSkill
- teachSkill
- ui-behaviourSkill
- ui-tellsSkill
- visual-diffSkill
- visual-testSkill
- wizardSkill
- workspaceSkill
- write-testsSkill
- x-postsSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 23
Look first in the project's docs repo: the emulator's name, its image, the package and the deep links are project data.
- Very specific: line 72
If /dev/kvm is not accessible, the user runs sudo usermod -aG kvm $USER in a real terminal and logs in again.
- Very specific: line 86
If it never finishes booting, /tmp/emulator.log says why.
- Distinctive: line 124
If the app is left on "Cannot connect to Expo CLI" after restarting Metro, forward the port and open the URL by hand:
≈ “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.
Files
device-loop
The goal is for the agent to see the real native app and be able to iterate
on it. The route is an Android emulator, headless, driven over adb, with
screencap for the eyes.
It is the native sibling of /visual-test. The why of each step and of each
gotcha is in <this-skill-directory>/references/why.md.
When to use
- The native app has to be tested, not a browser approximation.
- Validating what the browser does not have: notifications, widgets, native maps, dark mode, permissions.
- Setting up the emulator on a machine that does not have it.
- Reproducing a bug that only appears in the built app.
Look first in the project's docs repo: the emulator's name, its image, the package and the deep links are project data.
1. Install it, once per machine
Everything goes under one folder in your home: no Android Studio, no sudo,
no system packages. Around 4 GB to run apps; 7 GB with what compiling needs.
A=~/.local/android; mkdir -p "$A/sdk/cmdline-tools"
# JDK 17, portable
curl -sSL -o /tmp/jdk.tgz \
"https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jdk/hotspot/normal/eclipse"
mkdir -p "$A/jdk" && tar xzf /tmp/jdk.tgz -C "$A/jdk" --strip-components=1
# command-line tools: the current zip is on developer.android.com/studio,
# section "Command line tools only"
curl -sSL -o /tmp/clt.zip "<commandlinetools-linux-…_latest.zip>"
unzip -q /tmp/clt.zip -d /tmp/clt && mv /tmp/clt/cmdline-tools "$A/sdk/cmdline-tools/latest"The environment, in every shell that uses it (or in the shell profile):
export JAVA_HOME=~/.local/android/jdk ANDROID_HOME=~/.local/android/sdk
export PATH="$JAVA_HOME/bin:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH"The pieces and the virtual device. The image and the API level are the project's; these are an example:
yes | sdkmanager --licenses >/dev/null
sdkmanager "platform-tools" "emulator" "platforms;android-35" \
"system-images;android-35;google_apis;x86_64"
avdmanager create avd -n <avd-name> -d pixel_7 \
-k "system-images;android-35;google_apis;x86_64"To compile a development build, add what the project's Gradle asks for (build tools, NDK, CMake): the first build says which versions are missing.
Hardware acceleration
Without it the emulator runs in software and is unusable. Check it:
emulator -accel-check # must say KVM is usableIf /dev/kvm is not accessible, the user runs sudo usermod -aG kvm $USER in
a real terminal and logs in again. The agent cannot: sudo needs a TTY.
2. Start it
emulator -avd <avd-name> -no-window -no-audio -gpu swiftshader_indirect >/tmp/emulator.log 2>&1 &
adb wait-for-device
until [ "$(adb shell getprop sys.boot_completed | tr -d '\r')" = 1 ]; do sleep 2; done
adb shell input keyevent KEYCODE_WAKEUP && adb shell wm dismiss-keyguard
# boot_completed arrives while it still says "starting": wait for the launcher
until adb shell dumpsys window | grep mCurrentFocus | grep -qi launcher; do sleep 2; doneIf it never finishes booting, /tmp/emulator.log says why. To stop it:
adb emu kill.
3. See the screen
adb exec-out screencap -p > <path>/screen.pngAnd then open the PNG to look at it. Do not trust the first capture after launching: wait for the app to paint.
If the capture comes out black, the focus is usually on the notification shade or another window:
adb shell dumpsys window | grep mCurrentFocus
adb shell cmd statusbar collapseUseful states to validate:
adb shell cmd uimode night yes # dark mode (no to go back)
adb shell settings put system font_scale 1.34. Iterate
Expo Go
Enough for most of the interface. Metro on the machine, the app in the emulator:
npx expo start --androidIf the app is left on "Cannot connect to Expo CLI" after restarting Metro, forward the port and open the URL by hand:
adb reverse tcp:<port> tcp:<port>
adb shell am start -a android.intent.action.VIEW -d "exp://127.0.0.1:<port>"Development build
Notifications, widgets and native modules are not in Expo Go: the app is compiled. The usual route with Expo:
npx expo prebuild -p android --no-install
(cd android && ./gradlew assembleDebug -PreactNativeArchitectures=x86_64)
adb install -r android/app/build/outputs/apk/debug/app-debug.apkprebuild can rewrite package.json: save a copy before and put it back after,
not with git checkout, which also takes uncommitted dependencies.
The development build looks for Metro on 8081. On another port, forward it
with adb reverse and tell the app with its debug_http_host preference, or
use the deep link of the development client if the project has one.
Validating a specific APK
adb install -r <build>.apk
adb shell monkey -p <package> -c android.intent.category.LAUNCHER 1
adb exec-out screencap -p > shot.png5. Navigating the app
Taps and keys reach the app in the emulator. Coordinates change with the device, so look at the capture before tapping:
adb shell input tap <x> <y>
adb shell input text "<text>"
adb shell input keyevent KEYCODE_BACKFaster and more stable when it exists: open the screen by its deep link.
adb shell am start -a android.intent.action.VIEW -d "<deep-link>"
adb logcat -d -t 200 | grep -iE "ReactNative|Exception|FATAL"To reload code that only runs on mounting, close the app with
adb shell am kill <package> from the home screen and open it again. Not with
am force-stop: the app would stop receiving widget and notification events.
Anti-patterns
- Accepting a change without a screenshot that confirms it. It is the whole point of the skill.
- Installing Android Studio, or system packages, to get an emulator.
- Running the emulator without hardware acceleration. It looks like the app is slow when it is the emulator.
Related
/visual-test— the web sibling, with headless Chrome
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
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.

