UNPKG

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

481 lines 18.8 kB
/** * Workflow MCP Tools for CLI * * Tool definitions for workflow automation and orchestration. */ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'; import { join } from 'node:path'; // Storage paths const STORAGE_DIR = '.claude-flow'; const WORKFLOW_DIR = 'workflows'; const WORKFLOW_FILE = 'store.json'; function getWorkflowDir() { return join(process.cwd(), STORAGE_DIR, WORKFLOW_DIR); } function getWorkflowPath() { return join(getWorkflowDir(), WORKFLOW_FILE); } function ensureWorkflowDir() { const dir = getWorkflowDir(); if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } } function loadWorkflowStore() { try { const path = getWorkflowPath(); if (existsSync(path)) { const data = readFileSync(path, 'utf-8'); return JSON.parse(data); } } catch { // Return default store on error } return { workflows: {}, templates: {}, version: '3.0.0' }; } function saveWorkflowStore(store) { ensureWorkflowDir(); writeFileSync(getWorkflowPath(), JSON.stringify(store, null, 2), 'utf-8'); } export const workflowTools = [ { name: 'workflow_create', description: 'Create a new workflow', category: 'workflow', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Workflow name' }, description: { type: 'string', description: 'Workflow description' }, steps: { type: 'array', description: 'Workflow steps', items: { type: 'object', properties: { name: { type: 'string' }, type: { type: 'string', enum: ['task', 'condition', 'parallel', 'loop', 'wait'] }, config: { type: 'object' }, }, }, }, variables: { type: 'object', description: 'Initial variables' }, }, required: ['name'], }, handler: async (input) => { const store = loadWorkflowStore(); const workflowId = `workflow-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; const steps = (input.steps || []).map((s, i) => ({ stepId: `step-${i + 1}`, name: s.name || `Step ${i + 1}`, type: s.type || 'task', config: s.config || {}, status: 'pending', })); const workflow = { workflowId, name: input.name, description: input.description, steps, status: steps.length > 0 ? 'ready' : 'draft', currentStep: 0, variables: input.variables || {}, createdAt: new Date().toISOString(), }; store.workflows[workflowId] = workflow; saveWorkflowStore(store); return { workflowId, name: workflow.name, status: workflow.status, stepCount: steps.length, createdAt: workflow.createdAt, }; }, }, { name: 'workflow_execute', description: 'Execute a workflow', category: 'workflow', inputSchema: { type: 'object', properties: { workflowId: { type: 'string', description: 'Workflow ID to execute' }, variables: { type: 'object', description: 'Runtime variables to inject' }, startFromStep: { type: 'number', description: 'Step to start from (0-indexed)' }, }, required: ['workflowId'], }, handler: async (input) => { const store = loadWorkflowStore(); const workflowId = input.workflowId; const workflow = store.workflows[workflowId]; if (!workflow) { return { workflowId, error: 'Workflow not found' }; } if (workflow.status === 'running') { return { workflowId, error: 'Workflow already running' }; } // Inject runtime variables if (input.variables) { workflow.variables = { ...workflow.variables, ...input.variables }; } workflow.status = 'running'; workflow.startedAt = new Date().toISOString(); workflow.currentStep = input.startFromStep || 0; // Execute steps (in real implementation, this would be async/event-driven) const results = []; for (let i = workflow.currentStep; i < workflow.steps.length; i++) { const step = workflow.steps[i]; step.status = 'running'; step.startedAt = new Date().toISOString(); // For now, mark as completed (real implementation would execute actual tasks) step.status = 'completed'; step.completedAt = new Date().toISOString(); step.result = { executed: true, stepType: step.type }; results.push({ stepId: step.stepId, status: step.status }); workflow.currentStep = i + 1; } workflow.status = 'completed'; workflow.completedAt = new Date().toISOString(); saveWorkflowStore(store); return { workflowId, status: workflow.status, stepsExecuted: results.length, results, startedAt: workflow.startedAt, completedAt: workflow.completedAt, duration: new Date(workflow.completedAt).getTime() - new Date(workflow.startedAt).getTime(), }; }, }, { name: 'workflow_status', description: 'Get workflow status', category: 'workflow', inputSchema: { type: 'object', properties: { workflowId: { type: 'string', description: 'Workflow ID' }, verbose: { type: 'boolean', description: 'Include step details' }, }, required: ['workflowId'], }, handler: async (input) => { const store = loadWorkflowStore(); const workflowId = input.workflowId; const workflow = store.workflows[workflowId]; if (!workflow) { return { workflowId, error: 'Workflow not found' }; } const completedSteps = workflow.steps.filter(s => s.status === 'completed').length; const progress = workflow.steps.length > 0 ? (completedSteps / workflow.steps.length) * 100 : 0; const status = { workflowId: workflow.workflowId, name: workflow.name, status: workflow.status, progress, currentStep: workflow.currentStep, totalSteps: workflow.steps.length, completedSteps, createdAt: workflow.createdAt, startedAt: workflow.startedAt, completedAt: workflow.completedAt, }; if (input.verbose) { return { ...status, description: workflow.description, variables: workflow.variables, steps: workflow.steps.map(s => ({ stepId: s.stepId, name: s.name, type: s.type, status: s.status, startedAt: s.startedAt, completedAt: s.completedAt, })), error: workflow.error, }; } return status; }, }, { name: 'workflow_list', description: 'List all workflows', category: 'workflow', inputSchema: { type: 'object', properties: { status: { type: 'string', description: 'Filter by status' }, limit: { type: 'number', description: 'Max workflows to return' }, }, }, handler: async (input) => { const store = loadWorkflowStore(); let workflows = Object.values(store.workflows); // Apply filters if (input.status) { workflows = workflows.filter(w => w.status === input.status); } // Sort by creation date (newest first) workflows.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); // Apply limit const limit = input.limit || 20; workflows = workflows.slice(0, limit); return { workflows: workflows.map(w => ({ workflowId: w.workflowId, name: w.name, status: w.status, stepCount: w.steps.length, createdAt: w.createdAt, completedAt: w.completedAt, })), total: workflows.length, filters: { status: input.status }, }; }, }, { name: 'workflow_pause', description: 'Pause a running workflow', category: 'workflow', inputSchema: { type: 'object', properties: { workflowId: { type: 'string', description: 'Workflow ID' }, }, required: ['workflowId'], }, handler: async (input) => { const store = loadWorkflowStore(); const workflowId = input.workflowId; const workflow = store.workflows[workflowId]; if (!workflow) { return { workflowId, error: 'Workflow not found' }; } if (workflow.status !== 'running') { return { workflowId, error: 'Workflow not running' }; } workflow.status = 'paused'; saveWorkflowStore(store); return { workflowId, status: workflow.status, pausedAt: new Date().toISOString(), currentStep: workflow.currentStep, }; }, }, { name: 'workflow_resume', description: 'Resume a paused workflow', category: 'workflow', inputSchema: { type: 'object', properties: { workflowId: { type: 'string', description: 'Workflow ID' }, }, required: ['workflowId'], }, handler: async (input) => { const store = loadWorkflowStore(); const workflowId = input.workflowId; const workflow = store.workflows[workflowId]; if (!workflow) { return { workflowId, error: 'Workflow not found' }; } if (workflow.status !== 'paused') { return { workflowId, error: 'Workflow not paused' }; } workflow.status = 'running'; saveWorkflowStore(store); // Continue execution from current step const results = []; for (let i = workflow.currentStep; i < workflow.steps.length; i++) { const step = workflow.steps[i]; step.status = 'running'; step.startedAt = new Date().toISOString(); step.status = 'completed'; step.completedAt = new Date().toISOString(); step.result = { executed: true }; results.push({ stepId: step.stepId, status: step.status }); workflow.currentStep = i + 1; } workflow.status = 'completed'; workflow.completedAt = new Date().toISOString(); saveWorkflowStore(store); return { workflowId, status: workflow.status, resumed: true, stepsExecuted: results.length, completedAt: workflow.completedAt, }; }, }, { name: 'workflow_cancel', description: 'Cancel a workflow', category: 'workflow', inputSchema: { type: 'object', properties: { workflowId: { type: 'string', description: 'Workflow ID' }, reason: { type: 'string', description: 'Cancellation reason' }, }, required: ['workflowId'], }, handler: async (input) => { const store = loadWorkflowStore(); const workflowId = input.workflowId; const workflow = store.workflows[workflowId]; if (!workflow) { return { workflowId, error: 'Workflow not found' }; } if (workflow.status === 'completed' || workflow.status === 'failed') { return { workflowId, error: 'Workflow already finished' }; } workflow.status = 'failed'; workflow.error = input.reason || 'Cancelled by user'; workflow.completedAt = new Date().toISOString(); // Mark remaining steps as skipped for (let i = workflow.currentStep; i < workflow.steps.length; i++) { workflow.steps[i].status = 'skipped'; } saveWorkflowStore(store); return { workflowId, status: workflow.status, cancelledAt: workflow.completedAt, reason: workflow.error, skippedSteps: workflow.steps.length - workflow.currentStep, }; }, }, { name: 'workflow_delete', description: 'Delete a workflow', category: 'workflow', inputSchema: { type: 'object', properties: { workflowId: { type: 'string', description: 'Workflow ID' }, }, required: ['workflowId'], }, handler: async (input) => { const store = loadWorkflowStore(); const workflowId = input.workflowId; if (!store.workflows[workflowId]) { return { workflowId, error: 'Workflow not found' }; } const workflow = store.workflows[workflowId]; if (workflow.status === 'running') { return { workflowId, error: 'Cannot delete running workflow' }; } delete store.workflows[workflowId]; saveWorkflowStore(store); return { workflowId, deleted: true, deletedAt: new Date().toISOString(), }; }, }, { name: 'workflow_template', description: 'Save workflow as template or create from template', category: 'workflow', inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['save', 'create', 'list'], description: 'Template action' }, workflowId: { type: 'string', description: 'Workflow ID (for save)' }, templateId: { type: 'string', description: 'Template ID (for create)' }, templateName: { type: 'string', description: 'Template name (for save)' }, newName: { type: 'string', description: 'New workflow name (for create)' }, }, required: ['action'], }, handler: async (input) => { const store = loadWorkflowStore(); const action = input.action; if (action === 'save') { const workflow = store.workflows[input.workflowId]; if (!workflow) { return { action, error: 'Workflow not found' }; } const templateId = `template-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; const template = { ...workflow, workflowId: templateId, name: input.templateName || `${workflow.name} Template`, status: 'draft', currentStep: 0, createdAt: new Date().toISOString(), startedAt: undefined, completedAt: undefined, }; // Reset step statuses template.steps = template.steps.map(s => ({ ...s, status: 'pending', result: undefined, startedAt: undefined, completedAt: undefined, })); store.templates[templateId] = template; saveWorkflowStore(store); return { action, templateId, name: template.name, savedAt: new Date().toISOString(), }; } if (action === 'create') { const template = store.templates[input.templateId]; if (!template) { return { action, error: 'Template not found' }; } const workflowId = `workflow-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; const workflow = { ...template, workflowId, name: input.newName || template.name.replace(' Template', ''), status: 'ready', createdAt: new Date().toISOString(), }; store.workflows[workflowId] = workflow; saveWorkflowStore(store); return { action, workflowId, name: workflow.name, fromTemplate: input.templateId, createdAt: workflow.createdAt, }; } if (action === 'list') { return { action, templates: Object.values(store.templates).map(t => ({ templateId: t.workflowId, name: t.name, stepCount: t.steps.length, createdAt: t.createdAt, })), total: Object.keys(store.templates).length, }; } return { action, error: 'Unknown action' }; }, }, ]; //# sourceMappingURL=workflow-tools.js.map