Skip to main content

driven/workflows/
mod.rs

1//! Expanded Workflow Library
2//!
3//! Provides 30+ guided workflows for common development tasks,
4//! matching and exceeding BMAD-METHOD capabilities.
5
6mod builtin;
7mod engine;
8
9#[cfg(test)]
10mod property_tests;
11
12pub use builtin::*;
13pub use engine::WorkflowEngine;
14
15use serde::{Deserialize, Serialize};
16use std::collections::HashMap;
17
18/// A guided workflow for development tasks
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Workflow {
21    /// Unique identifier
22    pub id: String,
23    /// Display name
24    pub name: String,
25    /// Development phase this workflow belongs to
26    pub phase: WorkflowPhase,
27    /// Description of the workflow
28    pub description: String,
29    /// Steps in the workflow
30    pub steps: Vec<WorkflowStep>,
31    /// Variables available in the workflow
32    pub variables: HashMap<String, String>,
33    /// Checkpoint step IDs
34    pub checkpoints: Vec<String>,
35    /// Whether this is a built-in workflow
36    #[serde(default)]
37    pub builtin: bool,
38}
39
40/// Development phases for workflows
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
42#[serde(rename_all = "lowercase")]
43pub enum WorkflowPhase {
44    /// Analysis phase - understanding the problem
45    Analysis,
46    /// Planning phase - defining the solution
47    Planning,
48    /// Solutioning phase - designing the architecture
49    Solutioning,
50    /// Implementation phase - building the solution
51    Implementation,
52    /// Quick Flow - rapid development
53    QuickFlow,
54    /// Testing phase - validating the solution
55    Testing,
56    /// Documentation phase - documenting the solution
57    Documentation,
58    /// DevOps phase - deployment and operations
59    DevOps,
60}
61
62impl std::fmt::Display for WorkflowPhase {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        match self {
65            WorkflowPhase::Analysis => write!(f, "Analysis"),
66            WorkflowPhase::Planning => write!(f, "Planning"),
67            WorkflowPhase::Solutioning => write!(f, "Solutioning"),
68            WorkflowPhase::Implementation => write!(f, "Implementation"),
69            WorkflowPhase::QuickFlow => write!(f, "Quick Flow"),
70            WorkflowPhase::Testing => write!(f, "Testing"),
71            WorkflowPhase::Documentation => write!(f, "Documentation"),
72            WorkflowPhase::DevOps => write!(f, "DevOps"),
73        }
74    }
75}
76
77/// A step in a workflow
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct WorkflowStep {
80    /// Unique identifier within the workflow
81    pub id: String,
82    /// Display name
83    pub name: String,
84    /// Description of what this step does
85    pub description: String,
86    /// Agent responsible for this step
87    pub agent: String,
88    /// Actions to perform in this step
89    pub actions: Vec<String>,
90    /// Condition for executing this step
91    pub condition: Option<String>,
92    /// Branches from this step
93    pub branches: Vec<WorkflowBranch>,
94    /// Whether this is a checkpoint step
95    pub is_checkpoint: bool,
96}
97
98/// A branch in a workflow
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct WorkflowBranch {
101    /// Condition for taking this branch
102    pub condition: String,
103    /// ID of the next step
104    pub next_step: String,
105}
106
107/// Progress tracking for a workflow session
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct WorkflowProgress {
110    /// Session ID
111    pub session_id: String,
112    /// Workflow ID
113    pub workflow_id: String,
114    /// Current step ID
115    pub current_step: String,
116    /// Completed step IDs
117    pub completed_steps: Vec<String>,
118    /// Variable values
119    pub variables: HashMap<String, String>,
120    /// Started timestamp
121    pub started_at: String,
122    /// Last updated timestamp
123    pub updated_at: String,
124}
125
126/// A workflow session
127#[derive(Debug, Clone)]
128pub struct WorkflowSession {
129    /// Session ID
130    pub id: String,
131    /// The workflow being executed
132    pub workflow: Workflow,
133    /// Current progress
134    pub progress: WorkflowProgress,
135}
136
137/// Result of executing a workflow step
138#[derive(Debug, Clone)]
139pub struct StepResult {
140    /// Whether the step succeeded
141    pub success: bool,
142    /// Output from the step
143    pub output: Option<String>,
144    /// Next step ID (if branching)
145    pub next_step: Option<String>,
146    /// Whether to pause for user review
147    pub pause_for_review: bool,
148}
149
150impl Workflow {
151    /// Create a new workflow
152    pub fn new(
153        id: impl Into<String>,
154        name: impl Into<String>,
155        phase: WorkflowPhase,
156        description: impl Into<String>,
157    ) -> Self {
158        Self {
159            id: id.into(),
160            name: name.into(),
161            phase,
162            description: description.into(),
163            steps: Vec::new(),
164            variables: HashMap::new(),
165            checkpoints: Vec::new(),
166            builtin: false,
167        }
168    }
169
170    /// Add a step to the workflow
171    pub fn with_step(mut self, step: WorkflowStep) -> Self {
172        if step.is_checkpoint {
173            self.checkpoints.push(step.id.clone());
174        }
175        self.steps.push(step);
176        self
177    }
178
179    /// Add a variable to the workflow
180    pub fn with_variable(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
181        self.variables.insert(key.into(), value.into());
182        self
183    }
184
185    /// Mark as built-in workflow
186    pub fn as_builtin(mut self) -> Self {
187        self.builtin = true;
188        self
189    }
190
191    /// Get a step by ID
192    pub fn get_step(&self, id: &str) -> Option<&WorkflowStep> {
193        self.steps.iter().find(|s| s.id == id)
194    }
195
196    /// Get the first step
197    pub fn first_step(&self) -> Option<&WorkflowStep> {
198        self.steps.first()
199    }
200
201    /// Get the next step after the given step
202    pub fn next_step(&self, current_id: &str) -> Option<&WorkflowStep> {
203        let current_idx = self.steps.iter().position(|s| s.id == current_id)?;
204        self.steps.get(current_idx + 1)
205    }
206}
207
208impl WorkflowStep {
209    /// Create a new workflow step
210    pub fn new(
211        id: impl Into<String>,
212        name: impl Into<String>,
213        description: impl Into<String>,
214        agent: impl Into<String>,
215    ) -> Self {
216        Self {
217            id: id.into(),
218            name: name.into(),
219            description: description.into(),
220            agent: agent.into(),
221            actions: Vec::new(),
222            condition: None,
223            branches: Vec::new(),
224            is_checkpoint: false,
225        }
226    }
227
228    /// Add an action to this step
229    pub fn with_action(mut self, action: impl Into<String>) -> Self {
230        self.actions.push(action.into());
231        self
232    }
233
234    /// Add multiple actions
235    pub fn with_actions(mut self, actions: Vec<String>) -> Self {
236        self.actions.extend(actions);
237        self
238    }
239
240    /// Set a condition for this step
241    pub fn with_condition(mut self, condition: impl Into<String>) -> Self {
242        self.condition = Some(condition.into());
243        self
244    }
245
246    /// Add a branch
247    pub fn with_branch(
248        mut self,
249        condition: impl Into<String>,
250        next_step: impl Into<String>,
251    ) -> Self {
252        self.branches.push(WorkflowBranch {
253            condition: condition.into(),
254            next_step: next_step.into(),
255        });
256        self
257    }
258
259    /// Mark as checkpoint
260    pub fn as_checkpoint(mut self) -> Self {
261        self.is_checkpoint = true;
262        self
263    }
264}
265
266impl WorkflowProgress {
267    /// Create new progress for a workflow
268    pub fn new(session_id: impl Into<String>, workflow: &Workflow) -> Self {
269        let now = chrono::Utc::now().to_rfc3339();
270        Self {
271            session_id: session_id.into(),
272            workflow_id: workflow.id.clone(),
273            current_step: workflow
274                .first_step()
275                .map(|s| s.id.clone())
276                .unwrap_or_default(),
277            completed_steps: Vec::new(),
278            variables: workflow.variables.clone(),
279            started_at: now.clone(),
280            updated_at: now,
281        }
282    }
283
284    /// Mark a step as completed
285    pub fn complete_step(&mut self, step_id: &str) {
286        if !self.completed_steps.contains(&step_id.to_string()) {
287            self.completed_steps.push(step_id.to_string());
288        }
289        self.updated_at = chrono::Utc::now().to_rfc3339();
290    }
291
292    /// Set the current step
293    pub fn set_current_step(&mut self, step_id: impl Into<String>) {
294        self.current_step = step_id.into();
295        self.updated_at = chrono::Utc::now().to_rfc3339();
296    }
297
298    /// Set a variable value
299    pub fn set_variable(&mut self, key: impl Into<String>, value: impl Into<String>) {
300        self.variables.insert(key.into(), value.into());
301        self.updated_at = chrono::Utc::now().to_rfc3339();
302    }
303
304    /// Get a variable value
305    pub fn get_variable(&self, key: &str) -> Option<&String> {
306        self.variables.get(key)
307    }
308}
309
310impl StepResult {
311    /// Create a successful step result
312    pub fn success(output: Option<String>) -> Self {
313        Self {
314            success: true,
315            output,
316            next_step: None,
317            pause_for_review: false,
318        }
319    }
320
321    /// Create a failed step result
322    pub fn failure(output: Option<String>) -> Self {
323        Self {
324            success: false,
325            output,
326            next_step: None,
327            pause_for_review: false,
328        }
329    }
330
331    /// Set the next step
332    pub fn with_next_step(mut self, step_id: impl Into<String>) -> Self {
333        self.next_step = Some(step_id.into());
334        self
335    }
336
337    /// Mark as requiring user review
338    pub fn with_pause(mut self) -> Self {
339        self.pause_for_review = true;
340        self
341    }
342}