From ae8d303e5e284c7f1540fc28b4a9a71b87250dcc Mon Sep 17 00:00:00 2001 From: Lukas May Date: Fri, 30 Jan 2026 21:13:29 +0100 Subject: [PATCH] feat(06-03): add merge and coordinate CLI commands - Add cw merge queue 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 ] to process all ready merges --- src/cli/index.ts | 103 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/src/cli/index.ts b/src/cli/index.ts index 8d215be..ad1090c 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -524,6 +524,109 @@ export function createCli(serverHandler?: (port?: number) => Promise): Com } }); + // Merge command group + const mergeCommand = program + .command('merge') + .description('Manage merge queue'); + + // cw merge queue + mergeCommand + .command('queue ') + .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 ', '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; }