peachsim 0.1.527 → 0.1.571

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +28 -28
  2. package/cli/app-fonts.ts +1 -1
  3. package/cli/app-splash.ts +1 -1
  4. package/cli/bin.ts +1 -1
  5. package/cli/bridge-diagnostics.ts +1 -1
  6. package/cli/bridge-flow-runner.ts +72 -12
  7. package/cli/cloud-client.ts +273 -6
  8. package/cli/cloud-dispatch.ts +23 -2
  9. package/cli/cloud-session.ts +60 -4
  10. package/cli/cloud-test-result-files.ts +97 -0
  11. package/cli/cloud-test-tunnel.ts +354 -0
  12. package/cli/commands/box/bundle-plane.ts +2 -2
  13. package/cli/commands/box/checkout-plane.ts +1 -1
  14. package/cli/commands/box/rnx-command.ts +2 -2
  15. package/cli/commands/box/search-command.ts +2 -2
  16. package/cli/commands/box.ts +8 -8
  17. package/cli/commands/compat.ts +1 -1
  18. package/cli/commands/control.ts +22 -3
  19. package/cli/commands/detox.ts +1 -1
  20. package/cli/commands/device.ts +1 -1
  21. package/cli/commands/diagnose.ts +1 -1
  22. package/cli/commands/film.ts +1 -1
  23. package/cli/commands/flow.ts +60 -3
  24. package/cli/commands/inspect/actions.ts +1 -1
  25. package/cli/commands/inspect/core.ts +6 -7
  26. package/cli/commands/inspect/find.ts +2 -1
  27. package/cli/commands/inspect/resolve-target.ts +5 -3
  28. package/cli/commands/inspect/shared.ts +1 -1
  29. package/cli/commands/inspect/wait-event.ts +1 -1
  30. package/cli/commands/inspect/wait-selector.ts +15 -4
  31. package/cli/commands/inspect.ts +19 -14
  32. package/cli/commands/maestro.ts +582 -69
  33. package/cli/commands/permissions.ts +1 -1
  34. package/cli/commands/platform.ts +205 -8
  35. package/cli/commands/preview.ts +2 -2
  36. package/cli/commands/record.ts +2 -2
  37. package/cli/commands/report-issue.ts +2 -2
  38. package/cli/commands/screenshot-layers.ts +1 -1
  39. package/cli/commands/screenshot.ts +1 -1
  40. package/cli/commands/setup.ts +2 -2
  41. package/cli/commands/test.ts +600 -117
  42. package/cli/commands/three-mode.ts +1 -1
  43. package/cli/commands/upload.ts +5 -5
  44. package/cli/commands/what-happened.ts +1 -1
  45. package/cli/desktop-companion.ts +3 -3
  46. package/cli/drivers/playwright-sim-host.ts +2 -2
  47. package/cli/flow-file.ts +7 -0
  48. package/cli/flow-live-status.ts +65 -8
  49. package/cli/help-core.ts +3 -3
  50. package/cli/hidden-runtime-alias.ts +1 -1
  51. package/cli/maestro-report.ts +180 -0
  52. package/cli/main.ts +1 -1
  53. package/cli/outbound-endpoints.ts +187 -136
  54. package/cli/parse-args.ts +1 -1
  55. package/cli/registry.ts +1 -1
  56. package/cli/resolve-assets.ts +1 -1
  57. package/cli/run-registry.ts +10 -1
  58. package/cli/send-to-box.ts +4 -4
  59. package/cli/ws-bridge.ts +3 -5
  60. package/detox/expectations.ts +1 -1
  61. package/detox/gestures.ts +2 -2
  62. package/detox/index.ts +71 -29
  63. package/detox/jest-preset.cjs +5 -5
  64. package/detox/jest-setup-after-env.cjs +1 -1
  65. package/dist-lib/agent-daemon-client.cjs +2 -2
  66. package/dist-lib/agent-events.cjs +1 -1
  67. package/dist-lib/agent-identity.cjs +1 -1
  68. package/dist-lib/agent-sessions.cjs +2 -2
  69. package/dist-lib/attached-projects.cjs +2 -2
  70. package/dist-lib/auth/shared-session.cjs +1 -1
  71. package/dist-lib/backend-origin.cjs +1 -1
  72. package/dist-lib/beta.cjs +1 -1
  73. package/dist-lib/beta.mjs +1 -1
  74. package/dist-lib/bridge-constants.cjs +1 -1
  75. package/dist-lib/bridge-contract-input.cjs +1 -1
  76. package/dist-lib/bridge-contract-input.mjs +1 -1
  77. package/dist-lib/bridge-contract.cjs +1 -1
  78. package/dist-lib/bridge-contract.mjs +1 -1
  79. package/dist-lib/capture-contract.cjs +94 -3
  80. package/dist-lib/capture-contract.mjs +93 -3
  81. package/dist-lib/cli-constants.cjs +1 -1
  82. package/dist-lib/cloud-contract.cjs +1 -1
  83. package/dist-lib/cloud-contract.mjs +1 -1
  84. package/dist-lib/cloud-test-args.cjs +116 -0
  85. package/dist-lib/cloud-test-args.mjs +86 -0
  86. package/dist-lib/cloud.cjs +2 -2
  87. package/dist-lib/cloud.mjs +2 -2
  88. package/dist-lib/config.cjs +1 -1
  89. package/dist-lib/detox/index.cjs +106 -25
  90. package/dist-lib/detox/jest-preset.cjs +5 -5
  91. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  92. package/dist-lib/home-paths.cjs +1 -1
  93. package/dist-lib/host/bridge-host.cjs +150 -65
  94. package/dist-lib/host/fetch-proxy-handler.cjs +28 -2
  95. package/dist-lib/host/fetch-proxy-overrides.cjs +33 -3
  96. package/dist-lib/host/fetch-proxy-overrides.mjs +30 -2
  97. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  98. package/dist-lib/host/websocket-proxy.cjs +1 -1
  99. package/dist-lib/index.cjs +224 -41
  100. package/dist-lib/jump-to-source-babel.cjs +1 -1
  101. package/dist-lib/jump-to-source-native.cjs +1 -1
  102. package/dist-lib/menu.cjs +2 -2
  103. package/dist-lib/menu.mjs +2 -2
  104. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  105. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  106. package/dist-lib/metro-production-bundle.cjs +3 -3
  107. package/dist-lib/metro-production-bundle.mjs +3 -3
  108. package/dist-lib/metro.cjs +8 -8
  109. package/dist-lib/profiles.cjs +1 -1
  110. package/dist-lib/public-brand.cjs +2 -2
  111. package/dist-lib/react-native-host-modules.cjs +1 -1
  112. package/dist-lib/react-native-host-modules.mjs +1 -1
  113. package/dist-lib/render-mode.cjs +1 -1
  114. package/dist-lib/scripts/dev-server-scanner.cjs +31 -15
  115. package/dist-lib/sdk.cjs +3 -2
  116. package/dist-lib/sdk.mjs +3 -2
  117. package/dist-lib/skills.cjs +478 -266
  118. package/dist-lib/swift-compile.cjs +372 -0
  119. package/dist-lib/swift-compile.mjs +339 -0
  120. package/dist-lib/swift-package.cjs +78 -0
  121. package/dist-lib/swift-package.mjs +52 -0
  122. package/dist-lib/swift-submit.cjs +1 -1
  123. package/dist-lib/swift-submit.mjs +1 -1
  124. package/dist-lib/swift.cjs +583 -16
  125. package/dist-lib/vite.cjs +483 -150
  126. package/package.json +30 -9
  127. package/scripts/dev-server-scanner.ts +41 -14
  128. package/scripts/optional-demo-registry.ts +3 -3
  129. package/skills/contrast/SKILL.md +9 -9
  130. package/skills/rnx-debug/SKILL.md +14 -4
  131. package/skills/rnx-perf/SKILL.md +5 -5
  132. package/skills/rnx-setup/SKILL.md +11 -11
  133. package/skills/rnx-test/SKILL.md +14 -14
  134. package/skills/rnx-visual/SKILL.md +6 -6
  135. package/src/agent-sessions.ts +3 -3
  136. package/src/attached-projects.ts +1 -1
  137. package/src/bridge-contract-input.ts +1 -1
  138. package/src/bridge-contract.ts +23 -5
  139. package/src/capture-contract.ts +145 -3
  140. package/src/cloud-test-args.ts +100 -0
  141. package/src/config.ts +2 -2
  142. package/src/connect.ts +2 -2
  143. package/src/home-paths.ts +3 -3
  144. package/src/host/bridge-host.ts +2 -2
  145. package/src/host/fetch-proxy-handler.ts +4 -3
  146. package/src/host/fetch-proxy-overrides.ts +41 -0
  147. package/src/host/plane-host.ts +2 -2
  148. package/src/index.ts +3 -0
  149. package/src/metro-plugin.ts +4 -4
  150. package/src/metro-production-bundle.ts +2 -2
  151. package/src/native-seam-manifest.ts +6 -6
  152. package/src/public-brand.ts +1 -1
  153. package/src/rolldown-plugin-swift.ts +16 -12
  154. package/src/runtime-assets.ts +8 -6
  155. package/src/screenshots/frame-compose.ts +24 -56
  156. package/src/screenshots/org-deck.ts +1 -1
  157. package/src/screenshots/registry.ts +4 -4
  158. package/src/screenshots/schema.ts +1 -1
  159. package/src/sdk.ts +2 -2
  160. package/src/settings-bundle.ts +1 -1
  161. package/src/sim-client.ts +2 -4
  162. package/src/skills/builtin/compat-check.ts +1 -1
  163. package/src/swift-compile.ts +359 -0
  164. package/src/swift-package.ts +69 -0
  165. package/src/swift-submit.ts +3 -2
  166. package/src/vite-plugin-one.ts +1 -1
  167. package/src/vite-plugin-swift.ts +114 -113
  168. package/src/vite-plugin.ts +30 -17
  169. package/src/worklets-babel.ts +2 -2
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "peachsim",
3
- "version": "0.1.527",
4
- "description": "Vite and Metro plugins, testing drivers, and SDK exports for rnx.",
3
+ "version": "0.1.571",
4
+ "description": "Vite and Metro plugins, testing drivers, and SDK exports for peach.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/sootbean/soot.git",
10
- "directory": "packages/sootsim"
10
+ "directory": "packages/peachsim"
11
11
  },
