feat(06-03): add merge and coordinate CLI commands

- Add cw merge queue <taskId> to queue tasks for merge
- Add cw merge status to show merge queue status
- Add cw merge next to show next task ready to merge
- Add cw coordinate [--target <branch>] to process all ready merges
This commit is contained in:
Lukas May
2026-01-30 21:13:29 +01:00
parent 1a4de26966
commit ae8d303e5e

View File

@@ -524,6 +524,109 @@ export function createCli(serverHandler?: (port?: number) => Promise<void>): Com
}
});
// Merge command group
const mergeCommand = program
.command('merge')
.description('Manage merge queue');
// cw merge queue <taskId>
mergeCommand
.command('queue <taskId>')
.description('Queue a completed task for merge')
.action(async (taskId: string) => {
try {
const client = createDefaultTrpcClient();
await client.queueMerge.mutate({ taskId });
console.log(`Task '${taskId}' queued for merge`);
} catch (error) {
console.error('Failed to queue for merge:', (error as Error).message);
process.exit(1);
}
});
// cw merge status
mergeCommand
.command('status')
.description('Show merge queue status')
.action(async () => {
try {
const client = createDefaultTrpcClient();
const state = await client.getMergeQueueStatus.query();
console.log('Merge Queue Status');
console.log('==================');
console.log(`Queued: ${state.queued.length}`);
console.log(`In Progress: ${state.inProgress.length}`);
console.log(`Merged: ${state.merged.length}`);
console.log(`Conflicted: ${state.conflicted.length}`);
if (state.conflicted.length > 0) {
console.log('');
console.log('Conflicts:');
for (const c of state.conflicted) {
console.log(` ${c.taskId}: ${c.conflicts.join(', ')}`);
}
}
} catch (error) {
console.error('Failed to get merge queue status:', (error as Error).message);
process.exit(1);
}
});
// cw merge next
mergeCommand
.command('next')
.description('Show next task ready to merge')
.action(async () => {
try {
const client = createDefaultTrpcClient();
const next = await client.getNextMergeable.query();
if (next) {
console.log(`Next mergeable: ${next.taskId} (priority: ${next.priority})`);
} else {
console.log('No tasks ready to merge');
}
} catch (error) {
console.error('Failed to get next mergeable:', (error as Error).message);
process.exit(1);
}
});
// Coordinate command - process all ready merges
program
.command('coordinate')
.description('Process all ready merges in dependency order')
.option('-t, --target <branch>', 'Target branch for merges', 'main')
.action(async (options: { target: string }) => {
try {
const client = createDefaultTrpcClient();
console.log(`Processing merges to ${options.target}...`);
const { results } = await client.processMerges.mutate({
targetBranch: options.target,
});
const succeeded = results.filter(r => r.success).length;
const conflicted = results.filter(r => !r.success).length;
console.log('');
console.log('Results:');
console.log(` Merged: ${succeeded}`);
console.log(` Conflicts: ${conflicted}`);
if (conflicted > 0) {
console.log('');
console.log('Conflicted tasks (bounce-back tasks created):');
for (const r of results.filter(r => !r.success)) {
console.log(` ${r.taskId}: ${r.conflicts?.join(', ')}`);
}
}
} catch (error) {
console.error('Failed to process merges:', (error as Error).message);
process.exit(1);
}
});
return program;
}