From 8ad262d05e8e0dc73dd138b414a6106c64059d0d Mon Sep 17 00:00:00 2001 From: Lukas May Date: Mon, 2 Feb 2026 13:43:52 +0100 Subject: [PATCH] feat(cli): add phase dependency and dispatch commands Add phase command group with dependency management and dispatch operations: - cw phase add-dependency: Create dependency between phases - cw phase dependencies: List dependencies for a phase - cw phase queue: Queue phase for execution - cw phase dispatch: Dispatch next available phase - cw phase queue-status: Show queued, ready, and blocked phases --- src/cli/index.ts | 120 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/src/cli/index.ts b/src/cli/index.ts index 7609557..b4a31c8 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -824,6 +824,126 @@ export function createCli(serverHandler?: (port?: number) => Promise): Com } }); + // Phase command group + const phaseCommand = program + .command('phase') + .description('Phase dependency and dispatch management'); + + // cw phase add-dependency --phase --depends-on + phaseCommand + .command('add-dependency') + .description('Add a dependency between phases') + .requiredOption('--phase ', 'Phase that depends on another') + .requiredOption('--depends-on ', 'Phase that must complete first') + .action(async (options: { phase: string; dependsOn: string }) => { + try { + const client = createDefaultTrpcClient(); + await client.createPhaseDependency.mutate({ + phaseId: options.phase, + dependsOnPhaseId: options.dependsOn, + }); + console.log(`Added dependency: phase ${options.phase} depends on ${options.dependsOn}`); + } catch (error) { + console.error('Failed to add dependency:', (error as Error).message); + process.exit(1); + } + }); + + // cw phase dependencies + phaseCommand + .command('dependencies ') + .description('List dependencies for a phase') + .action(async (phaseId: string) => { + try { + const client = createDefaultTrpcClient(); + const result = await client.getPhaseDependencies.query({ phaseId }); + if (result.dependencies.length === 0) { + console.log('No dependencies'); + return; + } + console.log('Dependencies:'); + for (const dep of result.dependencies) { + console.log(` ${dep}`); + } + } catch (error) { + console.error('Failed to get dependencies:', (error as Error).message); + process.exit(1); + } + }); + + // cw phase queue + phaseCommand + .command('queue ') + .description('Queue a phase for execution') + .action(async (phaseId: string) => { + try { + const client = createDefaultTrpcClient(); + await client.queuePhase.mutate({ phaseId }); + console.log(`Phase ${phaseId} queued for execution`); + } catch (error) { + console.error('Failed to queue phase:', (error as Error).message); + process.exit(1); + } + }); + + // cw phase dispatch + phaseCommand + .command('dispatch') + .description('Dispatch next available phase') + .action(async () => { + try { + const client = createDefaultTrpcClient(); + const result = await client.dispatchNextPhase.mutate(); + if (result.success) { + console.log('Phase dispatched successfully'); + console.log(` Phase: ${result.phaseId}`); + } else { + console.log('Dispatch failed'); + console.log(` Phase: ${result.phaseId || '(none)'}`); + console.log(` Reason: ${result.reason}`); + } + } catch (error) { + console.error('Failed to dispatch phase:', (error as Error).message); + process.exit(1); + } + }); + + // cw phase queue-status + phaseCommand + .command('queue-status') + .description('Show phase queue status') + .action(async () => { + try { + const client = createDefaultTrpcClient(); + const state = await client.getPhaseQueueState.query(); + + console.log('Phase Queue Status'); + console.log('=================='); + console.log(`Queued: ${state.queued.length}`); + console.log(`Ready: ${state.ready.length}`); + console.log(`Blocked: ${state.blocked.length}`); + + if (state.ready.length > 0) { + console.log(''); + console.log('Ready phases:'); + for (const phase of state.ready) { + console.log(` ${phase.phaseId}`); + } + } + + if (state.blocked.length > 0) { + console.log(''); + console.log('Blocked phases:'); + for (const bp of state.blocked) { + console.log(` ${bp.phaseId}: ${bp.reason}`); + } + } + } catch (error) { + console.error('Failed to get queue status:', (error as Error).message); + process.exit(1); + } + }); + // Plan command group const planCommand = program .command('plan')