stim 1.0.0 → 1.2.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 (66) hide show
  1. package/README.md +29 -10
  2. package/dist/android-BW8i_YxU.mjs +204 -0
  3. package/dist/android-Bcmev2s2.mjs +2334 -0
  4. package/dist/android-K63_0UfF.mjs +727 -0
  5. package/dist/android-cas-D6HPZ6UQ.mjs +1078 -0
  6. package/dist/android-cas-compiler.mjs +2 -2
  7. package/dist/app-install-NFxUahnH.mjs +1149 -0
  8. package/dist/build-lock-BsVoLDaq.mjs +645 -0
  9. package/dist/build-slots-D0b6sJyo.mjs +152 -0
  10. package/dist/{cache-manifest-4tH64LQ9.mjs → cache-manifest-Cu4_Znw9.mjs} +2 -2
  11. package/dist/cache-manifest.mjs +1 -1
  12. package/dist/cli.mjs +24 -27166
  13. package/dist/collector-run.d.mts +1 -0
  14. package/dist/collector-run.mjs +7 -5
  15. package/dist/command-output-8wHMEde0.mjs +289 -0
  16. package/dist/{config-7kmtuhO2.mjs → config-CMtIaTk9.mjs} +44 -13
  17. package/dist/deps-BLBsTNDS.mjs +435 -0
  18. package/dist/dev-client-RpvXxCXV.mjs +620 -0
  19. package/dist/device-CoCPmazS.mjs +250 -0
  20. package/dist/device-lease-Dpik41RY.mjs +335 -0
  21. package/dist/device-pool-B5pEVa3t.mjs +408 -0
  22. package/dist/device-remote-E81KAdXf.mjs +1209 -0
  23. package/dist/doctor-CqPeMsSm.mjs +1130 -0
  24. package/dist/doctor-D_CX7ib5.mjs +530 -0
  25. package/dist/error-diagnostics-Dw8Gn3yw.mjs +636 -0
  26. package/dist/{exec-bsN9MJXb.mjs → exec-CyylIdq9.mjs} +4 -2
  27. package/dist/gc-B7KkDinF.mjs +1665 -0
  28. package/dist/guide-BS2Xc24o.mjs +4503 -0
  29. package/dist/ios-B7KLgIp5.mjs +2733 -0
  30. package/dist/ios-D1h1Di39.mjs +451 -0
  31. package/dist/ios-device-D3pzTKQE.mjs +81 -0
  32. package/dist/ios-device-xNt0Lg6v.mjs +413 -0
  33. package/dist/logs-DcX9AvRF.mjs +176 -0
  34. package/dist/logs-query-oqn47Ffx.mjs +273 -0
  35. package/dist/metro-COslRK_F.mjs +184 -0
  36. package/dist/metro-store-CDDZHtPl.mjs +92 -0
  37. package/dist/ownership-CGPart6T.mjs +60 -0
  38. package/dist/ownership-CYOyqxUo.mjs +475 -0
  39. package/dist/ownership-claim-7zRxxTfZ.mjs +569 -0
  40. package/dist/{project-Dit1CckW.mjs → project-BBTVABUz.mjs} +12 -3
  41. package/dist/reclaim-C5Jmuahx.mjs +348 -0
  42. package/dist/reload-Z8nKifAk.mjs +332 -0
  43. package/dist/remote-cache-DDmJ1Roq.mjs +994 -0
  44. package/dist/{server-bare-BmTVVmBB.mjs → server-bare-DEk37Mz2.mjs} +8 -2
  45. package/dist/{server-expo-CHJe1r6j.mjs → server-expo-C3ktAwUG.mjs} +6 -5
  46. package/dist/server-expo-Drd7ksHu.mjs +2 -0
  47. package/dist/settings-DwU1_U_q.mjs +708 -0
  48. package/dist/spawn-entry-nCE6m770.mjs +14 -0
  49. package/dist/start-BrY4EuIg.mjs +822 -0
  50. package/dist/{state-2nush3MK.mjs → state-CZ-ER-8d.mjs} +21 -22
  51. package/dist/stats-BCWEVLrM.mjs +307 -0
  52. package/dist/stats-BsF15Bti.mjs +62 -0
  53. package/dist/status-DFI1Hfpg.mjs +393 -0
  54. package/dist/stop-B7DR4awK.mjs +3 -0
  55. package/dist/stop-Btg770vV.mjs +591 -0
  56. package/dist/supervisor-run.d.mts +0 -1
  57. package/dist/supervisor-run.mjs +4 -4
  58. package/dist/workspace-process-lock-BKfdys1q.mjs +47 -0
  59. package/dist/worktree-BPTbI-LN.mjs +1322 -0
  60. package/dist/worktree-C79xx_kM.mjs +543 -0
  61. package/dist/xcode-DeKCribl.mjs +1832 -0
  62. package/package.json +5 -5
  63. package/shim/bundle-response.cjs +5 -0
  64. package/dist/android-jc8MnUyL.mjs +0 -514
  65. package/dist/metro-store-COLl1pOk.mjs +0 -1249
  66. package/dist/server-expo-BZJm--wA.mjs +0 -2