12
12
  "type": "module",
13
13
  "publishConfig": {
@@ -46,6 +46,20 @@
46
46
  "import": "./dist-lib/swift-submit.mjs",
47
47
  "default": "./dist-lib/swift-submit.cjs"
48
48
  },
49
+ "./swift-package": {
50
+ "types": "./src/swift-package.ts",
51
+ "source": "./src/swift-package.ts",
52
+ "bun": "./src/swift-package.ts",
53
+ "import": "./dist-lib/swift-package.mjs",
54
+ "default": "./dist-lib/swift-package.cjs"
55
+ },
56
+ "./swift-compile": {
57
+ "types": "./src/swift-compile.ts",
58
+ "source": "./src/swift-compile.ts",
59
+ "bun": "./src/swift-compile.ts",
60
+ "import": "./dist-lib/swift-compile.mjs",
61
+ "default": "./dist-lib/swift-compile.cjs"
62
+ },
49
63
  "./metro": {
50
64
  "source": "./src/metro-plugin.ts",
51
65
  "default": "./dist-lib/metro.cjs"
@@ -150,6 +164,13 @@
150
164
  "import": "./dist-lib/cloud-contract.mjs",
151
165
  "default": "./dist-lib/cloud-contract.cjs"
152
166
  },
167
+ "./cloud-test-args": {
168
+ "types": "./src/cloud-test-args.ts",
169
+ "source": "./src/cloud-test-args.ts",
170
+ "bun": "./src/cloud-test-args.ts",
171
+ "import": "./dist-lib/cloud-test-args.mjs",
172
+ "default": "./dist-lib/cloud-test-args.cjs"
173
+ },
153
174
  "./cloud": {
154
175
  "types": "./src/cloud.ts",
155
176
  "source": "./src/cloud.ts",
@@ -275,14 +296,14 @@
275
296
  },
