opencode-swarm 7.200.0 → 7.200.1

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 (100) hide show
  1. package/binaries/win32-arm64/swarm-sandbox-runner.exe +0 -0
  2. package/binaries/win32-x64/swarm-sandbox-runner.exe +0 -0
  3. package/dist/cli/{attest-task-8ekwny8v.js → attest-task-sg8b5a5j.js} +42 -42
  4. package/dist/cli/{auto-checkpoint-dqjk82s7.js → auto-checkpoint-bywgxh27.js} +7 -7
  5. package/dist/cli/{checkpoint-2ahps2sp.js → checkpoint-k2092cbw.js} +17 -15
  6. package/dist/cli/{coder-settlement-1dk37pa9.js → coder-settlement-y0hkcngn.js} +10 -10
  7. package/dist/cli/{config-doctor-73rctg85.js → config-doctor-0fnrwkga.js} +13 -13
  8. package/dist/cli/{core-y5mfws8v.js → core-mfyaj8b4.js} +3 -3
  9. package/dist/cli/{curation-policy-43gq6t0y.js → curation-policy-fth5n93x.js} +4 -4
  10. package/dist/cli/{curator-m278hyrq.js → curator-1s64jkc3.js} +41 -41
  11. package/dist/cli/{curator-drift-k7j9rcgp.js → curator-drift-famx6p6s.js} +8 -8
  12. package/dist/cli/curator-llm-factory-vya4epq6.js +82 -0
  13. package/dist/cli/{doctor-knowledge-receipt-phase-id-1m7pkzvt.js → doctor-knowledge-receipt-phase-id-v4y6pbek.js} +7 -7
  14. package/dist/cli/{evidence-summary-service-19k8x1g9.js → evidence-summary-service-rd7y6yjq.js} +17 -17
  15. package/dist/cli/{gate-evidence-hche0hf8.js → gate-evidence-kama66h5.js} +5 -5
  16. package/dist/cli/guardrail-explain-8fkhq0cg.js +83 -0
  17. package/dist/cli/{guardrail-log-tvbgnxns.js → guardrail-log-gdj19fq5.js} +6 -6
  18. package/dist/cli/{guardrail-reset-v8kvh9cn.js → guardrail-reset-bmg6czzk.js} +41 -41
  19. package/dist/cli/{hive-promoter-bqfa763z.js → hive-promoter-ychygdgc.js} +41 -41
  20. package/dist/cli/{index-1rfgzsaw.js → index-0ptf66x5.js} +4 -4
  21. package/dist/cli/{index-rfvnq6wd.js → index-0tygsk8b.js} +8 -8
  22. package/dist/cli/{index-qnst0jwc.js → index-12h2rtdj.js} +3 -3
  23. package/dist/cli/{index-bp476gwc.js → index-1v277wxc.js} +2 -2
  24. package/dist/cli/{index-511hw14d.js → index-2dm42tgp.js} +43 -43
  25. package/dist/cli/{index-hg4qa0aq.js → index-2hnnkj64.js} +1892 -2354
  26. package/dist/cli/{index-pyv8vzkx.js → index-2m740hyt.js} +5 -5
  27. package/dist/cli/{index-wfb6vrc2.js → index-2w5grmmj.js} +2 -2
  28. package/dist/cli/{index-gwc7jahh.js → index-3020n6d9.js} +1 -1
  29. package/dist/cli/{index-m9j9varx.js → index-3exe6k66.js} +3 -3
  30. package/dist/cli/{index-sk1rva12.js → index-3zspah3v.js} +2 -2
  31. package/dist/cli/{index-hxp5jgd8.js → index-51dw4bjc.js} +2 -2
  32. package/dist/cli/{index-0wg5q595.js → index-5snwc3dz.js} +1 -1
  33. package/dist/cli/{index-kwfhr3mz.js → index-b0d8ra32.js} +1 -1
  34. package/dist/cli/{index-r02aqt0a.js → index-b4kdms7x.js} +2 -2
  35. package/dist/cli/{index-n5k81wym.js → index-bn3aa5q1.js} +7 -7
  36. package/dist/cli/{index-md2180vx.js → index-c9g3hkjz.js} +3 -3
  37. package/dist/cli/{index-qnabg1wp.js → index-dgyddtje.js} +6 -6
  38. package/dist/cli/{index-q1x73nnd.js → index-epzadpmm.js} +1 -1
  39. package/dist/cli/{index-jbm301bq.js → index-f6nrw8am.js} +41 -41
  40. package/dist/cli/{index-dng8z8me.js → index-g57p8p1k.js} +1 -1
  41. package/dist/cli/{index-vmevv6my.js → index-na6ph1fp.js} +1 -1
  42. package/dist/cli/{index-tv9c3b3q.js → index-nawp90n7.js} +3 -3
  43. package/dist/cli/{index-wr0n2w3z.js → index-nzfq1ms2.js} +1 -1
  44. package/dist/cli/{index-yvv0my1a.js → index-p6v544jn.js} +1 -1
  45. package/dist/cli/{index-x25rhkmk.js → index-parkaa70.js} +4 -4
  46. package/dist/cli/{index-yss701b3.js → index-pe77h482.js} +4 -4
  47. package/dist/cli/{index-71bqn1nn.js → index-qaqyq25k.js} +7 -7
  48. package/dist/cli/{index-8rrs1nc0.js → index-qzakbjcg.js} +2 -2
  49. package/dist/cli/{index-22atsy06.js → index-rzkmg02a.js} +918 -154
  50. package/dist/cli/{index-nq3zcrhm.js → index-s3w41p1x.js} +1 -1
  51. package/dist/cli/{index-7v74n9a1.js → index-s8eac99p.js} +3 -3
  52. package/dist/cli/{index-99zt998b.js → index-s9y49avf.js} +1 -1
  53. package/dist/cli/{index-58cr1xg0.js → index-sva5swrs.js} +3 -3
  54. package/dist/cli/{index-p5m71yph.js → index-sz67z90q.js} +4 -4
  55. package/dist/cli/{index-e8m8bb1r.js → index-th667k1y.js} +4 -4
  56. package/dist/cli/{index-ac8vdmgv.js → index-traz987v.js} +1 -1
  57. package/dist/cli/{index-jy0mgzjq.js → index-v1h70r3y.js} +1 -1
  58. package/dist/cli/{index-d083d1q7.js → index-ves1rtqt.js} +1 -1
  59. package/dist/cli/{index-9nmrw7ek.js → index-vtkgg2qn.js} +3 -3
  60. package/dist/cli/{index-35tf03js.js → index-w2bhrp6z.js} +10 -10
  61. package/dist/cli/{index-8a3aasag.js → index-wgf1780e.js} +5 -5
  62. package/dist/cli/{index-x1z5d5kj.js → index-xyvsdn07.js} +1 -1
  63. package/dist/cli/{index-1tpz99db.js → index-ypxq0x7r.js} +1 -1
  64. package/dist/cli/{index-vqtca3xv.js → index-z9mysg5m.js} +2 -2
  65. package/dist/cli/index.js +42 -42
  66. package/dist/cli/{knowledge-escalator-qcra58bx.js → knowledge-escalator-zxd76jna.js} +12 -12
  67. package/dist/cli/{knowledge-events-9mnx1a3f.js → knowledge-events-z8h0wmaw.js} +10 -10
  68. package/dist/cli/{knowledge-link-2jsn9f6a.js → knowledge-link-wfhsfyn2.js} +3 -3
  69. package/dist/cli/{knowledge-store-fdneqf7z.js → knowledge-store-g2gm13s8.js} +4 -4
  70. package/dist/cli/{knowledge-validator-7nj987ga.js → knowledge-validator-2sech2sx.js} +6 -6
  71. package/dist/cli/{mcp-0g34wmz4.js → mcp-t88363gj.js} +1 -1
  72. package/dist/cli/{model-preflight-g11ykgc0.js → model-preflight-akj8g1m2.js} +41 -41
  73. package/dist/cli/{pending-delegations-5vme18wx.js → pending-delegations-bqp6b6as.js} +11 -11
  74. package/dist/cli/{pr-subscriptions-cbx5p838.js → pr-subscriptions-wxjvkg56.js} +10 -10
  75. package/dist/cli/{pr-workflow-gate-376gw6yj.js → pr-workflow-gate-2wcrfa6g.js} +41 -41
  76. package/dist/cli/{runner-vvd26z2z.js → runner-s1a73y5h.js} +4 -4
  77. package/dist/cli/{scan-cursor-x3j5wvfm.js → scan-cursor-8br4x41t.js} +5 -5
  78. package/dist/cli/{schema-d98zzrpn.js → schema-mc419qh8.js} +1 -1
  79. package/dist/cli/{scope-persistence-vkb8z4hc.js → scope-persistence-5gyjt70q.js} +13 -13
  80. package/dist/cli/{server-25svy6cb.js → server-bp247fqe.js} +41 -41
  81. package/dist/cli/{skill-generator-ktz33dt6.js → skill-generator-q4afafh3.js} +14 -14
  82. package/dist/cli/{snapshot-coordination-init-74x3psxc.js → snapshot-coordination-init-b4kmrgh7.js} +41 -41
  83. package/dist/cli/{speckit-checkoff-9t6cdn4a.js → speckit-checkoff-1v02b2dt.js} +4 -4
  84. package/dist/cli/{workspace-snapshot-afqvh9bq.js → workspace-snapshot-93gzfac2.js} +3 -3
  85. package/dist/cli/{worktree-collision-ownership-7a1n1f9j.js → worktree-collision-ownership-84m9f5an.js} +12 -12
  86. package/dist/cli/{worktree-isolation-8snw7w34.js → worktree-isolation-xwbszhja.js} +41 -41
  87. package/dist/commands/close/internals.d.ts +1 -1
  88. package/dist/commands/close/orchestrator.d.ts +7 -0
  89. package/dist/commands/destructive-purge.d.ts +15 -0
  90. package/dist/commands/git-status-read.d.ts +23 -0
  91. package/dist/commands/registry.d.ts +1 -1
  92. package/dist/commands/rollback-gate.d.ts +77 -10
  93. package/dist/git/branch.d.ts +22 -0
  94. package/dist/index.js +26 -22
  95. package/dist/session/snapshot-coordination-init.d.ts +34 -0
  96. package/dist/session/snapshot-writer.d.ts +5 -1
  97. package/dist/tools/checkpoint.d.ts +17 -0
  98. package/package.json +1 -1
  99. package/dist/cli/curator-llm-factory-rke0964p.js +0 -82
  100. package/dist/cli/guardrail-explain-jg5png4a.js +0 -83
@@ -4,10 +4,15 @@ import {
4
4
  ensureTaskCheckpointReceipt,
5
5
  loadPlan,
6
6
  updateTaskCheckpointReceipt
7
- } from "./index-35tf03js.js";
7
+ } from "./index-w2bhrp6z.js";
8
8
  import {
9
9
  tool
10
10
  } from "./index-9nyqt3kb.js";
