opencode-swarm 7.107.6 → 7.108.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 (76) hide show
  1. package/dist/cli/{capability-probe-wsjzcp48.js → capability-probe-sh9bwh39.js} +2 -1
  2. package/dist/cli/{config-doctor-kdq3vae4.js → config-doctor-q354tb2w.js} +2 -2
  3. package/dist/cli/core-vev13ej2.js +34 -0
  4. package/dist/cli/{curator-drift-ed7p5mfy.js → curator-drift-fdfgj9qf.js} +3 -3
  5. package/dist/cli/{curator-jpwvkpdv.js → curator-js92gc8a.js} +24 -20
  6. package/dist/cli/curator-llm-factory-n70bxf40.js +40 -0
  7. package/dist/cli/{dispatch-anmxxhen.js → dispatch-zr1xja4t.js} +2 -2
  8. package/dist/cli/{evidence-summary-service-6zsgw7x7.js → evidence-summary-service-bjhr75v0.js} +9 -7
  9. package/dist/cli/{gate-evidence-pgr7d942.js → gate-evidence-v6ds8rcp.js} +5 -4
  10. package/dist/cli/guardrail-explain-sn2a4pb7.js +41 -0
  11. package/dist/cli/{guardrail-log-fjypsmek.js → guardrail-log-8mqqsssq.js} +4 -4
  12. package/dist/cli/hive-promoter-3hh8ph4a.js +48 -0
  13. package/dist/cli/{index-9b7qp18e.js → index-1cn967sa.js} +2 -2
  14. package/dist/cli/{index-572t1qff.js → index-1emhz3zb.js} +1378 -2013
  15. package/dist/cli/{index-n1sptn1w.js → index-2nt4mq9n.js} +1 -1
  16. package/dist/cli/{index-6h82c2h0.js → index-48mc4d48.js} +1056 -192
  17. package/dist/cli/{index-bq8kd9r9.js → index-5gnp8fyw.js} +3 -3
  18. package/dist/cli/index-5mkc2f9z.js +154 -0
  19. package/dist/cli/{index-0kxc7t16.js → index-6jng9c8t.js} +1 -1
  20. package/dist/cli/{index-57rthc1z.js → index-7jyndvsy.js} +2 -2
  21. package/dist/cli/{index-b9v501fr.js → index-7xgqky26.js} +33 -5
  22. package/dist/cli/{index-4r5z8sgw.js → index-85bacexr.js} +2 -2
  23. package/dist/cli/{index-bpgqbwmf.js → index-9qxntjtx.js} +6 -6
  24. package/dist/cli/index-f1qyj61w.js +418 -0
  25. package/dist/cli/{index-wvxhynxq.js → index-fm7xz1ne.js} +1 -1
  26. package/dist/cli/{index-dfdg4ws5.js → index-fz1jgbvw.js} +6 -6
  27. package/dist/cli/{index-pchpahdf.js → index-h4c4rrwx.js} +20 -3
  28. package/dist/cli/{index-kxm3bdjh.js → index-mmny81g1.js} +6 -6
  29. package/dist/cli/{index-v4fcn4tr.js → index-q1exe2b3.js} +2 -198
  30. package/dist/cli/{index-57ymmh7h.js → index-r3j3458j.js} +4 -4
  31. package/dist/cli/{index-3naa2ajc.js → index-vvk2g744.js} +10 -156
  32. package/dist/cli/{index-zej8cy0j.js → index-vx76tcxe.js} +1 -1
  33. package/dist/cli/{index-xmarctsf.js → index-wg0665ft.js} +7 -7
  34. package/dist/cli/{index-4vsgp5a8.js → index-wj4jeavn.js} +1 -1
  35. package/dist/cli/index-wvrj16f0.js +206 -0
  36. package/dist/cli/{index-h28wfp7n.js → index-x06d2qqm.js} +28 -24
  37. package/dist/cli/index-x2q0gnt8.js +254 -0
  38. package/dist/cli/{index-xg18az81.js → index-yw8qpf51.js} +1 -1
  39. package/dist/cli/index.js +24 -20
  40. package/dist/cli/{knowledge-escalator-3sgwrgy9.js → knowledge-escalator-6v7sjprx.js} +8 -7
  41. package/dist/cli/{knowledge-events-kfc6xmkw.js → knowledge-events-6n3x3he2.js} +6 -5
  42. package/dist/cli/{knowledge-store-kyacem71.js → knowledge-store-exkw6yb1.js} +6 -5
  43. package/dist/cli/{knowledge-validator-ez3ws8ef.js → knowledge-validator-exymfcb9.js} +7 -6
  44. package/dist/cli/{pending-delegations-vfnpm8zt.js → pending-delegations-yyz3fcfn.js} +5 -4
  45. package/dist/cli/{pr-subscriptions-bpvazmsw.js → pr-subscriptions-vv8byxe6.js} +6 -5
  46. package/dist/cli/{schema-x0ekqzvs.js → schema-x17zmd03.js} +1 -1
  47. package/dist/cli/scope-persistence-6m5nsp1r.js +20 -0
  48. package/dist/cli/{skill-generator-x83re96d.js → skill-generator-mx9br4ty.js} +9 -8
  49. package/dist/commands/_shared/url-security.d.ts +13 -3
  50. package/dist/commands/close.d.ts +11 -0
  51. package/dist/commands/lanes.d.ts +35 -0
  52. package/dist/commands/registry.d.ts +7 -0
  53. package/dist/config/schema.d.ts +102 -0
  54. package/dist/git/branch.d.ts +53 -7
  55. package/dist/git/index.d.ts +5 -1
  56. package/dist/git/pr.d.ts +35 -2
  57. package/dist/hooks/delegation-gate/worktree-isolation.d.ts +118 -2
  58. package/dist/hooks/delegation-gate/worktree-merge-status.d.ts +17 -0
  59. package/dist/hooks/init-orphan-recovery-advisory.d.ts +58 -0
  60. package/dist/hooks/init-orphan-recovery.d.ts +65 -0
  61. package/dist/index.js +427 -416
  62. package/dist/sandbox/executor.d.ts +11 -1
  63. package/dist/sandbox/linux/bubblewrap-executor.d.ts +3 -1
  64. package/dist/sandbox/macos/sandbox-exec-executor.d.ts +3 -1
  65. package/dist/sandbox/win32/native-sandbox-executor.d.ts +1 -1
  66. package/dist/sandbox/win32/restricted-environment-executor.d.ts +4 -1
  67. package/dist/turbo/lean/runner.d.ts +11 -20
  68. package/dist/turbo/lean/worktree.d.ts +9 -3
  69. package/dist/utils/bun-compat.d.ts +30 -0
  70. package/dist/worktree/core.d.ts +55 -0
  71. package/dist/worktree/merge.d.ts +5 -1
  72. package/dist/worktree/types.d.ts +38 -0
  73. package/package.json +1 -1
  74. package/dist/cli/curator-llm-factory-9y15npd5.js +0 -36
  75. package/dist/cli/guardrail-explain-jk0amzae.js +0 -37
  76. package/dist/cli/hive-promoter-j2krbdn9.js +0 -44
@@ -3,15 +3,16 @@ import {
3
3
  readEffectiveSpecSync
4
4
  } from "./index-91j1sqzm.js";
5
5
  import {
6
- isValidTaskId,
7
- readTaskEvidence,
8
- readTaskEvidenceRaw,
9
- sanitizeTaskId
10
- } from "./index-0kxc7t16.js";
6
+ _internals,
7
+ buildDefaultPolicy,
8
+ execute,
9
+ init_runner_client,
10
+ probe
11
+ } from "./index-q1exe2b3.js";
11
12
  import {
12
13
  readSwarmFileAsync,
13
14
  validateSwarmPath
14
- } from "./index-57ymmh7h.js";
15
+ } from "./index-r3j3458j.js";
15
16
  import {
16
17
  readCachedParsedFile
17
18
  } from "./index-jtqkh8jf.js";
@@ -21,20 +22,825 @@ import {
21
22
  init_logger,
22
23
  warn
23
24
  } from "./index-zgwm4ryv.js";
25
+ import {
26
+ isValidTaskId,
27
+ readTaskEvidence,
28
+ readTaskEvidenceRaw,
29
+ sanitizeTaskId
30
+ } from "./index-6jng9c8t.js";
24
31
  import {
25
32
  ZodError,
26
33
  exports_external
27
34
  } from "./index-293f68mj.js";
28
35
  import {
29
36
  withEvidenceLock
30
- } from "./index-3naa2ajc.js";
31
- import {
32
- bunHash,
33
- bunWrite
34
- } from "./index-b9v501fr.js";
37
+ } from "./index-5mkc2f9z.js";
35
38
  import {
36
39
  emit
37
40
  } from "./index-vn263hqt.js";
