stim 1.26.0 → 1.27.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 (171) hide show
  1. package/dist/{acquire-artifact-DVsvSwf7.mjs → acquire-artifact-BKN2Yuhg.mjs} +2 -2
  2. package/dist/{act-C-BqJEpu.mjs → act-ROviTF_t.mjs} +297 -122
  3. package/dist/{activity-Lnb_EoRY.mjs → activity-Cd-ysFes.mjs} +6 -6
  4. package/dist/{agent-device-usage-output-DbXy5tg8.mjs → agent-device-usage-output-C5nCKWp9.mjs} +5 -5
  5. package/dist/{android-CSbLRD53.mjs → android--HBgpvpW.mjs} +5 -5
  6. package/dist/{android-CiNQw3kq.mjs → android-Cd6w7_qw.mjs} +96 -43
  7. package/dist/{android-B5P81yI-.mjs → android-Deb9X3_Y.mjs} +37 -15
  8. package/dist/{android-DTAcRwBC.mjs → android-DriNoAVy.mjs} +23 -18
  9. package/dist/{android-cas-BTVhZ0Qs.mjs → android-cas-D5e6ecUg.mjs} +4 -4
  10. package/dist/android-cas-compiler.mjs +1 -1
  11. package/dist/api.d.mts +13 -3
  12. package/dist/api.mjs +39 -29
  13. package/dist/{apk-signed-tail-B46U9hXc.mjs → apk-signed-tail-BHQHx3Rm.mjs} +2 -2
  14. package/dist/{artifact-BxREhAaV.mjs → artifact-0AGJYB9O.mjs} +18 -23
  15. package/dist/{artifact-CsAP6AGK.mjs → artifact-faemIZv5.mjs} +19 -24
  16. package/dist/{artifact-platform-CMRc0M_r.mjs → artifact-platform-BO2Uvk_A.mjs} +1 -1
  17. package/dist/auto-placement-z-H5pJza.mjs +350 -0
  18. package/dist/{browser-web-rYPyqhTf.mjs → browser-web-slD8AvLp.mjs} +16 -16
  19. package/dist/{build-cache-DrXNCy3T.mjs → build-cache-gBJ-WKzA.mjs} +43 -21
  20. package/dist/{build-DPhApsjV.mjs → build-hS0QyFlQ.mjs} +35 -31
  21. package/dist/{build-plan-DrMMdayj.mjs → build-plan-CBqYbzYM.mjs} +7 -6
  22. package/dist/{cache-manifest-BEZ9Bpl1.mjs → cache-manifest-DEjqGGat.mjs} +1 -1
  23. package/dist/cache-manifest.mjs +1 -1
  24. package/dist/{budget-k2mZGvvk.mjs → ccache-BWTze1Ls.mjs} +560 -1603
  25. package/dist/{cdp-BG3FJCCx.mjs → cdp-C3svvXiE.mjs} +1 -1
  26. package/dist/{chrome-BvV8ssI3.mjs → chrome-DyDWurWG.mjs} +1 -1
  27. package/dist/{cli-BBKFGOFl.mjs → cli-ftXDLto1.mjs} +24 -23
  28. package/dist/cli.mjs +1 -1
  29. package/dist/{client-D9CaRks-.mjs → client-xsdxrhWA.mjs} +48 -62
  30. package/dist/collector-run.d.mts +10 -9
  31. package/dist/collector-run.mjs +42 -27
  32. package/dist/{command-output-BF5AR2dL.mjs → command-output-Dj-WT4BP.mjs} +1 -1
  33. package/dist/{config-BktkY_zC.mjs → config-Cn94Jc0g.mjs} +23 -4
  34. package/dist/{dependencies-Dw6wfQdv.mjs → dependencies-BGokZ-Ng.mjs} +108 -60
  35. package/dist/{dependency-state-DFB41vfr.mjs → dependency-state-C3OtgG94.mjs} +14 -7
  36. package/dist/{detached-entry-KmieOKgN.mjs → detached-entry-BPlSaCwC.mjs} +1 -1
  37. package/dist/{dev-client-DXR_tPVY.mjs → dev-client-BMca82xo.mjs} +2 -2
  38. package/dist/{device-BcYeHSFu.mjs → device-BJm0irrq.mjs} +16 -16
  39. package/dist/{device-capacity-DCSvUF0l.mjs → device-capacity-6LP7QsaA.mjs} +101 -16
  40. package/dist/device-host-worker.mjs +19 -19
  41. package/dist/{device-ios-BLGfXQeK.mjs → device-ios-534c14fz.mjs} +251 -23
  42. package/dist/{device-lease-run-BmvIl7bI.mjs → device-lease-run-DwsGfjUN.mjs} +5 -5
  43. package/dist/{device-pool-Dluix4H3.mjs → device-pool-BnQqvs6o.mjs} +3 -3
  44. package/dist/{device-BK-zwyd_.mjs → device-tM41-TUq.mjs} +25 -24
  45. package/dist/{doctor-Cp-BhzTS.mjs → doctor-Dbx1ZpD2.mjs} +1 -1
  46. package/dist/{doctor-Bhg1ii70.mjs → doctor-DyqE9n1J.mjs} +28 -16
  47. package/dist/{doctor-OnwP94ne.mjs → doctor-F-3VUUyN.mjs} +76 -32
  48. package/dist/{doctor-fingerprint-D3a04Mhu.mjs → doctor-fingerprint-o7ZI9YLn.mjs} +1 -1
  49. package/dist/doctor-fingerprint.mjs +1 -1
  50. package/dist/{doctor-watchman-BStK05nt.mjs → doctor-watchman-CxDbyrrt.mjs} +5 -5
  51. package/dist/{eas-build-Dsj93iM9.mjs → eas-build-CjNSWmjo.mjs} +4 -4
  52. package/dist/eas-fallback-9fqb46kW.mjs +87 -0
  53. package/dist/{eas-session-ledger-B5vygTiD.mjs → eas-session-ledger-DrP4OuuM.mjs} +6 -6
  54. package/dist/{error-diagnostics-BOOm9Db7.mjs → error-diagnostics-snej9k4j.mjs} +51 -25
  55. package/dist/{exec-VFRpu1iA.mjs → exec-aULwtNly.mjs} +57 -6
  56. package/dist/{gc-C_79zvz4.mjs → gc-BRwAY3qT.mjs} +2 -2
  57. package/dist/{gc-ldgr-hXU.mjs → gc-CGZ1Yg88.mjs} +160 -45
  58. package/dist/{guide-D7K0X6KG.mjs → guide-_rfEh_zM.mjs} +580 -183
  59. package/dist/{hosted-android-Bk54ej6n.mjs → hosted-android-BMKzUNnY.mjs} +4 -4
  60. package/dist/{hosted-client-fHO8yqzc.mjs → hosted-client-D7Pk_Oo1.mjs} +12 -8
  61. package/dist/{hosted-logs-BfbmDM4M.mjs → hosted-logs-B1gqQuva.mjs} +8 -8
  62. package/dist/{hosted-logs-sync-B0pvmOk-.mjs → hosted-logs-sync-Dg3qrZmV.mjs} +5 -5
  63. package/dist/{hosted-macos-CPl0OZ9h.mjs → hosted-macos-B9w4H8hc.mjs} +6 -6
  64. package/dist/{hosted-native-CpDB3-PO.mjs → hosted-native-CdgaiuyP.mjs} +17 -16
  65. package/dist/{idle-shutdown-FfxL1KdG.mjs → idle-shutdown-Cbvo5KEh.mjs} +7 -8
  66. package/dist/{ios-ftxdQbJS.mjs → ios-B5HlVMhQ.mjs} +92 -37
  67. package/dist/{ios-B7-lToDu.mjs → ios-Bk4mWbtS.mjs} +8 -8
  68. package/dist/{ios-ENC4sRPA.mjs → ios-CsRVik64.mjs} +7 -1
  69. package/dist/{ios-WxpDJ5T1.mjs → ios-E26qSpkb.mjs} +27 -11
  70. package/dist/{ios-device-CsyryvpT.mjs → ios-device-C_QkIf1Z.mjs} +3 -3
  71. package/dist/{ios-device-B6HK951C.mjs → ios-device-CiBA-er9.mjs} +1 -1
  72. package/dist/{ios-project-Ty5pHl9O.mjs → ios-project-D8Jcy_br.mjs} +22 -9
  73. package/dist/{ios-signing-4c_qXvQm.mjs → ios-signing-BJzvDPfU.mjs} +2 -2
  74. package/dist/{ios-state-Cw7-WQf2.mjs → ios-state-BZ03NFgF.mjs} +1 -1
  75. package/dist/{js-swap-BMhXYVDS.mjs → js-swap-Dg2a945i.mjs} +8 -7
  76. package/dist/{launch-DOp8RjDz.mjs → launch-CRyBeNsA.mjs} +42 -38
  77. package/dist/lightened-BCsdAGj_.mjs +171 -0
  78. package/dist/{logs-dQDki6Tt.mjs → logs-CC_-ZzEE.mjs} +7 -7
  79. package/dist/{logs-s1dRIG1E.mjs → logs-aJQb-RDg.mjs} +46 -21
  80. package/dist/{macos-B0VEV3Gf.mjs → macos-BGmTpnv9.mjs} +1 -1
  81. package/dist/{macos-DBBPNnIA.mjs → macos-DlCBhMw5.mjs} +73 -38
  82. package/dist/macos-run.mjs +1 -1
  83. package/dist/maintenance-run.mjs +7 -7
  84. package/dist/{metro-CjC10t6E.mjs → metro-CvyD-xwe.mjs} +4 -4
  85. package/dist/{metro-gateway-B6_PgD0L.mjs → metro-gateway-BnHdqMO_.mjs} +3 -3
  86. package/dist/{named-ports-BG0j1X4g.mjs → named-ports-BCyMWA6b.mjs} +3 -3
  87. package/dist/{native-android-CL2NGiik.mjs → native-android-DlpxCyOP.mjs} +17 -18
  88. package/dist/{native-gradle-artifact-inputs-btSUulme.mjs → native-gradle-artifact-inputs-C13qT7R_.mjs} +5 -5
  89. package/dist/{native-runtime-Dq3RBcON.mjs → native-runtime-DqaIBO5w.mjs} +9 -8
  90. package/dist/{native-source-D-4Dh4JU.mjs → native-source-D1AzWiuP.mjs} +1 -1
  91. package/dist/{native-xcode-inputs-BW7H8JGu.mjs → native-xcode-inputs-B1W8SrxB.mjs} +3 -3
  92. package/dist/{native-xcode-ios-CBXJAxfC.mjs → native-xcode-ios-QPP0Rm68.mjs} +17 -17
  93. package/dist/{ndjson-CgE_o3Dl.mjs → ndjson-Dy2W4vRa.mjs} +2 -2
  94. package/dist/offload-worker.mjs +23 -24
  95. package/dist/{operation-exec-CUZifp6Z.mjs → operation-exec-Dp4FuIGR.mjs} +1 -1
  96. package/dist/{ownership-BjYvMNX_.mjs → ownership-Bx-0cku-.mjs} +3 -3
  97. package/dist/{page-CzEA_-5_.mjs → page-CQJlcj3x.mjs} +2 -2
  98. package/dist/{phone-reverses-lmj6kcPm.mjs → phone-reverses-C9yAaKEv.mjs} +18 -4
  99. package/dist/{placement-log-DIFPQAXp.mjs → placement-log-BXUbiZM6.mjs} +3 -3
  100. package/dist/{plan-placement-IiiSQhtz.mjs → plan-placement-UxnQjv5Z.mjs} +36 -10
  101. package/dist/{ports-C2_w1Z0Z.mjs → ports-uUrghqWN.mjs} +3 -3
  102. package/dist/{prebuild-gmouvmJZ.mjs → prebuild-BDpToDgR.mjs} +16 -17
  103. package/dist/{project-BUu6Is4k.mjs → project-DYj-HVx6.mjs} +17 -17
  104. package/dist/{pull-request-CbNlkFok.mjs → pull-request-BzLmzAIQ.mjs} +2 -2
  105. package/dist/pull-requests.mjs +1 -1
  106. package/dist/{react-native-android-Cgg99REN.mjs → react-native-android-DDBccgJw.mjs} +32 -26
  107. package/dist/{react-native-build-oNwqF5Wb.mjs → react-native-build-DaK2IFyP.mjs} +9 -9
  108. package/dist/{react-native-doctor-BhA3KUte.mjs → react-native-doctor-B-Mw6DcR.mjs} +6 -6
  109. package/dist/{react-native-ios-B-nf4rjN.mjs → react-native-ios-DjYwUF9U.mjs} +23 -17
  110. package/dist/{recordings-BldzVKq0.mjs → recordings-C20iUZfp.mjs} +1 -1
  111. package/dist/{registration-CN6nlhcD.mjs → registration-BwCe64PH.mjs} +1 -1
  112. package/dist/{reload-C9OduiNc.mjs → reload-DJBeF_rC.mjs} +1 -1
  113. package/dist/{reload-CCR7KC_l.mjs → reload-fUdkH2_p.mjs} +16 -16
  114. package/dist/{remote-C8S7FlPF.mjs → remote-y6n99WMW.mjs} +22 -9
  115. package/dist/{result-7F5hEwQJ.mjs → result-CTqBw2Te.mjs} +32 -21
  116. package/dist/{run-BqB0v3pt.mjs → run-DaCeNKRm.mjs} +3 -3
  117. package/dist/{selection-BNPBZ6T5.mjs → selection-Bs8phwhN.mjs} +37 -29
  118. package/dist/{server-bare-A75rDIdD.mjs → server-bare-BOh-iZEj.mjs} +2 -2
  119. package/dist/{server-command-CssKFOD7.mjs → server-command-8CuA51Cv.mjs} +6 -6
  120. package/dist/{server-expo-eQfOdydb.mjs → server-expo-ABQs2XFA.mjs} +4 -4
  121. package/dist/server-expo-Ba_Y21vB.mjs +2 -0
  122. package/dist/{settings-CosW5X-S.mjs → settings-BDVI8Ivc.mjs} +331 -62
  123. package/dist/{settings-OaGQF-ri.mjs → settings-CMHhQNwI.mjs} +24 -3
  124. package/dist/settings-payload-cm60Zhib.mjs +257 -0
  125. package/dist/settings-write-BdNRLlBn.mjs +185 -0
  126. package/dist/settings.schema.json +51 -33
  127. package/dist/{simslim-Bax1qcxu.mjs → simslim-DCQwzoPy.mjs} +2 -2
  128. package/dist/{slot-launch-CYCEhfBY.mjs → slot-launch-0l7RqSpr.mjs} +6 -6
  129. package/dist/{stage-jKQ7CP_B.mjs → stage-F4cAfJGi.mjs} +2 -2
  130. package/dist/{start-C_wefNKB.mjs → start-DcqnF9p8.mjs} +1 -1
  131. package/dist/{start-_BdJ32cZ.mjs → start-DrPwER_2.mjs} +27 -22
  132. package/dist/{state-C1i2nMOP.mjs → state-BX1y5syV.mjs} +4 -4
  133. package/dist/{state-C4OdpOTF.mjs → state-CZxLyetD.mjs} +2 -2
  134. package/dist/{stats-tEpEEH-u.mjs → stats-GxuCbles.mjs} +5 -5
  135. package/dist/{stats-report-n2U5plhk.mjs → stats-report-B86Cv7Uc.mjs} +1 -1
  136. package/dist/{status-CRXz-Gtj.mjs → status-7ULDEJ1e.mjs} +1 -1
  137. package/dist/{status-DQrqodLj.mjs → status-BfnK9pUd.mjs} +25 -25
  138. package/dist/{status-B9M7x4CZ.mjs → status-Bj7ZYvvc.mjs} +1 -1
  139. package/dist/{status-watch-B5wDIH_o.mjs → status-watch-BfabQn03.mjs} +5 -5
  140. package/dist/status-worker.mjs +1 -1
  141. package/dist/{stim-desktop-6Dqp-gjX.mjs → stim-desktop-CbWo21kb.mjs} +2 -2
  142. package/dist/{stop-ySNQHXMG.mjs → stop-CH79S3IZ.mjs} +5 -5
  143. package/dist/{stop-DRqQVqDJ.mjs → stop-D-EAr25B.mjs} +21 -20
  144. package/dist/{stop-CmdNhGlC.mjs → stop-x1Yu3He4.mjs} +2 -2
  145. package/dist/supervisor-run.mjs +17 -17
  146. package/dist/{support-CVOCp0Ft.mjs → support-Be0UeVZU.mjs} +10 -10
  147. package/dist/{support-BGSCBoib.mjs → support-CNg_YsOk.mjs} +6 -6
  148. package/dist/{swiftpm-artifact-inputs-CfUzt_0Z.mjs → swiftpm-artifact-inputs-uPqYvGBv.mjs} +4 -4
  149. package/dist/{swiftpm-macos-x6HPpl_2.mjs → swiftpm-macos-Ch4YjY_A.mjs} +11 -12
  150. package/dist/{toolchain-J4J8MrAr.mjs → toolchain-CbCG6C5Z.mjs} +321 -15
  151. package/dist/{toolchain-DoavrvIA.mjs → toolchain-JYAOquPg.mjs} +1 -1
  152. package/dist/{trigger-KVaWWNyn.mjs → trigger-DMou-X7j.mjs} +2 -2
  153. package/dist/trigger-qp2ji5wE.mjs +2 -0
  154. package/dist/{warm-progress-DvhTX_eu.mjs → warm-progress-C0RsbngN.mjs} +731 -727
  155. package/dist/{web-Bihbf7-u.mjs → web-Cq_AQ4DK.mjs} +9 -9
  156. package/dist/web-Je6abcpz.mjs +2 -0
  157. package/dist/web-run.mjs +9 -9
  158. package/dist/windows-report.mjs +1 -1
  159. package/dist/{workspace-state-pUg3_RRA.mjs → workspace-state-BjqdjnJR.mjs} +3 -3
  160. package/dist/{workspaces-g2lhdBXF.mjs → workspaces-DFbj07jp.mjs} +1795 -70
  161. package/dist/{worktree-BKuklFh_.mjs → worktree-COAJ3-OD.mjs} +934 -514
  162. package/dist/{worktree-Bm4cb-nU.mjs → worktree-JGUd_53L.mjs} +1 -1
  163. package/package.json +7 -7
  164. package/dist/ccache-Bx5TbxwG.mjs +0 -143
  165. package/dist/eas-fallback-FwkeqqGi.mjs +0 -381
  166. package/dist/hosted-ios-D55QhYaD.mjs +0 -15
  167. package/dist/native-run-BfIDFKLH.mjs +0 -275
  168. package/dist/server-expo-wvcj5tiZ.mjs +0 -2
  169. package/dist/settings-write-QjYfSrjF.mjs +0 -360
  170. package/dist/trigger-DzJu208Q.mjs +0 -2
  171. package/dist/web-BFLueSh-.mjs +0 -2