11
+ import {
12
+ init_executor,
13
+ isUntrustedEnvKey,
14
+ isValidEnvKey
15
+ } from "./index-8mrentmw.js";
11
16
  import {
12
17
  GitBinaryMissingError,
13
18
  GitSpawnCwdError,
@@ -17,13 +22,13 @@ import {
17
22
  } from "./index-spggmr30.js";
18
23
  import {
19
24
  validateSwarmPath
20
- } from "./index-x1z5d5kj.js";
25
+ } from "./index-xyvsdn07.js";
21
26
  import {
22
27
  readLedgerEvents
23
- } from "./index-7v74n9a1.js";
28
+ } from "./index-s8eac99p.js";
24
29
  import {
25
30
  appendCoreEventSync
26
- } from "./index-wr0n2w3z.js";
31
+ } from "./index-nzfq1ms2.js";
27
32
  import {
28
33
  derivePlanId,
29
34
  derivePlanIdentityHash
@@ -38,18 +43,22 @@ import {
38
43
  } from "./index-ejhxgyvm.js";
39
44
  import {
40
45
  atomicWriteSwarmFileSync
41
- } from "./index-q1x73nnd.js";
46
+ } from "./index-epzadpmm.js";
42
47
  import {
43
48
  loadPluginConfigWithMeta
44
- } from "./index-gwc7jahh.js";
49
+ } from "./index-3020n6d9.js";
45
50
  import {
51
+ GIT_BINARY_ENV_VAR,
52
+ describeGitResolution,
46
53
  init_git_executable,
47
54
  resolveGitExecutable,
48
55
  resolveGitExecutableAsync
49
56
  } from "./index-sp2cxpaq.js";
50
57
  import {
51
58
  bunSpawn,
52
- init_bun_compat
59
+ classifySpawnFailure,
60
+ init_bun_compat,
61
+ mergeEnvForChild
53
62
  } from "./index-qbxk2rn6.js";
