stim 1.3.1 → 1.5.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 (67) hide show
  1. package/README.md +16 -3
  2. package/dist/{android-hMj83VpN.mjs → android-DJWiTBkz.mjs} +65 -72
  3. package/dist/{android-cas-DXorPCi_.mjs → android-cas-CSS8gjR7.mjs} +15 -12
  4. package/dist/{android-CT-PkXUh.mjs → android-sobvO9ia.mjs} +197 -114
  5. package/dist/{app-install-DIp9riNx.mjs → app-install--PAocWvq.mjs} +2 -2
  6. package/dist/{build-slots-C4AouyNz.mjs → build-slots-CuPszJDu.mjs} +7 -8
  7. package/dist/{cache-manifest-CXR-Y8pH.mjs → cache-manifest-uoOKhgUD.mjs} +1 -1
  8. package/dist/cache-manifest.mjs +1 -1
  9. package/dist/cli.mjs +14 -13
  10. package/dist/collector-run.mjs +4 -4
  11. package/dist/{command-output-8wHMEde0.mjs → command-output-BuIb_ZOI.mjs} +1 -4
  12. package/dist/{config-CcefMY2Q.mjs → config-DMP7L7H6.mjs} +52 -52
  13. package/dist/dependency-state-CTL8A9p3.mjs +76 -0
  14. package/dist/{deps-0Yt0Inly.mjs → deps-BAGOnrc7.mjs} +1 -1
  15. package/dist/{dev-client-VfSZNLT4.mjs → dev-client-BoifdqkU.mjs} +12 -12
  16. package/dist/{device-Bbv45Z9Z.mjs → device-BUw4JBuH.mjs} +7 -7
  17. package/dist/{xcode-xnxyvJkD.mjs → device-DvKJ454A.mjs} +208 -967
  18. package/dist/{device-lease-CJwK7OZr.mjs → device-lease-r7yEQ1R8.mjs} +3 -3
  19. package/dist/{device-pool-CcBNTvM7.mjs → device-pool-DU4FQKp5.mjs} +4 -4
  20. package/dist/{device-remote-DAA0Zm6Z.mjs → device-remote-BTUqbl0i.mjs} +7 -6
  21. package/dist/{doctor-B4EITgQB.mjs → doctor-Di_B26uh.mjs} +396 -117
  22. package/dist/{error-diagnostics-DwhNBFDU.mjs → error-diagnostics-CTePWAzW.mjs} +7 -6
  23. package/dist/{gc-BRu62v4o.mjs → gc-C20dWw_8.mjs} +61 -27
  24. package/dist/{guide-D95E0u3i.mjs → guide-sAnoty2y.mjs} +151 -20
  25. package/dist/guide-status-BF6RsJJp.mjs +271 -0
  26. package/dist/{ios-DAju3TVy.mjs → ios-BdpkncL6.mjs} +2667 -2607
  27. package/dist/{ios-device-CWOM_S44.mjs → ios-device-DJOi9FR5.mjs} +1 -1
  28. package/dist/{logs-CAdwW6c2.mjs → logs-BfQdIhiF.mjs} +5 -5
  29. package/dist/metro-ato6ef3J.mjs +99 -0
  30. package/dist/named-ports-DH9SXd0f.mjs +137 -0
  31. package/dist/{ownership-8O073GIC.mjs → ownership-CD_XnpDu.mjs} +5 -5
  32. package/dist/{ownership-DXcol4oU.mjs → ownership-CVY9cqCr.mjs} +6 -6
  33. package/dist/ports-A6uTJSPj.mjs +39 -0
  34. package/dist/ports-BZprTJRa.mjs +90 -0
  35. package/dist/{project-fKmuIaMI.mjs → project-B_SDt4ip.mjs} +2 -2
  36. package/dist/{reclaim-CUmwucWG.mjs → reclaim-C_rB25f4.mjs} +20 -13
  37. package/dist/{reload-CWpXoOgN.mjs → reload-CYBJbJfI.mjs} +8 -8
  38. package/dist/rolldown-runtime-BsOwBuO_.mjs +30 -0
  39. package/dist/{server-bare-DEzPZLvf.mjs → server-bare-H3pAhbMs.mjs} +11 -35
  40. package/dist/server-expo-CSwUVYOa.mjs +2 -0
  41. package/dist/{server-expo-DizT4OMk.mjs → server-expo-Ds8_mfng.mjs} +6 -14
  42. package/dist/{settings-DEJf6_kJ.mjs → settings-kO2ZqIKy.mjs} +2 -2
  43. package/dist/{slot-launch-CZklomj6.mjs → slot-launch-BpHiHH6A.mjs} +4 -4
  44. package/dist/{start-B5G4NjiA.mjs → start-DiEfqEWk.mjs} +20 -22
  45. package/dist/state-CnWzvCWP.mjs +131 -0
  46. package/dist/{stats-CaaQr4yN.mjs → stats-DHrO0frH.mjs} +4 -4
  47. package/dist/{stats-BR-kwj0B.mjs → stats-Fo9ACIv7.mjs} +1 -1
  48. package/dist/{status-CI9clRSb.mjs → status-J5LAXpag.mjs} +15 -12
  49. package/dist/{stop-CjlgDk0a.mjs → stop-0jJNahwN.mjs} +15 -14
  50. package/dist/{stop-CQhffpmX.mjs → stop-CM1fejKW.mjs} +2 -2
  51. package/dist/supervisor-run.d.mts +7 -28
  52. package/dist/supervisor-run.mjs +20 -11
  53. package/dist/{teardown--gESs9ck.mjs → teardown-CAVcWeRD.mjs} +148 -109
  54. package/dist/{workspace-process-lock-IBj1Towr.mjs → workspace-process-lock-Bz19_R9R.mjs} +4 -4
  55. package/dist/workspace-state-WVwWw_hB.mjs +146 -0
  56. package/dist/{worktree-DtQGO9-s.mjs → worktree-CeUKDAQw.mjs} +38 -34
  57. package/dist/{worktree-Eq6TLH8N.mjs → worktree-DJnBbQZ9.mjs} +79 -22
  58. package/dist/xcode-BqDeRkpT.mjs +860 -0
  59. package/package.json +7 -7
  60. package/shim/expo-metro-config.cjs +11 -17
  61. package/dist/doctor-BueDfLYG.mjs +0 -530
  62. package/dist/metro-CORqXyTL.mjs +0 -184
  63. package/dist/ownership-claim-BtPtJVmq.mjs +0 -584
  64. package/dist/server-expo-44em5Pmg.mjs +0 -2
  65. package/dist/state-B3XA2wp2.mjs +0 -268
  66. package/shim/metro-cache-generation.cjs +0 -20
  67. package/shim/metro-cache-generation.d.cts +0 -5
