nightralph 0.0.16 → 0.0.18

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.
package/dist/merge.js CHANGED
@@ -16,8 +16,10 @@ async function mergeBranch(repoRoot, branch) {
16
16
  ["merge", branch, "--no-edit"],
17
17
  { cwd: repoRoot }
18
18
  );
19
- return true;
20
- } catch {
19
+ return { success: true };
20
+ } catch (err) {
21
+ const execErr = err;
22
+ const stderr = execErr.stderr || execErr.stdout;
21
23
  try {
22
24
  await execFileAsync(
23
25
  "git",
@@ -26,7 +28,7 @@ async function mergeBranch(repoRoot, branch) {
26
28
  );
27
29
  } catch {
28
30
  }
29
- return false;
31
+ return { success: false, stderr };
30
32
  }
31
33
  }
32
34
  __name(mergeBranch, "mergeBranch");
@@ -47,8 +49,10 @@ async function rebaseBranch(repoRoot, branch, onto, worktreePath) {
47
49
  ["rebase", onto],
48
50
  { cwd: worktreePath }
49
51
  );
50
- return true;
51
- } catch {
52
+ return { success: true };
53
+ } catch (err) {
54
+ const execErr = err;
55
+ const stderr = execErr.stderr || execErr.stdout;
52
56
  try {
53
57
  await execFileAsync(
54
58
  "git",
@@ -57,7 +61,7 @@ async function rebaseBranch(repoRoot, branch, onto, worktreePath) {
57
61
  );
58
62
  } catch {
59
63
  }
60
- return false;
64
+ return { success: false, stderr };
61
65
  }
62
66
  }
63
67
  try {
@@ -76,8 +80,9 @@ async function rebaseBranch(repoRoot, branch, onto, worktreePath) {
76
80
  ["checkout", onto],
77
81
  { cwd: repoRoot }
78
82
  );
79
- return true;
80
- } catch {
83
+ return { success: true };
84
+ } catch (err) {
85
+ const stderr = err.stderr;
81
86
  try {
82
87
  await execFileAsync(
83
88
  "git",
@@ -94,7 +99,7 @@ async function rebaseBranch(repoRoot, branch, onto, worktreePath) {
94
99
  );
95
100
  } catch {
96
101
  }
97
- return false;
102
+ return { success: false, stderr };
98
103
  }
99
104
  }
100
105
  __name(rebaseBranch, "rebaseBranch");
@@ -128,13 +133,16 @@ async function mergeWave(opts) {
128
133
  conflicted: false
129
134
  };
