1mod 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#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Workflow {
21 pub id: String,
23 pub name: String,
25 pub phase: WorkflowPhase,
27 pub description: String,
29 pub steps: Vec<WorkflowStep>,
31 pub variables: HashMap<String, String>,
33 pub checkpoints: Vec<String>,
35 #[serde(default)]
37 pub builtin: bool,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
42#[serde(rename_all = "lowercase")]
43pub enum WorkflowPhase {
44 Analysis,
46 Planning,
48 Solutioning,
50 Implementation,
52 QuickFlow,
54 Testing,
56 Documentation,
58 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#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct WorkflowStep {
80 pub id: String,
82 pub name: String,
84 pub description: String,
86 pub agent: String,
88 pub actions: Vec<String>,
90 pub condition: Option<String>,
92 pub branches: Vec<WorkflowBranch>,
94 pub is_checkpoint: bool,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct WorkflowBranch {
101 pub condition: String,
103 pub next_step: String,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct WorkflowProgress {
110 pub session_id: String,
112 pub workflow_id: String,
114 pub current_step: String,
116 pub completed_steps: Vec<String>,
118 pub variables: HashMap<String, String>,
120 pub started_at: String,
122 pub updated_at: String,
124}
125
126#[derive(Debug, Clone)]
128pub struct WorkflowSession {
129 pub id: String,
131 pub workflow: Workflow,
133 pub progress: WorkflowProgress,
135}
136
137#[derive(Debug, Clone)]
139pub struct StepResult {
140 pub success: bool,
142 pub output: Option<String>,
144 pub next_step: Option<String>,
146 pub pause_for_review: bool,
148}
149
150impl Workflow {
151 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 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 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 pub fn as_builtin(mut self) -> Self {
187 self.builtin = true;
188 self
189 }
190
191 pub fn get_step(&self, id: &str) -> Option<&WorkflowStep> {
193 self.steps.iter().find(|s| s.id == id)
194 }
195
196 pub fn first_step(&self) -> Option<&WorkflowStep> {
198 self.steps.first()
199 }
200
201 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 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 pub fn with_action(mut self, action: impl Into<String>) -> Self {
230 self.actions.push(action.into());
231 self
232 }
233
234 pub fn with_actions(mut self, actions: Vec<String>) -> Self {
236 self.actions.extend(actions);
237 self
238 }
239
240 pub fn with_condition(mut self, condition: impl Into<String>) -> Self {
242 self.condition = Some(condition.into());
243 self
244 }
245
246 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 pub fn as_checkpoint(mut self) -> Self {
261 self.is_checkpoint = true;
262 self
263 }
264}
265
266impl WorkflowProgress {
267 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 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 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 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 pub fn get_variable(&self, key: &str) -> Option<&String> {
306 self.variables.get(key)
307 }
308}
309
310impl StepResult {
311 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 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 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 pub fn with_pause(mut self) -> Self {
339 self.pause_for_review = true;
340 self
341 }
342}