276
297
  "devDependencies": {
277
298
  "@contrast/runtime-delivery": "workspace:*",
278
- "@rnx/box": "workspace:*",
279
- "@rnx/globals": "workspace:*",
280
- "@rnx/skills": "workspace:*",
281
- "@sootsim/compat": "workspace:*",
299
+ "@peach/box": "workspace:*",
300
+ "@peach/globals": "workspace:*",
301
+ "@peach/skills": "workspace:*",
302
+ "@peach/compat": "workspace:*",
282
303
  "@types/ws": "^8.5.13",
283
304
  "contrast-kit": "workspace:*",
284
- "rnx-cloud-box": "workspace:*",
285
- "sootsim-engine": "workspace:*",
305
+ "peach-cloud-box": "workspace:*",
306
+ "peach-engine": "workspace:*",
286
307
  "typescript": "7.0.2",
287
308
  "vite": "^8.2.2",
288
309
  "yaml": "^2.8.3"
@@ -35,7 +35,7 @@ import {
35
35
  readAppSettingsBundle,
36
36
  } from '../src/settings-bundle.ts'
37
37
  import { loadOptionalDemoApps, optionalDemoAppsSync } from './optional-demo-registry.ts'
38
- import type { AppSettingsSpecifier } from '@rnx/globals'
38
+ import type { AppSettingsSpecifier } from '@peach/globals'
39
39
 
40
40
  // how long past a command's own budget we wait before deciding its exit event is
41
41
  // never coming. generous, because this only fires when the child is already
@@ -629,7 +629,7 @@ async function resolveLocalIconPath(
629
629
  localIconPathCache.set(port, candidate)
630
630
  // the identified result caches as a strong entry and won't re-probe on
631
631
  // its own; drop it so the next scan republishes with the working url.
632
- portCache.delete(port)
632
+ invalidatePortCache(port)
633
633
  return
634
634
  }
635
635
  }
@@ -828,7 +828,7 @@ async function fetchManifestEnrichment(
828
828
  })
829
829
  // the nameless result cached as a strong entry and won't re-probe on its own;
830
830
  // drop it so the next scan re-runs probePort and publishes the enriched name.
831
- portCache.delete(result.port)
831
+ invalidatePortCache(result.port)
832
832
  }
833
833
 
834
834
  // ports confirmed to be running a non-sootsim dev server — safe to skip the
@@ -959,16 +959,22 @@ export async function probePort(
959
959
  )
960
960
  ? 'one'
961
961
  : 'expo'
962
- return applyManifest(
963
- {
964
- port,
965
- framework,
966
- bundleUrl: withRuntimeConfig(port, launchUrl, owner.cwd),
967
- hmrUrl: `${buildLoopbackProbeBaseUrl('ws:', probeHostname, port)}/hot`,
968
- lastSeen: Date.now(),
969
- ...owner,
970
- },
971
- manifestRes,
962
+ // enrichWithManifest kicks the local-icon shape probe; without it the
963
+ // optimistic metro icon URL publishes forever and the confirmed shape
964
+ // never lands. every other identified return wraps it too.
965
+ return enrichWithManifest(
966
+ applyManifest(
967
+ {
968
+ port,
969
+ framework,
970
+ bundleUrl: withRuntimeConfig(port, launchUrl, owner.cwd),
971
+ hmrUrl: `${buildLoopbackProbeBaseUrl('ws:', probeHostname, port)}/hot`,
972
+ lastSeen: Date.now(),
973
+ ...owner,
974
+ },
975
+ manifestRes,
976
+ buildIconProxyUrl,
977
+ ),
972
978
  buildIconProxyUrl,
973
979
  )
974
980
  }
@@ -1123,6 +1129,17 @@ interface PortCacheEntry {
1123
1129
  }
1124
1130
  const portCache = new Map<number, PortCacheEntry>()
1125
1131
 
1132
+ // out-of-band completions (the icon-shape probe, manifest enrichment) land
1133
+ // while a scan's own probeAll is still in flight, and the scan's cache write
1134
+ // would resurrect the entry they just dropped. stamp every such invalidation
1135
+ // so a scan only caches ports whose verdicts are newer than its own start.
1136
+ const portCacheInvalidatedAt = new Map<number, number>()
1137
+
1138
+ function invalidatePortCache(port: number): void {
1139
+ portCache.delete(port)
1140
+ portCacheInvalidatedAt.set(port, Date.now())
1141
+ }
1142
+
1126
1143
  // negative-cache holds a node/bun port that isn't a bundler (e.g. Contrast's own
1127
1144
  // api server, Zero, SQLite sync host, a dev tool). at scan interval 3s and a 1.5s ttl
1128
1145
  // we used to re-probe every scan, which hits `/` on the port every 3s — that
@@ -1166,6 +1183,7 @@ export function __resetScannerCache() {
1166
1183
  knownOne.clear()
1167
1184
  enrichmentCache.clear()
1168
1185
  enrichmentInFlight.clear()
1186
+ portCacheInvalidatedAt.clear()
1169
1187
  __resetAppSettingsBundleCacheForTests()
1170
1188
  }
1171
1189
 
