stim 1.8.0 → 1.9.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 (80) hide show
  1. package/README.md +23 -155
  2. package/dist/{android-BRgBYzU0.mjs → android-BPGizezh.mjs} +112 -135
  3. package/dist/{android-DeYdjjBP.mjs → android-DEgo4oCo.mjs} +2 -2
  4. package/dist/{android-DWssqHQ8.mjs → android-_y4Ohi5z.mjs} +54 -15
  5. package/dist/android-cas-compiler.mjs +2 -2
  6. package/dist/{android-cas-SWIjzeuL.mjs → android-cas-eiExNLf7.mjs} +21 -14
  7. package/dist/app-install-Uf3a83Tv.mjs +819 -0
  8. package/dist/{build-lock-CgtsB6TV.mjs → build-lock-DPXPLgTc.mjs} +120 -24
  9. package/dist/build-progress-CVqkfPC6.mjs +188 -0
  10. package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Dg7OnMrp.mjs} +4 -4
  11. package/dist/cache-manifest.mjs +1 -1
  12. package/dist/cli.mjs +22 -19
  13. package/dist/collector-run.d.mts +2 -0
  14. package/dist/collector-run.mjs +10 -9
  15. package/dist/{command-output-B3ycpsYv.mjs → command-output-gByVmIjy.mjs} +11 -2
  16. package/dist/{config-B7iyKWkL.mjs → config-Cp9lM3wX.mjs} +187 -57
  17. package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-Bq5DcLGn.mjs} +2 -2
  18. package/dist/{deps-oCXVNEqQ.mjs → deps-Cip5vUm1.mjs} +8 -5
  19. package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-KEu0_AXL.mjs} +1 -1
  20. package/dist/{device-BUoZyMoR.mjs → dev-client-HyqISM0h.mjs} +1178 -635
  21. package/dist/{device-RdujjHLB.mjs → device-D4U-xLty.mjs} +13 -20
  22. package/dist/{device-lease-Ffz_M5t0.mjs → device-lease-Crs-HkJY.mjs} +16 -9
  23. package/dist/{device-pool-BxRwdufO.mjs → device-pool-CnTAz8X1.mjs} +7 -7
  24. package/dist/{device-remote-ByN8P6CB.mjs → device-remote-AQ5rQPqr.mjs} +482 -35
  25. package/dist/{doctor-BMt156qe.mjs → doctor-B_Ei3bZu.mjs} +95 -107
  26. package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-Vfz83GCF.mjs} +22 -16
  27. package/dist/exec-jIAlgilk.mjs +132 -0
  28. package/dist/{gc-v2YcoJvm.mjs → gc-Ckw8RQHi.mjs} +763 -126
  29. package/dist/{guide-BTAlD1_y.mjs → guide-BiAbhs1J.mjs} +891 -501
  30. package/dist/{guide-status-Craq9z0t.mjs → guide-status-BAMVU0ZA.mjs} +5 -3
  31. package/dist/{ios-BcCiI4TS.mjs → ios-BL2NFckr.mjs} +43 -20
  32. package/dist/{ios-BP04pY5f.mjs → ios-BzUJWk1K.mjs} +141 -175
  33. package/dist/{ios-device-CauFJiYx.mjs → ios-device-D972i9wf.mjs} +149 -52
  34. package/dist/{ios-device-CFXA46ez.mjs → ios-device-DPsSm0v_.mjs} +3 -2
  35. package/dist/launch-verify-Q_OtW1Rf.mjs +365 -0
  36. package/dist/{logs-CakdBTtm.mjs → logs-CI9NQSK7.mjs} +18 -13
  37. package/dist/{logs-query-BGL-Pl12.mjs → logs-query-Cqma3WIu.mjs} +36 -28
  38. package/dist/{metro-C7bcJ0uo.mjs → metro-DrLHaMn2.mjs} +78 -8
  39. package/dist/{named-ports-DzMOlmaX.mjs → named-ports-CmLQSIRj.mjs} +4 -4
  40. package/dist/{ndjson-BZwI1NBv.mjs → ndjson-9uFNRwfe.mjs} +36 -3
  41. package/dist/{ownership-Cp52QAK1.mjs → ownership-D15Op8vN.mjs} +3 -3
  42. package/dist/{ownership-wolE-0Do.mjs → ownership-DCQPuWEc.mjs} +26 -7
  43. package/dist/{ports-BIbJaKNW.mjs → ports-BNKoWVWZ.mjs} +3 -3
  44. package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
  45. package/dist/{project-DDZWf_16.mjs → project-DS112DYB.mjs} +8 -3
  46. package/dist/{reload-BdypPbjM.mjs → reload-ZNJUo1pl.mjs} +12 -13
  47. package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DWkkcIxV.mjs} +2 -2
  48. package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-B_lctBR3.mjs} +8 -7
  49. package/dist/server-expo-CeILEu86.mjs +2 -0
  50. package/dist/settings-BKNh5rl4.mjs +376 -0
  51. package/dist/{settings-BXXorVM2.mjs → settings-D030k0t4.mjs} +464 -152
  52. package/dist/settings.schema.json +989 -0
  53. package/dist/{teardown-BJqEzPo_.mjs → sim-pool-KaIsp2Ft.mjs} +14 -419
  54. package/dist/simslim-B_LsGLuJ.mjs +259 -0
  55. package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-DU9A-gYw.mjs} +4 -4
  56. package/dist/spawn-claims-Dn3C_N6w.mjs +49 -0
  57. package/dist/{start-CmxtOKjU.mjs → start-DYkkKF0s.mjs} +27 -31
  58. package/dist/{state-DZobzxLW.mjs → state-NcJzMEBF.mjs} +5 -3
  59. package/dist/{stats-CLg5RuDS.mjs → stats-B63-JVAY.mjs} +4 -4
  60. package/dist/{stats-BYcZ6n1h.mjs → stats-DG9e66Rz.mjs} +66 -5
  61. package/dist/status-S6ehL4Ui.mjs +932 -0
  62. package/dist/{stop-C8JW6kKp.mjs → stop-BO28HGqF.mjs} +101 -25
  63. package/dist/{stop-B1kOpYei.mjs → stop-T1R-cZRz.mjs} +2 -2
  64. package/dist/supervisor-run.mjs +52 -34
  65. package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-DWaiRD0e.mjs} +14 -9
  66. package/dist/{workspace-state-BT5E3SA2.mjs → workspace-state-DF2gCFhx.mjs} +52 -29
  67. package/dist/worktree-DKhSF0UX.mjs +2 -0
  68. package/dist/{worktree-CFLA-6MX.mjs → worktree-LGB-UwgG.mjs} +603 -59
  69. package/dist/{worktree-FG_ZKKdb.mjs → worktree-R_cUlE7s.mjs} +62 -41
  70. package/dist/{xcode-B6YaWeg0.mjs → xcode-BZqyxh3e.mjs} +216 -58
  71. package/package.json +5 -6
  72. package/skill/SKILL.md +2 -0
  73. package/dist/app-install-BDbsSgCl.mjs +0 -1175
  74. package/dist/build-slots-C-oAHRT1.mjs +0 -151
  75. package/dist/dev-client-BmT-ioFI.mjs +0 -797
  76. package/dist/exec-B29XNuhP.mjs +0 -77
  77. package/dist/reclaim-KJ-0uWEF.mjs +0 -361
  78. package/dist/server-expo-YDDG3eP7.mjs +0 -2
  79. package/dist/status-DdkLki47.mjs +0 -426
  80. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
