claude-flow
Version:
Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration
617 lines • 23.8 kB
JavaScript
/**
* V3 CLI Workflow Command
* Workflow execution, validation, and template management
*/
import { output } from '../output.js';
import { select, confirm } from '../prompt.js';
import { callMCPTool, MCPClientError } from '../mcp-client.js';
// Workflow templates
const WORKFLOW_TEMPLATES = [
{ value: 'development', label: 'Development', hint: 'Standard development workflow' },
{ value: 'research', label: 'Research', hint: 'Research and analysis workflow' },
{ value: 'testing', label: 'Testing', hint: 'Comprehensive testing workflow' },
{ value: 'security-audit', label: 'Security Audit', hint: 'Security review workflow' },
{ value: 'code-review', label: 'Code Review', hint: 'Multi-agent code review' },
{ value: 'refactoring', label: 'Refactoring', hint: 'Code refactoring workflow' },
{ value: 'sparc', label: 'SPARC', hint: 'SPARC methodology workflow' },
{ value: 'custom', label: 'Custom', hint: 'Define custom workflow' }
];
// Run subcommand
const runCommand = {
name: 'run',
description: 'Execute a workflow',
options: [
{
name: 'template',
short: 't',
description: 'Workflow template',
type: 'string',
choices: WORKFLOW_TEMPLATES.map(t => t.value)
},
{
name: 'file',
short: 'f',
description: 'Workflow definition file (YAML/JSON)',
type: 'string'
},
{
name: 'task',
description: 'Task description',
type: 'string'
},
{
name: 'parallel',
short: 'p',
description: 'Enable parallel execution',
type: 'boolean',
default: true
},
{
name: 'max-agents',
short: 'm',
description: 'Maximum agents to spawn',
type: 'number',
default: 5
},
{
name: 'timeout',
description: 'Workflow timeout in minutes',
type: 'number',
default: 30
},
{
name: 'dry-run',
short: 'd',
description: 'Validate without executing',
type: 'boolean',
default: false
}
],
examples: [
{ command: 'claude-flow workflow run -t development --task "Build auth system"', description: 'Run development workflow' },
{ command: 'claude-flow workflow run -f ./workflow.yaml', description: 'Run from file' },
{ command: 'claude-flow workflow run -t sparc --dry-run', description: 'Validate SPARC workflow' }
],
action: async (ctx) => {
let template = ctx.flags.template;
const file = ctx.flags.file;
const task = ctx.flags.task || ctx.args[0];
const parallel = ctx.flags.parallel;
const maxAgents = ctx.flags.maxAgents;
const timeout = ctx.flags.timeout;
const dryRun = ctx.flags.dryRun;
if (!template && !file && ctx.interactive) {
template = await select({
message: 'Select workflow template:',
options: WORKFLOW_TEMPLATES
});
}
if (!template && !file) {
output.printError('Workflow template or file is required. Use --template or --file');
return { success: false, exitCode: 1 };
}
output.writeln();
if (dryRun) {
output.writeln(output.warning('DRY RUN MODE - No changes will be made'));
}
output.writeln(output.bold(`Workflow: ${template || file}`));
output.writeln();
const spinner = output.createSpinner({ text: 'Initializing workflow...', spinner: 'dots' });
try {
spinner.start();
// Call MCP tool to run workflow
const result = await callMCPTool('workflow_run', {
template: template || undefined,
file: file || undefined,
task,
options: {
parallel,
maxAgents,
timeout,
dryRun,
},
});
if (dryRun) {
spinner.succeed('Workflow validated successfully');
}
else {
spinner.succeed('Workflow started');
}
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
output.printBox([
`ID: ${result.workflowId}`,
`Template: ${result.template}`,
`Status: ${result.status}`,
`Stages: ${result.metrics.totalStages}`,
`Agents: ${result.metrics.agentsSpawned}`,
`Est. Duration: ${result.metrics.estimatedDuration}`
].join('\n'), 'Workflow Details');
output.writeln();
output.writeln(output.bold('Stages'));
output.printTable({
columns: [
{ key: 'name', header: 'Stage', width: 20 },
{ key: 'status', header: 'Status', width: 12, format: formatStageStatus },
{ key: 'agents', header: 'Agents', width: 30, format: (v) => Array.isArray(v) ? v.join(', ') : String(v) },
{ key: 'duration', header: 'Duration', width: 10, align: 'right', format: (v) => v ? `${v}ms` : '-' }
],
data: result.stages
});
if (!dryRun) {
output.writeln();
output.printInfo(`Track progress: claude-flow workflow status ${result.workflowId}`);
}
return { success: true, data: result };
}
catch (error) {
spinner.fail('Workflow failed');
if (error instanceof MCPClientError) {
output.printError(`Workflow error: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Validate subcommand
const validateCommand = {
name: 'validate',
description: 'Validate a workflow definition',
options: [
{
name: 'file',
short: 'f',
description: 'Workflow definition file',
type: 'string',
required: true
},
{
name: 'strict',
short: 's',
description: 'Strict validation mode',
type: 'boolean',
default: false
}
],
examples: [
{ command: 'claude-flow workflow validate -f ./workflow.yaml', description: 'Validate workflow file' },
{ command: 'claude-flow workflow validate -f ./workflow.json --strict', description: 'Strict validation' }
],
action: async (ctx) => {
const file = ctx.flags.file || ctx.args[0];
const strict = ctx.flags.strict;
if (!file) {
output.printError('Workflow file is required. Use --file or -f');
return { success: false, exitCode: 1 };
}
output.printInfo(`Validating: ${file}`);
try {
const result = await callMCPTool('workflow_validate', {
file,
strict,
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: result.valid, data: result };
}
output.writeln();
if (result.valid) {
output.printSuccess('Workflow is valid');
}
else {
output.printError('Workflow validation failed');
}
if (result.errors.length > 0) {
output.writeln();
output.writeln(output.bold(output.error('Errors')));
output.printTable({
columns: [
{ key: 'line', header: 'Line', width: 8, align: 'right' },
{ key: 'severity', header: 'Severity', width: 10 },
{ key: 'message', header: 'Message', width: 50 }
],
data: result.errors
});
}
if (result.warnings.length > 0) {
output.writeln();
output.writeln(output.bold(output.warning('Warnings')));
result.warnings.forEach(w => {
output.writeln(output.warning(` Line ${w.line}: ${w.message}`));
});
}
if (result.valid) {
output.writeln();
output.writeln(output.bold('Workflow Stats'));
output.printList([
`Stages: ${result.stats.stages}`,
`Agents Required: ${result.stats.agents}`,
`Est. Duration: ${result.stats.estimatedDuration}`
]);
}
return { success: result.valid, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Validation error: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// List subcommand
const listCommand = {
name: 'list',
aliases: ['ls'],
description: 'List workflows',
options: [
{
name: 'status',
short: 's',
description: 'Filter by status',
type: 'string',
choices: ['running', 'completed', 'failed', 'all']
},
{
name: 'limit',
short: 'l',
description: 'Maximum results',
type: 'number',
default: 10
}
],
action: async (ctx) => {
const status = ctx.flags.status;
const limit = ctx.flags.limit;
try {
const result = await callMCPTool('workflow_list', {
status: status || 'all',
limit,
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
output.writeln(output.bold('Workflows'));
output.writeln();
if (result.workflows.length === 0) {
output.printInfo('No workflows found');
return { success: true, data: result };
}
output.printTable({
columns: [
{ key: 'id', header: 'ID', width: 15 },
{ key: 'template', header: 'Template', width: 15 },
{ key: 'status', header: 'Status', width: 12, format: formatStageStatus },
{ key: 'progress', header: 'Progress', width: 10, align: 'right', format: (v) => `${v}%` },
{ key: 'startedAt', header: 'Started', width: 20, format: (v) => new Date(String(v)).toLocaleString() }
],
data: result.workflows
});
output.writeln();
output.printInfo(`Total: ${result.total} workflows`);
return { success: true, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Failed to list workflows: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Status subcommand
const statusCommand = {
name: 'status',
description: 'Show workflow status',
options: [
{
name: 'watch',
short: 'w',
description: 'Watch for changes',
type: 'boolean',
default: false
}
],
action: async (ctx) => {
const workflowId = ctx.args[0];
if (!workflowId) {
output.printError('Workflow ID is required');
return { success: false, exitCode: 1 };
}
try {
const result = await callMCPTool('workflow_status', {
workflowId,
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
output.printBox([
`ID: ${result.id}`,
`Template: ${result.template}`,
`Status: ${formatStageStatus(result.status)}`,
`Progress: ${result.progress}%`,
`Duration: ${(result.metrics.duration / 1000).toFixed(1)}s`,
`Tokens: ${result.metrics.tokensUsed.toLocaleString()}`,
`Agents: ${result.metrics.agentsSpawned}`
].join('\n'), 'Workflow Status');
output.writeln();
output.writeln(output.bold('Stage Progress'));
output.printTable({
columns: [
{ key: 'name', header: 'Stage', width: 20 },
{ key: 'status', header: 'Status', width: 12, format: formatStageStatus },
{ key: 'agents', header: 'Agents', width: 25, format: (v) => Array.isArray(v) ? v.length.toString() : '0' }
],
data: result.stages
});
return { success: true, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Failed to get status: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Stop subcommand
const stopCommand = {
name: 'stop',
description: 'Stop a running workflow',
options: [
{
name: 'force',
short: 'f',
description: 'Force stop without graceful shutdown',
type: 'boolean',
default: false
}
],
action: async (ctx) => {
const workflowId = ctx.args[0];
const force = ctx.flags.force;
if (!workflowId) {
output.printError('Workflow ID is required');
return { success: false, exitCode: 1 };
}
if (!force && ctx.interactive) {
const confirmed = await confirm({
message: `Stop workflow ${workflowId}?`,
default: false
});
if (!confirmed) {
output.printInfo('Operation cancelled');
return { success: true };
}
}
try {
const result = await callMCPTool('workflow_stop', {
workflowId,
graceful: !force,
});
output.printSuccess(`Workflow ${workflowId} stopped`);
return { success: true, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Failed to stop workflow: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Template subcommand
const templateCommand = {
name: 'template',
description: 'Manage workflow templates',
subcommands: [
{
name: 'list',
description: 'List available templates',
action: async (ctx) => {
if (ctx.flags.format === 'json') {
output.printJson(WORKFLOW_TEMPLATES);
return { success: true, data: WORKFLOW_TEMPLATES };
}
output.writeln();
output.writeln(output.bold('Available Workflow Templates'));
output.writeln();
output.printTable({
columns: [
{ key: 'value', header: 'Template', width: 20 },
{ key: 'label', header: 'Name', width: 20 },
{ key: 'hint', header: 'Description', width: 35 }
],
data: WORKFLOW_TEMPLATES
});
return { success: true, data: WORKFLOW_TEMPLATES };
}
},
{
name: 'show',
description: 'Show template details',
action: async (ctx) => {
const templateName = ctx.args[0];
if (!templateName) {
output.printError('Template name is required');
return { success: false, exitCode: 1 };
}
const template = WORKFLOW_TEMPLATES.find(t => t.value === templateName);
if (!template) {
output.printError(`Template "${templateName}" not found`);
return { success: false, exitCode: 1 };
}
// Show template details
const details = {
name: template.value,
description: template.hint,
stages: getTemplateStages(template.value),
agents: getTemplateAgents(template.value),
estimatedDuration: getTemplateDuration(template.value)
};
if (ctx.flags.format === 'json') {
output.printJson(details);
return { success: true, data: details };
}
output.writeln();
output.printBox([
`Name: ${details.name}`,
`Description: ${details.description}`,
`Stages: ${details.stages.length}`,
`Agents: ${details.agents.join(', ')}`,
`Est. Duration: ${details.estimatedDuration}`
].join('\n'), 'Template Details');
output.writeln();
output.writeln(output.bold('Stages'));
output.printList(details.stages.map((s, i) => `${i + 1}. ${s}`));
return { success: true, data: details };
}
},
{
name: 'create',
description: 'Create a new template from workflow',
options: [
{ name: 'name', short: 'n', description: 'Template name', type: 'string', required: true },
{ name: 'workflow', short: 'w', description: 'Workflow ID to save as template', type: 'string' },
{ name: 'file', short: 'f', description: 'Workflow file to save as template', type: 'string' }
],
action: async (ctx) => {
const name = ctx.flags.name;
if (!name) {
output.printError('Template name is required');
return { success: false, exitCode: 1 };
}
output.printSuccess(`Template "${name}" created`);
output.writeln(output.dim(' Use with: claude-flow workflow run -t ' + name));
return { success: true, data: { name, created: true } };
}
}
],
action: async () => {
output.writeln();
output.writeln(output.bold('Template Management'));
output.writeln();
output.writeln('Usage: claude-flow workflow template <subcommand>');
output.writeln();
output.writeln('Subcommands:');
output.printList([
`${output.highlight('list')} - List available templates`,
`${output.highlight('show')} - Show template details`,
`${output.highlight('create')} - Create new template`
]);
return { success: true };
}
};
// Main workflow command
export const workflowCommand = {
name: 'workflow',
description: 'Workflow execution and management',
subcommands: [runCommand, validateCommand, listCommand, statusCommand, stopCommand, templateCommand],
options: [],
examples: [
{ command: 'claude-flow workflow run -t development --task "Build feature"', description: 'Run workflow' },
{ command: 'claude-flow workflow validate -f ./workflow.yaml', description: 'Validate workflow' },
{ command: 'claude-flow workflow list', description: 'List workflows' }
],
action: async () => {
output.writeln();
output.writeln(output.bold('Workflow Commands'));
output.writeln();
output.writeln('Usage: claude-flow workflow <subcommand> [options]');
output.writeln();
output.writeln('Subcommands:');
output.printList([
`${output.highlight('run')} - Execute a workflow`,
`${output.highlight('validate')} - Validate workflow definition`,
`${output.highlight('list')} - List workflows`,
`${output.highlight('status')} - Show workflow status`,
`${output.highlight('stop')} - Stop running workflow`,
`${output.highlight('template')} - Manage templates`
]);
output.writeln();
output.writeln('Run "claude-flow workflow <subcommand> --help" for more info');
return { success: true };
}
};
// Helper functions
function formatStageStatus(status) {
const statusStr = String(status);
switch (statusStr) {
case 'completed':
case 'success':
return output.success(statusStr);
case 'running':
case 'in_progress':
return output.highlight(statusStr);
case 'pending':
case 'waiting':
return output.dim(statusStr);
case 'failed':
case 'error':
return output.error(statusStr);
case 'validated':
return output.success(statusStr);
default:
return statusStr;
}
}
function getTemplateStages(template) {
const stages = {
development: ['Planning', 'Implementation', 'Testing', 'Review', 'Integration'],
research: ['Discovery', 'Analysis', 'Synthesis', 'Documentation'],
testing: ['Unit Tests', 'Integration Tests', 'E2E Tests', 'Performance Tests'],
'security-audit': ['Threat Model', 'Static Analysis', 'Dynamic Analysis', 'Report'],
'code-review': ['Initial Review', 'Security Check', 'Quality Analysis', 'Feedback'],
refactoring: ['Analysis', 'Planning', 'Refactor', 'Validation'],
sparc: ['Specification', 'Pseudocode', 'Architecture', 'Refinement', 'Completion']
};
return stages[template] || ['Initialize', 'Execute', 'Complete'];
}
function getTemplateAgents(template) {
const agents = {
development: ['coder', 'tester', 'reviewer'],
research: ['researcher', 'analyst'],
testing: ['tester', 'coder'],
'security-audit': ['security-architect', 'security-auditor'],
'code-review': ['reviewer', 'security-auditor', 'analyst'],
refactoring: ['architect', 'coder', 'reviewer'],
sparc: ['architect', 'coder', 'tester', 'reviewer']
};
return agents[template] || ['coder'];
}
function getTemplateDuration(template) {
const durations = {
development: '15-30 min',
research: '10-20 min',
testing: '5-15 min',
'security-audit': '20-40 min',
'code-review': '10-25 min',
refactoring: '15-35 min',
sparc: '25-45 min'
};
return durations[template] || '10-20 min';
}
export default workflowCommand;
//# sourceMappingURL=workflow.js.map