Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/stim-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ All commands below take the same `npx stim-cli` prefix.
| `android [--json] [--no-metro-check] [--no-build-cache] [--variant <name>]` | The same over `gradlew assembleDebug` and `adb`, on this workspace's owned emulator, with `adb reverse tcp:8081 tcp:<port>` doing the port wiring. On a project with product flavors, `--variant productionDebug` (or the `android.variant` setting, which the flag overrides) runs `assembleProductionDebug`, finds the APK in `apk/production/debug/` and keys the build cache on the variant; the launched applicationId is always read from the built APK's manifest. A variant whose name **ends in `Release`** is a release build -- no second flag: the JS is embedded, so Metro is skipped entirely (`metroPort: null`, no `adb reverse`, no dev-client deep link, a plain `am start`, and `launched` proven by the app process being alive on the device). A release cache hit re-packs the cached APK -- copy aside, regenerate the bundle with the project's own tools + hermesc, `zip -0` it back in (stored, because the runtime mmaps it), zipalign, then `apksigner` with `android/app/debug.keystore` (override via `android.keystore` / `android.keystorePassword`). An **asset gate** compares the freshly emitted assets against the ones the APK carries and falls back to a full build on any difference, and a signer conflict from a CI-signed copy uninstalls the package once and retries. Local emulator installs only. |
| `stop [--force] [--json]` | The inverse of `start`: halt this workspace's supervisor, reap its device-log collectors, shut the owned device **down** (never deleted, so it stays assigned), and free the reserved port. Non-destructive and takes no target -- it acts on the current workspace. With no supervisor recorded it falls back to killing an identity-verified Metro on the reserved port; `--force` is only for an unproven listener there. Already-stopped is a success at every step. |
| `status [--json]` | Show every registered project (machine-wide by default; there is no `--all`): device assignments, Metro state, supervisor pid / mode / health, last build (fingerprint, cache hit, duration), log directory and error count since the last marker, plus machine capacity and free disk on the boot, STIM_CLI_HOME and current-project volumes when distinct. |
| `gc [--delete] [--older-than <days>] [--all]` | Report what stim-cli has left behind: entries for projects whose directory no longer exists, orphaned `stim-cli-*` devices, records naming a device that is no longer on the machine, and every shared build cache with its size. Reports and writes nothing by default; `--delete` reclaims the dead entries (freeing their Metro ports), reaps the orphaned devices, and clears the stale device records (the record only -- there is no device left to touch, so it issues no simctl/avdmanager command). `--older-than <days>` additionally reaps owned devices whose _project_ has gone untouched that long, and trims cache entries nothing has used in that time. `--all` (with `--delete`) empties the caches whole -- see below. |
| `gc [--delete] [--older-than <days>] [--all]` | Report what stim-cli has left behind: entries for projects whose directory no longer exists, orphaned `stim-cli-*` devices, records naming a device that is no longer on the machine, and every shared build cache with its size. Orphaned and stale owned Android AVD rows include their on-disk size when the AVD content directory can be read. Reports and writes nothing by default; `--delete` reclaims the dead entries (freeing their Metro ports), reaps the orphaned devices, and clears the stale device records (the record only -- there is no device left to touch, so it issues no simctl/avdmanager command). `--older-than <days>` additionally reaps owned devices whose _project_ has gone untouched that long, and trims cache entries nothing has used in that time. `--all` (with `--delete`) empties the caches whole -- see below. |
| `doctor [--json]` | Report what stim-cli cannot handle on its own. The ABSENCE of a project-side cache setting is not a finding -- stim-cli supplies the Metro store (bare React Native and Expo SDK 54+), the Xcode compilation cache and the Gradle build cache on its own command lines. What it reports is active misconfiguration: a missing dev client, ccache (which is what stops stim-cli adding its own compilation cache), a `cacheStores` wired behind a conditional so it is off in the case that matters, a compilation CAS left at the per-workspace default, a configured build-cache provider on the key this SDK ignores, an EAS session that cannot answer, and -- last, because it computes a real fingerprint twice via a temporary worktree of HEAD (removed again) -- a checkout that does not fingerprint like a fresh worktree. A clean run means nothing stim-cli cannot handle itself. Read-only, and always exits 0. |
| `worktree create <name> [--base <ref>] [--label <name>] [--carry-ignored]` | Create an isolated git worktree: carries over gitignored files, prints the worktree path (and, on stderr, what it branched from -- ref and short sha). `--base` takes `fresh` (origin/HEAD, the default), `head`, or any ref `git rev-parse` resolves; an unresolvable one is refused before anything is created. Does not install dependencies unless `--carry-ignored` clones the source's working state: its gitignored paths (node_modules, Pods, build output) plus its uncommitted tracked changes, applied when they fit the base and reported when they do not. |
| `worktree remove [<path>] [--force]` | Remove a worktree, reclaiming its global build artifacts, Metro port, and owned devices (deleted, not just freed). Defaults to the current workspace. Refuses if it has uncommitted or unpushed work unless `--force`, naming the right restore command per class (`git checkout --` for modified tracked files, `git clean -fd` for untracked ones). Current stim-cli state never dirties the project. On the main checkout it reclaims the environment only and never touches source files. |
Expand All @@ -203,7 +203,7 @@ All commands below take the same `npx stim-cli` prefix.

