Skip to main content

How it works

1. Read project identity (app.json / app.config.ts → name, scheme, bundle ID)
2. Load the shared registry (~/.simgrid/state.json) and reconcile it against
reality — sessions whose Metro process is no longer alive are dropped
3. Discover all simulators / emulators / physical devices
4. Annotate each device: dev build installed? busy with another project?
5. Show the picker (pre-checked = your last choice for this project)
6. For each selected device:
a. Boot / clone if needed
b. Allocate a free Metro port (one per project, reused across devices)
c. Start Metro (expo start --port N) — or reuse the running one
d. Deep-link the dev client (or expo run:<platform> if no build yet)
7. Register the session; deregister cleanly on Ctrl-C or Metro exit

One Metro per project

Every device you select for a project connects to the same Metro instance. simgrid allocates the first free port from 8081 upward and reuses it when you re-run the project.

The shared registry

~/.simgrid/state.json tracks which project runs on which device, the Metro port, and the process PID. Every run reconciles the registry by checking live PIDs before showing the picker, so sessions whose process has exited are dropped automatically. This is how a kill -9 self-heals and how cross-project 🔴 busy tags stay accurate. No daemon required.

Managed builds

If a device has no dev build, simgrid asks once how to build (a package.json script that runs expo run:<platform>, the npx expo run default, or a custom command), remembers the answer per project + platform, and rebuilds automatically next time. A build that fails forgets the command, so you're asked again rather than retrying a broken one.