@@ -0,0 +1,543 @@
1
+ import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
+ import { g as loadConfig, u as getConfigDir } from "./config-CMtIaTk9.mjs";
3
+ import { accessSync, chmodSync, constants, lstatSync, mkdirSync, mkdtempSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
4
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
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";
6
+ 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";
7
+ import { tmpdir } from "node:os";
8
+ //#region src/temporary.ts
9
+ function existingPath(path) {
10
+ const missing = [];
11
+ let current = resolve(path);
12
+ while (!lstatSync(current, { throwIfNoEntry: false })) {
13
+ missing.unshift(basename(current));
14
+ current = dirname(current);
15
+ }
16
+ const existing = realpathSync(current);
17
+ return {
18
+ existing,
19
+ resolved: join(existing, ...missing)
20
+ };
21
+ }
22
+ function filesystemDevice(path) {
23
+ return statSync(existingPath(path).existing).dev;
24
+ }
25
+ function outsideWorkingTree(path) {
26
+ let current = path;
27
+ while (true) {
28
+ if (lstatSync(join(current, ".git"), { throwIfNoEntry: false })) return false;
29
+ const parent = dirname(current);
30
+ if (parent === current) return true;
31
+ current = parent;
32
+ }
33
+ }
34
+ function temporaryRoot(near) {
35
+ const reference = existingPath(near);
36
+ const insideReference = (path) => {
37
+ const rel = relative(reference.resolved, path);
38
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
39
+ };
40
+ const override = process.env.STIM_TMPDIR ?? loadConfig()?.tempDir;
41
+ if (override !== void 0) {
42
+ if (typeof override !== "string" || !isAbsolute(override)) throw new Error("STIM_TMPDIR / tempDir must be an absolute directory path.");
43
+ const { existing, resolved } = existingPath(override);
44
+ if (!statSync(existing).isDirectory() || !outsideWorkingTree(existing)) throw new Error(`Temporary directory ${override} must be outside Git working trees.`);
45
+ if (insideReference(resolved)) throw new Error(`Temporary directory ${override} must not be inside ${near}.`);
46
+ accessSync(existing, constants.W_OK);
47
+ return resolved;
48
+ }
49
+ const { existing } = reference;
50
+ const device = statSync(existing).dev;
51
+ const candidates = [tmpdir()];
52
+ let current = existing === reference.resolved || !statSync(existing).isDirectory() ? dirname(existing) : existing;
53
+ while (statSync(current).dev === device) {
54
+ candidates.push(current);
55
+ const parent = dirname(current);
56
+ if (parent === current) break;
57
+ current = parent;
58
+ }
59
+ for (const candidate of candidates) try {
60
+ const path = realpathSync(candidate);
61
+ if (statSync(path).dev !== device || insideReference(path) || !outsideWorkingTree(path)) continue;
62
+ accessSync(path, constants.W_OK);
63
+ return path;
64
+ } catch {}
65
+ throw new Error(`No writable temporary directory on the filesystem containing ${near}. Set STIM_TMPDIR or tempDir in ${join(getConfigDir(), "config.json")} to a directory outside Git working trees.`);
66
+ }
67
+ function makeTemporaryDirectory(near, prefix) {
68
+ const root = temporaryRoot(near);
69
+ mkdirSync(root, {
70
+ recursive: true,
71
+ mode: 448
72
+ });
73
+ return mkdtempSync(join(root, prefix));
74
+ }
75
+ function removeTemporaryEntry(path) {
76
+ const stat = lstatSync(path, { throwIfNoEntry: false });
77
+ if (stat?.isDirectory()) {
78
+ chmodSync(path, stat.mode | 448);
79
+ for (const entry of readdirSync(path, { withFileTypes: true })) if (entry.isDirectory()) removeTemporaryEntry(join(path, entry.name));
80
+ }
81
+ rmSync(path, {
82
+ recursive: true,
83
+ force: true
84
+ });
85
+ }
86
+ //#endregion
87
+ //#region src/worktree.ts
88
+ const CARRY_SKIP_BASENAMES = /* @__PURE__ */ new Set([
89
+ ".DerivedData",
90
+ ".DS_Store",
91
+ ".idea"
92
+ ]);
93
+ function isCarrySkipped(rel) {
94
+ return /(^|\/)android\/build\/generated\/autolinking(\/|$)/.test(rel) || String(rel).split("/").some((seg) => CARRY_SKIP_BASENAMES.has(seg));
95
+ }
96
+ function gitCommonDir(cwd) {
97
+ const out = getExecutor().runFileQuiet("git", [
98
+ "-C",
99
+ cwd,
100
+ "rev-parse",
101
+ "--path-format=absolute",
102
+ "--git-common-dir"
103
+ ]);
104
+ return out ? out.trim() : null;
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
+ function repoRoot(cwd) {
120
+ const out = getExecutor().runFileQuiet("git", [
121
+ "-C",
122
+ cwd,
123
+ "rev-parse",
124
+ "--show-toplevel"
125
+ ]);
126
+ return out ? out.trim() : null;
127
+ }
128
+ function warmWorktreePaths(cwd) {
129
+ const currentRoot = repoRoot(cwd);
130
+ if (!currentRoot) throw new Error("Not a git repository.");
131
+ 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);
138
+ const sourceRoot = repoRoot(root);
139
+ const sourceCommon = gitCommonDir(root);
140
+ 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.");
142
+ return {
143
+ root,
144
+ target,
145
+ common: realpathSync$1(targetCommon)
146
+ };
147
+ }
148
+ function matchesInclude(path, patterns) {
149
+ for (const pattern of patterns || []) {
150
+ const rooted = pattern.startsWith("/");
151
+ const escaped = (rooted ? pattern.slice(1) : pattern).replace(/[.+^${}()|[\]\\?]/g, "\\$&").replace(/\*\*\//g, "::GLOBSTAR::").replace(/\*/g, "[^/]+").replace(/::GLOBSTAR::/g, "(?:.*/)?").replace(/\\\?/g, "[^/]");
152
+ if (new RegExp(`${rooted ? "^" : "(^|/)"}${escaped}$`).test(path)) return true;
153
+ }
154
+ return false;
155
+ }
156
+ function readWorktreeExclude(root) {
157
+ return readPatternFile(join$1(root, ".worktreeexclude"));
158
+ }
159
+ function readPatternFile(p) {
160
+ if (!existsSync$1(p)) return null;
161
+ return readFileSync$1(p, "utf-8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
162
+ }
163
+ function canonicalPath(path) {
164
+ try {
165
+ return realpathSync$1(path);
166
+ } catch {
167
+ return resolve$1(path);
168
+ }
169
+ }
170
+ function nestedWorktreePaths(root) {
171
+ const source = canonicalPath(root);
172
+ const paths = /* @__PURE__ */ new Set();
173
+ const out = getExecutor().runFileQuiet("git", [
174
+ "-C",
175
+ root,
176
+ "worktree",
177
+ "list",
178
+ "--porcelain"
179
+ ]);
180
+ if (out === null) throw new Error("Could not list Git worktrees. Refusing to carry ignored files.");
181
+ for (const entry of parseWorktrees(out)) {
182
+ const rel = relative$1(source, canonicalPath(entry.path));
183
+ if (!rel || rel === ".." || rel.startsWith(`..${sep$1}`) || isAbsolute$1(rel)) continue;
184
+ paths.add(rel.split(sep$1).join("/"));
185
+ }
186
+ return [...paths];
187
+ }
188
+ function overlapsNestedWorktree(rel, nestedPaths) {
189
+ const path = String(rel).replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/$/, "");
190
+ return nestedPaths.some((nested) => path === nested || path.startsWith(`${nested}/`) || nested.startsWith(`${path}/`));
191
+ }
192
+ function listTrackedPaths(dir) {
193
+ const out = getExecutor().runFileQuiet("git", [
194
+ "-C",
195
+ dir,
196
+ "ls-files",
197
+ "-z"
198
+ ]);
199
+ if (out === null) return null;
200
+ return out.split("\0").filter(Boolean);
201
+ }
202
+ function trackedGuard(dir) {
203
+ const paths = listTrackedPaths(dir);
204
+ if (paths === null) return {
205
+ known: false,
206
+ covers: () => false
207
+ };
208
+ const entries = new Set(paths);
209
+ const covered = new Set(paths);
210
+ for (const p of paths) for (let i = p.indexOf("/"); i !== -1; i = p.indexOf("/", i + 1)) covered.add(p.slice(0, i));
211
+ return {
212
+ known: true,
213
+ covers: (rel) => {
214
+ if (covered.has(rel)) return true;
215
+ for (let i = rel.indexOf("/"); i !== -1; i = rel.indexOf("/", i + 1)) if (entries.has(rel.slice(0, i))) return true;
216
+ return false;
217
+ }
218
+ };
219
+ }
220
+ function listGitignoredEntries(root) {
221
+ const args = [
222
+ "-C",
223
+ root,
224
+ "ls-files",
225
+ "--others",
226
+ "--ignored",
227
+ "--exclude-standard",
228
+ "--directory",
229
+ "--no-empty-directory",
230
+ "-z"
231
+ ];
232
+ const out = getExecutor().runFile("git", args);
233
+ if (!out) return [];
234
+ return out.split("\0").filter(Boolean).map((e) => e.endsWith("/") ? e.slice(0, -1) : e);
235
+ }
236
+ function listCarryableIgnoredEntries(root, patterns) {
237
+ const nested = nestedWorktreePaths(root);
238
+ return listGitignoredEntries(root).filter((rel) => !isCarrySkipped(rel) && !overlapsNestedWorktree(rel, nested) && !matchesInclude(rel, patterns));
239
+ }
240
+ function missingDestinationReason(target, rel) {
241
+ const parts = rel.split("/");
242
+ for (let i = 1; i < parts.length; i++) {
243
+ const parent = parts.slice(0, i).join("/");
244
+ const stat = lstatSync$1(join$1(target, parent), { throwIfNoEntry: false });
245
+ if (stat?.isSymbolicLink()) return `symlink ancestor: ${parent}`;
246
+ if (stat && !stat.isDirectory()) return `non-directory ancestor: ${parent}`;
247
+ }
248
+ return lstatSync$1(join$1(target, rel), { throwIfNoEntry: false }) ? "exists" : null;
249
+ }
250
+ function removeCopiedExclusions(path, rel) {
251
+ const entries = readdirSync$1(path, { withFileTypes: true });
252
+ const excluded = entries.filter((entry) => isCarrySkipped(`${rel}/${entry.name}`));
253
+ if (excluded.length > 0) {
254
+ const stat = lstatSync$1(path);
255
+ chmodSync$1(path, stat.mode | 448);
256
+ try {
257
+ for (const entry of excluded) removeTemporaryEntry(join$1(path, entry.name));
258
+ } finally {
259
+ utimesSync(path, stat.atime, stat.mtime);
260
+ chmodSync$1(path, stat.mode);
261
+ }
262
+ }
263
+ for (const entry of entries) {
264
+ const child = `${rel}/${entry.name}`;
265
+ if (entry.isDirectory() && !isCarrySkipped(child)) removeCopiedExclusions(join$1(path, entry.name), child);
266
+ }
267
+ }
268
+ function cloneIgnoredEntries({ root, target, patterns }) {
269
+ const copied = [];
270
+ const skipped = [];
271
+ const failed = [];
272
+ let cloned = true;
273
+ const guard = trackedGuard(target);
274
+ if (!guard.known) throw new Error("Could not list the destination's tracked files.");
275
+ const nested = nestedWorktreePaths(target);
276
+ for (const rel of listCarryableIgnoredEntries(root, patterns)) {
277
+ const from = join$1(root, rel);
278
+ const to = join$1(target, rel);
279
+ try {
280
+ const reason = guard.covers(rel) ? "tracked" : overlapsNestedWorktree(rel, nested) ? "nested worktree" : missingDestinationReason(target, rel);
281
+ if (reason) {
282
+ skipped.push({
283
+ file: rel,
284
+ reason
285
+ });
286
+ continue;
287
+ }
288
+ mkdirSync$1(dirname$1(to), { recursive: true });
289
+ try {
290
+ getExecutor().runFile("cp", [
291
+ "-Rc",
292
+ from,
293
+ to
294
+ ]);
295
+ } catch {
296
+ removeTemporaryEntry(to);
297
+ getExecutor().runFile("cp", [
298
+ "-R",
299
+ from,
300
+ to
301
+ ]);
302
+ cloned = false;
303
+ }
304
+ if (lstatSync$1(to).isDirectory()) removeCopiedExclusions(to, rel);
305
+ copied.push(rel);
306
+ } catch (e) {
307
+ failed.push({
308
+ file: rel,
309
+ error: String(e?.message || e)
310
+ });
311
+ }
312
+ }
313
+ return {
314
+ copied,
315
+ skipped,
316
+ failed,
317
+ cloned
318
+ };
319
+ }
320
+ function podsOutOfSync(target, copiedEntries, { read = readFileSync$1 } = {}) {
321
+ const problems = [];
322
+ for (const rel of copiedEntries || []) {
323
+ if (rel !== "Pods" && !rel.endsWith("/Pods")) continue;
324
+ const iosDir = rel === "Pods" ? "" : rel.slice(0, -5);
325
+ const manifest = join$1(target, rel, "Manifest.lock");
326
+ const podfileLock = join$1(target, iosDir, "Podfile.lock");
327
+ if (!existsSync$1(manifest)) continue;
328
+ if (!existsSync$1(podfileLock)) {
329
+ problems.push({
330
+ dir: iosDir || ".",
331
+ reason: "missing"
332
+ });
333
+ continue;
334
+ }
335
+ try {
336
+ if (read(manifest, "utf-8") !== read(podfileLock, "utf-8")) problems.push({
337
+ dir: iosDir || ".",
338
+ reason: "mismatch"
339
+ });
340
+ } catch {}
341
+ }
342
+ return problems;
343
+ }
344
+ const LOCKFILE_NAMES = [
345
+ "package-lock.json",
346
+ "yarn.lock",
347
+ "pnpm-lock.yaml",
348
+ "bun.lock",
349
+ "bun.lockb"
350
+ ];
351
+ function depsOutOfSync(root, target, copiedEntries, { read = readFileSync$1 } = {}) {
352
+ const problems = [];
353
+ for (const rel of copiedEntries || []) {
354
+ if (rel !== "node_modules" && !rel.endsWith("/node_modules")) continue;
355
+ const dir = rel === "node_modules" ? "" : rel.slice(0, -13);
356
+ for (const name of LOCKFILE_NAMES) {
357
+ const source = join$1(root, dir, name);
358
+ const branch = join$1(target, dir, name);
359
+ if (!existsSync$1(source) || !existsSync$1(branch)) continue;
360
+ try {
361
+ if (read(source, "utf-8") !== read(branch, "utf-8")) problems.push({
362
+ dir: dir || ".",
363
+ lockfile: name
364
+ });
365
+ } catch {}
366
+ break;
367
+ }
368
+ }
369
+ return problems;
370
+ }
371
+ const FINGERPRINT_INPUT_FILES = [
372
+ "app.json",
373
+ "app.config.ts",
374
+ "app.config.js",
375
+ "app.config.mjs",
376
+ "package.json"
377
+ ];
378
+ function dirtyFingerprintFiles(root) {
379
+ const out = getExecutor().runFileQuiet("git", [
380
+ "-C",
381
+ root,
382
+ "status",
383
+ "--porcelain",
384
+ "--",
385
+ ...FINGERPRINT_INPUT_FILES
386
+ ]);
387
+ if (out === null || out.trim() === "") return [];
388
+ return out.split("\n").map((line) => normalizePorcelainLine(line.trimEnd())).filter((line) => line !== "").map((line) => line.slice(3).trim()).filter((path) => path !== "");
389
+ }
390
+ function hasUncommittedWork(dir) {
391
+ const out = getExecutor().runFileQuiet("git", [
392
+ "-C",
393
+ dir,
394
+ "status",
395
+ "--porcelain"
396
+ ]);
397
+ if (out === null) return null;
398
+ return out.trim().length > 0;
399
+ }
400
+ function dirtyPaths(dir, { limit = 10 } = {}) {
401
+ const out = getExecutor().runFileQuiet("git", [
402
+ "-C",
403
+ dir,
404
+ "status",
405
+ "--porcelain"
406
+ ]);
407
+ if (out === null) return [];
408
+ return out.split("\n").map((l) => normalizePorcelainLine(l.trimEnd())).filter(Boolean).slice(0, limit);
409
+ }
410
+ function normalizePorcelainLine(line) {
411
+ if (line === "" || line[2] === " ") return line;
412
+ return ` ${line}`;
413
+ }
414
+ function restoreFile(dir, file) {
415
+ return getExecutor().runFileQuiet("git", [
416
+ "-C",
417
+ dir,
418
+ "checkout",
419
+ "--",
420
+ file
421
+ ]) !== null;
422
+ }
423
+ function isPodInstallChurn(paths) {
424
+ if (!paths || paths.length === 0) return false;
425
+ return paths.every((line) => /(?:^|\/)(?:Podfile\.lock|project\.pbxproj)$/.test(line.slice(3).trim()));
426
+ }
427
+ const SAFE_BRANCH_NAME = /^[A-Za-z0-9._/-]+$/;
428
+ function unpushedCommits(dir) {
429
+ const exec = getExecutor();
430
+ const branch = exec.runFileQuiet("git", [
431
+ "-C",
432
+ dir,
433
+ "symbolic-ref",
434
+ "--quiet",
435
+ "--short",
436
+ "HEAD"
437
+ ]);
438
+ const own = branch === null ? "" : branch.trim();
439
+ const protection = own && SAFE_BRANCH_NAME.test(own) ? [
440
+ "--remotes",
441
+ `--exclude=${own}`,
442
+ "--branches"
443
+ ] : ["--remotes"];
444
+ if (!own && exec.runFileQuiet("git", [
445
+ "-C",
446
+ dir,
447
+ "rev-parse",
448
+ "--abbrev-ref",
449
+ "HEAD"
450
+ ]) === "HEAD") protection.push("--branches");
451
+ const out = exec.runFileQuiet("git", [
452
+ "-C",
453
+ dir,
454
+ "log",
455
+ "--oneline",
456
+ "HEAD",
457
+ "--not",
458
+ ...protection
459
+ ]);
460
+ if (out === null) return null;
461
+ return out.split("\n").map((l) => l.trim()).filter(Boolean);
462
+ }
463
+ function hasRemote(dir) {
464
+ const out = getExecutor().runFileQuiet("git", [
465
+ "-C",
466
+ dir,
467
+ "remote"
468
+ ]);
469
+ return Boolean(out && out.trim().length > 0);
470
+ }
471
+ function branchExists(cwd, branch) {
472
+ const out = getExecutor().runFileQuiet("git", [
473
+ "-C",
474
+ cwd,
475
+ "rev-parse",
476
+ "--verify",
477
+ "--quiet",
478
+ `refs/heads/${branch}`
479
+ ]);
480
+ return Boolean(out);
481
+ }
482
+ function resolveFullRef(cwd, ref) {
483
+ try {
484
+ const out = getExecutor().runFile("git", [
485
+ "-C",
486
+ cwd,
487
+ "rev-parse",
488
+ "--verify",
489
+ "--quiet",
490
+ "--end-of-options",
491
+ `${ref}^{commit}`
492
+ ]);
493
+ return out && out.trim() ? out.trim() : null;
494
+ } catch {
495
+ return null;
496
+ }
497
+ }
498
+ function removeWorktree(path, { force = false } = {}) {
499
+ const args = [
500
+ "-C",
501
+ path,
502
+ "worktree",
503
+ "remove",
504
+ ...force ? ["--force"] : [],
505
+ "--",
506
+ path
507
+ ];
508
+ getExecutor().runFile("git", args);
509
+ }
510
+ function deleteBranch(cwd, branch, expectedSha) {
511
+ if (!SAFE_BRANCH_NAME.test(branch) || branch.startsWith("-")) throw new Error(`Refusing branch ${JSON.stringify(branch)}: it is not a safe local branch name.`);
512
+ getExecutor().runFile("git", [
513
+ "-C",
514
+ cwd,
515
+ "update-ref",
516
+ "-d",
517
+ `refs/heads/${branch}`,
518
+ expectedSha
519
+ ]);
520
+ }
521
+ function parseWorktrees(out) {
522
+ const entries = [];
523
+ let current = {};
524
+ for (const line of out.split("\n")) if (line.startsWith("worktree ")) {
525
+ if (current.path) entries.push(current);
526
+ current = { path: line.slice(9) };
527
+ } else if (line.startsWith("branch ")) current.branch = line.slice(7).replace("refs/heads/", "");
528
+ else if (line === "prunable" || line.startsWith("prunable ")) current.prunable = true;
529
+ if (current.path) entries.push(current);
530
+ return entries;
531
+ }
532
+ function listWorktrees(cwd) {
533
+ const out = getExecutor().runFileQuiet("git", [
534
+ "-C",
535
+ cwd,
536
+ "worktree",
537
+ "list",
538
+ "--porcelain"
539
+ ]);
540
+ return out ? parseWorktrees(out) : [];
541
+ }
542
+ //#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 };