nightralph 0.0.8 → 0.0.9

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/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport yargs from 'yargs'\nimport { hideBin } from 'yargs/helpers'\nimport { select } from '@inquirer/prompts'\nimport { setup, checkGhAuth, type TrackerKind } from './setup.js'\nimport {\n scanTickets,\n dryRun,\n dryRunIsolated,\n runWaves,\n runWavesIsolated,\n} from './orchestrator.js'\nimport {\n findFeatureDirs,\n resolveIssuesDir,\n getScriptDir,\n} from './resolve.js'\nimport { getRepoRoot, getRepoName } from './worktree.js'\n\nexport { findFeatureDirs, resolveIssuesDir }\n\nconst scriptDir = getScriptDir(import.meta.url)\n\nyargs(hideBin(process.argv))\n .scriptName('nightralph')\n .usage('$0 <command>')\n .command(\n 'setup',\n 'Install bundled skills and doc templates',\n y => y.option('claude', {\n type:'boolean',\n describe:'Also create .claude/skills/ symlinks',\n default:false,\n }),\n async argv => {\n const tracker = await select<TrackerKind>({\n message:'Issue tracker format',\n choices:[\n {\n name:'Local Markdown',\n value:'local' as const,\n description:'Issues as .md files in ' +\n '.scratch/',\n },\n {\n name:'GitHub Issues',\n value:'github' as const,\n description:'Issues via gh CLI',\n },\n ],\n })\n\n if (tracker === 'github') {\n if (!checkGhAuth()) {\n console.error(\n 'GitHub Issues requires the gh CLI, ' +\n 'authenticated.\\n' +\n 'Install: https://cli.github.com\\n' +\n 'Then run: gh auth login'\n )\n process.exit(2)\n }\n }\n\n setup({\n bundledSkillsDir:join(scriptDir, 'skills'),\n docsTemplatesDir:join(\n scriptDir, 'docs-templates'\n ),\n projectDir:process.cwd(),\n claude:argv.claude,\n tracker,\n })\n }\n )\n .command(\n '$0 <provider>',\n 'Execute tickets with the given provider',\n y => y\n .positional('provider', {\n type:'string',\n describe:'Agent CLI to spawn ' +\n '(e.g. claude, codex, pi)',\n demandOption:true,\n })\n .option('m', {\n alias:'model',\n type:'string',\n describe:'Model passed to the agent',\n })\n .option('spec', {\n type:'string',\n describe:'Feature name ' +\n '(resolves .scratch/<name>/)',\n })\n .option('dry-run', {\n type:'boolean',\n describe:'Show wave order without running',\n default:false,\n })\n .option('timeout', {\n type:'number',\n describe:'Kill agent after N seconds',\n default:900,\n })\n .option('X', {\n type:'boolean',\n describe:'Stop on merge conflict instead of ' +\n 're-spawning',\n default:false,\n }),\n async argv => {\n await runExecute({\n provider:argv.provider as string,\n model:argv.model as string|undefined,\n spec:argv.spec as string|undefined,\n dryRun:argv.dryRun as boolean|undefined,\n timeout:argv.timeout as\n number|undefined,\n stopOnConflict:argv.X as\n boolean|undefined,\n })\n }\n )\n .strict()\n .demandCommand(1, 'Specify a command or provider')\n .help()\n .parse()\n\nfunction isGitRepo ():boolean {\n try {\n getRepoRoot()\n return true\n } catch {\n return false\n }\n}\n\nfunction extractFeatureName (spec:string):string {\n const parts = spec.split(\n /[\\\\/]/\n )\n const scratchIndex = parts.indexOf('.scratch')\n if (scratchIndex >= 0 && scratchIndex < parts.length - 1) {\n return parts[scratchIndex + 1]\n }\n return parts[parts.length - 2] ??\n parts[parts.length - 1] ?? 'feature'\n}\n\nasync function runExecute (argv:{\n provider:string\n model?:string\n spec?:string\n dryRun?:boolean\n timeout?:number\n stopOnConflict?:boolean\n}):Promise<void> {\n const { dir, spec } = await resolveIssuesDir(\n argv.spec\n )\n\n if (!existsSync(dir)) {\n console.error(`Directory not found: ${dir}`)\n process.exit(2)\n }\n if (!existsSync(spec)) {\n console.error(`Spec file not found: ${spec}`)\n process.exit(2)\n }\n\n const specBody = readFileSync(spec, 'utf8')\n const tickets = scanTickets(dir)\n\n if (tickets.length === 0) {\n console.log('No ticket files found. Exiting.')\n return\n }\n\n console.log(`Found ${tickets.length} tickets`)\n\n const knownNums = new Set(tickets.map(t => t.num))\n for (const t of tickets) {\n const unknown = t.blockers.filter(\n b => !knownNums.has(b)\n )\n if (unknown.length > 0) {\n console.warn(\n `${t.filename}: blockers ` +\n `[${unknown.join(', ')}] ` +\n 'not found in directory'\n )\n }\n t.blockers = t.blockers.filter(\n b => knownNums.has(b)\n )\n }\n\n const logsDir = join(dirname(dir), 'logs')\n const timeout = argv.timeout ?? 300\n const featureName = extractFeatureName(spec)\n const conflictStrategy = argv.stopOnConflict ?\n 'stop' :\n 'respawn'\n const progressPath = join(\n dirname(dir), 'progress.md'\n )\n\n if (argv.dryRun) {\n if (isGitRepo()) {\n const repoName = getRepoName()\n dryRunIsolated({\n tickets,\n specBody,\n repoName,\n conflictStrategy,\n progressPath,\n })\n } else {\n dryRun(tickets, specBody)\n }\n return\n }\n\n let result:{ allDone:boolean }\n\n if (isGitRepo()) {\n result = await runWavesIsolated({\n tickets,\n specBody,\n agentCmd:argv.provider,\n model:argv.model ?? null,\n timeout,\n logsDir,\n featureName,\n conflictStrategy,\n })\n } else {\n result = await runWaves(\n tickets, specBody,\n argv.provider, argv.model ?? null,\n timeout,\n logsDir,\n )\n }\n\n if (!result.allDone) {\n process.exit(1)\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["#!/usr/bin/env node\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport yargs from 'yargs'\nimport { hideBin } from 'yargs/helpers'\nimport { select } from '@inquirer/prompts'\nimport { setup, checkGhAuth, type TrackerKind } from './setup.js'\nimport {\n scanTickets,\n dryRun,\n dryRunIsolated,\n runWaves,\n runWavesIsolated,\n} from './orchestrator.js'\nimport { createDisplay } from './display.js'\nimport {\n findFeatureDirs,\n resolveIssuesDir,\n getScriptDir,\n} from './resolve.js'\nimport { getRepoRoot, getRepoName } from './worktree.js'\n\nexport { findFeatureDirs, resolveIssuesDir }\n\nconst scriptDir = getScriptDir(import.meta.url)\n\nyargs(hideBin(process.argv))\n .scriptName('nightralph')\n .usage('$0 <command>')\n .command(\n 'setup',\n 'Install bundled skills and doc templates',\n y => y.option('claude', {\n type:'boolean',\n describe:'Also create .claude/skills/ symlinks',\n default:false,\n }),\n async argv => {\n const tracker = await select<TrackerKind>({\n message:'Issue tracker format',\n choices:[\n {\n name:'Local Markdown',\n value:'local' as const,\n description:'Issues as .md files in ' +\n '.scratch/',\n },\n {\n name:'GitHub Issues',\n value:'github' as const,\n description:'Issues via gh CLI',\n },\n ],\n })\n\n if (tracker === 'github') {\n if (!checkGhAuth()) {\n console.error(\n 'GitHub Issues requires the gh CLI, ' +\n 'authenticated.\\n' +\n 'Install: https://cli.github.com\\n' +\n 'Then run: gh auth login'\n )\n process.exit(2)\n }\n }\n\n setup({\n bundledSkillsDir:join(scriptDir, 'skills'),\n docsTemplatesDir:join(\n scriptDir, 'docs-templates'\n ),\n projectDir:process.cwd(),\n claude:argv.claude,\n tracker,\n })\n }\n )\n .command(\n '$0 <provider>',\n 'Execute tickets with the given provider',\n y => y\n .positional('provider', {\n type:'string',\n describe:'Agent CLI to spawn ' +\n '(e.g. claude, codex, pi)',\n demandOption:true,\n })\n .option('m', {\n alias:'model',\n type:'string',\n describe:'Model passed to the agent',\n })\n .option('spec', {\n type:'string',\n describe:'Feature name ' +\n '(resolves .scratch/<name>/)',\n })\n .option('dry-run', {\n type:'boolean',\n describe:'Show wave order without running',\n default:false,\n })\n .option('timeout', {\n type:'number',\n describe:'Kill agent after N seconds',\n default:900,\n })\n .option('X', {\n type:'boolean',\n describe:'Stop on merge conflict instead of ' +\n 're-spawning',\n default:false,\n }),\n async argv => {\n await runExecute({\n provider:argv.provider as string,\n model:argv.model as string|undefined,\n spec:argv.spec as string|undefined,\n dryRun:argv.dryRun as boolean|undefined,\n timeout:argv.timeout as\n number|undefined,\n stopOnConflict:argv.X as\n boolean|undefined,\n })\n }\n )\n .strict()\n .demandCommand(1, 'Specify a command or provider')\n .help()\n .parse()\n\nfunction isGitRepo ():boolean {\n try {\n getRepoRoot()\n return true\n } catch {\n return false\n }\n}\n\nfunction extractFeatureName (spec:string):string {\n const parts = spec.split(\n /[\\\\/]/\n )\n const scratchIndex = parts.indexOf('.scratch')\n if (scratchIndex >= 0 && scratchIndex < parts.length - 1) {\n return parts[scratchIndex + 1]\n }\n return parts[parts.length - 2] ??\n parts[parts.length - 1] ?? 'feature'\n}\n\nasync function runExecute (argv:{\n provider:string\n model?:string\n spec?:string\n dryRun?:boolean\n timeout?:number\n stopOnConflict?:boolean\n}):Promise<void> {\n const { dir, spec } = await resolveIssuesDir(\n argv.spec\n )\n\n if (!existsSync(dir)) {\n console.error(`Directory not found: ${dir}`)\n process.exit(2)\n }\n if (!existsSync(spec)) {\n console.error(`Spec file not found: ${spec}`)\n process.exit(2)\n }\n\n const specBody = readFileSync(spec, 'utf8')\n const tickets = scanTickets(dir)\n\n if (tickets.length === 0) {\n console.log('No ticket files found. Exiting.')\n return\n }\n\n console.log(`Found ${tickets.length} tickets`)\n\n const knownNums = new Set(tickets.map(t => t.num))\n for (const t of tickets) {\n const unknown = t.blockers.filter(\n b => !knownNums.has(b)\n )\n if (unknown.length > 0) {\n console.warn(\n `${t.filename}: blockers ` +\n `[${unknown.join(', ')}] ` +\n 'not found in directory'\n )\n }\n t.blockers = t.blockers.filter(\n b => knownNums.has(b)\n )\n }\n\n const logsDir = join(dirname(dir), 'logs')\n const timeout = argv.timeout ?? 300\n const featureName = extractFeatureName(spec)\n const conflictStrategy = argv.stopOnConflict ?\n 'stop' :\n 'respawn'\n const progressPath = join(\n dirname(dir), 'progress.md'\n )\n\n if (argv.dryRun) {\n if (isGitRepo()) {\n const repoName = getRepoName()\n dryRunIsolated({\n tickets,\n specBody,\n repoName,\n conflictStrategy,\n progressPath,\n })\n } else {\n dryRun(tickets, specBody)\n }\n return\n }\n\n const display = createDisplay()\n let result:{ allDone:boolean }\n\n if (isGitRepo()) {\n result = await runWavesIsolated({\n tickets,\n specBody,\n agentCmd:argv.provider,\n model:argv.model ?? null,\n timeout,\n logsDir,\n featureName,\n conflictStrategy,\n display,\n })\n } else {\n result = await runWaves(\n tickets, specBody,\n argv.provider, argv.model ?? null,\n timeout,\n logsDir,\n display,\n )\n }\n\n if (!result.allDone) {\n process.exit(1)\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/meta.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "inputs": {
3
3
  "src/index.ts": {
4
- "bytes": 7011,
4
+ "bytes": 7134,
5
5
  "imports": [],
6
6
  "format": "esm"
7
7
  },
8
8
  "src/orchestrator.ts": {
9
- "bytes": 30365,
9
+ "bytes": 33865,
10
10
  "imports": [],
11
11
  "format": "esm"
12
12
  },
@@ -34,6 +34,11 @@
34
34
  "bytes": 6529,
35
35
  "imports": [],
36
36
  "format": "esm"
37
+ },
38
+ "src/display.ts": {
39
+ "bytes": 10791,
40
+ "imports": [],
41
+ "format": "esm"
37
42
  }
38
43
  },
39
44
  "outputs": {
@@ -41,7 +46,7 @@
41
46
  "imports": [],
42
47
  "exports": [],
43
48
  "inputs": {},
44
- "bytes": 10940
49
+ "bytes": 11126
45
50
  },
46
51
  "dist/index.js": {
47
52
  "imports": [
@@ -80,6 +85,11 @@
80
85
  "kind": "import-statement",
81
86
  "external": true
82
87
  },
88
+ {
89
+ "path": "./display.js",
90
+ "kind": "import-statement",
91
+ "external": true
92
+ },
83
93
  {
84
94
  "path": "./resolve.js",
85
95
  "kind": "import-statement",
@@ -98,16 +108,16 @@
98
108
  "entryPoint": "src/index.ts",
99
109
  "inputs": {
100
110
  "src/index.ts": {
101
- "bytesInOutput": 5227
111
+ "bytesInOutput": 5338
102
112
  }
103
113
  },
104
- "bytes": 5460
114
+ "bytes": 5571
105
115
  },
106
116
  "dist/orchestrator.js.map": {
107
117
  "imports": [],
108
118
  "exports": [],
109
119
  "inputs": {},
110
- "bytes": 47352
120
+ "bytes": 52948
111
121
  },
112
122
  "dist/orchestrator.js": {
113
123
  "imports": [
@@ -150,10 +160,17 @@
150
160
  "path": "./merge.js",
151
161
  "kind": "import-statement",
152
162
  "external": true
163
+ },
164
+ {
165
+ "path": "./display.js",
166
+ "kind": "import-statement",
167
+ "external": true
153
168
  }
154
169
  ],
155
170
  "exports": [
171
+ "COMPLETED_RE",
156
172
  "TICKET_RE",
173
+ "checkItems",
157
174
  "createPiStreamFormatter",
158
175
  "dryRun",
159
176
  "dryRunIsolated",
@@ -174,10 +191,10 @@
174
191
  "entryPoint": "src/orchestrator.ts",
175
192
  "inputs": {
176
193
  "src/orchestrator.ts": {
177
- "bytesInOutput": 23243
194
+ "bytesInOutput": 26158
178
195
  }
179
196
  },
180
- "bytes": 23717
197
+ "bytes": 26662
181
198
  },
182
199
  "dist/setup.js.map": {
183
200
  "imports": [],
@@ -381,6 +398,30 @@
381
398
  }
382
399
  },
383
400
  "bytes": 4616
401
+ },
402
+ "dist/display.js.map": {
403
+ "imports": [],
404
+ "exports": [],
405
+ "inputs": {},
406
+ "bytes": 16701
407
+ },
408
+ "dist/display.js": {
409
+ "imports": [],
410
+ "exports": [
411
+ "PlainDisplay",
412
+ "StatusDisplay",
413
+ "ansi",
414
+ "color",
415
+ "createDisplay",
416
+ "renderDot"
417
+ ],
418
+ "entryPoint": "src/display.ts",
419
+ "inputs": {
420
+ "src/display.ts": {
421
+ "bytesInOutput": 7276
422
+ }
423
+ },
424
+ "bytes": 7532
384
425
  }
