import * as fs from 'fs-extra'; import * as path from 'path'; import * as yaml from 'yaml'; export interface WorkflowPhase { name: string; description: string; agents: string[]; dependencies?: string[]; deliverables: string[]; quality_gates: string[]; condition?: string; } export interface WorkflowDefinition { workflow: { name: string; version: string; description: string; enhanced_features?: string[]; }; phases: WorkflowPhase[]; quality_gates?: Record; success_metrics?: Record; risk_management?: Record; } export interface WorkflowState { workflowId: string; currentPhase: number; phaseStatus: 'pending' | 'in_progress' | 'completed' | 'blocked'; completedPhases: string[]; artifacts: Record; startedAt: Date; lastUpdated: Date; } export class WorkflowEngine { private vcsysRoot: string; private workflowsDir: string; private stateFile: string; constructor(projectRoot?: string) { this.vcsysRoot = projectRoot ? path.join(projectRoot, '.vcsys') : path.join(process.cwd(), '.vcsys'); this.workflowsDir = path.join(this.vcsysRoot, 'workflows'); this.stateFile = path.join(this.vcsysRoot, 'workflow-state.json'); } async listWorkflows(): Promise> { try { const files = await fs.readdir(this.workflowsDir); const workflows = []; for (const file of files) { if (file.endsWith('.yaml') || file.endsWith('.yml')) { const filePath = path.join(this.workflowsDir, file); const content = await fs.readFile(filePath, 'utf-8'); const workflow = yaml.parse(content) as WorkflowDefinition; workflows.push({ id: path.basename(file, path.extname(file)), name: workflow.workflow.name, description: workflow.workflow.description }); } } return workflows; } catch (error) { throw new Error(`Failed to list workflows: ${error}`); } } async loadWorkflow(workflowId: string): Promise { const workflowFile = path.join(this.workflowsDir, `${workflowId}.yaml`); if (!await fs.pathExists(workflowFile)) { throw new Error(`Workflow not found: ${workflowId}`); } try { const content = await fs.readFile(workflowFile, 'utf-8'); return yaml.parse(content) as WorkflowDefinition; } catch (error) { throw new Error(`Failed to load workflow ${workflowId}: ${error}`); } } async startWorkflow(workflowId: string): Promise { const workflow = await this.loadWorkflow(workflowId); const state: WorkflowState = { workflowId, currentPhase: 0, phaseStatus: 'pending', completedPhases: [], artifacts: {}, startedAt: new Date(), lastUpdated: new Date() }; await this.saveState(state); return state; } async getWorkflowStatus(): Promise { try { if (await fs.pathExists(this.stateFile)) { const content = await fs.readFile(this.stateFile, 'utf-8'); const state = JSON.parse(content) as WorkflowState; // Convert date strings back to Date objects state.startedAt = new Date(state.startedAt); state.lastUpdated = new Date(state.lastUpdated); return state; } return null; } catch (error) { throw new Error(`Failed to get workflow status: ${error}`); } } async getCurrentPhase(): Promise<{ phase: WorkflowPhase; workflow: WorkflowDefinition } | null> { const state = await this.getWorkflowStatus(); if (!state) return null; const workflow = await this.loadWorkflow(state.workflowId); const phase = workflow.phases[state.currentPhase]; return { phase, workflow }; } async getNextPhase(): Promise<{ phase: WorkflowPhase; phaseIndex: number } | null> { const state = await this.getWorkflowStatus(); if (!state) return null; const workflow = await this.loadWorkflow(state.workflowId); const nextIndex = state.currentPhase + 1; if (nextIndex >= workflow.phases.length) { return null; // Workflow complete } return { phase: workflow.phases[nextIndex], phaseIndex: nextIndex }; } async advancePhase(): Promise { const state = await this.getWorkflowStatus(); if (!state) { throw new Error('No active workflow found'); } const workflow = await this.loadWorkflow(state.workflowId); // Mark current phase as completed if (state.currentPhase < workflow.phases.length) { const currentPhase = workflow.phases[state.currentPhase]; state.completedPhases.push(currentPhase.name); } // Move to next phase state.currentPhase += 1; if (state.currentPhase >= workflow.phases.length) { state.phaseStatus = 'completed'; } else { state.phaseStatus = 'pending'; } state.lastUpdated = new Date(); await this.saveState(state); return state; } async updatePhaseStatus(status: 'pending' | 'in_progress' | 'completed' | 'blocked'): Promise { const state = await this.getWorkflowStatus(); if (!state) { throw new Error('No active workflow found'); } state.phaseStatus = status; state.lastUpdated = new Date(); await this.saveState(state); return state; } async addArtifact(phaseName: string, artifact: string): Promise { const state = await this.getWorkflowStatus(); if (!state) { throw new Error('No active workflow found'); } if (!state.artifacts[phaseName]) { state.artifacts[phaseName] = []; } state.artifacts[phaseName].push(artifact); state.lastUpdated = new Date(); await this.saveState(state); } async resetWorkflow(): Promise { if (await fs.pathExists(this.stateFile)) { await fs.remove(this.stateFile); } } private async saveState(state: WorkflowState): Promise { try { await fs.ensureDir(path.dirname(this.stateFile)); await fs.writeFile(this.stateFile, JSON.stringify(state, null, 2)); } catch (error) { throw new Error(`Failed to save workflow state: ${error}`); } } async validateDependencies(phase: WorkflowPhase): Promise { if (!phase.dependencies) return true; const state = await this.getWorkflowStatus(); if (!state) return false; return phase.dependencies.every(dep => state.completedPhases.includes(dep)); } async isWorkflowComplete(): Promise { const state = await this.getWorkflowStatus(); if (!state) return false; const workflow = await this.loadWorkflow(state.workflowId); return state.currentPhase >= workflow.phases.length && state.phaseStatus === 'completed'; } }