stim 1.6.0 → 1.7.0

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 (71) hide show
  1. package/README.md +1 -1
  2. package/dist/{android-BkYmndPp.mjs → android-DWssqHQ8.mjs} +165 -661
  3. package/dist/{android-DLJB8ljN.mjs → android-Da-G_o3n.mjs} +646 -548
  4. package/dist/{android-BW8i_YxU.mjs → android-DeYdjjBP.mjs} +6 -2
  5. package/dist/{android-cas-CrzI_Zha.mjs → android-cas-SWIjzeuL.mjs} +54 -18
  6. package/dist/android-cas-compiler.mjs +1 -1
  7. package/dist/{app-install--PAocWvq.mjs → app-install-DJg7bVv-.mjs} +36 -10
  8. package/dist/build-lock-CgtsB6TV.mjs +604 -0
  9. package/dist/{build-slots-CuPszJDu.mjs → build-slots-C-oAHRT1.mjs} +2 -2
  10. package/dist/{cache-manifest-uoOKhgUD.mjs → cache-manifest-CYJi8-Hx.mjs} +2 -2
  11. package/dist/cache-manifest.d.mts +1 -1
  12. package/dist/cache-manifest.mjs +1 -1
  13. package/dist/cli.mjs +14 -14
  14. package/dist/collector-run.d.mts +2 -0
  15. package/dist/collector-run.mjs +7 -7
  16. package/dist/{command-output-BuIb_ZOI.mjs → command-output-B3ycpsYv.mjs} +2 -2
  17. package/dist/{config-DMP7L7H6.mjs → config-B7iyKWkL.mjs} +56 -16
  18. package/dist/{dependency-state-CTL8A9p3.mjs → dependency-state-D0M7oKA7.mjs} +2 -2
  19. package/dist/{deps-C1zGnr61.mjs → deps-oCXVNEqQ.mjs} +6 -6
  20. package/dist/detached-entry-Bpo6ZY_I.mjs +65 -0
  21. package/dist/{dev-client-BaTXAAnC.mjs → dev-client-DDUkWgJL.mjs} +12 -12
  22. package/dist/{device-xoQxw8hR.mjs → device-CBSaC6BU.mjs} +7 -7
  23. package/dist/{device-XY_5eGdN.mjs → device-CLeDp-KH.mjs} +17 -15
  24. package/dist/{device-lease-DBW-IL4p.mjs → device-lease-Ffz_M5t0.mjs} +3 -3
  25. package/dist/{device-pool-C0s_83Og.mjs → device-pool-DNSHL5z8.mjs} +4 -4
  26. package/dist/{device-remote-D9wN3CdE.mjs → device-remote-BprxZp-m.mjs} +11 -11
  27. package/dist/{doctor-BRO8UKlK.mjs → doctor-CTAx9gMu.mjs} +90 -46
  28. package/dist/{error-diagnostics-B5STzxqN.mjs → error-diagnostics-D1Kaf-eT.mjs} +23 -17
  29. package/dist/{exec-CyylIdq9.mjs → exec-B29XNuhP.mjs} +14 -2
  30. package/dist/{gc-C-6grAQI.mjs → gc-CX0KRfLG.mjs} +17 -16
  31. package/dist/{guide-CYtd7xbA.mjs → guide-CPmAG9me.mjs} +38 -15
  32. package/dist/{guide-status-BF6RsJJp.mjs → guide-status-Craq9z0t.mjs} +3 -3
  33. package/dist/{ios-Ccm6c8Hj.mjs → ios-BRXYSxSc.mjs} +34 -37
  34. package/dist/{ios-device-DJOi9FR5.mjs → ios-device-CDbvn5H2.mjs} +2 -2
  35. package/dist/{ios-device-D3pzTKQE.mjs → ios-device-CFXA46ez.mjs} +1 -1
  36. package/dist/{ios-Deh5js5V.mjs → ios-lEJ5pKsq.mjs} +3 -3
  37. package/dist/{logs-D4Poojxy.mjs → logs-CakdBTtm.mjs} +6 -6
  38. package/dist/{logs-query-Cdp5aaCH.mjs → logs-query-BGL-Pl12.mjs} +1 -1
  39. package/dist/metro-C7bcJ0uo.mjs +233 -0
  40. package/dist/{named-ports-DH9SXd0f.mjs → named-ports-DzMOlmaX.mjs} +57 -42
  41. package/dist/{ownership-CD_XnpDu.mjs → ownership-Cp52QAK1.mjs} +3 -3
  42. package/dist/{ownership-CVY9cqCr.mjs → ownership-D5vkdm8H.mjs} +13 -13
  43. package/dist/{ports-A6uTJSPj.mjs → ports-BIbJaKNW.mjs} +3 -3
  44. package/dist/{process-output-C7IlIzd7.mjs → process-output-Dl5uKE03.mjs} +1 -1
  45. package/dist/{project-B_SDt4ip.mjs → project-DDZWf_16.mjs} +3 -3
  46. package/dist/{reclaim-WXTbXPX4.mjs → reclaim-D614jI6_.mjs} +11 -11
  47. package/dist/{reload-xlQvHKfN.mjs → reload-ChW46VDb.mjs} +9 -9
  48. package/dist/{server-bare-CCLb90FS.mjs → server-bare-BQkyxa_b.mjs} +1 -1
  49. package/dist/{server-expo-CX-zkgWl.mjs → server-expo-BVAA1OqJ.mjs} +23 -11
  50. package/dist/server-expo-YDDG3eP7.mjs +2 -0
  51. package/dist/{settings-CGHTt5vb.mjs → settings-BXXorVM2.mjs} +3 -3
  52. package/dist/{slot-launch-BYjnljAs.mjs → slot-launch-DbLvyrup.mjs} +4 -4
  53. package/dist/{start-DHYAyGzm.mjs → start-TTVILlUi.mjs} +111 -38
  54. package/dist/{state-CnWzvCWP.mjs → state-DZobzxLW.mjs} +2 -2
  55. package/dist/{stats-Fo9ACIv7.mjs → stats-BYcZ6n1h.mjs} +1 -1
  56. package/dist/{stats-DHrO0frH.mjs → stats-CLg5RuDS.mjs} +4 -4
  57. package/dist/{status-QeLZb8SM.mjs → status-DUNy_HZs.mjs} +14 -15
  58. package/dist/{stop-CHTaw65F.mjs → stop-CnUpnFT5.mjs} +2 -2
  59. package/dist/{stop-DsdOx9Hj.mjs → stop-ZrfODZ51.mjs} +13 -13
  60. package/dist/supervisor-run.mjs +7 -5
  61. package/dist/{teardown-sm1FwF6V.mjs → teardown-5J5UXz2P.mjs} +14 -17
  62. package/dist/{workspace-process-lock-Bz19_R9R.mjs → workspace-process-lock-BYfZz_S-.mjs} +1 -1
  63. package/dist/{workspace-state-WVwWw_hB.mjs → workspace-state-BT5E3SA2.mjs} +3 -3
  64. package/dist/{worktree-CCNPg_f0.mjs → worktree-BfE4lzHK.mjs} +21 -16
  65. package/dist/{worktree-DJnBbQZ9.mjs → worktree-FG_ZKKdb.mjs} +13 -9
  66. package/dist/{xcode-DUF7jrXj.mjs → xcode-B6YaWeg0.mjs} +8 -8
  67. package/package.json +12 -8
  68. package/shim/android-optimizations.gradle +6 -0
  69. package/dist/metro-ato6ef3J.mjs +0 -99
  70. package/dist/ports-BZprTJRa.mjs +0 -90
  71. package/dist/server-expo-CjeTJ273.mjs +0 -2