385
426
  }
386
427
  }
@@ -35,6 +35,9 @@ import {
35
35
  mergeWave,
36
36
  updateWorktreeFromBase
37
37
  } from "./merge.js";
38
+ import {
39
+ createDisplay
40
+ } from "./display.js";
38
41
  function formatErrorMessage(error) {
39
42
  return error instanceof Error ? error.message : String(error);
40
43
  }
@@ -97,6 +100,27 @@ function findReadyTickets(tickets) {
97
100
  });
98
101
  }
99
102
  __name(findReadyTickets, "findReadyTickets");
103
+ const COMPLETED_RE = /^\[x\]\s+(.+)$/i;
104
+ function checkItems(ticket, completedItems) {
105
+ if (completedItems.length === 0) return;
106
+ let body = ticket.body;
107
+ for (const item of completedItems) {
108
+ const escaped = item.replace(
109
+ /[.*+?^${}()|[\]\\]/g,
110
+ "\\$&"
111
+ );
112
+ body = body.replace(
113
+ new RegExp(
114
+ `- \\[ \\] ${escaped}`,
115
+ "i"
116
+ ),
117
+ `- [x] ${item}`
118
+ );
119
+ }
120
+ writeFileSync(ticket.filepath, body);
121
+ ticket.body = body;
122
+ }
123
+ __name(checkItems, "checkItems");
100
124
  function markDone(ticket) {
101
125
  const updated = ticket.body.replace(
102
126
  /\*\*Status:\*\*\s*.+/i,
@@ -124,6 +148,7 @@ function renderPrompt(specBody, ticket) {
124
148
  "- Do NOT research external APIs or services via web search. Use the spec and ticket body as your sole reference for API shapes.",
125
149
  "- Start writing code as soon as you understand the immediate context. You can read more files as needed while implementing.",
126
150
  "- Commit your work when finished.",
151
+ "- After completing each checklist item in the ticket, print a line: [x] <item text> (matching the checklist text exactly).",
127
152
  ""
128
153
  ].join("\n");
129
154
  }
@@ -336,26 +361,55 @@ function spawnAgent(opts) {
336
361
  });