@@ -1,17 +1,16 @@
1
1
  import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { S as upsertProject, f as getProject, g as loadConfig, h as isPathPrefix, u as getConfigDir, v as removeProject, w as captureProcessIdentity } from "./config-CcefMY2Q.mjs";
3
- import { c as readMetroTunnel, u as readRemoteSession } from "./state-B3XA2wp2.mjs";
4
- import { _ as resolveFullRef, b as warmWorktreePaths, c as hasRemote, d as isPodInstallChurn, f as listWorktrees, h as removeWorktree, i as depsOutOfSync, l as hasUncommittedWork, m as readWorktreeExclude, n as cloneIgnoredEntries, o as dirtyPaths, p as podsOutOfSync, r as deleteBranch, s as gitCommonDir, t as branchExists, u as isMainWorkingTree, v as restoreFile, y as unpushedCommits } from "./worktree-Eq6TLH8N.mjs";
5
- import { a as findProjectRoot } from "./project-fKmuIaMI.mjs";
6
- import { a as formatElapsed, f as releasedLeaseFact, i as formatDuration, l as phaseLine, m as shortUdid, u as plural } from "./command-output-8wHMEde0.mjs";
7
- import { a as clearClaimChild, c as markClaimChildPending, d as releaseClaim, f as setClaimChild, i as claimRemoveCommand, l as processGroupAlive, m as tryAcquireClaim, n as ClaimUnavailableError, p as settleClaim, r as claimFailure } from "./ownership-claim-BtPtJVmq.mjs";
8
- import { A as resolveSettings, I as unknownSettingKeys, N as settingShapeErrors, a as SETTING_SHAPE_REMEDY } from "./settings-DEJf6_kJ.mjs";
9
- import { a as POOL_SETTING_REMEDY, c as parkedMaxSetting } from "./teardown--gESs9ck.mjs";
10
- import { g as podInstallCommand, r as HEARTBEAT_INTERVAL_MS } from "./xcode-xnxyvJkD.mjs";
11
- import { a as installedNpmTreeIsValid, i as hasInstalledDependencies, o as locallyKnownUpstream, t as dependencyState } from "./doctor-B4EITgQB.mjs";
12
- import { c as withManagedTunnelRemovalLock, o as withManagedRemoteWorktreeRemovalLock } from "./ownership-DXcol4oU.mjs";
13
- import { n as reclaimProject } from "./reclaim-CUmwucWG.mjs";
14
- import { a as runPodInstall, i as runCaptured, n as podsAreStale, r as readPodState, t as DEPS_ERROR } from "./deps-0Yt0Inly.mjs";
2
+ import { E as ownership_claim_exports, S as upsertProject, f as getProject, g as loadConfig, h as isPathPrefix, u as getConfigDir, v as removeProject, w as process_identity_exports } from "./config-DMP7L7H6.mjs";
3
+ import { S as warmWorktreePaths, _ as resolveFullRef, b as sourceCheckoutOf, c as hasRemote, d as listWorktrees, f as locallyKnownUpstream, h as removeWorktree, i as depsOutOfSync, l as hasUncommittedWork, m as readWorktreeExclude, n as cloneIgnoredEntries, o as dirtyPaths, p as podsOutOfSync, r as deleteBranch, s as gitCommonDir, t as branchExists, u as isPodInstallChurn, x as unpushedCommits, y as restoreFile } from "./worktree-DJnBbQZ9.mjs";
4
+ import { a as findProjectRoot } from "./project-B_SDt4ip.mjs";
5
+ import { l as readRemoteSession, s as readMetroTunnel } from "./state-CnWzvCWP.mjs";
6
+ import { a as formatElapsed, f as releasedLeaseFact, i as formatDuration, l as phaseLine, m as shortUdid, u as plural } from "./command-output-BuIb_ZOI.mjs";
7
+ import { A as resolveSettings, I as unknownSettingKeys, N as settingShapeErrors, a as SETTING_SHAPE_REMEDY } from "./settings-kO2ZqIKy.mjs";
8
+ import { a as POOL_SETTING_REMEDY, l as parkedMaxSetting } from "./teardown-CAVcWeRD.mjs";
9
+ import { h as podInstallCommand, n as HEARTBEAT_INTERVAL_MS } from "./xcode-BqDeRkpT.mjs";
10
+ import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-CTL8A9p3.mjs";
11
+ import { c as withManagedTunnelRemovalLock, o as withManagedRemoteWorktreeRemovalLock } from "./ownership-CVY9cqCr.mjs";
12
+ import { n as reclaimProject } from "./reclaim-C_rB25f4.mjs";
13
+ import { a as runPodInstall, i as runCaptured, n as podsAreStale, r as readPodState, t as DEPS_ERROR } from "./deps-BAGOnrc7.mjs";
15
14
  import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
16
15
  import { createHash, randomUUID } from "node:crypto";
17
16
  import { join, relative } from "node:path";
@@ -72,22 +71,22 @@ function installerClaim(claim, { now, progressMs, out }) {
72
71
  const forget = () => {
73
72
  groups = [];
74
73
  try {
75
- clearClaimChild(claim);
74
+ (0, ownership_claim_exports.clearClaimChild)(claim);
76
75
  } catch {}
77
76
  };
78
- const alive = () => groups.find((pid) => processGroupAlive(pid)) ?? null;
77
+ const alive = () => groups.find((pid) => (0, ownership_claim_exports.processGroupAlive)(pid)) ?? null;
79
78
  return {
80
- declare: () => markClaimChildPending(claim),
79
+ declare: () => (0, ownership_claim_exports.markClaimChildPending)(claim),
81
80
  record: (pid) => {
82
81
  if (pid === null) {
83
82
  forget();
84
83
  return;
85
84
  }
86
85
  groups.push(pid);
87
- const captured = captureProcessIdentity(pid);
86
+ const captured = (0, process_identity_exports.captureProcessIdentity)(pid);
88
87
  let failure = captured.ok ? null : captured.reason;
89
88
  if (captured.ok) try {
90
- setClaimChild(claim, {
89
+ (0, ownership_claim_exports.setClaimChild)(claim, {
91
90
  pid,
92
91
  processToken: captured.token
93
92
  });
@@ -95,7 +94,7 @@ function installerClaim(claim, { now, progressMs, out }) {
95
94
  } catch (error) {
96
95
  failure = error?.message ?? String(error);
97
96
  }
98
- if (!processGroupAlive(pid)) {
97
+ if (!(0, ownership_claim_exports.processGroupAlive)(pid)) {
99
98
  forget();
100
99
  return;
101
100
  }
@@ -132,7 +131,7 @@ function installerClaim(claim, { now, progressMs, out }) {
132
131
  }
133
132
  function timeoutError(holder, elapsedMs, root) {
134
133
  const who = holder ? `${warmCommand(holder.phase)} (pid ${holder.pid})` : "another stim worktree warm";
135
- const error = /* @__PURE__ */ new Error(`Waited ${formatElapsed(elapsedMs)} for ${who} to release the warm lock on this repository. The lock is ${root}; remove it if nothing is really warming:\n ${claimRemoveCommand(root)}`);
134
+ const error = /* @__PURE__ */ new Error(`Waited ${formatElapsed(elapsedMs)} for ${who} to release the warm lock on this repository. The lock is ${root}; remove it if nothing is really warming:\n ${(0, ownership_claim_exports.claimRemoveCommand)(root)}`);
136
135
  error.code = "STIM_LOCK_TIMEOUT";
137
136
  return error;
138
137
  }
@@ -144,7 +143,7 @@ async function acquireWarmClaim({ repositoryRoot, phase, mode = phase === "refre
144
143
  let pending = null;
145
144
  try {
146
145
  for (;;) {
147
- const attempt = pending ? settleClaim(pending) : tryAcquireClaim({
146
+ const attempt = pending ? (0, ownership_claim_exports.settleClaim)(pending) : (0, ownership_claim_exports.tryAcquireClaim)({
148
147
  root,
149
148
  mode,
150
149
  label: WARM_CLAIM_LABEL
@@ -164,7 +163,7 @@ async function acquireWarmClaim({ repositoryRoot, phase, mode = phase === "refre
164
163
  installer,
165
164
  release: () => {
166
165
  if (installer.holding()) return;
167
- releaseClaim(acquired);
166
+ (0, ownership_claim_exports.releaseClaim)(acquired);
168
167
  }
169
168
  };
170
169
  }
@@ -180,13 +179,13 @@ async function acquireWarmClaim({ repositoryRoot, phase, mode = phase === "refre
180
179
  await sleep(pollMs);
181
180
  }
182
181
  } catch (error) {
183
- releaseClaim(pending);
182
+ (0, ownership_claim_exports.releaseClaim)(pending);
184
183
  if ([
185
184
  "EACCES",
186
185
  "EPERM",
187
186
  "EROFS",
188
187
  "ENOENT"
189
- ].includes(error?.code ?? "")) throw new ClaimUnavailableError(error.message, { claimPath: root });
188
+ ].includes(error?.code ?? "")) throw new ownership_claim_exports.ClaimUnavailableError(error.message, { claimPath: root });
190
189
  throw error;
191
190
  }
192
191
  }
@@ -883,7 +882,7 @@ function registerWarm(worktree) {
883
882
  } catch (error) {
884
883
  const code = error?.code;
885
884
  console.error(chalk.red(`Could not warm this worktree: ${error.message}`));
886
- const claim = claimFailure(error, `stim worktree warm${opts.refresh ? " --refresh" : ""}`);
885
+ const claim = (0, ownership_claim_exports.claimFailure)(error, `stim worktree warm${opts.refresh ? " --refresh" : ""}`);
887
886
  if (claim) console.error(chalk.dim(claim.remedy));
888
887
  if (typeof code === "string" && code.startsWith("STIM_")) console.error(chalk.red(`failed: ${code}`));
889
888
  process.exitCode = 1;
@@ -1238,7 +1237,19 @@ async function runRemove(target, opts = {}) {
1238
1237
  process.exitCode = 1;
1239
1238
  return;
1240
1239
  }
1241
- if (isMainWorkingTree(entry.path)) {
1240
+ if (entry.bare) {
1241
+ console.error(chalk.red(`Refusing to remove ${path}: ${entry.path} is the bare repository, not a worktree.`));
1242
+ console.error(chalk.dim(" Pass a worktree path, e.g. as printed by `git worktree list`."));
1243
+ process.exitCode = 1;
1244
+ return;
1245
+ }
1246
+ const source = sourceCheckoutOf(worktrees);
1247
+ if ("refusal" in source) {
1248
+ console.error(chalk.red(`Refusing to remove ${path}: ${source.refusal}`));
1249
+ process.exitCode = 1;
1250
+ return;
1251
+ }
1252
+ if (entry.path === source.path) {
1242
1253
  if (entry.path !== path) console.error(chalk.dim(`${path} is inside the source checkout ${entry.path}; reclaiming its environment.`));
1243
1254
  await reclaimEnvironment(entry.path, "it is the source checkout");
1244
1255
  return;
@@ -1258,7 +1269,7 @@ async function runRemove(target, opts = {}) {
1258
1269
  }
1259
1270
  const deleteOwnedBranch = Boolean(ownsBranch && approvedBranchSha && inspection.unpushed?.length === 0);
1260
1271
  const retainedBranchReason = !branch ? null : !ownsBranch ? "Stim did not create it" : inspection.unpushed === null ? "Stim could not verify whether it has unique commits" : inspection.unpushed.length > 0 ? `it has ${plural(inspection.unpushed.length, "unique commit")}` : null;
1261
- const branchDeleteCwd = worktrees.find((candidate) => isMainWorkingTree(candidate.path))?.path;
1272
+ const branchDeleteCwd = source.path;
1262
1273
  await withManagedRemoteWorktreeRemovalLock(path, () => withReclaimLocks(path, async (lockedKeys) => {
1263
1274
  const result = await reclaimAll(path, lockedKeys, { preserveRootProject: true });
1264
1275
  if (result.keptEntries.length) {
@@ -1290,13 +1301,6 @@ async function runRemove(target, opts = {}) {
1290
1301
  worktreeRemovalComplete: true,
1291
1302
  worktreePendingBranchSha: approvedBranchSha
1292
1303
  });
1293
- if (!branchDeleteCwd) {
1294
- console.error(chalk.yellow(phaseLine("branch", `kept ${branch} (Stim could not find the source checkout)`)));
1295
- console.error(chalk.dim(` Retry with: stim worktree remove ${path}`));
1296
- process.exitCode = 1;
1297
- finish();
1298
- return;
1299
- }
1300
1304
  const checkedOutAt = listWorktrees(branchDeleteCwd).find((candidate) => candidate.branch === branch && resolve$1(candidate.path) !== resolve$1(path))?.path;
1301
1305
  if (checkedOutAt) {
1302
1306
  console.error(chalk.yellow(phaseLine("branch", `kept ${branch} (it is checked out at ${checkedOutAt})`)));
@@ -1,5 +1,5 @@
1
1
  import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
- import { g as loadConfig, u as getConfigDir } from "./config-CcefMY2Q.mjs";
2
+ import { g as loadConfig, u as getConfigDir } from "./config-DMP7L7H6.mjs";
3
3
  import { accessSync, chmodSync, constants, lstatSync, mkdirSync, mkdtempSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
4
4
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
5
  import { chmodSync as chmodSync$1, existsSync as existsSync$1, lstatSync as lstatSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, utimesSync } from "fs";
@@ -103,19 +103,6 @@ function gitCommonDir(cwd) {
103
103
  ]);
104
104
  return out ? out.trim() : null;
105
105
  }
106
- function isMainWorkingTree(path) {
107
- const out = getExecutor().runFileQuiet("git", [
108
- "-C",
109
- path,
110
- "rev-parse",
111
- "--path-format=absolute",
112
- "--git-dir",
113
- "--git-common-dir"
114
- ]);
115
- if (!out) return false;
116
- const [gitDir, commonDir] = out.trim().split("\n").map((line) => line.trim());
117
- return Boolean(gitDir) && gitDir === commonDir;
118
- }
119
106
  function repoRoot(cwd) {
120
107
  const out = getExecutor().runFileQuiet("git", [
121
108
  "-C",
@@ -125,20 +112,49 @@ function repoRoot(cwd) {
125
112
  ]);
126
113
  return out ? out.trim() : null;
127
114
  }
115
+ function locallyKnownUpstream(projectRoot) {
116
+ try {
117
+ const name = getExecutor().runFile("git", [
118
+ "-C",
119
+ projectRoot,
120
+ "rev-parse",
121
+ "--abbrev-ref",
122
+ "--symbolic-full-name",
123
+ "@{upstream}"
124
+ ], { timeoutMs: 5e3 });
125
+ const counts = getExecutor().runFile("git", [
126
+ "-C",
127
+ projectRoot,
128
+ "rev-list",
129
+ "--left-right",
130
+ "--count",
131
+ "HEAD...@{upstream}"
132
+ ], { timeoutMs: 5e3 }).trim().split(/\s+/).map(Number);
133
+ const ahead = counts[0] ?? NaN;
134
+ const behind = counts[1] ?? NaN;
135
+ if (!name || !Number.isInteger(ahead) || !Number.isInteger(behind)) return null;
136
+ return {
137
+ name,
138
+ ahead,
139
+ behind
140
+ };
141
+ } catch {
142
+ return null;
143
+ }
144
+ }
128
145
  function warmWorktreePaths(cwd) {
129
146
  const currentRoot = repoRoot(cwd);
130
147
  if (!currentRoot) throw new Error("Not a git repository.");
131
148
  const target = realpathSync$1(currentRoot);
132
- if (isMainWorkingTree(target)) throw new Error("Run stim worktree warm from a linked worktree, not the source checkout.");
133
- const entries = listWorktrees(target);
134
- const current = entries.find((entry) => canonicalPath(entry.path) === target);
135
- const main = entries.find((entry) => isMainWorkingTree(entry.path));
136
- if (!current || !main) throw new Error("Could not identify the linked worktree and its source checkout.");
137
- const root = realpathSync$1(main.path);
149
+ const source = resolveSourceCheckout(target);
150
+ if ("refusal" in source) throw new Error(source.refusal);
151
+ if (!source.entries.find((entry) => canonicalPath(entry.path) === target)) throw new Error("Could not identify the linked worktree and its source checkout.");
152
+ const root = realpathSync$1(source.path);
153
+ if (root === target) throw new Error("Run stim worktree warm from a linked worktree, not the source checkout.");
138
154
  const sourceRoot = repoRoot(root);
139
155
  const sourceCommon = gitCommonDir(root);
140
156
  const targetCommon = gitCommonDir(target);
141
- if (root === target || !sourceRoot || realpathSync$1(sourceRoot) !== root || !sourceCommon || !targetCommon || realpathSync$1(sourceCommon) !== realpathSync$1(targetCommon)) throw new Error("Could not verify that the source checkout belongs to this linked worktree.");
157
+ if (!sourceRoot || realpathSync$1(sourceRoot) !== root || !sourceCommon || !targetCommon || realpathSync$1(sourceCommon) !== realpathSync$1(targetCommon)) throw new Error("Could not verify that the source checkout belongs to this linked worktree.");
142
158
  return {
143
159
  root,
144
160
  target,
@@ -526,6 +542,7 @@ function parseWorktrees(out) {
526
542
  current = { path: line.slice(9) };
527
543
  } else if (line.startsWith("branch ")) current.branch = line.slice(7).replace("refs/heads/", "");
528
544
  else if (line === "prunable" || line.startsWith("prunable ")) current.prunable = true;
545
+ else if (line === "bare") current.bare = true;
529
546
  if (current.path) entries.push(current);
530
547
  return entries;
531
548
  }
@@ -539,5 +556,45 @@ function listWorktrees(cwd) {
539
556
  ]);
540
557
  return out ? parseWorktrees(out) : [];
541
558
  }
559
+ function selectSourceCheckout(entries, bareHead) {
560
+ const first = entries[0];
561
+ if (!first) return { refusal: "Git lists no worktrees for this repository." };
562
+ if (!first.bare) return { path: first.path };
563
+ const bare = first.path;
564
+ const checkouts = entries.filter((entry) => !entry.bare && !entry.prunable);
565
+ const listed = checkouts.map((entry) => `${entry.branch ?? "detached"} (${entry.path})`).join(", ") || "none";
566
+ const pointHead = ` git -C ${bare} symbolic-ref HEAD refs/heads/<branch>`;
567
+ if (!bareHead) return { refusal: `The bare repository at ${bare} has a detached HEAD, so Stim cannot tell which worktree is the source checkout. Point HEAD at the source branch, then retry:\n${pointHead}\nChecked-out branches: ${listed}` };
568
+ const matches = checkouts.filter((entry) => entry.branch === bareHead);
569
+ const [only] = matches;
570
+ if (only && matches.length === 1) return { path: only.path };
571
+ if (!only) {
572
+ const stale = entries.find((entry) => entry.prunable && entry.branch === bareHead);
573
+ const beside = checkouts[0] ? dirname$1(checkouts[0].path) : bare;
574
+ return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, but no worktree has ${bareHead} checked out, so Stim has no source checkout to copy from. ${stale ? `Its worktree at ${stale.path} is missing. Recreate it, then retry:\n git -C ${bare} worktree prune\n git -C ${bare} worktree add ${stale.path} ${bareHead}\n` : `Create one, then retry:\n git -C ${bare} worktree add ${join$1(beside, bareHead)} ${bareHead}\n`}Or point HEAD at a branch that is checked out (${listed}):\n${pointHead}` };
575
+ }
576
+ return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, which is checked out in more than one worktree: ${matches.map((entry) => entry.path).join(", ")}. Remove the extra worktrees, or point HEAD at a branch checked out once, then retry:\n${pointHead}` };
577
+ }
578
+ function bareHeadBranch(bare) {
579
+ const out = getExecutor().runFileQuiet("git", [
580
+ "-C",
581
+ bare,
582
+ "symbolic-ref",
583
+ "--quiet",
584
+ "HEAD"
585
+ ])?.trim();
586
+ return out?.startsWith("refs/heads/") ? out.slice(11) : null;
587
+ }
588
+ function sourceCheckoutOf(entries) {
589
+ const first = entries[0];
590
+ return selectSourceCheckout(entries, first?.bare ? bareHeadBranch(first.path) : null);
591
+ }
592
+ function resolveSourceCheckout(cwd) {
593
+ const entries = listWorktrees(cwd);
594
+ return {
595
+ entries,
596
+ ...sourceCheckoutOf(entries)
597
+ };
598
+ }
542
599
  //#endregion
543
- export { removeTemporaryEntry as C, makeTemporaryDirectory as S, resolveFullRef as _, dirtyFingerprintFiles as a, warmWorktreePaths as b, hasRemote as c, isPodInstallChurn as d, listWorktrees as f, repoRoot as g, removeWorktree as h, depsOutOfSync as i, hasUncommittedWork as l, readWorktreeExclude as m, cloneIgnoredEntries as n, dirtyPaths as o, podsOutOfSync as p, deleteBranch as r, gitCommonDir as s, branchExists as t, isMainWorkingTree as u, restoreFile as v, temporaryRoot as w, filesystemDevice as x, unpushedCommits as y };
600
+ export { filesystemDevice as C, temporaryRoot as E, warmWorktreePaths as S, removeTemporaryEntry as T, resolveFullRef as _, dirtyFingerprintFiles as a, sourceCheckoutOf as b, hasRemote as c, listWorktrees as d, locallyKnownUpstream as f, repoRoot as g, removeWorktree as h, depsOutOfSync as i, hasUncommittedWork as l, readWorktreeExclude as m, cloneIgnoredEntries as n, dirtyPaths as o, podsOutOfSync as p, deleteBranch as r, gitCommonDir as s, branchExists as t, isPodInstallChurn as u, resolveSourceCheckout as v, makeTemporaryDirectory as w, unpushedCommits as x, restoreFile as y };