- **Config** at `~/.stim-cli/config.json`, keyed by absolute project path. Symlinked worktrees collapse via `realpath`. Every write goes through a lockfile and lands by atomic rename, so several agents provisioning at once cannot lose each other's device records. A config that will not parse is reported by name and never reset automatically -- it holds the records of every device stim-cli owns, and resetting it would orphan all of them.
- **Port allocation:** `start` scans upward from 8082 for a port that is both unclaimed in the registry and actually free on the machine, reclaiming ports from dead projects on the way. Claiming is race-safe: the write only lands if the config still shows the port unclaimed, so two parallel runs that probe the same free port cannot both take it. A project whose directory only _looks_ gone because its volume is unmounted keeps its port.
- **Owned device creation:** on iOS, `ios` creates the newest iPhone device type -- highest generation number, base model rather than Pro/Pro Max -- on the newest installed runtime by default (or reuses the project's already-recorded owned sim, booting it if shut down). On Android, it creates an AVD via `avdmanager create avd` against the newest installed arm64 system image (stim-cli never installs system images itself -- it errors with install instructions if none is found). Override the defaults with `ios.deviceType` / `ios.runtime` / `android.systemImage` in a settings file -- see "Settings" below.
- **Owned device creation:** on iOS, `ios` creates the newest iPhone device type -- highest generation number, base model rather than Pro/Pro Max -- on the newest installed runtime by default (or reuses the project's already-recorded owned sim, booting it if shut down). On Android, it creates an AVD via `avdmanager create avd` against the newest installed arm64 system image (stim-cli never installs system images itself -- it errors with install instructions if none is found). Owned AVDs always cold-boot: the emulator neither loads nor saves a Quick Boot snapshot, avoiding a large snapshot for a disposable device at the cost of a slower restart after `stop`. Override the defaults with `ios.deviceType` / `ios.runtime` / `android.systemImage` in a settings file -- see "Settings" below.
- **Runtime output is externalized.** Logs, state, pidfiles and Xcode DerivedData live under the global workspace directory, so `worktree remove` can reclaim them without project-tree state. Gradle still uses its normal project build directories; `--build-cache` points task caching at the shared Gradle user home.
- **The port is never baked into a build.** The fingerprint cache shares binaries across workspaces, so a port compiled in would let a binary built for 8082 be served to a workspace holding 8083. iOS gets `RCT_jsLocation` written into the app's simulator defaults (or an `expo-development-client` deep link); Android gets `adb reverse tcp:8081 tcp:<port>`. `RCT_METRO_PORT` is deliberately not passed to builds.
- **Starting the bundler yourself still works.** Both Expo and the RN CLI probe the port and skip spawning a second bundler when one already answers `/status`, and `ios`'s Metro gate accepts a server you started as long as it runs from inside the project -- but nothing is captured that way, so `stim-cli logs` stays empty. Teardown (`stop`, `worktree remove`, `gc`) finds Metro by port via `lsof` and only kills it after confirming it answers `/status` **and** runs from inside the project: a port is not identity, so an unidentified listener is reported instead of killed.
Expand Down
Loading
Loading