337
362
  proc.stdout.pipe(logStream);
338
363
  const piFormatter = isPi ? createPiStreamFormatter() : null;
364
+ const completedItems = [];
365
+ function collectCompleted(text) {
366
+ const match = text.match(COMPLETED_RE);
367
+ if (match) completedItems.push(match[1]);
368
+ }
369
+ __name(collectCompleted, "collectCompleted");
339
370
  const rl = createInterface({ input: proc.stdout });
340
371
  rl.on("line", (line) => {
341
372
  if (isClaude) {
342
373
  const formatted = formatStreamLine(line);
343
374
  if (formatted !== null) {
344
- console.log(`${prefix}${formatted}`);
375
+ collectCompleted(formatted);
376
+ const output = `${prefix}${formatted}`;
377
+ if (opts.onLine) {
378
+ opts.onLine(output);
379
+ } else {
380
+ console.log(output);
381
+ }
345
382
  }
346
383
  } else if (piFormatter) {
347
384
  const formatted = piFormatter(line);
348
385
  if (formatted !== null) {
349
- console.log(`${prefix}${formatted}`);
386
+ collectCompleted(formatted);
387
+ const output = `${prefix}${formatted}`;
388
+ if (opts.onLine) {
389
+ opts.onLine(output);
390
+ } else {
391
+ console.log(output);
392
+ }
350
393
  }
351
394
  } else {
352
- console.log(`${prefix}${line}`);
395
+ collectCompleted(line);
396
+ const output = `${prefix}${line}`;
397
+ if (opts.onLine) {
398
+ opts.onLine(output);
399
+ } else {
400
+ console.log(output);
401
+ }
353
402
  }
354
403
  });