54
63
  import {
55
64
  init_logger,
@@ -62,16 +71,675 @@ import {
62
71
 
63
72
  // src/tools/checkpoint.ts
64
73
  init_zod();
65
- import * as child_process from "child_process";
74
+ import * as child_process2 from "child_process";
66
75
  import * as fs4 from "fs";
67
- import * as path5 from "path";
76
+ import * as path6 from "path";
68
77
 
69
78
  // src/commands/destructive-purge.ts
70
79
  import { createHash, randomBytes } from "crypto";
71
80
  import * as fs from "fs";
72
- import * as path2 from "path";
81
+ import * as path3 from "path";
82
+ init_logger();
83
+
84
+ // src/git/branch.ts
85
+ init_executor();
86
+ init_bun_compat();
87
+ import * as child_process from "child_process";
88
+ import * as fsSync from "fs";
89
+ import path from "path";
90
+ init_git_executable();
73
91
  init_logger();
74
92
 
93
+ // src/utils/transient-retry.ts
94
+ var MAX_TRANSIENT_RETRIES = 5;
95
+ var RETRY_BASE_DELAY_MS = 200;
96
+ function isTransientSpawnError(error) {
97
+ if (!error || typeof error !== "object")
98
+ return false;
99
+ const code = error.code;
100
+ return code === "ETIMEDOUT";
101
+ }
102
+ function transientBackoff(attempt) {
103
+ const delay = RETRY_BASE_DELAY_MS * 2 ** attempt;
104
+ try {
105
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delay);
106
+ } catch {
107
+ const start = Date.now();
108
+ while (Date.now() - start < delay) {}
109
+ }
110
+ }
111
+
112
+ // src/git/safe-ref.ts
113
+ class UnsafeGitRefError extends Error {
114
+ ref;
115
+ context;
116
+ constructor(ref, context) {
117
+ super(`Refused unsafe git ref "${ref}" at ${context}: a ref argument ` + 'starting with "-" can be parsed by git as an option (issue #2265/#2476)');
118
+ this.ref = ref;
119
+ this.context = context;
120
+ this.name = "UnsafeGitRefError";
121
+ }
122
+ }
123
+ function isSafeGitRefArg(value) {
124
+ if (value.length === 0)
125
+ return false;
126
+ for (const side of value.split("..")) {
127
+ if (side.startsWith("-"))
128
+ return false;
129
+ }
130
+ return true;
131
+ }
132
+ function assertSafeGitRefArg(value, context) {
133
+ if (!isSafeGitRefArg(value)) {
134
+ throw new UnsafeGitRefError(value, context);
135
+ }
136
+ return value;
137
+ }
138
+
139
+ // src/git/branch.ts
140
+ var GIT_TIMEOUT_MS = 30000;
141
+ var GIT_MAX_BUFFER_BYTES = 5 * 1024 * 1024;
142
+ var MAX_RENDERED_GIT_CANDIDATES = 10;
143
+ function errorMessage(err) {
144
+ return err instanceof Error ? err.message : String(err);
145
+ }
146
+ function isNotGitRepositoryMessage(message) {
147
+ const lower = message.toLowerCase();
148
+ return lower.includes("not a git repository") || lower.includes("not a git repo");
149
+ }
150
+ function getChildProcessSpawnSync() {
151
+ return child_process.spawnSync;
152
+ }
153
+ var __spawnSyncSeam = {
154
+ spawnSync: (cmd, args, options) => {
155
+ const mergedEnv = mergeEnvForChild(options?.env, options?.envOverrides);
156
+ return getChildProcessSpawnSync()(cmd, args, {
157
+ ...options,
158
+ env: mergedEnv
159
+ });
160
+ }
161
+ };
162
+ function readLaneEnvFileFromDiskSync(worktreePath, laneIndex) {
163
+ const envPath = path.join(worktreePath, ".swarm", "lanes", `${laneIndex}.env`);
164
+ let content;
165
+ try {
166
+ content = fsSync.readFileSync(envPath, "utf-8");
167
+ } catch {
168
+ return {};
169
+ }
170
+ const result = {};
171
+ for (const rawLine of content.split(`
172
+ `)) {
173
+ const line = rawLine.trim();
174
+ if (!line || line.startsWith("#"))
175
+ continue;
176
+ const eqIdx = line.indexOf("=");
177
+ if (eqIdx < 0)
178
+ continue;
179
+ const k = line.slice(0, eqIdx);
180
+ const v = line.slice(eqIdx + 1);
181
+ if (!isValidEnvKey(k))
182
+ continue;
183
+ if (isUntrustedEnvKey(k))
184
+ continue;
185
+ result[k] = v;
186
+ }
187
+ return result;
188
+ }
189
+ function gitBinaryMissingMessage(cwd) {
190
+ const resolution = describeGitResolution();
191
+ const lines = [
192
+ `git executable is not available: spawning "${resolution.resolvedPath ?? "git"}" failed, and the working directory ${cwd} exists and is a directory.`
193
+ ];
194
+ if (resolution.attempts.length > 0) {
195
+ lines.push("Candidates tried:");
196
+ const shown = resolution.attempts.slice(0, MAX_RENDERED_GIT_CANDIDATES);
197
+ for (const attempt of shown) {
198
+ lines.push(` - [${attempt.source}] ${attempt.candidate}: ${attempt.accepted ? "accepted" : attempt.reason ?? "rejected"}`);
199
+ }
200
+ const omitted = resolution.attempts.length - shown.length;
201
+ if (omitted > 0)
202
+ lines.push(` ... and ${omitted} more`);
203
+ } else {
204
+ lines.push("No candidate probe results are recorded for this process (the resolver was pre-seeded or has not probed yet).");
205
+ }
206
+ lines.push(resolution.overrideValue ? `The configured override (${resolution.overrideSource ?? "unknown"}) "${resolution.overrideValue}" did not work \u2014 point the ${GIT_BINARY_ENV_VAR} environment variable, or the "git.binary" value in your USER config (a project .opencode config is ignored for this key), at a working git executable.` : `No override is configured. Set the ${GIT_BINARY_ENV_VAR} environment variable, or the "git.binary" value in your USER config (a project .opencode config is ignored for this key), to point at a working git executable.`);
207
+ return lines.join(`
208
+ `);
209
+ }
210
+ function gitExec(args, cwd, laneEnv, laneIndex) {
211
+ const resolvedLaneEnv = laneEnv ?? (laneIndex !== undefined ? readLaneEnvFileFromDiskSync(cwd, laneIndex) : undefined);
212
+ const subcommand = args[0];
213
+ const hardenedArgs = subcommand === "commit" || subcommand === "tag" ? ["-c", "commit.gpgsign=false", "-c", "tag.gpgsign=false", ...args] : args;
214
+ const command = _internals.resolveGitExecutable();
215
+ for (let attempt = 0;attempt < MAX_TRANSIENT_RETRIES; attempt++) {
216
+ const result = _internals.spawnSync(command, hardenedArgs, {
217
+ cwd,
218
+ encoding: "utf-8",
219
+ timeout: GIT_TIMEOUT_MS,
220
+ windowsHide: true,
221
+ maxBuffer: GIT_MAX_BUFFER_BYTES,
222
+ stdio: ["ignore", "pipe", "pipe"],
223
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
224
+ envOverrides: resolvedLaneEnv
225
+ });
226
+ if (!result.error && result.status === 0) {
227
+ return result.stdout;
228
+ }
229
+ if (result.error) {
230
+ if (isSpawnCwdMissing(result.error, cwd)) {
231
+ throw new GitSpawnCwdError(cwd, "missing", { cause: result.error });
232
+ }
233
+ if (isSpawnCwdUnreadable(result.error, cwd)) {
234
+ throw new GitSpawnCwdError(cwd, "unreadable", {
235
+ cause: result.error
236
+ });
237
+ }
238
+ if (isGitBinaryMissing(result.error, cwd)) {
239
+ throw new GitBinaryMissingError(gitBinaryMissingMessage(cwd), {
240
+ cause: result.error
241
+ });
242
+ }
243
+ if (isTransientSpawnError(result.error) && attempt < MAX_TRANSIENT_RETRIES - 1) {
244
+ transientBackoff(attempt);
245
+ continue;
246
+ }
247
+ throw new Error(errorMessage(result.error));
248
+ }
249
+ if (result.status !== 0) {
250
+ throw new Error(result.stderr || result.stdout || `git exited with ${result.status}`);
251
+ }
252
+ }
253
+ throw new GitBinaryMissingError(process.platform === "win32" ? "git executable is not available on PATH or common Windows install locations" : "git executable is not available on PATH");
254
+ }
255
+ function getGitRepositoryStatus(cwd) {
256
+ try {
257
+ gitExec(["rev-parse", "--git-dir"], cwd);
258
+ return { isRepo: true };
259
+ } catch (err) {
260
+ if (err instanceof GitBinaryMissingError) {
261
+ return {
262
+ isRepo: false,
263
+ reason: "git_unavailable",
264
+ message: err.message
265
+ };
266
+ }
267
+ const message = errorMessage(err);
268
+ return {
269
+ isRepo: false,
270
+ reason: isNotGitRepositoryMessage(message) ? "not_git_repo" : "git_error",
271
+ message
272
+ };
273
+ }
274
+ }
275
+ function isGitRepo(cwd) {
276
+ return getGitRepositoryStatus(cwd).isRepo;
277
+ }
278
+ function getCurrentBranch(cwd, laneEnv, laneIndex) {
279
+ const output = gitExec(["rev-parse", "--abbrev-ref", "HEAD"], cwd, laneEnv, laneIndex);
280
+ return output.trim();
281
+ }
282
+ function getDefaultBaseBranch(cwd) {
283
+ try {
284
+ gitExec(["rev-parse", "--verify", "origin/main"], cwd);
285
+ return "origin/main";
286
+ } catch {
287
+ try {
288
+ gitExec(["rev-parse", "--verify", "origin/master"], cwd);
289
+ return "origin/master";
290
+ } catch {
291
+ return "origin/main";
292
+ }
293
+ }
294
+ }
295
+ function hasUncommittedChanges(cwd, laneEnv, laneIndex) {
296
+ const status = gitExec(["status", "--porcelain"], cwd, laneEnv, laneIndex);
297
+ return status.trim().length > 0;
298
+ }
299
+ function detectDefaultRemoteBranch(cwd) {
300
+ try {
301
+ const output = gitExec(["symbolic-ref", "refs/remotes/origin/HEAD"], cwd);
302
+ const trimmed = output.trim();
303
+ if (trimmed.startsWith("refs/remotes/origin/")) {
304
+ return trimmed.slice("refs/remotes/origin/".length);
305
+ }
306
+ } catch {}
307
+ try {
308
+ const output = gitExec(["config", "init.defaultBranch"], cwd);
309
+ const branch = output.trim();
310
+ if (branch) {
311
+ return branch;
312
+ }
313
+ } catch {}
314
+ try {
315
+ gitExec(["rev-parse", "--verify", "origin/main"], cwd);
316
+ return "main";
317
+ } catch {}
318
+ try {
319
+ gitExec(["rev-parse", "--verify", "origin/master"], cwd);
320
+ return "master";
321
+ } catch {
322
+ return null;
323
+ }
324
+ }
325
+ async function resetToRemoteBranch(cwd, options) {
326
+ const warnings = [];
327
+ const prunedBranches = [];
328
+ try {
329
+ const currentBranch = getCurrentBranch(cwd);
330
+ const defaultRemoteBranch = _internals.detectDefaultRemoteBranch(cwd);
331
+ if (!defaultRemoteBranch) {
332
+ return {
333
+ success: false,
334
+ targetBranch: "",
335
+ localBranch: currentBranch,
336
+ message: "Could not detect default remote branch",
337
+ alreadyAligned: false,
338
+ prunedBranches: [],
339
+ warnings: []
340
+ };
341
+ }
342
+ assertSafeGitRefArg(currentBranch, "resetToRemoteBranch currentBranch");
343
+ assertSafeGitRefArg(defaultRemoteBranch, "resetToRemoteBranch defaultRemoteBranch");
344
+ const targetBranch = `origin/${defaultRemoteBranch}`;
345
+ if (currentBranch === "HEAD") {
346
+ return {
347
+ success: false,
348
+ targetBranch,
349
+ localBranch: "HEAD",
350
+ message: "Cannot reset: detached HEAD state",
351
+ alreadyAligned: false,
352
+ prunedBranches: [],
353
+ warnings: []
354
+ };
355
+ }
356
+ if (hasUncommittedChanges(cwd)) {
357
+ return {
358
+ success: false,
359
+ targetBranch,
360
+ localBranch: currentBranch,
361
+ message: "Cannot reset: uncommitted changes in working tree",
362
+ alreadyAligned: false,
363
+ prunedBranches: [],
364
+ warnings: []
365
+ };
366
+ }
367
+ try {
368
+ const logOutput = gitExec(["log", `${targetBranch}..HEAD`, "--oneline"], cwd);
369
+ if (logOutput.trim().length > 0) {
370
+ return {
371
+ success: false,
372
+ targetBranch,
373
+ localBranch: currentBranch,
374
+ message: "Cannot reset: unpushed commits",
375
+ alreadyAligned: false,
376
+ prunedBranches: [],
377
+ warnings: []
378
+ };
379
+ }
380
+ } catch {}
381
+ try {
382
+ gitExec(["fetch", "--prune", "origin"], cwd);
383
+ } catch (err) {
384
+ return {
385
+ success: false,
386
+ targetBranch,
387
+ localBranch: currentBranch,
388
+ message: `Fetch failed: ${err instanceof Error ? err.message : String(err)}`,
389
+ alreadyAligned: false,
390
+ prunedBranches: [],
391
+ warnings: []
392
+ };
393
+ }
394
+ const headSha = gitExec(["rev-parse", "HEAD"], cwd).trim();
395
+ const remoteSha = gitExec(["rev-parse", `${targetBranch}`], cwd).trim();
396
+ if (headSha === remoteSha) {
397
+ return {
398
+ success: true,
399
+ targetBranch,
400
+ localBranch: currentBranch,
401
+ message: "Already aligned with remote",
402
+ alreadyAligned: true,
403
+ prunedBranches: [],
404
+ warnings: []
405
+ };
406
+ }
407
+ try {
408
+ gitExec(["checkout", currentBranch], cwd);
409
+ } catch (err) {
410
+ return {
411
+ success: false,
412
+ targetBranch,
413
+ localBranch: currentBranch,
414
+ message: `Checkout failed: ${err instanceof Error ? err.message : String(err)}`,
415
+ alreadyAligned: false,
416
+ prunedBranches: [],
417
+ warnings: []
418
+ };
419
+ }
420
+ let resetSucceeded = false;
421
+ let lastError;
422
+ for (let retry = 0;retry < 4; retry++) {
423
+ if (retry > 0 && process.platform === "win32") {
424
+ await new Promise((resolve) => setTimeout(resolve, 500));
425
+ }
426
+ try {
427
+ gitExec(["reset", "--hard", targetBranch], cwd);
428
+ resetSucceeded = true;
429
+ break;
430
+ } catch (err) {
431
+ lastError = err;
432
+ }
433
+ }
434
+ if (!resetSucceeded) {
435
+ return {
436
+ success: false,
437
+ targetBranch,
438
+ localBranch: currentBranch,
439
+ message: `Reset failed: ${lastError instanceof Error ? lastError.message : String(lastError)}`,
440
+ alreadyAligned: false,
441
+ prunedBranches: [],
442
+ warnings: []
443
+ };
444
+ }
445
+ if (options?.pruneBranches) {
446
+ try {
447
+ const mergedOutput = gitExec(["branch", "--merged", targetBranch], cwd);
448
+ const mergedLines = mergedOutput.split(`
449
+ `);
450
+ for (const line of mergedLines) {
451
+ const trimmedLine = line.trim();
452
+ if (!trimmedLine || trimmedLine.startsWith("*")) {
453
+ continue;
454
+ }
455
+ try {
456
+ gitExec([
457
+ "branch",
458
+ "-d",
459
+ "--",
460
+ assertSafeGitRefArg(trimmedLine, "resetToRemoteBranch prune merged branch")
461
+ ], cwd);
462
+ prunedBranches.push(trimmedLine);
463
+ } catch {
464
+ warnings.push(`Could not safely delete branch: ${trimmedLine}`);
465
+ }
466
+ }
467
+ } catch (err) {
468
+ warnings.push(`Failed to get merged branches: ${err instanceof Error ? err.message : String(err)}`);
469
+ }
470
+ try {
471
+ const branchVvOutput = gitExec(["branch", "-vv"], cwd);
472
+ const vvLines = branchVvOutput.split(`
473
+ `);
474
+ for (const line of vvLines) {
475
+ const trimmedLine = line.trim();
476
+ if (!trimmedLine || trimmedLine.startsWith("*")) {
477
+ continue;
478
+ }
479
+ if (trimmedLine.includes(": gone]")) {
480
+ const parts = trimmedLine.split(/\s+/);
481
+ const branchName = parts[0];
482
+ try {
483
+ gitExec([
484
+ "branch",
485
+ "-d",
486
+ "--",
487
+ assertSafeGitRefArg(branchName, "resetToRemoteBranch prune gone branch")
488
+ ], cwd);
489
+ prunedBranches.push(branchName);
490
+ } catch {
491
+ warnings.push(`Could not delete gone branch: ${branchName}`);
492
+ }
493
+ }
494
+ }
495
+ } catch (err) {
496
+ warnings.push(`Failed to prune gone branches: ${err instanceof Error ? err.message : String(err)}`);
497
+ }
498
+ }
499
+ return {
500
+ success: true,
501
+ targetBranch,
502
+ localBranch: currentBranch,
503
+ message: "Successfully reset to remote branch",
504
+ alreadyAligned: false,
505
+ prunedBranches,
506
+ warnings
507
+ };
508
+ } catch (err) {
509
+ return {
510
+ success: false,
511
+ targetBranch: "",
512
+ localBranch: "",
513
+ message: `Unexpected error: ${err instanceof Error ? err.message : String(err)}`,
514
+ alreadyAligned: false,
515
+ prunedBranches: [],
516
+ warnings: []
517
+ };
518
+ }
519
+ }
520
+ var GITIGNORED_BUILD_ARTIFACTS = ["dist"];
521
+ async function resetToMainAfterMerge(cwd, options) {
522
+ const warnings = [];
523
+ try {
524
+ const defaultBranch = _internals.detectDefaultRemoteBranch(cwd);
525
+ if (!defaultBranch) {
526
+ return {
527
+ success: false,
528
+ targetBranch: "",
529
+ previousBranch: "",
530
+ message: "Could not detect default remote branch",
531
+ branchDeleted: false,
532
+ changesDiscarded: false,
533
+ warnings
534
+ };
535
+ }
536
+ const currentBranch = getCurrentBranch(cwd);
537
+ assertSafeGitRefArg(currentBranch, "resetToMainAfterMerge currentBranch");
538
+ assertSafeGitRefArg(defaultBranch, "resetToMainAfterMerge defaultBranch");
539
+ const targetBranch = `origin/${defaultBranch}`;
540
+ if (currentBranch === "HEAD") {
541
+ return {
542
+ success: false,
543
+ targetBranch,
544
+ previousBranch: "HEAD",
545
+ message: "Cannot reset: detached HEAD state",
546
+ branchDeleted: false,
547
+ changesDiscarded: false,
548
+ warnings
549
+ };
550
+ }
551
+ if (currentBranch === defaultBranch) {
552
+ try {
553
+ const logOutput = _internals.gitExec(["log", `${targetBranch}..HEAD`, "--oneline"], cwd);
554
+ if (logOutput.trim().length > 0) {
555
+ return {
556
+ success: false,
557
+ targetBranch,
558
+ previousBranch: currentBranch,
559
+ message: `Cannot reset: ${defaultBranch} has unpushed commits. Push them first.`,
560
+ branchDeleted: false,
561
+ changesDiscarded: false,
562
+ warnings
563
+ };
564
+ }
565
+ } catch {}
566
+ } else {
567
+ try {
568
+ _internals.gitExec(["rev-parse", "--abbrev-ref", `${currentBranch}@{upstream}`], cwd);
569
+ } catch {
570
+ try {
571
+ const localSha = _internals.gitExec(["rev-parse", "HEAD"], cwd).trim();
572
+ const remoteSha = _internals.gitExec(["rev-parse", targetBranch], cwd).trim();
573
+ if (localSha !== remoteSha) {
574
+ return {
575
+ success: false,
576
+ targetBranch,
577
+ previousBranch: currentBranch,
578
+ message: `Cannot reset: branch ${currentBranch} is local-only and diverges from ${defaultBranch}. Push or check manually.`,
579
+ branchDeleted: false,
580
+ changesDiscarded: false,
581
+ warnings
582
+ };
583
+ }
584
+ } catch {
585
+ return {
586
+ success: false,
587
+ targetBranch,
588
+ previousBranch: currentBranch,
589
+ message: `Cannot reset: unable to compare ${currentBranch} with ${defaultBranch}`,
590
+ branchDeleted: false,
591
+ changesDiscarded: false,
592
+ warnings
593
+ };
594
+ }
595
+ }
596
+ }
597
+ try {
598
+ _internals.gitExec(["fetch", "--prune", "origin"], cwd);
599
+ } catch (err) {
600
+ return {
601
+ success: false,
602
+ targetBranch,
603
+ previousBranch: currentBranch,
604
+ message: `Cannot reset: fetch failed \u2014 ${err instanceof Error ? err.message : String(err)}`,
605
+ branchDeleted: false,
606
+ changesDiscarded: false,
607
+ warnings
608
+ };
609
+ }
610
+ const previousBranch = currentBranch;
611
+ let switchedBranch = false;
612
+ if (currentBranch !== defaultBranch) {
613
+ try {
614
+ _internals.gitExec(["checkout", defaultBranch], cwd);
615
+ switchedBranch = true;
616
+ } catch (err) {
617
+ return {
618
+ success: false,
619
+ targetBranch,
620
+ previousBranch,
621
+ message: `Checkout to ${defaultBranch} failed: ${err instanceof Error ? err.message : String(err)}`,
622
+ branchDeleted: false,
623
+ changesDiscarded: false,
624
+ warnings
625
+ };
626
+ }
627
+ }
628
+ try {
629
+ _internals.gitExec(["reset", "--hard", targetBranch], cwd);
630
+ } catch (err) {
631
+ return {
632
+ success: false,
633
+ targetBranch,
634
+ previousBranch,
635
+ message: `Reset to ${targetBranch} failed: ${err instanceof Error ? err.message : String(err)}`,
636
+ branchDeleted: false,
637
+ changesDiscarded: false,
638
+ warnings
639
+ };
640
+ }
641
+ let changesDiscarded = false;
642
+ if (hasUncommittedChanges(cwd)) {
643
+ let discardSucceeded = false;
644
+ for (let retry = 0;retry < 4; retry++) {
645
+ if (retry > 0 && process.platform === "win32") {
646
+ await new Promise((resolve) => setTimeout(resolve, 500));
647
+ }
648
+ try {
649
+ _internals.gitExec(["checkout", "--", "."], cwd);
650
+ discardSucceeded = true;
651
+ break;
652
+ } catch {}
653
+ }
654
+ if (!discardSucceeded) {
655
+ warnings.push("Could not discard all uncommitted changes after reset");
656
+ }
657
+ changesDiscarded = discardSucceeded;
658
+ }
659
+ try {
660
+ _internals.gitExec(["clean", "-fdX", "--", ...GITIGNORED_BUILD_ARTIFACTS], cwd);
661
+ } catch {
662
+ warnings.push("Could not clean build artifacts");
663
+ }
664
+ let branchDeleted = false;
665
+ if (switchedBranch && previousBranch !== defaultBranch) {
666
+ try {
667
+ const mergedOutput = _internals.gitExec(["branch", "--merged", defaultBranch], cwd);
668
+ const isMerged = mergedOutput.split(`
669
+ `).some((line) => line.trim() === previousBranch || line.trim() === `* ${previousBranch}`);
670
+ if (isMerged) {
671
+ _internals.gitExec([
672
+ "branch",
673
+ "-d",
674
+ "--",
675
+ assertSafeGitRefArg(previousBranch, "resetToMainAfterMerge previousBranch")
676
+ ], cwd);
677
+ branchDeleted = true;
678
+ } else {
679
+ warnings.push(`Branch ${previousBranch} is not merged into ${defaultBranch} \u2014 keeping it`);
680
+ }
681
+ } catch {
682
+ warnings.push(`Could not delete branch ${previousBranch}`);
683
+ }
684
+ }
685
+ if (options?.pruneBranches) {
686
+ try {
687
+ const mergedOutput = _internals.gitExec(["branch", "--merged", defaultBranch], cwd);
688
+ const mergedLines = mergedOutput.split(`
689
+ `);
690
+ for (const line of mergedLines) {
691
+ const trimmedLine = line.trim();
692
+ if (!trimmedLine || trimmedLine.startsWith("*") || trimmedLine === defaultBranch) {
693
+ continue;
694
+ }
695
+ try {
696
+ _internals.gitExec([
697
+ "branch",
698
+ "-d",
699
+ "--",
700
+ assertSafeGitRefArg(trimmedLine, "resetToMainAfterMerge prune branch")
701
+ ], cwd);
702
+ } catch {
703
+ warnings.push(`Could not prune branch: ${trimmedLine}`);
704
+ }
705
+ }
706
+ } catch (err) {
707
+ warnings.push(`Prune failed: ${err instanceof Error ? err.message : String(err)}`);
708
+ }
709
+ }
710
+ return {
711
+ success: true,
712
+ targetBranch,
713
+ previousBranch,
714
+ message: branchDeleted ? `Reset to ${defaultBranch} and deleted branch ${previousBranch}` : `Reset to ${defaultBranch}`,
715
+ branchDeleted,
716
+ changesDiscarded,
717
+ warnings
718
+ };
719
+ } catch (err) {
720
+ return {
721
+ success: false,
722
+ targetBranch: "",
723
+ previousBranch: "",
724
+ message: `Unexpected error: ${err instanceof Error ? err.message : String(err)}`,
725
+ branchDeleted: false,
726
+ changesDiscarded: false,
727
+ warnings
728
+ };
729
+ }
730
+ }
731
+ var _internals = {
732
+ gitExec,
733
+ detectDefaultRemoteBranch,
734
+ getDefaultBaseBranch,
735
+ getGitRepositoryStatus,
736
+ resetToRemoteBranch,
737
+ resetToMainAfterMerge,
738
+ spawnSync: __spawnSyncSeam.spawnSync,
739
+ readLaneEnvFileFromDiskSync,
740
+ resolveGitExecutable
741
+ };
742
+
75
743
  // src/commands/git-status-read.ts
76
744
  init_bun_compat();
77
745
  init_git_executable();
@@ -269,10 +937,59 @@ async function readGitRepositoryStatusAsync(cwd) {
269
937
  const result = await gitStatusReadInternals.readOnce(["rev-parse", "--git-dir"], cwd, GIT_READ_DEFAULT_TIMEOUT_MS);
270
938
  return result.ok;
271
939
  }
940
+ async function readGitRepositoryStatusDetailedAsync(cwd) {
941
+ const result = await gitStatusReadInternals.readOnce(["rev-parse", "--git-dir"], cwd, GIT_READ_DEFAULT_TIMEOUT_MS);
942
+ if (result.ok)
943
+ return { isRepo: true };
944
+ if (result.code === "ENOENT") {
945
+ const failureClass = classifySpawnFailure({ code: result.code }, cwd);
946
+ if (failureClass === "cwd-missing") {
947
+ return {
948
+ isRepo: false,
949
+ reason: "git_error",
950
+ message: `git spawn cwd is missing: ${cwd} (ENOENT)`
951
+ };
952
+ }
953
+ if (failureClass === "cwd-unreadable") {
954
+ return {
955
+ isRepo: false,
956
+ reason: "git_error",
957
+ message: `git spawn cwd could not be inspected: ${cwd} (ENOENT)`
958
+ };
959
+ }
960
+ return {
961
+ isRepo: false,
962
+ reason: "git_unavailable",
963
+ message: gitBinaryMissingMessage(cwd)
964
+ };
965
+ }
966
+ if (result.code === null) {
967
+ const stderrTail2 = result.stderrTail ?? "";
968
+ if (isNotGitRepositoryMessage(stderrTail2)) {
969
+ return {
970
+ isRepo: false,
971
+ reason: "not_git_repo",
972
+ message: stderrTail2 || `rev-parse exited ${result.status}`
973
+ };
974
+ }
975
+ const exitNote = result.status === null ? "rev-parse failed (no exit status reported)" : `rev-parse exited ${result.status}`;
976
+ return {
977
+ isRepo: false,
978
+ reason: "git_error",
979
+ message: `${exitNote}${stderrTail2 ? `: ${stderrTail2}` : ""}`
980
+ };
981
+ }
982
+ const tail = result.stderrTail ?? "";
983
+ return {
984
+ isRepo: false,
985
+ reason: "git_error",
986
+ message: `git read failed (${result.code})${tail ? `: ${tail}` : ""}`
987
+ };
988
+ }
272
989
 
273
990
  // src/commands/rollback-gate.ts
274
- import * as fsSync from "fs";
275
- import * as path from "path";
991
+ import * as fsSync2 from "fs";
992
+ import * as path2 from "path";
276
993
  var ROLLBACK_GIT_KIND = "swarm-rollback-git";
277
994
  var ROLLBACK_PHASE_KIND = "swarm-rollback-phase";
278
995
  var GIT_LIST_MAX_BUFFER_BYTES = 32 * 1024 * 1024;
@@ -304,15 +1021,29 @@ async function readFailureDetail(raw) {
304
1021
  }
305
1022
  function isRealGitRepository(directory) {
306
1023
  try {
307
- if (fsSync.statSync(path.join(directory, ".git")).isFile())
1024
+ if (fsSync2.statSync(path2.join(directory, ".git")).isFile())
308
1025
  return true;
309
1026
  } catch {
310
1027
  return false;
311
1028
  }
312
- return fsSync.existsSync(path.join(directory, ".git", "HEAD"));
1029
+ return fsSync2.existsSync(path2.join(directory, ".git", "HEAD"));
1030
+ }
1031
+ function isOutOfRootRelativePath(rel) {
1032
+ if (rel === "")
1033
+ return false;
1034
+ const normalized = path2.posix.normalize(rel);
1035
+ return normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/");
313
1036
  }
314
- function parseTrackedDirtyPaths(statusOutput) {
1037
+ function parseTrackedDirtyPathsWithExclusions(statusOutput) {
315
1038
  const dirtyPaths = [];
1039
+ const excludedOutOfRoot = [];
1040
+ const push = (candidate) => {
1041
+ if (isOutOfRootRelativePath(candidate)) {
1042
+ excludedOutOfRoot.push(candidate);
1043
+ return;
1044
+ }
1045
+ dirtyPaths.push(candidate);
1046
+ };
316
1047
  const tokens = statusOutput.split("\x00");
317
1048
  for (let i = 0;i < tokens.length; i += 1) {
318
1049
  const token = tokens[i];
@@ -324,44 +1055,83 @@ function parseTrackedDirtyPaths(statusOutput) {
324
1055
  const filePath = token.slice(3);
325
1056
  if (!filePath)
326
1057
  continue;
327
- dirtyPaths.push(filePath);
1058
+ push(filePath);
328
1059
  if (/[RC]/.test(xy) && i + 1 < tokens.length && tokens[i + 1] !== "") {
329
- dirtyPaths.push(tokens[i + 1]);
1060
+ push(tokens[i + 1]);
330
1061
  i += 1;
331
1062
  }
332
1063
  }
333
- return dirtyPaths;
1064
+ return { dirtyPaths, excludedOutOfRoot };
334
1065
  }
335
1066
  async function untrackedPathsInTargetTree(directory, targetSha) {
336
- if (!isRealGitRepository(directory))
337
- return [];
338
- const targetTree = await readToString(await _rollbackGateInternals.runGitBuffered(["ls-tree", "-r", "-z", "--name-only", targetSha], directory));
339
- const index = await readToString(await _rollbackGateInternals.runGitBuffered(["ls-files", "-z"], directory));
340
- if (targetTree === null || index === null)
341
- return [];
1067
+ if (!isRealGitRepository(directory)) {
1068
+ return { paths: [], failure: null };
1069
+ }
1070
+ const targetTreeRaw = await _rollbackGateInternals.runGitBuffered(["ls-tree", "-r", "-z", "--name-only", targetSha], directory);
1071
+ const targetTree = await readToString(targetTreeRaw);
1072
+ if (targetTree === null) {
1073
+ const detail = await readFailureDetail(targetTreeRaw);
1074
+ return {
1075
+ paths: [],
1076
+ failure: detail ?? { code: null, status: null, stderrTail: "" }
1077
+ };
1078
+ }
1079
+ const indexRaw = await _rollbackGateInternals.runGitBuffered(["ls-files", "-z"], directory);
1080
+ const index = await readToString(indexRaw);
1081
+ if (index === null) {
1082
+ const detail = await readFailureDetail(indexRaw);
1083
+ return {
1084
+ paths: [],
1085
+ failure: detail ?? { code: null, status: null, stderrTail: "" }
1086
+ };
1087
+ }
342
1088
  const indexed = new Set(index.split("\x00").filter((p) => p !== ""));
343
1089
  const inWay = [];
344
1090
  for (const rel of targetTree.split("\x00")) {
345
1091
  if (rel === "" || indexed.has(rel))
346
1092
  continue;
347
- if (fsSync.existsSync(path.join(directory, rel)))
1093
+ if (fsSync2.existsSync(path2.join(directory, rel)))
348
1094
  inWay.push(rel);
349
1095
  }
350
- return inWay;
1096
+ return { paths: inWay, failure: null };
351
1097
  }
352
1098
  function trackedDirtyCandidates(directory, dirtyRelPaths) {
353
1099
  return dirtyRelPaths.map((rel) => ({
354
- path: path.join(directory, rel),
1100
+ path: path2.join(directory, rel),
355
1101
  reason: "uncommitted tracked change \u2014 destroyed by git reset --hard"
356
1102
  }));
357
1103
  }
358
1104
  function swarmOverwriteCandidates(directory, overwriteRelPaths) {
359
1105
  return overwriteRelPaths.map((rel) => ({
360
- path: path.join(directory, ".swarm", rel),
1106
+ path: path2.join(directory, ".swarm", rel),
361
1107
  reason: "live .swarm state replaced by the checkpoint copy"
362
1108
  }));
363
1109
  }
364
- function describeUnreadableCause(measurement) {
1110
+ function describeUnreadableCause(measurement, source = "status") {
1111
+ if (source === "status") {
1112
+ return describeUnreadableStatusCause(measurement);
1113
+ }
1114
+ if (measurement.code === "ETIMEDOUT") {
1115
+ return "the target-tree listing read timed out (ETIMEDOUT) \u2014 the host may be unable to complete child spawns while loaded; restart the host and retry";
1116
+ }
1117
+ if (measurement.code === "EOUTPUT") {
1118
+ return `the listing output exceeded the read safety bound (${measurement.stderrTail ?? "output too large"}) \u2014 reduce the change footprint (commit or stash some work, or remove untracked bulk) and retry`;
1119
+ }
1120
+ if (measurement.code === "ESIGNAL") {
1121
+ return "the target-tree listing process was terminated before it answered (signal) \u2014 something external stopped it; retry, and inspect what terminates child processes if it recurs";
1122
+ }
1123
+ if (measurement.code === "ENOENT") {
1124
+ return "the git executable was not found (ENOENT) \u2014 install git on PATH, or set git.binary in your USER-level config, and retry";
1125
+ }
1126
+ if (measurement.status !== null && measurement.status !== undefined) {
1127
+ return `the target-tree listing exited with status ${measurement.status}${measurement.stderrTail ? `: ${measurement.stderrTail}` : ""} \u2014 repair the repository state and retry`;
1128
+ }
1129
+ if (measurement.code) {
1130
+ return `the listing read failed with code ${measurement.code} \u2014 retry, or inspect the repository state manually`;
1131
+ }
1132
+ return "retry, or inspect the repository state manually";
1133
+ }
1134
+ function describeUnreadableStatusCause(measurement) {
365
1135
  if (measurement.code === "ETIMEDOUT") {
366
1136
  return "the read timed out (ETIMEDOUT) \u2014 the host may be unable to complete child spawns while loaded; restart the host and retry";
367
1137
  }
@@ -382,9 +1152,13 @@ function describeUnreadableCause(measurement) {
382
1152
  }
383
1153
  return "retry, or inspect git status manually";
384
1154
  }
1155
+ function formatExcludedRecord(rel) {
1156
+ const sanitized = rel.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
1157
+ return sanitized.length > 200 ? `${sanitized.slice(0, 200)}\u2026` : sanitized;
1158
+ }
385
1159
  async function measureGitDestruction(directory, targetSha) {
386
1160
  if (!isRealGitRepository(directory)) {
387
- return { kind: "ok", dirtyRelPaths: [] };
1161
+ return { kind: "ok", dirtyRelPaths: [], excludedOutOfRoot: [] };
388
1162
  }
389
1163
  const raw = _rollbackGateInternals.runGit(["--no-optional-locks", "status", "--porcelain", "-z"], directory);
390
1164
  const status = await readToString(raw);
@@ -394,23 +1168,35 @@ async function measureGitDestruction(directory, targetSha) {
394
1168
  kind: "unreadable",
395
1169
  code: failure.code,
396
1170
  status: failure.status,
397
- stderrTail: failure.stderrTail
398
- } : { kind: "unreadable" };
1171
+ stderrTail: failure.stderrTail,
1172
+ source: "status"
1173
+ } : { kind: "unreadable", source: "status" };
399
1174
  }
400
- const dirtyRelPaths = parseTrackedDirtyPaths(status);
1175
+ const { dirtyPaths: dirtyRelPaths, excludedOutOfRoot } = parseTrackedDirtyPathsWithExclusions(status);
401
1176
  if (targetSha) {
402
- for (const rel of await untrackedPathsInTargetTree(directory, targetSha)) {
1177
+ const { paths, failure } = await untrackedPathsInTargetTree(directory, targetSha);
1178
+ if (failure) {
1179
+ return {
1180
+ kind: "unreadable",
1181
+ code: failure.code,
1182
+ status: failure.status,
1183
+ stderrTail: failure.stderrTail,
1184
+ source: "enumeration",
1185
+ excludedOutOfRoot
1186
+ };
1187
+ }
1188
+ for (const rel of paths) {
403
1189
  if (!dirtyRelPaths.includes(rel))
404
1190
  dirtyRelPaths.push(rel);
405
1191
  }
406
1192
  }
407
- return { kind: "ok", dirtyRelPaths };
1193
+ return { kind: "ok", dirtyRelPaths, excludedOutOfRoot };
408
1194
  }
409
1195
 
410
1196
  // src/commands/destructive-purge.ts
411
1197
  var PENDING_PURGE_PATH = "pending-purge.json";
412
1198
  var PENDING_PURGE_TTL_MS = 15 * 60 * 1000;
413
- var _internals = {
1199
+ var _internals2 = {
414
1200
  readFileSync: fs.readFileSync,
415
1201
  rmSync: fs.rmSync.bind(fs),
416
1202
  existsSync: fs.existsSync.bind(fs),
@@ -426,7 +1212,7 @@ var GIT_STATUS_ARGS = [
426
1212
  "--porcelain"
427
1213
  ];
428
1214
  async function runGitStatusRead(args, cwd) {
429
- const raw = await _internals.runGitStatus(args, cwd);
1215
+ const raw = await _internals2.runGitStatus(args, cwd);
430
1216
  if (typeof raw === "string")
431
1217
  return { stdout: raw };
432
1218
  if (raw === null)
@@ -437,8 +1223,16 @@ async function runGitStatusRead(args, cwd) {
437
1223
  cause: { code: raw.code, status: raw.status, stderrTail: raw.stderrTail }
438
1224
  };
439
1225
  }
440
- function parseTrackedDirtyPaths2(statusOutput) {
1226
+ function parseTrackedDirtyPathsWithExclusions2(statusOutput) {
441
1227
  const dirtyPaths = [];
1228
+ const excludedOutOfRoot = [];
1229
+ const push = (candidate) => {
1230
+ if (isOutOfRootRelativePath(candidate)) {
1231
+ excludedOutOfRoot.push(candidate);
1232
+ return;
1233
+ }
1234
+ dirtyPaths.push(candidate);
1235
+ };
442
1236
  for (const line of statusOutput.split(`
443
1237
  `)) {
444
1238
  if (!line)
@@ -452,13 +1246,13 @@ function parseTrackedDirtyPaths2(statusOutput) {
452
1246
  for (const side of filePath.split(" -> ")) {
453
1247
  const cleaned = side.trim().replace(/^"|"$/g, "");
454
1248
  if (cleaned)
455
- dirtyPaths.push(cleaned);
1249
+ push(cleaned);
456
1250
  }
457
1251
  } else {
458
- dirtyPaths.push(filePath);
1252
+ push(filePath);
459
1253
  }
460
1254
  }
461
- return dirtyPaths;
1255
+ return { dirtyPaths, excludedOutOfRoot };
462
1256
  }
463
1257
  async function readTrackedDirtyPaths(directory) {
464
1258
  const read = await runGitStatusRead(GIT_STATUS_ARGS, directory);
@@ -466,7 +1260,8 @@ async function readTrackedDirtyPaths(directory) {
466
1260
  return { ok: false };
467
1261
  if ("cause" in read)
468
1262
  return { ok: false, failure: read.cause };
469
- return { ok: true, dirtyPaths: parseTrackedDirtyPaths2(read.stdout) };
1263
+ const { dirtyPaths, excludedOutOfRoot } = parseTrackedDirtyPathsWithExclusions2(read.stdout);
1264
+ return { ok: true, dirtyPaths, excludedOutOfRoot };
470
1265
  }
471
1266
  async function recheckBeforeDestructive(directory, expectedDirtyPaths) {
472
1267
  const read = await readTrackedDirtyPaths(directory);
@@ -502,7 +1297,7 @@ function resolveCandidates(scopeTarget, extra) {
502
1297
  }
503
1298
  function scopeDigest(kind, candidates) {
504
1299
  const paths = candidates.map((c) => {
505
- const resolved = path2.resolve(c.path);
1300
+ const resolved = path3.resolve(c.path);
506
1301
  if (resolved.includes("\x00") || resolved.includes(`
507
1302
  `)) {
508
1303
  throw new Error(`Purge candidate path contains a NUL or newline separator character and cannot be scope-bound: ${JSON.stringify(resolved)}`);
@@ -514,20 +1309,20 @@ function scopeDigest(kind, candidates) {
514
1309
  return createHash("sha256").update(`${kind}\x00${sorted}`).digest("hex");
515
1310
  }
516
1311
  function mintToken(digest) {
517
- return createHash("sha256").update(`${digest}:${_internals.randomBytes(16).toString("hex")}`).digest("hex").slice(0, 24);
1312
+ return createHash("sha256").update(`${digest}:${_internals2.randomBytes(16).toString("hex")}`).digest("hex").slice(0, 24);
518
1313
  }
519
1314
  function readPending(directory) {
520
1315
  try {
521
- const raw = _internals.readFileSync(pendingPath(directory), "utf-8");
1316
+ const raw = _internals2.readFileSync(pendingPath(directory), "utf-8");
522
1317
  const parsed = JSON.parse(raw);
523
1318
  if (!parsed || parsed.schemaVersion !== 1 || typeof parsed.scopeDigest !== "string" || typeof parsed.confirmToken !== "string" || typeof parsed.createdAt !== "number") {
524
1319
  return null;
525
1320
  }
526
1321
  if (!Number.isFinite(parsed.createdAt))
527
1322
  return null;
528
- if (parsed.createdAt > _internals.now() + 60000)
1323
+ if (parsed.createdAt > _internals2.now() + 60000)
529
1324
  return null;
530
- if (_internals.now() - parsed.createdAt > PENDING_PURGE_TTL_MS) {
1325
+ if (_internals2.now() - parsed.createdAt > PENDING_PURGE_TTL_MS) {
531
1326
  return null;
532
1327
  }
533
1328
  return {
@@ -541,7 +1336,7 @@ function readPending(directory) {
541
1336
  }
542
1337
  }
543
1338
  function writePending(directory, record) {
544
- _internals.atomicWriteSwarmFileSync(pendingPath(directory), JSON.stringify(record, null, 2));
1339
+ _internals2.atomicWriteSwarmFileSync(pendingPath(directory), JSON.stringify(record, null, 2));
545
1340
  }
546
1341
  function previewDestructivePurge(scopeTarget, projectRoot, extra) {
547
1342
  const scope = resolveCandidates(scopeTarget, extra);
@@ -566,7 +1361,7 @@ function issueConfirmToken(scopeTarget, projectRoot, extra) {
566
1361
  schemaVersion: 1,
567
1362
  scopeDigest: digest,
568
1363
  confirmToken: token,
569
- createdAt: _internals.now()
1364
+ createdAt: _internals2.now()
570
1365
  });
571
1366
  return token;
572
1367
  }
@@ -575,7 +1370,7 @@ function consumeConfirmToken(scopeTarget, projectRoot, token, extra) {
575
1370
  if (!verdict.ok)
576
1371
  return verdict;
577
1372
  try {
578
- _internals.rmSync(pendingPath(projectRoot), { force: true });
1373
+ _internals2.rmSync(pendingPath(projectRoot), { force: true });
579
1374
  } catch (error) {
580
1375
  log(`[destructive-purge] could not consume pending record: ${error instanceof Error ? error.message : String(error)}`);
581
1376
  }
@@ -609,14 +1404,14 @@ function executeDestructivePurge(scopeTarget, projectRoot, token, extra) {
609
1404
  const { candidates } = resolveCandidates(scopeTarget, extra);
610
1405
  const purged = [];
611
1406
  for (const candidate of candidates) {
612
- if (!_internals.existsSync(candidate.path))
1407
+ if (!_internals2.existsSync(candidate.path))
613
1408
  continue;
614
1409
  try {
615
- _internals.rmSync(candidate.path, { recursive: true, force: true });
1410
+ _internals2.rmSync(candidate.path, { recursive: true, force: true });
616
1411
  purged.push(candidate.path);
617
1412
  } catch (error) {
618
1413
  try {
619
- _internals.rmSync(pendingPath(projectRoot), { force: true });
1414
+ _internals2.rmSync(pendingPath(projectRoot), { force: true });
620
1415
  } catch {}
621
1416
  return {
622
1417
  ok: false,
@@ -625,7 +1420,7 @@ function executeDestructivePurge(scopeTarget, projectRoot, token, extra) {
625
1420
  }
626
1421
  }
627
1422
  try {
628
- _internals.rmSync(pendingPath(projectRoot), { force: true });
1423
+ _internals2.rmSync(pendingPath(projectRoot), { force: true });
629
1424
  } catch (error) {
630
1425
  log(`[destructive-purge] could not consume pending record: ${error instanceof Error ? error.message : String(error)}`);
631
1426
  }
@@ -634,12 +1429,12 @@ function executeDestructivePurge(scopeTarget, projectRoot, token, extra) {
634
1429
 
635
1430
  // src/commands/reset-backup.ts
636
1431
  import * as fs2 from "fs";
637
- import * as path3 from "path";
1432
+ import * as path4 from "path";
638
1433
  var RESET_BACKUP_RETENTION = 5;
639
1434
  function backupSwarmStateBeforeReset(directory, kind, relEntries, opts) {
640
1435
  const warnings = [];
641
1436
  const copied = [];
642
- const swarmDir = path3.join(directory, ".swarm");
1437
+ const swarmDir = path4.join(directory, ".swarm");
643
1438
  try {
644
1439
  const stat = fs2.lstatSync(swarmDir);
645
1440
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
@@ -651,8 +1446,8 @@ function backupSwarmStateBeforeReset(directory, kind, relEntries, opts) {
651
1446
  return { backupDir: null, copied, warnings };
652
1447
  }
653
1448
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
654
- const backupsRoot = path3.join(swarmDir, opts?.backupsRoot ?? "reset-backups");
655
- const backupDir = path3.join(backupsRoot, `${kind}-${timestamp}`);
1449
+ const backupsRoot = path4.join(swarmDir, opts?.backupsRoot ?? "reset-backups");
1450
+ const backupDir = path4.join(backupsRoot, `${kind}-${timestamp}`);
656
1451
  let backupDirCreated = false;
657
1452
  const ensureBackupDir = () => {
658
1453
  if (!backupDirCreated) {
@@ -672,8 +1467,8 @@ function backupSwarmStateBeforeReset(directory, kind, relEntries, opts) {
672
1467
  continue;
673
1468
  try {
674
1469
  ensureBackupDir();
675
- const dest = path3.join(backupDir, rel);
676
- fs2.mkdirSync(path3.dirname(dest), { recursive: true });
1470
+ const dest = path4.join(backupDir, rel);
1471
+ fs2.mkdirSync(path4.dirname(dest), { recursive: true });
677
1472
  fs2.cpSync(src, dest, { recursive: true });
678
1473
  copied.push(rel);
679
1474
  } catch (err) {
@@ -688,7 +1483,7 @@ function backupSwarmStateBeforeReset(directory, kind, relEntries, opts) {
688
1483
  }
689
1484
  return { backupDir: null, copied, warnings };
690
1485
  }
691
- pruneOldResetBackups(backupsRoot, warnings, path3.basename(backupDir));
1486
+ pruneOldResetBackups(backupsRoot, warnings, path4.basename(backupDir));
692
1487
  return { backupDir, copied, warnings };
693
1488
  }
694
1489
  var BACKUP_KIND_PREFIXES = [
@@ -712,7 +1507,7 @@ function pruneOldResetBackups(backupsRoot, warnings, keepDirName) {
712
1507
  if (stale.name === keepDirName)
713
1508
  continue;
714
1509
  try {
715
- fs2.rmSync(path3.join(backupsRoot, stale.name), {
1510
+ fs2.rmSync(path4.join(backupsRoot, stale.name), {
716
1511
  recursive: true,
717
1512
  force: true
718
1513
  });
@@ -727,7 +1522,7 @@ function pruneOldResetBackups(backupsRoot, warnings, keepDirName) {
727
1522
  function backupTrackedChangesBeforeRollback(directory, repoRelPaths) {
728
1523
  const warnings = [];
729
1524
  const copied = [];
730
- const swarmDir = path3.join(directory, ".swarm");
1525
+ const swarmDir = path4.join(directory, ".swarm");
731
1526
  try {
732
1527
  const stat = fs2.lstatSync(swarmDir);
733
1528
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
@@ -739,8 +1534,8 @@ function backupTrackedChangesBeforeRollback(directory, repoRelPaths) {
739
1534
  return { backupDir: null, copied, warnings };
740
1535
  }
741
1536
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
742
- const backupsRoot = path3.join(swarmDir, "rollback-backups");
743
- const backupDir = path3.join(backupsRoot, `rollback-git-${timestamp}`);
1537
+ const backupsRoot = path4.join(swarmDir, "rollback-backups");
1538
+ const backupDir = path4.join(backupsRoot, `rollback-git-${timestamp}`);
744
1539
  let backupDirCreated = false;
745
1540
  const ensureBackupDir = () => {
746
1541
  if (!backupDirCreated) {
@@ -751,13 +1546,13 @@ function backupTrackedChangesBeforeRollback(directory, repoRelPaths) {
751
1546
  for (const rel of repoRelPaths) {
752
1547
  if (!rel)
753
1548
  continue;
754
- const src = path3.join(directory, rel);
1549
+ const src = path4.join(directory, rel);
755
1550
  if (!fs2.existsSync(src))
756
1551
  continue;
757
1552
  try {
758
1553
  ensureBackupDir();
759
- const dest = path3.join(backupDir, "tracked", rel);
760
- fs2.mkdirSync(path3.dirname(dest), { recursive: true });
1554
+ const dest = path4.join(backupDir, "tracked", rel);
1555
+ fs2.mkdirSync(path4.dirname(dest), { recursive: true });
761
1556
  fs2.cpSync(src, dest, { recursive: true });
762
1557
  copied.push(rel);
763
1558
  } catch (err) {
@@ -772,38 +1567,19 @@ function backupTrackedChangesBeforeRollback(directory, repoRelPaths) {
772
1567
  }
773
1568
  return { backupDir: null, copied, warnings };
774
1569
  }
775
- pruneOldResetBackups(backupsRoot, warnings, path3.basename(backupDir));
1570
+ pruneOldResetBackups(backupsRoot, warnings, path4.basename(backupDir));
776
1571
  return { backupDir, copied, warnings };
777
1572
  }
778
1573
 
779
1574
  // src/tools/checkpoint.ts
780
1575
  init_git_executable();
781
1576
 
782
- // src/utils/transient-retry.ts
783
- var MAX_TRANSIENT_RETRIES = 5;
784
- var RETRY_BASE_DELAY_MS = 200;
785
- function isTransientSpawnError(error) {
786
- if (!error || typeof error !== "object")
787
- return false;
788
- const code = error.code;
789
- return code === "ETIMEDOUT";
790
- }
791
- function transientBackoff(attempt) {
792
- const delay = RETRY_BASE_DELAY_MS * 2 ** attempt;
793
- try {
794
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delay);
795
- } catch {
796
- const start = Date.now();
797
- while (Date.now() - start < delay) {}
798
- }
799
- }
800
-
801
1577
  // src/tools/create-tool.ts
802
1578
  init_zod();
803
1579
 
804
1580
  // src/tools/resolve-working-directory.ts
805
1581
  import * as fs3 from "fs";
806
- import * as path4 from "path";
1582
+ import * as path5 from "path";
807
1583
  function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
808
1584
  if (workingDirectory == null || workingDirectory === "") {
809
1585
  if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
@@ -848,8 +1624,8 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
848
1624
  message: "Invalid working_directory: path traversal sequences (..) are not allowed"
849
1625
  };
850
1626
  }
851
- const normalizedDir = path4.normalize(workingDirectory);
852
- const resolvedDir = path4.resolve(normalizedDir);
1627
+ const normalizedDir = path5.normalize(workingDirectory);
1628
+ const resolvedDir = path5.resolve(normalizedDir);
853
1629
  let statResult;
854
1630
  try {
855
1631
  statResult = fs3.statSync(resolvedDir);
@@ -876,7 +1652,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
876
1652
  if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
877
1653
  return { success: true, directory: resolvedDir };
878
1654
  }
879
- const resolvedFallback = path4.resolve(fallbackDirectory);
1655
+ const resolvedFallback = path5.resolve(fallbackDirectory);
880
1656
  let fallbackExists = false;
881
1657
  try {
882
1658
  fs3.statSync(resolvedFallback);
@@ -966,8 +1742,8 @@ var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
966
1742
  var PLAN_LOCK_PATH = "plan.json";
967
1743
  var CHECKPOINT_AGENT_NAME = "checkpoint";
968
1744
  var MAX_LABEL_LENGTH = 100;
969
- var GIT_TIMEOUT_MS = 30000;
970
- var GIT_MAX_BUFFER_BYTES = 5 * 1024 * 1024;
1745
+ var GIT_TIMEOUT_MS2 = 30000;
1746
+ var GIT_MAX_BUFFER_BYTES2 = 5 * 1024 * 1024;
971
1747
  var CHECKPOINT_LOCK_MAX_ATTEMPTS = 20;
972
1748
  var CHECKPOINT_LOCK_DELAY_MS = 250;
973
1749
  var CHECKPOINT_LOCK_DEADLINE_MS = 45000;
@@ -981,14 +1757,17 @@ var SHELL_METACHARACTERS = /[;|&$`(){}<>!'"]/;
981
1757
  var SAFE_LABEL_PATTERN = /^[a-zA-Z0-9_ -]+$/;
982
1758
  var CONTROL_CHAR_PATTERN = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/;
983
1759
  var NON_ASCII_PATTERN = /[^\x20-\x7E]/;
984
- var _internals2 = {
1760
+ var _internals3 = {
985
1761
  tryAcquireLock,
986
1762
  loadPlan,
987
- gitExec,
1763
+ gitExec: gitExec2,
988
1764
  sleep: (ms) => new Promise((resolve3) => setTimeout(resolve3, ms)),
989
1765
  findCommitByExactSubject,
990
1766
  stageAllExcludingSwarm,
991
- resolveGitExecutable
1767
+ resolveGitExecutable,
1768
+ measureGitDestruction,
1769
+ readGitStatusAsync,
1770
+ readGitRepositoryStatusDetailedAsync
992
1771
  };
993
1772
  function serializeResult(action, payload) {
994
1773
  return JSON.stringify({
@@ -1039,7 +1818,7 @@ function validateLabel(label) {
1039
1818
  return null;
1040
1819
  }
1041
1820
  function getCheckpointLogPath(directory) {
1042
- return path5.join(directory, CHECKPOINT_LOG_PATH);
1821
+ return path6.join(directory, CHECKPOINT_LOG_PATH);
1043
1822
  }
1044
1823
  function readCheckpointLog(directory) {
1045
1824
  const logPath = getCheckpointLogPath(directory);
@@ -1057,7 +1836,7 @@ function readCheckpointLog(directory) {
1057
1836
  }
1058
1837
  function writeCheckpointLog(log2, directory) {
1059
1838
  const logPath = getCheckpointLogPath(directory);
1060
- const dir = path5.dirname(logPath);
1839
+ const dir = path6.dirname(logPath);
1061
1840
  if (!fs4.existsSync(dir)) {
1062
1841
  fs4.mkdirSync(dir, { recursive: true });
1063
1842
  }
@@ -1124,17 +1903,17 @@ function upsertTaskCompletionCheckpointEntry(directory, log2, entry, maxCheckpoi
1124
1903
  }
1125
1904
  }
1126
1905
  function stageAllExcludingSwarm(directory) {
1127
- _internals2.gitExec(["add", "--all", "--", ...NON_SWARM_COMMIT_PATHSPEC], directory);
1906
+ _internals3.gitExec(["add", "--all", "--", ...NON_SWARM_COMMIT_PATHSPEC], directory);
1128
1907
  }
1129
- function gitExec(args, cwd) {
1908
+ function gitExec2(args, cwd) {
1130
1909
  for (let attempt = 0;attempt < MAX_TRANSIENT_RETRIES; attempt++) {
1131
- const result = child_process.spawnSync(_internals2.resolveGitExecutable(), args, {
1910
+ const result = child_process2.spawnSync(_internals3.resolveGitExecutable(), args, {
1132
1911
  cwd,
1133
1912
  encoding: "utf-8",
1134
- timeout: GIT_TIMEOUT_MS,
1913
+ timeout: GIT_TIMEOUT_MS2,
1135
1914
  stdio: ["ignore", "pipe", "pipe"],
1136
1915
  windowsHide: true,
1137
- maxBuffer: GIT_MAX_BUFFER_BYTES
1916
+ maxBuffer: GIT_MAX_BUFFER_BYTES2
1138
1917
  });
1139
1918
  if (result.error) {
1140
1919
  const code = result.error.code;
@@ -1163,29 +1942,10 @@ function gitExec(args, cwd) {
1163
1942
  throw new Error("git command failed after transient retries");
1164
1943
  }
1165
1944
  function getCurrentSha(directory) {
1166
- return gitExec(["rev-parse", "HEAD"], directory).trim();
1167
- }
1168
- function isGitRepo(directory) {
1169
- try {
1170
- gitExec(["rev-parse", "--git-dir"], directory);
1171
- return { isRepo: true };
1172
- } catch (e) {
1173
- const message = e instanceof Error ? e.message : String(e);
1174
- const isTransient = /ETIMEDOUT|timed out/i.test(message) && !/not a git repository/i.test(message);
1175
- if (isTransient) {
1176
- return {
1177
- isRepo: false,
1178
- warning: "git probe failed after retry exhaustion - treating as not a git repository"
1179
- };
1180
- }
1181
- return {
1182
- isRepo: false,
1183
- warning: "git probe failed - directory may not be a git repository"
1184
- };
1185
- }
1945
+ return gitExec2(["rev-parse", "HEAD"], directory).trim();
1186
1946
  }
1187
1947
  function findCommitByExactSubject(directory, subject) {
1188
- const output = _internals2.gitExec([
1948
+ const output = _internals3.gitExec([
1189
1949
  "log",
1190
1950
  "HEAD",
1191
1951
  "--format=%H%x00%s",
@@ -1215,7 +1975,7 @@ async function withTaskCompletionLocksResult(input) {
1215
1975
  attempts++;
1216
1976
  let planLockResult;
1217
1977
  try {
1218
- planLockResult = await _internals2.tryAcquireLock(input.directory, PLAN_LOCK_PATH, CHECKPOINT_AGENT_NAME, `save-task-completion-plan-${input.taskId}`);
1978
+ planLockResult = await _internals3.tryAcquireLock(input.directory, PLAN_LOCK_PATH, CHECKPOINT_AGENT_NAME, `save-task-completion-plan-${input.taskId}`);
1219
1979
  } catch (error) {
1220
1980
  if (input.onFailure) {
1221
1981
  return input.onFailure(error);
@@ -1229,13 +1989,13 @@ async function withTaskCompletionLocksResult(input) {
1229
1989
  if (attempts >= CHECKPOINT_LOCK_MAX_ATTEMPTS || Date.now() + CHECKPOINT_LOCK_DELAY_MS > deadline) {
1230
1990
  break;
1231
1991
  }
1232
- await _internals2.sleep(CHECKPOINT_LOCK_DELAY_MS);
1992
+ await _internals3.sleep(CHECKPOINT_LOCK_DELAY_MS);
1233
1993
  continue;
1234
1994
  }
1235
1995
  try {
1236
1996
  let checkpointLockResult;
1237
1997
  try {
1238
- checkpointLockResult = await _internals2.tryAcquireLock(input.directory, CHECKPOINT_LOG_PATH, CHECKPOINT_AGENT_NAME, `save-task-completion-checkpoint-${input.taskId}`);
1998
+ checkpointLockResult = await _internals3.tryAcquireLock(input.directory, CHECKPOINT_LOG_PATH, CHECKPOINT_AGENT_NAME, `save-task-completion-checkpoint-${input.taskId}`);
1239
1999
  } catch (error) {
1240
2000
  if (input.onFailure) {
1241
2001
  return input.onFailure(error);
@@ -1249,7 +2009,7 @@ async function withTaskCompletionLocksResult(input) {
1249
2009
  if (attempts >= CHECKPOINT_LOCK_MAX_ATTEMPTS || Date.now() + CHECKPOINT_LOCK_DELAY_MS > deadline) {
1250
2010
  break;
1251
2011
  }
1252
- await _internals2.sleep(CHECKPOINT_LOCK_DELAY_MS);
2012
+ await _internals3.sleep(CHECKPOINT_LOCK_DELAY_MS);
1253
2013
  continue;
1254
2014
  }
1255
2015
  try {
@@ -1305,7 +2065,7 @@ async function withCheckpointMutationLockResult(input) {
1305
2065
  attempts++;
1306
2066
  let lockResult;
1307
2067
  try {
1308
- lockResult = await _internals2.tryAcquireLock(input.directory, CHECKPOINT_LOG_PATH, CHECKPOINT_AGENT_NAME, input.operationKey);
2068
+ lockResult = await _internals3.tryAcquireLock(input.directory, CHECKPOINT_LOG_PATH, CHECKPOINT_AGENT_NAME, input.operationKey);
1309
2069
  } catch (error) {
1310
2070
  if (input.onFailure) {
1311
2071
  return input.onFailure(error);
@@ -1342,7 +2102,7 @@ async function withCheckpointMutationLockResult(input) {
1342
2102
  if (attempts >= CHECKPOINT_LOCK_MAX_ATTEMPTS || Date.now() + CHECKPOINT_LOCK_DELAY_MS > deadline) {
1343
2103
  break;
1344
2104
  }
1345
- await _internals2.sleep(CHECKPOINT_LOCK_DELAY_MS);
2105
+ await _internals3.sleep(CHECKPOINT_LOCK_DELAY_MS);
1346
2106
  }
1347
2107
  if (input.onBusy) {
1348
2108
  return input.onBusy(attempts);
@@ -1367,19 +2127,19 @@ function handleSave(label, directory) {
1367
2127
  });
1368
2128
  }
1369
2129
  const timestamp = new Date().toISOString();
1370
- _internals2.stageAllExcludingSwarm(directory);
2130
+ _internals3.stageAllExcludingSwarm(directory);
1371
2131
  const hasStagedChanges = (() => {
1372
2132
  try {
1373
- gitExec(["diff", "--cached", "--quiet"], directory);
2133
+ gitExec2(["diff", "--cached", "--quiet"], directory);
1374
2134
  return false;
1375
2135
  } catch {
1376
2136
  return true;
1377
2137
  }
1378
2138
  })();
1379
2139
  if (hasStagedChanges) {
1380
- gitExec(["commit", "-m", `checkpoint: ${label}`], directory);
2140
+ gitExec2(["commit", "-m", `checkpoint: ${label}`], directory);
1381
2141
  } else if (allowEmptyCommits) {
1382
- gitExec(["commit", "--allow-empty", "-m", `checkpoint: ${label}`], directory);
2142
+ gitExec2(["commit", "--allow-empty", "-m", `checkpoint: ${label}`], directory);
1383
2143
  }
1384
2144
  const newSha = getCurrentSha(directory);
1385
2145
  appendCheckpointEntry(directory, log2, {
@@ -1416,8 +2176,8 @@ async function saveCheckpointRecord(label, directory) {
1416
2176
  return { success: false, error: `duplicate label: "${label}"` };
1417
2177
  }
1418
2178
  let sha = "";
1419
- const repoProbe = isGitRepo(directory);
1420
- if (repoProbe.isRepo) {
2179
+ const saveProbe = await _internals3.readGitStatusAsync(["rev-parse", "--git-dir"], directory);
2180
+ if (saveProbe.ok) {
1421
2181
  try {
1422
2182
  sha = getCurrentSha(directory);
1423
2183
  } catch {
@@ -1456,14 +2216,16 @@ async function handleRestore(label, directory, confirmToken) {
1456
2216
  error: `checkpoint not found: "${label}"`
1457
2217
  });
1458
2218
  }
1459
- const measurement = await measureGitDestruction(directory, checkpoint.sha);
2219
+ const measurement = await _internals3.measureGitDestruction(directory, checkpoint.sha);
1460
2220
  if (measurement.kind === "unreadable") {
1461
- const cause = describeUnreadableCause(measurement);
2221
+ const cause = describeUnreadableCause(measurement, measurement.source);
2222
+ const unreadableNote = measurement.excludedOutOfRoot && measurement.excludedOutOfRoot.length > 0 ? ` \u2014 ${measurement.excludedOutOfRoot.length} out-of-root porcelain record(s) had been excluded from the scope before the read failed (first: ${formatExcludedRecord(measurement.excludedOutOfRoot[0])})` : "";
1462
2223
  return serializeResult("restore", {
1463
2224
  success: false,
1464
- error: `cannot verify the working tree (git status unreadable; ${cause}) \u2014 refusing to restore without a preview.`
2225
+ error: `cannot verify the working tree (${measurement.source === "enumeration" ? "the target-tree listing could not be read" : "git status unreadable"}; ${cause})${unreadableNote} \u2014 refusing to restore without a preview.`
1465
2226
  });
1466
2227
  }
2228
+ const exclusionNote = measurement.excludedOutOfRoot.length > 0 ? `${measurement.excludedOutOfRoot.length} out-of-root porcelain record(s) were excluded from this scope (first: ${formatExcludedRecord(measurement.excludedOutOfRoot[0])})` : null;
1467
2229
  if (measurement.dirtyRelPaths.length > 0) {
1468
2230
  const candidates = trackedDirtyCandidates(directory, measurement.dirtyRelPaths);
1469
2231
  const scopeAnchor = candidates[0].path;
@@ -1481,6 +2243,7 @@ async function handleRestore(label, directory, confirmToken) {
1481
2243
  `Restoring to checkpoint "${label}" runs a hard git reset and would DESTROY ${candidates.length} uncommitted tracked file(s):`,
1482
2244
  ...shown.map((p) => ` - ${p}`),
1483
2245
  ...overflow > 0 ? [` (+${overflow} more)`] : [],
2246
+ ...exclusionNote ? [`\u26A0 ${exclusionNote}`] : [],
1484
2247
  "Backups are taken automatically once confirmed."
1485
2248
  ],
1486
2249
  confirm_token: token,
@@ -1495,7 +2258,7 @@ async function handleRestore(label, directory, confirmToken) {
1495
2258
  });
1496
2259
  }
1497
2260
  const backup = backupTrackedChangesBeforeRollback(directory, measurement.dirtyRelPaths);
1498
- const atRiskExisting = measurement.dirtyRelPaths.filter((rel) => fs4.existsSync(path5.join(directory, rel))).length;
2261
+ const atRiskExisting = measurement.dirtyRelPaths.filter((rel) => fs4.existsSync(path6.join(directory, rel))).length;
1499
2262
  if (!backup.backupDir || backup.copied.length < atRiskExisting) {
1500
2263
  return serializeResult("restore", {
1501
2264
  success: false,
@@ -1503,7 +2266,7 @@ async function handleRestore(label, directory, confirmToken) {
1503
2266
  });
1504
2267
  }
1505
2268
  const logBeforeReset2 = log2;
1506
- gitExec(["reset", "--hard", checkpoint.sha], directory);
2269
+ gitExec2(["reset", "--hard", checkpoint.sha], directory);
1507
2270
  writeCheckpointLog(logBeforeReset2, directory);
1508
2271
  return serializeResult("restore", {
1509
2272
  success: true,
@@ -1511,17 +2274,17 @@ async function handleRestore(label, directory, confirmToken) {
1511
2274
  sha: checkpoint.sha,
1512
2275
  backup_dir: backup.backupDir,
1513
2276
  ...backup.warnings.length > 0 ? { backup_warnings: backup.warnings } : {},
1514
- message: `Restored to checkpoint: "${label}" (hard reset)${backup.backupDir ? `; destroyed bytes backed up to ${path5.relative(directory, backup.backupDir)}` : ""}${backup.warnings.length > 0 ? `; warnings: ${backup.warnings.join("; ")}` : ""}`
2277
+ message: `Restored to checkpoint: "${label}" (hard reset)${backup.backupDir ? `; destroyed bytes backed up to ${path6.relative(directory, backup.backupDir)}` : ""}${backup.warnings.length > 0 ? `; warnings: ${backup.warnings.join("; ")}` : ""}${exclusionNote ? `; \u26A0 ${exclusionNote}` : ""}`
1515
2278
  });
1516
2279
  }
1517
2280
  const logBeforeReset = log2;
1518
- gitExec(["reset", "--hard", checkpoint.sha], directory);
2281
+ gitExec2(["reset", "--hard", checkpoint.sha], directory);
1519
2282
  writeCheckpointLog(logBeforeReset, directory);
1520
2283
  return serializeResult("restore", {
1521
2284
  success: true,
1522
2285
  label,
1523
2286
  sha: checkpoint.sha,
1524
- message: `Restored to checkpoint: "${label}" (hard reset)`
2287
+ message: `Restored to checkpoint: "${label}" (hard reset)${exclusionNote ? `; \u26A0 ${exclusionNote}` : ""}`
1525
2288
  });
1526
2289
  } catch (e) {
1527
2290
  return serializeResult("restore", {
@@ -1568,7 +2331,7 @@ async function handleSaveTaskCompletion(taskId, directory) {
1568
2331
  directory,
1569
2332
  taskId,
1570
2333
  run: async () => {
1571
- const plan = await _internals2.loadPlan(directory);
2334
+ const plan = await _internals3.loadPlan(directory);
1572
2335
  if (!plan) {
1573
2336
  return serializeResult("save_task_completion", {
1574
2337
  success: false,
@@ -1588,7 +2351,7 @@ async function handleSaveTaskCompletion(taskId, directory) {
1588
2351
  error: `save_task_completion failed: task "${taskId}" is ${task.status}, not completed`
1589
2352
  });
1590
2353
  }
1591
- await _internals2.afterTaskEligibilityRead?.(directory, task.id);
2354
+ await _internals3.afterTaskEligibilityRead?.(directory, task.id);
1592
2355
  const planIdentityHash = derivePlanIdentityHash(plan);
1593
2356
  const planId = derivePlanId(plan);
1594
2357
  const ledgerEvents = await readLedgerEvents(directory);
@@ -1624,12 +2387,12 @@ async function handleSaveTaskCompletion(taskId, directory) {
1624
2387
  message: `Checkpoint task completion already logged for "${descriptor.taskId}"`
1625
2388
  });
1626
2389
  }
1627
- const recoveredSha = _internals2.findCommitByExactSubject(directory, descriptor.subject);
2390
+ const recoveredSha = _internals3.findCommitByExactSubject(directory, descriptor.subject);
1628
2391
  let committedSha = null;
1629
2392
  committedSha = recoveredSha ?? receipt.sha;
1630
2393
  if (!committedSha) {
1631
- _internals2.stageAllExcludingSwarm(directory);
1632
- _internals2.gitExec([
2394
+ _internals3.stageAllExcludingSwarm(directory);
2395
+ _internals3.gitExec([
1633
2396
  "commit",
1634
2397
  "--allow-empty",
1635
2398
  "-m",
@@ -1671,11 +2434,12 @@ var checkpoint = createSwarmTool({
1671
2434
  task_id: exports_external.string().optional().describe('Plan task ID (required for save_task_completion), e.g. "1.1"')
1672
2435
  },
1673
2436
  execute: async (args, directory) => {
1674
- const repoProbe = isGitRepo(directory);
1675
- if (!repoProbe.isRepo) {
2437
+ const repoStatus = await _internals3.readGitRepositoryStatusDetailedAsync(directory);
2438
+ if (!repoStatus.isRepo) {
2439
+ const reason = repoStatus.reason === "not_git_repo" ? "not a git repository" : repoStatus.reason === "git_unavailable" ? "git executable is not available" : `git probe failed (${repoStatus.message})`;
1676
2440
  return serializeResult("unknown", {
1677
2441
  success: false,
1678
- error: `${repoProbe.warning ?? "not a git repository"} - checkpoint tools require a git repository`
2442
+ error: `${reason} - checkpoint tools require a git repository`
1679
2443
  });
1680
2444
  }
1681
2445
  let action;
@@ -1764,4 +2528,4 @@ var checkpoint = createSwarmTool({
1764
2528
  }
1765
2529
  });
1766
2530
 
1767
- export { MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, gitStatusReadInternals, readGitStatusAsync, readGitRepositoryStatusAsync, ROLLBACK_GIT_KIND, ROLLBACK_PHASE_KIND, trackedDirtyCandidates, swarmOverwriteCandidates, describeUnreadableCause, measureGitDestruction, GIT_STATUS_ARGS, parseTrackedDirtyPaths2 as parseTrackedDirtyPaths, recheckBeforeDestructive, previewDestructivePurge, issueConfirmToken, consumeConfirmToken, executeDestructivePurge, backupSwarmStateBeforeReset, resolveWorkingDirectory, createSwarmTool, _internals2 as _internals, saveCheckpointRecord, checkpoint };
2531
+ export { MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, assertSafeGitRefArg, readLaneEnvFileFromDiskSync, isGitRepo, getDefaultBaseBranch, detectDefaultRemoteBranch, resetToRemoteBranch, resetToMainAfterMerge, _internals, gitStatusReadInternals, readGitStatusAsync, readGitRepositoryStatusAsync, readGitRepositoryStatusDetailedAsync, ROLLBACK_GIT_KIND, ROLLBACK_PHASE_KIND, trackedDirtyCandidates, swarmOverwriteCandidates, describeUnreadableCause, formatExcludedRecord, measureGitDestruction, GIT_STATUS_ARGS, parseTrackedDirtyPathsWithExclusions2 as parseTrackedDirtyPathsWithExclusions, recheckBeforeDestructive, previewDestructivePurge, issueConfirmToken, consumeConfirmToken, executeDestructivePurge, backupSwarmStateBeforeReset, resolveWorkingDirectory, createSwarmTool, _internals3 as _internals1, saveCheckpointRecord, checkpoint };