@@ -1223,6 +1241,9 @@ export async function scanDevServers(
1223
1241
  for (const p of [...localIconPathCache.keys()]) {
1224
1242
  if (!currentPorts.has(p)) localIconPathCache.delete(p)
1225
1243
  }
1244
+ for (const p of [...portCacheInvalidatedAt.keys()]) {
1245
+ if (!currentPorts.has(p)) portCacheInvalidatedAt.delete(p)
1246
+ }
1226
1247
 
1227
1248
  const results: DiscoveredServer[] = []
1228
1249
  const toProbe: ListeningProcess[] = []
@@ -1251,6 +1272,7 @@ export async function scanDevServers(
1251
1272
  }
1252
1273
 
1253
1274
  if (toProbe.length > 0) {
1275
+ const scanStartedAt = Date.now()
1254
1276
  const probed = await Promise.all(
1255
1277
  toProbe.map((p) => probePort(p.port, opts.buildIconProxyUrl, p)),
1256
1278
  )
@@ -1258,7 +1280,12 @@ export async function scanDevServers(
1258
1280
  const { port, pid } = toProbe[i]
1259
1281
  // only cache when we have a real pid to key the entry against.
1260
1282
  // fallback-list ports (pid 0) always reprobe so we catch state changes.
1261
- if (pid !== 0) portCache.set(port, { pid, result, cachedAt: Date.now() })
1283
+ // skip ports an out-of-band completion invalidated mid-scan, so the
1284
+ // next scan re-probes instead of re-caching this scan's stale verdict.
1285
+ const invalidatedAt = portCacheInvalidatedAt.get(port) ?? 0
1286
+ if (pid !== 0 && invalidatedAt < scanStartedAt) {
1287
+ portCache.set(port, { pid, result, cachedAt: Date.now() })
1288
+ }
1262
1289
  if (result) results.push(result)
1263
1290
  })
1264
1291
  }
@@ -20,8 +20,8 @@ import { basename, dirname, join } from 'node:path'
20
20
  import { fileURLToPath, pathToFileURL } from 'node:url'
21
21
 
22
22
  // this module is emitted at three different depths — the source at
23
- // packages/sootsim/scripts/, the built CLI at packages/sootsim/dist-cli/chunks/,
24
- // and the electron main bundle at packages/sootsim-engine/dist-electron/ — so a
23
+ // packages/peachsim/scripts/, the built CLI at packages/peachsim/dist-cli/chunks/,
24
+ // and the electron main bundle at packages/peach-engine/dist-electron/ — so a
25
25
  // relative './demo-app-registry.ts' is only correct from the source copy. from
26
26
  // the other two it points at a file that does not exist, the catch below
27
27
  // swallows that, and every demo app then boots with no runtimeConfig at all
@@ -29,7 +29,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
29
29
  // never reaches it). the one description that holds from every emit site is the
30
30
  // ancestor that holds the registry at its repo path, which in-repo is the
31
31
  // monorepo root.
32
- const REGISTRY_REPO_PATH = 'packages/sootsim/scripts/demo-app-registry.ts'
32
+ const REGISTRY_REPO_PATH = 'packages/peachsim/scripts/demo-app-registry.ts'
33
33
 
34
34
  // the walk must never leave the installed package. `sootsim` publishes this
35
35
  // loader's source, so on a published install it runs from
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: contrast
3
3
  description: >-
4
- index of all Contrast and rnx agent skills and CLI commands.
4
+ index of all Contrast and Peach agent skills and CLI commands.
5
5
  auto-generated from the CLI info registry — do not edit by hand.
6
6
  ---
7
7
 
@@ -12,7 +12,7 @@ overview of all available skills and CLI commands in this repo.
12
12
  > this file is auto-generated by `bun scripts/generate-contrast-skill.ts`.
13
13
  > to update, edit the source registries and re-run the generator.
14
14
 
15
- ## rnx CLI commands
15
+ ## Peach CLI commands
16
16
 
17
17
  the `rnx` CLI is the operational surface for booting shells, driving
18
18
  automation, inspecting the render tree, and managing sims.
@@ -87,17 +87,17 @@ run `rnx --help` to see the full list under each group.
87
87
  - `-t, --theme <scheme>` — override light, dark, or auto color scheme
88
88
  - `--headless` — run browser automation without a visible window where supported
89
89
 
90
- ## bundled rnx agent skills
90
+ ## bundled Peach agent skills
91
91
 
92
- skills for working with the rnx native preview engine:
92
+ skills for working with the Peach native preview engine:
93
93
 
94
94
  | skill | description |
95
95
  | --- | --- |