355
404
  proc.stderr.pipe(logStream);
356
405
  const rlErr = createInterface({ input: proc.stderr });
357
406
  rlErr.on("line", (line) => {
358
- console.error(`${prefix}${line}`);
407
+ const output = `${prefix}${line}`;
408
+ if (opts.onLine) {
409
+ opts.onLine(output);
410
+ } else {
411
+ console.error(output);
412
+ }
359
413
  });
360
414
  const timeout = setTimeout(() => {
361
415
  if (!proc.killed) {
@@ -376,7 +430,10 @@ function spawnAgent(opts) {
376
430
  rlErr.close();
377
431
  logStream.end();
378
432
  if (promptFile) cleanupPromptFile(promptFile);
379
- resolve(code ?? 1);
433
+ resolve({
434
+ exitCode: code ?? 1,
435
+ completedItems
436
+ });
380
437
  });
381
438
  proc.on("error", (err) => {
382
439
  clearTimeout(timeout);
@@ -388,7 +445,7 @@ function spawnAgent(opts) {
388
445
  "Agent process error:",
389
446
  formatErrorMessage(err)
390
447
  );
391
- resolve(1);
448
+ resolve({ exitCode: 1, completedItems });
392
449
  });
393
450
  });
394
451
  }
@@ -467,9 +524,12 @@ async function respawnAndRetryMerge(opts) {
467
524
  opts.worktreePath,
468
525
  opts.baseBranch
469
526
  );
