nightralph 0.0.16 → 0.0.17
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/README.md +8 -2
- package/dist/index.js +108 -25
- package/dist/index.js.map +3 -3
- package/dist/merge.js +31 -19
- package/dist/merge.js.map +2 -2
- package/dist/meta.json +6 -6
- package/dist/orchestrator.js +78 -6
- package/dist/orchestrator.js.map +3 -3
- package/dist/src/merge.d.ts +7 -2
- package/dist/src/merge.d.ts.map +1 -1
- package/dist/src/orchestrator.d.ts +1 -0
- package/dist/src/orchestrator.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -214,6 +214,9 @@ This is expected to run for a long time, like overnight.
|
|
|
214
214
|
|
|
215
215
|
### Mac OS
|
|
216
216
|
|
|
217
|
+
Use `caffeinate` to prevent your machine from sleeping. These examples will
|
|
218
|
+
keep the machine awake, but the display can still go dark.
|
|
219
|
+
|
|
217
220
|
#### Claude Opus 4.6
|
|
218
221
|
|
|
219
222
|
```sh
|
|
@@ -232,5 +235,8 @@ caffeinate -i npx nightralph claude -m claude-sonnet-5
|
|
|
232
235
|
caffeinate -i npx nightralph pi -m openrouter/z-ai/glm-5.3-flash
|
|
233
236
|
```
|
|
234
237
|
|
|
235
|
-
|
|
236
|
-
|
|
238
|
+
#### `codex` + Luna
|
|
239
|
+
|
|
240
|
+
```sh
|
|
241
|
+
caffeinate -i npx nightralph codex -m gpt-5.6-luna
|
|
242
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -507,8 +507,10 @@ async function mergeBranch(repoRoot, branch) {
|
|
|
507
507
|
["merge", branch, "--no-edit"],
|
|
508
508
|
{ cwd: repoRoot }
|
|
509
509
|
);
|
|
510
|
-
return true;
|
|
511
|
-
} catch {
|
|
510
|
+
return { success: true };
|
|
511
|
+
} catch (err) {
|
|
512
|
+
const execErr = err;
|
|
513
|
+
const stderr = execErr.stderr || execErr.stdout;
|
|
512
514
|
try {
|
|
513
515
|
await execFileAsync3(
|
|
514
516
|
"git",
|
|
@@ -517,7 +519,7 @@ async function mergeBranch(repoRoot, branch) {
|
|
|
517
519
|
);
|
|
518
520
|
} catch {
|
|
519
521
|
}
|
|
520
|
-
return false;
|
|
522
|
+
return { success: false, stderr };
|
|
521
523
|
}
|
|
522
524
|
}
|
|
523
525
|
__name(mergeBranch, "mergeBranch");
|
|
@@ -538,8 +540,10 @@ async function rebaseBranch(repoRoot, branch, onto, worktreePath) {
|
|
|
538
540
|
["rebase", onto],
|
|
539
541
|
{ cwd: worktreePath }
|
|
540
542
|
);
|
|
541
|
-
return true;
|
|
542
|
-
} catch {
|
|
543
|
+
return { success: true };
|
|
544
|
+
} catch (err) {
|
|
545
|
+
const execErr = err;
|
|
546
|
+
const stderr = execErr.stderr || execErr.stdout;
|
|
543
547
|
try {
|
|
544
548
|
await execFileAsync3(
|
|
545
549
|
"git",
|
|
@@ -548,7 +552,7 @@ async function rebaseBranch(repoRoot, branch, onto, worktreePath) {
|
|
|
548
552
|
);
|
|
549
553
|
} catch {
|
|
550
554
|
}
|
|
551
|
-
return false;
|
|
555
|
+
return { success: false, stderr };
|
|
552
556
|
}
|
|
553
557
|
}
|
|
554
558
|
try {
|
|
@@ -567,8 +571,9 @@ async function rebaseBranch(repoRoot, branch, onto, worktreePath) {
|
|
|
567
571
|
["checkout", onto],
|
|
568
572
|
{ cwd: repoRoot }
|
|
569
573
|
);
|
|
570
|
-
return true;
|
|
571
|
-
} catch {
|
|
574
|
+
return { success: true };
|
|
575
|
+
} catch (err) {
|
|
576
|
+
const stderr = err.stderr;
|
|
572
577
|
try {
|
|
573
578
|
await execFileAsync3(
|
|
574
579
|
"git",
|
|
@@ -585,7 +590,7 @@ async function rebaseBranch(repoRoot, branch, onto, worktreePath) {
|
|
|
585
590
|
);
|
|
586
591
|
} catch {
|
|
587
592
|
}
|
|
588
|
-
return false;
|
|
593
|
+
return { success: false, stderr };
|
|
589
594
|
}
|
|
590
595
|
}
|
|
591
596
|
__name(rebaseBranch, "rebaseBranch");
|
|
@@ -619,13 +624,16 @@ async function mergeWave(opts) {
|
|
|
619
624
|
conflicted: false
|
|
620
625
|
};
|
|
621
626
|
if (i === 0) {
|
|
622
|
-
const
|
|
627
|
+
const result = await mergeBranch(
|
|
623
628
|
repoRoot,
|
|
624
629
|
branch
|
|
625
630
|
);
|
|
626
|
-
mergingResult.merged = success;
|
|
627
|
-
mergingResult.conflicted = !success;
|
|
628
|
-
if (!success) {
|
|
631
|
+
mergingResult.merged = result.success;
|
|
632
|
+
mergingResult.conflicted = !result.success;
|
|
633
|
+
if (!result.success) {
|
|
634
|
+
mergingResult.stderr = result.stderr;
|
|
635
|
+
}
|
|
636
|
+
if (!result.success) {
|
|
629
637
|
if (strategy === "stop") {
|
|
630
638
|
stoppedAt = mergingResult;
|
|
631
639
|
merged.push(mergingResult);
|
|
@@ -635,14 +643,15 @@ async function mergeWave(opts) {
|
|
|
635
643
|
continue;
|
|
636
644
|
}
|
|
637
645
|
} else {
|
|
638
|
-
const
|
|
646
|
+
const rebaseResult = await rebaseBranch(
|
|
639
647
|
repoRoot,
|
|
640
648
|
branch,
|
|
641
649
|
baseBranch,
|
|
642
650
|
worktreePath
|
|
643
651
|
);
|
|
644
|
-
if (!
|
|
652
|
+
if (!rebaseResult.success) {
|
|
645
653
|
mergingResult.conflicted = true;
|
|
654
|
+
mergingResult.stderr = rebaseResult.stderr;
|
|
646
655
|
if (strategy === "stop") {
|
|
647
656
|
stoppedAt = mergingResult;
|
|
648
657
|
merged.push(mergingResult);
|
|
@@ -652,13 +661,16 @@ async function mergeWave(opts) {
|
|
|
652
661
|
continue;
|
|
653
662
|
}
|
|
654
663
|
}
|
|
655
|
-
const
|
|
664
|
+
const mergeResult = await mergeBranch(
|
|
656
665
|
repoRoot,
|
|
657
666
|
branch
|
|
658
667
|
);
|
|
659
|
-
mergingResult.merged =
|
|
660
|
-
mergingResult.conflicted = !
|
|
661
|
-
if (!
|
|
668
|
+
mergingResult.merged = mergeResult.success;
|
|
669
|
+
mergingResult.conflicted = !mergeResult.success;
|
|
670
|
+
if (!mergeResult.success) {
|
|
671
|
+
mergingResult.stderr = mergeResult.stderr;
|
|
672
|
+
}
|
|
673
|
+
if (!mergeResult.success) {
|
|
662
674
|
if (strategy === "stop") {
|
|
663
675
|
stoppedAt = mergingResult;
|
|
664
676
|
merged.push(mergingResult);
|
|
@@ -1089,6 +1101,29 @@ function renderPrompt(specBody, ticket) {
|
|
|
1089
1101
|
].join("\n");
|
|
1090
1102
|
}
|
|
1091
1103
|
__name(renderPrompt, "renderPrompt");
|
|
1104
|
+
function renderMergePrompt(specBody, ticket) {
|
|
1105
|
+
return [
|
|
1106
|
+
"---- SPEC CONTEXT ----",
|
|
1107
|
+
specBody,
|
|
1108
|
+
"",
|
|
1109
|
+
"---- TICKET ----",
|
|
1110
|
+
ticket.filename,
|
|
1111
|
+
ticket.body,
|
|
1112
|
+
"",
|
|
1113
|
+
"---- AGENT INSTRUCTIONS ----",
|
|
1114
|
+
"You are an autonomous coding agent resolving a merge conflict.",
|
|
1115
|
+
"",
|
|
1116
|
+
"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.",
|
|
1117
|
+
"",
|
|
1118
|
+
"Your task:",
|
|
1119
|
+
"1. Find and resolve all conflict markers (<<<<<<< / ======= / >>>>>>>).",
|
|
1120
|
+
"2. Make sure the code compiles and tests pass.",
|
|
1121
|
+
"3. Commit the resolved files.",
|
|
1122
|
+
"",
|
|
1123
|
+
"Do NOT re-implement the ticket from scratch."
|
|
1124
|
+
].join("\n");
|
|
1125
|
+
}
|
|
1126
|
+
__name(renderMergePrompt, "renderMergePrompt");
|
|
1092
1127
|
function listTickets(tickets) {
|
|
1093
1128
|
console.log("\nTickets:");
|
|
1094
1129
|
for (const t of tickets) {
|
|
@@ -1498,7 +1533,10 @@ async function respawnAndRetryMerge(opts) {
|
|
|
1498
1533
|
} else {
|
|
1499
1534
|
console.log(msg1);
|
|
1500
1535
|
}
|
|
1501
|
-
const prompt =
|
|
1536
|
+
const prompt = renderMergePrompt(
|
|
1537
|
+
opts.specBody,
|
|
1538
|
+
opts.ticket
|
|
1539
|
+
);
|
|
1502
1540
|
const logPath = join3(
|
|
1503
1541
|
opts.logsDir,
|
|
1504
1542
|
opts.ticket.filename.replace(/\.md$/, ".respawn.log")
|
|
@@ -1526,19 +1564,27 @@ async function respawnAndRetryMerge(opts) {
|
|
|
1526
1564
|
}
|
|
1527
1565
|
return false;
|
|
1528
1566
|
}
|
|
1529
|
-
const
|
|
1567
|
+
const mergeResult = await mergeBranch(
|
|
1530
1568
|
opts.repoRoot,
|
|
1531
1569
|
opts.branch
|
|
1532
1570
|
);
|
|
1533
|
-
if (!
|
|
1571
|
+
if (!mergeResult.success) {
|
|
1534
1572
|
const msg3 = ` ${opts.ticket.filename}: merge still conflicted after respawn`;
|
|
1535
1573
|
if (opts.onLine) {
|
|
1536
1574
|
opts.onLine(msg3);
|
|
1537
1575
|
} else {
|
|
1538
1576
|
console.log(msg3);
|
|
1539
1577
|
}
|
|
1578
|
+
if (mergeResult.stderr) {
|
|
1579
|
+
const stderrMsg = ` git: ${mergeResult.stderr.trim()}`;
|
|
1580
|
+
if (opts.onLine) {
|
|
1581
|
+
opts.onLine(stderrMsg);
|
|
1582
|
+
} else {
|
|
1583
|
+
console.log(stderrMsg);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1540
1586
|
}
|
|
1541
|
-
return
|
|
1587
|
+
return mergeResult.success;
|
|
1542
1588
|
}
|
|
1543
1589
|
__name(respawnAndRetryMerge, "respawnAndRetryMerge");
|
|
1544
1590
|
function runProgress(tickets) {
|
|
@@ -1559,6 +1605,10 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, di
|
|
|
1559
1605
|
const ready = findReadyTickets(tickets);
|
|
1560
1606
|
if (ready.length === 0) break;
|
|
1561
1607
|
waveNum++;
|
|
1608
|
+
const readyNums = new Set(ready.map((t) => t.num));
|
|
1609
|
+
const remaining2 = tickets.filter(
|
|
1610
|
+
(t) => t.status === "ready-for-agent" && !readyNums.has(t.num)
|
|
1611
|
+
);
|
|
1562
1612
|
d.startWave(
|
|
1563
1613
|
waveNum,
|
|
1564
1614
|
totalWaves,
|
|
@@ -1566,7 +1616,11 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, di
|
|
|
1566
1616
|
num: t.num,
|
|
1567
1617
|
filename: t.filename
|
|
1568
1618
|
})),
|
|
1569
|
-
runProgress(tickets)
|
|
1619
|
+
runProgress(tickets),
|
|
1620
|
+
remaining2.map((t) => ({
|
|
1621
|
+
num: t.num,
|
|
1622
|
+
filename: t.filename
|
|
1623
|
+
}))
|
|
1570
1624
|
);
|
|
1571
1625
|
const results = await Promise.allSettled(
|
|
1572
1626
|
ready.map(async (t) => {
|
|
@@ -1639,6 +1693,11 @@ async function runWavesIsolated(opts) {
|
|
|
1639
1693
|
const repoRoot = getRepoRoot();
|
|
1640
1694
|
const repoName = getRepoName();
|
|
1641
1695
|
const baseBranch = getCurrentBranch();
|
|
1696
|
+
if (isDirty(repoRoot)) {
|
|
1697
|
+
throw new Error(
|
|
1698
|
+
"Working tree is dirty; commit or stash changes before running"
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1642
1701
|
const progressPath = join3(
|
|
1643
1702
|
dirname(opts.logsDir),
|
|
1644
1703
|
"progress.md"
|
|
@@ -1659,6 +1718,10 @@ async function runWavesIsolated(opts) {
|
|
|
1659
1718
|
const ready = findReadyTickets(opts.tickets);
|
|
1660
1719
|
if (ready.length === 0) break;
|
|
1661
1720
|
waveNum++;
|
|
1721
|
+
const readyNums = new Set(ready.map((t) => t.num));
|
|
1722
|
+
const remaining2 = opts.tickets.filter(
|
|
1723
|
+
(t) => t.status === "ready-for-agent" && !readyNums.has(t.num)
|
|
1724
|
+
);
|
|
1662
1725
|
d.startWave(
|
|
1663
1726
|
waveNum,
|
|
1664
1727
|
totalWaves,
|
|
@@ -1666,7 +1729,11 @@ async function runWavesIsolated(opts) {
|
|
|
1666
1729
|
num: t.num,
|
|
1667
1730
|
filename: t.filename
|
|
1668
1731
|
})),
|
|
1669
|
-
runProgress(opts.tickets)
|
|
1732
|
+
runProgress(opts.tickets),
|
|
1733
|
+
remaining2.map((t) => ({
|
|
1734
|
+
num: t.num,
|
|
1735
|
+
filename: t.filename
|
|
1736
|
+
}))
|
|
1670
1737
|
);
|
|
1671
1738
|
const worktreeSettled = await Promise.allSettled(
|
|
1672
1739
|
ready.map((t) => createWorktree({
|
|
@@ -1879,6 +1946,22 @@ async function runWavesIsolated(opts) {
|
|
|
1879
1946
|
d.log(
|
|
1880
1947
|
` ${ticket.filename}: merge failed, staying ready-for-agent`
|
|
1881
1948
|
);
|
|
1949
|
+
if (branchResult.stderr) {
|
|
1950
|
+
d.log(
|
|
1951
|
+
" git: " + branchResult.stderr.trim()
|
|
1952
|
+
);
|
|
1953
|
+
}
|
|
1954
|
+
checkTicket(progress, ticket.num, {
|
|
1955
|
+
branch: branchResult.branch,
|
|
1956
|
+
exitCode: 0,
|
|
1957
|
+
done: false
|
|
1958
|
+
});
|
|
1959
|
+
writeProgress(progressPath, progress);
|
|
1960
|
+
await commitProgress(
|
|
1961
|
+
repoRoot,
|
|
1962
|
+
progressPath,
|
|
1963
|
+
`nightralph: ${ticket.filename} merge failed`
|
|
1964
|
+
);
|
|
1882
1965
|
}
|
|
1883
1966
|
}
|
|
1884
1967
|
}
|