96
- | `/rnx-debug` | Debug rendering, animation, gesture, performance, and accessibility issues in a React Native app running under rnx. Reach for this whenever an animation looks wrong (janky, stutters, snaps, only updates once, or doesn't move at all), a swipe/transition/gesture misbehaves or feels broken, scrolling drops frames or feels slow, the UI renders the wrong tree/props, or a screen reader can't reach something — and drive the rnx CLI debug tooling first, not screenshots or ad-hoc logging. |
97
- | `/rnx-perf` | Optimize rnx engine and app performance — scroll jank, dropped frames, slow animation, idle CPU, memory growth, and "smooth normally but laggy under load" (video calls, busy machines). Covers measuring on the right thread with the right browser, proving each perf tier actually engages via its counters, a CPU-contention test harness, and the map of existing perf machinery so you improve it instead of reinventing it. For general app debugging load /rnx-debug first. |
98
- | `/rnx-setup` | Get rnx connected to your React Native app with no project setup |
99
- | `/rnx-test` | Run automated tests against your React Native app under rnx with Maestro YAML or Detox/Jest |
100
- | `/rnx-visual` | Pixel-level rendering comparison between rnx and native iOS — fonts, gradients, shadows, image fidelity |
96
+ | `/rnx-debug` | Debug rendering, animation, gesture, performance, and accessibility issues in a React Native app running under Peach. Reach for this whenever an animation looks wrong (janky, stutters, snaps, only updates once, or doesn't move at all), a swipe/transition/gesture misbehaves or feels broken, scrolling drops frames or feels slow, the UI renders the wrong tree/props, or a screen reader can't reach something — and drive the rnx CLI debug tooling first, not screenshots or ad-hoc logging. |
97
+ | `/rnx-perf` | Optimize Peach engine and app performance — scroll jank, dropped frames, slow animation, idle CPU, memory growth, and "smooth normally but laggy under load" (video calls, busy machines). Covers measuring on the right thread with the right browser, proving each perf tier actually engages via its counters, a CPU-contention test harness, and the map of existing perf machinery so you improve it instead of reinventing it. For general app debugging load /rnx-debug first. |
98
+ | `/rnx-setup` | Get Peach connected to your React Native app with no project setup |
99
+ | `/rnx-test` | Run automated tests against your React Native app under Peach with Maestro YAML or Detox/Jest |
100
+ | `/rnx-visual` | Pixel-level rendering comparison between Peach and native iOS — fonts, gradients, shadows, image fidelity |
101
101
 
102
102
  ## automatic settling
103
103
 
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: rnx-debug
3
- description: Debug rendering, animation, gesture, performance, and accessibility issues in a React Native app running under rnx. Reach for this whenever an animation looks wrong (janky, stutters, snaps, only updates once, or doesn't move at all), a swipe/transition/gesture misbehaves or feels broken, scrolling drops frames or feels slow, the UI renders the wrong tree/props, or a screen reader can't reach something — and drive the rnx CLI debug tooling first, not screenshots or ad-hoc logging.
3
+ description: Debug rendering, animation, gesture, performance, and accessibility issues in a React Native app running under Peach. Reach for this whenever an animation looks wrong (janky, stutters, snaps, only updates once, or doesn't move at all), a swipe/transition/gesture misbehaves or feels broken, scrolling drops frames or feels slow, the UI renders the wrong tree/props, or a screen reader can't reach something — and drive the rnx CLI debug tooling first, not screenshots or ad-hoc logging.
4
4
  ---
5
5
 
6
- # rnx debug
6
+ # Peach debug
7
7
 
8
- the foundational skill for "something is wrong with my app in rnx".
8
+ the foundational skill for "something is wrong with my app in Peach".
9
9
  covers debugging, performance, and accessibility — three branches that
10
10
  share the same CLI primitives, the same auto-settling contract, and the
11
11
  same diff/snapshot workflow. start here for any rendering, interaction,
@@ -49,7 +49,7 @@ regression test. a pile of ad-hoc logs helps no one and gets deleted.
49
49
 
50
50
  ## anti-patterns (apply to every branch)
51
51
 
52
- - **`a1`/`a2`/`b0` style ids mean rnx sims, never browser tabs.** when a user
52
+ - **`a1`/`a2`/`b0` style ids mean Peach sims, never browser tabs.** when a user
53
53
  says "look at a3", that's the `rnx list` sim id. do not open chrome
54
54
  MCP unless the user explicitly says "use chrome" — the canvas has no
55
55
  DOM to inspect anyway.
@@ -76,6 +76,16 @@ regression test. a pile of ad-hoc logs helps no one and gets deleted.
76
76
  them*. a "fixed" behavior with a new console error is not fixed.
77
77
  - **"found a testID" is not done.** done is: reliable repro + identified
78
78
  root cause (specific node + prop, or event + handler) + verified fix.
79
+ - **an empty `find --pressable` is not proof there is no affordance.** it
80
+ lists registered touch responders, and a tamagui `styled()` view whose
81
+ `onPress` comes from the call site is tappable without ever appearing there.
82
+ before concluding a screen has no way out, corroborate with `get a11y` or an
83
+ actual `do tap-text`.
84
+ - **after an unexplained restart, re-establish which sim you are on.** a sim
85
+ that reconnected, or a dev stack that came back on a port another process now
86
+ holds, is a fresh instrument; a port answering 200 says something is
87
+ listening, not that it is yours. every Peach command prints its own `→ <sim>`
88
+ line, so reading that line is the check.
79
89
 
80
90
  ## the timeline ("what happened")
81
91
 
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: rnx-perf
3
- description: Optimize rnx engine and app performance — scroll jank, dropped frames, slow animation, idle CPU, memory growth, and "smooth normally but laggy under load" (video calls, busy machines). Covers measuring on the right thread with the right browser, proving each perf tier actually engages via its counters, a CPU-contention test harness, and the map of existing perf machinery so you improve it instead of reinventing it. For general app debugging load /rnx-debug first.
3
+ description: Optimize Peach engine and app performance — scroll jank, dropped frames, slow animation, idle CPU, memory growth, and "smooth normally but laggy under load" (video calls, busy machines). Covers measuring on the right thread with the right browser, proving each perf tier actually engages via its counters, a CPU-contention test harness, and the map of existing perf machinery so you improve it instead of reinventing it. For general app debugging load /rnx-debug first.
4
4
  ---
5
5
 
6
- # rnx performance
6
+ # Peach performance
7
7
 
8
- optimize performance in rnx: the shell frame pipeline, the compositor
8
+ optimize performance in Peach: the shell frame pipeline, the compositor
9
9
  tiers, and the guest app. this skill exists because every real perf win in
10
10
  this engine followed the same loop — measure on the right thread, read the
11
11
  counters that prove machinery engaged, fix the one thing the counters
@@ -384,7 +384,7 @@ tier fire on feeds came entirely from rejection counters.
384
384
  - `Animated.Value` on `width`/`height` forces layout per frame — use
385
385
  `transform` (scale/translate).
386
386
  - per-frame React work from reanimated-style code is an engine conformance
387
- bug (valid worklet apps must not commit per frame) — fix rnx core,
387
+ bug (valid worklet apps must not commit per frame) — fix Peach core,
388
388
  never add a divergent fast-path (upstream alignment overrides perf; see
389
389
  the `shouldFreezeOffscreenStackContent` incident).
390
390
  - render-storm loops (poll → remount) show up as commit-rate repaints and
@@ -396,4 +396,4 @@ done means: a named hot path + the counter or split that indicts it + the
396
396
  same capture re-run showing the number moved + no new errors (`get errors`)
397
397
  + nothing torn down left running (close sims you opened, kill load loops).
398
398
  for engine changes, run the affected integration tests exposed by the installed
399
- rnx version and capture stable screenshots of surfaces you touched.
399
+ Peach version and capture stable screenshots of surfaces you touched.
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: rnx-setup
3
- description: Get rnx connected to your React Native app with no project setup
3
+ description: Get Peach connected to your React Native app with no project setup
4
4
  ---
5
5
 
6
- # rnx setup
6
+ # Peach setup
7
7
 
8
- you want to drive a React Native app with rnx from the CLI. this skill
8
+ you want to drive a React Native app with Peach from the CLI. this skill
9
9
  takes you from a running development server to a connected, pinned sim that
10
- every other rnx skill assumes exists. rnx requires no project setup or per-app
10
+ every other Peach skill assumes exists. Peach requires no project setup or per-app
11
11
  install. if
12
12
  `rnx describe` already
13
13
  returns a tree, you're past this skill — load `/rnx-debug` for
@@ -21,7 +21,7 @@ debugging or `/rnx-test` for writing tests.
21
21
  - **don't switch sims mid-investigation.** pin once with `rnx use
22
22
  <id>` and keep it pinned. an unpinned command refuses to choose when more
23
23
  than one driveable sim is live.
24
- - **an empty `rnx list` is not "broken rnx".** it usually means the
24
+ - **an empty `rnx list` is not "broken Peach".** it usually means the
25
25
  daemon is down, the dev server port is wrong, or the runtime is missing.
26
26
  walk the recovery checklist before reinstalling.
27
27
  - **keep one sim for your whole session.** the first `rnx open <port>`
@@ -53,7 +53,7 @@ debugging or `/rnx-test` for writing tests.
53
53
  - **never retry-loop `rnx open` against a wedged or slow stack.** a
54
54
  connect timeout means diagnose the stack. do not retry. if the first launch
55
55
  never connects, each retry can create another browser tree before there is a
56
- saved sim to reuse. verify the development server and rnx runtime, then
56
+ saved sim to reuse. verify the development server and Peach runtime, then
57
57
  retry once after fixing the cause.
58
58
 
59
59
  ## first move
@@ -69,7 +69,7 @@ rnx describe # smoke test — should print a render tree
69
69
  ```
70
70
 
71
71
  the background service keeps the local bridge and runtime ready between
72
- commands. without it, rnx still works by starting an on-demand bridge, but
72
+ commands. without it, Peach still works by starting an on-demand bridge, but
73
73
  inspect, interaction, and test commands pay repeated startup cost. if the user
74
74
  declined it during onboarding, `rnx daemon install` is the direct way to enable
75
75
  it. CI does not need or install the service.
@@ -100,8 +100,8 @@ rnx open https://my-app.local # load a hosted bundle
100
100
  ## compatibility feedback requires approval
101
101
 
102
102
  run `rnx compat --json` from the app project root during setup. if the scan
103
- identifies a plausible missing rnx seam, or the running app gives concrete
104
- evidence that behavior is missing or broken specifically in rnx, summarize
103
+ identifies a plausible missing Peach seam, or the running app gives concrete
104
+ evidence that behavior is missing or broken specifically in Peach, summarize
105
105
  the package, expected behavior, actual behavior, and runtime evidence for the
106
106
  user. then offer to send that finding with `rnx report-issue`.
107
107
 
@@ -133,7 +133,7 @@ if the daemon is not installed: run `rnx daemon install`. if the registered
133
133
  daemon is down, run `rnx daemon restart`, or `rnx serve` in another shell for
134
134
  a foreground bridge. if no runtime is installed, `rnx open` installs it
135
135
  before launching. if open errors with "port unreachable": confirm your dev
136
- server is actually serving — the bundler has to be up before rnx can
136
+ server is actually serving — the bundler has to be up before Peach can
137
137
  attach.
138
138
 
139
139
  **two sims appear unexpectedly.** an earlier `rnx claim` didn't
@@ -151,7 +151,7 @@ Run `rnx runtime install`, then retry `rnx open <port>`.
151
151
  do not retry in a loop. verify the development server first: `__soot/` must
152
152
  respond and the engine watchers must be running. inspect the driver diagnostic
153
153
  path printed by the failed command. restart the broken development stack at its
154
- supervisor, then retry once. rnx owns the isolated browser profile and its
154
+ supervisor, then retry once. Peach owns the isolated browser profile and its
155
155
  process tree; do not open the shell URL through an operating-system browser.
156
156
 
157
157
  **bridge disconnects mid-sim.** the WebSocket dropped (laptop slept,
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: rnx-test
3
- description: Run automated tests against your React Native app under rnx with Maestro YAML or Detox/Jest
3
+ description: Run automated tests against your React Native app under Peach with Maestro YAML or Detox/Jest
4
4
  ---
5
5
 
6
- # rnx tests
6
+ # Peach tests
7
7
 
8
- rnx has two test runners. Maestro owns every YAML test, whether it was
8
+ Peach has two test runners. Maestro owns every YAML test, whether it was
9
9
  hand-authored, generated from a goal, or recorded from live CLI actions.
10
- Detox runs existing imperative Jest suites through the rnx driver.
10
+ Detox runs existing imperative Jest suites through the Peach driver.
11
11
 
12
12
  > needs a connected, pinned sim. if `rnx describe` errors, load
13
13
  > `/rnx-setup` first.
@@ -165,7 +165,7 @@ rnx maestro test .maestro/login.yaml
165
165
  ```
166
166
 
167
167
  no test rewrites, no detox config, no simulator. the runtime auto-launches
168
- an rnx shell if one isn't running.
168
+ a Peach shell if one isn't running.
169
169
 
170
170
  ```sh
171
171
  rnx maestro # auto-discover .maestro/ or maestro/
@@ -190,10 +190,10 @@ interpolation.
190
190
 
191
191
  ### partial
192
192
 
193
- - `clearKeychain` — no keychain in rnx; logs a warning and continues.
193
+ - `clearKeychain` — no keychain in Peach; logs a warning and continues.
194
194
  if a test depends on keychain state being reset, add an explicit
195
195
  in-app "sign out" step instead.
196
- - `when.platform` — rnx emulates iOS; android branches are skipped.
196
+ - `when.platform` — Peach emulates iOS; android branches are skipped.
197
197
  - `openLink`: app routing works; there is no OS-level browser fallback.
198
198
 
199
199
  `stopApp` keeps the guest runtime stopped. The next `launchApp` starts a fresh
@@ -216,7 +216,7 @@ these throw `unsupported maestro verb: X`:
216
216
  - **"missing environment variable for flow placeholder: FOO"** — pass
217
217
  `--env FOO=bar` or export it. pick one mechanism per repo and document
218
218
  it in the flow directory's README.
219
- - **flow can't find an element** — rnx's matcher uses testID + visible
219
+ - **flow can't find an element** — Peach's matcher uses testID + visible
220
220
  text. UIKit-only labels that real maestro scrapes from the host OS won't
221
221
  resolve here; switch to testID.
222
222
  - **flow runs but nothing renders** — confirm the bundler your flow
@@ -225,7 +225,7 @@ these throw `unsupported maestro verb: X`:
225
225
 
226
226
  ## branch — detox drop-in
227
227
 
228
- an existing detox suite runs against rnx with **one line** of jest
228
+ an existing detox suite runs against Peach with **one line** of jest
229
229
  config and zero test changes.
230
230
 
231
231
  `import { by, device, element, expect, waitFor } from 'detox'` is
@@ -238,7 +238,7 @@ upstream detox surface.
238
238
  ```js
239
239
  // rnx-detox.config.cjs
240
240
  module.exports = {
241
- preset: 'rnxsim/detox',
241
+ preset: 'peachsim/detox',
242
242
  rootDir: __dirname,
243
243
  testMatch: ['<rootDir>/e2e/**/*.test.ts'],
244
244
  }
@@ -248,7 +248,7 @@ or, if you already have a jest config, pull in just the module mapper:
248
248
 
249
249
  ```js
250
250
  moduleNameMapper: {
251
- '^detox$': require.resolve('rnxsim/detox'),
251
+ '^detox$': require.resolve('peachsim/detox'),
252
252
  }
253
253
  ```
254
254
 
@@ -271,7 +271,7 @@ rnx detox init # scaffold config + e2e/example.test.ts
271
271
 
272
272
  ### compat surface
273
273
 
274
- | detox API | rnx |
274
+ | detox API | Peach |
275
275
  | ---------------------------------- | ------- |
276
276
  | `element(by.id(...))` | supported |
277
277
  | `element(by.text(...))` | supported — but prefer `by.id` |
@@ -291,10 +291,10 @@ rnx detox init # scaffold config + e2e/example.test.ts
291
291
 
292
292
  ### troubleshooting
293
293
 
294
- - **"no rnx shell reachable"** — start one yourself
294
+ - **"no Peach shell reachable"** — start one yourself
295
295
  (`rnx open <port>`) or pass `--port <n>`.
296
296
  - **"jest cannot find module 'detox'"** — the preset isn't wired. confirm
297
- `preset: 'rnxsim/detox'` is in the jest config jest
297
+ `preset: 'peachsim/detox'` is in the jest config jest
298
298
  actually loaded (`--config` overrides package.json).
299
299
  - **matchers fail on elements that exist** — canvas has no DOM. use
300
300
  `by.id` (testID); `rnx describe` shows what the shell sees.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: rnx-visual
3
- description: Pixel-level rendering comparison between rnx and native iOS — fonts, gradients, shadows, image fidelity
3
+ description: Pixel-level rendering comparison between Peach and native iOS — fonts, gradients, shadows, image fidelity
4
4
  ---
5
5
 
6
- # rnx visual fidelity
6
+ # Peach visual fidelity
7
7
 
8
- pixel-level rendering fidelity: does the rnx canvas match native iOS
8
+ pixel-level rendering fidelity: does the Peach canvas match native iOS
9
9
  for text, gradients, shadows, blur, and image rendering? the `rnx`
10
10
  CLI gives you two primitives — **canvas capture** (`rnx screenshot`)
11
11
  and **structural diff** (`rnx debug snapshot`/`diff`). pixel
@@ -23,7 +23,7 @@ the capture step is the hard part and it's fully reproducible.
23
23
  diff` is faster, deterministic, and tells you *why*.
24
24
  - visual change (color, gradient, shadow, font metric, blur, image)? →
25
25
  continue here.
26
- - comparing rnx to your iOS simulator for fidelity? → continue.
26
+ - comparing Peach to your iOS simulator for fidelity? → continue.
27
27
  - regressing your own app against itself across commits? → continue.
28
28
 
29
29
  ## anti-patterns (read first)
@@ -64,7 +64,7 @@ screen you want to inspect and capture it with a distinct output filename.
64
64
 
65
65
  capture both sides to PNGs, then diff with any standard image tool.
66
66
 
67
- **rnx vs rnx (regression across your own commits):**
67
+ **Peach vs Peach (regression across your own commits):**
68
68
 
69
69
  ```sh
70
70
  rnx screenshot -o before.png # baseline, before your change
@@ -74,7 +74,7 @@ npx pixelmatch before.png after.png diff.png 0.1 # writes diff.png
74
74
  # or: magick compare before.png after.png diff.png
75
75
  ```
76
76
 
77
- **rnx vs native iOS (fidelity to the real thing):**
77
+ **Peach vs native iOS (fidelity to the real thing):**
78
78
 
79
79
  ```sh
80
80
  # native side: lifecycle and UI use xcodebuildmcp with one claimed simulator
@@ -163,7 +163,7 @@ function tryWorkspaceSootsim(): RnxSelfInvocation | null {
163
163
 
164
164
  /** locate the workspace sootsim package directory. tries `require.resolve`
165
165
  * first (works in most contexts), then walks up from this module file
166
- * looking for `packages/sootsim/package.json` (works when sootsim is
166
+ * looking for `packages/peachsim/package.json` (works when sootsim is
167
167
  * loaded via vite's native TS resolution, where require.resolve doesn't
168
168
  * know about the workspace). */
169
169
  function resolveSootsimPackageDir(): string | null {
@@ -172,8 +172,8 @@ function resolveSootsimPackageDir(): string | null {
172
172
  const resolved = require.resolve(`${rnxPublicBrand.packageName}/package.json`)
173
173
  return path.dirname(resolved)
174
174
  } catch {}
175
- // walk up from this file: .../packages/sootsim/src/agent-sessions.ts
176
- // → .../packages/sootsim/. the module path is whatever the runtime
175
+ // walk up from this file: .../packages/peachsim/src/agent-sessions.ts
176
+ // → .../packages/peachsim/. the module path is whatever the runtime
177
177
  // gave us; when bundled by esbuild this will be the bundle path, not
178
178
  // the source path, which is why we try require.resolve first.
179
179
  const here = fileFromImportMeta()
@@ -501,7 +501,7 @@ export function deletePreviewAttachment(id: string): void {
501
501
 
502
502
  // --- seed from demo registry ---
503
503
 
504
- /** seed the store from packages/sootsim/scripts/demo-app-registry. only runs
504
+ /** seed the store from packages/peachsim/scripts/demo-app-registry. only runs
505
505
  * when the store is completely empty (never re-seeds) and only adds apps
506
506
  * whose `dir` exists on disk (concern 6.4 — demo dirs vary per machine). */
507
507
  export async function seedFromDemoAppRegistry(): Promise<void> {
@@ -12,7 +12,7 @@ import type {
12
12
  SimStateOptions,
13
13
  } from './bridge-contract'
14
14
  import type { SimPerformStep, SimScreenshotOptions } from './sim-client'
15
- import type { SootSimReplayTarget } from '@rnx/globals'
15
+ import type { SootSimReplayTarget } from '@peach/globals'
16
16
 
17
17
  /** the caller sent a value this bridge argument cannot be read from. */
18
18
  export class BridgeInputError extends Error {
@@ -1,11 +1,11 @@
1
1
  // the wire contract for the sootsim ws bridge. the engine implements it in
2
- // sootsim-engine/src/mcp/ws-bridge.ts; the rnxsim cli client, the SimClient
2
+ // sootsim-engine/src/mcp/ws-bridge.ts; the peachsim cli client, the SimClient
3
3
  // sdk, and the flow runner speak it. it is the single owner of these shapes,
4
4
  // and neither side redeclares them.
5
5
  //
6
- // it lives in rnxsim rather than the engine because rnxsim is published and
7
- // sootsim-engine is `private: true`. the engine already depends on rnxsim
8
- // (see its `rnxsim` dependency and its `rnxsim/bridge-constants` import), so
6
+ // it lives in peachsim rather than the engine because peachsim is published and
7
+ // sootsim-engine is `private: true`. the engine already depends on peachsim
8
+ // (see its `peachsim` dependency and its `peachsim/bridge-constants` import), so
9
9
  // this is the direction that ships.
10
10
 
11
11
  import type {
@@ -13,7 +13,7 @@ import type {
13
13
  SootSimCaptureFilter,
14
14
  SootSimExternalAppLifecycleOptions,
15
15
  SootSimStorageResetStrategy,
16
- } from '@rnx/globals'
16
+ } from '@peach/globals'
17
17
 
18
18
  export type BridgeCommandType =
19
19
  | 'evaluate'
@@ -42,11 +42,29 @@ export type BridgeCommandType =
42
42
  | 'diagnostics'
43
43
  | 'storageSnapshot'
44
44
 
45
+ // the bridge host answers these itself, before the engine's handleCommand ever
46
+ // sees them. they are not engine commands and the cloud plane does not route
47
+ // them, so they stay out of BridgeCommandType and out of its cloud scope table.
48
+ export type BridgeHostCommandType = 'bridge:list-sims' | 'bridge:claim'
49
+
50
+ // what a caller hands a bridge's `send`: the command minus the correlation id,
51
+ // which the transport assigns. every send seam names this type rather than an
52
+ // index-signature bag, because an index signature erases excess-property
53
+ // checking and a field spelled wrong then compiles and arrives as `undefined`.
54
+ export type WsBridgeCommand = Omit<WsCommand, 'id' | 'type'> & {
55
+ type: BridgeCommandType | BridgeHostCommandType
56
+ }
57
+
45
58
  export interface WsCommand {
46
59
  id: string | number
47
60
  type: BridgeCommandType
48
61
  // local daemon routing; cloud transport strips it before execution.
49
62
  simId?: string
63
+ // take or refresh the cli lease on the target sim before running. reads omit
64
+ // it; anything that mutates user-facing state sets it. read in bridge-host.
65
+ acquireLock?: boolean
66
+ // bridge:claim — take the lease even when another cli holds it.
67
+ force?: boolean
50
68
  // flowStatus — live flow-runner progress payload (see flow-run-store)
51
69
  status?: Record<string, unknown>
52
70
  code?: string