Skip to main content

ai_agent/constants/
output_styles.rs

1//! Output styles module
2//!
3//! Defines output style configurations for different modes of interaction.
4
5use std::collections::HashMap;
6
7/// Source of the output style configuration
8#[derive(Debug, Clone, PartialEq)]
9pub enum OutputStyleSource {
10    BuiltIn,
11    Plugin,
12    PolicySettings,
13    UserSettings,
14    ProjectSettings,
15}
16
17impl OutputStyleSource {
18    pub fn as_str(&self) -> &str {
19        match self {
20            OutputStyleSource::BuiltIn => "built-in",
21            OutputStyleSource::Plugin => "plugin",
22            OutputStyleSource::PolicySettings => "policySettings",
23            OutputStyleSource::UserSettings => "userSettings",
24            OutputStyleSource::ProjectSettings => "projectSettings",
25        }
26    }
27}
28
29/// Configuration for an output style
30#[derive(Debug, Clone)]
31pub struct OutputStyleConfig {
32    pub name: String,
33    pub description: String,
34    pub prompt: String,
35    pub source: OutputStyleSource,
36    pub keep_coding_instructions: Option<bool>,
37    pub force_for_plugin: Option<bool>,
38}
39
40impl OutputStyleConfig {
41    pub fn new(name: &str, description: &str, prompt: &str) -> Self {
42        Self {
43            name: name.to_string(),
44            description: description.to_string(),
45            prompt: prompt.to_string(),
46            source: OutputStyleSource::BuiltIn,
47            keep_coding_instructions: None,
48            force_for_plugin: None,
49        }
50    }
51
52    pub fn with_keep_coding_instructions(mut self, keep: bool) -> Self {
53        self.keep_coding_instructions = Some(keep);
54        self
55    }
56
57    pub fn with_source(mut self, source: OutputStyleSource) -> Self {
58        self.source = source;
59        self
60    }
61}
62
63/// Default output style name
64pub const DEFAULT_OUTPUT_STYLE_NAME: &str = "default";
65
66/// Explanatory feature prompt used in both Explanatory and Learning modes
67pub const EXPLANATORY_FEATURE_PROMPT: &str = r#"## Insights
68In order to encourage learning, before and after writing code, always provide brief educational explanations about implementation choices using (with backticks):
69"`* Insight ─────────────────────────────────────`
70[2-3 key educational points]
71`─────────────────────────────────────────────────`"
72
73These insights should be included in the conversation, not in the codebase. You should generally focus on interesting insights that are specific to the codebase or the code you just wrote, rather than general programming concepts."#;
74
75/// Get the built-in output style configuration
76pub fn get_output_style_config() -> HashMap<String, Option<OutputStyleConfig>> {
77    let mut styles = HashMap::new();
78
79    // Default style - null means use default behavior
80    styles.insert(DEFAULT_OUTPUT_STYLE_NAME.to_string(), None);
81
82    // Explanatory style
83    let explanatory_prompt = format!(
84        "You are an interactive CLI tool that helps users with software engineering tasks. In addition to software engineering tasks, you should provide educational insights about the codebase along the way.\n\nYou should be clear and educational, providing helpful explanations while remaining focused on the task. Balance educational content with task completion. When providing insights, you may exceed typical length constraints, but remain focused and relevant.\n\n# Explanatory Style Active\n{}",
85        EXPLANATORY_FEATURE_PROMPT
86    );
87
88    styles.insert(
89        "Explanatory".to_string(),
90        Some(
91            OutputStyleConfig::new(
92                "Explanatory",
93                "Claude explains its implementation choices and codebase patterns",
94                &explanatory_prompt,
95            )
96            .with_keep_coding_instructions(true),
97        ),
98    );
99
100    // Learning style
101    let learning_prompt = format!(
102        "You are an interactive CLI tool that helps users with software engineering tasks. In addition to software engineering tasks, you should help users learn more about the codebase through hands-on practice and educational insights.\n\nYou should be collaborative and encouraging. Balance task completion with learning by requesting user input for meaningful design decisions while handling routine implementation yourself.\n\n# Learning Style Active\n## Requesting Human Contributions\nIn order to encourage learning, ask the human to contribute 2-10 line code pieces when generating 20+ lines involving:\n- Design decisions (error handling, data structures)\n- Business logic with multiple valid approaches\n- Key algorithms or interface definitions\n\n## Insights\n{}",
103        EXPLANATORY_FEATURE_PROMPT
104    );
105
106    styles.insert(
107        "Learning".to_string(),
108        Some(
109            OutputStyleConfig::new(
110                "Learning",
111                "Claude pauses and asks you to write small pieces of code for hands-on practice",
112                &learning_prompt,
113            )
114            .with_keep_coding_instructions(true),
115        ),
116    );
117
118    styles
119}
120
121/// Get all built-in output style names
122pub fn get_builtin_style_names() -> Vec<&'static str> {
123    vec![DEFAULT_OUTPUT_STYLE_NAME, "Explanatory", "Learning"]
124}
125
126/// Check if a style name is valid
127pub fn is_valid_style_name(name: &str) -> bool {
128    get_builtin_style_names().contains(&name) || name == DEFAULT_OUTPUT_STYLE_NAME
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_default_style() {
137        let styles = get_output_style_config();
138        assert!(styles.get(DEFAULT_OUTPUT_STYLE_NAME).is_some());
139        assert!(styles.get(DEFAULT_OUTPUT_STYLE_NAME).unwrap().is_none());
140    }
141
142    #[test]
143    fn test_explanatory_style() {
144        let styles = get_output_style_config();
145        let explanatory = styles.get("Explanatory").unwrap();
146        assert!(explanatory.is_some());
147        let config = explanatory.as_ref().unwrap();
148        assert_eq!(config.name, "Explanatory");
149        assert!(config.keep_coding_instructions.unwrap_or(false));
150    }
151
152    #[test]
153    fn test_learning_style() {
154        let styles = get_output_style_config();
155        let learning = styles.get("Learning").unwrap();
156        assert!(learning.is_some());
157        let config = learning.as_ref().unwrap();
158        assert_eq!(config.name, "Learning");
159    }
160
161    #[test]
162    fn test_valid_style_names() {
163        assert!(is_valid_style_name("default"));
164        assert!(is_valid_style_name("Explanatory"));
165        assert!(is_valid_style_name("Learning"));
166        assert!(!is_valid_style_name("Invalid"));
167    }
168}