@@ -1,28 +1,29 @@
1
- import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { g as loadConfig, l as getConcurrencyLimits, o as configCorruptRepair } from "./config-DMP7L7H6.mjs";
3
- import { l as sharedBuildCache, m as workspaceDerivedData } from "./workspace-state-WVwWw_hB.mjs";
4
- import { C as filesystemDevice, E as temporaryRoot, a as dirtyFingerprintFiles, d as listWorktrees, f as locallyKnownUpstream, g as repoRoot, s as gitCommonDir, v as resolveSourceCheckout, w as makeTemporaryDirectory } from "./worktree-DJnBbQZ9.mjs";
5
- import { a as findProjectRoot, i as detectIsExpo, t as appProjectProblem } from "./project-B_SDt4ip.mjs";
6
- import { d as quotedPath, l as phaseLine, u as plural } from "./command-output-BuIb_ZOI.mjs";
7
- import { B as listBuildLocks, Z as fingerprintProject, q as diffFingerprintSources, y as listAdbDevices } from "./android-BkYmndPp.mjs";
8
- import { A as resolveSettings, E as remoteAndroidSetting, I as unknownSettingKeys, M as settingOrigin, N as settingShapeErrors, O as remoteIosSetting, P as settingsLayers, U as resolveOptimizations, V as compilerCacheFallbackMessage, a as SETTING_SHAPE_REMEDY, i as SETTINGS_WITH_RESOLVE_TIME_FALLBACK, n as PATH_SETTINGS, v as iosSimSlimProfileSetting, w as projectOptimizations, y as mergeSettingsLayers } from "./settings-CGHTt5vb.mjs";
9
- import { S as resolveEasCliBin, a as POOL_SETTING_REMEDY, b as ownerFromConfig, h as checkEasAuth$1, l as parkedMaxSetting, x as providerFromConfig } from "./teardown-sm1FwF6V.mjs";
10
- import { E as readHostMemoryPressure, T as hostMemoryPressureAdvice, u as listAllIosSims } from "./ios-Deh5js5V.mjs";
11
- import { n as listBuildSlots } from "./build-slots-CuPszJDu.mjs";
12
- import { a as liveOwnedDeviceCount, d as simslimIsOnPath } from "./device-XY_5eGdN.mjs";
13
- import { d as projectCmakeLauncher, i as apkOutputsDir, r as resolveAndroidCompilerCache, t as readAndroidCasToolchain } from "./android-cas-CrzI_Zha.mjs";
14
- import { h as podInstallCommand, i as ccacheEnabled, o as detectXcodeMajor } from "./xcode-DUF7jrXj.mjs";
15
- import { i as inspectStimVersions, n as recordDoctorRun, r as compareStimVersions } from "./guide-status-BF6RsJJp.mjs";
16
- import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-CTL8A9p3.mjs";
1
+ import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
+ import { g as loadConfig, l as getConcurrencyLimits, o as configCorruptRepair } from "./config-B7iyKWkL.mjs";
3
+ import { l as sharedBuildCache, m as workspaceDerivedData } from "./workspace-state-BT5E3SA2.mjs";
4
+ import { C as filesystemDevice, E as temporaryRoot, a as dirtyFingerprintFiles, d as listWorktrees, f as locallyKnownUpstream, g as repoRoot, s as gitCommonDir, v as resolveSourceCheckout, w as makeTemporaryDirectory } from "./worktree-FG_ZKKdb.mjs";
5
+ import { a as findProjectRoot, i as detectIsExpo, t as appProjectProblem } from "./project-DDZWf_16.mjs";
6
+ import { A as resolveSettings, E as remoteAndroidSetting, I as unknownSettingKeys, M as settingOrigin, N as settingShapeErrors, O as remoteIosSetting, P as settingsLayers, U as resolveOptimizations, V as compilerCacheFallbackMessage, a as SETTING_SHAPE_REMEDY, i as SETTINGS_WITH_RESOLVE_TIME_FALLBACK, n as PATH_SETTINGS, v as iosSimSlimProfileSetting, w as projectOptimizations, y as mergeSettingsLayers } from "./settings-BXXorVM2.mjs";
7
+ import { S as listAdbDevices, x as hostSystemImageArch } from "./android-DWssqHQ8.mjs";
8
+ import { d as quotedPath, l as phaseLine, u as plural } from "./command-output-B3ycpsYv.mjs";
9
+ import { m as fingerprintProject, r as listBuildLocks, u as diffFingerprintSources } from "./build-lock-CgtsB6TV.mjs";
10
+ import { S as resolveEasCliBin, a as POOL_SETTING_REMEDY, b as ownerFromConfig, h as checkEasAuth$1, l as parkedMaxSetting, x as providerFromConfig } from "./teardown-5J5UXz2P.mjs";
11
+ import { E as readHostMemoryPressure, T as hostMemoryPressureAdvice, u as listAllIosSims } from "./ios-lEJ5pKsq.mjs";
12
+ import { n as listBuildSlots } from "./build-slots-C-oAHRT1.mjs";
13
+ import { a as liveOwnedDeviceCount, d as simslimIsOnPath } from "./device-CLeDp-KH.mjs";
14
+ import { d as projectCmakeLauncher, g as androidPathRoomRemedy, h as androidPathRoomMessage, i as apkOutputsDir, m as androidPathRoom, r as resolveAndroidCompilerCache, t as readAndroidCasToolchain } from "./android-cas-SWIjzeuL.mjs";
15
+ import { h as podInstallCommand, i as ccacheEnabled, o as detectXcodeMajor } from "./xcode-B6YaWeg0.mjs";
16
+ import { i as inspectStimVersions, n as recordDoctorRun, r as compareStimVersions } from "./guide-status-Craq9z0t.mjs";
17
+ import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-D0M7oKA7.mjs";
17
18
  import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, rmSync } from "node:fs";
18
19
  import { InvalidArgumentError } from "commander";
19
20
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
20
21
  import { existsSync as existsSync$1, lstatSync as lstatSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, renameSync as renameSync$1, rmSync as rmSync$1, writeFileSync as writeFileSync$1 } from "fs";
21
22
  import { homedir } from "os";
22
- import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1 } from "path";
23
+ import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
23
24
  import * as expoFingerprint from "@expo/fingerprint";
24
25
  import chalk from "chalk";
25
- //#region src/sandbox.ts
26
+ //#region src/diagnostics/sandbox.ts
26
27
  /**
27
28
  * What a sandboxed harness has to permit for Stim to work: its state
28
29
  * directory, the XPC service simctl talks to, and the port adb's server binds.
@@ -111,8 +112,12 @@ function nested(source, ...keys) {
111
112
  }
112
113
  /** Whether an allowWrite entry covers the Stim home, including as a parent. */
