Skip to main content

driven/templates/
mod.rs

1//! Template System
2//!
3//! Built-in library of professional AI agent templates.
4
5mod composer;
6mod persona;
7mod project;
8mod registry;
9mod standards;
10mod task;
11mod workflow;
12
13pub use composer::TemplateComposer;
14pub use persona::PersonaTemplate;
15pub use project::ProjectTemplate;
16pub use registry::TemplateRegistry;
17pub use standards::StandardsTemplate;
18pub use task::TaskTemplate;
19pub use workflow::WorkflowTemplate;
20
21use crate::{Result, parser::UnifiedRule};
22
23/// A loadable template
24pub trait Template: Send + Sync {
25    /// Get the template name
26    fn name(&self) -> &str;
27
28    /// Get a description of this template
29    fn description(&self) -> &str;
30
31    /// Get the category of this template
32    fn category(&self) -> TemplateCategory;
33
34    /// Expand this template to rules
35    fn expand(&self) -> Result<Vec<UnifiedRule>>;
36
37    /// Get template tags for discovery
38    fn tags(&self) -> Vec<&str> {
39        Vec::new()
40    }
41
42    /// Render this template to a string
43    fn render(&self, _variables: &std::collections::HashMap<String, String>) -> Result<String> {
44        // Default implementation: expand to rules and format as markdown
45        let rules = self.expand()?;
46        let mut output = format!("# {} Template\n\n", self.name());
47        output.push_str(&format!("{}\n\n", self.description()));
48
49        for rule in rules {
50            match rule {
51                UnifiedRule::Persona {
52                    name,
53                    role,
54                    identity,
55                    style,
56                    traits,
57                    principles,
58                } => {
59                    output.push_str("## Persona\n\n");
60                    output.push_str(&format!("**{}** - {}\n\n", name, role));
61                    if let Some(id) = identity {
62                        output.push_str(&format!("{}\n\n", id));
63                    }
64                    if let Some(s) = style {
65                        output.push_str(&format!("Style: {}\n\n", s));
66                    }
67                    if !traits.is_empty() {
68                        output.push_str("### Traits\n");
69                        for t in traits {
70                            output.push_str(&format!("- {}\n", t));
71                        }
72                        output.push('\n');
73                    }
74                    if !principles.is_empty() {
75                        output.push_str("### Principles\n");
76                        for p in principles {
77                            output.push_str(&format!("- {}\n", p));
78                        }
79                        output.push('\n');
80                    }
81                }
82                UnifiedRule::Standard {
83                    category,
84                    description,
85                    ..
86                } => {
87                    output.push_str(&format!("### {:?}\n", category));
88                    output.push_str(&format!("- {}\n\n", description));
89                }
90                UnifiedRule::Context {
91                    includes,
92                    excludes,
93                    focus,
94                } => {
95                    output.push_str("## Context\n\n");
96                    if !includes.is_empty() {
97                        output.push_str("### Include\n");
98                        for inc in includes {
99                            output.push_str(&format!("- {}\n", inc));
100                        }
101                        output.push('\n');
102                    }
103                    if !excludes.is_empty() {
104                        output.push_str("### Exclude\n");
105                        for exc in excludes {
106                            output.push_str(&format!("- {}\n", exc));
107                        }
108                        output.push('\n');
109                    }
110                    if !focus.is_empty() {
111                        output.push_str("### Focus\n");
112                        for f in focus {
113                            output.push_str(&format!("- {}\n", f));
114                        }
115                        output.push('\n');
116                    }
117                }
118                UnifiedRule::Workflow { name, steps } => {
119                    output.push_str(&format!("## Workflow: {}\n\n", name));
120                    for step in steps {
121                        output.push_str(&format!("### Step: {}\n", step.name));
122                        output.push_str(&format!("{}\n\n", step.description));
123                    }
124                }
125                UnifiedRule::Raw { content } => {
126                    output.push_str(&content);
127                    output.push('\n');
128                }
129            }
130        }
131
132        Ok(output)
133    }
134}
135
136/// Template categories
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
138pub enum TemplateCategory {
139    /// AI persona definitions
140    Persona,
141    /// Project structure templates
142    Project,
143    /// Coding standards
144    Standards,
145    /// Development workflows
146    Workflow,
147    /// Task-specific guidance
148    Task,
149}
150
151impl std::fmt::Display for TemplateCategory {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        match self {
154            TemplateCategory::Persona => write!(f, "Persona"),
155            TemplateCategory::Project => write!(f, "Project"),
156            TemplateCategory::Standards => write!(f, "Standards"),
157            TemplateCategory::Workflow => write!(f, "Workflow"),
158            TemplateCategory::Task => write!(f, "Task"),
159        }
160    }
161}
162
163/// Built-in template definitions
164pub mod builtin {
165    use super::*;
166
167    /// Get all built-in persona templates
168    pub fn personas() -> Vec<Box<dyn Template>> {
169        vec![
170            Box::new(PersonaTemplate::architect()),
171            Box::new(PersonaTemplate::reviewer()),
172            Box::new(PersonaTemplate::documenter()),
173            Box::new(PersonaTemplate::security()),
174            Box::new(PersonaTemplate::performance()),
175            Box::new(PersonaTemplate::teacher()),
176        ]
177    }
178
179    /// Get all built-in project templates
180    pub fn projects() -> Vec<Box<dyn Template>> {
181        vec![
182            Box::new(ProjectTemplate::rust_workspace()),
183            Box::new(ProjectTemplate::typescript_monorepo()),
184            Box::new(ProjectTemplate::fullstack()),
185            Box::new(ProjectTemplate::cli_tool()),
186            Box::new(ProjectTemplate::library()),
187        ]
188    }
189
190    /// Get all built-in standards templates
191    pub fn standards() -> Vec<Box<dyn Template>> {
192        vec![
193            Box::new(StandardsTemplate::rust_idioms()),
194            Box::new(StandardsTemplate::error_handling()),
195            Box::new(StandardsTemplate::testing()),
196            Box::new(StandardsTemplate::documentation()),
197            Box::new(StandardsTemplate::git_conventions()),
198        ]
199    }
200
201    /// Get all built-in workflow templates
202    pub fn workflows() -> Vec<Box<dyn Template>> {
203        vec![
204            Box::new(WorkflowTemplate::tdd()),
205            Box::new(WorkflowTemplate::feature_development()),
206            Box::new(WorkflowTemplate::bug_fixing()),
207            Box::new(WorkflowTemplate::refactoring()),
208            Box::new(WorkflowTemplate::code_review()),
209        ]
210    }
211
212    /// Get all built-in task templates
213    pub fn tasks() -> Vec<Box<dyn Template>> {
214        vec![
215            Box::new(TaskTemplate::implement_feature()),
216            Box::new(TaskTemplate::write_tests()),
217            Box::new(TaskTemplate::fix_bug()),
218            Box::new(TaskTemplate::optimize()),
219            Box::new(TaskTemplate::document()),
220        ]
221    }
222
223    /// Get all built-in templates
224    pub fn all() -> Vec<Box<dyn Template>> {
225        let mut all = Vec::new();
226        all.extend(personas());
227        all.extend(projects());
228        all.extend(standards());
229        all.extend(workflows());
230        all.extend(tasks());
231        all
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    fn test_category_display() {
241        assert_eq!(format!("{}", TemplateCategory::Persona), "Persona");
242        assert_eq!(format!("{}", TemplateCategory::Workflow), "Workflow");
243    }
244
245    #[test]
246    fn test_builtin_templates() {
247        let all = builtin::all();
248        assert!(!all.is_empty());
249
250        // Check we have templates in each category
251        let personas = builtin::personas();
252        assert!(!personas.is_empty());
253    }
254}