@@ -1,62 +1,41 @@
1
- import { b as operationState, l as WorkspaceProcessCleanupError, n as isTimeoutError, t as getExecutor } from "./exec-VFRpu1iA.mjs";
2
- import { j as workspaceStateFile$1, k as workspaceLogsDir$1, r as readWorkspaceState$1, u as workspaceLastUsed } from "./workspace-state-pUg3_RRA.mjs";
3
- import { F as isOnMountedVolume, G as projectDeviceSlots, I as listMountedVolumes, L as measuredDirectorySizeAsync, N as directorySizeAsync, P as formatBytes, _ as loadConfig$1, p as getProject } from "./config-BktkY_zC.mjs";
4
- import { o as formatLongDuration, u as plural } from "./command-output-BF5AR2dL.mjs";
5
- import { c as gitQuiet, l as gitQuietAsync, n as runOperationFileQuiet, r as childEnvironment } from "./operation-exec-CUZifp6Z.mjs";
6
- import { Bt as unpushedCommitsAsync, Ct as hasUncommittedWorkAsync, D as maintenanceKeepSetting, Dt as listWorktreesAsync, Et as listWorktrees, Gt as removeTemporaryEntryAsync, It as resolveFullRefAsync, J as resolveProjectSettings, Lt as resolveSourceCheckoutAsync, Y as resolveProjectSettingsAsync, _t as dirtyPathsAsync, h as deviceIdleShutdownMinutesSetting, xt as hasPopulatedSubmodulesAsync, zt as sourceCheckoutOf } from "./settings-CosW5X-S.mjs";
7
- import { i as findProjectRoot$1 } from "./project-BUu6Is4k.mjs";
8
- import { F as listAvdsAsync, G as ownedAvdSerialResolverAsync, P as listAvds, V as ownedAvdDirectory, yt as readParked } from "./android-DTAcRwBC.mjs";
9
- import { a as unregister, n as readManifest } from "./cache-manifest-BEZ9Bpl1.mjs";
10
- import { A as collectOwnedDeviceActivityAsync, Cr as stopIdleGradleDaemons, Dr as discoverCaches, Dt as deleteParkedSims, Et as deleteParkedAvds, Ft as findStaleProjectDevices, Or as pruneCache, Ot as deleteProjectDevices, P as workspaceBuildInProgress, St as DEVICE_LIST_TIMEOUT_MS, Tt as collectParkedSims, ct as planCacheEmptying, d as trimmableCaches, f as stopOwnedMetro, i as enforceBudget, jt as deviceSweepIsScoped, k as collectOwnedDeviceActivity, mr as collectMemoryReport, mt as deleteGradleVersion, nt as emptyCaches, o as machineNumber, pt as collectGradleVersions, r as cacheLabel, u as resolveBudget, wr as phase, wt as collectParkedAvds, yr as memorySweepIsScoped, yt as waitForGradleExit } from "./budget-k2mZGvvk.mjs";
1
+ import { l as WorkspaceProcessCleanupError, n as isTimeoutError, t as getExecutor } from "./exec-aULwtNly.mjs";
2
+ import { j as workspaceStateFile$1, k as workspaceLogsDir$1, r as readWorkspaceState$1, u as workspaceLastUsed } from "./workspace-state-BjqdjnJR.mjs";
3
+ import { F as isOnMountedVolume, G as projectDeviceSlots, I as listMountedVolumes, L as measuredDirectorySizeAsync, N as directorySizeAsync, P as formatBytes, _ as loadConfig$1, p as getProject } from "./config-Cn94Jc0g.mjs";
4
+ import { o as formatLongDuration, u as plural } from "./command-output-Dj-WT4BP.mjs";
5
+ import { c as gitQuiet, l as gitQuietAsync, n as runOperationFileQuiet, r as childEnvironment } from "./operation-exec-Dp4FuIGR.mjs";
6
+ import { At as listWorktreesAsync, Bt as resolveSourceCheckoutAsync, Ct as hasPopulatedSubmodulesAsync, Ht as sourceCheckoutOf, O as maintenanceKeepSetting, Tt as hasUncommittedWorkAsync, Ut as unpushedCommitsAsync, X as resolveProjectSettings, Z as resolveProjectSettingsAsync, h as deviceIdleShutdownMinutesSetting, kt as listWorktrees, tn as removeTemporaryEntryAsync, yt as dirtyPathsAsync, zt as resolveFullRefAsync } from "./settings-BDVI8Ivc.mjs";
7
+ import { i as findProjectRoot$1 } from "./project-DYj-HVx6.mjs";
8
+ import { F as listAvdsAsync, G as ownedAvdSerialResolverAsync, P as listAvds, V as ownedAvdDirectory, yt as readParked } from "./android-DriNoAVy.mjs";
9
+ import { A as diskPressureLevel, G as planCacheEmptying, N as lowestFree, P as protectWindowMs, X as collectGradleVersions, Z as deleteGradleVersion, Zn as collectMemoryReport, _ as resolveBudget, at as DEVICE_LIST_TIMEOUT_MS, ct as collectParkedSims, d as enforceBudget, dr as pruneCache, dt as deleteProjectDevices, j as levelFloors, lt as deleteParkedAvds, mt as deviceSweepIsScoped, nr as memorySweepIsScoped, nt as waitForGradleExit, or as stopIdleGradleDaemons, p as machineNumber, sr as phase, st as collectParkedAvds, u as cacheLabel, ur as discoverCaches, ut as deleteParkedSims, v as trimmableCaches, vt as findStaleProjectDevices, y as stopOwnedMetro, z as emptyCaches } from "./ccache-BWTze1Ls.mjs";
10
+ import { a as unregister, n as readManifest } from "./cache-manifest-DEjqGGat.mjs";
11
+ import { Dt as readConcurrency, L as workspaceBuildInProgress, M as collectOwnedDeviceActivityAsync, St as GC_OUTPUT, Tt as eachLimited, _ as withIdleWorkspace, a as collectOrphanedWorkspaces, c as isInsideWorkspaces, h as ARTIFACT_DIRS, i as clearWorkspaceOutputs, j as collectOwnedDeviceActivity, l as listWorkspaceDirs, m as workspaceDirRemoved, o as collectWorkspaceOutputs, p as workspaceBuildReasons, s as deleteOrphanedWorkspaces, t as LIGHTEN_AFTER_MS, u as recentNativeUseReason, v as workspaceInUse, wt as takeGcResults, y as workspaceInUseAsync } from "./workspaces-DFbj07jp.mjs";
11
12
  import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
