sootsim 0.1.169 → 0.1.170

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 (184) hide show
  1. package/README.md +66 -63
  2. package/detox/index.ts +3 -3
  3. package/dist-cli/bin.js +3 -3
  4. package/dist-cli/chunks/agent-3C2BKQKX.js +46 -0
  5. package/dist-cli/chunks/{agent-wrapper-Y5FQA3RL.js → agent-wrapper-OIKCYOXZ.js} +2 -2
  6. package/dist-cli/chunks/{app-fonts-FYVA4XV6.js → app-fonts-25O32NAP.js} +2 -2
  7. package/dist-cli/chunks/{assert-U64QBDM2.js → assert-ONEP4NQU.js} +2 -2
  8. package/dist-cli/chunks/beta-4E5USTEI.js +2 -0
  9. package/dist-cli/chunks/{camera-3GXBVIAM.js → camera-RORW4MAA.js} +2 -2
  10. package/dist-cli/chunks/{chunk-5C6BR24W.js → chunk-25RFZ6WP.js} +6 -6
  11. package/dist-cli/chunks/chunk-35GMFXVJ.js +2 -0
  12. package/dist-cli/chunks/{chunk-GLRFKYDV.js → chunk-3TM7DHWE.js} +2 -2
  13. package/dist-cli/chunks/{chunk-4F3BBP7B.js → chunk-4XVY5TUF.js} +2 -2
  14. package/dist-cli/chunks/{chunk-LZEL2RU3.js → chunk-5ZHUPPS5.js} +1 -1
  15. package/dist-cli/chunks/{chunk-GC4THE6K.js → chunk-6FAEORJ2.js} +2 -2
  16. package/dist-cli/chunks/{chunk-G2ZFDIPK.js → chunk-6TSXXN2W.js} +4 -4
  17. package/dist-cli/chunks/chunk-AJF3QF4K.js +1 -0
  18. package/dist-cli/chunks/{chunk-FIDPRJYH.js → chunk-AUCNBB6Q.js} +2 -2
  19. package/dist-cli/chunks/{chunk-SIB6I72P.js → chunk-BP3EGFER.js} +2 -2
  20. package/dist-cli/chunks/{chunk-OGLWXPVG.js → chunk-DAXME77O.js} +3 -3
  21. package/dist-cli/chunks/chunk-DGJTEFE7.js +13 -0
  22. package/dist-cli/chunks/chunk-DIRDEKRW.js +23 -0
  23. package/dist-cli/chunks/{chunk-3L4NRJ6C.js → chunk-DKTC53HV.js} +1 -1
  24. package/dist-cli/chunks/{chunk-KUUFY6FC.js → chunk-DWXC25XG.js} +1 -1
  25. package/dist-cli/chunks/{chunk-CR6DOLUD.js → chunk-E3FKMY5E.js} +1 -1
  26. package/dist-cli/chunks/chunk-ER4GBJRY.js +2 -0
  27. package/dist-cli/chunks/chunk-EX6V4ZV6.js +6 -0
  28. package/dist-cli/chunks/{chunk-VMYMPL6H.js → chunk-GQH3PD3W.js} +5 -5
  29. package/dist-cli/chunks/{chunk-MVOHANJG.js → chunk-GST4YSBO.js} +4 -4
  30. package/dist-cli/chunks/{chunk-BIWJE7CB.js → chunk-I4RV5I6V.js} +1 -1
  31. package/dist-cli/chunks/{chunk-Y6IDAARY.js → chunk-I5WXVKR6.js} +1 -1
  32. package/dist-cli/chunks/{chunk-HC72ILJK.js → chunk-IO7KAVFK.js} +2 -2
  33. package/dist-cli/chunks/{chunk-XEH4QGWB.js → chunk-LMX7UB5T.js} +2 -2
  34. package/dist-cli/chunks/{chunk-YADWU7OZ.js → chunk-N4YCSOCR.js} +2 -2
  35. package/dist-cli/chunks/{chunk-IODOTOOP.js → chunk-NI3L4A65.js} +2 -2
  36. package/dist-cli/chunks/{chunk-IACEJQIO.js → chunk-ODGEH6OW.js} +2 -2
  37. package/dist-cli/chunks/{chunk-DNA6VS6M.js → chunk-Q4NDYO2U.js} +1 -1
  38. package/dist-cli/chunks/{chunk-PV6MBKKE.js → chunk-QPRF3UYX.js} +2 -2
  39. package/dist-cli/chunks/{chunk-M2TMFGZH.js → chunk-QXJMXSQP.js} +1 -1
  40. package/dist-cli/chunks/chunk-RLKSLUNR.js +2 -0
  41. package/dist-cli/chunks/{chunk-WZG3YVJR.js → chunk-S244IT4W.js} +1 -1
  42. package/dist-cli/chunks/chunk-SDHOM6ZG.js +62 -0
  43. package/dist-cli/chunks/{chunk-B7BMSU7H.js → chunk-TXKOJ3P5.js} +1 -1
  44. package/dist-cli/chunks/{chunk-IBEMMV5E.js → chunk-UDMJSFSH.js} +8 -8
  45. package/dist-cli/chunks/{chunk-DQZEN256.js → chunk-ULV4PWN4.js} +1 -1
  46. package/dist-cli/chunks/{chunk-2TYEWVS4.js → chunk-V4HTQPMA.js} +1 -1
  47. package/dist-cli/chunks/chunk-VATHADSI.js +2 -0
  48. package/dist-cli/chunks/{chunk-5Q7IHJ7K.js → chunk-VUOCUCJ2.js} +2 -2
  49. package/dist-cli/chunks/{chunk-JDGLBW64.js → chunk-WN7R4GL4.js} +1 -1
  50. package/dist-cli/chunks/chunk-XOTJD6PU.js +118 -0
  51. package/dist-cli/chunks/chunk-XUNPVF5K.js +1 -0
  52. package/dist-cli/chunks/{chunk-KZIXAYN5.js → chunk-YMFSRCHL.js} +4 -4
  53. package/dist-cli/chunks/{chunk-X2HNVXNM.js → chunk-YV74D4MG.js} +2 -2
  54. package/dist-cli/chunks/{chunk-SNX3M4XD.js → chunk-YVTWSC5Y.js} +3 -3
  55. package/dist-cli/chunks/cli-version-EKKWCHRX.js +2 -0
  56. package/dist-cli/chunks/{compat-ORRWNXVT.js → compat-ZVZM5TML.js} +3 -3
  57. package/dist-cli/chunks/control-TWINYHMY.js +2 -0
  58. package/dist-cli/chunks/{cpu-profile-6PRSBZ2F.js → cpu-profile-RWGQTWLP.js} +2 -2
  59. package/dist-cli/chunks/daemon-AOSMNBD4.js +2 -0
  60. package/dist-cli/chunks/{debug-Q3J2X4WW.js → debug-R7PL3MHF.js} +5 -5
  61. package/dist-cli/chunks/{detox-DN57GMMJ.js → detox-UIJMKIGQ.js} +2 -2
  62. package/dist-cli/chunks/{device-IL5TPYGU.js → device-7SRVX2PU.js} +2 -2
  63. package/dist-cli/chunks/{diagnose-BW6QWP4F.js → diagnose-OXAKLLZT.js} +2 -2
  64. package/dist-cli/chunks/drivers-BPPG22VP.js +2 -0
  65. package/dist-cli/chunks/{electron-DGMSXQWG.js → electron-CTCH4VOL.js} +3 -3
  66. package/dist-cli/chunks/flow-KA2WCWDL.js +2 -0
  67. package/dist-cli/chunks/help-XBQIK2XY.js +2 -0
  68. package/dist-cli/chunks/{hints-TK3EHB4U.js → hints-P3ZIAP3Z.js} +2 -2
  69. package/dist-cli/chunks/home-paths-ZSCTLOYE.js +2 -0
  70. package/dist-cli/chunks/{inspect-NFWFGU4B.js → inspect-2ABU5WET.js} +24 -24
  71. package/dist-cli/chunks/{install-desktop-GB3JKSNX.js → install-desktop-4NOQ3YY4.js} +3 -3
  72. package/dist-cli/chunks/{keys-RAAHRIFH.js → keys-RPVEUNUC.js} +2 -2
  73. package/dist-cli/chunks/{login-KQ37GHDE.js → login-6D6DXBO3.js} +4 -4
  74. package/dist-cli/chunks/{logout-63SH2Y25.js → logout-T5OSWDTJ.js} +2 -2
  75. package/dist-cli/chunks/{maestro-24BT3MML.js → maestro-MKCHEOKA.js} +2 -2
  76. package/dist-cli/chunks/optional-demo-registry-EYD2K3QH.js +2 -0
  77. package/dist-cli/chunks/{preview-KQAOVN6R.js → preview-S52ZNGN2.js} +2 -2
  78. package/dist-cli/chunks/{profile-AN2B4NJT.js → profile-IG5F3LTL.js} +2 -2
  79. package/dist-cli/chunks/{react-ATGV35B4.js → react-275HCOFC.js} +2 -2
  80. package/dist-cli/chunks/{record-36662T3R.js → record-YT63SUHO.js} +4 -4
  81. package/dist-cli/chunks/runtime-WYZMDZAK.js +2 -0
  82. package/dist-cli/chunks/{screenshot-K4N5MPAT.js → screenshot-5AZ4MY3A.js} +4 -4
  83. package/dist-cli/chunks/{screenshot-mode-4QOJDLSH.js → screenshot-mode-4S4EX2XJ.js} +2 -2
  84. package/dist-cli/chunks/{screenshots-TB35YMSR.js → screenshots-UUJNZSG7.js} +2 -2
  85. package/dist-cli/chunks/{screenshots-capture-W6X33BZ4.js → screenshots-capture-I66JD3AD.js} +2 -2
  86. package/dist-cli/chunks/{server-L2PKDJZ2.js → serve-O46Q4WAD.js} +18 -17
  87. package/dist-cli/chunks/setup-C44N54O7.js +77 -0
  88. package/dist-cli/chunks/skills-PPVS4XOQ.js +40 -0
  89. package/dist-cli/chunks/{slides-TORKO36P.js → slides-ZWSZJZUY.js} +2 -2
  90. package/dist-cli/chunks/store-3SLGP5O3.js +2 -0
  91. package/dist-cli/chunks/telemetry-2XLLWARK.js +2 -0
  92. package/dist-cli/chunks/{test-5VCENVW3.js → test-362G4M5A.js} +3 -3
  93. package/dist-cli/chunks/{three-mode-ZVS2DSX6.js → three-mode-JNVTM2UT.js} +2 -2
  94. package/dist-cli/chunks/{timeline-TNC5OWLF.js → timeline-VKC6ULRK.js} +2 -2
  95. package/dist-cli/chunks/{upgrade-E7IO6A7Z.js → upgrade-SJPX4SPA.js} +2 -2
  96. package/dist-cli/chunks/upload-MXOBV6RT.js +2 -0
  97. package/dist-cli/chunks/version-4Z7AFNH3.js +4 -0
  98. package/dist-cli/chunks/web-WB2I2AWL.js +2 -0
  99. package/dist-cli/chunks/{what-happened-RMHZ7DGX.js → what-happened-KQCWMQJT.js} +3 -3
  100. package/dist-cli/chunks/{whoami-D53ANHR5.js → whoami-K76PXAD4.js} +2 -2
  101. package/dist-lib/agent-daemon-client.cjs +4 -5
  102. package/dist-lib/agent-events.cjs +1 -1
  103. package/dist-lib/agent-sessions.cjs +1 -1
  104. package/dist-lib/attached-projects.cjs +1 -1
  105. package/dist-lib/auth/shared-session.cjs +1 -1
  106. package/dist-lib/backend-origin.cjs +1 -1
  107. package/dist-lib/beta.cjs +3 -6
  108. package/dist-lib/beta.mjs +3 -5
  109. package/dist-lib/bridge-constants.cjs +1 -1
  110. package/dist-lib/cli-constants.cjs +1 -1
  111. package/dist-lib/config.cjs +1 -1
  112. package/dist-lib/detox/index.cjs +4 -4
  113. package/dist-lib/dev-bundle-resolution.cjs +2 -9
  114. package/dist-lib/home-paths.cjs +59 -1
  115. package/dist-lib/host/bridge-host.cjs +67 -5
  116. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  117. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  118. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  119. package/dist-lib/host/websocket-proxy.cjs +1 -1
  120. package/dist-lib/index.cjs +2 -2
  121. package/dist-lib/menu.cjs +1 -1
  122. package/dist-lib/menu.mjs +1 -1
  123. package/dist-lib/metro.cjs +2 -2
  124. package/dist-lib/profiles.cjs +1 -1
  125. package/dist-lib/render-mode.cjs +1 -1
  126. package/dist-lib/scripts/dev-server-scanner.cjs +5 -4
  127. package/dist-lib/sdk.cjs +1 -1
  128. package/dist-lib/sdk.mjs +1 -1
  129. package/dist-lib/skills.cjs +124 -8
  130. package/dist-lib/vite.cjs +2 -2
  131. package/package.json +2 -1
  132. package/scripts/dev-server-scanner.ts +3 -4
  133. package/scripts/postinstall.cjs +2 -2
  134. package/scripts/preuninstall.cjs +5 -7
  135. package/skills/soot.md +178 -0
  136. package/skills/sootsim-debug.md +384 -0
  137. package/skills/sootsim-setup.md +96 -0
  138. package/skills/sootsim-test.md +292 -0
  139. package/skills/sootsim-visual.md +122 -0
  140. package/src/agent-daemon-client.ts +4 -14
  141. package/src/beta.ts +3 -5
  142. package/src/dev-bundle-resolution.ts +1 -14
  143. package/src/home-paths.ts +77 -2
  144. package/src/host/agent-host.ts +1 -1
  145. package/src/host/bridge-host.ts +65 -6
  146. package/src/metro-plugin.ts +1 -1
  147. package/src/runtime-assets.ts +1 -1
  148. package/src/vite-plugin.ts +1 -0
  149. package/dist-cli/chunks/agent-V3GVCRPL.js +0 -46
  150. package/dist-cli/chunks/auto-bootstrap-YXDP3WKH.js +0 -2
  151. package/dist-cli/chunks/beta-FGSMZWLP.js +0 -2
  152. package/dist-cli/chunks/chunk-2IFWVIKM.js +0 -2
  153. package/dist-cli/chunks/chunk-3T36XLP3.js +0 -119
  154. package/dist-cli/chunks/chunk-5KY7TQ42.js +0 -5
  155. package/dist-cli/chunks/chunk-BQOMDL4K.js +0 -1
  156. package/dist-cli/chunks/chunk-FHMPJZKM.js +0 -7
  157. package/dist-cli/chunks/chunk-GQAWFQNN.js +0 -23
  158. package/dist-cli/chunks/chunk-HJTKKF7M.js +0 -13
  159. package/dist-cli/chunks/chunk-K4JAWKOY.js +0 -1
  160. package/dist-cli/chunks/chunk-NXGQS7IR.js +0 -2
  161. package/dist-cli/chunks/chunk-R2INPDGW.js +0 -1
  162. package/dist-cli/chunks/chunk-SLL5SZN4.js +0 -2
  163. package/dist-cli/chunks/chunk-XM66K64H.js +0 -79
  164. package/dist-cli/chunks/cli-version-3L6FFDIX.js +0 -2
  165. package/dist-cli/chunks/config-5IB6ONJ2.js +0 -45
  166. package/dist-cli/chunks/control-BM4V27OB.js +0 -2
  167. package/dist-cli/chunks/daemon-62MZT4G4.js +0 -67
  168. package/dist-cli/chunks/drivers-V6QDFZQD.js +0 -2
  169. package/dist-cli/chunks/flow-VBM6TU32.js +0 -2
  170. package/dist-cli/chunks/help-XWKNL743.js +0 -2
  171. package/dist-cli/chunks/home-paths-G23H3RO6.js +0 -2
  172. package/dist-cli/chunks/install-5YL7FITA.js +0 -2
  173. package/dist-cli/chunks/launch-DYFE4CAD.js +0 -16
  174. package/dist-cli/chunks/optional-demo-registry-JJXPB3UD.js +0 -2
  175. package/dist-cli/chunks/runtime-NRMC3U3D.js +0 -2
  176. package/dist-cli/chunks/runtime-delivery-RO6CTYPQ.js +0 -2
  177. package/dist-cli/chunks/setup-repo-AK6QOUE7.js +0 -2
  178. package/dist-cli/chunks/skills-KDSOWUOE.js +0 -10
  179. package/dist-cli/chunks/start-GH2AOUX6.js +0 -23
  180. package/dist-cli/chunks/store-JHW65VGQ.js +0 -2
  181. package/dist-cli/chunks/telemetry-SZLRXJXW.js +0 -2
  182. package/dist-cli/chunks/upload-KKEWETA5.js +0 -2
  183. package/dist-cli/chunks/version-37OD33SE.js +0 -4
  184. package/dist-cli/chunks/web-4JVMIBTB.js +0 -2
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.169 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.170 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.169 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.170 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.169 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.170 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -33,6 +33,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
33
33
  var dev_server_scanner_exports = {};
