nightralph 0.0.52 → 0.0.53
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 +64 -8
- package/dist/display.js +80 -62
- package/dist/display.js.map +2 -2
- package/dist/index.js +162 -94
- package/dist/index.js.map +2 -2
- package/dist/meta.json +6 -6
- package/dist/orchestrator.js +86 -33
- package/dist/orchestrator.js.map +2 -2
- package/dist/src/display.d.ts +35 -33
- package/dist/src/display.d.ts.map +1 -1
- package/dist/src/orchestrator.d.ts +10 -0
- package/dist/src/orchestrator.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/meta.json
CHANGED
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"format": "esm"
|
|
106
106
|
},
|
|
107
107
|
"src/display.ts": {
|
|
108
|
-
"bytes":
|
|
108
|
+
"bytes": 15265,
|
|
109
109
|
"imports": [
|
|
110
110
|
{
|
|
111
111
|
"path": "<runtime>",
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
"format": "esm"
|
|
143
143
|
},
|
|
144
144
|
"src/orchestrator.ts": {
|
|
145
|
-
"bytes":
|
|
145
|
+
"bytes": 81131,
|
|
146
146
|
"imports": [
|
|
147
147
|
{
|
|
148
148
|
"path": "node:child_process",
|
|
@@ -320,7 +320,7 @@
|
|
|
320
320
|
"imports": [],
|
|
321
321
|
"exports": [],
|
|
322
322
|
"inputs": {},
|
|
323
|
-
"bytes":
|
|
323
|
+
"bytes": 212042
|
|
324
324
|
},
|
|
325
325
|
"dist/index.js": {
|
|
326
326
|
"imports": [
|
|
@@ -488,7 +488,7 @@
|
|
|
488
488
|
"bytesInOutput": 4816
|
|
489
489
|
},
|
|
490
490
|
"src/orchestrator.ts": {
|
|
491
|
-
"bytesInOutput":
|
|
491
|
+
"bytesInOutput": 57013
|
|
492
492
|
},
|
|
493
493
|
"src/worktree.ts": {
|
|
494
494
|
"bytesInOutput": 4235
|
|
@@ -500,7 +500,7 @@
|
|
|
500
500
|
"bytesInOutput": 4995
|
|
501
501
|
},
|
|
502
502
|
"src/display.ts": {
|
|
503
|
-
"bytesInOutput":
|
|
503
|
+
"bytesInOutput": 9578
|
|
504
504
|
},
|
|
505
505
|
"src/testcmd.ts": {
|
|
506
506
|
"bytesInOutput": 1914
|
|
@@ -509,7 +509,7 @@
|
|
|
509
509
|
"bytesInOutput": 2785
|
|
510
510
|
}
|
|
511
511
|
},
|
|
512
|
-
"bytes":
|
|
512
|
+
"bytes": 99130
|
|
513
513
|
}
|
|
514
514
|
}
|
|
515
515
|
}
|
package/dist/orchestrator.js
CHANGED
|
@@ -41,7 +41,8 @@ import {
|
|
|
41
41
|
updateWorktreeFromBase
|
|
42
42
|
} from "./merge.js";
|
|
43
43
|
import {
|
|
44
|
-
createDisplay
|
|
44
|
+
createDisplay,
|
|
45
|
+
ticketLabel
|
|
45
46
|
} from "./display.js";
|
|
46
47
|
import { runTestCmd } from "./testcmd.js";
|
|
47
48
|
function formatErrorMessage(error) {
|
|
@@ -165,6 +166,37 @@ function findReadyTickets(tickets) {
|
|
|
165
166
|
});
|
|
166
167
|
}
|
|
167
168
|
__name(findReadyTickets, "findReadyTickets");
|
|
169
|
+
function findBlockedTickets(tickets, failedKeys) {
|
|
170
|
+
const byKey = new Map(
|
|
171
|
+
tickets.map((t) => [ticketKey(t.feature, t.num), t])
|
|
172
|
+
);
|
|
173
|
+
function collectDead(ticket, visited, found) {
|
|
174
|
+
for (const b of ticket.blockers) {
|
|
175
|
+
const key = ticketKey(ticket.feature, b);
|
|
176
|
+
if (visited.has(key)) continue;
|
|
177
|
+
visited.add(key);
|
|
178
|
+
const blocker = byKey.get(key);
|
|
179
|
+
if (!blocker || blocker.status === "done") continue;
|
|
180
|
+
if (failedKeys.has(key) || blocker.status !== "ready-for-agent") {
|
|
181
|
+
found.add(key);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
collectDead(blocker, visited, found);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
__name(collectDead, "collectDead");
|
|
188
|
+
const blocked = /* @__PURE__ */ new Map();
|
|
189
|
+
for (const t of tickets) {
|
|
190
|
+
const key = ticketKey(t.feature, t.num);
|
|
191
|
+
if (t.status !== "ready-for-agent") continue;
|
|
192
|
+
if (failedKeys.has(key)) continue;
|
|
193
|
+
const found = /* @__PURE__ */ new Set();
|
|
194
|
+
collectDead(t, /* @__PURE__ */ new Set([key]), found);
|
|
195
|
+
if (found.size > 0) blocked.set(key, [...found]);
|
|
196
|
+
}
|
|
197
|
+
return blocked;
|
|
198
|
+
}
|
|
199
|
+
__name(findBlockedTickets, "findBlockedTickets");
|
|
168
200
|
const COMPLETED_RE = /^\s*[-*+]?\s*\[x\]\s+(.+)$/i;
|
|
169
201
|
const TOOL_USE_RE = /^\[[\w.]+\]/;
|
|
170
202
|
const CHECKBOX_RE = /^(\s*[-*+] )\[( |x|X)\]( .*)$/;
|
|
@@ -701,7 +733,7 @@ function spawnAgent(opts) {
|
|
|
701
733
|
detached: true
|
|
702
734
|
});
|
|
703
735
|
if (proc.pid !== void 0) runningAgents.add(proc.pid);
|
|
704
|
-
proc.stdout.pipe(logStream);
|
|
736
|
+
proc.stdout.pipe(logStream, { end: false });
|
|
705
737
|
const piFormatter = isPi ? createPiStreamFormatter() : null;
|
|
706
738
|
const completedItems = [];
|
|
707
739
|
const seenItems = /* @__PURE__ */ new Set();
|
|
@@ -750,7 +782,7 @@ function spawnAgent(opts) {
|
|
|
750
782
|
}
|
|
751
783
|
}
|
|
752
784
|
});
|
|
753
|
-
proc.stderr.pipe(logStream);
|
|
785
|
+
proc.stderr.pipe(logStream, { end: false });
|
|
754
786
|
const rlErr = createInterface({ input: proc.stderr });
|
|
755
787
|
rlErr.on("line", (line) => {
|
|
756
788
|
if (isClaude && !TOOL_USE_RE.test(line)) return;
|
|
@@ -1344,6 +1376,29 @@ function renderFinishSummary(tickets, totalCompleted, failedKeys) {
|
|
|
1344
1376
|
return lines.join("\n");
|
|
1345
1377
|
}
|
|
1346
1378
|
__name(renderFinishSummary, "renderFinishSummary");
|
|
1379
|
+
function sessionTickets(tickets) {
|
|
1380
|
+
return [...tickets].filter((t) => t.status === "ready-for-agent").sort((a, b) => a.feature.localeCompare(b.feature) || a.num - b.num);
|
|
1381
|
+
}
|
|
1382
|
+
__name(sessionTickets, "sessionTickets");
|
|
1383
|
+
function toRef(t) {
|
|
1384
|
+
return { num: t.num, filename: t.filename, feature: t.feature };
|
|
1385
|
+
}
|
|
1386
|
+
__name(toRef, "toRef");
|
|
1387
|
+
function reportBlocked(d, tickets, failedKeys) {
|
|
1388
|
+
const byKey = new Map(
|
|
1389
|
+
tickets.map((t) => [ticketKey(t.feature, t.num), t])
|
|
1390
|
+
);
|
|
1391
|
+
for (const [key, dead] of findBlockedTickets(
|
|
1392
|
+
tickets,
|
|
1393
|
+
failedKeys
|
|
1394
|
+
)) {
|
|
1395
|
+
const ticket = byKey.get(key);
|
|
1396
|
+
if (!ticket) continue;
|
|
1397
|
+
const labels = dead.map((k) => byKey.get(k)).filter((t) => t !== void 0).map((t) => ticketLabel(toRef(t)));
|
|
1398
|
+
d.setBlocked(ticket.num, ticket.feature, labels);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
__name(reportBlocked, "reportBlocked");
|
|
1347
1402
|
function runProgress(tickets) {
|
|
1348
1403
|
return {
|
|
1349
1404
|
completed: tickets.filter((t) => t.status === "done").length,
|
|
@@ -1364,7 +1419,9 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, te
|
|
|
1364
1419
|
let totalCompleted = 0;
|
|
1365
1420
|
let waveNum = 0;
|
|
1366
1421
|
const failedKeys = /* @__PURE__ */ new Set();
|
|
1422
|
+
const session = sessionTickets(tickets);
|
|
1367
1423
|
while (true) {
|
|
1424
|
+
if (waveNum > 0) reportBlocked(d, tickets, failedKeys);
|
|
1368
1425
|
const ready = findReadyTickets(tickets).filter(
|
|
1369
1426
|
(t) => !failedKeys.has(
|
|
1370
1427
|
ticketKey(t.feature, t.num)
|
|
@@ -1372,12 +1429,10 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, te
|
|
|
1372
1429
|
);
|
|
1373
1430
|
if (ready.length === 0) break;
|
|
1374
1431
|
waveNum++;
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
(t) => t.status === "ready-for-agent" && !readyKeys.has(ticketKey(t.feature, t.num))
|
|
1380
|
-
);
|
|
1432
|
+
if (waveNum === 1) {
|
|
1433
|
+
d.startRun(session.map(toRef));
|
|
1434
|
+
reportBlocked(d, tickets, failedKeys);
|
|
1435
|
+
}
|
|
1381
1436
|
d.startWave(
|
|
1382
1437
|
waveNum,
|
|
1383
1438
|
totalWaves,
|
|
@@ -1386,12 +1441,7 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, te
|
|
|
1386
1441
|
filename: t.filename,
|
|
1387
1442
|
feature: t.feature
|
|
1388
1443
|
})),
|
|
1389
|
-
runProgress(
|
|
1390
|
-
remaining2.map((t) => ({
|
|
1391
|
-
num: t.num,
|
|
1392
|
-
filename: t.filename,
|
|
1393
|
-
feature: t.feature
|
|
1394
|
-
}))
|
|
1444
|
+
runProgress(session)
|
|
1395
1445
|
);
|
|
1396
1446
|
const results = await runPool(
|
|
1397
1447
|
ready,
|
|
@@ -1488,10 +1538,10 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, te
|
|
|
1488
1538
|
);
|
|
1489
1539
|
}
|
|
1490
1540
|
if (waveCompleted === 0) {
|
|
1491
|
-
const
|
|
1541
|
+
const remaining2 = findReadyTickets(tickets).filter((t) => !failedKeys.has(
|
|
1492
1542
|
ticketKey(t.feature, t.num)
|
|
1493
1543
|
));
|
|
1494
|
-
if (
|
|
1544
|
+
if (remaining2.length === 0) {
|
|
1495
1545
|
d.log(
|
|
1496
1546
|
"\nNo tickets completed this wave and no ready tickets remain. Stopping."
|
|
1497
1547
|
);
|
|
@@ -1499,10 +1549,11 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, te
|
|
|
1499
1549
|
}
|
|
1500
1550
|
d.log(
|
|
1501
1551
|
`
|
|
1502
|
-
No tickets completed this wave. ${
|
|
1552
|
+
No tickets completed this wave. ${remaining2.length} ready ticket${remaining2.length === 1 ? "" : "s"} remain; continuing.`
|
|
1503
1553
|
);
|
|
1504
1554
|
}
|
|
1505
1555
|
}
|
|
1556
|
+
if (waveNum > 0) reportBlocked(d, tickets, failedKeys);
|
|
1506
1557
|
const remaining = tickets.filter(
|
|
1507
1558
|
(t) => t.status === "ready-for-agent"
|
|
1508
1559
|
);
|
|
@@ -1595,7 +1646,9 @@ async function runWavesIsolated(opts) {
|
|
|
1595
1646
|
let totalCompleted = 0;
|
|
1596
1647
|
let waveNum = 0;
|
|
1597
1648
|
const failedKeys = /* @__PURE__ */ new Set();
|
|
1649
|
+
const session = sessionTickets(opts.tickets);
|
|
1598
1650
|
while (true) {
|
|
1651
|
+
if (waveNum > 0) reportBlocked(d, opts.tickets, failedKeys);
|
|
1599
1652
|
const ready = findReadyTickets(opts.tickets).filter(
|
|
1600
1653
|
(t) => !failedKeys.has(
|
|
1601
1654
|
ticketKey(t.feature, t.num)
|
|
@@ -1603,12 +1656,10 @@ async function runWavesIsolated(opts) {
|
|
|
1603
1656
|
);
|
|
1604
1657
|
if (ready.length === 0) break;
|
|
1605
1658
|
waveNum++;
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
(t) => t.status === "ready-for-agent" && !readyKeys.has(ticketKey(t.feature, t.num))
|
|
1611
|
-
);
|
|
1659
|
+
if (waveNum === 1) {
|
|
1660
|
+
d.startRun(session.map(toRef));
|
|
1661
|
+
reportBlocked(d, opts.tickets, failedKeys);
|
|
1662
|
+
}
|
|
1612
1663
|
d.startWave(
|
|
1613
1664
|
waveNum,
|
|
1614
1665
|
totalWaves,
|
|
@@ -1617,12 +1668,7 @@ async function runWavesIsolated(opts) {
|
|
|
1617
1668
|
filename: t.filename,
|
|
1618
1669
|
feature: t.feature
|
|
1619
1670
|
})),
|
|
1620
|
-
runProgress(
|
|
1621
|
-
remaining2.map((t) => ({
|
|
1622
|
-
num: t.num,
|
|
1623
|
-
filename: t.filename,
|
|
1624
|
-
feature: t.feature
|
|
1625
|
-
}))
|
|
1671
|
+
runProgress(session)
|
|
1626
1672
|
);
|
|
1627
1673
|
const settled = await runPool(
|
|
1628
1674
|
ready,
|
|
@@ -1884,6 +1930,11 @@ async function runWavesIsolated(opts) {
|
|
|
1884
1930
|
failedKeys.add(
|
|
1885
1931
|
ticketKey(ticket.feature, ticket.num)
|
|
1886
1932
|
);
|
|
1933
|
+
d.setTicketStatus(
|
|
1934
|
+
ticket.num,
|
|
1935
|
+
"failed",
|
|
1936
|
+
ticket.feature
|
|
1937
|
+
);
|
|
1887
1938
|
d.log(
|
|
1888
1939
|
` ${ticket.filename}: merge failed, staying ready-for-agent`
|
|
1889
1940
|
);
|
|
@@ -1931,12 +1982,12 @@ Merge stopped at ${mergeResult.stoppedAt.branch}`
|
|
|
1931
1982
|
}
|
|
1932
1983
|
}
|
|
1933
1984
|
if (waveCompleted === 0) {
|
|
1934
|
-
const
|
|
1985
|
+
const remaining2 = findReadyTickets(
|
|
1935
1986
|
opts.tickets
|
|
1936
1987
|
).filter((t) => !failedKeys.has(
|
|
1937
1988
|
ticketKey(t.feature, t.num)
|
|
1938
1989
|
));
|
|
1939
|
-
if (
|
|
1990
|
+
if (remaining2.length === 0) {
|
|
1940
1991
|
d.log(
|
|
1941
1992
|
"\nNo tickets completed this wave and no ready tickets remain. Stopping."
|
|
1942
1993
|
);
|
|
@@ -1944,10 +1995,11 @@ Merge stopped at ${mergeResult.stoppedAt.branch}`
|
|
|
1944
1995
|
}
|
|
1945
1996
|
d.log(
|
|
1946
1997
|
`
|
|
1947
|
-
No tickets completed this wave. ${
|
|
1998
|
+
No tickets completed this wave. ${remaining2.length} ready ticket${remaining2.length === 1 ? "" : "s"} remain; continuing.`
|
|
1948
1999
|
);
|
|
1949
2000
|
}
|
|
1950
2001
|
}
|
|
2002
|
+
if (waveNum > 0) reportBlocked(d, opts.tickets, failedKeys);
|
|
1951
2003
|
const remaining = opts.tickets.filter(
|
|
1952
2004
|
(t) => t.status === "ready-for-agent"
|
|
1953
2005
|
);
|
|
@@ -1975,6 +2027,7 @@ export {
|
|
|
1975
2027
|
dryRun,
|
|
1976
2028
|
dryRunIsolated,
|
|
1977
2029
|
escalateEffort,
|
|
2030
|
+
findBlockedTickets,
|
|
1978
2031
|
findReadyTickets,
|
|
1979
2032
|
formatErrorMessage,
|
|
1980
2033
|
formatStreamLine,
|