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
This commit is contained in:
Lukas May
2026-02-02 13:43:52 +01:00
parent 8bbd313488
commit 8ad262d05e

View File

@@ -824,6 +824,126 @@ export function createCli(serverHandler?: (port?: number) => Promise<void>): Com
}
});
// Phase command group
const phaseCommand = program
.command('phase')
.description('Phase dependency and dispatch management');
// cw phase add-dependency --phase <id> --depends-on <id>
phaseCommand
.command('add-dependency')
.description('Add a dependency between phases')
.requiredOption('--phase <id>', 'Phase that depends on another')
.requiredOption('--depends-on <id>', '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 <phaseId>
phaseCommand
.command('dependencies <phaseId>')
.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 <phaseId>
phaseCommand
.command('queue <phaseId>')
.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')