34
34
  __export(dev_server_scanner_exports, {
35
35
  __applyManifestForTests: () => __applyManifestForTests,
36
+ __isSootSelfServerForTests: () => __isSootSelfServerForTests,
36
37
  __resetScannerCache: () => __resetScannerCache,
37
38
  __shouldReuseScannerCacheEntry: () => __shouldReuseScannerCacheEntry,
38
39
  discoverListeningPorts: () => discoverListeningPorts,
@@ -412,12 +413,11 @@ async function probePort(port, buildIconProxyUrl) {
412
413
  return null;
413
414
  }
414
415
  function isSootSelfServer(server) {
415
- const projectName = server.projectName?.trim().toLowerCase();
416
- if (projectName === "soot" || projectName === "sootsim") return true;
417
416
  const bundleId = server.bundleId?.trim().toLowerCase();
418
- if (bundleId?.startsWith("dev.soot")) return true;
417
+ if (bundleId?.startsWith("dev.sootsim.")) return true;
419
418
  return false;
420
419
  }
420
+ var __isSootSelfServerForTests = isSootSelfServer;
421
421
  var portCache = /* @__PURE__ */ new Map();
422
422
  var NEGATIVE_CACHE_TTL_MS = 3e4;
423
423
  var WEAK_RESULT_CACHE_TTL_MS = 1500;
@@ -507,6 +507,7 @@ async function scanDevServers(opts = {}) {
507
507
  // Annotate the CommonJS export names for ESM import in node:
508
508
  0 && (module.exports = {
509
509
  __applyManifestForTests,
510
+ __isSootSelfServerForTests,
510
511
  __resetScannerCache,
511
512
  __shouldReuseScannerCacheEntry,
512
513
  discoverListeningPorts,
package/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.169 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.170 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.169 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.170 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // cli/commands/inspect/core.ts
4
4
  function resolveMaxMsFlag(args, fallbackMs) {
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.169 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.170 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -3249,8 +3249,8 @@ var init_registry = __esm({
3249
3249
  {
3250
3250
  range: ">=3.1.0",
3251
3251
  coverage: 0.85,
3252
- note: "ExpoSpeechRecognition module and Web Speech wrapper backed by browser SpeechRecognition when available",
3253
- working: "ExpoSpeechRecognitionModule start/stop/abort/state, native event addListener/remove, granted permission helpers, recognition availability, speech services/default-service helpers, supported locales, iOS audio-session getters/setters, ExpoWebSpeechRecognition class, grammar/list classes, constants",
3252
+ note: "native-seam only \u2014 upstream expo-speech-recognition JS (ExpoWebSpeechRecognition, useSpeechRecognitionEvent, constants, and direct module API) runs unchanged from the bundle; SootSim registers only the ExpoSpeechRecognition native module, backed by the host-service SpeechRecognition bridge when available",
3253
+ working: "ExpoSpeechRecognition native module start/stop/abort/state, native event addListener/remove, granted permission helpers, recognition availability, speech services/default-service helpers, supported-locales shape, iOS audio-session getters/setters; upstream ExpoWebSpeechRecognition class, grammar/list classes, hook, and constants stay authoritative",
3254
3254
  missing: "native SFSpeechRecognizer and Android SpeechRecognizer engines, real microphone permission prompts, audioSource transcription, recording persistence, locale installation data, offline model downloads, volume metering"
3255
3255
  }
3256
3256
  ]
@@ -10350,10 +10350,8 @@ async function probePort(port, buildIconProxyUrl) {
10350
10350
  return null;
10351
10351
  }
10352
10352
  function isSootSelfServer(server) {
10353
- const projectName = server.projectName?.trim().toLowerCase();
10354
- if (projectName === "soot" || projectName === "sootsim") return true;
10355
10353
  const bundleId = server.bundleId?.trim().toLowerCase();
10356
- if (bundleId?.startsWith("dev.soot")) return true;
10354
+ if (bundleId?.startsWith("dev.sootsim.")) return true;
10357
10355
  return false;
10358
10356
  }
10359
10357
  function isWeakCachedResult(result) {
@@ -10507,6 +10505,27 @@ function sootsimHomeDir() {
10507
10505
  if (override && override.length > 0) return import_node_path.default.resolve(override);
10508
10506
  return import_node_path.default.join((0, import_node_os.homedir)(), ".sootsim");
10509
10507
  }
10508
+ function isSootsimDevCheckout() {
10509
+ if (process.env.SOOTSIM_FORCE_DAEMON_INSTALL === "1") return false;
10510
+ const env = process.env.SOOTSIM_DEV;
10511
+ if (env === "1" || env === "true") return true;
10512
+ if (env === "0" || env === "false") return false;
10513
+ const argv1 = process.argv[1];
10514
+ if (!argv1) return false;
10515
+ try {
10516
+ const real = import_node_fs2.default.realpathSync(argv1);
10517
+ return real.includes(`${import_node_path.default.sep}packages${import_node_path.default.sep}sootsim${import_node_path.default.sep}`);
10518
+ } catch {
10519
+ return false;
10520
+ }
10521
+ }
10522
+ function isDevWorkstation() {
10523
+ if (process.env.SOOTSIM_FORCE_DAEMON_INSTALL === "1") return false;
10524
+ return process.env.IS_TAMAGUI_DEV === "1";
10525
+ }
10526
+ function shouldSkipPersistentDaemon() {
10527
+ return isSootsimDevCheckout() || isDevWorkstation();
10528
+ }
10510
10529
  function runtimesDir() {
10511
10530
  return import_node_path.default.join(sootsimHomeDir(), "runtimes");
10512
10531
  }
@@ -10531,6 +10550,9 @@ function cacheDir() {
10531
10550
  function daemonLockfilePath() {
10532
10551
  return import_node_path.default.join(sootsimHomeDir(), DAEMON_LOCKFILE);
10533
10552
  }
10553
+ function devBridgeLockfilePath() {
10554
+ return import_node_path.default.join(sootsimHomeDir(), DEV_BRIDGE_LOCKFILE);
10555
+ }
10534
10556
  function configFilePath() {
10535
10557
  return import_node_path.default.join(sootsimHomeDir(), CONFIG_FILE);
10536
10558
  }
@@ -10636,6 +10658,25 @@ function readDaemonLockfile() {
10636
10658
  return null;
10637
10659
  }
10638
10660
  }
10661
+ function readDevBridgeLockfile() {
10662
+ try {
10663
+ const fd = import_node_fs2.default.openSync(devBridgeLockfilePath(), "r");
10664
+ try {
10665
+ const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES);
10666
+ const bytesRead = import_node_fs2.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
10667
+ const raw = buf.subarray(0, bytesRead).toString("utf8");
10668
+ const parsed = JSON.parse(raw);
10669
+ if (parsed && parsed.schema === 1 && parsed.source === "vite-dev" && typeof parsed.pid === "number" && typeof parsed.bridgePort === "number" && typeof parsed.runtimePort === "number" && typeof parsed.cwd === "string" && typeof parsed.startedAt === "number" && typeof parsed.heartbeatAt === "number") {
10670
+ return parsed;
10671
+ }
10672
+ return null;
10673
+ } finally {
10674
+ import_node_fs2.default.closeSync(fd);
10675
+ }
10676
+ } catch {
10677
+ return null;
10678
+ }
10679
+ }
10639
10680
  function isDaemonLockfileFresh(lock, now = Date.now()) {
10640
10681
  if (!lock) return false;
10641
10682
  if (now - lock.heartbeatAt > DAEMON_HEARTBEAT_STALE_MS) return false;
@@ -10646,6 +10687,16 @@ function isDaemonLockfileFresh(lock, now = Date.now()) {
10646
10687
  return false;
10647
10688
  }
10648
10689
  }
10690
+ function isDevBridgeLockfileFresh(lock, now = Date.now()) {
10691
+ if (!lock) return false;
10692
+ if (now - lock.heartbeatAt > DAEMON_HEARTBEAT_STALE_MS) return false;
10693
+ try {
10694
+ process.kill(lock.pid, 0);
10695
+ return true;
10696
+ } catch {
10697
+ return false;
10698
+ }
10699
+ }
10649
10700
  function writeDaemonLockfile(data) {
10650
10701
  ensureSootsimHome();
10651
10702
  const tmp = `${daemonLockfilePath()}.tmp`;
@@ -10653,6 +10704,13 @@ function writeDaemonLockfile(data) {
10653
10704
  `, "utf8");
10654
10705
  import_node_fs2.default.renameSync(tmp, daemonLockfilePath());
10655
10706
  }
10707
+ function writeDevBridgeLockfile(data) {
10708
+ ensureSootsimHome();
10709
+ const tmp = `${devBridgeLockfilePath()}.tmp`;
10710
+ import_node_fs2.default.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}
10711
+ `, "utf8");
10712
+ import_node_fs2.default.renameSync(tmp, devBridgeLockfilePath());
10713
+ }
10656
10714
  function claimDaemonLockfile(data) {
10657
10715
  ensureSootsimHome();
10658
10716
  const existing = readDaemonLockfile();
@@ -10668,7 +10726,13 @@ function removeDaemonLockfile() {
10668
10726
  } catch {
10669
10727
  }
10670
10728
  }
10671
- var import_node_fs2, import_node_os, import_node_path, SOOTSIM_HOME_ENV, ACTIVE_RUNTIME_FILE, DAEMON_LOCKFILE, CONFIG_FILE, DAEMON_HEARTBEAT_STALE_MS, DAEMON_LOCKFILE_MAX_BYTES;
10729
+ function removeDevBridgeLockfile() {
10730
+ try {
10731
+ import_node_fs2.default.unlinkSync(devBridgeLockfilePath());
10732
+ } catch {
10733
+ }
10734
+ }
10735
+ var import_node_fs2, import_node_os, import_node_path, SOOTSIM_HOME_ENV, ACTIVE_RUNTIME_FILE, DAEMON_LOCKFILE, DEV_BRIDGE_LOCKFILE, CONFIG_FILE, DAEMON_HEARTBEAT_STALE_MS, DAEMON_LOCKFILE_MAX_BYTES;
10672
10736
  var init_home_paths = __esm({
10673
10737
  "src/home-paths.ts"() {
10674
10738
  "use strict";
@@ -10678,6 +10742,7 @@ var init_home_paths = __esm({
10678
10742
  SOOTSIM_HOME_ENV = "SOOTSIM_HOME";
10679
10743
  ACTIVE_RUNTIME_FILE = "active";
10680
10744
  DAEMON_LOCKFILE = "daemon.json";
10745
+ DEV_BRIDGE_LOCKFILE = "dev-bridge.json";
10681
10746
  CONFIG_FILE = "config.json";
10682
10747
  DAEMON_HEARTBEAT_STALE_MS = 3e4;
10683
10748
  DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
@@ -12860,10 +12925,12 @@ var init_bridge_host = __esm({
12860
12925
  maxConcurrentAutomationSims;
12861
12926
  automationSimActiveGraceMs;
12862
12927
  shouldWriteLockfile;
12928
+ shouldWriteDevLockfile;
12863
12929
  sootbeanOrigin = null;
12864
12930
  effectivePort = 0;
12865
12931
  startedAt = 0;
12866
12932
  heartbeatTimer = null;
12933
+ devHeartbeatTimer = null;
12867
12934
  // ws-level heartbeat: sims that hang up uncleanly (page navigated, network
12868
12935
  // dropped, sim crashed) leave their server-side WebSocket sitting "open"
12869
12936
  // forever. ping every WS_HEARTBEAT_INTERVAL_MS; if the previous round's
@@ -12890,6 +12957,7 @@ var init_bridge_host = __esm({
12890
12957
  this.preferredPort = opts.port || DEFAULT_SOOTSIM_BRIDGE_PORT;
12891
12958
  this.port = this.preferredPort;
12892
12959
  this.shouldWriteLockfile = opts.writeLockfile === true;
12960
+ this.shouldWriteDevLockfile = opts.writeDevLockfile === true;
12893
12961
  this.portFallbackCount = Math.max(1, opts.portFallbackCount ?? 10);
12894
12962
  this.openUrlHandler = opts.openUrl;
12895
12963
  this.agentHost = new AgentHost({ getExcludePorts: opts.agentScanExcludes });
@@ -13407,7 +13475,7 @@ var init_bridge_host = __esm({
13407
13475
  });
13408
13476
  });
13409
13477
  }
13410
- /** after a successful bind: start the cli idle sweep, write the daemon
13478
+ /** after a successful bind: start the cli idle sweep, write the daemon/dev
13411
13479
  * lockfile (if this host owns it), seed the agent host, kick off the
13412
13480
  * heartbeat loop. idempotent across rebinds because close() tears down
13413
13481
  * every timer and the lockfile. */
@@ -13449,6 +13517,23 @@ var init_bridge_host = __esm({
13449
13517
  this.heartbeatTimer.unref();
13450
13518
  this.startRuntimeUpdater();
13451
13519
  }
13520
+ if (this.shouldWriteDevLockfile) {
13521
+ try {
13522
+ this.writeDevLockfileSnapshot();
13523
+ } catch (err) {
13524
+ process.stderr.write(
13525
+ `ws bridge failed to write dev bridge lockfile: ${String(err)}
13526
+ `
13527
+ );
13528
+ }
13529
+ this.devHeartbeatTimer = setInterval(() => {
13530
+ try {
13531
+ this.writeDevLockfileSnapshot();
13532
+ } catch {
13533
+ }
13534
+ }, DAEMON_HEARTBEAT_INTERVAL_MS);
13535
+ this.devHeartbeatTimer.unref();
13536
+ }
13452
13537
  void this.agentHost.seedOnBoot();
13453
13538
  }
13454
13539
  bootstrapping = true;
@@ -13466,9 +13551,25 @@ var init_bridge_host = __esm({
13466
13551
  bootstrapping: this.bootstrapping
13467
13552
  };
13468
13553
  }
13554
+ buildDevLockfileSnapshot() {
13555
+ return {
13556
+ schema: 1,
13557
+ pid: process.pid,
13558
+ platform: process.platform,
13559
+ bridgePort: this.effectivePort,
13560
+ runtimePort: this.effectivePort,
13561
+ cwd: process.cwd(),
13562
+ startedAt: this.startedAt,
13563
+ heartbeatAt: Date.now(),
13564
+ source: "vite-dev"
13565
+ };
13566
+ }
13469
13567
  writeLockfileSnapshot() {
13470
13568
  writeDaemonLockfile(this.buildLockfileSnapshot());
13471
13569
  }
13570
+ writeDevLockfileSnapshot() {
13571
+ writeDevBridgeLockfile(this.buildDevLockfileSnapshot());
13572
+ }
13472
13573
  refreshActiveRuntime() {
13473
13574
  this.activeRuntimeVersion = readActiveRuntime();
13474
13575
  this.activeRuntimeDirPath = activeRuntimeDir();
@@ -13704,6 +13805,7 @@ var init_bridge_host = __esm({
13704
13805
  bridgePort: this.effectivePort,
13705
13806
  runtimePort: this.effectivePort,
13706
13807
  activeRuntime: this.activeRuntimeVersion,
13808
+ bridgeSource: this.shouldWriteDevLockfile ? "vite-dev" : this.shouldWriteLockfile ? "daemon" : "embedded",
13707
13809
  startedAt: this.startedAt,
13708
13810
  uptimeMs: this.startedAt > 0 ? Date.now() - this.startedAt : 0
13709
13811
  })
@@ -14085,6 +14187,10 @@ var init_bridge_host = __esm({
14085
14187
  clearInterval(this.heartbeatTimer);
14086
14188
  this.heartbeatTimer = null;
14087
14189
  }
14190
+ if (this.devHeartbeatTimer) {
14191
+ clearInterval(this.devHeartbeatTimer);
14192
+ this.devHeartbeatTimer = null;
14193
+ }
14088
14194
  if (this.wsHeartbeatTimer) {
14089
14195
  clearInterval(this.wsHeartbeatTimer);
14090
14196
  this.wsHeartbeatTimer = null;
@@ -14099,6 +14205,12 @@ var init_bridge_host = __esm({
14099
14205
  } catch {
14100
14206
  }
14101
14207
  }
14208
+ if (this.shouldWriteDevLockfile) {
14209
+ try {
14210
+ removeDevBridgeLockfile();
14211
+ } catch {
14212
+ }
14213
+ }
14102
14214
  this.effectivePort = 0;
14103
14215
  this.startedAt = 0;
14104
14216
  this.agentHost.close();
@@ -14708,6 +14820,10 @@ __export(ws_bridge_exports, {
14708
14820
  resolveDefaultBridgePort: () => resolveDefaultBridgePort
14709
14821
  });
14710
14822
  function resolveDefaultBridgePort() {
14823
+ if (shouldSkipPersistentDaemon()) {
14824
+ const dev = readDevBridgeLockfile();
14825
+ if (dev && isDevBridgeLockfileFresh(dev)) return dev.bridgePort;
14826
+ }
14711
14827
  const lock = readDaemonLockfile();
14712
14828
  if (lock && isDaemonLockfileFresh(lock)) return lock.bridgePort;
14713
14829
  return DEFAULT_SOOTSIM_BRIDGE_PORT;
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.169 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.170 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -74,7 +74,7 @@ function readActiveRuntime() {
74
74
  var DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
75
75
 
76
76
  // src/runtime-assets.ts
77
- var SOOTSIM_RUNTIME_MISSING_MESSAGE = "[sootsim] no engine runtime installed \u2014 run `sootsim setup-repo` in your project, or `sootsim runtime install`";
77
+ var SOOTSIM_RUNTIME_MISSING_MESSAGE = "[sootsim] no engine runtime installed \u2014 run `sootsim setup`, or `sootsim runtime install`";
78
78
  var MIME_TYPES = {
79
79
  ".js": "application/javascript",
80
80
  ".mjs": "application/javascript",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sootsim",
3
- "version": "0.1.169",
3
+ "version": "0.1.170",
4
4
  "description": "sootsim CLI + vite/metro plugins + skills registry. bridge client for driving the proprietary sootsim-engine over WebSocket.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "MIT",
@@ -132,6 +132,7 @@
132
132
  "dist-cli/",
133
133
  "dist-lib/",
134
134
  "src/",
135
+ "skills/",
135
136
  "scripts/dev-server-scanner.ts",
136
137
  "scripts/optional-demo-registry.ts",
137
138
  "detox/",
@@ -556,15 +556,14 @@ export interface ScanOptions {
556
556
  }
557
557
 
558
558
  function isSootSelfServer(server: DiscoveredServer): boolean {
559
- const projectName = server.projectName?.trim().toLowerCase()
560
- if (projectName === 'soot' || projectName === 'sootsim') return true
561
-
562
559
  const bundleId = server.bundleId?.trim().toLowerCase()
563
- if (bundleId?.startsWith('dev.soot')) return true
560
+ if (bundleId?.startsWith('dev.sootsim.')) return true
564
561
 
565
562
  return false
566
563
  }
567
564
 
565
+ export const __isSootSelfServerForTests = isSootSelfServer
566
+
568
567
  // per-(port, pid) probe cache. as long as the same process still owns the
569
568
  // port, its signature endpoints can't have changed, so we return the cached
570
569
  // result without issuing any HTTP. negative results (null) are cached too,
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  // postinstall — best-effort runtime install after `npm i -g sootsim`.
3
3
  //
4
- // runs as a child of npm. failures here are non-fatal: any first invocation
5
- // of the CLI will retry the install via auto-bootstrap.
4
+ // runs as a child of npm. failures here are non-fatal; setup/open/runtime
5
+ // commands can install the runtime later.
6
6
  //
7
7
  // skipped when running as root via sudo: $HOME would be /root, but the user
8
8
  // who actually wants the runtime probably isn't root. their first sootsim
@@ -11,8 +11,8 @@
11
11
  // reliability: npm preuninstall hooks are best-effort — they don't fire when
12
12
  // the package wasn't installed by npm (yarn/bun/pnpm have their own lifecycle
13
13
  // rules), and they don't run as part of `npm cache verify` cleanup or manual
14
- // dir removal. that's why we also document `sootsim daemon uninstall` as the
15
- // manual fallback and show a hint pointing at it.
14
+ // dir removal. that's why `sootsim daemon uninstall` is also documented as
15
+ // the manual teardown path.
16
16
  //
17
17
  // strategy: spawn the bundled bin to run `daemon uninstall` (single source of
18
18
  // truth — the same logic the user invokes manually). if that fails for any
@@ -73,17 +73,15 @@ function rawCleanup() {
73
73
  const home = sootsimHomeDir()
74
74
  if (process.platform === 'darwin') {
75
75
  safeSpawn('launchctl', ['bootout', `gui/${getuid()}/dev.sootsim.daemon`])
76
- safeSpawn('launchctl', ['bootout', `gui/${getuid()}/dev.sootsim.server`])
77
76
  safeRemove(path.join(os.homedir(), 'Library/LaunchAgents/dev.sootsim.daemon.plist'))
78
- safeRemove(path.join(os.homedir(), 'Library/LaunchAgents/dev.sootsim.server.plist'))
79
77
  safeRemove(path.join(os.homedir(), 'Library/Logs/sootsim'), {
80
78
  recursive: true,
81
79
  })
82
80
  } else if (process.platform === 'linux') {
83
- safeSpawn('systemctl', ['--user', 'disable', '--now', 'sootsim-server'])
81
+ safeSpawn('systemctl', ['--user', 'disable', '--now', 'sootsim-daemon'])
84
82
  safeSpawn('systemctl', ['--user', 'daemon-reload'])
85
- safeSpawn('systemctl', ['--user', 'reset-failed', 'sootsim-server'])
86
- safeRemove(path.join(os.homedir(), '.config/systemd/user/sootsim-server.service'))
83
+ safeSpawn('systemctl', ['--user', 'reset-failed', 'sootsim-daemon'])
84
+ safeRemove(path.join(os.homedir(), '.config/systemd/user/sootsim-daemon.service'))
87
85
  safeRemove(path.join(os.homedir(), '.local/state/sootsim'), { recursive: true })
88
86
  }
89
87
  // ~/.sootsim is the same on every platform; honor $SOOTSIM_HOME.
package/skills/soot.md ADDED
@@ -0,0 +1,178 @@
1
+ ---
2
+ name: soot
3
+ description: >-
4
+ index of all Soot and SootSim agent skills and CLI commands.
5
+ auto-generated from the CLI info registry — do not edit by hand.
6
+ ---
7
+
8
+ # soot
9
+
10
+ overview of all available skills and CLI commands in this repo.
11
+
12
+ > this file is auto-generated by `bun scripts/generate-soot-skill.ts`.
13
+ > to update, edit the source registries and re-run the generator.
14
+
15
+ ## sootsim CLI commands
16
+
17
+ the `sootsim` CLI is the operational surface for booting shells, driving
18
+ automation, inspecting the render tree, and managing sims.
19
+
20
+ | command | description |
21
+ | --- | --- |
22
+ | `sootsim preview` | production-like preview build |
23
+ | `sootsim test` | run tests against sootsim |
24
+ | `sootsim flow` | run flows through the current sim or manage a draft from live inspect actions |
25
+ | `sootsim maestro` | run existing Maestro YAML flows against sootsim |
26
+ | `sootsim detox` | run existing Detox suites against sootsim on headless Chromium |
27
+ | `sootsim record` | capture the live sim as webm/mp4/gif, or sample N png frames (--frames) |
28
+ | `sootsim profile` | manage isolated storage profiles |
29
+ | `sootsim storage-clear` | clear the current guest app storage and reload it |
30
+ | `sootsim cpu-profile` | capture a JS CPU profile of the sim workers over the Chrome DevTools Protocol |
31
+ | `sootsim react` | inspect React component render cost in the tenant worker |
32
+ | `sootsim assert` | run any read verb and convert its output into an exit code |
33
+ | `sootsim screenshot` | capture the canvas (full or cropped) as PNG |
34
+ | `sootsim get` | read runtime state (tree, a11y, url, count, errors, …) |
35
+ | `sootsim screenshots` | capture + compose app-store screenshot sets from a single plan |
36
+ | `sootsim do` | drive the app (tap, type, scroll, swipe, key, reload, …) |
37
+ | `sootsim screenshots-capture` | headlessly (re)capture editable App Store slides and upload them to your org |
38
+ | `sootsim screenshot-mode` | toggle the screenshot-mode chrome overlay |
39
+ | `sootsim wait` | block until the runtime reaches a known state |
40
+ | `sootsim shell` | drive shell state and simulator chrome |
41
+ | `sootsim slides` | compose App Store screenshot slides in the live sim |
42
+ | `sootsim three-mode` | toggle the live browser 3d device stage |
43
+ | `sootsim upload` | publish the current recorded bundle as a shareable /preview/<id> link |
44
+ | `sootsim device` | inspect or change the live device preset for a sim |
45
+ | `sootsim inspect` | query, describe, and drive the running sootsim instance |
46
+ | `sootsim network` | inspect live network traffic from the running sootsim worker |
47
+ | `sootsim logs` | inspect live console output from the running sootsim worker |
48
+ | `sootsim debug` | drive __sootsimDebug: channels, snapshots, and inspectors |
49
+ | `sootsim camera` | drive the sim camera — inject synthetic barcode scans |
50
+ | `sootsim what-happened` | show recent semantic events: toasts, keyboards, screens, alerts, fetches, errors |
51
+ | `sootsim diagnose` | composed read commands for "what just went wrong?" |
52
+ | `sootsim timeline` | control the semantic event timeline (enable opt-in kinds, clear, dump) |
53
+ | `sootsim open` | load a target into the current sim or open a new one |
54
+ | `sootsim list` | list connected sims or available launch drivers |
55
+ | `sootsim use` | select a connected sim |
56
+ | `sootsim skill` | install and inspect bundled SootSim agent skills |
57
+ | `sootsim claim` | take an exclusive lease on a sim |
58
+ | `sootsim agent` | attached projects + local agent sessions (codex/claude) |
59
+ | `sootsim close` | close a connected sim |
60
+ | `sootsim setup` | set up this machine and, optionally, this repo |
61
+ | `sootsim compat` | check package compatibility (alias: scan) |
62
+ | `sootsim electron` | launch the desktop companion |
63
+ | `sootsim daemon` | manage the sootsim bridge as a login agent |
64
+ | `sootsim version` | show the CLI + engine runtime versions you are running |
65
+ | `sootsim upgrade` | update the sootsim CLI and engine runtime to the latest |
66
+ | `sootsim serve` | run the sootsim bridge in the foreground |
67
+ | `sootsim runtime` | manage engine runtimes under ~/.sootsim/runtimes/ |
68
+ | `sootsim install-desktop` | download and install the optional desktop GUI |
69
+ | `sootsim login` | sign in so preview uploads are associated with your account |
70
+ | `sootsim logout` | clear the local desktop auth state |
71
+ | `sootsim whoami` | print the currently signed-in account |
72
+ | `sootsim keys` | manage the api keys that authorize CI preview uploads |
73
+ | `sootsim hints` | inspect and manage the CLI hint registry |
74
+
75
+ ### runtime verb groups
76
+
77
+ every runtime verb is organized under one of these grouping prefixes.
78
+ run `sootsim --help` to see the full list under each group.
79
+
80
+ - `sootsim describe` — summarize the current UI
81
+ - `sootsim find <...>` — unified finder (bare text, --testid, --role, --type, --pressable, --visible)
82
+ - `sootsim get <noun>` — read runtime state (tree, a11y, url, count, errors, warnings, requests, globals, layout, node)
83
+ - `sootsim do <action>` — drive the app (tap, tap-text, tap-id, type, type-into, key, dismiss, scroll, drag, swipe, pinch, reload, shell, sleep, settle, wait)
84
+ - `sootsim debug <tool>` — instrumentation (state, js, eval, perf, sample-color, enable/disable channels, snapshots, diff, record, recent)
85
+
86
+ ### global flags
87
+
88
+ - `-h, --help` — show help
89
+ - `-V, --version` — show version
90
+ - `-v, --verbose` — verbose output
91
+ - `-p, --port <number>` — bridge or preview port, depending on command
92
+ - `-d, --device <model>` — override the simulated device model
93
+ - `-t, --theme <scheme>` — override light, dark, or auto color scheme
94
+ - `--headless` — run browser automation without a visible window where supported
95
+
96
+ ## bundled SootSim agent skills
97
+
98
+ skills for working with the SootSim native preview engine:
99
+
100
+ | skill | description |
101
+ | --- | --- |
102
+ | `/sootsim-debug` | Debug rendering, performance, and accessibility issues in your React Native app under sootsim |
103
+ | `/sootsim-setup` | Get sootsim connected to your React Native app — from `npm install sootsim` to a driveable sim |
104
+ | `/sootsim-test` | Run automated tests against your React Native app under sootsim — native YAML flows, Maestro drop-in, or Detox drop-in |
105
+ | `/sootsim-visual` | Pixel-level rendering comparison between sootsim and native iOS — fonts, gradients, shadows, image fidelity |
106
+
107
+ ## automatic settling
108
+
109
+ every CLI command already waits at the right moment — **do not wrap
110
+ commands in your own `sleep` or chain `do settle` "just to be safe"**.
111
+
112
+ - **before read commands** (`describe`, `find`, `get`, `tree`, `node`,
113
+ `layout`, `screenshot`, `capture`, `a11y`, `sample-color`, `count`,
114
+ and every gesture — `tap`, `tap-id`, `tap-text`, `type-into`,
115
+ `touch`, `swipe`, `drag`, `scroll`, `pinch`, `long-press`,
116
+ `double-tap`, `gesture`) the CLI waits up to **1800 ms** for any
117
+ in-flight screen transition to actually start, so the tree you read is
118
+ never mid-flip.
119
+ - **after write commands** (`tap`, `tap-text`, `tap-id`, `type`,
120
+ `type-into`, `key`, `key-sequence`, `keycode`, `drag`, `swipe`,
121
+ `long-press`, `touch`, `gesture`, `pinch`, `scroll`, `shell`)
122
+ the CLI polls the layout hash until it is stable for two consecutive
123
+ frames. budget: **1200 ms for agents** (`SOOTSIM_AGENT=1`, set by the
124
+ harness), 350 ms for humans.
125
+
126
+ what this means:
127
+
128
+ - `sootsim do tap-id login` then `sootsim find "Welcome"` Just Works™.
129
+ - you only need `sootsim do settle <seconds>` or `sootsim wait idle
130
+ --max-ms <N>` when the CLI explicitly prints
131
+ `⚠ auto-wait timed out after Nms — next command may see mid-animation
132
+ state`, or when the transition you care about lasts longer than ~1.2 s
133
+ (full stack navigation, crossfades, video load).
134
+ - opt-out: `--no-wait` on a single command, `SOOTSIM_NO_AUTO_WAIT=1`
135
+ for a whole lane.
136
+ - if a read keeps racing its write, the fix is **never** a blind `sleep` —
137
+ prefer `wait selector`, `wait ready`, `wait idle`, or a longer
138
+ explicit `do settle`, and investigate whether the app's animation
139
+ completion signal is actually firing.
140
+
141
+ ## quick reference
142
+
143
+ ### dev workflow
144
+
145
+ ```sh
146
+ bun dev # start everything
147
+ sootsim dev -- pnpm dev # wrap a bundler
148
+ sootsim open 8081 # load an app
149
+ sootsim describe # inspect the UI tree
150
+ ```
151
+
152
+ ### debugging
153
+
154
+ ```sh
155
+ sootsim get errors 5 # recent console errors
156
+ sootsim get requests 5 # recent failed requests
157
+ sootsim debug snapshot before # capture state
158
+ sootsim debug diff before after # compare snapshots
159
+ sootsim debug state shell # runtime state dump
160
+ ```
161
+
162
+ ### testing
163
+
164
+ ```sh
165
+ sootsim test # run tests
166
+ sootsim flow flows/login.yaml # run a YAML flow
167
+ sootsim flow start # start recording a flow
168
+ ```
169
+
170
+ ### inspection
171
+
172
+ ```sh
173
+ sootsim describe # visible elements
174
+ sootsim find "Sign in" # find by text
175
+ sootsim find --testid loginButton # find by testID
176
+ sootsim do tap-text "Submit" # tap by text
177
+ sootsim do type "hello" # type text
178
+ ```