113
114
  function covers(entry, target, home) {
114
- const e = expandHome(entry.replace(/\/+\*+$/, "").replace(/\/+$/, ""), home);
115
- const t = expandHome(target.replace(/\/+$/, ""), home);
115
+ const posix = (value) => {
116
+ const expanded = expandHome(value, home);
117
+ return sep$1 === "/" ? expanded : expanded.replaceAll(sep$1, "/");
118
+ };
119
+ const e = posix(entry.replace(/\/+\*+$/, "").replace(/\/+$/, ""));
120
+ const t = posix(target.replace(/\/+$/, ""));
116
121
  return e === t || t.startsWith(`${e}/`);
117
122
  }
118
123
  /**
@@ -224,8 +229,8 @@ function sandboxFinding(settingsRoot, { env = process.env, home = homedir(), sti
224
229
  };
225
230
  }
226
231
  //#endregion
227
- //#region src/doctor-storage.ts
228
- function checkStorageLayout(projectRoot, { platform, device = filesystemDevice, stagingRoot = temporaryRoot } = {}) {
232
+ //#region src/diagnostics/doctor-storage.ts
233
+ function checkStorageLayout(projectRoot, { platform, host = process.platform, device = filesystemDevice, stagingRoot = temporaryRoot } = {}) {
229
234
  const findings = [];
230
235
  const temporaryFix = "Unset STIM_TMPDIR / machine tempDir to select same-volume staging automatically, or point the override at a writable directory on the relevant volume outside Git working trees.";
231
236
  const check = (operation, paths, fix) => {
@@ -244,7 +249,7 @@ function checkStorageLayout(projectRoot, { platform, device = filesystemDevice,
244
249
  const cache = sharedBuildCache();
245
250
  check("Cached app/APK staging", [cache, stagingRoot(join(cache, "artifact.app"))], temporaryFix);
246
251
  const cacheFix = "Place STIM_BUILD_CACHE / machine caches.buildCache on the build-output volume, or accept the full-copy cost of keeping the cache on a separate volume.";
247
- if (platform === "ios" || platform !== "android" && existsSync(join(projectRoot, "ios"))) {
252
+ if ((platform === "ios" || platform !== "android" && existsSync(join(projectRoot, "ios"))) && host === "darwin") {
248
253
  const output = join(workspaceDerivedData(projectRoot), "Build", "Products");
249
254
  check("iOS build-cache storage", [output, cache], cacheFix);
250
255
  check("iOS device app staging", [output, stagingRoot(join(output, "artifact.app"))], temporaryFix);
@@ -261,7 +266,7 @@ function checkStorageLayout(projectRoot, { platform, device = filesystemDevice,
261
266
  return findings;
262
267
  }
263
268
  //#endregion
264
- //#region src/doctor-ios-architectures.ts
269
+ //#region src/diagnostics/doctor-ios-architectures.ts
265
270
  const COMPILED_PRODUCTS = /* @__PURE__ */ new Set([
266
271
  "com.apple.product-type.application",
267
272
  "com.apple.product-type.application.on-demand-install-capable",
@@ -397,7 +402,7 @@ function inspectIosDebugArchitectures(projectRoot) {
397
402
  return findings;
398
403
  }
399
404
  //#endregion
400
- //#region src/doctor-cxx.ts
405
+ //#region src/diagnostics/doctor-cxx.ts
401
406
  function parseCmakeCacheLauncher(source) {
402
407
  if (typeof source !== "string") return null;
403
408
  return /^CMAKE_CXX_COMPILER_LAUNCHER(?::[A-Z]+)?=(.*)$/m.exec(source)?.[1]?.trim() || null;
@@ -474,7 +479,7 @@ function repairCxxLauncherState(root) {
474
479
  refused: []
475
480
  };
476
481
  if (selectedCompilerCache(root) !== "ccache") return result;
477
- if (!getExecutor().runQuiet("command -v ccache", { timeoutMs: 5e3 })) return result;
482
+ if (!getExecutor().findExecutable("ccache")) return result;
478
483
  const appOverrides = declaredLauncher(join(root, "android", "app"));
479
484
  const targets = readCxxLauncherStates(root).filter(({ launcher }) => {
480
485
  if (!launcher) return true;
@@ -530,7 +535,7 @@ function repairCxxLauncherState(root) {
530
535
  return result;
531
536
  }
532
537
  //#endregion
533
- //#region src/doctor-config.ts
538
+ //#region src/diagnostics/doctor-config.ts
534
539
  function readMachineSettings(context) {
535
540
  try {
536
541
  const layers = settingsLayers(context);
@@ -639,7 +644,7 @@ function missingPath(value, projectRoot, exists, trim = false) {
639
644
  return opened === opened.trim() ? path : JSON.stringify(path);
640
645
  }
641
646
  //#endregion
642
- //#region src/doctor.ts
647
+ //#region src/diagnostics/doctor.ts
643
648
  function finding(level, title, detail, fix) {
644
649
  return {
645
650
  level,
@@ -768,7 +773,7 @@ function seedFindings(mainRoot, upstream) {
768
773
  if (defaultBranch !== null && defaultBranch !== branch) findings.push(finding("note", `The source checkout is on ${branch}, not the default branch ${defaultBranch}`, `Every worktree warmed from here carries ${branch}'s dependencies. \`stim worktree warm --refresh\` warns and continues; it never switches a branch under another checkout.`, `Run \`git -C ${quotedRoot} checkout ${defaultBranch}\`.`));
769
774
  return findings;
770
775
  }
771
- function checkMainCheckout(projectRoot, { npmTreeValid, brokenPods = void 0, upstream = void 0, linkedWorktrees = void 0, platform } = {}) {
776
+ function checkMainCheckout(projectRoot, { npmTreeValid, brokenPods = void 0, upstream = void 0, linkedWorktrees = void 0, platform, localIos = platform !== "android" } = {}) {
772
777
  const mainRoot = mainCheckoutProjectRoot(projectRoot);
773
778
  const findings = [];
774
779
  const dependencies = dependencyState(mainRoot);
@@ -783,7 +788,7 @@ function checkMainCheckout(projectRoot, { npmTreeValid, brokenPods = void 0, ups
783
788
  const podfileLock = join$1(mainRoot, "ios", "Podfile.lock");
784
789
  const podManifest = join$1(mainRoot, "ios", "Pods", "Manifest.lock");
785
790
  const podsRoot = join$1(mainRoot, "ios", "Pods");
786
- if (platform !== "android" && existsSync$1(podfileLock)) {
791
+ if (localIos && existsSync$1(podfileLock)) {
787
792
  let podsState = null;
788
793
  if (!existsSync$1(podManifest)) podsState = "missing";
789
794
  else try {
@@ -803,7 +808,7 @@ function checkMainCheckout(projectRoot, { npmTreeValid, brokenPods = void 0, ups
803
808
  findings.push(finding("cost", "The source checkout CocoaPods state has broken links", `${broken.length} symlink${broken.length === 1 ? "" : "s"} under ios/Pods point to missing files. Worktrees copy these broken links and can fail during compilation. First: ${broken[0]}.`, `Run \`${podCommand}\` before creating native worktrees.`));
804
809
  }
805
810
  }
806
- const coldPlatforms = [platform !== "android" && existsSync$1(join$1(mainRoot, "ios")) && !hasIosWarmOutput(mainRoot) ? "iOS" : null, platform !== "ios" && existsSync$1(join$1(mainRoot, "android")) && !existsSync$1(join$1(mainRoot, "android", "build")) && !existsSync$1(join$1(mainRoot, "android", "app", "build")) ? "Android" : null].filter((coldPlatform) => coldPlatform !== null);
811
+ const coldPlatforms = [localIos && existsSync$1(join$1(mainRoot, "ios")) && !hasIosWarmOutput(mainRoot) ? "iOS" : null, platform !== "ios" && existsSync$1(join$1(mainRoot, "android")) && !existsSync$1(join$1(mainRoot, "android", "build")) && !existsSync$1(join$1(mainRoot, "android", "app", "build")) ? "Android" : null].filter((coldPlatform) => coldPlatform !== null);
807
812
  if (coldPlatforms.length) findings.push(finding("note", `The source checkout has no ${coldPlatforms.join(" or ")} warm build output`, "The shared Stim artifact or compilation cache can still be warm. Building the source checkout once gives later native worktrees the strongest warm starting point.", `When more native worktrees are expected, run \`stim start\`, \`stim ${coldPlatforms[0] === "iOS" ? "ios" : "android"}\`, and \`stim stop\` from ${mainRoot}.`));
808
813
  if (linkedWorktrees === void 0 ? hasLinkedWorktree(projectRoot) : linkedWorktrees) findings.push(...seedFindings(mainRoot, upstream === void 0 ? locallyKnownUpstream(mainRoot) : upstream));
809
814
  return findings;
@@ -846,7 +851,7 @@ function checkCcacheConflict(podfileSource, podfileProperties) {
846
851
  }
847
852
  function checkCcacheInstalled(onPath) {
848
853
  if (onPath) return null;
849
- return finding("cost", "ccache is not on PATH, so Android C++ recompiles in every worktree", "Stim resolves the launcher with `command -v ccache` -- the same PATH lookup the build itself uses -- and found nothing, so it passes no CMAKE_C_COMPILER_LAUNCHER / CMAKE_CXX_COMPILER_LAUNCHER to Gradle and every AGP CMake task compiles without one. Those tasks are uncacheable by Gradle, so not one C++ object crosses a worktree. Measured on trailhead (arm64, fresh worktree): 49.6s without the launcher against 34.2s with it.", "brew install ccache, then delete android/app/.cxx and node_modules/**/android/.cxx once so CMake reconfigures with the launcher. The shell that runs Stim must have the install location on PATH -- agent shells often lack /opt/homebrew/bin.");
854
+ return finding("cost", "ccache is not on PATH, so Android C++ recompiles in every worktree", "Stim resolves the launcher with a PATH lookup for `ccache` -- the same lookup the build itself uses -- and found nothing, so it passes no CMAKE_C_COMPILER_LAUNCHER / CMAKE_CXX_COMPILER_LAUNCHER to Gradle and every AGP CMake task compiles without one. Those tasks are uncacheable by Gradle, so not one C++ object crosses a worktree. Measured on trailhead (arm64, fresh worktree): 49.6s without the launcher against 34.2s with it.", "brew install ccache, then delete android/app/.cxx and node_modules/**/android/.cxx once so CMake reconfigures with the launcher. The shell that runs Stim must have the install location on PATH -- agent shells often lack /opt/homebrew/bin.");
850
855
  }
851
856
  function launcherPath(launcher) {
852
857
  const command = launcher?.split(";")[0]?.trim();
@@ -868,7 +873,7 @@ function checkCxxCompilerLauncher({ states, ccacheOnPath, launcherExists = exist
868
873
  }
869
874
  function ccacheIsOnPath() {
870
875
  try {
871
- return Boolean(getExecutor().runQuiet("command -v ccache", { timeoutMs: 5e3 }));
876
+ return Boolean(getExecutor().findExecutable("ccache"));
872
877
  } catch {
873
878
  return false;
874
879
  }
@@ -915,7 +920,25 @@ function checkSimSlim({ configured = false, profileError = null, onPath = false
915
920
  if (onPath) return null;
916
921
  return finding("cost", "A SimSlim profile is configured, but SimSlim is not installed", "`stim ios` needs the `simslim` command to apply the profile to its owned simulator.", "brew install mobai-app/tap/simslim");
917
922
  }
918
- function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null, easAuth = checkEasAuth$1, concurrency = getConcurrencyLimits, liveDevices = null, activeBuilds = null, remoteEnv = process.env, lookupAgentDevice = null, lookupEasCli = null, lookupSimSlim = null, memoryPressure = readHostMemoryPressure, lookupCcache = null, platform } = {}) {
923
+ function checkIosHost(platform, host) {
924
+ if (platform !== "ios" || host === "darwin") return null;
925
+ return finding("note", `iOS runs through EAS on this ${host} host`, "Xcode, CocoaPods and simctl exist only on macOS, so `stim ios` cannot build or boot a simulator here. `stim ios --remote eas --eas-profile <simulator profile>` downloads a finished EAS development build and runs it on an EAS Simulator session driven through agent-device; Metro stays on this machine behind a tunnel.", "Install eas-cli and agent-device, then run `stim start --remote` and `stim ios --remote eas --eas-profile <profile>`. Read `stim guide metro` for the remote device backends.");
926
+ }
927
+ function checkXcodeEnvLineEndings(projectRoot, platform, host) {
928
+ if (platform === "android" || host !== "win32") return null;
929
+ const xcodeEnv = join$1(projectRoot, "ios", ".xcode.env");
930
+ if (!existsSync$1(xcodeEnv)) return null;
931
+ let content;
932
+ try {
933
+ content = readFileSync$1(xcodeEnv, "utf-8");
934
+ } catch {
935
+ return null;
936
+ }
937
+ if (!content.includes("\r")) return null;
938
+ return finding("cost", "ios/.xcode.env has CRLF line endings", "An EAS build uploads the working-tree bytes of ios/.xcode.env, and Xcode sources that file with sh on the build worker, where each carriage return becomes part of the line and the build fails with `: command not found`. On Windows the usual cause is core.autocrlf=true rewriting the file at checkout.", "Run `git config core.autocrlf input` then `git checkout -- ios/.xcode.env`, or add `ios/.xcode.env text eol=lf` to .gitattributes and check the file out again.");
939
+ }
940
+ function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null, easAuth = checkEasAuth$1, concurrency = getConcurrencyLimits, liveDevices = null, activeBuilds = null, remoteEnv = process.env, lookupAgentDevice = null, lookupEasCli = null, lookupSimSlim = null, memoryPressure = readHostMemoryPressure, lookupCcache = null, platform, host = process.platform } = {}) {
941
+ const localIos = platform !== "android" && host === "darwin";
919
942
  const read = (rel) => {
920
943
  const p = join$1(projectRoot, rel);
921
944
  if (!existsSync$1(p)) return null;
@@ -982,12 +1005,12 @@ function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null,
982
1005
  }
983
1006
  let simslimProfile = null;
984
1007
  let simslimProfileError = null;
985
- if (platform !== "android") try {
1008
+ if (localIos) try {
986
1009
  simslimProfile = iosSimSlimProfileSetting(projectSettings, projectRoot);
987
1010
  } catch (error) {
988
1011
  simslimProfileError = String(error?.message || error);
989
1012
  }
990
- const simslimFinding = platform === "android" || !simslimProfile && !simslimProfileError && remoteIosSetting(projectSettings) ? null : checkSimSlim({
1013
+ const simslimFinding = !localIos || !simslimProfile && !simslimProfileError && remoteIosSetting(projectSettings) ? null : checkSimSlim({
991
1014
  configured: Boolean(simslimProfile),
992
1015
  profileError: simslimProfileError,
993
1016
  onPath: simslimProfile ? lookupSimSlim ? lookupSimSlim() : simslimIsOnPath() : false
@@ -1002,16 +1025,25 @@ function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null,
1002
1025
  agentDeviceOnPath,
1003
1026
  easCliResolvable
1004
1027
  })).filter((remoteFinding) => remoteFinding !== null);
1005
- const memoryAdvice = platform !== "android" && !remoteIosSetting(projectSettings) ? hostMemoryPressureAdvice(memoryPressure()) : null;
1028
+ const memoryAdvice = localIos && !remoteIosSetting(projectSettings) ? hostMemoryPressureAdvice(memoryPressure()) : null;
1006
1029
  return [
1007
1030
  checkAppProject(projectRoot),
1008
- ...checkMainCheckout(projectRoot, { platform }),
1009
- ...platform === "android" ? [] : inspectIosDebugArchitectures(mainCheckoutProjectRoot(projectRoot)),
1010
- ...checkStorageLayout(projectRoot, { platform }),
1031
+ checkIosHost(platform, host),
1032
+ checkXcodeEnvLineEndings(projectRoot, platform, host),
1033
+ ...checkMainCheckout(projectRoot, {
1034
+ platform,
1035
+ localIos
1036
+ }),
1037
+ ...localIos ? inspectIosDebugArchitectures(mainCheckoutProjectRoot(projectRoot)) : [],
1038
+ ...checkStorageLayout(projectRoot, {
1039
+ platform,
1040
+ host
1041
+ }),
1011
1042
  optimizations?.metroSharedCache ? checkMetroCache(metroConfig) : null,
1012
- platform === "android" || !optimizations?.ios.compilationCache ? null : checkCompilationCache(podfile, xcodeMajor),
1013
- platform === "android" || !optimizations?.ios.compilationCache ? null : checkCcacheConflict(podfile, podfileProperties),
1043
+ localIos && optimizations?.ios.compilationCache ? checkCompilationCache(podfile, xcodeMajor) : null,
1044
+ localIos && optimizations?.ios.compilationCache ? checkCcacheConflict(podfile, podfileProperties) : null,
1014
1045
  ...platform === "ios" || optimizations?.android.compilerCache !== "ccache" ? [] : androidCcacheFindings(projectRoot, platform, lookupCcache),
1046
+ checkAndroidPathRoom(projectRoot, platform, host),
1015
1047
  remoteBuildCache ? checkBuildCacheProvider(appConfig, sdkMajor, isExpo, dynamicConfig) : null,
1016
1048
  easFinding,
1017
1049
  concurrencyFinding,
@@ -1028,6 +1060,17 @@ function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null,
1028
1060
  })
1029
1061
  ].filter((f) => Boolean(f));
1030
1062
  }
1063
+ function checkAndroidPathRoom(projectRoot, platform, host, hostArch = process.arch) {
1064
+ const room = platform === "ios" ? null : androidPathRoom(projectRoot, {
1065
+ abi: hostSystemImageArch(hostArch),
1066
+ platform: host
1067
+ });
1068
+ if (!room) return null;
1069
+ return {
1070
+ code: "android-path-room",
1071
+ ...finding("cost", "The project path leaves no room for Android native object paths", `${androidPathRoomMessage(room)} \`stim android\` refuses to build here with STIM_PATH_TOO_LONG, because Gradle would fail deep inside ninja instead.`, androidPathRoomRemedy(room))
1072
+ };
1073
+ }
1031
1074
  function androidCcacheFindings(projectRoot, platform, lookupCcache) {
1032
1075
  if (platform !== "android" && !existsSync$1(join$1(projectRoot, "android"))) return [];
1033
1076
  const onPath = lookupCcache ? lookupCcache() : ccacheIsOnPath();
@@ -1050,7 +1093,7 @@ function checkAppProject(projectRoot) {
1050
1093
  }
1051
1094
  function agentDeviceIsOnPath() {
1052
1095
  try {
1053
- return Boolean(getExecutor().runQuiet("command -v agent-device", { timeoutMs: 5e3 }));
1096
+ return Boolean(getExecutor().findExecutable("agent-device"));
1054
1097
  } catch {
1055
1098
  return true;
1056
1099
  }
@@ -1338,7 +1381,7 @@ function applySandboxFix(root, env = process.env) {
1338
1381
  console.error(chalk.green(`${result.status === "created" ? "Wrote" : "Updated"} ${target}`));
1339
1382
  console.error(chalk.dim("Added writes to Stim's state directory, the simulator XPC service, and local port binding. Claude Code reads project settings from the directory a session starts in, so this file only counts for sessions rooted there. Restart the session for it to take effect."));
1340
1383
  }
1341
- function doctorCommand(program, version, inspectVersions = inspectStimVersions) {
1384
+ function doctorCommand(program, version, inspectVersions = inspectStimVersions, host = process.platform) {
1342
1385
  program.command("doctor").description("Inspect the source checkout and report project state that can make native worktrees slow or invalid. The checkout is left untouched unless --fix is passed; --platform filters native findings. Each run is recorded in Stim state so guide can say when doctor is due.").option("--json", "print the findings as JSON").option("--platform <platform>", "report shared findings plus only this native platform: ios or android", parseDoctorPlatform).option("--fix", "repair the sandbox allowance when the report names it, and stale Android .cxx configurations in this checkout. Stop native builds first. Generated CMake output must be ignored and untracked; custom launcher settings and source files are preserved.").action(async (opts) => {
1343
1386
  const root = findProjectRoot(process.cwd());
1344
1387
  if (!root) {
@@ -1360,8 +1403,9 @@ function doctorCommand(program, version, inspectVersions = inspectStimVersions)
1360
1403
  }
1361
1404
  const stim = await inspectVersions(version);
1362
1405
  const findings = runDoctor(root, {
1363
- xcodeMajor: opts.platform === "android" ? null : detectXcodeMajor(),
1364
- platform: opts.platform
1406
+ xcodeMajor: opts.platform !== "android" && host === "darwin" ? detectXcodeMajor() : null,
1407
+ platform: opts.platform,
1408
+ host
1365
1409
  });
1366
1410
  const parity = await detectFingerprintParity(root, { platform: opts.platform });
1367
1411
  if (parity) findings.push(parity);
@@ -1,19 +1,19 @@
1
- import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { D as withDirLock, F as deviceSlotPlatforms, P as deviceSlotKey, f as getProject } from "./config-DMP7L7H6.mjs";
3
- import { r as readWorkspaceState } from "./workspace-state-WVwWw_hB.mjs";
4
- import { d as readWorkspaceLaunches } from "./state-CnWzvCWP.mjs";
5
- import { n as deviceConsoleLevel } from "./ios-device-D3pzTKQE.mjs";
6
- import { i as resolveProjectMetro } from "./metro-ato6ef3J.mjs";
7
- import { n as parseLogcatLine, t as androidClockOffset } from "./android-BW8i_YxU.mjs";
8
- import { _ as launchErrorPreview } from "./command-output-BuIb_ZOI.mjs";
9
- import { n as androidHome } from "./android-BkYmndPp.mjs";
10
- import { a as parseLease, n as fileLeaseIo, t as deviceLeasePath } from "./device-lease-DBW-IL4p.mjs";
11
- import { l as readLogRecords, t as attachExpoErrorContext } from "./logs-query-Cdp5aaCH.mjs";
1
+ import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
+ import { D as withDirLock, F as deviceSlotKey, I as deviceSlotPlatforms, f as getProject } from "./config-B7iyKWkL.mjs";
3
+ import { r as readWorkspaceState } from "./workspace-state-BT5E3SA2.mjs";
4
+ import { d as readWorkspaceLaunches } from "./state-DZobzxLW.mjs";
5
+ import { n as deviceConsoleLevel } from "./ios-device-CFXA46ez.mjs";
6
+ import { a as resolveProjectMetro } from "./metro-C7bcJ0uo.mjs";
7
+ import { r as androidHome, s as androidToolCwd } from "./android-DWssqHQ8.mjs";
8
+ import { n as parseLogcatLine, t as androidClockOffset } from "./android-DeYdjjBP.mjs";
9
+ import { _ as launchErrorPreview } from "./command-output-B3ycpsYv.mjs";
10
+ import { a as parseLease, n as fileLeaseIo, t as deviceLeasePath } from "./device-lease-Ffz_M5t0.mjs";
11
+ import { l as readLogRecords, t as attachExpoErrorContext } from "./logs-query-BGL-Pl12.mjs";
12
12
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
13
13
  import { createHash, randomUUID } from "node:crypto";
14
14
  import { basename, dirname, join, resolve, sep } from "node:path";
15
15
  import { homedir } from "node:os";
16
- //#region src/diagnostic-store.ts
16
+ //#region src/diagnostics/diagnostic-store.ts
17
17
  /** Publishes an immutable diagnostic snapshot atomically; existing evidence wins. */
18
18
  function writeDiagnosticOnce(path, content) {
19
19
  mkdirSync(dirname(path), { recursive: true });
@@ -34,7 +34,7 @@ function writeDiagnosticOnce(path, content) {
34
34
  });
35
35
  }
36
36
  //#endregion
37
- //#region src/native-crash.ts
37
+ //#region src/diagnostics/native-crash.ts
38
38
  const IOS_CRASH_REPORT_RETRY = "iOS crash reports can take about a minute or longer to appear. Run `stim logs --errors` again for the native stack.";
39
39
  /** Accepts only a report for this app, simulator and launch window. */
40
40
  function parseIosCrash(text, target) {
@@ -329,7 +329,10 @@ function captureNativeCrashes(target, logsDir) {
329
329
  "time,epoch",
330
330
  "-t",
331
331
  "2000"
332
- ], { timeoutMs: 3e3 });
332
+ ], {
333
+ timeoutMs: 3e3,
334
+ cwd: androidToolCwd()
335
+ });
333
336
  const deadline = Date.now() + 4e3;
334
337
  if (text) records = parseAndroidCrashes(text, target, offset).map((record) => symbolicateAndroid(record, target.root, deadline));
335
338
  }
@@ -384,7 +387,10 @@ function captureWorkspaceCrashes(root, logsDir) {
384
387
  "emu",
385
388
  "avd",
386
389
  "name"
387
- ], { timeoutMs: 2e3 })?.split("\n")[0]?.trim() !== configured.avdName) continue;
390
+ ], {
391
+ timeoutMs: 2e3,
392
+ cwd: androidToolCwd()
393
+ })?.split("\n")[0]?.trim() !== configured.avdName) continue;
388
394
  }
389
395
  }
390
396
  captureNativeCrashes({
@@ -402,7 +408,7 @@ function captureWorkspaceCrashes(root, logsDir) {
402
408
  }
403
409
  }
404
410
  //#endregion
405
- //#region src/error-symbolication.ts
411
+ //#region src/diagnostics/error-symbolication.ts
406
412
  const LOCATION = /https?:\/\/[^\s)'"\\]+?:\d+:\d+/g;
407
413
  function frameFromLocation(location) {
408
414
  const match = /^(https?:\/\/.+):(\d+):(\d+)$/.exec(location);
@@ -490,7 +496,7 @@ async function symbolicateErrors(records, { port, timeoutMs = 2e3, request = fet
490
496
  }
491
497
  }
492
498
  //#endregion
493
- //#region src/error-diagnostics.ts
499
+ //#region src/diagnostics/error-diagnostics.ts
494
500
  function key(record) {
495
501
  return createHash("sha256").update(JSON.stringify(record)).digest("hex");
496
502
  }
@@ -1,4 +1,6 @@
1
- import { execFileSync, execSync, spawn } from "child_process";
1
+ import { execSync } from "child_process";
2
+ import spawn from "cross-spawn";
3
+ import which from "which";
2
4
  //#region src/exec.ts
3
5
  const MAX_BUFFER = 67108864;
4
6
  let active = {
@@ -38,7 +40,14 @@ let active = {
38
40
  for (const key of omitEnv ?? []) delete childEnv[key];
39
41
  opts.env = childEnv;
40
42
  }
41
- return String(execFileSync(file, args, opts)).trim();
43
+ const result = spawn.sync(file, args, opts);
44
+ if (result.error) throw Object.assign(result.error, result);
45
+ if (result.status !== 0) {
46
+ const stderr = String(result.stderr ?? "");
47
+ const message = `Command failed: ${[file, ...args].join(" ")}${stderr ? `\n${stderr}` : ""}`;
48
+ throw Object.assign(new Error(message), result);
49
+ }
50
+ return String(result.stdout).trim();
42
51
  },
43
52
  runQuiet(cmd, opts) {
44
53
  try {
@@ -56,6 +65,9 @@ let active = {
56
65
  },
57
66
  spawn(cmd, args = [], opts = {}) {
58
67
  return spawn(cmd, args, opts);
68
+ },
69
+ findExecutable(name) {
70
+ return which.sync(name, { nothrow: true });
59
71
  }
60
72
  };
61
73
  function getExecutor() {
@@ -1,25 +1,26 @@
1
- import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { A as isOnMountedVolume, C as withConfigLock, E as ownership_claim_exports, F as deviceSlotPlatforms, L as projectDeviceSlots, M as volumeRootFor, O as directorySize, f as getProject, g as loadConfig, i as clearDevice, j as listMountedVolumes, k as formatBytes, u as getConfigDir, v as removeProject } from "./config-DMP7L7H6.mjs";
3
- import { i as registeredCaches } from "./cache-manifest-uoOKhgUD.mjs";
4
- import { h as workspaceDir, y as workspaceStateFile } from "./workspace-state-WVwWw_hB.mjs";
5
- import { g as repoRoot, s as gitCommonDir } from "./worktree-DJnBbQZ9.mjs";
6
- import { a as findProjectRoot, i as detectIsExpo } from "./project-B_SDt4ip.mjs";
7
- import { m as shortUdid, o as formatLongDuration, u as plural } from "./command-output-BuIb_ZOI.mjs";
8
- import { B as listBuildLocks, S as listOrphanedAvdDirectories, T as ownedAvdDirectory, b as listAvds } from "./android-BkYmndPp.mjs";
9
- import { A as resolveSettings, N as settingShapeErrors, a as SETTING_SHAPE_REMEDY } from "./settings-CGHTt5vb.mjs";
10
- import { S as resolveEasCliBin, a as POOL_SETTING_REMEDY, i as teardownParkedIosSim, l as parkedMaxSetting, n as teardownOwnedIosSim, r as teardownParkedAvd, s as dropParked, t as teardownOwnedAvd, u as readParked } from "./teardown-sm1FwF6V.mjs";
11
- import { d as listIosDeviceTypes, g as parseRuntimeVersion, u as listAllIosSims } from "./ios-Deh5js5V.mjs";
12
- import { i as listLeaseFiles, r as leaseIsExpired, u as removeExpiredLease } from "./device-lease-DBW-IL4p.mjs";
13
- import { n as listBuildSlots } from "./build-slots-CuPszJDu.mjs";
14
- import { C as verifyStoppedSession, S as stopSessionArgs, _ as inspectSessionForTeardown, d as withEasProjectLock, f as easMachineStateRoot, g as getSessionArgs, h as findOrphanedOwnedSessions, m as removeEasSessionClaim, p as readEasSessionLedger, u as withRemoteSessionLock, v as isDefinitiveMissingSessionError, x as parseSessionListPage, y as listOwnedSessionsArgs } from "./device-remote-D9wN3CdE.mjs";
15
- import { n as reclaimProject, t as describeDereferenced } from "./reclaim-WXTbXPX4.mjs";
1
+ import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
+ import { A as isOnMountedVolume, C as withConfigLock, E as ownership_claim_exports, I as deviceSlotPlatforms, M as volumeRootFor, O as directorySize, R as projectDeviceSlots, f as getProject, g as loadConfig, i as clearDevice, j as listMountedVolumes, k as formatBytes, u as getConfigDir, v as removeProject } from "./config-B7iyKWkL.mjs";
3
+ import { i as registeredCaches } from "./cache-manifest-CYJi8-Hx.mjs";
4
+ import { h as workspaceDir, y as workspaceStateFile } from "./workspace-state-BT5E3SA2.mjs";
5
+ import { g as repoRoot, s as gitCommonDir } from "./worktree-FG_ZKKdb.mjs";
6
+ import { a as findProjectRoot, i as detectIsExpo } from "./project-DDZWf_16.mjs";
7
+ import { A as resolveSettings, N as settingShapeErrors, a as SETTING_SHAPE_REMEDY } from "./settings-BXXorVM2.mjs";
8
+ import { C as listAvds, O as ownedAvdDirectory, T as listOrphanedAvdDirectories } from "./android-DWssqHQ8.mjs";
9
+ import { m as shortUdid, o as formatLongDuration, u as plural } from "./command-output-B3ycpsYv.mjs";
10
+ import { r as listBuildLocks } from "./build-lock-CgtsB6TV.mjs";
11
+ import { S as resolveEasCliBin, a as POOL_SETTING_REMEDY, i as teardownParkedIosSim, l as parkedMaxSetting, n as teardownOwnedIosSim, r as teardownParkedAvd, s as dropParked, t as teardownOwnedAvd, u as readParked } from "./teardown-5J5UXz2P.mjs";
12
+ import { d as listIosDeviceTypes, g as parseRuntimeVersion, u as listAllIosSims } from "./ios-lEJ5pKsq.mjs";
13
+ import { i as listLeaseFiles, r as leaseIsExpired, u as removeExpiredLease } from "./device-lease-Ffz_M5t0.mjs";
14
+ import { n as listBuildSlots } from "./build-slots-C-oAHRT1.mjs";
15
+ import { C as verifyStoppedSession, S as stopSessionArgs, _ as inspectSessionForTeardown, d as withEasProjectLock, f as easMachineStateRoot, g as getSessionArgs, h as findOrphanedOwnedSessions, m as removeEasSessionClaim, p as readEasSessionLedger, u as withRemoteSessionLock, v as isDefinitiveMissingSessionError, x as parseSessionListPage, y as listOwnedSessionsArgs } from "./device-remote-BprxZp-m.mjs";
16
+ import { n as reclaimProject, t as describeDereferenced } from "./reclaim-D614jI6_.mjs";
16
17
  import { InvalidArgumentError } from "commander";
17
18
  import { existsSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "fs";
18
19
  import { homedir, tmpdir } from "os";
19
20
  import { basename, dirname, isAbsolute, join, relative, resolve } from "path";
20
21
  import { METRO_NAMED_CACHE_LAYOUT } from "@stim-cli/core";
21
22
  import chalk from "chalk";
22
- //#region src/caches.ts
23
+ //#region src/cache/caches.ts
23
24
  function xcodeDerivedDataRoot() {
24
25
  return join(homedir(), "Library", "Developer", "Xcode", "DerivedData");
25
26
  }
@@ -1,6 +1,6 @@
1
- import { a as findProjectRoot } from "./project-B_SDt4ip.mjs";
2
- import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-CGHTt5vb.mjs";
3
- import { t as guideStatus } from "./guide-status-BF6RsJJp.mjs";
1
+ import { a as findProjectRoot } from "./project-DDZWf_16.mjs";
2
+ import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-BXXorVM2.mjs";
3
+ import { t as guideStatus } from "./guide-status-Craq9z0t.mjs";
4
4
  import chalk from "chalk";
5
5
  //#endregion
6
6
  //#region src/guide/index.ts
@@ -516,7 +516,7 @@ line by design (see \`guide logs\`), not this single-payload contract.`,
516
516
  the remedies all target -- read from the BUILT APK's
517
517
  manifest, which on a flavored project is the flavor's
518
518
  applicationId, not what the project files say
519
- debugHttpHost "10.0.2.2:<port>" on an emulator, "localhost:<port>" on a
519
+ debugHttpHost "127.0.0.1:<port>" on an emulator, "localhost:<port>" on a
520
520
  physical device, when the app's SharedPreferences were
521
521
  pointed at this workspace's Metro; null when they were not.
522
522
  A healthy run reverses only <port> -> <port>, which is what
@@ -927,8 +927,9 @@ metro is reserved; use stim start and stim stop for managed Metro.
927
927
 
928
928
  New allocations scan TCP ports 8900-8999. They skip registry reservations
929
929
  and existing listeners, announcing occupied ports and upward retries on
930
- stderr. Allocation requires lsof. All 100 ports occupied or reserved is a
931
- refusal; stop or release unused allocations in their owning workspaces.
930
+ stderr. Listener checks require lsof, or netstat on Windows. All 100 ports
931
+ occupied or reserved is a refusal; stop or release unused allocations in
932
+ their owning workspaces.
932
933
 
933
934
  The machine registry, under STIM_HOME, serializes allocation and cleanup.
934
935
  The workspace is the nearest package.json directory, resolved through
@@ -940,9 +941,11 @@ supervise, or capture logs for these servers.
940
941
 
941
942
  ports lists named labels and ports, plus Metro marked managed.
942
943
  ports stop [label] kills TCP listeners on those named ports and releases
943
- the allocations. It sends SIGTERM, waits two seconds, then SIGKILL if needed.
944
- It prints the PID and command for each stopped process. The listener's cwd
945
- can be anywhere: the named reservation is permission to stop that listener.
944
+ the allocations. It sends SIGTERM, waits two seconds, then SIGKILL if needed;
945
+ on Windows it terminates the listener's process tree with taskkill.
946
+ It prints the PID and command (the image name on Windows) for each stopped
947
+ process. The listener's cwd can be anywhere: the named reservation is
948
+ permission to stop that listener.
946
949
  Reserve only services this workspace may stop. --dry-run prints what would
947
950
  be stopped and released, without doing either. A failed inspection or stop
948
951
  keeps that allocation for retry; other labels are still processed.
@@ -1380,6 +1383,25 @@ code, never on the message.`,
1380
1383
  object, and upgrading the CLI does not clear it. Empty that one cache with
1381
1384
  \`gc --delete --cache "compilation cache"\`, then build again. The next
1382
1385
  build is a cold one.`
1386
+ },
1387
+ STIM_PATH_TOO_LONG: {
1388
+ summary: "Windows only: the project root leaves no room for the NDK object paths ninja must open",
1389
+ body: () => `STIM_PATH_TOO_LONG (android, Windows only)
1390
+ The NDK's ninja is not long-path aware, so every CMake object path has to
1391
+ stay under Windows' MAX_PATH. React Native's codegen objects are named
1392
+ after their mangled absolute source path, so each carries the project root
1393
+ twice unless CMake shortens it, and CMake only shortens a name that then
1394
+ fits the object path limit Stim's Gradle shim sets. The message names the
1395
+ longest known object (measured on the e2e fixture, for the ABI being
1396
+ built) and the longest root it leaves room for. Refused BEFORE Gradle runs
1397
+ -- a cache hit still installs -- because the build would otherwise fail
1398
+ deep inside ninja ("mkdir ... No such file or directory" or "Filename
1399
+ longer than 260 characters"). Map the project to a drive letter (\`subst
1400
+ X: <root>\`, then run Stim from X:\\) or move it under a shorter root.
1401
+ A custom Gradle buildStagingDirectory can be shorter than the default; this
1402
+ preflight does not inspect that configured path and may refuse it too.
1403
+ \`doctor --platform android\` reports the same root as a finding for the
1404
+ emulator's ABI.`
1383
1405
  },
1384
1406
  fallbacks: {
1385
1407
  summary: "release cache-hit notes that are not codes: swap failure, asset gate, uninstall, device fallbacks",
@@ -2317,7 +2339,7 @@ not on any remote" (worktree remove)
2317
2339
  the no-install form.
2318
2340
 
2319
2341
  "Unsupported engine" or a syntax error before Stim starts
2320
- Stim requires Node 20.19.4 or later on Node 20, or Node 22.12.0 or later.
2342
+ Stim requires Node 22.12.0 or later.
2321
2343
  Switch Node versions, then run the command again.
2322
2344
 
2323
2345
 
@@ -2527,7 +2549,9 @@ from bundle delivery and readiness verification: it gives CoreSimulator time
2527
2549
  to accept the launch, not the app extra time to report readiness. A timeout
2528
2550
  still fails the launch; Stim does not automatically retry it.
2529
2551
 
2530
- For an unverified Android launch, follow the emitted device-specific remedy.
2552
+ For an unverified local Android launch, follow the emitted device-specific remedy.
2553
+ Only a dev client gets the Expo development-server picker step; a bare app
2554
+ starts with the printed process restart.
2531
2555
  If the app is stuck before loading its first bundle, restart its process with
2532
2556
  the printed force-stop and launcher commands. Foregrounding the same process
2533
2557
  does not restart initialization. Confirm the bundle request and expected UI.
@@ -3611,9 +3635,8 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
3611
3635
  The build, the fingerprint, the build cache and the Metro port gate are
3612
3636
  unchanged. What is skipped is everything that manages an owned device:
3613
3637
  no capacity check, no AVD creation, no boot wait, and no owned-device
3614
- registry entry. The app is pointed at
3615
- localhost:<port>, which the adb reverse serves, instead of the emulator's
3616
- 10.0.2.2. Stim never creates, boots, shuts down, or deletes hardware.
3638
+ registry entry. A debug app uses localhost:<port> through adb reverse.
3639
+ Stim never creates, boots, shuts down, or deletes hardware.
3617
3640
 
3618
3641
  \`ios --device [udid]\` selects a connected iPhone, the same way
3619
3642
  \`android --device\` selects a connected phone: with no UDID it takes the
@@ -3822,7 +3845,7 @@ THE POOL: WHICH DEVICE AN ID-LESS \`--device\` PICKS
3822
3845
  workspace's bundle with the project's own tools (\`expo export:embed\` /
3823
3846
  \`react-native bundle\`, then the project's own hermesc when
3824
3847
  \`hermesEnabled\` is not false in android/gradle.properties), re-packs it
3825
- into the copy with plain zip surgery (stored, not deflated -- the runtime
3848
+ into the copy with the JDK's jar tool (stored, not deflated -- the runtime
3826
3849
  mmaps it), then zipaligns and re-signs with apksigner. The keystore defaults
3827
3850
  to android/app/debug.keystore with the standard password; android.keystore /
3828
3851
  android.keystorePassword override it (see \`guide settings\`). The cache
@@ -1,8 +1,8 @@
1
- import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { S as upsertProject, f as getProject, u as getConfigDir } from "./config-DMP7L7H6.mjs";
1
+ import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
+ import { S as upsertProject, f as getProject, u as getConfigDir } from "./config-B7iyKWkL.mjs";
3
3
  import { accessSync, constants, mkdirSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { delimiter, join, resolve } from "node:path";
5
- //#region src/stim-installations.ts
5
+ //#region src/diagnostics/stim-installations.ts
6
6
  const VERSION_OUTPUT_LIMIT = 4096;
7
7
  function parsedVersion(version) {
8
8
  const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(version);