//! Workflow script — parse and execute user-defined workflow scripts. use anyhow::Result; use tracing::info; /// A single phase in a parsed workflow script. #[derive(Debug, Clone)] pub struct WorkflowPhase { pub name: String, pub directive: String, } /// A parsed workflow script with named phases. #[derive(Debug, Clone)] pub struct WorkflowScript { pub name: String, pub phases: Vec, } impl WorkflowScript { /// Parse a YAML string into a WorkflowScript. /// /// Expected format: /// ```yaml /// name: my-workflow /// phases: /// - name: research /// directive: "Explore the codebase..." /// - name: implement /// directive: "Implement the changes..." /// ``` pub fn parse(yaml: &str) -> Result { let parsed: serde_json::Value = serde_yaml_ng::from_str(yaml) .map_err(|e| anyhow::anyhow!("Failed to parse workflow YAML: {e}"))?; let name = parsed .get("name") .and_then(|v| v.as_str()) .unwrap_or("unnamed") .to_string(); let mut phases = Vec::new(); if let Some(phases_arr) = parsed.get("phases").and_then(|v| v.as_array()) { for phase_val in phases_arr { let phase_name = phase_val .get("name") .and_then(|v| v.as_str()) .unwrap_or("phase") .to_string(); let directive = phase_val .get("directive") .and_then(|v| v.as_str()) .unwrap_or("") .to_string(); phases.push(WorkflowPhase { name: phase_name, directive, }); } } info!("Parsed workflow script: {name} ({} phases)", phases.len()); Ok(WorkflowScript { name, phases }) } }