41
+ import {
42
+ bunHash,
43
+ bunWrite,
44
+ mergeEnvForChild
45
+ } from "./index-7xgqky26.js";
46
+ import {
47
+ __esm,
48
+ __export,
49
+ __require,
50
+ __toCommonJS
51
+ } from "./index-a76rekgs.js";
52
+
53
+ // src/sandbox/linux/bubblewrap-executor.ts
54
+ import { spawnSync } from "child_process";
55
+ function probeBwrap() {
56
+ try {
57
+ const result = spawnSync("bwrap", ["--version"], {
58
+ windowsHide: true,
59
+ encoding: "utf-8",
60
+ timeout: 5000,
61
+ stdio: ["ignore", "pipe", "ignore"]
62
+ });
63
+ if (result.error) {
64
+ const code = result.error.code;
65
+ if (code && BWRAP_UNAVAILABLE_CODES.has(code)) {
66
+ warn(`Sandbox disabled: bwrap error (${code}). Falling through to tool-layer enforcement.`);
67
+ return false;
68
+ }
69
+ warn(`Sandbox disabled: bwrap spawn error (${result.error.message}). Falling through to tool-layer enforcement.`);
70
+ return false;
71
+ }
72
+ return result.status === BWRAP_VERSION_EXIT && result.stdout.trim().length > 0;
73
+ } catch (err) {
74
+ const message = err instanceof Error ? err.message : String(err);
75
+ warn(`Sandbox disabled: probe threw (${message}). Falling through to tool-layer enforcement.`);
76
+ return false;
77
+ }
78
+ }
79
+ function shellEscape(s) {
80
+ return s.replace(/'/g, "'\\''");
81
+ }
82
+
83
+ class BubblewrapSandboxExecutor {
84
+ mechanism = "Bubblewrap";
85
+ _scopePaths;
86
+ _tempDir;
87
+ _available;
88
+ _disabledReason;
89
+ constructor(scopePaths = [], tempDir) {
90
+ this._scopePaths = scopePaths;
91
+ this._tempDir = tempDir;
92
+ this._available = false;
93
+ this._disabledReason = null;
94
+ try {
95
+ if (!_internals2.probeBwrap()) {
96
+ this._disabledReason = "bwrap not available or not functional";
97
+ warn(`Sandbox disabled: ${this._disabledReason}. Falling through to tool-layer enforcement.`);
98
+ } else {
99
+ this._available = true;
100
+ }
101
+ } catch (err) {
102
+ const message = err instanceof Error ? err.message : String(err);
103
+ this._disabledReason = `constructor threw: ${message}`;
104
+ this._available = false;
105
+ warn(`Sandbox disabled: ${this._disabledReason}. Falling through to tool-layer enforcement.`);
106
+ }
107
+ }
108
+ isAvailable() {
109
+ return this._available;
110
+ }
111
+ disable(reason) {
112
+ this._available = false;
113
+ this._disabledReason = reason;
114
+ warn(`Sandbox disabled: ${reason}. Falling through to tool-layer enforcement.`);
115
+ }
116
+ wrapCommand(command, scopePaths, tempDir, envOverrides) {
117
+ if (!this._available) {
118
+ throw new SandboxError("Sandbox not available", "SANDBOX_UNAVAILABLE");
119
+ }
120
+ const temp = tempDir ?? this._tempDir ?? "/tmp";
121
+ const allScopes = [...this._scopePaths, ...scopePaths];
122
+ const bindArgs = allScopes.flatMap((p) => [
123
+ "--bind",
124
+ `'${shellEscape(p)}'`,
125
+ `'${shellEscape(p)}'`
126
+ ]);
127
+ const envArgs = [];
128
+ if (envOverrides) {
129
+ for (const [key, value] of Object.entries(envOverrides)) {
130
+ if (!isValidEnvKey(key)) {
131
+ continue;
132
+ }
133
+ if (value === null) {
134
+ envArgs.push("--unsetenv", key);
135
+ } else {
136
+ envArgs.push("--setenv", key, value);
137
+ }
138
+ }
139
+ }
140
+ const args = [
141
+ "--unshare-user",
142
+ "--unshare-net",
143
+ "--unshare-ipc",
144
+ "--die-with-parent",
145
+ "--new-session",
146
+ "--cap-drop",
147
+ "ALL",
148
+ ...bindArgs,
149
+ "--dev",
150
+ "/dev",
151
+ "--size",
152
+ "500M",
153
+ "--tmpfs",
154
+ `'${shellEscape(temp)}'`,
155
+ "--ro-bind",
156
+ "/etc",
157
+ "/etc",
158
+ "--ro-bind",
159
+ "/usr",
160
+ "/usr",
161
+ "--ro-bind",
162
+ "/lib",
163
+ "/lib",
164
+ "--ro-bind",
165
+ "/lib64",
166
+ "/lib64",
167
+ "--proc",
168
+ "/proc",
169
+ "--unshare-pid",
170
+ ...envArgs,
171
+ "--",
172
+ "bash",
173
+ "-c",
174
+ `'${shellEscape(command)}'`
175
+ ];
176
+ return `bwrap ${args.join(" ")}`;
177
+ }
178
+ getEnvOverrides() {
179
+ return {};
180
+ }
181
+ }
182
+ var BWRAP_VERSION_EXIT = 0, BWRAP_UNAVAILABLE_CODES, _internals2;
183
+ var init_bubblewrap_executor = __esm(() => {
184
+ init_logger();
185
+ init_executor();
186
+ BWRAP_UNAVAILABLE_CODES = new Set(["ENOENT", "EACCES", "ENOSPC"]);
187
+ _internals2 = {
188
+ probeBwrap
189
+ };
190
+ });
191
+
192
+ // src/sandbox/executors/bubblewrap.ts
193
+ var exports_bubblewrap = {};
194
+ __export(exports_bubblewrap, {
195
+ BubblewrapSandboxExecutor: () => BubblewrapSandboxExecutor
196
+ });
197
+ var init_bubblewrap = __esm(() => {
198
+ init_bubblewrap_executor();
199
+ });
200
+
201
+ // src/sandbox/macos/sandbox-exec-executor.ts
202
+ import { spawnSync as spawnSync2 } from "child_process";
203
+ import { mkdtempSync, writeFileSync } from "fs";
204
+ import * as os from "os";
205
+ import * as path from "path";
206
+ function probeSandboxExec() {
207
+ try {
208
+ const result = spawnSync2("sandbox-exec", ["--version"], {
209
+ windowsHide: true,
210
+ encoding: "utf-8",
211
+ timeout: 5000,
212
+ stdio: ["ignore", "pipe", "ignore"]
213
+ });
214
+ if (result.error) {
215
+ const code = result.error.code;
216
+ if (code && SANDBOX_UNAVAILABLE_CODES.has(code)) {
217
+ warn(`Sandbox disabled: sandbox-exec error (${code}). Falling through to tool-layer enforcement.`);
218
+ return false;
219
+ }
220
+ warn(`Sandbox disabled: spawn error (${result.error.message}). Falling through to tool-layer enforcement.`);
221
+ return false;
222
+ }
223
+ return result.status === 0;
224
+ } catch (err) {
225
+ const message = err instanceof Error ? err.message : String(err);
226
+ warn(`Sandbox disabled: probe threw (${message}). Falling through to tool-layer enforcement.`);
227
+ return false;
228
+ }
229
+ }
230
+ function shellEscape2(s) {
231
+ return s.replace(/'/g, "'\\''");
232
+ }
233
+ function sbplEscapePath(path2) {
234
+ const withoutControlChars = path2.split("").filter((ch) => {
235
+ const cp = ch.codePointAt(0);
236
+ return cp >= 32 && cp !== 127;
237
+ }).join("");
238
+ return withoutControlChars.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
239
+ }
240
+ function buildSandboxProfile(scopePaths, tempDir, envOverrides) {
241
+ const rwPaths = [...scopePaths];
242
+ if (tempDir) {
243
+ rwPaths.push(tempDir);
244
+ }
245
+ const rwAllowLines = rwPaths.map((p) => `(allow file-write* (subpath "${sbplEscapePath(p)}"))`).join(`
246
+ `);
247
+ const envLines = [];
248
+ if (envOverrides) {
249
+ for (const [key, value] of Object.entries(envOverrides)) {
250
+ if (!isValidEnvKey(key)) {
251
+ continue;
252
+ }
253
+ if (value === null) {
254
+ envLines.push(`(unsetenv ${key})`);
255
+ } else {
256
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
257
+ envLines.push(`(setenv ${key} "${escaped}")`);
258
+ }
259
+ }
260
+ }
261
+ const profile = `(version 1)
262
+ (allow default)
263
+ (allow file-read* (subpath "/usr"))
264
+ (allow file-read* (subpath "/bin"))
265
+ (allow file-read* (subpath "/sbin"))
266
+ (allow file-read* (subpath "/lib"))
267
+ (allow file-read* (subpath "/lib64"))
268
+ ${rwAllowLines}
269
+ ${envLines.join(`
270
+ `)}
271
+ (deny file-write*)`;
272
+ return profile;
273
+ }
274
+
275
+ class MacOSSandboxExecutor {
276
+ mechanism = "sandbox-exec";
277
+ _scopePaths;
278
+ _tempDir;
279
+ _available;
280
+ _disabledReason;
281
+ constructor(scopePaths = [], tempDir) {
282
+ if (process.platform !== "darwin") {
283
+ throw new Error("MacOSSandboxExecutor not yet implemented");
284
+ }
285
+ this._scopePaths = scopePaths;
286
+ this._tempDir = tempDir;
287
+ this._available = false;
288
+ this._disabledReason = null;
289
+ try {
290
+ if (!_internals3.probeSandboxExec()) {
291
+ this._disabledReason = "sandbox-exec not available or not functional";
292
+ warn(`Sandbox disabled: ${this._disabledReason}. Falling through to tool-layer enforcement.`);
293
+ } else {
294
+ this._available = true;
295
+ }
296
+ } catch (err) {
297
+ const message = err instanceof Error ? err.message : String(err);
298
+ this._disabledReason = `constructor threw: ${message}`;
299
+ this._available = false;
300
+ warn(`Sandbox disabled: ${this._disabledReason}. Falling through to tool-layer enforcement.`);
301
+ }
302
+ }
303
+ isAvailable() {
304
+ return this._available;
305
+ }
306
+ disable(reason) {
307
+ this._disabledReason = reason;
308
+ this._available = false;
309
+ warn(`Sandbox disabled: ${reason}. Falling through to tool-layer enforcement.`);
310
+ }
311
+ wrapCommand(command, scopePaths, tempDir, envOverrides) {
312
+ if (!this._available) {
313
+ throw new SandboxError("Sandbox not available", "SANDBOX_UNAVAILABLE");
314
+ }
315
+ if (!_internals3.probeSandboxExec()) {
316
+ this._available = false;
317
+ this._disabledReason = "sandbox-exec became unavailable between calls";
318
+ warn(`Sandbox disabled: ${this._disabledReason}. Falling through to tool-layer enforcement.`);
319
+ throw new SandboxError("Sandbox not available", "SANDBOX_UNAVAILABLE");
320
+ }
321
+ const temp = tempDir ?? this._tempDir ?? os.tmpdir();
322
+ const allScopes = [...this._scopePaths, ...scopePaths];
323
+ const profile = buildSandboxProfile(allScopes, temp, envOverrides);
324
+ let profilePath;
325
+ try {
326
+ const profileDir = mkdtempSync(path.join(os.tmpdir(), "sandbox-"));
327
+ profilePath = path.join(profileDir, `profile-${process.pid}-${Date.now()}.sb`);
328
+ writeFileSync(profilePath, profile, { mode: 384 });
329
+ } catch (err) {
330
+ const message = err instanceof Error ? err.message : String(err);
331
+ warn(`Sandbox disabled: failed to write profile (${message}). Falling through to tool-layer enforcement.`);
332
+ throw new SandboxError("Sandbox not available", "SANDBOX_UNAVAILABLE");
333
+ }
334
+ const escapedCommand = shellEscape2(command);
335
+ const escapedProfilePath = shellEscape2(profilePath);
336
+ return `sandbox-exec -f '${escapedProfilePath}' bash -c '${escapedCommand}'`;
337
+ }
338
+ getEnvOverrides() {
339
+ return {
340
+ DYLD_INSERT_LIBRARIES: null,
341
+ DYLD_LIBRARY_PATH: null,
342
+ DYLD_FRAMEWORK_PATH: null,
343
+ DYLD_ROOT_PATH: null,
344
+ PATH: "/usr/bin:/bin:/usr/sbin:/sbin"
345
+ };
346
+ }
347
+ }
348
+ var SANDBOX_UNAVAILABLE_CODES, _internals3;
349
+ var init_sandbox_exec_executor = __esm(() => {
350
+ init_logger();
351
+ init_executor();
352
+ SANDBOX_UNAVAILABLE_CODES = new Set(["ENOENT", "EACCES", "ENOSPC"]);
353
+ _internals3 = {
354
+ probeSandboxExec
355
+ };
356
+ });
357
+
358
+ // src/sandbox/executors/macos.ts
359
+ var exports_macos = {};
360
+ __export(exports_macos, {
361
+ MacOSSandboxExecutor: () => MacOSSandboxExecutor
362
+ });
363
+ var init_macos = __esm(() => {
364
+ init_sandbox_exec_executor();
365
+ });
366
+
367
+ // src/sandbox/win32/edge-cases.ts
368
+ function detectPowerShellEscape(command) {
369
+ if (process.platform !== "win32")
370
+ return false;
371
+ const encodedCommandPattern = /-EncodedCommand/i;
372
+ if (encodedCommandPattern.test(command)) {
373
+ return true;
374
+ }
375
+ const encodedBase64Pattern = /-Enc(odedCommand)?\s+[A-Za-z0-9+/=]+/i;
376
+ if (encodedBase64Pattern.test(command)) {
377
+ return true;
378
+ }
379
+ const psExePattern = /powershell\.exe?\s+.*-(?:e(?:n(?:c(?:odedCommand)?)?)?|wmi|remote)/i;
380
+ if (psExePattern.test(command)) {
381
+ return true;
382
+ }
383
+ const iexPattern = /\biex\b/i;
384
+ if (iexPattern.test(command)) {
385
+ return true;
386
+ }
387
+ const invokeExpressionPattern = /Invoke-Expression/i;
388
+ if (invokeExpressionPattern.test(command)) {
389
+ return true;
390
+ }
391
+ const invokeCommandRemotePattern = /Invoke-Command\s+.*-ComputerName/i;
392
+ if (invokeCommandRemotePattern.test(command)) {
393
+ return true;
394
+ }
395
+ const psCommandPattern = /powershell.*-Command\s+["'].*\$/i;
396
+ if (psCommandPattern.test(command)) {
397
+ return true;
398
+ }
399
+ const execPolicyPattern = /-ExecutionPolicy\s+(?:Bypass|RemoteSigned|Unrestricted)/i;
400
+ if (execPolicyPattern.test(command)) {
401
+ return true;
402
+ }
403
+ const noProfilePattern = /-NoProfile\s+-/i;
404
+ if (noProfilePattern.test(command)) {
405
+ return true;
406
+ }
407
+ const automationPattern = /New-Object\s+.*Management\.Automation/i;
408
+ if (automationPattern.test(command)) {
409
+ return true;
410
+ }
411
+ const downloadPattern = /\[System\.Net\.(?:WebClient|Http).*\]\.(?:DownloadString|DownloadFile)/i;
412
+ if (downloadPattern.test(command)) {
413
+ return true;
414
+ }
415
+ const hiddenWindowPattern = /-WindowStyle\s+Hidden/i;
416
+ if (hiddenWindowPattern.test(command)) {
417
+ return true;
418
+ }
419
+ const scriptInjectionPattern = /\$(?:env|input|host)\b.*\|/i;
420
+ if (scriptInjectionPattern.test(command)) {
421
+ return true;
422
+ }
423
+ const obfuscatedIexPattern = /IEX|i:eX/i;
424
+ if (obfuscatedIexPattern.test(command)) {
425
+ return true;
426
+ }
427
+ return false;
428
+ }
429
+ var init_edge_cases = () => {};
430
+
431
+ // src/sandbox/win32/restricted-environment-executor.ts
432
+ import { spawnSync as spawnSync3 } from "child_process";
433
+ import * as os2 from "os";
434
+ import * as path2 from "path";
435
+ function probeWindowsSandbox() {
436
+ try {
437
+ const result = spawnSync3("cmd", ["/c", "echo", "ok"], {
438
+ windowsHide: true,
439
+ encoding: "utf-8",
440
+ timeout: 5000,
441
+ stdio: ["ignore", "pipe", "ignore"]
442
+ });
443
+ if (result.error) {
444
+ const code = result.error.code;
445
+ if (code && SANDBOX_UNAVAILABLE_CODES2.has(code)) {
446
+ warn(`Sandbox disabled: spawn error (${code}). Falling through to tool-layer enforcement.`);
447
+ return false;
448
+ }
449
+ warn(`Sandbox disabled: spawn error (${result.error.message}). Falling through to tool-layer enforcement.`);
450
+ return false;
451
+ }
452
+ return result.status === 0;
453
+ } catch (err) {
454
+ const message = err instanceof Error ? err.message : String(err);
455
+ warn(`Sandbox disabled: probe threw (${message}). Falling through to tool-layer enforcement.`);
456
+ return false;
457
+ }
458
+ }
459
+ function psStringEscape(s) {
460
+ return s.replace(/[`"$]/g, "`$&");
461
+ }
462
+ function getSafeWindowsPath() {
463
+ const raw = process.env.SystemRoot;
464
+ const isValid = typeof raw === "string" && raw.length > 0 && /^[A-Za-z]:[\\/](?:[^\\/;"'<>|*?\r\n]+[\\/]?)*$/.test(raw);
465
+ const sysRoot = isValid ? raw : "C:\\Windows";
466
+ return `${sysRoot}\\System32;${sysRoot}`;
467
+ }
468
+ function isPowerShellNativeCommand(command) {
469
+ return /^(?:Remove-Item|rm|del|erase|Copy-Item|cp|copy|Move-Item|mv|move|Rename-Item|ren|New-Item|ni|Get-Item|gi|Get-ChildItem|ls|dir|gci|Get-Content|cat|type|gc|Set-Content|sc|Add-Content|ac|Clear-Content|clc|Test-Path|Resolve-Path|Split-Path|Join-Path|Out-File|Get-Date)\b/i.test(command.trimStart());
470
+ }
471
+ function isSafePsCommandBody(command) {
472
+ return !/[;&|`$()\r\n]/.test(command);
473
+ }
474
+ function isPathInScopes(command, scopes) {
475
+ if (scopes.length === 0)
476
+ return true;
477
+ const pathPattern = /[A-Za-z]:(?:[^\\/:*?"<>|\r\n]+(?:\\[^\\/:*?"<>|\r\n]+)*)/g;
478
+ const paths = command.match(pathPattern) || [];
479
+ if (paths.length === 0)
480
+ return true;
481
+ const normalizedPaths = paths.map((p) => path2.win32.resolve(p));
482
+ const normalizedScopes = scopes.map((s) => s.toLowerCase().replace(/\\+$/, ""));
483
+ return normalizedPaths.every((p) => {
484
+ const lower = p.toLowerCase();
485
+ return normalizedScopes.some((scope) => lower.startsWith(scope));
486
+ });
487
+ }
488
+
489
+ class WindowsSandboxExecutor {
490
+ mechanism = "powershell-wrapper";
491
+ _scopePaths;
492
+ _tempDir;
493
+ _available;
494
+ _disabled;
495
+ _disabledReason;
496
+ constructor(scopePaths = [], tempDir) {
497
+ this._scopePaths = scopePaths;
498
+ this._tempDir = tempDir;
499
+ this._available = false;
500
+ this._disabled = false;
501
+ this._disabledReason = null;
502
+ try {
503
+ if (!_internals4.probeWindowsSandbox()) {
504
+ this._available = false;
505
+ this._disabledReason = "Windows sandbox not available or not functional";
506
+ warn(`Sandbox unavailable: ${this._disabledReason}. Falling through to tool-layer enforcement.`);
507
+ } else {
508
+ this._available = true;
509
+ }
510
+ } catch (err) {
511
+ const message = err instanceof Error ? err.message : String(err);
512
+ this._available = false;
513
+ this._disabledReason = `constructor probe threw: ${message}`;
514
+ warn(`Sandbox unavailable: ${this._disabledReason}. Falling through to tool-layer enforcement.`);
515
+ }
516
+ }
517
+ isAvailable() {
518
+ return this._available && !this._disabled;
519
+ }
520
+ disable(reason) {
521
+ this._disabled = true;
522
+ this._disabledReason = reason;
523
+ warn(`Sandbox disabled: ${reason}. Falling through to tool-layer enforcement.`);
524
+ }
525
+ wrapCommand(command, scopePaths, tempDir, envOverrides) {
526
+ if (!this.isAvailable()) {
527
+ throw new SandboxError("Sandbox not available", "SANDBOX_UNAVAILABLE");
528
+ }
529
+ if (!_internals4.probeWindowsSandbox()) {
530
+ this._available = false;
531
+ this._disabledReason = "Windows sandbox became unavailable between calls";
532
+ warn(`Sandbox disabled: ${this._disabledReason}. Falling through to tool-layer enforcement.`);
533
+ throw new SandboxError("Sandbox not available", "SANDBOX_UNAVAILABLE");
534
+ }
535
+ const temp = tempDir ?? this._tempDir ?? os2.tmpdir();
536
+ const _allScopes = [...this._scopePaths, ...scopePaths];
537
+ if (detectPowerShellEscape(command)) {
538
+ throw new SandboxError("Command contains PowerShell escape patterns", "DETECT_POWERSHELL_ESCAPE");
539
+ }
540
+ if (!isPathInScopes(command, _allScopes)) {
541
+ throw new SandboxError("Command targets paths outside authorized scopes", "PATH_ESCAPE_SCOPE");
542
+ }
543
+ if (isPowerShellNativeCommand(command) && !isSafePsCommandBody(command)) {
544
+ throw new SandboxError("PowerShell-native command body contains unsafe characters", "UNSAFE_PS_COMMAND");
545
+ }
546
+ const safePath = getSafeWindowsPath();
547
+ const escapedTemp = psStringEscape(temp);
548
+ const escapedCommand = psStringEscape(command);
549
+ const envOverrideLines = [];
550
+ if (envOverrides) {
551
+ for (const [key, value] of Object.entries(envOverrides)) {
552
+ if (!isValidEnvKey(key)) {
553
+ continue;
554
+ }
555
+ if (value === null) {
556
+ envOverrideLines.push(`Remove-Item Env:${key} -Force -ErrorAction SilentlyContinue;`);
557
+ } else {
558
+ const psEscaped = value.replace(/'/g, "''");
559
+ envOverrideLines.push(`$env:${key} = '${psEscaped}';`);
560
+ }
561
+ }
562
+ }
563
+ const commandExec = isPowerShellNativeCommand(command) ? `Invoke-Expression "${escapedCommand}"` : `cmd /c "${escapedCommand}"`;
564
+ const envOverrideBlock = envOverrideLines.length > 0 ? `
565
+ ${envOverrideLines.join(`
566
+ `)}` : "";
567
+ const psScript = `
568
+ $ErrorActionPreference = 'Stop';
569
+ try {
570
+ # Set scoped temp directory
571
+ $env:TEMP = '${escapedTemp}';
572
+ $env:TMP = '${escapedTemp}';
573
+
574
+ # Restrict PATH to safe system paths only
575
+ $env:PATH = '${safePath}';
576
+
577
+ # Remove dangerous environment variables that could be used to bypass restrictions
578
+ # Note: LD_PRELOAD, DYLD_* don't apply on Windows but we clear them for completeness
579
+ $dangerousVars = @(
580
+ 'LD_PRELOAD',
581
+ 'DYLD_INSERT_LIBRARIES',
582
+ 'DYLD_LIBRARY_PATH',
583
+ 'DYLD_FRAMEWORK_PATH',
584
+ 'DYLD_ROOT_PATH',
585
+ 'DYLD_FORCE_FLAT_NAMESPACE'
586
+ );
587
+ foreach ($v in $dangerousVars) {
588
+ if (Test-Path Env:$v) {
589
+ Remove-Item Env:$v -Force -ErrorAction SilentlyContinue;
590
+ }
591
+ }
592
+ ${envOverrideBlock}
593
+
594
+ # Execute the command \u2014 PS-native cmdlets via Invoke-Expression, others via the standard command interpreter
595
+ ${commandExec};
596
+ } catch {
597
+ Write-Error $_.Exception.Message;
598
+ exit 1;
599
+ }`;
600
+ return `powershell -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -Command "${psScript.replace(/\n/g, " ").trim()}"`;
601
+ }
602
+ getEnvOverrides() {
603
+ return {
604
+ PATH: getSafeWindowsPath(),
605
+ TEMP: null,
606
+ TMP: null,
607
+ LD_PRELOAD: null,
608
+ DYLD_INSERT_LIBRARIES: null,
609
+ DYLD_LIBRARY_PATH: null,
610
+ DYLD_FRAMEWORK_PATH: null,
611
+ DYLD_ROOT_PATH: null,
612
+ DYLD_FORCE_FLAT_NAMESPACE: null
613
+ };
614
+ }
615
+ }
616
+ var SANDBOX_UNAVAILABLE_CODES2, _internals4;
617
+ var init_restricted_environment_executor = __esm(() => {
618
+ init_logger();
619
+ init_executor();
620
+ init_edge_cases();
621
+ SANDBOX_UNAVAILABLE_CODES2 = new Set([
622
+ "ENOENT",
623
+ "EACCES",
624
+ "EPERM",
625
+ "ENOSPC"
626
+ ]);
627
+ _internals4 = {
628
+ probeWindowsSandbox
629
+ };
630
+ });
631
+
632
+ // src/sandbox/win32/native-sandbox-executor.ts
633
+ import * as crypto from "crypto";
634
+ import * as fs from "fs";
635
+ import * as os3 from "os";
636
+ import * as path3 from "path";
637
+
638
+ class NativeWindowsSandboxExecutor {
639
+ mechanism;
640
+ _probeResult;
641
+ _fallbackExecutor;
642
+ _strength;
643
+ _disabled;
644
+ _disabledReason;
645
+ constructor(scopePaths = [], tempDir) {
646
+ this._disabled = false;
647
+ this._disabledReason = null;
648
+ this._fallbackExecutor = new WindowsSandboxExecutor(scopePaths, tempDir);
649
+ this._probeResult = probe();
650
+ if (this._probeResult.available) {
651
+ this._strength = "strong";
652
+ this.mechanism = `native-runner/${this._probeResult.mode}`;
653
+ } else if (this._fallbackExecutor.isAvailable()) {
654
+ this._strength = "weak";
655
+ this.mechanism = "powershell-wrapper";
656
+ warn(`Native sandbox runner unavailable (${this._probeResult.error ?? "unknown"}); ` + "falling back to PowerShell environment restrictions (weak sandbox)");
657
+ } else {
658
+ this._strength = "none";
659
+ this.mechanism = "none";
660
+ }
661
+ }
662
+ get strength() {
663
+ if (this._disabled)
664
+ return "none";
665
+ return this._strength;
666
+ }
667
+ isAvailable() {
668
+ return !this._disabled && this._strength !== "none";
669
+ }
670
+ disable(reason) {
671
+ this._disabled = true;
672
+ this._disabledReason = reason;
673
+ warn(`Sandbox disabled: ${reason}. Falling through to tool-layer enforcement.`);
674
+ }
675
+ wrapCommand(command, scopePaths, tempDir, envOverrides) {
676
+ if (!this.isAvailable()) {
677
+ throw new SandboxError("Sandbox not available", "SANDBOX_UNAVAILABLE");
678
+ }
679
+ if (this._strength === "strong") {
680
+ return this._wrapWithRunner(command, scopePaths, tempDir, envOverrides);
681
+ }
682
+ return this._fallbackExecutor.wrapCommand(command, scopePaths, tempDir, envOverrides);
683
+ }
684
+ _wrapWithRunner(command, scopePaths, tempDir, _envOverrides) {
685
+ const binary = _internals.findRunnerBinary();
686
+ if (!binary) {
687
+ throw new SandboxError("Runner binary not found", "RUNNER_NOT_FOUND");
688
+ }
689
+ const workspaceRoot = scopePaths[0] ?? process.cwd();
690
+ const runId = `swarm-${crypto.randomUUID?.() ?? Date.now()}`;
691
+ const policy = buildDefaultPolicy(workspaceRoot, runId);
692
+ if (tempDir) {
693
+ policy.temp_root = tempDir;
694
+ }
695
+ policy.workspace_roots = scopePaths.length > 0 ? scopePaths : [workspaceRoot];
696
+ policy.writable_roots = policy.workspace_roots;
697
+ if (_envOverrides) {
698
+ for (const [key, value] of Object.entries(_envOverrides)) {
699
+ if (!isValidEnvKey(key))
700
+ continue;
701
+ if (typeof value === "string") {
702
+ policy.env_overrides[key] = value;
703
+ }
704
+ }
705
+ }
706
+ const policyJson = JSON.stringify(policy);
707
+ const policyDir = path3.join(os3.tmpdir(), "swarm-sandbox-policies");
708
+ try {
709
+ fs.mkdirSync(policyDir, { recursive: true });
710
+ } catch {}
711
+ const policyFile = path3.join(policyDir, `${runId}.json`);
712
+ fs.writeFileSync(policyFile, policyJson, "utf-8");
713
+ const mode = this._probeResult.mode === "none" ? "auto" : this._probeResult.mode;
714
+ const escapedBinary = binary.includes(" ") ? `"${binary}"` : binary;
715
+ const escapedPolicyFile = policyFile.includes(" ") ? `"${policyFile}"` : policyFile;
716
+ const escapedCommand = command.replace(/"/g, "\\\"");
717
+ return `cmd /c "type ${escapedPolicyFile} | ${escapedBinary} --policy-stdin --mode ${mode} -- ${escapedCommand} & del ${escapedPolicyFile} 2>nul"`;
718
+ }
719
+ getEnvOverrides() {
720
+ if (this._strength === "strong") {
721
+ return {
722
+ PATH: null,
723
+ TEMP: null,
724
+ TMP: null,
725
+ LD_PRELOAD: null,
726
+ DYLD_INSERT_LIBRARIES: null,
727
+ DYLD_LIBRARY_PATH: null,
728
+ DYLD_FRAMEWORK_PATH: null,
729
+ DYLD_ROOT_PATH: null,
730
+ DYLD_FORCE_FLAT_NAMESPACE: null
731
+ };
732
+ }
733
+ return this._fallbackExecutor.getEnvOverrides();
734
+ }
735
+ async executeNative(command, workspaceRoot, runId) {
736
+ if (this._strength !== "strong") {
737
+ throw new SandboxError("Native execution requires strong sandbox (runner binary)", "NATIVE_UNAVAILABLE");
738
+ }
739
+ const policy = buildDefaultPolicy(workspaceRoot, runId);
740
+ return execute(command, policy);
741
+ }
742
+ get hasNativeRunner() {
743
+ return this._strength === "strong";
744
+ }
745
+ get disabledReason() {
746
+ return this._disabledReason;
747
+ }
748
+ get probeResult() {
749
+ return this._probeResult;
750
+ }
751
+ }
752
+ var init_native_sandbox_executor = __esm(() => {
753
+ init_logger();
754
+ init_executor();
755
+ init_restricted_environment_executor();
756
+ init_runner_client();
757
+ });
758
+
759
+ // src/sandbox/executors/windows.ts
760
+ var exports_windows = {};
761
+ __export(exports_windows, {
762
+ WindowsSandboxExecutor: () => NativeWindowsSandboxExecutor,
763
+ LegacyWindowsSandboxExecutor: () => WindowsSandboxExecutor
764
+ });
765
+ var init_windows = __esm(() => {
766
+ init_native_sandbox_executor();
767
+ init_restricted_environment_executor();
768
+ });
769
+
770
+ // src/sandbox/executor.ts
771
+ function isValidEnvKey(key) {
772
+ return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key);
773
+ }
774
+ async function getExecutor() {
775
+ if (_cachedExecutorPromise !== undefined) {
776
+ return _cachedExecutorPromise;
777
+ }
778
+ _cachedExecutorPromise = _createExecutor();
779
+ return _cachedExecutorPromise;
780
+ }
781
+ async function _createExecutor() {
782
+ const platform = process.platform;
783
+ if (platform === "linux") {
784
+ return _createLinuxExecutor();
785
+ }
786
+ if (platform === "darwin") {
787
+ return _createMacOSExecutor();
788
+ }
789
+ if (platform === "win32") {
790
+ return _createWindowsExecutor();
791
+ }
792
+ return null;
793
+ }
794
+ async function _createLinuxExecutor() {
795
+ const { SandboxCapabilityProbe, isBubblewrapAvailable } = await import("./capability-probe-sh9bwh39.js");
796
+ await new SandboxCapabilityProbe().detect();
797
+ if (!isBubblewrapAvailable()) {
798
+ return null;
799
+ }
800
+ try {
801
+ const { BubblewrapSandboxExecutor: BubblewrapSandboxExecutor2 } = (init_bubblewrap(), __toCommonJS(exports_bubblewrap));
802
+ return new BubblewrapSandboxExecutor2([]);
803
+ } catch {
804
+ return null;
805
+ }
806
+ }
807
+ async function _createMacOSExecutor() {
808
+ const { SandboxCapabilityProbe, isSandboxExecAvailable } = await import("./capability-probe-sh9bwh39.js");
809
+ await new SandboxCapabilityProbe().detect();
810
+ if (!isSandboxExecAvailable()) {
811
+ return null;
812
+ }
813
+ try {
814
+ const { MacOSSandboxExecutor: MacOSSandboxExecutor2 } = (init_macos(), __toCommonJS(exports_macos));
815
+ return new MacOSSandboxExecutor2([]);
816
+ } catch {
817
+ return null;
818
+ }
819
+ }
820
+ async function _createWindowsExecutor() {
821
+ const { SandboxCapabilityProbe, isWindowsSandboxAvailable } = await import("./capability-probe-sh9bwh39.js");
822
+ await new SandboxCapabilityProbe().detect();
823
+ if (!isWindowsSandboxAvailable()) {
824
+ return null;
825
+ }
826
+ try {
827
+ const { WindowsSandboxExecutor: WindowsSandboxExecutor2 } = (init_windows(), __toCommonJS(exports_windows));
828
+ return new WindowsSandboxExecutor2([]);
829
+ } catch {
830
+ return null;
831
+ }
832
+ }
833
+ var SandboxError, _cachedExecutorPromise;
834
+ var init_executor = __esm(() => {
835
+ SandboxError = class SandboxError extends Error {
836
+ code;
837
+ constructor(message, code) {
838
+ super(message);
839
+ this.code = code;
840
+ this.name = "SandboxError";
841
+ }
842
+ };
843
+ });
38
844
 
39
845
  // src/plan/manager.ts
40
846
  import {
@@ -45,7 +851,7 @@ import {
45
851
  unlinkSync as unlinkSync3
46
852
  } from "fs";
47
853
  import * as fsPromises from "fs/promises";
48
- import * as path5 from "path";
854
+ import * as path9 from "path";
49
855
 
50
856
  // src/config/plan-schema.ts
51
857
  var ExecutionProfileSchema = exports_external.object({
@@ -109,8 +915,10 @@ var PlanSchema = exports_external.object({
109
915
  });
110
916
 
111
917
  // src/git/branch.ts
918
+ init_executor();
112
919
  import * as child_process from "child_process";
113
- import path from "path";
920
+ import * as fsSync from "fs";
921
+ import path4 from "path";
114
922
 
115
923
  // src/utils/git-binary-missing-error.ts
116
924
  class GitBinaryMissingError extends Error {
@@ -157,11 +965,11 @@ function windowsGitCandidates() {
157
965
  const roots = unique([
158
966
  process.env.ProgramFiles ?? "",
159
967
  process.env["ProgramFiles(x86)"] ?? "",
160
- process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, "Programs") : ""
968
+ process.env.LOCALAPPDATA ? path4.join(process.env.LOCALAPPDATA, "Programs") : ""
161
969
  ]);
162
970
  const installed = roots.flatMap((root) => [
163
- path.join(root, "Git", "cmd", "git.exe"),
164
- path.join(root, "Git", "bin", "git.exe")
971
+ path4.join(root, "Git", "cmd", "git.exe"),
972
+ path4.join(root, "Git", "bin", "git.exe")
165
973
  ]);
166
974
  return unique(["git", ...installed]);
167
975
  }
@@ -172,20 +980,59 @@ function isNotGitRepositoryMessage(message) {
172
980
  const lower = message.toLowerCase();
173
981
  return lower.includes("not a git repository") || lower.includes("not a git repo");
174
982
  }
175
- function gitExec(args, cwd) {
983
+ function getChildProcessSpawnSync() {
984
+ return child_process.spawnSync;
985
+ }
986
+ var __spawnSyncSeam = {
987
+ spawnSync: (cmd, args, options) => {
988
+ const mergedEnv = mergeEnvForChild(options?.env, options?.envOverrides);
989
+ return getChildProcessSpawnSync()(cmd, args, {
990
+ ...options,
991
+ env: mergedEnv
992
+ });
993
+ }
994
+ };
995
+ function readLaneEnvFileFromDiskSync(worktreePath, laneIndex) {
996
+ const envPath = path4.join(worktreePath, ".swarm", "lanes", `${laneIndex}.env`);
997
+ let content;
998
+ try {
999
+ content = fsSync.readFileSync(envPath, "utf-8");
1000
+ } catch {
1001
+ return {};
1002
+ }
1003
+ const result = {};
1004
+ for (const rawLine of content.split(`
1005
+ `)) {
1006
+ const line = rawLine.trim();
1007
+ if (!line || line.startsWith("#"))
1008
+ continue;
1009
+ const eqIdx = line.indexOf("=");
1010
+ if (eqIdx < 0)
1011
+ continue;
1012
+ const k = line.slice(0, eqIdx);
1013
+ const v = line.slice(eqIdx + 1);
1014
+ if (!isValidEnvKey(k))
1015
+ continue;
1016
+ result[k] = v;
1017
+ }
1018
+ return result;
1019
+ }
1020
+ function gitExec(args, cwd, laneEnv, laneIndex) {
1021
+ const resolvedLaneEnv = laneEnv ?? (laneIndex !== undefined ? readLaneEnvFileFromDiskSync(cwd, laneIndex) : undefined);
176
1022
  let missingGitError;
177
1023
  const subcommand = args[0];
178
1024
  const hardenedArgs = subcommand === "commit" || subcommand === "tag" ? ["-c", "commit.gpgsign=false", "-c", "tag.gpgsign=false", ...args] : args;
179
1025
  for (const command of windowsGitCandidates()) {
180
1026
  for (let attempt = 0;attempt < MAX_TRANSIENT_RETRIES; attempt++) {
181
- const result = child_process.spawnSync(command, hardenedArgs, {
1027
+ const result = _internals5.spawnSync(command, hardenedArgs, {
182
1028
  cwd,
183
1029
  encoding: "utf-8",
184
1030
  timeout: GIT_TIMEOUT_MS,
185
1031
  windowsHide: true,
186
1032
  maxBuffer: GIT_MAX_BUFFER_BYTES,
187
1033
  stdio: ["ignore", "pipe", "pipe"],
188
- env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
1034
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
1035
+ envOverrides: resolvedLaneEnv
189
1036
  });
190
1037
  if (!result.error && result.status === 0) {
191
1038
  return result.stdout;
@@ -231,8 +1078,8 @@ function getGitRepositoryStatus(cwd) {
231
1078
  function isGitRepo(cwd) {
232
1079
  return getGitRepositoryStatus(cwd).isRepo;
233
1080
  }
234
- function getCurrentBranch(cwd) {
235
- const output = gitExec(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
1081
+ function getCurrentBranch(cwd, laneEnv, laneIndex) {
1082
+ const output = gitExec(["rev-parse", "--abbrev-ref", "HEAD"], cwd, laneEnv, laneIndex);
236
1083
  return output.trim();
237
1084
  }
238
1085
  function getDefaultBaseBranch(cwd) {
@@ -248,8 +1095,8 @@ function getDefaultBaseBranch(cwd) {
248
1095
  }
249
1096
  }
250
1097
  }
251
- function hasUncommittedChanges(cwd) {
252
- const status = gitExec(["status", "--porcelain"], cwd);
1098
+ function hasUncommittedChanges(cwd, laneEnv, laneIndex) {
1099
+ const status = gitExec(["status", "--porcelain"], cwd, laneEnv, laneIndex);
253
1100
  return status.trim().length > 0;
254
1101
  }
255
1102
  function detectDefaultRemoteBranch(cwd) {
@@ -283,7 +1130,7 @@ async function resetToRemoteBranch(cwd, options) {
283
1130
  const prunedBranches = [];
284
1131
  try {
285
1132
  const currentBranch = getCurrentBranch(cwd);
286
- const defaultRemoteBranch = _internals.detectDefaultRemoteBranch(cwd);
1133
+ const defaultRemoteBranch = _internals5.detectDefaultRemoteBranch(cwd);
287
1134
  if (!defaultRemoteBranch) {
288
1135
  return {
289
1136
  success: false,
@@ -465,7 +1312,7 @@ var GITIGNORED_BUILD_ARTIFACTS = ["dist"];
465
1312
  async function resetToMainAfterMerge(cwd, options) {
466
1313
  const warnings = [];
467
1314
  try {
468
- const defaultBranch = _internals.detectDefaultRemoteBranch(cwd);
1315
+ const defaultBranch = _internals5.detectDefaultRemoteBranch(cwd);
469
1316
  if (!defaultBranch) {
470
1317
  return {
471
1318
  success: false,
@@ -492,7 +1339,7 @@ async function resetToMainAfterMerge(cwd, options) {
492
1339
  }
493
1340
  if (currentBranch === defaultBranch) {
494
1341
  try {
495
- const logOutput = _internals.gitExec(["log", `${targetBranch}..HEAD`, "--oneline"], cwd);
1342
+ const logOutput = _internals5.gitExec(["log", `${targetBranch}..HEAD`, "--oneline"], cwd);
496
1343
  if (logOutput.trim().length > 0) {
497
1344
  return {
498
1345
  success: false,
@@ -507,11 +1354,11 @@ async function resetToMainAfterMerge(cwd, options) {
507
1354
  } catch {}
508
1355
  } else {
509
1356
  try {
510
- _internals.gitExec(["rev-parse", "--abbrev-ref", `${currentBranch}@{upstream}`], cwd);
1357
+ _internals5.gitExec(["rev-parse", "--abbrev-ref", `${currentBranch}@{upstream}`], cwd);
511
1358
  } catch {
512
1359
  try {
513
- const localSha = _internals.gitExec(["rev-parse", "HEAD"], cwd).trim();
514
- const remoteSha = _internals.gitExec(["rev-parse", targetBranch], cwd).trim();
1360
+ const localSha = _internals5.gitExec(["rev-parse", "HEAD"], cwd).trim();
1361
+ const remoteSha = _internals5.gitExec(["rev-parse", targetBranch], cwd).trim();
515
1362
  if (localSha !== remoteSha) {
516
1363
  return {
517
1364
  success: false,
@@ -537,7 +1384,7 @@ async function resetToMainAfterMerge(cwd, options) {
537
1384
  }
538
1385
  }
539
1386
  try {
540
- _internals.gitExec(["fetch", "--prune", "origin"], cwd);
1387
+ _internals5.gitExec(["fetch", "--prune", "origin"], cwd);
541
1388
  } catch (err) {
542
1389
  return {
543
1390
  success: false,
@@ -553,7 +1400,7 @@ async function resetToMainAfterMerge(cwd, options) {
553
1400
  let switchedBranch = false;
554
1401
  if (currentBranch !== defaultBranch) {
555
1402
  try {
556
- _internals.gitExec(["checkout", defaultBranch], cwd);
1403
+ _internals5.gitExec(["checkout", defaultBranch], cwd);
557
1404
  switchedBranch = true;
558
1405
  } catch (err) {
559
1406
  return {
@@ -568,7 +1415,7 @@ async function resetToMainAfterMerge(cwd, options) {
568
1415
  }
569
1416
  }
570
1417
  try {
571
- _internals.gitExec(["reset", "--hard", targetBranch], cwd);
1418
+ _internals5.gitExec(["reset", "--hard", targetBranch], cwd);
572
1419
  } catch (err) {
573
1420
  return {
574
1421
  success: false,
@@ -588,7 +1435,7 @@ async function resetToMainAfterMerge(cwd, options) {
588
1435
  await new Promise((resolve) => setTimeout(resolve, 500));
589
1436
  }
590
1437
  try {
591
- _internals.gitExec(["checkout", "--", "."], cwd);
1438
+ _internals5.gitExec(["checkout", "--", "."], cwd);
592
1439
  discardSucceeded = true;
593
1440
  break;
594
1441
  } catch {}
@@ -599,18 +1446,18 @@ async function resetToMainAfterMerge(cwd, options) {
599
1446
  changesDiscarded = discardSucceeded;
600
1447
  }
601
1448
  try {
602
- _internals.gitExec(["clean", "-fdX", "--", ...GITIGNORED_BUILD_ARTIFACTS], cwd);
1449
+ _internals5.gitExec(["clean", "-fdX", "--", ...GITIGNORED_BUILD_ARTIFACTS], cwd);
603
1450
  } catch {
604
1451
  warnings.push("Could not clean build artifacts");
605
1452
  }
606
1453
  let branchDeleted = false;
607
1454
  if (switchedBranch && previousBranch !== defaultBranch) {
608
1455
  try {
609
- const mergedOutput = _internals.gitExec(["branch", "--merged", defaultBranch], cwd);
1456
+ const mergedOutput = _internals5.gitExec(["branch", "--merged", defaultBranch], cwd);
610
1457
  const isMerged = mergedOutput.split(`
611
1458
  `).some((line) => line.trim() === previousBranch || line.trim() === `* ${previousBranch}`);
612
1459
  if (isMerged) {
613
- _internals.gitExec(["branch", "-d", previousBranch], cwd);
1460
+ _internals5.gitExec(["branch", "-d", previousBranch], cwd);
614
1461
  branchDeleted = true;
615
1462
  } else {
616
1463
  warnings.push(`Branch ${previousBranch} is not merged into ${defaultBranch} \u2014 keeping it`);
@@ -621,7 +1468,7 @@ async function resetToMainAfterMerge(cwd, options) {
621
1468
  }
622
1469
  if (options?.pruneBranches) {
623
1470
  try {
624
- const mergedOutput = _internals.gitExec(["branch", "--merged", defaultBranch], cwd);
1471
+ const mergedOutput = _internals5.gitExec(["branch", "--merged", defaultBranch], cwd);
625
1472
  const mergedLines = mergedOutput.split(`
626
1473
  `);
627
1474
  for (const line of mergedLines) {
@@ -630,7 +1477,7 @@ async function resetToMainAfterMerge(cwd, options) {
630
1477
  continue;
631
1478
  }
632
1479
  try {
633
- _internals.gitExec(["branch", "-d", trimmedLine], cwd);
1480
+ _internals5.gitExec(["branch", "-d", trimmedLine], cwd);
634
1481
  } catch {
635
1482
  warnings.push(`Could not prune branch: ${trimmedLine}`);
636
1483
  }
@@ -660,28 +1507,31 @@ async function resetToMainAfterMerge(cwd, options) {
660
1507
  };
661
1508
  }
662
1509
  }
663
- var _internals = {
1510
+ var _internals5 = {
664
1511
  gitExec,
665
1512
  detectDefaultRemoteBranch,
666
1513
  getDefaultBaseBranch,
667
1514
  getGitRepositoryStatus,
668
1515
  resetToRemoteBranch,
669
- resetToMainAfterMerge
1516
+ resetToMainAfterMerge,
1517
+ spawnSync: __spawnSyncSeam.spawnSync,
1518
+ readLaneEnvFileFromDiskSync
670
1519
  };
671
1520
 
672
1521
  // src/hooks/delegation-gate/worktree-merge-status.ts
673
- import * as fs from "fs";
1522
+ import * as fs2 from "fs";
1523
+ import * as path5 from "path";
674
1524
  var failuresByTask = new Map;
675
1525
  var durableStatusPath;
676
1526
  var hasLoaded = false;
677
1527
  function loadDurableStatus(statusPath) {
678
1528
  try {
679
1529
  hasLoaded = true;
680
- if (!fs.existsSync(statusPath)) {
1530
+ if (!fs2.existsSync(statusPath)) {
681
1531
  failuresByTask.clear();
682
1532
  return;
683
1533
  }
684
- const data = JSON.parse(fs.readFileSync(statusPath, "utf-8"));
1534
+ const data = JSON.parse(fs2.readFileSync(statusPath, "utf-8"));
685
1535
  if (data && typeof data === "object") {
686
1536
  failuresByTask.clear();
687
1537
  for (const [taskId, failure] of Object.entries(data)) {
@@ -692,25 +1542,39 @@ function loadDurableStatus(statusPath) {
692
1542
  failuresByTask.clear();
693
1543
  }
694
1544
  }
1545
+ function initDurableStatusPath(projectDir) {
1546
+ const nextPath = path5.join(projectDir, ".swarm", "worktree-merge-status.json");
1547
+ if (durableStatusPath === nextPath && hasLoaded)
1548
+ return;
1549
+ durableStatusPath = nextPath;
1550
+ hasLoaded = false;
1551
+ loadDurableStatus(durableStatusPath);
1552
+ }
695
1553
  function getWorktreeMergeFailure(taskId) {
696
1554
  if (!hasLoaded && durableStatusPath) {
697
1555
  loadDurableStatus(durableStatusPath);
698
1556
  }
699
1557
  return failuresByTask.get(taskId);
700
1558
  }
1559
+ function getAllWorktreeMergeFailures() {
1560
+ if (!hasLoaded && durableStatusPath) {
1561
+ loadDurableStatus(durableStatusPath);
1562
+ }
1563
+ return failuresByTask.entries();
1564
+ }
701
1565
 
702
1566
  // src/turbo/epic/state.ts
703
1567
  init_logger();
704
- import * as fs2 from "fs";
705
- import * as path2 from "path";
1568
+ import * as fs3 from "fs";
1569
+ import * as path6 from "path";
706
1570
  var STATE_FILE = "epic-state.json";
707
1571
  function nowISO() {
708
1572
  return new Date().toISOString();
709
1573
  }
710
1574
  function ensureSwarmDir(directory) {
711
- const swarmDir = path2.resolve(directory, ".swarm");
712
- if (!fs2.existsSync(swarmDir)) {
713
- fs2.mkdirSync(swarmDir, { recursive: true });
1575
+ const swarmDir = path6.resolve(directory, ".swarm");
1576
+ if (!fs3.existsSync(swarmDir)) {
1577
+ fs3.mkdirSync(swarmDir, { recursive: true });
714
1578
  }
715
1579
  return swarmDir;
716
1580
  }
@@ -730,17 +1594,17 @@ function markStateUnreadable(directory, reason) {
730
1594
  }
731
1595
  function readPersisted(directory) {
732
1596
  try {
733
- const filePath = path2.join(directory, ".swarm", STATE_FILE);
734
- if (!fs2.existsSync(filePath)) {
1597
+ const filePath = path6.join(directory, ".swarm", STATE_FILE);
1598
+ if (!fs3.existsSync(filePath)) {
735
1599
  const seed = emptyPersisted();
736
1600
  try {
737
1601
  ensureSwarmDir(directory);
738
- fs2.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
1602
+ fs3.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
739
1603
  `, "utf-8");
740
1604
  } catch {}
741
1605
  return seed;
742
1606
  }
743
- const raw = fs2.readFileSync(filePath, "utf-8");
1607
+ const raw = fs3.readFileSync(filePath, "utf-8");
744
1608
  const parsed = JSON.parse(raw);
745
1609
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
746
1610
  markStateUnreadable(directory, `malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
@@ -766,7 +1630,7 @@ function writePersisted(directory, persisted) {
766
1630
  let payload;
767
1631
  try {
768
1632
  ensureSwarmDir(directory);
769
- filePath = path2.join(directory, ".swarm", STATE_FILE);
1633
+ filePath = path6.join(directory, ".swarm", STATE_FILE);
770
1634
  tmpPath = `${filePath}.tmp.${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
771
1635
  persisted.updatedAt = nowISO();
772
1636
  payload = `${JSON.stringify(persisted, null, 2)}
@@ -777,14 +1641,14 @@ function writePersisted(directory, persisted) {
777
1641
  throw new Error(`Epic state persistence prepare failed: ${msg}`);
778
1642
  }
779
1643
  try {
780
- fs2.writeFileSync(tmpPath, payload, "utf-8");
781
- fs2.renameSync(tmpPath, filePath);
1644
+ fs3.writeFileSync(tmpPath, payload, "utf-8");
1645
+ fs3.renameSync(tmpPath, filePath);
782
1646
  } catch (error2) {
783
1647
  const msg = error2 instanceof Error ? error2.message : String(error2);
784
1648
  error(`[turbo/epic/state] Failed to persist ${STATE_FILE} atomically: ${msg}`);
785
1649
  try {
786
- if (fs2.existsSync(tmpPath))
787
- fs2.unlinkSync(tmpPath);
1650
+ if (fs3.existsSync(tmpPath))
1651
+ fs3.unlinkSync(tmpPath);
788
1652
  } catch {}
789
1653
  throw new Error(`Epic state persistence failed: ${msg}`);
790
1654
  }
@@ -815,7 +1679,7 @@ function isEpicModeActive(directory, sessionID) {
815
1679
  function isEpicModeActiveForProject(directory) {
816
1680
  if (stateUnreadableMap.get(directory))
817
1681
  return false;
818
- if (!fs2.existsSync(path2.join(directory, ".swarm", STATE_FILE))) {
1682
+ if (!fs3.existsSync(path6.join(directory, ".swarm", STATE_FILE))) {
819
1683
  return false;
820
1684
  }
821
1685
  const persisted = readPersisted(directory);
@@ -866,11 +1730,11 @@ function isLockContentionError(err) {
866
1730
  return INDEX_LOCK_ERROR_RE.test(msg);
867
1731
  }
868
1732
  async function commitTaskCompletion(directory, taskId, description, scopePaths) {
869
- if (!_internals2.isGitRepo(directory)) {
1733
+ if (!_internals6.isGitRepo(directory)) {
870
1734
  return { committed: false, reason: "no-git" };
871
1735
  }
872
1736
  try {
873
- if (_internals2.hasExistingTaskCommit(directory, taskId)) {
1737
+ if (_internals6.hasExistingTaskCommit(directory, taskId)) {
874
1738
  return { committed: true, reason: "idempotent-skip" };
875
1739
  }
876
1740
  } catch {}
@@ -891,15 +1755,15 @@ async function commitTaskCompletion(directory, taskId, description, scopePaths)
891
1755
  for (let attempt = 0;attempt <= INDEX_LOCK_BACKOFF_MS.length; attempt++) {
892
1756
  try {
893
1757
  if (paths.length > 0) {
894
- _internals2.stageScopedPaths(directory, paths);
1758
+ _internals6.stageScopedPaths(directory, paths);
895
1759
  }
896
- _internals2.commitAllowEmpty(directory, message);
897
- const sha = _internals2.gitHeadSha(directory);
1760
+ _internals6.commitAllowEmpty(directory, message);
1761
+ const sha = _internals6.gitHeadSha(directory);
898
1762
  return { committed: true, reason: "success", sha };
899
1763
  } catch (err) {
900
1764
  lastError = err;
901
1765
  if (attempt < INDEX_LOCK_BACKOFF_MS.length && isLockContentionError(err)) {
902
- await _internals2.sleep(INDEX_LOCK_BACKOFF_MS[attempt]);
1766
+ await _internals6.sleep(INDEX_LOCK_BACKOFF_MS[attempt]);
903
1767
  continue;
904
1768
  }
905
1769
  break;
@@ -909,26 +1773,26 @@ async function commitTaskCompletion(directory, taskId, description, scopePaths)
909
1773
  criticalWarn(`[epic:task-commit] commit for task ${taskId} failed (non-fatal): ${msg}`);
910
1774
  return { committed: false, reason: "commit-failed", error: msg };
911
1775
  }
912
- var _internals2 = {
1776
+ var _internals6 = {
913
1777
  isGitRepo: (cwd) => isGitRepo(cwd),
914
1778
  stageScopedPaths: (cwd, paths) => {
915
1779
  const CHUNK = 200;
916
1780
  for (let i = 0;i < paths.length; i += CHUNK) {
917
1781
  const chunk = paths.slice(i, i + CHUNK);
918
- _internals.gitExec(["add", "--", ...chunk, ":(exclude,glob)**/.swarm/**"], cwd);
1782
+ _internals5.gitExec(["add", "--", ...chunk, ":(exclude,glob)**/.swarm/**"], cwd);
919
1783
  }
920
1784
  },
921
1785
  commitAllowEmpty: (cwd, message) => {
922
- _internals.gitExec(["commit", "--allow-empty", "--no-verify", "-m", message], cwd);
1786
+ _internals5.gitExec(["commit", "--allow-empty", "--no-verify", "-m", message], cwd);
923
1787
  },
924
1788
  gitHeadSha: (cwd) => {
925
- return _internals.gitExec(["rev-parse", "HEAD"], cwd).trim();
1789
+ return _internals5.gitExec(["rev-parse", "HEAD"], cwd).trim();
926
1790
  },
927
1791
  sleep: (ms) => new Promise((resolve2) => setTimeout(resolve2, ms)),
928
1792
  hasExistingTaskCommit: (cwd, taskId) => {
929
1793
  const safeId = scrubTaskIdForGitSubject(taskId);
930
1794
  const escaped = safeId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
931
- const output = _internals.gitExec([
1795
+ const output = _internals5.gitExec([
932
1796
  "log",
933
1797
  "--extended-regexp",
934
1798
  `--grep=^swarm\\(task ${escaped}\\):`,
@@ -941,8 +1805,8 @@ var _internals2 = {
941
1805
  };
942
1806
 
943
1807
  // src/turbo/lean/conflicts.ts
944
- import * as fs3 from "fs";
945
- import * as path3 from "path";
1808
+ import * as fs4 from "fs";
1809
+ import * as path7 from "path";
946
1810
 
947
1811
  // src/utils/path.ts
948
1812
  function normalizePath(p) {
@@ -1070,12 +1934,12 @@ function isProtectedPath(normalizedPath) {
1070
1934
  return false;
1071
1935
  }
1072
1936
  function readTaskScopes(directory, taskId) {
1073
- const scopePath = path3.join(directory, ".swarm", "scopes", `scope-${taskId}.json`);
1937
+ const scopePath = path7.join(directory, ".swarm", "scopes", `scope-${taskId}.json`);
1074
1938
  try {
1075
- if (!fs3.existsSync(scopePath)) {
1939
+ if (!fs4.existsSync(scopePath)) {
1076
1940
  return null;
1077
1941
  }
1078
- const raw = fs3.readFileSync(scopePath, "utf-8");
1942
+ const raw = fs4.readFileSync(scopePath, "utf-8");
1079
1943
  const parsed = JSON.parse(raw);
1080
1944
  if (!parsed || !Array.isArray(parsed.files)) {
1081
1945
  return null;
@@ -1088,16 +1952,16 @@ function readTaskScopes(directory, taskId) {
1088
1952
 
1089
1953
  // src/utils/spec-hash.ts
1090
1954
  import { createHash } from "crypto";
1091
- import * as fsSync from "fs";
1092
- import { join as join3 } from "path";
1955
+ import * as fsSync2 from "fs";
1956
+ import { join as join6 } from "path";
1093
1957
  async function computeSpecHash(directory) {
1094
- const spec = _internals3.readEffectiveSpecSync(directory);
1958
+ const spec = _internals7.readEffectiveSpecSync(directory);
1095
1959
  if (!spec)
1096
1960
  return null;
1097
1961
  return createHash("sha256").update(spec.content, "utf-8").digest("hex");
1098
1962
  }
1099
1963
  async function isSpecStale(directory, plan) {
1100
- const currentHash = await _internals3.computeSpecHash(directory);
1964
+ const currentHash = await _internals7.computeSpecHash(directory);
1101
1965
  if (!plan.specHash) {
1102
1966
  return { stale: false };
1103
1967
  }
@@ -1118,15 +1982,15 @@ async function isSpecStale(directory, plan) {
1118
1982
  return { stale: false };
1119
1983
  }
1120
1984
  function isObligationPreserving(directory) {
1121
- const snapshotPath = join3(directory, ".swarm", "spec-snapshot.md");
1985
+ const snapshotPath = join6(directory, ".swarm", "spec-snapshot.md");
1122
1986
  try {
1123
1987
  let recordedContent;
1124
1988
  try {
1125
- recordedContent = fsSync.readFileSync(snapshotPath, "utf-8");
1989
+ recordedContent = fsSync2.readFileSync(snapshotPath, "utf-8");
1126
1990
  } catch {
1127
1991
  return false;
1128
1992
  }
1129
- const currentResult = _internals3.readEffectiveSpecSync(directory);
1993
+ const currentResult = _internals7.readEffectiveSpecSync(directory);
1130
1994
  if (!currentResult || !currentResult.content) {
1131
1995
  return false;
1132
1996
  }
@@ -1156,7 +2020,7 @@ function isObligationPreserving(directory) {
1156
2020
  return false;
1157
2021
  }
1158
2022
  }
1159
- var _internals3 = {
2023
+ var _internals7 = {
1160
2024
  computeSpecDiff,
1161
2025
  computeSpecHash,
1162
2026
  isObligationPreserving,
@@ -1164,19 +2028,19 @@ var _internals3 = {
1164
2028
  readEffectiveSpecSync
1165
2029
  };
1166
2030
  function computeSpecDiff(directory) {
1167
- const snapshotPath = join3(directory, ".swarm", "spec-snapshot.md");
2031
+ const snapshotPath = join6(directory, ".swarm", "spec-snapshot.md");
1168
2032
  let recordedContent;
1169
2033
  try {
1170
- const snapshotSize = fsSync.statSync(snapshotPath).size;
2034
+ const snapshotSize = fsSync2.statSync(snapshotPath).size;
1171
2035
  const MAX_SNAPSHOT_BYTES = 512 * 1024;
1172
2036
  if (snapshotSize > MAX_SNAPSHOT_BYTES) {
1173
2037
  return null;
1174
2038
  }
1175
- recordedContent = fsSync.readFileSync(snapshotPath, "utf-8");
2039
+ recordedContent = fsSync2.readFileSync(snapshotPath, "utf-8");
1176
2040
  } catch {
1177
2041
  return null;
1178
2042
  }
1179
- const spec = _internals3.readEffectiveSpecSync(directory);
2043
+ const spec = _internals7.readEffectiveSpecSync(directory);
1180
2044
  if (!spec) {
1181
2045
  return null;
1182
2046
  }
@@ -1250,9 +2114,9 @@ function computeLcsDiff(oldLines, newLines) {
1250
2114
  }
1251
2115
 
1252
2116
  // src/plan/ledger.ts
1253
- import * as crypto from "crypto";
1254
- import * as fs4 from "fs";
1255
- import * as path4 from "path";
2117
+ import * as crypto2 from "crypto";
2118
+ import * as fs5 from "fs";
2119
+ import * as path8 from "path";
1256
2120
 
1257
2121
  // src/plan/utils.ts
1258
2122
  function derivePlanId(plan) {
@@ -1262,7 +2126,7 @@ function derivePlanId(plan) {
1262
2126
  // src/plan/ledger.ts
1263
2127
  var LEDGER_SCHEMA_VERSION = "1.1.0";
1264
2128
  var LEDGER_FILENAME = "plan-ledger.jsonl";
1265
- var LEDGER_LOCK_PATH = path4.join(".swarm", LEDGER_FILENAME);
2129
+ var LEDGER_LOCK_PATH = path8.join(".swarm", LEDGER_FILENAME);
1266
2130
  var PLAN_JSON_FILENAME = "plan.json";
1267
2131
 
1268
2132
  class LedgerStaleWriterError extends Error {
@@ -1272,10 +2136,10 @@ class LedgerStaleWriterError extends Error {
1272
2136
  }
1273
2137
  }
1274
2138
  function getLedgerPath(directory) {
1275
- return path4.join(directory, ".swarm", LEDGER_FILENAME);
2139
+ return path8.join(directory, ".swarm", LEDGER_FILENAME);
1276
2140
  }
1277
2141
  function getPlanJsonPath(directory) {
1278
- return path4.join(directory, ".swarm", PLAN_JSON_FILENAME);
2142
+ return path8.join(directory, ".swarm", PLAN_JSON_FILENAME);
1279
2143
  }
1280
2144
  function computePlanHash(plan) {
1281
2145
  const normalized = {
@@ -1305,12 +2169,12 @@ function computePlanHash(plan) {
1305
2169
  }))
1306
2170
  };
1307
2171
  const jsonString = JSON.stringify(normalized);
1308
- return crypto.createHash("sha256").update(jsonString, "utf8").digest("hex");
2172
+ return crypto2.createHash("sha256").update(jsonString, "utf8").digest("hex");
1309
2173
  }
1310
2174
  function computeCurrentPlanHash(directory) {
1311
2175
  const planPath = getPlanJsonPath(directory);
1312
2176
  try {
1313
- const content = fs4.readFileSync(planPath, "utf8");
2177
+ const content = fs5.readFileSync(planPath, "utf8");
1314
2178
  const plan = JSON.parse(content);
1315
2179
  return computePlanHash(plan);
1316
2180
  } catch {
@@ -1319,15 +2183,15 @@ function computeCurrentPlanHash(directory) {
1319
2183
  }
1320
2184
  async function ledgerExists(directory) {
1321
2185
  const ledgerPath = getLedgerPath(directory);
1322
- return fs4.existsSync(ledgerPath);
2186
+ return fs5.existsSync(ledgerPath);
1323
2187
  }
1324
2188
  async function getLatestLedgerSeq(directory) {
1325
2189
  const ledgerPath = getLedgerPath(directory);
1326
- if (!fs4.existsSync(ledgerPath)) {
2190
+ if (!fs5.existsSync(ledgerPath)) {
1327
2191
  return 0;
1328
2192
  }
1329
2193
  try {
1330
- const content = fs4.readFileSync(ledgerPath, "utf8");
2194
+ const content = fs5.readFileSync(ledgerPath, "utf8");
1331
2195
  const lines = content.trim().split(`
1332
2196
  `).filter((line) => line.trim() !== "");
1333
2197
  if (lines.length === 0) {
@@ -1349,11 +2213,11 @@ async function getLatestLedgerSeq(directory) {
1349
2213
  }
1350
2214
  async function readLedgerEvents(directory) {
1351
2215
  const ledgerPath = getLedgerPath(directory);
1352
- if (!fs4.existsSync(ledgerPath)) {
2216
+ if (!fs5.existsSync(ledgerPath)) {
1353
2217
  return [];
1354
2218
  }
1355
2219
  try {
1356
- const content = fs4.readFileSync(ledgerPath, "utf8");
2220
+ const content = fs5.readFileSync(ledgerPath, "utf8");
1357
2221
  const lines = content.trim().split(`
1358
2222
  `).filter((line) => line.trim() !== "");
1359
2223
  const events = [];
@@ -1378,15 +2242,15 @@ async function readLedgerEvents(directory) {
1378
2242
  async function initLedger(directory, planId, initialPlanHash, initialPlan) {
1379
2243
  const ledgerPath = getLedgerPath(directory);
1380
2244
  const planJsonPath = getPlanJsonPath(directory);
1381
- if (fs4.existsSync(ledgerPath)) {
2245
+ if (fs5.existsSync(ledgerPath)) {
1382
2246
  throw new Error("Ledger already initialized. Use appendLedgerEvent to add events.");
1383
2247
  }
1384
2248
  let planHashAfter = initialPlanHash ?? "";
1385
2249
  let embeddedPlan = initialPlan;
1386
2250
  if (!initialPlanHash) {
1387
2251
  try {
1388
- if (fs4.existsSync(planJsonPath)) {
1389
- const content = fs4.readFileSync(planJsonPath, "utf8");
2252
+ if (fs5.existsSync(planJsonPath)) {
2253
+ const content = fs5.readFileSync(planJsonPath, "utf8");
1390
2254
  const plan = JSON.parse(content);
1391
2255
  planHashAfter = computePlanHash(plan);
1392
2256
  if (!embeddedPlan)
@@ -1406,12 +2270,12 @@ async function initLedger(directory, planId, initialPlanHash, initialPlan) {
1406
2270
  schema_version: LEDGER_SCHEMA_VERSION,
1407
2271
  ...payload ? { payload } : {}
1408
2272
  };
1409
- fs4.mkdirSync(path4.join(directory, ".swarm"), { recursive: true });
2273
+ fs5.mkdirSync(path8.join(directory, ".swarm"), { recursive: true });
1410
2274
  const tempPath = `${ledgerPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
1411
2275
  const line = `${JSON.stringify(event)}
1412
2276
  `;
1413
- fs4.writeFileSync(tempPath, line, "utf8");
1414
- fs4.renameSync(tempPath, ledgerPath);
2277
+ fs5.writeFileSync(tempPath, line, "utf8");
2278
+ fs5.renameSync(tempPath, ledgerPath);
1415
2279
  }
1416
2280
  async function appendLedgerEvent(directory, eventInput, options) {
1417
2281
  return withEvidenceLock(directory, LEDGER_LOCK_PATH, "plan-ledger", "append-ledger-event", async () => {
@@ -1434,17 +2298,17 @@ async function appendLedgerEvent(directory, eventInput, options) {
1434
2298
  plan_hash_after: planHashAfter,
1435
2299
  schema_version: LEDGER_SCHEMA_VERSION
1436
2300
  };
1437
- fs4.mkdirSync(path4.join(directory, ".swarm"), { recursive: true });
2301
+ fs5.mkdirSync(path8.join(directory, ".swarm"), { recursive: true });
1438
2302
  const tempPath = `${ledgerPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
1439
2303
  const line = `${JSON.stringify(event)}
1440
2304
  `;
1441
- if (fs4.existsSync(ledgerPath)) {
1442
- const existingContent = fs4.readFileSync(ledgerPath, "utf8");
1443
- fs4.writeFileSync(tempPath, existingContent + line, "utf8");
2305
+ if (fs5.existsSync(ledgerPath)) {
2306
+ const existingContent = fs5.readFileSync(ledgerPath, "utf8");
2307
+ fs5.writeFileSync(tempPath, existingContent + line, "utf8");
1444
2308
  } else {
1445
2309
  throw new Error("Ledger not initialized. Call initLedger() first.");
1446
2310
  }
1447
- fs4.renameSync(tempPath, ledgerPath);
2311
+ fs5.renameSync(tempPath, ledgerPath);
1448
2312
  return event;
1449
2313
  });
1450
2314
  }
@@ -1532,12 +2396,12 @@ async function replayFromLedger(directory, _options) {
1532
2396
  }
1533
2397
  }
1534
2398
  const planJsonPath = getPlanJsonPath(directory);
1535
- if (!fs4.existsSync(planJsonPath)) {
2399
+ if (!fs5.existsSync(planJsonPath)) {
1536
2400
  return null;
1537
2401
  }
1538
2402
  let plan;
1539
2403
  try {
1540
- const content = fs4.readFileSync(planJsonPath, "utf8");
2404
+ const content = fs5.readFileSync(planJsonPath, "utf8");
1541
2405
  plan = JSON.parse(content);
1542
2406
  } catch {
1543
2407
  return null;
@@ -1632,11 +2496,11 @@ function applyEventToPlan(plan, event) {
1632
2496
  }
1633
2497
  async function readLedgerEventsWithIntegrity(directory) {
1634
2498
  const ledgerPath = getLedgerPath(directory);
1635
- if (!fs4.existsSync(ledgerPath)) {
2499
+ if (!fs5.existsSync(ledgerPath)) {
1636
2500
  return { events: [], truncated: false, badSuffix: null };
1637
2501
  }
1638
2502
  try {
1639
- const content = fs4.readFileSync(ledgerPath, "utf8");
2503
+ const content = fs5.readFileSync(ledgerPath, "utf8");
1640
2504
  const lines = content.split(`
1641
2505
  `);
1642
2506
  const events = [];
@@ -1665,9 +2529,9 @@ async function readLedgerEventsWithIntegrity(directory) {
1665
2529
  }
1666
2530
  async function quarantineLedgerSuffix(directory, badSuffix) {
1667
2531
  try {
1668
- const quarantinePath = path4.join(directory, ".swarm", "plan-ledger.quarantine");
1669
- fs4.writeFileSync(quarantinePath, badSuffix, "utf8");
1670
- console.warn(`[ledger] Corrupted suffix quarantined to ${path4.relative(directory, quarantinePath)}`);
2532
+ const quarantinePath = path8.join(directory, ".swarm", "plan-ledger.quarantine");
2533
+ fs5.writeFileSync(quarantinePath, badSuffix, "utf8");
2534
+ console.warn(`[ledger] Corrupted suffix quarantined to ${path8.relative(directory, quarantinePath)}`);
1671
2535
  } catch {}
1672
2536
  }
1673
2537
  async function loadLastApprovedPlan(directory, expectedPlanId) {
@@ -1726,7 +2590,7 @@ var startupLedgerCheckedWorkspaces = new Set;
1726
2590
  var ledgerStaleWorkspaces = new Set;
1727
2591
  var recoveryMutexes = new Map;
1728
2592
  var PLAN_JSON_CACHE_NAMESPACE = "plan-json:validated:v1";
1729
- var _internals4 = {
2593
+ var _internals8 = {
1730
2594
  loadPlan,
1731
2595
  loadPlanJsonOnly,
1732
2596
  regeneratePlanMarkdown,
@@ -1866,7 +2730,7 @@ async function getLatestLedgerHash(directory) {
1866
2730
  }
1867
2731
  }
1868
2732
  async function surfaceLedgerStaleIfPersisted(directory, plan) {
1869
- const resolvedWorkspace = path5.resolve(directory);
2733
+ const resolvedWorkspace = path9.resolve(directory);
1870
2734
  if (!ledgerStaleWorkspaces.has(resolvedWorkspace)) {
1871
2735
  return plan;
1872
2736
  }
@@ -1885,7 +2749,7 @@ async function surfaceLedgerStaleIfPersisted(directory, plan) {
1885
2749
  return plan;
1886
2750
  }
1887
2751
  async function parsePlanJsonCached(directory) {
1888
- const planJsonPath = path5.resolve(directory, ".swarm", "plan.json");
2752
+ const planJsonPath = path9.resolve(directory, ".swarm", "plan.json");
1889
2753
  return readCachedParsedFile(planJsonPath, PLAN_JSON_CACHE_NAMESPACE, () => readSwarmFileAsync(directory, "plan.json"), (planJsonContent) => {
1890
2754
  if (planJsonContent.includes("\x00") || planJsonContent.includes("\uFFFD")) {
1891
2755
  throw new Error("Plan rejected: .swarm/plan.json contains null bytes or invalid encoding");
@@ -1945,13 +2809,13 @@ async function isPlanMdInSync(directory, plan, cache) {
1945
2809
  return false;
1946
2810
  }
1947
2811
  async function regeneratePlanMarkdown(directory, plan) {
1948
- const swarmDir = path5.resolve(directory, ".swarm");
2812
+ const swarmDir = path9.resolve(directory, ".swarm");
1949
2813
  const contentHash = computePlanContentHash(plan);
1950
2814
  const markdown = derivePlanMarkdown(plan);
1951
2815
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
1952
2816
  ${markdown}`;
1953
- const mdPath = path5.join(swarmDir, "plan.md");
1954
- const mdTempPath = path5.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2817
+ const mdPath = path9.join(swarmDir, "plan.md");
2818
+ const mdTempPath = path9.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
1955
2819
  try {
1956
2820
  await bunWrite(mdTempPath, markdownWithHash);
1957
2821
  renameSync4(mdTempPath, mdPath);
@@ -1975,7 +2839,7 @@ async function loadPlan(directory, cache) {
1975
2839
  const inSync = await isPlanMdInSync(directory, validated, cache);
1976
2840
  if (!inSync) {
1977
2841
  try {
1978
- await _internals4.regeneratePlanMarkdown(directory, validated);
2842
+ await _internals8.regeneratePlanMarkdown(directory, validated);
1979
2843
  } catch (regenError) {
1980
2844
  warn(`Failed to regenerate plan.md: ${regenError instanceof Error ? regenError.message : String(regenError)}. Proceeding with plan.json only.`);
1981
2845
  }
@@ -1983,7 +2847,7 @@ async function loadPlan(directory, cache) {
1983
2847
  if (await ledgerExists(directory)) {
1984
2848
  const planHash = computePlanHash(validated);
1985
2849
  const ledgerHash = await getLatestLedgerHash(directory);
1986
- const resolvedWorkspace = path5.resolve(directory);
2850
+ const resolvedWorkspace = path9.resolve(directory);
1987
2851
  if (!startupLedgerCheckedWorkspaces.has(resolvedWorkspace)) {
1988
2852
  startupLedgerCheckedWorkspaces.add(resolvedWorkspace);
1989
2853
  if (ledgerHash !== "" && planHash !== ledgerHash) {
@@ -2049,7 +2913,7 @@ async function loadPlan(directory, cache) {
2049
2913
  if (!preserving) {
2050
2914
  try {
2051
2915
  const diffInfo = computeSpecDiff(directory);
2052
- const specStalenessPath = path5.join(directory, ".swarm", "spec-staleness.json");
2916
+ const specStalenessPath = path9.join(directory, ".swarm", "spec-staleness.json");
2053
2917
  await fsPromises.writeFile(specStalenessPath, JSON.stringify({
2054
2918
  type: "spec_stale_detected",
2055
2919
  timestamp: new Date().toISOString(),
@@ -2063,7 +2927,7 @@ async function loadPlan(directory, cache) {
2063
2927
  }, null, 2), "utf-8");
2064
2928
  } catch {}
2065
2929
  try {
2066
- const eventsPath = path5.join(directory, ".swarm", "events.jsonl");
2930
+ const eventsPath = path9.join(directory, ".swarm", "events.jsonl");
2067
2931
  const event = {
2068
2932
  type: "spec_stale_detected",
2069
2933
  timestamp: new Date().toISOString(),
@@ -2129,11 +2993,11 @@ async function loadPlan(directory, cache) {
2129
2993
  return migrated;
2130
2994
  }
2131
2995
  if (await ledgerExists(directory)) {
2132
- const resolvedDir = path5.resolve(directory);
2996
+ const resolvedDir = path9.resolve(directory);
2133
2997
  const existingMutex = recoveryMutexes.get(resolvedDir);
2134
2998
  if (existingMutex) {
2135
2999
  await existingMutex;
2136
- const postRecoveryPlan = await _internals4.loadPlanJsonOnly(directory);
3000
+ const postRecoveryPlan = await _internals8.loadPlanJsonOnly(directory);
2137
3001
  if (postRecoveryPlan)
2138
3002
  return postRecoveryPlan;
2139
3003
  }
@@ -2193,7 +3057,7 @@ async function loadPlan(directory, cache) {
2193
3057
  return null;
2194
3058
  }
2195
3059
  async function savePlanWithAutoAcknowledgedRemovals(directory, plan, source, reason, options) {
2196
- const existing = await _internals4.loadPlanJsonOnly(directory);
3060
+ const existing = await _internals8.loadPlanJsonOnly(directory);
2197
3061
  const newIds = new Set;
2198
3062
  for (const phase of plan.phases) {
2199
3063
  for (const task of phase.tasks)
@@ -2221,7 +3085,7 @@ async function savePlan(directory, plan, options) {
2221
3085
  const validated = PlanSchema.parse(plan);
2222
3086
  if (options?.preserveCompletedStatuses !== false) {
2223
3087
  try {
2224
- const currentPlan2 = await _internals4.loadPlanJsonOnly(directory);
3088
+ const currentPlan2 = await _internals8.loadPlanJsonOnly(directory);
2225
3089
  if (currentPlan2) {
2226
3090
  const completedTaskIds = new Set;
2227
3091
  for (const phase of currentPlan2.phases) {
@@ -2243,7 +3107,7 @@ async function savePlan(directory, plan, options) {
2243
3107
  } catch {}
2244
3108
  }
2245
3109
  derivePhaseStatusesInPlace(validated);
2246
- const currentPlan = await _internals4.loadPlanJsonOnly(directory);
3110
+ const currentPlan = await _internals8.loadPlanJsonOnly(directory);
2247
3111
  const planId = derivePlanId(validated);
2248
3112
  const planHashForInit = computePlanHash(validated);
2249
3113
  if (!await ledgerExists(directory)) {
@@ -2258,9 +3122,9 @@ async function savePlan(directory, plan, options) {
2258
3122
  } else {
2259
3123
  const existingEvents = await readLedgerEvents(directory);
2260
3124
  if (existingEvents.length > 0 && existingEvents[0].plan_id !== planId) {
2261
- const swarmDir2 = path5.resolve(directory, ".swarm");
2262
- const oldLedgerPath = path5.join(swarmDir2, "plan-ledger.jsonl");
2263
- const oldLedgerBackupPath = path5.join(swarmDir2, `plan-ledger.backup-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
3125
+ const swarmDir2 = path9.resolve(directory, ".swarm");
3126
+ const oldLedgerPath = path9.join(swarmDir2, "plan-ledger.jsonl");
3127
+ const oldLedgerBackupPath = path9.join(swarmDir2, `plan-ledger.backup-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
2264
3128
  let backupExists = false;
2265
3129
  if (existsSync5(oldLedgerPath)) {
2266
3130
  try {
@@ -2298,7 +3162,7 @@ async function savePlan(directory, plan, options) {
2298
3162
  }
2299
3163
  }
2300
3164
  if (initSucceeded && backupExists) {
2301
- const archivePath = path5.join(swarmDir2, `plan-ledger.archived-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
3165
+ const archivePath = path9.join(swarmDir2, `plan-ledger.archived-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
2302
3166
  try {
2303
3167
  renameSync4(oldLedgerBackupPath, archivePath);
2304
3168
  warn(`[savePlan] Ledger identity mismatch (was "${existingEvents[0].plan_id}", now "${planId}") \u2014 archived old ledger to ${archivePath} and reinitializing.`);
@@ -2323,7 +3187,7 @@ async function savePlan(directory, plan, options) {
2323
3187
  const toRemove = archivedSiblings.slice(0, archivedSiblings.length - MAX_ARCHIVED_SIBLINGS);
2324
3188
  for (const file of toRemove) {
2325
3189
  try {
2326
- unlinkSync3(path5.join(swarmDir2, file));
3190
+ unlinkSync3(path9.join(swarmDir2, file));
2327
3191
  } catch {}
2328
3192
  }
2329
3193
  }
@@ -2388,7 +3252,7 @@ async function savePlan(directory, plan, options) {
2388
3252
  expectedHash: currentHash,
2389
3253
  planHashAfter: hashAfter,
2390
3254
  verifyValid: async () => {
2391
- const onDisk = await _internals4.loadPlanJsonOnly(directory);
3255
+ const onDisk = await _internals8.loadPlanJsonOnly(directory);
2392
3256
  if (!onDisk)
2393
3257
  return true;
2394
3258
  for (const p of onDisk.phases) {
@@ -2426,7 +3290,7 @@ async function savePlan(directory, plan, options) {
2426
3290
  expectedHash: currentHash,
2427
3291
  planHashAfter: hashAfter,
2428
3292
  verifyValid: async () => {
2429
- const onDisk = await _internals4.loadPlanJsonOnly(directory);
3293
+ const onDisk = await _internals8.loadPlanJsonOnly(directory);
2430
3294
  if (!onDisk)
2431
3295
  return true;
2432
3296
  for (const p of onDisk.phases) {
@@ -2466,9 +3330,9 @@ async function savePlan(directory, plan, options) {
2466
3330
  source: "savePlan_manager"
2467
3331
  });
2468
3332
  }
2469
- const swarmDir = path5.resolve(directory, ".swarm");
2470
- const planPath = path5.join(swarmDir, "plan.json");
2471
- const tempPath = path5.join(swarmDir, `plan.json.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3333
+ const swarmDir = path9.resolve(directory, ".swarm");
3334
+ const planPath = path9.join(swarmDir, "plan.json");
3335
+ const tempPath = path9.join(swarmDir, `plan.json.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2472
3336
  try {
2473
3337
  await bunWrite(tempPath, JSON.stringify(projectedPlan, null, 2));
2474
3338
  renameSync4(tempPath, planPath);
@@ -2478,7 +3342,7 @@ async function savePlan(directory, plan, options) {
2478
3342
  } catch {}
2479
3343
  }
2480
3344
  try {
2481
- const markerPath = path5.join(swarmDir, ".plan-write-marker");
3345
+ const markerPath = path9.join(swarmDir, ".plan-write-marker");
2482
3346
  const inProgressMarker = JSON.stringify({
2483
3347
  source: "plan_manager",
2484
3348
  timestamp: new Date().toISOString(),
@@ -2493,8 +3357,8 @@ async function savePlan(directory, plan, options) {
2493
3357
  const markdown = derivePlanMarkdown(projectedPlan);
2494
3358
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
2495
3359
  ${markdown}`;
2496
- const mdPath = path5.join(swarmDir, "plan.md");
2497
- const mdTempPath = path5.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3360
+ const mdPath = path9.join(swarmDir, "plan.md");
3361
+ const mdTempPath = path9.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2498
3362
  try {
2499
3363
  await bunWrite(mdTempPath, markdownWithHash);
2500
3364
  renameSync4(mdTempPath, mdPath);
@@ -2515,7 +3379,7 @@ ${markdown}`;
2515
3379
  } catch {}
2516
3380
  }
2517
3381
  try {
2518
- const markerPath = path5.join(swarmDir, ".plan-write-marker");
3382
+ const markerPath = path9.join(swarmDir, ".plan-write-marker");
2519
3383
  const tasksCount = projectedPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
2520
3384
  const marker = JSON.stringify({
2521
3385
  source: "plan_manager",
@@ -2531,14 +3395,14 @@ async function rebuildPlan(directory, plan, options) {
2531
3395
  const targetPlan = plan ?? await replayFromLedger(directory);
2532
3396
  if (!targetPlan)
2533
3397
  return null;
2534
- const swarmDir = path5.join(directory, ".swarm");
2535
- const planPath = path5.join(swarmDir, "plan.json");
2536
- const mdPath = path5.join(swarmDir, "plan.md");
2537
- const tempPlanPath = path5.join(swarmDir, `plan.json.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3398
+ const swarmDir = path9.join(directory, ".swarm");
3399
+ const planPath = path9.join(swarmDir, "plan.json");
3400
+ const mdPath = path9.join(swarmDir, "plan.md");
3401
+ const tempPlanPath = path9.join(swarmDir, `plan.json.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2538
3402
  await bunWrite(tempPlanPath, JSON.stringify(targetPlan, null, 2));
2539
3403
  renameSync4(tempPlanPath, planPath);
2540
3404
  try {
2541
- const markerPath = path5.join(swarmDir, ".plan-write-marker");
3405
+ const markerPath = path9.join(swarmDir, ".plan-write-marker");
2542
3406
  const inProgressMarker = JSON.stringify({
2543
3407
  source: "plan_manager",
2544
3408
  timestamp: new Date().toISOString(),
@@ -2553,12 +3417,12 @@ async function rebuildPlan(directory, plan, options) {
2553
3417
  const markdown = derivePlanMarkdown(targetPlan);
2554
3418
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
2555
3419
  ${markdown}`;
2556
- const tempMdPath = path5.join(swarmDir, `plan.md.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3420
+ const tempMdPath = path9.join(swarmDir, `plan.md.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2557
3421
  await bunWrite(tempMdPath, markdownWithHash);
2558
3422
  renameSync4(tempMdPath, mdPath);
2559
3423
  } finally {
2560
3424
  try {
2561
- const markerPath = path5.join(swarmDir, ".plan-write-marker");
3425
+ const markerPath = path9.join(swarmDir, ".plan-write-marker");
2562
3426
  const tasksCount = targetPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
2563
3427
  const marker = JSON.stringify({
2564
3428
  source: "plan_manager",
@@ -2621,13 +3485,13 @@ async function closePlanTerminalState(directory, plan, options) {
2621
3485
  planHashAfter: hashAfter,
2622
3486
  source: "close_terminal"
2623
3487
  });
2624
- const swarmDir = path5.join(directory, ".swarm");
2625
- const planPath = path5.join(swarmDir, "plan.json");
2626
- const tempPlanPath = path5.join(swarmDir, `plan.json.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3488
+ const swarmDir = path9.join(directory, ".swarm");
3489
+ const planPath = path9.join(swarmDir, "plan.json");
3490
+ const tempPlanPath = path9.join(swarmDir, `plan.json.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2627
3491
  await bunWrite(tempPlanPath, JSON.stringify(validated, null, 2));
2628
3492
  renameSync4(tempPlanPath, planPath);
2629
3493
  try {
2630
- const markerPath = path5.join(swarmDir, ".plan-write-marker");
3494
+ const markerPath = path9.join(swarmDir, ".plan-write-marker");
2631
3495
  const inProgressMarker = JSON.stringify({
2632
3496
  source: "plan_manager_close",
2633
3497
  timestamp: new Date().toISOString(),
@@ -2638,17 +3502,17 @@ async function closePlanTerminalState(directory, plan, options) {
2638
3502
  await bunWrite(markerPath, inProgressMarker);
2639
3503
  } catch {}
2640
3504
  try {
2641
- const mdPath = path5.join(swarmDir, "plan.md");
3505
+ const mdPath = path9.join(swarmDir, "plan.md");
2642
3506
  const contentHash = computePlanContentHash(validated);
2643
3507
  const markdown = derivePlanMarkdown(validated);
2644
3508
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
2645
3509
  ${markdown}`;
2646
- const mdTempPath = path5.join(swarmDir, `plan.md.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3510
+ const mdTempPath = path9.join(swarmDir, `plan.md.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2647
3511
  await bunWrite(mdTempPath, markdownWithHash);
2648
3512
  renameSync4(mdTempPath, mdPath);
2649
3513
  } finally {
2650
3514
  try {
2651
- const markerPath = path5.join(swarmDir, ".plan-write-marker");
3515
+ const markerPath = path9.join(swarmDir, ".plan-write-marker");
2652
3516
  const tasksCount = validated.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
2653
3517
  const marker = JSON.stringify({
2654
3518
  source: "plan_manager_close",
@@ -2932,15 +3796,15 @@ function migrateLegacyPlan(planContent, swarmId) {
2932
3796
 
2933
3797
  // src/evidence/manager.ts
2934
3798
  import {
2935
- mkdirSync as mkdirSync4,
3799
+ mkdirSync as mkdirSync5,
2936
3800
  readdirSync as readdirSync2,
2937
3801
  realpathSync,
2938
3802
  rmSync,
2939
3803
  statSync as statSync2
2940
3804
  } from "fs";
2941
- import * as fs5 from "fs/promises";
2942
- import * as os from "os";
2943
- import * as path6 from "path";
3805
+ import * as fs6 from "fs/promises";
3806
+ import * as os4 from "os";
3807
+ import * as path10 from "path";
2944
3808
 
2945
3809
  // src/config/evidence-schema.ts
2946
3810
  var EVIDENCE_MAX_JSON_BYTES = 500 * 1024;
@@ -3261,10 +4125,10 @@ var PROJECT_INDICATORS = [
3261
4125
  function isWeakConfigContainerRoot(directory) {
3262
4126
  try {
3263
4127
  const resolved = realpathSync(directory);
3264
- return resolved === realpathSync(os.tmpdir()) || resolved === realpathSync(os.homedir());
4128
+ return resolved === realpathSync(os4.tmpdir()) || resolved === realpathSync(os4.homedir());
3265
4129
  } catch {
3266
- const resolved = path6.resolve(directory);
3267
- return resolved === path6.resolve(os.tmpdir()) || resolved === path6.resolve(os.homedir());
4130
+ const resolved = path10.resolve(directory);
4131
+ return resolved === path10.resolve(os4.tmpdir()) || resolved === path10.resolve(os4.homedir());
3268
4132
  }
3269
4133
  }
3270
4134
  function hasAccessibleProjectIndicator(directory, options = {}) {
@@ -3274,7 +4138,7 @@ function hasAccessibleProjectIndicator(directory, options = {}) {
3274
4138
  continue;
3275
4139
  }
3276
4140
  try {
3277
- const indicatorStat = statSync2(path6.join(directory, indicator));
4141
+ const indicatorStat = statSync2(path10.join(directory, indicator));
3278
4142
  if (indicatorStat.isFile() || indicatorStat.isDirectory()) {
3279
4143
  return true;
3280
4144
  }
@@ -3296,14 +4160,14 @@ function validateProjectRoot(directory) {
3296
4160
  if (depth >= MAX_DEPTH)
3297
4161
  break;
3298
4162
  depth++;
3299
- const parent = path6.dirname(current);
4163
+ const parent = path10.dirname(current);
3300
4164
  if (parent === current)
3301
4165
  break;
3302
- if (path6.dirname(parent) === parent) {
4166
+ if (path10.dirname(parent) === parent) {
3303
4167
  current = parent;
3304
4168
  continue;
3305
4169
  }
3306
- const parentSwarm = path6.join(parent, ".swarm");
4170
+ const parentSwarm = path10.join(parent, ".swarm");
3307
4171
  try {
3308
4172
  if (statSync2(parentSwarm).isDirectory()) {
3309
4173
  if (hasAccessibleProjectIndicator(parent, {
@@ -3322,14 +4186,14 @@ function validateProjectRoot(directory) {
3322
4186
  }
3323
4187
  }
3324
4188
  async function saveEvidence(directory, taskId, evidence) {
3325
- _internals5.validateProjectRoot(directory);
4189
+ _internals9.validateProjectRoot(directory);
3326
4190
  const sanitizedTaskId = sanitizeTaskId2(taskId);
3327
- const relativePath = path6.join("evidence", sanitizedTaskId, "evidence.json");
4191
+ const relativePath = path10.join("evidence", sanitizedTaskId, "evidence.json");
3328
4192
  validateSwarmPath(directory, relativePath);
3329
- _internals5.validateEvidence(evidence);
4193
+ _internals9.validateEvidence(evidence);
3330
4194
  return withEvidenceLock(directory, relativePath, "evidence-manager", sanitizedTaskId, async () => {
3331
4195
  const evidencePath = validateSwarmPath(directory, relativePath);
3332
- const evidenceDir = path6.dirname(evidencePath);
4196
+ const evidenceDir = path10.dirname(evidencePath);
3333
4197
  let bundle;
3334
4198
  const existingContent = await readSwarmFileAsync(directory, relativePath);
3335
4199
  if (existingContent !== null) {
@@ -3371,11 +4235,11 @@ async function saveEvidence(directory, taskId, evidence) {
3371
4235
  if (bundleJson.length > EVIDENCE_MAX_JSON_BYTES) {
3372
4236
  throw new Error(`Evidence bundle size (${bundleJson.length} bytes) exceeds maximum (${EVIDENCE_MAX_JSON_BYTES} bytes)`);
3373
4237
  }
3374
- mkdirSync4(evidenceDir, { recursive: true });
3375
- const tempPath = path6.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
4238
+ mkdirSync5(evidenceDir, { recursive: true });
4239
+ const tempPath = path10.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
3376
4240
  try {
3377
4241
  await bunWrite(tempPath, bundleJson);
3378
- await fs5.rename(tempPath, evidencePath);
4242
+ await fs6.rename(tempPath, evidencePath);
3379
4243
  } catch (error2) {
3380
4244
  try {
3381
4245
  rmSync(tempPath, { force: true });
@@ -3416,7 +4280,7 @@ function wrapFlatRetrospective(flatEntry, taskId) {
3416
4280
  }
3417
4281
  async function loadEvidence(directory, taskId) {
3418
4282
  const sanitizedTaskId = sanitizeTaskId2(taskId);
3419
- const relativePath = path6.join("evidence", sanitizedTaskId, "evidence.json");
4283
+ const relativePath = path10.join("evidence", sanitizedTaskId, "evidence.json");
3420
4284
  if (relativePath.length > 4096) {
3421
4285
  return { status: "not_found" };
3422
4286
  }
@@ -3432,17 +4296,17 @@ async function loadEvidence(directory, taskId) {
3432
4296
  return { status: "invalid_schema", errors: ["Invalid JSON"] };
3433
4297
  }
3434
4298
  if (isFlatRetrospective(parsed)) {
3435
- const wrappedBundle = _internals5.wrapFlatRetrospective(parsed, sanitizedTaskId);
4299
+ const wrappedBundle = _internals9.wrapFlatRetrospective(parsed, sanitizedTaskId);
3436
4300
  try {
3437
4301
  const validated = EvidenceBundleSchema.parse(wrappedBundle);
3438
4302
  try {
3439
4303
  await withEvidenceLock(directory, relativePath, "evidence-loader", sanitizedTaskId, async () => {
3440
- const evidenceDir = path6.dirname(evidencePath);
4304
+ const evidenceDir = path10.dirname(evidencePath);
3441
4305
  const bundleJson = JSON.stringify(validated);
3442
- const tempPath = path6.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
4306
+ const tempPath = path10.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
3443
4307
  try {
3444
4308
  await bunWrite(tempPath, bundleJson);
3445
- await fs5.rename(tempPath, evidencePath);
4309
+ await fs6.rename(tempPath, evidencePath);
3446
4310
  } catch (writeError) {
3447
4311
  try {
3448
4312
  rmSync(tempPath, { force: true });
@@ -3484,7 +4348,7 @@ async function listEvidenceTaskIds(directory) {
3484
4348
  }
3485
4349
  const taskIds = [];
3486
4350
  for (const entry of entries) {
3487
- const entryPath = path6.join(evidenceBasePath, entry);
4351
+ const entryPath = path10.join(evidenceBasePath, entry);
3488
4352
  try {
3489
4353
  const stats = statSync2(entryPath);
3490
4354
  if (!stats.isDirectory()) {
@@ -3502,7 +4366,7 @@ async function listEvidenceTaskIds(directory) {
3502
4366
  }
3503
4367
  async function deleteEvidence(directory, taskId) {
3504
4368
  const sanitizedTaskId = sanitizeTaskId2(taskId);
3505
- const relativePath = path6.join("evidence", sanitizedTaskId);
4369
+ const relativePath = path10.join("evidence", sanitizedTaskId);
3506
4370
  const evidenceDir = validateSwarmPath(directory, relativePath);
3507
4371
  try {
3508
4372
  statSync2(evidenceDir);
@@ -3518,24 +4382,24 @@ async function deleteEvidence(directory, taskId) {
3518
4382
  }
3519
4383
  }
3520
4384
  async function checkRequirementCoverage(phase, directory) {
3521
- const relativePath = path6.join("evidence", `req-coverage-phase-${phase}.json`);
3522
- const absolutePath = path6.resolve(directory, ".swarm", relativePath);
4385
+ const relativePath = path10.join("evidence", `req-coverage-phase-${phase}.json`);
4386
+ const absolutePath = path10.resolve(directory, ".swarm", relativePath);
3523
4387
  try {
3524
- await fs5.access(absolutePath);
4388
+ await fs6.access(absolutePath);
3525
4389
  return { exists: true, path: absolutePath };
3526
4390
  } catch {
3527
4391
  return { exists: false, path: absolutePath };
3528
4392
  }
3529
4393
  }
3530
4394
  async function archiveEvidence(directory, maxAgeDays, maxBundles) {
3531
- const taskIds = await _internals5.listEvidenceTaskIds(directory);
4395
+ const taskIds = await _internals9.listEvidenceTaskIds(directory);
3532
4396
  const cutoffDate = new Date;
3533
4397
  cutoffDate.setDate(cutoffDate.getDate() - maxAgeDays);
3534
4398
  const cutoffIso = cutoffDate.toISOString();
3535
4399
  const archived = [];
3536
4400
  const remainingBundles = [];
3537
4401
  for (const taskId of taskIds) {
3538
- const result = await _internals5.loadEvidence(directory, taskId);
4402
+ const result = await _internals9.loadEvidence(directory, taskId);
3539
4403
  if (result.status !== "found") {
3540
4404
  continue;
3541
4405
  }
@@ -3566,7 +4430,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles) {
3566
4430
  function validateEvidence(evidence) {
3567
4431
  return EvidenceSchema.parse(evidence);
3568
4432
  }
3569
- var _internals5 = {
4433
+ var _internals9 = {
3570
4434
  wrapFlatRetrospective,
3571
4435
  loadEvidence,
3572
4436
  listEvidenceTaskIds,
@@ -3685,4 +4549,4 @@ function mergeDurableGateEntriesFromEvidence(taskId, entries, evidence) {
3685
4549
  return merged;
3686
4550
  }
3687
4551
 
3688
- export { PlanSchema, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, resetToRemoteBranch, resetToMainAfterMerge, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, derivePlanId, computePlanHash, initLedger, appendLedgerEvent, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown, RetrospectiveEvidenceSchema, isValidEvidenceType, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };
4552
+ export { PlanSchema, getExecutor, init_executor, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, resetToRemoteBranch, resetToMainAfterMerge, initDurableStatusPath, getAllWorktreeMergeFailures, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, derivePlanId, computePlanHash, initLedger, appendLedgerEvent, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown, RetrospectiveEvidenceSchema, isValidEvidenceType, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };