Share Flutter devices, emulators and heavy builds between Claude Code sessions with anti-stuck-flutter. Use before running a Flutter app on a phone or emulator, running integration tests, installing with adb, booting an emulator, running a release build, or connecting Marionette to a running app, and whenever anti-stuck refuses a flutter/adb/emulator command and points to anti-stuck-flutter.
Installs into .claude/skills of the current project.
Are you the author of Device Ledger?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/toyaji-device-ledger)
---
name: device-ledger
description: Share Flutter devices, emulators and heavy builds between Claude Code sessions with anti-stuck-flutter. Use before running a Flutter app on a phone or emulator, running integration tests, installing with adb, booting an emulator, running a release build, or connecting Marionette to a running app, and whenever anti-stuck refuses a flutter/adb/emulator command and points to anti-stuck-flutter.
---
# anti-stuck-flutter — the shared device and build ledger
Several Claude Code sessions on one machine can use the same phone or emulator. anti-stuck-flutter keeps one ledger so a session
never kills another session's app, never builds over another's build, and hands devices over in order.
`flutter run`, `adb install`, `emulator -avd` and similar commands are refused unless they go through it.
The command is:
```
"${CLAUDE_PLUGIN_ROOT}/scripts/anti-stuck-flutter"
```
| Task | Command |
|---|---|
| See who uses what (do this first) | `"${CLAUDE_PLUGIN_ROOT}/scripts/anti-stuck-flutter" status` |
| Start or reuse the app on a device | `"${CLAUDE_PLUGIN_ROOT}/scripts/anti-stuck-flutter" run <device> [flutter run args]` |
| Run a command that needs the device | `"${CLAUDE_PLUGIN_ROOT}/scripts/anti-stuck-flutter" exec <device> -- <command>` |
| Heavy build, one at a time across sessions | `"${CLAUDE_PLUGIN_ROOT}/scripts/anti-stuck-flutter" build -- <command>` |
| Boot an emulator and take it | `"${CLAUDE_PLUGIN_ROOT}/scripts/anti-stuck-flutter" emu <avd>` |
| Wait for a busy device (you are woken when it is free) | `"${CLAUDE_PLUGIN_ROOT}/scripts/anti-stuck-flutter" wait <device> "purpose"` |
| Release a device: with waiters the app keeps running and they are woken; with none the app is stopped and an Android phone's screen turned off | `"${CLAUDE_PLUGIN_ROOT}/scripts/anti-stuck-flutter" release <device>` |
| Stop the app (only your own, only if nobody waits) | `"${CLAUDE_PLUGIN_ROOT}/scripts/anti-stuck-flutter" stop <device>` |
Rules:
1. Run `status` before touching any device.
2. If another session owns the device, `wait` — do not stop its app or start a second one. After `wait` you can keep
working on something else or stop: a hook wakes this session the moment the device is free.
3. When woken, take the device **only if you will use it right now**. Every waiting session is woken at once and the
first to take it gets it, so a session that grabs it "for later" blocks one that is waiting with nothing else to do.
Being woken takes you off the waiters list, so if you lose the race, `wait` again. Reuse the running app through the Marionette address in the wake message instead
of rebuilding.
4. `run`, `emu`, `exec` and `build` can be slow: start them with `# eta:<seconds> <what>` on the first line and tell the
user the expected time.
5. Release a device as soon as your work on it is done. If nobody waits, `release` stops the app and turns an Android
phone's screen off. Pausing mid-work for something else? Check `status` and release only if someone is waiting —
they get the running app. A session that merely goes idle releases its devices automatically but keeps apps running, so its
next turn can reuse them; a device left free with nobody waiting is cleaned up the same way after 15 minutes.
6. Hot reload and hot restart go through the Marionette MCP `hot_reload` / `hot_restart` tools, connected to the
address `status` shows. `flutter run` does not read keys from a pipe.