470
- console.log(
471
- ` ${opts.ticket.filename}: respawning agent to resolve merge conflict`
472
- );
527
+ const msg1 = ` ${opts.ticket.filename}: respawning agent to resolve merge conflict`;
528
+ if (opts.onLine) {
529
+ opts.onLine(msg1);
530
+ } else {
531
+ console.log(msg1);
532
+ }
473
533
  const prompt = renderPrompt(opts.specBody, opts.ticket);
474
534
  const logPath = join(
475
535
  opts.logsDir,
@@ -479,19 +539,23 @@ async function respawnAndRetryMerge(opts) {
479
539
  /\.md$/,
480
540
  ""
481
541
  );
482
- const exitCode = await spawnAgent({
542
+ const result = await spawnAgent({
483
543
  prompt,
484
544
  agentCmd: opts.agentCmd,
485
545
  model: opts.model,
486
546
  timeout: opts.timeout,
487
547
  logPath,
488
548
  cwd: opts.worktreePath,
489
- label
549
+ label,
550
+ onLine: opts.onLine
490
551
  });
491
- if (exitCode !== 0) {
492
- console.log(
493
- ` ${opts.ticket.filename}: respawned agent exited ${exitCode}`
494
- );
552
+ if (result.exitCode !== 0) {
553
+ const msg2 = ` ${opts.ticket.filename}: respawned agent exited ${result.exitCode}`;
554
+ if (opts.onLine) {
555
+ opts.onLine(msg2);
556
+ } else {
557
+ console.log(msg2);
558
+ }
495
559
  return false;
496
560
  }
497
561
  const merged = await mergeBranch(
@@ -499,24 +563,34 @@ async function respawnAndRetryMerge(opts) {
499
563
  opts.branch
500
564
  );
501
565
  if (!merged) {
502
- console.log(
503
- ` ${opts.ticket.filename}: merge still conflicted after respawn`
504
- );
566
+ const msg3 = ` ${opts.ticket.filename}: merge still conflicted after respawn`;
567
+ if (opts.onLine) {
568
+ opts.onLine(msg3);
569
+ } else {
570
+ console.log(msg3);
571
+ }
505
572
  }
506
573
  return merged;
507
574
  }
508
575
  __name(respawnAndRetryMerge, "respawnAndRetryMerge");
509
- async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir) {
576
+ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, display) {
510
577
  mkdirSync(logsDir, { recursive: true });
578
+ const d = display ?? createDisplay();
579
+ let totalWaves = 0;
580
+ for (const _ of simulateWaves(tickets)) totalWaves++;
511
581
  let totalCompleted = 0;
512
582
  let waveNum = 0;
513
583
  while (true) {
514
584
  const ready = findReadyTickets(tickets);
515
585
  if (ready.length === 0) break;
516
586
  waveNum++;
517
- console.log(
518
- `
519
- Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
587
+ d.startWave(
588
+ waveNum,
589
+ totalWaves,
590
+ ready.map((t) => ({
591
+ num: t.num,
592
+ filename: t.filename
593
+ }))
520
594
  );
521
595
  const results = await Promise.allSettled(
522
596
  ready.map(async (t) => {
@@ -529,37 +603,42 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
529
603
  /\.md$/,
530
604
  ""
531
605
  );
532
- console.log(` Starting ${t.filename}`);
533
- const code = await spawnAgent({
606
+ d.setTicketStatus(t.num, "in-progress");
607
+ d.log(` Starting ${t.filename}`);
608
+ const result = await spawnAgent({
534
609
  prompt,
535
610
  agentCmd,
536
611
  model,
537
612
  timeout,
538
613
  logPath,
539
- label
614
+ label,
615
+ onLine: /* @__PURE__ */ __name((line) => d.log(line), "onLine")
540
616
  });
541
- return { ticket: t, exitCode: code };
617
+ return { ticket: t, result };
542
618
  })
543
619
  );
544
620
  let waveCompleted = 0;
545
621
  for (const r of results) {
546
622
  if (r.status === "rejected") continue;
547
- const { ticket, exitCode } = r.value;
548
- if (exitCode !== 0) {
549
- console.log(
550
- ` ${ticket.filename}: agent exited ${exitCode}`
623
+ const { ticket, result } = r.value;
624
+ if (result.exitCode !== 0) {
625
+ d.setTicketStatus(ticket.num, "failed");
626
+ d.log(
627
+ ` ${ticket.filename}: agent exited ${result.exitCode}`
551
628
  );
552
629
  continue;
553
630
  }
631
+ checkItems(ticket, result.completedItems);
554
632
  markDone(ticket);
555
633
  waveCompleted++;
556
634
  totalCompleted++;
557
- console.log(
635
+ d.setTicketStatus(ticket.num, "done");
636
+ d.log(
558
637
  ` ${ticket.filename}: done`
559
638
  );
560
639
  }
561
640
  if (waveCompleted === 0) {
562
- console.log(
641
+ d.log(
563
642
  "\nNo tickets completed this wave. Stopping."
564
643
  );
565
644
  break;
@@ -568,15 +647,19 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
568
647
  const remaining = tickets.filter(
569
648
  (t) => t.status === "ready-for-agent"
570
649
  );
571
- console.log(
650
+ d.log(
572
651
  `
573
652
  Finished: ${totalCompleted} completed` + (remaining.length > 0 ? `, ${remaining.length} remaining` : "")
574
653
  );
654
+ d.cleanup();
575
655
  return { allDone: remaining.length === 0 };
576
656
  }
577
657
  __name(runWaves, "runWaves");
578
658
  async function runWavesIsolated(opts) {
579
659
  mkdirSync(opts.logsDir, { recursive: true });
660
+ const d = opts.display ?? createDisplay();
661
+ let totalWaves = 0;
662
+ for (const _ of simulateWaves(opts.tickets)) totalWaves++;
580
663
  const repoRoot = getRepoRoot();
581
664
  const repoName = getRepoName();
582
665
  const baseBranch = getCurrentBranch();
@@ -600,9 +683,13 @@ async function runWavesIsolated(opts) {
600
683
  const ready = findReadyTickets(opts.tickets);
601
684
  if (ready.length === 0) break;
602
685
  waveNum++;
603
- console.log(
604
- `
605
- Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
686
+ d.startWave(
687
+ waveNum,
688
+ totalWaves,
689
+ ready.map((t) => ({
690
+ num: t.num,
691
+ filename: t.filename
692
+ }))
606
693
  );
607
694
  const worktreeSettled = await Promise.allSettled(
608
695
  ready.map((t) => createWorktree({
@@ -618,7 +705,8 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
618
705
  const settled = worktreeSettled[i];
619
706
  const ticket = ready[i];
620
707
  if (settled.status === "rejected") {
621
- console.log(
708
+ d.setTicketStatus(ticket.num, "failed");
709
+ d.log(
622
710
  ` ${ticket.filename}: failed to create worktree: ${formatErrorMessage(settled.reason)}`
623
711
  );
624
712
  continue;
@@ -637,7 +725,8 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
637
725
  /\.md$/,
638
726
  ""
639
727
  );
640
- console.log(` Starting ${t.filename}`);
728
+ d.setTicketStatus(t.num, "in-progress");
729
+ d.log(` Starting ${t.filename}`);
641
730
  return spawnAgent({
642
731
  prompt,
643
732
  agentCmd: opts.agentCmd,
@@ -645,7 +734,8 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
645
734
  timeout: opts.timeout,
646
735
  logPath,
647
736
  cwd: worktrees[i].worktreePath,
648
- label
737
+ label,
738
+ onLine: /* @__PURE__ */ __name((line) => d.log(line), "onLine")
649
739
  });
650
740
  })
651
741
  );
@@ -656,16 +746,18 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
656
746
  for (let i = 0; i < results.length; i++) {
657
747
  const r = results[i];
658
748
  if (r.status === "rejected") continue;
659
- const exitCode = r.value;
749
+ const agentResult = r.value;
660
750
  const ticket = activeTickets[i];
661
751
  const worktree = worktrees[i];
662
- if (exitCode !== 0) {
663
- console.log(
664
- ` ${ticket.filename}: agent exited ${exitCode}`
752
+ if (agentResult.exitCode !== 0) {
753
+ d.setTicketStatus(ticket.num, "failed");
754
+ d.log(
755
+ ` ${ticket.filename}: agent exited ${agentResult.exitCode}`
665
756
  );
757
+ checkItems(ticket, agentResult.completedItems);
666
758
  checkTicket(progress, ticket.num, {
667
759
  branch: worktree.branchName,
668
- exitCode,
760
+ exitCode: agentResult.exitCode,
669
761
  done: false
670
762
  });
671
763
  writeProgress(progressPath, progress);
@@ -677,7 +769,7 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
677
769
  try {
678
770
  await removeWorktree(worktree.worktreePath);
679
771
  } catch (err) {
680
- console.error(
772
+ d.log(
681
773
  ` Failed to remove worktree ${worktree.worktreePath}: ` + formatErrorMessage(err)
682
774
  );
683
775
  }
@@ -689,7 +781,7 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
689
781
  `nightralph: auto-commit ${ticket.filename} remaining changes`
690
782
  );
691
783
  if (committed) {
692
- console.log(
784
+ d.log(
693
785
  ` ${ticket.filename}: auto-committed remaining changes`
694
786
  );
695
787
  }
@@ -699,12 +791,14 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
699
791
  baseBranch,
700
792
  worktree.branchName
701
793
  )) {
702
- console.log(
794
+ d.setTicketStatus(ticket.num, "failed");
795
+ d.log(
703
796
  ` ${ticket.filename}: agent exited 0 but made no changes; leaving ticket ready-for-agent`
704
797
  );
705
798
  continue;
706
799
  }
707
- console.log(
800
+ checkItems(ticket, agentResult.completedItems);
801
+ d.log(
708
802
  ` ${ticket.filename}: agent finished, awaiting merge`
709
803
  );
710
804
  ticketsByNum.set(ticket.num, ticket);
@@ -734,7 +828,8 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
734
828
  markDone(ticket);
735
829
  waveCompleted++;
736
830
  totalCompleted++;
737
- console.log(
831
+ d.setTicketStatus(ticket.num, "done");
832
+ d.log(
738
833
  ` ${ticket.filename}: done`
739
834
  );
740
835
  checkTicket(progress, ticket.num, {
@@ -754,7 +849,7 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
754
849
  try {
755
850
  await removeWorktree(worktreePath);
756
851
  } catch (err) {
757
- console.error(
852
+ d.log(
758
853
  ` Failed to remove worktree ${worktreePath}: ` + formatErrorMessage(err)
759
854
  );
760
855
  }
@@ -773,13 +868,15 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
773
868
  agentCmd: opts.agentCmd,
774
869
  model: opts.model,
775
870
  timeout: opts.timeout,
776
- logsDir: opts.logsDir
871
+ logsDir: opts.logsDir,
872
+ onLine: /* @__PURE__ */ __name((msg) => d.log(msg), "onLine")
777
873
  }) : false;
778
874
  if (recovered) {
779
875
  markDone(ticket);
780
876
  waveCompleted++;
781
877
  totalCompleted++;
782
- console.log(
878
+ d.setTicketStatus(ticket.num, "done");
879
+ d.log(
783
880
  ` ${ticket.filename}: done (resolved via respawn)`
784
881
  );
785
882
  checkTicket(progress, ticket.num, {
@@ -796,20 +893,20 @@ Wave ${waveNum}: ` + ready.map((t) => t.filename).join(", ")
796
893
  try {
797
894
  await removeWorktree(worktreePath);
798
895
  } catch (err) {
799
- console.error(
896
+ d.log(
800
897
  ` Failed to remove worktree ${worktreePath}: ` + formatErrorMessage(err)
801
898
  );
802
899
  }
803
900
  }
804
901
  } else {
805
- console.log(
902
+ d.log(
806
903
  ` ${ticket.filename}: merge failed, staying ready-for-agent`
807
904
  );
808
905
  }
809
906
  }
810
907
  }
811
908
  if (mergeResult.stoppedAt) {
812
- console.log(
909
+ d.log(
813
910
  `
814
911
  Merge stopped at ${mergeResult.stoppedAt.branch}`
815
912
  );
@@ -819,7 +916,7 @@ Merge stopped at ${mergeResult.stoppedAt.branch}`
819
916
  try {
820
917
  await removeWorktree(sb.worktreePath);
821
918
  } catch (err) {
822
- console.error(
919
+ d.log(
823
920
  ` Failed to remove worktree ${sb.worktreePath}: ` + formatErrorMessage(err)
824
921
  );
825
922
  }
@@ -828,7 +925,7 @@ Merge stopped at ${mergeResult.stoppedAt.branch}`
828
925
  }
829
926
  }
830
927
  if (waveCompleted === 0) {
831
- console.log(
928
+ d.log(
832
929
  "\nNo tickets completed this wave. Stopping."
833
930
  );
834
931
  break;
@@ -837,15 +934,18 @@ Merge stopped at ${mergeResult.stoppedAt.branch}`
837
934
  const remaining = opts.tickets.filter(
838
935
  (t) => t.status === "ready-for-agent"
839
936
  );
840
- console.log(
937
+ d.log(
841
938
  `
842
939
  Finished: ${totalCompleted} completed` + (remaining.length > 0 ? `, ${remaining.length} remaining` : "")
843
940
  );
941
+ d.cleanup();
844
942
  return { allDone: remaining.length === 0 };
845
943
  }
846
944
  __name(runWavesIsolated, "runWavesIsolated");
847
945
  export {
946
+ COMPLETED_RE,
848
947
  TICKET_RE,
948
+ checkItems,
849
949
  createPiStreamFormatter,
850
950
  dryRun,
851
951
  dryRunIsolated,