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/dist/meta.json CHANGED
@@ -105,7 +105,7 @@
105
105
  "format": "esm"
106
106
  },
107
107
  "src/display.ts": {
108
- "bytes": 14240,
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": 78213,
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": 206097
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": 55078
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": 9023
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": 96640
512
+ "bytes": 99130
513
513
  }
514
514
  }
515
515
  }
@@ -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
- const readyKeys = new Set(
1376
- ready.map((t) => ticketKey(t.feature, t.num))
1377
- );
1378
- const remaining2 = tickets.filter(
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(tickets),
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 remaining3 = findReadyTickets(tickets).filter((t) => !failedKeys.has(
1541
+ const remaining2 = findReadyTickets(tickets).filter((t) => !failedKeys.has(
1492
1542
  ticketKey(t.feature, t.num)
1493
1543
  ));
1494
- if (remaining3.length === 0) {
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. ${remaining3.length} ready ticket${remaining3.length === 1 ? "" : "s"} remain; continuing.`
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
- const readyKeys = new Set(
1607
- ready.map((t) => ticketKey(t.feature, t.num))
1608
- );
1609
- const remaining2 = opts.tickets.filter(
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(opts.tickets),
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 remaining3 = findReadyTickets(
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 (remaining3.length === 0) {
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. ${remaining3.length} ready ticket${remaining3.length === 1 ? "" : "s"} remain; continuing.`
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,