12
- import { B as GC_OUTPUT, H as takeGcResults, a as collectWorkspaceOutputs, c as listWorkspaceDirs, d as workspaceDirRemoved, h as workspaceInUseAsync, i as collectOrphanedWorkspaces, m as workspaceInUse, o as deleteOrphanedWorkspaces, p as withIdleWorkspace, r as clearWorkspaceOutputs, s as isInsideWorkspaces } from "./workspaces-g2lhdBXF.mjs";
13
- import { N as listBuildLocks, _ as workspaceDeviceLastUseAt, b as leaseIsExpired, g as workspaceActivity, x as listLeaseFiles } from "./activity-Lnb_EoRY.mjs";
14
- import { d as listAllIosSims, f as listAllIosSimsAsync } from "./ios-WxpDJ5T1.mjs";
13
+ import { N as listBuildLocks, _ as workspaceDeviceLastUseAt, b as leaseIsExpired, g as workspaceActivity, x as listLeaseFiles } from "./activity-Cd-ysFes.mjs";
14
+ import { d as listAllIosSims, f as listAllIosSimsAsync } from "./ios-E26qSpkb.mjs";
15
15
  import { s as metroStoreRoot } from "./errors-DHm_U5gv.mjs";
16
16
  import { t as resolveMaintenanceSettings } from "./settings-Djl-mpjX.mjs";