@@ -1,28 +1,27 @@
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-BJqEzPo_.mjs";
11
- import { D as hostMemoryPressureAdvice, O as readHostMemoryPressure, u as listAllIosSims } from "./ios-BcCiI4TS.mjs";
12
- import { n as listBuildSlots } from "./build-slots-C-oAHRT1.mjs";
13
- import { a as liveOwnedDeviceCount, d as simslimIsOnPath } from "./device-BUoZyMoR.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";
1
+ import { N as readJsonObject, g as loadConfig, l as getConcurrencyLimits, o as configCorruptRepair } from "./config-Cp9lM3wX.mjs";
2
+ import { t as getExecutor } from "./exec-jIAlgilk.mjs";
3
+ import { d as sharedBuildCache, g as workspaceDerivedData } from "./workspace-state-DF2gCFhx.mjs";
4
+ import { D as temporaryRoot, T as makeTemporaryDirectory, _ as repoRoot, a as dirtyFingerprintFiles, f as listWorktrees, p as locallyKnownUpstream, s as gitCommonDir, w as filesystemDevice, y as resolveSourceCheckout } from "./worktree-R_cUlE7s.mjs";
5
+ import { a as detectIsExpo, n as appProjectProblem, o as findProjectRoot } from "./project-DS112DYB.mjs";
6
+ import { C as projectOptimizations, D as remoteIosSetting, E as remoteAndroidSetting, F as unknownSettingKeys, M as settingShapeErrors, N as settingsLayers, V as settingDefinition, Y as resolveOptimizations, _ as iosSimSlimProfileSetting, i as SETTING_SHAPE_REMEDY, j as settingOrigin, k as resolveSettings, n as PATH_SETTINGS, q as compilerCacheFallbackMessage, r as SETTINGS_WITH_RESOLVE_TIME_FALLBACK, v as mergeSettingsLayers } from "./settings-D030k0t4.mjs";
7
+ import { C as listAdbDevices, S as hostSystemImageArch } from "./android-_y4Ohi5z.mjs";
8
+ import { d as quotedPath, f as refuseNoProject, l as phaseLine, u as plural } from "./command-output-gByVmIjy.mjs";
9
+ import { l as diffFingerprintSources, n as listBuildLocks, p as fingerprintProject } from "./build-lock-DPXPLgTc.mjs";
10
+ import { b as resolveEasCliBin, o as parkedMaxSetting, p as checkEasAuth$1, t as POOL_SETTING_REMEDY, v as ownerFromConfig, y as providerFromConfig } from "./sim-pool-KaIsp2Ft.mjs";
11
+ import { A as readHostMemoryPressure, k as hostMemoryPressureAdvice, u as listAllIosSims } from "./ios-BL2NFckr.mjs";
12
+ import { _ as listBuildSlots, h as podInstallCommand, i as ccacheEnabled, o as detectXcodeMajor } from "./xcode-BZqyxh3e.mjs";
13
+ import { a as liveOwnedDeviceCount, n as simslimIsOnPath } from "./simslim-B_LsGLuJ.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-eiExNLf7.mjs";
15
+ import { i as inspectStimVersions, n as recordDoctorRun, r as compareStimVersions } from "./guide-status-BAMVU0ZA.mjs";
16
+ import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-Bq5DcLGn.mjs";
18
17
  import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, rmSync } from "node:fs";
19
18
  import { InvalidArgumentError } from "commander";
20
- import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
21
19
  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";