130
135
  if (i === 0) {
131
- const success = await mergeBranch(
136
+ const result = await mergeBranch(
132
137
  repoRoot,
133
138
  branch
134
139
  );
135
- mergingResult.merged = success;
136
- mergingResult.conflicted = !success;
137
- if (!success) {
140
+ mergingResult.merged = result.success;
141
+ mergingResult.conflicted = !result.success;
142
+ if (!result.success) {
143
+ mergingResult.stderr = result.stderr;
144
+ }
145
+ if (!result.success) {
138
146
  if (strategy === "stop") {
139
147
  stoppedAt = mergingResult;
140
148
  merged.push(mergingResult);
@@ -144,14 +152,15 @@ async function mergeWave(opts) {
144
152
  continue;
145
153
  }
146
154
  } else {
147
- const rebaseSuccess = await rebaseBranch(
155
+ const rebaseResult = await rebaseBranch(
148
156
  repoRoot,
149
157
  branch,
150
158
  baseBranch,
151
159
  worktreePath
152
160
  );
153
- if (!rebaseSuccess) {
161
+ if (!rebaseResult.success) {
154
162
  mergingResult.conflicted = true;
163
+ mergingResult.stderr = rebaseResult.stderr;
155
164
  if (strategy === "stop") {
156
165
  stoppedAt = mergingResult;
157
166
  merged.push(mergingResult);
@@ -161,13 +170,16 @@ async function mergeWave(opts) {
161
170
  continue;
162
171
  }
163
172
  }
164
- const mergeSuccess = await mergeBranch(
173
+ const mergeResult = await mergeBranch(
165
174
  repoRoot,
166
175
  branch
167
176
  );
168
- mergingResult.merged = mergeSuccess;
169
- mergingResult.conflicted = !mergeSuccess;
170
- if (!mergeSuccess) {
177
+ mergingResult.merged = mergeResult.success;
178
+ mergingResult.conflicted = !mergeResult.success;
179
+ if (!mergeResult.success) {
180
+ mergingResult.stderr = mergeResult.stderr;
181
+ }
182
+ if (!mergeResult.success) {
171
183
  if (strategy === "stop") {
172
184
  stoppedAt = mergingResult;
173
185
  merged.push(mergingResult);
package/dist/merge.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/merge.ts"],
4
- "sourcesContent": ["import { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\n\nconst execFileAsync = promisify(execFile)\n\nexport type MergeConflictStrategy = 'respawn'|'stop'\n\nexport type MergeResult = {\n branch:string\n ticketNum:number\n merged:boolean\n conflicted:boolean\n}\n\nexport type WaveMergeResult = {\n merged:Array<MergeResult>\n stoppedAt:MergeResult|null\n}\n\nexport async function mergeBranch (\n repoRoot:string,\n branch:string\n):Promise<boolean> {\n if (!repoRoot || typeof repoRoot !== 'string') {\n throw new Error('repoRoot must be a non-empty string')\n }\n if (!branch || typeof branch !== 'string') {\n throw new Error('branch must be a non-empty string')\n }\n\n try {\n await execFileAsync(\n 'git',\n ['merge', branch, '--no-edit'],\n { cwd:repoRoot }\n )\n return true\n } catch {\n try {\n await execFileAsync(\n 'git',\n ['merge', '--abort'],\n { cwd:repoRoot }\n )\n } catch {\n // no merge in progress to abort\n }\n return false\n }\n}\n\nexport async function rebaseBranch (\n repoRoot:string,\n branch:string,\n onto:string,\n worktreePath?:string\n):Promise<boolean> {\n if (!repoRoot || typeof repoRoot !== 'string') {\n throw new Error('repoRoot must be a non-empty string')\n }\n if (!branch || typeof branch !== 'string') {\n throw new Error('branch must be a non-empty string')\n }\n if (!onto || typeof onto !== 'string') {\n throw new Error('onto must be a non-empty string')\n }\n\n // When the branch has its own worktree, it is already checked\n // out there -- checking it out in repoRoot too would fail with\n // \"already used by worktree\", so rebase in place instead.\n if (worktreePath) {\n try {\n await execFileAsync(\n 'git',\n ['rebase', onto],\n { cwd:worktreePath }\n )\n return true\n } catch {\n try {\n await execFileAsync(\n 'git',\n ['rebase', '--abort'],\n { cwd:worktreePath }\n )\n } catch {\n // ignore abort errors\n }\n return false\n }\n }\n\n try {\n await execFileAsync(\n 'git',\n ['checkout', branch],\n { cwd:repoRoot }\n )\n\n await execFileAsync(\n 'git',\n ['rebase', onto],\n { cwd:repoRoot }\n )\n\n await execFileAsync(\n 'git',\n ['checkout', onto],\n { cwd:repoRoot }\n )\n\n return true\n } catch {\n try {\n await execFileAsync(\n 'git',\n ['rebase', '--abort'],\n { cwd:repoRoot }\n )\n } catch {\n // ignore abort errors\n }\n try {\n await execFileAsync(\n 'git',\n ['checkout', onto],\n { cwd:repoRoot }\n )\n } catch {\n // ignore checkout errors\n }\n return false\n }\n}\n\n// Merges baseBranch into the worktree's branch so a\n// respawned agent sees the latest code. Conflicts are\n// left in place (not aborted) so the agent can resolve\n// and commit them.\nexport async function updateWorktreeFromBase (\n worktreePath:string,\n baseBranch:string\n):Promise<void> {\n try {\n await execFileAsync(\n 'git',\n ['merge', baseBranch, '--no-edit'],\n { cwd:worktreePath }\n )\n } catch {\n // conflict markers intentionally left for the\n // respawned agent to resolve\n }\n}\n\nexport async function mergeWave (\n opts:Readonly<{\n repoRoot:string\n baseBranch:string\n branches:ReadonlyArray<{\n branch:string\n ticketNum:number\n worktreePath?:string\n }>\n strategy:MergeConflictStrategy\n }>\n):Promise<WaveMergeResult> {\n const { repoRoot, baseBranch, branches, strategy } = opts\n\n if (!repoRoot || typeof repoRoot !== 'string') {\n throw new Error('repoRoot must be a non-empty string')\n }\n if (!baseBranch || typeof baseBranch !== 'string') {\n throw new Error('baseBranch must be a non-empty string')\n }\n\n const merged:Array<MergeResult> = []\n let stoppedAt:MergeResult|null = null\n\n for (let i = 0; i < branches.length; i++) {\n const { branch, ticketNum, worktreePath } = branches[i]\n const mergingResult:MergeResult = {\n branch,\n ticketNum,\n merged:false,\n conflicted:false,\n }\n\n if (i === 0) {\n const success = await mergeBranch(\n repoRoot,\n branch\n )\n mergingResult.merged = success\n mergingResult.conflicted = !success\n\n if (!success) {\n if (strategy === 'stop') {\n stoppedAt = mergingResult\n merged.push(mergingResult)\n break\n }\n merged.push(mergingResult)\n continue\n }\n } else {\n const rebaseSuccess = await rebaseBranch(\n repoRoot,\n branch,\n baseBranch,\n worktreePath\n )\n\n if (!rebaseSuccess) {\n mergingResult.conflicted = true\n\n if (strategy === 'stop') {\n stoppedAt = mergingResult\n merged.push(mergingResult)\n break\n } else {\n // strategy === 'respawn'\n merged.push(mergingResult)\n continue\n }\n }\n\n const mergeSuccess = await mergeBranch(\n repoRoot,\n branch\n )\n mergingResult.merged = mergeSuccess\n mergingResult.conflicted = !mergeSuccess\n\n if (!mergeSuccess) {\n if (strategy === 'stop') {\n stoppedAt = mergingResult\n merged.push(mergingResult)\n break\n }\n merged.push(mergingResult)\n continue\n }\n }\n\n merged.push(mergingResult)\n }\n\n return {\n merged,\n stoppedAt,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\n\nconst execFileAsync = promisify(execFile)\n\nexport type MergeConflictStrategy = 'respawn'|'stop'\n\nexport type MergeResult = {\n branch:string\n ticketNum:number\n merged:boolean\n conflicted:boolean\n stderr?:string\n}\n\nexport type WaveMergeResult = {\n merged:Array<MergeResult>\n stoppedAt:MergeResult|null\n}\n\nexport type GitOpResult = {\n success:boolean\n stderr?:string\n}\n\nexport async function mergeBranch (\n repoRoot:string,\n branch:string\n):Promise<GitOpResult> {\n if (!repoRoot || typeof repoRoot !== 'string') {\n throw new Error('repoRoot must be a non-empty string')\n }\n if (!branch || typeof branch !== 'string') {\n throw new Error('branch must be a non-empty string')\n }\n\n try {\n await execFileAsync(\n 'git',\n ['merge', branch, '--no-edit'],\n { cwd:repoRoot }\n )\n return { success:true }\n } catch (err) {\n const execErr = err as {\n stdout?:string; stderr?:string\n }\n const stderr =\n execErr.stderr || execErr.stdout\n try {\n await execFileAsync(\n 'git',\n ['merge', '--abort'],\n { cwd:repoRoot }\n )\n } catch {\n // no merge in progress to abort\n }\n return { success:false, stderr }\n }\n}\n\nexport async function rebaseBranch (\n repoRoot:string,\n branch:string,\n onto:string,\n worktreePath?:string\n):Promise<GitOpResult> {\n if (!repoRoot || typeof repoRoot !== 'string') {\n throw new Error('repoRoot must be a non-empty string')\n }\n if (!branch || typeof branch !== 'string') {\n throw new Error('branch must be a non-empty string')\n }\n if (!onto || typeof onto !== 'string') {\n throw new Error('onto must be a non-empty string')\n }\n\n // When the branch has its own worktree, it is already checked\n // out there -- checking it out in repoRoot too would fail with\n // \"already used by worktree\", so rebase in place instead.\n if (worktreePath) {\n try {\n await execFileAsync(\n 'git',\n ['rebase', onto],\n { cwd:worktreePath }\n )\n return { success:true }\n } catch (err) {\n const execErr = err as {\n stdout?:string; stderr?:string\n }\n const stderr =\n execErr.stderr || execErr.stdout\n try {\n await execFileAsync(\n 'git',\n ['rebase', '--abort'],\n { cwd:worktreePath }\n )\n } catch {\n // ignore abort errors\n }\n return { success:false, stderr }\n }\n }\n\n try {\n await execFileAsync(\n 'git',\n ['checkout', branch],\n { cwd:repoRoot }\n )\n\n await execFileAsync(\n 'git',\n ['rebase', onto],\n { cwd:repoRoot }\n )\n\n await execFileAsync(\n 'git',\n ['checkout', onto],\n { cwd:repoRoot }\n )\n\n return { success:true }\n } catch (err) {\n const stderr =\n (err as { stderr?:string }).stderr\n try {\n await execFileAsync(\n 'git',\n ['rebase', '--abort'],\n { cwd:repoRoot }\n )\n } catch {\n // ignore abort errors\n }\n try {\n await execFileAsync(\n 'git',\n ['checkout', onto],\n { cwd:repoRoot }\n )\n } catch {\n // ignore checkout errors\n }\n return { success:false, stderr }\n }\n}\n\n// Merges baseBranch into the worktree's branch so a\n// respawned agent sees the latest code. Conflicts are\n// left in place (not aborted) so the agent can resolve\n// and commit them.\nexport async function updateWorktreeFromBase (\n worktreePath:string,\n baseBranch:string\n):Promise<void> {\n try {\n await execFileAsync(\n 'git',\n ['merge', baseBranch, '--no-edit'],\n { cwd:worktreePath }\n )\n } catch {\n // conflict markers intentionally left for the\n // respawned agent to resolve\n }\n}\n\nexport async function mergeWave (\n opts:Readonly<{\n repoRoot:string\n baseBranch:string\n branches:ReadonlyArray<{\n branch:string\n ticketNum:number\n worktreePath?:string\n }>\n strategy:MergeConflictStrategy\n }>\n):Promise<WaveMergeResult> {\n const { repoRoot, baseBranch, branches, strategy } = opts\n\n if (!repoRoot || typeof repoRoot !== 'string') {\n throw new Error('repoRoot must be a non-empty string')\n }\n if (!baseBranch || typeof baseBranch !== 'string') {\n throw new Error('baseBranch must be a non-empty string')\n }\n\n const merged:Array<MergeResult> = []\n let stoppedAt:MergeResult|null = null\n\n for (let i = 0; i < branches.length; i++) {\n const { branch, ticketNum, worktreePath } = branches[i]\n const mergingResult:MergeResult = {\n branch,\n ticketNum,\n merged:false,\n conflicted:false,\n }\n\n if (i === 0) {\n const result = await mergeBranch(\n repoRoot,\n branch\n )\n mergingResult.merged = result.success\n mergingResult.conflicted = !result.success\n if (!result.success) {\n mergingResult.stderr = result.stderr\n }\n\n if (!result.success) {\n if (strategy === 'stop') {\n stoppedAt = mergingResult\n merged.push(mergingResult)\n break\n }\n merged.push(mergingResult)\n continue\n }\n } else {\n const rebaseResult = await rebaseBranch(\n repoRoot,\n branch,\n baseBranch,\n worktreePath\n )\n\n if (!rebaseResult.success) {\n mergingResult.conflicted = true\n mergingResult.stderr = rebaseResult.stderr\n\n if (strategy === 'stop') {\n stoppedAt = mergingResult\n merged.push(mergingResult)\n break\n } else {\n // strategy === 'respawn'\n merged.push(mergingResult)\n continue\n }\n }\n\n const mergeResult = await mergeBranch(\n repoRoot,\n branch\n )\n mergingResult.merged = mergeResult.success\n mergingResult.conflicted = !mergeResult.success\n if (!mergeResult.success) {\n mergingResult.stderr = mergeResult.stderr\n }\n\n if (!mergeResult.success) {\n if (strategy === 'stop') {\n stoppedAt = mergingResult\n merged.push(mergingResult)\n break\n }\n merged.push(mergingResult)\n continue\n }\n }\n\n merged.push(mergingResult)\n }\n\n return {\n merged,\n stoppedAt,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/meta.json CHANGED
@@ -84,7 +84,7 @@
84
84
  "format": "esm"
85
85
  },
86
86
  "src/merge.ts": {
87
- "bytes": 6529,
87
+ "bytes": 7457,
88
88
  "imports": [
89
89
  {
90
90
  "path": "node:child_process",
@@ -116,7 +116,7 @@
116
116
  "format": "esm"
117
117
  },
118
118
  "src/orchestrator.ts": {
119
- "bytes": 35287,
119
+ "bytes": 38180,
120
120
  "imports": [
121
121
  {
122
122
  "path": "node:child_process",
@@ -203,7 +203,7 @@
203
203
  "format": "esm"
204
204
  },
205
205
  "src/index.ts": {
206
- "bytes": 7134,
206
+ "bytes": 7132,
207
207
  "imports": [
208
208
  {
209
209
  "path": "node:fs",
@@ -269,7 +269,7 @@
269
269
  "imports": [],
270
270
  "exports": [],
271
271
  "inputs": {},
272
- "bytes": 122509
272
+ "bytes": 128120
273
273
  },
274
274
  "dist/index.js": {
275
275
  "imports": [
@@ -411,13 +411,13 @@
411
411
  "entryPoint": "src/index.ts",
412
412
  "inputs": {
413
413
  "src/index.ts": {
414
- "bytesInOutput": 5060
414
+ "bytesInOutput": 5058
415
415
  },
416
416
  "src/setup.ts": {
417
417
  "bytesInOutput": 4318
418
418
  },
419
419
  "src/orchestrator.ts": {
420
- "bytesInOutput": 27034
420
+ "bytesInOutput": 29296
421
421
  },
422
422
  "src/worktree.ts": {
423
423
  "bytesInOutput": 3194
@@ -426,7 +426,7 @@
426
426
  "bytesInOutput": 4233
427
427
  },
428
428
  "src/merge.ts": {
429
- "bytesInOutput": 4410
429
+ "bytesInOutput": 4995
430
430
  },
431
431
  "src/display.ts": {
432
432
  "bytesInOutput": 8226
@@ -435,7 +435,7 @@
435
435
  "bytesInOutput": 1937
436
436
  }
437
437
  },
438
- "bytes": 58839
438
+ "bytes": 61684
439
439
  }
440
440
  }
441
441
  }
@@ -154,6 +154,29 @@ function renderPrompt(specBody, ticket) {
154
154
  ].join("\n");
155
155
  }
156
156
  __name(renderPrompt, "renderPrompt");
157
+ function renderMergePrompt(specBody, ticket) {
158
+ return [
159
+ "---- SPEC CONTEXT ----",
160
+ specBody,
161
+ "",
162
+ "---- TICKET ----",
163
+ ticket.filename,
164
+ ticket.body,
165
+ "",
166
+ "---- AGENT INSTRUCTIONS ----",
167
+ "You are an autonomous coding agent resolving a merge conflict.",
168
+ "",
169
+ "Another agent already implemented this ticket on a feature branch. The branch was then merged with the base branch, but conflicts arose. The working tree may contain conflict markers that need resolution.",
170
+ "",
171
+ "Your task:",
172
+ "1. Find and resolve all conflict markers (<<<<<<< / ======= / >>>>>>>).",
173
+ "2. Make sure the code compiles and tests pass.",
174
+ "3. Commit the resolved files.",
175
+ "",
176
+ "Do NOT re-implement the ticket from scratch."
177
+ ].join("\n");
178
+ }
179
+ __name(renderMergePrompt, "renderMergePrompt");
157
180
  function listTickets(tickets) {
158
181
  console.log("\nTickets:");
159
182
  for (const t of tickets) {
@@ -563,7 +586,10 @@ async function respawnAndRetryMerge(opts) {
563
586
  } else {
564
587
  console.log(msg1);
565
588
  }
566
- const prompt = renderPrompt(opts.specBody, opts.ticket);
589
+ const prompt = renderMergePrompt(
590
+ opts.specBody,
591
+ opts.ticket
592
+ );
567
593
  const logPath = join(
568
594
  opts.logsDir,
569
595
  opts.ticket.filename.replace(/\.md$/, ".respawn.log")
@@ -591,19 +617,27 @@ async function respawnAndRetryMerge(opts) {
591
617
  }
592
618
  return false;
593
619
  }
594
- const merged = await mergeBranch(
620
+ const mergeResult = await mergeBranch(
595
621
  opts.repoRoot,
596
622
  opts.branch
597
623
  );
598
- if (!merged) {
624
+ if (!mergeResult.success) {
599
625
  const msg3 = ` ${opts.ticket.filename}: merge still conflicted after respawn`;
600
626
  if (opts.onLine) {
601
627
  opts.onLine(msg3);
602
628
  } else {
603
629
  console.log(msg3);
604
630
  }
631
+ if (mergeResult.stderr) {
632
+ const stderrMsg = ` git: ${mergeResult.stderr.trim()}`;
633
+ if (opts.onLine) {
634
+ opts.onLine(stderrMsg);
635
+ } else {
636
+ console.log(stderrMsg);
637
+ }
638
+ }
605
639
  }
606
- return merged;
640
+ return mergeResult.success;
607
641
  }
608
642
  __name(respawnAndRetryMerge, "respawnAndRetryMerge");
609
643
  function runProgress(tickets) {
@@ -624,6 +658,10 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, di
624
658
  const ready = findReadyTickets(tickets);
625
659
  if (ready.length === 0) break;
626
660
  waveNum++;
661
+ const readyNums = new Set(ready.map((t) => t.num));
662
+ const remaining2 = tickets.filter(
663
+ (t) => t.status === "ready-for-agent" && !readyNums.has(t.num)
664
+ );
627
665
  d.startWave(
628
666
  waveNum,
629
667
  totalWaves,
@@ -631,7 +669,11 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, di
631
669
  num: t.num,
632
670
  filename: t.filename
633
671
  })),
634
- runProgress(tickets)
672
+ runProgress(tickets),
673
+ remaining2.map((t) => ({
674
+ num: t.num,
675
+ filename: t.filename
676
+ }))
635
677
  );
636
678
  const results = await Promise.allSettled(
637
679
  ready.map(async (t) => {
@@ -704,6 +746,11 @@ async function runWavesIsolated(opts) {
704
746
  const repoRoot = getRepoRoot();
705
747
  const repoName = getRepoName();
706
748
  const baseBranch = getCurrentBranch();
749
+ if (isDirty(repoRoot)) {
750
+ throw new Error(
751
+ "Working tree is dirty; commit or stash changes before running"
752
+ );
753
+ }
707
754
  const progressPath = join(
708
755
  dirname(opts.logsDir),
709
756
  "progress.md"
@@ -724,6 +771,10 @@ async function runWavesIsolated(opts) {
724
771
  const ready = findReadyTickets(opts.tickets);
725
772
  if (ready.length === 0) break;
726
773
  waveNum++;
774
+ const readyNums = new Set(ready.map((t) => t.num));
775
+ const remaining2 = opts.tickets.filter(
776
+ (t) => t.status === "ready-for-agent" && !readyNums.has(t.num)
777
+ );
727
778
  d.startWave(
728
779
  waveNum,
729
780
  totalWaves,
@@ -731,7 +782,11 @@ async function runWavesIsolated(opts) {
731
782
  num: t.num,
732
783
  filename: t.filename
733
784
  })),
734
- runProgress(opts.tickets)
785
+ runProgress(opts.tickets),
786
+ remaining2.map((t) => ({
787
+ num: t.num,
788
+ filename: t.filename
789
+ }))
735
790
  );
736
791
  const worktreeSettled = await Promise.allSettled(
737
792
  ready.map((t) => createWorktree({
@@ -944,6 +999,22 @@ async function runWavesIsolated(opts) {
944
999
  d.log(
945
1000
  ` ${ticket.filename}: merge failed, staying ready-for-agent`
946
1001
  );
1002
+ if (branchResult.stderr) {
1003
+ d.log(
1004
+ " git: " + branchResult.stderr.trim()
1005
+ );
1006
+ }
1007
+ checkTicket(progress, ticket.num, {
1008
+ branch: branchResult.branch,
1009
+ exitCode: 0,
1010
+ done: false
1011
+ });
1012
+ writeProgress(progressPath, progress);
1013
+ await commitProgress(
1014
+ repoRoot,
1015
+ progressPath,
1016
+ `nightralph: ${ticket.filename} merge failed`
1017
+ );
947
1018
  }
948
1019
  }
949
1020
  }
@@ -1000,6 +1071,7 @@ export {
1000
1071
  parseBlockers,
1001
1072
  parseStatus,
1002
1073
  parseTicketFilename,
1074
+ renderMergePrompt,
1003
1075
  renderPrompt,
1004
1076
  runWaves,
1005
1077
  runWavesIsolated,