17
- import { i as writeIdleShutdownEvent, n as dueIdleDevices, r as shutDownIdleDevices } from "./idle-shutdown-FfxL1KdG.mjs";
18
- import { A as measurePressureAsync, D as measureDisk, E as cacheBlocked, I as lowestFree, L as protectWindowMs, N as diskPressureLevel, O as measureDiskAsync, P as levelFloors, a as matchWorktreeEntry, c as reclaimKeys, i as excludeWatchmanCookies, k as measurePressure, m as removeWorktreeTarget, o as nestedWorktrees, r as excludePodChurn, s as porcelainPath } from "./worktree-BKuklFh_.mjs";
19
- import { c as FETCH_FRESH_MS, d as localDefaultBranchAsync, l as fetchDefaultBranchAsync, o as recordPullRequestLookup, p as mergeStateAsync, u as lastFetchedAtAsync } from "./warm-progress-DvhTX_eu.mjs";
20
- import { n as describePullRequest, r as endedPullRequest, s as pullRequestLookups } from "./pull-request-CbNlkFok.mjs";
17
+ import { D as stimCliEntries, E as serviceRunningFrom, N as worktreeArchiveLimits, O as stimCliUseOf, S as measurePressureAsync, T as uncommittedActivityOf, a as matchWorktreeEntry, b as measureDiskAsync, c as reclaimKeys, i as excludeWatchmanCookies, m as removeWorktreeTarget, o as nestedWorktrees, r as excludePodChurn, s as porcelainPath, v as cacheBlocked, x as measurePressure, y as measureDisk } from "./worktree-COAJ3-OD.mjs";
18
+ import { n as describePullRequest, r as endedPullRequest, s as pullRequestLookups } from "./pull-request-BzLmzAIQ.mjs";
19
+ import { a as lastFetchedAtAsync, c as mergeStateAsync, f as recordPullRequestLookup, i as fetchDefaultBranchAsync, o as localDefaultBranchAsync, r as FETCH_FRESH_MS } from "./warm-progress-C0RsbngN.mjs";
20
+ import { a as restoreProblem, r as recordLightened, t as finishLightened } from "./lightened-BCsdAGj_.mjs";
21
+ import { i as writeIdleShutdownEvent, n as dueIdleDevices, r as shutDownIdleDevices } from "./idle-shutdown-Cbvo5KEh.mjs";
21
22
  import { readClaimSet } from "@stim-cli/core/ownership-claim";
22
- import { existsSync, lstatSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
23
+ import { existsSync, lstatSync, readdirSync, readlinkSync, realpathSync, rmSync, statSync } from "node:fs";
23
24
  import { tmpdir } from "node:os";
24
25
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
25
26
  import { buildCacheRoot, configDir, metroCacheRoot } from "@stim-cli/core";
26
- import { LAST_BUILD_KEYS, MAINTENANCE_LOG_NAME, loadConfig, readBuildHistory, readMaintenanceState, readPullRequestCache, settingDefinition, sharedCompilationCache } from "@stim-cli/core/state";
27
+ import { LAST_BUILD_KEYS, MAINTENANCE_LOG_NAME, lastNativeUseFrom, loadConfig, readBuildHistory, readMaintenanceState, readPullRequestCache, settingDefinition, sharedCompilationCache } from "@stim-cli/core/state";
27
28
  import { isAbsolute as isAbsolute$1, join as join$1 } from "path";
28
29
  import chalk from "chalk";
29
30
  import { existsSync as existsSync$1, readdirSync as readdirSync$1, statSync as statSync$1 } from "fs";
30
31
  import { lstat, readdir, realpath, rename, stat } from "node:fs/promises";
31
- //#region src/commands/gc/limited.ts
32
- const isCleanupFailure = (error) => error instanceof WorkspaceProcessCleanupError || error instanceof Error && "reportCleanup" in error;
33
- async function eachLimited(items, limit, run) {
34
- const queue = items[Symbol.iterator]();
35
- let failed = false;
36
- const worker = async () => {
37
- for (const item of queue) {
38
- if (failed) return;
39
- try {
40
- await run(item);
41
- } catch (error) {
42
- failed = true;
43
- throw error;
44
- }
45
- }
46
- };
47
- const errors = (await Promise.allSettled(Array.from({ length: Math.min(limit, items.length) }, worker))).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
48
- if (errors.length) throw errors.find(isCleanupFailure) ?? errors[0];
49
- }
50
- function readConcurrency(operation) {
51
- return operationState(operation).declaring.size ? 1 : 6;
52
- }
53
- //#endregion
54
32
  //#region src/commands/gc/worktrees.ts
55
33
  const DEFAULT_WORKTREE_IDLE_DAYS = 7;
56
34
  const DIRTY_PATHS_SHOWN = 3;
57
35
  const DAY_MS = 864e5;
58
36
  const MINUTE_MS = 6e4;
59
37
  const GRACE_SETTING = "gc.worktreeGraceMinutes";
38
+ const PROTECT_SETTING = "maintenance.protectRecentHours";
60
39
  const NO_OWN_COMMITS_PRESSURE_FLOOR_MS = 5 * MINUTE_MS;
61
40
  const PULL_REQUEST_NOT_CHECKED = "not checked since the last maintenance worktree check";
62
41
  const WORKTREE_CHECK_SETTING = "maintenance.worktreeCheckMinutes";
@@ -101,6 +80,18 @@ function noOwnCommitsActivityBlocker(activity, idleMs, now) {
101
80
  eligibleAt
102
81
  };
103
82
  }
83
+ function dirtyLines(porcelain) {
84
+ return excludePodChurn(excludeWatchmanCookies(porcelain).lines).lines;
85
+ }
86
+ function finished(facts) {
87
+ return Boolean(facts.merge?.merged || endedPullRequest(facts.pullRequest));
88
+ }
89
+ function openPullRequest(lookup) {
90
+ return Boolean(lookup && "pullRequest" in lookup && lookup.pullRequest?.state === "open");
91
+ }
92
+ function newerActivity(a, b) {
93
+ return a && b ? b.at > a.at ? b : a : a ?? b;
94
+ }
104
95
  function hasNoOwnCommits(merge) {
105
96
  return merge?.merged === false && merge.noOwnCommits === true;
106
97
  }
@@ -139,11 +130,16 @@ function removalBlocker(facts, olderThan, now, noOwnCommitsIdleMs) {
139
130
  if (facts.nested?.length) return skip("nested-worktree", `contains the worktree ${facts.nested.join(", ")}`);
140
131
  if (facts.inUse.length) return skip("in-use", `in use: ${facts.inUse.join("; ")}`);
141
132
  if (facts.porcelain === null) return skip("status-unreadable", "git status could not be read");
142
- const dirty = excludePodChurn(excludeWatchmanCookies(facts.porcelain).lines).lines;
133
+ const dirty = dirtyLines(facts.porcelain);
143
134
  if (dirty.length) {
144
135
  const paths = dirty.slice(0, DIRTY_PATHS_SHOWN).flatMap((line) => porcelainPath(line) ?? []);
145
136
  const more = dirty.length > DIRTY_PATHS_SHOWN ? ", ..." : "";
146
- return skip("dirty", `dirty: ${plural(dirty.length, "uncommitted or untracked file")} (${paths.join(", ")}${more})`);
137
+ const kept = skip("dirty", `dirty: ${plural(dirty.length, "uncommitted or untracked file")} (${paths.join(", ")}${more})`);
138
+ if (!facts.archiveDirty) return kept;
139
+ return removalBlocker({
140
+ ...facts,
141
+ porcelain: []
142
+ }, olderThan, now, noOwnCommitsIdleMs) === null && finished(facts) ? null : kept;
147
143
  }
148
144
  if (facts.unpushed === null) return skip("unpushed-unchecked", "unpushed commits could not be checked");
149
145
  const merged = facts.merge?.merged ? facts.merge : null;
@@ -154,17 +150,9 @@ function removalBlocker(facts, olderThan, now, noOwnCommitsIdleMs) {
154
150
  if (merged || ended) return null;
155
151
  const notMerged = facts.merge && !facts.merge.merged ? facts.merge : null;
156
152
  const details = [notMerged?.detail, pullRequestNote(facts.pullRequest)].filter(Boolean);
157
- const noOwnCommits = hasNoOwnCommits(notMerged);
158
- if (olderThan === null) {
159
- if (noOwnCommits && noOwnCommitsIdleMs !== null) return noOwnCommitsActivityBlocker(facts.activity, noOwnCommitsIdleMs, now);
160
- return skip(notMerged?.unknown ? "merge-unknown" : "not-merged", details.join("; ") || "not merged");
161
- }
153
+ if (hasNoOwnCommits(notMerged) && !openPullRequest(facts.pullRequest) && noOwnCommitsIdleMs !== null) return noOwnCommitsActivityBlocker(facts.activity, noOwnCommitsIdleMs, now);
154
+ if (olderThan === null) return skip(notMerged?.unknown ? "merge-unknown" : "not-merged", details.join("; ") || "not merged");
162
155
  const also = details.map((detail) => `; ${detail}`).join("");
163
- if (facts.idleDays === null && noOwnCommits && facts.activity) {
164
- const days = activityIdleDays(facts.activity, now);
165
- if (days < olderThan) return skip("recently-used", `recently active: ${facts.activity.basis} ${days}d ago${also}`);
166
- return null;
167
- }
168
156
  if (facts.idleDays === null) return skip("last-use-unknown", `recently used: its last use is unknown${also}`);
169
157
  if (facts.idleDays < olderThan) return skip("recently-used", `recently used ${facts.idleDays}d ago${also}`);
170
158
  return null;
@@ -176,11 +164,16 @@ function removalBlocker(facts, olderThan, now, noOwnCommitsIdleMs) {
176
164
  */
177
165
  function worktreeRemovalReason(candidate, sweep) {
178
166
  const { merge, activity } = candidate;
179
- if (merge?.merged) return `merged into ${merge.into}`;
167
+ const archived = candidate.dirty ? `, ${plural(candidate.dirty, "uncommitted file")} archived first` : "";
168
+ if (merge?.merged) return `merged into ${merge.into}${archived}`;
180
169
  const ended = endedPullRequest(candidate.pullRequest);
181
- if (ended) return describePullRequest(ended);
170
+ if (ended) return `${describePullRequest(ended)}${archived}`;
182
171
  if (merge && hasNoOwnCommits(merge)) {
183
- if (sweep.noOwnCommitsIdleMs !== null && activity) return `${merge.detail}, inactive for ${formatLongDuration(Math.max(0, sweep.now - activity.at))} under disk pressure`;
172
+ if (sweep.noOwnCommitsIdleMs !== null && activity) {
173
+ const age = Math.max(0, sweep.now - activity.at);
174
+ if (sweep.underPressure) return `${merge.detail}, inactive for ${formatLongDuration(age)} under disk pressure`;
175
+ return age >= DAY_MS ? `${merge.detail}, idle ${activityIdleDays(activity, sweep.now)}d` : `${merge.detail}, inactive for ${formatLongDuration(age)}`;
176
+ }
184
177
  const days = candidate.idleDays ?? (activity ? activityIdleDays(activity, sweep.now) : null);
185
178
  return days === null ? merge.detail : `${merge.detail}, idle ${days}d`;
186
179
  }
@@ -366,6 +359,10 @@ function activityFromGitDir(gitDir, keys) {
366
359
  const newest = newestMtime(paths);
367
360
  return newest === "unreadable" ? null : newest;
368
361
  }
362
+ function protectRecentMs(operation) {
363
+ const { value, error } = machineNumber(PROTECT_SETTING, loadConfig$1(), operation?.env ?? process.env);
364
+ return (error ? Number(settingDefinition(PROTECT_SETTING)?.default) : value ?? 0) * 60 * MINUTE_MS;
365
+ }
369
366
  function graceMsSetting(operation) {
370
367
  const { value, error } = machineNumber(GRACE_SETTING, loadConfig$1(), operation?.env ?? process.env);
371
368
  if (error) {
@@ -493,22 +490,25 @@ async function checkMergeStates(pending, idle, grace, noOwnCommitsIdleMs, refres
493
490
  }
494
491
  }
495
492
  /**
496
- * Classifies each linked worktree of a registered project or its repository. A merged one is removable; with `idle`, so is one unused for
497
- * `olderThan` days; with `removesNoOwnCommits`, so is one with no commits of its own beyond the default branch whose
498
- * last activity is older than the grace period, and never younger than 5 minutes. Device use is read last, in one pass against one simulator list and one AVD resolver, so
493
+ * Classifies each linked worktree of a registered project or its repository. A merged one is removable, a dirty one
494
+ * after archiving its uncommitted work when archives are on; with `idle`, so is one unused for `olderThan` days. One
495
+ * with no commits of its own beyond the default branch is removable once its last activity is older than the grace
496
+ * period and `maintenance.protectRecentHours`, or, `underPressure`, than the grace period alone; never younger than 5
497
+ * minutes. Device use is read last, in one pass against one simulator list and one AVD resolver, so
499
498
  * the resolver's time budget covers only device reads. Pull requests are asked once per repository, sequentially
500
499
  * with an operation and concurrently otherwise. Merge state is checked only where it decides the verdict, after one fetch per repository.
501
500
  * Without `refresh`, it runs no fetch and no `gh`: merge state comes from the existing remote-tracking ref and pull
502
501
  * requests from the cache `status --watch` writes, and a kept verdict that fresher state could change says so.
503
502
  */
504
- async function collectWorktreeSweep({ idle, olderThan, now, graceMs, removesNoOwnCommits = false, refresh = true, operation }) {
503
+ async function collectWorktreeSweep({ idle, olderThan, now, graceMs, underPressure = false, refresh = true, operation }) {
505
504
  const days = idle ? olderThan ?? DEFAULT_WORKTREE_IDLE_DAYS : null;
506
505
  const grace = {
507
506
  ms: graceMs ?? graceMsSetting(operation),
508
507
  now
509
508
  };
510
509
  const freshMs = refresh ? 0 : reportFreshMs(operation);
511
- const noOwnCommitsIdleMs = removesNoOwnCommits ? Math.max(grace.ms, NO_OWN_COMMITS_PRESSURE_FLOOR_MS) : null;
510
+ const noOwnCommitsIdleMs = Math.max(grace.ms, underPressure ? 0 : protectRecentMs(operation), NO_OWN_COMMITS_PRESSURE_FLOOR_MS);
511
+ const cli = stimCliEntries(operation?.env);
512
512
  const groups = /* @__PURE__ */ new Map();
513
513
  operation?.signal.throwIfAborted();
514
514
  const read = {
@@ -551,22 +551,29 @@ async function collectWorktreeSweep({ idle, olderThan, now, graceMs, removesNoOw
551
551
  const keys = [.../* @__PURE__ */ new Set([...roots, ...reclaimKeys(path)])];
552
552
  const idleDays = idleDaysOf(keys, now);
553
553
  const linked = !("refusal" in source) && entry !== null && source.path !== entry.path;
554
- const activity = linked && (grace.ms > 0 || days !== null || removesNoOwnCommits) ? await worktreeActivityOfAsync(path, keys, operation) : null;
555
- const gitAnswered = linked ? await unlessTimedOut(() => hasUncommittedWorkAsync(path, read), null) : null;
554
+ const porcelain = await porcelainOf(path, linked ? await unlessTimedOut(() => hasUncommittedWorkAsync(path, read), null) : null, operation);
555
+ const dirty = linked ? dirtyLines(porcelain).length : 0;
556
+ const archiveDirty = dirty > 0 && !underPressure && !("refusal" in worktreeArchiveLimits(path, operation?.env));
557
+ let activity = linked ? await worktreeActivityOfAsync(path, keys, operation) : null;
558
+ if (activity && archiveDirty) {
559
+ const edited = await uncommittedActivityOf(path, operation);
560
+ activity = edited === "unreadable" ? null : newerActivity(activity, edited);
561
+ }
556
562
  const head = linked ? await resolveFullRefAsync(path, "HEAD", read) : null;
557
563
  const facts = {
558
564
  source: "refusal" in source ? source : linked ? "linked" : "source",
559
565
  bare: Boolean(entry?.bare),
560
566
  locked: Boolean(entry?.locked),
561
567
  nested: entry ? nestedWorktrees(entries, entry.path) : [],
562
- porcelain: await porcelainOf(path, gitAnswered, operation),
568
+ porcelain,
563
569
  unpushed: linked ? await unlessTimedOut(() => unpushedCommitsAsync(path, read), null) : null,
564
570
  submodules: linked ? await unlessTimedOut(() => hasPopulatedSubmodulesAsync(path, read), null) : false,
565
- inUse: [],
571
+ inUse: linked ? stimCliUseOf(path, canonicalPath, cli) : [],
566
572
  idleDays,
567
573
  merge: null,
568
574
  pullRequest: null,
569
- activity
575
+ activity,
576
+ archiveDirty
570
577
  };
571
578
  inspected[index] = {
572
579
  candidate: {
@@ -579,7 +586,8 @@ async function collectWorktreeSweep({ idle, olderThan, now, graceMs, removesNoOw
579
586
  skipCode: null,
580
587
  skipped: null,
581
588
  eligibleAt: null,
582
- activity
589
+ activity,
590
+ ...archiveDirty ? { dirty } : {}
583
591
  },
584
592
  facts,
585
593
  repo: "refusal" in source ? null : source.path,
@@ -602,7 +610,7 @@ async function collectWorktreeSweep({ idle, olderThan, now, graceMs, removesNoOw
602
610
  skipped: verdict?.text ?? null,
603
611
  eligibleAt: verdict?.eligibleAt ?? null
604
612
  });
605
- if (verdict && MERGE_DECIDES.has(verdict.code) && repo !== null) pending.push({
613
+ if (verdict && (MERGE_DECIDES.has(verdict.code) || verdict.code === "dirty" && facts.archiveDirty) && repo !== null) pending.push({
606
614
  candidate,
607
615
  facts,
608
616
  repo
@@ -619,6 +627,7 @@ async function collectWorktreeSweep({ idle, olderThan, now, graceMs, removesNoOw
619
627
  graceMs: grace.ms,
620
628
  now,
621
629
  noOwnCommitsIdleMs,
630
+ underPressure,
622
631
  worktrees: listed
623
632
  };
624
633
  }
@@ -686,16 +695,8 @@ function cachedPullRequest(cached, branch, head, now, freshMs) {
686
695
  } };
687
696
  }
688
697
  async function noOwnCommitsRecheck(sweep, path, keys, now, operation) {
689
- if (!sweep.idle) {
690
- if (sweep.noOwnCommitsIdleMs === null) return "it has no commits of its own and is not idle";
691
- return noOwnCommitsActivityBlocker(await worktreeActivityOfAsync(path, keys, operation), sweep.noOwnCommitsIdleMs, now)?.text ?? null;
692
- }
693
- const idleDays = idleDaysOf(keys, now);
694
- if (idleDays !== null) return idleDays < sweep.idle.olderThan ? `used ${idleDays}d ago since gc checked it` : null;
695
- const activity = await worktreeActivityOfAsync(path, keys, operation);
696
- if (!activity) return "its last activity could not be read";
697
- const days = activityIdleDays(activity, now);
698
- return days < sweep.idle.olderThan ? `active ${days}d ago since gc checked it: ${activity.basis}` : null;
698
+ if (sweep.noOwnCommitsIdleMs === null) return "it has no commits of its own and is not idle";
699
+ return noOwnCommitsActivityBlocker(await worktreeActivityOfAsync(path, keys, operation), sweep.noOwnCommitsIdleMs, now)?.text ?? null;
699
700
  }
700
701
  async function removeWorktrees(sweep, { now = Date.now(), operation } = {}, output = GC_OUTPUT) {
701
702
  let failures = 0;
@@ -707,7 +708,11 @@ async function removeWorktrees(sweep, { now = Date.now(), operation } = {}, outp
707
708
  const keys = [.../* @__PURE__ */ new Set([...candidate.keys, ...lockedKeys])];
708
709
  const unlocked = keys.filter((key) => !lockedKeys.includes(key));
709
710
  const devices = await reportDeviceReads(keys, operation);
710
- const reasons = [...await inUseOf(lockedKeys, { managedLocks: false }, devices, operation?.signal, operation), ...await inUseOf(unlocked, { managedLocks: true }, devices, operation?.signal, operation)].map((r) => `in use: ${r}`);
711
+ const reasons = [
712
+ ...stimCliUseOf(candidate.path, canonicalPath, stimCliEntries(operation?.env)),
713
+ ...await inUseOf(lockedKeys, { managedLocks: false }, devices, operation?.signal, operation),
714
+ ...await inUseOf(unlocked, { managedLocks: true }, devices, operation?.signal, operation)
715
+ ].map((r) => `in use: ${r}`);
711
716
  const mergedHead = candidate.merge?.merged ? candidate.merge.head : endedPullRequest(candidate.pullRequest) ? candidate.head : null;
712
717
  if (mergedHead) {
713
718
  if (await resolveFullRefAsync(candidate.path, "HEAD", {
@@ -728,10 +733,12 @@ async function removeWorktrees(sweep, { now = Date.now(), operation } = {}, outp
728
733
  if (idleDays === null || !sweep.idle || idleDays < sweep.idle.olderThan) reasons.push(`used ${idleDays === null ? "at an unknown time" : `${idleDays}d ago`} since gc checked it`);
729
734
  }
730
735
  if (sweep.graceMs > 0) {
736
+ const activity = await worktreeActivityOfAsync(candidate.path, keys, operation);
737
+ const edited = candidate.dirty && activity ? await uncommittedActivityOf(candidate.path, operation) : null;
731
738
  const recent = graceBlocker({
732
739
  merge: candidate.merge,
733
740
  pullRequest: candidate.pullRequest,
734
- activity: await worktreeActivityOfAsync(candidate.path, keys, operation)
741
+ activity: edited === "unreadable" ? null : newerActivity(activity, edited)
735
742
  }, {
736
743
  ms: sweep.graceMs,
737
744
  now: Date.now()
@@ -743,6 +750,7 @@ async function removeWorktrees(sweep, { now = Date.now(), operation } = {}, outp
743
750
  };
744
751
  let removed = false;
745
752
  let error = null;
753
+ const archived = { archive: null };
746
754
  try {
747
755
  const ended = endedPullRequest(candidate.pullRequest);
748
756
  const mergedHead = candidate.merge?.merged && candidate.merge.coversUnpushed ? candidate.merge.head : ended?.state === "merged" ? candidate.head ?? void 0 : void 0;
@@ -753,7 +761,17 @@ async function removeWorktrees(sweep, { now = Date.now(), operation } = {}, outp
753
761
  mergedHead,
754
762
  expectedHead,
755
763
  output,
756
- operation
764
+ operation,
765
+ ...candidate.dirty ? { archiveDirty: {
766
+ pullRequest: ended ? {
767
+ number: ended.number,
768
+ url: ended.url,
769
+ state: ended.state
770
+ } : null,
771
+ onArchived: (saved) => {
772
+ archived.archive = saved;
773
+ }
774
+ } } : {}
757
775
  });
758
776
  } catch (thrown) {
759
777
  if (thrown instanceof WorkspaceProcessCleanupError || thrown instanceof Error && "reportCleanup" in thrown) throw thrown;
@@ -762,8 +780,16 @@ async function removeWorktrees(sweep, { now = Date.now(), operation } = {}, outp
762
780
  output.error(chalk.red(`Could not remove ${candidate.path}: ${error}`));
763
781
  }
764
782
  if (removed) {
765
- output.log(chalk.green(`Removed the worktree ${candidate.path} (${worktreeRemovalReason(candidate, sweep)})`));
766
- output.record("worktree", "done", candidate.path, { detail: worktreeRemovalReason(candidate, sweep) });
783
+ const saved = archived.archive;
784
+ const reason = saved ? `${worktreeRemovalReason({
785
+ ...candidate,
786
+ dirty: 0
787
+ }, sweep)}; archived ${plural(saved.tracked.length + saved.untracked.length, "uncommitted file")} to ${saved.dir}; restore: ${saved.restore.join(" && ")}` : worktreeRemovalReason({
788
+ ...candidate,
789
+ dirty: 0
790
+ }, sweep);
791
+ output.log(chalk.green(`Removed the worktree ${candidate.path} (${reason})`));
792
+ output.record("worktree", "done", candidate.path, { detail: reason });
767
793
  } else if (kept.length) {
768
794
  output.log(chalk.yellow(`Kept the worktree ${candidate.path}: ${kept.join("; ")}`));
769
795
  output.record("worktree", "kept", candidate.path, { detail: kept.join("; ") });
@@ -1399,7 +1425,7 @@ async function planWorktrees({ protectedRoot, pinned, now, level, graceMs, sizes
1399
1425
  idle: idleDays !== null,
1400
1426
  olderThan: idleDays,
1401
1427
  now,
1402
- removesNoOwnCommits: level === 3,
1428
+ underPressure: level === 3,
1403
1429
  operation,
1404
1430
  ...graceMs === void 0 ? {} : { graceMs },
1405
1431
  ...refresh === void 0 ? {} : { refresh }
@@ -1455,18 +1481,118 @@ async function planWorktrees({ protectedRoot, pinned, now, level, graceMs, sizes
1455
1481
  };
1456
1482
  }
1457
1483
  //#endregion
1484
+ //#region src/workspace/running-from.ts
1485
+ const LIST_TIMEOUT_MS = 1e4;
1486
+ function parsePs(text) {
1487
+ return text.split("\n").flatMap((row) => {
1488
+ const match = /^\s*(\d+)\s+(.*)$/.exec(row);
1489
+ return match ? [{
1490
+ pid: Number(match[1]),
1491
+ line: match[2] ?? ""
1492
+ }] : [];
1493
+ });
1494
+ }
1495
+ /** `lsof -Fpn` output: a `p<pid>` line, then the `n<path>` of its working directory. */
1496
+ function parseLsof(text) {
1497
+ const cwds = [];
1498
+ let pid = 0;
1499
+ for (const row of text.split("\n")) if (row.startsWith("p")) pid = Number(row.slice(1));
1500
+ else if (row.startsWith("n") && pid) cwds.push({
1501
+ pid,
1502
+ path: row.slice(1)
1503
+ });
1504
+ return cwds;
1505
+ }
1506
+ function procCwds() {
1507
+ try {
1508
+ return readdirSync("/proc").filter((name) => /^\d+$/.test(name)).flatMap((name) => {
1509
+ try {
1510
+ return [{
1511
+ pid: Number(name),
1512
+ path: readlinkSync(join("/proc", name, "cwd"))
1513
+ }];
1514
+ } catch {
1515
+ return [];
1516
+ }
1517
+ });
1518
+ } catch {
1519
+ return null;
1520
+ }
1521
+ }
1522
+ function listsSelf(list) {
1523
+ return list.some(({ pid }) => pid === process.pid);
1524
+ }
1525
+ function runningFrom(ps, cwds) {
1526
+ if (ps === null || cwds === null) return null;
1527
+ const commands = parsePs(ps);
1528
+ return listsSelf(commands) && listsSelf(cwds) ? {
1529
+ commands,
1530
+ cwds
1531
+ } : null;
1532
+ }
1533
+ const PS_ARGS = [
1534
+ "-axww",
1535
+ "-o",
1536
+ "pid=,args="
1537
+ ];
1538
+ const LSOF_ARGS = [
1539
+ "-a",
1540
+ "-d",
1541
+ "cwd",
1542
+ "-Fpn",
1543
+ "-w"
1544
+ ];
1545
+ function readRunningProcesses(platform = process.platform) {
1546
+ if (platform !== "darwin" && platform !== "linux") return null;
1547
+ const exec = getExecutor();
1548
+ const ps = exec.runFileQuiet("ps", PS_ARGS, { timeoutMs: LIST_TIMEOUT_MS });
1549
+ if (platform === "linux") return runningFrom(ps, procCwds());
1550
+ const lsof = exec.runFileQuiet("lsof", LSOF_ARGS, { timeoutMs: LIST_TIMEOUT_MS });
1551
+ return runningFrom(ps, lsof === null ? null : parseLsof(lsof));
1552
+ }
1553
+ async function readRunningProcessesAsync(signal, platform = process.platform) {
1554
+ if (platform !== "darwin" && platform !== "linux") return null;
1555
+ const exec = getExecutor();
1556
+ const ps = await exec.runFileQuietAsync("ps", PS_ARGS, {
1557
+ timeoutMs: LIST_TIMEOUT_MS,
1558
+ signal
1559
+ });
1560
+ if (platform === "linux") return runningFrom(ps, procCwds());
1561
+ const lsof = await exec.runFileQuietAsync("lsof", LSOF_ARGS, {
1562
+ timeoutMs: LIST_TIMEOUT_MS,
1563
+ signal
1564
+ });
1565
+ return runningFrom(ps, lsof === null ? null : parseLsof(lsof));
1566
+ }
1567
+ function stimOnPath() {
1568
+ const found = getExecutor().findExecutable("stim");
1569
+ if (!found) return null;
1570
+ try {
1571
+ return realpathSync(found);
1572
+ } catch {
1573
+ return null;
1574
+ }
1575
+ }
1576
+ /**
1577
+ * Why something installed in or running from `folder` still needs it, or null: an installed stim-server service, the
1578
+ * `stim` on PATH, or a live process whose command line or working directory is inside it.
1579
+ */
1580
+ function runningFromReason(folder, running) {
1581
+ const root = canonicalPath(folder);
1582
+ const inside = (path) => path === root || path.startsWith(`${root}${sep}`);
1583
+ const service = serviceRunningFrom(root, canonicalPath);
1584
+ if (service) return `the stim-server service ${service.label} runs from it`;
1585
+ const stim = stimOnPath();
1586
+ if (stim && inside(stim)) return `the stim on PATH runs from it (${stim})`;
1587
+ if (!running) return "the running processes cannot be listed to prove nothing runs from it";
1588
+ const command = running.commands.find(({ line }) => line.includes(`${root}${sep}`));
1589
+ if (command) return `process ${command.pid} runs from it`;
1590
+ const cwd = running.cwds.find(({ path }) => inside(canonicalPath(path)));
1591
+ if (cwd) return `process ${cwd.pid} works in it`;
1592
+ return null;
1593
+ }
1594
+ //#endregion
1458
1595
  //#region src/maintenance/artifacts.ts
1459
- /** Directories that hold only what a build or an install regenerates, relative to an app or package root. */
1460
- const ARTIFACT_DIRS = {
1461
- build: [
1462
- "ios/build",
1463
- "android/app/build",
1464
- "android/build",
1465
- "android/.gradle",
1466
- ".build"
1467
- ],
1468
- dependencies: ["node_modules", "ios/Pods"]
1469
- };
1470
1596
  const PACKAGE_PARENTS = ["apps", "packages"];
1471
1597
  const SCAN_DEPTH = {
1472
1598
  build: 3,
@@ -1480,6 +1606,14 @@ function isDirectory(path) {
1480
1606
  return false;
1481
1607
  }
1482
1608
  }
1609
+ function pathExists(path) {
1610
+ try {
1611
+ lstatSync(path);
1612
+ return true;
1613
+ } catch {
1614
+ return false;
1615
+ }
1616
+ }
1483
1617
  function artifactWorktrees() {
1484
1618
  const found = /* @__PURE__ */ new Map();
1485
1619
  for (const root of candidateRoots()) {
@@ -1565,8 +1699,8 @@ async function newestWriteAsync(dir, maxDepth, signal) {
1565
1699
  };
1566
1700
  return await walk(dir, 0) ? newest : null;
1567
1701
  }
1568
- function keptReason(candidate, kind, { now, windowMs, pinned, locked }, reads) {
1569
- const guarded = artifactGuard(candidate, kind, pinned, locked);
1702
+ function keptReason(candidate, kind, { now, windowMs, pinned, locked, running }, reads) {
1703
+ const guarded = artifactGuard(candidate, kind, pinned, locked, running);
1570
1704
  if (guarded) return guarded;
1571
1705
  const { worktree, path, keys } = candidate;
1572
1706
  const tracked = reads ? reads.tracked : gitQuiet(worktree, [
@@ -1576,15 +1710,22 @@ function keptReason(candidate, kind, { now, windowMs, pinned, locked }, reads) {
1576
1710
  ]);
1577
1711
  if (tracked === null) return "git cannot tell whether it holds tracked files";
1578
1712
  if (tracked.trim()) return "it holds files git tracks";
1713
+ if (kind === "build") return buildNativeUseReason(keys, now);
1579
1714
  const activity = reads ? reads.activity : worktreeActivityOf(worktree, keys);
1580
1715
  const written = reads ? reads.written : newestWrite(path, SCAN_DEPTH[kind]);
1581
1716
  if (!activity || written === null) return "its last activity cannot be read";
1582
1717
  if (now - Math.max(activity.at, written) < windowMs) return `used within the last ${Math.round(windowMs / 6e4)} minutes`;
1583
1718
  return null;
1584
1719
  }
1585
- function artifactGuard(candidate, kind, pinned, locked) {
1720
+ function buildNativeUseReason(keys, now) {
1721
+ const states = keys.map((key) => readWorkspaceState$1(key)).filter((state) => state !== null);
1722
+ if (!states.length) return "no Stim workspace records when it last ran ios, android or macos";
1723
+ return recentNativeUseReason(Math.max(...states.map((state) => lastNativeUseFrom(state)).filter(Number.isFinite)), now);
1724
+ }
1725
+ function artifactGuard(candidate, kind, pinned, locked, running) {
1586
1726
  const { worktree, path, keys } = candidate;
1587
1727
  if (keys.some(pinned)) return "pinned by maintenance.keep";
1728
+ if (!pathExists(path)) return "it is already gone";
1588
1729
  let real;
1589
1730
  try {
1590
1731
  real = canonicalPath(path);
@@ -1592,16 +1733,15 @@ function artifactGuard(candidate, kind, pinned, locked) {
1592
1733
  return "its path cannot be resolved";
1593
1734
  }
1594
1735
  if (!isDirectory(path) || real !== join(canonicalPath(worktree), relative(worktree, path))) return "it or a directory above it is a link, or it lies outside its worktree";
1595
- const inUse = keys.flatMap((key) => workspaceInUse(key, {
1596
- supervisor: kind === "dependencies",
1597
- nativeRun: key !== locked
1598
- }));
1736
+ const inUse = keys.flatMap((key) => kind === "build" ? workspaceBuildReasons(key, { nativeRun: key !== locked }) : workspaceInUse(key, { nativeRun: key !== locked }));
1599
1737
  if (inUse.length) return `in use: ${inUse.join("; ")}`;
1738
+ if (kind === "dependencies") return runningFromReason(worktree, running === void 0 ? readRunningProcesses() : running);
1600
1739
  return null;
1601
1740
  }
1602
1741
  async function collectArtifactsAsync(kind, options, signal, operation) {
1603
1742
  const candidates = [];
1604
1743
  const skips = [];
1744
+ const running = kind === "dependencies" ? await readRunningProcessesAsync(signal) : null;
1605
1745
  for (const worktree of await artifactWorktreesAsync(signal, operation)) {
1606
1746
  if (kind === "dependencies" && !worktree.linked) continue;
1607
1747
  const root = canonicalPath(worktree.path);
@@ -1632,7 +1772,7 @@ async function collectArtifactsAsync(kind, options, signal, operation) {
1632
1772
  path,
1633
1773
  keys: worktree.keys
1634
1774
  };
1635
- let reason = protectedHere ? "the command that started this pass runs inside its worktree" : artifactGuard(candidate, kind, options.pinned);
1775
+ let reason = protectedHere ? "the command that started this pass runs inside its worktree" : artifactGuard(candidate, kind, options.pinned, void 0, running);
1636
1776
  if (!reason) {
1637
1777
  const tracked = await gitQuietAsync(worktree.path, [
1638
1778
  "ls-files",
@@ -1645,11 +1785,16 @@ async function collectArtifactsAsync(kind, options, signal, operation) {
1645
1785
  declareFor: operation,
1646
1786
  killSignal: "SIGKILL"
1647
1787
  });
1648
- const untracked = tracked !== null && !tracked.trim();
1649
- reason = keptReason(candidate, kind, options, {
1788
+ const windowed = tracked !== null && !tracked.trim() && kind === "dependencies";
1789
+ const activity = windowed ? await worktreeActivityOfAsync(worktree.path, worktree.keys, operation) : null;
1790
+ const written = windowed ? await newestWriteAsync(path, SCAN_DEPTH[kind], signal) : null;
1791
+ reason = keptReason(candidate, kind, {
1792
+ ...options,
1793
+ running
1794
+ }, {
1650
1795
  tracked,
1651
- activity: untracked ? await worktreeActivityOfAsync(worktree.path, worktree.keys, operation) : null,
1652
- written: untracked ? await newestWriteAsync(path, SCAN_DEPTH[kind], signal) : null
1796
+ activity,
1797
+ written
1653
1798
  });
1654
1799
  }
1655
1800
  if (reason) skips.push({
@@ -1668,6 +1813,7 @@ async function collectArtifactsAsync(kind, options, signal, operation) {
1668
1813
  function collectArtifacts(kind, options) {
1669
1814
  const candidates = [];
1670
1815
  const skips = [];
1816
+ const running = kind === "dependencies" ? readRunningProcesses() : null;
1671
1817
  for (const worktree of artifactWorktrees()) {
1672
1818
  if (kind === "dependencies" && !worktree.linked) continue;
1673
1819
  const root = canonicalPath(worktree.path);
@@ -1684,7 +1830,10 @@ function collectArtifacts(kind, options) {
1684
1830
  path,
1685
1831
  keys: worktree.keys
1686
1832
  };
1687
- const reason = protectedHere ? "the command that started this pass runs inside its worktree" : keptReason(candidate, kind, options);
1833
+ const reason = protectedHere ? "the command that started this pass runs inside its worktree" : keptReason(candidate, kind, {
1834
+ ...options,
1835
+ running
1836
+ });
1688
1837
  if (reason) skips.push({
1689
1838
  target: path,
1690
1839
  reason
@@ -1720,34 +1869,50 @@ async function clearArtifact(action, kind, options) {
1720
1869
  path: action.target,
1721
1870
  keys: worktree.keys
1722
1871
  };
1872
+ const running = kind === "dependencies" ? await readRunningProcessesAsync() : null;
1723
1873
  const early = keptReason(candidate, kind, {
1724
1874
  ...options,
1725
- now: Date.now()
1875
+ now: Date.now(),
1876
+ running
1726
1877
  });
1727
1878
  if (early) return {
1728
1879
  status: "kept",
1729
1880
  bytes: 0,
1730
1881
  detail: early
1731
1882
  };
1883
+ const unrestorable = kind === "dependencies" ? await restoreProblem(candidate, options.restoreCache) : null;
1884
+ if (unrestorable && options.requireRestorable) return {
1885
+ status: "kept",
1886
+ bytes: 0,
1887
+ detail: unrestorable
1888
+ };
1732
1889
  const before = lowestFree(measureDisk());
1733
1890
  const [key] = candidate.keys;
1734
- const remove = () => {
1891
+ const remove = async () => {
1735
1892
  const reason = keptReason(candidate, kind, {
1736
1893
  ...options,
1737
1894
  now: Date.now(),
1895
+ running: kind === "dependencies" ? await readRunningProcessesAsync() : null,
1738
1896
  ...key ? { locked: key } : {}
1739
1897
  });
1740
1898
  if (reason) return reason;
1899
+ const record = kind === "dependencies" && !unrestorable;
1900
+ if (record) recordLightened(candidate.keys, candidate.path);
1741
1901
  rmSync(candidate.path, {
1742
1902
  recursive: true,
1743
1903
  force: true
1744
1904
  });
1905
+ if (record) finishLightened(candidate.keys, candidate.path);
1745
1906
  return null;
1746
1907
  };
1747
1908
  const kept = key ? await withIdleWorkspace(key, remove, {
1748
1909
  purpose: "maintenance",
1749
- supervisor: kind === "dependencies"
1750
- }).then((run) => run.ran ? run.value : `in use: ${run.reasons.join("; ")}`) : remove();
1910
+ supervisor: kind === "dependencies",
1911
+ ...kind === "build" ? {
1912
+ hosted: false,
1913
+ managedLocks: false
1914
+ } : {}
1915
+ }).then((run) => run.ran ? run.value : `in use: ${run.reasons.join("; ")}`) : await remove();
1751
1916
  if (kept) return {
1752
1917
  status: "kept",
1753
1918
  bytes: 0,
@@ -2110,17 +2275,19 @@ async function planLevel2({ settings, sizes, protectedRoot, pinned, now, operati
2110
2275
  const actions = [];
2111
2276
  const skips = [];
2112
2277
  const reason = `disk pressure level 2, idle for ${settings.pressureProtectMinutes} minutes`;
2278
+ const lightenReason = `disk pressure level 2, no ios, android or macos run in the last ${LIGHTEN_AFTER_MS / 6e4} minutes`;
2113
2279
  const outputs = (await collectWorkspaceOutputs({
2114
2280
  olderThan: null,
2115
2281
  now,
2116
2282
  measure: false,
2117
2283
  signal: operation?.signal,
2118
- operation
2284
+ operation,
2285
+ lighten: true
2119
2286
  })).workspaces.flatMap((entry) => {
2120
2287
  const root = entry.projectRoot === null ? null : canonicalPath(entry.projectRoot);
2121
2288
  if (root === null || !entry.willClear || root === protectedRoot || pinned(root)) return [];
2122
2289
  const used = workspaceLastUsed(root);
2123
- return Number.isFinite(used) && now - used >= windowMs ? [{
2290
+ return Number.isFinite(used) ? [{
2124
2291
  root,
2125
2292
  used
2126
2293
  }] : [];
@@ -2132,7 +2299,7 @@ async function planLevel2({ settings, sizes, protectedRoot, pinned, now, operati
2132
2299
  target: root,
2133
2300
  workspace: root,
2134
2301
  bytes: size?.bytes ?? 0,
2135
- reason,
2302
+ reason: lightenReason,
2136
2303
  level: 2
2137
2304
  });
2138
2305
  }
@@ -2168,7 +2335,7 @@ async function planLevel2({ settings, sizes, protectedRoot, pinned, now, operati
2168
2335
  kind: "would-clear-build-dirs",
2169
2336
  target: candidate.path,
2170
2337
  bytes: 0,
2171
- reason,
2338
+ reason: lightenReason,
2172
2339
  level: 2
2173
2340
  });
2174
2341
  skips.push(...build.skips);
@@ -2628,14 +2795,16 @@ async function clearOutputs(action, context) {
2628
2795
  if (!root) return kept("the action names no workspace");
2629
2796
  if (root === context.protectedRoot) return kept("the command that started this pass runs in it");
2630
2797
  if (maintenancePinned(root)) return kept("pinned by maintenance.keep");
2631
- const recent = recentUse(root, context.settings, protectWindowMs(action, context.settings));
2798
+ const lighten = action.level === 2;
2799
+ const recent = lighten ? null : recentUse(root, context.settings, protectWindowMs(action, context.settings));
2632
2800
  if (recent) return kept(recent);
2633
2801
  const olderThan = action.olderThanDays ?? null;
2634
2802
  const now = Date.now();
2635
2803
  const report = collectWorkspaceOutputs({
2636
2804
  olderThan,
2637
2805
  now,
2638
- measure: false
2806
+ measure: false,
2807
+ lighten
2639
2808
  });
2640
2809
  const entry = report.workspaces.find((candidate) => candidate.projectRoot && canonicalPath(candidate.projectRoot) === root);
2641
2810
  if (!entry) return kept("it has no build outputs any more");
@@ -2644,7 +2813,8 @@ async function clearOutputs(action, context) {
2644
2813
  workspaces: [entry]
2645
2814
  }, {
2646
2815
  olderThan,
2647
- now
2816
+ now,
2817
+ lighten
2648
2818
  }));
2649
2819
  return outcomeOf(results, action.bytes);
2650
2820
  }
@@ -2660,7 +2830,12 @@ async function removeWorktree(action, context) {
2660
2830
  ...sweep,
2661
2831
  worktrees: [worktree]
2662
2832
  }));
2663
- return outcomeOf(results, freedSince(before));
2833
+ const outcome = outcomeOf(results, freedSince(before));
2834
+ const removal = results.find((result) => result.status === "done")?.detail;
2835
+ return outcome.status === "done" ? {
2836
+ ...outcome,
2837
+ detail: removal ?? action.reason
2838
+ } : outcome;
2664
2839
  }
2665
2840
  async function removeOrphan(action) {
2666
2841
  const orphan = (await collectOrphanedWorkspaces(Object.keys(loadConfig$1()?.projects ?? {}), listMountedVolumes())).orphaned.find((candidate) => candidate.dir === action.target);
@@ -2935,4 +3110,4 @@ function diskRecovered(budget) {
2935
3110
  return measureDisk().every((disk) => disk.freeMb >= floor);
2936
3111
  }
2937
3112
  //#endregion
2938
- export { readConcurrency as S, collectWorktreeSweep as _, previewMaintenance as a, worktreeRemovalReason as b, deleteTempLeftovers as c, ARTIFACT_DIRS as d, appRoots as f, candidateRootsAsync as g, ownedDeviceUsage as h, plannedMaintenance as i, tempLeftoverLines as l, measureOwnedDevices as m, diskRecovered as n, triggeringRoot as o, finishedWorkspaces as p, executeAction as r, collectTempLeftovers as s, deleteIdleWorkspaceDevices as t, tempLeftoverSection as u, removeWorktrees as v, eachLimited as x, worktreePullRequestNote as y };
3113
+ export { worktreeRemovalReason as C, worktreePullRequestNote as S, ownedDeviceUsage as _, previewMaintenance as a, collectWorktreeSweep as b, deleteTempLeftovers as c, appRoots as d, artifactWorktrees as f, measureOwnedDevices as g, finishedWorkspaces as h, plannedMaintenance as i, tempLeftoverLines as l, collectArtifactsAsync as m, diskRecovered as n, triggeringRoot as o, clearArtifact as p, executeAction as r, collectTempLeftovers as s, deleteIdleWorkspaceDevices as t, tempLeftoverSection as u, maintenancePinned as v, removeWorktrees as x, candidateRootsAsync as y };