20
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "path";
21
+ import { basename as basename$1, 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 "node:path";
22
22
  import { homedir } from "os";
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";
24
- import * as expoFingerprint from "@expo/fingerprint";
25
23
  import chalk from "chalk";
24
+ import * as expoFingerprint from "@expo/fingerprint";
26
25
  //#region src/diagnostics/sandbox.ts
27
26
  /**
28
27
  * What a sandboxed harness has to permit for Stim to work: its state
@@ -43,8 +42,8 @@ function detectHarness(env = process.env) {
43
42
  function expandHome(value, home) {
44
43
  const v = value.trim();
45
44
  if (v === "~" || v === "$HOME") return home;
46
- if (v.startsWith("~/")) return join$1(home, v.slice(2));
47
- if (v.startsWith("$HOME/")) return join$1(home, v.slice(6));
45
+ if (v.startsWith("~/")) return join(home, v.slice(2));
46
+ if (v.startsWith("$HOME/")) return join(home, v.slice(6));
48
47
  return v;
49
48
  }
50
49
  /**
@@ -54,7 +53,7 @@ function expandHome(value, home) {
54
53
  */
55
54
  function sandboxBlocksStimHome(stimHome, home = homedir()) {
56
55
  const dir = expandHome(stimHome, home);
57
- const probe = join$1(dir, `.probe-${process.pid}-${Math.random().toString(36).slice(2)}`);
56
+ const probe = join(dir, `.probe-${process.pid}-${Math.random().toString(36).slice(2)}`);
58
57
  try {
59
58
  mkdirSync$1(dir, { recursive: true });
60
59
  writeFileSync$1(probe, "");
@@ -67,14 +66,14 @@ function sandboxBlocksStimHome(stimHome, home = homedir()) {
67
66
  }
68
67
  /** Where a per-user, gitignored Claude Code settings file belongs for this project. */
69
68
  function claudeLocalSettingsPath(root) {
70
- return join$1(root, ".claude", "settings.local.json");
69
+ return join(root, ".claude", "settings.local.json");
71
70
  }
72
71
  /** Every settings file that may already carry the allowance, most local first. */
73
72
  function allowanceSearchPaths(settingsRoot, home = homedir()) {
74
73
  return [
75
74
  claudeLocalSettingsPath(settingsRoot),
76
- join$1(settingsRoot, ".claude", "settings.json"),
77
- join$1(home, ".claude", "settings.json")
75
+ join(settingsRoot, ".claude", "settings.json"),
76
+ join(home, ".claude", "settings.json")
78
77
  ];
79
78
  }
80
79
  function isPlainObject(value) {
@@ -114,10 +113,10 @@ function nested(source, ...keys) {
114
113
  function covers(entry, target, home) {
115
114
  const posix = (value) => {
116
115
  const expanded = expandHome(value, home);
117
- return sep$1 === "/" ? expanded : expanded.replaceAll(sep$1, "/");
116
+ return sep === "/" ? expanded : expanded.replaceAll(sep, "/");
118
117
  };
119
- const e = posix(entry.replace(/\/+\*+$/, "").replace(/\/+$/, ""));
120
- const t = posix(target.replace(/\/+$/, ""));
118
+ const e = posix(entry).replace(/\/+\*+$/, "").replace(/\/+$/, "");
119
+ const t = posix(target).replace(/\/+$/, "");
121
120
  return e === t || t.startsWith(`${e}/`);
122
121
  }
123
122
  /**
@@ -186,7 +185,7 @@ function applyClaudeAllowance(path, allowance) {
186
185
  status: "refused",
187
186
  reason: `${path} holds a "${blocker}" that is not an object, so Stim cannot merge into it without discarding that value.`
188
187
  };
189
- mkdirSync$1(dirname$1(path), { recursive: true });
188
+ mkdirSync$1(dirname(path), { recursive: true });
190
189
  const tmp = `${path}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
191
190
  writeFileSync$1(tmp, `${JSON.stringify(withAllowance(existing, allowance), null, 2)}\n`);
192
191
  try {
@@ -247,14 +246,14 @@ function checkStorageLayout(projectRoot, { platform, host = process.platform, de
247
246
  const source = resolveSourceCheckout(target);
248
247
  check("Worktree copy", ["path" in source ? source.path : target, target], "Keep the source checkout and linked worktree on the same volume to share file blocks when warming.");
249
248
  const cache = sharedBuildCache();
250
- check("Cached app/APK staging", [cache, stagingRoot(join(cache, "artifact.app"))], temporaryFix);
249
+ check("Cached app/APK staging", [cache, stagingRoot(join$1(cache, "artifact.app"))], temporaryFix);
251
250
  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.";
252
- if ((platform === "ios" || platform !== "android" && existsSync(join(projectRoot, "ios"))) && host === "darwin") {
253
- const output = join(workspaceDerivedData(projectRoot), "Build", "Products");
251
+ if ((platform === "ios" || platform !== "android" && existsSync(join$1(projectRoot, "ios"))) && host === "darwin") {
252
+ const output = join$1(workspaceDerivedData(projectRoot), "Build", "Products");
254
253
  check("iOS build-cache storage", [output, cache], cacheFix);
255
- check("iOS device app staging", [output, stagingRoot(join(output, "artifact.app"))], temporaryFix);
254
+ check("iOS device app staging", [output, stagingRoot(join$1(output, "artifact.app"))], temporaryFix);
256
255
  }
257
- if (platform === "android" || platform !== "ios" && existsSync(join(projectRoot, "android"))) check("Android build-cache storage", [apkOutputsDir(projectRoot), cache], cacheFix);
256
+ if (platform === "android" || platform !== "ios" && existsSync(join$1(projectRoot, "android"))) check("Android build-cache storage", [apkOutputsDir(projectRoot), cache], cacheFix);
258
257
  } catch (error) {
259
258
  findings.push({
260
259
  level: "cost",
@@ -338,23 +337,23 @@ function parseIosDebugArchitectures(output) {
338
337
  };
339
338
  }
340
339
  function inspectIosDebugArchitectures(projectRoot) {
341
- const iosRoot = join(projectRoot, "ios");
340
+ const iosRoot = join$1(projectRoot, "ios");
342
341
  if (!existsSync(iosRoot)) return [];
343
342
  const findings = [];
344
343
  const unknown = [];
345
344
  let projects;
346
345
  try {
347
- projects = readdirSync(iosRoot).filter((name) => name.endsWith(".xcodeproj")).toSorted().map((name) => join(iosRoot, name));
346
+ projects = readdirSync(iosRoot).filter((name) => name.endsWith(".xcodeproj")).toSorted().map((name) => join$1(iosRoot, name));
348
347
  } catch {
349
348
  projects = [];
350
349
  }
351
350
  if (!projects.length) unknown.push("app project");
352
- const pods = join(iosRoot, "Pods", "Pods.xcodeproj");
351
+ const pods = join$1(iosRoot, "Pods", "Pods.xcodeproj");
353
352
  if (existsSync(pods)) projects.push(pods);
354
- else if (existsSync(join(iosRoot, "Podfile"))) unknown.push("Pods project (not generated)");
353
+ else if (existsSync(join$1(iosRoot, "Podfile"))) unknown.push("Pods project (not generated)");
355
354
  const deadline = Date.now() + 6e4;
356
355
  for (const project of projects) {
357
- const label = relative(projectRoot, project);
356
+ const label = relative$1(projectRoot, project);
358
357
  const timeoutMs = Math.min(3e4, deadline - Date.now());
359
358
  if (timeoutMs <= 0) {
360
359
  unknown.push(label);
@@ -416,13 +415,13 @@ function children(path) {
416
415
  }
417
416
  }
418
417
  function moduleRoots(root) {
419
- const modules = [join(root, "android", "app")];
420
- const dependencies = join(root, "node_modules");
418
+ const modules = [join$1(root, "android", "app")];
419
+ const dependencies = join$1(root, "node_modules");
421
420
  for (const name of children(dependencies)) {
422
421
  if (name.startsWith(".")) continue;
423
- const entry = join(dependencies, name);
424
- const packages = name.startsWith("@") ? children(entry).map((child) => join(entry, child)) : [entry];
425
- modules.push(...packages.map((pkg) => join(pkg, "android")));
422
+ const entry = join$1(dependencies, name);
423
+ const packages = name.startsWith("@") ? children(entry).map((child) => join$1(entry, child)) : [entry];
424
+ modules.push(...packages.map((pkg) => join$1(pkg, "android")));
426
425
  }
427
426
  return modules;
428
427
  }
@@ -430,7 +429,7 @@ function cacheFiles(base, depth = 0) {
430
429
  const files = [];
431
430
  for (const name of children(base)) {
432
431
  if (depth === 0 && /^stim-[a-f0-9]{16}$/.test(name)) continue;
433
- const path = join(base, name);
432
+ const path = join$1(base, name);
434
433
  const entry = lstatSync(path);
435
434
  if (entry.isSymbolicLink()) continue;
436
435
  if (name === "CMakeCache.txt" && entry.isFile()) files.push(path);
@@ -439,8 +438,8 @@ function cacheFiles(base, depth = 0) {
439
438
  return files;
440
439
  }
441
440
  function readCxxLauncherStates(root) {
442
- return moduleRoots(root).flatMap((module) => cacheFiles(join(module, ".cxx")).map((path) => ({
443
- path: relative(root, path),
441
+ return moduleRoots(root).flatMap((module) => cacheFiles(join$1(module, ".cxx")).map((path) => ({
442
+ path: relative$1(root, path),
444
443
  launcher: parseCmakeCacheLauncher(readFileSync(path, "utf8"))
445
444
  })));
446
445
  }
@@ -450,13 +449,13 @@ function declaredLauncher(module) {
450
449
  "build.gradle.kts",
451
450
  "CMakeLists.txt"
452
451
  ].some((name) => {
453
- const path = join(module, name);
452
+ const path = join$1(module, name);
454
453
  return existsSync(path) && Boolean(projectCmakeLauncher(readFileSync(path, "utf8")));
455
454
  });
456
455
  }
457
456
  function contained(root, path) {
458
- const rel = relative(root, path);
459
- return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
457
+ const rel = relative$1(root, path);
458
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep$1}`) && !isAbsolute$1(rel);
460
459
  }
461
460
  function selectedCompilerCache(root) {
462
461
  try {
@@ -480,24 +479,24 @@ function repairCxxLauncherState(root) {
480
479
  };
481
480
  if (selectedCompilerCache(root) !== "ccache") return result;
482
481
  if (!getExecutor().findExecutable("ccache")) return result;
483
- const appOverrides = declaredLauncher(join(root, "android", "app"));
482
+ const appOverrides = declaredLauncher(join$1(root, "android", "app"));
484
483
  const targets = readCxxLauncherStates(root).filter(({ launcher }) => {
485
484
  if (!launcher) return true;
486
485
  const command = launcher.split(";")[0]?.trim();
487
- return Boolean(command && isAbsolute(command) && basename(command) === "ccache" && !existsSync(command));
486
+ return Boolean(command && isAbsolute$1(command) && basename$1(command) === "ccache" && !existsSync(command));
488
487
  });
489
488
  for (const state of targets) {
490
- const target = dirname(join(root, state.path));
491
- const label = relative(root, target);
489
+ const target = dirname$1(join$1(root, state.path));
490
+ const label = relative$1(root, target);
492
491
  try {
493
492
  let cxx = target;
494
- while (contained(root, cxx) && basename(cxx) !== ".cxx") cxx = dirname(cxx);
495
- if (basename(cxx) !== ".cxx" || target === cxx) throw new Error("unrecognized generated CMake layout");
496
- if (appOverrides || declaredLauncher(dirname(cxx))) throw new Error("project configures its own compiler launcher");
493
+ while (contained(root, cxx) && basename$1(cxx) !== ".cxx") cxx = dirname$1(cxx);
494
+ if (basename$1(cxx) !== ".cxx" || target === cxx) throw new Error("unrecognized generated CMake layout");
495
+ if (appOverrides || declaredLauncher(dirname$1(cxx))) throw new Error("project configures its own compiler launcher");
497
496
  const canonicalTarget = realpathSync(target);
498
497
  if (!contained(canonicalRoot, canonicalTarget)) throw new Error("generated directory resolves outside this checkout");
499
- if (realpathSync(cxx) !== join(realpathSync(dirname(cxx)), ".cxx")) throw new Error(".cxx is a symbolic link");
500
- const gitPath = relative(canonicalRoot, canonicalTarget);
498
+ if (realpathSync(cxx) !== join$1(realpathSync(dirname$1(cxx)), ".cxx")) throw new Error(".cxx is a symbolic link");
499
+ const gitPath = relative$1(canonicalRoot, canonicalTarget);
501
500
  if (getExecutor().runFile("git", [
502
501
  "ls-files",
503
502
  "-z",
@@ -551,14 +550,13 @@ function readMachineSettings(context) {
551
550
  layers: [],
552
551
  corrupt: {
553
552
  level: "note",
554
- title: "The Stim config is not valid JSON",
553
+ title: "The Stim config is not a valid JSON object",
555
554
  detail: `${String(error.message).split("\n")[0]}. Every setting and device record lives in that file, so no other machine-level check can run until it parses.`,
556
555
  fix: configCorruptRepair()
557
556
  }
558
557
  };
559
558
  }
560
559
  }
561
- const PATH_SETTING_ENVIRONMENT = { "optimizations.android.casToolchain": "STIM_ANDROID_CAS_TOOLCHAIN" };
562
560
  const PATH_SETTINGS_TRIMMED_BY_THEIR_CONSUMER = /* @__PURE__ */ new Set(["android.keystore"]);
563
561
  function checkMachineSettings({ settings, layers, projectRoot, optimizations = null, reportedElsewhere = [], exists = existsSync, env = process.env }) {
564
562
  const findings = [];
@@ -570,7 +568,7 @@ function checkMachineSettings({ settings, layers, projectRoot, optimizations = n
570
568
  if (reported) skip.add(reported.key);
571
569
  if (!optimizations) for (const key of SETTINGS_WITH_RESOLVE_TIME_FALLBACK) skip.add(key);
572
570
  for (const key of PATH_SETTINGS) {
573
- const variable = PATH_SETTING_ENVIRONMENT[key];
571
+ const variable = settingDefinition(key)?.env;
574
572
  if (skip.has(key) || variable && skip.has(variable)) continue;
575
573
  const override = variable ? env[variable] : void 0;
576
574
  if (variable && override) {
@@ -639,7 +637,7 @@ function compilerCacheFinding({ fallback, optimizations, file }) {
639
637
  function missingPath(value, projectRoot, exists, trim = false) {
640
638
  const opened = trim ? value.trim() : value;
641
639
  if (opened.trim() === "") return null;
642
- const path = isAbsolute(opened) ? opened : resolve(projectRoot, opened);
640
+ const path = isAbsolute$1(opened) ? opened : resolve$1(projectRoot, opened);
643
641
  if (exists(path)) return null;
644
642
  return opened === opened.trim() ? path : JSON.stringify(path);
645
643
  }
@@ -653,22 +651,15 @@ function finding(level, title, detail, fix) {
653
651
  fix
654
652
  };
655
653
  }
656
- function readJson(path) {
657
- try {
658
- return JSON.parse(readFileSync$1(path, "utf-8"));
659
- } catch {
660
- return null;
661
- }
662
- }
663
654
  function mainCheckoutProjectRoot(projectRoot) {
664
655
  const currentRepoRoot = repoRoot(projectRoot);
665
656
  if (!currentRepoRoot) return projectRoot;
666
657
  try {
667
658
  const source = resolveSourceCheckout(currentRepoRoot);
668
659
  if ("refusal" in source) return projectRoot;
669
- const projectRel = relative$1(currentRepoRoot, realpathSync$1(projectRoot));
660
+ const projectRel = relative(currentRepoRoot, realpathSync$1(projectRoot));
670
661
  if (projectRel.startsWith("..")) return projectRoot;
671
- return resolve$1(source.path, projectRel);
662
+ return resolve(source.path, projectRel);
672
663
  } catch {
673
664
  return projectRoot;
674
665
  }
@@ -687,11 +678,11 @@ function brokenPodLinks(podsRoot) {
687
678
  }
688
679
  }
689
680
  function hasIosWarmOutput(root) {
690
- if (existsSync$1(join$1(root, "ios", "build"))) return true;
681
+ if (existsSync$1(join(root, "ios", "build"))) return true;
691
682
  const derivedData = workspaceDerivedData(root);
692
- if (existsSync$1(join$1(derivedData, "Build", "Products"))) return true;
683
+ if (existsSync$1(join(derivedData, "Build", "Products"))) return true;
693
684
  try {
694
- return readdirSync$1(derivedData).some((entry) => /^scheme-[a-f0-9]{64}$/.test(entry) && existsSync$1(join$1(derivedData, entry, "Build", "Products")));
685
+ return readdirSync$1(derivedData).some((entry) => /^scheme-[a-f0-9]{64}$/.test(entry) && existsSync$1(join(derivedData, entry, "Build", "Products")));
695
686
  } catch {
696
687
  return false;
697
688
  }
@@ -720,8 +711,8 @@ function mainOperation(root) {
720
711
  "--git-dir"
721
712
  ])?.trim();
722
713
  if (!gitDir) return null;
723
- if (existsSync$1(join$1(gitDir, "rebase-merge")) || existsSync$1(join$1(gitDir, "rebase-apply"))) return "rebase";
724
- return existsSync$1(join$1(gitDir, "MERGE_HEAD")) ? "merge" : null;
714
+ if (existsSync$1(join(gitDir, "rebase-merge")) || existsSync$1(join(gitDir, "rebase-apply"))) return "rebase";
715
+ return existsSync$1(join(gitDir, "MERGE_HEAD")) ? "merge" : null;
725
716
  }
726
717
  function dirtyTrackedPaths(root) {
727
718
  return (getExecutor().runFileQuiet("git", [
@@ -785,9 +776,9 @@ function checkMainCheckout(projectRoot, { npmTreeValid, brokenPods = void 0, ups
785
776
  if ((npmTreeValid === void 0 ? installedNpmTreeIsValid(dependencies.root) : npmTreeValid) === false) findings.push(finding("cost", "The source checkout dependency tree is stale", `npm reports that ${dependencies.root}/node_modules does not match the project dependency graph. Copying it makes each worktree start from the same invalid state.`, `Run \`${installCommand}\` before creating native worktrees, or let \`stim worktree warm --refresh\` run it from a linked worktree.`));
786
777
  }
787
778
  }
788
- const podfileLock = join$1(mainRoot, "ios", "Podfile.lock");
789
- const podManifest = join$1(mainRoot, "ios", "Pods", "Manifest.lock");
790
- const podsRoot = join$1(mainRoot, "ios", "Pods");
779
+ const podfileLock = join(mainRoot, "ios", "Podfile.lock");
780
+ const podManifest = join(mainRoot, "ios", "Pods", "Manifest.lock");
781
+ const podsRoot = join(mainRoot, "ios", "Pods");
791
782
  if (localIos && existsSync$1(podfileLock)) {
792
783
  let podsState = null;
793
784
  if (!existsSync$1(podManifest)) podsState = "missing";
@@ -797,18 +788,18 @@ function checkMainCheckout(projectRoot, { npmTreeValid, brokenPods = void 0, ups
797
788
  podsState = "stale";
798
789
  }
799
790
  if (podsState) {
800
- const iosRoot = join$1(mainRoot, "ios");
791
+ const iosRoot = join(mainRoot, "ios");
801
792
  const podCommand = `cd ${quotedPath(iosRoot)} && ${podInstallCommand(mainRoot)}`;
802
793
  findings.push(finding("cost", `The source checkout CocoaPods state is ${podsState}`, `ios/Pods cannot be reused safely because its Manifest.lock ${podsState === "missing" ? "is absent" : "does not match ios/Podfile.lock"}.`, `Run \`${podCommand}\` before creating native worktrees, or let \`stim worktree warm --refresh\` run it from a linked worktree.`));
803
794
  }
804
795
  const broken = brokenPods === void 0 && existsSync$1(podsRoot) ? brokenPodLinks(podsRoot) : brokenPods || [];
805
796
  if (broken.length) {
806
- const iosRoot = join$1(mainRoot, "ios");
797
+ const iosRoot = join(mainRoot, "ios");
807
798
  const podCommand = `cd ${quotedPath(iosRoot)} && ${podInstallCommand(mainRoot, "--clean-install")}`;
808
799
  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.`));
809
800
  }
810
801
  }
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);
802
+ const coldPlatforms = [localIos && existsSync$1(join(mainRoot, "ios")) && !hasIosWarmOutput(mainRoot) ? "iOS" : null, platform !== "ios" && existsSync$1(join(mainRoot, "android")) && !existsSync$1(join(mainRoot, "android", "build")) && !existsSync$1(join(mainRoot, "android", "app", "build")) ? "Android" : null].filter((coldPlatform) => coldPlatform !== null);
812
803
  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}.`));
813
804
  if (linkedWorktrees === void 0 ? hasLinkedWorktree(projectRoot) : linkedWorktrees) findings.push(...seedFindings(mainRoot, upstream === void 0 ? locallyKnownUpstream(mainRoot) : upstream));
814
805
  return findings;
@@ -855,7 +846,7 @@ function checkCcacheInstalled(onPath) {
855
846
  }
856
847
  function launcherPath(launcher) {
857
848
  const command = launcher?.split(";")[0]?.trim();
858
- return command && isAbsolute$1(command) ? command : null;
849
+ return command && isAbsolute(command) ? command : null;
859
850
  }
860
851
  function checkCxxCompilerLauncher({ states, ccacheOnPath, launcherExists = existsSync$1 }) {
861
852
  const missing = states.map((state) => ({
@@ -926,7 +917,7 @@ function checkIosHost(platform, host) {
926
917
  }
927
918
  function checkXcodeEnvLineEndings(projectRoot, platform, host) {
928
919
  if (platform === "android" || host !== "win32") return null;
929
- const xcodeEnv = join$1(projectRoot, "ios", ".xcode.env");
920
+ const xcodeEnv = join(projectRoot, "ios", ".xcode.env");
930
921
  if (!existsSync$1(xcodeEnv)) return null;
931
922
  let content;
932
923
  try {
@@ -940,7 +931,7 @@ function checkXcodeEnvLineEndings(projectRoot, platform, host) {
940
931
  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
932
  const localIos = platform !== "android" && host === "darwin";
942
933
  const read = (rel) => {
943
- const p = join$1(projectRoot, rel);
934
+ const p = join(projectRoot, rel);
944
935
  if (!existsSync$1(p)) return null;
945
936
  try {
946
937
  return readFile(p, "utf-8");
@@ -956,15 +947,15 @@ function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null,
956
947
  });
957
948
  if (machineSettings.corrupt) return [machineSettings.corrupt];
958
949
  const projectSettings = machineSettings.settings;
959
- const pkg = readJson(join$1(projectRoot, "package.json"));
960
- const appConfig = readJson(join$1(projectRoot, "app.json"));
950
+ const pkg = readJsonObject(join(projectRoot, "package.json"));
951
+ const appConfig = readJsonObject(join(projectRoot, "app.json"));
961
952
  const dynamicConfig = appConfig ? null : [
962
953
  "app.config.ts",
963
954
  "app.config.js",
964
955
  "app.config.mjs"
965
- ].find((f) => existsSync$1(join$1(projectRoot, f))) || null;
966
- const podfileProperties = readJson(join$1(projectRoot, "ios", "Podfile.properties.json"));
967
- const podfile = read(join$1("ios", "Podfile"));
956
+ ].find((f) => existsSync$1(join(projectRoot, f))) || null;
957
+ const podfileProperties = readJsonObject(join(projectRoot, "ios", "Podfile.properties.json"));
958
+ const podfile = read(join("ios", "Podfile"));
968
959
  const metroConfig = read("metro.config.js") ?? read("metro.config.cjs");
969
960
  const isExpo = detectIsExpo(projectRoot);
970
961
  const expoRange = (pkg?.dependencies)?.expo || "";
@@ -986,9 +977,7 @@ function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null,
986
977
  optimizations: resolveOptimizations(projectSettings),
987
978
  use: readAndroidCasToolchain
988
979
  }).optimizations;
989
- } catch (error) {
990
- settingShapeFindings.push(finding("cost", "Invalid optimization setting", error.message, SETTING_SHAPE_REMEDY));
991
- }
980
+ } catch {}
992
981
  const remoteBuildCache = optimizations?.buildCache && optimizations.remoteBuildCache;
993
982
  const easFinding = remoteBuildCache && provider === "eas" ? checkEasAuth({
994
983
  provider,
@@ -1072,7 +1061,7 @@ function checkAndroidPathRoom(projectRoot, platform, host, hostArch = process.ar
1072
1061
  };
1073
1062
  }
1074
1063
  function androidCcacheFindings(projectRoot, platform, lookupCcache) {
1075
- if (platform !== "android" && !existsSync$1(join$1(projectRoot, "android"))) return [];
1064
+ if (platform !== "android" && !existsSync$1(join(projectRoot, "android"))) return [];
1076
1065
  const onPath = lookupCcache ? lookupCcache() : ccacheIsOnPath();
1077
1066
  let cxx;
1078
1067
  try {
@@ -1135,7 +1124,7 @@ function checkFingerprintParity({ projectHash, worktreeHash, changed = [], dirty
1135
1124
  }
1136
1125
  function gitMetadataAt(path) {
1137
1126
  try {
1138
- const git = lstatSync$1(join$1(path, ".git"));
1127
+ const git = lstatSync$1(join(path, ".git"));
1139
1128
  return git.isFile() || git.isDirectory();
1140
1129
  } catch {
1141
1130
  return false;
@@ -1150,14 +1139,14 @@ function listDirectory(dir) {
1150
1139
  }
1151
1140
  function hasLinkedPackageWithGit(projectRoot) {
1152
1141
  const root = repoRoot(projectRoot) ?? projectRoot;
1153
- for (let dir = projectRoot;; dir = dirname$1(dir)) {
1154
- if (linkedPackageWithGitIn(join$1(dir, "node_modules"))) return true;
1155
- if (dir === root || dirname$1(dir) === dir || relative$1(root, dirname$1(dir)).startsWith("..")) return false;
1142
+ for (let dir = projectRoot;; dir = dirname(dir)) {
1143
+ if (linkedPackageWithGitIn(join(dir, "node_modules"))) return true;
1144
+ if (dir === root || dirname(dir) === dir || relative(root, dirname(dir)).startsWith("..")) return false;
1156
1145
  }
1157
1146
  }
1158
1147
  function linkedPackageWithGitIn(nodeModules) {
1159
- return listDirectory(nodeModules).filter((entry) => !entry.startsWith(".")).flatMap((entry) => entry.startsWith("@") ? listDirectory(join$1(nodeModules, entry)).map((child) => join$1(entry, child)) : [entry]).some((name) => {
1160
- const root = join$1(nodeModules, name);
1148
+ return listDirectory(nodeModules).filter((entry) => !entry.startsWith(".")).flatMap((entry) => entry.startsWith("@") ? listDirectory(join(nodeModules, entry)).map((child) => join(entry, child)) : [entry]).some((name) => {
1149
+ const root = join(nodeModules, name);
1161
1150
  try {
1162
1151
  return lstatSync$1(root).isSymbolicLink() && gitMetadataAt(root);
1163
1152
  } catch {
@@ -1209,14 +1198,14 @@ async function detectFingerprintParity(projectRoot, { createFingerprint = expoFi
1209
1198
  "--git-dir"
1210
1199
  ], { timeoutMs: 1e4 }) == null) return null;
1211
1200
  if (hasInstalledDependencies(projectRoot)) return null;
1212
- const platform = selectedPlatform ?? (existsSync$1(join$1(projectRoot, "ios")) ? "ios" : existsSync$1(join$1(projectRoot, "android")) ? "android" : void 0);
1201
+ const platform = selectedPlatform ?? (existsSync$1(join(projectRoot, "ios")) ? "ios" : existsSync$1(join(projectRoot, "android")) ? "android" : void 0);
1213
1202
  let base;
1214
1203
  try {
1215
1204
  base = makeTemporaryDirectory(projectRoot, "stim-parity-");
1216
1205
  } catch {
1217
1206
  return null;
1218
1207
  }
1219
- const worktree = join$1(base, "head");
1208
+ const worktree = join(base, "head");
1220
1209
  if (exec.runFileQuiet("git", [
1221
1210
  "-C",
1222
1211
  projectRoot,
@@ -1382,11 +1371,10 @@ function applySandboxFix(root, env = process.env) {
1382
1371
  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."));
1383
1372
  }
1384
1373
  function doctorCommand(program, version, inspectVersions = inspectStimVersions, host = process.platform) {
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) => {
1374
+ 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 in a React Native or Expo app 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) => {
1386
1375
  const root = findProjectRoot(process.cwd());
1387
1376
  if (!root) {
1388
- console.error(chalk.red("Not in a React Native project (no package.json found)."));
1389
- process.exitCode = 1;
1377
+ refuseNoProject({ json: Boolean(opts.json) });
1390
1378
  return;
1391
1379
  }
1392
1380
  if (opts.fix) {
@@ -1,17 +1,17 @@
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";
1
+ import { A as withDirLock, H as deviceSlotKey, U as deviceSlotPlatforms, f as getProject } from "./config-Cp9lM3wX.mjs";
2
+ import { t as getExecutor } from "./exec-jIAlgilk.mjs";
3
+ import { r as readWorkspaceState } from "./workspace-state-DF2gCFhx.mjs";
4
+ import { d as readWorkspaceLaunches } from "./state-NcJzMEBF.mjs";
5
+ import { r as deviceConsoleLevel } from "./ios-device-DPsSm0v_.mjs";
6
+ import { c as resolveProjectMetro } from "./metro-DrLHaMn2.mjs";
7
+ import { r as androidHome, s as androidToolCwd } from "./android-_y4Ohi5z.mjs";
8
+ import { n as parseLogcatLine, t as androidClockOffset } from "./android-DEgo4oCo.mjs";
9
+ import { v as launchErrorPreview } from "./command-output-gByVmIjy.mjs";
10
+ import { a as parseLease, n as fileLeaseIo, t as deviceLeasePath } from "./device-lease-Crs-HkJY.mjs";
11
+ import { f as sortByTs, n as attachExpoErrorContext, u as readLogRecords } from "./logs-query-Cqma3WIu.mjs";
12
12
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
13
- import { createHash, randomUUID } from "node:crypto";
14
13
  import { basename, dirname, join, resolve, sep } from "node:path";
14
+ import { createHash, randomUUID } from "node:crypto";
15
15
  import { homedir } from "node:os";
16
16
  //#region src/diagnostics/diagnostic-store.ts
17
17
  /** Publishes an immutable diagnostic snapshot atomically; existing evidence wins. */
@@ -35,6 +35,7 @@ function writeDiagnosticOnce(path, content) {
35
35
  }
36
36
  //#endregion
37
37
  //#region src/diagnostics/native-crash.ts
38
+ const CRASH_LOG_PREFIX = "native-crash-";
38
39
  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
40
  /** Accepts only a report for this app, simulator and launch window. */
40
41
  function parseIosCrash(text, target) {
@@ -346,7 +347,7 @@ function captureNativeCrashes(target, logsDir) {
346
347
  record.incident ?? record.deviceTs,
347
348
  record.rawReport
348
349
  ])).digest("hex");
349
- writeDiagnosticOnce(join(logsDir, `native-crash-${key}.ndjson`), `${JSON.stringify(record)}\n`);
350
+ writeDiagnosticOnce(join(logsDir, `${CRASH_LOG_PREFIX}${key}.ndjson`), `${JSON.stringify(record)}\n`);
350
351
  } catch {}
351
352
  }
352
353
  return records;
@@ -355,8 +356,10 @@ function printNativeCrashReport(target, logsDir, emit, remote) {
355
356
  if (remote) return;
356
357
  for (const line of launchErrorPreview(captureNativeCrashes(target, logsDir), target.root)) emit(line);
357
358
  }
359
+ /** Captures native crash reports for this workspace's current launches and returns the whole timeline. */
358
360
  function captureWorkspaceCrashes(root, logsDir) {
359
- const attempts = readLogRecords(logsDir).filter((record) => record.event === "launch_attempt");
361
+ const timeline = readLogRecords(logsDir, (name) => !isCrashLog(name));
362
+ const attempts = timeline.filter((record) => record.event === "launch_attempt");
360
363
  const launches = readWorkspaceLaunches(root);
361
364
  const latest = /* @__PURE__ */ new Map();
362
365
  for (const attempt of attempts) if (attempt.platform === "ios" || attempt.platform === "android") latest.set(deviceSlotKey(attempt.platform, String(attempt.slot ?? "default")), attempt);
@@ -406,6 +409,10 @@ function captureWorkspaceCrashes(root, logsDir) {
406
409
  }, logsDir);
407
410
  } catch {}
408
411
  }
412
+ return sortByTs([...timeline, ...readLogRecords(logsDir, isCrashLog)]);
413
+ }
414
+ function isCrashLog(name) {
415
+ return name.startsWith(CRASH_LOG_PREFIX);
409
416
  }
410
417
  //#endregion
411
418
  //#region src/diagnostics/error-symbolication.ts
@@ -583,8 +590,7 @@ function mergeErrorCopies(records, root) {
583
590
  return result;
584
591
  }
585
592
  /** Keeps symbolicated launch evidence stable when Metro later rebuilds or stops. */
586
- async function errorDiagnostics(input, { root, logsDir, port, allowRequest = false }) {
587
- const timeline = readLogRecords(logsDir);
593
+ async function errorDiagnostics(input, { root, logsDir, port, allowRequest = false, timeline = readLogRecords(logsDir) }) {
588
594
  const records = attachDeviceErrorContext(timeline, attachExpoErrorContext(timeline, input.map((record) => ({
589
595
  ...record,
590
596
  src: typeof record.src === "string" ? record.src : void 0,